001: package net.sf.saxon.functions;
002:
003: import net.sf.saxon.Err;
004: import net.sf.saxon.type.ItemType;
005: import net.sf.saxon.type.TypeHierarchy;
006: import net.sf.saxon.type.Type;
007: import net.sf.saxon.expr.*;
008: import net.sf.saxon.om.Item;
009: import net.sf.saxon.om.SequenceIterator;
010: import net.sf.saxon.sort.DescendingComparer;
011: import net.sf.saxon.trans.XPathException;
012: import net.sf.saxon.value.*;
013:
014: import java.util.Comparator;
015:
016: /**
017: * This class implements the min() and max() functions
018: */
019:
020: public class Minimax extends CollatingFunction {
021:
022: public static final int MIN = 2;
023: public static final int MAX = 3;
024:
025: private boolean ignoreNaN = false;
026:
027: public void setIgnoreNaN(boolean ignore) {
028: ignoreNaN = ignore;
029: }
030:
031: /**
032: * Static analysis: prevent sorting of the argument
033: */
034:
035: public void checkArguments(StaticContext env) throws XPathException {
036: // TODO: rewrite the expression if the static cardinality is (0-1), (0-0), or (1-1)
037: super .checkArguments(env);
038: Optimizer opt = env.getConfiguration().getOptimizer();
039: argument[0] = ExpressionTool.unsorted(opt, argument[0], false);
040: }
041:
042: /**
043: * Perform optimisation of an expression and its subexpressions.
044: * <p/>
045: * <p>This method is called after all references to functions and variables have been resolved
046: * to the declaration of the function or variable, and after all type checking has been done.</p>
047: *
048: * @param opt the optimizer in use. This provides access to supporting functions; it also allows
049: * different optimization strategies to be used in different circumstances.
050: * @param env the static context of the expression
051: * @param contextItemType the static type of "." at the point where this expression is invoked.
052: * The parameter is set to null if it is known statically that the context item will be undefined.
053: * If the type of the context item is not known statically, the argument is set to
054: * {@link net.sf.saxon.type.Type#ITEM_TYPE}
055: * @return the original expression, rewritten if appropriate to optimize execution
056: * @throws net.sf.saxon.trans.StaticError if an error is discovered during this phase
057: * (typically a type error)
058: */
059:
060: public Expression optimize(Optimizer opt, StaticContext env,
061: ItemType contextItemType) throws XPathException {
062: Expression e = super .optimize(opt, env, contextItemType);
063: if (e != this ) {
064: return e;
065: }
066: if (getNumberOfArguments() == 1) {
067: // often happens after (A<B) is rewritten as (min(A) lt max(B))
068: int card = argument[0].getCardinality();
069: TypeHierarchy th = env.getNamePool().getTypeHierarchy();
070: if (!Cardinality.allowsMany(card)
071: && th.isSubType(argument[0].getItemType(th),
072: Type.NUMBER_TYPE)) {
073: return argument[0];
074: }
075: }
076: return this ;
077: }
078:
079: /**
080: * Evaluate the function
081: */
082:
083: public Item evaluateItem(XPathContext context)
084: throws XPathException {
085: Comparator collator = getAtomicComparer(1, context);
086:
087: if (operation == MAX) {
088: collator = new DescendingComparer(collator);
089: }
090:
091: SequenceIterator iter = argument[0].iterate(context);
092:
093: // Get the first value in the sequence, ignoring any NaN values
094: AtomicValue min;
095: while (true) {
096: min = (AtomicValue) iter.next();
097: if (min == null) {
098: return null;
099: }
100: if (min instanceof UntypedAtomicValue) {
101: try {
102: min = new DoubleValue(Value.stringToNumber(min
103: .getStringValueCS()));
104: } catch (NumberFormatException e) {
105: dynamicError("Failure converting "
106: + Err.wrap(min.getStringValueCS())
107: + " to a number", "FORG0001", context);
108: }
109: }
110: if (min instanceof NumericValue
111: && ((NumericValue) min).isNaN()) {
112: // if there's a NaN in the sequence, return NaN, unless ignoreNaN is set
113: if (ignoreNaN) {
114: continue; // ignore the NaN and treat the next item as the first real one
115: } else {
116: return min; // return NaN
117: }
118: } else {
119: break; // process the rest of the sequence
120: }
121: }
122:
123: while (true) {
124: AtomicValue test = (AtomicValue) iter.next();
125: if (test == null)
126: break;
127: AtomicValue test2 = test;
128: if (test instanceof UntypedAtomicValue) {
129: try {
130: test2 = new DoubleValue(Value.stringToNumber(test
131: .getStringValueCS()));
132: } catch (NumberFormatException e) {
133: dynamicError("Failure converting "
134: + Err.wrap(test.getStringValueCS())
135: + " to a number", "FORG0001", context);
136: }
137: }
138: if (test2 instanceof NumericValue
139: && ((NumericValue) test2).isNaN()) {
140: // if there's a NaN in the sequence, return NaN, unless ignoreNaN is set
141: if (!ignoreNaN) {
142: return test2;
143: }
144: } else {
145: try {
146: if (collator.compare(test2, min) < 0) {
147: min = test2;
148: }
149: } catch (ClassCastException err) {
150: final TypeHierarchy th = context.getNamePool()
151: .getTypeHierarchy();
152: typeError("Cannot compare " + min.getItemType(th)
153: + " with " + test2.getItemType(th),
154: "FORG0006", context);
155: return null;
156: }
157: }
158: }
159: return min;
160: }
161:
162: }
163:
164: //
165: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
166: // you may not use this file except in compliance with the License. You may obtain a copy of the
167: // License at http://www.mozilla.org/MPL/
168: //
169: // Software distributed under the License is distributed on an "AS IS" basis,
170: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
171: // See the License for the specific language governing rights and limitations under the License.
172: //
173: // The Original Code is: all this file.
174: //
175: // The Initial Developer of the Original Code is Michael H. Kay.
176: //
177: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
178: //
179: // Contributor(s): none.
180: //
|