Repository: camel Updated Branches: refs/heads/master 966846055 -> 7eef2d129
CAMEL-7613 Updated camel-api-component-maven-plugin to support parsing Java8 Javadoc HTML Project: http://git-wip-us.apache.org/repos/asf/camel/repo Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/7eef2d12 Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/7eef2d12 Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/7eef2d12 Branch: refs/heads/master Commit: 7eef2d12930c53cbbe97c8528ed6fa6e13a1e1dc Parents: 9668460 Author: Dhiraj Bokde <dhira...@yahoo.com> Authored: Wed Jul 23 12:42:21 2014 -0700 Committer: Dhiraj Bokde <dhira...@yahoo.com> Committed: Wed Jul 23 12:42:38 2014 -0700 ---------------------------------------------------------------------- .../maven/JavadocApiMethodGeneratorMojo.java | 161 +-------------- .../org/apache/camel/maven/JavadocParser.java | 197 +++++++++++++++++++ .../apache/camel/maven/JavadocParserTest.java | 55 ++++++ 3 files changed, 253 insertions(+), 160 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/camel/blob/7eef2d12/tooling/maven/camel-api-component-maven-plugin/src/main/java/org/apache/camel/maven/JavadocApiMethodGeneratorMojo.java ---------------------------------------------------------------------- diff --git a/tooling/maven/camel-api-component-maven-plugin/src/main/java/org/apache/camel/maven/JavadocApiMethodGeneratorMojo.java b/tooling/maven/camel-api-component-maven-plugin/src/main/java/org/apache/camel/maven/JavadocApiMethodGeneratorMojo.java index 572fbf9..dd76a02 100644 --- a/tooling/maven/camel-api-component-maven-plugin/src/main/java/org/apache/camel/maven/JavadocApiMethodGeneratorMojo.java +++ b/tooling/maven/camel-api-component-maven-plugin/src/main/java/org/apache/camel/maven/JavadocApiMethodGeneratorMojo.java @@ -19,25 +19,17 @@ package org.apache.camel.maven; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; -import java.io.UnsupportedEncodingException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import java.net.URLDecoder; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; -import javax.swing.text.ChangedCharSetException; -import javax.swing.text.SimpleAttributeSet; -import javax.swing.text.html.HTML; import javax.swing.text.html.parser.DTD; -import javax.swing.text.html.parser.Parser; -import javax.swing.text.html.parser.TagElement; import org.apache.camel.util.component.ApiMethodParser; -import org.apache.commons.lang.StringEscapeUtils; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugins.annotations.LifecyclePhase; import org.apache.maven.plugins.annotations.Mojo; @@ -58,7 +50,6 @@ public class JavadocApiMethodGeneratorMojo extends AbstractApiMethodGeneratorMoj } protected static final String DEFAULT_EXCLUDE_PACKAGES = "javax?\\.lang.*"; - private static final Pattern ARGTYPES_PATTERN = Pattern.compile("\\s*([^<\\s,]+\\s*(<[^>]+>)?)\\s*,?"); private static final Pattern RAW_ARGTYPES_PATTERN = Pattern.compile("\\s*([^<\\s,]+)\\s*(<[^>]+>)?\\s*,?"); @Parameter(property = PREFIX + "excludePackages", defaultValue = DEFAULT_EXCLUDE_PACKAGES) @@ -139,9 +130,7 @@ public class JavadocApiMethodGeneratorMojo extends AbstractApiMethodGeneratorMoj } final String resultType = getResultType(aClass, name, types); if (resultType != null) { - final StringBuilder signature = new StringBuilder(resultType); - signature.append(" ").append(name).append(methodMap.get(method)); - result.put(method, signature.toString()); + result.put(method, resultType + " " + name + methodMap.get(method)); } } } @@ -194,152 +183,4 @@ public class JavadocApiMethodGeneratorMojo extends AbstractApiMethodGeneratorMoj return result; } - private static class JavadocParser extends Parser { - private static final String NON_BREAKING_SPACE = "\u00A0"; - private String hrefPattern; - - private ParserState parserState; - private String methodWithTypes; - private StringBuilder methodTextBuilder = new StringBuilder(); - - private List<String> methods = new ArrayList<String>(); - private Map<String, String> methodText = new HashMap<String, String>(); - private String errorMessage; - - public JavadocParser(DTD dtd, String docPath) { - super(dtd); - this.hrefPattern = docPath + "#"; - } - - @Override - protected void startTag(TagElement tag) throws ChangedCharSetException { - super.startTag(tag); - - final HTML.Tag htmlTag = tag.getHTMLTag(); - if (htmlTag != null) { - if (HTML.Tag.A.equals(htmlTag)) { - final SimpleAttributeSet attributes = getAttributes(); - final Object name = attributes.getAttribute(HTML.Attribute.NAME); - if (name != null) { - final String nameAttr = (String) name; - if (parserState == null && "method_summary".equals(nameAttr)) { - parserState = ParserState.METHOD_SUMMARY; - } else if (parserState == ParserState.METHOD_SUMMARY && nameAttr.startsWith("methods_inherited_from_class_")) { - parserState = null; - } else if (parserState == ParserState.METHOD && methodWithTypes == null) { - final Object href = attributes.getAttribute(HTML.Attribute.HREF); - if (href != null) { - String hrefAttr = (String) href; - if (hrefAttr.contains(hrefPattern)) { - // unescape HTML - methodWithTypes = unescapeHtml(hrefAttr.substring(hrefAttr.indexOf('#') + 1)); - } - } - } - } - } else if (parserState == ParserState.METHOD_SUMMARY && HTML.Tag.CODE.equals(htmlTag)) { - parserState = ParserState.METHOD; - } - } - } - - private static String unescapeHtml(String htmlString) { - String result = StringEscapeUtils.unescapeHtml(htmlString).replaceAll(NON_BREAKING_SPACE, " "); - try { - result = URLDecoder.decode(result, "UTF-8"); - } catch (UnsupportedEncodingException ignored) { - - } - return result; - } - - @Override - protected void handleEmptyTag(TagElement tag) { - if (parserState == ParserState.METHOD && HTML.Tag.CODE.equals(tag.getHTMLTag())) { - if (methodWithTypes != null) { - // process collected method data - methods.add(methodWithTypes); - this.methodText.put(methodWithTypes, getArgSignature()); - - // clear the text builder for next method - methodTextBuilder.delete(0, methodTextBuilder.length()); - methodWithTypes = null; - } - - parserState = ParserState.METHOD_SUMMARY; - } - } - - private String getArgSignature() { - final String typeString = methodWithTypes.substring(methodWithTypes.indexOf('(') + 1, methodWithTypes.indexOf(')')); - if (typeString.isEmpty()) { - return "()"; - } - - // split types list - final List<String> typeList = new ArrayList<String>(); - final Matcher typeMatcher = ARGTYPES_PATTERN.matcher(typeString); - while (typeMatcher.find()) { - typeList.add(typeMatcher.group(1).replaceAll(" ", "")); - } - - // unescape HTML method text - final String plainText = unescapeHtml(methodTextBuilder.toString()); - final String argsString = plainText.substring(plainText.indexOf('(') + 1, plainText.indexOf(')')); - final Matcher argMatcher = ApiMethodParser.ARGS_PATTERN.matcher(argsString); - final List<String> argNames = new ArrayList<String>(); - while (argMatcher.find()) { - argNames.add(argMatcher.group(3)); - } - - // make sure number of types and names match - final int nTypes = typeList.size(); - if (nTypes != argNames.size()) { - throw new IllegalArgumentException("Unexpected Javadoc error, different number of arg types and names"); - } - - final String[] names = argNames.toArray(new String[nTypes]); - final StringBuilder builder = new StringBuilder("("); - int i = 0; - for (String type : typeList) { - // split on space or non-breaking space - builder.append(type).append(' ').append(names[i++]); - if (i < nTypes) { - builder.append(','); - } - } - builder.append(')'); - return builder.toString(); - } - - @Override - protected void handleText(char[] text) { - if (parserState == ParserState.METHOD && methodWithTypes != null) { - methodTextBuilder.append(text); - } - } - - @Override - protected void handleError(int ln, String msg) { - if (msg.startsWith("exception ")) { - this.errorMessage = "Exception parsing Javadoc line " + ln + ": " + msg; - } - } - - private String getErrorMessage() { - return errorMessage; - } - - private List<String> getMethods() { - return methods; - } - - private Map<String, String> getMethodText() { - return methodText; - } - } - - private static enum ParserState { - METHOD_SUMMARY, METHOD; - } } http://git-wip-us.apache.org/repos/asf/camel/blob/7eef2d12/tooling/maven/camel-api-component-maven-plugin/src/main/java/org/apache/camel/maven/JavadocParser.java ---------------------------------------------------------------------- diff --git a/tooling/maven/camel-api-component-maven-plugin/src/main/java/org/apache/camel/maven/JavadocParser.java b/tooling/maven/camel-api-component-maven-plugin/src/main/java/org/apache/camel/maven/JavadocParser.java new file mode 100644 index 0000000..24e4489 --- /dev/null +++ b/tooling/maven/camel-api-component-maven-plugin/src/main/java/org/apache/camel/maven/JavadocParser.java @@ -0,0 +1,197 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.camel.maven; + +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.swing.text.ChangedCharSetException; +import javax.swing.text.SimpleAttributeSet; +import javax.swing.text.html.HTML; +import javax.swing.text.html.parser.DTD; +import javax.swing.text.html.parser.Parser; +import javax.swing.text.html.parser.TagElement; + +import org.apache.commons.lang.StringEscapeUtils; + +/** + * Parses Javadoc HTML to get Method Signatures from Method Sumary. Supports Java 6, 7 and 8 Javadoc formats. + */ +public class JavadocParser extends Parser { + + private static final String NON_BREAKING_SPACE = "\u00A0"; + + private final String hrefPattern; + + private ParserState parserState; + private String methodWithTypes; + private StringBuilder methodTextBuilder = new StringBuilder(); + + private List<String> methods = new ArrayList<String>(); + private Map<String, String> methodText = new HashMap<String, String>(); + private String errorMessage; + + public JavadocParser(DTD dtd, String docPath) { + super(dtd); + this.hrefPattern = docPath + "#"; + parserState = ParserState.INIT; + } + + public void reset() { + parserState = ParserState.INIT; + + methodWithTypes = null; + methodTextBuilder = new StringBuilder(); + + methods.clear(); + methodText.clear(); + + errorMessage = null; + } + + @Override + protected void startTag(TagElement tag) throws ChangedCharSetException { + super.startTag(tag); + + final HTML.Tag htmlTag = tag.getHTMLTag(); + if (htmlTag != null) { + if (HTML.Tag.A.equals(htmlTag)) { + final SimpleAttributeSet attributes = getAttributes(); + final Object name = attributes.getAttribute(HTML.Attribute.NAME); + if (name != null) { + final String nameAttr = (String) name; + if (parserState == ParserState.INIT + && ("method_summary".equals(nameAttr) || "method.summary".equals(nameAttr))) { + parserState = ParserState.METHOD_SUMMARY; + } else if (parserState == ParserState.METHOD) { + if (methodWithTypes == null) { + + final String hrefAttr = (String) attributes.getAttribute(HTML.Attribute.HREF); + if (hrefAttr != null && hrefAttr.contains(hrefPattern)) { + + // unescape HTML + String methodSignature = hrefAttr.substring(hrefAttr.indexOf('#') + 1); + final int firstHyphen = methodSignature.indexOf('-'); + if (firstHyphen != -1) { + final int lastHyphen = methodSignature.lastIndexOf('-'); + methodSignature = methodSignature.substring(0, firstHyphen) + "(" + + methodSignature.substring(firstHyphen + 1, lastHyphen) + ")"; + methodSignature = methodSignature.replaceAll("-", ","); + } + // support varargs + if (methodSignature.contains("...)")) { + methodSignature = methodSignature.replaceAll("\\.\\.\\.\\)", "[])"); + } + // map Java8 array types + if (methodSignature.contains(":A")) { + methodSignature = methodSignature.replaceAll(":A", "[]"); + } + methodWithTypes = unescapeHtml(methodSignature); + } + } else { + final String title = (String) attributes.getAttribute(HTML.Attribute.TITLE); + if (title != null) { + // append package name to type name text + methodTextBuilder.append(title.substring(title.lastIndexOf(' '))).append('.'); + } + } + } + } + } else if (parserState == ParserState.METHOD_SUMMARY && HTML.Tag.CODE.equals(htmlTag)) { + parserState = ParserState.METHOD; + } + } + } + + private static String unescapeHtml(String htmlString) { + String result = StringEscapeUtils.unescapeHtml(htmlString).replaceAll(NON_BREAKING_SPACE, " "); + try { + result = URLDecoder.decode(result, "UTF-8"); + } catch (UnsupportedEncodingException ignored) { + + } + return result; + } + + @Override + protected void handleEmptyTag(TagElement tag) { + if (parserState == ParserState.METHOD && HTML.Tag.CODE.equals(tag.getHTMLTag())) { + if (methodWithTypes != null) { + // process collected method data + methods.add(methodWithTypes); + this.methodText.put(methodWithTypes, getArgSignature()); + + // clear the text builder for next method + methodTextBuilder.delete(0, methodTextBuilder.length()); + methodWithTypes = null; + } + + parserState = ParserState.METHOD_SUMMARY; + } else if (parserState == ParserState.METHOD_SUMMARY && HTML.Tag.TABLE.equals(tag.getHTMLTag())) { + // end of method summary table + parserState = ParserState.INIT; + } + } + + private String getArgSignature() { + final String typeString = methodWithTypes.substring(methodWithTypes.indexOf('(') + 1, methodWithTypes.indexOf(')')); + if (typeString.isEmpty()) { + return "()"; + } + + // unescape HTML method text + String plainText = unescapeHtml(methodTextBuilder.toString()); + // support varargs + if (plainText.contains("...")) { + plainText = plainText.replaceAll("\\.\\.\\.", "[]"); + } + return plainText.substring(plainText.indexOf('('), plainText.indexOf(')') + 1); + } + + @Override + protected void handleText(char[] text) { + if (parserState == ParserState.METHOD && methodWithTypes != null) { + methodTextBuilder.append(text); + } + } + + @Override + protected void handleError(int ln, String msg) { + if (msg.startsWith("exception ")) { + this.errorMessage = "Exception parsing Javadoc line " + ln + ": " + msg; + } + } + + public String getErrorMessage() { + return errorMessage; + } + + public List<String> getMethods() { + return methods; + } + + public Map<String, String> getMethodText() { + return methodText; + } + + private static enum ParserState { + INIT, METHOD_SUMMARY, METHOD + } +} http://git-wip-us.apache.org/repos/asf/camel/blob/7eef2d12/tooling/maven/camel-api-component-maven-plugin/src/test/java/org/apache/camel/maven/JavadocParserTest.java ---------------------------------------------------------------------- diff --git a/tooling/maven/camel-api-component-maven-plugin/src/test/java/org/apache/camel/maven/JavadocParserTest.java b/tooling/maven/camel-api-component-maven-plugin/src/test/java/org/apache/camel/maven/JavadocParserTest.java new file mode 100644 index 0000000..c98812d --- /dev/null +++ b/tooling/maven/camel-api-component-maven-plugin/src/test/java/org/apache/camel/maven/JavadocParserTest.java @@ -0,0 +1,55 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.camel.maven; + +import java.io.InputStreamReader; +import java.net.URL; +import javax.swing.text.html.parser.DTD; + +import org.junit.Assert; +import org.junit.Test; + +/** + * Test JavadocParser using {@link java.lang.String} javadoc for Java 6, 7 and 8. + */ +public class JavadocParserTest extends Assert { + + private static final String JAVA6_STRING = "http://docs.oracle.com/javase/6/docs/api/java/lang/String.html"; + private static final String JAVA7_STRING = "http://docs.oracle.com/javase/7/docs/api/java/lang/String.html"; + private static final String JAVA8_STRING = "http://docs.oracle.com/javase/8/docs/api/java/lang/String.html"; + + @Test + public void testGetMethods() throws Exception { + final DTD dtd = DTD.getDTD("html.dtd"); + final String javaDocPath = String.class.getName().replaceAll("\\.", "/") + ".html"; + final JavadocParser htmlParser = new JavadocParser(dtd, javaDocPath); + + htmlParser.parse(new InputStreamReader(new URL(JAVA6_STRING).openStream(), "UTF-8")); + assertNull("Java6 getErrorMessage", htmlParser.getErrorMessage()); + assertFalse("Java6 getMethods", htmlParser.getMethods().isEmpty()); + htmlParser.reset(); + + htmlParser.parse(new InputStreamReader(new URL(JAVA7_STRING).openStream(), "UTF-8")); + assertNull("Java7 getErrorMessage", htmlParser.getErrorMessage()); + assertFalse("Java7 getMethods", htmlParser.getMethods().isEmpty()); + htmlParser.reset(); + + htmlParser.parse(new InputStreamReader(new URL(JAVA8_STRING).openStream(), "UTF-8")); + assertNull("Java8 getErrorMessage", htmlParser.getErrorMessage()); + assertFalse("Java8 getMethods", htmlParser.getMethods().isEmpty()); + } +}