KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sf > saxon > pull > PullNamespaceReducer


1 package net.sf.saxon.pull;
2 import net.sf.saxon.om.*;
3 import net.sf.saxon.trans.XPathException;
4
5 import java.util.ArrayList JavaDoc;
6 import java.util.Iterator JavaDoc;
7 import java.util.List JavaDoc;
8
9 /**
10  * PullNamespaceReducer is a PullFilter responsible for removing duplicate namespace
11  * declarations. It also performs namespace fixup: that is, it ensures that the
12  * namespaces used in element and attribute names are all declared.
13  *
14  * <p>This class is derived from, and contains much common code with, the NamespaceReducer
15  * in the push pipeline. (In the push version, however, namespace fixup is not
16  * performed by the NamespaceReducer, but by the ComplexContentOutputter).</p>
17  * @see net.sf.saxon.event.NamespaceReducer
18  */

19
20 public class PullNamespaceReducer extends PullFilter implements NamespaceResolver
21 {
22
23     // We keep track of namespaces to avoid outputting duplicate declarations. The namespaces
24
// vector holds a list of all namespaces currently declared (organised as integer namespace codes).
25
// The countStack contains an entry for each element currently open; the
26
// value on the countStack is an Integer giving the number of namespaces added to the main
27
// namespace stack by that element.
28

29     private int[] allNamespaces = new int[50]; // all namespace codes currently declared
30
private int allNamespacesSize = 0; // all namespaces currently declared
31
private int[] countStack = new int[50];
32     private int depth = 0;
33     private int[] localNamespaces;
34     private int localNamespacesSize = 0;
35     private int nameCode; // the namecode of the current element
36

37     private NamespaceDeclarations declaredNamespaces;
38     private AttributeCollection attributeCollection;
39
40     // Creating an element does not automatically inherit the namespaces of the containing element.
41
// TODO: disinheriting namespaces is not yet supported by the pull pipeline
42
//private boolean[] disinheritStack = new boolean[50];
43

44     private int[] pendingUndeclarations = null;
45
46     public PullNamespaceReducer(PullProvider base) {
47         super(base);
48     }
49
50     /**
51     * next(): handle next event.
52     * The START_ELEMENT event removes redundant namespace declarations, and
53     * possibly adds an xmlns="" undeclaration.
54     */

55
56     public int next() throws XPathException {
57
58         int event = super.next();
59
60         switch (event) {
61             case START_ELEMENT:
62                 startElement();
63                 break;
64             case END_ELEMENT:
65                 endElement();
66                 break;
67             case PROCESSING_INSTRUCTION:
68             case ATTRIBUTE:
69             case NAMESPACE:
70                 nameCode = super.getNameCode();
71                 break;
72             default:
73                 nameCode = -1;
74         }
75         return event;
76     }
77
78     private void startElement() throws XPathException {
79
80         // If the parent element specified inherit=no, keep a list of namespaces that need to be
81
// undeclared
82

83 // if (depth>0 && disinheritStack[depth-1]) {
84
// pendingUndeclarations = new int[namespacesSize];
85
// System.arraycopy(namespaces, 0, pendingUndeclarations, 0, namespacesSize);
86
// } else {
87
// pendingUndeclarations = null;
88
// }
89

90
91
92         // Record the current height of the namespace list so it can be reset at endElement time
93

94         countStack[depth] = 0;
95         //disinheritStack[depth] = (properties & ReceiverOptions.DISINHERIT_NAMESPACES) != 0;
96
if (++depth >= countStack.length) {
97             int[] newstack = new int[depth*2];
98             System.arraycopy(countStack, 0, newstack, 0, depth);
99             //boolean[] disStack2 = new boolean[depth*2];
100
//System.arraycopy(disinheritStack, 0, disStack2, 0, depth);
101
countStack = newstack;
102             //disinheritStack = disStack2;
103
}
104
105         // Get the list of namespaces associated with this element
106

107         NamespaceDeclarations declarations = super.getNamespaceDeclarations();
108         localNamespaces = declarations.getNamespaceCodes(nsBuffer);
109         localNamespacesSize = 0;
110         for (int i=0; i<localNamespaces.length; i++) {
111             if (localNamespaces[i] == -1) {
112                 break;
113             } else {
114                 if (isNeeded(localNamespaces[i])) {
115                     addGlobalNamespace(localNamespaces[i]);
116                     countStack[depth - 1]++;
117                     localNamespaces[localNamespacesSize++] = localNamespaces[i];
118                 }
119             }
120         }
121
122         // Namespace fixup: ensure that the element namespace is output
123

124
125        nameCode = checkProposedPrefix(super.getNameCode(), 0);
126
127 // int elementNS = getNamePool().allocateNamespaceCode(getNameCode());
128
// if (isNeeded(elementNS)) {
129
// appendNamespace(elementNS);
130
// }
131

132         // Namespace fixup: ensure that all namespaces used in attribute names are declared
133

134         attributeCollection = super.getAttributes();
135         boolean modified = false;
136         for (int i=0; i<attributeCollection.getLength(); i++) {
137             int nc = attributeCollection.getNameCode(i);
138             if ((nc & ~NamePool.FP_MASK) != 0) {
139                 // Only need to do checking for an attribute that's namespaced
140
int newnc = checkProposedPrefix(nc, i+1);
141                 if (nc != newnc) {
142                     if (!modified) {
143                         attributeCollection = copyAttributeCollection(attributeCollection);
144                         modified = true;
145                     }
146                     ((AttributeCollectionImpl)attributeCollection).setAttribute(i, newnc,
147                             attributeCollection.getTypeAnnotation(i),
148                             attributeCollection.getValue(i),
149                             attributeCollection.getLocationId(i),
150                             attributeCollection.getProperties(i));
151                 }
152             }
153         }
154
155
156         if (localNamespacesSize < localNamespaces.length) {
157             localNamespaces[localNamespacesSize] = -1; // add a terminator
158
}
159
160         declaredNamespaces = new NamespaceDeclarationsImpl(getNamePool(), localNamespaces);
161         countStack[depth-1] = localNamespacesSize;
162     }
163
164     private int[] nsBuffer = new int[20];
165
166     private void addLocalNamespace(int nc) {
167         if (localNamespacesSize < localNamespaces.length) {
168             localNamespaces[localNamespacesSize++] = nc;
169         } else {
170             if (localNamespacesSize == 0) {
171                 localNamespaces = new int[10];
172             } else {
173                 int[] nc2 = new int[localNamespacesSize*2];
174                 System.arraycopy (localNamespaces, 0, nc2, 0, localNamespacesSize);
175                 localNamespaces = nc2;
176                 localNamespaces[localNamespacesSize++] = nc;
177             }
178         }
179         addGlobalNamespace(nc);
180     }
181
182     /**
183     * Determine whether a namespace declaration is needed
184     */

185
186     private boolean isNeeded(int nscode) {
187         if (nscode==NamespaceConstant.XML_NAMESPACE_CODE) {
188                 // Ignore the XML namespace
189
return false;
190         }
191
192         // First cancel any pending undeclaration of this namespace prefix (there may be more than one)
193

194         if (pendingUndeclarations != null) {
195             for (int p=0; p<pendingUndeclarations.length; p++) {
196                 if ((nscode>>16) == (pendingUndeclarations[p]>>16)) {
197                     pendingUndeclarations[p] = -1;
198                     //break;
199
}
200             }
201         }
202
203         for (int i=allNamespacesSize-1; i>=0; i--) {
204             if (allNamespaces[i]==nscode) {
205                 // it's a duplicate so we don't need it
206
return false;
207             }
208             if ((allNamespaces[i]>>16) == (nscode>>16)) {
209                 // same prefix, different URI, so we do need it
210
return true;
211             }
212         }
213
214         // we need it unless it's a redundant xmlns=""
215
return (nscode != NamespaceConstant.NULL_NAMESPACE_CODE);
216
217
218         // startContent: Add any namespace undeclarations needed to stop
219
// namespaces being inherited from parent elements
220

221 // if (pendingUndeclarations != null) {
222
// for (int i=0; i<pendingUndeclarations.length; i++) {
223
// int nscode1 = pendingUndeclarations[i];
224
// if (nscode1 != -1) {
225
// namespace(nscode1 & 0xffff0000, 0);
226
// // relies on the namespace() method to prevent duplicate undeclarations
227
// }
228
// }
229
// }
230
//pendingUndeclarations = null;
231
}
232
233     /**
234     * Check that the prefix for an element or attribute is acceptable, allocating a substitute
235     * prefix if not. The prefix is acceptable unless a namespace declaration has been
236     * written that assignes this prefix to a different namespace URI. This method
237     * also checks that the element or attribute namespace has been declared, and declares it
238     * if not.
239     */

240
241     private int checkProposedPrefix(int nameCode, int seq) {
242         NamePool namePool = getNamePool();
243         int nscode = namePool.getNamespaceCode(nameCode);
244         if (nscode == -1) {
245             // avoid calling allocate where possible, because it's synchronized
246
nscode = namePool.allocateNamespaceCode(nameCode);
247         }
248         int nsprefix = nscode>>16;
249
250         for (int i=allNamespacesSize-1; i>=0; i--) {
251             if (nsprefix == (allNamespaces[i]>>16)) {
252                 // same prefix
253
if ((nscode & 0xffff) == (allNamespaces[i] & 0xffff)) {
254                     // same URI
255
return nameCode; // all is well
256
} else {
257                     // same prefix is bound to a different URI. Action depends on whether the declaration
258
// is local to this element or at an outer level
259
if (i + localNamespacesSize >= allNamespacesSize) {
260                         // the prefix is already defined locally, so allocate a new one
261
String JavaDoc prefix = getSubstitutePrefix(nscode, seq);
262
263                         int newNameCode = namePool.allocate(
264                                             prefix,
265                                             namePool.getURI(nameCode),
266                                             namePool.getLocalName(nameCode));
267                         int newNSCode = namePool.allocateNamespaceCode(newNameCode);
268                         addLocalNamespace(newNSCode);
269                         return newNameCode;
270                     } else {
271                         // the prefix has been used on an outer level, but we can reuse it here
272
addLocalNamespace(nscode);
273                         return nameCode;
274                     }
275                 }
276             }
277         }
278         // there is no declaration of this prefix: declare it now
279
if (nscode != NamespaceConstant.NULL_NAMESPACE_CODE) {
280             addLocalNamespace(nscode);
281         }
282         return nameCode;
283     }
284
285     /**
286     * It is possible for a single output element to use the same prefix to refer to different
287     * namespaces. In this case we have to generate an alternative prefix for uniqueness. The
288     * one we generate is based on the sequential position of the element/attribute: this is
289     * designed to ensure both uniqueness (with a high probability) and repeatability
290     */

291
292     private String JavaDoc getSubstitutePrefix(int nscode, int seq) {
293         String JavaDoc prefix = getNamePool().getPrefixFromNamespaceCode(nscode);
294         return prefix + '_' + seq;
295     }
296
297     /**
298     * Add a namespace declaration to the stack
299     */

300
301     private void addGlobalNamespace(int nscode) {
302         // expand the stack if necessary
303
if (allNamespacesSize+1 >= allNamespaces.length) {
304             int[] newlist = new int[allNamespacesSize*2];
305             System.arraycopy(allNamespaces, 0, newlist, 0, allNamespacesSize);
306             allNamespaces = newlist;
307         }
308         allNamespaces[allNamespacesSize++] = nscode;
309     }
310
311     /**
312      * Get the nameCode identifying the name of the current node. This method
313      * can be used after the {@link #START_ELEMENT}, {@link #PROCESSING_INSTRUCTION},
314      * {@link #ATTRIBUTE}, or {@link #NAMESPACE} events. With some PullProvider implementations,
315      * <b>but not this one</b>, it can also be used after {@link #END_ELEMENT}: a client that
316      * requires the information at that point (for example, to do serialization) should insert an
317      * {@link ElementNameTracker} into the pipeline.
318      * If called at other times, the result is undefined and may result in an IllegalStateException.
319      * If called when the current node is an unnamed namespace node (a node representing the default namespace)
320      * the returned value is -1.
321      *
322      * @return the nameCode. The nameCode can be used to obtain the prefix, local name,
323      * and namespace URI from the name pool.
324      */

325
326     public int getNameCode() {
327         return nameCode;
328     }
329
330     /**
331      * Get the attributes associated with the current element. This method must
332      * be called only after a START_ELEMENT event has been notified. The contents
333      * of the returned AttributeCollection are guaranteed to remain unchanged
334      * until the next START_ELEMENT event, but may be modified thereafter. The object
335      * should not be modified by the client.
336      * <p/>
337      * <p>Attributes may be read before or after reading the namespaces of an element,
338      * but must not be read after the first child node has been read, or after calling
339      * one of the methods skipToEnd(), getStringValue(), or getTypedValue().</p>
340      *
341      * @return an AttributeCollection representing the attributes of the element
342      * that has just been notified.
343      */

344
345     public AttributeCollection getAttributes() throws XPathException {
346         return attributeCollection;
347     }
348
349     private AttributeCollectionImpl copyAttributeCollection(AttributeCollection in) {
350         AttributeCollectionImpl out = new AttributeCollectionImpl(getNamePool());
351         for (int i=0; i<in.getLength(); i++) {
352             out.addAttribute(in.getNameCode(i),
353                     in.getTypeAnnotation(i),
354                     in.getValue(i),
355                     in.getLocationId(i),
356                     in.getProperties(i));
357         }
358         return out;
359     }
360
361     /**
362      * Get the namespace declarations associated with the current element. This method must
363      * be called only after a START_ELEMENT event has been notified. In the case of a top-level
364      * START_ELEMENT event (that is, an element that either has no parent node, or whose parent
365      * is not included in the sequence being read), the NamespaceDeclarations object returned
366      * will contain a namespace declaration for each namespace that is in-scope for this element
367      * node. In the case of a non-top-level element, the NamespaceDeclarations will contain
368      * a set of namespace declarations and undeclarations, representing the differences between
369      * this element and its parent.
370      * <p/>
371      * <p>It is permissible for this method to return namespace declarations that are redundant.</p>
372      * <p/>
373      * <p>The NamespaceDeclarations object is guaranteed to remain unchanged until the next START_ELEMENT
374      * event, but may then be overwritten. The object should not be modified by the client.</p>
375      * <p/>
376      * <p>Namespaces may be read before or after reading the attributes of an element,
377      * but must not be read after the first child node has been read, or after calling
378      * one of the methods skipToEnd(), getStringValue(), or getTypedValue().</p>*
379      */

380
381     public NamespaceDeclarations getNamespaceDeclarations() throws XPathException {
382         return declaredNamespaces;
383     }
384
385     /**
386     * endElement: Discard the namespaces declared on this element.
387     */

388
389
390     public void endElement () throws XPathException
391     {
392         if (depth-- == 0) {
393             throw new IllegalStateException JavaDoc("Attempt to output end tag with no matching start tag");
394         }
395
396         int nscount = countStack[depth];
397         allNamespacesSize -= nscount;
398     }
399
400     /**
401      * Get the URI code corresponding to a given prefix code, by searching the
402      * in-scope namespaces. This is a service provided to subclasses.
403      * @param prefixCode the 16-bit prefix code required
404      * @return the 16-bit URI code, or -1 if the prefix is not found
405      */

406
407     protected short getURICode(short prefixCode) {
408         for (int i=allNamespacesSize-1; i>=0; i--) {
409             if ((allNamespaces[i]>>16) == (prefixCode)) {
410                 return (short)(allNamespaces[i]&0xffff);
411             }
412         }
413         if (prefixCode == 0) {
414             return 0; // by default, no prefix means no namespace URI
415
} else {
416             return -1;
417         }
418     }
419
420     /**
421      * Get the namespace URI corresponding to a given prefix. Return null
422      * if the prefix is not in scope.
423      *
424      * @param prefix the namespace prefix
425      * @param useDefault true if the default namespace is to be used when the
426      * prefix is ""
427      * @return the uri for the namespace, or null if the prefix is not in scope
428      */

429
430     public String JavaDoc getURIForPrefix(String JavaDoc prefix, boolean useDefault) {
431         NamePool pool = getNamePool();
432         if ("".equals(prefix) && !useDefault) {
433             return "";
434         } else if ("xml".equals(prefix)) {
435             return NamespaceConstant.XML;
436         } else {
437             short prefixCode = pool.getCodeForPrefix(prefix);
438             short uriCode = getURICode(prefixCode);
439             if (uriCode == -1) {
440                 return null;
441             }
442             return pool.getURIFromURICode(uriCode);
443         }
444     }
445
446     /**
447      * Get an iterator over all the prefixes declared in this namespace context. This will include
448      * the default namespace (prefix="") and the XML namespace where appropriate
449      */

450
451     public Iterator JavaDoc iteratePrefixes() {
452         NamePool pool = getNamePool();
453         List JavaDoc prefixes = new ArrayList JavaDoc(allNamespacesSize);
454         for (int i=allNamespacesSize-1; i>=0; i--) {
455             String JavaDoc prefix = pool.getPrefixFromNamespaceCode(allNamespaces[i]);
456             if (!prefixes.contains(prefix)) {
457                 prefixes.add(prefix);
458             }
459         }
460         prefixes.add("xml");
461         return prefixes.iterator();
462     }
463
464
465 }
466
467 //
468
// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
469
// you may not use this file except in compliance with the License. You may obtain a copy of the
470
// License at http://www.mozilla.org/MPL/
471
//
472
// Software distributed under the License is distributed on an "AS IS" basis,
473
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
474
// See the License for the specific language governing rights and limitations under the License.
475
//
476
// The Original Code is: all this file.
477
//
478
// The Initial Developer of the Original Code is Michael H. Kay.
479
//
480
// Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
481
//
482
// Contributor(s): none.
483
//
484
Popular Tags