Package org.pentaho.reporting.libraries.xmlns.parser

Source Code of org.pentaho.reporting.libraries.xmlns.parser.AbstractXmlResourceFactory

/**
* =========================================
* LibXML : a free Java layouting library
* =========================================
*
* Project Info:  http://reporting.pentaho.org/libxml/
*
* (C) Copyright 2006-2008, by Object Refinery Ltd, Pentaho Corporation and Contributors.
*
* 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.
*
* [Java is a trademark or registered trademark of Sun Microsystems, Inc.
* in the United States and other countries.]
*
*
* ------------
* AbstractXmlResourceFactory.java
* ------------
*/
package org.pentaho.reporting.libraries.xmlns.parser;

import java.io.IOException;
import java.net.URL;
import java.util.Iterator;
import java.util.Map;
import java.util.ArrayList;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.pentaho.reporting.libraries.resourceloader.CompoundResource;
import org.pentaho.reporting.libraries.resourceloader.FactoryParameterKey;
import org.pentaho.reporting.libraries.resourceloader.Resource;
import org.pentaho.reporting.libraries.resourceloader.ResourceCreationException;
import org.pentaho.reporting.libraries.resourceloader.ResourceData;
import org.pentaho.reporting.libraries.resourceloader.ResourceFactory;
import org.pentaho.reporting.libraries.resourceloader.ResourceKey;
import org.pentaho.reporting.libraries.resourceloader.ResourceKeyCreationException;
import org.pentaho.reporting.libraries.resourceloader.ResourceLoadingException;
import org.pentaho.reporting.libraries.resourceloader.ResourceManager;
import org.pentaho.reporting.libraries.resourceloader.loader.raw.RawResourceData;
import org.pentaho.reporting.libraries.base.config.Configuration;
import org.pentaho.reporting.libraries.base.config.DefaultConfiguration;
import org.pentaho.reporting.libraries.base.util.ObjectUtilities;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;

/**
* A base-class for resource-factories that load their resources from XML files. This class provides a multiplexing
* option. For this, the parser looks at the root-element of the document to be parsed and selects the most suitable
* XmlFactoryModule implementation registered.
*
* @author Thomas Morgner
*/
public abstract class AbstractXmlResourceFactory implements ResourceFactory
{
  private static final Log logger = LogFactory.getLog(AbstractXmlResourceFactory.class);

  /**
   * A key for the content base.
   */
  public static final String CONTENTBASE_KEY = "content-base";
  private static final byte[] EMPTY_DATA = new byte[0];

  private ArrayList modules;
  private SAXParserFactory factory;

  /**
   * Default-Constructor.
   */
  protected AbstractXmlResourceFactory()
  {
    modules = new ArrayList();
  }


  /**
   * Returns a SAX parser.
   *
   * @return a SAXParser.
   * @throws ParserConfigurationException if there is a problem configuring the
   *                                      parser.
   * @throws SAXException                 if there is a problem with the parser
   *                                      initialisation
   */
  protected SAXParser getParser()
      throws ParserConfigurationException, SAXException
  {
    if (this.factory == null)
    {
      this.factory = SAXParserFactory.newInstance();
    }
    return this.factory.newSAXParser();
  }


  /**
   * Configures the xml reader. Use this to set features or properties before
   * the documents get parsed.
   *
   * @param handler the parser implementation that will handle the
   *                SAX-Callbacks.
   * @param reader  the xml reader that should be configured.
   */
  protected void configureReader(final XMLReader reader,
                                 final MultiplexRootElementHandler handler)
  {
    try
    {
      reader.setProperty
          ("http://xml.org/sax/properties/lexical-handler",
              handler.getCommentHandler());
    }
    catch (SAXException se)
    {
      logger.debug("Comments are not supported by this SAX implementation.");
    }

    try
    {
      reader.setFeature("http://xml.org/sax/features/xmlns-uris", true);
    }
    catch (SAXException e)
    {
      handler.setXmlnsUrisNotAvailable(true);
    }
    try
    {
      reader.setFeature("http://xml.org/sax/features/namespaces", true);
      reader.setFeature("http://xml.org/sax/features/namespace-prefixes", true);
    }
    catch (SAXException e)
    {
      logger.warn("No Namespace features will be available. (Yes, this is serious)");
    }
  }

  /**
   * Creates a resource by interpreting the data given in the resource-data object. If additional datastreams need to
   * be parsed, the provided resource manager should be used. This method parses the given resource-data as XML stream.
   *
   * @param manager the resource manager used for all resource loading.
   * @param data    the resource-data from where the binary data is read.
   * @param context the resource context used to resolve relative resource paths.
   * @return the parsed result, never null.
   * @throws ResourceCreationException if the resource could not be parsed due to syntaxctial or logical errors in the data.
   * @throws ResourceLoadingException  if the resource could not be accessed from the physical storage.
   */
  public Resource create(final ResourceManager manager,
                         final ResourceData data,
                         final ResourceKey context)
      throws ResourceCreationException, ResourceLoadingException
  {
    try
    {
      final SAXParser parser = getParser();

      final XMLReader reader = parser.getXMLReader();
      final XmlFactoryModule[] rootHandlers = getModules();
      if (rootHandlers.length == 0)
      {
        throw new ResourceCreationException("There are no root-handlers registered for the factory for type " + getFactoryType());
      }

      final ResourceDataInputSource input = new ResourceDataInputSource(data, manager);

      final ResourceKey contextKey;
      final long version;
      final ResourceKey targetKey = data.getKey();
      if (context == null)
      {
        contextKey = targetKey;
        version = data.getVersion(manager);
      }
      else
      {
        contextKey = context;
        version = -1;
      }

      final MultiplexRootElementHandler handler =
          new MultiplexRootElementHandler(manager, targetKey,
              contextKey, version, rootHandlers);

      final DefaultConfiguration parserConfiguration = handler.getParserConfiguration();
      final URL value = manager.toURL(contextKey);
      if (value != null)
      {
        parserConfiguration.setConfigProperty(CONTENTBASE_KEY, value.toExternalForm());
      }

      configureReader(reader, handler);
      reader.setContentHandler(handler);
      reader.setDTDHandler(handler);
      reader.setEntityResolver(handler.getEntityResolver());
      reader.setErrorHandler(getErrorHandler());

      final Map parameters = targetKey.getFactoryParameters();
      final Iterator it = parameters.keySet().iterator();
      while (it.hasNext())
      {
        final Object o = it.next();
        if (o instanceof FactoryParameterKey)
        {
          final FactoryParameterKey fpk = (FactoryParameterKey) o;
          handler.setHelperObject(fpk.getName(), parameters.get(fpk));
        }
      }

      reader.parse(input);

      final Object createdProduct = finishResult
          (handler.getResult(), manager, data, contextKey);
      handler.getDependencyCollector().add(targetKey, data.getVersion(manager));
      return createResource(targetKey, handler, createdProduct);
    }
    catch (ParserConfigurationException e)
    {
      throw new ResourceCreationException
          ("Unable to initialize the XML-Parser", e);
    }
    catch (SAXException e)
    {
      throw new ResourceCreationException("Unable to parse the document: " + data.getKey(), e);
    }
    catch (IOException e)
    {
      throw new ResourceLoadingException("Unable to read the stream from document: " + data.getKey(), e);
    }
  }

  /**
   * A method to allow to invoke the parsing without accessing the LibLoader layer. The data to be parsed is held in
   * the given InputSource object.
   *
   * @param manager the resource manager used for all resource loading.
   * @param input    the raw-data given as SAX-InputSource.
   * @param context the resource context used to resolve relative resource paths.
   * @param parameters the parse parameters.
   * @return the parsed result, never null.
   * @throws ResourceCreationException if the resource could not be parsed due to syntaxctial or logical errors in the data.
   * @throws ResourceLoadingException  if the resource could not be accessed from the physical storage.
   * @throws ResourceKeyCreationException if creating the context key failed.
   */
  public Object parseDirectly(final ResourceManager manager,
                              final InputSource input,
                              final ResourceKey context,
                              final Map parameters)
      throws ResourceKeyCreationException, ResourceCreationException, ResourceLoadingException
  {
    try
    {
      final SAXParser parser = getParser();

      final XMLReader reader = parser.getXMLReader();
      final XmlFactoryModule[] rootHandlers = getModules();

      final ResourceKey targetKey = manager.createKey(EMPTY_DATA);
      final ResourceKey contextKey;
      if (context == null)
      {
        contextKey = targetKey;
      }
      else
      {
        contextKey = context;
      }

      final MultiplexRootElementHandler handler =
          new MultiplexRootElementHandler(manager, targetKey, contextKey, -1, rootHandlers);

      final DefaultConfiguration parserConfiguration = handler.getParserConfiguration();
      final URL value = manager.toURL(contextKey);
      if (value != null)
      {
        parserConfiguration.setConfigProperty(CONTENTBASE_KEY, value.toExternalForm());
      }

      configureReader(reader, handler);
      reader.setContentHandler(handler);
      reader.setDTDHandler(handler);
      reader.setEntityResolver(handler.getEntityResolver());
      reader.setErrorHandler(getErrorHandler());

      final Iterator it = parameters.keySet().iterator();
      while (it.hasNext())
      {
        final Object o = it.next();
        if (o instanceof FactoryParameterKey)
        {
          final FactoryParameterKey fpk = (FactoryParameterKey) o;
          handler.setHelperObject(fpk.getName(), parameters.get(fpk));
        }
      }

      reader.parse(input);

      return finishResult(handler.getResult(), manager, new RawResourceData(targetKey), contextKey);
    }
    catch (ParserConfigurationException e)
    {
      throw new ResourceCreationException
          ("Unable to initialize the XML-Parser", e);
    }
    catch (SAXException e)
    {
      throw new ResourceCreationException("Unable to parse the document", e);
    }
    catch (IOException e)
    {
      throw new ResourceLoadingException("Unable to read the stream", e);
    }

  }

  /**
   * Returns the registered XmlFactoryModules as array.
   *
   * @return the modules as array.
   */
  private XmlFactoryModule[] getModules()
  {
    return (XmlFactoryModule[]) modules.toArray(new XmlFactoryModule[modules.size()]);
  }

  /**
   * Creates a Resource object for the given product. By default this returns a compound-resource that holds
   * all the key that identify the resources used during the content production.
   *
   * @param targetKey the target key.
   * @param handler the root handler used for the parsing.
   * @param createdProduct the created product.
   * @return the product wrapped into a resource object.
   */
  protected Resource createResource(final ResourceKey targetKey,
                                    final MultiplexRootElementHandler handler,
                                    final Object createdProduct)
  {
    return new CompoundResource
        (targetKey, handler.getDependencyCollector(), createdProduct);
  }

  /**
   * Finishes up the result. This can be used for general clean up and post-parse initializaion of the result.
   * The default implementation does nothing and just returns the object itself.
   *
   * @param res the parsed resource.
   * @param manager the resource manager that was used to load the resource.
   * @param data the data object from where the resource is loaded.
   * @param context the context that resolves relative resource paths.
   * @return the parsed resource.
   * @throws ResourceCreationException if the post initialization fails.
   * @throws ResourceLoadingException if loading external resources failed with an IO error.
   */
  protected Object finishResult(final Object res,
                                final ResourceManager manager,
                                final ResourceData data,
                                final ResourceKey context)
      throws ResourceCreationException, ResourceLoadingException
  {
    return res;
  }

  /**
   * Returns the configuration that should be used to initialize this factory.
   *
   * @return the configuration for initializing the factory.
   */
  protected abstract Configuration getConfiguration();

  /**
   * Loads all XmlFactoryModule-implementations from the given configuration.
   *
   * @see #getConfiguration()
   */
  public void initializeDefaults()
  {
    final String type = getFactoryType().getName();
    final String prefix = ResourceFactory.CONFIG_PREFIX + type;
    final Configuration config = getConfiguration();
    final Iterator itType = config.findPropertyKeys(prefix);
    while (itType.hasNext())
    {
      final String key = (String) itType.next();
      final String modClass = config.getConfigProperty(key);
      final Object maybeFactory = ObjectUtilities.loadAndInstantiate
          (modClass, AbstractXmlResourceFactory.class, XmlFactoryModule.class);
      if (maybeFactory instanceof XmlFactoryModule == false)
      {
        continue;
      }
      registerModule((XmlFactoryModule) maybeFactory);
    }
  }

  /**
   * Registers a factory module for being used during the parsing. If the factory module does not return a
   * result that matches the factory's type, the parsing will always fail.
   *
   * @param factoryModule the factory module.
   * @throws NullPointerException if the module given is null.
   */
  public void registerModule(final XmlFactoryModule factoryModule)
  {
    if (factoryModule == null)
    {
      throw new NullPointerException();
    }
    modules.add(factoryModule);
  }

  /**
   * Returns the XML-Error handler that should be registered with the XML parser. By default, this returns
   * a logger.
   *
   * @return the error handler.
   */
  protected ErrorHandler getErrorHandler()
  {
    return new LoggingErrorHandler();
  }
}
TOP

Related Classes of org.pentaho.reporting.libraries.xmlns.parser.AbstractXmlResourceFactory

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.