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 */ 25 26 package com.sun.xml.internal.bind.v2.runtime.output; 27 28 import java.io.IOException; 29 import java.util.Collections; 30 import java.util.Iterator; 31 32 import javax.xml.XMLConstants; 33 import javax.xml.stream.XMLStreamException; 34 35 import com.sun.istack.internal.NotNull; 36 import com.sun.istack.internal.Nullable; 37 import com.sun.xml.internal.bind.marshaller.NamespacePrefixMapper; 38 import com.sun.xml.internal.bind.v2.WellKnownNamespace; 39 import com.sun.xml.internal.bind.v2.runtime.Name; 40 import com.sun.xml.internal.bind.v2.runtime.NamespaceContext2; 41 import com.sun.xml.internal.bind.v2.runtime.XMLSerializer; 42 43 import org.xml.sax.SAXException; 44 45 /** 46 * Keeps track of in-scope namespace bindings for the marshaller. 47 * 48 * <p> 49 * This class is also used to keep track of tag names for each element 50 * for the marshaller (for the performance reason.) 51 * 52 * @author Kohsuke Kawaguchi 53 */ 54 public final class NamespaceContextImpl implements NamespaceContext2 { 55 private final XMLSerializer owner; 56 57 private String[] prefixes = new String[4]; 58 private String[] nsUris = new String[4]; 59 // /** 60 // * True if the correponding namespace declaration is an authentic one that should be printed. 61 // * 62 // * False if it's a re-discovered in-scope namespace binding available at the ancestor elements 63 // * outside this marhsalling. The false value is used to incorporate the in-scope namespace binding 64 // * information from {@link #inscopeNamespaceContext}. When false, such a declaration does not need 65 // * to be printed, as it's already available in ancestors. 66 // */ 67 // private boolean[] visible = new boolean[4]; 68 // 69 // /** 70 // * {@link NamespaceContext} that informs this {@link XMLSerializer} about the 71 // * in-scope namespace bindings of the ancestor elements outside this marshalling. 72 // * 73 // * <p> 74 // * This is used when the marshaller is marshalling into a subtree that has ancestor 75 // * elements created outside the JAXB marshaller. 76 // * 77 // * Its {@link NamespaceContext#getPrefix(String)} is used to discover in-scope namespace 78 // * binding, 79 // */ 80 // private final NamespaceContext inscopeNamespaceContext; 81 82 /** 83 * Number of URIs declared. Identifies the valid portion of 84 * the {@link #prefixes} and {@link #nsUris} arrays. 85 */ 86 private int size; 87 88 private Element current; 89 90 /** 91 * This is the {@link Element} whose prev==null. 92 * This element is used to hold the contextual namespace bindings 93 * that are assumed to be outside of the document we are marshalling. 94 * Specifically the xml prefix and any other user-specified bindings. 95 * 96 * @see NamespacePrefixMapper#getPreDeclaredNamespaceUris() 97 */ 98 private final Element top; 99 100 /** 101 * Never null. 102 */ 103 private NamespacePrefixMapper prefixMapper = defaultNamespacePrefixMapper; 104 105 /** 106 * True to allow new URIs to be declared. False otherwise. 107 */ 108 public boolean collectionMode; 109 110 111 public NamespaceContextImpl(XMLSerializer owner) { 112 this.owner = owner; 113 114 current = top = new Element(this,null); 115 // register namespace URIs that are implicitly bound 116 put(XMLConstants.XML_NS_URI,XMLConstants.XML_NS_PREFIX); 117 } 118 119 public void setPrefixMapper( NamespacePrefixMapper mapper ) { 120 if(mapper==null) 121 mapper = defaultNamespacePrefixMapper; 122 this.prefixMapper = mapper; 123 } 124 125 public NamespacePrefixMapper getPrefixMapper() { 126 return prefixMapper; 127 } 128 129 public void reset() { 130 current = top; 131 size = 1; 132 collectionMode = false; 133 } 134 135 /** 136 * Returns the prefix index to the specified URI. 137 * This method allocates a new URI if necessary. 138 */ 139 public int declareNsUri( String uri, String preferedPrefix, boolean requirePrefix ) { 140 preferedPrefix = prefixMapper.getPreferredPrefix(uri,preferedPrefix,requirePrefix); 141 142 if(uri.length()==0) { 143 for( int i=size-1; i>=0; i-- ) { 144 if(nsUris[i].length()==0) 145 return i; // already declared 146 if(prefixes[i].length()==0) { 147 // the default prefix is already taken. 148 // move that URI to another prefix, then assign "" to the default prefix. 149 assert current.defaultPrefixIndex==-1 && current.oldDefaultNamespaceUriIndex==-1; 150 151 String oldUri = nsUris[i]; 152 String[] knownURIs = owner.nameList.namespaceURIs; 153 154 if(current.baseIndex<=i) { 155 // this default prefix is declared in this context. just reassign it 156 157 nsUris[i] = ""; 158 159 int subst = put(oldUri,null); 160 161 // update uri->prefix table if necessary 162 for( int j=knownURIs.length-1; j>=0; j-- ) { 163 if(knownURIs[j].equals(oldUri)) { 164 owner.knownUri2prefixIndexMap[j] = subst; 165 break; 166 } 167 } 168 if (current.elementLocalName != null) { 169 current.setTagName(subst, current.elementLocalName, current.getOuterPeer()); 170 } 171 return i; 172 } else { 173 // first, if the previous URI assigned to "" is 174 // a "known URI", remember what we've reallocated 175 // so that we can fix it when this context pops. 176 for( int j=knownURIs.length-1; j>=0; j-- ) { 177 if(knownURIs[j].equals(oldUri)) { 178 current.defaultPrefixIndex = i; 179 current.oldDefaultNamespaceUriIndex = j; 180 // assert commented out; too strict/not valid any more 181 // assert owner.knownUri2prefixIndexMap[j]==current.defaultPrefixIndex; 182 // update the table to point to the prefix we'll declare 183 owner.knownUri2prefixIndexMap[j] = size; 184 break; 185 } 186 } 187 if (current.elementLocalName!=null) { 188 current.setTagName(size, current.elementLocalName, current.getOuterPeer()); 189 } 190 191 put(nsUris[i],null); 192 return put("", ""); 193 } 194 } 195 } 196 197 // "" isn't in use 198 return put("", ""); 199 } else { 200 // check for the existing binding 201 for( int i=size-1; i>=0; i-- ) { 202 String p = prefixes[i]; 203 if(nsUris[i].equals(uri)) { 204 if (!requirePrefix || p.length()>0) 205 return i; 206 // declared but this URI is bound to empty. Look further 207 } 208 if(p.equals(preferedPrefix)) { 209 // the suggested prefix is already taken. can't use it 210 preferedPrefix = null; 211 } 212 } 213 214 if(preferedPrefix==null && requirePrefix) 215 // we know we can't bind to "", but we don't have any possible name at hand. 216 // generate it here to avoid this namespace to be bound to "". 217 preferedPrefix = makeUniquePrefix(); 218 219 // haven't been declared. allocate a new one 220 // if the preferred prefix is already in use, it should have been set to null by this time 221 return put(uri, preferedPrefix); 222 } 223 } 224 225 public int force(@NotNull String uri, @NotNull String prefix) { 226 // check for the existing binding 227 228 for( int i=size-1; i>=0; i-- ) { 229 if(prefixes[i].equals(prefix)) { 230 if(nsUris[i].equals(uri)) 231 return i; // found duplicate 232 else 233 // the prefix is used for another namespace. we need to declare it 234 break; 235 } 236 } 237 238 return put(uri, prefix); 239 } 240 241 /** 242 * Puts this new binding into the declared prefixes list 243 * without doing any duplicate check. 244 * 245 * This can be used to forcibly set namespace declarations. 246 * 247 * <p> 248 * Most of the time {@link #declareNamespace(String, String, boolean)} shall be used. 249 * 250 * @return 251 * the index of this new binding. 252 */ 253 public int put(@NotNull String uri, @Nullable String prefix) { 254 if(size==nsUris.length) { 255 // reallocate 256 String[] u = new String[nsUris.length*2]; 257 String[] p = new String[prefixes.length*2]; 258 System.arraycopy(nsUris,0,u,0,nsUris.length); 259 System.arraycopy(prefixes,0,p,0,prefixes.length); 260 nsUris = u; 261 prefixes = p; 262 } 263 if(prefix==null) { 264 if(size==1) 265 prefix = ""; // if this is the first user namespace URI we see, use "". 266 else { 267 // otherwise make up an unique name 268 prefix = makeUniquePrefix(); 269 } 270 } 271 nsUris[size] = uri; 272 prefixes[size] = prefix; 273 274 return size++; 275 } 276 277 private String makeUniquePrefix() { 278 String prefix; 279 prefix = new StringBuilder(5).append("ns").append(size).toString(); 280 while(getNamespaceURI(prefix)!=null) { 281 prefix += '_'; // under a rare circumstance there might be existing 'nsNNN', so rename them 282 } 283 return prefix; 284 } 285 286 287 public Element getCurrent() { 288 return current; 289 } 290 291 /** 292 * Returns the prefix index of the specified URI. 293 * It is an error if the URI is not declared. 294 */ 295 public int getPrefixIndex( String uri ) { 296 for( int i=size-1; i>=0; i-- ) { 297 if(nsUris[i].equals(uri)) 298 return i; 299 } 300 throw new IllegalStateException(); 301 } 302 303 /** 304 * Gets the prefix from a prefix index. 305 * 306 * The behavior is undefined if the index is out of range. 307 */ 308 public String getPrefix(int prefixIndex) { 309 return prefixes[prefixIndex]; 310 } 311 312 public String getNamespaceURI(int prefixIndex) { 313 return nsUris[prefixIndex]; 314 } 315 316 /** 317 * Gets the namespace URI that is bound to the specified prefix. 318 * 319 * @return null 320 * if the prefix is unbound. 321 */ 322 public String getNamespaceURI(String prefix) { 323 for( int i=size-1; i>=0; i-- ) 324 if(prefixes[i].equals(prefix)) 325 return nsUris[i]; 326 return null; 327 } 328 329 /** 330 * Returns the prefix of the specified URI, 331 * or null if none exists. 332 */ 333 public String getPrefix( String uri ) { 334 if(collectionMode) { 335 return declareNamespace(uri,null,false); 336 } else { 337 for( int i=size-1; i>=0; i-- ) 338 if(nsUris[i].equals(uri)) 339 return prefixes[i]; 340 return null; 341 } 342 } 343 344 public Iterator<String> getPrefixes(String uri) { 345 String prefix = getPrefix(uri); 346 if(prefix==null) 347 return Collections.<String>emptySet().iterator(); 348 else 349 return Collections.singleton(uri).iterator(); 350 } 351 352 public String declareNamespace(String namespaceUri, String preferedPrefix, boolean requirePrefix) { 353 int idx = declareNsUri(namespaceUri,preferedPrefix,requirePrefix); 354 return getPrefix(idx); 355 } 356 357 /** 358 * Number of total bindings declared. 359 */ 360 public int count() { 361 return size; 362 } 363 364 365 /** 366 * This model of namespace declarations maintain the following invariants. 367 * 368 * <ul> 369 * <li>If a non-empty prefix is declared, it will never be reassigned to different namespace URIs. 370 * <li> 371 */ 372 public final class Element { 373 374 public final NamespaceContextImpl context; 375 376 /** 377 * {@link Element}s form a doubly-linked list. 378 */ 379 private final Element prev; 380 private Element next; 381 382 private int oldDefaultNamespaceUriIndex; 383 private int defaultPrefixIndex; 384 385 386 /** 387 * The numbe of prefixes declared by ancestor {@link Element}s. 388 */ 389 private int baseIndex; 390 391 /** 392 * The depth of the {@link Element}. 393 * 394 * This value is equivalent as the result of the following computation. 395 * 396 * <pre> 397 * int depth() { 398 * int i=-1; 399 * for(Element e=this; e!=null;e=e.prev) 400 * i++; 401 * return i; 402 * } 403 * </pre> 404 */ 405 private final int depth; 406 407 408 409 private int elementNamePrefix; 410 private String elementLocalName; 411 412 /** 413 * Tag name of this element. 414 * Either this field is used or the {@link #elementNamePrefix} and {@link #elementLocalName} pair. 415 */ 416 private Name elementName; 417 418 /** 419 * Used for the binder. The JAXB object that corresponds to this element. 420 */ 421 private Object outerPeer; 422 private Object innerPeer; 423 424 425 private Element(NamespaceContextImpl context,Element prev) { 426 this.context = context; 427 this.prev = prev; 428 this.depth = (prev==null) ? 0 : prev.depth+1; 429 } 430 431 /** 432 * Returns true if this {@link Element} represents the root element that 433 * we are marshalling. 434 */ 435 public boolean isRootElement() { 436 return depth==1; 437 } 438 439 public Element push() { 440 if(next==null) 441 next = new Element(context,this); 442 next.onPushed(); 443 return next; 444 } 445 446 public Element pop() { 447 if(oldDefaultNamespaceUriIndex>=0) { 448 // restore the old default namespace URI binding 449 context.owner.knownUri2prefixIndexMap[oldDefaultNamespaceUriIndex] = defaultPrefixIndex; 450 } 451 context.size = baseIndex; 452 context.current = prev; 453 // release references to user objects 454 outerPeer = innerPeer = null; 455 return prev; 456 } 457 458 private void onPushed() { 459 oldDefaultNamespaceUriIndex = defaultPrefixIndex = -1; 460 baseIndex = context.size; 461 context.current = this; 462 } 463 464 public void setTagName( int prefix, String localName, Object outerPeer ) { 465 assert localName!=null; 466 this.elementNamePrefix = prefix; 467 this.elementLocalName = localName; 468 this.elementName = null; 469 this.outerPeer = outerPeer; 470 } 471 472 public void setTagName( Name tagName, Object outerPeer ) { 473 assert tagName!=null; 474 this.elementName = tagName; 475 this.outerPeer = outerPeer; 476 } 477 478 public void startElement(XmlOutput out, Object innerPeer) throws IOException, XMLStreamException { 479 this.innerPeer = innerPeer; 480 if(elementName!=null) { 481 out.beginStartTag(elementName); 482 } else { 483 out.beginStartTag(elementNamePrefix,elementLocalName); 484 } 485 } 486 487 public void endElement(XmlOutput out) throws IOException, SAXException, XMLStreamException { 488 if(elementName!=null) { 489 out.endTag(elementName); 490 elementName = null; 491 } else { 492 out.endTag(elementNamePrefix,elementLocalName); 493 } 494 } 495 496 /** 497 * Gets the number of bindings declared on this element. 498 */ 499 public final int count() { 500 return context.size-baseIndex; 501 } 502 503 /** 504 * Gets the prefix declared in this context. 505 * 506 * @param idx 507 * between 0 and {@link #count()} 508 */ 509 public final String getPrefix(int idx) { 510 return context.prefixes[baseIndex+idx]; 511 } 512 513 /** 514 * Gets the namespace URI declared in this context. 515 * 516 * @param idx 517 * between 0 and {@link #count()} 518 */ 519 public final String getNsUri(int idx) { 520 return context.nsUris[baseIndex+idx]; 521 } 522 523 public int getBase() { 524 return baseIndex; 525 } 526 527 public Object getOuterPeer() { 528 return outerPeer; 529 } 530 531 public Object getInnerPeer() { 532 return innerPeer; 533 } 534 535 /** 536 * Gets the parent {@link Element}. 537 */ 538 public Element getParent() { 539 return prev; 540 } 541 } 542 543 544 /** 545 * Default {@link NamespacePrefixMapper} implementation used when 546 * it is not specified by the user. 547 */ 548 private static final NamespacePrefixMapper defaultNamespacePrefixMapper = new NamespacePrefixMapper() { 549 public String getPreferredPrefix(String namespaceUri, String suggestion, boolean requirePrefix) { 550 if( namespaceUri.equals(WellKnownNamespace.XML_SCHEMA_INSTANCE) ) 551 return "xsi"; 552 if( namespaceUri.equals(WellKnownNamespace.XML_SCHEMA) ) 553 return "xs"; 554 if( namespaceUri.equals(WellKnownNamespace.XML_MIME_URI) ) 555 return "xmime"; 556 return suggestion; 557 } 558 }; 559 }