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