001: package net.sf.saxon.tinytree;
002:
003: import net.sf.saxon.event.Receiver;
004: import net.sf.saxon.om.NamespaceConstant;
005: import net.sf.saxon.om.NamespaceResolver;
006: import net.sf.saxon.om.Navigator;
007: import net.sf.saxon.om.NodeInfo;
008: import net.sf.saxon.style.StandardNames;
009: import net.sf.saxon.trans.XPathException;
010: import net.sf.saxon.type.Type;
011:
012: /**
013: * A node in the XML parse tree representing an XML element.<P>
014: * This class is an implementation of NodeInfo. The object is a wrapper around
015: * one entry in the arrays maintained by the TinyTree. Note that the same node
016: * might be represented by different TinyElementImpl objects at different times.
017: * @author Michael H. Kay
018: */
019:
020: final class TinyElementImpl extends TinyParentNodeImpl {
021:
022: /**
023: * Constructor
024: */
025:
026: public TinyElementImpl(TinyTree tree, int nodeNr) {
027: this .tree = tree;
028: this .nodeNr = nodeNr;
029: }
030:
031: /**
032: * Return the type of node.
033: * @return Type.ELEMENT
034: */
035:
036: public final int getNodeKind() {
037: return Type.ELEMENT;
038: }
039:
040: /**
041: * Get the base URI of this element node. This will be the same as the System ID unless
042: * xml:base has been used.
043: */
044:
045: public String getBaseURI() {
046: return Navigator.getBaseURI(this );
047: }
048:
049: /**
050: * Get the type annotation of this node, if any
051: * Returns Type.UNTYPED_ANY if there is no type annotation
052: */
053:
054: public int getTypeAnnotation() {
055: return tree.getTypeAnnotation(nodeNr);
056: }
057:
058: /**
059: * Output all namespace nodes associated with this element.
060: * @param out The relevant outputter
061: * @param includeAncestors True if namespaces associated with ancestor
062: */
063:
064: public void sendNamespaceDeclarations(Receiver out,
065: boolean includeAncestors) throws XPathException {
066:
067: if (!tree.usesNamespaces) {
068: return;
069: }
070:
071: int ns = tree.beta[nodeNr]; // by convention
072: if (ns > 0) {
073: while (ns < tree.numberOfNamespaces
074: && tree.namespaceParent[ns] == nodeNr) {
075: int nscode = tree.namespaceCode[ns];
076: out.namespace(nscode, 0);
077: ns++;
078: }
079: }
080:
081: // now add the namespaces defined on the ancestor nodes. We rely on the receiver
082: // to eliminate multiple declarations of the same prefix
083:
084: if (includeAncestors) {
085: NodeInfo parent = getParent();
086: if (parent != null) {
087: parent.sendNamespaceDeclarations(out, true);
088: }
089: // terminates when the parent is a root node
090: }
091: }
092:
093: /**
094: * Get all namespace undeclarations and undeclarations defined on this element.
095: *
096: * @param buffer If this is non-null, and the result array fits in this buffer, then the result
097: * may overwrite the contents of this array, to avoid the cost of allocating a new array on the heap.
098: * @return An array of integers representing the namespace declarations and undeclarations present on
099: * this element. For a node other than an element, return null. Otherwise, the returned array is a
100: * sequence of namespace codes, whose meaning may be interpreted by reference to the name pool. The
101: * top half word of each namespace code represents the prefix, the bottom half represents the URI.
102: * If the bottom half is zero, then this is a namespace undeclaration rather than a declaration.
103: * The XML namespace is never included in the list. If the supplied array is larger than required,
104: * then the first unused entry will be set to -1.
105: * <p/>
106: * <p>For a node other than an element, the method returns null.</p>
107: */
108:
109: public int[] getDeclaredNamespaces(int[] buffer) {
110: return getDeclaredNamespaces(tree, nodeNr, buffer);
111: }
112:
113: /**
114: * Static method to get all namespace undeclarations and undeclarations defined on a given element,
115: * without instantiating the node object.
116: * @param tree The tree containing the given element node
117: * @param nodeNr The node number of the given element node within the tinyTree
118: * @param buffer If this is non-null, and the result array fits in this buffer, then the result
119: * may overwrite the contents of this array, to avoid the cost of allocating a new array on the heap.
120: * @return An array of integers representing the namespace declarations and undeclarations present on
121: * this element. For a node other than an element, return null. Otherwise, the returned array is a
122: * sequence of namespace codes, whose meaning may be interpreted by reference to the name pool. The
123: * top half word of each namespace code represents the prefix, the bottom half represents the URI.
124: * If the bottom half is zero, then this is a namespace undeclaration rather than a declaration.
125: * The XML namespace is never included in the list. If the supplied array is larger than required,
126: * then the first unused entry will be set to -1.
127: * <p/>
128: * <p>For a node other than an element, the method returns null.</p>
129: */
130:
131: static final int[] getDeclaredNamespaces(TinyTree tree, int nodeNr,
132: int[] buffer) {
133: int ns = tree.beta[nodeNr]; // by convention
134: if (ns > 0) {
135: int count = 0;
136: while (ns < tree.numberOfNamespaces
137: && tree.namespaceParent[ns] == nodeNr) {
138: count++;
139: ns++;
140: }
141: if (count == 0) {
142: return NodeInfo.EMPTY_NAMESPACE_LIST;
143: } else if (count <= buffer.length) {
144: System.arraycopy(tree.namespaceCode, tree.beta[nodeNr],
145: buffer, 0, count);
146: if (count < buffer.length) {
147: buffer[count] = -1;
148: }
149: return buffer;
150: } else {
151: int[] array = new int[count];
152: System.arraycopy(tree.namespaceCode, tree.beta[nodeNr],
153: array, 0, count);
154: return array;
155: }
156: } else {
157: return NodeInfo.EMPTY_NAMESPACE_LIST;
158: }
159: }
160:
161: /**
162: * Get all the inscope namespaces for an element node. This method is better than the generic method
163: * provided by {@link net.sf.saxon.om.NamespaceIterator} because it doesn't require the element node
164: * (or its ancestors) to be instantiated as objects.
165: * @param tree the TinyTree containing the element node whose in-scope namespaces are required
166: * @param nodeNr the node number of the element node within the TinyTree. The caller is responsible
167: * for ensuring that this is indeed an element node
168: * @param buffer a buffer to hold the result, assuming it is large enough
169: * @return an integer array of namespace codes representing the inscope namespaces of the given element.
170: * The returned array will either be fully used, or it will contain a -1 entry marking the effective end
171: * of the list of namespace codes. Note that only distinct declared namespaces are included in the result;
172: * it does not contain any entries for namespace undeclarations or for overridden declarations.
173: */
174:
175: static final int[] getInScopeNamespaces(TinyTree tree, int nodeNr,
176: int[] buffer) {
177:
178: if (buffer == null || buffer.length == 0) {
179: buffer = new int[10];
180: }
181: buffer[0] = NamespaceConstant.XML_NAMESPACE_CODE;
182: int used = 1;
183:
184: if (tree.usesNamespaces) {
185: do {
186: // gather the namespaces declared for this node
187: int ns = tree.beta[nodeNr]; // by convention
188: if (ns > 0) {
189: while (ns < tree.numberOfNamespaces
190: && tree.namespaceParent[ns] == nodeNr) {
191: int nscode = tree.namespaceCode[ns];
192:
193: // See if the prefix has already been declared; if so, this declaration is ignored
194: short prefixCode = (short) (nscode >> 16);
195: boolean duplicate = false;
196: for (int i = 0; i < used; i++) {
197: if ((buffer[i] >> 16) == prefixCode) {
198: duplicate = true;
199: break;
200: }
201: }
202: if (!duplicate) {
203: if (used >= buffer.length) {
204: int[] b2 = new int[used * 2];
205: System
206: .arraycopy(buffer, 0, b2, 0,
207: used);
208: buffer = b2;
209: }
210: buffer[used++] = nscode;
211: }
212: ns++;
213: }
214: }
215:
216: // move on to the parent of this node
217: nodeNr = getParentNodeNr(tree, nodeNr);
218: } while (nodeNr != -1);
219:
220: // The list of namespaces we have built up includes undeclarations as well as declarations.
221: // We now remove the undeclarations (which have a URI code of zero)
222:
223: int j = 0;
224: for (int i = 0; i < used; i++) {
225: int nscode = buffer[i];
226: if ((nscode & 0xffff) != 0) {
227: buffer[j++] = nscode;
228: }
229: }
230: used = j;
231: }
232:
233: // If there are unused entries at the end of the array, add a -1 to mark the end
234: if (used < buffer.length) {
235: buffer[used] = -1;
236: }
237:
238: return buffer;
239: }
240:
241: /**
242: * Get the value of a given attribute of this node
243: * @param fingerprint The fingerprint of the attribute name
244: * @return the attribute value if it exists or null if not
245: */
246:
247: public String getAttributeValue(int fingerprint) {
248: int a = tree.alpha[nodeNr];
249: if (a < 0)
250: return null;
251: while (a < tree.numberOfAttributes
252: && tree.attParent[a] == nodeNr) {
253: if ((tree.attCode[a] & 0xfffff) == fingerprint) {
254: return tree.attValue[a].toString();
255: }
256: a++;
257: }
258: return null;
259: }
260:
261: /**
262: * Copy this node to a given receiver
263: * @param whichNamespaces indicates which namespaces should be copied: all, none,
264: * or local (i.e., those not declared on a parent element)
265: */
266:
267: public void copy(Receiver receiver, int whichNamespaces,
268: boolean copyAnnotations, int locationId)
269: throws XPathException {
270:
271: // Based on an algorithm supplied by Ruud Diterwich
272:
273: // Performance measurements show that this achieves no speed-up over the OLD version
274: // (in 7.4). So might as well switch back.
275:
276: // control vars
277: short level = -1;
278: boolean closePending = false;
279: short startLevel = tree.depth[nodeNr];
280: boolean first = true;
281: int next = nodeNr;
282:
283: // document.diagnosticDump();
284:
285: do {
286:
287: // determine node depth
288: short nodeLevel = tree.depth[next];
289:
290: // extra close required?
291: if (closePending) {
292: level++;
293: }
294:
295: // close former elements
296: for (; level > nodeLevel; level--) {
297: receiver.endElement();
298: }
299:
300: // new node level
301: level = nodeLevel;
302:
303: // output depends on node type
304: switch (tree.nodeKind[next]) {
305: case Type.ELEMENT: {
306:
307: // start element
308: receiver.startElement(tree.nameCode[next],
309: (copyAnnotations ? tree.getTypeAnnotation(next)
310: : StandardNames.XDT_UNTYPED),
311: locationId, 0);
312: //(first ? ReceiverOptions.DISINHERIT_NAMESPACES : 0));
313:
314: // there is an element to close
315: closePending = true;
316:
317: // output namespaces
318: if (whichNamespaces != NO_NAMESPACES
319: && tree.usesNamespaces) {
320: if (first) {
321: sendNamespaceDeclarations(receiver,
322: whichNamespaces == ALL_NAMESPACES);
323: } else {
324: int ns = tree.beta[next]; // by convention
325: if (ns > 0) {
326: while (ns < tree.numberOfNamespaces
327: && tree.namespaceParent[ns] == next) {
328: int nscode = tree.namespaceCode[ns];
329: receiver.namespace(nscode, 0);
330: ns++;
331: }
332: }
333: }
334: }
335: first = false;
336:
337: // output attributes
338:
339: int att = tree.alpha[next];
340: if (att >= 0) {
341: while (att < tree.numberOfAttributes
342: && tree.attParent[att] == next) {
343: int attCode = tree.attCode[att];
344: int attType = (copyAnnotations ? tree
345: .getAttributeAnnotation(att) : -1);
346: receiver.attribute(attCode, attType,
347: tree.attValue[att], locationId, 0);
348: att++;
349: }
350: }
351:
352: // start content
353: receiver.startContent();
354: break;
355: }
356: case Type.TEXT: {
357:
358: // don't close text nodes
359: closePending = false;
360:
361: // output characters
362: int start = tree.alpha[next];
363: int len = tree.beta[next];
364: receiver.characters(new CharSlice(tree.charBuffer,
365: start, len), locationId, 0);
366: break;
367: }
368: case Type.COMMENT: {
369:
370: // don't close text nodes
371: closePending = false;
372:
373: // output copy of comment
374: int start = tree.alpha[next];
375: int len = tree.beta[next];
376: if (len > 0) {
377: receiver.comment(tree.commentBuffer.subSequence(
378: start, start + len), locationId, 0);
379: } else {
380: receiver.comment("", 0, 0);
381: }
382: break;
383: }
384: case Type.PROCESSING_INSTRUCTION: {
385:
386: // don't close text nodes
387: closePending = false;
388:
389: // output copy of PI
390: NodeInfo pi = tree.getNode(next);
391: receiver.processingInstruction(pi.getLocalPart(), pi
392: .getStringValue(), locationId, 0);
393: break;
394: }
395:
396: case Type.PARENT_POINTER: {
397: closePending = false;
398: }
399: }
400:
401: next++;
402:
403: } while (next < tree.numberOfNodes
404: && tree.depth[next] > startLevel);
405:
406: // close all remaining elements
407: if (closePending) {
408: level++;
409: }
410: for (; level > startLevel; level--) {
411: receiver.endElement();
412: }
413: }
414:
415: // public void copyOLD(Receiver out, int whichNamespaces, boolean copyAnnotations) throws XPathException {
416: //
417: // int nc = getNameCode();
418: // int typeCode = (copyAnnotations ? getTypeAnnotation() : 0);
419: // out.startElement(nc, typeCode, 0, 0);
420: //
421: // // output the namespaces
422: //
423: // if (whichNamespaces != NO_NAMESPACES) {
424: // outputNamespaceNodes(out, whichNamespaces==ALL_NAMESPACES);
425: // }
426: //
427: // // output the attributes
428: //
429: // int a = document.alpha[nodeNr];
430: // if (a >= 0) {
431: // while (a < document.numberOfAttributes && document.attParent[a] == nodeNr) {
432: // document.getAttributeNode(a).copy(out, NO_NAMESPACES, copyAnnotations, locationId);
433: // a++;
434: // }
435: // }
436: //
437: // // output the children
438: //
439: // AxisIterator children =
440: // iterateAxis(Axis.CHILD, AnyNodeTest.getInstance());
441: //
442: // int childNamespaces = (whichNamespaces==NO_NAMESPACES ? NO_NAMESPACES : LOCAL_NAMESPACES);
443: // while (true) {
444: // NodeInfo next = (NodeInfo)children.next();
445: // if (next==null) break;
446: // next.copy(out, childNamespaces, copyAnnotations, locationId);
447: // }
448: // out.endElement();
449: // }
450:
451: /**
452: * Get the namespace URI corresponding to a given prefix. Return null
453: * if the prefix is not in scope.
454: *
455: * @param prefix the namespace prefix. May be the zero-length string, indicating
456: * that there is no prefix. This indicates either the default namespace or the
457: * null namespace, depending on the value of useDefault.
458: * @param useDefault true if the default namespace is to be used when the
459: * prefix is "". If false, the method returns "" when the prefix is "".
460: * @return the uri for the namespace, or null if the prefix is not in scope.
461: * The "null namespace" is represented by the pseudo-URI "".
462: */
463:
464: public String getURIForPrefix(String prefix, boolean useDefault) {
465: if (!useDefault && "".equals(prefix)) {
466: return "";
467: }
468: int prefixCode = getNamePool().getCodeForPrefix(prefix);
469: if (prefixCode == -1) {
470: return null;
471: }
472: int ns = tree.beta[nodeNr]; // by convention
473: if (ns > 0) {
474: while (ns < tree.numberOfNamespaces
475: && tree.namespaceParent[ns] == nodeNr) {
476: int nscode = tree.namespaceCode[ns];
477: if ((nscode >> 16) == prefixCode) {
478: int uriCode = nscode & 0xffff;
479: if (uriCode == 0) {
480: // this is a namespace undeclaration, so the prefix is not in scope
481: if (prefixCode == 0) {
482: // the namespace xmlns="" is always in scope
483: return "";
484: } else {
485: return null;
486: }
487: } else {
488: return getNamePool().getURIFromURICode(
489: (short) uriCode);
490: }
491: }
492: ns++;
493: }
494: }
495:
496: // now search the namespaces defined on the ancestor nodes.
497:
498: NodeInfo parent = getParent();
499: if (parent instanceof NamespaceResolver) {
500: return ((NamespaceResolver) parent).getURIForPrefix(prefix,
501: useDefault);
502: }
503: return null;
504: }
505:
506: }
507:
508: //
509: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
510: // you may not use this file except in compliance with the License. You may obtain a copy of the
511: // License at http://www.mozilla.org/MPL/
512: //
513: // Software distributed under the License is distributed on an "AS IS" basis,
514: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
515: // See the License for the specific language governing rights and limitations under the License.
516: //
517: // The Original Code is: all this file.
518: //
519: // The Initial Developer of the Original Code is Michael H. Kay.
520: //
521: // The new copy() routine (in version 7.4.1) is contributed by Ruud Diterwich
522: //
523: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
524: //
525: // Contributor(s): none.
526: //
|