001: package net.sf.saxon.pattern;
002:
003: import net.sf.saxon.Configuration;
004: import net.sf.saxon.tinytree.TinyTree;
005: import net.sf.saxon.functions.Nilled;
006: import net.sf.saxon.om.NodeInfo;
007: import net.sf.saxon.om.NamePool;
008: import net.sf.saxon.style.StandardNames;
009: import net.sf.saxon.type.*;
010:
011: /**
012: * NodeTest is an interface that enables a test of whether a node matches particular
013: * conditions. ContentTypeTest tests for an element or attribute node with a particular
014: * type annotation.
015: *
016: * @author Michael H. Kay
017: */
018:
019: public class ContentTypeTest extends NodeTest {
020:
021: private int kind; // element or attribute
022: private SchemaType schemaType;
023: private int requiredType;
024: private Configuration config;
025: private boolean nillable = false;
026: private boolean matchDTDTypes = false;
027:
028: /**
029: * Create a ContentTypeTest
030: * @param nodeKind the kind of nodes to be matched: always elements or attributes
031: * @param schemaType the required type annotation, as a simple or complex schema type
032: * @param config the Configuration, supplied because this KindTest needs access to schema information
033: */
034:
035: public ContentTypeTest(int nodeKind, SchemaType schemaType,
036: Configuration config) {
037: this .kind = nodeKind;
038: this .schemaType = schemaType;
039: this .requiredType = schemaType.getFingerprint();
040: if (requiredType == -1) {
041: requiredType = StandardNames.XDT_UNTYPED; // probably doesn't happen
042: }
043: this .config = config;
044: }
045:
046: /**
047: * Indicate whether nilled elements should be matched (the default is false)
048: * @param nillable true if nilled elements should be matched
049: */
050: public void setNillable(boolean nillable) {
051: this .nillable = nillable;
052: }
053:
054: /**
055: * The test is nillable if "?" was specified in the SequenceType syntax
056: * @return true if the test is nillable
057: */
058:
059: public boolean isNillable() {
060: return nillable;
061: }
062:
063: /**
064: * Indicate whether DTD-derived content types should be matched (the default is false)
065: * @param matched true if DTD-derived types should be matched. If false, DTD-derived types are treated
066: * as untypedAtomic
067: */
068:
069: public void setMatchDTDTypes(boolean matched) {
070: this .matchDTDTypes = matched;
071: }
072:
073: /**
074: * Test whether DTD-derived content types should be matched (the default is false)
075: * @return true if DTD-derived types should be matched. If false, DTD-derived types are treated
076: * as untypedAtomic
077: */
078:
079: public boolean matchesDTDTypes() {
080: return matchDTDTypes;
081: }
082:
083: public SchemaType getSchemaType() {
084: return schemaType;
085: }
086:
087: public ItemType getSuperType(TypeHierarchy th) {
088: return NodeKindTest.makeNodeKindTest(kind);
089: }
090:
091: /**
092: * Test whether this node test is satisfied by a given node
093: * @param nodeKind The type of node to be matched
094: * @param fingerprint identifies the expanded name of the node to be matched
095: * @param annotation The actual content type of the node
096: */
097:
098: public boolean matches(int nodeKind, int fingerprint, int annotation) {
099: if (kind != nodeKind) {
100: return false;
101: }
102: return matchesAnnotation(annotation);
103: }
104:
105: /**
106: * Test whether this node test is satisfied by a given node on a TinyTree. The node
107: * must be a document, element, text, comment, or processing instruction node.
108: * This method is provided
109: * so that when navigating a TinyTree a node can be rejected without
110: * actually instantiating a NodeInfo object.
111: *
112: * @param tree the TinyTree containing the node
113: * @param nodeNr the number of the node within the TinyTree
114: * @return true if the node matches the NodeTest, otherwise false
115: */
116:
117: public boolean matches(TinyTree tree, int nodeNr) {
118: if (kind != tree.getNodeKind(nodeNr)) {
119: return false;
120: }
121: return matchesAnnotation(tree.getTypeAnnotation(nodeNr))
122: && (nillable || !tree.isNilled(nodeNr));
123: }
124:
125: /**
126: * Test whether this node test is satisfied by a given node. This alternative
127: * method is used in the case of nodes where calculating the fingerprint is expensive,
128: * for example DOM or JDOM nodes.
129: * @param node the node to be matched
130: */
131:
132: public boolean matches(NodeInfo node) {
133: return node.getNodeKind() == kind
134: && matchesAnnotation(node.getTypeAnnotation())
135: && (nillable || !Nilled.isNilled(node));
136: }
137:
138: private boolean matchesAnnotation(int annotation) {
139: if (requiredType == StandardNames.XS_ANY_TYPE) {
140: return true;
141: }
142:
143: if (annotation == -1) {
144: annotation = (kind == Type.ATTRIBUTE ? StandardNames.XDT_UNTYPED_ATOMIC
145: : StandardNames.XDT_UNTYPED);
146: }
147:
148: if (matchDTDTypes) {
149: annotation = annotation & NamePool.FP_MASK;
150: } else if (((annotation & NodeInfo.IS_DTD_TYPE) != 0)) {
151: return (requiredType == StandardNames.XDT_UNTYPED_ATOMIC);
152: }
153:
154: if (annotation == requiredType) {
155: return true;
156: }
157:
158: // see if the type annotation is a subtype of the required type
159:
160: try {
161: SchemaType type = config.getSchemaType(
162: annotation & NamePool.FP_MASK).getBaseType();
163: if (type == null) {
164: // only true if annotation = XS_ANY_TYPE
165: return false;
166: }
167: ItemType actual = new ContentTypeTest(kind, type, config);
168: return config.getNamePool().getTypeHierarchy().isSubType(
169: actual, this );
170: // while (type != null) {
171: // if (type.getFingerprint() == requiredType) {
172: // return true;
173: // }
174: // type = type.getBaseType();
175: // }
176: } catch (UnresolvedReferenceException e) {
177: throw new IllegalStateException(e.getMessage());
178: }
179: //return false;
180: }
181:
182: /**
183: * Determine the default priority of this node test when used on its own as a Pattern
184: */
185:
186: public final double getDefaultPriority() {
187: return 0;
188: }
189:
190: /**
191: * Determine the types of nodes to which this pattern applies. Used for optimisation.
192: * @return the type of node matched by this pattern. e.g. Type.ELEMENT or Type.TEXT
193: */
194:
195: public int getPrimitiveType() {
196: return kind;
197: }
198:
199: /**
200: * Get a mask indicating which kinds of nodes this NodeTest can match. This is a combination
201: * of bits: 1<<Type.ELEMENT for element nodes, 1<<Type.TEXT for text nodes, and so on.
202: */
203:
204: public int getNodeKindMask() {
205: return 1 << kind;
206: }
207:
208: /**
209: * Get the content type allowed by this NodeTest (that is, the type annotation of the matched nodes).
210: * Return AnyType if there are no restrictions. The default implementation returns AnyType.
211: */
212:
213: public SchemaType getContentType() {
214: return schemaType;
215: }
216:
217: /**
218: * Get the item type of the atomic values that will be produced when an item
219: * of this type is atomized (assuming that atomization succeeds)
220: */
221:
222: public AtomicType getAtomizedItemType() {
223: SchemaType type = config.getSchemaType(requiredType);
224: if (type instanceof AtomicType) {
225: return (AtomicType) type;
226: } else if (type instanceof ListType) {
227: SimpleType mem = ((ListType) type).getItemType();
228: if (mem instanceof AtomicType) {
229: return (AtomicType) mem;
230: }
231: }
232: return Type.ANY_ATOMIC_TYPE;
233: }
234:
235: public String toString() {
236: return (kind == Type.ELEMENT ? "element(*, " : "attribute(*, ")
237: + schemaType.getDescription() + ')';
238: }
239:
240: /**
241: * Returns a hash code value for the object.
242: */
243:
244: public int hashCode() {
245: return kind << 20 ^ requiredType;
246: }
247:
248: /**
249: * Indicates whether some other object is "equal to" this one.
250: */
251: public boolean equals(Object other) {
252: return other instanceof ContentTypeTest
253: && ((ContentTypeTest) other).kind == kind
254: && ((ContentTypeTest) other).schemaType == schemaType
255: && ((ContentTypeTest) other).requiredType == requiredType
256: && ((ContentTypeTest) other).nillable == nillable
257: && ((ContentTypeTest) other).matchDTDTypes == matchDTDTypes;
258: }
259:
260: }
261:
262: //
263: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
264: // you may not use this file except in compliance with the License. You may obtain a copy of the
265: // License at http://www.mozilla.org/MPL/
266: //
267: // Software distributed under the License is distributed on an "AS IS" basis,
268: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
269: // See the License for the specific language governing rights and limitations under the License.
270: //
271: // The Original Code is: all this file.
272: //
273: // The Initial Developer of the Original Code is Michael H. Kay.
274: //
275: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
276: //
277: // Contributor(s): none.
278: //
|