001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common Development
008: * and Distribution License("CDDL") (collectively, the "License"). You
009: * may not use this file except in compliance with the License. You can obtain
010: * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
011: * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
012: * language governing permissions and limitations under the License.
013: *
014: * When distributing the software, include this License Header Notice in each
015: * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
016: * Sun designates this particular file as subject to the "Classpath" exception
017: * as provided by Sun in the GPL Version 2 section of the License file that
018: * accompanied this code. If applicable, add the following below the License
019: * Header, with the fields enclosed by brackets [] replaced by your own
020: * identifying information: "Portions Copyrighted [year]
021: * [name of copyright owner]"
022: *
023: * Contributor(s):
024: *
025: * If you wish your version of this file to be governed by only the CDDL or
026: * only the GPL Version 2, indicate your decision by adding "[Contributor]
027: * elects to include this software in this distribution under the [CDDL or GPL
028: * Version 2] license." If you don't indicate a single choice of license, a
029: * recipient has the option to distribute your version of this file under
030: * either the CDDL, the GPL Version 2 or to extend the choice of license to
031: * its licensees as provided above. However, if you add GPL Version 2 code
032: * and therefore, elected the GPL Version 2 license, then the option applies
033: * only if the new code is made subject to such option by the copyright
034: * holder.
035: */
036:
037: package com.sun.xml.bind.v2.runtime.output;
038:
039: import java.io.IOException;
040: import java.util.Collections;
041: import java.util.Iterator;
042:
043: import javax.xml.XMLConstants;
044: import javax.xml.stream.XMLStreamException;
045:
046: import com.sun.istack.NotNull;
047: import com.sun.istack.Nullable;
048: import com.sun.xml.bind.marshaller.NamespacePrefixMapper;
049: import com.sun.xml.bind.v2.WellKnownNamespace;
050: import com.sun.xml.bind.v2.runtime.Name;
051: import com.sun.xml.bind.v2.runtime.NamespaceContext2;
052: import com.sun.xml.bind.v2.runtime.XMLSerializer;
053:
054: import org.xml.sax.SAXException;
055:
056: /**
057: * Keeps track of in-scope namespace bindings for the marshaller.
058: *
059: * <p>
060: * This class is also used to keep track of tag names for each element
061: * for the marshaller (for the performance reason.)
062: *
063: * @author Kohsuke Kawaguchi
064: */
065: public final class NamespaceContextImpl implements NamespaceContext2 {
066: private final XMLSerializer owner;
067:
068: private String[] prefixes = new String[4];
069: private String[] nsUris = new String[4];
070: // /**
071: // * True if the correponding namespace declaration is an authentic one that should be printed.
072: // *
073: // * False if it's a re-discovered in-scope namespace binding available at the ancestor elements
074: // * outside this marhsalling. The false value is used to incorporate the in-scope namespace binding
075: // * information from {@link #inscopeNamespaceContext}. When false, such a declaration does not need
076: // * to be printed, as it's already available in ancestors.
077: // */
078: // private boolean[] visible = new boolean[4];
079: //
080: // /**
081: // * {@link NamespaceContext} that informs this {@link XMLSerializer} about the
082: // * in-scope namespace bindings of the ancestor elements outside this marshalling.
083: // *
084: // * <p>
085: // * This is used when the marshaller is marshalling into a subtree that has ancestor
086: // * elements created outside the JAXB marshaller.
087: // *
088: // * Its {@link NamespaceContext#getPrefix(String)} is used to discover in-scope namespace
089: // * binding,
090: // */
091: // private final NamespaceContext inscopeNamespaceContext;
092:
093: /**
094: * Number of URIs declared. Identifies the valid portion of
095: * the {@link #prefixes} and {@link #nsUris} arrays.
096: */
097: private int size;
098:
099: private Element current;
100:
101: /**
102: * This is the {@link Element} whose prev==null.
103: * This element is used to hold the contextual namespace bindings
104: * that are assumed to be outside of the document we are marshalling.
105: * Specifically the xml prefix and any other user-specified bindings.
106: *
107: * @see NamespacePrefixMapper#getPreDeclaredNamespaceUris()
108: */
109: private final Element top;
110:
111: /**
112: * Never null.
113: */
114: private NamespacePrefixMapper prefixMapper = defaultNamespacePrefixMapper;
115:
116: /**
117: * True to allow new URIs to be declared. False otherwise.
118: */
119: public boolean collectionMode;
120:
121: public NamespaceContextImpl(XMLSerializer owner) {
122: this .owner = owner;
123:
124: current = top = new Element(this , null);
125: // register namespace URIs that are implicitly bound
126: put(XMLConstants.XML_NS_URI, XMLConstants.XML_NS_PREFIX);
127: }
128:
129: public void setPrefixMapper(NamespacePrefixMapper mapper) {
130: if (mapper == null)
131: mapper = defaultNamespacePrefixMapper;
132: this .prefixMapper = mapper;
133: }
134:
135: public NamespacePrefixMapper getPrefixMapper() {
136: return prefixMapper;
137: }
138:
139: public void reset() {
140: current = top;
141: size = 1;
142: collectionMode = false;
143: }
144:
145: /**
146: * Returns the prefix index to the specified URI.
147: * This method allocates a new URI if necessary.
148: */
149: public int declareNsUri(String uri, String preferedPrefix,
150: boolean requirePrefix) {
151: preferedPrefix = prefixMapper.getPreferredPrefix(uri,
152: preferedPrefix, requirePrefix);
153:
154: if (uri.length() == 0) {
155: for (int i = size - 1; i >= 0; i--) {
156: if (nsUris[i].length() == 0)
157: return i; // already declared
158: if (prefixes[i].length() == 0) {
159: // the default prefix is already taken.
160: // move that URI to another prefix, then assign "" to the default prefix.
161: assert current.defaultPrefixIndex == -1
162: && current.oldDefaultNamespaceUriIndex == -1;
163:
164: String oldUri = nsUris[i];
165: String[] knownURIs = owner.nameList.namespaceURIs;
166:
167: if (current.baseIndex <= i) {
168: // this default prefix is declared in this context. just reassign it
169:
170: nsUris[i] = "";
171:
172: int subst = put(oldUri, null);
173:
174: // update uri->prefix table if necessary
175: for (int j = knownURIs.length - 1; j >= 0; j--) {
176: if (knownURIs[j].equals(oldUri)) {
177: owner.knownUri2prefixIndexMap[j] = subst;
178: break;
179: }
180: }
181:
182: return i;
183: } else {
184: // first, if the previous URI assigned to "" is
185: // a "known URI", remember what we've reallocated
186: // so that we can fix it when this context pops.
187: for (int j = knownURIs.length - 1; j >= 0; j--) {
188: if (knownURIs[j].equals(oldUri)) {
189: current.defaultPrefixIndex = i;
190: current.oldDefaultNamespaceUriIndex = j;
191: assert owner.knownUri2prefixIndexMap[j] == current.defaultPrefixIndex;
192: // update the table to point to the prefix we'll declare
193: owner.knownUri2prefixIndexMap[j] = size;
194: break;
195: }
196: }
197:
198: put(nsUris[i], null);
199: return put("", "");
200: }
201: }
202: }
203:
204: // "" isn't in use
205: return put("", "");
206: } else {
207: // check for the existing binding
208: for (int i = size - 1; i >= 0; i--) {
209: String p = prefixes[i];
210: if (nsUris[i].equals(uri)) {
211: if (!requirePrefix || p.length() > 0)
212: return i;
213: // declared but this URI is bound to empty. Look further
214: }
215: if (p.equals(preferedPrefix)) {
216: // the suggested prefix is already taken. can't use it
217: preferedPrefix = null;
218: }
219: }
220:
221: if (preferedPrefix == null && requirePrefix)
222: // we know we can't bind to "", but we don't have any possible name at hand.
223: // generate it here to avoid this namespace to be bound to "".
224: preferedPrefix = makeUniquePrefix();
225:
226: // haven't been declared. allocate a new one
227: // if the preferred prefix is already in use, it should have been set to null by this time
228: return put(uri, preferedPrefix);
229: }
230: }
231:
232: public int force(@NotNull
233: String uri, @NotNull
234: String prefix) {
235: // check for the existing binding
236: for (int i = size - 1; i >= 0; i--) {
237: if (prefixes[i].equals(prefix)) {
238: if (nsUris[i].equals(uri))
239: return i; // found duplicate
240: else
241: // the prefix is used for another namespace. we need to declare it
242: break;
243: }
244: }
245:
246: return put(uri, prefix);
247: }
248:
249: /**
250: * Puts this new binding into the declared prefixes list
251: * without doing any duplicate check.
252: *
253: * This can be used to forcibly set namespace declarations.
254: *
255: * <p>
256: * Most of the time {@link #declareNamespace(String, String, boolean)} shall be used.
257: *
258: * @return
259: * the index of this new binding.
260: */
261: public int put(@NotNull
262: String uri, @Nullable
263: String prefix) {
264: if (size == nsUris.length) {
265: // reallocate
266: String[] u = new String[nsUris.length * 2];
267: String[] p = new String[prefixes.length * 2];
268: System.arraycopy(nsUris, 0, u, 0, nsUris.length);
269: System.arraycopy(prefixes, 0, p, 0, prefixes.length);
270: nsUris = u;
271: prefixes = p;
272: }
273: if (prefix == null) {
274: if (size == 1)
275: prefix = ""; // if this is the first user namespace URI we see, use "".
276: else {
277: // otherwise make up an unique name
278: prefix = makeUniquePrefix();
279: }
280: }
281: nsUris[size] = uri;
282: prefixes[size] = prefix;
283:
284: return size++;
285: }
286:
287: private String makeUniquePrefix() {
288: String prefix;
289: prefix = new StringBuilder(5).append("ns").append(size)
290: .toString();
291: while (getNamespaceURI(prefix) != null) {
292: prefix += '_'; // under a rare circumstance there might be existing 'nsNNN', so rename them
293: }
294: return prefix;
295: }
296:
297: public Element getCurrent() {
298: return current;
299: }
300:
301: /**
302: * Returns the prefix index of the specified URI.
303: * It is an error if the URI is not declared.
304: */
305: public int getPrefixIndex(String uri) {
306: for (int i = size - 1; i >= 0; i--) {
307: if (nsUris[i].equals(uri))
308: return i;
309: }
310: throw new IllegalStateException();
311: }
312:
313: /**
314: * Gets the prefix from a prefix index.
315: *
316: * The behavior is undefined if the index is out of range.
317: */
318: public String getPrefix(int prefixIndex) {
319: return prefixes[prefixIndex];
320: }
321:
322: public String getNamespaceURI(int prefixIndex) {
323: return nsUris[prefixIndex];
324: }
325:
326: /**
327: * Gets the namespace URI that is bound to the specified prefix.
328: *
329: * @return null
330: * if the prefix is unbound.
331: */
332: public String getNamespaceURI(String prefix) {
333: for (int i = size - 1; i >= 0; i--)
334: if (prefixes[i].equals(prefix))
335: return nsUris[i];
336: return null;
337: }
338:
339: /**
340: * Returns the prefix of the specified URI,
341: * or null if none exists.
342: */
343: public String getPrefix(String uri) {
344: if (collectionMode) {
345: return declareNamespace(uri, null, false);
346: } else {
347: for (int i = size - 1; i >= 0; i--)
348: if (nsUris[i].equals(uri))
349: return prefixes[i];
350: return null;
351: }
352: }
353:
354: public Iterator<String> getPrefixes(String uri) {
355: String prefix = getPrefix(uri);
356: if (prefix == null)
357: return Collections.<String> emptySet().iterator();
358: else
359: return Collections.singleton(uri).iterator();
360: }
361:
362: public String declareNamespace(String namespaceUri,
363: String preferedPrefix, boolean requirePrefix) {
364: int idx = declareNsUri(namespaceUri, preferedPrefix,
365: requirePrefix);
366: return getPrefix(idx);
367: }
368:
369: /**
370: * Number of total bindings declared.
371: */
372: public int count() {
373: return size;
374: }
375:
376: /**
377: * This model of namespace declarations maintain the following invariants.
378: *
379: * <ul>
380: * <li>If a non-empty prefix is declared, it will never be reassigned to different namespace URIs.
381: * <li>
382: */
383: public final class Element {
384:
385: public final NamespaceContextImpl context;
386:
387: /**
388: * {@link Element}s form a doubly-linked list.
389: */
390: private final Element prev;
391: private Element next;
392:
393: private int oldDefaultNamespaceUriIndex;
394: private int defaultPrefixIndex;
395:
396: /**
397: * The numbe of prefixes declared by ancestor {@link Element}s.
398: */
399: private int baseIndex;
400:
401: /**
402: * The depth of the {@link Element}.
403: *
404: * This value is equivalent as the result of the following computation.
405: *
406: * <pre>
407: * int depth() {
408: * int i=-1;
409: * for(Element e=this; e!=null;e=e.prev)
410: * i++;
411: * return i;
412: * }
413: * </pre>
414: */
415: private final int depth;
416:
417: private int elementNamePrefix;
418: private String elementLocalName;
419:
420: /**
421: * Tag name of this element.
422: * Either this field is used or the {@link #elementNamePrefix} and {@link #elementLocalName} pair.
423: */
424: private Name elementName;
425:
426: /**
427: * Used for the binder. The JAXB object that corresponds to this element.
428: */
429: private Object outerPeer;
430: private Object innerPeer;
431:
432: private Element(NamespaceContextImpl context, Element prev) {
433: this .context = context;
434: this .prev = prev;
435: this .depth = (prev == null) ? 0 : prev.depth + 1;
436: }
437:
438: /**
439: * Returns true if this {@link Element} represents the root element that
440: * we are marshalling.
441: */
442: public boolean isRootElement() {
443: return depth == 1;
444: }
445:
446: public Element push() {
447: if (next == null)
448: next = new Element(context, this );
449: next.onPushed();
450: return next;
451: }
452:
453: public Element pop() {
454: if (oldDefaultNamespaceUriIndex >= 0) {
455: // restore the old default namespace URI binding
456: context.owner.knownUri2prefixIndexMap[oldDefaultNamespaceUriIndex] = defaultPrefixIndex;
457: }
458: context.size = baseIndex;
459: context.current = prev;
460: return prev;
461: }
462:
463: private void onPushed() {
464: oldDefaultNamespaceUriIndex = defaultPrefixIndex = -1;
465: baseIndex = context.size;
466: context.current = this ;
467: }
468:
469: public void setTagName(int prefix, String localName,
470: Object outerPeer) {
471: assert localName != null;
472: this .elementNamePrefix = prefix;
473: this .elementLocalName = localName;
474: this .elementName = null;
475: this .outerPeer = outerPeer;
476: }
477:
478: public void setTagName(Name tagName, Object outerPeer) {
479: assert tagName != null;
480: this .elementName = tagName;
481: this .outerPeer = outerPeer;
482: }
483:
484: public void startElement(XmlOutput out, Object innerPeer)
485: throws IOException, XMLStreamException {
486: this .innerPeer = innerPeer;
487: if (elementName != null) {
488: out.beginStartTag(elementName);
489: } else {
490: out.beginStartTag(elementNamePrefix, elementLocalName);
491: }
492: }
493:
494: public void endElement(XmlOutput out) throws IOException,
495: SAXException, XMLStreamException {
496: if (elementName != null) {
497: out.endTag(elementName);
498: elementName = null;
499: } else {
500: out.endTag(elementNamePrefix, elementLocalName);
501: }
502: }
503:
504: /**
505: * Gets the number of bindings declared on this element.
506: */
507: public final int count() {
508: return context.size - baseIndex;
509: }
510:
511: /**
512: * Gets the prefix declared in this context.
513: *
514: * @param idx
515: * between 0 and {@link #count()}
516: */
517: public final String getPrefix(int idx) {
518: return context.prefixes[baseIndex + idx];
519: }
520:
521: /**
522: * Gets the namespace URI declared in this context.
523: *
524: * @param idx
525: * between 0 and {@link #count()}
526: */
527: public final String getNsUri(int idx) {
528: return context.nsUris[baseIndex + idx];
529: }
530:
531: public int getBase() {
532: return baseIndex;
533: }
534:
535: public Object getOuterPeer() {
536: return outerPeer;
537: }
538:
539: public Object getInnerPeer() {
540: return innerPeer;
541: }
542:
543: /**
544: * Gets the parent {@link Element}.
545: */
546: public Element getParent() {
547: return prev;
548: }
549: }
550:
551: /**
552: * Default {@link NamespacePrefixMapper} implementation used when
553: * it is not specified by the user.
554: */
555: private static final NamespacePrefixMapper defaultNamespacePrefixMapper = new NamespacePrefixMapper() {
556: public String getPreferredPrefix(String namespaceUri,
557: String suggestion, boolean requirePrefix) {
558: if (namespaceUri
559: .equals(WellKnownNamespace.XML_SCHEMA_INSTANCE))
560: return "xsi";
561: if (namespaceUri.equals(WellKnownNamespace.XML_SCHEMA))
562: return "xs";
563: if (namespaceUri.equals(WellKnownNamespace.XML_MIME_URI))
564: return "xmime";
565: return suggestion;
566: }
567: };
568: }
|