org.datacleaner.result.html.HtmlAnalysisResultWriter Maven / Gradle / Ivy
/**
* DataCleaner (community edition)
* Copyright (C) 2014 Free Software Foundation, Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.datacleaner.result.html;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.Writer;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import java.util.function.Predicate;
import java.util.function.Supplier;
import org.apache.metamodel.util.TruePredicate;
import org.datacleaner.api.AnalyzerResult;
import org.datacleaner.api.Renderer;
import org.datacleaner.configuration.DataCleanerConfiguration;
import org.datacleaner.descriptors.ComponentDescriptor;
import org.datacleaner.documentation.ComponentDocumentationWrapper;
import org.datacleaner.job.ComponentJob;
import org.datacleaner.result.AnalysisResult;
import org.datacleaner.result.AnalysisResultWriter;
import org.datacleaner.result.renderer.HtmlRenderingFormat;
import org.datacleaner.result.renderer.RendererFactory;
import org.datacleaner.util.ComponentJobComparator;
import org.datacleaner.util.LabelUtils;
import org.datacleaner.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* {@link AnalysisResultWriter} which writes an analysis result as a HTML page.
*/
public class HtmlAnalysisResultWriter implements AnalysisResultWriter {
private static final Logger logger = LoggerFactory.getLogger(HtmlAnalysisResultWriter.class);
private final boolean _tabs;
private final boolean _headers;
private final Predicate> _jobInclusionPredicate;
public HtmlAnalysisResultWriter() {
this(true);
}
public HtmlAnalysisResultWriter(final boolean tabs) {
this(tabs, new TruePredicate<>());
}
public HtmlAnalysisResultWriter(final boolean tabs,
final Predicate> jobInclusionPredicate) {
this(tabs, jobInclusionPredicate, true);
}
public HtmlAnalysisResultWriter(final boolean tabs,
final Predicate> jobInclusionPredicate, final boolean headers) {
_tabs = tabs;
_jobInclusionPredicate = jobInclusionPredicate;
_headers = headers;
}
@Override
public void write(final AnalysisResult result, final DataCleanerConfiguration configuration,
final Supplier writerRef, final Supplier outputStreamRef) throws IOException {
final Writer writer = writerRef.get();
write(result, configuration, writer);
}
public void write(final AnalysisResult result, final DataCleanerConfiguration configuration, final Writer writer)
throws IOException {
final HtmlRenderingContext context = new DefaultHtmlRenderingContext();
final RendererFactory rendererFactory = new RendererFactory(configuration);
final Map htmlFragments = new LinkedHashMap<>();
final Map resultMap = new TreeMap<>(new ComponentJobComparator());
resultMap.putAll(result.getResultMap());
for (final Entry entry : resultMap.entrySet()) {
final ComponentJob componentJob = entry.getKey();
final AnalyzerResult analyzerResult = entry.getValue();
if (_jobInclusionPredicate.test(entry)) {
final Renderer super AnalyzerResult, ? extends HtmlFragment> renderer =
rendererFactory.getRenderer(analyzerResult, HtmlRenderingFormat.class);
if (renderer == null) {
throw new IllegalStateException("No HTML renderer found for result: " + analyzerResult);
}
final HtmlRenderingContext localContext = new ComponentHtmlRenderingContext(context, componentJob);
try {
final HtmlFragment htmlFragment = renderer.render(analyzerResult);
htmlFragment.initialize(localContext);
htmlFragments.put(componentJob, htmlFragment);
} catch (final Exception e) {
logger.error("Error while rendering analyzer result: " + analyzerResult, e);
writeRenderingError(writer, componentJob, analyzerResult, e);
}
} else {
logger.debug("Skipping job {} / result {} because predicate evaluated false", componentJob,
analyzerResult);
}
}
writeHtmlBegin(writer, context);
writeHead(writer, htmlFragments, context);
writeBody(writer, htmlFragments, context);
writeHtmlEnd(writer, context);
}
private void writeMaterializationError(final Writer writer, final ComponentJob componentJob, final Exception e)
throws IOException {
writeGenericError(writer, componentJob, null, e);
}
private void writeRenderingError(final Writer writer, final ComponentJob componentJob,
final AnalyzerResult analyzerResult, final Exception e) throws IOException {
writeGenericError(writer, componentJob, analyzerResult, e);
}
private void writeGenericError(final Writer writer, final ComponentJob componentJob,
final AnalyzerResult analyzerResult, final Exception e) throws IOException {
writer.write("");
writer.write("Component job: " + LabelUtils.getLabel(componentJob) + "
");
if (analyzerResult != null) {
writer.write("Analyzer result: " + analyzerResult + "
");
}
writer.write("");
final PrintWriter printWriter = new PrintWriter(writer);
e.printStackTrace(printWriter);
writer.write("
");
writer.write("");
}
protected void writeHtmlBegin(final Writer writer, final HtmlRenderingContext context) throws IOException {
writer.write("\n");
writer.write("\n");
}
protected void writeHtmlEnd(final Writer writer, final HtmlRenderingContext context) throws IOException {
writer.write("");
}
protected void writeHead(final Writer writer, final Map htmlFragments,
final HtmlRenderingContext context) throws IOException {
final Set allHeadElements = new HashSet<>();
writeHeadBegin(writer);
// add base element no matter what
{
final HeadElement baseHeadElement = createBaseHeadElement();
writeHeadElement(writer, null, baseHeadElement, context);
allHeadElements.add(baseHeadElement);
}
for (final Entry entry : htmlFragments.entrySet()) {
final HtmlFragment htmlFragment = entry.getValue();
final List headElements = htmlFragment.getHeadElements();
for (final HeadElement headElement : headElements) {
if (!allHeadElements.contains(headElement)) {
final ComponentJob componentJob = entry.getKey();
writeHeadElement(writer, componentJob, headElement, context);
allHeadElements.add(headElement);
}
}
}
writeHeadEnd(writer);
}
protected HeadElement createBaseHeadElement() {
return new BaseHeadElement();
}
protected void writeHeadBegin(final Writer writer) throws IOException {
writer.write("\n");
writer.write(" Analysis result ");
}
protected void writeHeadEnd(final Writer writer) throws IOException {
writer.write("");
}
protected void writeHeadElement(final Writer writer, final ComponentJob componentJob, final HeadElement headElement,
final HtmlRenderingContext context) throws IOException {
final HtmlRenderingContext localContext = new ComponentHtmlRenderingContext(context, componentJob);
writer.write(" ");
try {
final String html = headElement.toHtml(localContext);
writer.write(html);
} catch (final Exception e) {
writeMaterializationError(writer, componentJob, e);
}
writer.write('\n');
}
protected void writeBody(final Writer writer, final Map htmlFragments,
final HtmlRenderingContext context) throws IOException {
final Set> htmlFragmentSet = htmlFragments.entrySet();
writeBodyBegin(writer, context);
writer.write("");
if (_tabs) {
// write a with all descriptors in it (a TOC)
{
writer.write("");
ComponentDescriptor> lastDescriptor = null;
for (final Entry entry : htmlFragmentSet) {
final ComponentJob componentJob = entry.getKey();
final ComponentDescriptor> descriptor = componentJob.getDescriptor();
if (!descriptor.equals(lastDescriptor)) {
final ComponentDocumentationWrapper wrapper = new ComponentDocumentationWrapper(descriptor);
final String iconSrc = wrapper.getIconSrc(22);
final String styleName = toStyleName(descriptor.getDisplayName());
writer.write("- ");
writer.write(context.escapeHtml(descriptor.getDisplayName()));
writer.write("
");
lastDescriptor = descriptor;
}
}
writer.write("
");
}
}
writer.write("
");
// write all descriptor groups
{
boolean descriptorGroupBegin = false;
ComponentDescriptor> lastDescriptor = null;
for (final Entry entry : htmlFragmentSet) {
final ComponentJob componentJob = entry.getKey();
final ComponentDescriptor> descriptor = componentJob.getDescriptor();
final HtmlFragment htmlFragment = entry.getValue();
if (!descriptor.equals(lastDescriptor)) {
if (descriptorGroupBegin) {
writer.write("
");
lastDescriptor = descriptor;
descriptorGroupBegin = true;
}
writeBodyHtmlFragment(writer, componentJob, htmlFragment, context);
}
if (descriptorGroupBegin) {
writer.write("
\n");
}
}
writeBodyEnd(writer, context);
}
protected void writeBodyBegin(final Writer writer, final HtmlRenderingContext context) throws IOException {
writer.write("\n");
writer.write("\n");
}
protected void writeBodyEnd(final Writer writer, final HtmlRenderingContext context) throws IOException {
writer.write("
\n");
writer.write("");
}
protected void writeBodyHtmlFragment(final Writer writer, final ComponentJob componentJob,
final HtmlFragment htmlFragment, final HtmlRenderingContext context) throws IOException {
final String displayName = componentJob.getDescriptor().getDisplayName();
final String styleName = toStyleName(displayName);
writer.write("");
if (_headers) {
writeHeader(writer, componentJob, context, htmlFragment);
}
writer.write("
\n");
}
protected void writeHeader(final Writer writer, final ComponentJob componentJob, final HtmlRenderingContext context,
final HtmlFragment htmlFragment) throws IOException {
final String label = LabelUtils.getLabel(componentJob);
writer.write("\n");
final List bodyElements = htmlFragment.getBodyElements();
for (final BodyElement bodyElement : bodyElements) {
writeBodyElement(writer, componentJob, htmlFragment, bodyElement, context);
}
writer.write("
");
writer.write("");
writer.write("");
writer.write("
");
}
protected String toStyleName(final String displayName) {
final String camelCase = StringUtils.toCamelCase(displayName);
return camelCase.replaceAll("/", "_").replaceAll("&", "_");
}
protected void writeBodyElement(final Writer writer, final ComponentJob componentJob,
final HtmlFragment htmlFragment, final BodyElement bodyElement, final HtmlRenderingContext context)
throws IOException {
final HtmlRenderingContext localContext = new ComponentHtmlRenderingContext(context, componentJob);
writer.write(" ");
try {
final String html = bodyElement.toHtml(localContext);
writer.write(html);
} catch (final Exception e) {
writeMaterializationError(writer, componentJob, e);
}
writer.write('\n');
}
}