ICU-4700 more cldr tools
X-SVN-Rev: 18830
This commit is contained in:
parent
c932702a9c
commit
9a15ff31a2
@ -9,6 +9,7 @@
|
|||||||
package com.ibm.icu.dev.test.format;
|
package com.ibm.icu.dev.test.format;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.PrintStream;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
@ -35,7 +36,8 @@ public class GlobalizationPreferencesTest {
|
|||||||
private static final String[] WidthNames = {"abbreviated", "wide", "narrow"};
|
private static final String[] WidthNames = {"abbreviated", "wide", "narrow"};
|
||||||
|
|
||||||
public static void main(String[] args) throws IOException {
|
public static void main(String[] args) throws IOException {
|
||||||
PrintWriter out = BagFormatter.openUTF8Writer("c:/", "tempFile.txt");
|
PrintStream out = System.out;
|
||||||
|
//PrintWriter out = BagFormatter.openUTF8Writer("c:/", "tempFile.txt");
|
||||||
try {
|
try {
|
||||||
Date now = new Date();
|
Date now = new Date();
|
||||||
|
|
||||||
@ -49,13 +51,13 @@ public class GlobalizationPreferencesTest {
|
|||||||
out.println("Check defaulting");
|
out.println("Check defaulting");
|
||||||
String[] localeList = {"fr_BE;q=0.5,de", "fr_BE,de", "fr", "en_NZ", "en", "en-TH", "zh-Hant", "zh-MO", "zh", "it", "as", "haw", "ar-EG", "ar", "qqq"};
|
String[] localeList = {"fr_BE;q=0.5,de", "fr_BE,de", "fr", "en_NZ", "en", "en-TH", "zh-Hant", "zh-MO", "zh", "it", "as", "haw", "ar-EG", "ar", "qqq"};
|
||||||
for (int i = 0; i < localeList.length; ++i) {
|
for (int i = 0; i < localeList.length; ++i) {
|
||||||
lPreferences.setULocales(localeList[i]);
|
lPreferences.setLocales(localeList[i]);
|
||||||
out.println("\tdefaults for: \t" + localeList[i] + "\t"
|
out.println("\tdefaults for: \t" + localeList[i] + "\t"
|
||||||
+ lPreferences.getULocales()
|
+ lPreferences.getLocales()
|
||||||
+ ", \t" + lPreferences.getTerritory()
|
+ ", \t" + lPreferences.getTerritory()
|
||||||
+ ", \t" + lPreferences.getCurrency()
|
+ ", \t" + lPreferences.getCurrency()
|
||||||
+ ", \t" + lPreferences.getCalendar().getClass()
|
+ ", \t" + lPreferences.getCalendar().getClass()
|
||||||
+ ", \t" + lPreferences.getTimezone().getID()
|
+ ", \t" + lPreferences.getTimeZone().getID()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,13 +68,17 @@ public class GlobalizationPreferencesTest {
|
|||||||
out.println("\tdate: \t" + lPreferences.getDateFormat(DateFormat.FULL, GlobalizationPreferences.NONE).format(now));
|
out.println("\tdate: \t" + lPreferences.getDateFormat(DateFormat.FULL, GlobalizationPreferences.NONE).format(now));
|
||||||
|
|
||||||
out.println("setting locale to Germany");
|
out.println("setting locale to Germany");
|
||||||
lPreferences.setULocales(ULocale.GERMANY);
|
lPreferences.setLocale(ULocale.GERMANY);
|
||||||
out.println("\tdate: \t" + lPreferences.getDateFormat(DateFormat.FULL, GlobalizationPreferences.NONE).format(now));
|
out.println("\tdate: \t" + lPreferences.getDateFormat(DateFormat.FULL, GlobalizationPreferences.NONE).format(now));
|
||||||
|
|
||||||
out.println("setting date locale to France");
|
out.println("setting date locale to France");
|
||||||
lPreferences.setDateLocale(ULocale.FRANCE);
|
lPreferences.setDateLocale(ULocale.FRANCE);
|
||||||
out.println("\tdate: \t" + lPreferences.getDateFormat(DateFormat.FULL, GlobalizationPreferences.NONE).format(now));
|
out.println("\tdate: \t" + lPreferences.getDateFormat(DateFormat.FULL, GlobalizationPreferences.NONE).format(now));
|
||||||
|
|
||||||
|
out.println("setting explicit pattern");
|
||||||
|
lPreferences.setDateFormat(DateFormat.FULL, GlobalizationPreferences.NONE, "GGG yyyy+MMM+DD vvvv");
|
||||||
|
out.println("\tdate: \t" + lPreferences.getDateFormat(DateFormat.FULL, GlobalizationPreferences.NONE).format(now));
|
||||||
|
|
||||||
out.println("setting date format to yyyy-MMM-dd (Italy)");
|
out.println("setting date format to yyyy-MMM-dd (Italy)");
|
||||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MMM-dd",ULocale.ITALY);
|
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MMM-dd",ULocale.ITALY);
|
||||||
lPreferences.setDateFormat(DateFormat.FULL, GlobalizationPreferences.NONE, sdf);
|
lPreferences.setDateFormat(DateFormat.FULL, GlobalizationPreferences.NONE, sdf);
|
||||||
@ -111,11 +117,21 @@ public class GlobalizationPreferencesTest {
|
|||||||
lPreferences.setNumberLocale(new ULocale("hi-IN"));
|
lPreferences.setNumberLocale(new ULocale("hi-IN"));
|
||||||
out.println("\tcurrency: \t" + lPreferences.getNumberFormat(GlobalizationPreferences.CURRENCY).format(1234.567));
|
out.println("\tcurrency: \t" + lPreferences.getNumberFormat(GlobalizationPreferences.CURRENCY).format(1234.567));
|
||||||
|
|
||||||
|
out.println();
|
||||||
|
out.println("Comparison");
|
||||||
|
out.println("setting number locale to Germany");
|
||||||
|
lPreferences.setLocale(ULocale.GERMANY);
|
||||||
|
out.println("\tcompare: \u00e4 & z \t" + lPreferences.getCollator().compare("\u00e4", "z"));
|
||||||
|
|
||||||
|
out.println("setting number locale to Swedish");
|
||||||
|
lPreferences.setLocale(new ULocale("sv"));
|
||||||
|
out.println("\tcompare: \u00e4 & z \t" + lPreferences.getCollator().compare("\u00e4", "z"));
|
||||||
|
|
||||||
// now try a fallback within locales
|
// now try a fallback within locales
|
||||||
out.println();
|
out.println();
|
||||||
out.println("Display Names");
|
out.println("Display Names");
|
||||||
lPreferences.setULocales(new ULocale[]{new ULocale("as"),new ULocale("pl"),new ULocale("fr")});
|
lPreferences.setLocales(new ULocale[]{new ULocale("as"),new ULocale("pl"),new ULocale("fr")});
|
||||||
out.println("Trying fallback for multiple locales: " + lPreferences.getULocales());
|
out.println("Trying fallback for multiple locales: " + lPreferences.getLocales());
|
||||||
String[][] testItems = {
|
String[][] testItems = {
|
||||||
{GlobalizationPreferences.LOCALEID+"", "as_FR", "en_RU","haw_CA","se_Cyrl_AT"},
|
{GlobalizationPreferences.LOCALEID+"", "as_FR", "en_RU","haw_CA","se_Cyrl_AT"},
|
||||||
{GlobalizationPreferences.LANGUAGEID+"", "as", "en","haw","se","kok"},
|
{GlobalizationPreferences.LANGUAGEID+"", "as", "en","haw","se","kok"},
|
||||||
|
@ -42,75 +42,6 @@ public class BagFormatter {
|
|||||||
SHOW_FILES = showFiles;
|
SHOW_FILES = showFiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final String BASE_RULES =
|
|
||||||
"'<' > '<' ;" +
|
|
||||||
"'<' < '&'[lL][Tt]';' ;" +
|
|
||||||
"'&' > '&' ;" +
|
|
||||||
"'&' < '&'[aA][mM][pP]';' ;" +
|
|
||||||
"'>' < '&'[gG][tT]';' ;" +
|
|
||||||
"'\"' < '&'[qQ][uU][oO][tT]';' ; " +
|
|
||||||
"'' < '&'[aA][pP][oO][sS]';' ; ";
|
|
||||||
|
|
||||||
private static final String CONTENT_RULES =
|
|
||||||
"'>' > '>' ;";
|
|
||||||
|
|
||||||
private static final String HTML_RULES = BASE_RULES + CONTENT_RULES +
|
|
||||||
"'\"' > '"' ; ";
|
|
||||||
|
|
||||||
private static final String HTML_RULES_CONTROLS = HTML_RULES +
|
|
||||||
"([[:C:][:Z:][:whitespace:][:Default_Ignorable_Code_Point:]]) > &hex/unicode($1) ; ";
|
|
||||||
|
|
||||||
private static final String XML_RULES = HTML_RULES +
|
|
||||||
"'' > ''' ; ";
|
|
||||||
|
|
||||||
/*
|
|
||||||
The ampersand character (&) and the left angle bracket (<) MUST NOT appear
|
|
||||||
|
|
||||||
in their literal form, except when used as markup delimiters, or within a
|
|
||||||
|
|
||||||
comment, a processing instruction, or a CDATA section. If they are needed
|
|
||||||
|
|
||||||
elsewhere, they MUST be escaped using either numeric character references or
|
|
||||||
|
|
||||||
the strings "&" and "<" respectively. The right angle bracket (>) MAY
|
|
||||||
|
|
||||||
be represented using the string ">", and MUST, for compatibility, be
|
|
||||||
|
|
||||||
escaped using either ">" or a character reference when it appears in the string
|
|
||||||
|
|
||||||
"]]>" in content, when that string is not marking the end of a CDATA section.
|
|
||||||
|
|
||||||
In the content of elements, character data is any string of characters which does
|
|
||||||
|
|
||||||
not contain the start-delimiter of any markup and does not include the
|
|
||||||
|
|
||||||
CDATA-section-close delimiter, "]]>". In a CDATA section, character data is
|
|
||||||
|
|
||||||
any string of characters not including the CDATA-section-close delimiter,
|
|
||||||
|
|
||||||
"]]>".
|
|
||||||
|
|
||||||
To allow attribute values to contain both single and double quotes, the
|
|
||||||
|
|
||||||
apostrophe or single-quote character (') MAY be represented as "'", and
|
|
||||||
|
|
||||||
the double-quote character (") as """.
|
|
||||||
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
public static final Transliterator toXML = Transliterator.createFromRules(
|
|
||||||
"any-xml", XML_RULES, Transliterator.FORWARD);
|
|
||||||
public static final Transliterator fromXML = Transliterator.createFromRules(
|
|
||||||
"xml-any", XML_RULES, Transliterator.REVERSE);
|
|
||||||
|
|
||||||
public static final Transliterator toHTML = Transliterator.createFromRules(
|
|
||||||
"any-html", HTML_RULES, Transliterator.FORWARD);
|
|
||||||
public static final Transliterator toHTMLControl = Transliterator.createFromRules(
|
|
||||||
"any-html", HTML_RULES_CONTROLS, Transliterator.FORWARD);
|
|
||||||
public static final Transliterator fromHTML = Transliterator.createFromRules(
|
|
||||||
"html-any", HTML_RULES, Transliterator.REVERSE);
|
|
||||||
|
|
||||||
public static final PrintWriter CONSOLE = new PrintWriter(System.out,true);
|
public static final PrintWriter CONSOLE = new PrintWriter(System.out,true);
|
||||||
|
|
||||||
private static PrintWriter log = CONSOLE;
|
private static PrintWriter log = CONSOLE;
|
||||||
|
@ -61,4 +61,80 @@ public class TransliteratorUtilities {
|
|||||||
br.close();
|
br.close();
|
||||||
return buffer.toString();
|
return buffer.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final String BASE_RULES =
|
||||||
|
":: (hex-any/xml);" +
|
||||||
|
":: (hex-any/xml10);" +
|
||||||
|
"'<' > '<' ;" +
|
||||||
|
"'<' < '&'[lL][Tt]';' ;" +
|
||||||
|
"'&' > '&' ;" +
|
||||||
|
"'&' < '&'[aA][mM][pP]';' ;" +
|
||||||
|
"'>' < '&'[gG][tT]';' ;" +
|
||||||
|
"'\"' < '&'[qQ][uU][oO][tT]';' ; " +
|
||||||
|
"'' < '&'[aA][pP][oO][sS]';' ; ";
|
||||||
|
|
||||||
|
private static final String CONTENT_RULES =
|
||||||
|
"'>' > '>' ;";
|
||||||
|
|
||||||
|
private static final String HTML_RULES = BASE_RULES + CONTENT_RULES +
|
||||||
|
"'\"' > '"' ; ";
|
||||||
|
|
||||||
|
private static final String HTML_RULES_CONTROLS = HTML_RULES +
|
||||||
|
":: [[:C:][:Z:][:whitespace:][:Default_Ignorable_Code_Point:]] hex/unicode ; ";
|
||||||
|
|
||||||
|
private static final String HTML_RULES_ASCII = HTML_RULES +
|
||||||
|
":: [[:C:][:^ASCII:]] any-hex/xml ; ";
|
||||||
|
|
||||||
|
private static final String XML_RULES = HTML_RULES +
|
||||||
|
"'' > ''' ; "
|
||||||
|
;
|
||||||
|
|
||||||
|
/*
|
||||||
|
The ampersand character (&) and the left angle bracket (<) MUST NOT appear
|
||||||
|
|
||||||
|
in their literal form, except when used as markup delimiters, or within a
|
||||||
|
|
||||||
|
comment, a processing instruction, or a CDATA section. If they are needed
|
||||||
|
|
||||||
|
elsewhere, they MUST be escaped using either numeric character references or
|
||||||
|
|
||||||
|
the strings "&" and "<" respectively. The right angle bracket (>) MAY
|
||||||
|
|
||||||
|
be represented using the string ">", and MUST, for compatibility, be
|
||||||
|
|
||||||
|
escaped using either ">" or a character reference when it appears in the string
|
||||||
|
|
||||||
|
"]]>" in content, when that string is not marking the end of a CDATA section.
|
||||||
|
|
||||||
|
In the content of elements, character data is any string of characters which does
|
||||||
|
|
||||||
|
not contain the start-delimiter of any markup and does not include the
|
||||||
|
|
||||||
|
CDATA-section-close delimiter, "]]>". In a CDATA section, character data is
|
||||||
|
|
||||||
|
any string of characters not including the CDATA-section-close delimiter,
|
||||||
|
|
||||||
|
"]]>".
|
||||||
|
|
||||||
|
To allow attribute values to contain both single and double quotes, the
|
||||||
|
|
||||||
|
apostrophe or single-quote character (') MAY be represented as "'", and
|
||||||
|
|
||||||
|
the double-quote character (") as """.
|
||||||
|
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
public static final Transliterator toXML = Transliterator.createFromRules(
|
||||||
|
"any-xml", XML_RULES, Transliterator.FORWARD);
|
||||||
|
public static final Transliterator fromXML = Transliterator.createFromRules(
|
||||||
|
"xml-any", XML_RULES, Transliterator.REVERSE);
|
||||||
|
public static final Transliterator toHTML = Transliterator.createFromRules(
|
||||||
|
"any-html", HTML_RULES, Transliterator.FORWARD);
|
||||||
|
public static final Transliterator toHTMLControl = Transliterator.createFromRules(
|
||||||
|
"any-html", HTML_RULES_CONTROLS, Transliterator.FORWARD);
|
||||||
|
public static final Transliterator toHTMLAscii = Transliterator.createFromRules(
|
||||||
|
"any-html", HTML_RULES_ASCII, Transliterator.FORWARD);
|
||||||
|
public static final Transliterator fromHTML = Transliterator.createFromRules(
|
||||||
|
"html-any", HTML_RULES, Transliterator.REVERSE);
|
||||||
}
|
}
|
@ -20,6 +20,8 @@ import java.util.TreeMap;
|
|||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import com.ibm.icu.impl.Utility;
|
import com.ibm.icu.impl.Utility;
|
||||||
|
import com.ibm.icu.impl.CollectionUtilities.InverseMatcher;
|
||||||
|
import com.ibm.icu.impl.CollectionUtilities.ObjectMatcher;
|
||||||
import com.ibm.icu.text.SymbolTable;
|
import com.ibm.icu.text.SymbolTable;
|
||||||
import com.ibm.icu.text.UTF16;
|
import com.ibm.icu.text.UTF16;
|
||||||
import com.ibm.icu.text.UnicodeMatcher;
|
import com.ibm.icu.text.UnicodeMatcher;
|
||||||
@ -215,7 +217,7 @@ Name: Unicode_1_Name
|
|||||||
public final UnicodeSet getSet(String propertyValue) {
|
public final UnicodeSet getSet(String propertyValue) {
|
||||||
return getSet(propertyValue,null);
|
return getSet(propertyValue,null);
|
||||||
}
|
}
|
||||||
public final UnicodeSet getSet(Matcher matcher) {
|
public final UnicodeSet getSet(PatternMatcher matcher) {
|
||||||
return getSet(matcher,null);
|
return getSet(matcher,null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -229,7 +231,7 @@ Name: Unicode_1_Name
|
|||||||
|
|
||||||
public static final String UNUSED = "??";
|
public static final String UNUSED = "??";
|
||||||
|
|
||||||
public final UnicodeSet getSet(Matcher matcher, UnicodeSet result) {
|
public final UnicodeSet getSet(PatternMatcher matcher, UnicodeSet result) {
|
||||||
if (result == null) result = new UnicodeSet();
|
if (result == null) result = new UnicodeSet();
|
||||||
if (isType(STRING_OR_MISC_MASK)) {
|
if (isType(STRING_OR_MISC_MASK)) {
|
||||||
for (int i = 0; i <= 0x10FFFF; ++i) {
|
for (int i = 0; i <= 0x10FFFF; ++i) {
|
||||||
@ -605,12 +607,12 @@ Name: Unicode_1_Name
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
InverseMatcher inverseMatcher = new InverseMatcher();
|
InversePatternMatcher inverseMatcher = new InversePatternMatcher();
|
||||||
/**
|
/**
|
||||||
* Format is:
|
* Format is:
|
||||||
* propname ('=' | '!=') propvalue ( '|' propValue )*
|
* propname ('=' | '!=') propvalue ( '|' propValue )*
|
||||||
*/
|
*/
|
||||||
public final UnicodeSet getSet(String propAndValue, Matcher matcher, UnicodeSet result) {
|
public final UnicodeSet getSet(String propAndValue, PatternMatcher matcher, UnicodeSet result) {
|
||||||
int equalPos = propAndValue.indexOf('=');
|
int equalPos = propAndValue.indexOf('=');
|
||||||
String prop = propAndValue.substring(0,equalPos);
|
String prop = propAndValue.substring(0,equalPos);
|
||||||
String value = propAndValue.substring(equalPos+1);
|
String value = propAndValue.substring(equalPos+1);
|
||||||
@ -632,7 +634,7 @@ Name: Unicode_1_Name
|
|||||||
return up.getSet(matcher.set(value), result);
|
return up.getSet(matcher.set(value), result);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final UnicodeSet getSet(String propAndValue, Matcher matcher) {
|
public final UnicodeSet getSet(String propAndValue, PatternMatcher matcher) {
|
||||||
return getSet(propAndValue, matcher, null);
|
return getSet(propAndValue, matcher, null);
|
||||||
}
|
}
|
||||||
public final UnicodeSet getSet(String propAndValue) {
|
public final UnicodeSet getSet(String propAndValue) {
|
||||||
@ -855,57 +857,51 @@ Name: Unicode_1_Name
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface Matcher {
|
public interface PatternMatcher extends ObjectMatcher {
|
||||||
/**
|
public PatternMatcher set(String pattern);
|
||||||
* Must be able to handle null
|
|
||||||
* @param value
|
|
||||||
* @return true if the value matches
|
|
||||||
*/
|
|
||||||
public boolean matches(String value);
|
|
||||||
public Matcher set(String pattern);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class InverseMatcher implements Matcher {
|
public static class InversePatternMatcher extends InverseMatcher implements PatternMatcher {
|
||||||
Matcher other;
|
PatternMatcher other;
|
||||||
public Matcher set(Matcher toInverse) {
|
public PatternMatcher set(PatternMatcher toInverse) {
|
||||||
other = toInverse;
|
other = toInverse;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
public boolean matches(String value) {
|
public boolean matches(Object value) {
|
||||||
return !other.matches(value);
|
return !other.matches(value);
|
||||||
}
|
}
|
||||||
public Matcher set(String pattern) {
|
public PatternMatcher set(String pattern) {
|
||||||
other.set(pattern);
|
other.set(pattern);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class SimpleMatcher implements Matcher {
|
public static class SimpleMatcher implements PatternMatcher {
|
||||||
Comparator comparator;
|
Comparator comparator;
|
||||||
String pattern;
|
String pattern;
|
||||||
public SimpleMatcher(String pattern, Comparator comparator) {
|
public SimpleMatcher(String pattern, Comparator comparator) {
|
||||||
this.comparator = comparator;
|
this.comparator = comparator;
|
||||||
this.pattern = pattern;
|
this.pattern = pattern;
|
||||||
}
|
}
|
||||||
public boolean matches(String value) {
|
public boolean matches(Object value) {
|
||||||
if (comparator == null) return pattern.equals(value);
|
if (comparator == null) return pattern.equals(value);
|
||||||
return comparator.compare(pattern, value) == 0;
|
return comparator.compare(pattern, value) == 0;
|
||||||
}
|
}
|
||||||
public Matcher set(String pattern) {
|
public PatternMatcher set(String pattern) {
|
||||||
this.pattern = pattern;
|
this.pattern = pattern;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class RegexMatcher implements UnicodeProperty.Matcher {
|
public static class RegexMatcher implements UnicodeProperty.PatternMatcher {
|
||||||
private java.util.regex.Matcher matcher;
|
private java.util.regex.Matcher matcher;
|
||||||
|
|
||||||
public UnicodeProperty.Matcher set(String pattern) {
|
public UnicodeProperty.PatternMatcher set(String pattern) {
|
||||||
matcher = Pattern.compile(pattern).matcher("");
|
matcher = Pattern.compile(pattern).matcher("");
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
public boolean matches(String value) {
|
public boolean matches(Object value) {
|
||||||
matcher.reset(value);
|
matcher.reset(value.toString());
|
||||||
return matcher.matches();
|
return matcher.matches();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,9 +13,11 @@ import java.util.HashMap;
|
|||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.SortedSet;
|
import java.util.SortedSet;
|
||||||
|
|
||||||
//#ifndef FOUNDATION
|
//#ifndef FOUNDATION
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
//#endif
|
//#endif
|
||||||
|
|
||||||
import com.ibm.icu.text.Transliterator;
|
import com.ibm.icu.text.Transliterator;
|
||||||
import com.ibm.icu.text.UTF16;
|
import com.ibm.icu.text.UTF16;
|
||||||
import com.ibm.icu.text.UnicodeSet;
|
import com.ibm.icu.text.UnicodeSet;
|
||||||
@ -25,6 +27,27 @@ import com.ibm.icu.text.UnicodeSetIterator;
|
|||||||
* Utilities that ought to be on collections, but aren't
|
* Utilities that ought to be on collections, but aren't
|
||||||
*/
|
*/
|
||||||
public final class CollectionUtilities {
|
public final class CollectionUtilities {
|
||||||
|
|
||||||
|
public static String join(Object[] array, String separator) {
|
||||||
|
StringBuffer result = new StringBuffer();
|
||||||
|
for (int i = 0; i < array.length; ++i) {
|
||||||
|
if (i != 0) result.append(separator);
|
||||||
|
result.append(array[i]);
|
||||||
|
}
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String join(Collection collection, String separator) {
|
||||||
|
StringBuffer result = new StringBuffer();
|
||||||
|
boolean first = true;
|
||||||
|
for (Iterator it = collection.iterator(); it.hasNext();) {
|
||||||
|
if (first) first = false;
|
||||||
|
else result.append(separator);
|
||||||
|
result.append(it.next());
|
||||||
|
}
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility like Arrays.asList()
|
* Utility like Arrays.asList()
|
||||||
*/
|
*/
|
||||||
@ -90,11 +113,25 @@ public final class CollectionUtilities {
|
|||||||
return bestSoFar;
|
return bestSoFar;
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface Filter {
|
public interface ObjectMatcher {
|
||||||
|
/**
|
||||||
|
* Must handle null, never throw exception
|
||||||
|
*/
|
||||||
boolean matches(Object o);
|
boolean matches(Object o);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class InverseMatcher implements ObjectMatcher {
|
||||||
|
ObjectMatcher other;
|
||||||
|
public ObjectMatcher set(ObjectMatcher toInverse) {
|
||||||
|
other = toInverse;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public boolean matches(Object value) {
|
||||||
|
return !other.matches(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static Collection removeAll(Collection c, Filter f) {
|
public static Collection removeAll(Collection c, ObjectMatcher f) {
|
||||||
for (Iterator it = c.iterator(); it.hasNext();) {
|
for (Iterator it = c.iterator(); it.hasNext();) {
|
||||||
Object item = it.next();
|
Object item = it.next();
|
||||||
if (f.matches(item)) it.remove();
|
if (f.matches(item)) it.remove();
|
||||||
@ -102,7 +139,7 @@ public final class CollectionUtilities {
|
|||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Collection retainAll(Collection c, Filter f) {
|
public static Collection retainAll(Collection c, ObjectMatcher f) {
|
||||||
for (Iterator it = c.iterator(); it.hasNext();) {
|
for (Iterator it = c.iterator(); it.hasNext();) {
|
||||||
Object item = it.next();
|
Object item = it.next();
|
||||||
if (!f.matches(item)) it.remove();
|
if (!f.matches(item)) it.remove();
|
||||||
|
@ -112,6 +112,7 @@ public class PrettyPrinter {
|
|||||||
* @return formatted UnicodeSet
|
* @return formatted UnicodeSet
|
||||||
*/
|
*/
|
||||||
public String toPattern(UnicodeSet uset) {
|
public String toPattern(UnicodeSet uset) {
|
||||||
|
first = true;
|
||||||
// make sure that comparison separates all strings, even canonically equivalent ones
|
// make sure that comparison separates all strings, even canonically equivalent ones
|
||||||
Set orderedStrings = new TreeSet(ordering);
|
Set orderedStrings = new TreeSet(ordering);
|
||||||
for (UnicodeSetIterator it = new UnicodeSetIterator(uset); it.next();) {
|
for (UnicodeSetIterator it = new UnicodeSetIterator(uset); it.next();) {
|
||||||
|
@ -3447,7 +3447,7 @@ public class UnicodeSet extends UnicodeFilter {
|
|||||||
* @deprecated
|
* @deprecated
|
||||||
* @author medavis
|
* @author medavis
|
||||||
*/
|
*/
|
||||||
abstract static class XSymbolTable implements SymbolTable {
|
abstract public static class XSymbolTable implements SymbolTable {
|
||||||
public UnicodeMatcher lookupMatcher(int i) {
|
public UnicodeMatcher lookupMatcher(int i) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -7,15 +7,304 @@
|
|||||||
package com.ibm.icu.util;
|
package com.ibm.icu.util;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides a flexible mechanism for controlling access, without requiring that a class be immutable.
|
* <pre>
|
||||||
* Once locked, an object can never be unlocked, so it is thread-safe from that point onward.
|
* DRAFT
|
||||||
* The implementation of both methods must be synchronized.
|
* Copyright (C) 2005, International Business Machines Corporation and
|
||||||
* Once the object has been locked, it must guarantee that no changes can be made to it.
|
* others. All Rights Reserved.
|
||||||
* Any attempt to alter it must raise an UnsupportedOperationException exception.
|
* </pre>
|
||||||
* This means that when the object returns internal objects,
|
*
|
||||||
* or if anyone has references to those internal objects, that those internal objects must either be immutable,
|
* Provides a flexible mechanism for controlling access, without requiring that
|
||||||
* or must also raise exceptions if any attempt to modify them is made. Of course, the object can return clones
|
* a class be immutable. Once locked, an object can never be unlocked, so it is
|
||||||
* of internal objects, since those are safe. * @author davis
|
* thread-safe from that point onward. The implementation of both methods must
|
||||||
|
* be synchronized. Once the object has been locked, it must guarantee that no
|
||||||
|
* changes can be made to it. Any attempt to alter it must raise an
|
||||||
|
* UnsupportedOperationException exception. This means that when the object
|
||||||
|
* returns internal objects, or if anyone has references to those internal
|
||||||
|
* objects, that those internal objects must either be immutable, or must also
|
||||||
|
* raise exceptions if any attempt to modify them is made. Of course, the object
|
||||||
|
* can return clones of internal objects, since those are safe.
|
||||||
|
* <h2>Background</h2>
|
||||||
|
* <p>
|
||||||
|
* There are often times when you need objects to be objects 'safe', so that
|
||||||
|
* they can't be modified. Examples are when objects need to be thread-safe, or
|
||||||
|
* in writing robust code, or in caches. If you are only creating your own
|
||||||
|
* objects, you can guarantee this, of course -- but only if you don't make a
|
||||||
|
* mistake. If you have objects handed into you, or are creating objects using
|
||||||
|
* others handed into you, it is a different story. It all comes down to whether
|
||||||
|
* you want to take the Blanche Dubois approach ("depend on the kindness of
|
||||||
|
* strangers") or the Andy Grove approach ("Only the Paranoid
|
||||||
|
* Survive").
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* For example, suppose we have a simple class:
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* public class A {
|
||||||
|
* protected Collection b;
|
||||||
|
*
|
||||||
|
* protected Collection c;
|
||||||
|
*
|
||||||
|
* public Collection get_b() {
|
||||||
|
* return b;
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* public Collection get_c() {
|
||||||
|
* return c;
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* public A(Collection new_b, Collection new_c) {
|
||||||
|
* b = new_b;
|
||||||
|
* c = new_c;
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Since the class doesn't have any setters, someone might think that it is
|
||||||
|
* immutable. You know where this is leading, of course; this class is unsafe in
|
||||||
|
* a number of ways. The following illustrates that.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* public test1(SupposedlyImmutableClass x, SafeStorage y) {
|
||||||
|
* <font color="#0000FF"> <b>// unsafe getter</b>
|
||||||
|
* </font> A a = x.getA();
|
||||||
|
* Collection col = a.get_b();
|
||||||
|
* col.add(something);<font color="#0000FF"> // a has now been changed, and x too
|
||||||
|
* </font>
|
||||||
|
* <font color="#0000FF"><b>// unsafe constructor</b></font>
|
||||||
|
* a = new A(col, col);
|
||||||
|
* y.store(a);
|
||||||
|
* col.add(something);<font color="#0000FF"> // a has now been changed, and y too
|
||||||
|
*
|
||||||
|
* </font>}
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* There are a few different techniques for having safe classes.
|
||||||
|
* </p>
|
||||||
|
* <ol>
|
||||||
|
* <li>Const objects. In C++, you can declare parameters const.</li>
|
||||||
|
* <li>Immutable wrappers. For example, you can put a collection in an
|
||||||
|
* immutable wrapper.</li>
|
||||||
|
* <li>Always-Immutable objects. Java uses this approach, with a few
|
||||||
|
* variations. Examples:
|
||||||
|
* <ol>
|
||||||
|
* <li>Simple. Once a Color is created (eg from R, G, and B integers) it is
|
||||||
|
* immutable.</li>
|
||||||
|
* <li>Builder Class. There is a separate 'builder' class. For example,
|
||||||
|
* modifiable Strings are created using StringBuffer (which doesn't have the
|
||||||
|
* full String API available). Once you want an immutable form, you create one
|
||||||
|
* with toString().</li>
|
||||||
|
* <li>Primitives. These are always safe, since they are copied on input/output
|
||||||
|
* from methods.</li>
|
||||||
|
* </ol>
|
||||||
|
* </li>
|
||||||
|
* <li>Cloning. Where you need an object to be safe, you clone it.</li>
|
||||||
|
* </ol>
|
||||||
|
* <p>
|
||||||
|
* There are advantages and disadvantages of each of these.
|
||||||
|
* </p>
|
||||||
|
* <ol>
|
||||||
|
* <li>Const provides a certain level of protection, but since const can be and
|
||||||
|
* is often cast away, it only protects against most inadvertent mistakes. It
|
||||||
|
* also offers no threading protection, since anyone who has a pointer to the
|
||||||
|
* (unconst) object in another thread can mess you up.</li>
|
||||||
|
* <li>Immutable wrappers are safer than const in that the constness can't be
|
||||||
|
* cast away. But other than that they have all the same problems: not safe if
|
||||||
|
* someone else keeps hold of the original object, or if any of the objects
|
||||||
|
* returned by the class are mutable.</li>
|
||||||
|
* <li>Always-Immutable Objects are safe, but usage can require excessive
|
||||||
|
* object creation.</li>
|
||||||
|
* <li>Cloning is only safe if the object truly has a 'safe' clone; defined as
|
||||||
|
* one that <i>ensures that no change to the clone affects the original</i>.
|
||||||
|
* Unfortunately, many objects don't have a 'safe' clone, and always cloning can
|
||||||
|
* require excessive object creation.</li>
|
||||||
|
* </ol>
|
||||||
|
* <h2>Freezable Model</h2>
|
||||||
|
* <p>
|
||||||
|
* The <code>Freezable</code> model supplements these choices by giving you
|
||||||
|
* the ability to build up an object by calling various methods, then when it is
|
||||||
|
* in a final state, you can <i>make</i> it immutable. Once immutable, an
|
||||||
|
* object cannot <i>ever </i>be modified, and is completely thread-safe: that
|
||||||
|
* is, multiple threads can have references to it without any synchronization.
|
||||||
|
* If someone needs a mutable version of an object, they can use
|
||||||
|
* <code>cloneAsThawed()</code>, and modify the copy. This provides a simple,
|
||||||
|
* effective mechanism for safe classes in circumstances where the alternatives
|
||||||
|
* are insufficient or clumsy. (If an object is shared before it is immutable,
|
||||||
|
* then it is the responsibility of each thread to mutex its usage (as with
|
||||||
|
* other objects).)
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* Here is what needs to be done to implement this interface, depending on the
|
||||||
|
* type of the object.
|
||||||
|
* </p>
|
||||||
|
* <h3><b>Immutable Objects</b></h3>
|
||||||
|
* <p>
|
||||||
|
* These are the easiest. You just use the interface to reflect that, by adding
|
||||||
|
* the following:
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* public class A implements Freezable {
|
||||||
|
* ...
|
||||||
|
* public final boolean isFrozen() {return true;}
|
||||||
|
* public final Object freeze() {return this;}
|
||||||
|
* public final Object cloneAsThawed() { return this; }
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* These can be final methods because subclasses of immutable objects must
|
||||||
|
* themselves be immutable. (Note: <code>freeze</code> is returning
|
||||||
|
* <code>this</code> for chaining.)
|
||||||
|
* </p>
|
||||||
|
* <h3><b>Mutable Objects</b></h3>
|
||||||
|
* <p>
|
||||||
|
* Add a protected 'flagging' field:
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* protected boolean immutable;
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Add the following methods:
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* public final boolean isFrozen() {
|
||||||
|
* return frozen;
|
||||||
|
* };
|
||||||
|
*
|
||||||
|
* public Object freeze() {
|
||||||
|
* frozen = true;
|
||||||
|
* return this;
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Add a <code>cloneAsThawed()</code> method following the normal pattern for
|
||||||
|
* <code>clone()</code>, except that <code>frozen=false</code> in the new
|
||||||
|
* clone.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* Then take the setters (that is, any method that can change the internal state
|
||||||
|
* of the object), and add the following as the first statement:
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* if (isFrozen()) {
|
||||||
|
* throw new UnsupportedOperationException("Attempt to modify frozen object");
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <h4><b>Subclassing</b></h4>
|
||||||
|
* <p>
|
||||||
|
* Any subclass of a <code>Freezable</code> will just use its superclass's
|
||||||
|
* flagging field. It must override <code>freeze()</code> and
|
||||||
|
* <code>cloneAsThawed()</code> to call the superclass, but normally does not
|
||||||
|
* override <code>isFrozen()</code>. It must then just pay attention to its
|
||||||
|
* own getters, setters and fields.
|
||||||
|
* </p>
|
||||||
|
* <h4><b>Internal Caches</b></h4>
|
||||||
|
* <p>
|
||||||
|
* Internal caches are cases where the object is logically unmodified, but
|
||||||
|
* internal state of the object changes. For example, there are const C++
|
||||||
|
* functions that cast away the const on the "this" pointer in order
|
||||||
|
* to modify an object cache. These cases are handled by mutexing the internal
|
||||||
|
* cache to ensure thread-safety. For example, suppose that UnicodeSet had an
|
||||||
|
* internal marker to the last code point accessed. In this case, the field is
|
||||||
|
* not externally visible, so the only thing you need to do is to synchronize
|
||||||
|
* the field for thread safety.
|
||||||
|
* </p>
|
||||||
|
* <h4>Unsafe Internal Access</h4>
|
||||||
|
* <p>
|
||||||
|
* Internal fields are called <i>safe</i> if they are either
|
||||||
|
* <code>frozen</code> or immutable (such as String or primitives). If you've
|
||||||
|
* never allowed internal access to these, then you are all done. For example,
|
||||||
|
* converting UnicodeSet to be <code>Freezable</code> is just accomplished
|
||||||
|
* with the above steps. But remember that you <i><b>have</b></i> allowed
|
||||||
|
* access to unsafe internals if you have any code like the following, in a
|
||||||
|
* getter, setter, or constructor:
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* Collection getStuff() {
|
||||||
|
* return stuff;
|
||||||
|
* } // caller could keep reference & modify
|
||||||
|
*
|
||||||
|
* void setStuff(Collection x) {
|
||||||
|
* stuff = x;
|
||||||
|
* } // caller could keep reference & modify
|
||||||
|
*
|
||||||
|
* MyClass(Collection x) {
|
||||||
|
* stuff = x;
|
||||||
|
* } // caller could keep reference & modify
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* These also illustrated in the code sample in <b>Background</b> above.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* To deal with unsafe internals, the simplest course of action is to do the
|
||||||
|
* work in the <code>
|
||||||
|
freeze()</code> function. Just make all of your internal
|
||||||
|
* fields frozen, and set the frozen flag. Any subsequent getter/setter will
|
||||||
|
* work properly. Here is an example:
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* public Object freeze() {
|
||||||
|
* if (!frozen) {
|
||||||
|
* foo.freeze();
|
||||||
|
* frozen = true;
|
||||||
|
* }
|
||||||
|
* return this;
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* If the field is a <code>Collection</code> or <code>Map</code>, then to
|
||||||
|
* make it frozen you have two choices. If you have never allowed access to the
|
||||||
|
* collection from outside your object, then just wrap it to prevent future
|
||||||
|
* modification.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* zone_to_country = Collections.unmodifiableMap(zone_to_country);
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* If you have <i>ever</i> allowed access, then do a <code>clone()</code>
|
||||||
|
* before wrapping it.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* zone_to_country = Collections.unmodifiableMap(zone_to_country.clone());
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* If a collection <i>(or any other container of objects)</i> itself can
|
||||||
|
* contain mutable objects, then for a safe clone you need to recurse through it
|
||||||
|
* to make the entire collection immutable. The recursing code should pick the
|
||||||
|
* most specific collection available, to avoid the necessity of later
|
||||||
|
* downcasing.
|
||||||
|
* </p>
|
||||||
|
* <blockquote>
|
||||||
|
* <p>
|
||||||
|
* <b>Note: </b>An annoying flaw in Java is that the generic collections, like
|
||||||
|
* <code>Map</code> or <code>Set</code>, don't have a <code>clone()</code>
|
||||||
|
* operation. When you don't know the type of the collection, the simplest
|
||||||
|
* course is to just create a new collection:
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* zone_to_country = Collections.unmodifiableMap(new HashMap(zone_to_country));
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* </blockquote>
|
||||||
*/
|
*/
|
||||||
public interface Freezable extends Cloneable {
|
public interface Freezable extends Cloneable {
|
||||||
/**
|
/**
|
||||||
|
@ -23,11 +23,23 @@ import java.util.regex.Pattern;
|
|||||||
|
|
||||||
import com.ibm.icu.impl.Utility;
|
import com.ibm.icu.impl.Utility;
|
||||||
import com.ibm.icu.impl.ZoneMeta;
|
import com.ibm.icu.impl.ZoneMeta;
|
||||||
|
import com.ibm.icu.text.Collator;
|
||||||
import com.ibm.icu.text.DateFormat;
|
import com.ibm.icu.text.DateFormat;
|
||||||
|
import com.ibm.icu.text.DecimalFormat;
|
||||||
|
import com.ibm.icu.text.DecimalFormatSymbols;
|
||||||
import com.ibm.icu.text.NumberFormat;
|
import com.ibm.icu.text.NumberFormat;
|
||||||
import com.ibm.icu.text.SimpleDateFormat;
|
import com.ibm.icu.text.SimpleDateFormat;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Copyright (C) 2004-2005, International Business Machines Corporation and *
|
||||||
|
* others. All Rights Reserved.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* <pre>
|
||||||
|
* DRAFT
|
||||||
|
* Copyright (C) 2005, International Business Machines Corporation and
|
||||||
|
* others. All Rights Reserved.
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
* This convenience class provides a mechanism for bundling together different
|
* This convenience class provides a mechanism for bundling together different
|
||||||
* globalization preferences. It includes:
|
* globalization preferences. It includes:
|
||||||
* <ul>
|
* <ul>
|
||||||
@ -37,6 +49,57 @@ import com.ibm.icu.text.SimpleDateFormat;
|
|||||||
* <li>A timezone</li>
|
* <li>A timezone</li>
|
||||||
* <li>A calendar</li>
|
* <li>A calendar</li>
|
||||||
* <li>A collator (for language-sensitive sorting, searching, and matching).</li>
|
* <li>A collator (for language-sensitive sorting, searching, and matching).</li>
|
||||||
|
<<<<<<< GlobalizationPreferences.java
|
||||||
|
* <li>And explicit overrides for date/time formats, etc.</li>
|
||||||
|
* </ul>
|
||||||
|
* The class will heuristically compute implicit, heuristic values for the above
|
||||||
|
* based on available data if explicit values are not supplied. These implicit
|
||||||
|
* values can be presented to users for confirmation, or replacement if the
|
||||||
|
* values are incorrect.
|
||||||
|
* <p>
|
||||||
|
* To reset any explicit field so that it will get heuristic values, pass in
|
||||||
|
* null. For example, myPreferences.setLocale(null);
|
||||||
|
* <p>
|
||||||
|
* All of the heuristics can be customized by subclasses, by overriding
|
||||||
|
* getTerritory(), guessCollator(), etc.
|
||||||
|
* <p>
|
||||||
|
* The class also supplies display names for languages, scripts, territories,
|
||||||
|
* currencies, timezones, etc. These are computed according to the
|
||||||
|
* locale/language preference list. Thus, if the preference is Breton; French;
|
||||||
|
* English, then the display name for a language will be returned in Breton if
|
||||||
|
* available, otherwise in French if available, otherwise in English.
|
||||||
|
* <p>
|
||||||
|
* The codes used to reference territory, currency, etc. are as defined elsewhere in ICU,
|
||||||
|
* and are taken from CLDR (which reflects RFC 3066bis usage, ISO 4217, and the
|
||||||
|
* TZ Timezone database identifiers).
|
||||||
|
* <p>
|
||||||
|
* <b>This is at a prototype stage, and has not incorporated all the design
|
||||||
|
* changes that we would like yet; further feedback is welcome.</b></p>
|
||||||
|
* <p>
|
||||||
|
* TODO:<ul>
|
||||||
|
* <li>Separate out base class</li>
|
||||||
|
* <li>Add BreakIterator</li>
|
||||||
|
* <li>Add Holidays</li>
|
||||||
|
* <li>Add convenience to get/take Locale as well as ULocale.</li>
|
||||||
|
* <li>Add getResourceBundle(String baseName, ClassLoader loader);</li>
|
||||||
|
* <li>Add getFallbackLocales();</li>
|
||||||
|
* <li>Add Lenient datetime formatting when that is available.</li>
|
||||||
|
* <li>Should this be serializable?</li>
|
||||||
|
* <li>Other utilities?</li>
|
||||||
|
* </ul>
|
||||||
|
* Note:
|
||||||
|
* <ul>
|
||||||
|
* <li>to get the display name for the first day of the week, use the calendar +
|
||||||
|
* display names.</li>
|
||||||
|
* <li>to get the work days, ask the calendar (when that is available).</li>
|
||||||
|
* <li>to get papersize / measurement system/bidi-orientation, ask the locale
|
||||||
|
* (when that is available there)</li>
|
||||||
|
* <li>to get the field order in a date, and whether a time is 24hour or not,
|
||||||
|
* ask the DateFormat (when that is available there)</li>
|
||||||
|
* <li>it will support HOST locale when it becomes available (it is a special
|
||||||
|
* locale that will ask the services to use the host platform's values).</li>
|
||||||
|
* </ul>
|
||||||
|
=======
|
||||||
* <li>And explicit overrides for date/time formats, etc.</li></ul>
|
* <li>And explicit overrides for date/time formats, etc.</li></ul>
|
||||||
* The class will heuristically compute implicit, heuristic values for the above based on available
|
* The class will heuristically compute implicit, heuristic values for the above based on available
|
||||||
* data if explicit values are not supplied. These implicit values can be presented to users
|
* data if explicit values are not supplied. These implicit values can be presented to users
|
||||||
@ -50,8 +113,9 @@ import com.ibm.icu.text.SimpleDateFormat;
|
|||||||
*
|
*
|
||||||
* @internal
|
* @internal
|
||||||
* @deprecated ICU 3.4.2
|
* @deprecated ICU 3.4.2
|
||||||
|
>>>>>>> 1.8
|
||||||
*/
|
*/
|
||||||
public class GlobalizationPreferences {
|
public class GlobalizationPreferences implements Freezable {
|
||||||
/**
|
/**
|
||||||
* Number Format types
|
* Number Format types
|
||||||
*/
|
*/
|
||||||
@ -75,57 +139,85 @@ public class GlobalizationPreferences {
|
|||||||
* @param locales list of locales in priority order, eg {"be", "fr"} for Breton first, then French if that fails.
|
* @param locales list of locales in priority order, eg {"be", "fr"} for Breton first, then French if that fails.
|
||||||
* @return this, for chaining
|
* @return this, for chaining
|
||||||
*/
|
*/
|
||||||
public GlobalizationPreferences setULocales(List locales) {
|
public GlobalizationPreferences setLocales(List locales) {
|
||||||
this.locales = new ArrayList(locales);
|
if (isFrozen()) {
|
||||||
explicitLocales = true;
|
throw new UnsupportedOperationException("Attempt to modify immutable object");
|
||||||
if (!explicitTerritory) guessTerritory();
|
}
|
||||||
if (!explicitCurrency) guessCurrency();
|
if (locales.size() == 0) {
|
||||||
if (!explicitTimezone) guessTimeZone();
|
this.locales = locales.get(0);
|
||||||
if (!explicitCalendar) guessCalendar();
|
} else {
|
||||||
|
this.locales = new ArrayList(locales); // clone for safety
|
||||||
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
|
* Get a copy of the language/locale priority list
|
||||||
* @return a copy of the language/locale priority list.
|
* @return a copy of the language/locale priority list.
|
||||||
*/
|
*/
|
||||||
public List getULocales() {
|
public List getLocales() {
|
||||||
return new ArrayList(locales); // clone for safety
|
List result = new ArrayList(); // clone for safety
|
||||||
|
if (locales == null) {
|
||||||
|
result = guessLocales();
|
||||||
|
} else if (locales instanceof ULocale) {
|
||||||
|
result.add(locales);
|
||||||
|
} else {
|
||||||
|
result.addAll((List)locales);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convenience function for getting the locales in priority order
|
* Convenience function for getting the locales in priority order
|
||||||
* @return first item.
|
* @param index The index (0..n) of the desired item.
|
||||||
|
* @return desired item.
|
||||||
*/
|
*/
|
||||||
public ULocale getULocale(int i) {
|
public ULocale getLocale(int index) {
|
||||||
return (ULocale)locales.get(i);
|
if (locales == null) {
|
||||||
|
return (ULocale)guessLocales().get(index);
|
||||||
|
} else if (locales instanceof ULocale) {
|
||||||
|
if (index != 0) throw new IllegalArgumentException("Out of bounds: " + index);
|
||||||
|
return (ULocale)locales;
|
||||||
|
} else {
|
||||||
|
return (ULocale)((List)locales).get(index);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convenience routine for setting the language/locale priority list from an array.
|
* Convenience routine for setting the language/locale priority list from an array.
|
||||||
* @see #setULocales(List locales)
|
* @see #setLocales(List locales)
|
||||||
* @param uLocales list of locales in an array
|
* @param uLocales list of locales in an array
|
||||||
* @return this, for chaining
|
* @return this, for chaining
|
||||||
*/
|
*/
|
||||||
public GlobalizationPreferences setULocales(ULocale[] uLocales) {
|
public GlobalizationPreferences setLocales(ULocale[] uLocales) {
|
||||||
return setULocales(Arrays.asList(uLocales));
|
if (isFrozen()) {
|
||||||
|
throw new UnsupportedOperationException("Attempt to modify immutable object");
|
||||||
|
}
|
||||||
|
return setLocales(Arrays.asList(uLocales));
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Convenience routine for setting the language/locale priority list from a single locale/language.
|
* Convenience routine for setting the language/locale priority list from a single locale/language.
|
||||||
* @see #setULocales(List locales)
|
* @see #setLocales(List locales)
|
||||||
* @param uLocale single locale
|
* @param uLocale single locale
|
||||||
* @return this, for chaining
|
* @return this, for chaining
|
||||||
*/
|
*/
|
||||||
public GlobalizationPreferences setULocales(ULocale uLocale) {
|
public GlobalizationPreferences setLocale(ULocale uLocale) {
|
||||||
return setULocales(new ULocale[]{uLocale});
|
if (isFrozen()) {
|
||||||
|
throw new UnsupportedOperationException("Attempt to modify immutable object");
|
||||||
|
}
|
||||||
|
return setLocales(new ULocale[]{uLocale});
|
||||||
}
|
}
|
||||||
|
|
||||||
//#ifndef FOUNDATION
|
//#ifndef FOUNDATION
|
||||||
/**
|
/**
|
||||||
* Convenience routine for setting the locale priority list from an Accept-Language string.
|
* Convenience routine for setting the locale priority list from an Accept-Language string.
|
||||||
* @see #setULocales(List locales)
|
* @see #setLocales(List locales)
|
||||||
* @param acceptLanguageString Accept-Language list, as defined by Section 14.4 of the RFC 2616 (HTTP 1.1)
|
* @param acceptLanguageString Accept-Language list, as defined by Section 14.4 of the RFC 2616 (HTTP 1.1)
|
||||||
* @return this, for chaining
|
* @return this, for chaining
|
||||||
*/
|
*/
|
||||||
public GlobalizationPreferences setULocales(String acceptLanguageString) {
|
public GlobalizationPreferences setLocales(String acceptLanguageString) {
|
||||||
|
if (isFrozen()) {
|
||||||
|
throw new UnsupportedOperationException("Attempt to modify immutable object");
|
||||||
|
}
|
||||||
/*
|
/*
|
||||||
Accept-Language = "Accept-Language" ":" 1#( language-range [ ";" "q" "=" qvalue ] )
|
Accept-Language = "Accept-Language" ":" 1#( language-range [ ";" "q" "=" qvalue ] )
|
||||||
x matches x-...
|
x matches x-...
|
||||||
@ -160,7 +252,7 @@ public class GlobalizationPreferences {
|
|||||||
result.add(0, new ULocale((String)it2.next()));
|
result.add(0, new ULocale((String)it2.next()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return setULocales(result);
|
return setLocales(result);
|
||||||
}
|
}
|
||||||
//#endif
|
//#endif
|
||||||
|
|
||||||
@ -172,11 +264,10 @@ public class GlobalizationPreferences {
|
|||||||
* @return this, for chaining
|
* @return this, for chaining
|
||||||
*/
|
*/
|
||||||
public GlobalizationPreferences setTerritory(String territory) {
|
public GlobalizationPreferences setTerritory(String territory) {
|
||||||
|
if (isFrozen()) {
|
||||||
|
throw new UnsupportedOperationException("Attempt to modify immutable object");
|
||||||
|
}
|
||||||
this.territory = territory;
|
this.territory = territory;
|
||||||
explicitTerritory = true;
|
|
||||||
if (!explicitCurrency) guessCurrency();
|
|
||||||
if (!explicitTimezone) guessTimeZone();
|
|
||||||
if (!explicitCalendar) guessCalendar();
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
@ -184,6 +275,7 @@ public class GlobalizationPreferences {
|
|||||||
* @return territory code, explicit or implicit.
|
* @return territory code, explicit or implicit.
|
||||||
*/
|
*/
|
||||||
public String getTerritory() {
|
public String getTerritory() {
|
||||||
|
if (territory == null) return guessTerritory();
|
||||||
return territory; // immutable, so don't need to clone
|
return territory; // immutable, so don't need to clone
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,8 +285,10 @@ public class GlobalizationPreferences {
|
|||||||
* @return this, for chaining
|
* @return this, for chaining
|
||||||
*/
|
*/
|
||||||
public GlobalizationPreferences setCurrency(Currency currency) {
|
public GlobalizationPreferences setCurrency(Currency currency) {
|
||||||
|
if (isFrozen()) {
|
||||||
|
throw new UnsupportedOperationException("Attempt to modify immutable object");
|
||||||
|
}
|
||||||
this.currency = currency;
|
this.currency = currency;
|
||||||
explicitCurrency = true;
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
@ -202,6 +296,7 @@ public class GlobalizationPreferences {
|
|||||||
* @return currency code, explicit or implicit.
|
* @return currency code, explicit or implicit.
|
||||||
*/
|
*/
|
||||||
public Currency getCurrency() {
|
public Currency getCurrency() {
|
||||||
|
if (currency == null) return guessCurrency();
|
||||||
return currency; // immutable, so don't have to clone
|
return currency; // immutable, so don't have to clone
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -211,42 +306,79 @@ public class GlobalizationPreferences {
|
|||||||
* @return this, for chaining
|
* @return this, for chaining
|
||||||
*/
|
*/
|
||||||
public GlobalizationPreferences setCalendar(Calendar calendar) {
|
public GlobalizationPreferences setCalendar(Calendar calendar) {
|
||||||
this.calendar = calendar;
|
if (isFrozen()) {
|
||||||
explicitCalendar = true;
|
throw new UnsupportedOperationException("Attempt to modify immutable object");
|
||||||
|
}
|
||||||
|
this.calendar = calendar;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Get a copy of the calendar according to the settings.
|
* Get a copy of the calendar according to the settings.
|
||||||
* @return currency code, explicit or implicit.
|
* @return calendar explicit or implicit.
|
||||||
*/
|
*/
|
||||||
public Calendar getCalendar() {
|
public Calendar getCalendar() {
|
||||||
return (Calendar) calendar.clone(); // clone for safety
|
if (calendar == null) return guessCalendar();
|
||||||
|
Calendar temp = (Calendar) calendar.clone(); // clone for safety
|
||||||
|
temp.setTimeZone(getTimeZone());
|
||||||
|
return temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the timezone ID. If this has not been set, uses default for territory.
|
* Sets the timezone ID. If this has not been set, uses default for territory.
|
||||||
* @param timezone a valid TZID (see UTS#35).
|
* @param timezone a valid TZID (see UTS#35).
|
||||||
* @return the object, for chaining.
|
* @return this, for chaining
|
||||||
*/
|
*/
|
||||||
public GlobalizationPreferences setTimezone(TimeZone timezone) {
|
public GlobalizationPreferences setTimeZone(TimeZone timezone) {
|
||||||
|
if (isFrozen()) {
|
||||||
|
throw new UnsupportedOperationException("Attempt to modify immutable object");
|
||||||
|
}
|
||||||
this.timezone = timezone;
|
this.timezone = timezone;
|
||||||
explicitTimezone = true;
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Get the timezone. It was either explicitly set, or is heuristically computed from other settings.
|
* Get the timezone. It was either explicitly set, or is heuristically computed from other settings.
|
||||||
* @return timezone, either implicitly or explicitly set
|
* @return timezone, either implicitly or explicitly set
|
||||||
*/
|
*/
|
||||||
public TimeZone getTimezone() {
|
public TimeZone getTimeZone() {
|
||||||
|
if (timezone == null) return guessTimeZone();
|
||||||
return (TimeZone) timezone.clone(); // clone for safety
|
return (TimeZone) timezone.clone(); // clone for safety
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a copy of the collator according to the settings.
|
||||||
|
* @return collator explicit or implicit.
|
||||||
|
*/
|
||||||
|
public Collator getCollator() {
|
||||||
|
if (collator == null) return guessCollator();
|
||||||
|
try {
|
||||||
|
return (Collator) collator.clone(); // clone for safety
|
||||||
|
} catch (CloneNotSupportedException e) {
|
||||||
|
throw new InternalError("Error in cloning collator");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Explicitly set the collator for this object.
|
||||||
|
* @param collator
|
||||||
|
* @return this, for chaining
|
||||||
|
*/
|
||||||
|
public GlobalizationPreferences setCollator(Collator collator) {
|
||||||
|
if (isFrozen()) {
|
||||||
|
throw new UnsupportedOperationException("Attempt to modify immutable object");
|
||||||
|
}
|
||||||
|
this.collator = collator;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the date locale.
|
* Set the date locale.
|
||||||
* @param dateLocale If not null, overrides the locale priority list for all the date formats.
|
* @param dateLocale If not null, overrides the locale priority list for all the date formats.
|
||||||
* @return the object, for chaining
|
* @return this, for chaining
|
||||||
*/
|
*/
|
||||||
public GlobalizationPreferences setDateLocale(ULocale dateLocale) {
|
public GlobalizationPreferences setDateLocale(ULocale dateLocale) {
|
||||||
|
if (isFrozen()) {
|
||||||
|
throw new UnsupportedOperationException("Attempt to modify immutable object");
|
||||||
|
}
|
||||||
this.dateLocale = dateLocale;
|
this.dateLocale = dateLocale;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@ -255,15 +387,18 @@ public class GlobalizationPreferences {
|
|||||||
* @return date locale. Null if none was set explicitly.
|
* @return date locale. Null if none was set explicitly.
|
||||||
*/
|
*/
|
||||||
public ULocale getDateLocale() {
|
public ULocale getDateLocale() {
|
||||||
return dateLocale;
|
return dateLocale != null ? dateLocale : getLocale(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the number locale.
|
* Set the number locale.
|
||||||
* @param numberLocale If not null, overrides the locale priority list for all the date formats.
|
* @param numberLocale If not null, overrides the locale priority list for all the date formats.
|
||||||
* @return the object, for chaining
|
* @return this, for chaining
|
||||||
*/
|
*/
|
||||||
public GlobalizationPreferences setNumberLocale(ULocale numberLocale) {
|
public GlobalizationPreferences setNumberLocale(ULocale numberLocale) {
|
||||||
|
if (isFrozen()) {
|
||||||
|
throw new UnsupportedOperationException("Attempt to modify immutable object");
|
||||||
|
}
|
||||||
this.numberLocale = numberLocale;
|
this.numberLocale = numberLocale;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@ -273,7 +408,7 @@ public class GlobalizationPreferences {
|
|||||||
* @return number locale. Null if none was set explicitly.
|
* @return number locale. Null if none was set explicitly.
|
||||||
*/
|
*/
|
||||||
public ULocale getNumberLocale() {
|
public ULocale getNumberLocale() {
|
||||||
return numberLocale;
|
return numberLocale != null ? numberLocale : getLocale(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -285,7 +420,7 @@ public class GlobalizationPreferences {
|
|||||||
*/
|
*/
|
||||||
public String getDisplayName(String id, int type) {
|
public String getDisplayName(String id, int type) {
|
||||||
String result = id;
|
String result = id;
|
||||||
for (Iterator it = locales.iterator(); it.hasNext();) {
|
for (Iterator it = getLocales().iterator(); it.hasNext();) {
|
||||||
ULocale locale = (ULocale) it.next();
|
ULocale locale = (ULocale) it.next();
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case LOCALEID:
|
case LOCALEID:
|
||||||
@ -328,9 +463,9 @@ public class GlobalizationPreferences {
|
|||||||
// TODO, have method that doesn't require us to create a timezone
|
// TODO, have method that doesn't require us to create a timezone
|
||||||
// fix other hacks
|
// fix other hacks
|
||||||
// hack for couldn't match
|
// hack for couldn't match
|
||||||
// note, compiling with FOUNDATION omits this check for now
|
// note, compiling with FOUNDATION omits this check for now
|
||||||
//#ifndef FOUNDATION
|
//#ifndef FOUNDATION
|
||||||
if (badTimezone.reset(result).matches()) continue;
|
if (badTimeZone.reset(result).matches()) continue;
|
||||||
//#endif
|
//#endif
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -344,51 +479,80 @@ public class GlobalizationPreferences {
|
|||||||
}
|
}
|
||||||
//#ifndef FOUNDATION
|
//#ifndef FOUNDATION
|
||||||
// TODO remove need for this
|
// TODO remove need for this
|
||||||
private static final Matcher badTimezone = Pattern.compile("[A-Z]{2}|.*\\s\\([A-Z]{2}\\)").matcher("");
|
private static final Matcher badTimeZone = Pattern.compile("[A-Z]{2}|.*\\s\\([A-Z]{2}\\)").matcher("");
|
||||||
//#endif
|
//#endif
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set an explicit date format. Overrides both the date locale, and the locale priority list
|
* Set an explicit date format. Overrides both the date locale, and the locale priority list
|
||||||
* for a particular combination of dateStyle and timeStyle. NONE should be used if for the style,
|
* for a particular combination of dateStyle and timeStyle. NONE should be used if for the style,
|
||||||
* where only the date or time format individually is being set.
|
* where only the date or time format individually is being set.
|
||||||
* @param dateStyle
|
* @param dateStyle NONE, or DateFormat.FULL, LONG, MEDIUM, SHORT
|
||||||
* @param timeStyle
|
* @param timeStyle NONE, or DateFormat.FULL, LONG, MEDIUM, SHORT
|
||||||
* @param format
|
* @param format
|
||||||
* @return this, for chaining
|
* @return this, for chaining
|
||||||
*/
|
*/
|
||||||
public GlobalizationPreferences setDateFormat(int dateStyle, int timeStyle, DateFormat format) {
|
public GlobalizationPreferences setDateFormat(int dateStyle, int timeStyle, DateFormat format) {
|
||||||
if (dateFormats == null) dateFormats = new DateFormat[NONE+1][NONE+1];
|
if (isFrozen()) {
|
||||||
|
throw new UnsupportedOperationException("Attempt to modify immutable object");
|
||||||
|
}
|
||||||
|
if (dateFormats == null) dateFormats = new Object[NONE+1][NONE+1];
|
||||||
dateFormats[dateStyle][timeStyle] = (DateFormat) format.clone(); // for safety
|
dateFormats[dateStyle][timeStyle] = (DateFormat) format.clone(); // for safety
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set an explicit date format. Overrides both the date locale, and the locale priority list
|
||||||
|
* for a particular combination of dateStyle and timeStyle. NONE should be used if for the style,
|
||||||
|
* where only the date or time format individually is being set.
|
||||||
|
* @param dateStyle NONE, or DateFormat.FULL, LONG, MEDIUM, SHORT
|
||||||
|
* @param timeStyle NONE, or DateFormat.FULL, LONG, MEDIUM, SHORT
|
||||||
|
* @param formatPattern date pattern, eg "yyyy-MMM-dd"
|
||||||
|
* @return this, for chaining
|
||||||
|
*/
|
||||||
|
public GlobalizationPreferences setDateFormat(int dateStyle, int timeStyle, String formatPattern) {
|
||||||
|
if (isFrozen()) {
|
||||||
|
throw new UnsupportedOperationException("Attempt to modify immutable object");
|
||||||
|
}
|
||||||
|
if (dateFormats == null) dateFormats = new Object[NONE+1][NONE+1];
|
||||||
|
// test the format to make sure it won't throw an error later
|
||||||
|
new SimpleDateFormat(formatPattern, getDateLocale());
|
||||||
|
dateFormats[dateStyle][timeStyle] = formatPattern; // for safety
|
||||||
|
return this;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Gets a date format according to the current settings. If there is an explicit (non-null) date/time
|
* Gets a date format according to the current settings. If there is an explicit (non-null) date/time
|
||||||
* format set, a copy of that is returned. Otherwise, if there is a non-null date locale, that is used.
|
* format set, a copy of that is returned. Otherwise, if there is a non-null date locale, that is used.
|
||||||
* Otherwise, the language priority list is used. NONE should be used for the style,
|
* Otherwise, the language priority list is used. NONE should be used for the style,
|
||||||
* where only the date or time format individually is being gotten.
|
* where only the date or time format individually is being gotten.
|
||||||
* @param dateStyle
|
* @param dateStyle NONE, or DateFormat.FULL, LONG, MEDIUM, SHORT
|
||||||
* @param timeStyle
|
* @param timeStyle NONE, or DateFormat.FULL, LONG, MEDIUM, SHORT
|
||||||
* @return a DateFormat, according to the above description
|
* @return a DateFormat, according to the above description
|
||||||
*/
|
*/
|
||||||
public DateFormat getDateFormat(int dateStyle, int timeStyle) {
|
public DateFormat getDateFormat(int dateStyle, int timeStyle) {
|
||||||
try {
|
try {
|
||||||
DateFormat result = null;
|
DateFormat result = null;
|
||||||
if (dateFormats != null) result = dateFormats[dateStyle][timeStyle];
|
if (dateFormats != null) { // and override can either be a string or a pattern
|
||||||
|
Object temp = dateFormats[dateStyle][timeStyle];
|
||||||
|
if (temp instanceof DateFormat) {
|
||||||
|
result = (DateFormat) temp;
|
||||||
|
} else {
|
||||||
|
result = new SimpleDateFormat((String)temp, getDateLocale());
|
||||||
|
}
|
||||||
|
}
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
result = (DateFormat) result.clone(); // clone for safety
|
result = (DateFormat) result.clone(); // clone for safety
|
||||||
result.setCalendar(calendar);
|
result.setCalendar(getCalendar());
|
||||||
} else {
|
} else {
|
||||||
// In the case of date formats, we don't have to look at more than one
|
// In the case of date formats, we don't have to look at more than one
|
||||||
// locale. May be different for other cases
|
// locale. May be different for other cases
|
||||||
ULocale currentLocale = dateLocale != null ? dateLocale : (ULocale)locales.get(0);
|
|
||||||
// TODO Make this one function.
|
// TODO Make this one function.
|
||||||
if (timeStyle == NONE) {
|
if (timeStyle == NONE) {
|
||||||
result = DateFormat.getDateInstance(calendar, dateStyle, currentLocale);
|
result = DateFormat.getDateInstance(getCalendar(), dateStyle, getDateLocale());
|
||||||
} else if (dateStyle == NONE) {
|
} else if (dateStyle == NONE) {
|
||||||
result = DateFormat.getTimeInstance(calendar, timeStyle, currentLocale);
|
result = DateFormat.getTimeInstance(getCalendar(), timeStyle, getDateLocale());
|
||||||
} else {
|
} else {
|
||||||
result = DateFormat.getDateTimeInstance(calendar, dateStyle, timeStyle, currentLocale);
|
result = DateFormat.getDateTimeInstance(getCalendar(), dateStyle, timeStyle, getDateLocale());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
@ -404,32 +568,39 @@ public class GlobalizationPreferences {
|
|||||||
/**
|
/**
|
||||||
* Gets a number format according to the current settings.
|
* Gets a number format according to the current settings.
|
||||||
* If there is an explicit (non-null) number
|
* If there is an explicit (non-null) number
|
||||||
* format set, a copy of that is returned. Otherwise, if there is a non-null date locale, that is used.
|
* format set, a copy of that is returned. Otherwise, if there is a non-null number locale, that is used.
|
||||||
* Otherwise, the language priority list is used. NONE should be used for the style,
|
* Otherwise, the language priority list is used. NONE should be used for the style,
|
||||||
* where only the date or time format individually is being gotten.
|
* where only the date or time format individually is being gotten.
|
||||||
|
* @param style CURRENCY, NUMBER, INTEGER, SCIENTIFIC, PERCENT
|
||||||
*/
|
*/
|
||||||
public NumberFormat getNumberFormat(int style) {
|
public NumberFormat getNumberFormat(int style) {
|
||||||
try {
|
try {
|
||||||
NumberFormat result = null;
|
NumberFormat result = null;
|
||||||
if (numberFormats != null) result = numberFormats[style];
|
if (numberFormats != null) {
|
||||||
|
Object temp = numberFormats[style];
|
||||||
|
if (temp instanceof NumberFormat) {
|
||||||
|
result = (NumberFormat) temp;
|
||||||
|
} else {
|
||||||
|
result = new DecimalFormat((String)temp, new DecimalFormatSymbols(getDateLocale()));
|
||||||
|
}
|
||||||
|
}
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
result = (NumberFormat) result.clone(); // clone for safety
|
result = (NumberFormat) result.clone(); // clone for safety (later optimize)
|
||||||
if (style == CURRENCY) {
|
if (style == CURRENCY) {
|
||||||
result.setCurrency(currency);
|
result.setCurrency(getCurrency());
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
// In the case of date formats, we don't have to look at more than one
|
// In the case of date formats, we don't have to look at more than one
|
||||||
// locale. May be different for other cases
|
// locale. May be different for other cases
|
||||||
ULocale currentLocale = numberLocale != null ? numberLocale : (ULocale)locales.get(0);
|
|
||||||
switch (style) {
|
switch (style) {
|
||||||
case NUMBER: return NumberFormat.getInstance(currentLocale);
|
case NUMBER: return NumberFormat.getInstance(getNumberLocale());
|
||||||
case SCIENTIFIC: return NumberFormat.getScientificInstance(currentLocale);
|
case SCIENTIFIC: return NumberFormat.getScientificInstance(getNumberLocale());
|
||||||
case INTEGER: return NumberFormat.getIntegerInstance(currentLocale);
|
case INTEGER: return NumberFormat.getIntegerInstance(getNumberLocale());
|
||||||
case PERCENT: return NumberFormat.getPercentInstance(currentLocale);
|
case PERCENT: return NumberFormat.getPercentInstance(getNumberLocale());
|
||||||
case CURRENCY: result = NumberFormat.getCurrencyInstance(currentLocale);
|
case CURRENCY: result = NumberFormat.getCurrencyInstance(getNumberLocale());
|
||||||
result.setCurrency(currency);
|
result.setCurrency(getCurrency());
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
} catch (RuntimeException e) {}
|
} catch (RuntimeException e) {}
|
||||||
throw new IllegalArgumentException(); // fix later
|
throw new IllegalArgumentException(); // fix later
|
||||||
@ -437,55 +608,111 @@ public class GlobalizationPreferences {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets a number format explicitly. Overrides the number locale and the general locale settings.
|
* Sets a number format explicitly. Overrides the number locale and the general locale settings.
|
||||||
|
* @param style CURRENCY, NUMBER, INTEGER, SCIENTIFIC, PERCENT
|
||||||
|
* @return this, for chaining
|
||||||
*/
|
*/
|
||||||
public GlobalizationPreferences setNumberFormat(int style, DateFormat format) {
|
public GlobalizationPreferences setNumberFormat(int style, DateFormat format) {
|
||||||
if (numberFormats == null) numberFormats = new NumberFormat[NUMBER_LIMIT];
|
if (isFrozen()) {
|
||||||
|
throw new UnsupportedOperationException("Attempt to modify immutable object");
|
||||||
|
}
|
||||||
|
if (numberFormats == null) numberFormats = new Object[NUMBER_LIMIT];
|
||||||
numberFormats[style] = (NumberFormat) format.clone(); // for safety
|
numberFormats[style] = (NumberFormat) format.clone(); // for safety
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Restore the object to the initial state.
|
* Sets a number format explicitly. Overrides the number locale and the general locale settings.
|
||||||
* @return the object, for chaining
|
* @return this, for chaining
|
||||||
*/
|
*/
|
||||||
public GlobalizationPreferences clear() {
|
public GlobalizationPreferences setNumberFormat(int style, String formatPattern) {
|
||||||
explicitLocales = explicitTerritory = explicitCurrency = explicitTimezone = explicitCalendar = false;
|
if (isFrozen()) {
|
||||||
locales.add(ULocale.getDefault());
|
throw new UnsupportedOperationException("Attempt to modify immutable object");
|
||||||
if (!explicitTerritory) guessTerritory();
|
}
|
||||||
if (!explicitCurrency) guessCurrency();
|
if (numberFormats == null) numberFormats = new Object[NUMBER_LIMIT];
|
||||||
if (!explicitTimezone) guessTimeZone();
|
// check to make sure it compiles
|
||||||
if (!explicitCalendar) guessCalendar();
|
new DecimalFormat((String)formatPattern, new DecimalFormatSymbols(getDateLocale()));
|
||||||
|
numberFormats[style] = formatPattern; // for safety
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restore the object to the initial state.
|
||||||
|
* @return this, for chaining
|
||||||
|
*/
|
||||||
|
public GlobalizationPreferences reset() {
|
||||||
|
if (isFrozen()) {
|
||||||
|
throw new UnsupportedOperationException("Attempt to modify immutable object");
|
||||||
|
}
|
||||||
|
territory = null;
|
||||||
|
calendar = null;
|
||||||
|
collator = null;
|
||||||
|
timezone = null;
|
||||||
|
currency = null;
|
||||||
|
dateFormats = null;
|
||||||
|
numberFormats = null;
|
||||||
|
dateLocale = null;
|
||||||
|
numberLocale = null;
|
||||||
|
locales = null;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
// protected helper functions
|
/**
|
||||||
protected void guessTerritory() {
|
* This function can be overridden by subclasses to use different heuristics.
|
||||||
|
*/
|
||||||
|
protected String guessTerritory() {
|
||||||
|
String result;
|
||||||
// pass through locales to see if there is a territory.
|
// pass through locales to see if there is a territory.
|
||||||
for (Iterator it = locales.iterator(); it.hasNext();) {
|
for (Iterator it = getLocales().iterator(); it.hasNext();) {
|
||||||
ULocale locale = (ULocale)it.next();
|
ULocale locale = (ULocale)it.next();
|
||||||
String temp = locale.getCountry();
|
result = locale.getCountry();
|
||||||
if (temp.length() != 0) {
|
if (result.length() != 0) {
|
||||||
territory = temp;
|
return result;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// if not, guess from the first language tag, or maybe from intersection of languages, eg nl + fr => BE
|
// if not, guess from the first language tag, or maybe from intersection of languages, eg nl + fr => BE
|
||||||
// TODO fix using real data
|
// TODO fix using real data
|
||||||
// for now, just use fixed values
|
// for now, just use fixed values
|
||||||
ULocale firstLocale = (ULocale)locales.iterator().next();
|
ULocale firstLocale = getLocale(0);
|
||||||
String language = firstLocale.getLanguage();
|
String language = firstLocale.getLanguage();
|
||||||
String script = firstLocale.getScript();
|
String script = firstLocale.getScript();
|
||||||
territory = null;
|
result = null;
|
||||||
if (script.length() != 0) {
|
if (script.length() != 0) {
|
||||||
territory = (String) language_territory_hack_map.get(language + "_" + script);
|
result = (String) language_territory_hack_map.get(language + "_" + script);
|
||||||
}
|
}
|
||||||
if (territory == null) territory = (String) language_territory_hack_map.get(language);
|
if (result == null) result = (String) language_territory_hack_map.get(language);
|
||||||
if (territory == null) territory = "US"; // need *some* default
|
if (result == null) result = "US"; // need *some* default
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
protected void guessCurrency() {
|
/**
|
||||||
currency = Currency.getInstance(new ULocale("und-" + territory));
|
* This function can be overridden by subclasses to use different heuristics
|
||||||
|
*/
|
||||||
|
protected Currency guessCurrency() {
|
||||||
|
return Currency.getInstance(new ULocale("und-" + getTerritory()));
|
||||||
}
|
}
|
||||||
protected void guessTimeZone() {
|
/**
|
||||||
|
* This function can be overridden by subclasses to use different heuristics
|
||||||
|
* <b>It MUST return a 'safe' value,
|
||||||
|
* one whose modification will not affect this object.</b>
|
||||||
|
*/
|
||||||
|
protected List guessLocales() {
|
||||||
|
List result = new ArrayList(0);
|
||||||
|
result.add(ULocale.getDefault());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* This function can be overridden by subclasses to use different heuristics.
|
||||||
|
* <b>It MUST return a 'safe' value,
|
||||||
|
* one whose modification will not affect this object.</b>
|
||||||
|
*/
|
||||||
|
protected Collator guessCollator() {
|
||||||
|
return Collator.getInstance(getLocale(0));
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* This function can be overridden by subclasses to use different heuristics.
|
||||||
|
* <b>It MUST return a 'safe' value,
|
||||||
|
* one whose modification will not affect this object.</b>
|
||||||
|
*/
|
||||||
|
protected TimeZone guessTimeZone() {
|
||||||
// TODO fix using real data
|
// TODO fix using real data
|
||||||
// for single-zone countries, pick that zone
|
// for single-zone countries, pick that zone
|
||||||
// for others, pick the most populous zone
|
// for others, pick the most populous zone
|
||||||
@ -493,9 +720,9 @@ public class GlobalizationPreferences {
|
|||||||
// NOTE: in a few cases can do better by looking at language.
|
// NOTE: in a few cases can do better by looking at language.
|
||||||
// Eg haw+US should go to Pacific/Honolulu
|
// Eg haw+US should go to Pacific/Honolulu
|
||||||
// fr+CA should go to America/Montreal
|
// fr+CA should go to America/Montreal
|
||||||
String timezoneString = (String) territory_tzid_hack_map.get(territory);
|
String timezoneString = (String) territory_tzid_hack_map.get(getTerritory());
|
||||||
if (timezoneString == null) {
|
if (timezoneString == null) {
|
||||||
String[] attempt = ZoneMeta.getAvailableIDs(territory);
|
String[] attempt = ZoneMeta.getAvailableIDs(getTerritory());
|
||||||
if (attempt.length == 0) {
|
if (attempt.length == 0) {
|
||||||
timezoneString = "Etc/GMT"; // gotta do something
|
timezoneString = "Etc/GMT"; // gotta do something
|
||||||
} else {
|
} else {
|
||||||
@ -508,35 +735,39 @@ public class GlobalizationPreferences {
|
|||||||
timezoneString = attempt[i];
|
timezoneString = attempt[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
timezone = TimeZone.getTimeZone(timezoneString);
|
return TimeZone.getTimeZone(timezoneString);
|
||||||
}
|
}
|
||||||
protected void guessCalendar() {
|
/**
|
||||||
|
* This function can be overridden by subclasses to use different heuristics.
|
||||||
|
* <b>It MUST return a 'safe' value,
|
||||||
|
* one whose modification will not affect this object.</b>
|
||||||
|
*/
|
||||||
|
protected Calendar guessCalendar() {
|
||||||
// TODO add better API
|
// TODO add better API
|
||||||
calendar = Calendar.getInstance(new ULocale("und-" + territory));
|
return Calendar.getInstance(new ULocale("und-" + getTerritory()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// PRIVATES
|
// PRIVATES
|
||||||
|
|
||||||
private ArrayList locales = new ArrayList();
|
private Object locales;
|
||||||
private String territory;
|
private String territory;
|
||||||
private Currency currency;
|
private Currency currency;
|
||||||
private TimeZone timezone;
|
private TimeZone timezone;
|
||||||
private Calendar calendar;
|
private Calendar calendar;
|
||||||
private boolean explicitLocales;
|
private Collator collator;
|
||||||
private boolean explicitTerritory;
|
|
||||||
private boolean explicitCurrency;
|
|
||||||
private boolean explicitTimezone;
|
|
||||||
private boolean explicitCalendar;
|
|
||||||
|
|
||||||
private ULocale dateLocale;
|
private ULocale dateLocale;
|
||||||
private DateFormat[][] dateFormats;
|
private Object[][] dateFormats;
|
||||||
private ULocale numberLocale;
|
private ULocale numberLocale;
|
||||||
private NumberFormat[] numberFormats;
|
private Object[] numberFormats;
|
||||||
|
|
||||||
{
|
{
|
||||||
clear();
|
reset();
|
||||||
}
|
}
|
||||||
//
|
|
||||||
|
/** WARNING: All of this data is temporary, until we start importing from CLDR!!!
|
||||||
|
*
|
||||||
|
*/
|
||||||
private static final Map language_territory_hack_map = new HashMap();
|
private static final Map language_territory_hack_map = new HashMap();
|
||||||
private static final String[][] language_territory_hack = {
|
private static final String[][] language_territory_hack = {
|
||||||
{"af", "ZA"},
|
{"af", "ZA"},
|
||||||
@ -737,4 +968,24 @@ public class GlobalizationPreferences {
|
|||||||
territory_tzid_hack_map.put(territory_tzid_hack[i][0],territory_tzid_hack[i][1]);
|
territory_tzid_hack_map.put(territory_tzid_hack[i][0],territory_tzid_hack[i][1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected boolean frozen;
|
||||||
|
public boolean isFrozen() {
|
||||||
|
return frozen;
|
||||||
|
}
|
||||||
|
public Object freeze() {
|
||||||
|
frozen = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public Object cloneAsThawed() {
|
||||||
|
try {
|
||||||
|
GlobalizationPreferences result = (GlobalizationPreferences) clone();
|
||||||
|
result.frozen = false;
|
||||||
|
return result;
|
||||||
|
} catch (CloneNotSupportedException e) {
|
||||||
|
// will always work
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user