org.pentaho.reporting.libraries.xmlns.parser.AbstractReadHandlerFactory.java Source code

Java tutorial

Introduction

Here is the source code for org.pentaho.reporting.libraries.xmlns.parser.AbstractReadHandlerFactory.java

Source

/*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software
* Foundation.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
* or from the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* This program 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.
*
* Copyright (c) 2001 - 2013 Object Refinery Ltd, Hitachi Vantara and Contributors..  All rights reserved.
*/

package org.pentaho.reporting.libraries.xmlns.parser;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.pentaho.reporting.libraries.base.config.Configuration;
import org.pentaho.reporting.libraries.base.util.ObjectUtilities;

import java.util.HashMap;
import java.util.Iterator;

/**
 * The AbstractReadHandlerFactory provides a base implementation for all read-handler factories. A read-handler factory
 * decouples the tag-handlers of a SAX parser and allows to configure alternate parser configuations at runtime,
 * resulting in a more flexible parsing process.
 *
 * @author Thomas Morgner
 */
public abstract class AbstractReadHandlerFactory<T extends XmlReadHandler> {
    /**
     * The TagDefinitionKey is a compund key to lookup handler implementations using a namespace and tagname.
     */
    private static class TagDefinitionKey {
        private String namespace;
        private String tagName;

        /**
         * Creates a new key.
         *
         * @param namespace the namespace (can be null for undefined).
         * @param tagName   the tagname (can be null for undefined).
         */
        private TagDefinitionKey(final String namespace, final String tagName) {
            this.namespace = namespace;
            this.tagName = tagName;
        }

        /**
         * Compares this key for equality with an other object.
         *
         * @param o the other object.
         * @return true, if this key is the same as the given object, false otherwise.
         */
        public boolean equals(final Object o) {
            if (this == o) {
                return true;
            }
            if (o == null || getClass() != o.getClass()) {
                return false;
            }

            final TagDefinitionKey that = (TagDefinitionKey) o;

            if (namespace != null ? !namespace.equals(that.namespace) : that.namespace != null) {
                return false;
            }
            if (tagName != null ? !tagName.equals(that.tagName) : that.tagName != null) {
                return false;
            }

            return true;
        }

        /**
         * Computes the hashcode for this key.
         *
         * @return the hashcode.
         */
        public int hashCode() {
            int result = (namespace != null ? namespace.hashCode() : 0);
            result = 29 * result + (tagName != null ? tagName.hashCode() : 0);
            return result;
        }

        public String toString() {
            final StringBuilder sb = new StringBuilder();
            sb.append("[");
            if (namespace == null) {
                sb.append("<null>");
            } else {
                sb.append(namespace);
            }
            sb.append('\'');
            sb.append("|");
            if (tagName == null) {
                sb.append("<null>");
            } else {
                sb.append(tagName);
            }
            sb.append('\'');
            sb.append(']');
            return sb.toString();
        }
    }

    private static class TagDefinitionValue {
        private String className;
        private boolean legacyOverride;

        private TagDefinitionValue(final String className, final boolean legacyOverride) {
            this.className = className;
            this.legacyOverride = legacyOverride;
        }

        public String getClassName() {
            return className;
        }

        public boolean isLegacyOverride() {
            return legacyOverride;
        }
    }

    private HashMap<TagDefinitionKey, TagDefinitionValue> tagData;
    private String defaultNamespace;

    /**
     * A default-constructor.
     */
    protected AbstractReadHandlerFactory() {
        tagData = new HashMap<TagDefinitionKey, TagDefinitionValue>();
    }

    public void configureGlobal(final Configuration config, final String prefix) {
        final Iterator propertyKeys = config.findPropertyKeys(prefix);
        while (propertyKeys.hasNext()) {
            final String key = (String) propertyKeys.next();
            final String value = config.getConfigProperty(key);
            if (value != null) {
                configure(config, value);
            }
        }
    }

    /**
     * Configures this factory from the given configuration using the specified prefix as filter.
     *
     * @param conf   the configuration.
     * @param prefix the key-prefix.
     * @noinspection ObjectAllocationInLoop as this method configures the factory.
     */
    public void configure(final Configuration conf, final String prefix) {
        final HashMap<String, String> knownNamespaces = new HashMap<String, String>();

        final String nsConfPrefix = prefix + "namespace.";
        final Iterator namespaces = conf.findPropertyKeys(nsConfPrefix);
        while (namespaces.hasNext()) {
            final String key = (String) namespaces.next();
            final String nsPrefix = key.substring(nsConfPrefix.length());
            final String nsUri = conf.getConfigProperty(key);
            knownNamespaces.put(nsPrefix, nsUri);
        }

        final Log legacyWarningLog = LogFactory.getLog(getClass());
        boolean warnedLegacyConfig = false;
        final String legacyDefaultNamespace = knownNamespaces.get(conf.getConfigProperty(prefix + "namespace"));
        if (legacyDefaultNamespace != null) {
            if (warnedLegacyConfig == false) {
                legacyWarningLog.warn("Configured configuration-properties based override for global read-hander. "
                        + "Change your code to use proper module-initializers instead. "
                        + "This method of configuring the parser will go away in the next major version.");
                warnedLegacyConfig = true;
            }
            setDefaultNamespace(legacyDefaultNamespace);
        }

        final String globalDefaultKey = prefix + "default";
        final String globalValue = conf.getConfigProperty(globalDefaultKey);
        if (isValidHandler(globalValue)) {
            this.tagData.put(new TagDefinitionKey(null, null), new TagDefinitionValue(globalValue, true));
            if (warnedLegacyConfig == false) {
                legacyWarningLog.warn("Configured configuration-properties based override for global read-hander. "
                        + "Change your code to use proper module-initializers instead. "
                        + "This method of configuring the parser will go away in the next major version.");
                warnedLegacyConfig = true;
            }
        }

        final String nsDefaultPrefix = prefix + "default.";
        final Iterator defaults = conf.findPropertyKeys(nsDefaultPrefix);
        while (defaults.hasNext()) {
            final String key = (String) defaults.next();
            final String nsPrefix = key.substring(nsDefaultPrefix.length());
            final String nsUri = knownNamespaces.get(nsPrefix);
            if (nsUri == null) {
                continue;
            }

            final String tagData = conf.getConfigProperty(key);
            if (tagData == null) {
                continue;
            }
            if (isValidHandler(tagData)) {
                if (warnedLegacyConfig == false) {
                    legacyWarningLog
                            .warn("Configured configuration-properties based override for global read-hander. "
                                    + "Change your code to use proper module-initializers instead. "
                                    + "This method of configuring the parser will go away in the next major version.");
                    warnedLegacyConfig = true;
                }
                this.tagData.put(new TagDefinitionKey(nsUri, null), new TagDefinitionValue(tagData, true));
            }
        }

        final String nsTagsPrefix = prefix + "tag.";
        final Iterator tags = conf.findPropertyKeys(nsTagsPrefix);
        while (tags.hasNext()) {
            final String key = (String) tags.next();
            final String tagDef = key.substring(nsTagsPrefix.length());
            final String tagData = conf.getConfigProperty(key);
            if (tagData == null) {
                continue;
            }
            if (isValidHandler(tagData) == false) {
                continue;
            }

            if (warnedLegacyConfig == false) {
                legacyWarningLog.warn("Configured configuration-properties based override for global read-hander. "
                        + "Change your code to use proper module-initializers instead. "
                        + "This method of configuring the parser will go away in the next major version.");
                warnedLegacyConfig = true;
            }

            final int delim = tagDef.indexOf('.');
            if (delim == -1) {
                this.tagData.put(new TagDefinitionKey(null, tagDef), new TagDefinitionValue(tagData, true));
            } else {
                final String nsPrefix = tagDef.substring(0, delim);
                final String nsUri = knownNamespaces.get(nsPrefix);
                if (nsUri == null) {
                    continue;
                }

                final String tagName = tagDef.substring(delim + 1);
                this.tagData.put(new TagDefinitionKey(nsUri, tagName), new TagDefinitionValue(tagData, true));
            }
        }
    }

    /**
     * Adds a configuration default for the given namespace to the tag-descriptions. If the namespace URI given here is
     * null, this defines the global default for all namespaces.
     *
     * @param namespaceUri the namespace URI for which a default should be configured.
     * @param hasCData     the default value.
     */
    public void setNamespaceHandler(final String namespaceUri, final Class<? extends T> hasCData) {
        final TagDefinitionKey key = new TagDefinitionKey(namespaceUri, null);
        setValueIfNotDefinedAsLegacy(key, hasCData);
    }

    /**
     * Adds a configuration entry for the given namespace and tag-name to the tag-descriptions.
     *
     * @param namespaceUri the namespace URI for which a default should be configured.
     * @param tagName      the tagname for which the entry should be added.
     * @param hasCData     the default value.
     */
    public void setElementHandler(final String namespaceUri, final String tagName,
            final Class<? extends T> hasCData) {
        if (namespaceUri == null) {
            throw new NullPointerException();
        }
        if (tagName == null) {
            throw new NullPointerException();
        }
        final TagDefinitionKey key = new TagDefinitionKey(namespaceUri, tagName);
        setValueIfNotDefinedAsLegacy(key, hasCData);
    }

    /**
     * Adds a configuration entry for the given namespace and tag-name to the tag-descriptions.
     *
     * @param tagName  the tagname for which the entry should be added.
     * @param hasCData the default value.
     */
    public void setElementHandler(final String tagName, final Class<? extends T> hasCData) {
        if (tagName == null) {
            throw new NullPointerException();
        }
        final TagDefinitionKey key = new TagDefinitionKey(defaultNamespace, tagName);
        setValueIfNotDefinedAsLegacy(key, hasCData);
    }

    private void setValueIfNotDefinedAsLegacy(final TagDefinitionKey key, final Class<? extends T> hasCData) {
        final TagDefinitionValue existingValue = tagData.get(key);
        if (existingValue != null && existingValue.isLegacyOverride()) {
            final Log legacyWarningLog = LogFactory.getLog(getClass());
            legacyWarningLog.debug(
                    "Module-Configuration ignored as a legacy properties-based configuration exists for " + key);
            return;
        }
        tagData.put(key, new TagDefinitionValue(hasCData.getName(), false));
    }

    public void setDefaultNamespace(final String defaultNamespace) {
        this.defaultNamespace = defaultNamespace;
    }

    public String getDefaultNamespace() {
        return defaultNamespace;
    }

    /**
     * Checks, whether the given handler classname can be instantiated and is in fact an object of the required
     * target-type.
     *
     * @param className the classname that should be checked.
     * @return true, if the handler is valid, false otherwise.
     */
    private boolean isValidHandler(final String className) {
        if (className == null) {
            return false;
        }
        final XmlReadHandler o = ObjectUtilities.loadAndInstantiate(className, getClass(), getTargetClass());
        return o != null;
    }

    /**
     * Returns the implementation class for this read-handler factory.
     *
     * @return the implementation class.
     */
    protected abstract Class<T> getTargetClass();

    /**
     * The returned handler can be null, in case no handler is registered.
     *
     * @param namespace the namespace of the xml-tag for which a handler should be returned.
     * @param tagname   the tagname of the xml-tag.
     * @return the instantiated read handler, or null if there is no handler registered.
     */
    public T getHandler(String namespace, final String tagname) {
        if (namespace == null) {
            namespace = defaultNamespace;
        }

        final TagDefinitionKey key = new TagDefinitionKey(namespace, tagname);
        final TagDefinitionValue tagVal = tagData.get(key);
        if (tagVal != null) {
            return ObjectUtilities.loadAndInstantiate(tagVal.getClassName(), getClass(), getTargetClass());
        }

        final TagDefinitionKey defaultKey = new TagDefinitionKey(namespace, null);
        final TagDefinitionValue className = tagData.get(defaultKey);
        if (className != null) {
            return ObjectUtilities.loadAndInstantiate(className.getClassName(), getClass(), getTargetClass());
        }

        final TagDefinitionKey fallbackKey = new TagDefinitionKey(null, null);
        final TagDefinitionValue fallbackName = tagData.get(fallbackKey);
        if (fallbackName == null) {
            return null;
        }
        final T fallbackValue = ObjectUtilities.loadAndInstantiate(fallbackName.getClassName(), getClass(),
                getTargetClass());
        if (fallbackValue != null) {
            return fallbackValue;
        }
        return null;
    }

}