package client.net.sf.saxon.ce.functions; import client.net.sf.saxon.ce.Configuration; import client.net.sf.saxon.ce.PreparedStylesheet; import client.net.sf.saxon.ce.expr.*; import client.net.sf.saxon.ce.lib.NamespaceConstant; import client.net.sf.saxon.ce.om.*; import client.net.sf.saxon.ce.trans.XPathException; import client.net.sf.saxon.ce.type.BuiltInAtomicType; import client.net.sf.saxon.ce.type.SchemaType; import client.net.sf.saxon.ce.value.AtomicValue; import client.net.sf.saxon.ce.value.BooleanValue; import client.net.sf.saxon.ce.value.NumericValue; import client.net.sf.saxon.ce.value.StringValue; /** * This class supports the XSLT element-available and function-available functions. */ public class Available extends SystemFunction { public Available(int operation) { this.operation = operation; } public Available newInstance() { return new Available(operation); } public static final int ELEMENT_AVAILABLE = 0; public static final int FUNCTION_AVAILABLE = 1; public static final int TYPE_AVAILABLE = 2; private NamespaceResolver nsContext; private transient boolean checked = false; public void checkArguments(ExpressionVisitor visitor) throws XPathException { // the second time checkArguments is called, it's a global check so the static context is inaccurate if (checked) { return; } checked = true; super.checkArguments(visitor); if (!(argument[0] instanceof Literal && (argument.length==1 || argument[1] instanceof Literal))) { // we need to save the namespace context nsContext = visitor.getStaticContext().getNamespaceResolver(); } } /** * preEvaluate: this method uses the static context to do early evaluation of the function * if the argument is known (which is the normal case) * @param visitor the expression visitor */ public Expression preEvaluate(ExpressionVisitor visitor) throws XPathException { String lexicalQName = ((Literal)argument[0]).getValue().getStringValue(); StaticContext env = visitor.getStaticContext(); boolean b = false; Configuration config = visitor.getConfiguration(); switch(operation) { case ELEMENT_AVAILABLE: b = env.isElementAvailable(lexicalQName); break; case FUNCTION_AVAILABLE: long arity = -1; if (argument.length == 2) { arity = ((NumericValue)argument[1].evaluateItem(env.makeEarlyEvaluationContext())).intValue(); } try { String[] parts = NameChecker.getQNameParts(lexicalQName); String prefix = parts[0]; String uri; if (prefix.length() == 0) { uri = env.getDefaultFunctionNamespace(); } else { uri = env.getURIForPrefix(prefix); } StructuredQName functionName = new StructuredQName(prefix, uri, parts[1]); b = (env.getFunctionLibrary().hasFunctionSignature(functionName, (int)arity)); } catch (QNameException e) { XPathException err = new XPathException(e.getMessage()); err.setErrorCode("XTDE1400"); throw err; } catch (XPathException e2) { if ("XTDE0290".equals(e2.getErrorCodeLocalPart())) { e2.setErrorCode("XTDE1400"); } throw e2; } break; case TYPE_AVAILABLE: try { String[] parts = NameChecker.getQNameParts(lexicalQName); String prefix = parts[0]; String uri; if (prefix.length() == 0) { uri = env.getDefaultElementNamespace(); } else { uri = env.getURIForPrefix(prefix); } int fingerprint = config.getNamePool().allocate(prefix, uri, parts[1]) & 0xfffff; SchemaType type = config.getSchemaType(fingerprint); b = type instanceof BuiltInAtomicType; } catch (QNameException e) { XPathException err = new XPathException(e.getMessage()); err.setErrorCode("XTDE1425"); throw err; } } return Literal.makeLiteral(BooleanValue.get(b)); } /** * Run-time evaluation. This is the only thing in the spec that requires information * about in-scope functions to be available at run-time. */ public Item evaluateItem(XPathContext context) throws XPathException { AtomicValue av1 = (AtomicValue)argument[0].evaluateItem(context); long arity = -1; if (argument.length == 2) { arity = ((NumericValue)argument[1].evaluateItem(context)).intValue(); } StringValue nameValue = (StringValue)av1; String lexicalName = nameValue.getStringValue(); StructuredQName qName; try { if (lexicalName.indexOf(':') < 0) { // we're in XSLT, where the default namespace for functions can't be changed String uri = (operation == FUNCTION_AVAILABLE ? NamespaceConstant.FN : nsContext.getURIForPrefix("", true)); qName = new StructuredQName("", uri, lexicalName); } else { qName = StructuredQName.fromLexicalQName(lexicalName, false, nsContext); } } catch (XPathException e) { dynamicError(e.getMessage(), badQNameCode(), context); return null; } boolean b = false; switch(operation) { case ELEMENT_AVAILABLE: b = isElementAvailable(qName.getNamespaceURI(), qName.getLocalName(), context); break; case FUNCTION_AVAILABLE: final FunctionLibrary lib = context.getController().getExecutable().getFunctionLibrary(); b = (lib.hasFunctionSignature(qName, (int)arity)); break; case TYPE_AVAILABLE: final int fp = context.getNamePool().allocate( qName.getPrefix(), qName.getNamespaceURI(), qName.getLocalName()) & 0xfffff; SchemaType type = context.getConfiguration().getSchemaType(fp); b = (type != null); } return BooleanValue.get(b); } private String badQNameCode() { switch (operation) { case FUNCTION_AVAILABLE: return "XTDE1400"; case TYPE_AVAILABLE: return "XTDE1428"; case ELEMENT_AVAILABLE: return "XTDE1440"; default: return null; } } /** * Determine at run-time whether a particular instruction is available. Returns true * only in the case of XSLT instructions and Saxon extension instructions; returns false * for user-defined extension instructions * @param uri the namespace URI of the element * @param localname the local part of the element name * @param context the XPath evaluation context * @return true if the instruction is available, in the sense of the XSLT element-available() function */ private boolean isElementAvailable(String uri, String localname, XPathContext context) { // This is horribly inefficient. But hopefully it's hardly ever executed, because there // is very little point calling element-available() with a dynamically-constructed argument. // And the inefficiency is only incurred once, on the first call. // Note: this requires the compile-time classes to be available at run-time; it will need // changing if we ever want to build a run-time JAR file. try { PreparedStylesheet pss = (PreparedStylesheet)context.getController().getExecutable(); return pss.getStyleNodeFactory().isElementAvailable(uri, localname); } catch (Exception err) { //err.printStackTrace(); return false; } } } // 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.