package org.jboss.windup.reporting.config;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.lang3.StringUtils;
import org.jboss.forge.furnace.util.Assert;
import org.jboss.windup.config.GraphRewrite;
import org.jboss.windup.config.parameters.ParameterizedIterationOperation;
import org.jboss.windup.graph.model.FileLocationModel;
import org.jboss.windup.graph.model.LinkModel;
import org.jboss.windup.reporting.model.QuickfixModel;
import org.jboss.windup.graph.model.resource.SourceFileModel;
import org.jboss.windup.graph.service.GraphService;
import org.jboss.windup.reporting.model.InlineHintModel;
import org.jboss.windup.reporting.quickfix.Quickfix;
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.OperationBuilder;
import org.ocpsoft.rewrite.config.Rule;
import org.ocpsoft.rewrite.context.EvaluationContext;
import org.ocpsoft.rewrite.param.ParameterStore;
import org.ocpsoft.rewrite.param.RegexParameterizedPatternParser;
/**
* Used as an intermediate to support the addition of {@link InlineHintModel} objects to the graph via an Operation.
*/
public class Hint extends ParameterizedIterationOperation<FileLocationModel> implements HintText, HintLink, HintWithIssueCategory, HintEffort, HintQuickfix
{
private static final Logger LOG = Logging.get(Hint.class);
private RegexParameterizedPatternParser hintTitlePattern;
private RegexParameterizedPatternParser hintTextPattern;
private int effort;
private IssueCategory issueCategory;
private List<Link> links = new ArrayList<>();
private Set<String> tags = Collections.emptySet();
private List<Quickfix> quickfixes = new ArrayList<>();
protected Hint(String variable)
{
super(variable);
}
protected Hint()
{
super();
}
/**
* Create a new {@link Hint} in the {@link FileLocationModel} resolved by the given variable.
*/
public static HintBuilderIn in(String fileVariable)
{
return new HintBuilderIn(fileVariable);
}
/**
* Create a new {@link Hint} with the specified title.
*/
public static HintBuilderTitle titled(String title)
{
return new HintBuilderTitle(title);
}
/**
* Create a new {@link Hint} in the current {@link FileLocationModel}, and specify the text or content to be displayed in the report.
*/
public static HintText withText(String text)
{
Assert.notNull(text, "Hint text must not be null.");
Hint hint = new Hint();
hint.setText(text);
return hint;
}
@Override
public HintWithIssueCategory withIssueCategory(IssueCategory issueCategory)
{
this.issueCategory = issueCategory;
return this;
}
/**
* Returns the currently set {@link IssueCategory}.
*/
public IssueCategory getIssueCategory()
{
return this.issueCategory;
}
@Override
public void performParameterized(final GraphRewrite event, final EvaluationContext context, final FileLocationModel locationModel)
{
ExecutionStatistics.get().begin("Hint.performParameterized");
try
{
GraphService<InlineHintModel> service = new GraphService<>(event.getGraphContext(), InlineHintModel.class);
InlineHintModel hintModel = service.create();
hintModel.setRuleID(((Rule) context.get(Rule.class)).getId());
hintModel.setLineNumber(locationModel.getLineNumber());
hintModel.setColumnNumber(locationModel.getColumnNumber());
hintModel.setLength(locationModel.getLength());
hintModel.setFileLocationReference(locationModel);
hintModel.setFile(locationModel.getFile());
hintModel.setEffort(effort);
IssueCategoryRegistry issueCategoryRegistry = IssueCategoryRegistry.instance(event.getRewriteContext());
IssueCategoryModel issueCategoryModel;
if (this.issueCategory == null)
issueCategoryModel = issueCategoryRegistry.loadFromGraph(event.getGraphContext(), IssueCategoryRegistry.DEFAULT);
else
issueCategoryModel = issueCategoryRegistry.loadFromGraph(event.getGraphContext(), this.issueCategory.getCategoryID());
hintModel.setIssueCategory(issueCategoryModel);
if (hintTitlePattern != null)
{
try
{
hintModel.setTitle(StringUtils.trim(hintTitlePattern.getBuilder().build(event, context)));
}
catch (Throwable t)
{
LOG.log(Level.WARNING, "Failed to generate parameterized Hint title due to: " + t.getMessage(), t);
hintModel.setTitle(hintTitlePattern.toString().trim());
}
}
else
{
// If there is no title, just use the description of the location
// (eg, 'Constructing com.otherproduct.Foo()')
hintModel.setTitle(locationModel.getDescription());
}
String hintText;
try
{
hintText = hintTextPattern.getBuilder().build(event, context);
}
catch (Throwable t)
{
LOG.log(Level.WARNING, "Failed to generate parameterized Hint body due to: " + t.getMessage(), t);
hintText = hintTextPattern.toString();
}
hintModel.setHint(StringUtils.trim(hintText));
GraphService<LinkModel> linkService = new GraphService<>(event.getGraphContext(), LinkModel.class);
for (Link link : links)
{
LinkModel linkModel = linkService.create();
linkModel.setDescription(StringUtils.trim(link.getTitle()));
linkModel.setLink(StringUtils.trim(link.getLink()));
hintModel.addLink(linkModel);
}
GraphService<QuickfixModel> quickfixService = new GraphService<>(event.getGraphContext(), QuickfixModel.class);
for (Quickfix quickfix : quickfixes)
{
hintModel.addQuickfix(quickfix.createQuickfix(event.getGraphContext()));
}
Set<String> tags = new HashSet<>(this.getTags());
TagSetService tagSetService = new TagSetService(event.getGraphContext());
hintModel.setTagModel(tagSetService.getOrCreate(event, tags));
if (locationModel.getFile() instanceof SourceFileModel)
((SourceFileModel) locationModel.getFile()).setGenerateSourceReport(true);
LOG.info("Hint added to " + locationModel.getFile().getPrettyPathWithinProject() + " [" + this.toString(hintModel.getTitle(), hintText)
+ "] with tags: " + StringUtils.join(this.getTags(), " "));
}
finally
{
ExecutionStatistics.get().end("Hint.performParameterized");
}
}
@Override
public HintEffort withEffort(int effort)
{
this.effort = effort;
return this;
}
@Override
public OperationBuilder withTags(Set<String> tags)
{
this.tags = tags;
return this;
}
@Override
public HintLink with(Link link)
{
this.links.add(link);
return this;
}
/**
* Set the inner hint text on this instance.
*/
protected void setText(String text)
{
this.hintTextPattern = new RegexParameterizedPatternParser(text);
}
protected void setTitle(String title)
{
this.hintTitlePattern = new RegexParameterizedPatternParser(title);
}
public RegexParameterizedPatternParser getHintText()
{
return hintTextPattern;
}
public int getEffort()
{
return effort;
}
public List<Link> getLinks()
{
return links;
}
public Set<String> getTags()
{
return Collections.unmodifiableSet(tags);
}
@Override
public Set<String> getRequiredParameterNames()
{
final Set<String> result = new LinkedHashSet<>();
result.addAll(hintTextPattern.getRequiredParameterNames());
if (hintTitlePattern != null)
result.addAll(hintTitlePattern.getRequiredParameterNames());
return result;
}
@Override
public void setParameterStore(ParameterStore store)
{
hintTextPattern.setParameterStore(store);
if (hintTitlePattern != null)
hintTitlePattern.setParameterStore(store);
}
@Override
public String toString()
{
String title = "";
if (hintTitlePattern != null)
title = hintTitlePattern.getPattern();
return toString(title, hintTextPattern.getPattern());
}
private String toString(String title, String text)
{
StringBuilder result = new StringBuilder();
result.append("Hint");
if (title != null)
result.append(".titled(\"").append(title).append("\")");
result.append(".withText(\"").append(text).append("\")");
if (effort != 0)
result.append(".withEffort(").append(effort).append(")");
if (links != null && !links.isEmpty())
result.append(".with(").append(links).append(")");
if (tags != null && !tags.isEmpty())
result.append(".withTags(").append(tags).append(")");
return result.toString();
}
/**
* @return the quickfixes
*/
public List<Quickfix> getQuickfixes()
{
return quickfixes;
}
/**
* @param quickfix the quickfixes to set
*/
public HintQuickfix withQuickfix(Quickfix quickfix)
{
this.quickfixes.add(quickfix);
return this;
}
}