From 529c0d1e555772fd6e58c525446572f5d1e0062b Mon Sep 17 00:00:00 2001 From: "Brian M. Long" Date: Wed, 21 Dec 2022 16:40:14 -0500 Subject: [PATCH 1/3] added README --- README.md | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..7b893b1 --- /dev/null +++ b/README.md @@ -0,0 +1,49 @@ +# Activiti API Doclet + +This library provides a Javadoc Doclet for the generation of Activiti API documentation. You can use it by defining a Javadoc plugin in your Activiti extension Maven JAR project. That plugin should be configured to use this doclet. See the snippet below. + +```xml + + apidocs + + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.4.1 + + + generate-doclet + generate-resources + javadoc + + com.inteligr8.activiti.doclet.ActivitiDoclet + + com.inteligr8.activiti + activiti-api-doclet + 1.0.0 + + false + destDir + + -d d + --flavor bitbucket + --title 'API Documentation' + --apiName '${project.name} + + + + + + + + +``` + +You can generate the docs with the following command: + +``` +mvn -Papidocs generate-resources +``` + +The documentation is always generated relative to the `target/site/apidocs` directory. Hopefully that can be fixed in the future. From cf5a753d1fb3b7b48da718b80a218f4d89af5ac1 Mon Sep 17 00:00:00 2001 From: Brian Long Date: Wed, 21 Dec 2022 17:14:53 -0500 Subject: [PATCH 2/3] fixed output directory --- README.md | 8 +++---- src/it/base/pom.xml | 6 +++-- .../activiti/doclet/ActivitiDoclet.java | 24 ++----------------- .../activiti/doclet/MarkdownWriter.java | 11 ++------- 4 files changed, 12 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index 7b893b1..ee4dd59 100644 --- a/README.md +++ b/README.md @@ -21,12 +21,12 @@ This library provides a Javadoc Doclet for the generation of Activiti API docume com.inteligr8.activiti activiti-api-doclet - 1.0.0 + 1.0.1 false - destDir + apidocs + ${basedir} - -d d --flavor bitbucket --title 'API Documentation' --apiName '${project.name} @@ -46,4 +46,4 @@ You can generate the docs with the following command: mvn -Papidocs generate-resources ``` -The documentation is always generated relative to the `target/site/apidocs` directory. Hopefully that can be fixed in the future. +The documentation will be placed in `/`. diff --git a/src/it/base/pom.xml b/src/it/base/pom.xml index 9c6ce45..cccac76 100644 --- a/src/it/base/pom.xml +++ b/src/it/base/pom.xml @@ -50,10 +50,12 @@ @pom.version@ false - destDir + apidocs + ${basedir} - -d d --flavor bitbucket + --title 'Example Title' + --apiName '${project.name}' diff --git a/src/main/java/com/inteligr8/activiti/doclet/ActivitiDoclet.java b/src/main/java/com/inteligr8/activiti/doclet/ActivitiDoclet.java index a7c2681..90a7781 100644 --- a/src/main/java/com/inteligr8/activiti/doclet/ActivitiDoclet.java +++ b/src/main/java/com/inteligr8/activiti/doclet/ActivitiDoclet.java @@ -44,7 +44,7 @@ import jdk.javadoc.doclet.Reporter; public class ActivitiDoclet implements Doclet { private final Logger logger = LoggerFactory.getLogger(this.getClass()); - private String outputDirectory; + private String flavor; private String title; private String apiName; @@ -57,13 +57,6 @@ public class ActivitiDoclet implements Doclet { @Override public Set getSupportedOptions() { return new HashSet<>(Arrays.asList( - new PathOption("-d", "Destination directory for output files", Kind.STANDARD, "--destDir") { - @Override - public boolean process(String name, List valueAndClasses) { - outputDirectory = valueAndClasses.get(0); - return true; - } - }, new ValueOption("--flavor", "Flavor of the markdown host: github or bitbucket", Kind.STANDARD) { @Override public boolean process(String name, List valueAndClasses) { @@ -122,7 +115,7 @@ public class ActivitiDoclet implements Doclet { List beandocs = docfilter.build(); try { - MarkdownWriter mdwriter = new MarkdownWriter(docenv, this.outputDirectory); + MarkdownWriter mdwriter = new MarkdownWriter(docenv); if (this.title != null) mdwriter.setTitle(this.title); if (this.apiName != null) @@ -192,18 +185,5 @@ public class ActivitiDoclet implements Doclet { } } - - private abstract class PathOption extends ValueOption { - - public PathOption(String name, String description, Kind kind, String... additionalNames) { - super(name, description, kind, additionalNames); - } - - @Override - public String getParameters() { - return ""; - } - - } } diff --git a/src/main/java/com/inteligr8/activiti/doclet/MarkdownWriter.java b/src/main/java/com/inteligr8/activiti/doclet/MarkdownWriter.java index 9e5e1c5..15f001a 100644 --- a/src/main/java/com/inteligr8/activiti/doclet/MarkdownWriter.java +++ b/src/main/java/com/inteligr8/activiti/doclet/MarkdownWriter.java @@ -61,15 +61,9 @@ class MarkdownWriter { private String apiName; private String flavor; - public MarkdownWriter(DocletEnvironment docenv, String outputDirectory) throws IOException { + public MarkdownWriter(DocletEnvironment docenv) throws IOException { this.docenv = docenv; - this.outputDirectory = (outputDirectory == null || outputDirectory.trim().length() == 0) ? new File(".") : new File(outputDirectory); - this.fmconfig = this.getFreemarkerConfiguration(); - } - - public MarkdownWriter(DocletEnvironment docenv, File outputDirectory) throws IOException { - this.docenv = docenv; - this.outputDirectory = outputDirectory == null ? new File(".") : outputDirectory; + this.outputDirectory = new File("."); this.fmconfig = this.getFreemarkerConfiguration(); } @@ -111,7 +105,6 @@ class MarkdownWriter { this.logger.debug("Building documentation index model"); IndexFreemarkerModel indexModel = new IndexFreemarkerModel(); - System.out.println("title: " + this.title); indexModel.setTitle(this.title); indexModel.setApiName(this.apiName); From 20c8adbfd2f8ef3e6003d4824b6a785787d3fcfe Mon Sep 17 00:00:00 2001 From: Brian Long Date: Thu, 22 Dec 2022 15:57:44 -0500 Subject: [PATCH 3/3] @throws expansion; general fixes --- src/it/base/src/main/java/TestNamedBean.java | 6 + .../activiti/doclet/ActivitiApiBeanDoc.java | 7 +- .../activiti/doclet/ActivitiDocFilter.java | 1 - .../inteligr8/activiti/doclet/FluentMap.java | 95 ++++++++++++++ .../activiti/doclet/MarkdownWriter.java | 117 +++++++++++++----- .../MethodSignatureFreemarkerModel.java | 10 ++ src/main/resources/templates/bean.md.ftl | 46 ++++--- 7 files changed, 233 insertions(+), 49 deletions(-) create mode 100644 src/main/java/com/inteligr8/activiti/doclet/FluentMap.java diff --git a/src/it/base/src/main/java/TestNamedBean.java b/src/it/base/src/main/java/TestNamedBean.java index 7021dac..e4cf7f0 100644 --- a/src/it/base/src/main/java/TestNamedBean.java +++ b/src/it/base/src/main/java/TestNamedBean.java @@ -7,6 +7,8 @@ import org.springframework.stereotype.Component; * Here is a second line that happens * to span multiple lines. * + * Here is a {@link http://inteligr8.com} for testing purposes. And here is another { @link http://inteligr8.com } one. + * * @author brian@inteligr8.com * @version 1.0 */ @@ -81,6 +83,10 @@ public class TestNamedBean { * @param param A parameter comment. * @return A return comment. * @throws java.lang.Exception An exception comment. + * @throws org.activiti.engine.delegate.BpmnError http-404 + * @throws BpmnError http-400 A client error. + * @see `test.bean` + * @see http://inteligr8.com */ public String apiMethod8(String param) throws Exception { return null; diff --git a/src/main/java/com/inteligr8/activiti/doclet/ActivitiApiBeanDoc.java b/src/main/java/com/inteligr8/activiti/doclet/ActivitiApiBeanDoc.java index 90ff6f3..8ffd654 100644 --- a/src/main/java/com/inteligr8/activiti/doclet/ActivitiApiBeanDoc.java +++ b/src/main/java/com/inteligr8/activiti/doclet/ActivitiApiBeanDoc.java @@ -54,9 +54,12 @@ class ActivitiApiBeanDoc { return this; } + public List getMethodElements() { + return new ArrayList<>(this.methodElements.values()); + } + public List getSortedMethodElements() { - List methodElements = new ArrayList<>(this.methodElements.size()); - methodElements.addAll(this.methodElements.values()); + List methodElements = this.getMethodElements(); Collections.sort(methodElements, new Comparator() { @Override public int compare(ExecutableElement methodElement1, ExecutableElement methodElement2) { diff --git a/src/main/java/com/inteligr8/activiti/doclet/ActivitiDocFilter.java b/src/main/java/com/inteligr8/activiti/doclet/ActivitiDocFilter.java index 89ff82e..e413d55 100644 --- a/src/main/java/com/inteligr8/activiti/doclet/ActivitiDocFilter.java +++ b/src/main/java/com/inteligr8/activiti/doclet/ActivitiDocFilter.java @@ -91,7 +91,6 @@ class ActivitiDocFilter { // getAllTypeElements() will get inherited interfaces for (TypeMirror interfaceType : classElement.getInterfaces()) { - this.logger.info("{} contains {}", beanId, interfaceType); this.logger.trace("Found interface '{}' on bean '{}'", interfaceType, beanId); switch (interfaceType.toString()) { diff --git a/src/main/java/com/inteligr8/activiti/doclet/FluentMap.java b/src/main/java/com/inteligr8/activiti/doclet/FluentMap.java new file mode 100644 index 0000000..94ab54c --- /dev/null +++ b/src/main/java/com/inteligr8/activiti/doclet/FluentMap.java @@ -0,0 +1,95 @@ +package com.inteligr8.activiti.doclet; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +public class FluentMap implements Map { + + private Map map; + + public FluentMap(Map map) { + this.map = map; + } + + @Override + public void clear() { + this.map.clear(); + } + + @Override + public boolean containsKey(Object key) { + return this.map.containsKey(key); + } + + @Override + public boolean containsValue(Object value) { + return this.map.containsValue(value); + } + + @Override + public Set> entrySet() { + return this.map.entrySet(); + } + + @Override + public boolean equals(Object obj) { + return this.map.equals(obj); + } + + @Override + public V get(Object key) { + return this.map.get(key); + } + + @Override + public int hashCode() { + return this.map.hashCode(); + } + + @Override + public boolean isEmpty() { + return this.map.isEmpty(); + } + + @Override + public Set keySet() { + return this.map.keySet(); + } + + @Override + public V put(K key, V value) { + return this.map.put(key, value); + } + + @Override + public void putAll(Map m) { + this.map.putAll(m); + } + + @Override + public V remove(Object key) { + return this.map.remove(key); + } + + @Override + public int size() { + return this.map.size(); + } + + @Override + public Collection values() { + return this.map.values(); + } + + public FluentMap with(K key, V value) { + this.map.put(key, value); + return this; + } + + public FluentMap without(K key) { + this.map.remove(key); + return this; + } + +} diff --git a/src/main/java/com/inteligr8/activiti/doclet/MarkdownWriter.java b/src/main/java/com/inteligr8/activiti/doclet/MarkdownWriter.java index 15f001a..bc0a9ef 100644 --- a/src/main/java/com/inteligr8/activiti/doclet/MarkdownWriter.java +++ b/src/main/java/com/inteligr8/activiti/doclet/MarkdownWriter.java @@ -18,11 +18,15 @@ import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.nio.charset.Charset; +import java.util.Arrays; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -52,11 +56,25 @@ import jdk.javadoc.doclet.DocletEnvironment; class MarkdownWriter { private final Logger logger = LoggerFactory.getLogger(this.getClass()); - private final Pattern tagPattern = Pattern.compile("@[^ ]+ ?(.*)$"); - private final Pattern namedTagPattern = Pattern.compile("@[^ ]+ ([^ ]+) ?(.*)$"); + private final Set bpmnErrorSet = new HashSet<>(Arrays.asList( + "BpmnError".toLowerCase(), + "org.activiti.engine.delegate.BpmnError".toLowerCase())); + + private final Pattern tagPattern = Pattern.compile("@([^ ]+) ?(.*)$"); + private final Pattern namedCommentPattern = Pattern.compile("([^ ]+) ?(.*)$"); + private final Map sanitizations = new FluentMap<>(new LinkedHashMap()) + .with(Pattern.compile("^[ ]+", Pattern.MULTILINE), "") + .with(Pattern.compile("\r\n", Pattern.MULTILINE), "\n") + .with(Pattern.compile("([^\n])\n([^\n])", Pattern.MULTILINE), "$1 $2") + .with(Pattern.compile("([^\\.\\?!])[ ]{2}", Pattern.MULTILINE), "$1 ") + .with(Pattern.compile("\\{ ?(@[^}]+) \\}"), "{$1}") + .with(Pattern.compile("\\{@link ([^}]+)\\}"), "[$1]($1)") + .with(Pattern.compile("([A-Za-z0-9\\-\\._]+@[A-Za-z0-9\\-\\._]+)"), "[$1](mailto:$1)"); + private final DocletEnvironment docenv; private final File outputDirectory; private final Configuration fmconfig; + private String title = "API Documentation"; private String apiName; private String flavor; @@ -128,9 +146,9 @@ class MarkdownWriter { this.logger.debug("Found documentation for bean: {}", beandoc.getBeanId()); if (commentTree.getFirstSentence() != null) - beanModel.setDocFirstSentence(commentTree.getFirstSentence().toString()); + beanModel.setDocFirstSentence(this.sanitize(commentTree.getFirstSentence())); if (commentTree.getBody() != null) - beanModel.setDocBody(commentTree.getFullBody().toString()); + beanModel.setDocBody(this.sanitize(commentTree.getFullBody())); for (DocTree tag : commentTree.getBlockTags()) { String tagName = null; if (tag instanceof BlockTagTree) { @@ -165,50 +183,57 @@ class MarkdownWriter { } Map paramComments = new HashMap<>(); - Map throwComments = new HashMap<>(); + Map throwComments = new LinkedHashMap<>(); + Map errorComments = new LinkedHashMap<>(); MethodSignatureFreemarkerModel sigModel = new MethodSignatureFreemarkerModel(); if (methodCommentTree != null) { this.logger.debug("Found documentation for method: {}.{}", beandoc.getBeanId(), methodElement); if (methodCommentTree.getFirstSentence() != null) { + sigModel.setDocFirstSentence(this.sanitize(methodCommentTree.getFirstSentence())); if (methodModel.getDocFirstSentence() == null) - methodModel.setDocFirstSentence(methodCommentTree.getFirstSentence().toString()); - sigModel.setDocFirstSentence(methodCommentTree.getFirstSentence().toString()); + methodModel.setDocFirstSentence(sigModel.getDocFirstSentence()); } if (methodCommentTree.getBody() != null) - sigModel.setDocBody(methodCommentTree.getFullBody().toString()); + sigModel.setDocBody(this.sanitize(methodCommentTree.getFullBody())); for (DocTree tag : methodCommentTree.getBlockTags()) { - String tagName = null; - if (tag instanceof BlockTagTree) { - tagName = ((BlockTagTree)tag).getTagName(); - } else if (tag instanceof InlineTagTree) { - tagName = ((InlineTagTree)tag).getTagName(); - } else { - this.logger.error("A tag comment is not of an expected type: {}", tag.getClass()); + Matcher matcher = this.tagPattern.matcher(tag.toString()); + if (!matcher.find()) { + this.logger.warn("A tag for the {} bean and {} method is improperly formatted", beanModel.getBeanId(), methodElement); continue; } + + String tagName = matcher.group(1); + String tagComment = matcher.group(2).trim(); - this.logger.trace("Found tag '{}' for method: {}.{}", tagName, beandoc.getBeanId(), methodElement); + this.logger.info("Found tag '{}' for method: {}.{}", tagName, beandoc.getBeanId(), methodElement); - Matcher matcher; switch (tagName) { case "param": - matcher = this.namedTagPattern.matcher(tag.toString()); + matcher = this.namedCommentPattern.matcher(tagComment); if (matcher.find()) - paramComments.put(matcher.group(1), matcher.group(2).trim()); + paramComments.put(matcher.group(1), this.sanitize(matcher.group(2).trim())); else this.logger.warn("A @param for the {} bean and {} method is improperly formatted", beanModel.getBeanId(), methodElement); + break; case "throws": - matcher = this.namedTagPattern.matcher(tag.toString()); - if (matcher.find()) - throwComments.put(matcher.group(1), matcher.group(2).trim()); + matcher = this.namedCommentPattern.matcher(tagComment); + if (matcher.find()) { + if (this.bpmnErrorSet.contains(matcher.group(1).toLowerCase())) { + Matcher ecmatcher = this.namedCommentPattern.matcher(matcher.group(2).trim()); + if (ecmatcher.find()) { + errorComments.put(ecmatcher.group(1), this.sanitize(ecmatcher.group(2).trim())); + } else { + this.logger.warn("A @throws for the {} bean and {} method is improperly formatted", beanModel.getBeanId(), methodElement); + } + } else { + throwComments.put(matcher.group(1), this.sanitize(matcher.group(2).trim())); + } + } else this.logger.warn("A @throws for the {} bean and {} method is improperly formatted", beanModel.getBeanId(), methodElement); break; default: - matcher = this.tagPattern.matcher(tag.toString()); - if (matcher.find()) - this.putAdd(sigModel.getTags(), tagName, matcher.group(1).trim()); - else this.logger.warn("A '@{}' tag for the {} bean and {} method is improperly formatted", tagName, beanModel.getBeanId(), methodElement); + this.putAdd(sigModel.getTags(), tagName, this.sanitize(tagComment)); } } } @@ -221,21 +246,47 @@ class MarkdownWriter { ParamFreemarkerModel paramModel = new ParamFreemarkerModel(); paramModel.setName(varElement.getSimpleName().toString()); paramModel.setType(varElement.asType().toString()); - paramModel.setComment(paramComments.get(paramModel.getName())); + paramModel.setComment(paramComments.remove(paramModel.getName())); sigModel.getParams().put(paramModel.getName(), paramModel); methodSignature.append(varElement.getSimpleName()).append(": ").append(varElement.asType()).append(", "); } + if (!paramComments.isEmpty()) + this.logger.warn("The {} bean and {} method has documented parameters that don't exist; ignoring ...", beandoc.getBeanId(), methodElement); + for (TypeMirror thrownType : methodElement.getThrownTypes()) { this.logger.trace("Found thrown '{}' for method: {}.{}", thrownType, beandoc.getBeanId(), methodElement); + if ("org.activiti.engine.delegate.BpmnError".equals(thrownType.toString()) && !errorComments.isEmpty()) + continue; + ThrowFreemarkerModel throwModel = new ThrowFreemarkerModel(); throwModel.setType(thrownType.toString()); - throwModel.setComment(throwComments.get(throwModel.getType())); + throwModel.setComment(throwComments.remove(throwModel.getType())); sigModel.getThrowTypes().put(throwModel.getType(), throwModel); } + + for (Entry throwComment : throwComments.entrySet()) { + this.logger.trace("Found documented but not declared thrown '{}' for method: {}.{}", throwComment.getKey(), beandoc.getBeanId(), methodElement); + + ThrowFreemarkerModel throwModel = new ThrowFreemarkerModel(); + throwModel.setType(throwComment.getKey()); + throwModel.setComment(throwComment.getValue()); + + sigModel.getThrowTypes().put(throwModel.getType(), throwModel); + } + + for (Entry errorComment : errorComments.entrySet()) { + this.logger.trace("Found BPMN error thrown code '{}' for method: {}.{}", errorComment.getKey(), beandoc.getBeanId(), methodElement); + + ThrowFreemarkerModel errorModel = new ThrowFreemarkerModel(); + errorModel.setType(errorComment.getKey()); + errorModel.setComment(errorComment.getValue()); + + sigModel.getBpmnErrors().put(errorModel.getType(), errorModel); + } this.logger.trace("Found return '{}' for method: {}.{}", methodElement.getReturnType(), beandoc.getBeanId(), methodElement); if (!"void".equals(methodElement.getReturnType().toString())) @@ -293,6 +344,16 @@ class MarkdownWriter { } } + private String sanitize(List docs) { + return this.sanitize(docs.toString()); + } + + private String sanitize(String str) { + for (Entry sanitize : this.sanitizations.entrySet()) + str = sanitize.getKey().matcher(str).replaceAll(sanitize.getValue()); + return str; + } + private T putAdd(Map> map, String key, T value) { List values = map.get(key); if (values == null) diff --git a/src/main/java/com/inteligr8/activiti/doclet/MethodSignatureFreemarkerModel.java b/src/main/java/com/inteligr8/activiti/doclet/MethodSignatureFreemarkerModel.java index 5a9bc52..7c8ab74 100644 --- a/src/main/java/com/inteligr8/activiti/doclet/MethodSignatureFreemarkerModel.java +++ b/src/main/java/com/inteligr8/activiti/doclet/MethodSignatureFreemarkerModel.java @@ -28,6 +28,8 @@ public class MethodSignatureFreemarkerModel { private Map throwTypes = new LinkedHashMap<>(); + private Map bpmnErrors = new LinkedHashMap<>(); + private String docFirstSentence; private String docBody; @@ -65,6 +67,14 @@ public class MethodSignatureFreemarkerModel { public void setThrowTypes(Map throwTypes) { this.throwTypes = throwTypes; } + + public Map getBpmnErrors() { + return bpmnErrors; + } + + public void setBpmnErrors(Map bpmnErrors) { + this.bpmnErrors = bpmnErrors; + } public String getDocFirstSentence() { return docFirstSentence; diff --git a/src/main/resources/templates/bean.md.ftl b/src/main/resources/templates/bean.md.ftl index 36c2b20..f699851 100644 --- a/src/main/resources/templates/bean.md.ftl +++ b/src/main/resources/templates/bean.md.ftl @@ -1,19 +1,22 @@ # ${title}<#if apiName??>: ${apiName}: `${beanId}` -<#if docBody??>${docBody}<#else>No documentation available. - -<#if (tags.author?? || tags.since?? || tags.see?? || tags.deprecated??)> +<#if (tags.author?? || tags.since?? || tags.deprecated??)> <#if tags.author??><#list tags.author as author> *Author*: ${author} <#if tags.since??>*Since*: ${tags.since[0]} -<#if tags.see??><#list tags.see as see> -*See Also*: ${see} - <#if tags.deprecated??>*Deprecated* -<#if (delegateRoles?size > 0)> + +<#if docBody??>${docBody}<#else>No documentation available. + +<#if tags.see??><#list tags.see as see> +*See Also*: ${see} + + + +<#if (delegateRoles?size > 0)> ## Delegate Uses This bean may be used in the following entities and their fields in Activiti. In these cases, the value of the "Delegate Expression" field should be as shown in the snippet below. @@ -48,38 +51,45 @@ This bean and its methods may be used in any "Expression" field or script in a S <#list method.signatures as sig> **`${beanId}.${sig.methodSignature}`** +<#if (sig.tags.author?? || sig.tags.since?? || sig.tags.deprecated??)> +<#if sig.tags.author??><#list sig.tags.author as author> +*Author*: ${author} + +<#if sig.tags.since??>*Since*: ${sig.tags.since[0]} +<#if sig.tags.deprecated??>*Deprecated* + + <#if sig.docBody??>${sig.docBody}<#else>No documentation available. <#if (sig.params?size > 0)> | Parameter Name | Java Type | Documentation | | ------------------------ | ------------------------------------------------ | ------------------------ | <#list sig.params as paramName, param> -| ${("`" + paramName + "`")?right_pad(24)} | ${("`" + param.type + "`")?right_pad(48)} | <#if param.comment??>${param.comment?right_pad(24)}<#else>No documentation available. | +| ${("`" + paramName + "`")?right_pad(24)} | ${("`" + param.type + "`")?right_pad(48)} | <#if (param.comment?? && param.comment?length > 0)>${param.comment?right_pad(24)}<#else>No documentation available. | <#if (sig.returnType?? || sig.throwTypes?size > 0)> -| Result Type | Java Type | Documentation | +| Result Type | Java Type or BPMN Error Code | Documentation | | ------------------------ | ------------------------------------------------ | ------------------------ | <#if sig.returnType??> -| Return Value | ${("`" + sig.returnType + "`")?right_pad(48)} | <#if sig.tags.return??>${sig.tags.return[0]?right_pad(24)}<#else>No documentation available. | +| Return Value | ${("`" + sig.returnType + "`")?right_pad(48)} | <#if (sig.tags.return?? && sig.tags.return[0]?length > 0)>${sig.tags.return[0]?right_pad(24)}<#else>No documentation available. | <#if (sig.throwTypes?size > 0)> <#list sig.throwTypes as throwType, throw> -| Thrown Exception | ${("`" + throwType + "`")?right_pad(48)} | <#if throw.comment??>${throw.comment?right_pad(24)}<#else>No documentation available. | +| Thrown Exception | ${("`" + throwType + "`")?right_pad(48)} | <#if (throw.comment?? && throw.comment?length > 0)>${throw.comment?right_pad(24)}<#else>No documentation available. | + + +<#if (sig.bpmnErrors?size > 0)> +<#list sig.bpmnErrors as errorCode, bpmnError> +| Thrown BPMN Error | ${("`" + errorCode + "`")?right_pad(48)} | <#if (bpmnError.comment?? && bpmnError.comment?length > 0)>${bpmnError.comment?right_pad(24)}<#else>No documentation available. | -<#if (sig.tags.author?? || sig.tags.since?? || sig.tags.see?? || sig.tags.deprecated??)> -<#if sig.tags.author??><#list sig.tags.author as author> -*Author*: ${author} - -<#if sig.tags.since??>*Since*: ${sig.tags.since[0]} <#if sig.tags.see??><#list sig.tags.see as see> *See Also*: ${see} - -<#if sig.tags.deprecated??>*Deprecated* +