/* * @(#)$Id: AttributeWildcardComputer.java,v 1.3 2002/09/27 04:14:32 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.grammar.*; import com.sun.msv.grammar.xmlschema.*; import com.sun.msv.grammar.util.ExpressionWalker; import java.util.Set; import java.util.HashSet; import java.util.Stack; /** * Processes the attribtue wildcard according to the spec. * * <p> * Since the definition of the attribute wildcard is very adhoc, * it cannot be naturally caputred by our AGM. * * <p> * Therefore, when we parse a schema, we just parse <anyAttribute> directly. * After all components are loaded, arcane computation is done to correctly * compute the attribute wildcard. * * <p> * Attribute wildcard will be ultimately converted into an expression, and that * will be attached to the {@link ComplexTypeExp#attWildcard}. * * <p> * This class also computes the attribute propagation that happens * only when a complex type is derived by restriction. * * Consider the following fragment: * * <pre><xmp> * <complexType name="base"> * <attribute name="abc" ... /> * </complexType> * * <complexType name="derived"> * <complexContent> * <restriction base="base"/> * </complexContent> * </complexType> * </xmp></pre> * * <p> * According to the spec, the derived type will have the 'abc' attribute. * By "propagation", we mean this behavior. * * * @author <a href="mailto:kohsuke.kawaguchi@eng.sun.com">Kohsuke KAWAGUCHI</a> */ public class AttributeWildcardComputer extends ExpressionWalker { public static void compute( XMLSchemaReader reader, Expression topLevel ) { new AttributeWildcardComputer(reader).compute(topLevel); } private void compute( Expression topLevel ) { topLevel.visit(this); while(!unprocessedElementExps.isEmpty()) ((ElementExp)unprocessedElementExps.pop()).contentModel.visit(this); } protected AttributeWildcardComputer( XMLSchemaReader _reader ) { this.reader = _reader; } private final XMLSchemaReader reader; /** * Visited ElementExps and ReferenceExps to prevent infinite recursion. */ private final Set visitedExps = new HashSet(); private final Stack unprocessedElementExps = new Stack(); /** * Used to collect AttributeWildcards of children. */ private Set wildcards = null; public void onElement( ElementExp exp ) { if( !visitedExps.add(exp) ) return; // this element has already been processed unprocessedElementExps.add(exp); } public void onRef( ReferenceExp exp ) { if( visitedExps.add(exp) ) { if( exp instanceof AttributeGroupExp ) { AttributeGroupExp aexp = (AttributeGroupExp)exp; final Set o = wildcards; { // process children and collect their wildcards. wildcards = new HashSet(); exp.exp.visit(this); // compute the attribute wildcard aexp.wildcard = calcCompleteWildcard( aexp.wildcard, wildcards ); } wildcards = o; } else if( exp instanceof ComplexTypeExp ) { ComplexTypeExp cexp = (ComplexTypeExp)exp; final Set o = wildcards; { // process children and collect their wildcards. wildcards = new HashSet(); exp.exp.visit(this); // compute the attribute wildcard cexp.wildcard = calcCompleteWildcard( cexp.wildcard, wildcards ); // if(cexp.wildcard==null) // System.out.println("complete wildcard is: none"); // else // System.out.println("complete wildcard is: "+cexp.wildcard.getName()); // if the base type is a complex type and the extension is chosen, // then we need one last step. Sigh. if(cexp.complexBaseType!=null) { // System.out.println("check the base type"); // process the base type first. cexp.complexBaseType.visit(this); if(cexp.derivationMethod==cexp.EXTENSION) cexp.wildcard = calcComplexTypeWildcard( cexp.wildcard, cexp.complexBaseType.wildcard ); propagateAttributes(cexp); } // create the expression for this complex type. if( cexp.wildcard!=null ) cexp.attWildcard.exp = cexp.wildcard.createExpression(reader.grammar); } wildcards = o; } else // otherwise process it normally. super.onRef(exp); } if( wildcards!=null ) { // add the complete att wildcard of this component. if( exp instanceof AttWildcardExp ) { AttributeWildcard w = ((AttWildcardExp)exp).getAttributeWildcard(); if(w!=null) wildcards.add(w); } } } /** * Computes the "complete attribute wildcard" */ private AttributeWildcard calcCompleteWildcard( AttributeWildcard local, Set s ) { final AttributeWildcard[] children = (AttributeWildcard[])s.toArray(new AttributeWildcard[s.size()]); // 1st step is to compute the complete wildcard. if( children.length==0 ) return local; // assert(children.length>0) // compute the intersection of wildcard. NameClass target = children[0].getName(); for( int i=1; i<children.length; i++ ) target = NameClass.intersection(target,children[i].getName()); if( local!=null ) return new AttributeWildcard( NameClass.intersection(local.getName(),target), local.getProcessMode() ); else return new AttributeWildcard( target, children[0].getProcessMode() ); } private AttributeWildcard calcComplexTypeWildcard( AttributeWildcard complete, AttributeWildcard base ) { if(base!=null) { if(complete==null) return base; else return new AttributeWildcard( NameClass.union( complete.getName(), base.getName() ), complete.getProcessMode() ); } else { // the spec does not have a description for this case. // this is my guess. return complete; } } /** * Computes the propagated attributes. */ private void propagateAttributes( final ComplexTypeExp cexp ) { // propagation will be done only if this type is derived from // another complex type by restriction. if(cexp.derivationMethod!=cexp.RESTRICTION || cexp.complexBaseType==null) return; // strangely, this computation does not apply if the base type is // complex ur-type. if( cexp.complexBaseType==reader.complexUrType ) return; final Set explicitAtts = new HashSet(); // visit the derived type and enumerate explicitly declared attributes in it. cexp.body.visit( new ExpressionWalker() { // stop if we hit an ElementExp. public void onElement( ElementExp exp ) {} public void onAttribute( AttributeExp exp ) { if(!(exp.nameClass instanceof SimpleNameClass)) // attribute uses must have a simple name. throw new Error(exp.nameClass.toString()); explicitAtts.add( ((SimpleNameClass)exp.nameClass).toStringPair() ); } }); // visit the base type and enumerate all attributes in it. cexp.complexBaseType.body.visit( new ExpressionWalker() { private boolean isOptional = false; public void onChoice( ChoiceExp exp ) { boolean b = isOptional; isOptional = true; super.onChoice(exp); isOptional = b; } // stop if we hit an ElementExp. public void onElement( ElementExp exp ) {} public void onAttribute( AttributeExp exp ) { // found an attribute if(!(exp.nameClass instanceof SimpleNameClass)) throw new Error(); // attribute uses must have a simple name. SimpleNameClass snc = (SimpleNameClass)exp.nameClass; // see if the dervied type has a definition that // overrides this attribute. if( !explicitAtts.contains(snc.toStringPair()) ) { // this attribute is not defined. copy it. cexp.body.exp = reader.pool.createSequence( cexp.body.exp, isOptional?reader.pool.createOptional(exp):exp ); } } }); } }