package net.sf.saxon.java;
import net.sf.saxon.Configuration;
import net.sf.saxon.sort.*;
import net.sf.saxon.trans.XPathException;
import java.text.Collator;
import java.text.ParseException;
import java.text.RuleBasedCollator;
import java.util.Comparator;
import java.util.Locale;
import java.util.Properties;
/**
* A JavaCollationFactory allows a Collation to be created given
* a set of properties that the collation should have. This class creates a collator using
* the facilities of the Java platform; there is a corresponding class that uses the .NET
* platform.
*/
public abstract class JavaCollationFactory {
/**
* The class is a never instantiated
*/
private JavaCollationFactory() {
}
/**
* Make a collator with given properties
* @param config the Configuration
* @param props the desired properties of the collation
* @return a collation with these properties
*/
public static StringCollator makeCollation(Configuration config, String uri, Properties props)
throws XPathException {
Collator collator = null;
StringCollator stringCollator = null;
// If a specific collation class is requested, this overrides everything else
String classAtt = props.getProperty("class");
if (classAtt != null) {
Object comparator = config.getInstance(classAtt, null);
if (comparator instanceof Collator) {
collator = (Collator)comparator;
} else if (comparator instanceof StringCollator) {
stringCollator = (StringCollator)comparator;
} else if (comparator instanceof Comparator) {
stringCollator = new NamedCollation(uri, (Comparator)comparator);
} else {
throw new XPathException("Requested collation class " + classAtt + " is not a Comparator");
}
}
// If rules are specified, create a RuleBasedCollator
if (collator == null && stringCollator == null) {
String rulesAtt = props.getProperty("rules");
if (rulesAtt != null) {
try {
collator = new RuleBasedCollator(rulesAtt);
} catch (ParseException e) {
throw new XPathException("Invalid collation rules: " + e.getMessage());
}
}
// Start with the lang attribute
if (collator == null) {
String langAtt = props.getProperty("lang");
if (langAtt!=null) {
collator = Collator.getInstance(getLocale(langAtt));
} else {
collator = Collator.getInstance(); // use default locale
}
}
}
if (collator != null) {
// See if there is a strength attribute
String strengthAtt = props.getProperty("strength");
if (strengthAtt!=null) {
if (strengthAtt.equals("primary")) {
collator.setStrength(Collator.PRIMARY);
} else if (strengthAtt.equals("secondary")) {
collator.setStrength(Collator.SECONDARY);
} else if (strengthAtt.equals("tertiary")) {
collator.setStrength(Collator.TERTIARY);
} else if (strengthAtt.equals("identical")) {
collator.setStrength(Collator.IDENTICAL);
} else {
throw new XPathException("strength must be primary, secondary, tertiary, or identical");
}
}
// Look for the properties ignore-case, ignore-modifiers, ignore-width
String ignore = props.getProperty("ignore-width");
if (ignore != null) {
if (ignore.equals("yes") && strengthAtt == null) {
collator.setStrength(Collator.TERTIARY);
} else if (ignore.equals("no")) {
// no-op
} else {
throw new XPathException("ignore-width must be yes or no");
}
}
ignore = props.getProperty("ignore-case");
if (ignore != null && strengthAtt == null) {
if (ignore.equals("yes")) {
collator.setStrength(Collator.SECONDARY);
} else if (ignore.equals("no")) {
// no-op
} else {
throw new XPathException("ignore-case must be yes or no");
}
}
ignore = props.getProperty("ignore-modifiers");
if (ignore != null) {
if (ignore.equals("yes") && strengthAtt == null) {
collator.setStrength(Collator.PRIMARY);
} else if (ignore.equals("no")) {
// no-op
} else {
throw new XPathException("ignore-modifiers must be yes or no");
}
}
// The ignore-symbols property is ignored
// See if there is a decomposition attribute
String decompositionAtt = props.getProperty("decomposition");
if (decompositionAtt!=null) {
if (decompositionAtt.equals("none")) {
collator.setDecomposition(Collator.NO_DECOMPOSITION);
} else if (decompositionAtt.equals("standard")) {
collator.setDecomposition(Collator.CANONICAL_DECOMPOSITION);
} else if (decompositionAtt.equals("full")) {
collator.setDecomposition(Collator.FULL_DECOMPOSITION);
} else {
throw new XPathException("decomposition must be non, standard, or full");
}
}
}
if (stringCollator == null) {
stringCollator = new NamedCollation(uri, collator);
}
// See if there is a case-order property
String caseOrder = props.getProperty("case-order");
if (caseOrder != null && !"#default".equals(caseOrder)) {
// force base collator to ignore case differences
if (collator != null) {
collator.setStrength(Collator.SECONDARY);
}
if (caseOrder.equals("lower-first")) {
stringCollator = new LowercaseFirstCollator(stringCollator);
} else if (caseOrder.equals("upper-first")) {
stringCollator = new UppercaseFirstCollator(stringCollator);
} else {
throw new XPathException("case-order must be lower-first, upper-first, or #default");
}
}
// See if there is an alphanumeric property
String alphanumeric = props.getProperty("alphanumeric");
if (alphanumeric != null && !"no".equals(alphanumeric)) {
if (alphanumeric.equals("yes")) {
stringCollator = new AlphanumericCollator(stringCollator);
} else if (alphanumeric.equals("codepoint")) {
stringCollator = new AlphanumericCollator(CodepointCollator.getInstance());
} else {
throw new XPathException("alphanumeric must be yes, no, or codepoint");
}
}
return stringCollator;
}
/**
* Get a locale given a language code in XML format
*/
private static Locale getLocale(String lang) {
int hyphen = lang.indexOf("-");
String language, country;
if (hyphen < 1) {
language = lang;
country = "";
} else {
language = lang.substring(0, hyphen);
country = lang.substring(hyphen+1);
}
return new Locale(language, country);
}
}
//
// The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
// you may not use this file except in compliance with the License. You may obtain a copy of the
// License at http://www.mozilla.org/MPL/
//
// Software distributed under the License is distributed on an "AS IS" basis,
// WITHOUT WARRANTY OF ANY KIND, either express or implied.
// See the License for the specific language governing rights and limitations under the License.
//
// The Original Code is: all this file.
//
// The Initial Developer of the Original Code is Michael Kay
//
// Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
//
// Contributor(s): none.
//