Package de.mindcrimeilab.xsanalyzer

Source Code of de.mindcrimeilab.xsanalyzer.XsModelWalker

// $Id: XsModelWalker.java 173 2010-09-08 19:22:07Z agony $
/*
* xsAnalyzer - XML schema analyzing tool. Copyright (C) 2008 Michael Engelhardt
*
* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later
* version.
*
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/**
*
*/
package de.mindcrimeilab.xsanalyzer;

import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.Stack;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.xerces.xs.XSAttributeDeclaration;
import org.apache.xerces.xs.XSAttributeUse;
import org.apache.xerces.xs.XSComplexTypeDefinition;
import org.apache.xerces.xs.XSConstants;
import org.apache.xerces.xs.XSElementDeclaration;
import org.apache.xerces.xs.XSModel;
import org.apache.xerces.xs.XSModelGroup;
import org.apache.xerces.xs.XSNamedMap;
import org.apache.xerces.xs.XSObject;
import org.apache.xerces.xs.XSObjectList;
import org.apache.xerces.xs.XSParticle;
import org.apache.xerces.xs.XSSimpleTypeDefinition;
import org.apache.xerces.xs.XSTerm;
import org.apache.xerces.xs.XSTypeDefinition;

import de.mindcrimeilab.xsanalyzer.util.XSModelHelper;
import de.mindcrimeilab.xsanalyzer.xsext.AnonymousTypeFactory;

/**
* The {@code XsModelWalker} iterates through a XML Schema tree representation. For each node in the tree the walker
* asks the known workers if they are applicable to this type of node and pass the node to further processing to the
* worker instances. Any user of this object might to subscribe to a property change listener to get notified about the
* iteration progress. The change listener will advise the name of the current node.
*
* References:
* <ul>
* <li>[1] http://www.w3.org/Submission/2004/SUBM-xmlschema-api-20040309/xml-schema -api.html</li>
* </ul>
*
* @author Michael Engelhardt<me@mindcrime-ilab.de>
* @author $Author: agony $
* @version $Revision: 173 $
*
*/
public class XsModelWalker {

    /** name of the property change event to subscribe to the walking progress */
    public static final String PC_CURRENT_COMPONENT_NAME = XsModelWalker.class.getName() + ".pc.currentComponentName";

    /** logger instance */
    private static final Log logger = LogFactory.getLog("xsAnalyzerApplicationLogger");

    /** property change listener support */
    private final PropertyChangeSupport changeSupport;

    /** Stack of components which are already visited by this walker */
    private final Stack<XSObject> seenComponents;

    /** List of workes to hand over the nodes for further processing */
    private final List<XsComponentWorker> workerList;

    /** Set of already analyzed components */
    ;

    private final Set<XSObject> analyzedComponents;

    /**
     * ctor() Construct a new {@code XsModelWalker}.
     */
    public XsModelWalker() {
        changeSupport = new PropertyChangeSupport(this);
        seenComponents = new Stack<XSObject>();
        analyzedComponents = new HashSet<XSObject>();
        workerList = new LinkedList<XsComponentWorker>();
    }

    // ------------- PropertyChange Support -----------------------
    /**
     * Adds a property change listener to this instance.
     *
     * @param listener
     * @see java.beans.PropertyChangeSupport#addPropertyChangeListener(java.beans.PropertyChangeListener)
     */
    public void addPropertyChangeListener(PropertyChangeListener listener) {
        changeSupport.addPropertyChangeListener(listener);
    }

    /**
     * Add a property change listener for a specific property to this instance.
     *
     * @param propertyName
     *            property to listen on
     * @param listener
     *            listener instance to add
     * @see java.beans.PropertyChangeSupport#addPropertyChangeListener(java.lang.String,
     *      java.beans.PropertyChangeListener)
     */
    public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
        changeSupport.addPropertyChangeListener(propertyName, listener);
    }

    /**
     * Returns an array of registered property change listeners.
     *
     * @return
     * @see java.beans.PropertyChangeSupport#getPropertyChangeListeners()
     */
    public PropertyChangeListener[] getPropertyChangeListeners() {
        return changeSupport.getPropertyChangeListeners();
    }

    /**
     * Returns an array of registered property change listeners for a specific property.
     *
     * @param propertyName
     * @return
     * @see java.beans.PropertyChangeSupport#getPropertyChangeListeners(java.lang.String)
     */
    public PropertyChangeListener[] getPropertyChangeListeners(String propertyName) {
        return changeSupport.getPropertyChangeListeners(propertyName);
    }

    /**
     * Check if there are any listeners for specific property
     *
     * @param propertyName
     * @return
     * @see java.beans.PropertyChangeSupport#hasListeners(java.lang.String)
     */
    public boolean hasListeners(String propertyName) {
        return changeSupport.hasListeners(propertyName);
    }

    /**
     * Removes the property change listener from the list of bound listeners
     *
     * @param listener
     * @see java.beans.PropertyChangeSupport#removePropertyChangeListener(java.beans.PropertyChangeListener)
     */
    public void removePropertyChangeListener(PropertyChangeListener listener) {
        changeSupport.removePropertyChangeListener(listener);
    }

    /**
     * Remove the property change listener for the specific property from the list of bound listeners for certain
     * property.
     *
     * @param propertyName
     * @param listener
     * @see java.beans.PropertyChangeSupport#removePropertyChangeListener(java.lang.String,
     *      java.beans.PropertyChangeListener)
     */
    public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
        changeSupport.removePropertyChangeListener(propertyName, listener);
    }

    // ------------- Worker Handling-----------------------

    /**
     * Register a new {@code XsComponentWorker} to the {@code XsModelWalker}.
     *
     * @param worker
     *            worker to add
     * @return true if worker was added successfully otherwise false
     */
    public boolean addWorker(XsComponentWorker worker) {
        return workerList.add(worker);
    }

    /**
     * Add a list of {@code XsComponentWorker}s to the {@code XsModleWalker}
     *
     * @param workers
     *            list of workers to add
     * @return true if the workers where add successfully
     */
    public boolean addWorkers(Collection<XsComponentWorker> workers) {
        return workerList.addAll(workers);
    }

    /**
     * Remove a {@code XsComponentWorker} from {@code XsModelWalker}
     *
     * @param worker
     * @return true if the worker was removed successfully, otherwise false
     */
    public boolean removeWorker(XsComponentWorker worker) {
        return workerList.remove(worker);
    }

    /**
     * Removes all {@code XsComponentWorker}s from the {@code XsModelWalker}
     */
    public void clearWorkers() {
        workerList.clear();
    }

    /**
     * Returns an unmodifiable list of all registered {@code XsComponentWorker}s.
     *
     * @return never null.
     */
    public List<XsComponentWorker> getWorkers() {
        return Collections.unmodifiableList(workerList);
    }

    /**
     * Check if the certain {@code XsComponentWorker} is registered to the {@code XsModelWalker}
     *
     * @param worker
     * @return true if the worker is in the list, otherwise false.
     */
    public boolean hasWorker(XsComponentWorker worker) {
        return workerList.contains(worker);
    }

    /**
     * Walk through the given model executing all applicable, registered workers for each node.
     *
     * @param model
     *            model to walk through
     */
    public void walkModel(XSModel model) {
        List<? extends XSObject> globalComponents = initialize(model);

        for (XSObject object : globalComponents) {
            XsModelWalker.logger.debug("Global component [{" + object.getNamespace() + "}:" + object.getName() + "]");
            visitComponent(object, null);
        }
    }

    /**
     * Visit a component.
     *
     * @param object
     * @param parent
     */
    private void visitComponent(XSObject object, XSObject parent) {
        if (seenComponents.contains(object) || analyzedComponents.contains(object)) { return; }
        seenComponents.push(object);

        changeSupport.firePropertyChange(XsModelWalker.PC_CURRENT_COMPONENT_NAME, null, object.getName());

        final XSTypeDefinition typeDefinition;
        switch (object.getType()) {
            case XSConstants.TYPE_DEFINITION:
                XSTypeDefinition type = (XSTypeDefinition) object;
                if (type.getAnonymous()) {
                    XsModelWalker.logger.debug("Handling anonymous type");
                    type = AnonymousTypeFactory.getProxy(type);
                }
                typeDefinition = onVisitTypeDefinition(type, parent);
                break;
            case XSConstants.ELEMENT_DECLARATION:
                XSElementDeclaration elementDeclaration = (XSElementDeclaration) object;
                typeDefinition = onVisitElementDeclaration(elementDeclaration, parent);

                break;
            case XSConstants.ATTRIBUTE_DECLARATION:
                throw new RuntimeException("Not implemented yet - XSConstants.ATTRIBUTE_DECLARATION !");
            case XSConstants.NOTATION_DECLARATION:
                throw new RuntimeException("Not implemented yet - XSConstants.NOTATION_DECLARATION !");
            case XSConstants.MODEL_GROUP_DEFINITION:
                throw new RuntimeException("Not implemented yet - XSConstants.MODEL_GROUP_DEFINITION !");
            default:
                XsModelWalker.logger.info("Unknown component type [" + object.getType() + "]");
                typeDefinition = null;
                break;
        }

        if (null != typeDefinition) {
            // iterate in depth...
            XsModelWalker.logger.debug("==> of base type [{" + typeDefinition.getNamespace() + "}:" + typeDefinition.getName() + "]");
            visitComponent(typeDefinition, object);
        }

        seenComponents.pop();
        analyzedComponents.add(object);
    }

    private XSTypeDefinition onVisitTypeDefinition(XSTypeDefinition type, XSObject parent) {
        final XSTypeDefinition baseType;
        switch (type.getTypeCategory()) {
            case XSTypeDefinition.COMPLEX_TYPE:
                XsModelWalker.logger.debug("==> Found complex type definition");
                XSComplexTypeDefinition ctypedef = (XSComplexTypeDefinition) type;
                baseType = onVisitComplexTypeDefinition(ctypedef, parent);

                break;
            case XSTypeDefinition.SIMPLE_TYPE:
                XsModelWalker.logger.debug("==> Found simple type definition");
                XSSimpleTypeDefinition stypedef = (XSSimpleTypeDefinition) type;
                baseType = onVisitSimpleTypeDefinition(stypedef, parent);
                break;
            default:
                baseType = null;
                break;
        }

        // both may contain attribute definitions

        // simple types may return null - depending on type definition
        // (list|atomic...)
        return baseType;
    }

    /**
     *
     * @param stypedef
     *
     */
    private XSTypeDefinition onVisitSimpleTypeDefinition(XSSimpleTypeDefinition stypedef, XSObject parent) {
        executeWorker(stypedef, parent);

        // TODO handle embedded components (union, list)?
        switch (stypedef.getVariety()) {
            case XSSimpleTypeDefinition.VARIETY_ABSENT:
                // anySimpleType
                XsModelWalker.logger.debug("==> Found simple type variety absent");
                break;
            case XSSimpleTypeDefinition.VARIETY_ATOMIC:
                // atomic (restriction)
                XsModelWalker.logger.debug("==> Found simple type variety atomic");
                break;
            case XSSimpleTypeDefinition.VARIETY_LIST:
                // list
                XsModelWalker.logger.debug("==> Found simple type variety list");
                XSSimpleTypeDefinition itemType = stypedef.getItemType();
                visitComponent(itemType, stypedef);
                break;
            case XSSimpleTypeDefinition.VARIETY_UNION:
                // union
                XsModelWalker.logger.debug("==> Found simple type variety union");
                XSObjectList unionedTypes = stypedef.getMemberTypes();
                if (null != unionedTypes) {
                    for (int i = 0; i < unionedTypes.getLength(); ++i) {
                        visitComponent(unionedTypes.item(i), stypedef);
                    }
                }
                break;
            default:
                XsModelWalker.logger.warn("-------------- UNKNOWN SIMPLE TYPE VARIETY OF TYPE [" + stypedef.getVariety() + "]");
                throw new RuntimeException("Unexpected branch selection - Not implemented");
        }

        return XSModelHelper.getBaseType(stypedef);
    }

    private XSTypeDefinition onVisitElementDeclaration(XSElementDeclaration elementDeclaration, XSObject parent) {
        executeWorker(elementDeclaration, parent);
        XSTypeDefinition typedef = elementDeclaration.getTypeDefinition();
        return typedef;
    }

    private XSTypeDefinition onVisitComplexTypeDefinition(XSComplexTypeDefinition ctypedef, XSObject parent) {
        executeWorker(ctypedef, parent);
        // handle attributes
        XSObjectList attributes = ctypedef.getAttributeUses();
        onVisitAttributes(attributes, ctypedef);

        XSParticle particle = ctypedef.getParticle();
        if (null != particle) {
            XSTerm term = particle.getTerm();
            // according to [1]#Interface-XSParticle this should be either a
            // model group, an element or a wildcard declaration
            onVisitTerm(ctypedef, term);
        }

        return ctypedef.getBaseType();
    }

    /**
     * @param ctypedef
     * @param term
     */
    private void onVisitTerm(XSComplexTypeDefinition ctypedef, XSTerm term) {
        short termType = term.getType();

        switch (termType) {
            case XSConstants.MODEL_GROUP:
                XsModelWalker.logger.debug("==> found model group");
                XSModelGroup group = (XSModelGroup) term;
                XSObjectList list = group.getParticles();
                for (int i = 0; i < list.getLength(); ++i) {
                    XSParticle part = (XSParticle) list.item(i);
                    XSTerm item = part.getTerm();
                    XsModelWalker.logger.debug("==--> embedded [{" + item.getNamespace() + "}:" + item.getName() + "]");
                    onVisitTerm(ctypedef, item);
                }
                break;
            case XSConstants.ELEMENT_DECLARATION:
                XsModelWalker.logger.debug("==> found element declaration");
                visitComponent(term, ctypedef);
                break;
            case XSConstants.WILDCARD:
                XsModelWalker.logger.debug("==> found wildcard");
                // ignore wildcards, no usable content - almost everything is
                // useable..
                break;
            default:
                XsModelWalker.logger.warn("-------------- UNKNOWN PARTICLE TYPE OF TYPE [" + termType + "]");
                throw new RuntimeException("Unexpected branch selection - Not implemented");
        }
    }

    private void onVisitAttributes(XSObjectList attributeUsesList, XSObject parent) {
        for (int i = 0; i < attributeUsesList.getLength(); ++i) {
            XSAttributeUse xsAttribute = (XSAttributeUse) attributeUsesList.item(i);
            // attribute declaration contains interesting properties of the
            // attribute
            XSAttributeDeclaration xsAttributeType = xsAttribute.getAttrDeclaration();

            XsModelWalker.logger.debug("--> Attribute [{" + xsAttributeType.getNamespace() + "}:" + xsAttributeType.getName() + "]");

            executeWorker(xsAttributeType, parent);

            // handle
            /*
             * TODO check if we really need this...
             *
             * A {scope} of global identifies attribute declarations available for use in complex type definitions
             * throughout the schema. Locally scoped declarations are available for use only within the complex type
             * definition identified by the {scope} property. This property is ·absent· in the case of declarations
             * within attribute group definitions: their scope will be determined when they are used in the construction
             * of complex type definitions.
             */
            XSComplexTypeDefinition ctypedef = xsAttributeType.getEnclosingCTDefinition();
            if (null != ctypedef) {
                visitComponent(ctypedef, parent);
            }

            visitComponent(xsAttributeType.getTypeDefinition(), parent);
        }

    }

    private List<? extends XSObject> initialize(XSModel model) {
        List<? extends XSObject> global = new LinkedList<XSObject>();

        /*
         * Add globally defined schema components to list. This list will be the starting point for schema iteration.
         *
         * Trying to add components in an optimized way assuming that most schemas will have a number of simple types
         * which are derived from basic xsd types.
         *
         * Complex types will be extend or restrict simple types.
         *
         * Elements will be use complex types as well as simple types.
         */
        XSNamedMap typesMap = model.getComponents(XSConstants.TYPE_DEFINITION);
        XSModelHelper.addComponents(typesMap, global);

        // Global element definitions
        XSNamedMap elementsMap = model.getComponents(XSConstants.ELEMENT_DECLARATION);

        XSModelHelper.addComponents(elementsMap, global);

        return global;
    }

    private void executeWorker(XSObject object, XSObject parent) {
        for (XsComponentWorker worker : workerList) {
            if (worker.isSupported(object)) {
                worker.execute(object, parent);
            }
        }
    }
}
TOP

Related Classes of de.mindcrimeilab.xsanalyzer.XsModelWalker

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.