| /* |
| * 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.IOException; |
| import java.io.InputStream; |
| import java.io.InputStreamReader; |
| import java.lang.reflect.Method; |
| import java.lang.reflect.Modifier; |
| 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.html.parser.DTD; |
| |
| import org.apache.camel.support.component.ApiMethodParser; |
| import org.apache.maven.plugin.MojoExecutionException; |
| import org.apache.maven.plugins.annotations.LifecyclePhase; |
| import org.apache.maven.plugins.annotations.Mojo; |
| import org.apache.maven.plugins.annotations.Parameter; |
| import org.apache.maven.plugins.annotations.ResolutionScope; |
| |
| /** |
| * Parses ApiMethod signatures from Javadoc. |
| */ |
| @Mojo(name = "fromJavadoc", requiresDependencyResolution = ResolutionScope.TEST, requiresProject = true, |
| defaultPhase = LifecyclePhase.GENERATE_SOURCES, threadSafe = true) |
| public class JavadocApiMethodGeneratorMojo extends AbstractApiMethodGeneratorMojo { |
| |
| static { |
| // set Java AWT to headless before using Swing HTML parser |
| System.setProperty("java.awt.headless", "true"); |
| } |
| |
| protected static final String DEFAULT_EXCLUDE_PACKAGES = "javax?\\.lang.*"; |
| private static final Pattern RAW_ARGTYPES_PATTERN = Pattern.compile("\\s*([^<\\s,]+)\\s*(<[^>]+>)?\\s*,?"); |
| |
| @Parameter(property = PREFIX + "excludePackages", defaultValue = DEFAULT_EXCLUDE_PACKAGES) |
| protected String excludePackages; |
| |
| @Parameter(property = PREFIX + "excludeClasses") |
| protected String excludeClasses; |
| |
| @Parameter(property = PREFIX + "includeMethods") |
| protected String includeMethods; |
| |
| @Parameter(property = PREFIX + "excludeMethods") |
| protected String excludeMethods; |
| |
| @Parameter(property = PREFIX + "includeStaticMethods") |
| protected Boolean includeStaticMethods; |
| |
| @Override |
| public List<String> getSignatureList() throws MojoExecutionException { |
| // signatures as map from signature with no arg names to arg names from JavadocParser |
| Map<String, String> result = new HashMap<>(); |
| |
| final Pattern packagePatterns = Pattern.compile(excludePackages); |
| final Pattern classPatterns = (excludeClasses != null) ? Pattern.compile(excludeClasses) : null; |
| final Pattern includeMethodPatterns = (includeMethods != null) ? Pattern.compile(includeMethods) : null; |
| final Pattern excludeMethodPatterns = (excludeMethods != null) ? Pattern.compile(excludeMethods) : null; |
| |
| // for proxy class and super classes not matching excluded packages or classes |
| for (Class<?> aClass = getProxyType(); |
| aClass != null && !packagePatterns.matcher(aClass.getPackage().getName()).matches() |
| && (classPatterns == null || !classPatterns.matcher(aClass.getSimpleName()).matches()); |
| aClass = aClass.getSuperclass()) { |
| |
| log.debug("Processing " + aClass.getName()); |
| final String javaDocPath = aClass.getName().replaceAll("\\.", "/").replace('$', '.') + ".html"; |
| |
| // read javadoc html text for class |
| |
| try (InputStream inputStream = getProjectClassLoader().getResourceAsStream(javaDocPath)) { |
| if (inputStream == null) { |
| log.debug("JavaDoc not found on classpath for " + aClass.getName()); |
| break; |
| } |
| // transform the HTML to get method summary as text |
| // dummy DTD |
| final DTD dtd = DTD.getDTD("html.dtd"); |
| final JavadocParser htmlParser = new JavadocParser(dtd, javaDocPath); |
| htmlParser.parse(new InputStreamReader(inputStream, "UTF-8")); |
| |
| // look for parse errors |
| final String parseError = htmlParser.getErrorMessage(); |
| if (parseError != null) { |
| throw new MojoExecutionException(parseError); |
| } |
| |
| // get public method signature |
| final Map<String, String> methodMap = htmlParser.getMethodText(); |
| for (String method : htmlParser.getMethods()) { |
| if (!result.containsKey(method) |
| && (includeMethodPatterns == null || includeMethodPatterns.matcher(method).find()) |
| && (excludeMethodPatterns == null || !excludeMethodPatterns.matcher(method).find())) { |
| |
| final int leftBracket = method.indexOf('('); |
| final String name = method.substring(0, leftBracket); |
| final String args = method.substring(leftBracket + 1, method.length() - 1); |
| String[] types; |
| if (args.isEmpty()) { |
| types = new String[0]; |
| } else { |
| // get raw types from args |
| final List<String> rawTypes = new ArrayList<>(); |
| final Matcher argTypesMatcher = RAW_ARGTYPES_PATTERN.matcher(args); |
| while (argTypesMatcher.find()) { |
| rawTypes.add(argTypesMatcher.group(1)); |
| } |
| types = rawTypes.toArray(new String[rawTypes.size()]); |
| } |
| final String resultType = getResultType(aClass, name, types); |
| if (resultType != null) { |
| result.put(method, resultType + " " + name + methodMap.get(method)); |
| } |
| } |
| } |
| } catch (IOException e) { |
| throw new MojoExecutionException(e.getMessage(), e); |
| } |
| } |
| |
| if (result.isEmpty()) { |
| throw new MojoExecutionException("No public non-static methods found, " |
| + "make sure Javadoc is available as project test dependency"); |
| } |
| return new ArrayList<>(result.values()); |
| } |
| |
| private String getResultType(Class<?> aClass, String name, String[] types) throws MojoExecutionException { |
| Class<?>[] argTypes = new Class<?>[types.length]; |
| final ClassLoader classLoader = getProjectClassLoader(); |
| for (int i = 0; i < types.length; i++) { |
| try { |
| try { |
| argTypes[i] = ApiMethodParser.forName(types[i], classLoader); |
| } catch (ClassNotFoundException e) { |
| throw new MojoExecutionException(e.getMessage(), e); |
| } |
| } catch (IllegalArgumentException e) { |
| throw new MojoExecutionException(e.getCause().getMessage(), e.getCause()); |
| } |
| } |
| |
| // return null for non-public methods, and for non-static methods if includeStaticMethods is null or false |
| String result = null; |
| try { |
| final Method method = aClass.getMethod(name, argTypes); |
| int modifiers = method.getModifiers(); |
| if (!Modifier.isStatic(modifiers) || Boolean.TRUE.equals(includeStaticMethods)) { |
| result = method.getReturnType().getName(); |
| } |
| } catch (NoSuchMethodException e) { |
| // could be a non-public method |
| try { |
| aClass.getDeclaredMethod(name, argTypes); |
| } catch (NoSuchMethodException e1) { |
| throw new MojoExecutionException(e1.getMessage(), e1); |
| } |
| } |
| |
| return result; |
| } |
| |
| } |