package org.jboss.windup.rules.apps.java.scan.provider; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import org.jboss.forge.furnace.util.Strings; import org.jboss.forge.roaster.ParserException; import org.jboss.forge.roaster.Roaster; import org.jboss.forge.roaster.model.Extendable; import org.jboss.forge.roaster.model.InterfaceCapable; import org.jboss.forge.roaster.model.source.JavaSource; import org.jboss.windup.config.AbstractRuleProvider; import org.jboss.windup.config.GraphRewrite; import org.jboss.windup.config.loader.RuleLoaderContext; import org.jboss.windup.config.metadata.RuleMetadata; import org.jboss.windup.config.operation.Commit; import org.jboss.windup.config.operation.IterationProgress; import org.jboss.windup.config.operation.iteration.AbstractIterationOperation; import org.jboss.windup.config.phase.ClassifyFileTypesPhase; import org.jboss.windup.config.query.Query; import org.jboss.windup.graph.GraphContext; import org.jboss.windup.graph.model.WindupConfigurationModel; import org.jboss.windup.graph.model.resource.FileModel; import org.jboss.windup.graph.service.FileService; import org.jboss.windup.graph.service.GraphService; import org.jboss.windup.reporting.model.TechnologyTagLevel; import org.jboss.windup.reporting.service.ClassificationService; import org.jboss.windup.reporting.service.TechnologyTagService; import org.jboss.windup.rules.apps.java.condition.SourceMode; import org.jboss.windup.rules.apps.java.model.JavaClassModel; import org.jboss.windup.rules.apps.java.model.JavaSourceFileModel; import org.jboss.windup.rules.apps.java.scan.ast.WindupWildcardImportResolver; import static org.jboss.windup.rules.apps.java.scan.provider.AnalyzeJavaFilesRuleProvider.UNPARSEABLE_JAVA_CLASSIFICATION; import static org.jboss.windup.rules.apps.java.scan.provider.AnalyzeJavaFilesRuleProvider.UNPARSEABLE_JAVA_DESCRIPTION; import org.jboss.windup.rules.apps.java.service.JavaClassService; import org.jboss.windup.util.Logging; import org.jboss.windup.util.PathUtil; import org.jboss.windup.util.exception.WindupException; import org.ocpsoft.rewrite.config.Configuration; import org.ocpsoft.rewrite.config.ConfigurationBuilder; import org.ocpsoft.rewrite.context.EvaluationContext; /** * Discovers .java files from the applications being analyzed. * * @author <a href="mailto:jesse.sightler@gmail.com">Jesse Sightler</a> */ @RuleMetadata(phase = ClassifyFileTypesPhase.class) public class IndexJavaSourceFilesRuleProvider extends AbstractRuleProvider { private static final Logger LOG = Logging.get(IndexJavaSourceFilesRuleProvider.class); public static final String TECH_TAG = "Java Source"; private static final TechnologyTagLevel TECH_TAG_LEVEL = TechnologyTagLevel.INFORMATIONAL; @Override public Configuration getConfiguration(RuleLoaderContext ruleLoaderContext) { return ConfigurationBuilder.begin() .addRule() .when(Query.fromType(JavaSourceFileModel.class)) .perform(new IndexJavaFileIterationOperator() .and(Commit.every(100)) .and(IterationProgress.monitoring("Index Java Source Files", 250))); } private final class IndexJavaFileIterationOperator extends AbstractIterationOperation<JavaSourceFileModel> { private static final int JAVA_SUFFIX_LEN = 5; private IndexJavaFileIterationOperator() { super(); } /** * If this is in the input directory, return the input directory. Otherwise, return null. */ private String getInputPathForSource(WindupConfigurationModel configuration, String javaPath) { for (FileModel input : configuration.getInputPaths()) { if (javaPath.startsWith(input.getFilePath())) return input.getFilePath(); } return null; } @Override public void perform(GraphRewrite event, EvaluationContext context, JavaSourceFileModel payload) { // If we are in binary mode, then we ignore java sources. Just remove them from the graph if (SourceMode.isDisabled().evaluate(event, context)) { payload.asVertex().remove(); return; } WindupWildcardImportResolver.setContext(event.getGraphContext()); try { TechnologyTagService technologyTagService = new TechnologyTagService(event.getGraphContext()); GraphContext graphContext = event.getGraphContext(); WindupConfigurationModel configuration = new GraphService<>(graphContext, WindupConfigurationModel.class) .getUnique(); String filepath = payload.getFilePath(); filepath = Paths.get(filepath).toAbsolutePath().toString(); String classFilePath; String inputDir = getInputPathForSource(configuration, filepath); if (inputDir != null) { classFilePath = filepath.substring(inputDir.length() + 1); } else { classFilePath = payload.getPrettyPathWithinProject(); } String qualifiedName = classFilePath.replace(File.separatorChar, '.').substring(0, classFilePath.length() - JAVA_SUFFIX_LEN); String packageName = ""; if (qualifiedName.contains(".")) packageName = qualifiedName.substring(0, qualifiedName.lastIndexOf('.')); if (packageName.startsWith("src.main.java.")) { packageName = packageName.substring("src.main.java.".length()); } // make sure we mark this as a Java file technologyTagService.addTagToFileModel(payload, TECH_TAG, TECH_TAG_LEVEL); payload.setPackageName(packageName); try (FileInputStream fis = new FileInputStream(payload.getFilePath())) { addParsedClassToFile(event, context, payload, fis); } catch (FileNotFoundException e) { throw new WindupException("File in " + payload.getFilePath() + " was not found.", e); } catch (IOException e) { throw new WindupException("IOException thrown when parsing file located in " + payload.getFilePath(), e); } catch (Exception e) { LOG.log(Level.WARNING, "Could not parse java file: " + payload.getFilePath() + " due to: " + e.getMessage(), e); ClassificationService classificationService = new ClassificationService(graphContext); classificationService.attachClassification(event, context, payload, UNPARSEABLE_JAVA_CLASSIFICATION, UNPARSEABLE_JAVA_DESCRIPTION); payload.setParseError(UNPARSEABLE_JAVA_CLASSIFICATION + ": " + e.getMessage()); return; } } finally { WindupWildcardImportResolver.setContext(null); } } private void addParsedClassToFile(GraphRewrite event, EvaluationContext context, JavaSourceFileModel sourceFileModel, FileInputStream fis) { JavaSource<?> javaSource; try { javaSource = Roaster.parse(JavaSource.class, fis); } catch (ParserException e) { ClassificationService classificationService = new ClassificationService(event.getGraphContext()); classificationService.attachClassification(event, context, sourceFileModel, UNPARSEABLE_JAVA_CLASSIFICATION, UNPARSEABLE_JAVA_DESCRIPTION); sourceFileModel.setParseError(UNPARSEABLE_JAVA_CLASSIFICATION + ": " + e.getMessage()); return; } String packageName = javaSource.getPackage(); // set the package name to the parsed value sourceFileModel.setPackageName(packageName); // Set the root path of this source file (if possible). As this could be coming from user-provided source, it // is possible that the path will not match the package name. In this case, we will likely end up with a null // root path. Path rootSourcePath = PathUtil.getRootFolderForSource(sourceFileModel.asFile().toPath(), packageName); if (rootSourcePath != null) { FileModel rootSourceFileModel = new FileService(event.getGraphContext()).createByFilePath(rootSourcePath.toString()); sourceFileModel.setRootSourceFolder(rootSourceFileModel); } String qualifiedName = javaSource.getQualifiedName(); String simpleName = qualifiedName; if (packageName != null && !packageName.isEmpty() && simpleName != null) { simpleName = simpleName.substring(packageName.length() + 1); } JavaClassService javaClassService = new JavaClassService(event.getGraphContext()); JavaClassModel javaClassModel = javaClassService.create(qualifiedName); javaClassModel.setOriginalSource(sourceFileModel); javaClassModel.setSimpleName(simpleName); javaClassModel.setPackageName(packageName); javaClassModel.setQualifiedName(qualifiedName); javaClassModel.setClassFile(sourceFileModel); javaClassModel.setPublic(javaSource.isPublic()); javaClassModel.setInterface(javaSource.isInterface()); if (javaSource instanceof InterfaceCapable) { InterfaceCapable interfaceCapable = (InterfaceCapable) javaSource; List<String> interfaceNames = interfaceCapable.getInterfaces(); if (interfaceNames != null) { for (String iface : interfaceNames) { JavaClassModel interfaceModel = javaClassService.getOrCreatePhantom(iface); javaClassService.addInterface(javaClassModel, interfaceModel); } } } if (!javaSource.isInterface() && javaSource instanceof Extendable) { Extendable<?> extendable = (Extendable<?>) javaSource; String superclassName = extendable.getSuperType(); if (Strings.isNullOrEmpty(superclassName)) javaClassModel.setExtends(javaClassService.getOrCreatePhantom(superclassName)); } sourceFileModel.addJavaClass(javaClassModel); } @Override public String toString() { return "AttachJavaSourceInformationToGraph"; } } }