001: package net.sf.saxon.style;
002:
003: import net.sf.saxon.expr.Expression;
004: import net.sf.saxon.instruct.Executable;
005: import net.sf.saxon.om.AttributeCollection;
006: import net.sf.saxon.om.NamespaceException;
007: import net.sf.saxon.om.QNameException;
008: import net.sf.saxon.trans.DecimalFormatManager;
009: import net.sf.saxon.trans.DecimalSymbols;
010: import net.sf.saxon.trans.StaticError;
011: import net.sf.saxon.trans.XPathException;
012: import net.sf.saxon.value.StringValue;
013:
014: import java.util.Arrays;
015: import java.util.HashMap;
016:
017: /**
018: * Handler for xsl:decimal-format elements in stylesheet. <br>
019: */
020:
021: public class XSLDecimalFormat extends StyleElement {
022:
023: boolean prepared = false;
024:
025: String name;
026: String decimalSeparator;
027: String groupingSeparator;
028: String infinity;
029: String minusSign;
030: String NaN;
031: String percent;
032: String perMille;
033: String zeroDigit;
034: String digit;
035: String patternSeparator;
036:
037: public void prepareAttributes() throws XPathException {
038:
039: if (prepared) {
040: return;
041: }
042: prepared = true;
043:
044: AttributeCollection atts = getAttributeList();
045:
046: for (int a = 0; a < atts.getLength(); a++) {
047: int nc = atts.getNameCode(a);
048: String f = getNamePool().getClarkName(nc);
049: if (f == StandardNames.NAME) {
050: name = atts.getValue(a).trim();
051: } else if (f == StandardNames.DECIMAL_SEPARATOR) {
052: decimalSeparator = atts.getValue(a);
053: } else if (f == StandardNames.GROUPING_SEPARATOR) {
054: groupingSeparator = atts.getValue(a);
055: } else if (f == StandardNames.INFINITY) {
056: infinity = atts.getValue(a);
057: } else if (f == StandardNames.MINUS_SIGN) {
058: minusSign = atts.getValue(a);
059: } else if (f == StandardNames.NAN) {
060: NaN = atts.getValue(a);
061: } else if (f == StandardNames.PERCENT) {
062: percent = atts.getValue(a);
063: } else if (f == StandardNames.PER_MILLE) {
064: perMille = atts.getValue(a);
065: } else if (f == StandardNames.ZERO_DIGIT) {
066: zeroDigit = atts.getValue(a);
067: } else if (f == StandardNames.DIGIT) {
068: digit = atts.getValue(a);
069: } else if (f == StandardNames.PATTERN_SEPARATOR) {
070: patternSeparator = atts.getValue(a);
071: } else {
072: checkUnknownAttribute(nc);
073: }
074: }
075: }
076:
077: public void validate() throws XPathException {
078: checkTopLevel(null);
079: checkEmpty();
080: }
081:
082: public DecimalSymbols makeDecimalFormatSymbols()
083: throws XPathException {
084: DecimalSymbols d = new DecimalSymbols();
085: DecimalFormatManager.setDefaults(d);
086: if (decimalSeparator != null) {
087: d.decimalSeparator = (toChar(decimalSeparator));
088: }
089: if (groupingSeparator != null) {
090: d.groupingSeparator = (toChar(groupingSeparator));
091: }
092: if (infinity != null) {
093: d.infinity = (infinity);
094: }
095: if (minusSign != null) {
096: d.minusSign = (toChar(minusSign));
097: }
098: if (NaN != null) {
099: d.NaN = (NaN);
100: }
101: if (percent != null) {
102: d.percent = (toChar(percent));
103: }
104: if (perMille != null) {
105: d.permill = (toChar(perMille));
106: }
107: if (zeroDigit != null) {
108: d.zeroDigit = (toChar(zeroDigit));
109: checkZeroDigit();
110: }
111: if (digit != null) {
112: d.digit = (toChar(digit));
113: }
114: if (patternSeparator != null) {
115: d.patternSeparator = (toChar(patternSeparator));
116: }
117: checkDistinctRoles(d);
118: return d;
119: }
120:
121: /**
122: * Check that no character is used in more than one role
123: * @throws XPathException
124: */
125:
126: private void checkDistinctRoles(DecimalSymbols dfs)
127: throws XPathException {
128: HashMap map = new HashMap(20);
129: Integer c = new Integer(dfs.decimalSeparator);
130: map.put(c, StandardNames.DECIMAL_SEPARATOR);
131:
132: c = new Integer(dfs.groupingSeparator);
133: if (map.get(c) != null) {
134: duplicate(StandardNames.GROUPING_SEPARATOR, (String) map
135: .get(c));
136: }
137: map.put(c, StandardNames.GROUPING_SEPARATOR);
138:
139: c = new Integer(dfs.percent);
140: if (map.get(c) != null) {
141: duplicate(StandardNames.PERCENT, (String) map.get(c));
142: }
143: map.put(c, StandardNames.PERCENT);
144:
145: c = new Integer(dfs.permill);
146: if (map.get(c) != null) {
147: duplicate(StandardNames.PER_MILLE, (String) map.get(c));
148: }
149: map.put(c, StandardNames.PER_MILLE);
150:
151: c = new Integer(dfs.zeroDigit);
152: if (map.get(c) != null) {
153: duplicate(StandardNames.ZERO_DIGIT, (String) map.get(c));
154: }
155: map.put(c, StandardNames.ZERO_DIGIT);
156:
157: c = new Integer(dfs.digit);
158: if (map.get(c) != null) {
159: duplicate(StandardNames.DIGIT, (String) map.get(c));
160: }
161: map.put(c, StandardNames.DIGIT);
162:
163: c = new Integer(dfs.patternSeparator);
164: if (map.get(c) != null) {
165: duplicate(StandardNames.PATTERN_SEPARATOR, (String) map
166: .get(c));
167: }
168: map.put(c, StandardNames.PATTERN_SEPARATOR);
169: }
170:
171: private void duplicate(String role1, String role2)
172: throws XPathException {
173: compileError("The same character is used as the " + role1
174: + " and as the " + role2, "XTSE1300");
175: }
176:
177: /**
178: * Check that the character declared as a zero-digit is indeed a valid zero-digit
179: * @throws XPathException
180: */
181:
182: public void checkZeroDigit() throws XPathException {
183: int d;
184: if (zeroDigit.length() == 1) {
185: d = zeroDigit.charAt(0);
186: } else {
187: d = StringValue.expand(zeroDigit)[0];
188: }
189: if (Arrays.binarySearch(zeroDigits, d) < 0) {
190: compileError(
191: "The value of the zero-digit attribute must be a Unicode digit with value zero",
192: "XTSE1295");
193: }
194: }
195:
196: static int[] zeroDigits = { 0x0030, 0x0660, 0x06f0, 0x0966, 0x09e6,
197: 0x0a66, 0x0ae6, 0x0b66, 0x0be6, 0x0c66, 0x0ce6, 0x0d66,
198: 0x0e50, 0x0ed0, 0x0f20, 0x1040, 0x17e0, 0x1810, 0x1946,
199: 0x19d0, 0xff10, 0x104a0, 0x1d7ce, 0x1d7d8, 0x1d7e2,
200: 0x1d7ec, 0x1d7f6 };
201:
202: public void register() throws XPathException {
203: prepareAttributes();
204: DecimalSymbols d = makeDecimalFormatSymbols();
205: DecimalFormatManager dfm = getPrincipalStylesheet()
206: .getDecimalFormatManager();
207: if (name == null) {
208: try {
209: dfm.setDefaultDecimalFormat(d, getPrecedence());
210: } catch (StaticError err) {
211: compileError(err.getMessage(), err
212: .getErrorCodeLocalPart());
213: }
214: } else {
215: try {
216: makeNameCode(name); // checks for reserved namespaces
217: String[] parts = getConfiguration().getNameChecker()
218: .getQNameParts(name);
219: String uri = getURIForPrefix(parts[0], false);
220: try {
221: dfm.setNamedDecimalFormat(uri, parts[1], d,
222: getPrecedence());
223: } catch (StaticError err) {
224: compileError(err.getMessage(), err
225: .getErrorCodeLocalPart());
226: }
227: } catch (XPathException err) {
228: compileError("Invalid decimal format name. "
229: + err.getMessage(), "XTSE0020");
230: } catch (QNameException err) {
231: compileError("Invalid decimal format name. "
232: + err.getMessage(), "XTSE0020");
233: } catch (NamespaceException err) {
234: compileError("Invalid decimal format name. "
235: + err.getMessage(), "XTSE0280");
236: }
237: }
238: }
239:
240: public Expression compile(Executable exec) throws XPathException {
241: return null;
242: }
243:
244: /**
245: * Get the Unicode codepoint corresponding to a String, which must represent a single Unicode character
246: * @param s the input string, representing a single Unicode character, perhaps as a surrogate pair
247: * @return
248: * @throws XPathException
249: */
250: private int toChar(String s) throws XPathException {
251: int[] e = StringValue.expand(s);
252: if (e.length != 1)
253: compileError("Attribute \"" + s
254: + "\" should be a single character", "XTSE0020");
255: return e[0];
256: }
257:
258: }
259: //
260: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
261: // you may not use this file except in compliance with the License. You may obtain a copy of the
262: // License at http://www.mozilla.org/MPL/
263: //
264: // Software distributed under the License is distributed on an "AS IS" basis,
265: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
266: // See the License for the specific language governing rights and limitations under the License.
267: //
268: // The Original Code is: all this file.
269: //
270: // The Initial Developer of the Original Code is Michael H. Kay.
271: //
272: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
273: //
274: // Contributor(s): none.
275: //
|