package client.net.sf.saxon.ce.style; import client.net.sf.saxon.ce.Controller; import client.net.sf.saxon.ce.event.ProxyReceiver; import client.net.sf.saxon.ce.event.StartTagBuffer; import client.net.sf.saxon.ce.expr.*; import client.net.sf.saxon.ce.expr.instruct.SlotManager; import client.net.sf.saxon.ce.lib.NamespaceConstant; import client.net.sf.saxon.ce.om.NamePool; import client.net.sf.saxon.ce.om.NamespaceBinding; import client.net.sf.saxon.ce.om.NamespaceResolver; import client.net.sf.saxon.ce.om.StandardNames; import client.net.sf.saxon.ce.trans.XPathException; import client.net.sf.saxon.ce.tree.util.SourceLocator; import client.net.sf.saxon.ce.type.ItemType; import client.net.sf.saxon.ce.type.Type; import client.net.sf.saxon.ce.value.DateTimeValue; import java.util.Stack; /** * This is a filter inserted into the input pipeline for processing stylesheet modules, whose * task is to evaluate use-when expressions and discard those parts of the stylesheet module * for which the use-when attribute evaluates to false. */ public class UseWhenFilter extends ProxyReceiver { private StartTagBuffer startTag; private NamespaceResolver nsResolver; private int useWhenCode; private int xslUseWhenCode; private int defaultNamespaceCode; private int depthOfHole = 0; private boolean emptyStylesheetElement = false; private Stack defaultNamespaceStack = new Stack(); private DateTimeValue currentDateTime = DateTimeValue.getCurrentDateTime(null); /** * Create a UseWhenFilter * @param startTag a preceding filter on the pipeline that buffers the attributes of a start tag */ public UseWhenFilter(StartTagBuffer startTag, NamespaceResolver resolver) { this.startTag = startTag; this.nsResolver = resolver; } /** * Start of document */ public void open() throws XPathException { useWhenCode = getNamePool().allocate("", "", "use-when") & 0xfffff; xslUseWhenCode = getNamePool().allocate("xsl", NamespaceConstant.XSLT, "use-when"); defaultNamespaceCode = getNamePool().allocate("", "", "xpath-default-namespace"); nextReceiver.open(); } /** * Notify the start of an element. * * @param nameCode integer code identifying the name of the element within the name pool. * @param properties bit-significant properties of the element node */ public void startElement(int nameCode, int properties) throws XPathException { defaultNamespaceStack.push(startTag.getAttribute(defaultNamespaceCode)); if (emptyStylesheetElement) { depthOfHole++; return; } if (depthOfHole == 0) { String useWhen; int uriCode = getNamePool().getURICode(nameCode); if (uriCode == NamespaceConstant.XSLT_CODE) { useWhen = startTag.getAttribute(useWhenCode); } else { useWhen = startTag.getAttribute(xslUseWhenCode); } if (useWhen != null) { Expression expr = null; try { SourceLocator loc = new SourceLocator() { public String getSystemId() { return UseWhenFilter.this.getSystemId(); } public String getLocation() { return "use-when expression in " + getSystemId(); } }; UseWhenStaticContext staticContext = new UseWhenStaticContext(getConfiguration(), nsResolver, loc); expr = prepareUseWhen(useWhen, staticContext, loc); boolean b = evaluateUseWhen(expr, staticContext); if (!b) { int fp = nameCode & NamePool.FP_MASK; if (fp == StandardNames.XSL_STYLESHEET || fp == StandardNames.XSL_TRANSFORM) { emptyStylesheetElement = true; } else { depthOfHole = 1; return; } } } catch (XPathException e) { XPathException err = new XPathException("Error in use-when expression. " + e.getMessage()); err.setLocator(expr.getSourceLocator()); err.setErrorCodeQName(e.getErrorCodeQName()); getPipelineConfiguration().getErrorListener().error(err); err.setHasBeenReported(true); throw err; } } nextReceiver.startElement(nameCode, properties); } else { depthOfHole++; } } /** * Notify a namespace. Namespaces are notified <b>after</b> the startElement event, and before * any children for the element. The namespaces that are reported are only required * to include those that are different from the parent element; however, duplicates may be reported. * A namespace must not conflict with any namespaces already used for element or attribute names. * * @param nsBinding an integer: the top half is a prefix code, the bottom half a URI code. * These may be translated into an actual prefix and URI using the name pool. A prefix code of * zero represents the empty prefix (that is, the default namespace). A URI code of zero represents * a URI of "", that is, a namespace undeclaration. * @throws IllegalStateException: attempt to output a namespace when there is no open element * start tag */ public void namespace(NamespaceBinding nsBinding, int properties) throws XPathException { if (depthOfHole == 0) { nextReceiver.namespace(nsBinding, properties); } } /** * Notify an attribute. Attributes are notified after the startElement event, and before any * children. Namespaces and attributes may be intermingled. * * @param nameCode The name of the attribute, as held in the name pool * @throws IllegalStateException: attempt to output an attribute when there is no open element * start tag */ public void attribute(int nameCode, CharSequence value) throws XPathException { if (depthOfHole == 0) { nextReceiver.attribute(nameCode, value); } } /** * Notify the start of the content, that is, the completion of all attributes and namespaces. * Note that the initial receiver of output from XSLT instructions will not receive this event, * it has to detect it itself. Note that this event is reported for every element even if it has * no attributes, no namespaces, and no content. */ public void startContent() throws XPathException { if (depthOfHole == 0) { nextReceiver.startContent(); } } /** * End of element */ public void endElement() throws XPathException { defaultNamespaceStack.pop(); if (depthOfHole > 0) { depthOfHole--; } else { nextReceiver.endElement(); } } /** * Character data */ public void characters(CharSequence chars) throws XPathException { if (depthOfHole == 0) { nextReceiver.characters(chars); } } /** * Processing Instruction */ public void processingInstruction(String target, CharSequence data) { // these are ignored in a stylesheet } /** * Output a comment */ public void comment(CharSequence chars) throws XPathException { // these are ignored in a stylesheet } /** * Evaluate a use-when attribute * @param expression the expression to be evaluated * @param sourceLocator identifies the location of the expression in case error need to be reported * @return the effective boolean value of the result of evaluating the expression */ public Expression prepareUseWhen(String expression, UseWhenStaticContext staticContext, SourceLocator sourceLocator) throws XPathException { // TODO: The following doesn't take account of xml:base attributes staticContext.setBaseURI(sourceLocator.getSystemId()); staticContext.setDefaultElementNamespace(NamespaceConstant.NULL); for (int i=defaultNamespaceStack.size()-1; i>=0; i--) { String uri = (String)defaultNamespaceStack.get(i); if (uri != null) { staticContext.setDefaultElementNamespace(uri); break; } } Expression expr = ExpressionTool.make(expression, staticContext, staticContext, 0, Token.EOF, sourceLocator); expr.setContainer(staticContext); return expr; } public boolean evaluateUseWhen(Expression expr, UseWhenStaticContext staticContext) throws XPathException { ItemType contextItemType = Type.ITEM_TYPE; ExpressionVisitor visitor = ExpressionVisitor.make(staticContext, staticContext.getExecutable()); expr = visitor.typeCheck(expr, contextItemType); SlotManager stackFrameMap = new SlotManager(); ExpressionTool.allocateSlots(expr, stackFrameMap.getNumberOfVariables(), stackFrameMap); Controller controller = new Controller(getConfiguration()); // TODO:CLAXON ensure calls on doc() are unsuccessful controller.setCurrentDateTime(currentDateTime); // this is to ensure that all use-when expressions in a module use the same date and time XPathContext dynamicContext = controller.newXPathContext(); dynamicContext = dynamicContext.newCleanContext(); ((XPathContextMajor)dynamicContext).openStackFrame(stackFrameMap); return expr.effectiveBooleanValue(dynamicContext); } } // 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.