/* * Copyright 2016 Juan Manuel Fernandez * * Licensed 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 com.github.juanmf.java2plant.goal; import java.io.File; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import com.github.juanmf.java2plant.render.filters.ChainFilter; import com.github.juanmf.java2plant.render.filters.Filter; import org.apache.maven.artifact.DependencyResolutionRequiredException; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugins.annotations.Component; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.plugins.annotations.ResolutionScope; import org.apache.maven.project.MavenProject; import com.github.juanmf.java2plant.Parser; import static com.github.juanmf.java2plant.render.filters.Filters.CHAIN_CLASSES_CUSTOM_NAME; import static com.github.juanmf.java2plant.render.filters.Filters.CHAIN_RELATIONS_CUSTOM_NAME; import static com.github.juanmf.java2plant.render.filters.Filters.CHAIN_RELATION_TYPE_CUSTOM_NAME; import static com.github.juanmf.java2plant.render.filters.Filters.FILTERS; /** * Usage: * <pre> * mvn -Dparse.thePackage="com.my.package" clean compile java2PlantUML:parse * mvn -Dparse.thePackage="com.my.package, com.other.package" clean compile java2PlantUML:parse * mvn -Dparse.thePackage="com.my.package, com.other.Class" clean compile java2PlantUML:parse * </pre> * * Usage of Filters: * Default Filters are: * <pre> * "parse.relationTypeFilter" = "FILTER_CHAIN_RELATION_TYPE_STANDARD" * "parse.classesFilter" = "FILTER_CHAIN_CLASSES_STANDARD" * "parse.relationsFilter" = "FILTER_CHAIN_RELATION_STANDARD" * </pre> * To change them use: * <pre> * mvn -Dparse.thePackage="p1" -Dparse.classesFilter="FILTER_FORBID_ANONIMOUS" clean compile java2PlantUML:parse * mvn -Dparse.thePackage="p1" -Dparse.relationTypeFilter="FILTER_FORBID_USES" clean compile java2PlantUML:parse * mvn -Dparse.thePackage="p1" -Dparse.relationsFilter="FILTER_RELATION_FORBID_TO_PRIMITIVE" clean compile java2PlantUML:parse * </pre> * * Available filter names and types are defined in {@link com.github.juanmf.java2plant.render.filters.Filters#FILTERS} Map * Read {@link com.github.juanmf.java2plant.render.filters.Filters} javadoc to understand the three types of filters * you can use. * * Custom filters usage: * You can use a custom chain filter for each of the three types of filters, combining any of the existing filters * without the need to code. * <pre> * mvn -Dparse.thePackage="p1" \ * -Dparse.classesFilter="FILTER_CHAIN_CLASSES_CUSTOM" \ * -Dparse.customClassesFilter="FILTER_FORBID_ANONIMOUS,FILTER_FORBID_PRIMITIVES" \ * clean compile java2PlantUML:parse * * mvn -Dparse.thePackage="p1" \ * -Dparse.relationTypeFilter="FILTER_CHAIN_RELATION_TYPE_CUSTOM" \ * -Dparse.customRelationTypeFilters="FILTER_FORBID_USES,FILTER_FORBID_AGGREGATION" \ * clean compile java2PlantUML:parse * * mvn -Dparse.thePackage="p1" \ * -Dparse.relationsFilter="FILTER_CHAIN_RELATION_TYPE_CUSTOM" \ * -Dparse.customRelationsFilter="FILTER_RELATION_FORBID_TO_PRIMITIVE,FILTER_RELATION_FORBID_FROM_ANONIMOUS" \ * clean compile java2PlantUML:parse * </pre> * * Of course you can use all three custom chain filters un a single run. * * @author [email protected] */ @Mojo(name = "parse", requiresDependencyResolution = ResolutionScope.COMPILE) public class Parse extends AbstractMojo { /** * The package to parse for Types and Associations */ @Parameter(property = "parse.thePackage", defaultValue = "com.github.juanmf.java2plant.structure") private String thePackage; /** * The package to parse for Types and Associations */ @Parameter(property = "parse.relationTypeFilter", defaultValue = "FILTER_CHAIN_RELATION_TYPE_STANDARD") private String relationTypeFilter; @Parameter(property = "parse.classesFilter", defaultValue = "FILTER_CHAIN_CLASSES_STANDARD") private String classesFilter; @Parameter(property = "parse.relationsFilter", defaultValue = "FILTER_CHAIN_RELATION_STANDARD") private String relationsFilter; /** * The package to parse for Types and Associations */ @Parameter(property = "parse.customRelationTypeFilter") private String customRelationTypeFilter; @Parameter(property = "parse.customClassesFilter") private String customClassesFilter; @Parameter(property = "parse.customRelationsFilter") private String customRelationsFilter; /** * If given one or more single Class names, and this is set, the hierarchy will be traversed downstream. */ @Parameter(property = "parse.scanChildren") private Boolean scanChildren; @Component private MavenProject project; /** * Send thePackage and a convenient fixed set of filters to Parser and outputs Parser's result * * Todo: allow filters to be set from CLI * * <code> * // A very permisive filter configuration: * getLog().info("Following is the PlantUML src: \n" + Parser.parse( * thePackage, Filters.FILTER_ALLOW_ALL_RELATIONS, Filters.FILTER_ALLOW_ALL_CLASSES, loader, * Filters.FILTER_RELATION_ALLOW_ALL)); * </code> * @throws MojoExecutionException * @throws MojoFailureException */ @SuppressWarnings("unchecked") public void execute() throws MojoExecutionException, MojoFailureException { filterParamsSanityCheck(); prepareCustomFilters(); try { URLClassLoader loader = getLoader(); getLog().debug("loader URLs: " + Arrays.toString(loader.getURLs())); getLog().info("Following is the PlantUML src: \n" + Parser.parse( thePackage, FILTERS.get(this.relationTypeFilter), FILTERS.get(this.classesFilter), FILTERS.get(this.relationsFilter), loader)); } catch (DependencyResolutionRequiredException e) { throw new MojoExecutionException("Something went wrong", e); } } private void prepareCustomFilters() throws MojoExecutionException { if (CHAIN_RELATION_TYPE_CUSTOM_NAME.equals(relationTypeFilter)) { if (null == customRelationTypeFilter) { throw new MojoExecutionException("Need to set parse.customRelationTypeFilter!"); } prepareRelationTypeFilter(); } if (CHAIN_RELATIONS_CUSTOM_NAME.equals(relationsFilter)) { if (null == customRelationsFilter) { throw new MojoExecutionException("Need to set parse.customRelationsFilter!"); } prepareRelationsFilter(); } if (CHAIN_CLASSES_CUSTOM_NAME.equals(classesFilter)) { if (null == customClassesFilter) { throw new MojoExecutionException("Need to set parse.customClassesFilter!"); } prepareClassesFilter(); } } private void prepareClassesFilter() throws MojoExecutionException { ChainFilter classesFilter = (ChainFilter) FILTERS.get(CHAIN_CLASSES_CUSTOM_NAME); loadFilters(classesFilter, customClassesFilter); } private void prepareRelationsFilter() throws MojoExecutionException { ChainFilter relationFilter = (ChainFilter) FILTERS.get(CHAIN_RELATIONS_CUSTOM_NAME); loadFilters(relationFilter, customRelationsFilter); } private void prepareRelationTypeFilter() throws MojoExecutionException { ChainFilter relTypeFilter = (ChainFilter) FILTERS.get(CHAIN_RELATION_TYPE_CUSTOM_NAME); loadFilters(relTypeFilter, customRelationTypeFilter); } private void loadFilters(ChainFilter filter, String FilterNames) throws MojoExecutionException { for (String filterName : customClassesFilter.split("\\s*,\\s*")) { filterExists(filterName); // Could explode if wrong filterType filter.addFilter(FILTERS.get(filterName)); } } private void filterParamsSanityCheck() throws MojoExecutionException { filterExists(this.classesFilter); filterExists(this.relationsFilter); filterExists(this.relationTypeFilter); } void filterExists(String filterName) throws MojoExecutionException { String message = "Non existent Filter name: %s. Available Filter names: " + FILTERS.toString(); if (! FILTERS.containsKey(filterName)) { throw new MojoExecutionException(String.format(message, filterName)); } } /** * Fetches all project's class path elements and creates a URLClassLoader to be used by * Reflections. * * @return All class path's URLs in a ClassLoader * @throws DependencyResolutionRequiredException * @throws MojoExecutionException */ private URLClassLoader getLoader() throws DependencyResolutionRequiredException, MojoExecutionException { List<String> classpathElements = null; classpathElements = project.getCompileClasspathElements(); List<URL> projectClasspathList = new ArrayList<>(); for (String element : classpathElements) { try { projectClasspathList.add(new File(element).toURI().toURL()); } catch (MalformedURLException e) { throw new MojoExecutionException(element + " is an invalid classpath element", e); } } URLClassLoader loader = new URLClassLoader(projectClasspathList.toArray(new URL[0])); return loader; } }