Commit 20c8adbf authored by Brian Long's avatar Brian Long
Browse files

@throws expansion; general fixes

parent cf5a753d
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -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;
+5 −2
Original line number Diff line number Diff line
@@ -54,9 +54,12 @@ class ActivitiApiBeanDoc {
		return this;
	}
	
	public List<ExecutableElement> getMethodElements() {
		return new ArrayList<>(this.methodElements.values());
	}
	
	public List<ExecutableElement> getSortedMethodElements() {
		List<ExecutableElement> methodElements = new ArrayList<>(this.methodElements.size());
		methodElements.addAll(this.methodElements.values());
		List<ExecutableElement> methodElements = this.getMethodElements();
		Collections.sort(methodElements, new Comparator<ExecutableElement>() {
			@Override
			public int compare(ExecutableElement methodElement1, ExecutableElement methodElement2) {
+0 −1
Original line number Diff line number Diff line
@@ -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()) {
+95 −0
Original line number Diff line number Diff line
package com.inteligr8.activiti.doclet;

import java.util.Collection;
import java.util.Map;
import java.util.Set;

public class FluentMap<K, V> implements Map<K, V> {
	
	private Map<K, V> map;
	
	public FluentMap(Map<K, V> 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<Entry<K, V>> 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<K> keySet() {
		return this.map.keySet();
	}
	
	@Override
	public V put(K key, V value) {
		return this.map.put(key, value);
	}
	
	@Override
	public void putAll(Map<? extends K, ? extends V> 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<V> values() {
		return this.map.values();
	}
	
	public FluentMap<K, V> with(K key, V value) {
		this.map.put(key, value);
		return this;
	}
	
	public FluentMap<K, V> without(K key) {
		this.map.remove(key);
		return this;
	}

}
+89 −28
Original line number Diff line number Diff line
@@ -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<String> 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<Pattern, String> sanitizations = new FluentMap<>(new LinkedHashMap<Pattern, String>())
			.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<String, String> paramComments = new HashMap<>();
				Map<String, String> throwComments = new HashMap<>();
				Map<String, String> throwComments = new LinkedHashMap<>();
				Map<String, String> 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;
						}
						
						this.logger.trace("Found tag '{}' for method: {}.{}", tagName, beandoc.getBeanId(), methodElement);
						String tagName = matcher.group(1);
						String tagComment = matcher.group(2).trim();

						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,22 +246,48 @@ 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<String, String> 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<String, String> 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()))
					sigModel.setReturnType(methodElement.getReturnType().toString());
@@ -293,6 +344,16 @@ class MarkdownWriter {
		}
	}
	
	private String sanitize(List<? extends DocTree> docs) {
		return this.sanitize(docs.toString());
	}
	
	private String sanitize(String str) {
		for (Entry<Pattern, String> sanitize : this.sanitizations.entrySet())
			str = sanitize.getKey().matcher(str).replaceAll(sanitize.getValue());
		return str;
	}
	
	private <T> T putAdd(Map<String, List<T>> map, String key, T value) {
		List<T> values = map.get(key);
		if (values == null)
Loading