package client.net.sf.saxon.ce.expr.instruct; import client.net.sf.saxon.ce.expr.*; import client.net.sf.saxon.ce.om.Axis; import client.net.sf.saxon.ce.tree.iter.*; import client.net.sf.saxon.ce.om.Item; import client.net.sf.saxon.ce.om.SequenceIterator; import client.net.sf.saxon.ce.pattern.EmptySequenceTest; import client.net.sf.saxon.ce.trans.XPathException; import client.net.sf.saxon.ce.type.*; import client.net.sf.saxon.ce.value.*; import java.util.*; /** * An expression that delivers the concatenation of the results of its subexpressions. This may * represent an XSLT sequence constructor, or an XPath/XQuery expression of the form (a,b,c,d). */ public class Block extends Instruction { // TODO: allow the last expression in a Block to be a tail-call of a function, at least in push mode private Expression[] children; /** * Create an empty block */ public Block() { } /** * Static factory method to create a block. If one of the arguments is already a block, * the contents will be merged into a new composite block * @param e1 the first subexpression (child) of the block * @param e2 the second subexpression (child) of the block * @return a Block containing the two subexpressions, and if either of them is a block, it will * have been collapsed to create a flattened sequence */ public static Expression makeBlock(Expression e1, Expression e2) { if (e1==null || Literal.isEmptySequence(e1)) { return e2; } if (e2==null || Literal.isEmptySequence(e2)) { return e1; } if (e1 instanceof Block || e2 instanceof Block) { List list = new ArrayList(10); if (e1 instanceof Block) { Iterator it1 = e1.iterateSubExpressions(); while (it1.hasNext()) { list.add(it1.next()); } } else { list.add(e1); } if (e2 instanceof Block) { Iterator it2 = e2.iterateSubExpressions(); while (it2.hasNext()) { list.add(it2.next()); } } else { list.add(e2); } Expression[] exps = new Expression[list.size()]; exps = (Expression[])list.toArray(exps); Block b = new Block(); b.setChildren(exps); return b; } else { Expression[] exps = {e1, e2}; Block b = new Block(); b.setChildren(exps); return b; } } /** * Static factory method to create a block from a list of expressions * @param list the list of expressions making up this block. The members of the List must * be instances of Expression * @return a Block containing the two subexpressions, and if either of them is a block, it will * have been collapsed to create a flattened sequence */ public static Expression makeBlock(List<Expression> list) { if (list.size() == 0) { return Literal.makeEmptySequence(); } else if (list.size() == 1) { return list.get(0); } else { Expression[] exps = new Expression[list.size()]; exps = list.toArray(exps); Block b = new Block(); b.setChildren(exps); return b; } } /** * Set the children of this instruction * @param children The instructions that are children of this instruction */ public void setChildren(Expression[] children) { this.children = children; for (int c=0; c<children.length; c++) { adoptChildExpression(children[c]); } } public String getExpressionName() { return "sequence"; } /** * Get the children of this instruction * @return the children of this instruction, as an array of Instruction objects. May return * a zero-length array if there are no children */ public Expression[] getChildren() { return children; } public int computeSpecialProperties() { if (children.length == 0) { // An empty sequence has all special properties except "has side effects". return StaticProperty.SPECIAL_PROPERTY_MASK &~ StaticProperty.HAS_SIDE_EFFECTS; } int p = super.computeSpecialProperties(); // if all the expressions are axis expressions, we have a same-document node-set boolean allAxisExpressions = true; boolean allChildAxis = true; boolean allSubtreeAxis = true; for (int i=0; i<children.length; i++) { if (!(children[i] instanceof AxisExpression)) { allAxisExpressions = false; allChildAxis = false; allSubtreeAxis = false; break; } byte axis = ((AxisExpression)children[i]).getAxis(); if (axis != Axis.CHILD) { allChildAxis = false; } if (!Axis.isSubtreeAxis[axis]) { allSubtreeAxis = false; } } if (allAxisExpressions) { p |= StaticProperty.CONTEXT_DOCUMENT_NODESET | StaticProperty.SINGLE_DOCUMENT_NODESET | StaticProperty.NON_CREATIVE; // if they all use the child axis, then we have a peer node-set if (allChildAxis) { p |= StaticProperty.PEER_NODESET; } if (allSubtreeAxis) { p |= StaticProperty.SUBTREE_NODESET; } // special case: selecting attributes then children, node-set is sorted if (children.length == 2 && ((AxisExpression)children[0]).getAxis() == Axis.ATTRIBUTE && ((AxisExpression)children[1]).getAxis() == Axis.CHILD) { p |= StaticProperty.ORDERED_NODESET; } } return p; } /** * Merge any adjacent instructions that create literal text nodes * @return the expression after merging literal text instructions */ public Expression mergeAdjacentTextInstructions() { boolean[] isLiteralText = new boolean[children.length]; boolean hasAdjacentTextNodes = false; for (int i=0; i<children.length; i++) { isLiteralText[i] = children[i] instanceof ValueOf && ((ValueOf)children[i]).getContentExpression() instanceof StringLiteral; if (i > 0 && isLiteralText[i] && isLiteralText[i-1]) { hasAdjacentTextNodes = true; } } if (hasAdjacentTextNodes) { List content = new ArrayList(children.length); String pendingText = null; for (int i=0; i<children.length; i++) { if (isLiteralText[i]) { pendingText = (pendingText == null ? "" : pendingText) + ((StringLiteral)((ValueOf)children[i]).getContentExpression()).getStringValue(); } else { if (pendingText != null) { ValueOf inst = new ValueOf(new StringLiteral(pendingText), false); content.add(inst); pendingText = null; } content.add(children[i]); } } if (pendingText != null) { ValueOf inst = new ValueOf(new StringLiteral(pendingText), false); content.add(inst); } return makeBlock(content); } else { return this; } } public Iterator<Expression> iterateSubExpressions() { return Arrays.asList(children).iterator(); } /** * Test whether the Block includes a LocalParam instruction (which will be true only if it is the * body of an XSLT template) * @return true if the Block contains a LocalParam instruction */ public boolean containsLocalParam() { return children.length > 0 && children[0] instanceof LocalParam; } /** * Replace one subexpression by a replacement subexpression * @param original the original subexpression * @param replacement the replacement subexpression * @return true if the original subexpression is found */ public boolean replaceSubExpression(Expression original, Expression replacement) { boolean found = false; for (int c=0; c<children.length; c++) { if (children[c] == original) { children[c] = replacement; found = true; } } return found; } /** * Determine the data type of the items returned by this expression * @return the data type * @param th the type hierarchy cache */ public final ItemType getItemType(TypeHierarchy th) { if (children.length==0) { return EmptySequenceTest.getInstance(); } ItemType t1 = children[0].getItemType(th); for (int i=1; i<children.length; i++) { t1 = Type.getCommonSuperType(t1, children[i].getItemType(th), th); if (t1 instanceof AnyItemType) { return t1; // no point going any further } } return t1; } /** * Determine the cardinality of the expression */ public final int getCardinality() { if (children.length==0) { return StaticProperty.EMPTY; } int c1 = children[0].getCardinality(); for (int i=1; i<children.length; i++) { c1 = Cardinality.sum(c1, children[i].getCardinality()); if (c1 == StaticProperty.ALLOWS_ZERO_OR_MORE) { break; } } return c1; } /** * Determine whether this instruction creates new nodes. * This implementation returns true if any child instruction * returns true. */ public final boolean createsNewNodes() { for (int i=0; i<children.length; i++) { int props = children[i].getSpecialProperties(); if ((props & StaticProperty.NON_CREATIVE) == 0) { return true; } } return false; } /** * Simplify an expression. This performs any static optimization (by rewriting the expression * as a different expression). The default implementation does nothing. * * @exception XPathException if an error is discovered during expression * rewriting * @return the simplified expression * @param visitor an expression visitor */ public Expression simplify(ExpressionVisitor visitor) throws XPathException { boolean allAtomic = true; boolean nested = false; for (int c=0; c<children.length; c++) { children[c] = visitor.simplify(children[c]); if (!Literal.isAtomic(children[c])) { allAtomic = false; } if (children[c] instanceof Block) { nested = true; } else if (Literal.isEmptySequence(children[c])) { nested = true; } } if (children.length == 1) { return getChildren()[0]; } if (children.length == 0) { Expression result = Literal.makeEmptySequence(); ExpressionTool.copyLocationInfo(this, result); return result; } if (nested) { List list = new ArrayList(children.length*2); flatten(list); children = new Expression[list.size()]; for (int i=0; i<children.length; i++) { children[i] = (Expression)list.get(i); adoptChildExpression(children[i]); } } if (allAtomic) { AtomicValue[] values = new AtomicValue[children.length]; for (int c=0; c<children.length; c++) { values[c] = (AtomicValue)((Literal)children[c]).getValue(); } Expression result = Literal.makeLiteral(new SequenceExtent(values)); ExpressionTool.copyLocationInfo(this, result); return result; } return this; } /** * Simplify the contents of a Block by merging any nested blocks, merging adjacent * literals, and eliminating any empty sequences. * @param targetList the new list of expressions comprising the contents of the block * after simplification * @throws XPathException should not happen */ private void flatten(List<Expression> targetList) throws XPathException { List<Item> currentLiteralList = null; for (int i=0; i<children.length; i++) { if (Literal.isEmptySequence(children[i])) { // do nothing, omit it from the output } else if (children[i] instanceof Block) { flushCurrentLiteralList(currentLiteralList, targetList); currentLiteralList = null; ((Block)children[i]).flatten(targetList); } else if (children[i] instanceof Literal &&!(((Literal)children[i]).getValue() instanceof IntegerRange)) { SequenceIterator iterator = ((Literal)children[i]).getValue().iterate(); if (currentLiteralList == null) { currentLiteralList = new ArrayList<Item>(10); } while (true) { Item item = iterator.next(); if (item == null) { break; } currentLiteralList.add(item); } // no-op } else { flushCurrentLiteralList(currentLiteralList, targetList); currentLiteralList = null; targetList.add(children[i]); } } flushCurrentLiteralList(currentLiteralList, targetList); } private void flushCurrentLiteralList(List<Item> currentLiteralList, List<Expression> list) throws XPathException { if (currentLiteralList != null) { SequenceIterator iter = new client.net.sf.saxon.ce.tree.iter.ListIterator(currentLiteralList); list.add(Literal.makeLiteral((Value)SequenceExtent.makeSequenceExtent(iter))); } } public Expression typeCheck(ExpressionVisitor visitor, ItemType contextItemType) throws XPathException { for (int c=0; c<children.length; c++) { children[c] = visitor.typeCheck(children[c], contextItemType); adoptChildExpression(children[c]); } return this; } public Expression optimize(ExpressionVisitor visitor, ItemType contextItemType) throws XPathException { for (int c=0; c<children.length; c++) { children[c] = visitor.optimize(children[c], contextItemType); adoptChildExpression(children[c]); } boolean canSimplify = false; boolean prevLiteral = false; // Simplify the expression by collapsing nested blocks and merging adjacent literals for (int c=0; c<children.length; c++) { if (children[c] instanceof Block) { canSimplify = true; break; } if (children[c] instanceof Literal) { if (prevLiteral || Literal.isEmptySequence(children[c])) { canSimplify = true; break; } prevLiteral = true; } else { prevLiteral = false; } } if (canSimplify) { List list = new ArrayList(children.length*2); flatten(list); children = new Expression[list.size()]; for (int i=0; i<children.length; i++) { children[i] = (Expression)list.get(i); adoptChildExpression(children[i]); } } if (children.length == 0) { return Literal.makeEmptySequence(); } else if (children.length == 1) { return children[0]; } else { return this; } } /** * Handle promotion offers, that is, non-local tree rewrites. * @param offer The type of rewrite being offered * @throws XPathException */ protected void promoteInst(PromotionOffer offer) throws XPathException { for (int c=0; c<children.length; c++) { children[c] = doPromotion(children[c], offer); } } public TailCall processLeavingTail(XPathContext context) throws XPathException { TailCall tc = null; for (int i=0; i<children.length; i++) { try { if (children[i] instanceof TailCallReturner) { tc = ((TailCallReturner)children[i]).processLeavingTail(context); } else { children[i].process(context); tc = null; } } catch (XPathException e) { e.maybeSetLocation(children[i].getSourceLocator()); e.maybeSetContext(context); throw e; } } return tc; } /** * An implementation of Expression must provide at least one of the methods evaluateItem(), iterate(), or process(). * This method indicates which of these methods is provided. This implementation provides both iterate() and * process() methods natively. */ public int getImplementationMethod() { return ITERATE_METHOD | PROCESS_METHOD; } /** * Iterate over the results of all the child expressions */ public SequenceIterator iterate(XPathContext context) throws XPathException { if (children.length == 0) { return EmptyIterator.getInstance(); } else if (children.length == 1) { return children[0].iterate(context); } else { return new BlockIterator(children, context); } } } // This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. // If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. // This Source Code Form is “Incompatible With Secondary Licenses”, as defined by the Mozilla Public License, v. 2.0.