001: /*
002: Copyright (c) 2004-2005, Dennis M. Sosnoski.
003: All rights reserved.
004:
005: Redistribution and use in source and binary forms, with or without modification,
006: are permitted provided that the following conditions are met:
007:
008: * Redistributions of source code must retain the above copyright notice, this
009: list of conditions and the following disclaimer.
010: * Redistributions in binary form must reproduce the above copyright notice,
011: this list of conditions and the following disclaimer in the documentation
012: and/or other materials provided with the distribution.
013: * Neither the name of JiBX nor the names of its contributors may be used
014: to endorse or promote products derived from this software without specific
015: prior written permission.
016:
017: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
018: ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
019: WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
020: DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
021: ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
022: (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
023: LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
024: ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
025: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
026: SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
027: */
028:
029: package org.jibx.runtime.impl;
030:
031: import java.io.IOException;
032: import java.util.Stack;
033:
034: import org.jibx.runtime.IXMLWriter;
035:
036: /**
037: * Base implementation of XML writer interface namespace handling. This tracks
038: * only the namespace declarations and the element nesting depth. It can be used
039: * as a base class for all forms of output.
040: *
041: * @author Dennis M. Sosnoski
042: * @version 1.0
043: */
044: public abstract class XMLWriterNamespaceBase implements IXMLWriter {
045: /** Empty array for default return. */
046: private static final int[] EMPTY_INT_ARRAY = new int[0];
047:
048: /** URIs for namespaces. */
049: protected String[] m_uris;
050:
051: /** Prefixes currently defined for namespaces. */
052: protected String[] m_prefixes;
053:
054: /** Depth of nested tags. */
055: private int m_nestingDepth;
056:
057: /** Stack of information for namespace declarations. */
058: private Stack m_namespaceStack;
059:
060: /** Depth of top namespace declaration level. */
061: private int m_namespaceDepth;
062:
063: /** Extension namespace URIs (<code>null</code> if not in use). */
064: private String[][] m_extensionUris;
065:
066: /** Extension namespace prefixes (<code>null</code> if not in use). */
067: private String[][] m_extensionPrefixes;
068:
069: /**
070: * Constructor.
071: *
072: * @param uris ordered array of URIs for namespaces used in document (must
073: * be constant; the value in position 0 must always be the empty string "",
074: * and the value in position 1 must always be the XML namespace
075: * "http://www.w3.org/XML/1998/namespace")
076: */
077: public XMLWriterNamespaceBase(String[] uris) {
078: m_uris = uris;
079: m_prefixes = new String[uris.length];
080: m_prefixes[0] = "";
081: m_prefixes[1] = "xml";
082: m_namespaceStack = new Stack();
083: m_namespaceDepth = -1;
084: }
085:
086: /**
087: * Copy constructor. This initializes the extension namespace information
088: * from an existing instance.
089: *
090: * @param base existing instance
091: * @param uris ordered array of URIs for namespaces used in document
092: */
093: public XMLWriterNamespaceBase(XMLWriterNamespaceBase base,
094: String[] uris) {
095: this (uris);
096: m_extensionUris = base.m_extensionUris;
097: m_extensionPrefixes = base.m_extensionPrefixes;
098: m_nestingDepth = base.m_nestingDepth;
099: }
100:
101: /**
102: * Report to subclass that namespace has been defined.
103: *
104: * @param index namespace URI index number
105: * @param prefix prefix used for namespace
106: * @throws IOException if error writing to document
107: */
108: protected abstract void defineNamespace(int index, String prefix)
109: throws IOException;
110:
111: /**
112: * Report to subclass that namespace has been undefined.
113: *
114: * @param index namespace URI index number
115: */
116: protected abstract void undefineNamespace(int index);
117:
118: /**
119: * Set prefix for namespace.
120: *
121: * @param index namespace URI index number
122: */
123: private void setNamespacePrefix(int index, String prefix) {
124: if (index < m_prefixes.length) {
125: m_prefixes[index] = prefix;
126: } else if (m_extensionUris != null) {
127: index -= m_prefixes.length;
128: for (int i = 0; i < m_extensionUris.length; i++) {
129: int length = m_extensionUris[i].length;
130: if (index < length) {
131: m_extensionPrefixes[i][index] = prefix;
132: break;
133: } else {
134: index -= length;
135: }
136: }
137: }
138: }
139:
140: /**
141: * Open the specified namespaces. Previously active namespace declarations
142: * are not duplicated.
143: *
144: * @param nums array of namespace indexes defined by this element (must
145: * be constant, reference is kept until end of element)
146: * @param prefs array of namespace prefixes mapped by this element (no
147: * <code>null</code> values, use "" for default namespace declaration)
148: * @return array of indexes for namespaces not previously active (the ones
149: * actually needing to be declared, in the case of text output)
150: * @throws IOException on error writing to document
151: */
152: public int[] openNamespaces(int[] nums, String[] prefs)
153: throws IOException {
154:
155: // find the number of namespaces actually being declared
156: int count = 0;
157: for (int i = 0; i < nums.length; i++) {
158:
159: // set prefix only if different and not changing prefix to default
160: String newpref = prefs[i];
161: String oldpref = getNamespacePrefix(nums[i]);
162: boolean use = false;
163: if (!newpref.equals(oldpref)
164: && (!"".equals(newpref) || oldpref == null)) {
165: use = true;
166: for (int j = 0; j < i; j++) {
167: if (nums[i] == nums[j]) {
168: if (prefs[i] == null) {
169: nums[i] = -1;
170: } else {
171: use = false;
172: break;
173: }
174: }
175: }
176: }
177: if (use) {
178: count++;
179: } else {
180: nums[i] = -1;
181: }
182: }
183:
184: // check if there's actually any change
185: int[] deltas = EMPTY_INT_ARRAY;
186: if (count > 0) {
187:
188: // get the set of namespace indexes that are changing
189: String[] priors = new String[count];
190: if (count == nums.length) {
191:
192: // replace the full set, tracking the prior values
193: deltas = nums;
194: for (int i = 0; i < count; i++) {
195: int slot = deltas[i];
196: priors[i] = getNamespacePrefix(slot);
197: setNamespacePrefix(slot, prefs[i]);
198: defineNamespace(slot, prefs[i]);
199: }
200:
201: } else {
202:
203: // replace only changed ones, tracking both indexes and priors
204: int fill = 0;
205: deltas = new int[count];
206: for (int i = 0; i < nums.length; i++) {
207: int slot = nums[i];
208: if (slot >= 0) {
209: deltas[fill] = slot;
210: priors[fill++] = getNamespacePrefix(slot);
211: setNamespacePrefix(slot, prefs[i]);
212: defineNamespace(slot, prefs[i]);
213: }
214: }
215: }
216:
217: // set up for undeclaring namespaces on close of element
218: m_namespaceStack.push(new DeclarationInfo(m_nestingDepth,
219: deltas, priors));
220: m_namespaceDepth = m_nestingDepth;
221:
222: }
223: return deltas;
224: }
225:
226: /**
227: * Ends the current innermost set of nested namespace definitions. Reverts
228: * the namespaces involved to their previously-declared prefixes, and sets
229: * up for ending the new innermost set.
230: */
231: private void closeNamespaces() {
232:
233: // revert prefixes for namespaces included in last declaration
234: DeclarationInfo info = (DeclarationInfo) m_namespaceStack.pop();
235: int[] deltas = info.m_deltas;
236: String[] priors = info.m_priors;
237: for (int i = 0; i < deltas.length; i++) {
238: int index = deltas[i];
239: undefineNamespace(index);
240: if (index < m_prefixes.length) {
241: m_prefixes[index] = priors[i];
242: } else if (m_extensionUris != null) {
243: index -= m_prefixes.length;
244: for (int j = 0; j < m_extensionUris.length; j++) {
245: int length = m_extensionUris[j].length;
246: if (index < length) {
247: m_extensionPrefixes[j][index] = priors[i];
248: } else {
249: index -= length;
250: }
251: }
252: }
253: }
254:
255: // set up for clearing next nested set
256: if (m_namespaceStack.empty()) {
257: m_namespaceDepth = -1;
258: } else {
259: m_namespaceDepth = ((DeclarationInfo) m_namespaceStack
260: .peek()).m_depth;
261: }
262: }
263:
264: /**
265: * Get the current element nesting depth. Elements are only counted in the
266: * depth returned when they're officially open - after the start tag has
267: * been output and before the end tag has been output.
268: *
269: * @return number of nested elements at current point in output
270: */
271: public final int getNestingDepth() {
272: return m_nestingDepth;
273: }
274:
275: /**
276: * Get the number of namespaces currently defined. This is equivalent to the
277: * index of the next extension namespace added.
278: *
279: * @return namespace count
280: */
281: public final int getNamespaceCount() {
282: int count = m_uris.length;
283: if (m_extensionUris != null) {
284: for (int i = 0; i < m_extensionUris.length; i++) {
285: count += m_extensionUris[i].length;
286: }
287: }
288: return count;
289: }
290:
291: /**
292: * Increment the current nesting depth. Subclasses need to call this method
293: * whenever an element start tag is written.
294: */
295: protected void incrementNesting() {
296: m_nestingDepth++;
297: }
298:
299: /**
300: * Decrement the current nesting depth. Subclasses need to call this method
301: * whenever an element end tag is written.
302: */
303: protected void decrementNesting() {
304: --m_nestingDepth;
305: if (m_nestingDepth >= 0) {
306: while (m_nestingDepth == m_namespaceDepth) {
307: closeNamespaces();
308: }
309: }
310: }
311:
312: /**
313: * Reset to initial state for reuse. Subclasses overriding this method need
314: * to call this base class implementation during their processing.
315: */
316:
317: public void reset() {
318: m_nestingDepth = 0;
319: m_namespaceDepth = -1;
320: m_namespaceStack.clear();
321: m_extensionUris = null;
322: m_extensionPrefixes = null;
323: }
324:
325: /**
326: * Get namespace URIs for mapping. This gets the full ordered array of
327: * namespaces known in the binding used for this marshalling, where the
328: * index number of each namespace URI is the namespace index used to lookup
329: * the prefix when marshalling a name in that namespace. The returned array
330: * must not be modified.
331: *
332: * @return array of namespaces
333: */
334: public final String[] getNamespaces() {
335: return m_uris;
336: }
337:
338: /**
339: * Get URI for namespace.
340: *
341: * @param index namespace URI index number
342: * @return namespace URI text, or <code>null</code> if the namespace index
343: * is invalid
344: */
345: public final String getNamespaceUri(int index) {
346: if (index < m_uris.length) {
347: return m_uris[index];
348: } else if (m_extensionUris != null) {
349: index -= m_uris.length;
350: for (int i = 0; i < m_extensionUris.length; i++) {
351: int length = m_extensionUris[i].length;
352: if (index < length) {
353: return m_extensionUris[i][index];
354: } else {
355: index -= length;
356: }
357: }
358: }
359: return null;
360: }
361:
362: /**
363: * Get current prefix defined for namespace.
364: *
365: * @param index namespace URI index number
366: * @return current prefix text, or <code>null</code> if the namespace is not
367: * currently mapped
368: */
369: public final String getNamespacePrefix(int index) {
370: if (index < m_prefixes.length) {
371: return m_prefixes[index];
372: } else if (m_extensionUris != null) {
373: index -= m_prefixes.length;
374: for (int i = 0; i < m_extensionUris.length; i++) {
375: int length = m_extensionUris[i].length;
376: if (index < length) {
377: return m_extensionPrefixes[i][index];
378: } else {
379: index -= length;
380: }
381: }
382: }
383: return null;
384: }
385:
386: /**
387: * Get index of namespace mapped to prefix. This can be an expensive
388: * operation with time proportional to the number of namespaces defined, so
389: * it should be used with care.
390: *
391: * @param prefix text to match (non-<code>null</code>, use "" for default
392: * prefix)
393: * @return index namespace URI index number mapped to prefix
394: */
395: public final int getPrefixIndex(String prefix) {
396: if (m_extensionPrefixes != null) {
397: for (int i = m_extensionPrefixes.length - 1; i >= 0; i--) {
398: String[] prefixes = m_extensionPrefixes[i];
399: for (int j = prefixes.length - 1; j >= 0; j--) {
400: if (prefix.equals(prefixes[j])) {
401: int index = j + m_prefixes.length;
402: for (int k = i - 1; k >= 0; k--) {
403: index += m_extensionPrefixes[k].length;
404: }
405: return index;
406: }
407: }
408: }
409: }
410: for (int i = m_prefixes.length - 1; i >= 0; i--) {
411: if (prefix.equals(m_prefixes[i])) {
412: return i;
413: }
414: }
415: return -1;
416: }
417:
418: /**
419: * Grow array of array of strings.
420: *
421: * @param base array to be grown (<code>null</code> is treated as zero
422: * length)
423: * @param items array of strings to be added at end of base array
424: * @return array with added array of items
425: */
426: protected static String[][] growArray(String[][] base,
427: String[] items) {
428: if (base == null) {
429: return new String[][] { items };
430: } else {
431: int length = base.length;
432: String[][] grow = new String[length + 1][];
433: System.arraycopy(base, 0, grow, 0, length);
434: grow[length] = items;
435: return grow;
436: }
437: }
438:
439: /**
440: * Shrink array of array of strings.
441: *
442: * @param base array to be shrunk
443: * @return array with last set of items eliminated (<code>null</code> if
444: * empty)
445: */
446: protected static String[][] shrinkArray(String[][] base) {
447: int length = base.length;
448: if (length == 1) {
449: return null;
450: } else {
451: String[][] shrink = new String[length - 1][];
452: System.arraycopy(base, 0, shrink, 0, length - 1);
453: return shrink;
454: }
455: }
456:
457: /**
458: * Append extension namespace URIs to those in mapping.
459: *
460: * @param uris namespace URIs to extend those in mapping
461: */
462: public void pushExtensionNamespaces(String[] uris) {
463: m_extensionUris = growArray(m_extensionUris, uris);
464: m_extensionPrefixes = growArray(m_extensionPrefixes,
465: new String[uris.length]);
466: }
467:
468: /**
469: * Remove extension namespace URIs. This removes the last set of
470: * extension namespaces pushed using {@link #pushExtensionNamespaces}.
471: */
472: public void popExtensionNamespaces() {
473: m_extensionUris = shrinkArray(m_extensionUris);
474: m_extensionPrefixes = shrinkArray(m_extensionPrefixes);
475: }
476:
477: /**
478: * Get extension namespace URIs added to those in mapping. This gets the
479: * current set of extension definitions. The returned arrays must not be
480: * modified.
481: *
482: * @return array of arrays of extension namespaces (<code>null</code> if
483: * none)
484: */
485: public final String[][] getExtensionNamespaces() {
486: return m_extensionUris;
487: }
488:
489: /**
490: * Namespace declaration tracking information. This tracks all information
491: * associated with an element that declares namespaces.
492: */
493: private static class DeclarationInfo {
494: /** Depth of element making declaration. */
495: public final int m_depth;
496:
497: /** Indexes of namespaces included in declarations. */
498: public final int[] m_deltas;
499:
500: /** Prior prefixes for namespaces. */
501: public final String[] m_priors;
502:
503: /** Simple constructor. */
504: public DeclarationInfo(int depth, int[] deltas, String[] priors) {
505: m_depth = depth;
506: m_deltas = deltas;
507: m_priors = priors;
508: }
509: }
510: }
|