All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.metawidget.inspector.xsd.XmlSchemaInspector Maven / Gradle / Ivy

The newest version!
// Metawidget
//
// This file is dual licensed under both the LGPL
// (http://www.gnu.org/licenses/lgpl-2.1.html) and the EPL
// (http://www.eclipse.org/org/documents/epl-v10.php). As a
// recipient of Metawidget, you may choose to receive it under either
// the LGPL or the EPL.
//
// Commercial licenses are also available. See http://metawidget.org
// for details.

package org.metawidget.inspector.xsd;

import static org.metawidget.inspector.InspectionResultConstants.*;

import java.util.List;
import java.util.Map;

import org.metawidget.inspector.iface.InspectorException;
import org.metawidget.inspector.impl.BaseXmlInspector;
import org.metawidget.util.CollectionUtils;
import org.metawidget.util.XmlUtils;
import org.w3c.dom.Element;

/**
 * Inspector to look for metadata in XML Schema (XSD) files.
 * 

* If * XmlSchemaInspector is used for a Java environment, consider using it in conjunction with XmlSchemaToJavaTypeMappingProcessor * . For returning results to JavaScript environments, consider * JavaToJavaScriptTypeMappingProcessor and XmlUtils.elementToJson. * * @author Richard Kennard */ public class XmlSchemaInspector extends BaseXmlInspector { // // Private statics // private static final String COMPLEX_TYPE = "complexType"; private static final String SIMPLE_TYPE = "simpleType"; private static final String COMPLEX_CONTENT = "complexContent"; private static final String SIMPLE_CONTENT = "simpleContent"; private static final String REF = "ref"; private static final String BASE = "base"; private static final String ALL = "all"; private static final String SEQUENCE = "sequence"; private static final String CHOICE = "choice"; private static final String EXTENSION = "extension"; private static final String RESTRICTION = "restriction"; private static final String ENUMERATION = "enumeration"; private static final String VALUE = "value"; // // Constructor // public XmlSchemaInspector( XmlSchemaInspectorConfig config ) { super( config ); } // // Protected methods // /** * Overridden to search by name=, not type=. */ @Override protected String getTopLevelTypeAttribute() { return NAME; } /** * Overridden to introduce a reference attribute ref=. */ @Override protected String getReferenceAttribute() { return REF; } /** * Overridden because, with XML Schema, the named children are often nested a couple of levels * deep under the top-level element. For example * xs:element/xs:complexType/xs:sequence. */ @Override protected Element traverseFromTopLevelTypeToNamedChildren( Element topLevel ) { // 'simpleType' is a top-level element if ( SIMPLE_TYPE.equals( XmlUtils.getLocalName( topLevel ) ) ) { return topLevel; } // 'complexType' needs to be traversed into Element complexType; if ( COMPLEX_TYPE.equals( XmlUtils.getLocalName( topLevel ) ) ) { complexType = topLevel; } else { // The most usual case is an 'element' containing a 'complexType' which needs to be // traversed into complexType = XmlUtils.getFirstChildElement( topLevel ); if ( complexType == null ) { // If no 'complexType' child, perhaps top-level 'element' has a @type... if ( !topLevel.hasAttribute( TYPE ) ) { return null; } // ...if so, start over with this new type complexType = XmlUtils.getChildWithAttributeValue( topLevel.getOwnerDocument().getDocumentElement(), NAME, topLevel.getAttribute( TYPE ) ); if ( complexType == null ) { return null; } } // 'simpleType' is a top-level element, so stop here if ( SIMPLE_TYPE.equals( XmlUtils.getLocalName( complexType ) ) ) { return topLevel; } // Should be at a 'complexType' by now if ( !COMPLEX_TYPE.equals( XmlUtils.getLocalName( complexType ) ) ) { throw InspectorException.newException( "Unexpected child node '" + XmlUtils.getLocalName( complexType ) + "'" ); } } // Within 'complexType', 'sequence' needs to be traversed into Element sequence = XmlUtils.getFirstChildElement( complexType ); if ( sequence == null ) { return null; } // Skip over 'annotation' (if any) String sequenceLocalName = XmlUtils.getLocalName( sequence ); if ( "annotation".equals( sequenceLocalName ) ) { sequence = XmlUtils.getNextSiblingElement( sequence ); sequenceLocalName = XmlUtils.getLocalName( sequence ); } if ( CHOICE.equals( sequenceLocalName )) { return sequence; } // Within 'simpleContent', 'extension' needs to be traversed into if ( SIMPLE_CONTENT.equals( sequenceLocalName )) { Element extension = XmlUtils.getChildNamed( sequence, EXTENSION ); if ( extension != null ) { return extension; } return sequence; } // Within 'sequence', 'choice' needs to be traversed into if ( SEQUENCE.equals( sequenceLocalName )) { Element choice = XmlUtils.getChildNamed( sequence, CHOICE ); if ( choice != null ) { return choice; } return sequence; } if ( !ALL.equals( sequenceLocalName ) && !COMPLEX_CONTENT.equals( sequenceLocalName ) && !"attributeGroup".equals( sequenceLocalName ) ) { throw InspectorException.newException( "Unexpected child node '" + sequenceLocalName + "' inside " + XmlUtils.nodeToString( complexType, true ) ); } return sequence; } @Override protected void inspectTraits( Element toInspect, Element toAddTo ) { Element toInspectToUse = toInspect; // Simple type (inherit @type attribute) if ( SIMPLE_TYPE.equals( XmlUtils.getLocalName( toInspectToUse ) ) ) { Map attributes = CollectionUtils.newHashMap(); inspectRestriction( toInspectToUse, attributes ); XmlUtils.setMapAsAttributes( toAddTo, attributes ); return; } // Simple content (inherit @type attribute) if ( SIMPLE_CONTENT.equals( XmlUtils.getLocalName( toInspectToUse ) ) ) { Map attributes = CollectionUtils.newHashMap(); inspectExtension( toInspectToUse, attributes ); inspectRestriction( toInspectToUse, attributes ); XmlUtils.setMapAsAttributes( toAddTo, attributes ); return; } // Complex content (inherit @type attribute) if ( COMPLEX_CONTENT.equals( XmlUtils.getLocalName( toInspectToUse ) ) ) { toInspectToUse = XmlUtils.getChildNamed( toInspectToUse, EXTENSION ); if ( toInspectToUse == null ) { throw InspectorException.newException( "Expected " + COMPLEX_CONTENT + " to have an " + EXTENSION ); } String base = toInspectToUse.getAttribute( BASE ); if ( base == null ) { throw InspectorException.newException( "Expected " + EXTENSION + " to have a " + BASE ); } // Look up the base element... Element baseElement = XmlUtils.getChildWithAttributeValue( toInspectToUse.getOwnerDocument().getDocumentElement(), getTopLevelTypeAttribute(), base ); Element baseSequence = XmlUtils.getFirstChildElement( baseElement ); if ( COMPLEX_CONTENT.equals( XmlUtils.getLocalName( baseSequence ) ) ) { inspectTraits( baseSequence, toAddTo ); return; } if ( "anyAttribute".equals( XmlUtils.getLocalName( baseSequence ) ) ) { return; } if ( !SEQUENCE.equals( XmlUtils.getLocalName( baseSequence ) ) ) { throw InspectorException.newException( "Unexpected child node '" + XmlUtils.getLocalName( baseSequence ) + "'" ); } inspectTraits( baseSequence, toAddTo ); // ...then continue with our own sequence toInspectToUse = XmlUtils.getFirstChildElement( toInspectToUse ); if ( !SEQUENCE.equals( XmlUtils.getLocalName( toInspectToUse ) ) ) { throw InspectorException.newException( "Unexpected child node '" + XmlUtils.getLocalName( toInspectToUse ) + "'" ); } } super.inspectTraits( toInspectToUse, toAddTo ); } @Override protected Map inspectProperty( Element toInspect ) { Map attributes = CollectionUtils.newHashMap(); Element toInspectToUse = toInspect; while ( true ) { if ( toInspectToUse == null ) { return null; } // All bets are off for xs:any if ( "any".equals( XmlUtils.getLocalName( toInspectToUse ) ) ) { return null; } // Redirect to ref (if any) if ( toInspectToUse.hasAttribute( REF ) ) { String name = toInspectToUse.getAttribute( REF ); if ( !attributes.containsKey( NAME ) ) { attributes.put( NAME, name ); } toInspectToUse = XmlUtils.getChildWithAttributeValue( toInspectToUse.getOwnerDocument().getDocumentElement(), getTopLevelTypeAttribute(), name ); continue; } // Redirect to type (if any) if ( !toInspectToUse.hasChildNodes() && toInspectToUse.hasAttribute( TYPE ) ) { // Current toInspectToUse may have minOccurs, etc. inspectElement( toInspectToUse, attributes ); if ( !attributes.containsKey( NAME ) ) { attributes.put( NAME, toInspectToUse.getAttribute( NAME ) ); } Element typeToUse = XmlUtils.getChildWithAttributeValue( toInspectToUse.getOwnerDocument().getDocumentElement(), getTopLevelTypeAttribute(), toInspectToUse.getAttribute( TYPE ) ); if ( typeToUse == null ) { break; } toInspectToUse = typeToUse; continue; } // Complex type may just contain a simple content if ( COMPLEX_TYPE.equals( XmlUtils.getLocalName( toInspectToUse ) ) ) { Element simpleContent = XmlUtils.getChildNamed( toInspectToUse, SIMPLE_CONTENT ); if ( simpleContent != null ) { inspectExtension( simpleContent, attributes ); inspectRestriction( simpleContent, attributes ); } return attributes; } // Normal node if ( !attributes.containsKey( NAME ) && toInspectToUse.hasAttribute( NAME ) ) { attributes.put( NAME, toInspectToUse.getAttribute( NAME ) ); } break; } inspectElement( toInspectToUse, attributes ); return attributes; } // // Private methods // private void inspectElement( Element element, Map attributes ) { // Type if ( element.hasAttribute( TYPE ) ) { attributes.put( TYPE, element.getAttribute( TYPE ) ); } else { // Type inferred from nested complexType or simpleType Element complexType = XmlUtils.getChildNamed( element, COMPLEX_TYPE ); if ( complexType != null ) { Element simpleContent = XmlUtils.getChildNamed( complexType, SIMPLE_CONTENT ); if ( simpleContent != null ) { inspectExtension( simpleContent, attributes ); inspectRestriction( simpleContent, attributes ); } } else if ( SIMPLE_TYPE.equals( XmlUtils.getLocalName( element ) ) ) { inspectRestriction( element, attributes ); } else { Element simpleType = XmlUtils.getChildNamed( element, SIMPLE_TYPE ); if ( simpleType != null ) { inspectRestriction( simpleType, attributes ); } } } // Required String notNull = element.getAttribute( "minOccurs" ); if ( !"".equals( notNull ) && Integer.parseInt( notNull ) > 0 ) { attributes.put( REQUIRED, TRUE ); } } private void inspectExtension( Element parent, Map attributes ) { Element extension = XmlUtils.getChildNamed( parent, EXTENSION ); if ( extension == null ) { return; } if ( extension.hasAttribute( BASE ) ) { attributes.put( TYPE, extension.getAttribute( BASE ) ); } } private void inspectRestriction( Element parent, Map attributes ) { Element restriction = XmlUtils.getChildNamed( parent, RESTRICTION ); if ( restriction == null ) { return; } if ( restriction.hasAttribute( BASE ) ) { attributes.put( TYPE, restriction.getAttribute( BASE ) ); } // Minimum/maximum length Element minLength = XmlUtils.getChildNamed( restriction, "minLength" ); if ( minLength != null ) { attributes.put( MINIMUM_LENGTH, minLength.getAttribute( VALUE ) ); } Element maxLength = XmlUtils.getChildNamed( restriction, "maxLength" ); if ( maxLength != null ) { attributes.put( MAXIMUM_LENGTH, maxLength.getAttribute( VALUE ) ); } // Minimum/maximum value Element minInclusive = XmlUtils.getChildNamed( restriction, "minInclusive" ); if ( minInclusive != null ) { attributes.put( MINIMUM_VALUE, minInclusive.getAttribute( VALUE ) ); } Element maxInclusive = XmlUtils.getChildNamed( restriction, "maxInclusive" ); if ( maxInclusive != null ) { attributes.put( MAXIMUM_VALUE, maxInclusive.getAttribute( VALUE ) ); } // Fraction digits Element fractionDigits = XmlUtils.getChildNamed( restriction, "fractionDigits" ); if ( fractionDigits != null ) { attributes.put( MAXIMUM_FRACTIONAL_DIGITS, fractionDigits.getAttribute( VALUE ) ); } // Lookup Element enumeration = XmlUtils.getChildNamed( restriction, ENUMERATION ); if ( enumeration != null ) { List lookup = CollectionUtils.newArrayList(); while ( enumeration != null ) { lookup.add( enumeration.getAttribute( VALUE ) ); enumeration = XmlUtils.getSiblingNamed( enumeration, ENUMERATION ); } attributes.put( LOOKUP, CollectionUtils.toString( lookup ) ); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy