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