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 18 19 public final class CardinalityChecker extends UnaryExpression { 20 21 private int requiredCardinality = -1; 22 private RoleLocator role; 23 24 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 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 60 61 public int getRequiredCardinality() { 62 return requiredCardinality; 63 } 64 65 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 85 86 public void setErrorCode(String code) { 87 role.setErrorCode(code); 88 } 89 90 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 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 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 129 130 private class CardinalityCheckingFunction implements MappingFunction { 131 132 public SequenceIterator iterator; 133 134 public Object map(Item item, XPathContext context) throws XPathException { 135 int pos = iterator.position(); 136 if (item instanceof ObjectValue && ((ObjectValue)item).getObject() == CardinalityChecker.this) { 137 if (pos==1) { 139 typeError("An empty sequence is not allowed as the " + 140 role.getMessage(), role.getErrorCode(), context); 141 } 142 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 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 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 214 215 public ItemType getItemType() { 216 return operand.getItemType(); 217 } 218 219 222 223 public int computeCardinality() { 224 return requiredCardinality; 225 } 226 227 232 233 public int computeSpecialProperties() { 234 return operand.getSpecialProperties(); 235 } 236 237 240 241 public boolean equals(Object other) { 242 return super.equals(other) && 243 this.requiredCardinality == ((CardinalityChecker)other).requiredCardinality; 244 } 245 246 249 250 public String displayOperator(NamePool pool) { 251 return "checkCardinality (" + Cardinality.toString(requiredCardinality) + ')'; 252 } 253 254 } 255 256 257 258 | Popular Tags |