Save This Page
Home » openjdk-7 » com.sun.xml.internal » xsom » impl » parser » [javadoc | source]
    1   /*
    2    * Copyright 2006 Sun Microsystems, Inc.  All Rights Reserved.
    3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    4    *
    5    * This code is free software; you can redistribute it and/or modify it
    6    * under the terms of the GNU General Public License version 2 only, as
    7    * published by the Free Software Foundation.  Sun designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Sun in the LICENSE file that accompanied this code.
   10    *
   11    * This code is distributed in the hope that it will be useful, but WITHOUT
   12    * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   13    * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   14    * version 2 for more details (a copy is included in the LICENSE file that
   15    * accompanied this code).
   16    *
   17    * You should have received a copy of the GNU General Public License version
   18    * 2 along with this work; if not, write to the Free Software Foundation,
   19    * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   20    *
   21    * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
   22    * CA 95054 USA or visit www.sun.com if you need additional information or
   23    * have any questions.
   24    */
   25   
   26   package com.sun.xml.internal.xsom.impl.parser;
   27   
   28   import com.sun.xml.internal.xsom.XSDeclaration;
   29   import com.sun.xml.internal.xsom.XmlString;
   30   import com.sun.xml.internal.xsom.impl.ForeignAttributesImpl;
   31   import com.sun.xml.internal.xsom.impl.SchemaImpl;
   32   import com.sun.xml.internal.xsom.impl.UName;
   33   import com.sun.xml.internal.xsom.impl.parser.state.NGCCRuntime;
   34   import com.sun.xml.internal.xsom.impl.parser.state.Schema;
   35   import com.sun.xml.internal.xsom.impl.util.Uri;
   36   import com.sun.xml.internal.xsom.parser.AnnotationParser;
   37   import org.relaxng.datatype.ValidationContext;
   38   import org.xml.sax.Attributes;
   39   import org.xml.sax.EntityResolver;
   40   import org.xml.sax.ErrorHandler;
   41   import org.xml.sax.InputSource;
   42   import org.xml.sax.Locator;
   43   import org.xml.sax.SAXException;
   44   import org.xml.sax.SAXParseException;
   45   import org.xml.sax.helpers.LocatorImpl;
   46   
   47   import java.io.IOException;
   48   import java.text.MessageFormat;
   49   import java.util.Stack;
   50   
   51   /**
   52    * NGCCRuntime extended with various utility methods for
   53    * parsing XML Schema.
   54    *
   55    * @author Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com)
   56    */
   57   public class NGCCRuntimeEx extends NGCCRuntime implements PatcherManager {
   58   
   59       /** coordinator. */
   60       public final ParserContext parser;
   61   
   62       /** The schema currently being parsed. */
   63       public SchemaImpl currentSchema;
   64   
   65       /** The @finalDefault value of the current schema. */
   66       public int finalDefault = 0;
   67       /** The @blockDefault value of the current schema. */
   68       public int blockDefault = 0;
   69   
   70       /**
   71        * The @elementFormDefault value of the current schema.
   72        * True if local elements are qualified by default.
   73        */
   74       public boolean elementFormDefault = false;
   75   
   76       /**
   77        * The @attributeFormDefault value of the current schema.
   78        * True if local attributes are qualified by default.
   79        */
   80       public boolean attributeFormDefault = false;
   81   
   82       /**
   83        * True if the current schema is in a chameleon mode.
   84        * This changes the way QNames are interpreted.
   85        *
   86        * Life is very miserable with XML Schema, as you see.
   87        */
   88       public boolean chameleonMode = false;
   89   
   90       /**
   91        * URI that identifies the schema document.
   92        * Maybe null if the system ID is not available.
   93        */
   94       private String documentSystemId;
   95   
   96       /**
   97        * Keep the local name of elements encountered so far.
   98        * This information is passed to AnnotationParser as
   99        * context information
  100        */
  101       private final Stack<String> elementNames = new Stack<String>();
  102   
  103       /**
  104        * Points to the schema document (the parser of it) that included/imported
  105        * this schema.
  106        */
  107       private final NGCCRuntimeEx referer;
  108   
  109       /**
  110        * Points to the {@link SchemaDocumentImpl} that represents the
  111        * schema document being parsed.
  112        */
  113       public SchemaDocumentImpl document;
  114   
  115       NGCCRuntimeEx( ParserContext _parser ) {
  116           this(_parser,false,null);
  117       }
  118   
  119       private NGCCRuntimeEx( ParserContext _parser, boolean chameleonMode, NGCCRuntimeEx referer ) {
  120           this.parser = _parser;
  121           this.chameleonMode = chameleonMode;
  122           this.referer = referer;
  123   
  124           // set up the default namespace binding
  125           currentContext = new Context("","",null);
  126           currentContext = new Context("xml","http://www.w3.org/XML/1998/namespace",currentContext);
  127       }
  128   
  129       public void checkDoubleDefError( XSDeclaration c ) throws SAXException {
  130           if(c==null) return;
  131           reportError( Messages.format(Messages.ERR_DOUBLE_DEFINITION,c.getName()) );
  132           reportError( Messages.format(Messages.ERR_DOUBLE_DEFINITION_ORIGINAL), c.getLocator() );
  133       }
  134   
  135   
  136   
  137       /* registers a patcher that will run after all the parsing has finished. */
  138       public void addPatcher( Patch patcher ) {
  139           parser.patcherManager.addPatcher(patcher);
  140       }
  141       public void reportError( String msg, Locator loc ) throws SAXException {
  142           parser.patcherManager.reportError(msg,loc);
  143       }
  144       public void reportError( String msg ) throws SAXException {
  145           reportError(msg,getLocator());
  146       }
  147   
  148   
  149       /**
  150        * Resolves relative URI found in the document.
  151        *
  152        * @param namespaceURI
  153        *      passed to the entity resolver.
  154        * @param relativeUri
  155        *      value of the schemaLocation attribute. Can be null.
  156        *
  157        * @return
  158        *      non-null if {@link EntityResolver} returned an {@link InputSource},
  159        *      or if the relativeUri parameter seems to be pointing to something.
  160        *      Otherwise it returns null, in which case import/include should be abandoned.
  161        */
  162       private InputSource resolveRelativeURL( String namespaceURI, String relativeUri ) throws SAXException {
  163           try {
  164               String baseUri = getLocator().getSystemId();
  165               if(baseUri==null)
  166                   // if the base URI is not available, the document system ID is
  167                   // better than nothing.
  168                   baseUri=documentSystemId;
  169   
  170               String systemId = null;
  171               if(relativeUri!=null)
  172                   systemId = Uri.resolve(baseUri,relativeUri);
  173   
  174               EntityResolver er = parser.getEntityResolver();
  175               if(er!=null) {
  176                   InputSource is = er.resolveEntity(namespaceURI,systemId);
  177                   if(is!=null)
  178                       return is;
  179               }
  180   
  181               if(systemId!=null)
  182                   return new InputSource(systemId);
  183               else
  184                   return null;
  185           } catch (IOException e) {
  186               SAXParseException se = new SAXParseException(e.getMessage(),getLocator(),e);
  187               parser.errorHandler.error(se);
  188               return null;
  189           }
  190       }
  191   
  192       /** Includes the specified schema. */
  193       public void includeSchema( String schemaLocation ) throws SAXException {
  194           NGCCRuntimeEx runtime = new NGCCRuntimeEx(parser,chameleonMode,this);
  195           runtime.currentSchema = this.currentSchema;
  196           runtime.blockDefault = this.blockDefault;
  197           runtime.finalDefault = this.finalDefault;
  198   
  199           if( schemaLocation==null ) {
  200               SAXParseException e = new SAXParseException(
  201                   Messages.format( Messages.ERR_MISSING_SCHEMALOCATION ), getLocator() );
  202               parser.errorHandler.fatalError(e);
  203               throw e;
  204           }
  205   
  206           runtime.parseEntity( resolveRelativeURL(null,schemaLocation),
  207               true, currentSchema.getTargetNamespace(), getLocator() );
  208       }
  209   
  210       /** Imports the specified schema. */
  211       public void importSchema( String ns, String schemaLocation ) throws SAXException {
  212           NGCCRuntimeEx newRuntime = new NGCCRuntimeEx(parser,false,this);
  213           InputSource source = resolveRelativeURL(ns,schemaLocation);
  214           if(source!=null)
  215               newRuntime.parseEntity( source, false, ns, getLocator() );
  216           // if source == null,
  217           // we can't locate this document. Let's just hope that
  218           // we already have the schema components for this schema
  219           // or we will receive them in the future.
  220       }
  221   
  222       /**
  223        * Called when a new document is being parsed and checks
  224        * if the document has already been parsed before.
  225        *
  226        * <p>
  227        * Used to avoid recursive inclusion. Note that the same
  228        * document will be parsed multiple times if they are for different
  229        * target namespaces.
  230        *
  231        * <h2>Document Graph Model</h2>
  232        * <p>
  233        * The challenge we are facing here is that you have a graph of
  234        * documents that reference each other. Each document has an unique
  235        * URI to identify themselves, and references are done by using those.
  236        * The graph may contain cycles.
  237        *
  238        * <p>
  239        * Our goal here is to parse all the documents in the graph, without
  240        * parsing the same document twice. This method implements this check.
  241        *
  242        * <p>
  243        * One complication is the chameleon schema; a document can be parsed
  244        * multiple times if they are under different target namespaces.
  245        *
  246        * <p>
  247        * Also, note that when you resolve relative URIs in the @schemaLocation,
  248        * their base URI is *NOT* the URI of the document.
  249        *
  250        * @return true if the document has already been processed and thus
  251        *      needs to be skipped.
  252        */
  253       public boolean hasAlreadyBeenRead() {
  254           if( documentSystemId!=null ) {
  255               if( documentSystemId.startsWith("file:///") )
  256                   // change file:///abc to file:/abc
  257                   // JDK File.toURL method produces the latter, but according to RFC
  258                   // I don't think that's a valid URL. Since two different ways of
  259                   // producing URLs could produce those two different forms,
  260                   // we need to canonicalize one to the other.
  261                   documentSystemId = "file:/"+documentSystemId.substring(8);
  262           } else {
  263               // if the system Id is not provided, we can't test the identity,
  264               // so we have no choice but to read it.
  265               // the newly created SchemaDocumentImpl will be unique one
  266           }
  267   
  268           assert document ==null;
  269           document = new SchemaDocumentImpl( currentSchema, documentSystemId );
  270   
  271           SchemaDocumentImpl existing = parser.parsedDocuments.get(document);
  272           if(existing==null) {
  273               parser.parsedDocuments.put(document,document);
  274           } else {
  275               document = existing;
  276           }
  277   
  278           assert document !=null;
  279   
  280           if(referer!=null) {
  281               assert referer.document !=null : "referer "+referer.documentSystemId+" has docIdentity==null";
  282               referer.document.references.add(this.document);
  283               this.document.referers.add(referer.document);
  284           }
  285   
  286           return existing!=null;
  287       }
  288   
  289       /**
  290        * Parses the specified entity.
  291        *
  292        * @param importLocation
  293        *      The source location of the import/include statement.
  294        *      Used for reporting errors.
  295        */
  296       public void parseEntity( InputSource source, boolean includeMode, String expectedNamespace, Locator importLocation )
  297               throws SAXException {
  298   
  299           documentSystemId = source.getSystemId();
  300   //        System.out.println("parsing "+baseUri);
  301   
  302   
  303   
  304           try {
  305               Schema s = new Schema(this,includeMode,expectedNamespace);
  306               setRootHandler(s);
  307   
  308               try {
  309                   parser.parser.parse(source,this,
  310                       getErrorHandler(),
  311                       parser.getEntityResolver());
  312               } catch( IOException e ) {
  313                   SAXParseException se = new SAXParseException(
  314                       e.toString(),importLocation,e);
  315                   parser.errorHandler.fatalError(se);
  316                   throw se;
  317               }
  318           } catch( SAXException e ) {
  319               parser.setErrorFlag();
  320               throw e;
  321           }
  322       }
  323   
  324       /**
  325        * Creates a new instance of annotation parser.
  326        */
  327       public AnnotationParser createAnnotationParser() {
  328           if(parser.getAnnotationParserFactory()==null)
  329               return DefaultAnnotationParser.theInstance;
  330           else
  331               return parser.getAnnotationParserFactory().create();
  332       }
  333   
  334       /**
  335        * Gets the element name that contains the annotation element.
  336        * This method works correctly only when called by the annotation handler.
  337        */
  338       public String getAnnotationContextElementName() {
  339           return elementNames.get( elementNames.size()-2 );
  340       }
  341   
  342       /** Creates a copy of the current locator object. */
  343       public Locator copyLocator() {
  344           return new LocatorImpl(getLocator());
  345       }
  346   
  347       public ErrorHandler getErrorHandler() {
  348           return parser.errorHandler;
  349       }
  350   
  351       public void onEnterElementConsumed(String uri, String localName, String qname, Attributes atts)
  352           throws SAXException {
  353           super.onEnterElementConsumed(uri, localName, qname, atts);
  354           elementNames.push(localName);
  355       }
  356   
  357       public void onLeaveElementConsumed(String uri, String localName, String qname) throws SAXException {
  358           super.onLeaveElementConsumed(uri, localName, qname);
  359           elementNames.pop();
  360       }
  361   
  362   
  363   
  364   //
  365   //
  366   // ValidationContext implementation
  367   //
  368   //
  369       // this object lives longer than the parser itself,
  370       // so it's important for this object not to have any reference
  371       // to the parser.
  372       private static class Context implements ValidationContext {
  373           Context( String _prefix, String _uri, Context _context ) {
  374               this.previous = _context;
  375               this.prefix = _prefix;
  376               this.uri = _uri;
  377           }
  378   
  379           public String resolveNamespacePrefix(String p) {
  380               if(p.equals(prefix))    return uri;
  381               if(previous==null)      return null;
  382               else                    return previous.resolveNamespacePrefix(p);
  383           }
  384   
  385           private final String prefix;
  386           private final String uri;
  387           private final Context previous;
  388   
  389           // XSDLib don't use those methods, so we cut a corner here.
  390           public String getBaseUri() { return null; }
  391           public boolean isNotation(String arg0) { return false; }
  392           public boolean isUnparsedEntity(String arg0) { return false; }
  393       }
  394   
  395       private Context currentContext=null;
  396   
  397       /** Returns an immutable snapshot of the current context. */
  398       public ValidationContext createValidationContext() {
  399           return currentContext;
  400       }
  401   
  402       public XmlString createXmlString(String value) {
  403           if(value==null)     return null;
  404           else    return new XmlString(value,createValidationContext());
  405       }
  406   
  407       public void startPrefixMapping( String prefix, String uri ) throws SAXException {
  408           super.startPrefixMapping(prefix,uri);
  409           currentContext = new Context(prefix,uri,currentContext);
  410       }
  411       public void endPrefixMapping( String prefix ) throws SAXException {
  412           super.endPrefixMapping(prefix);
  413           currentContext = currentContext.previous;
  414       }
  415   
  416   
  417   
  418   
  419   
  420   //
  421   //
  422   // Utility functions
  423   //
  424   //
  425   
  426   
  427       /** Parses UName under the given context. */
  428       public UName parseUName( String qname ) throws SAXException {
  429           int idx = qname.indexOf(':');
  430           if(idx<0) {
  431               String uri = resolveNamespacePrefix("");
  432   
  433               // chamelon behavior. ugly...
  434               if( uri.equals("") && chameleonMode )
  435                   uri = currentSchema.getTargetNamespace();
  436   
  437               // this is guaranteed to resolve
  438               return new UName(uri,qname,qname);
  439           } else {
  440               String prefix = qname.substring(0,idx);
  441               String uri = currentContext.resolveNamespacePrefix(prefix);
  442               if(uri==null) {
  443                   // prefix failed to resolve.
  444                   reportError(Messages.format(
  445                       Messages.ERR_UNDEFINED_PREFIX,prefix));
  446                   uri="undefined"; // replace with a dummy
  447               }
  448               return new UName( uri, qname.substring(idx+1), qname );
  449           }
  450       }
  451   
  452       public boolean parseBoolean(String v) {
  453           if(v==null) return false;
  454           v=v.trim();
  455           return v.equals("true") || v.equals("1");
  456       }
  457   
  458   
  459       protected void unexpectedX(String token) throws SAXException {
  460           SAXParseException e = new SAXParseException(MessageFormat.format(
  461               "Unexpected {0} appears at line {1} column {2}",
  462                   token,
  463                   getLocator().getLineNumber(),
  464                   getLocator().getColumnNumber()),
  465               getLocator());
  466   
  467           parser.errorHandler.fatalError(e);
  468           throw e;    // we will abort anyway
  469       }
  470   
  471       public ForeignAttributesImpl parseForeignAttributes( ForeignAttributesImpl next ) {
  472           ForeignAttributesImpl impl = new ForeignAttributesImpl(createValidationContext(),copyLocator(),next);
  473   
  474           Attributes atts = getCurrentAttributes();
  475           for( int i=0; i<atts.getLength(); i++ ) {
  476               if(atts.getURI(i).length()>0) {
  477                   impl.addAttribute(
  478                       atts.getURI(i),
  479                       atts.getLocalName(i),
  480                       atts.getQName(i),
  481                       atts.getType(i),
  482                       atts.getValue(i)
  483                   );
  484               }
  485           }
  486   
  487           return impl;
  488       }
  489   
  490   
  491       public static final String XMLSchemaNSURI = "http://www.w3.org/2001/XMLSchema";
  492   }

Save This Page
Home » openjdk-7 » com.sun.xml.internal » xsom » impl » parser » [javadoc | source]