/**
*
*/
package org.cdlib.xtf.saxonExt;
/**
* Copyright (c) 2007, Regents of the University of California
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* - Neither the name of the University of California nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import net.sf.saxon.Configuration;
import net.sf.saxon.expr.Expression;
import net.sf.saxon.expr.ExpressionTool;
import net.sf.saxon.expr.Optimizer;
import net.sf.saxon.expr.PromotionOffer;
import net.sf.saxon.expr.StaticContext;
import net.sf.saxon.expr.StaticProperty;
import net.sf.saxon.expr.XPathContext;
import net.sf.saxon.instruct.Instruction;
import net.sf.saxon.instruct.TailCall;
import net.sf.saxon.om.Item;
import net.sf.saxon.om.SequenceIterator;
import net.sf.saxon.pattern.EmptySequenceTest;
import net.sf.saxon.trans.DynamicError;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.type.ItemType;
import net.sf.saxon.type.TypeHierarchy;
/**
* Base class that automates much of the tedious Saxon housekeeping for an
* extension instruction that supports arbitrary content.
*
* @author Martin Haye
*/
public abstract class InstructionWithContent extends Instruction
{
protected final String name;
private int nameCode;
protected Map<String, Expression> attribs;
protected Expression content;
/**
* Construct the content instruction.
*/
public InstructionWithContent(String name,
Map<String, Expression> attribs,
Expression content)
{
// Record input parameters
this.name = name;
this.attribs = attribs;
this.content = content;
// Own all the attribute exprssions and the content
for (Expression exp : attribs.values())
adoptChildExpression(exp);
adoptChildExpression(content);
}
// XTF convenience functions
public String getAttribStr(String attrName, XPathContext context)
throws XPathException
{
return getAttribStr(attrName, context, null);
}
public String getAttribStr(String attrName, XPathContext context, String defaultVal)
throws XPathException
{
if (!attribs.containsKey(attrName))
return defaultVal;
return attribs.get(attrName).evaluateAsString(context);
}
public boolean getAttribBool(String attrName, XPathContext context, boolean defaultVal)
throws XPathException
{
String str = getAttribStr(attrName, context);
if (str == null)
return defaultVal;
if (str.toLowerCase().matches("1|yes|true"))
return true;
if (str.toLowerCase().matches("0|no|false"))
return false;
return defaultVal;
}
/**
* Simplify an expression. This performs any static optimization (by rewriting the expression
* as a different expression). The default implementation does nothing.
* @return the simplified expression
* @throws net.sf.saxon.trans.XPathException
* if an error is discovered during expression rewriting
*/
public Expression simplify(StaticContext env) throws XPathException
{
for (Entry<String, Expression> attrib : attribs.entrySet())
attrib.setValue(attrib.getValue().simplify(env));
if (content != null) {
content = content.simplify(env);
}
return this;
}
public Expression typeCheck(StaticContext env, ItemType contextItemType) throws XPathException
{
for (Entry<String, Expression> attrib : attribs.entrySet()) {
attrib.setValue(attrib.getValue().typeCheck(env, contextItemType));
adoptChildExpression(attrib.getValue());
}
if (content != null) {
content = content.typeCheck(env, contextItemType);
adoptChildExpression(content);
}
return this;
}
public Expression optimize(Optimizer opt, StaticContext env, ItemType contextItemType) throws XPathException
{
for (Entry<String, Expression> attrib : attribs.entrySet()) {
Expression exp = attrib.getValue();
exp = exp.optimize(opt, env, contextItemType);
attrib.setValue(exp);
adoptChildExpression(exp);
}
if (content != null) {
content = content.optimize(opt, env, contextItemType);
adoptChildExpression(content);
}
return this;
}
/**
* Get the name of this instruction for diagnostic and tracing purposes
*/
public int getInstructionNameCode() {
return nameCode;
}
public ItemType getItemType(TypeHierarchy th) {
return EmptySequenceTest.getInstance();
}
public int getCardinality() {
return StaticProperty.EMPTY;
}
/**
* Determine whether this instruction creates new nodes.
* This implementation returns true.
*/
public final boolean createsNewNodes() {
return true;
}
/**
* 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 (Entry<String, Expression> attrib : attribs.entrySet()) {
Expression exp = attrib.getValue();
if (exp != null) {
exp = doPromotion(exp, offer);
attrib.setValue(exp);
}
}
if (content != null) {
content = doPromotion(content, offer);
}
}
/**
* Get all the XPath expressions associated with this instruction
* (in XSLT terms, the expression present on attributes of the instruction,
* as distinct from the child instructions in a sequence construction)
*/
public Iterator iterateSubExpressions()
{
ArrayList<Expression> list = new ArrayList<Expression>(attribs.size() + 1);
for (Expression exp : attribs.values())
list.add(exp);
if (content != null) {
list.add(content);
}
return list.iterator();
}
/**
* 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 (Entry<String, Expression> attrib : attribs.entrySet()) {
Expression exp = attrib.getValue();
if (exp == original) {
attrib.setValue(replacement);
found = true;
}
}
if (content == original) {
content = replacement;
found = true;
}
return found;
}
/**
* This is where the main work should be performed. Subclasses must implement
* this method.
*/
public abstract TailCall processLeavingTail(XPathContext context) throws XPathException;
/**
* Utility function to convert an expression (which might be a sequence) to a string
* value.
*/
protected static String sequenceToString(Expression exp, XPathContext context)
throws XPathException
{
StringBuffer buf = new StringBuffer();
SequenceIterator iter = exp.iterate(context);
Item item;
while ((item = iter.next()) != null)
buf.append(item.getStringValue());
return buf.toString();
}
/**
* Diagnostic print of expression structure. The expression is written to the System.err
* output stream
*
* @param level indentation level for this expression
* @param out
* @param config
*/
public void display(int level, PrintStream out, Configuration config)
{
out.println(ExpressionTool.indent(level) + name);
}
/** Special version of dynamicError that includes a cause with the exception. **/
protected void dynamicError(Throwable cause, String message, String code, XPathContext context)
throws DynamicError
{
DynamicError err = new DynamicError(message, getSourceLocator(), cause);
err.setXPathContext(context);
err.setErrorCode(code);
throw err;
}
}