All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.sf.jasperreports.engine.data.AbstractXmlDataSource Maven / Gradle / Ivy

There is a newer version: 6.21.3
Show newest version
/*
 * JasperReports - Free Java Reporting Library.
 * Copyright (C) 2001 - 2019 TIBCO Software Inc. All rights reserved.
 * http://www.jaspersoft.com
 *
 * Unless you have purchased a commercial license agreement from Jaspersoft,
 * the following license terms apply:
 *
 * This program is part of JasperReports.
 *
 * JasperReports is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * JasperReports 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 JasperReports. If not, see .
 */

/*
 * Contributors:
 * Tim Thomas - [email protected] 
 */
package net.sf.jasperreports.engine.data;

import java.util.HashMap;
import java.util.Map;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import net.sf.jasperreports.annotations.properties.Property;
import net.sf.jasperreports.annotations.properties.PropertyScope;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JRField;
import net.sf.jasperreports.engine.JRPropertiesUtil;
import net.sf.jasperreports.engine.query.JRXPathQueryExecuterFactory;
import net.sf.jasperreports.properties.PropertyConstants;

/**
 * Abstract XML data source implementation that allows to access the data from a xml
 * document using XPath expressions.
 * 

* The data source is constructed around a node set (record set) selected * by an XPath expression from the xml document. *

*

* Each field can provide an additional XPath expression that will be used to * select its value. This expression must be specified using the {@link #PROPERTY_FIELD_EXPRESSION} * custom property at field level. The use of the {@link net.sf.jasperreports.engine.JRField#getDescription() field description} to specify the XPath expression * is still supported, but is now discouraged, the above mentioned custom property taking precedence * over the field description. In case no XPath expression is specified, the name of the field will be used for the selection of the value. * The expression is evaluated in the context of the current * node thus the expression should be relative to the current node. *

*

* To support subreports, sub data sources can be created. There are two different methods * for creating sub data sources. The first one allows to create a sub data source rooted * at the current node. The current node can be seen as a new document around which the * sub data source is created. The second method allows to create a sub data source that * is rooted at the same document that is used by the data source but uses a different * XPath select expression. *

*

* Example: *

 * <A>
 * 	<B id="0">
 * 		<C>
 * 		<C>
 * 	</B>
 * 	<B id="1">
 * 		<C>
 * 		<C>
 * 	</B>
 * 	<D id="3">
 * 		<E>
 * 		<E>
 * 	</D>
 * </A>
 * 
*

* Data source creation *

    *
  • new JRXmlDataSource(document, "/A/B") - creates a data source with two nodes of type /A/B *
  • new JRXmlDataSource(document, "/A/D") - creates a data source with two nodes of type /A/D *
* Field selection *
    *
  • @id - will select the "id" attribute from the current node *
  • C - will select the value of the first node of type C under the current node. *
* Sub data source creation *
    *
  • "((net.sf.jasperreports.engine.data.JRXmlDataSource)$P{REPORT_DATA_SOURCE}).subDataSource("/B/C") * - in the context of the node B, creates a data source with elements of type /B/C *
  • "((net.sf.jasperreports.engine.data.JRXmlDataSource)$P{REPORT_DATA_SOURCE}).dataSource("/A/D") * - creates a data source with elements of type /A/D *
*

*

* Generally the full power of XPath expression is available. As an example, "/A/B[@id > 0"] will select all the * nodes of type /A/B having the id greater than 0. * You'll find a short XPath tutorial here. * *

*

* Note on performance. Due to the fact that all the XPath expression are interpreted the * data source performance is not great. For the cases where more speed is required, * consider implementing a custom data source that directly accesses the Document through the DOM API. *

* @author Narcis Marcu ([email protected]) */ public abstract class AbstractXmlDataSource> extends JRAbstractTextDataSource implements RandomAccessDataSource, HierarchicalDataSource { /** * Property specifying the XPath expression for the dataset field. */ @Property ( category = PropertyConstants.CATEGORY_DATA_SOURCE, scopes = {PropertyScope.FIELD}, scopeQualifications = {JRXPathQueryExecuterFactory.QUERY_EXECUTER_NAME}, sinceVersion = PropertyConstants.VERSION_6_3_1 ) public static final String PROPERTY_FIELD_EXPRESSION = JRPropertiesUtil.PROPERTY_PREFIX + "xpath.field.expression"; private Map fieldExpressions = new HashMap(); public abstract Node getCurrentNode(); public abstract Object getSelectObject(Node currentNode, String expression) throws JRException; /* * (non-Javadoc) * * @see net.sf.jasperreports.engine.JRDataSource#getFieldValue(net.sf.jasperreports.engine.JRField) */ @Override public Object getFieldValue(JRField jrField) throws JRException { if(getCurrentNode() == null) { return null; } String expression = null; if (fieldExpressions.containsKey(jrField.getName())) { expression = fieldExpressions.get(jrField.getName()); } else { expression = getFieldExpression(jrField); fieldExpressions.put(jrField.getName(), expression); } if (expression == null || expression.length() == 0) { return null; } Object value = null; Class valueClass = jrField.getValueClass(); Object selectedObject = getSelectObject(getCurrentNode(), expression); if(Object.class != valueClass) { if (selectedObject != null) { if (selectedObject instanceof Node) { String text = getText((Node) selectedObject); if (text != null) { value = convertStringValue(text, valueClass); } } else if (selectedObject instanceof Boolean && valueClass.equals(Boolean.class)) { value = selectedObject; } else if (selectedObject instanceof Number && Number.class.isAssignableFrom(valueClass)) { value = convertNumber((Number) selectedObject, valueClass); } else { String text = selectedObject.toString(); value = convertStringValue(text, valueClass); } } } else { value = selectedObject; } return value; } /** * Creates a sub data source using the current node (record) as the root * of the document. An additional XPath expression specifies the select criteria applied to * this new document and that produces the nodes (records) for the data source. * * @param selectExpr the XPath select expression * @return the xml sub data source * @throws JRException if the sub data source couldn't be created * @see JRXmlDataSource#JRXmlDataSource(Document, String) */ @Override public abstract T subDataSource(String selectExpr) throws JRException; /** * Creates a sub data source using the current node (record) as the root * of the document. The data source will contain exactly one record consisting * of the document node itself. * * @return the xml sub data source * @throws JRException if the data source cannot be created * @see JRXmlDataSource#subDataSource(String) * @see JRXmlDataSource#JRXmlDataSource(Document) */ @Override public T subDataSource() throws JRException { return subDataSource("."); } /** * Creates a document using the current node as root. * * @return a document having the current node as root * @throws JRException */ public abstract Document subDocument() throws JRException; /** * Creates a sub data source using as root document the document used by "this" data source. * An additional XPath expression specifies the select criteria applied to * this document and that produces the nodes (records) for the data source. * * @param selectExpr the XPath select expression * @return the xml sub data source * @throws JRException if the sub data source couldn't be created * @see JRXmlDataSource#JRXmlDataSource(Document, String) */ public abstract T dataSource(String selectExpr) throws JRException; /** * Creates a sub data source using as root document the document used by "this" data source. * The data source will contain exactly one record consisting of the document node itself. * * @return the xml sub data source * @throws JRException if the data source cannot be created * @see JRXmlDataSource#dataSource(String) * @see JRXmlDataSource#JRXmlDataSource(Document) */ public T dataSource() throws JRException { return dataSource("."); } /** * Return the text that a node contains. This routine: *
    *
  • Ignores comments and processing instructions. *
  • Concatenates TEXT nodes, CDATA nodes, and the results of recursively * processing EntityRef nodes. *
  • Ignores any element nodes in the sublist. (Other possible options * are to recurse into element sublists or throw an exception.) *
* * @param node a DOM node * @return a String representing node contents or null */ public String getText(Node node) { if (!node.hasChildNodes()) { return node.getNodeValue(); } StringBuilder result = new StringBuilder(); NodeList list = node.getChildNodes(); for (int i = 0; i < list.getLength(); i++) { Node subnode = list.item(i); if (subnode.getNodeType() == Node.TEXT_NODE) { String value = subnode.getNodeValue(); if(value != null) { result.append(value); } } else if (subnode.getNodeType() == Node.CDATA_SECTION_NODE) { String value = subnode.getNodeValue(); if(value != null) { result.append(value); } } else if (subnode.getNodeType() == Node.ENTITY_REFERENCE_NODE) { // Recurse into the subtree for text // (and ignore comments) String value = getText(subnode); if(value != null) { result.append(value); } } } return result.toString(); } protected String getFieldExpression(JRField field) { String fieldExpression = null; if (field.hasProperties()) { fieldExpression = field.getPropertiesMap().getProperty(PROPERTY_FIELD_EXPRESSION); } if (fieldExpression == null || fieldExpression.length() == 0) { fieldExpression = field.getDescription(); if (fieldExpression == null || fieldExpression.length() == 0) { fieldExpression = field.getName(); } } return fieldExpression; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy