Tue, 09 Apr 2013 14:51:13 +0100
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 <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 <text/> and won't
182 * be able to process <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 <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 <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 }