package org.jboss.windup.reporting.xml;
import static org.joox.JOOX.$;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.SystemUtils;
import org.jboss.windup.config.exception.ConfigurationException;
import org.jboss.windup.config.parser.ElementHandler;
import org.jboss.windup.config.parser.NamespaceElementHandler;
import org.jboss.windup.config.parser.ParserContext;
import org.jboss.windup.config.parser.xml.RuleProviderHandler;
import org.jboss.windup.reporting.config.Hint;
import org.jboss.windup.reporting.config.HintText;
import org.jboss.windup.reporting.config.Link;
import org.jboss.windup.reporting.quickfix.Quickfix;
import org.jboss.windup.reporting.config.classification.Classification;
import org.jboss.windup.reporting.category.IssueCategory;
import org.jboss.windup.reporting.category.IssueCategoryRegistry;
import org.jboss.windup.util.exception.WindupException;
import org.w3c.dom.Element;
/**
* Adds the provided {@link Classification} operation to the currently selected items.
*
* Expected format:
*
* <pre>
* <hint message="hint" effort="8" severity="INFO">
* </hint>
* </pre>
*
* Alternatively, the hint message can be in its own element. This is primary useful for longer hint content:
*
* <pre>
* <hint title="Short description" effort="8">
* <message>
* Longer help contents go here (markdown format is supported)
* </message>
* </hint>
* </pre>
*
* Also note that markdown formatting is fully supported via the <a href="http://www.pegdown.org/">Pegdown</a> library.
*
* @author <a href="mailto:jesse.sightler@gmail.com">Jesse Sightler</a>
*/
@NamespaceElementHandler(elementName = "hint", namespace = RuleProviderHandler.WINDUP_RULE_NAMESPACE)
public class HintHandler implements ElementHandler<Hint>
{
@Override
public Hint processElement(ParserContext handlerManager, Element element) throws ConfigurationException
{
String title = $(element).attr("title");
String categoryID = $(element).attr("category-id");
// Backwards compatibility with old rules
if (StringUtils.isBlank(categoryID))
categoryID = $(element).attr("severity");
String message = $(element).attr("message");
String in = $(element).attr("in");
Set<String> tags = new HashSet<>();
if (StringUtils.isBlank(message))
{
StringBuilder messageBuilder = new StringBuilder();
List<Element> children = $(element).children().get();
for (Element child : children)
{
if (child.getNodeName().equals("message"))
{
messageBuilder.append((String)handlerManager.processElement(child));
}
}
message = messageBuilder.toString();
// remove the leading spaces as these can mess with markdown formatting
message = trimLeadingAndTrailingSpaces(message);
}
if (StringUtils.isBlank(message))
{
throw new WindupException("Error, 'hint' element must have a non-empty 'message' attribute or element");
}
String effortStr = $(element).attr("effort");
HintText hint;
if (!StringUtils.isBlank(title))
{
hint = Hint.in(in).titled(title).withText(message);
}
else
{
hint = Hint.in(in).withText(message);
}
if (StringUtils.isNotBlank(categoryID))
{
IssueCategoryRegistry issueCategoryRegistry = IssueCategoryRegistry.instance(handlerManager.getRuleLoaderContext().getContext());
IssueCategory issueCategory = issueCategoryRegistry.getByID(categoryID);
hint.withIssueCategory(issueCategory);
}
if (!StringUtils.isBlank(effortStr))
{
try
{
int effort = Integer.parseInt(effortStr);
hint.withEffort(effort);
}
catch (NumberFormatException e)
{
throw new WindupException("Could not parse effort level: " + effortStr + " as an integer!");
}
}
List<Element> children = $(element).children().get();
for (Element child : children)
{
switch (child.getNodeName())
{
case "link":
Link link = handlerManager.processElement(child);
hint.with(link);
break;
case "tag":
tags.add(child.getTextContent());
break;
case "quickfix":
Quickfix quickfix = handlerManager.processElement(child);
hint.withQuickfix(quickfix);
break;
}
}
hint.withTags(tags);
return (Hint) hint;
}
public static String trimLeadingAndTrailingSpaces(String markdown)
{
StringBuilder markdownSB = new StringBuilder();
StringBuilder currentLine = new StringBuilder();
String firstLineIndent = null;
for (int i = 0; i < markdown.length(); i++)
{
char currentChar = markdown.charAt(i);
if (currentChar == '\r' || currentChar == '\n')
{
String currentLineString = currentLine.toString();
if (firstLineIndent == null && !StringUtils.isEmpty(currentLineString))
{
int firstNonWhitespaceIndex = StringUtils.indexOfAnyBut(currentLineString, " \t");
if (firstNonWhitespaceIndex != -1)
firstLineIndent = currentLineString.substring(0, firstNonWhitespaceIndex);
}
if (firstLineIndent != null)
currentLineString = StringUtils.removeStart(currentLineString, firstLineIndent);
markdownSB.append(currentLineString).append(SystemUtils.LINE_SEPARATOR);
currentLine.setLength(0);
// skip the next line separator for \r\n cases
if (currentChar == '\r' && markdown.length() > (i + 1) && markdown.charAt(i + 1) == '\n')
{
i++;
}
}
else
{
currentLine.append(currentChar);
}
}
markdownSB.append(currentLine);
return markdownSB.toString();
}
}