package org.jboss.windup.reporting.config.classification;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.lang.StringUtils;
import org.jboss.forge.furnace.util.Assert;
import org.jboss.windup.config.GraphRewrite;
import org.jboss.windup.config.operation.Iteration;
import org.jboss.windup.config.parameters.ParameterizedIterationOperation;
import org.jboss.windup.graph.GraphContext;
import org.jboss.windup.graph.model.FileReferenceModel;
import org.jboss.windup.graph.model.LinkModel;
import org.jboss.windup.graph.model.WindupVertexFrame;
import org.jboss.windup.graph.model.resource.FileModel;
import org.jboss.windup.graph.model.resource.SourceFileModel;
import org.jboss.windup.graph.service.LinkService;
import org.jboss.windup.reporting.config.Link;
import org.jboss.windup.reporting.quickfix.Quickfix;
import org.jboss.windup.reporting.model.ClassificationModel;
import org.jboss.windup.reporting.service.ClassificationService;
import org.jboss.windup.reporting.service.TagSetService;
import org.jboss.windup.reporting.category.IssueCategory;
import org.jboss.windup.reporting.category.IssueCategoryModel;
import org.jboss.windup.reporting.category.IssueCategoryRegistry;
import org.jboss.windup.util.ExecutionStatistics;
import org.jboss.windup.util.Logging;
import org.ocpsoft.rewrite.config.Rule;
import org.ocpsoft.rewrite.context.EvaluationContext;
import org.ocpsoft.rewrite.param.ParameterStore;
import org.ocpsoft.rewrite.param.RegexParameterizedPatternParser;
/**
* Classifies a {@link FileModel} {@link Iteration} payload.
*
* @author <a href="mailto:lincolnbaxter@gmail.com">Lincoln Baxter, III</a>
* @author <a href="mailto:dynawest@gmail.com">Ondrej Zizka</a>
*/
public class Classification extends ParameterizedIterationOperation<FileModel> implements ClassificationAs, ClassificationEffort,
ClassificationDescription, ClassificationLink, ClassificationTags, ClassificationWithIssueCategory, ClassificationQuickfix
{
private static final Logger LOG = Logging.get(Classification.class);
private List<Link> links = new ArrayList<>();
private Set<String> tags = new HashSet<>();
private List<Quickfix> quickfixes = new ArrayList<>();
private RegexParameterizedPatternParser classificationPattern;
private RegexParameterizedPatternParser descriptionPattern;
private int effort;
private IssueCategory issueCategory = null;
Classification(String variable)
{
super(variable);
}
Classification()
{
super();
}
/**
* Create a new classification for the given ref.
*/
public static ClassificationBuilderOf of(String variable)
{
return new ClassificationBuilderOf(variable);
}
/**
* Classify the current {@link FileModel} as the given text.
*/
public static ClassificationAs as(String classification)
{
Assert.notNull(classification, "Classification text must not be null.");
Classification result = new Classification();
result.classificationPattern = new RegexParameterizedPatternParser(classification);
return result;
}
/**
* @return the quickfixes
*/
public List<Quickfix> getQuickfixes()
{
return quickfixes;
}
/**
* Set the payload to the fileModel of the given instance even though the variable is not directly referencing it. This is mainly to simplify the
* creation of the rule, when the FileModel itself is not being iterated but just a model referencing it.
*
*/
@Override
public FileModel resolvePayload(GraphRewrite event, EvaluationContext context, WindupVertexFrame payload)
{
checkVariableName(event, context);
if (payload instanceof FileReferenceModel)
{
return ((FileReferenceModel) payload).getFile();
}
if (payload instanceof FileModel)
{
return (FileModel) payload;
}
return null;
}
/**
* Gets the configured {@link IssueCategory} level.
*/
public IssueCategory getIssueCategory()
{
return issueCategory;
}
/**
* Sets the {@link IssueCategory} to a non-default level.
*/
@Override
public ClassificationWithIssueCategory withIssueCategory(IssueCategory issueCategory)
{
this.issueCategory = issueCategory;
return this;
}
/**
* Set the description of this {@link Classification}.
*/
public ClassificationDescription withDescription(String description)
{
this.descriptionPattern = new RegexParameterizedPatternParser(description);
return this;
}
/**
* Add a {@link Link} to this {@link Classification}.
*/
public ClassificationLink with(Link link)
{
this.links.add(link);
return this;
}
public ClassificationQuickfix withQuickfix(Quickfix fix)
{
this.quickfixes.add(fix);
return this;
}
/**
* Add the given tags to this {@link Classification}.
*/
public ClassificationTags withTags(Set<String> tags)
{
this.tags.addAll(tags);
return this;
}
public Classification withEffort(int effort)
{
this.effort = effort;
return this;
}
private Set<String> getTags()
{
return Collections.unmodifiableSet(tags);
}
@Override
public void performParameterized(GraphRewrite event, EvaluationContext context, FileModel payload)
{
ExecutionStatistics.get().begin("Classification.performParameterized");
try
{
/*
* Check for duplicate classifications before we do anything. If a classification already exists, then we don't want to add another.
*/
String description = null;
if (descriptionPattern != null)
{
try
{
description = descriptionPattern.getBuilder().build(event, context);
}
catch (Throwable t)
{
LOG.log(Level.WARNING, "Failed to generate parameterized Classification description due to: " + t.getMessage(), t);
description = descriptionPattern.toString();
}
}
String text;
try
{
text = classificationPattern.getBuilder().build(event, context);
}
catch (Throwable t)
{
LOG.log(Level.WARNING, "Failed to generate parameterized Classification due to: " + t.getMessage(), t);
text = classificationPattern.toString();
}
GraphContext graphContext = event.getGraphContext();
ClassificationService classificationService = new ClassificationService(graphContext);
ClassificationModel classification = classificationService.getUniqueByProperty(ClassificationModel.CLASSIFICATION, text);
if (classification == null)
{
classification = classificationService.create();
classification.setEffort(effort);
IssueCategoryModel issueCategoryModel;
if (this.issueCategory == null)
issueCategoryModel = IssueCategoryRegistry.loadFromGraph(event.getGraphContext(), IssueCategoryRegistry.DEFAULT);
else
issueCategoryModel = IssueCategoryRegistry.loadFromGraph(event.getGraphContext(), this.issueCategory.getCategoryID());
classification.setIssueCategory(issueCategoryModel);
classification.setDescription(StringUtils.trim(description));
classification.setClassification(StringUtils.trim(text));
Set<String> tags = new HashSet<>(this.getTags());
TagSetService tagSetService = new TagSetService(event.getGraphContext());
classification.setTagModel(tagSetService.getOrCreate(event, tags));
classification.setRuleID(((Rule) context.get(Rule.class)).getId());
LinkService linkService = new LinkService(graphContext);
for (Link link : links)
{
LinkModel linkModel = linkService.getOrCreate(
StringUtils.trim(link.getTitle()),
StringUtils.trim(link.getLink()));
classification.addLink(linkModel);
}
for (Quickfix quickfix : quickfixes)
{
classification.addQuickfix(quickfix.createQuickfix(graphContext));
}
}
classificationService.attachClassification(event, classification, payload);
if (payload instanceof SourceFileModel)
((SourceFileModel) payload).setGenerateSourceReport(true);
LOG.info("Classification added to " + payload.getPrettyPathWithinProject() + " [" + this + "] ");
}
finally
{
ExecutionStatistics.get().end("Classification.performParameterized");
}
}
protected void setClassificationText(String classification)
{
this.classificationPattern = new RegexParameterizedPatternParser(classification);
}
@Override
public String toString()
{
StringBuilder result = new StringBuilder();
result.append("Classification.as(").append(classificationPattern.getPattern()).append(")");
if (descriptionPattern != null && !descriptionPattern.getPattern().trim().isEmpty())
result.append(".withDescription(").append(descriptionPattern).append(")");
if (effort != 0)
result.append(".withEffort(").append(effort).append(")");
if (links != null && !links.isEmpty())
result.append(".with(").append(links).append(")");
return result.toString();
}
public List<Link> getLinks()
{
return links;
}
public RegexParameterizedPatternParser getClassificationPattern()
{
return classificationPattern;
}
public RegexParameterizedPatternParser getDescriptionPattern()
{
return descriptionPattern;
}
public int getEffort()
{
return effort;
}
@Override
public Set<String> getRequiredParameterNames()
{
Set<String> result = new HashSet<>(classificationPattern.getRequiredParameterNames());
if (descriptionPattern != null)
result.addAll(descriptionPattern.getRequiredParameterNames());
return result;
}
@Override
public void setParameterStore(ParameterStore store)
{
classificationPattern.setParameterStore(store);
if (descriptionPattern != null)
descriptionPattern.setParameterStore(store);
}
}