package client.net.sf.saxon.ce.functions; import client.net.sf.saxon.ce.Configuration; import client.net.sf.saxon.ce.dom.XMLDOM; import client.net.sf.saxon.ce.expr.Expression; import client.net.sf.saxon.ce.expr.ExpressionVisitor; import client.net.sf.saxon.ce.expr.StaticProperty; import client.net.sf.saxon.ce.expr.XPathContext; import client.net.sf.saxon.ce.om.Item; import client.net.sf.saxon.ce.trans.XPathException; import client.net.sf.saxon.ce.tree.util.URI; import client.net.sf.saxon.ce.value.BooleanValue; import client.net.sf.saxon.ce.value.StringValue; public class UnparsedText extends SystemFunction { public UnparsedText(int operation) { this.operation = operation; } public UnparsedText newInstance() { return new UnparsedText(operation); } // TODO: There is now a requirement that the results should be stable // TODO: Consider supporting a query parameter ?substitute-character=xFFDE String expressionBaseURI = null; public static final int UNPARSED_TEXT = 0; public static final int UNPARSED_TEXT_AVAILABLE = 1; public void checkArguments(ExpressionVisitor visitor) throws XPathException { if (expressionBaseURI == null) { super.checkArguments(visitor); expressionBaseURI = visitor.getStaticContext().getBaseURI(); } } /** * preEvaluate: this method suppresses compile-time evaluation by doing nothing * @param visitor an expression visitor */ public Expression preEvaluate(ExpressionVisitor visitor) { return this; // in principle we could pre-evaluate any call of unparsed-text() with // constant arguments. But we don't, because the file contents might // change before the stylesheet executes. } public int computeSpecialProperties() { return super.computeSpecialProperties() &~ StaticProperty.NON_CREATIVE; // Pretend the function is creative to prevent the result going into a global variable, // which takes excessive memory. (TODO: But it does ensure stability...) } /** * This method handles evaluation of the function: * it returns a StringValue in the case of unparsed-text(), or a BooleanValue * in the case of unparsed-text-available(). In the case of unparsed-text-lines() * this shouldn't be called, but we deal with it anyway. */ public Item evaluateItem(XPathContext context) throws XPathException { CharSequence content; StringValue result; try { StringValue hrefVal = (StringValue)argument[0].evaluateItem(context); if (hrefVal == null) { return null; } String href = hrefVal.getStringValue(); String encoding = null; if (getNumberOfArguments() == 2) { encoding = argument[1].evaluateItem(context).getStringValue(); } content = readFile(href, expressionBaseURI, encoding, context); result = new StringValue(content); } catch (XPathException err) { if (operation == UNPARSED_TEXT_AVAILABLE) { return BooleanValue.FALSE; } else { err.maybeSetErrorCode("XTDE1170"); throw err; } } switch (operation) { case UNPARSED_TEXT_AVAILABLE: return BooleanValue.TRUE; case UNPARSED_TEXT: return result; default: throw new UnsupportedOperationException(operation+""); } } /** * Supporting routine to load one external file given a URI (href) and a baseURI */ private CharSequence readFile(String href, String baseURI, String encoding, XPathContext context) throws XPathException { // Use the URI machinery to validate and resolve the URIs URI absoluteURI = getAbsoluteURI(href, baseURI); try { return XMLDOM.makeHTTPRequest(absoluteURI.toString()); } catch (Exception e) { throw new XPathException(e); } } private URI getAbsoluteURI(String href, String baseURI) throws XPathException { URI absoluteURI; try { absoluteURI = ResolveURI.makeAbsolute(href, baseURI); } catch (URI.URISyntaxException err) { XPathException e = new XPathException(err.getMessage()); e.setErrorCode("XTDE1170"); throw e; } if (absoluteURI.getFragment() != null) { XPathException e = new XPathException("URI for unparsed-text() must not contain a fragment identifier"); e.setErrorCode("XTDE1170"); throw e; } // The URL dereferencing classes throw all kinds of strange exceptions if given // ill-formed sequences of %hh escape characters. So we do a sanity check that the // escaping is well-formed according to UTF-8 rules EscapeURI.checkPercentEncoding(absoluteURI.toString()); return absoluteURI; } } // 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.