Java tutorial
package com.itelis.worker.dev.template.service; /* * ============================================================================ GNU Lesser General Public License * ============================================================================ JasperReports - Free Java report-generating library. * Copyright (C) 2001-2006 JasperSoft Corporation http://www.jaspersoft.com This library 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 2.1 of * the License, or (at your option) any later version. This library 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 library; if not, write * to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. JasperSoft Corporation 303 Second Street, * Suite 450 North San Francisco, CA 94107 http://www.jaspersoft.com */ /* * Contributors: Tim Thomas - tthomas48@users.sourceforge.net */ import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.math.BigDecimal; import java.math.BigInteger; import java.util.Date; import java.util.Locale; import java.util.TimeZone; import net.sf.jasperreports.engine.JRException; import net.sf.jasperreports.engine.JRField; import net.sf.jasperreports.engine.JRRewindableDataSource; import net.sf.jasperreports.engine.design.JRDesignField; import net.sf.jasperreports.engine.util.JRDateLocaleConverter; import net.sf.jasperreports.engine.util.JRXmlUtils; import net.sf.jasperreports.engine.util.xml.JRXPathExecuter; import net.sf.jasperreports.engine.util.xml.JRXPathExecuterUtils; import org.apache.commons.beanutils.locale.LocaleConvertUtilsBean; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; import com.itelis.worker.dev.experteofeed.FieldNode; import com.itelis.worker.dev.experteofeed.JRXMLDataSource; /** * XML data source implementation that allows to access the data from a xml document using XPath expressions. * <p> * The data source is constructed around a node set (record set) selected by an XPath expression from the xml document. * </p> * <p> * Each field can provide an additional XPath expresion that will be used to select its value. This expression must be specified using the * "fieldDescription" element of the field. The expression is evaluated in the context of the current node thus the expression should be * relative to the current node. * </p> * <p> * 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. * </p> * <p> * Example: * * <pre> * <A> * <B id="0"> * <C> * <C> * </B> * <B id="1"> * <C> * <C> * </B> * <D id="3"> * <E> * <E> * </D> * </A> * </pre> * * <p> * Data source creation * <ul> * <li>new JRXmlDataSource(document, "/A/B") - creates a data source with two nodes of type /A/B * <li>new JRXmlDataSource(document, "/A/D") - creates a data source with two nodes of type /A/D * </ul> * Field selection * <ul> * <li> * @id - will select the "id" attribute from the current node * <li>C - will select the value of the first node of type C under the current node. * </ul> * Sub data source creation * <ul> * <li>"((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 * <li>"((net.sf.jasperreports.engine.data.JRXmlDataSource)$P{REPORT_DATA_SOURCE}).dataSource("/A/D") - creates a data source with * elements of type /A/D * </ul> * </p> * <p> * 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 <a * href="http://www.zvon.org/xxl/XPathTutorial/General/examples.html" target="_blank">here</a>. * </p> * <p> * 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. * </p> * @author Peter Severin (peter_p_s@sourceforge.net, contact@jasperassistant.com) * @version $Id: JRXmlDataSource.java 1790 2007-07-26 10:45:46Z lucianc $ * @see JRXPathExecuterUtils */ public class JRXmlDataSource implements JRRewindableDataSource { // the xml document private Document document; // the XPath select expression that gives the nodes to iterate private String selectExpression; // the node list private NodeList nodeList; // the node list length private int nodeListLength; // the current node private Node currentNode; // current node index private int currentNodeIndex = -1; private final JRXPathExecuter xPathExecuter; private LocaleConvertUtilsBean convertBean = null; private Locale locale = null; private String datePattern = null; private String numberPattern = null; private TimeZone timeZone = null; // ----------------------------------------------------------------- // Constructors /** * Creates the data source by parsing the xml document from the given file. The data source will contain exactly one record consisting * of the document node itself. * @param document the document * @throws JRException if the data source cannot be created */ public JRXmlDataSource(Document document) throws JRException { this(document, "."); } /** * Creates the data source by parsing the xml document from the given file. An additional XPath expression specifies the select criteria * that produces the nodes (records) for the data source. * @param document the document * @param selectExpression the XPath select expression * @throws JRException if the data source cannot be created */ public JRXmlDataSource(Document document, String selectExpression) throws JRException { this.document = document; this.selectExpression = selectExpression; this.xPathExecuter = JRXPathExecuterUtils.getXPathExecuter(); moveFirst(); } /** * Creates the data source by parsing the xml document from the given input stream. * @param in the input stream * @see JRXmlDataSource#JRXmlDataSource(Document) */ public JRXmlDataSource(InputStream in) throws JRException { this(in, "."); } /** * Creates the data source by parsing the xml document from the given input stream. * @see JRXmlDataSource#JRXmlDataSource(InputStream) * @see JRXmlDataSource#JRXmlDataSource(Document, String) */ public JRXmlDataSource(InputStream in, String selectExpression) throws JRException { this(JRXmlUtils.parse(new InputSource(in)), selectExpression); } /** * Creates the data source by parsing the xml document from the given system identifier (URI). * <p> * If the system identifier is a URL, it must be full resolved. * </p> * @param uri the system identifier * @see JRXmlDataSource#JRXmlDataSource(Document) */ public JRXmlDataSource(String uri) throws JRException { this(uri, "."); } /** * Creates the data source by parsing the xml document from the given system identifier (URI). * @see JRXmlDataSource#JRXmlDataSource(String) * @see JRXmlDataSource#JRXmlDataSource(Document, String) */ public JRXmlDataSource(String uri, String selectExpression) throws JRException { this(JRXmlUtils.parse(uri), selectExpression); } /** * Creates the data source by parsing the xml document from the given file. * @param file the file * @see JRXmlDataSource#JRXmlDataSource(Document) */ public JRXmlDataSource(File file) throws JRException { this(file, "."); } /** * Creates the data source by parsing the xml document from the given file. * @see JRXmlDataSource#JRXmlDataSource(File) * @see JRXmlDataSource#JRXmlDataSource(Document, String) */ public JRXmlDataSource(File file, String selectExpression) throws JRException { this(JRXmlUtils.parse(file), selectExpression); } // ----------------------------------------------------------------- // Implementation /* * (non-Javadoc) * @see net.sf.jasperreports.engine.JRRewindableDataSource#moveFirst() */ public void moveFirst() throws JRException { if (document == null) throw new JRException("document cannot be null"); if (selectExpression == null) throw new JRException("selectExpression cannot be null"); currentNode = null; currentNodeIndex = -1; nodeListLength = 0; nodeList = xPathExecuter.selectNodeList(document, selectExpression); nodeListLength = nodeList.getLength(); } /* * (non-Javadoc) * @see net.sf.jasperreports.engine.JRDataSource#next() */ public boolean next() { if (currentNodeIndex == nodeListLength - 1) return false; currentNode = nodeList.item(++currentNodeIndex); return true; } /* * (non-Javadoc) * @see net.sf.jasperreports.engine.JRDataSource#getFieldValue(net.sf.jasperreports.engine.JRField) */ public Object getFieldValue(JRField jrField) throws JRException { if (currentNode == null) return null; String expression = jrField.getDescription(); if (expression == null || expression.length() == 0) return null; Object value = null; Class valueClass = jrField.getValueClass(); if (Object.class != valueClass) { Object selectedObject = xPathExecuter.selectObject(currentNode, expression); if (selectedObject != null) { if (selectedObject instanceof Node) { String text = getText((Node) selectedObject); 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); } } } return value; } protected Object convertStringValue(String text, Class valueClass) { Object value = null; // patch JRA if (text != null) { if (String.class.equals(valueClass)) { value = text; } else if (Number.class.isAssignableFrom(valueClass)) { value = getConvertBean().convert(text.trim(), valueClass, locale, numberPattern); } else if (Date.class.isAssignableFrom(valueClass)) { value = getConvertBean().convert(text.trim(), valueClass, locale, datePattern); } else if (Boolean.class.equals(valueClass)) { value = Boolean.valueOf(text); } } return value; } protected Object convertNumber(Number number, Class valueClass) throws JRException { Number value = null; if (valueClass.equals(Byte.class)) { value = new Byte(number.byteValue()); } else if (valueClass.equals(Short.class)) { value = new Short(number.shortValue()); } else if (valueClass.equals(Integer.class)) { value = new Integer(number.intValue()); } else if (valueClass.equals(Long.class)) { value = new Long(number.longValue()); } else if (valueClass.equals(Float.class)) { value = new Float(number.floatValue()); } else if (valueClass.equals(Double.class)) { value = new Double(number.doubleValue()); } else if (valueClass.equals(BigInteger.class)) { value = BigInteger.valueOf(number.longValue()); } else if (valueClass.equals(BigDecimal.class)) { value = new BigDecimal(Double.toString(number.doubleValue())); } else { throw new JRException("Unknown number class " + valueClass.getName()); } 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) */ public JRXmlDataSource subDataSource(String selectExpr) throws JRException { // create a new document from the current node Document doc = subDocument(); JRXmlDataSource subDataSource = new JRXmlDataSource(doc, selectExpr); subDataSource.setLocale(locale); subDataSource.setDatePattern(datePattern); subDataSource.setNumberPattern(numberPattern); subDataSource.setTimeZone(timeZone); return subDataSource; } /** * 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) */ public JRXmlDataSource 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 Document subDocument() throws JRException { if (currentNode == null) { throw new JRException("No node available. Iterate or rewind the data source."); } // create a new document from the current node Document doc = JRXmlUtils.createDocument(currentNode); return doc; } /** * 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 JRXmlDataSource dataSource(String selectExpr) throws JRException { JRXmlDataSource subDataSource = new JRXmlDataSource(document, selectExpr); subDataSource.setLocale(locale); subDataSource.setDatePattern(datePattern); subDataSource.setNumberPattern(numberPattern); subDataSource.setTimeZone(timeZone); return subDataSource; } /** * 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 JRXmlDataSource dataSource() throws JRException { return dataSource("."); } /** * Return the text that a node contains. This routine: * <ul> * <li>Ignores comments and processing instructions. * <li>Concatenates TEXT nodes, CDATA nodes, and the results of recursively processing EntityRef nodes. * <li>Ignores any element nodes in the sublist. (Other possible options are to recurse into element sublists or throw an exception.) * </ul> * @param node a DOM node * @return a String representing node contents or null */ public String getText(Node node) { if (!node.hasChildNodes()) return node.getNodeValue(); StringBuffer result = new StringBuffer(); 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(); } public static void main(String[] args) throws Exception { JRXmlDataSource ds = new JRXmlDataSource(new FileInputStream("Test_Flux_Retour_dentaire.xml"), "/DOSSIER"); JRDesignField field = new JRDesignField(); field.setDescription("BENEF"); field.setValueClass(String.class); ds.next(); String v = (String) ds.getFieldValue(field); System.out.println(field.getDescription() + "=" + v); JRXmlDataSource subDs = ds.dataSource("/DOSSIER/BENEF"); JRDesignField field1 = new JRDesignField(); field1.setDescription("NOM"); field1.setValueClass(String.class); subDs.next(); String v1 = (String) subDs.getFieldValue(field1); System.out.println(field1.getDescription() + "=" + v1); } protected LocaleConvertUtilsBean getConvertBean() { if (convertBean == null) { convertBean = new LocaleConvertUtilsBean(); if (locale != null) { convertBean.setDefaultLocale(locale); convertBean.deregister(); // convertBean.lookup(); } convertBean.register(new JRDateLocaleConverter(timeZone), java.util.Date.class, locale); } return convertBean; } public Locale getLocale() { return locale; } public void setLocale(Locale locale) { this.locale = locale; convertBean = null; } public String getDatePattern() { return datePattern; } public void setDatePattern(String datePattern) { this.datePattern = datePattern; convertBean = null; } public String getNumberPattern() { return numberPattern; } public void setNumberPattern(String numberPattern) { this.numberPattern = numberPattern; convertBean = null; } public TimeZone getTimeZone() { return timeZone; } public void setTimeZone(TimeZone timeZone) { this.timeZone = timeZone; convertBean = null; } private Object getPathValue(FieldNode startingNode, String path) { String tag = ""; if (path == null) return startingNode.getValue(); if (path.startsWith("/")) path = path.substring(1); String sub_path = ""; if (path.indexOf("+") >= 0) { sub_path = path.substring(path.indexOf("+") + 1); path = path.substring(0, path.indexOf("+") + 1); } if (path.indexOf("/") < 0) { if (path.indexOf("@") >= 0) { tag = path.substring(path.indexOf("@") + 1); return startingNode.getAttribute(tag); //path = path.substring(0, path.indexOf("@")); } else if (path.indexOf("*") >= 0) { String childName = path.substring(path.indexOf("*") + 1); // Create a datasource based on this type of chils... FieldNode fn = new FieldNode(startingNode.getName()); fn.setAttributes(startingNode.getAttributes()); fn.setChildren(startingNode.getChilddren(childName)); return new JRXMLDataSource(fn, "/" + startingNode.getName() + "/" + childName); //path = path.substring(0, path.indexOf("@")); } else if (path.indexOf("+") >= 0) { String childToTake = sub_path; childToTake = getNextNodeName(sub_path); return getSubPathValue(startingNode.getChild(childToTake), sub_path); } else { return startingNode.getValue(); } } path = path.substring(path.indexOf("/") + 1); path += sub_path; /* if (path.indexOf("/") > 0) { // go to the next path child... String childToTake = path.substring(0,path.indexOf("/")); System.out.println("taking child " + childToTake); return getPathValue(startingNode.getChild(childToTake), path ); } */ return getPathValue(startingNode.getNextChild(), path); } private Object getSubPathValue(FieldNode startingNode, String path) { String tag = ""; if (path == null) return startingNode.getValue() + "[" + path + "]"; //System.out.println("Resolving path " + path + " now in " + startingNode.getName()); //System.out.flush(); if (path.startsWith("/")) path = path.substring(1); if (path.indexOf("/") < 0) { if (path.indexOf("@") >= 0) { tag = path.substring(path.indexOf("@") + 1); return startingNode.getAttribute(tag); //+ "(tag of " + startingNode.getName() + ")"; //path = path.substring(0, path.indexOf("@")); } else if (path.indexOf("*") >= 0) { String childName = path.substring(path.indexOf("*") + 1); // Create a datasource based on this type of chils... FieldNode fn = new FieldNode(startingNode.getName()); fn.setAttributes(startingNode.getAttributes()); fn.setChildren(startingNode.getChilddren(childName)); return new JRXMLDataSource(fn, "/" + startingNode.getName() + "/" + childName); //path = path.substring(0, path.indexOf("@")); } else { return startingNode.getValue(); //+" " + startingNode.getAttributes().size() +" ("+startingNode.getName() + ")"; } } else { path = path.substring(path.indexOf("/") + 1); String childToTake = path; childToTake = getNextNodeName(path); return getSubPathValue(startingNode.getChild(childToTake), path); //"(child of "+startingNode.getName() + " " + startingNode.getAttributes().size() + ")"; } //return getSubPathValue(startingNode.getNextChild(), path ); } private static String getNextNodeName(String path) { if (path == null || path.length() == 0) return ""; if (path.startsWith("/")) path = path.substring(1); String childToTake = path; if (path.indexOf("/") >= 0) { childToTake = path.substring(0, path.indexOf("/")); } if (childToTake.indexOf("@") >= 0) { childToTake = childToTake.substring(0, path.indexOf("@")); } if (childToTake.indexOf("*") >= 0) { childToTake = childToTake.substring(0, path.indexOf("*")); } return childToTake; } }