src/share/jaxws_classes/com/sun/xml/internal/bind/v2/runtime/output/NamespaceContextImpl.java

changeset 0
373ffda63c9a
equal deleted inserted replaced
-1:000000000000 0:373ffda63c9a
1 /*
2 * Copyright (c) 1997, 2011, 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 }

mercurial