Java tutorial
/*********************************************************************************************************************** * Copyright (c) 2008 empolis GmbH and brox IT Solutions GmbH. All rights reserved. This program and the accompanying * materials are made available under the terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html * * Contributors: brox IT-Solutions GmbH - initial creator **********************************************************************************************************************/ package org.eclipse.smila.utils.xml; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FilenameFilter; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Iterator; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.xerces.dom.DocumentImpl; import org.apache.xerces.impl.Constants; import org.apache.xerces.parsers.DOMParser; import org.apache.xerces.parsers.XMLGrammarPreparser; import org.apache.xerces.util.SymbolTable; import org.apache.xerces.util.XMLGrammarPoolImpl; import org.apache.xerces.xni.grammars.Grammar; import org.apache.xerces.xni.grammars.XMLGrammarDescription; import org.apache.xerces.xni.parser.XMLInputSource; import org.apache.xml.serialize.OutputFormat; import org.apache.xml.serialize.XMLSerializer; import org.osgi.framework.BundleContext; import org.osgi.util.tracker.ServiceTracker; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; import org.xml.sax.SAXException; /** * All <code>parse()</code> methods must be given a wellformatted XML rescource, in which the <b>encoding</b> is set * forth, since none of them recieves such an parameter. If no encoding is specified in the XML recource, UTF-8 is * assumed as set forth by the W3C recommendation. * <p> * For all <code>stream()</code> methods without the encoding-paramter the default encoding UTF-8 is assumed (W3C * Recommendation?). */ public abstract class XMLUtils { /** * initialise the secure random instance. */ private static final java.security.SecureRandom SEEDER = new java.security.SecureRandom(); /** * a larg(ish) prime to use for a symbol table to be shared among potentially man parsers. Start one as close to 2K * (20 times larger than normal) and see what happens... */ private static final int BIG_PRIME = 2039; /** Property identifier: symbol table. */ private static final String SYMBOL_TABLE = Constants.XERCES_PROPERTY_PREFIX + Constants.SYMBOL_TABLE_PROPERTY; /** Property identifier: grammar pool. */ private static final String GRAMMAR_POOL = Constants.XERCES_PROPERTY_PREFIX + Constants.XMLGRAMMAR_POOL_PROPERTY; /** * s_symbolTable. */ private static SymbolTable s_symbolTable; /** * s_grammarPool. */ private static XMLGrammarPoolImpl s_grammarPool; /** Cached per JVM server IP. */ private static String s_hexServerIP; /** * Constructor. */ private XMLUtils() { } /** * clearGrammarCache. */ public static synchronized void clearGrammarCache() { s_symbolTable = null; s_grammarPool = null; } /** * @param xmlBoolean * - * @return boolean */ public static boolean decodeBoolean(String xmlBoolean) { return "true".equals(xmlBoolean) || "yes".equals(xmlBoolean) || "on".equals(xmlBoolean) || "1".equals(xmlBoolean); } public static Date decodeDate(String xmlDate) throws ParseException { final SimpleDateFormat df = new SimpleDateFormat(); df.applyPattern("yyyy-MM-dd"); return df.parse(xmlDate); } public static Date decodeDateTime(String xmlDate) throws ParseException { final SimpleDateFormat df = new SimpleDateFormat(); df.applyPattern("yyyy-MM-dd'T'hh:mm:ss"); return df.parse(xmlDate); } public static int decodeInteger(String xmlInteger) { return new Integer(xmlInteger).intValue(); } public static String encodeBoolean(boolean javaBoolean) { return javaBoolean ? "true" : "false"; } public static String encodeDate(Date javaDate) { final SimpleDateFormat df = new SimpleDateFormat(); df.applyPattern("yyyy-MM-dd"); return df.format(javaDate); } public static String encodeDateTime(Date javaDate) { final SimpleDateFormat df = new SimpleDateFormat(); df.applyPattern("yyyy-MM-dd'T'hh:mm:ss"); return df.format(javaDate); } public static String encodeInteger(int value) { return value + ""; } public static String encodeInteger(Integer value) { return value.intValue() + ""; } /** * A 32 byte GUID generator (Globally Unique ID). These artificial keys SHOULD <strong>NOT </strong> be seen by the * user, not even touched by the DBA but with very rare exceptions, just manipulated by the database and the programs. * * Usage: Add an id field (type java.lang.String) to your EJB, and add setId(XXXUtil.generateGUID(this)); to the * ejbCreate method. * * @return String * @param o * - */ public static final String generateGUID(Object o) { final Log log = LogFactory.getLog(XMLUtils.class); final StringBuffer tmpBuffer = new StringBuffer(16); if (s_hexServerIP == null) { java.net.InetAddress localInetAddress = null; try { // get the inet address localInetAddress = java.net.InetAddress.getLocalHost(); } catch (final java.net.UnknownHostException uhe) { if (log.isErrorEnabled()) { log.error( "ConfigurationUtil: Could not get the local IP address using InetAddress.getLocalHost()!", uhe); } return null; } final byte[] serverIP = localInetAddress.getAddress(); s_hexServerIP = hexFormat(getInt(serverIP), 8); } final String hashcode = hexFormat(System.identityHashCode(o), 8); tmpBuffer.append(s_hexServerIP); tmpBuffer.append(hashcode); final long timeNow = System.currentTimeMillis(); final int timeLow = (int) timeNow & 0xFFFFFFFF; final int node = SEEDER.nextInt(); final StringBuffer guid = new StringBuffer(32); guid.append(hexFormat(timeLow, 8)); guid.append(tmpBuffer.toString()); guid.append(hexFormat(node, 8)); return guid.toString(); } /** * Returns an empty document whose document element is not set! * * The type of the document is by default an XML-Document - I think. * * @return Document */ public static Document getDocument() { // TODO: add DocumentType ? <? xml version=1.0> return new DocumentImpl(); } /** * Creates a document with a new document element with it's name set to the given text. * * @return Document * @param docElement * - */ public static Document getDocument(String docElement) { final Document doc = new DocumentImpl(); final Element el = doc.createElement(docElement); doc.appendChild(el); return doc; } private static int getInt(byte[] bytes) { int i = 0; int j = 24; for (int k = 0; j >= 0; k++) { final int l = bytes[k] & 0xff; i += l << j; j -= 8; } return i; } /** * This method converts a given XML attribute or element name to a local representation by stripping it of its * namespace prefix. * * * @return String * @param xml * - */ public static String getLocalPart(String xml) { final int pos = xml.indexOf(":"); if (pos >= 0) { xml = xml.substring(pos + 1); } return xml; } /** * Returns a parser with set proerties and features as set in the XMLUtilsConfig Object. If any properties or features * are set there that are not supported or the value is wrong the exception is raised here! * * @return DOMParser * @param cfg * - * @throws XMLUtilsException * - */ private static synchronized DOMParser getParser(XMLUtilsConfig cfg) throws XMLUtilsException { final DOMParser parser = new DOMParser(); /* * if (cfg.getValidate() != null && cfg.getValidate().booleanValue() && Version.fVersion.equals("Xerces 1.2.0")) { * * //if version of parser to small throw new XMLUtilsException("Cant perform validation with Xerces 1.2.0"); } */ if (cfg.getIncludeIgnorabelWhitespace() != null && !cfg.getIncludeIgnorabelWhitespace().booleanValue() && (cfg.getValidate() == null || !cfg.getValidate().booleanValue())) { throw new XMLUtilsException("When setting ignorableWhitespace, validate must be true too! " + "To remove all TextNodes containg only WS use method XMLUtils.removeWhitespaceTextNodes(Element)"); } try { if (s_symbolTable != null) { parser.setProperty(SYMBOL_TABLE, s_symbolTable); } if (s_grammarPool != null) { parser.setProperty(GRAMMAR_POOL, s_grammarPool); } final Iterator iter = cfg.getFeatures(); while (iter.hasNext()) { final String feat = (String) iter.next(); parser.setFeature(feat, cfg.getFeatureValue(feat)); } } catch (final SAXException e) { throw new XMLUtilsException("Error while setting feature!", e); } // set features (and properties) -- last not yet supported parser.setErrorHandler(new DOMErrorHandler()); return parser; } private static String hexFormat(int i, int j) { final String s = Integer.toHexString(i); return padHex(s, j) + s; } public static boolean isXML(byte[] message) { boolean retval = false; try { XMLUtils.parse(message, false); retval = true; } catch (final XMLUtilsException e) { ; // nothing to do } return retval; } public static boolean isXML(File file) { boolean retval = false; try { XMLUtils.parse(file, false); retval = true; } catch (final XMLUtilsException e) { ; // nothing to do } return retval; } public static boolean isXML(InputStream is) { boolean retval = false; try { XMLUtils.parse(is, false); retval = true; } catch (final XMLUtilsException e) { ; // nothing to do } return retval; } /** * @param schemaName * Schema name. * @throws XMLUtilsException * Unable to resolve schema. */ public static synchronized void loadSchema(String schemaName, BundleContext context) throws XMLUtilsException { final ServiceTracker st = new ServiceTracker(context, SchemaResolver.class.getName(), null); st.open(); final Object[] oSchemaResolvers = st.getServices(); byte[] schema = null; for (final Object o : oSchemaResolvers) { final SchemaResolver schemaResolver = (SchemaResolver) o; schema = schemaResolver.getSchemaByName(schemaName); if (schema != null) { break; } } if (schema == null) { throw new XMLUtilsException("unable to locate schema [" + schemaName + "]"); } if (schema.length == 0) { throw new XMLUtilsException("unable to load empty schema [" + schemaName + "]"); } loadSchema(schemaName, new ByteArrayInputStream(schema)); } /** * @param schemaName * - * @param schema * - * @throws XMLUtilsException * - */ public static synchronized void loadSchema(String schemaName, InputStream schema) throws XMLUtilsException { if (s_symbolTable == null) { s_symbolTable = new SymbolTable(BIG_PRIME); s_grammarPool = new XMLGrammarPoolImpl(); } final XMLGrammarPreparser preparser = new XMLGrammarPreparser(s_symbolTable); preparser.registerPreparser(XMLGrammarDescription.XML_SCHEMA, null); preparser.setProperty(XMLUtils.GRAMMAR_POOL, s_grammarPool); try { final Grammar g = preparser.preparseGrammar(XMLGrammarDescription.XML_SCHEMA, new XMLInputSource(null, schemaName, null, schema, null)); if (g != null) { ;// remove compiler warning (g is not used) } } catch (final IOException e) { throw new XMLUtilsException("unable to cache schema [" + schemaName + "]", e); } } public static synchronized void loadSchemas(File folder, final String filter, String schemaUrlPrefix) throws XMLUtilsException { try { final File[] schemas = folder.listFiles(new FilenameFilter() { public boolean accept(File dir, String name) { return name.matches(filter); } }); for (int i = 0; i < schemas.length; i++) { FileInputStream fis = null; try { fis = new FileInputStream(schemas[i]); loadSchema(schemaUrlPrefix + schemas[i].getName(), fis); } catch (final IOException e) { throw e; } finally { if (fis != null) { try { fis.close(); } catch (final IOException e) { ; // nothing to do } } } } } catch (final IOException e) { throw new XMLUtilsException("unable to cache schemas", e); } } private static String padHex(String s, int i) { final StringBuffer tmpBuffer = new StringBuffer(); if (s.length() < i) { for (int j = 0; j < i - s.length(); j++) { tmpBuffer.append('0'); } } return tmpBuffer.toString(); } /** * Shorthand for <code>parse(bytes, new XMLUtilsConfig(validate, * true)).</code> * * @return Document * @param bytes * - * @param validate * - * @throws XMLUtilsException * - */ public static Document parse(byte[] bytes, boolean validate) throws XMLUtilsException { return parse(new InputSource(new ByteArrayInputStream(bytes)), new XMLUtilsConfig(validate, true)); } /** * Parses a document from the given byte-array.The applied enconding must be declared within the XML-document-instance * or othersise UTF-8 is assumened according to the W3C recommendation. * * @exception XMLUtilsException * if cgf contains invalid values or parsing results in an error. * * @return Document * @param bytes * - * @param cfg * - * @throws XMLUtilsException * - */ public static Document parse(byte[] bytes, XMLUtilsConfig cfg) throws XMLUtilsException { return parse(new InputSource(new ByteArrayInputStream(bytes)), cfg); } /** * Shorthand for <code>parse(file, new XMLUtilsConfig(validate, * true)).</code> * * @return Document * @param file * - * @param validate * - * @throws XMLUtilsException * - */ public static Document parse(File file, boolean validate) throws XMLUtilsException { return parse(file, new XMLUtilsConfig(validate, true)); } /** * Parses a document from the given File. The applied enconding must be declared within the XML-document-instance or * othersise UTF-8 is assumened according to the W3C recommendation. * * @exception XMLUtilsException * if the File dosn't point to a valid location, cgf contains invalid values, or parsing results in an * error. * * @return Document * @param file * - * @param cfg * - * @throws XMLUtilsException * - */ public static Document parse(File file, XMLUtilsConfig cfg) throws XMLUtilsException { try { return parse(new InputSource(new FileInputStream(file)), cfg); } catch (final Throwable e) { throw new XMLUtilsException("File not found.", e); } } /** * @exception XMLUtilsException * if cgf contains invalid values or parsing results in an error. * @return Document * @param inputSource * - * @param cfg * - * @throws XMLUtilsException * - */ private static Document parse(InputSource inputSource, XMLUtilsConfig cfg) throws XMLUtilsException { final DOMParser parser = getParser(cfg); try { parser.parse(inputSource); return parser.getDocument(); } catch (final Exception e) { throw new XMLUtilsException("Error while parsing!", e); } } /** * Shorthand for <code>parse(is , new XMLUtilsConfig(validate, * true)).</code> * * @return Document * @param is * - * @param validate * - * @throws XMLUtilsException * - */ public static Document parse(InputStream is, boolean validate) throws XMLUtilsException { return parse(is, new XMLUtilsConfig(validate, true)); } /** * Parses a document from the given InputStream.The applied enconding must be declared within the * XML-document-instance or othersise UTF-8 is assumened according to the W3C recommendation. * * @exception XMLUtilsException * if cgf contains invalid values or parsing results in an error. * @return Document * @param cfg * --- * @throws XMLUtilsException * - * @param is * - */ public static Document parse(InputStream is, XMLUtilsConfig cfg) throws XMLUtilsException { return parse(new InputSource(is), cfg); } /* * Code zum Testen der Xerces-Version. Zum Prfen Komentare entfernen. */ /* * static { // checks which version of Xalan is being used // see http://xml.apache.org/xalan-j/faq.html#faq-N100CB * boolean environmentOK = (new org.apache.xalan.xslt.EnvironmentCheck()) .checkEnvironment (new * java.io.PrintWriter(System.err)); } */ /** * Traverserses through a DOM tree starting at the given element and removes all those text-nodes from it that only * contain whitespaces. * * @param parent * - */ public static void removeWhitespaceTextNodes(Element parent) { final NodeList nl = parent.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { final Node child = nl.item(i); if (child.getNodeType() == Node.TEXT_NODE) { if (child.getNodeValue().trim().length() == 0) { parent.removeChild(child); i--; // since the child is removed counting up must be made undone } } else if (child.getNodeType() == Node.ELEMENT_NODE && child.getChildNodes().getLength() > 0) { removeWhitespaceTextNodes((Element) child); } } } public static synchronized void setGrammarCacheLock(boolean lockState) { if (s_grammarPool != null) { if (lockState) { s_grammarPool.lockPool(); } else { s_grammarPool.unlockPool(); } } } /** * Shorthand for <code>stream(el , validate, * "UTF-8")).</code> * * @return byte[] * @param el * - * @param validate * - * @throws XMLUtilsException * - */ public static byte[] stream(Element el, boolean validate) throws XMLUtilsException { return stream(el, validate, "UTF-8", false); } /** * Shorthand for <code>stream(el , validate, * "UTF-8")).</code> * * @return byte[] * @param el * - * @param validate * - * @param preserveSpace * - * @throws XMLUtilsException * - */ public static byte[] stream(Element el, boolean validate, boolean preserveSpace) throws XMLUtilsException { return stream(el, validate, "UTF-8", preserveSpace); } /** * Streams the given DOM-(Sub)tree starting at the given Element into the returned byte-array. * * @param enc * the encoding that shall be applied. The given String must conform to the encodings as set forth by the W3C * and are somewhat different to that of Java. * @param validate * whether the given DOM-(sub)tree shall be validated against an XML-Schema that is referenced in the given * element. * @return byte[] * @param preserveSpace * - * @throws XMLUtilsException * - * @param el * - */ public static byte[] stream(Element el, boolean validate, String enc, boolean preserveSpace) throws XMLUtilsException { /* * this is a fully implemented method not relying on anyother than the parse method, to achieve highest performance! */ final ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] xml = null; try { // create & configure OutputFormat final OutputFormat of = new OutputFormat("XML", enc, true); of.setIndent(2); of.setLineWidth(0); if (preserveSpace) { of.setPreserveSpace(preserveSpace); } // stream final XMLSerializer xs = new XMLSerializer(baos, of); xs.setNamespaces(true); xs.serialize(el); xml = baos.toByteArray(); baos.close(); } catch (final Exception e) { throw new XMLUtilsException("Failed to stream due to: ", e); } // validate the produced document tree try { /* validation is performed here afterwards to save doing things twice */ if (validate) { XMLUtils.parse(xml, validate); } return xml; } catch (final XMLUtilsException e) { throw new XMLUtilsException("The given xml document is not valid!", e); } finally { try { if (baos != null) { baos.close(); } } catch (final IOException e) { ; // nothing to do } } } /** * Streams the given DOM-(Sub)tree starting at the given Element into the given File. * * @param enc * the encoding that shall be applied. The given String must conform to the encodings as set forth by the W3C * and are somewhat different to that of Java. * @param validate * whether the given DOM-(sub)tree shall be validated against an XML-Schema that is referenced in the given * element. * @param file * if it exist it will be overwritten otherwise created. If acces rights are violated an XMLUtilsExcpetion is * thrown. * @param el * - * @throws XMLUtilsException * - */ public static void stream(Element el, boolean validate, String enc, File file) throws XMLUtilsException { FileOutputStream fos = null; try { fos = new FileOutputStream(file); stream(el, validate, enc, fos); fos.close(); } catch (final FileNotFoundException e) { throw new XMLUtilsException("File not found.", e); } catch (final IOException e) { throw new XMLUtilsException("Other file-access exception", e); } finally { try { if (fos != null) { fos.close(); } } catch (final IOException e) { ; // nothing to do } } } /** * Streams the given DOM-(Sub)tree starting at the given Element into the returned byte-array. * * @param enc * the encoding that shall be applied. The given String must conform to the encodings as set forth by the W3C * and are somewhat different to that of Java. * @param validate * whether the given DOM-(sub)tree shall be validated against an XML-Schema that is referenced in the given * element. * @param el * - * @throws XMLUtilsException * - * @param os * - */ public static void stream(Element el, boolean validate, String enc, OutputStream os) throws XMLUtilsException { if (validate) { validate(el); } try { // create & configure OutputFormat final OutputFormat of = new OutputFormat("XML", enc, true); of.setIndent(2); of.setLineWidth(0); // stream final XMLSerializer xs = new XMLSerializer(os, of); xs.setNamespaces(true); xs.serialize(el); } catch (final Exception e) { throw new XMLUtilsException("Failed to stream due to: ", e); } } /** * Validates the given element and it's decendants against the XML-Schema as specified in the given element. When * validation has been succesfull true is returned but an exception if the document isnt valid, since this is in most * cases the desired result anyways. * <p> * Unless someone finds a way to validate a life DOM-Tree against a given schema, the quickest way to do this is to, * write the damn thing into an ByteArrayOutputStream and to read from that again, and while reading it perform a * validation. * * @return boolean * @param el * - * @throws XMLUtilsException * - */ public static boolean validate(Element el) throws XMLUtilsException { /* * this is a fully implemented method not relying on anyother than the parse method, to achieve highest performance! * Encoding US-ASCII is suposed to be the fasted, since it can be expected to have no need of many other letters * than those in that set. (see XERXES FAQ) Also pritty-printing is turned off! */ final ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] xml = null; // stream the (sub-)tree try { // create & configure OutputFormat final OutputFormat of = new OutputFormat("XML", "UTF-8", false); of.setIndent(0); of.setLineWidth(0); // stream final XMLSerializer xs = new XMLSerializer(baos, of); xs.setNamespaces(true); xs.serialize(el); xml = baos.toByteArray(); baos.close(); } catch (final Exception e) { throw new XMLUtilsException("Failed to stream due to: ", e); } finally { try { if (baos != null) { baos.close(); } } catch (final IOException e) { ; // nothing to do } } // validate the produced document tree by (re)parsing it try { XMLUtils.parse(xml, true); return true; } catch (final XMLUtilsException e) { throw new XMLUtilsException("The given XML document is not valid!", e); } } /* * Code zum Testen der Xerces-Version. Zum Prfen Komentare entfernen. */ /* * static { // checks which version of Xalan is being used // see http://xml.apache.org/xalan-j/faq.html#faq-N100CB * boolean environmentOK = (new org.apache.xalan.xslt.EnvironmentCheck()) .checkEnvironment (new * java.io.PrintWriter(System.err)); } */ }