001: package net.sf.saxon.instruct;
002:
003: import net.sf.saxon.Err;
004: import net.sf.saxon.Configuration;
005: import net.sf.saxon.event.SequenceReceiver;
006: import net.sf.saxon.expr.*;
007: import net.sf.saxon.om.NamePool;
008: import net.sf.saxon.pattern.NodeKindTest;
009: import net.sf.saxon.style.StandardNames;
010: import net.sf.saxon.trans.DynamicError;
011: import net.sf.saxon.trans.XPathException;
012: import net.sf.saxon.type.ItemType;
013: import net.sf.saxon.type.TypeHierarchy;
014: import net.sf.saxon.value.SequenceType;
015: import net.sf.saxon.value.Whitespace;
016:
017: import java.io.PrintStream;
018: import java.util.ArrayList;
019: import java.util.Iterator;
020:
021: /**
022: * An xsl:processing-instruction element in the stylesheet.
023: */
024:
025: public class ProcessingInstruction extends SimpleNodeConstructor {
026:
027: private Expression name;
028:
029: /**
030: * Create an xsl:processing-instruction instruction
031: * @param name the expression used to compute the name of the generated
032: * processing-instruction
033: */
034:
035: public ProcessingInstruction(Expression name) {
036: this .name = name;
037: adoptChildExpression(name);
038: }
039:
040: /**
041: * Get the name of this instruction for diagnostic and tracing purposes
042: * @return the string "xsl:processing-instruction"
043: */
044:
045: public int getInstructionNameCode() {
046: return StandardNames.XSL_PROCESSING_INSTRUCTION;
047: }
048:
049: public ItemType getItemType(TypeHierarchy th) {
050: return NodeKindTest.PROCESSING_INSTRUCTION;
051: }
052:
053: public int getCardinality() {
054: return StaticProperty.EXACTLY_ONE;
055: }
056:
057: public Expression simplify(StaticContext env) throws XPathException {
058: name = name.simplify(env);
059: return super .simplify(env);
060: }
061:
062: public void localTypeCheck(StaticContext env,
063: ItemType contextItemType) throws XPathException {
064: name = name.typeCheck(env, contextItemType);
065: adoptChildExpression(name);
066:
067: RoleLocator role = new RoleLocator(RoleLocator.INSTRUCTION,
068: "processing-instruction:name", 0, null);
069: role.setSourceLocator(this );
070: name = TypeChecker.staticTypeCheck(name,
071: SequenceType.SINGLE_STRING, false, role, env);
072: adoptChildExpression(name);
073: }
074:
075: public int getDependencies() {
076: return name.getDependencies() | super .getDependencies();
077: }
078:
079: public Iterator iterateSubExpressions() {
080: ArrayList list = new ArrayList(6);
081: if (select != null) {
082: list.add(select);
083: }
084: // if (separator != null && !(separator instanceof StringValue)) {
085: // list.add(separator);
086: // }
087: list.add(name);
088: return list.iterator();
089: }
090:
091: /**
092: * Offer promotion for subexpressions. The offer will be accepted if the subexpression
093: * is not dependent on the factors (e.g. the context item) identified in the PromotionOffer.
094: * By default the offer is not accepted - this is appropriate in the case of simple expressions
095: * such as constant values and variable references where promotion would give no performance
096: * advantage. This method is always called at compile time.
097: *
098: * @param offer details of the offer, for example the offer to move
099: * expressions that don't depend on the context to an outer level in
100: * the containing expression
101: * @exception XPathException if any error is detected
102: */
103:
104: protected void promoteInst(PromotionOffer offer)
105: throws XPathException {
106: name = doPromotion(name, offer);
107: super .promoteInst(offer);
108: }
109:
110: /**
111: * Process this instruction, that is, produce a processing-instruction node in the
112: * result sequence.
113: * @param context the dynamic context of this transformation
114: * @throws XPathException if any non-recoverable dynamic error occurs
115: * @return always returns null in this implementation
116: */
117:
118: public TailCall processLeavingTail(XPathContext context)
119: throws XPathException {
120: String expandedName = evaluateName(context);
121: if (expandedName != null) {
122: String data = expandChildren(context).toString();
123: data = checkContent(data, context);
124: SequenceReceiver out = context.getReceiver();
125: out
126: .processingInstruction(expandedName, data,
127: locationId, 0);
128: }
129: return null;
130: }
131:
132: /**
133: * Check the content of the node, and adjust it if necessary
134: *
135: * @param data the supplied content
136: * @return the original content, unless adjustments are needed
137: * @throws net.sf.saxon.trans.DynamicError
138: * if the content is invalid
139: */
140:
141: protected String checkContent(String data, XPathContext context)
142: throws DynamicError {
143: int hh;
144: while ((hh = data.indexOf("?>")) >= 0) {
145: if (isXSLT(context)) {
146: data = data.substring(0, hh + 1) + ' '
147: + data.substring(hh + 1);
148: } else {
149: DynamicError err = new DynamicError(
150: "Invalid characters (?>) in processing instruction",
151: this );
152: err.setErrorCode("XQDY0026");
153: err.setXPathContext(context);
154: context.getController().recoverableError(err);
155: }
156: }
157: if (context.getController().getExecutable().getHostLanguage() == Configuration.XQUERY) {
158: data = Whitespace.removeLeadingWhitespace(data).toString();
159: }
160: return data;
161: }
162:
163: protected int evaluateNameCode(XPathContext context)
164: throws XPathException {
165: String expandedName = evaluateName(context);
166: if (expandedName == null) {
167: throw new SkipInstructionException("");
168: }
169: return context.getNamePool().allocate("", "", expandedName);
170: }
171:
172: /**
173: * Evaluate the name of the processing instruction. If it is invalid, report a recoverable error
174: * and return null.
175: * @param context
176: * @return the name of the processing instruction (an NCName), or null, incicating an invalid name
177: * @throws XPathException if evaluation fails, or if the recoverable error is treated as fatal
178: */
179: private String evaluateName(XPathContext context)
180: throws XPathException {
181: String expandedName = name.evaluateAsString(context);
182: if (!(context.getConfiguration().getNameChecker()
183: .isValidNCName(expandedName))) {
184: DynamicError e = new DynamicError(
185: "Processing instruction name "
186: + Err.wrap(expandedName)
187: + " is not a valid NCName");
188: e.setXPathContext(context);
189: e.setErrorCode((isXSLT(context) ? "XTDE0890" : "XQDY0041"));
190: throw dynamicError(this , e, context);
191: }
192: if (expandedName.equalsIgnoreCase("xml")) {
193: DynamicError e = new DynamicError(
194: "Processing instructions cannot be named 'xml' in any combination of upper/lower case");
195: e.setXPathContext(context);
196: e.setErrorCode((isXSLT(context) ? "XTDE0890" : "XQDY0064"));
197: throw dynamicError(this , e, context);
198: }
199: return expandedName;
200: }
201:
202: public void display(int level, NamePool pool, PrintStream out) {
203: out.println(ExpressionTool.indent(level)
204: + "processing-instruction");
205: name.display(level + 1, pool, out);
206: super .display(level + 1, pool, out);
207: }
208:
209: }
210:
211: //
212: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
213: // you may not use this file except in compliance with the License. You may obtain a copy of the
214: // License at http://www.mozilla.org/MPL/
215: //
216: // Software distributed under the License is distributed on an "AS IS" basis,
217: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
218: // See the License for the specific language governing rights and limitations under the License.
219: //
220: // The Original Code is: all this file.
221: //
222: // The Initial Developer of the Original Code is Michael H. Kay.
223: //
224: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
225: //
226: // Contributor(s): none.
227: //
|