001: package net.sf.saxon.style;
002:
003: import net.sf.saxon.expr.*;
004: import net.sf.saxon.instruct.Executable;
005: import net.sf.saxon.instruct.NumberInstruction;
006: import net.sf.saxon.instruct.ValueOf;
007: import net.sf.saxon.number.NumberFormatter;
008: import net.sf.saxon.number.Numberer;
009: import net.sf.saxon.number.Numberer_en;
010: import net.sf.saxon.om.AttributeCollection;
011: import net.sf.saxon.pattern.NodeKindTest;
012: import net.sf.saxon.pattern.Pattern;
013: import net.sf.saxon.trans.XPathException;
014: import net.sf.saxon.type.ItemType;
015: import net.sf.saxon.value.SequenceType;
016: import net.sf.saxon.value.StringValue;
017:
018: /**
019: * An xsl:number element in the stylesheet. <br>
020: */
021:
022: public class XSLNumber extends StyleElement {
023:
024: private static final int SINGLE = 0;
025: private static final int MULTI = 1;
026: private static final int ANY = 2;
027: private static final int SIMPLE = 3;
028:
029: private int level;
030: private Pattern count = null;
031: private Pattern from = null;
032: private Expression select = null;
033: private Expression value = null;
034: private Expression format = null;
035: private Expression groupSize = null;
036: private Expression groupSeparator = null;
037: private Expression letterValue = null;
038: private Expression lang = null;
039: private Expression ordinal = null;
040: private NumberFormatter formatter = null;
041: private Numberer numberer = null;
042: private boolean hasVariablesInPatterns = false;
043:
044: private static Numberer defaultNumberer = new Numberer_en();
045:
046: /**
047: * Determine whether this node is an instruction.
048: * @return true - it is an instruction
049: */
050:
051: public boolean isInstruction() {
052: return true;
053: }
054:
055: /**
056: * Determine the type of item returned by this instruction (only relevant if
057: * it is an instruction).
058: * @return the item type returned
059: */
060:
061: protected ItemType getReturnedItemType() {
062: return NodeKindTest.TEXT;
063: }
064:
065: public void prepareAttributes() throws XPathException {
066:
067: AttributeCollection atts = getAttributeList();
068:
069: String selectAtt = null;
070: String valueAtt = null;
071: String countAtt = null;
072: String fromAtt = null;
073: String levelAtt = null;
074: String formatAtt = null;
075: String gsizeAtt = null;
076: String gsepAtt = null;
077: String langAtt = null;
078: String letterValueAtt = null;
079: String ordinalAtt = null;
080:
081: for (int a = 0; a < atts.getLength(); a++) {
082: int nc = atts.getNameCode(a);
083: String f = getNamePool().getClarkName(nc);
084: if (f == StandardNames.SELECT) {
085: selectAtt = atts.getValue(a);
086: } else if (f == StandardNames.VALUE) {
087: valueAtt = atts.getValue(a);
088: } else if (f == StandardNames.COUNT) {
089: countAtt = atts.getValue(a);
090: } else if (f == StandardNames.FROM) {
091: fromAtt = atts.getValue(a);
092: } else if (f == StandardNames.LEVEL) {
093: levelAtt = atts.getValue(a).trim();
094: } else if (f == StandardNames.FORMAT) {
095: formatAtt = atts.getValue(a);
096: } else if (f == StandardNames.LANG) {
097: langAtt = atts.getValue(a);
098: } else if (f == StandardNames.LETTER_VALUE) {
099: letterValueAtt = atts.getValue(a).trim();
100: } else if (f == StandardNames.GROUPING_SIZE) {
101: gsizeAtt = atts.getValue(a).trim();
102: } else if (f == StandardNames.GROUPING_SEPARATOR) {
103: gsepAtt = atts.getValue(a);
104: } else if (f == StandardNames.ORDINAL) {
105: ordinalAtt = atts.getValue(a);
106: } else {
107: checkUnknownAttribute(nc);
108: }
109: }
110:
111: if (selectAtt != null) {
112: select = makeExpression(selectAtt);
113: }
114:
115: if (valueAtt != null) {
116: value = makeExpression(valueAtt);
117: if (selectAtt != null) {
118: compileError(
119: "The select attribute and value attribute must not both be present",
120: "XTSE0975");
121: }
122: if (countAtt != null) {
123: compileError(
124: "The count attribute and value attribute must not both be present",
125: "XTSE0975");
126: }
127: if (fromAtt != null) {
128: compileError(
129: "The from attribute and value attribute must not both be present",
130: "XTSE0975");
131: }
132: if (levelAtt != null) {
133: compileError(
134: "The level attribute and value attribute must not both be present",
135: "XTSE0975");
136: }
137: }
138:
139: if (countAtt != null) {
140: count = makePattern(countAtt);
141: // the following test is a very crude way of testing if the pattern might
142: // contain variables, but it's good enough...
143: if (countAtt.indexOf('$') >= 0) {
144: hasVariablesInPatterns = true;
145: }
146: }
147:
148: if (fromAtt != null) {
149: from = makePattern(fromAtt);
150: if (fromAtt.indexOf('$') >= 0) {
151: hasVariablesInPatterns = true;
152: }
153: }
154:
155: if (levelAtt == null) {
156: level = SINGLE;
157: } else if (levelAtt.equals("single")) {
158: level = SINGLE;
159: } else if (levelAtt.equals("multiple")) {
160: level = MULTI;
161: } else if (levelAtt.equals("any")) {
162: level = ANY;
163: } else {
164: compileError("Invalid value for level attribute",
165: "XTSE0020");
166: }
167:
168: if (level == SINGLE && from == null && count == null) {
169: level = SIMPLE;
170: }
171:
172: if (formatAtt != null) {
173: format = makeAttributeValueTemplate(formatAtt);
174: if (format instanceof StringValue) {
175: formatter = new NumberFormatter();
176: formatter.prepare(((StringValue) format)
177: .getStringValue());
178: }
179: // else we'll need to allocate the formatter at run-time
180: } else {
181: formatter = new NumberFormatter();
182: formatter.prepare("1");
183: }
184:
185: if (gsepAtt != null && gsizeAtt != null) {
186: // the spec says that if only one is specified, it is ignored
187: groupSize = makeAttributeValueTemplate(gsizeAtt);
188: groupSeparator = makeAttributeValueTemplate(gsepAtt);
189: }
190:
191: if (langAtt == null) {
192: numberer = defaultNumberer;
193: } else {
194: lang = makeAttributeValueTemplate(langAtt);
195: if (lang instanceof StringValue) {
196: numberer = makeNumberer(((StringValue) lang)
197: .getStringValue());
198: } // else we allocate a numberer at run-time
199: }
200:
201: if (letterValueAtt != null) {
202: letterValue = makeAttributeValueTemplate(letterValueAtt);
203: }
204:
205: if (ordinalAtt != null) {
206: ordinal = makeAttributeValueTemplate(ordinalAtt);
207: }
208:
209: }
210:
211: public void validate() throws XPathException {
212: checkWithinTemplate();
213: checkEmpty();
214:
215: select = typeCheck("select", select);
216: value = typeCheck("value", value);
217: format = typeCheck("format", format);
218: groupSize = typeCheck("group-size", groupSize);
219: groupSeparator = typeCheck("group-separator", groupSeparator);
220: letterValue = typeCheck("letter-value", letterValue);
221: ordinal = typeCheck("ordinal", ordinal);
222: lang = typeCheck("lang", lang);
223: from = typeCheck("from", from);
224: count = typeCheck("count", count);
225:
226: if (select != null) {
227: try {
228: RoleLocator role = new RoleLocator(
229: RoleLocator.INSTRUCTION, "xsl:number/select",
230: 0, null);
231: role.setSourceLocator(new ExpressionLocation(this ));
232: select = TypeChecker.staticTypeCheck(select,
233: SequenceType.SINGLE_NODE, false, role,
234: getStaticContext());
235: } catch (XPathException err) {
236: compileError(err);
237: }
238: }
239: }
240:
241: public Expression compile(Executable exec) throws XPathException {
242: NumberInstruction expr = new NumberInstruction(exec
243: .getConfiguration(), select, level, count, from, value,
244: format, groupSize, groupSeparator, letterValue,
245: ordinal, lang, formatter, numberer,
246: hasVariablesInPatterns,
247: backwardsCompatibleModeIsEnabled());
248: int loc = getStaticContext().getLocationMap()
249: .allocateLocationId(getSystemId(), getLineNumber());
250: expr.setLocationId(loc);
251: ExpressionTool.makeParentReferences(expr);
252: ValueOf inst = new ValueOf(expr, false, false);
253: inst.setLocationId(allocateLocationId(getSystemId(),
254: getLineNumber()));
255: ExpressionTool.makeParentReferences(inst);
256: inst.setIsNumberingInstruction();
257: return inst;
258: }
259:
260: /**
261: * Load a Numberer class for a given language and check it is OK.
262: */
263:
264: protected Numberer makeNumberer(String language) {
265: Numberer numberer;
266: if (language.equals("en")) {
267: numberer = defaultNumberer;
268: } else {
269: String langClassName = "net.sf.saxon.number.Numberer_";
270: for (int i = 0; i < language.length(); i++) {
271: if (Character.isLetter(language.charAt(i))) {
272: langClassName += language.charAt(i);
273: }
274: }
275: try {
276: numberer = (Numberer) (getConfiguration().getInstance(
277: langClassName, null));
278: } catch (Exception err) {
279: numberer = defaultNumberer;
280: }
281: }
282:
283: return numberer;
284: }
285: }
286:
287: //
288: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
289: // you may not use this file except in compliance with the License. You may obtain a copy of the
290: // License at http://www.mozilla.org/MPL/
291: //
292: // Software distributed under the License is distributed on an "AS IS" basis,
293: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
294: // See the License for the specific language governing rights and limitations under the License.
295: //
296: // The Original Code is: all this file.
297: //
298: // The Initial Developer of the Original Code is Michael H. Kay.
299: //
300: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
301: //
302: // Contributor(s): none.
303: //
|