Package com.google.visualization.datasource.render

Source Code of com.google.visualization.datasource.render.HtmlRenderer

// Copyright 2009 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.visualization.datasource.render;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.StringWriter;
import java.io.Writer;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

import com.google.visualization.datasource.DataSourceRequest;
import com.google.visualization.datasource.base.ReasonType;
import com.google.visualization.datasource.base.ResponseStatus;
import com.google.visualization.datasource.base.StatusType;
import com.google.visualization.datasource.base.Warning;
import com.google.visualization.datasource.datatable.ColumnDescription;
import com.google.visualization.datasource.datatable.DataTable;
import com.google.visualization.datasource.datatable.TableCell;
import com.google.visualization.datasource.datatable.TableRow;
import com.google.visualization.datasource.datatable.ValueFormatter;
import com.google.visualization.datasource.datatable.value.BooleanValue;
import com.google.visualization.datasource.datatable.value.ValueType;

/**
* Takes a data table and returns an html string.
*
* @author Nimrod T.
*/
public class HtmlRenderer extends AbstractRenderer {
    private static final Log log = LogFactory.getLog(HtmlRenderer.class);

    /**
     * Pattern for matching against <a> tags with hrefs.
     * Used in sanitizeDetailedMessage.
     */
    private static final Pattern DETAILED_MESSAGE_A_TAG_REGEXP = Pattern.compile(
            "([^<]*<a(( )*target=\"_blank\")*(( )*target='_blank')*"
                    + "(( )*href=\"[^\"]*\")*(( )*href='[^']*')*>[^<]*</a>)+[^<]*");

    /**
     * Pattern for matching against "javascript:".
     * Used in sanitizeDetailedMessage.
     */
    private static final Pattern BAD_JAVASCRIPT_REGEXP = Pattern.compile("javascript(( )*):");

    @Override
    public String getMimetype() {
        return "text/html";
    }

    /**
     * Generates an HTML string representation of a data table.
     */
    @Override
    public CharSequence render(final DataSourceRequest request, final DataTable dataTable) {
        // Create an xml document with head and an empty body.
        Document document = this.createDocument();
        Element bodyElement = this.appendHeadAndBody(document);

        // Populate the xml document.
        Element tableElement = document.createElement("table");
        bodyElement.appendChild(tableElement);
        tableElement.setAttribute("border", "1");
        tableElement.setAttribute("cellpadding", "2");
        tableElement.setAttribute("cellspacing", "0");

        // Labels tr element.
        List<ColumnDescription> columnDescriptions = dataTable.getColumnDescriptions();
        Element trElement = document.createElement("tr");
        trElement.setAttribute("style", "font-weight: bold; background-color: #aaa;");
        for(ColumnDescription columnDescription : columnDescriptions) {
            Element tdElement = document.createElement("td");
            tdElement.setTextContent(columnDescription.getLabel());
            trElement.appendChild(tdElement);
        }
        tableElement.appendChild(trElement);

        Map<ValueType, ValueFormatter> formatters = ValueFormatter.createDefaultFormatters(request.getUserLocale());
        // Table tr elements.
        int rowCount = 0;
        for(TableRow row : dataTable.getRows()) {
            rowCount++;
            trElement = document.createElement("tr");
            String backgroundColor = (rowCount % 2 != 0) ? "#f0f0f0" : "#ffffff";
            trElement.setAttribute("style", "background-color: " + backgroundColor);
            List<TableCell> cells = row.getCells();
            for(int c = 0; c < cells.size(); c++) {
                ValueType valueType = columnDescriptions.get(c).getType();
                TableCell cell = cells.get(c);
                Element tdElement = document.createElement("td");
                if(cell.isNull()) {
                    tdElement.setTextContent("\u00a0");
                }
                else {
                    String cellFormattedText = cell.getFormattedValue();
                    if(cellFormattedText == null) {
                        cellFormattedText = formatters.get(cell.getType()).format(cell.getValue());
                    }
                    switch(valueType) {
                        case NUMBER:
                            tdElement.setAttribute("align", "right");
                            tdElement.setTextContent(cellFormattedText);
                            break;
                        case BOOLEAN:
                            BooleanValue booleanValue = (BooleanValue) cell.getValue();
                            tdElement.setAttribute("align", "center");
                            if(booleanValue.getValue()) {
                                tdElement.setTextContent("\u2714"); // Check mark.
                            }
                            else {
                                tdElement.setTextContent("\u2717"); // X mark.
                            }
                            break;
                        default:
                            if(StringUtils.isEmpty(cellFormattedText)) {
                                tdElement.setTextContent("\u00a0"); // nbsp.
                            }
                            else {
                                tdElement.setTextContent(cellFormattedText);
                            }
                    }
                }
                trElement.appendChild(tdElement);
            }
            tableElement.appendChild(trElement);
        }
        bodyElement.appendChild(tableElement);

        // Warnings:
        for(Warning warning : dataTable.getWarnings()) {
            bodyElement.appendChild(document.createElement("br"));
            bodyElement.appendChild(document.createElement("br"));
            Element messageElement = document.createElement("div");
            messageElement.setTextContent(warning.getReasonType().getMessageForReasonType() + ". " +
                    warning.getMessage());
            bodyElement.appendChild(messageElement);
        }

        return transformDocumentToHtmlString(document);
    }

    /**
     * Transforms a document to a valid html string.
     *
     * @param document The document to transform
     * @return A string representation of a valid html.
     */
    private String transformDocumentToHtmlString(Document document) {
        // Generate a CharSequence from the xml document.
        Transformer transformer = null;
        try {
            transformer = TransformerFactory.newInstance().newTransformer();
        }
        catch(TransformerConfigurationException e) {
            log.error("Couldn't create a transformer", e);
            throw new RuntimeException("Couldn't create a transformer. This should never happen.", e);
        }
        transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        transformer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, "-//W3C//DTD HTML 4.01//EN");
        transformer.setOutputProperty(OutputKeys.METHOD, "html");
        transformer.setOutputProperty(OutputKeys.VERSION, "4.01");

        DOMSource source = new DOMSource(document);
        Writer writer = new StringWriter();
        StreamResult result = new StreamResult(writer);
        try {
            transformer.transform(source, result);
        }
        catch(TransformerException e) {
            log.error("Couldn't transform", e);
            throw new RuntimeException("Couldn't transform. This should never happen.", e);
        }

        return writer.toString();
    }

    /**
     * Sanitizes the html in the detailedMessage, to allow only href inside &lt;a&gt; tags.
     *
     * @param detailedMessage The detailedMessage.
     * @return The sanitized detailedMessage.
     */
    String sanitizeDetailedMessage(String detailedMessage) {
        if(StringUtils.isEmpty(detailedMessage)) {
            return "";
        }

        if(DETAILED_MESSAGE_A_TAG_REGEXP.matcher(detailedMessage).matches()
                && (!BAD_JAVASCRIPT_REGEXP.matcher(detailedMessage).find())) {
            // No need to escape.
            return detailedMessage;
        }
        else {
            // Need to html escape.
            return EscapeUtil.htmlEscape(detailedMessage);
        }
    }

    /**
     * Renders a simple html for the given responseStatus.
     *
     * @param request Data source request
     * @param status  The response status.
     * @return A simple HTML for the given response status.
     */
    @Override
    public CharSequence error(final DataSourceRequest request, final ResponseStatus status) {
        // Get the responseStatus details.
        StatusType type = status.getStatusType();
        ReasonType reason = status.getReasonType();
        String detailedMessage = status.getDescription();

        // Create an xml document with head and an empty body.
        Document document = createDocument();
        Element bodyElement = appendHeadAndBody(document);

        // Populate the xml document.
        Element oopsElement = document.createElement("h3");
        oopsElement.setTextContent("Oops, an error occured.");
        bodyElement.appendChild(oopsElement);

        if(type != null) {
            String text = "Status: " + type.lowerCaseString();
            appendSimpleText(document, bodyElement, text);
        }

        if(reason != null) {
            String text = "Reason: " + reason.getMessageForReasonType(null);
            appendSimpleText(document, bodyElement, text);
        }

        if(detailedMessage != null) {
            String text = "Description: " + sanitizeDetailedMessage(detailedMessage);
            appendSimpleText(document, bodyElement, text);
        }

        return transformDocumentToHtmlString(document);
    }

    /**
     * Creates a document element.
     *
     * @return A document element.
     */
    private Document createDocument() {
        DocumentBuilder documentBuilder;
        try {
            documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
        }
        catch(ParserConfigurationException e) {
            log.error("Couldn't create a document builder", e);
            throw new RuntimeException(
                    "Couldn't create a document builder. This should never happen.", e);
        }
        return documentBuilder.newDocument();
    }

    /**
     * Appends &lt;html&gt;, &lt;head&gt;, &lt;title&gt;, and &lt;body&gt; elements to the document.
     *
     * @param document The containing document.
     * @return The &lt;body&gt; element.
     */
    private Element appendHeadAndBody(Document document) {
        Element htmlElement = document.createElement("html");
        document.appendChild(htmlElement);
        Element headElement = document.createElement("head");
        htmlElement.appendChild(headElement);
        Element titleElement = document.createElement("title");
        titleElement.setTextContent("Google Visualization");
        headElement.appendChild(titleElement);
        Element bodyElement = document.createElement("body");
        htmlElement.appendChild(bodyElement);
        return bodyElement;
    }

    /**
     * Appends a simple text line to the body of the document.
     *
     * @param document    The containing document.
     * @param bodyElement The body of the document.
     * @param text        The text to append.
     */
    private void appendSimpleText(Document document, Element bodyElement, String text) {
        Element statusElement = document.createElement("div");
        statusElement.setTextContent(text);
        bodyElement.appendChild(statusElement);
    }
}
TOP

Related Classes of com.google.visualization.datasource.render.HtmlRenderer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.