001: package net.sf.saxon.pattern;
002:
003: import net.sf.saxon.om.FingerprintedNode;
004: import net.sf.saxon.om.NamePool;
005: import net.sf.saxon.om.NodeInfo;
006: import net.sf.saxon.sort.IntHashSet;
007: import net.sf.saxon.tinytree.TinyTree;
008: import net.sf.saxon.type.ItemType;
009: import net.sf.saxon.type.Type;
010: import net.sf.saxon.type.TypeHierarchy;
011:
012: /**
013: * NodeTest is an interface that enables a test of whether a node has a particular
014: * name and type. A NameTest matches the node kind and the namespace URI and the local
015: * name.
016: *
017: * @author Michael H. Kay
018: */
019:
020: public class NameTest extends NodeTest {
021:
022: private int nodeKind;
023: private int fingerprint;
024: private NamePool namePool;
025: private String uri = null;
026: private String localName = null;
027:
028: public NameTest(int nodeType, int nameCode, NamePool namePool) {
029: this .nodeKind = nodeType;
030: this .fingerprint = nameCode & 0xfffff;
031: this .namePool = namePool;
032: }
033:
034: /**
035: * Create a NameTest for nodes of the same type and name as a given node
036: */
037:
038: public NameTest(NodeInfo node) {
039: this .nodeKind = node.getNodeKind();
040: this .fingerprint = node.getFingerprint();
041: this .namePool = node.getNamePool();
042: }
043:
044: /**
045: * Test whether this node test is satisfied by a given node
046: * @param nodeKind The type of node to be matched
047: * @param nameCode identifies the expanded name of the node to be matched
048: */
049:
050: public boolean matches(int nodeKind, int nameCode, int annotation) {
051: // System.err.println("Matching node " + nameCode + " against " + this.fingerprint);
052: // System.err.println(" " + ((nameCode&0xfffff) == this.fingerprint && nodeType == this.nodeType));
053: return ((nameCode & 0xfffff) == this .fingerprint && nodeKind == this .nodeKind);
054: // deliberately in this order for speed (first test usually fails)
055: }
056:
057: /**
058: * Test whether this node test is satisfied by a given node on a TinyTree. The node
059: * must be a document, element, text, comment, or processing instruction node.
060: * This method is provided so that when navigating a TinyTree a node can be rejected without
061: * actually instantiating a NodeInfo object.
062: *
063: * @param tree the TinyTree containing the node
064: * @param nodeNr the number of the node within the TinyTree
065: * @return true if the node matches the NodeTest, otherwise false
066: */
067:
068: public boolean matches(TinyTree tree, int nodeNr) {
069: return ((tree.getNameCode(nodeNr) & 0xfffff) == this .fingerprint && tree
070: .getNodeKind(nodeNr) == this .nodeKind);
071: }
072:
073: /**
074: * Test whether this node test is satisfied by a given node. This alternative
075: * method is used in the case of nodes where calculating the fingerprint is expensive,
076: * for example DOM or JDOM nodes.
077: * @param node the node to be matched
078: */
079:
080: public boolean matches(NodeInfo node) {
081: if (node.getNodeKind() != nodeKind) {
082: return false;
083: }
084:
085: // Two different algorithms are used for name matching. If the fingerprint of the node is readily
086: // available, we use it to do an integer comparison. Otherwise, we do string comparisons on the URI
087: // and local name. In practice, Saxon's native node implementations use fingerprint matching, while
088: // DOM and JDOM nodes use string comparison of names
089:
090: if (node instanceof FingerprintedNode) {
091: return node.getFingerprint() == fingerprint;
092: } else {
093: if (uri == null) {
094: uri = namePool.getURI(fingerprint);
095: }
096: if (localName == null) {
097: localName = namePool.getLocalName(fingerprint);
098: }
099: return localName.equals(node.getLocalPart())
100: && uri.equals(node.getURI());
101: }
102: }
103:
104: /**
105: * Determine the default priority of this node test when used on its own as a Pattern
106: */
107:
108: public final double getDefaultPriority() {
109: return 0.0;
110: }
111:
112: /**
113: * Get the fingerprint required
114: */
115:
116: public int getFingerprint() {
117: return fingerprint;
118: }
119:
120: /**
121: * Determine the types of nodes to which this pattern applies. Used for optimisation.
122: * For patterns that match nodes of several types, return Type.NODE
123: * @return the type of node matched by this pattern. e.g. Type.ELEMENT or Type.TEXT
124: */
125:
126: public int getPrimitiveType() {
127: return nodeKind;
128: }
129:
130: /**
131: * Get the type from which this item type is derived by restriction. This
132: * is the supertype in the XPath type heirarchy, as distinct from the Schema
133: * base type: this means that the supertype of xs:boolean is xdt:anyAtomicType,
134: * whose supertype is item() (rather than xs:anySimpleType).
135: * <p>
136: * In fact the concept of "supertype" is not really well-defined, because the types
137: * form a lattice rather than a hierarchy. The only real requirement on this function
138: * is that it returns a type that strictly subsumes this type, ideally as narrowly
139: * as possible.
140: * @return the supertype, or null if this type is item()
141: * @param th
142: */
143:
144: public ItemType getSuperType(TypeHierarchy th) {
145: return NodeKindTest.makeNodeKindTest(nodeKind);
146: }
147:
148: /**
149: * Get a mask indicating which kinds of nodes this NodeTest can match. This is a combination
150: * of bits: 1<<Type.ELEMENT for element nodes, 1<<Type.TEXT for text nodes, and so on.
151: */
152:
153: public int getNodeKindMask() {
154: return 1 << nodeKind;
155: }
156:
157: /**
158: * Get the set of node names allowed by this NodeTest. This is returned as a set of Integer fingerprints.
159: * A null value indicates that all names are permitted (i.e. that there are no constraints on the node name.
160: * The default implementation returns null.
161: */
162:
163: public IntHashSet getRequiredNodeNames() {
164: IntHashSet s = new IntHashSet(1);
165: s.add(fingerprint);
166: return s;
167: }
168:
169: public String toString() {
170: return toString(namePool);
171: }
172:
173: public String toString(NamePool pool) {
174: switch (nodeKind) {
175: case Type.ELEMENT:
176: return "element(" + pool.getClarkName(fingerprint) + ')';
177: case Type.ATTRIBUTE:
178: return "attribute(" + pool.getClarkName(fingerprint) + ')';
179: case Type.PROCESSING_INSTRUCTION:
180: return "processing-instruction("
181: + pool.getDisplayName(fingerprint) + ')';
182: case Type.NAMESPACE:
183: return "namespace(" + pool.getDisplayName(fingerprint)
184: + ')';
185: }
186: return pool.getDisplayName(fingerprint);
187: }
188:
189: /**
190: * Returns a hash code value for the object.
191: */
192:
193: public int hashCode() {
194: return nodeKind << 20 ^ fingerprint;
195: }
196:
197: /**
198: * Determines whether two NameTests are equal
199: */
200:
201: public boolean equals(Object other) {
202: return other instanceof NameTest
203: && ((NameTest) other).namePool == namePool
204: && ((NameTest) other).nodeKind == nodeKind
205: && ((NameTest) other).fingerprint == fingerprint;
206: }
207:
208: }
209:
210: //
211: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
212: // you may not use this file except in compliance with the License. You may obtain a copy of the
213: // License at http://www.mozilla.org/MPL/
214: //
215: // Software distributed under the License is distributed on an "AS IS" basis,
216: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
217: // See the License for the specific language governing rights and limitations under the License.
218: //
219: // The Original Code is: all this file.
220: //
221: // The Initial Developer of the Original Code is Michael H. Kay.
222: //
223: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
224: //
225: // Contributor(s): none.
226: //
|