/* * @(#)$Id: XSDatatypeExp.java,v 1.4 2002/10/06 18:07:05 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.datatype.xsd; import com.sun.msv.grammar.ReferenceExp; import com.sun.msv.grammar.ExpressionPool; import com.sun.msv.reader.GrammarReader; import com.sun.msv.reader.State; import com.sun.msv.datatype.xsd.XSDatatype; import com.sun.msv.datatype.xsd.XSDatatypeImpl; import com.sun.msv.datatype.xsd.TypeIncubator; import com.sun.msv.datatype.xsd.DatatypeFactory; import com.sun.msv.datatype.xsd.FinalComponent; import com.sun.msv.datatype.xsd.StringType; import org.relaxng.datatype.ValidationContext; import org.relaxng.datatype.DatatypeException; import org.xml.sax.Locator; import java.util.Collection; import java.util.Iterator; import java.util.Stack; import java.util.Vector; /** * A wrapper of XSDatatype that serves as an expression * and encapsulates lazy-constructed datatypes. * * @author <a href="mailto:kohsuke.kawaguchi@eng.sun.com">Kohsuke KAWAGUCHI</a> */ public class XSDatatypeExp extends ReferenceExp implements GrammarReader.BackPatch { /** Creates this object from existing XSDatatype. */ public XSDatatypeExp( XSDatatype dt, ExpressionPool _pool ) { super(dt.getName()); this.namespaceUri = dt.getNamespaceUri(); this.dt = dt; this.pool = _pool; this.ownerState = null; this.renderer = null; super.exp = _pool.createData(dt); } /** Creates lazily created datatype. */ public XSDatatypeExp( String nsUri, String typeName, GrammarReader reader, Renderer _renderer ) { // this(typeName, reader, reader.getCurrentState(), _renderer ); super(typeName); this.namespaceUri = nsUri; this.dt = null; this.ownerState = reader.getCurrentState(); this.renderer = _renderer; this.pool = reader.pool; reader.addBackPatchJob(this); } /** Used only for cloning */ private XSDatatypeExp( String nsUri, String localName ) { super(localName); this.namespaceUri = nsUri; } /** * Namespace URI of this datatype. * Local name is stored in the name field of ReferenceExp. */ private final String namespaceUri; /** * Creates an incubator so that the caller can add more facets * and derive a new type. */ public XSTypeIncubator createIncubator() { if( isLateBind() ) return new LazyTypeIncubator(this,ownerState.reader); // normal incubator return new XSTypeIncubator() { private final TypeIncubator core = new TypeIncubator(dt); public void addFacet( String name, String value, ValidationContext context ) throws DatatypeException { core.addFacet(name,value,context); } public XSDatatypeExp derive(String uri,String localName) throws DatatypeException { return new XSDatatypeExp( core.derive(uri,localName), pool ); } }; } /** * Datatype object wrapped by this expression. * * This field can be null if the datatype object is not available * at this moment (say, because of the forward reference). In this * case, {@link #ownerState} and {@link #renderer} fields are * available. */ private XSDatatype dt; /** ExpressionPool that can be used if necessary. */ private ExpressionPool pool; /** * Gets a encapsulated datatype object * <b>This method can be called only after all the datatypes are created.</b> * * <p> * Some of the datatypes are lazily during the back-patching phase. */ public XSDatatype getCreatedType() { if(dt==null) throw new IllegalStateException(); return dt; } /** * Gets the type definition. * This method renders the datatype object if it's not rendered yet. * Internal use only. */ public XSDatatype getType( RenderingContext context ) { if(dt!=null) // the datatype is already rendered. return dt; if(context==null) // create a new context. context = new RenderingContext(); if( context.callStack.contains(this) ) { // a recursive definition is detected. Vector locs = new Vector(); for( int i=0; i<context.callStack.size(); i++ ) locs.add( ((XSDatatypeExp)context.callStack.get(i)).ownerState.getLocation() ); ownerState.reader.reportError( (Locator[])locs.toArray(new Locator[0]), ownerState.reader.ERR_RECURSIVE_DATATYPE, null ); return StringType.theInstance; } context.callStack.push(this); try { dt = renderer.render(context); } catch( DatatypeException e ) { ownerState.reader.reportError( ownerState.reader.ERR_BAD_TYPE, new Object[]{e}, e, new Locator[]{ownerState.getLocation()} ); dt = StringType.theInstance; // recover by assuming a valid type. } context.callStack.pop(); if( dt==null ) throw new Error(); // renderer must render some datatype. // if there was an error, it must recover from it. // set the expression this.exp = pool.createData(dt); return dt; } /** Renders the type (GrammarReader.BackPatch implementation). */ public void patch() { getType(null); } public State getOwnerState() { return ownerState; } // these fields should not be serialized /** * State object that creates this late-binding object. * The source location of this state is used for error message. */ private transient State ownerState; /** * Once the parsing is completed, this function object should * be able to render the actual datatype object. */ private transient Renderer renderer; public final boolean isLateBind() { return dt==null; } /** Gets a clone of this object. */ public XSDatatypeExp getClone() { XSDatatypeExp t = new XSDatatypeExp(this.namespaceUri,this.name); t.redefine(this); return t; } /** Updates this object by copying the state from rhs */ public void redefine( XSDatatypeExp rhs ) { // this.name = rhs.name; this.exp = rhs.exp; this.dt = rhs.dt; this.pool = rhs.pool; this.ownerState = rhs.ownerState; this.renderer = rhs.renderer; // rhs might be a late-bind object. // note that it's safe to call this method // if this is not a late-bind object. if(ownerState!=null) ownerState.reader.addBackPatchJob(this); } /** Derives a new type by setting final values. */ public XSDatatypeExp createFinalizedType( final int finalValue, GrammarReader reader ) { if(finalValue==0) return this; // there is no need to create a new object. if( !isLateBind() ) return new XSDatatypeExp( new FinalComponent( (XSDatatypeImpl)dt, finalValue ), pool ); // create datatype lazily return new XSDatatypeExp( this.namespaceUri, this.name, reader, new Renderer() { public XSDatatype render( RenderingContext context ) throws DatatypeException { return new FinalComponent( (XSDatatypeImpl)getType(context), finalValue ); } }); } /** Derives a new type by list. */ public static XSDatatypeExp makeList( final String nsUri, final String typeName, final XSDatatypeExp itemType, GrammarReader reader ) throws DatatypeException { if(!itemType.isLateBind()) // create it normally return new XSDatatypeExp( DatatypeFactory.deriveByList(nsUri,typeName,itemType.dt), reader.pool ); // create it lazily return new XSDatatypeExp( nsUri, typeName, reader, new Renderer() { public XSDatatype render( RenderingContext context ) throws DatatypeException { return DatatypeFactory.deriveByList( nsUri, typeName, itemType.getType(context) ); } }); } /** Derives a new type by union. */ public static XSDatatypeExp makeUnion( final String typeNameUri, final String typeName, final Collection members, GrammarReader reader ) throws DatatypeException { final XSDatatype[] m = new XSDatatype[members.size()]; int i=0; Iterator itr = members.iterator(); while(itr.hasNext()) { XSDatatypeExp item = (XSDatatypeExp)itr.next(); if( item.isLateBind() ) { // create the union lazily. return new XSDatatypeExp(typeNameUri,typeName,reader, new Renderer(){ public XSDatatype render( RenderingContext context ) throws DatatypeException { int i=0; Iterator itr = members.iterator(); while(itr.hasNext()) m[i++] = ((XSDatatypeExp)itr.next()).getType(context); return DatatypeFactory.deriveByUnion( typeNameUri, typeName, m ); } }); } m[i++] = item.dt; } return new XSDatatypeExp( DatatypeFactory.deriveByUnion( typeNameUri, typeName, m ), reader.pool ); } // // // related interfaces // // /** this object renders the actual datatype object. */ public interface Renderer { /** * creates (or retrieves, whatever) the actual, concrete, real * XSDatatype object. * * <p> * This method is typically called from the wrapUp method of the GrammarReader. * * @return * the XSDatatype object which this LateBindDatatype object is representing. * It shall not return an instance of LateBindDatatype object. * * @param context * If this renderer calls the getBody method of the other * LateBindDatatype objects, then this context should be passed * to the getBody method. This context object is responsible for * detecting recursive references. * * @exception * If an error occurs during rendering, the renderer should throw * a DatatypeException instead of trying to report an error by itself. * The caller of this method will report an error message to the appropriate * handler. */ XSDatatype render( RenderingContext context ) throws DatatypeException; } /** * this object is used to keep the information about * the dependency between late-bind datatype objects. * * <p> * Consider the following schema: * * <PRE><XMP> * <xs:simpleType name="foo"> * <xs:restriction base="bar"> * <xs:minLength value="3"/> * </xs:restriction> * </xs:simpleType> * <xs:simpleType name="bar"> * <xs:restriction base="foo"> * <xs:minLength value="3"/> * </xs:restriction> * </xs:simpleType> * </XMP></PRE> * * Since two types are depending on each other, if you call the * getBody method of "foo" type, it will call the getBody method of "bar" type. * Then in turn it will call "foo" again. So this will result in the * infinite recursion. * * <p> * This context object is used to detect such condition and reports the * dependency to the user. * * <p> * No method is publicly accessible. */ public static class RenderingContext { RenderingContext() {} private final Stack callStack = new Stack(); } }