001: package net.sf.saxon.style;
002:
003: import net.sf.saxon.Configuration;
004: import net.sf.saxon.expr.Expression;
005: import net.sf.saxon.instruct.Executable;
006: import net.sf.saxon.om.AttributeCollection;
007: import net.sf.saxon.om.NamespaceConstant;
008: import net.sf.saxon.trans.XPathException;
009:
010: import java.text.Collator;
011: import java.text.ParseException;
012: import java.text.RuleBasedCollator;
013: import java.util.Comparator;
014: import java.util.Locale;
015: import java.net.URI;
016: import java.net.URISyntaxException;
017:
018: /**
019: * A saxon:collation element in the style sheet: this is a top-level
020: * element that defines details of a named collation. The attributes of the
021: * element provide different ways of instantiating an instance of java.util.Comparator
022: */
023:
024: public class SaxonCollation extends StyleElement {
025:
026: private String collationName;
027:
028: private Comparator collator; // it's best to supply a java.text.Collator,
029: // but we can cope with any java.util.Comparator
030: private boolean isDefault = false;
031:
032: public void prepareAttributes() throws XPathException {
033:
034: AttributeCollection atts = getAttributeList();
035:
036: String nameAtt = null; // collation name for use in expressions
037: String classAtt = null; // Java class name of Collator
038: String strengthAtt = null; // primary, secondary, tertiary, or identical
039: String decompositionAtt = null; // canonical, full, or none
040: String langAtt = null; // identifies a locale: country+language
041: String rulesAtt = null; // collation rules as used in RuleBasedCollator
042: String defaultAtt = null;
043:
044: for (int a = 0; a < atts.getLength(); a++) {
045: int nc = atts.getNameCode(a);
046: String f = getNamePool().getClarkName(nc);
047: if (f == StandardNames.NAME) {
048: nameAtt = atts.getValue(a).trim();
049: } else if (f == StandardNames.CLASS) {
050: classAtt = atts.getValue(a).trim();
051: } else if (f == StandardNames.STRENGTH) {
052: strengthAtt = atts.getValue(a).trim();
053: } else if (f == StandardNames.DECOMPOSITION) {
054: decompositionAtt = atts.getValue(a).trim();
055: } else if (f == StandardNames.LANG) {
056: langAtt = atts.getValue(a).trim();
057: } else if (f == StandardNames.RULES) {
058: rulesAtt = atts.getValue(a).trim();
059: } else if (f == StandardNames.DEFAULT) {
060: defaultAtt = atts.getValue(a).trim();
061: } else {
062: checkUnknownAttribute(nc);
063: }
064: }
065:
066: if (nameAtt != null) {
067: collationName = nameAtt.trim();
068: URI collationURI;
069: try {
070: collationURI = new URI(collationName);
071: if (!collationURI.isAbsolute()) {
072: URI base = new URI(getBaseURI());
073: collationURI = base.resolve(collationURI);
074: collationName = collationURI.toString();
075: }
076: } catch (URISyntaxException err) {
077: compileError("Collation name '" + collationName
078: + "' is not a valid URI");
079: collationName = NamespaceConstant.CODEPOINT_COLLATION_URI;
080: }
081: }
082:
083: if (classAtt != null) {
084: if (rulesAtt != null || langAtt != null
085: || strengthAtt != null || decompositionAtt != null) {
086: compileError("The class attribute cannot be combined with rules, lang, strength, or decomposition");
087: }
088: try {
089: collator = getConfiguration().makeCollator(classAtt);
090: return;
091: } catch (XPathException err) {
092: collator = Collator.getInstance(); // so that error paths work
093: throw err;
094: }
095: }
096:
097: if (rulesAtt != null) {
098: if (langAtt != null || strengthAtt != null
099: || decompositionAtt != null) {
100: compileError("The rules attribute cannot be combined with lang, strength, or decomposition");
101: }
102: try {
103: collator = new RuleBasedCollator(rulesAtt);
104: } catch (ParseException e) {
105: collator = Collator.getInstance(); // so that error paths work
106: compileError("Invalid collation rules: "
107: + e.getMessage());
108: }
109: }
110:
111: // Start with the lang attribute
112:
113: if (langAtt != null) {
114: collator = Collator.getInstance(Configuration
115: .getLocale(langAtt));
116: } else if (collator == null) {
117: collator = Collator.getInstance(); // use default locale
118: }
119:
120: if (strengthAtt != null && collator instanceof Collator) {
121: if (strengthAtt.equals("primary")) {
122: ((Collator) collator).setStrength(Collator.PRIMARY);
123: } else if (strengthAtt.equals("secondary")) {
124: ((Collator) collator).setStrength(Collator.SECONDARY);
125: } else if (strengthAtt.equals("tertiary")) {
126: ((Collator) collator).setStrength(Collator.TERTIARY);
127: } else if (strengthAtt.equals("identical")) {
128: ((Collator) collator).setStrength(Collator.IDENTICAL);
129: } else {
130: compileError("strength must be primary, secondary, tertiary, or identical");
131: }
132: }
133:
134: if (decompositionAtt != null && collator instanceof Collator) {
135: if (decompositionAtt.equals("none")) {
136: ((Collator) collator)
137: .setDecomposition(Collator.NO_DECOMPOSITION);
138: } else if (decompositionAtt.equals("standard")) {
139: ((Collator) collator)
140: .setDecomposition(Collator.CANONICAL_DECOMPOSITION);
141: } else if (decompositionAtt.equals("full")) {
142: ((Collator) collator)
143: .setDecomposition(Collator.FULL_DECOMPOSITION);
144: } else {
145: compileError("decomposition must be none, standard, or full");
146: }
147: }
148:
149: if (defaultAtt != null) {
150: if (defaultAtt.equals("yes")) {
151: isDefault = true;
152: } else if (defaultAtt.equals("no")) {
153: // ignore it
154: } else {
155: compileError("default attribute must be yes or no");
156: }
157: }
158:
159: // register the collation early, so it's available when optimizing XPath expressions
160: getPrincipalStylesheet().setCollation(collationName, collator,
161: isDefault);
162: }
163:
164: public void validate() throws XPathException {
165: checkTopLevel(null);
166: checkEmpty();
167: }
168:
169: public Expression compile(Executable exec) throws XPathException {
170: getPrincipalStylesheet().setCollation(collationName, collator,
171: isDefault);
172: exec
173: .setReasonUnableToCompile("Cannot compile a stylesheet that uses saxon:collation (because the Java class "
174: + "java.text.RuleBasedCollator is not serializable)");
175: return null;
176: }
177:
178: public String getCollationName() {
179: return collationName;
180: }
181:
182: public boolean isDefaultCollation() {
183: return isDefault;
184: }
185:
186: public Comparator getCollator() {
187: return collator;
188: }
189:
190: /**
191: * Utility method to print details of the locales for which a collator
192: * is available. (The results depend on the Java VM)
193: */
194:
195: public static void main(String[] args) {
196: System.err
197: .println("The following locales have collations available:");
198: Locale[] loc = Collator.getAvailableLocales();
199: for (int i = 0; i < loc.length; i++) {
200: Locale l = loc[i];
201: System.err.println("Locale:"
202: + ("".equals(l.getCountry()) ? "" : " country='"
203: + l.getCountry() + "' ("
204: + l.getDisplayCountry() + ")")
205: + ("".equals(l.getLanguage()) ? "" : " language='"
206: + l.getLanguage() + "' ("
207: + l.getDisplayLanguage() + ")")
208: + ("".equals(l.getVariant()) ? "" : " variant='"
209: + l.getVariant() + "' ("
210: + l.getDisplayVariant() + ")"));
211: }
212: }
213:
214: }
215:
216: //
217: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
218: // you may not use this file except in compliance with the License. You may obtain a copy of the
219: // License at http://www.mozilla.org/MPL/
220: //
221: // Software distributed under the License is distributed on an "AS IS" basis,
222: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
223: // See the License for the specific language governing rights and limitations under the License.
224: //
225: // The Original Code is: all this file.
226: //
227: // The Initial Developer of the Original Code is Michael H. Kay of International Computers Limited (mhkay@iclway.co.uk).
228: //
229: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
230: //
231: // Contributor(s): none.
232: //
|