package org.jboss.windup.reporting.freemarker;
import java.lang.ref.SoftReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jboss.windup.config.GraphRewrite;
import org.jboss.windup.util.Logging;
import org.pegdown.Extensions;
import org.pegdown.LinkRenderer;
import org.pegdown.PegDownProcessor;
import org.pegdown.ToHtmlSerializer;
import org.pegdown.VerbatimSerializer;
import org.pegdown.ast.RootNode;
import org.pegdown.plugins.PegDownPlugins;
import org.pegdown.plugins.ToHtmlSerializerPlugin;
import freemarker.template.SimpleScalar;
import freemarker.template.TemplateModelException;
/**
* Converts from an input string in Markdown format to an output string in HTML.
*
* @author <a href="mailto:jesse.sightler@gmail.com">Jesse Sightler</a>
*/
public class MarkdownToHtmlMethod implements WindupFreeMarkerMethod
{
private static final Logger LOG = Logging.get(MarkdownToHtmlMethod.class);
public static final long MAX_PARSING_TIME_MILLIS = 100000;
private static final Map<String, SoftReference<String>> cache = new ConcurrentHashMap<>();
@Override
public Object exec(@SuppressWarnings("rawtypes") List arguments) throws TemplateModelException
{
if (arguments.size() != 1)
{
throw new TemplateModelException("Error, method expects one argument (String)");
}
SimpleScalar freemarkerArg = (SimpleScalar) arguments.get(0);
String markdownSource = freemarkerArg.getAsString();
SoftReference<String> cachedResult = cache.get(markdownSource);
String cachedResultString;
if (cachedResult != null && (cachedResultString = cachedResult.get()) != null)
{
return cachedResultString;
}
try
{
// build the plugins object with our extensions
PegDownPlugins plugins = PegDownPlugins.builder().build();
PegDownProcessor processor = new PegDownProcessor(Extensions.FENCED_CODE_BLOCKS, MAX_PARSING_TIME_MILLIS, plugins);
// build the node and then serialize it so that we can make sure the serializer uses our plugins
RootNode outputNode = processor.parseMarkdown(markdownSource.toCharArray());
// Our plugin is also a serializer, so build a plugins list for serialization as well
List<ToHtmlSerializerPlugin> serializerPlugins = new ArrayList<>(1);
ToHtmlSerializer serializer = new ToHtmlSerializerExtended(new LinkRenderer(), Collections.<String, VerbatimSerializer> emptyMap(),
serializerPlugins);
String result = serializer.toHtml(outputNode);
cache.put(markdownSource, new SoftReference<>(result));
return result;
}
catch (Throwable t)
{
LOG.log(Level.WARNING, "Failed to parse markdown due to: " + t.getMessage() + " markdown source: " + markdownSource, t);
// Return the unformatted markdown, as this is better than failing the report completely.
return markdownSource;
}
}
@Override
public String getMethodName()
{
return "markdownToHtml";
}
@Override
public String getDescription()
{
return "Converts from an input string in Markdown format to an output string in HTML format";
}
@Override
public void setContext(GraphRewrite event)
{
}
}