/* * @(#)$Id: ElementDeclState.java,v 1.31 2002/09/13 15:08:40 kk122374 Exp $ * * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. * * This software is the proprietary information of Sun Microsystems, Inc. * Use is subject to license terms. * */ package com.sun.msv.reader.xmlschema; import com.sun.msv.datatype.xsd.XSDatatype; import com.sun.msv.datatype.xsd.BooleanType; import com.sun.msv.grammar.Expression; import com.sun.msv.grammar.AttributeExp; import com.sun.msv.grammar.ReferenceExp; import com.sun.msv.grammar.SimpleNameClass; import com.sun.msv.grammar.trex.ElementPattern; import com.sun.msv.grammar.xmlschema.ElementDeclExp; import com.sun.msv.grammar.xmlschema.ComplexTypeExp; import com.sun.msv.grammar.xmlschema.XMLSchemaSchema; import com.sun.msv.grammar.xmlschema.XMLSchemaTypeExp; import com.sun.msv.grammar.xmlschema.IdentityConstraint; import com.sun.msv.util.StartTagInfo; import com.sun.msv.util.StringPair; import com.sun.msv.reader.GrammarReader; import com.sun.msv.reader.State; import com.sun.msv.reader.IgnoreState; import com.sun.msv.reader.ExpressionWithChildState; import org.xml.sax.Locator; import org.relaxng.datatype.Datatype; import java.util.StringTokenizer; import java.util.Vector; /** * used to parse <element > element without ref attribute. * * this state uses ExpressionWithChildState to collect content model * of this element declaration. * * @author <a href="mailto:kohsuke.kawaguchi@eng.sun.com">Kohsuke KAWAGUCHI</a> */ public class ElementDeclState extends ExpressionWithChildState { protected State createChildState( StartTagInfo tag ) { final XMLSchemaReader reader = (XMLSchemaReader)this.reader; // type declaration is allowed only when we haven't seen type definition. if( super.exp==null ) { if( tag.localName.equals("simpleType") ) return reader.sfactory.simpleType(this,tag); if( tag.localName.equals("complexType") ) return reader.sfactory.complexTypeDecl(this,tag); } // unique/key/keyref are ignored. if( tag.localName.equals("unique") ) return reader.sfactory.unique(this,tag); if( tag.localName.equals("key") ) return reader.sfactory.key(this,tag); if( tag.localName.equals("keyref") ) return reader.sfactory.keyref(this,tag); return null; } protected Expression initialExpression() { // if <element> element has type attribute, then // it shall be used as content type. String typeQName = startTag.getAttribute("type"); if( typeQName==null ) return null; return resolveTypeRef(typeQName); } /** * If this element declaration has @type, then this method * is called to resolve it. * * Since the type refered to may not be processed yet, * a late binding is needed here. */ protected Expression resolveTypeRef( final String typeQName ) { final XMLSchemaReader reader = (XMLSchemaReader)this.reader; // TODO: shall I memorize this as a backward reference? // reader.backwardReference.memorizeLink(???); // symbol may not be defined at this moment. // so just return an empty ReferenceExp and back-patch the actual definition later. final ReferenceExp ref = new ReferenceExp("elementType("+typeQName+")"); final String[] s = reader.splitQName(typeQName); if(s==null) { reader.reportError( reader.ERR_UNDECLARED_PREFIX, typeQName ); ref.exp = Expression.nullSet; // recover by setting a dummy definition. return ref; } reader.addBackPatchJob( new GrammarReader.BackPatch(){ public State getOwnerState() { return ElementDeclState.this; } public void patch() { Expression e=null; if( reader.isSchemaNamespace(s[0]) ) // datatypes of XML Schema part 2 e = reader.resolveBuiltinSimpleType(s[1]); if(e==null) { XMLSchemaSchema g = reader.getOrCreateSchema(s[0]/*uri*/); e = g.simpleTypes.get(s[1]/*local name*/); if(e==null) e = g.complexTypes.get(s[1]); if(e==null ) { // both simpleType and complexType are undefined. reader.reportError( reader.ERR_UNDEFINED_ELEMENTTYPE, typeQName ); e = Expression.nullSet; // recover by dummy definition. } } ref.exp = e; } }); return ref; } protected Expression castExpression( Expression halfCastedExpression, Expression newChildExpression ) { if( halfCastedExpression!=null ) // assertion failed: // createChildState shouldn't allow parsing child <simpleType> // if one is already present. throw new Error(); return newChildExpression; } protected Expression defaultExpression() { if( startTag.containsAttribute("substitutionGroup") ) reader.reportError( XMLSchemaReader.ERR_UNIMPLEMENTED_FEATURE, "omitting type attribute in <element> element with substitutionGroup attribute"); // recover by assuming ur-type. // if no content model is given, then this element type is ur-type. // TODO: confirm it. final XMLSchemaReader reader = (XMLSchemaReader)this.reader; reader.reportWarning( reader.WRN_IMPLICIT_URTYPE_FOR_ELEMENT, null ); return reader.complexUrType; } protected Expression annealExpression(Expression contentType) { final XMLSchemaReader reader = (XMLSchemaReader)this.reader; String name = startTag.getAttribute("name"); if( name==null ) { reader.reportError( reader.ERR_MISSING_ATTRIBUTE, "element", "name" ); // recover by abandoning this element. return Expression.nullSet; } String targetNamespace; if( isGlobal() ) // TODO: form attribute is prohibited at toplevel. targetNamespace = reader.currentSchema.targetNamespace; else // in local attribute declaration, // targetNamespace is affected by @form and schema's @attributeFormDefault. targetNamespace = ((XMLSchemaReader)reader).resolveNamespaceOfElementDecl( startTag.getAttribute("form") ); // TODO: better way to handle the "fixed" attribtue. String fixed = startTag.getAttribute("fixed"); if( fixed!=null ) // TODO: is this 'fixed' value should be added through enumeration facet? // TODO: check if content model is a simpleType. contentType = reader.pool.createValue( com.sun.msv.datatype.xsd.TokenType.theInstance, new StringPair("","token"), fixed ); // emulate RELAX NG built-in token type ElementDeclExp decl; if( isGlobal() ) { decl = reader.currentSchema.elementDecls.getOrCreate(name); if( decl.getElementExp()!=null ) reader.reportError( new Locator[]{this.location,reader.getDeclaredLocationOf(decl)}, reader.ERR_DUPLICATE_ELEMENT_DEFINITION, new Object[]{name} ); } else { // create a local object. decl = new ElementDeclExp(reader.currentSchema,null); } reader.setDeclaredLocationOf(decl); ElementDeclExp.XSElementExp exp = decl.new XSElementExp( new SimpleNameClass(targetNamespace,name), contentType ); // set the body. decl.setElementExp(exp); // set identity constraints exp.identityConstraints.addAll(idcs); // process the nillable attribute. boolean isNillable = false; String nillable = startTag.getAttribute("nillable"); if( nillable!=null ) decl.isNillable = nillable.equals("true") || nillable.equals("1"); // process the "abstract" attribute. String abstract_ = startTag.getAttribute("abstract"); decl.setAbstract( "true".equals(abstract_)||"1".equals(abstract_) ); if( abstract_!=null && !BooleanType.theInstance.isValid(abstract_,null) ) // recover by assuming false. reader.reportError( XMLSchemaReader.ERR_BAD_ATTRIBUTE_VALUE, "abstract", abstract_ ); // TODO: abstract is prohibited for the local element. // TODO: substitutionGroup is also prohibited for the local element. String substitutionGroupQName = startTag.getAttribute("substitutionGroup"); if( substitutionGroupQName!=null ) { String[] r = reader.splitQName(substitutionGroupQName); if(r==null) { reader.reportError( reader.ERR_UNDECLARED_PREFIX, substitutionGroupQName ); // recover by ignoring substitutionGroup. } else { // register this declaration to the head elementDecl. ElementDeclExp head = reader.getOrCreateSchema(r[0]/*uri*/). elementDecls.getOrCreate(r[1]/*local name*/); decl.substitutionAffiliation = head; // before adding this "decl" to "head.substitutions", // we need to check the block attribute of various related components. // Therefore, this process is done at the wrapUp method of XMLSchemaReader. } } String block = startTag.getAttribute("block"); if(block==null) block = reader.blockDefault; if(block!=null) { if( block.indexOf("#all")>=0 ) decl.block |= decl.RESTRICTION|decl.EXTENSION|decl.SUBSTITUTION; if( block.indexOf("extension")>=0 ) decl.block |= decl.EXTENSION; if( block.indexOf("restriction")>=0 ) decl.block |= decl.RESTRICTION; if( block.indexOf("substitution")>=0 ) decl.block |= decl.SUBSTITUTION; } String finalValue = startTag.getAttribute("final"); if(finalValue==null) finalValue = reader.finalDefault; if(finalValue!=null) { if( finalValue.indexOf("#all")>=0 ) decl.finalValue |= decl.RESTRICTION|decl.EXTENSION|decl.SUBSTITUTION; if( finalValue.indexOf("extension")>=0 ) decl.finalValue |= decl.EXTENSION; if( finalValue.indexOf("restriction")>=0 ) decl.finalValue |= decl.RESTRICTION; } // minOccurs/maxOccurs is processed through interception // call the hook to let derived classes modify the content model return annealDeclaration(decl); } /** * This method is called after this class finishes augmenting * ElementDeclExp. Derived classes can override this method * and modify an ElementDeclExp further. */ protected Expression annealDeclaration( ElementDeclExp exp ) { return exp; } /** * Returns true if this element declaration is a global element declaration. */ public boolean isGlobal() { return parentState instanceof GlobalDeclState; } /** identity constraints found in this element. */ protected final Vector idcs = new Vector(); /** this method is called when an identity constraint declaration is found. */ protected void onIdentityConstraint( IdentityConstraint idc ) { idcs.add(idc); } }