package org.jboss.windup.reporting.service;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import com.google.common.base.Function;
import com.google.common.collect.Iterables;
import org.jboss.windup.config.GraphRewrite;
import org.jboss.windup.graph.GraphContext;
import org.jboss.windup.graph.model.FileReferenceModel;
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.reporting.model.EffortReportModel;
import org.jboss.windup.reporting.model.InlineHintModel;
import org.jboss.windup.reporting.category.IssueCategoryModel;
import com.thinkaurelius.titan.core.attribute.Text;
import com.tinkerpop.blueprints.Compare;
import com.tinkerpop.blueprints.Vertex;
import com.tinkerpop.frames.structures.FramedVertexIterable;
import com.tinkerpop.gremlin.java.GremlinPipeline;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* This provides helper functions for finding and creating {@link InlineHintModel} instances within the graph.
*
* @author <a href="mailto:jesse.sightler@gmail.com">Jesse Sightler</a>
*/
public class InlineHintService extends GraphService<InlineHintModel>
{
public static final Logger LOG = Logger.getLogger(InlineHintService.class.getName());
public InlineHintService(GraphContext context)
{
super(context, InlineHintModel.class);
}
/**
* Gets all {@link InlineHintModel} instances that are directly associated with the given {@link FileReferenceModel}
*/
public Iterable<InlineHintModel> getHintsForFileReference(FileReferenceModel reference)
{
GremlinPipeline<Vertex, Vertex> inlineHintPipeline = new GremlinPipeline<>(reference.asVertex());
inlineHintPipeline.in(InlineHintModel.FILE_LOCATION_REFERENCE);
inlineHintPipeline.has(WindupVertexFrame.TYPE_PROP, Text.CONTAINS, InlineHintModel.TYPE);
return new FramedVertexIterable<>(getGraphContext().getFramed(), inlineHintPipeline, InlineHintModel.class);
}
/**
* Gets all {@link InlineHintModel} instances that are directly associated with the given {@link FileModel}
*/
public Iterable<InlineHintModel> getHintsForFile(FileModel file)
{
GremlinPipeline<Vertex, Vertex> inlineHintPipeline = new GremlinPipeline<>(file.asVertex());
inlineHintPipeline.in(FileReferenceModel.FILE_MODEL);
inlineHintPipeline.has(WindupVertexFrame.TYPE_PROP, Text.CONTAINS, InlineHintModel.TYPE);
return new FramedVertexIterable<>(getGraphContext().getFramed(), inlineHintPipeline, InlineHintModel.class);
}
/**
* Returns the total effort points in all of the {@link InlineHintModel} instances associated with the provided {@link FileModel}.
*/
public int getMigrationEffortPoints(FileModel fileModel)
{
GremlinPipeline<Vertex, Vertex> inlineHintPipeline = new GremlinPipeline<>(fileModel.asVertex());
inlineHintPipeline.in(InlineHintModel.FILE_MODEL);
inlineHintPipeline.has(EffortReportModel.EFFORT, Compare.GREATER_THAN, 0);
inlineHintPipeline.has(WindupVertexFrame.TYPE_PROP, Text.CONTAINS, InlineHintModel.TYPE);
int hintEffort = 0;
for (Vertex v : inlineHintPipeline)
{
hintEffort += (Integer) v.getProperty(InlineHintModel.EFFORT);
}
return hintEffort;
}
private Collection<Vertex> getProjectAndChildren(ProjectModel projectModel)
{
ArrayList<Vertex> result = new ArrayList<>();
result.add(projectModel.asVertex());
for (ProjectModel child : projectModel.getChildProjects())
{
result.addAll(getProjectAndChildren(child));
}
return result;
}
/**
* Returns all hints for the given {@link ProjectModel}. If recursive is set, then this will recurse into
* child projects as well.
*/
public Iterable<InlineHintModel> getHintsForProject(ProjectModel projectModel, boolean recursive)
{
final Iterable<Vertex> initialVertices;
if (recursive)
{
initialVertices = getProjectAndChildren(projectModel);
}
else
{
initialVertices = Collections.singletonList(projectModel.asVertex());
}
return getInlineHintModels(initialVertices);
}
public Iterable<InlineHintModel> getHintsForProjects(Iterable<ProjectModel> projectModels)
{
Iterable<Vertex> projectVertexIterable = Iterables.transform(projectModels, new Function<ProjectModel, Vertex>()
{
@Override
public Vertex apply(ProjectModel input)
{
return input.asVertex();
}
});
return getInlineHintModels(projectVertexIterable);
}
private Iterable<InlineHintModel> getInlineHintModels(Iterable<Vertex> initialProjectVertices) {
GremlinPipeline<Vertex, Vertex> inlineHintPipeline = new GremlinPipeline<>(initialProjectVertices);
inlineHintPipeline.out(ProjectModel.PROJECT_MODEL_TO_FILE);
inlineHintPipeline.in(InlineHintModel.FILE_MODEL).has(WindupVertexFrame.TYPE_PROP, Text.CONTAINS, InlineHintModel.TYPE);
Set<InlineHintModel> results = new LinkedHashSet<>();
for (Vertex v : inlineHintPipeline)
{
results.add(frame(v));
}
return results;
}
/**
* <p>
* Returns the total effort points in all of the {@link InlineHintModel}s
* associated with the {@link FileModel} instances in the given {@link ProjectModelTraversal}.
* </p>
* <p>
* If set to recursive, then also include the effort points from child projects.
* </p>
* <p>
* The result is a Map, the key contains the effort level and the value contains the number of incidents.
* </p>
*/
public Map<Integer, Integer> getMigrationEffortByPoints(
ProjectModelTraversal traversal, Set<String> includeTags, Set<String> excludeTags, boolean recursive, boolean includeZero)
{
MapSumEffortAccumulatorFunction<Integer> accumulator = new MapSumEffortAccumulatorFunction(){
public Object vertexToKey(Vertex effortReportVertex) {
Integer migrationEffort = effortReportVertex.getProperty(EffortReportModel.EFFORT);
return migrationEffort;
}
};
getMigrationEffortDetails(traversal, includeTags, excludeTags, recursive, includeZero, accumulator);
return accumulator.getResults();
}
/**
* Returns the total incidents in all of the {@link InlineHintModel}s associated with the files in this project by severity.
*/
public Map<IssueCategoryModel, Integer> getMigrationEffortBySeverity(GraphRewrite event, ProjectModelTraversal traversal, Set<String> includeTags, Set<String> excludeTags,
boolean recursive)
{
MapSumEffortAccumulatorFunction<IssueCategoryModel> accumulator = new MapSumEffortAccumulatorFunction<IssueCategoryModel>()
{
public IssueCategoryModel vertexToKey(Vertex effortReportVertex)
{
return frame(effortReportVertex).getIssueCategory();
}
};
this.getMigrationEffortDetails(traversal, includeTags, excludeTags, recursive, true, accumulator);
return accumulator.getResults();
}
private void getMigrationEffortDetails(ProjectModelTraversal traversal, Set<String> includeTags, Set<String> excludeTags, boolean recursive,
boolean includeZero, EffortAccumulatorFunction accumulatorFunction)
{
LOG.log(Level.INFO, String.format("\n\t\t\tEFFORT H: getMigrationEffortDetails() with: %s, %srecur, %sincludeZero, %s, tags: %s, excl: %s",
traversal, recursive ? "" : "!", includeZero ? "" : "!", accumulatorFunction, includeTags, excludeTags));
final Set<Vertex> initialVertices = traversal.getAllProjectsAsVertices(recursive);
GremlinPipeline<Vertex, Vertex> pipeline = new GremlinPipeline<>(this.getGraphContext().getGraph());
pipeline.V();
// If the multivalue index is not 1st, then it doesn't work - https://github.com/thinkaurelius/titan/issues/403
if (!includeZero)
{
pipeline.has(EffortReportModel.EFFORT, Compare.GREATER_THAN, 0);
pipeline.has(WindupVertexFrame.TYPE_PROP, Text.CONTAINS, InlineHintModel.TYPE);
}
else
{
pipeline.has(WindupVertexFrame.TYPE_PROP, InlineHintModel.TYPE);
}
pipeline.as("hint");
pipeline.out(InlineHintModel.FILE_MODEL);
pipeline.in(ProjectModel.PROJECT_MODEL_TO_FILE);
pipeline.filter(new SetMembersFilter(initialVertices));
pipeline.back("hint");
boolean checkTags = !includeTags.isEmpty() || !excludeTags.isEmpty();
for (Vertex v : pipeline)
{
// only check tags if we have some passed in
if (checkTags && !frame(v).matchesTags(includeTags, excludeTags))
continue;
accumulatorFunction.accumulate(v);
}
}
}