001: package net.sf.saxon.style;
002:
003: import net.sf.saxon.Controller;
004: import net.sf.saxon.event.ProxyReceiver;
005: import net.sf.saxon.event.StartTagBuffer;
006: import net.sf.saxon.expr.*;
007: import net.sf.saxon.instruct.SlotManager;
008: import net.sf.saxon.om.NamespaceConstant;
009: import net.sf.saxon.trans.StaticError;
010: import net.sf.saxon.trans.XPathException;
011: import net.sf.saxon.type.ItemType;
012: import net.sf.saxon.type.Type;
013:
014: import javax.xml.transform.Source;
015: import javax.xml.transform.TransformerException;
016: import javax.xml.transform.URIResolver;
017: import java.util.Stack;
018:
019: /**
020: * This is a filter inserted into the input pipeline for processing stylesheet modules, whose
021: * task is to evaluate use-when expressions and discard those parts of the stylesheet module
022: * for which the use-when attribute evaluates to false.
023: */
024:
025: public class UseWhenFilter extends ProxyReceiver {
026:
027: private StartTagBuffer startTag;
028: private int useWhenCode;
029: private int xslUseWhenCode;
030: private int defaultNamespaceCode;
031: private int depthOfHole = 0;
032: private boolean emptyStylesheetElement = false;
033: private Stack defaultNamespaceStack = new Stack();
034:
035: public UseWhenFilter(StartTagBuffer startTag) {
036: this .startTag = startTag;
037: }
038:
039: /**
040: * Start of document
041: */
042:
043: public void open() throws XPathException {
044: useWhenCode = getNamePool().allocate("", "", "use-when") & 0xfffff;
045: xslUseWhenCode = getNamePool().allocate("xsl",
046: NamespaceConstant.XSLT, "use-when");
047: defaultNamespaceCode = getNamePool().allocate("", "",
048: "xpath-default-namespace");
049: super .open();
050: }
051:
052: /**
053: * Notify the start of an element.
054: *
055: * @param nameCode integer code identifying the name of the element within the name pool.
056: * @param typeCode integer code identifying the element's type within the name pool.
057: * @param properties bit-significant properties of the element node
058: */
059:
060: public void startElement(int nameCode, int typeCode,
061: int locationId, int properties) throws XPathException {
062: defaultNamespaceStack.push(startTag
063: .getAttribute(defaultNamespaceCode));
064: if (emptyStylesheetElement) {
065: depthOfHole = 1;
066: }
067: if (depthOfHole == 0) {
068: String useWhen;
069: if ((nameCode & 0xfffff) < 1024) {
070: useWhen = startTag.getAttribute(useWhenCode);
071: } else {
072: useWhen = startTag.getAttribute(xslUseWhenCode);
073: }
074: if (useWhen != null) {
075: try {
076: boolean b = evaluateUseWhen(useWhen,
077: getDocumentLocator().getLineNumber(
078: locationId));
079: if (!b) {
080: int fp = nameCode & 0xfffff;
081: if (fp == StandardNames.XSL_STYLESHEET
082: || fp == StandardNames.XSL_TRANSFORM) {
083: emptyStylesheetElement = true;
084: } else {
085: depthOfHole = 1;
086: return;
087: }
088: }
089: } catch (XPathException e) {
090: StaticError err = new StaticError(
091: "Error in use-when expression. "
092: + e.getMessage());
093: ExpressionLocation loc = new ExpressionLocation();
094: loc.setSystemId(getDocumentLocator().getSystemId(
095: locationId));
096: loc.setLineNumber(getDocumentLocator()
097: .getLineNumber(locationId));
098: err.setLocator(loc);
099: err.setErrorCode(e.getErrorCodeLocalPart());
100: try {
101: getPipelineConfiguration().getErrorListener()
102: .fatalError(err);
103: } catch (TransformerException tex) {
104: throw StaticError.makeStaticError(tex);
105: }
106: err.setHasBeenReported();
107: throw err;
108: }
109: }
110: } else {
111: depthOfHole++;
112: }
113: super .startElement(nameCode, typeCode, locationId, properties);
114: }
115:
116: /**
117: * Notify a namespace. Namespaces are notified <b>after</b> the startElement event, and before
118: * any children for the element. The namespaces that are reported are only required
119: * to include those that are different from the parent element; however, duplicates may be reported.
120: * A namespace must not conflict with any namespaces already used for element or attribute names.
121: *
122: * @param namespaceCode an integer: the top half is a prefix code, the bottom half a URI code.
123: * These may be translated into an actual prefix and URI using the name pool. A prefix code of
124: * zero represents the empty prefix (that is, the default namespace). A URI code of zero represents
125: * a URI of "", that is, a namespace undeclaration.
126: * @throws IllegalStateException: attempt to output a namespace when there is no open element
127: * start tag
128: */
129:
130: public void namespace(int namespaceCode, int properties)
131: throws XPathException {
132: if (depthOfHole == 0) {
133: super .namespace(namespaceCode, properties);
134: }
135: }
136:
137: /**
138: * Notify an attribute. Attributes are notified after the startElement event, and before any
139: * children. Namespaces and attributes may be intermingled.
140: *
141: * @param nameCode The name of the attribute, as held in the name pool
142: * @param typeCode The type of the attribute, as held in the name pool
143: * @param properties Bit significant value. The following bits are defined:
144: * <dd>DISABLE_ESCAPING</dd> <dt>Disable escaping for this attribute</dt>
145: * <dd>NO_SPECIAL_CHARACTERS</dd> <dt>Attribute value contains no special characters</dt>
146: * @throws IllegalStateException: attempt to output an attribute when there is no open element
147: * start tag
148: */
149:
150: public void attribute(int nameCode, int typeCode,
151: CharSequence value, int locationId, int properties)
152: throws XPathException {
153: if (depthOfHole == 0) {
154: super .attribute(nameCode, typeCode, value, locationId,
155: properties);
156: }
157: }
158:
159: /**
160: * Notify the start of the content, that is, the completion of all attributes and namespaces.
161: * Note that the initial receiver of output from XSLT instructions will not receive this event,
162: * it has to detect it itself. Note that this event is reported for every element even if it has
163: * no attributes, no namespaces, and no content.
164: */
165:
166: public void startContent() throws XPathException {
167: if (depthOfHole == 0) {
168: super .startContent();
169: }
170: }
171:
172: /**
173: * End of element
174: */
175:
176: public void endElement() throws XPathException {
177: defaultNamespaceStack.pop();
178: if (depthOfHole > 0) {
179: depthOfHole--;
180: } else {
181: super .endElement();
182: }
183: }
184:
185: /**
186: * Character data
187: */
188:
189: public void characters(CharSequence chars, int locationId,
190: int properties) throws XPathException {
191: if (depthOfHole == 0) {
192: super .characters(chars, locationId, properties);
193: }
194: }
195:
196: /**
197: * Processing Instruction
198: */
199:
200: public void processingInstruction(String target, CharSequence data,
201: int locationId, int properties) {
202: // these are ignored in a stylesheet
203: }
204:
205: /**
206: * Output a comment
207: */
208:
209: public void comment(CharSequence chars, int locationId,
210: int properties) throws XPathException {
211: // these are ignored in a stylesheet
212: }
213:
214: /**
215: * Evaluate a use-when attribute
216: */
217:
218: public boolean evaluateUseWhen(String expression, int locationId)
219: throws XPathException {
220: UseWhenStaticContext staticContext = new UseWhenStaticContext(
221: getConfiguration(), startTag);
222: // The following is an approximation: it doesn't take account of xml:base attributes
223: staticContext.setBaseURI(getDocumentLocator().getSystemId(
224: locationId));
225: for (int i = defaultNamespaceStack.size() - 1; i >= 0; i--) {
226: String uri = (String) defaultNamespaceStack.get(i);
227: if (uri != null) {
228: short code = getNamePool().getCodeForURI(uri);
229: staticContext.setDefaultElementNamespace(code);
230: break;
231: }
232: }
233: Expression expr = ExpressionTool.make(expression,
234: staticContext, 0, Token.EOF, getDocumentLocator()
235: .getLineNumber(locationId));
236: ItemType contextItemType = Type.ITEM_TYPE;
237: expr = expr.typeCheck(staticContext, contextItemType);
238: SlotManager stackFrameMap = getPipelineConfiguration()
239: .getConfiguration().makeSlotManager();
240: ExpressionTool.allocateSlots(expr, stackFrameMap
241: .getNumberOfVariables(), stackFrameMap);
242: Controller controller = new Controller(getConfiguration());
243: controller.setURIResolver(new URIPreventer());
244: XPathContext dynamicContext = controller.newXPathContext();
245: dynamicContext = dynamicContext.newCleanContext();
246: ((XPathContextMajor) dynamicContext)
247: .openStackFrame(stackFrameMap.getNumberOfVariables());
248: return expr.effectiveBooleanValue(dynamicContext);
249: }
250:
251: /**
252: * Define a URIResolver that disallows all URIs
253: */
254:
255: private static class URIPreventer implements URIResolver {
256: /**
257: * Called by the processor when it encounters
258: * an xsl:include, xsl:import, or document() function.
259: *
260: * @param href An href attribute, which may be relative or absolute.
261: * @param base The base URI against which the first argument will be made
262: * absolute if the absolute URI is required.
263: * @return A Source object, or null if the href cannot be resolved,
264: * and the processor should try to resolve the URI itself.
265: * @throws javax.xml.transform.TransformerException
266: * if an error occurs when trying to
267: * resolve the URI.
268: */
269: public Source resolve(String href, String base)
270: throws TransformerException {
271: throw new TransformerException(
272: "No external documents are available within an [xsl]use-when expression");
273: }
274: }
275:
276: }
277:
278: //
279: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
280: // you may not use this file except in compliance with the License. You may obtain a copy of the
281: // License at http://www.mozilla.org/MPL/
282: //
283: // Software distributed under the License is distributed on an "AS IS" basis,
284: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
285: // See the License for the specific language governing rights and limitations under the License.
286: //
287: // The Original Code is: all this file.
288: //
289: // The Initial Developer of the Original Code is Michael H. Kay.
290: //
291: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
292: //
293: // Contributor(s): none.
294: //
|