com.microsoft.tfs.util.xml.JAXPUtils.java Source code

Java tutorial

Introduction

Here is the source code for com.microsoft.tfs.util.xml.JAXPUtils.java

Source

// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See License.txt in the repository root.

package com.microsoft.tfs.util.xml;

import java.io.File;
import java.io.InputStream;
import java.text.MessageFormat;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.FactoryConfigurationError;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.xml.sax.InputSource;

/**
 * {@link JAXPUtils} contains static utility methods for creating JAXP objects.
 * No caching of any of these objects is done by this class.
 */
public class JAXPUtils {
    private static final Log log = LogFactory.getLog(JAXPUtils.class);

    private static final String JAXP_SCHEMA_LANGUAGE = "http://java.sun.com/xml/jaxp/properties/schemaLanguage"; //$NON-NLS-1$
    private static final String W3C_XML_SCHEMA = "http://www.w3.org/2001/XMLSchema"; //$NON-NLS-1$
    private static final String JAXP_SCHEMA_SOURCE = "http://java.sun.com/xml/jaxp/properties/schemaSource"; //$NON-NLS-1$

    /**
     * A convenience method to create a new {@link DocumentBuilderFactory}. The
     * factory is configured to be namespace aware (
     * {@link DocumentBuilderFactory#setNamespaceAware(boolean)} is called with
     * <code>true</code>).
     *
     * @throws XMLException
     *         if a new factory can't be created
     *
     * @return a new {@link DocumentBuilderFactory} (never <code>null</code>)
     */
    public static DocumentBuilderFactory newDocumentBuilderFactory() {
        return newDocumentBuilderFactory(null);
    }

    /**
     * A convenience method to create a new {@link DocumentBuilderFactory}. If
     * the given {@link ClassLoader} is not <code>null</code>, it is temporarily
     * set as the calling thread's context classloader while calling into JAXP
     * to get a {@link DocumentBuilderFactory} instance. The factory is
     * configured to be namespace aware (
     * {@link DocumentBuilderFactory#setNamespaceAware(boolean)} is called with
     * <code>true</code>).
     *
     * @throws XMLException
     *         if a new factory can't be created
     *
     * @param contextClassloader
     *        the context classloader or <code>null</code> if none
     * @return a new {@link DocumentBuilderFactory} (never <code>null</code>)
     */
    public static DocumentBuilderFactory newDocumentBuilderFactory(final ClassLoader contextClassloader) {
        final boolean setTCL = contextClassloader != null;
        ClassLoader currentTCL = null;

        if (setTCL) {
            currentTCL = Thread.currentThread().getContextClassLoader();
            Thread.currentThread().setContextClassLoader(contextClassloader);
        }

        try {
            final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

            if (log.isTraceEnabled()) {
                final String messageFormat = "Created a new DocumentBuilderFactory: {0}(loaded from: {1})"; //$NON-NLS-1$
                final String message = MessageFormat.format(messageFormat, factory.getClass().getName(),
                        factory.getClass().getClassLoader());
                log.trace(message);
            }

            factory.setNamespaceAware(true);

            return factory;
        } catch (final FactoryConfigurationError e) {
            throw new XMLException(e);
        } finally {
            if (setTCL) {
                Thread.currentThread().setContextClassLoader(currentTCL);
            }
        }
    }

    /**
     * A convenience method to create a new {@link DocumentBuilder}. The new
     * {@link DocumentBuilder} is not configured in any way by this method. To
     * create the {@link DocumentBuilder}, a new {@link DocumentBuilderFactory}
     * is created by calling {@link #newDocumentBuilderFactory()}.
     *
     * @throws XMLException
     *         if a new {@link DocumentBuilder} can't be created
     *
     * @return a new {@link DocumentBuilder} (never <code>null</code>)
     */
    public static DocumentBuilder newDocumentBuilder() {
        return newDocumentBuilder(null);
    }

    /**
     * A convenience method to create a new {@link DocumentBuilder} using the
     * given {@link DocumentBuilderFactory}. The new {@link DocumentBuilder} is
     * not configured in any way by this method.
     *
     * @throws XMLException
     *         if a new {@link DocumentBuilder} can't be created
     *
     * @param factory
     *        the {@link DocumentBuilderFactory} to use or <code>null</code> to
     *        create a new factory using the
     *        {@link #newDocumentBuilderFactory()} method
     * @return a new {@link DocumentBuilder} created from the given
     *         {@link DocumentBuilderFactory} (never <code>null</code>)
     */
    public static DocumentBuilder newDocumentBuilder(DocumentBuilderFactory factory) {
        if (factory == null) {
            factory = newDocumentBuilderFactory();
        }

        try {
            final DocumentBuilder builder = factory.newDocumentBuilder();

            if (log.isTraceEnabled()) {
                final String messageFormat = "Created a new DocumentBuilder: {0} (loaded from: {1}) (factory: {2})"; //$NON-NLS-1$
                final String message = MessageFormat.format(messageFormat, builder.getClass().getName(),
                        builder.getClass().getClassLoader(), factory.getClass().getName());
                log.trace(message);
            }

            return builder;
        } catch (final ParserConfigurationException e) {
            throw new XMLException(e);
        }
    }

    /**
     * A convenience method to create a new {@link TransformerFactory}. The
     * factory is not configured in any way by this method.
     *
     * @throws XMLException
     *         if a new {@link TransformerFactory} can't be created
     *
     * @return a new {@link TransformerFactory} (never <code>null</code>)
     */
    public static TransformerFactory newTransformerFactory() {
        return newTransformerFactory(null);
    }

    /**
     * A convenience method to create a new {@link TransformerFactory}. If the
     * given {@link ClassLoader} is not <code>null</code>, it is temporarily set
     * as the calling thread's context classloader while calling into JAXP to
     * get a {@link TransformerFactory} instance. The factory is not configured
     * in any way by this method.
     *
     * @throws XMLException
     *         if a new {@link TransformerFactory} can't be created
     *
     * @param contextClassloader
     *        the context classloader or <code>null</code> if none
     * @return a new {@link TransformerFactory} (never <code>null</code>)
     */
    public static TransformerFactory newTransformerFactory(final ClassLoader contextClassloader) {
        final boolean setTCL = contextClassloader != null;
        ClassLoader currentTCL = null;

        if (setTCL) {
            currentTCL = Thread.currentThread().getContextClassLoader();
            Thread.currentThread().setContextClassLoader(contextClassloader);
        }

        try {
            final TransformerFactory factory = TransformerFactory.newInstance();

            if (log.isTraceEnabled()) {
                final String messageFormat = "Created a new TransformerFactory: {0} (loaded from: {1})"; //$NON-NLS-1$
                final String message = MessageFormat.format(messageFormat, factory.getClass().getName(),
                        factory.getClass().getClassLoader());
                log.trace(message);
            }

            return factory;
        } catch (final TransformerFactoryConfigurationError e) {
            throw new XMLException(e);
        } finally {
            if (setTCL) {
                Thread.currentThread().setContextClassLoader(currentTCL);
            }
        }
    }

    /**
     * A convenience method to create a new {@link Transformer}. The new
     * {@link Transformer} is not configured in any way by this method. To
     * create the {@link Transformer}, a new {@link TransformerFactory} is
     * created by calling {@link #newTransformerFactory()}.
     *
     * @throws XMLException
     *         if a new {@link Transformer} can't be created
     *
     * @return a new {@link Transformer} (never <code>null</code>)
     */
    public static Transformer newTransformer() {
        return newTransformer(null);
    }

    /**
     * A convenience method to create a new {@link Transformer} using the given
     * {@link TransformerFactory}. The new {@link Transformer} is not configured
     * in any way by this method.
     *
     * @throws XMLException
     *         if a new {@link Transformer} can't be created
     *
     * @param factory
     *        the {@link TransformerFactory} to use or <code>null</code> to
     *        create a new factory using the {@link #newTransformerFactory()}
     *        method
     * @return a new {@link Transformer} created from the given
     *         {@link TransformerFactory} (never <code>null</code>)
     */
    public static Transformer newTransformer(TransformerFactory factory) {
        if (factory == null) {
            factory = newTransformerFactory();
        }

        try {
            final Transformer transformer = factory.newTransformer();

            if (log.isTraceEnabled()) {
                final String messageFormat = "Created a new Transformer: {0} (loaded from: {1}) (factory: {2})"; //$NON-NLS-1$
                final String message = MessageFormat.format(messageFormat, transformer.getClass().getName(),
                        transformer.getClass().getClassLoader(), factory.getClass().getName());
                log.trace(message);
            }

            return transformer;
        } catch (final TransformerConfigurationException e) {
            throw new XMLException(e);
        }
    }

    /**
     * <p>
     * Creates a new (or configures an existing) {@link DocumentBuilderFactory}
     * that will perform XML Schema validation when parsing. This method is
     * called before parsing to obtain a configured
     * {@link DocumentBuilderFactory} that produces {@link DocumentBuilder}s
     * that will be used for XML Schema for validation.
     * </p>
     *
     * <p>
     * The supplied <code>schemaSource</code> object must be one of the
     * following:
     * <ul>
     * <li>A {@link String} that points to the URI of the schema</li>
     *
     * <li>An {@link InputStream} with the schema contents (will not be closed
     * by this method)</li>
     *
     * <li>A SAX {@link InputSource} that indicates the schema</li>
     *
     * <li>A {@link File} that indicates the schema</li>
     *
     * <li>An array of objects, each one of which is one of the above</li>
     * </ul>
     * </p>
     *
     * @throws XMLException
     *         if the {@link DocumentBuilderFactory} can't be created or
     *         properly configured
     *
     * @param schemaSource
     *        the schema source as described above
     * @return a configured {@link DocumentBuilderFactory} (never
     *         <code>null</code>)
     */
    public static DocumentBuilderFactory newDocumentBuilderFactoryForXSValidation(final Object schemaSource) {
        return newDocumentBuilderFactoryForXSValidation(null, schemaSource);
    }

    /**
     * <p>
     * Creates a new (or configures an existing) {@link DocumentBuilderFactory}
     * that will perform XML Schema validation when parsing. This method is
     * called before parsing to obtain a configured
     * {@link DocumentBuilderFactory} that produces {@link DocumentBuilder}s
     * that will be used for XML Schema for validation.
     * </p>
     *
     * <p>
     * The supplied <code>schemaSource</code> object must be one of the
     * following:
     * <ul>
     * <li>A {@link String} that points to the URI of the schema</li>
     *
     * <li>An {@link InputStream} with the schema contents (will not be closed
     * by this method)</li>
     *
     * <li>A SAX {@link InputSource} that indicates the schema</li>
     *
     * <li>A {@link File} that indicates the schema</li>
     *
     * <li>An array of objects, each one of which is one of the above</li>
     * </ul>
     * </p>
     *
     * @throws XMLException
     *         if the {@link DocumentBuilderFactory} can't be created or
     *         properly configured
     *
     * @param factory
     *        the {@link DocumentBuilderFactory} to configure, or
     *        <code>null</code> to create a {@link DocumentBuilderFactory} using
     *        the {@link #newDocumentBuilderFactory()} method
     * @param schemaSource
     *        the schema source as described above
     * @return a configured {@link DocumentBuilderFactory} (never
     *         <code>null</code>)
     */
    public static DocumentBuilderFactory newDocumentBuilderFactoryForXSValidation(DocumentBuilderFactory factory,
            final Object schemaSource) {
        if (factory == null) {
            factory = newDocumentBuilderFactory();
        }

        factory.setNamespaceAware(true);
        factory.setValidating(true);

        try {
            factory.setAttribute(JAXP_SCHEMA_LANGUAGE, W3C_XML_SCHEMA);
        } catch (final IllegalArgumentException e) {
            final String messageFormat = "The DocumentBuilderFactory [{0}] loaded from ClassLoader [{1}] does not support JAXP 1.2"; //$NON-NLS-1$
            final String message = MessageFormat.format(messageFormat, factory.getClass().getName(),
                    factory.getClass().getClassLoader());
            throw new XMLException(message, e);
        }

        if (schemaSource != null) {
            factory.setAttribute(JAXP_SCHEMA_SOURCE, schemaSource);
        }

        return factory;
    }
}