001: package net.sf.saxon.style;
002:
003: import net.sf.saxon.expr.*;
004: import net.sf.saxon.functions.Matches;
005: import net.sf.saxon.instruct.AnalyzeString;
006: import net.sf.saxon.instruct.Executable;
007: import net.sf.saxon.om.AttributeCollection;
008: import net.sf.saxon.om.Axis;
009: import net.sf.saxon.om.AxisIterator;
010: import net.sf.saxon.om.NodeInfo;
011: import net.sf.saxon.trans.XPathException;
012: import net.sf.saxon.type.ItemType;
013: import net.sf.saxon.type.RegexTranslator;
014: import net.sf.saxon.value.SequenceType;
015: import net.sf.saxon.value.StringValue;
016:
017: import java.util.regex.Pattern;
018: import java.util.regex.PatternSyntaxException;
019:
020: /**
021: * An xsl:analyze-string elements in the stylesheet. New at XSLT 2.0<BR>
022: */
023:
024: public class XSLAnalyzeString extends StyleElement {
025:
026: private Expression select;
027: private Expression regex;
028: private Expression flags;
029: private StyleElement matching;
030: private StyleElement nonMatching;
031: private Pattern pattern;
032:
033: /**
034: * Determine whether this node is an instruction.
035: * @return true - it is an instruction
036: */
037:
038: public boolean isInstruction() {
039: return true;
040: }
041:
042: /**
043: * Determine whether this type of element is allowed to contain an xsl:fallback
044: * instruction
045: */
046:
047: public boolean mayContainFallback() {
048: return true;
049: }
050:
051: /**
052: * Determine the type of item returned by this instruction (only relevant if
053: * it is an instruction).
054: * @return the item type returned
055: */
056:
057: protected ItemType getReturnedItemType() {
058: return getCommonChildItemType();
059: }
060:
061: public void prepareAttributes() throws XPathException {
062: String selectAtt = null;
063: String regexAtt = null;
064: String flagsAtt = null;
065:
066: AttributeCollection atts = getAttributeList();
067:
068: for (int a = 0; a < atts.getLength(); a++) {
069: int nc = atts.getNameCode(a);
070: String f = getNamePool().getClarkName(nc);
071: if (f == StandardNames.REGEX) {
072: regexAtt = atts.getValue(a);
073: } else if (f == StandardNames.SELECT) {
074: selectAtt = atts.getValue(a);
075: } else if (f == StandardNames.FLAGS) {
076: flagsAtt = atts.getValue(a).trim();
077: } else {
078: checkUnknownAttribute(nc);
079: }
080: }
081:
082: if (selectAtt == null) {
083: reportAbsence("select");
084: selectAtt = "."; // for error recovery
085: }
086: select = makeExpression(selectAtt);
087:
088: if (regexAtt == null) {
089: reportAbsence("regex");
090: regexAtt = "xxx"; // for error recovery
091: }
092: regex = makeAttributeValueTemplate(regexAtt);
093:
094: if (flagsAtt == null) {
095: flagsAtt = "";
096: }
097: flags = makeAttributeValueTemplate(flagsAtt);
098:
099: if (regex instanceof StringValue
100: && flags instanceof StringValue) {
101: int jflags = 0;
102: try {
103: jflags = Matches.setFlags(((StringValue) flags)
104: .getStringValueCS());
105: } catch (XPathException err) {
106: compileError(
107: "Invalid value of flags attribute: " + err,
108: "XTDE1145");
109: jflags = 0;
110: }
111: try {
112: String javaRegex = RegexTranslator.translate(
113: ((StringValue) regex).getStringValueCS(), true);
114: pattern = Pattern.compile(javaRegex, jflags);
115: if (pattern.matcher("").matches()) {
116: invalidRegex(
117: "The regular expression must not be one that matches a zero-length string",
118: "XTDE1150");
119: }
120: } catch (RegexTranslator.RegexSyntaxException err) {
121: invalidRegex("Error in regular expression: " + err,
122: "XTDE1140");
123: } catch (PatternSyntaxException err) {
124: invalidRegex("Error in regular expression: " + err,
125: "XTDE1140");
126: }
127: }
128:
129: }
130:
131: private void invalidRegex(String message, String errorCode)
132: throws XPathException {
133: // if (forwardsCompatibleModeIsEnabled()) {
134: // pattern = null; // defer the error until run-time
135: // } else {
136: compileError(message, errorCode);
137: pattern = null;
138: // }
139: }
140:
141: public void validate() throws XPathException {
142: checkWithinTemplate();
143:
144: AxisIterator kids = iterateAxis(Axis.CHILD);
145: while (true) {
146: NodeInfo curr = (NodeInfo) kids.next();
147: if (curr == null) {
148: break;
149: }
150: if (curr instanceof XSLFallback) {
151: // no-op
152: } else if (curr instanceof XSLMatchingSubstring) {
153: boolean b = curr.getLocalPart().equals(
154: "matching-substring");
155: if (b) {
156: if (matching != null) {
157: compileError(
158: "xsl:matching-substring element must only appear once",
159: "XTSE0010");
160: }
161: matching = (StyleElement) curr;
162: } else {
163: if (nonMatching != null) {
164: compileError(
165: "xsl:non-matching-substring element must only appear once",
166: "XTSE0010");
167: }
168: nonMatching = (StyleElement) curr;
169: }
170: } else {
171: compileError(
172: "Only xsl:matching-substring and xsl:non-matching-substring are allowed here",
173: "XTSE0010");
174: }
175: }
176:
177: if (matching == null && nonMatching == null) {
178: compileError(
179: "At least one xsl:matching-substring or xsl:non-matching-substring element must be present",
180: "XTSE1130");
181: }
182:
183: select = typeCheck("select", select);
184: regex = typeCheck("regex", regex);
185: flags = typeCheck("flags", flags);
186:
187: try {
188: RoleLocator role = new RoleLocator(RoleLocator.INSTRUCTION,
189: "xsl:analyze-string/select", 0, null);
190: ExpressionLocation locator = new ExpressionLocation(this );
191: role.setSourceLocator(locator);
192: select = TypeChecker.staticTypeCheck(select,
193: SequenceType.SINGLE_STRING, false, role,
194: getStaticContext());
195:
196: role = new RoleLocator(RoleLocator.INSTRUCTION,
197: "xsl:analyze-string/regex", 0, null);
198: role.setSourceLocator(locator);
199: regex = TypeChecker.staticTypeCheck(regex,
200: SequenceType.SINGLE_STRING, false, role,
201: getStaticContext());
202:
203: role = new RoleLocator(RoleLocator.INSTRUCTION,
204: "xsl:analyze-string/flags", 0, null);
205: role.setSourceLocator(locator);
206: flags = TypeChecker.staticTypeCheck(flags,
207: SequenceType.SINGLE_STRING, false, role,
208: getStaticContext());
209: } catch (XPathException err) {
210: compileError(err);
211: }
212:
213: }
214:
215: public Expression compile(Executable exec) throws XPathException {
216: Expression matchingBlock = null;
217: if (matching != null) {
218: matchingBlock = matching.compileSequenceConstructor(exec,
219: matching.iterateAxis(Axis.CHILD), false);
220: }
221:
222: Expression nonMatchingBlock = null;
223: if (nonMatching != null) {
224: nonMatchingBlock = nonMatching.compileSequenceConstructor(
225: exec, nonMatching.iterateAxis(Axis.CHILD), false);
226: }
227:
228: try {
229: AnalyzeString anal = new AnalyzeString(select, regex,
230: flags, (matchingBlock == null ? null
231: : matchingBlock.simplify(matching
232: .getStaticContext())),
233: (nonMatchingBlock == null ? null : nonMatchingBlock
234: .simplify(nonMatching.getStaticContext())),
235: pattern);
236: ExpressionTool.makeParentReferences(anal);
237: return anal;
238: } catch (XPathException e) {
239: compileError(e);
240: return null;
241: }
242: }
243:
244: }
245:
246: //
247: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
248: // you may not use this file except in compliance with the License. You may obtain a copy of the
249: // License at http://www.mozilla.org/MPL/
250: //
251: // Software distributed under the License is distributed on an "AS IS" basis,
252: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
253: // See the License for the specific language governing rights and limitations under the License.
254: //
255: // The Original Code is: all this file.
256: //
257: // The Initial Developer of the Original Code is Michael H. Kay
258: //
259: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
260: //
261: // Contributor(s):
262: // Portions marked "e.g." are from Edwin Glaser (edwin@pannenleiter.de)
263: //
|