001: package net.sf.saxon.sort;
002:
003: import net.sf.saxon.expr.*;
004: import net.sf.saxon.om.EmptyIterator;
005: import net.sf.saxon.om.NamePool;
006: import net.sf.saxon.om.SequenceIterator;
007: import net.sf.saxon.trans.XPathException;
008: import net.sf.saxon.type.ItemType;
009: import net.sf.saxon.type.TypeHierarchy;
010: import net.sf.saxon.value.Cardinality;
011: import net.sf.saxon.value.Value;
012:
013: import java.io.PrintStream;
014: import java.util.ArrayList;
015: import java.util.Iterator;
016: import java.util.List;
017:
018: /**
019: * Expression equivalent to the imaginary syntax
020: * expr sortby (sort-key)+
021: */
022:
023: public class SortExpression extends ComputedExpression {
024:
025: private Expression select = null;
026: private SortKeyDefinition[] sortKeys = null;
027: private FixedSortKeyDefinition[] fixedSortKeys = null;
028:
029: public SortExpression(Expression select,
030: SortKeyDefinition[] sortKeys) {
031: this .select = select;
032: this .sortKeys = sortKeys;
033: boolean fixed = true;
034: for (int i = 0; i < sortKeys.length; i++) {
035: if (!(sortKeys[i] instanceof FixedSortKeyDefinition)) {
036: fixed = false;
037: break;
038: }
039: ;
040: sortKeys[i].setParentExpression(this );
041: }
042: if (fixed) {
043: fixedSortKeys = new FixedSortKeyDefinition[sortKeys.length];
044: System.arraycopy(sortKeys, 0, fixedSortKeys, 0,
045: sortKeys.length);
046: }
047: Iterator children = iterateSubExpressions();
048: while (children.hasNext()) {
049: Expression exp = (Expression) children.next();
050: adoptChildExpression(exp);
051: }
052: }
053:
054: /**
055: * Get the immediate sub-expressions of this expression. Default implementation
056: * returns a zero-length array, appropriate for an expression that has no
057: * sub-expressions.
058: * @return an iterator containing the sub-expressions of this expression
059: */
060:
061: public Iterator iterateSubExpressions() {
062: List list = new ArrayList(8);
063: list.add(select);
064: for (int i = 0; i < sortKeys.length; i++) {
065: list.add(sortKeys[i].getSortKey());
066: Expression e = sortKeys[i].order;
067: if (e != null && !(e instanceof Value)) {
068: list.add(e);
069: }
070: e = sortKeys[i].caseOrder;
071: if (e != null && !(e instanceof Value)) {
072: list.add(e);
073: }
074: e = sortKeys[i].dataTypeExpression;
075: if (e != null && !(e instanceof Value)) {
076: list.add(e);
077: }
078: e = sortKeys[i].language;
079: if (e != null && !(e instanceof Value)) {
080: list.add(e);
081: }
082: e = sortKeys[i].collationName;
083: if (e != null && !(e instanceof Value)) {
084: list.add(e);
085: }
086: }
087: return list.iterator();
088: }
089:
090: /**
091: * Simplify an expression
092: */
093:
094: public Expression simplify(StaticContext env) throws XPathException {
095: select = select.simplify(env);
096: return this ;
097: }
098:
099: /**
100: * Type-check the expression
101: */
102:
103: public Expression typeCheck(StaticContext env,
104: ItemType contextItemType) throws XPathException {
105: select = select.typeCheck(env, contextItemType);
106: return this ;
107: }
108:
109: /**
110: * Perform optimisation of an expression and its subexpressions.
111: * <p/>
112: * <p>This method is called after all references to functions and variables have been resolved
113: * to the declaration of the function or variable, and after all type checking has been done.</p>
114: *
115: * @param opt the optimizer in use. This provides access to supporting functions; it also allows
116: * different optimization strategies to be used in different circumstances.
117: * @param env the static context of the expression
118: * @param contextItemType the static type of "." at the point where this expression is invoked.
119: * The parameter is set to null if it is known statically that the context item will be undefined.
120: * If the type of the context item is not known statically, the argument is set to
121: * {@link net.sf.saxon.type.Type#ITEM_TYPE}
122: * @return the original expression, rewritten if appropriate to optimize execution
123: * @throws net.sf.saxon.trans.StaticError if an error is discovered during this phase
124: * (typically a type error)
125: */
126:
127: public Expression optimize(Optimizer opt, StaticContext env,
128: ItemType contextItemType) throws XPathException {
129: if (Cardinality.allowsMany(select.getCardinality())) {
130: return this ;
131: } else {
132: return select;
133: }
134: }
135:
136: /**
137: * Offer promotion for this subexpression. The offer will be accepted if the subexpression
138: * is not dependent on the factors (e.g. the context item) identified in the PromotionOffer.
139: * By default the offer is not accepted - this is appropriate in the case of simple expressions
140: * such as constant values and variable references where promotion would give no performance
141: * advantage. This method is always called at compile time.
142: *
143: * @param offer details of the offer, for example the offer to move
144: * expressions that don't depend on the context to an outer level in
145: * the containing expression
146: * @return if the offer is not accepted, return this expression unchanged.
147: * Otherwise return the result of rewriting the expression to promote
148: * this subexpression
149: * @throws net.sf.saxon.trans.XPathException
150: * if any error is detected
151: */
152:
153: public Expression promote(PromotionOffer offer)
154: throws XPathException {
155: Expression exp = offer.accept(this );
156: if (exp != null) {
157: return exp;
158: } else {
159: select = doPromotion(select, offer);
160: for (int i = 0; i < sortKeys.length; i++) {
161: sortKeys[i].setSortKey((sortKeys[i].getSortKey()
162: .promote(offer)));
163: if (sortKeys[i].caseOrder != null) {
164: sortKeys[i].caseOrder = sortKeys[i].caseOrder
165: .promote(offer);
166: }
167: if (sortKeys[i].dataTypeExpression != null) {
168: sortKeys[i].dataTypeExpression = sortKeys[i].dataTypeExpression
169: .promote(offer);
170: }
171: if (sortKeys[i].language != null) {
172: sortKeys[i].language = sortKeys[i].language
173: .promote(offer);
174: }
175: if (sortKeys[i].collationName != null) {
176: sortKeys[i].collationName = sortKeys[i].collationName
177: .promote(offer);
178: }
179: }
180: return this ;
181: }
182: }
183:
184: /**
185: * Test whether a given expression is one of the sort keys
186: */
187:
188: public boolean isSortKey(Expression child) {
189: for (int i = 0; i < sortKeys.length; i++) {
190: Expression exp = sortKeys[i].getSortKey();
191: if (exp == child) {
192: return true;
193: }
194: }
195: return false;
196: }
197:
198: /**
199: * Determine the static cardinality
200: */
201:
202: public int computeCardinality() {
203: return select.getCardinality();
204: }
205:
206: /**
207: * Determine the data type of the items returned by the expression, if possible
208: * @return a value such as Type.STRING, Type.BOOLEAN, Type.NUMBER, Type.NODE,
209: * or Type.ITEM (meaning not known in advance)
210: * @param th
211: */
212:
213: public ItemType getItemType(TypeHierarchy th) {
214: return select.getItemType(th);
215: }
216:
217: /**
218: * Get the static properties of this expression (other than its type). The result is
219: * bit-significant. These properties are used for optimizations. In general, if
220: * property bit is set, it is true, but if it is unset, the value is unknown.
221: */
222:
223: public int computeSpecialProperties() {
224: int props = 0;
225: if ((select.getSpecialProperties() & StaticProperty.CONTEXT_DOCUMENT_NODESET) != 0) {
226: props |= StaticProperty.CONTEXT_DOCUMENT_NODESET;
227: }
228: if ((select.getSpecialProperties() & StaticProperty.SINGLE_DOCUMENT_NODESET) != 0) {
229: props |= StaticProperty.SINGLE_DOCUMENT_NODESET;
230: }
231: if ((select.getSpecialProperties() & StaticProperty.NON_CREATIVE) != 0) {
232: props |= StaticProperty.NON_CREATIVE;
233: }
234: return props;
235: }
236:
237: /**
238: * Enumerate the results of the expression
239: */
240:
241: public SequenceIterator iterate(XPathContext context)
242: throws XPathException {
243:
244: SequenceIterator iter = select.iterate(context);
245: if (iter instanceof EmptyIterator) {
246: return iter;
247: }
248: XPathContext xpc = context.newMinorContext();
249: xpc.setOrigin(this );
250:
251: FixedSortKeyDefinition[] reducedSortKeys;
252: if (fixedSortKeys != null) {
253: reducedSortKeys = fixedSortKeys;
254: } else {
255: reducedSortKeys = new FixedSortKeyDefinition[sortKeys.length];
256: for (int s = 0; s < sortKeys.length; s++) {
257: reducedSortKeys[s] = sortKeys[s].reduce(xpc);
258: }
259: }
260: iter = new SortedIterator(xpc, iter, reducedSortKeys);
261: return iter;
262: }
263:
264: public void display(int level, NamePool pool, PrintStream out) {
265: out.println(ExpressionTool.indent(level) + "sort");
266: select.display(level + 1, pool, out);
267: }
268: }
269:
270: //
271: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
272: // you may not use this file except in compliance with the License. You may obtain a copy of the
273: // License at http://www.mozilla.org/MPL/
274: //
275: // Software distributed under the License is distributed on an "AS IS" basis,
276: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
277: // See the License for the specific language governing rights and limitations under the License.
278: //
279: // The Original Code is: all this file.
280: //
281: // The Initial Developer of the Original Code is Michael H. Kay
282: //
283: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
284: //
285: // Contributor(s): none.
286: //
|