/*! ******************************************************************************
*
* Pentaho Data Integration
*
* Copyright (C) 2002-2016 by Pentaho : http://www.pentaho.com
*
*******************************************************************************
*
* 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 org.pentaho.di.core.xml;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.io.StringWriter;
import java.math.BigDecimal;
import java.net.MalformedURLException;
import java.net.URL;
import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
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.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.vfs2.FileObject;
import org.owasp.encoder.Encode;
import org.pentaho.di.core.Const;
import org.pentaho.di.core.util.Utils;
import org.pentaho.di.core.KettleAttributeInterface;
import org.pentaho.di.core.exception.KettleException;
import org.pentaho.di.core.exception.KettleXMLException;
import org.pentaho.di.core.row.ValueMeta;
import org.pentaho.di.core.vfs.KettleVFS;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
/**
* This class contains a number of (static) methods to facilitate the retrieval of information from XML Node(s).
*
* @author Matt
* @since 04-04-2003
*
*/
public class XMLHandler {
//TODO Change impl for some standard XML processing (like StAX, for example) because ESAPI has charset processing issues.
private static XMLHandlerCache cache = XMLHandlerCache.getInstance();
private static final SimpleDateFormat simpleDateFormat = new SimpleDateFormat( ValueMeta.DEFAULT_DATE_FORMAT_MASK );
private static final SimpleDateFormat simpleTimeStampFormat = new SimpleDateFormat( ValueMeta.DEFAULT_TIMESTAMP_FORMAT_MASK );
/**
* The header string to specify encoding in UTF-8 for XML files
*
* @return The XML header.
*/
public static String getXMLHeader() {
return getXMLHeader( Const.XML_ENCODING );
}
/**
* The header string to specify encoding in an XML file
*
* @param encoding
* The desired encoding to use in the XML file
* @return The XML header.
*/
public static String getXMLHeader( String encoding ) {
return "<?xml version=\"1.0\" encoding=\"" + encoding + "\"?>" + Const.CR;
}
/**
* Get the value of a tag in a node
*
* @param n
* The node to look in
* @param tag
* The tag to look for
* @return The value of the tag or null if nothing was found.
*/
public static String getTagValue( Node n, KettleAttributeInterface code ) {
return getTagValue( n, code.getXmlCode() );
}
/**
* Get the value of a tag in a node
*
* @param n
* The node to look in
* @param tag
* The tag to look for
* @return The value of the tag or null if nothing was found.
*/
public static String getTagValue( Node n, String tag ) {
NodeList children;
Node childnode;
if ( n == null ) {
return null;
}
children = n.getChildNodes();
for ( int i = 0; i < children.getLength(); i++ ) {
childnode = children.item( i );
if ( childnode.getNodeName().equalsIgnoreCase( tag ) ) {
if ( childnode.getFirstChild() != null ) {
return childnode.getFirstChild().getNodeValue();
}
}
}
return null;
}
/**
* Get the value of a tag in a node
*
* @param n
* The node to look in
* @param tag
* The tag to look for
* @return The value of the tag or null if nothing was found.
*/
public static String getTagValueWithAttribute( Node n, String tag, String attribute ) {
NodeList children;
Node childnode;
if ( n == null ) {
return null;
}
children = n.getChildNodes();
for ( int i = 0; i < children.getLength(); i++ ) {
childnode = children.item( i );
if ( childnode.getNodeName().equalsIgnoreCase( tag )
&& childnode.getAttributes().getNamedItem( attribute ) != null ) {
if ( childnode.getFirstChild() != null ) {
return childnode.getFirstChild().getNodeValue();
}
}
}
return null;
}
/**
* Search a node for a certain tag, in that subnode search for a certain subtag. Return the value of that subtag.
*
* @param n
* The node to look in
* @param tag
* The tag to look for
* @param subtag
* The subtag to look for
* @return The string of the subtag or null if nothing was found.
*/
public static String getTagValue( Node n, String tag, String subtag ) {
NodeList children, tags;
Node childnode, tagnode;
if ( n == null ) {
return null;
}
children = n.getChildNodes();
for ( int i = 0; i < children.getLength(); i++ ) {
childnode = children.item( i );
if ( childnode.getNodeName().equalsIgnoreCase( tag ) ) {
// <file>
tags = childnode.getChildNodes();
for ( int j = 0; j < tags.getLength(); j++ ) {
tagnode = tags.item( j );
if ( tagnode.getNodeName().equalsIgnoreCase( subtag ) ) {
if ( tagnode.getFirstChild() != null ) {
return tagnode.getFirstChild().getNodeValue();
}
}
}
}
}
return null;
}
/**
* Count nodes with a certain tag
*
* @param n
* The node to look in
* @param tag
* The tags to count
* @return The number of nodes found with a certain tag
*/
public static int countNodes( Node n, String tag ) {
NodeList children;
Node childnode;
int count = 0;
if ( n == null ) {
return 0;
}
children = n.getChildNodes();
for ( int i = 0; i < children.getLength(); i++ ) {
childnode = children.item( i );
if ( childnode.getNodeName().equalsIgnoreCase( tag ) ) {
// <file>
count++;
}
}
return count;
}
/**
* Get nodes with a certain tag one level down
*
* @param n
* The node to look in
* @param tag
* The tags to count
* @return The list of nodes found with the specified tag
*/
public static List<Node> getNodes( Node n, String tag ) {
NodeList children;
Node childnode;
List<Node> nodes = new ArrayList<Node>();
if ( n == null ) {
return nodes;
}
children = n.getChildNodes();
for ( int i = 0; i < children.getLength(); i++ ) {
childnode = children.item( i );
if ( childnode.getNodeName().equalsIgnoreCase( tag ) ) {
// <file>
nodes.add( childnode );
}
}
return nodes;
}
/**
* Get node child with a certain subtag set to a certain value
*
* @param n
* The node to search in
* @param tag
* The tag to look for
* @param subtag
* The subtag to look for
* @param subtagvalue
* The value the subtag should have
* @param nr
* The nr of occurance of the value
* @return The node found or null if we couldn't find anything.
*/
public static Node getNodeWithTagValue( Node n, String tag, String subtag, String subtagvalue, int nr ) {
NodeList children;
Node childnode, tagnode;
String value;
int count = 0;
children = n.getChildNodes();
for ( int i = 0; i < children.getLength(); i++ ) {
childnode = children.item( i );
if ( childnode.getNodeName().equalsIgnoreCase( tag ) ) {
// <hop>
tagnode = getSubNode( childnode, subtag );
value = getNodeValue( tagnode );
if ( value.equalsIgnoreCase( subtagvalue ) ) {
if ( count == nr ) {
return childnode;
}
count++;
}
}
}
return null;
}
/**
* Get node child with a certain subtag set to a certain value
*
* @param n
* The node to search in
* @param tag
* The tag to look for
* @param subtag
* The subtag to look for
* @param subtagvalue
* The value the subtag should have
* @param copyNr
* The nr of occurance of the value
* @return The node found or null if we couldn't find anything.
*/
public static Node getNodeWithAttributeValue( Node n, String tag, String attributeName,
String attributeValue ) {
NodeList children;
Node childnode;
children = n.getChildNodes();
for ( int i = 0; i < children.getLength(); i++ ) {
childnode = children.item( i );
if ( childnode.getNodeName().equalsIgnoreCase( tag ) ) {
// <hop>
Node attribute = childnode.getAttributes().getNamedItem( attributeName );
if ( attribute != null && attributeValue.equals( attribute.getTextContent() ) ) {
return childnode;
}
}
}
return null;
}
/**
* Search for a subnode in the node with a certain tag.
*
* @param n
* The node to look in
* @param tag
* The tag to look for
* @return The subnode if the tag was found, or null if nothing was found.
*/
public static Node getSubNode( Node n, String tag ) {
int i;
NodeList children;
Node childnode;
if ( n == null ) {
return null;
}
// Get the childres one by one out of the node,
// compare the tags and return the first found.
//
children = n.getChildNodes();
for ( i = 0; i < children.getLength(); i++ ) {
childnode = children.item( i );
if ( childnode.getNodeName().equalsIgnoreCase( tag ) ) {
return childnode;
}
}
return null;
}
/**
* Search a node for a child of child
*
* @param n
* The node to look in
* @param tag
* The tag to look for in the node
* @param subtag
* The tag to look for in the children of the node
* @return The sub-node found or null if nothing was found.
*/
public static Node getSubNode( Node n, String tag, String subtag ) {
Node t = getSubNode( n, tag );
if ( t != null ) {
return getSubNode( t, subtag );
}
return null;
}
/**
* Get a subnode in a node by nr.<br>
* This method uses caching and assumes you loop over subnodes in sequential order (nr is increasing by 1 each call)
*
* @param n
* The node to look in
* @param tag
* The tag to count
* @param nr
* The position in the node
* @return The subnode found or null in case the position was invalid.
*/
public static Node getSubNodeByNr( Node n, String tag, int nr ) {
return getSubNodeByNr( n, tag, nr, true );
}
/**
* Get a subnode in a node by nr.<br>
* It optially allows you to use caching.<br>
* Caching assumes that you loop over subnodes in sequential order (nr is increasing by 1 each call)
*
* @param n
* The node to look in
* @param tag
* The tag to count
* @param nr
* The position in the node
* @param useCache
* set this to false if you don't want to use caching. For example in cases where you want to loop over
* subnodes of a certain tag in reverse or random order.
* @return The subnode found or null in case the position was invalid.
*/
public static Node getSubNodeByNr( Node n, String tag, int nr, boolean useCache ) {
NodeList children;
Node childnode;
if ( n == null ) {
return null;
}
int count = 0;
// Find the child-nodes of this Node n:
children = n.getChildNodes();
int lastChildNr = -1;
XMLHandlerCacheEntry entry = null;
if ( useCache ) {
entry = new XMLHandlerCacheEntry( n, tag );
lastChildNr = cache.getLastChildNr( entry );
}
if ( lastChildNr < 0 ) {
lastChildNr = 0;
} else {
count = nr; // we assume we found the previous nr-1 at the lastChildNr
lastChildNr++; // we left off at the previouso one, so continue with the next.
}
for ( int i = lastChildNr; i < children.getLength(); i++ ) { // Try all children
childnode = children.item( i );
if ( childnode.getNodeName().equalsIgnoreCase( tag ) ) { // We found the right tag
if ( count == nr ) {
if ( useCache ) {
cache.storeCache( entry, i );
}
return childnode;
}
count++;
}
}
return null;
}
/**
* Find the value entry in a node
*
* @param n
* The node
* @return The value entry as a string
*/
public static String getNodeValue( Node n ) {
if ( n == null ) {
return null;
}
// Find the child-nodes of this Node n:
NodeList children = n.getChildNodes();
for ( int i = 0; i < children.getLength(); i++ ) {
// Try all children
Node childnode = children.item( i );
String retval = childnode.getNodeValue();
if ( retval != null ) { // We found the right value
return retval;
}
}
return null;
}
public static String getTagAttribute( Node node, String attribute ) {
if ( node == null ) {
return null;
}
String retval = null;
NamedNodeMap nnm = node.getAttributes();
if ( nnm != null ) {
Node attr = nnm.getNamedItem( attribute );
if ( attr != null ) {
retval = attr.getNodeValue();
}
}
return retval;
}
/**
* Load a file into an XML document
*
* @param filename
* The filename to load into a document
* @return the Document if all went well, null if an error occurred!
*/
public static Document loadXMLFile( String filename ) throws KettleXMLException {
try {
return loadXMLFile( KettleVFS.getFileObject( filename ) );
} catch ( Exception e ) {
throw new KettleXMLException( e );
}
}
/**
* Load a file into an XML document
*
* @param fileObject
* The fileObject to load into a document
* @return the Document if all went well, null if an error occured!
*/
public static Document loadXMLFile( FileObject fileObject ) throws KettleXMLException {
return loadXMLFile( fileObject, null, false, false );
}
/**
* Load a file into an XML document
*
* @param fileObject
* The fileObject to load into a document
* @param systemID
* Provide a base for resolving relative URIs.
* @param ignoreEntities
* Ignores external entities and returns an empty dummy.
* @param namespaceAware
* support XML namespaces.
* @return the Document if all went well, null if an error occured!
*/
public static Document loadXMLFile( FileObject fileObject, String systemID, boolean ignoreEntities,
boolean namespaceAware ) throws KettleXMLException {
try {
return loadXMLFile( KettleVFS.getInputStream( fileObject ), systemID, ignoreEntities, namespaceAware );
} catch ( IOException e ) {
throw new KettleXMLException( "Unable to read file [" + fileObject.toString() + "]", e );
}
}
/**
* Read in an XML file from the passed input stream and return an XML document
*
* @param inputStream
* The filename input stream to read the document from
* @return the Document if all went well, null if an error occurred!
*/
public static Document loadXMLFile( InputStream inputStream ) throws KettleXMLException {
return loadXMLFile( inputStream, null, false, false );
}
/**
* Load a file into an XML document
*
* @param inputStream
* The stream to load a document from
* @param systemID
* Provide a base for resolving relative URIs.
* @param ignoreEntities
* Ignores external entities and returns an empty dummy.
* @param namespaceAware
* support XML namespaces.
* @return the Document if all went well, null if an error occured!
*/
public static Document loadXMLFile( InputStream inputStream, String systemID, boolean ignoreEntities,
boolean namespaceAware ) throws KettleXMLException {
try {
// Check and open XML document
//
DocumentBuilderFactory dbf = XMLParserFactoryProducer.createSecureDocBuilderFactory();
dbf.setIgnoringComments( true );
dbf.setNamespaceAware( namespaceAware );
DocumentBuilder db = dbf.newDocumentBuilder();
// even dbf.setValidating(false) will the parser NOT prevent from checking the existance of the DTD
// thus we need to give the BaseURI (systemID) below to have a chance to get it
// or return empty dummy documents for all external entities (sources)
//
if ( ignoreEntities ) {
db.setEntityResolver( new DTDIgnoringEntityResolver() );
}
Document doc;
try {
if ( Utils.isEmpty( systemID ) ) {
// Normal parsing
//
doc = db.parse( inputStream );
} else {
// Do extra verifications
//
String systemIDwithEndingSlash = systemID.trim();
// make sure we have an ending slash, otherwise the last part will be ignored
//
if ( !systemIDwithEndingSlash.endsWith( "/" ) && !systemIDwithEndingSlash.endsWith( "\\" ) ) {
systemIDwithEndingSlash = systemIDwithEndingSlash.concat( "/" );
}
doc = db.parse( inputStream, systemIDwithEndingSlash );
}
} catch ( FileNotFoundException ef ) {
throw new KettleXMLException( ef );
} finally {
if ( inputStream != null ) {
inputStream.close();
}
}
return doc;
} catch ( Exception e ) {
throw new KettleXMLException( "Error reading information from input stream", e );
}
}
public static Document loadXMLFile( File resource ) throws KettleXMLException {
try {
return loadXMLFile( resource.toURI().toURL() );
} catch ( MalformedURLException e ) {
throw new KettleXMLException( e );
}
}
/**
* Load a file into an XML document
*
* @param resource
* The resource to load into a document
* @return the Document if all went well, null if an error occured!
*/
public static Document loadXMLFile( URL resource ) throws KettleXMLException {
DocumentBuilderFactory dbf;
DocumentBuilder db;
Document doc;
try {
// Check and open XML document
dbf = XMLParserFactoryProducer.createSecureDocBuilderFactory();
db = dbf.newDocumentBuilder();
InputStream inputStream = resource.openStream();
try {
doc = db.parse( inputStream );
} catch ( IOException ef ) {
throw new KettleXMLException( ef );
} finally {
inputStream.close();
}
return doc;
} catch ( Exception e ) {
throw new KettleXMLException( "Error reading information from resource", e );
}
}
/**
* Calls loadXMLString with deferNodeExpansion = TRUE
*
* @param string
* @return
* @throws KettleXMLException
*/
public static Document loadXMLString( String string ) throws KettleXMLException {
return loadXMLString( string, Boolean.FALSE, Boolean.TRUE );
}
/**
* Loads the XML document in parameter xml and returns the 'tag' entry.
*
* @param xml
* the XML to load
* @param tag
* the node to return
* @return the requested node
* @throws KettleXMLException
* in case there is a problem reading the XML
*/
public static Node loadXMLString( String xml, String tag ) throws KettleXMLException {
Document doc = loadXMLString( xml );
return getSubNode( doc, tag );
}
/**
* Load a String into an XML document
*
* @param string
* The XML text to load into a document
* @param deferNodeExpansion
* true to defer node expansion, false to not defer.
* @return the Document if all went well, null if an error occurred!
*/
public static Document loadXMLString( String string, Boolean namespaceAware, Boolean deferNodeExpansion ) throws KettleXMLException {
DocumentBuilder db = createDocumentBuilder( namespaceAware, deferNodeExpansion );
return loadXMLString( db, string );
}
public static Document loadXMLString( DocumentBuilder db, String string ) throws KettleXMLException {
try {
StringReader stringReader = new java.io.StringReader( string );
InputSource inputSource = new InputSource( stringReader );
Document doc;
try {
doc = db.parse( inputSource );
} catch ( IOException ef ) {
throw new KettleXMLException( "Error parsing XML", ef );
} finally {
stringReader.close();
}
return doc;
} catch ( Exception e ) {
throw new KettleXMLException( "Error reading information from XML string : " + Const.CR + string, e );
}
}
public static DocumentBuilder createDocumentBuilder( boolean namespaceAware, boolean deferNodeExpansion )
throws KettleXMLException {
try {
DocumentBuilderFactory dbf = XMLParserFactoryProducer.createSecureDocBuilderFactory();
dbf.setFeature( "http://apache.org/xml/features/dom/defer-node-expansion", deferNodeExpansion );
dbf.setNamespaceAware( namespaceAware );
return dbf.newDocumentBuilder();
} catch ( ParserConfigurationException e ) {
throw new KettleXMLException( e );
}
}
public static String getString() {
return XMLHandler.class.getName();
}
/**
* Build an XML string for a certain tag String value
*
* @param tag
* The XML tag
* @param val
* The String value of the tag
* @param cr
* true if a carriage return is desired after the ending tag.
* @return The XML String for the tag.
*/
public static String addTagValue( String tag, String val, boolean cr, String... attributes ) {
StringBuilder value = new StringBuilder( "<" );
value.append( Encode.forXml( tag ) );
for ( int i = 0; i < attributes.length; i += 2 ) {
value.append( " " ).append( Encode.forXml( attributes[i] ) ).append( "=\"" ).append(
Encode.forXmlAttribute( attributes[i + 1] ) ).append( "\" " );
}
if ( val != null && val.length() > 0 ) {
value.append( '>' );
value.append( Encode.forXml( val ) );
value.append( "</" );
value.append( Encode.forXml( tag ) );
value.append( '>' );
} else {
value.append( "/>" );
}
if ( cr ) {
value.append( Const.CR );
}
return value.toString();
}
public static void appendReplacedChars( StringBuilder value, String string ) {
value.append( Encode.forXml( string ) );
}
/**
* Build an XML string (including a carriage return) for a certain tag String value
*
* @param tag
* The XML tag
* @param val
* The String value of the tag
* @return The XML String for the tag.
*/
public static String addTagValue( KettleAttributeInterface tag, String val ) {
return addTagValue( tag.getXmlCode(), val );
}
/**
* Build an XML string (including a carriage return) for a certain tag String value
*
* @param tag
* The XML tag
* @param val
* The String value of the tag
* @return The XML String for the tag.
*/
public static String addTagValue( String tag, String val ) {
return addTagValue( tag, val, true );
}
/**
* Build an XML string (including a carriage return) for a certain tag boolean value
*
* @param tag
* The XML tag
* @param bool
* The boolean value of the tag
* @return The XML String for the tag.
*/
public static String addTagValue( KettleAttributeInterface tag, boolean bool ) {
return addTagValue( tag.getXmlCode(), bool );
}
/**
* Build an XML string (including a carriage return) for a certain tag boolean value
*
* @param tag
* The XML tag
* @param bool
* The boolean value of the tag
* @return The XML String for the tag.
*/
public static String addTagValue( String tag, boolean bool ) {
return addTagValue( tag, bool, true );
}
/**
* Build an XML string for a certain tag boolean value
*
* @param tag
* The XML tag
* @param bool
* The boolean value of the tag
* @param cr
* true if a carriage return is desired after the ending tag.
* @return The XML String for the tag.
*/
public static String addTagValue( String tag, boolean bool, boolean cr ) {
return addTagValue( tag, bool ? "Y" : "N", cr );
}
/**
* Build an XML string for a certain tag long integer value
*
* @param tag
* The XML tag
* @param l
* The long integer value of the tag
* @return The XML String for the tag.
*/
public static String addTagValue( String tag, long l ) {
return addTagValue( tag, l, true );
}
/**
* Build an XML string for a certain tag long integer value
*
* @param tag
* The XML tag
* @param l
* The long integer value of the tag
* @param cr
* true if a carriage return is desired after the ending tag.
* @return The XML String for the tag.
*/
public static String addTagValue( String tag, long l, boolean cr ) {
// Tom modified this for performance
// return addTagValue(tag, ""+l, cr);
return addTagValue( tag, String.valueOf( l ), cr );
}
/**
* Build an XML string (with carriage return) for a certain tag integer value
*
* @param tag
* The XML tag
* @param i
* The integer value of the tag
* @return The XML String for the tag.
*/
public static String addTagValue( KettleAttributeInterface tag, int i ) {
return addTagValue( tag.getXmlCode(), i );
}
/**
* Build an XML string (with carriage return) for a certain tag integer value
*
* @param tag
* The XML tag
* @param i
* The integer value of the tag
* @return The XML String for the tag.
*/
public static String addTagValue( String tag, int i ) {
return addTagValue( tag, i, true );
}
/**
* Build an XML string for a certain tag integer value
*
* @param tag
* The XML tag
* @param i
* The integer value of the tag
* @param cr
* true if a carriage return is desired after the ending tag.
* @return The XML String for the tag.
*/
public static String addTagValue( String tag, int i, boolean cr ) {
return addTagValue( tag, String.valueOf( i ), cr );
}
/**
* Build an XML string (with carriage return) for a certain tag double value
*
* @param tag
* The XML tag
* @param d
* The double value of the tag
* @return The XML String for the tag.
*/
public static String addTagValue( String tag, double d ) {
return addTagValue( tag, d, true );
}
/**
* Build an XML string for a certain tag double value
*
* @param tag
* The XML tag
* @param d
* The double value of the tag
* @param cr
* true if a carriage return is desired after the ending tag.
* @return The XML String for the tag.
*/
public static String addTagValue( String tag, double d, boolean cr ) {
return addTagValue( tag, String.valueOf( d ), cr );
}
/**
* Build an XML string (with carriage return) for a certain tag Date value
*
* @param tag
* The XML tag
* @param date
* The Date value of the tag
* @return The XML String for the tag.
*/
public static String addTagValue( String tag, Date date ) {
return addTagValue( tag, date, true );
}
/**
* Build an XML string for a certain tag Date value
*
* @param tag
* The XML tag
* @param date
* The Date value of the tag
* @param cr
* true if a carriage return is desired after the ending tag.
* @return The XML String for the tag.
*/
public static String addTagValue( String tag, Date date, boolean cr ) {
return addTagValue( tag, date2string( date ), cr );
}
/**
* Build an XML string (including a carriage return) for a certain tag BigDecimal value
*
* @param tag
* The XML tag
* @param val
* The BigDecimal value of the tag
* @return The XML String for the tag.
*/
public static String addTagValue( String tag, BigDecimal val ) {
return addTagValue( tag, val, true );
}
/**
* Build an XML string (including a carriage return) for a certain tag BigDecimal value
*
* @param tag
* The XML tag
* @param val
* The BigDecimal value of the tag
*
* @return The XML String for the tag.
*/
public static String addTagValue( String tag, BigDecimal val, boolean cr ) {
return addTagValue( tag, val != null ? val.toString() : (String) null, cr );
}
/**
* Build an XML string (including a carriage return) for a certain tag binary (byte[]) value
*
* @param tag
* The XML tag
* @param val
* The binary value of the tag
* @return The XML String for the tag.
* @throws IOException
* in case there is an Base64 or GZip encoding problem
*/
public static String addTagValue( String tag, byte[] val ) throws IOException {
return addTagValue( tag, val, true );
}
/**
* Build an XML string (including a carriage return) for a certain tag binary (byte[]) value
*
* @param tag
* The XML tag
* @param val
* The binary value of the tag
* @return The XML String for the tag.
* @throws IOException
* in case there is an Base64 or GZip encoding problem
*/
public static String addTagValue( String tag, byte[] val, boolean cr ) throws IOException {
String string;
if ( val == null ) {
string = null;
} else {
string = encodeBinaryData( val );
}
return addTagValue( tag, string, cr );
}
public static String encodeBinaryData( byte[] val ) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
GZIPOutputStream gzos = new GZIPOutputStream( baos );
BufferedOutputStream bos = new BufferedOutputStream( gzos );
bos.write( val );
bos.flush();
bos.close();
return new String( Base64.encodeBase64( baos.toByteArray() ) );
}
/**
* Get all the attributes in a certain node (on the root level)
*
* @param node
* The node to examine
* @return an array of strings containing the names of the attributes.
*/
public static String[] getNodeAttributes( Node node ) {
NamedNodeMap nnm = node.getAttributes();
if ( nnm != null ) {
String[] attributes = new String[nnm.getLength()];
for ( int i = 0; i < nnm.getLength(); i++ ) {
Node attr = nnm.item( i );
attributes[i] = attr.getNodeName();
}
return attributes;
}
return null;
}
public static String[] getNodeElements( Node node ) {
ArrayList<String> elements = new ArrayList<String>(); // List of String
NodeList nodeList = node.getChildNodes();
if ( nodeList == null ) {
return null;
}
for ( int i = 0; i < nodeList.getLength(); i++ ) {
String nodeName = nodeList.item( i ).getNodeName();
if ( elements.indexOf( nodeName ) < 0 ) {
elements.add( nodeName );
}
}
if ( elements.isEmpty() ) {
return null;
}
return elements.toArray( new String[ elements.size() ] );
}
public static Date stringToDate( String dateString ) {
if ( Utils.isEmpty( dateString ) ) {
return null;
}
try {
synchronized ( simpleDateFormat ) {
return simpleDateFormat.parse( dateString );
}
} catch ( ParseException e ) {
return null;
}
}
public static String date2string( Date date ) {
if ( date == null ) {
return null;
}
synchronized ( simpleDateFormat ) {
return simpleDateFormat.format( date );
}
}
public static String timestamp2string( Timestamp timestamp ) {
if ( timestamp == null ) {
return null;
}
synchronized ( simpleTimeStampFormat ) {
return simpleTimeStampFormat.format( timestamp );
}
}
/**
* Convert a XML encoded binary string back to binary format
*
* @param string
* the (Byte64/GZip) encoded string
* @return the decoded binary (byte[]) object
* @throws KettleException
* In case there is a decoding error
*/
public static byte[] stringToBinary( String string ) throws KettleException {
try {
byte[] bytes;
if ( string == null ) {
bytes = new byte[] {};
} else {
bytes = Base64.decodeBase64( string.getBytes() );
}
if ( bytes.length > 0 ) {
ByteArrayInputStream bais = new ByteArrayInputStream( bytes );
GZIPInputStream gzip = new GZIPInputStream( bais );
BufferedInputStream bi = new BufferedInputStream( gzip );
byte[] result = new byte[] {};
byte[] extra = new byte[1000000];
int nrExtra = bi.read( extra );
while ( nrExtra >= 0 ) {
// add it to bytes...
//
int newSize = result.length + nrExtra;
byte[] tmp = new byte[newSize];
for ( int i = 0; i < result.length; i++ ) {
tmp[i] = result[i];
}
for ( int i = 0; i < nrExtra; i++ ) {
tmp[result.length + i] = extra[i];
}
// change the result
result = tmp;
nrExtra = bi.read( extra );
}
bytes = result;
gzip.close();
}
return bytes;
} catch ( Exception e ) {
throw new KettleException( "Error converting string to binary", e );
}
}
public static String buildCDATA( String string ) {
return buildCDATA( new StringBuilder(), string ).toString();
}
public static StringBuilder buildCDATA( StringBuilder builder, String string ) {
return builder.append( "<![CDATA[" ).append( Const.NVL( string, "" ) ).append( "]]>" );
}
public static String openTag( String tag ) {
return openTag( new StringBuilder(), tag ).toString();
}
public static StringBuilder openTag( StringBuilder builder, String tag ) {
return builder.append( '<' ).append( tag ).append( '>' );
}
public static String closeTag( String tag ) {
return closeTag( new StringBuilder(), tag ).toString();
}
public static StringBuilder closeTag( StringBuilder builder, String tag ) {
return builder.append( "</" ).append( tag ).append( '>' );
}
public static String formatNode( Node node ) throws KettleXMLException {
StringWriter sw = new StringWriter();
try {
Transformer t = TransformerFactory.newInstance().newTransformer();
t.setOutputProperty( OutputKeys.OMIT_XML_DECLARATION, "yes" );
t.transform( new DOMSource( node ), new StreamResult( sw ) );
} catch ( Exception e ) {
throw new KettleXMLException( "Unable to format Node as XML", e );
}
return sw.toString();
}
}
/**
* Handle external references and return an empty dummy document.
*
* @author jb
* @since 2007-12-21
*
*/
class DTDIgnoringEntityResolver implements EntityResolver {
@Override
public InputSource resolveEntity( java.lang.String publicID, java.lang.String systemID ) throws IOException {
System.out.println( "Public-ID: " + publicID.toString() );
System.out.println( "System-ID: " + systemID.toString() );
return new InputSource( new ByteArrayInputStream( "<?xml version='1.0' encoding='UTF-8'?>".getBytes() ) );
}
}