/* * Copyright (c) 2006-2007, Dennis M. Sosnoski. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the * following conditions are met: * * Redistributions of source code must retain the above copyright notice, this list of conditions and the following * disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the * following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of * JiBX nor the names of its contributors may be used to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.jibx.schema; import org.apache.log4j.Level; import org.apache.log4j.Logger; import org.jibx.schema.elements.AllElement; import org.jibx.schema.elements.AnnotatedBase; import org.jibx.schema.elements.AnnotationElement; import org.jibx.schema.elements.AnyAttributeElement; import org.jibx.schema.elements.AnyElement; import org.jibx.schema.elements.AppInfoElement; import org.jibx.schema.elements.AttributeElement; import org.jibx.schema.elements.AttributeGroupElement; import org.jibx.schema.elements.AttributeGroupRefElement; import org.jibx.schema.elements.ChoiceElement; import org.jibx.schema.elements.ComplexContentElement; import org.jibx.schema.elements.ComplexExtensionElement; import org.jibx.schema.elements.ComplexRestrictionElement; import org.jibx.schema.elements.ComplexTypeElement; import org.jibx.schema.elements.DocumentationElement; import org.jibx.schema.elements.ElementElement; import org.jibx.schema.elements.FacetElement; import org.jibx.schema.elements.GroupElement; import org.jibx.schema.elements.GroupRefElement; import org.jibx.schema.elements.ImportElement; import org.jibx.schema.elements.IncludeElement; import org.jibx.schema.elements.ListElement; import org.jibx.schema.elements.NotationElement; import org.jibx.schema.elements.OpenAttrBase; import org.jibx.schema.elements.RedefineElement; import org.jibx.schema.elements.SchemaBase; import org.jibx.schema.elements.SchemaElement; import org.jibx.schema.elements.SchemaLocationBase; import org.jibx.schema.elements.SequenceElement; import org.jibx.schema.elements.SimpleContentElement; import org.jibx.schema.elements.SimpleExtensionElement; import org.jibx.schema.elements.SimpleRestrictionElement; import org.jibx.schema.elements.SimpleTypeElement; import org.jibx.schema.elements.UnionElement; /** * Handles walking the tree structure of schema model. This traverses the structure defined by the nesting of elements * and schema references in the XML representation. * * @author Dennis M. Sosnoski */ public class TreeWalker { /** Logger for class. */ private static final Logger s_logger = Logger.getLogger(TreeWalker.class.getName()); /** Selector for elements to be skipped when walking tree (null if unused). */ private final ISkipElements m_skipSet; /** Listener for entering and exiting referenced schemas. (null if unused). */ private final ISchemaListener m_schemaListener; /** * Constructor. * * @param skip selector for elements to be skipped (null if none skipped) * @param listen schema reference listener (null if none) */ public TreeWalker(ISkipElements skip, ISchemaListener listen) { m_skipSet = skip; m_schemaListener = listen; } /** * Control the logging level for this class. Since the generated logs at debug level can become huge, this gives a * way for external code to provide granular control over the logging. * * @param level * @return prior level */ public static Level setLogging(Level level) { Level prior = s_logger.getLevel(); s_logger.setLevel(level); return prior; } /** * Walk entire schema model. * * @param schema root element of schema to be traversed * @param visitor target visitor for element notifications */ public void walkSchema(SchemaElement schema, SchemaVisitor visitor) { if (schema != null) { if (m_schemaListener == null || m_schemaListener.enterSchema(schema)) { walkElement(schema, visitor); if (m_schemaListener != null) { m_schemaListener.exitSchema(); } } } } /** * Walk schema model element tree. This recursively traverses the schema model tree rooted in the supplied element, * including the element itself, notifying the visitor of each element visited during the traversal. * * @param root node of tree to be toured * @param visitor target visitor for element notifications */ public void walkElement(SchemaBase root, SchemaVisitor visitor) { // check for fatal error on element if (m_skipSet != null && m_skipSet.isSkipped(root)) { if (s_logger.isDebugEnabled() && root instanceof OpenAttrBase) { s_logger.debug("Skipping node " + SchemaUtils.componentPath((OpenAttrBase)root)); } return; } // visit the actual root of tree boolean expand = false; if (s_logger.isDebugEnabled() && root instanceof OpenAttrBase) { s_logger.debug("Entering node " + SchemaUtils.componentPath((OpenAttrBase)root)); } switch (root.type()) { case SchemaBase.ALL_TYPE: expand = visitor.visit((AllElement)root); break; case SchemaBase.ANNOTATION_TYPE: expand = visitor.visit((AnnotationElement)root); break; case SchemaBase.ANY_TYPE: expand = visitor.visit((AnyElement)root); break; case SchemaBase.ANYATTRIBUTE_TYPE: expand = visitor.visit((AnyAttributeElement)root); break; case SchemaBase.APPINFO_TYPE: expand = visitor.visit((AppInfoElement)root); break; case SchemaBase.ATTRIBUTE_TYPE: expand = visitor.visit((AttributeElement)root); break; case SchemaBase.ATTRIBUTEGROUP_TYPE: if (root instanceof AttributeGroupElement) { expand = visitor.visit((AttributeGroupElement)root); } else { expand = visitor.visit((AttributeGroupRefElement)root); } break; case SchemaBase.CHOICE_TYPE: expand = visitor.visit((ChoiceElement)root); break; case SchemaBase.COMPLEXCONTENT_TYPE: expand = visitor.visit((ComplexContentElement)root); break; case SchemaBase.COMPLEXTYPE_TYPE: expand = visitor.visit((ComplexTypeElement)root); break; case SchemaBase.DOCUMENTATION_TYPE: expand = visitor.visit((DocumentationElement)root); break; case SchemaBase.ELEMENT_TYPE: expand = visitor.visit((ElementElement)root); break; case SchemaBase.EXTENSION_TYPE: if (root instanceof ComplexExtensionElement) { expand = visitor.visit((ComplexExtensionElement)root); } else { expand = visitor.visit((SimpleExtensionElement)root); } break; case SchemaBase.FIELD_TYPE: // expand = visitor.visit((FieldElement)root); break; case SchemaBase.GROUP_TYPE: if (root instanceof GroupElement) { expand = visitor.visit((GroupElement)root); } else { expand = visitor.visit((GroupRefElement)root); } break; case SchemaBase.IMPORT_TYPE: expand = visitor.visit((ImportElement)root); break; case SchemaBase.INCLUDE_TYPE: expand = visitor.visit((IncludeElement)root); break; case SchemaBase.KEY_TYPE: // expand = visitor.visit((KeyElement)root); break; case SchemaBase.KEYREF_TYPE: // expand = visitor.visit((KeyrefElement)root); break; case SchemaBase.LIST_TYPE: expand = visitor.visit((ListElement)root); break; case SchemaBase.NOTATION_TYPE: expand = visitor.visit((NotationElement)root); break; case SchemaBase.REDEFINE_TYPE: expand = visitor.visit((RedefineElement)root); break; case SchemaBase.RESTRICTION_TYPE: if (root instanceof SimpleRestrictionElement) { expand = visitor.visit((SimpleRestrictionElement)root); } else { expand = visitor.visit((ComplexRestrictionElement)root); } break; case SchemaBase.SCHEMA_TYPE: expand = visitor.visit((SchemaElement)root); break; case SchemaBase.SELECTOR_TYPE: // expand = visitor.visit((SelectorElement)root); break; case SchemaBase.SEQUENCE_TYPE: expand = visitor.visit((SequenceElement)root); break; case SchemaBase.SIMPLECONTENT_TYPE: expand = visitor.visit((SimpleContentElement)root); break; case SchemaBase.SIMPLETYPE_TYPE: expand = visitor.visit((SimpleTypeElement)root); break; case SchemaBase.UNION_TYPE: expand = visitor.visit((UnionElement)root); break; case SchemaBase.UNIQUE_TYPE: // expand = visitor.visit((UniqueElement)root); break; case SchemaBase.ENUMERATION_TYPE: case SchemaBase.FRACTIONDIGITS_TYPE: case SchemaBase.LENGTH_TYPE: case SchemaBase.MAXEXCLUSIVE_TYPE: case SchemaBase.MAXINCLUSIVE_TYPE: case SchemaBase.MAXLENGTH_TYPE: case SchemaBase.MINEXCLUSIVE_TYPE: case SchemaBase.MININCLUSIVE_TYPE: case SchemaBase.MINLENGTH_TYPE: case SchemaBase.PATTERN_TYPE: case SchemaBase.TOTALDIGITS_TYPE: case SchemaBase.WHITESPACE_TYPE: expand = visitor.visit((FacetElement)root); break; default: throw new IllegalStateException("Internal error: unknown element type"); } // check for expansion needed if (expand && (m_skipSet == null || !m_skipSet.isSkipped(root))) { walkChildren(root, visitor); } // exit the actual root of tree switch (root.type()) { case SchemaBase.ALL_TYPE: visitor.exit((AllElement)root); break; case SchemaBase.ANNOTATION_TYPE: visitor.exit((AnnotationElement)root); break; case SchemaBase.ANY_TYPE: visitor.exit((AnyElement)root); break; case SchemaBase.ANYATTRIBUTE_TYPE: visitor.exit((AnyAttributeElement)root); break; case SchemaBase.APPINFO_TYPE: visitor.exit((AppInfoElement)root); break; case SchemaBase.ATTRIBUTE_TYPE: visitor.exit((AttributeElement)root); break; case SchemaBase.ATTRIBUTEGROUP_TYPE: if (root instanceof AttributeGroupElement) { visitor.exit((AttributeGroupElement)root); } else { visitor.exit((AttributeGroupRefElement)root); } break; case SchemaBase.CHOICE_TYPE: visitor.exit((ChoiceElement)root); break; case SchemaBase.COMPLEXCONTENT_TYPE: visitor.exit((ComplexContentElement)root); break; case SchemaBase.COMPLEXTYPE_TYPE: visitor.exit((ComplexTypeElement)root); break; case SchemaBase.DOCUMENTATION_TYPE: visitor.exit((DocumentationElement)root); break; case SchemaBase.ELEMENT_TYPE: visitor.exit((ElementElement)root); break; case SchemaBase.EXTENSION_TYPE: if (root instanceof ComplexExtensionElement) { visitor.exit((ComplexExtensionElement)root); } else { visitor.exit((SimpleExtensionElement)root); } break; case SchemaBase.FIELD_TYPE: // visitor.exit((FieldElement)root); break; case SchemaBase.GROUP_TYPE: if (root instanceof GroupElement) { visitor.exit((GroupElement)root); } else { visitor.exit((GroupRefElement)root); } break; case SchemaBase.IMPORT_TYPE: visitor.exit((ImportElement)root); break; case SchemaBase.INCLUDE_TYPE: visitor.exit((IncludeElement)root); break; case SchemaBase.KEY_TYPE: // visitor.exit((KeyElement)root); break; case SchemaBase.KEYREF_TYPE: // visitor.exit((KeyrefElement)root); break; case SchemaBase.LIST_TYPE: visitor.exit((ListElement)root); break; case SchemaBase.NOTATION_TYPE: visitor.exit((NotationElement)root); break; case SchemaBase.REDEFINE_TYPE: visitor.exit((RedefineElement)root); break; case SchemaBase.RESTRICTION_TYPE: if (root instanceof SimpleRestrictionElement) { visitor.exit((SimpleRestrictionElement)root); } else { visitor.exit((ComplexRestrictionElement)root); } break; case SchemaBase.SCHEMA_TYPE: visitor.exit((SchemaElement)root); break; case SchemaBase.SELECTOR_TYPE: // visitor.exit((SelectorElement)root); break; case SchemaBase.SEQUENCE_TYPE: visitor.exit((SequenceElement)root); break; case SchemaBase.SIMPLECONTENT_TYPE: visitor.exit((SimpleContentElement)root); break; case SchemaBase.SIMPLETYPE_TYPE: visitor.exit((SimpleTypeElement)root); break; case SchemaBase.UNION_TYPE: visitor.exit((UnionElement)root); break; case SchemaBase.UNIQUE_TYPE: // visitor.exit((UniqueElement)root); break; case SchemaBase.ENUMERATION_TYPE: case SchemaBase.FRACTIONDIGITS_TYPE: case SchemaBase.LENGTH_TYPE: case SchemaBase.MAXEXCLUSIVE_TYPE: case SchemaBase.MAXINCLUSIVE_TYPE: case SchemaBase.MAXLENGTH_TYPE: case SchemaBase.MINEXCLUSIVE_TYPE: case SchemaBase.MININCLUSIVE_TYPE: case SchemaBase.MINLENGTH_TYPE: case SchemaBase.PATTERN_TYPE: case SchemaBase.TOTALDIGITS_TYPE: case SchemaBase.WHITESPACE_TYPE: visitor.exit((FacetElement)root); break; default: break; } if (s_logger.isDebugEnabled() && root instanceof OpenAttrBase) { s_logger.debug("Left node " + SchemaUtils.componentPath((OpenAttrBase)root)); } } /** * Walk the descendants of a root element. This recursively traverses the schema model tree rooted in the supplied * element, excluding the element itself, notifying the visitor of each element visited during the traversal. * * @param root node of tree to be toured * @param visitor target visitor for element notifications */ public void walkChildren(SchemaBase root, SchemaVisitor visitor) { // first expand the annotation, if present if (root instanceof AnnotatedBase) { AnnotationElement anno = ((AnnotatedBase)root).getAnnotation(); if (anno != null) { walkElement(anno, visitor); } } // perform special handling for element expansion if (root instanceof SchemaLocationBase) { // references just delegate to the included schema element SchemaElement schema = ((SchemaLocationBase)root).getReferencedSchema(); System.out.println(" checking referenced schema " + schema.getResolver().getName()); if (schema != null && (m_schemaListener == null || m_schemaListener.enterSchema(schema))) { walkElement(schema, visitor); if (m_schemaListener != null) { m_schemaListener.exitSchema(); } } } // handle normal element expansion if (root instanceof OpenAttrBase) { OpenAttrBase parent = (OpenAttrBase)root; int count = parent.getChildCount(); for (int i = 0; i < count; i++) { walkElement(parent.getChild(i), visitor); } } } }