org.pentaho.reporting.engine.classic.extensions.datasources.xpath.LegacyXPathTableModel.java Source code

Java tutorial

Introduction

Here is the source code for org.pentaho.reporting.engine.classic.extensions.datasources.xpath.LegacyXPathTableModel.java

Source

/*!
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software
* Foundation.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
* or from the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* 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.
*
* Copyright (c) 2002-2017 Hitachi Vantara..  All rights reserved.
*/

package org.pentaho.reporting.engine.classic.extensions.datasources.xpath;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.pentaho.reporting.engine.classic.core.ClassicEngineBoot;
import org.pentaho.reporting.engine.classic.core.DataRow;
import org.pentaho.reporting.engine.classic.core.ReportDataFactoryException;
import org.pentaho.reporting.engine.classic.core.util.IntegerCache;
import org.pentaho.reporting.libraries.base.config.Configuration;
import org.pentaho.reporting.libraries.base.util.GenericObjectTable;
import org.pentaho.reporting.libraries.resourceloader.ResourceData;
import org.pentaho.reporting.libraries.resourceloader.ResourceLoadingException;
import org.pentaho.reporting.libraries.resourceloader.ResourceManager;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import javax.swing.table.AbstractTableModel;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import javax.xml.xpath.XPathVariableResolver;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.sql.Date;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;

public class LegacyXPathTableModel extends AbstractTableModel {
    private static final Log logger = LogFactory.getLog(LegacyXPathTableModel.class);
    public static final String DISALLOW_DOCTYPE_DECL = "http://apache.org/xml/features/disallow-doctype-decl";
    public static final String XPATH_ENABLE_DTDS = "org.pentaho.reporting.engine.classic.extensions.datasources.xpath.EnableDTDs";

    private static class InternalXPathVariableResolver implements XPathVariableResolver {
        private final DataRow parameters;

        private InternalXPathVariableResolver(final DataRow parameters) {
            this.parameters = parameters;
        }

        public Object resolveVariable(final QName variableName) {
            if (parameters != null) {
                final String var = variableName.getLocalPart();
                return parameters.get(var);
            }
            return null;
        }
    }

    private static final Map<String, Class> SUPPORTED_TYPES;

    static {
        final HashMap<String, Class> types = new HashMap<String, Class>();
        types.put("java.lang.String", String.class);
        types.put("java.sql.Date", Date.class);
        types.put("java.math.BigDecimal", BigDecimal.class);
        types.put("java.sql.Timestamp", Timestamp.class);
        types.put("java.lang.Integer", Integer.class);
        types.put("java.lang.Double", Double.class);
        types.put("java.lang.Long", Long.class);

        SUPPORTED_TYPES = Collections.unmodifiableMap(types);
    }

    private GenericObjectTable data;
    private ArrayList<Class> columnTypes;
    private ArrayList<String> columnNames;

    public LegacyXPathTableModel(final ResourceData xmlResource, final ResourceManager resourceManager,
            final String xPathExpression, final DataRow parameters, final int maxRowsToProcess)
            throws ReportDataFactoryException {
        try {
            columnTypes = new ArrayList<Class>();
            columnNames = new ArrayList<String>();

            DocumentBuilderFactory dbf = calculateDocumentBuilderFactory(
                    ClassicEngineBoot.getInstance().getGlobalConfig());

            final XPath xPath = XPathFactory.newInstance().newXPath();
            xPath.setXPathVariableResolver(new InternalXPathVariableResolver(parameters));

            // load metadata (number of rows, row names, row types)

            final String nodeValue = computeColDeclaration(xmlResource, resourceManager, xPath,
                    dbf.newDocumentBuilder());
            if (nodeValue != null) {
                final StringTokenizer stringTokenizer = new StringTokenizer(nodeValue, ",");
                while (stringTokenizer.hasMoreTokens()) {
                    final String className = stringTokenizer.nextToken();
                    if (SUPPORTED_TYPES.containsKey(className)) {
                        columnTypes.add(SUPPORTED_TYPES.get(className));
                    } else {
                        columnTypes.add(String.class);
                    }
                }
            }

            // try to find all valid column names
            // visit all entries and add the names as we find them
            final NodeList rows = evaluateNodeList(xPath, xPathExpression, xmlResource, resourceManager);
            final HashMap<String, Integer> columnNamesToPositionMap = new HashMap<String, Integer>();
            final int rowCount = rows.getLength();
            data = new GenericObjectTable(Math.max(1, rowCount), Math.max(1, columnTypes.size()));
            logger.debug("Processing " + rowCount + " rows");

            for (int row = 0; row < rowCount; row++) {

                if (maxRowsToProcess >= 0) {
                    // query at least one row, so that we get the column names ...
                    final int count = data.getRowCount();
                    if (count > 0 && count >= maxRowsToProcess) {
                        break;
                    }
                }

                final Node node = rows.item(row);
                if (node.getNodeType() != Node.ELEMENT_NODE) {
                    continue;
                }

                logger.debug("Processing row " + row);
                final NodeList childNodes = node.getChildNodes();
                for (int column = 0; column < childNodes.getLength(); column++) {
                    final Node child = childNodes.item(column);
                    if (child.getNodeType() != Node.ELEMENT_NODE) {
                        continue;
                    }

                    final String columnName = child.getNodeName();
                    final String textContent = extractText(child);

                    final int columnPosition;
                    final Integer rawPos = columnNamesToPositionMap.get(columnName);
                    if (rawPos == null) {
                        // a new one
                        columnPosition = columnNames.size();
                        columnNames.add(columnName);
                        columnNamesToPositionMap.put(columnName, IntegerCache.getInteger(columnPosition));
                    } else {
                        columnPosition = rawPos.intValue();
                    }
                    logger.debug("Processing column " + columnPosition + " Name=" + columnName + " value="
                            + textContent);

                    final Class columnClass;
                    if (columnPosition < columnTypes.size()) {
                        columnClass = columnTypes.get(columnPosition);
                    } else {
                        columnClass = String.class;
                    }

                    if (String.class.equals(columnClass)) {
                        data.setObject(row, columnPosition, textContent);
                        continue;
                    }

                    if (columnClass == Date.class) {
                        data.setObject(row, columnPosition, new Date(Long.parseLong(textContent)));
                    } else if (columnClass == BigDecimal.class) {
                        data.setObject(row, columnPosition, new BigDecimal(textContent));
                    } else if (columnClass == Timestamp.class) {
                        data.setObject(row, columnPosition, new Timestamp(Long.parseLong(textContent)));
                    } else if (columnClass == Integer.class) {
                        data.setObject(row, columnPosition, Integer.valueOf(textContent));
                    } else if (columnClass == Double.class) {
                        data.setObject(row, columnPosition, Double.valueOf(textContent));
                    } else if (columnClass == Long.class) {
                        data.setObject(row, columnPosition, Long.valueOf(textContent));
                    } else {
                        data.setObject(row, columnPosition, textContent);
                    }
                }
            }
        } catch (Exception e) {
            throw new ReportDataFactoryException("Failed to query XPath datasource", e);
        }
    }

    private DocumentBuilderFactory calculateDocumentBuilderFactory(final Configuration configuration)
            throws ParserConfigurationException {
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        dbf.setXIncludeAware(false);
        if (!"true".equals(configuration.getConfigProperty(XPATH_ENABLE_DTDS))) {
            dbf.setFeature(DISALLOW_DOCTYPE_DECL, true);
        }
        return dbf;
    }

    private String computeColDeclaration(final ResourceData xmlResource, final ResourceManager resourceManager,
            final XPath xPath, final DocumentBuilder builder) throws XPathExpressionException,
            ResourceLoadingException, IOException, ParserConfigurationException, SAXException {

        final Node pi = evaluateNode(xPath.compile("/processing-instruction('pentaho-dataset')"), xmlResource,
                resourceManager, builder);
        if (pi != null) {
            final String text = pi.getNodeValue();
            if (text.length() > 0) {
                return text;
            }
        }
        final Node types = evaluateNode(xPath.compile("/comment()"), xmlResource, resourceManager, builder);
        if (types != null) {
            final String text = types.getNodeValue();
            if (text.length() > 0) {
                return text;
            }
        }

        final Node resultsetComment = evaluateNode(xPath.compile("/result-set/comment()"), xmlResource,
                resourceManager, builder);
        if (resultsetComment != null) {
            final String text = resultsetComment.getNodeValue();
            if (text.length() > 0) {
                return text;
            }
        }

        return null;
    }

    private String extractText(final Node child) {
        final NodeList contentNodes = child.getChildNodes();
        final StringBuilder textContent = new StringBuilder(32);
        for (int k = 0; k < contentNodes.getLength(); k++) {
            final Node t = contentNodes.item(k);
            if (t.getNodeType() == Node.TEXT_NODE) {
                textContent.append(t.getNodeValue());
            }
        }
        return textContent.toString();
    }

    private NodeList evaluateNodeList(final XPath xpath, final String xpathQuery,
            final ResourceData xmlResourceData, final ResourceManager resourceManager)
            throws XPathExpressionException, ResourceLoadingException, IOException {
        final InputStream stream = xmlResourceData.getResourceAsStream(resourceManager);
        try {
            return (NodeList) xpath.evaluate(xpathQuery, new InputSource(stream), XPathConstants.NODESET);
        } finally {
            stream.close();
        }
    }

    private Node evaluateNode(final XPathExpression xPathExpression, final ResourceData xmlResourceData,
            final ResourceManager resourceManager, final DocumentBuilder builder)
            throws XPathExpressionException, ResourceLoadingException, IOException, SAXException {
        final InputStream stream = xmlResourceData.getResourceAsStream(resourceManager);
        try {
            return (Node) xPathExpression.evaluate(builder.parse(new InputSource(stream)), XPathConstants.NODE);

        } finally {
            stream.close();
        }
    }

    public Class getColumnClass(final int columnIndex) {
        if (columnIndex < columnTypes.size()) {
            return columnTypes.get(columnIndex);
        }
        return String.class;
    }

    public int getRowCount() {
        return data.getRowCount();
    }

    public int getColumnCount() {
        return data.getColumnCount();
    }

    public String getColumnName(final int column) {
        return columnNames.get(column);
    }

    public Object getValueAt(final int rowIndex, final int columnIndex) {
        return data.getObject(rowIndex, columnIndex);
    }
}