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";
}
}
}