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