001: package net.sf.saxon.tree;
002:
003: import net.sf.saxon.event.LocationCopier;
004: import net.sf.saxon.event.Receiver;
005: import net.sf.saxon.om.*;
006: import net.sf.saxon.trans.XPathException;
007: import net.sf.saxon.type.Type;
008: import net.sf.saxon.style.StandardNames;
009:
010: import java.util.HashSet;
011: import java.util.Iterator;
012: import java.util.Set;
013:
014: /**
015: * A node in the XML parse tree representing an XML element.<P>
016: * This class is an implementation of NodeInfo
017: * @author Michael H. Kay
018: * @version 8 August 2000: separated from ElementImpl
019: */
020:
021: // The name of the element and its attributes are now namespace-resolved by the
022: // parser. However, this class retains the ability to do namespace resolution for other
023: // names, for example variable and template names in a stylesheet.
024: public class ElementWithAttributes extends ElementImpl {
025:
026: protected AttributeCollection attributeList; // this excludes namespace attributes
027: protected int[] namespaceList = null; // list of namespace codes
028:
029: // note that this namespace list includes only the namespaces actually defined on
030: // this element, not those inherited from outer elements.
031:
032: /**
033: * Initialise a new ElementWithAttributes with an element name and attribute list
034: * @param nameCode The element name, with namespaces resolved
035: * @param atts The attribute list, after namespace processing
036: * @param parent The parent node
037: */
038:
039: public void initialise(int nameCode, AttributeCollectionImpl atts,
040: NodeInfo parent, String baseURI, int lineNumber,
041: int sequenceNumber) {
042: this .nameCode = nameCode;
043: this .attributeList = atts;
044: this .parent = (ParentNodeImpl) parent;
045: this .sequence = sequenceNumber;
046: this .root = (DocumentImpl) parent.getDocumentRoot();
047: root.setLineNumber(sequenceNumber, lineNumber);
048: root.setSystemId(sequenceNumber, baseURI);
049: }
050:
051: /**
052: * Set the namespace declarations for the element
053: */
054:
055: public void setNamespaceDeclarations(int[] namespaces,
056: int namespacesUsed) {
057: namespaceList = new int[namespacesUsed];
058: System.arraycopy(namespaces, 0, namespaceList, 0,
059: namespacesUsed);
060: }
061:
062: /**
063: * Get the namespace URI corresponding to a given prefix. Return null
064: * if the prefix is not in scope.
065: *
066: * @param prefix the namespace prefix. May be the zero-length string, indicating
067: * that there is no prefix. This indicates either the default namespace or the
068: * null namespace, depending on the value of useDefault.
069: * @param useDefault true if the default namespace is to be used when the
070: * prefix is "". If false, the method returns "" when the prefix is "".
071: * @return the uri for the namespace, or null if the prefix is not in scope.
072: * The "null namespace" is represented by the pseudo-URI "".
073: */
074:
075: public String getURIForPrefix(String prefix, boolean useDefault) {
076: if (prefix.equals("xml")) {
077: return NamespaceConstant.XML;
078: }
079: if (prefix.equals("") && !useDefault) {
080: return "";
081: }
082:
083: NamePool pool = getNamePool();
084: int prefixCode = pool.getCodeForPrefix(prefix);
085: if (prefixCode == -1) {
086: return null;
087: }
088: try {
089: short uriCode = getURICodeForPrefixCode(prefixCode);
090: return pool.getURIFromURICode(uriCode);
091: } catch (NamespaceException e) {
092: return null;
093: }
094: }
095:
096: /**
097: * Get an iterator over all the prefixes declared in this namespace context. This will include
098: * the default namespace (prefix="") and the XML namespace where appropriate
099: */
100:
101: public Iterator iteratePrefixes() {
102: Set inScope = new HashSet(10);
103: Set outOfScope = new HashSet(10);
104: inScope.add("");
105: inScope.add("xml");
106: gatherNamespacePrefixes(getNamePool(), inScope, outOfScope);
107: return inScope.iterator();
108: }
109:
110: /**
111: * Search the NamespaceList for a given prefix, returning the corresponding URI.
112: * @param prefix The prefix to be matched. To find the default namespace, supply ""
113: * @return The URI code corresponding to this namespace. If it is an unnamed default namespace,
114: * return Namespace.NULL_CODE.
115: * @throws NamespaceException if the prefix has not been declared on this NamespaceList.
116: */
117:
118: public short getURICodeForPrefix(String prefix)
119: throws NamespaceException {
120: if (prefix.equals("xml"))
121: return NamespaceConstant.XML_CODE;
122:
123: NamePool pool = getNamePool();
124: int prefixCode = pool.getCodeForPrefix(prefix);
125: if (prefixCode == -1) {
126: throw new NamespaceException(prefix);
127: }
128: return getURICodeForPrefixCode(prefixCode);
129: }
130:
131: private short getURICodeForPrefixCode(int prefixCode)
132: throws NamespaceException {
133: if (namespaceList != null) {
134: for (int i = 0; i < namespaceList.length; i++) {
135: if ((namespaceList[i] >> 16) == prefixCode) {
136: return (short) (namespaceList[i] & 0xffff);
137: }
138: }
139: }
140: NodeInfo next = parent;
141: while (true) {
142: if (next.getNodeKind() == Type.DOCUMENT) {
143: // prefixCode==0 represents the empty namespace prefix ""
144: if (prefixCode == 0)
145: return NamespaceConstant.NULL_CODE;
146: throw new NamespaceException(getNamePool()
147: .getPrefixFromNamespaceCode(prefixCode << 16));
148: } else if (next instanceof ElementWithAttributes) {
149: return ((ElementWithAttributes) next)
150: .getURICodeForPrefixCode(prefixCode);
151: } else {
152: next = next.getParent();
153: }
154: }
155: }
156:
157: /**
158: * Search the NamespaceList for a given URI, returning the corresponding prefix.
159: * @param uri The URI to be matched.
160: * @return The prefix corresponding to this URI. If not found, return null. If there is
161: * more than one prefix matching the URI, the first one found is returned. If the URI matches
162: * the default namespace, return an empty string.
163: */
164:
165: public String getPrefixForURI(String uri) {
166: if (uri.equals(NamespaceConstant.XML))
167: return "xml";
168:
169: NamePool pool = getNamePool();
170: int uriCode = pool.getCodeForURI(uri);
171: if (uriCode < 0)
172: return null;
173: return getPrefixForURICode(uriCode);
174: }
175:
176: private String getPrefixForURICode(int code) {
177: if (namespaceList != null) {
178: for (int i = 0; i < namespaceList.length; i++) {
179: if ((namespaceList[i] & 0xffff) == code) {
180: return getNamePool().getPrefixFromNamespaceCode(
181: namespaceList[i]);
182: }
183: }
184: }
185: NodeInfo next = parent;
186: while (true) {
187: if (next instanceof DocumentInfo) {
188: return null;
189: } else if (next instanceof ElementWithAttributes) {
190: return ((ElementWithAttributes) next)
191: .getPrefixForURICode(code);
192: } else {
193: next = next.getParent();
194: }
195: }
196: }
197:
198: private void gatherNamespacePrefixes(NamePool pool, Set inScope,
199: Set outOfScope) {
200: if (namespaceList != null) {
201: for (int i = 0; i < namespaceList.length; i++) {
202: int nscode = namespaceList[i];
203: String prefix = pool.getPrefixFromNamespaceCode(nscode);
204: if ((nscode & 0xffff) == 0) {
205: // this is an undeclaration
206: outOfScope.add(prefix);
207: } else if (!outOfScope.contains(prefix)) {
208: inScope.add(prefix);
209: outOfScope.add(prefix);
210: }
211: }
212: }
213:
214: // now add the namespaces defined on the ancestor nodes
215:
216: NodeInfo parent = getParent();
217: while (parent != null) {
218: if (parent instanceof ElementWithAttributes) {
219: ((ElementWithAttributes) parent)
220: .gatherNamespacePrefixes(pool, inScope,
221: outOfScope);
222: }
223: }
224: }
225:
226: /**
227: * Output all namespace nodes associated with this element.
228: * @param out The relevant outputter
229: */
230:
231: public void sendNamespaceDeclarations(Receiver out,
232: boolean includeAncestors) throws XPathException {
233:
234: if (namespaceList != null) {
235: for (int i = 0; i < namespaceList.length; i++) {
236: out.namespace(namespaceList[i], 0);
237: }
238: }
239:
240: // now add the namespaces defined on the ancestor nodes. We rely on the outputter
241: // to eliminate multiple declarations of the same prefix
242:
243: if (includeAncestors) {
244: if (parent.getNodeKind() != Type.DOCUMENT) {
245: parent.sendNamespaceDeclarations(out, true);
246: }
247: }
248: }
249:
250: /**
251: * Get all namespace undeclarations and undeclarations defined on this element.
252: *
253: * @param buffer If this is non-null, and the result array fits in this buffer, then the result
254: * may overwrite the contents of this array, to avoid the cost of allocating a new array on the heap.
255: * @return An array of integers representing the namespace declarations and undeclarations present on
256: * this element. For a node other than an element, return null. Otherwise, the returned array is a
257: * sequence of namespace codes, whose meaning may be interpreted by reference to the name pool. The
258: * top half word of each namespace code represents the prefix, the bottom half represents the URI.
259: * If the bottom half is zero, then this is a namespace undeclaration rather than a declaration.
260: * The XML namespace is never included in the list. If the supplied array is larger than required,
261: * then the first unused entry will be set to -1.
262: * <p/>
263: * <p>For a node other than an element, the method returns null.</p>
264: */
265:
266: public int[] getDeclaredNamespaces(int[] buffer) {
267: return namespaceList;
268: }
269:
270: /**
271: * Get the list of in-scope namespaces for this element as an array of
272: * namespace codes. (Used by LiteralResultElement)
273: */
274:
275: public int[] getInScopeNamespaceCodes() {
276: return new NamespaceIterator(this , null)
277: .getInScopeNamespaceCodes();
278: }
279:
280: /**
281: * Get the attribute list for this element.
282: * @return The attribute list. This will not include any
283: * namespace attributes. The attribute names will be in expanded form, with prefixes
284: * replaced by URIs
285: */
286:
287: public AttributeCollection getAttributeList() {
288: return attributeList;
289: }
290:
291: /**
292: * Get the value of a given attribute of this node
293: * @param fingerprint The fingerprint of the attribute name
294: * @return the attribute value if it exists or null if not
295: */
296:
297: public String getAttributeValue(int fingerprint) {
298: return attributeList.getValueByFingerprint(fingerprint);
299: }
300:
301: /**
302: * Copy this node to a given outputter (supporting xsl:copy-of)
303: * @param out The outputter
304: * @param whichNamespaces indicates which namespaces should be output: all, none, or local
305: * namespaces only (those not declared on the parent element)
306: */
307:
308: public void copy(Receiver out, int whichNamespaces,
309: boolean copyAnnotations, int locationId)
310: throws XPathException {
311:
312: int typeCode = (copyAnnotations ? getTypeAnnotation() : -1);
313: if (locationId == 0 && out instanceof LocationCopier) {
314: out.setSystemId(getBaseURI());
315: ((LocationCopier) out).setLineNumber(getLineNumber());
316: }
317: out.startElement(getNameCode(), typeCode, locationId, 0);
318:
319: // output the namespaces
320:
321: if (whichNamespaces != NO_NAMESPACES) {
322: sendNamespaceDeclarations(out,
323: whichNamespaces == ALL_NAMESPACES);
324: }
325:
326: // output the attributes
327:
328: for (int i = 0; i < attributeList.getLength(); i++) {
329: out.attribute(attributeList.getNameCode(i),
330: StandardNames.XDT_UNTYPED_ATOMIC, attributeList
331: .getValue(i), 0, 0);
332: }
333:
334: out.startContent();
335:
336: // output the children
337:
338: int childNamespaces = (whichNamespaces == NO_NAMESPACES ? NO_NAMESPACES
339: : LOCAL_NAMESPACES);
340: NodeImpl next = (NodeImpl) getFirstChild();
341: while (next != null) {
342: next
343: .copy(out, childNamespaces, copyAnnotations,
344: locationId);
345: next = (NodeImpl) next.getNextSibling();
346: }
347:
348: out.endElement();
349: }
350:
351: }
352:
353: //
354: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
355: // you may not use this file except in compliance with the License. You may obtain a copy of the
356: // License at http://www.mozilla.org/MPL/
357: //
358: // Software distributed under the License is distributed on an "AS IS" basis,
359: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
360: // See the License for the specific language governing rights and limitations under the License.
361: //
362: // The Original Code is: all this file.
363: //
364: // The Initial Developer of the Original Code is Michael H. Kay.
365: //
366: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
367: //
368: // Contributor(s): none.
369: //
|