KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sf > saxon > expr > CardinalityChecker


1 package net.sf.saxon.expr;
2 import net.sf.saxon.om.Item;
3 import net.sf.saxon.om.NamePool;
4 import net.sf.saxon.om.SequenceIterator;
5 import net.sf.saxon.trans.XPathException;
6 import net.sf.saxon.type.ItemType;
7 import net.sf.saxon.type.Type;
8 import net.sf.saxon.value.Cardinality;
9 import net.sf.saxon.value.ObjectValue;
10 import net.sf.saxon.event.SequenceReceiver;
11 import net.sf.saxon.event.TypeCheckingFilter;
12 import net.sf.saxon.pattern.DocumentNodeTest;
13
14 /**
15 * A CardinalityChecker implements the cardinality checking of "treat as": that is,
16 * it returns the supplied sequence, checking that its cardinality is correct
17 */

18
19 public final class CardinalityChecker extends UnaryExpression {
20
21     private int requiredCardinality = -1;
22     private RoleLocator role;
23
24     /**
25     * Private Constructor: use factory method
26     */

27
28     private CardinalityChecker(Expression sequence, int cardinality, RoleLocator role) {
29         super(sequence);
30         this.requiredCardinality = cardinality;
31         this.role = role;
32         computeStaticProperties();
33         adoptChildExpression(sequence);
34     }
35
36     /**
37      * Factory method to construct a CardinalityChecker. The method may create an expression that combines
38      * the cardinality checking with the functionality of the underlying expression class
39      * @param sequence
40      * @param cardinality
41      * @param role
42      * @return
43      */

44
45     public static ComputedExpression makeCardinalityChecker(Expression sequence, int cardinality, RoleLocator role) {
46         if (sequence instanceof Atomizer && !Cardinality.allowsMany(cardinality)) {
47             Expression base = ((Atomizer)sequence).getBaseExpression();
48             if (base instanceof ComputedExpression) {
49                 ((ComputedExpression)base).setParentExpression(sequence.getParentExpression());
50             }
51             return new SingletonAtomizer(base, role, Cardinality.allowsZero(cardinality));
52         } else {
53             return new CardinalityChecker(sequence, cardinality, role);
54         }
55     }
56
57     /**
58      * Get the required cardinality
59      */

60
61     public int getRequiredCardinality() {
62         return requiredCardinality;
63     }
64
65     /**
66     * Type-check the expression
67     */

68
69     public Expression typeCheck(StaticContext env, ItemType contextItemType) throws XPathException {
70         operand = operand.typeCheck(env, contextItemType);
71         if (requiredCardinality == StaticProperty.ALLOWS_ZERO_OR_MORE) {
72             return operand;
73         }
74         if (Cardinality.subsumes(requiredCardinality, operand.getCardinality())) {
75             return operand;
76         }
77         return this;
78     }
79
80
81     /**
82      * Set the error code to be returned (this is used when evaluating the functions such
83      * as exactly-one() which have their own error codes)
84      */

85
86     public void setErrorCode(String JavaDoc code) {
87         role.setErrorCode(code);
88     }
89
90     /**
91      * An implementation of Expression must provide at least one of the methods evaluateItem(), iterate(), or process().
92      * This method indicates which of these methods is provided. This implementation provides both iterate() and
93      * process() methods natively.
94      */

95
96     public int getImplementationMethod() {
97         int m = ITERATE_METHOD | PROCESS_METHOD;
98         if (!Cardinality.allowsMany(requiredCardinality)) {
99             m |= EVALUATE_METHOD;
100         }
101         return m;
102     }
103
104
105     /**
106     * Iterate over the sequence of values
107     */

108
109     public SequenceIterator iterate(XPathContext context) throws XPathException {
110         SequenceIterator base = operand.iterate(context);
111
112         ObjectValue stopper;
113         if (!Cardinality.allowsZero(requiredCardinality)) {
114             // To check for an empty sequence, we add a special item to the base
115
// iteration, to ensure that the mapping function gets called at least
116
// once. This item will cause an error if it is the first in the sequence,
117
// and will be ignored otherwise.
118
stopper = new ObjectValue(this);
119             base = new AppendIterator(base, stopper, context);
120         }
121         CardinalityCheckingFunction map = new CardinalityCheckingFunction();
122         map.iterator = base;
123         return new MappingIterator(base, map, null);
124     }
125
126     /**
127     * Mapping function
128     */

129
130     private class CardinalityCheckingFunction implements MappingFunction {
131
132         public SequenceIterator iterator;
133
134         public Object JavaDoc map(Item item, XPathContext context) throws XPathException {
135             int pos = iterator.position();
136             if (item instanceof ObjectValue && ((ObjectValue)item).getObject() == CardinalityChecker.this) {
137                 // we've hit the stopper object
138
if (pos==1) {
139                      typeError("An empty sequence is not allowed as the " +
140                              role.getMessage(), role.getErrorCode(), context);
141                 }
142                 // don't include the stopper in the result
143
return null;
144             }
145             if (pos==2 && !Cardinality.allowsMany(requiredCardinality)) {
146                 typeError(
147                         "A sequence of more than one item is not allowed as the " +
148                         role.getMessage(), role.getErrorCode(), context);
149                 return null;
150             }
151             return item;
152         }
153     }
154
155     /**
156     * Evaluate as an Item.
157     */

158
159     public Item evaluateItem(XPathContext context) throws XPathException {
160         SequenceIterator iter = operand.iterate(context);
161         Item item = null;
162         while (true) {
163             Item nextItem = iter.next();
164             if (nextItem == null) break;
165             if (item != null) {
166                 typeError("A sequence of more than one item is not allowed as the " +
167                     role.getMessage(), role.getErrorCode(), context);
168                 return null;
169             }
170             item = nextItem;
171         }
172         if (item == null && !Cardinality.allowsZero(requiredCardinality)) {
173             typeError("An empty sequence is not allowed as the " +
174                     role.getMessage(), role.getErrorCode(), context);
175             return null;
176         }
177         return item;
178     }
179
180     /**
181      * Process the instruction, without returning any tail calls
182      *
183      * @param context The dynamic context, giving access to the current node,
184      * the current variables, etc.
185      */

186
187     public void process(XPathContext context) throws XPathException {
188         Expression next = operand;
189         ItemType type = Type.ITEM_TYPE;
190         if (next instanceof ItemChecker) {
191             type = ((ItemChecker)next).getRequiredType();
192             next = ((ItemChecker)next).getBaseExpression();
193         }
194         if ((next.getImplementationMethod() & PROCESS_METHOD) != 0 && !(type instanceof DocumentNodeTest)) {
195             SequenceReceiver out = context.getReceiver();
196             TypeCheckingFilter filter = new TypeCheckingFilter();
197             filter.setUnderlyingReceiver(out);
198             filter.setPipelineConfiguration(out.getPipelineConfiguration());
199             filter.setRequiredType(type, requiredCardinality, role);
200             context.setReceiver(filter);
201             next.process(context);
202             filter.close();
203             context.setReceiver(out);
204         } else {
205             super.process(context);
206         }
207     }
208
209     /**
210     * Determine the data type of the items returned by the expression, if possible
211     * @return a value such as Type.STRING, Type.BOOLEAN, Type.NUMBER, Type.NODE,
212     * or Type.ITEM (meaning not known in advance)
213     */

214
215     public ItemType getItemType() {
216         return operand.getItemType();
217     }
218
219     /**
220     * Determine the static cardinality of the expression
221     */

222
223     public int computeCardinality() {
224         return requiredCardinality;
225     }
226
227     /**
228     * Get the static properties of this expression (other than its type). The result is
229     * bit-signficant. These properties are used for optimizations. In general, if
230     * property bit is set, it is true, but if it is unset, the value is unknown.
231     */

232
233     public int computeSpecialProperties() {
234         return operand.getSpecialProperties();
235     }
236
237     /**
238     * Is this expression the same as another expression?
239     */

240
241     public boolean equals(Object JavaDoc other) {
242         return super.equals(other) &&
243                 this.requiredCardinality == ((CardinalityChecker)other).requiredCardinality;
244     }
245
246     /**
247     * Diagnostic print of expression structure
248     */

249
250     public String JavaDoc displayOperator(NamePool pool) {
251         return "checkCardinality (" + Cardinality.toString(requiredCardinality) + ')';
252     }
253
254 }
255
256
257
258 //
259
// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
260
// you may not use this file except in compliance with the License. You may obtain a copy of the
261
// License at http://www.mozilla.org/MPL/
262
//
263
// Software distributed under the License is distributed on an "AS IS" basis,
264
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
265
// See the License for the specific language governing rights and limitations under the License.
266
//
267
// The Original Code is: all this file.
268
//
269
// The Initial Developer of the Original Code is Michael H. Kay
270
//
271
// Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
272
//
273
// Contributor(s): none.
274
//
275
Popular Tags