src/share/jaxws_classes/com/sun/tools/internal/jxc/gen/config/NGCCRuntime.java

Tue, 09 Apr 2013 14:51:13 +0100

author
alanb
date
Tue, 09 Apr 2013 14:51:13 +0100
changeset 368
0989ad8c0860
parent 286
f50545b5e2f1
child 397
b99d7e355d4b
permissions
-rw-r--r--

8010393: Update JAX-WS RI to 2.2.9-b12941
Reviewed-by: alanb, erikj
Contributed-by: miroslav.kos@oracle.com, martin.grebac@oracle.com

     1 /*
     2  * Copyright (c) 1997, 2012, Oracle and/or its affiliates. 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.  Oracle designates this
     8  * particular file as subject to the "Classpath" exception as provided
     9  * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    22  * or visit www.oracle.com if you need additional information or have any
    23  * questions.
    24  */
    26 package com.sun.tools.internal.jxc.gen.config;
    28 import java.text.MessageFormat;
    29 import java.util.ArrayList;
    30 import java.util.Stack;
    31 import java.util.StringTokenizer;
    33 import org.xml.sax.Attributes;
    34 import org.xml.sax.ContentHandler;
    35 import org.xml.sax.Locator;
    36 import org.xml.sax.SAXException;
    37 import org.xml.sax.SAXParseException;
    39 /**
    40  * Runtime Engine for RELAXNGCC execution.
    41  *
    42  * This class has the following functionalities:
    43  *
    44  * <ol>
    45  *  <li>Managing a stack of NGCCHandler objects and
    46  *      switching between them appropriately.
    47  *
    48  *  <li>Keep track of all Attributes.
    49  *
    50  *  <li>manage mapping between namespace URIs and prefixes.
    51  *
    52  *  <li>TODO: provide support for interleaving.
    53  *
    54  * @version $Id: NGCCRuntime.java,v 1.15 2002/09/29 02:55:48 okajima Exp $
    55  * @author Kohsuke Kawaguchi (kk@kohsuke.org)
    56  */
    57 public class NGCCRuntime implements ContentHandler, NGCCEventSource {
    59     public NGCCRuntime() {
    60         reset();
    61     }
    63     /**
    64      * Sets the root handler, which will be used to parse the
    65      * root element.
    66      * <p>
    67      * This method can be called right after the object is created
    68      * or the reset method is called. You can't replace the root
    69      * handler while parsing is in progress.
    70      * <p>
    71      * Usually a generated class that corresponds to the &lt;start>
    72      * pattern will be used as the root handler, but any NGCCHandler
    73      * can be a root handler.
    74      *
    75      * @exception IllegalStateException
    76      *      If this method is called but it doesn't satisfy the
    77      *      pre-condition stated above.
    78      */
    79     public void setRootHandler( NGCCHandler rootHandler ) {
    80         if(currentHandler!=null)
    81             throw new IllegalStateException();
    82         currentHandler = rootHandler;
    83     }
    86     /**
    87      * Cleans up all the data structure so that the object can be reused later.
    88      * Normally, applications do not need to call this method directly,
    89      *
    90      * as the runtime resets itself after the endDocument method.
    91      */
    92     public void reset() {
    93         attStack.clear();
    94         currentAtts = null;
    95         currentHandler = null;
    96         indent=0;
    97         locator = null;
    98         namespaces.clear();
    99         needIndent = true;
   100         redirect = null;
   101         redirectionDepth = 0;
   102         text = new StringBuffer();
   104         // add a dummy attributes at the bottom as a "centinel."
   105         attStack.push(new AttributesImpl());
   106     }
   108     // current content handler can be acccessed via set/getContentHandler.
   110     private Locator locator;
   111     public void setDocumentLocator( Locator _loc ) { this.locator=_loc; }
   112     /**
   113      * Gets the source location of the current event.
   114      *
   115      * <p>
   116      * One can call this method from RelaxNGCC handlers to access
   117      * the line number information. Note that to
   118      */
   119     public Locator getLocator() { return locator; }
   122     /** stack of {@link Attributes}. */
   123     private final Stack attStack = new Stack();
   124     /** current attributes set. always equal to attStack.peek() */
   125     private AttributesImpl currentAtts;
   127     /**
   128      * Attributes that belong to the current element.
   129      * <p>
   130      * It's generally not recommended for applications to use
   131      * this method. RelaxNGCC internally removes processed attributes,
   132      * so this doesn't correctly reflect all the attributes an element
   133      * carries.
   134      */
   135     public Attributes getCurrentAttributes() {
   136         return currentAtts;
   137     }
   139     /** accumulated text. */
   140     private StringBuffer text = new StringBuffer();
   145     /** The current NGCCHandler. Always equals to handlerStack.peek() */
   146     private NGCCEventReceiver currentHandler;
   148     public int replace( NGCCEventReceiver o, NGCCEventReceiver n ) {
   149         if(o!=currentHandler)
   150             throw new IllegalStateException();  // bug of RelaxNGCC
   151         currentHandler = n;
   153         return 0;   // we only have one thread.
   154     }
   156     /**
   157      * Processes buffered text.
   158      *
   159      * This method will be called by the start/endElement event to process
   160      * buffered text as a text event.
   161      *
   162      * <p>
   163      * Whitespace handling is a tricky business. Consider the following
   164      * schema fragment:
   165      *
   166      * <xmp>
   167      * <element name="foo">
   168      *   <choice>
   169      *     <element name="bar"><empty/></element>
   170      *     <text/>
   171      *   </choice>
   172      * </element>
   173      * </xmp>
   174      *
   175      * Assume we hit the following instance:
   176      * <xmp>
   177      * <foo> <bar/></foo>
   178      * </xmp>
   179      *
   180      * Then this first space needs to be ignored (for otherwise, we will
   181      * end up treating this space as the match to &lt;text/> and won't
   182      * be able to process &lt;bar>.)
   183      *
   184      * Now assume the following instance:
   185      * <xmp>
   186      * <foo/>
   187      * </xmp>
   188      *
   189      * This time, we need to treat this empty string as a text, for
   190      * otherwise we won't be able to accept this instance.
   191      *
   192      * <p>
   193      * This is very difficult to solve in general, but one seemingly
   194      * easy solution is to use the type of next event. If a text is
   195      * followed by a start tag, it follows from the constraint on
   196      * RELAX NG that that text must be either whitespaces or a match
   197      * to &lt;text/>.
   198      *
   199      * <p>
   200      * On the contrary, if a text is followed by a end tag, then it
   201      * cannot be whitespace unless the content model can accept empty,
   202      * in which case sending a text event will be harmlessly ignored
   203      * by the NGCCHandler.
   204      *
   205      * <p>
   206      * Thus this method take one parameter, which controls the
   207      * behavior of this method.
   208      *
   209      * <p>
   210      * TODO: according to the constraint of RELAX NG, if characters
   211      * follow an end tag, then they must be either whitespaces or
   212      * must match to &lt;text/>.
   213      *
   214      * @param   possiblyWhitespace
   215      *      True if the buffered character can be ignorabale. False if
   216      *      it needs to be consumed.
   217      */
   218     private void processPendingText(boolean ignorable) throws SAXException {
   219         if(ignorable && text.toString().trim().length()==0)
   220             ; // ignore. See the above javadoc comment for the description
   221         else
   222             currentHandler.text(text.toString());   // otherwise consume this token
   224         // truncate StringBuffer, but avoid excessive allocation.
   225         if(text.length()>1024)  text = new StringBuffer();
   226         else                    text.setLength(0);
   227     }
   229     public void processList( String str ) throws SAXException {
   230         StringTokenizer t = new StringTokenizer(str, " \t\r\n");
   231         while(t.hasMoreTokens())
   232             currentHandler.text(t.nextToken());
   233     }
   235     public void startElement(String uri, String localname, String qname, Attributes atts)
   236             throws SAXException {
   238         if(redirect!=null) {
   239             redirect.startElement(uri,localname,qname,atts);
   240             redirectionDepth++;
   241         } else {
   242             processPendingText(true);
   243     //        System.out.println("startElement:"+localname+"->"+_attrStack.size());
   244             currentHandler.enterElement(uri, localname, qname, atts);
   245         }
   246     }
   248     /**
   249      * Called by the generated handler code when an enter element
   250      * event is consumed.
   251      *
   252      * <p>
   253      * Pushes a new attribute set.
   254      *
   255      * <p>
   256      * Note that attributes are NOT pushed at the startElement method,
   257      * because the processing of the enterElement event can trigger
   258      * other attribute events and etc.
   259      * <p>
   260      * This method will be called from one of handlers when it truely
   261      * consumes the enterElement event.
   262      */
   263     public void onEnterElementConsumed(
   264         String uri, String localName, String qname,Attributes atts) throws SAXException {
   265         attStack.push(currentAtts=new AttributesImpl(atts));
   266         nsEffectiveStack.push( new Integer(nsEffectivePtr) );
   267         nsEffectivePtr = namespaces.size();
   268     }
   270     public void onLeaveElementConsumed(String uri, String localName, String qname) throws SAXException {
   271         attStack.pop();
   272         if(attStack.isEmpty())
   273             currentAtts = null;
   274         else
   275             currentAtts = (AttributesImpl)attStack.peek();
   276         nsEffectivePtr = ((Integer)nsEffectiveStack.pop()).intValue();
   277     }
   279     public void endElement(String uri, String localname, String qname)
   280             throws SAXException {
   282         if(redirect!=null) {
   283             redirect.endElement(uri,localname,qname);
   284             redirectionDepth--;
   286             if(redirectionDepth!=0)
   287                 return;
   289             // finished redirection.
   290             for( int i=0; i<namespaces.size(); i+=2 )
   291                 redirect.endPrefixMapping((String)namespaces.get(i));
   292             redirect.endDocument();
   294             redirect = null;
   295             // then process this element normally
   296         }
   298         processPendingText(false);
   300         currentHandler.leaveElement(uri, localname, qname);
   301 //        System.out.println("endElement:"+localname);
   302     }
   304     public void characters(char[] ch, int start, int length) throws SAXException {
   305         if(redirect!=null)
   306             redirect.characters(ch,start,length);
   307         else
   308             text.append(ch,start,length);
   309     }
   310     public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException {
   311         if(redirect!=null)
   312             redirect.ignorableWhitespace(ch,start,length);
   313         else
   314             text.append(ch,start,length);
   315     }
   317     public int getAttributeIndex(String uri, String localname) {
   318         return currentAtts.getIndex(uri, localname);
   319     }
   320     public void consumeAttribute(int index) throws SAXException {
   321         final String uri    = currentAtts.getURI(index);
   322         final String local  = currentAtts.getLocalName(index);
   323         final String qname  = currentAtts.getQName(index);
   324         final String value  = currentAtts.getValue(index);
   325         currentAtts.removeAttribute(index);
   327         currentHandler.enterAttribute(uri,local,qname);
   328         currentHandler.text(value);
   329         currentHandler.leaveAttribute(uri,local,qname);
   330     }
   333     public void startPrefixMapping( String prefix, String uri ) throws SAXException {
   334         if(redirect!=null)
   335             redirect.startPrefixMapping(prefix,uri);
   336         else {
   337             namespaces.add(prefix);
   338             namespaces.add(uri);
   339         }
   340     }
   342     public void endPrefixMapping( String prefix ) throws SAXException {
   343         if(redirect!=null)
   344             redirect.endPrefixMapping(prefix);
   345         else {
   346             namespaces.remove(namespaces.size()-1);
   347             namespaces.remove(namespaces.size()-1);
   348         }
   349     }
   351     public void skippedEntity( String name ) throws SAXException {
   352         if(redirect!=null)
   353             redirect.skippedEntity(name);
   354     }
   356     public void processingInstruction( String target, String data ) throws SAXException {
   357         if(redirect!=null)
   358             redirect.processingInstruction(target,data);
   359     }
   361     /** Impossible token. This value can never be a valid XML name. */
   362     static final String IMPOSSIBLE = "\u0000";
   364     public void endDocument() throws SAXException {
   365         // consume the special "end document" token so that all the handlers
   366         // currently at the stack will revert to their respective parents.
   367         //
   368         // this is necessary to handle a grammar like
   369         // <start><ref name="X"/></start>
   370         // <define name="X">
   371         //   <element name="root"><empty/></element>
   372         // </define>
   373         //
   374         // With this grammar, when the endElement event is consumed, two handlers
   375         // are on the stack (because a child object won't revert to its parent
   376         // unless it sees a next event.)
   378         // pass around an "impossible" token.
   379         currentHandler.leaveElement(IMPOSSIBLE,IMPOSSIBLE,IMPOSSIBLE);
   381         reset();
   382     }
   383     public void startDocument() {}
   388 //
   389 //
   390 // event dispatching methods
   391 //
   392 //
   394     public void sendEnterAttribute( int threadId,
   395         String uri, String local, String qname) throws SAXException {
   397         currentHandler.enterAttribute(uri,local,qname);
   398     }
   400     public void sendEnterElement( int threadId,
   401         String uri, String local, String qname, Attributes atts) throws SAXException {
   403         currentHandler.enterElement(uri,local,qname,atts);
   404     }
   406     public void sendLeaveAttribute( int threadId,
   407         String uri, String local, String qname) throws SAXException {
   409         currentHandler.leaveAttribute(uri,local,qname);
   410     }
   412     public void sendLeaveElement( int threadId,
   413         String uri, String local, String qname) throws SAXException {
   415         currentHandler.leaveElement(uri,local,qname);
   416     }
   418     public void sendText(int threadId, String value) throws SAXException {
   419         currentHandler.text(value);
   420     }
   423 //
   424 //
   425 // redirection of SAX2 events.
   426 //
   427 //
   428     /** When redirecting a sub-tree, this value will be non-null. */
   429     private ContentHandler redirect = null;
   431     /**
   432      * Counts the depth of the elements when we are re-directing
   433      * a sub-tree to another ContentHandler.
   434      */
   435     private int redirectionDepth = 0;
   437     /**
   438      * This method can be called only from the enterElement handler.
   439      * The sub-tree rooted at the new element will be redirected
   440      * to the specified ContentHandler.
   441      *
   442      * <p>
   443      * Currently active NGCCHandler will only receive the leaveElement
   444      * event of the newly started element.
   445      *
   446      * @param   uri,local,qname
   447      *      Parameters passed to the enter element event. Used to
   448      *      simulate the startElement event for the new ContentHandler.
   449      */
   450     public void redirectSubtree( ContentHandler child,
   451         String uri, String local, String qname ) throws SAXException {
   453         redirect = child;
   454         redirect.setDocumentLocator(locator);
   455         redirect.startDocument();
   457         // TODO: when a prefix is re-bound to something else,
   458         // the following code is potentially dangerous. It should be
   459         // modified to report active bindings only.
   460         for( int i=0; i<namespaces.size(); i+=2 )
   461             redirect.startPrefixMapping(
   462                 (String)namespaces.get(i),
   463                 (String)namespaces.get(i+1)
   464             );
   466         redirect.startElement(uri,local,qname,currentAtts);
   467         redirectionDepth=1;
   468     }
   470 //
   471 //
   472 // validation context implementation
   473 //
   474 //
   475     /** in-scope namespace mapping.
   476      * namespaces[2n  ] := prefix
   477      * namespaces[2n+1] := namespace URI */
   478     private final ArrayList namespaces = new ArrayList();
   479     /**
   480      * Index on the namespaces array, which points to
   481      * the top of the effective bindings. Because of the
   482      * timing difference between the startPrefixMapping method
   483      * and the execution of the corresponding actions,
   484      * this value can be different from <code>namespaces.size()</code>.
   485      * <p>
   486      * For example, consider the following schema:
   487      * <pre><xmp>
   488      *  <oneOrMore>
   489      *   <element name="foo"><empty/></element>
   490      *  </oneOrMore>
   491      *  code fragment X
   492      *  <element name="bob"/>
   493      * </xmp></pre>
   494      * Code fragment X is executed after we see a startElement event,
   495      * but at this time the namespaces variable already include new
   496      * namespace bindings declared on "bob".
   497      */
   498     private int nsEffectivePtr=0;
   500     /**
   501      * Stack to preserve old nsEffectivePtr values.
   502      */
   503     private final Stack nsEffectiveStack = new Stack();
   505     public String resolveNamespacePrefix( String prefix ) {
   506         for( int i = nsEffectivePtr-2; i>=0; i-=2 )
   507             if( namespaces.get(i).equals(prefix) )
   508                 return (String)namespaces.get(i+1);
   510         // no binding was found.
   511         if(prefix.equals(""))   return "";  // return the default no-namespace
   512         if(prefix.equals("xml"))    // pre-defined xml prefix
   513             return "http://www.w3.org/XML/1998/namespace";
   514         else    return null;    // prefix undefined
   515     }
   518 // error reporting
   519     protected void unexpectedX(String token) throws SAXException {
   520         throw new SAXParseException(MessageFormat.format(
   521             "Unexpected {0} appears at line {1} column {2}",
   522             new Object[]{
   523                 token,
   524                 new Integer(getLocator().getLineNumber()),
   525                 new Integer(getLocator().getColumnNumber()) }),
   526             getLocator());
   527     }
   532 //
   533 //
   534 // trace functions
   535 //
   536 //
   537     private int indent=0;
   538     private boolean needIndent=true;
   539     private void printIndent() {
   540         for( int i=0; i<indent; i++ )
   541             System.out.print("  ");
   542     }
   543     public void trace( String s ) {
   544         if(needIndent) {
   545             needIndent=false;
   546             printIndent();
   547         }
   548         System.out.print(s);
   549     }
   550     public void traceln( String s ) {
   551         trace(s);
   552         trace("\n");
   553         needIndent=true;
   554     }
   555 }

mercurial