package org.jboss.windup.rules.apps.java.service;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.jboss.windup.ast.java.data.ResolutionStatus;
import org.jboss.windup.ast.java.data.TypeReferenceLocation;
import org.jboss.windup.graph.GraphContext;
import org.jboss.windup.graph.model.DuplicateProjectModel;
import org.jboss.windup.graph.model.FileLocationModel;
import org.jboss.windup.graph.model.ProjectModel;
import org.jboss.windup.graph.model.WindupVertexFrame;
import org.jboss.windup.graph.model.resource.FileModel;
import org.jboss.windup.graph.service.GraphService;
import org.jboss.windup.graph.traversal.ProjectModelTraversal;
import org.jboss.windup.graph.traversal.TraversalStrategy;
import org.jboss.windup.reporting.TagUtil;
import org.jboss.windup.reporting.model.InlineHintModel;
import org.jboss.windup.reporting.service.InlineHintService;
import org.jboss.windup.rules.apps.java.scan.ast.JavaTypeReferenceModel;
import org.jboss.windup.util.ExecutionStatistics;
import com.thinkaurelius.titan.core.attribute.Text;
import com.tinkerpop.blueprints.Vertex;
import com.tinkerpop.gremlin.java.GremlinPipeline;
/**
* Adds the getPackageUseFrequencies() and createTypeReference().
*/
public class TypeReferenceService extends GraphService<JavaTypeReferenceModel>
{
public TypeReferenceService(GraphContext context)
{
super(context, JavaTypeReferenceModel.class);
}
/**
* This performs the same function as {@link TypeReferenceService#getPackageUseFrequencies(ProjectModel, Set, Set, int, boolean)},
* however it is designed to use a {@link ProjectModelTraversal} instead of only {@link ProjectModel}.
*
* This is useful for cases where the {@link ProjectModelTraversal} needs to use a custom {@link TraversalStrategy}.
*/
public Map<String, Integer> getPackageUseFrequencies(ProjectModelTraversal projectTraversal, Set<String> includeTags,
Set<String> excludeTags, int nameDepth, boolean recursive)
{
Map<String, Integer> packageUseCount = new HashMap<>();
getPackageUseFrequencies(packageUseCount, projectTraversal, includeTags, excludeTags, nameDepth, recursive);
return packageUseCount;
}
private Map<String, Integer> getPackageUseFrequencies(Map<String, Integer> packageUseCount,
ProjectModelTraversal projectTraversal, Set<String> includeTags,
Set<String> excludeTags, int nameDepth, boolean recursive)
{
getPackageUseFrequencies(packageUseCount, projectTraversal.getCurrent(), includeTags, excludeTags, nameDepth, false);
if (recursive)
{
for (ProjectModelTraversal childTraversal : projectTraversal.getChildren())
{
getPackageUseFrequencies(packageUseCount, childTraversal, includeTags, excludeTags, nameDepth, recursive);
}
}
return packageUseCount;
}
/**
* Returns the list of most frequently hinted packages (based upon JavaInlineHintModel references) within the given ProjectModel. If recursive is
* set to true, then also include child projects.
*
* nameDepth controls how many package levels to include (com.* vs com.example.* vs com.example.sub.*)
*/
public Map<String, Integer> getPackageUseFrequencies(ProjectModel projectModel, Set<String> includeTags, Set<String> excludeTags, int nameDepth,
boolean recursive)
{
ExecutionStatistics.get().begin("TypeReferenceService.getPackageUseFrequencies(projectModel,nameDepth,recursive)");
Map<String, Integer> packageUseCount = new HashMap<>();
getPackageUseFrequencies(packageUseCount, projectModel, includeTags, excludeTags, nameDepth, recursive);
ExecutionStatistics.get().end("TypeReferenceService.getPackageUseFrequencies(projectModel,nameDepth,recursive)");
return packageUseCount;
}
private void getPackageUseFrequencies(Map<String, Integer> data, ProjectModel projectModel, Set<String> includeTags, Set<String> excludeTags,
int nameDepth, boolean recursive)
{
ExecutionStatistics.get().begin("TypeReferenceService.getPackageUseFrequencies(data,projectModel,nameDepth,recursive)");
if (projectModel instanceof DuplicateProjectModel)
projectModel = ((DuplicateProjectModel)projectModel).getCanonicalProject();
InlineHintService hintService = new InlineHintService(getGraphContext());
// 1. Get all JavaHints for the given project
GremlinPipeline<Vertex, Vertex> pipeline = new GremlinPipeline<>(projectModel.asVertex());
pipeline.out(ProjectModel.PROJECT_MODEL_TO_FILE).in(InlineHintModel.FILE_MODEL);
pipeline.has(WindupVertexFrame.TYPE_PROP, Text.CONTAINS, InlineHintModel.TYPE);
pipeline.as("inlineHintVertex");
pipeline.out(InlineHintModel.FILE_LOCATION_REFERENCE).has(WindupVertexFrame.TYPE_PROP, Text.CONTAINS,
JavaTypeReferenceModel.TYPE);
pipeline.back("inlineHintVertex");
// 2. Organize them by package name
// summarize results.
for (Vertex inlineHintVertex : pipeline)
{
InlineHintModel javaInlineHint = hintService.frame(inlineHintVertex);
// only check tags if we have some passed in
if (!includeTags.isEmpty() || !excludeTags.isEmpty())
{
if (!TagUtil.checkMatchingTags(javaInlineHint.getTags(), includeTags, excludeTags))
continue;
}
int val = 1;
FileLocationModel fileLocationModel = javaInlineHint.getFileLocationReference();
if (fileLocationModel == null || !(fileLocationModel instanceof JavaTypeReferenceModel))
{
continue;
}
JavaTypeReferenceModel typeReferenceModel = (JavaTypeReferenceModel) fileLocationModel;
String pattern = typeReferenceModel.getResolvedSourceSnippit();
String[] keyArray = pattern.split("\\.");
if (keyArray.length > 1 && nameDepth > 1)
{
StringBuilder patternSB = new StringBuilder();
for (int i = 0; i < nameDepth; i++)
{
String subElement = keyArray[i];
// FIXME/TODO - This shouldn't be necessary, but is at the moment due to some stuff emmitted by our
// AST
if (subElement.contains("(") || subElement.contains(")"))
{
continue;
}
if (patternSB.length() != 0)
{
patternSB.append(".");
}
patternSB.append(subElement);
}
if (patternSB.toString().contains("."))
{
patternSB.append(".*");
}
pattern = patternSB.toString();
}
if (pattern.contains("("))
{
pattern = pattern.substring(0, pattern.indexOf('('));
}
if (data.containsKey(pattern))
{
val = data.get(pattern);
val++;
}
data.put(pattern, val);
}
if (recursive)
{
for (ProjectModel childProject : projectModel.getChildProjects())
{
ExecutionStatistics.get().end("TypeReferenceService.getPackageUseFrequencies(data,projectModel,nameDepth,recursive)");
getPackageUseFrequencies(data, childProject, includeTags, excludeTags, nameDepth, recursive);
ExecutionStatistics.get().begin("TypeReferenceService.getPackageUseFrequencies(data,projectModel,nameDepth,recursive)");
}
}
ExecutionStatistics.get().end("TypeReferenceService.getPackageUseFrequencies(data,projectModel,nameDepth,recursive)");
}
public JavaTypeReferenceModel createTypeReference(FileModel fileModel, TypeReferenceLocation location,
ResolutionStatus resolutionStatus, int lineNumber, int columnNumber, int length, String resolvedSource, String line)
{
ExecutionStatistics.get().begin("TypeReferenceService.createTypeReference(fileModel,location,lineNumber,columnNumber,length,source)");
JavaTypeReferenceModel model = create();
model.setFile(fileModel);
model.setLineNumber(lineNumber);
model.setColumnNumber(columnNumber);
model.setLength(length);
model.setResolvedSourceSnippit(resolvedSource);
model.setSourceSnippit(line);
model.setReferenceLocation(location);
model.setResolutionStatus(resolutionStatus);
ExecutionStatistics.get().end("TypeReferenceService.createTypeReference(fileModel,location,lineNumber,columnNumber,length,source)");
return model;
}
}