History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/java/text (120 of 18352 files). Show all

Unified · Split

120 files
  1. java/pkg/java/text/Annotation.class +0 −0
  2. java/pkg/java/text/Annotation.java +84 −0
  3. java/pkg/java/text/AttributeEntry.class +0 −0
  4. java/pkg/java/text/AttributedCharacterIterator$Attribute.class +0 −0
  5. java/pkg/java/text/AttributedCharacterIterator.class +0 −0
  6. java/pkg/java/text/AttributedCharacterIterator.java +254 −0
  7. java/pkg/java/text/AttributedString$AttributeMap.class +0 −0
  8. java/pkg/java/text/AttributedString$AttributedStringIterator.class +0 −0
  9. java/pkg/java/text/AttributedString.class +0 −0
  10. java/pkg/java/text/AttributedString.java +1120 −0
  11. java/pkg/java/text/Bidi.class +0 −0
  12. java/pkg/java/text/Bidi.java +328 −0
  13. java/pkg/java/text/BreakDictionary$1.class +0 −0
  14. java/pkg/java/text/BreakDictionary.class +0 −0
  15. java/pkg/java/text/BreakDictionary.java +341 −0
  16. java/pkg/java/text/BreakIterator$1.class +0 −0
  17. java/pkg/java/text/BreakIterator$BreakIteratorCache.class +0 −0
  18. java/pkg/java/text/BreakIterator$BreakIteratorGetter.class +0 −0
  19. java/pkg/java/text/BreakIterator.class +0 −0
  20. java/pkg/java/text/BreakIterator.java +715 −0
  21. java/pkg/java/text/CalendarBuilder.class +0 −0
  22. java/pkg/java/text/CalendarBuilder.java +170 −0
  23. java/pkg/java/text/CharacterIterator.class +0 −0
  24. java/pkg/java/text/CharacterIterator.java +193 −0
  25. java/pkg/java/text/CharacterIteratorFieldDelegate.class +0 −0
  26. java/pkg/java/text/CharacterIteratorFieldDelegate.java +124 −0
  27. java/pkg/java/text/ChoiceFormat.class +0 −0
  28. java/pkg/java/text/ChoiceFormat.java +619 −0
  29. java/pkg/java/text/CollationElementIterator.class +0 −0
  30. java/pkg/java/text/CollationElementIterator.java +777 −0
  31. java/pkg/java/text/CollationKey.class +0 −0
  32. java/pkg/java/text/CollationKey.java +145 −0
  33. java/pkg/java/text/CollationRules.class +0 −0
  34. java/pkg/java/text/CollationRules.java +274 −0
  35. java/pkg/java/text/Collator$CollatorGetter.class +0 −0
  36. java/pkg/java/text/Collator.class +0 −0
  37. java/pkg/java/text/Collator.java +553 −0
  38. java/pkg/java/text/DateFormat$DateFormatGetter.class +0 −0
  39. java/pkg/java/text/DateFormat$Field.class +0 −0
  40. java/pkg/java/text/DateFormat.class +0 −0
  41. java/pkg/java/text/DateFormat.java +1030 −0
  42. java/pkg/java/text/DateFormatSymbols$DateFormatSymbolsGetter.class +0 −0
  43. java/pkg/java/text/DateFormatSymbols.class +0 −0
  44. java/pkg/java/text/DateFormatSymbols.java +794 −0
  45. java/pkg/java/text/DecimalFormat.class +0 −0
  46. java/pkg/java/text/DecimalFormat.java +3278 −0
  47. java/pkg/java/text/DecimalFormatSymbols$DecimalFormatSymbolsGetter.class +0 −0
  48. java/pkg/java/text/DecimalFormatSymbols.class +0 −0
  49. java/pkg/java/text/DecimalFormatSymbols.java +837 −0
  50. java/pkg/java/text/DictionaryBasedBreakIterator.class +0 −0
  51. java/pkg/java/text/DictionaryBasedBreakIterator.java +514 −0
  52. java/pkg/java/text/DigitList$1.class +0 −0
  53. java/pkg/java/text/DigitList.class +0 −0
  54. java/pkg/java/text/DigitList.java +715 −0
  55. java/pkg/java/text/DontCareFieldPosition$1.class +0 −0
  56. java/pkg/java/text/DontCareFieldPosition.class +0 −0
  57. java/pkg/java/text/DontCareFieldPosition.java +53 −0
  58. java/pkg/java/text/EntryPair.class +0 −0
  59. java/pkg/java/text/EntryPair.java +60 −0
  60. java/pkg/java/text/FieldPosition$1.class +0 −0
  61. java/pkg/java/text/FieldPosition$Delegate.class +0 −0
  62. java/pkg/java/text/FieldPosition.class +0 −0
  63. java/pkg/java/text/FieldPosition.java +303 −0
  64. java/pkg/java/text/Format$Field.class +0 −0
  65. java/pkg/java/text/Format$FieldDelegate.class +0 −0
  66. java/pkg/java/text/Format.class +0 −0
  67. java/pkg/java/text/Format.java +406 −0
  68. java/pkg/java/text/MergeCollation.class +0 −0
  69. java/pkg/java/text/MergeCollation.java +342 −0
  70. java/pkg/java/text/MessageFormat$Field.class +0 −0
  71. java/pkg/java/text/MessageFormat.class +0 −0
  72. java/pkg/java/text/MessageFormat.java +1594 −0
  73. java/pkg/java/text/Normalizer$Form.class +0 −0
  74. java/pkg/java/text/Normalizer.class +0 −0
  75. java/pkg/java/text/Normalizer.java +183 −0
  76. java/pkg/java/text/NumberFormat$Field.class +0 −0
  77. java/pkg/java/text/NumberFormat$NumberFormatGetter.class +0 −0
  78. java/pkg/java/text/NumberFormat.class +0 −0
  79. java/pkg/java/text/NumberFormat.java +1162 −0
  80. java/pkg/java/text/ParseException.class +0 −0
  81. java/pkg/java/text/ParseException.java +78 −0
  82. java/pkg/java/text/ParsePosition.class +0 −0
  83. java/pkg/java/text/ParsePosition.java +139 −0
  84. java/pkg/java/text/PatternEntry$Parser.class +0 −0
  85. java/pkg/java/text/PatternEntry.class +0 −0
  86. java/pkg/java/text/PatternEntry.java +306 −0
  87. java/pkg/java/text/RBCollationTables$1.class +0 −0
  88. java/pkg/java/text/RBCollationTables$BuildAPI.class +0 −0
  89. java/pkg/java/text/RBCollationTables.class +0 −0
  90. java/pkg/java/text/RBCollationTables.java +303 −0
  91. java/pkg/java/text/RBTableBuilder.class +0 −0
  92. java/pkg/java/text/RBTableBuilder.java +618 −0
  93. java/pkg/java/text/RuleBasedBreakIterator$1.class +0 −0
  94. java/pkg/java/text/RuleBasedBreakIterator$SafeCharIterator.class +0 −0
  95. java/pkg/java/text/RuleBasedBreakIterator.class +0 −0
  96. java/pkg/java/text/RuleBasedBreakIterator.java +1148 −0
  97. java/pkg/java/text/RuleBasedCollationKey.class +0 −0
  98. java/pkg/java/text/RuleBasedCollationKey.java +123 −0
  99. java/pkg/java/text/RuleBasedCollator.class +0 −0
  100. java/pkg/java/text/RuleBasedCollator.java +763 −0
  101. java/pkg/java/text/SimpleDateFormat.class +0 −0
  102. java/pkg/java/text/SimpleDateFormat.java +2350 −0
  103. java/pkg/java/text/StringCharacterIterator.class +0 −0
  104. java/pkg/java/text/StringCharacterIterator.java +279 −0
  105. java/pkg/java/text/mkfile +49 −0
  106. java/pkg/java/text/package.html +65 −0
  107. java/pkg/java/text/spi/BreakIteratorProvider.class +0 −0
  108. java/pkg/java/text/spi/BreakIteratorProvider.java +107 −0
  109. java/pkg/java/text/spi/CollatorProvider.class +0 −0
  110. java/pkg/java/text/spi/CollatorProvider.java +61 −0
  111. java/pkg/java/text/spi/DateFormatProvider.class +0 −0
  112. java/pkg/java/text/spi/DateFormatProvider.java +114 −0
  113. java/pkg/java/text/spi/DateFormatSymbolsProvider.class +0 −0
  114. java/pkg/java/text/spi/DateFormatSymbolsProvider.java +62 −0
  115. java/pkg/java/text/spi/DecimalFormatSymbolsProvider.class +0 −0
  116. java/pkg/java/text/spi/DecimalFormatSymbolsProvider.java +62 −0
  117. java/pkg/java/text/spi/NumberFormatProvider.class +0 −0
  118. java/pkg/java/text/spi/NumberFormatProvider.java +113 −0
  119. java/pkg/java/text/spi/mkfile +13 −0
  120. java/pkg/java/text/spi/package.html +50 −0
java/pkg/java/text/Annotation.class+0−0

Binary file not shown.

java/pkg/java/text/Annotation.java+84−0
@@ -0,0 +1,84 @@
1/*
2 * Copyright (c) 1997, 2002, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package java.text;
27
28/**
29* An Annotation object is used as a wrapper for a text attribute value if
30* the attribute has annotation characteristics. These characteristics are:
31* <ul>
32* <li>The text range that the attribute is applied to is critical to the
33* semantics of the range. That means, the attribute cannot be applied to subranges
34* of the text range that it applies to, and, if two adjacent text ranges have
35* the same value for this attribute, the attribute still cannot be applied to
36* the combined range as a whole with this value.
37* <li>The attribute or its value usually do no longer apply if the underlying text is
38* changed.
39* </ul>
40*
41* An example is grammatical information attached to a sentence:
42* For the previous sentence, you can say that "an example"
43* is the subject, but you cannot say the same about "an", "example", or "exam".
44* When the text is changed, the grammatical information typically becomes invalid.
45* Another example is Japanese reading information (yomi).
46*
47* <p>
48* Wrapping the attribute value into an Annotation object guarantees that
49* adjacent text runs don't get merged even if the attribute values are equal,
50* and indicates to text containers that the attribute should be discarded if
51* the underlying text is modified.
52*
53* @see AttributedCharacterIterator
54* @since 1.2
55*/
56
57public class Annotation {
58
59 /**
60 * Constructs an annotation record with the given value, which
61 * may be null.
62 * @param value The value of the attribute
63 */
64 public Annotation(Object value) {
65 this.value = value;
66 }
67
68 /**
69 * Returns the value of the attribute, which may be null.
70 */
71 public Object getValue() {
72 return value;
73 }
74
75 /**
76 * Returns the String representation of this Annotation.
77 */
78 public String toString() {
79 return getClass().getName() + "[value=" + value + "]";
80 }
81
82 private Object value;
83
84};
java/pkg/java/text/AttributeEntry.class+0−0

Binary file not shown.

java/pkg/java/text/AttributedCharacterIterator$Attribute.class+0−0

Binary file not shown.

java/pkg/java/text/AttributedCharacterIterator.class+0−0

Binary file not shown.

java/pkg/java/text/AttributedCharacterIterator.java+254−0
@@ -0,0 +1,254 @@
1/*
2 * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package java.text;
27
28import java.io.InvalidObjectException;
29import java.io.Serializable;
30import java.util.HashMap;
31import java.util.Map;
32import java.util.Set;
33
34/**
35 * An {@code AttributedCharacterIterator} allows iteration through both text and
36 * related attribute information.
37 *
38 * <p>
39 * An attribute is a key/value pair, identified by the key. No two
40 * attributes on a given character can have the same key.
41 *
42 * <p>The values for an attribute are immutable, or must not be mutated
43 * by clients or storage. They are always passed by reference, and not
44 * cloned.
45 *
46 * <p>A <em>run with respect to an attribute</em> is a maximum text range for
47 * which:
48 * <ul>
49 * <li>the attribute is undefined or {@code null} for the entire range, or
50 * <li>the attribute value is defined and has the same non-{@code null} value for the
51 * entire range.
52 * </ul>
53 *
54 * <p>A <em>run with respect to a set of attributes</em> is a maximum text range for
55 * which this condition is met for each member attribute.
56 *
57 * <p>When getting a run with no explicit attributes specified (i.e.,
58 * calling {@link #getRunStart()} and {@link #getRunLimit()}), any
59 * contiguous text segments having the same attributes (the same set
60 * of attribute/value pairs) are treated as separate runs if the
61 * attributes have been given to those text segments separately.
62 *
63 * <p>The returned indexes are limited to the range of the iterator.
64 *
65 * <p>The returned attribute information is limited to runs that contain
66 * the current character.
67 *
68 * <p>
69 * Attribute keys are instances of {@link AttributedCharacterIterator.Attribute} and its
70 * subclasses, such as {@link java.awt.font.TextAttribute}.
71 *
72 * @see AttributedCharacterIterator.Attribute
73 * @see java.awt.font.TextAttribute
74 * @see AttributedString
75 * @see Annotation
76 * @since 1.2
77 */
78
79public interface AttributedCharacterIterator extends CharacterIterator {
80
81 /**
82 * Defines attribute keys that are used to identify text attributes. These
83 * keys are used in {@code AttributedCharacterIterator} and {@code AttributedString}.
84 * @see AttributedCharacterIterator
85 * @see AttributedString
86 * @since 1.2
87 */
88
89 public static class Attribute implements Serializable {
90
91 /**
92 * The name of this {@code Attribute}. The name is used primarily by {@code readResolve}
93 * to look up the corresponding predefined instance when deserializing
94 * an instance.
95 * @serial
96 */
97 private String name;
98
99 // table of all instances in this class, used by readResolve
100 private static final Map instanceMap = new HashMap(7);
101
102 /**
103 * Constructs an {@code Attribute} with the given name.
104 */
105 protected Attribute(String name) {
106 this.name = name;
107 if (this.getClass() == Attribute.class) {
108 instanceMap.put(name, this);
109 }
110 }
111
112 /**
113 * Compares two objects for equality. This version only returns true
114 * for <code>x.equals(y)</code> if <code>x</code> and <code>y</code> refer
115 * to the same object, and guarantees this for all subclasses.
116 */
117 public final boolean equals(Object obj) {
118 return super.equals(obj);
119 }
120
121 /**
122 * Returns a hash code value for the object. This version is identical to
123 * the one in {@code Object}, but is also final.
124 */
125 public final int hashCode() {
126 return super.hashCode();
127 }
128
129 /**
130 * Returns a string representation of the object. This version returns the
131 * concatenation of class name, {@code "("}, a name identifying the attribute
132 * and {@code ")"}.
133 */
134 public String toString() {
135 return getClass().getName() + "(" + name + ")";
136 }
137
138 /**
139 * Returns the name of the attribute.
140 */
141 protected String getName() {
142 return name;
143 }
144
145 /**
146 * Resolves instances being deserialized to the predefined constants.
147 */
148 protected Object readResolve() throws InvalidObjectException {
149 if (this.getClass() != Attribute.class) {
150 throw new InvalidObjectException("subclass didn't correctly implement readResolve");
151 }
152
153 Attribute instance = (Attribute) instanceMap.get(getName());
154 if (instance != null) {
155 return instance;
156 } else {
157 throw new InvalidObjectException("unknown attribute name");
158 }
159 }
160
161 /**
162 * Attribute key for the language of some text.
163 * <p> Values are instances of {@link java.util.Locale Locale}.
164 * @see java.util.Locale
165 */
166 public static final Attribute LANGUAGE = new Attribute("language");
167
168 /**
169 * Attribute key for the reading of some text. In languages where the written form
170 * and the pronunciation of a word are only loosely related (such as Japanese),
171 * it is often necessary to store the reading (pronunciation) along with the
172 * written form.
173 * <p>Values are instances of {@link Annotation} holding instances of {@link String}.
174 * @see Annotation
175 * @see java.lang.String
176 */
177 public static final Attribute READING = new Attribute("reading");
178
179 /**
180 * Attribute key for input method segments. Input methods often break
181 * up text into segments, which usually correspond to words.
182 * <p>Values are instances of {@link Annotation} holding a {@code null} reference.
183 * @see Annotation
184 */
185 public static final Attribute INPUT_METHOD_SEGMENT = new Attribute("input_method_segment");
186
187 // make sure the serial version doesn't change between compiler versions
188 private static final long serialVersionUID = -9142742483513960612L;
189
190 };
191
192 /**
193 * Returns the index of the first character of the run
194 * with respect to all attributes containing the current character.
195 *
196 * <p>Any contiguous text segments having the same attributes (the
197 * same set of attribute/value pairs) are treated as separate runs
198 * if the attributes have been given to those text segments separately.
199 */
200 public int getRunStart();
201
202 /**
203 * Returns the index of the first character of the run
204 * with respect to the given {@code attribute} containing the current character.
205 */
206 public int getRunStart(Attribute attribute);
207
208 /**
209 * Returns the index of the first character of the run
210 * with respect to the given {@code attributes} containing the current character.
211 */
212 public int getRunStart(Set<? extends Attribute> attributes);
213
214 /**
215 * Returns the index of the first character following the run
216 * with respect to all attributes containing the current character.
217 *
218 * <p>Any contiguous text segments having the same attributes (the
219 * same set of attribute/value pairs) are treated as separate runs
220 * if the attributes have been given to those text segments separately.
221 */
222 public int getRunLimit();
223
224 /**
225 * Returns the index of the first character following the run
226 * with respect to the given {@code attribute} containing the current character.
227 */
228 public int getRunLimit(Attribute attribute);
229
230 /**
231 * Returns the index of the first character following the run
232 * with respect to the given {@code attributes} containing the current character.
233 */
234 public int getRunLimit(Set<? extends Attribute> attributes);
235
236 /**
237 * Returns a map with the attributes defined on the current
238 * character.
239 */
240 public Map<Attribute,Object> getAttributes();
241
242 /**
243 * Returns the value of the named {@code attribute} for the current character.
244 * Returns {@code null} if the {@code attribute} is not defined.
245 */
246 public Object getAttribute(Attribute attribute);
247
248 /**
249 * Returns the keys of all attributes defined on the
250 * iterator's text range. The set is empty if no
251 * attributes are defined.
252 */
253 public Set<Attribute> getAllAttributeKeys();
254};
java/pkg/java/text/AttributedString$AttributeMap.class+0−0

Binary file not shown.

java/pkg/java/text/AttributedString$AttributedStringIterator.class+0−0

Binary file not shown.

java/pkg/java/text/AttributedString.class+0−0

Binary file not shown.

java/pkg/java/text/AttributedString.java+1120−0
@@ -0,0 +1,1120 @@
1/*
2 * Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package java.text;
27
28import java.util.*;
29import java.text.AttributedCharacterIterator.Attribute;
30
31/**
32 * An AttributedString holds text and related attribute information. It
33 * may be used as the actual data storage in some cases where a text
34 * reader wants to access attributed text through the AttributedCharacterIterator
35 * interface.
36 *
37 * <p>
38 * An attribute is a key/value pair, identified by the key. No two
39 * attributes on a given character can have the same key.
40 *
41 * <p>The values for an attribute are immutable, or must not be mutated
42 * by clients or storage. They are always passed by reference, and not
43 * cloned.
44 *
45 * @see AttributedCharacterIterator
46 * @see Annotation
47 * @since 1.2
48 */
49
50public class AttributedString {
51
52 // since there are no vectors of int, we have to use arrays.
53 // We allocate them in chunks of 10 elements so we don't have to allocate all the time.
54 private static final int ARRAY_SIZE_INCREMENT = 10;
55
56 // field holding the text
57 String text;
58
59 // fields holding run attribute information
60 // run attributes are organized by run
61 int runArraySize; // current size of the arrays
62 int runCount; // actual number of runs, <= runArraySize
63 int runStarts[]; // start index for each run
64 Vector runAttributes[]; // vector of attribute keys for each run
65 Vector runAttributeValues[]; // parallel vector of attribute values for each run
66
67 /**
68 * Constructs an AttributedString instance with the given
69 * AttributedCharacterIterators.
70 *
71 * @param iterators AttributedCharacterIterators to construct
72 * AttributedString from.
73 * @throws NullPointerException if iterators is null
74 */
75 AttributedString(AttributedCharacterIterator[] iterators) {
76 if (iterators == null) {
77 throw new NullPointerException("Iterators must not be null");
78 }
79 if (iterators.length == 0) {
80 text = "";
81 }
82 else {
83 // Build the String contents
84 StringBuffer buffer = new StringBuffer();
85 for (int counter = 0; counter < iterators.length; counter++) {
86 appendContents(buffer, iterators[counter]);
87 }
88
89 text = buffer.toString();
90
91 if (text.length() > 0) {
92 // Determine the runs, creating a new run when the attributes
93 // differ.
94 int offset = 0;
95 Map last = null;
96
97 for (int counter = 0; counter < iterators.length; counter++) {
98 AttributedCharacterIterator iterator = iterators[counter];
99 int start = iterator.getBeginIndex();
100 int end = iterator.getEndIndex();
101 int index = start;
102
103 while (index < end) {
104 iterator.setIndex(index);
105
106 Map attrs = iterator.getAttributes();
107
108 if (mapsDiffer(last, attrs)) {
109 setAttributes(attrs, index - start + offset);
110 }
111 last = attrs;
112 index = iterator.getRunLimit();
113 }
114 offset += (end - start);
115 }
116 }
117 }
118 }
119
120 /**
121 * Constructs an AttributedString instance with the given text.
122 * @param text The text for this attributed string.
123 * @exception NullPointerException if <code>text</code> is null.
124 */
125 public AttributedString(String text) {
126 if (text == null) {
127 throw new NullPointerException();
128 }
129 this.text = text;
130 }
131
132 /**
133 * Constructs an AttributedString instance with the given text and attributes.
134 * @param text The text for this attributed string.
135 * @param attributes The attributes that apply to the entire string.
136 * @exception NullPointerException if <code>text</code> or
137 * <code>attributes</code> is null.
138 * @exception IllegalArgumentException if the text has length 0
139 * and the attributes parameter is not an empty Map (attributes
140 * cannot be applied to a 0-length range).
141 */
142 public AttributedString(String text,
143 Map<? extends Attribute, ?> attributes)
144 {
145 if (text == null || attributes == null) {
146 throw new NullPointerException();
147 }
148 this.text = text;
149
150 if (text.length() == 0) {
151 if (attributes.isEmpty())
152 return;
153 throw new IllegalArgumentException("Can't add attribute to 0-length text");
154 }
155
156 int attributeCount = attributes.size();
157 if (attributeCount > 0) {
158 createRunAttributeDataVectors();
159 Vector newRunAttributes = new Vector(attributeCount);
160 Vector newRunAttributeValues = new Vector(attributeCount);
161 runAttributes[0] = newRunAttributes;
162 runAttributeValues[0] = newRunAttributeValues;
163 Iterator iterator = attributes.entrySet().iterator();
164 while (iterator.hasNext()) {
165 Map.Entry entry = (Map.Entry) iterator.next();
166 newRunAttributes.addElement(entry.getKey());
167 newRunAttributeValues.addElement(entry.getValue());
168 }
169 }
170 }
171
172 /**
173 * Constructs an AttributedString instance with the given attributed
174 * text represented by AttributedCharacterIterator.
175 * @param text The text for this attributed string.
176 * @exception NullPointerException if <code>text</code> is null.
177 */
178 public AttributedString(AttributedCharacterIterator text) {
179 // If performance is critical, this constructor should be
180 // implemented here rather than invoking the constructor for a
181 // subrange. We can avoid some range checking in the loops.
182 this(text, text.getBeginIndex(), text.getEndIndex(), null);
183 }
184
185 /**
186 * Constructs an AttributedString instance with the subrange of
187 * the given attributed text represented by
188 * AttributedCharacterIterator. If the given range produces an
189 * empty text, all attributes will be discarded. Note that any
190 * attributes wrapped by an Annotation object are discarded for a
191 * subrange of the original attribute range.
192 *
193 * @param text The text for this attributed string.
194 * @param beginIndex Index of the first character of the range.
195 * @param endIndex Index of the character following the last character
196 * of the range.
197 * @exception NullPointerException if <code>text</code> is null.
198 * @exception IllegalArgumentException if the subrange given by
199 * beginIndex and endIndex is out of the text range.
200 * @see java.text.Annotation
201 */
202 public AttributedString(AttributedCharacterIterator text,
203 int beginIndex,
204 int endIndex) {
205 this(text, beginIndex, endIndex, null);
206 }
207
208 /**
209 * Constructs an AttributedString instance with the subrange of
210 * the given attributed text represented by
211 * AttributedCharacterIterator. Only attributes that match the
212 * given attributes will be incorporated into the instance. If the
213 * given range produces an empty text, all attributes will be
214 * discarded. Note that any attributes wrapped by an Annotation
215 * object are discarded for a subrange of the original attribute
216 * range.
217 *
218 * @param text The text for this attributed string.
219 * @param beginIndex Index of the first character of the range.
220 * @param endIndex Index of the character following the last character
221 * of the range.
222 * @param attributes Specifies attributes to be extracted
223 * from the text. If null is specified, all available attributes will
224 * be used.
225 * @exception NullPointerException if <code>text</code> is null.
226 * @exception IllegalArgumentException if the subrange given by
227 * beginIndex and endIndex is out of the text range.
228 * @see java.text.Annotation
229 */
230 public AttributedString(AttributedCharacterIterator text,
231 int beginIndex,
232 int endIndex,
233 Attribute[] attributes) {
234 if (text == null) {
235 throw new NullPointerException();
236 }
237
238 // Validate the given subrange
239 int textBeginIndex = text.getBeginIndex();
240 int textEndIndex = text.getEndIndex();
241 if (beginIndex < textBeginIndex || endIndex > textEndIndex || beginIndex > endIndex)
242 throw new IllegalArgumentException("Invalid substring range");
243
244 // Copy the given string
245 StringBuffer textBuffer = new StringBuffer();
246 text.setIndex(beginIndex);
247 for (char c = text.current(); text.getIndex() < endIndex; c = text.next())
248 textBuffer.append(c);
249 this.text = textBuffer.toString();
250
251 if (beginIndex == endIndex)
252 return;
253
254 // Select attribute keys to be taken care of
255 HashSet keys = new HashSet();
256 if (attributes == null) {
257 keys.addAll(text.getAllAttributeKeys());
258 } else {
259 for (int i = 0; i < attributes.length; i++)
260 keys.add(attributes[i]);
261 keys.retainAll(text.getAllAttributeKeys());
262 }
263 if (keys.isEmpty())
264 return;
265
266 // Get and set attribute runs for each attribute name. Need to
267 // scan from the top of the text so that we can discard any
268 // Annotation that is no longer applied to a subset text segment.
269 Iterator itr = keys.iterator();
270 while (itr.hasNext()) {
271 Attribute attributeKey = (Attribute)itr.next();
272 text.setIndex(textBeginIndex);
273 while (text.getIndex() < endIndex) {
274 int start = text.getRunStart(attributeKey);
275 int limit = text.getRunLimit(attributeKey);
276 Object value = text.getAttribute(attributeKey);
277
278 if (value != null) {
279 if (value instanceof Annotation) {
280 if (start >= beginIndex && limit <= endIndex) {
281 addAttribute(attributeKey, value, start - beginIndex, limit - beginIndex);
282 } else {
283 if (limit > endIndex)
284 break;
285 }
286 } else {
287 // if the run is beyond the given (subset) range, we
288 // don't need to process further.
289 if (start >= endIndex)
290 break;
291 if (limit > beginIndex) {
292 // attribute is applied to any subrange
293 if (start < beginIndex)
294 start = beginIndex;
295 if (limit > endIndex)
296 limit = endIndex;
297 if (start != limit) {
298 addAttribute(attributeKey, value, start - beginIndex, limit - beginIndex);
299 }
300 }
301 }
302 }
303 text.setIndex(limit);
304 }
305 }
306 }
307
308 /**
309 * Adds an attribute to the entire string.
310 * @param attribute the attribute key
311 * @param value the value of the attribute; may be null
312 * @exception NullPointerException if <code>attribute</code> is null.
313 * @exception IllegalArgumentException if the AttributedString has length 0
314 * (attributes cannot be applied to a 0-length range).
315 */
316 public void addAttribute(Attribute attribute, Object value) {
317
318 if (attribute == null) {
319 throw new NullPointerException();
320 }
321
322 int len = length();
323 if (len == 0) {
324 throw new IllegalArgumentException("Can't add attribute to 0-length text");
325 }
326
327 addAttributeImpl(attribute, value, 0, len);
328 }
329
330 /**
331 * Adds an attribute to a subrange of the string.
332 * @param attribute the attribute key
333 * @param value The value of the attribute. May be null.
334 * @param beginIndex Index of the first character of the range.
335 * @param endIndex Index of the character following the last character of the range.
336 * @exception NullPointerException if <code>attribute</code> is null.
337 * @exception IllegalArgumentException if beginIndex is less then 0, endIndex is
338 * greater than the length of the string, or beginIndex and endIndex together don't
339 * define a non-empty subrange of the string.
340 */
341 public void addAttribute(Attribute attribute, Object value,
342 int beginIndex, int endIndex) {
343
344 if (attribute == null) {
345 throw new NullPointerException();
346 }
347
348 if (beginIndex < 0 || endIndex > length() || beginIndex >= endIndex) {
349 throw new IllegalArgumentException("Invalid substring range");
350 }
351
352 addAttributeImpl(attribute, value, beginIndex, endIndex);
353 }
354
355 /**
356 * Adds a set of attributes to a subrange of the string.
357 * @param attributes The attributes to be added to the string.
358 * @param beginIndex Index of the first character of the range.
359 * @param endIndex Index of the character following the last
360 * character of the range.
361 * @exception NullPointerException if <code>attributes</code> is null.
362 * @exception IllegalArgumentException if beginIndex is less then
363 * 0, endIndex is greater than the length of the string, or
364 * beginIndex and endIndex together don't define a non-empty
365 * subrange of the string and the attributes parameter is not an
366 * empty Map.
367 */
368 public void addAttributes(Map<? extends Attribute, ?> attributes,
369 int beginIndex, int endIndex)
370 {
371 if (attributes == null) {
372 throw new NullPointerException();
373 }
374
375 if (beginIndex < 0 || endIndex > length() || beginIndex > endIndex) {
376 throw new IllegalArgumentException("Invalid substring range");
377 }
378 if (beginIndex == endIndex) {
379 if (attributes.isEmpty())
380 return;
381 throw new IllegalArgumentException("Can't add attribute to 0-length text");
382 }
383
384 // make sure we have run attribute data vectors
385 if (runCount == 0) {
386 createRunAttributeDataVectors();
387 }
388
389 // break up runs if necessary
390 int beginRunIndex = ensureRunBreak(beginIndex);
391 int endRunIndex = ensureRunBreak(endIndex);
392
393 Iterator iterator = attributes.entrySet().iterator();
394 while (iterator.hasNext()) {
395 Map.Entry entry = (Map.Entry) iterator.next();
396 addAttributeRunData((Attribute) entry.getKey(), entry.getValue(), beginRunIndex, endRunIndex);
397 }
398 }
399
400 private synchronized void addAttributeImpl(Attribute attribute, Object value,
401 int beginIndex, int endIndex) {
402
403 // make sure we have run attribute data vectors
404 if (runCount == 0) {
405 createRunAttributeDataVectors();
406 }
407
408 // break up runs if necessary
409 int beginRunIndex = ensureRunBreak(beginIndex);
410 int endRunIndex = ensureRunBreak(endIndex);
411
412 addAttributeRunData(attribute, value, beginRunIndex, endRunIndex);
413 }
414
415 private final void createRunAttributeDataVectors() {
416 // use temporary variables so things remain consistent in case of an exception
417 int newRunStarts[] = new int[ARRAY_SIZE_INCREMENT];
418 Vector newRunAttributes[] = new Vector[ARRAY_SIZE_INCREMENT];
419 Vector newRunAttributeValues[] = new Vector[ARRAY_SIZE_INCREMENT];
420 runStarts = newRunStarts;
421 runAttributes = newRunAttributes;
422 runAttributeValues = newRunAttributeValues;
423 runArraySize = ARRAY_SIZE_INCREMENT;
424 runCount = 1; // assume initial run starting at index 0
425 }
426
427 // ensure there's a run break at offset, return the index of the run
428 private final int ensureRunBreak(int offset) {
429 return ensureRunBreak(offset, true);
430 }
431
432 /**
433 * Ensures there is a run break at offset, returning the index of
434 * the run. If this results in splitting a run, two things can happen:
435 * <ul>
436 * <li>If copyAttrs is true, the attributes from the existing run
437 * will be placed in both of the newly created runs.
438 * <li>If copyAttrs is false, the attributes from the existing run
439 * will NOT be copied to the run to the right (>= offset) of the break,
440 * but will exist on the run to the left (< offset).
441 * </ul>
442 */
443 private final int ensureRunBreak(int offset, boolean copyAttrs) {
444 if (offset == length()) {
445 return runCount;
446 }
447
448 // search for the run index where this offset should be
449 int runIndex = 0;
450 while (runIndex < runCount && runStarts[runIndex] < offset) {
451 runIndex++;
452 }
453
454 // if the offset is at a run start already, we're done
455 if (runIndex < runCount && runStarts[runIndex] == offset) {
456 return runIndex;
457 }
458
459 // we'll have to break up a run
460 // first, make sure we have enough space in our arrays
461 if (runCount == runArraySize) {
462 int newArraySize = runArraySize + ARRAY_SIZE_INCREMENT;
463 int newRunStarts[] = new int[newArraySize];
464 Vector newRunAttributes[] = new Vector[newArraySize];
465 Vector newRunAttributeValues[] = new Vector[newArraySize];
466 for (int i = 0; i < runArraySize; i++) {
467 newRunStarts[i] = runStarts[i];
468 newRunAttributes[i] = runAttributes[i];
469 newRunAttributeValues[i] = runAttributeValues[i];
470 }
471 runStarts = newRunStarts;
472 runAttributes = newRunAttributes;
473 runAttributeValues = newRunAttributeValues;
474 runArraySize = newArraySize;
475 }
476
477 // make copies of the attribute information of the old run that the new one used to be part of
478 // use temporary variables so things remain consistent in case of an exception
479 Vector newRunAttributes = null;
480 Vector newRunAttributeValues = null;
481
482 if (copyAttrs) {
483 Vector oldRunAttributes = runAttributes[runIndex - 1];
484 Vector oldRunAttributeValues = runAttributeValues[runIndex - 1];
485 if (oldRunAttributes != null) {
486 newRunAttributes = (Vector) oldRunAttributes.clone();
487 }
488 if (oldRunAttributeValues != null) {
489 newRunAttributeValues = (Vector) oldRunAttributeValues.clone();
490 }
491 }
492
493 // now actually break up the run
494 runCount++;
495 for (int i = runCount - 1; i > runIndex; i--) {
496 runStarts[i] = runStarts[i - 1];
497 runAttributes[i] = runAttributes[i - 1];
498 runAttributeValues[i] = runAttributeValues[i - 1];
499 }
500 runStarts[runIndex] = offset;
501 runAttributes[runIndex] = newRunAttributes;
502 runAttributeValues[runIndex] = newRunAttributeValues;
503
504 return runIndex;
505 }
506
507 // add the attribute attribute/value to all runs where beginRunIndex <= runIndex < endRunIndex
508 private void addAttributeRunData(Attribute attribute, Object value,
509 int beginRunIndex, int endRunIndex) {
510
511 for (int i = beginRunIndex; i < endRunIndex; i++) {
512 int keyValueIndex = -1; // index of key and value in our vectors; assume we don't have an entry yet
513 if (runAttributes[i] == null) {
514 Vector newRunAttributes = new Vector();
515 Vector newRunAttributeValues = new Vector();
516 runAttributes[i] = newRunAttributes;
517 runAttributeValues[i] = newRunAttributeValues;
518 } else {
519 // check whether we have an entry already
520 keyValueIndex = runAttributes[i].indexOf(attribute);
521 }
522
523 if (keyValueIndex == -1) {
524 // create new entry
525 int oldSize = runAttributes[i].size();
526 runAttributes[i].addElement(attribute);
527 try {
528 runAttributeValues[i].addElement(value);
529 }
530 catch (Exception e) {
531 runAttributes[i].setSize(oldSize);
532 runAttributeValues[i].setSize(oldSize);
533 }
534 } else {
535 // update existing entry
536 runAttributeValues[i].set(keyValueIndex, value);
537 }
538 }
539 }
540
541 /**
542 * Creates an AttributedCharacterIterator instance that provides access to the entire contents of
543 * this string.
544 *
545 * @return An iterator providing access to the text and its attributes.
546 */
547 public AttributedCharacterIterator getIterator() {
548 return getIterator(null, 0, length());
549 }
550
551 /**
552 * Creates an AttributedCharacterIterator instance that provides access to
553 * selected contents of this string.
554 * Information about attributes not listed in attributes that the
555 * implementor may have need not be made accessible through the iterator.
556 * If the list is null, all available attribute information should be made
557 * accessible.
558 *
559 * @param attributes a list of attributes that the client is interested in
560 * @return an iterator providing access to the entire text and its selected attributes
561 */
562 public AttributedCharacterIterator getIterator(Attribute[] attributes) {
563 return getIterator(attributes, 0, length());
564 }
565
566 /**
567 * Creates an AttributedCharacterIterator instance that provides access to
568 * selected contents of this string.
569 * Information about attributes not listed in attributes that the
570 * implementor may have need not be made accessible through the iterator.
571 * If the list is null, all available attribute information should be made
572 * accessible.
573 *
574 * @param attributes a list of attributes that the client is interested in
575 * @param beginIndex the index of the first character
576 * @param endIndex the index of the character following the last character
577 * @return an iterator providing access to the text and its attributes
578 * @exception IllegalArgumentException if beginIndex is less then 0,
579 * endIndex is greater than the length of the string, or beginIndex is
580 * greater than endIndex.
581 */
582 public AttributedCharacterIterator getIterator(Attribute[] attributes, int beginIndex, int endIndex) {
583 return new AttributedStringIterator(attributes, beginIndex, endIndex);
584 }
585
586 // all (with the exception of length) reading operations are private,
587 // since AttributedString instances are accessed through iterators.
588
589 // length is package private so that CharacterIteratorFieldDelegate can
590 // access it without creating an AttributedCharacterIterator.
591 int length() {
592 return text.length();
593 }
594
595 private char charAt(int index) {
596 return text.charAt(index);
597 }
598
599 private synchronized Object getAttribute(Attribute attribute, int runIndex) {
600 Vector currentRunAttributes = runAttributes[runIndex];
601 Vector currentRunAttributeValues = runAttributeValues[runIndex];
602 if (currentRunAttributes == null) {
603 return null;
604 }
605 int attributeIndex = currentRunAttributes.indexOf(attribute);
606 if (attributeIndex != -1) {
607 return currentRunAttributeValues.elementAt(attributeIndex);
608 }
609 else {
610 return null;
611 }
612 }
613
614 // gets an attribute value, but returns an annotation only if it's range does not extend outside the range beginIndex..endIndex
615 private Object getAttributeCheckRange(Attribute attribute, int runIndex, int beginIndex, int endIndex) {
616 Object value = getAttribute(attribute, runIndex);
617 if (value instanceof Annotation) {
618 // need to check whether the annotation's range extends outside the iterator's range
619 if (beginIndex > 0) {
620 int currIndex = runIndex;
621 int runStart = runStarts[currIndex];
622 while (runStart >= beginIndex &&
623 valuesMatch(value, getAttribute(attribute, currIndex - 1))) {
624 currIndex--;
625 runStart = runStarts[currIndex];
626 }
627 if (runStart < beginIndex) {
628 // annotation's range starts before iterator's range
629 return null;
630 }
631 }
632 int textLength = length();
633 if (endIndex < textLength) {
634 int currIndex = runIndex;
635 int runLimit = (currIndex < runCount - 1) ? runStarts[currIndex + 1] : textLength;
636 while (runLimit <= endIndex &&
637 valuesMatch(value, getAttribute(attribute, currIndex + 1))) {
638 currIndex++;
639 runLimit = (currIndex < runCount - 1) ? runStarts[currIndex + 1] : textLength;
640 }
641 if (runLimit > endIndex) {
642 // annotation's range ends after iterator's range
643 return null;
644 }
645 }
646 // annotation's range is subrange of iterator's range,
647 // so we can return the value
648 }
649 return value;
650 }
651
652 // returns whether all specified attributes have equal values in the runs with the given indices
653 private boolean attributeValuesMatch(Set attributes, int runIndex1, int runIndex2) {
654 Iterator iterator = attributes.iterator();
655 while (iterator.hasNext()) {
656 Attribute key = (Attribute) iterator.next();
657 if (!valuesMatch(getAttribute(key, runIndex1), getAttribute(key, runIndex2))) {
658 return false;
659 }
660 }
661 return true;
662 }
663
664 // returns whether the two objects are either both null or equal
665 private final static boolean valuesMatch(Object value1, Object value2) {
666 if (value1 == null) {
667 return value2 == null;
668 } else {
669 return value1.equals(value2);
670 }
671 }
672
673 /**
674 * Appends the contents of the CharacterIterator iterator into the
675 * StringBuffer buf.
676 */
677 private final void appendContents(StringBuffer buf,
678 CharacterIterator iterator) {
679 int index = iterator.getBeginIndex();
680 int end = iterator.getEndIndex();
681
682 while (index < end) {
683 iterator.setIndex(index++);
684 buf.append(iterator.current());
685 }
686 }
687
688 /**
689 * Sets the attributes for the range from offset to the next run break
690 * (typically the end of the text) to the ones specified in attrs.
691 * This is only meant to be called from the constructor!
692 */
693 private void setAttributes(Map attrs, int offset) {
694 if (runCount == 0) {
695 createRunAttributeDataVectors();
696 }
697
698 int index = ensureRunBreak(offset, false);
699 int size;
700
701 if (attrs != null && (size = attrs.size()) > 0) {
702 Vector runAttrs = new Vector(size);
703 Vector runValues = new Vector(size);
704 Iterator iterator = attrs.entrySet().iterator();
705
706 while (iterator.hasNext()) {
707 Map.Entry entry = (Map.Entry)iterator.next();
708
709 runAttrs.add(entry.getKey());
710 runValues.add(entry.getValue());
711 }
712 runAttributes[index] = runAttrs;
713 runAttributeValues[index] = runValues;
714 }
715 }
716
717 /**
718 * Returns true if the attributes specified in last and attrs differ.
719 */
720 private static boolean mapsDiffer(Map last, Map attrs) {
721 if (last == null) {
722 return (attrs != null && attrs.size() > 0);
723 }
724 return (!last.equals(attrs));
725 }
726
727
728 // the iterator class associated with this string class
729
730 final private class AttributedStringIterator implements AttributedCharacterIterator {
731
732 // note on synchronization:
733 // we don't synchronize on the iterator, assuming that an iterator is only used in one thread.
734 // we do synchronize access to the AttributedString however, since it's more likely to be shared between threads.
735
736 // start and end index for our iteration
737 private int beginIndex;
738 private int endIndex;
739
740 // attributes that our client is interested in
741 private Attribute[] relevantAttributes;
742
743 // the current index for our iteration
744 // invariant: beginIndex <= currentIndex <= endIndex
745 private int currentIndex;
746
747 // information about the run that includes currentIndex
748 private int currentRunIndex;
749 private int currentRunStart;
750 private int currentRunLimit;
751
752 // constructor
753 AttributedStringIterator(Attribute[] attributes, int beginIndex, int endIndex) {
754
755 if (beginIndex < 0 || beginIndex > endIndex || endIndex > length()) {
756 throw new IllegalArgumentException("Invalid substring range");
757 }
758
759 this.beginIndex = beginIndex;
760 this.endIndex = endIndex;
761 this.currentIndex = beginIndex;
762 updateRunInfo();
763 if (attributes != null) {
764 relevantAttributes = (Attribute[]) attributes.clone();
765 }
766 }
767
768 // Object methods. See documentation in that class.
769
770 public boolean equals(Object obj) {
771 if (this == obj) {
772 return true;
773 }
774 if (!(obj instanceof AttributedStringIterator)) {
775 return false;
776 }
777
778 AttributedStringIterator that = (AttributedStringIterator) obj;
779
780 if (AttributedString.this != that.getString())
781 return false;
782 if (currentIndex != that.currentIndex || beginIndex != that.beginIndex || endIndex != that.endIndex)
783 return false;
784 return true;
785 }
786
787 public int hashCode() {
788 return text.hashCode() ^ currentIndex ^ beginIndex ^ endIndex;
789 }
790
791 public Object clone() {
792 try {
793 AttributedStringIterator other = (AttributedStringIterator) super.clone();
794 return other;
795 }
796 catch (CloneNotSupportedException e) {
797 throw new InternalError();
798 }
799 }
800
801 // CharacterIterator methods. See documentation in that interface.
802
803 public char first() {
804 return internalSetIndex(beginIndex);
805 }
806
807 public char last() {
808 if (endIndex == beginIndex) {
809 return internalSetIndex(endIndex);
810 } else {
811 return internalSetIndex(endIndex - 1);
812 }
813 }
814
815 public char current() {
816 if (currentIndex == endIndex) {
817 return DONE;
818 } else {
819 return charAt(currentIndex);
820 }
821 }
822
823 public char next() {
824 if (currentIndex < endIndex) {
825 return internalSetIndex(currentIndex + 1);
826 }
827 else {
828 return DONE;
829 }
830 }
831
832 public char previous() {
833 if (currentIndex > beginIndex) {
834 return internalSetIndex(currentIndex - 1);
835 }
836 else {
837 return DONE;
838 }
839 }
840
841 public char setIndex(int position) {
842 if (position < beginIndex || position > endIndex)
843 throw new IllegalArgumentException("Invalid index");
844 return internalSetIndex(position);
845 }
846
847 public int getBeginIndex() {
848 return beginIndex;
849 }
850
851 public int getEndIndex() {
852 return endIndex;
853 }
854
855 public int getIndex() {
856 return currentIndex;
857 }
858
859 // AttributedCharacterIterator methods. See documentation in that interface.
860
861 public int getRunStart() {
862 return currentRunStart;
863 }
864
865 public int getRunStart(Attribute attribute) {
866 if (currentRunStart == beginIndex || currentRunIndex == -1) {
867 return currentRunStart;
868 } else {
869 Object value = getAttribute(attribute);
870 int runStart = currentRunStart;
871 int runIndex = currentRunIndex;
872 while (runStart > beginIndex &&
873 valuesMatch(value, AttributedString.this.getAttribute(attribute, runIndex - 1))) {
874 runIndex--;
875 runStart = runStarts[runIndex];
876 }
877 if (runStart < beginIndex) {
878 runStart = beginIndex;
879 }
880 return runStart;
881 }
882 }
883
884 public int getRunStart(Set<? extends Attribute> attributes) {
885 if (currentRunStart == beginIndex || currentRunIndex == -1) {
886 return currentRunStart;
887 } else {
888 int runStart = currentRunStart;
889 int runIndex = currentRunIndex;
890 while (runStart > beginIndex &&
891 AttributedString.this.attributeValuesMatch(attributes, currentRunIndex, runIndex - 1)) {
892 runIndex--;
893 runStart = runStarts[runIndex];
894 }
895 if (runStart < beginIndex) {
896 runStart = beginIndex;
897 }
898 return runStart;
899 }
900 }
901
902 public int getRunLimit() {
903 return currentRunLimit;
904 }
905
906 public int getRunLimit(Attribute attribute) {
907 if (currentRunLimit == endIndex || currentRunIndex == -1) {
908 return currentRunLimit;
909 } else {
910 Object value = getAttribute(attribute);
911 int runLimit = currentRunLimit;
912 int runIndex = currentRunIndex;
913 while (runLimit < endIndex &&
914 valuesMatch(value, AttributedString.this.getAttribute(attribute, runIndex + 1))) {
915 runIndex++;
916 runLimit = runIndex < runCount - 1 ? runStarts[runIndex + 1] : endIndex;
917 }
918 if (runLimit > endIndex) {
919 runLimit = endIndex;
920 }
921 return runLimit;
922 }
923 }
924
925 public int getRunLimit(Set<? extends Attribute> attributes) {
926 if (currentRunLimit == endIndex || currentRunIndex == -1) {
927 return currentRunLimit;
928 } else {
929 int runLimit = currentRunLimit;
930 int runIndex = currentRunIndex;
931 while (runLimit < endIndex &&
932 AttributedString.this.attributeValuesMatch(attributes, currentRunIndex, runIndex + 1)) {
933 runIndex++;
934 runLimit = runIndex < runCount - 1 ? runStarts[runIndex + 1] : endIndex;
935 }
936 if (runLimit > endIndex) {
937 runLimit = endIndex;
938 }
939 return runLimit;
940 }
941 }
942
943 public Map<Attribute,Object> getAttributes() {
944 if (runAttributes == null || currentRunIndex == -1 || runAttributes[currentRunIndex] == null) {
945 // ??? would be nice to return null, but current spec doesn't allow it
946 // returning Hashtable saves AttributeMap from dealing with emptiness
947 return new Hashtable();
948 }
949 return new AttributeMap(currentRunIndex, beginIndex, endIndex);
950 }
951
952 public Set<Attribute> getAllAttributeKeys() {
953 // ??? This should screen out attribute keys that aren't relevant to the client
954 if (runAttributes == null) {
955 // ??? would be nice to return null, but current spec doesn't allow it
956 // returning HashSet saves us from dealing with emptiness
957 return new HashSet();
958 }
959 synchronized (AttributedString.this) {
960 // ??? should try to create this only once, then update if necessary,
961 // and give callers read-only view
962 Set keys = new HashSet();
963 int i = 0;
964 while (i < runCount) {
965 if (runStarts[i] < endIndex && (i == runCount - 1 || runStarts[i + 1] > beginIndex)) {
966 Vector currentRunAttributes = runAttributes[i];
967 if (currentRunAttributes != null) {
968 int j = currentRunAttributes.size();
969 while (j-- > 0) {
970 keys.add(currentRunAttributes.get(j));
971 }
972 }
973 }
974 i++;
975 }
976 return keys;
977 }
978 }
979
980 public Object getAttribute(Attribute attribute) {
981 int runIndex = currentRunIndex;
982 if (runIndex < 0) {
983 return null;
984 }
985 return AttributedString.this.getAttributeCheckRange(attribute, runIndex, beginIndex, endIndex);
986 }
987
988 // internally used methods
989
990 private AttributedString getString() {
991 return AttributedString.this;
992 }
993
994 // set the current index, update information about the current run if necessary,
995 // return the character at the current index
996 private char internalSetIndex(int position) {
997 currentIndex = position;
998 if (position < currentRunStart || position >= currentRunLimit) {
999 updateRunInfo();
1000 }
1001 if (currentIndex == endIndex) {
1002 return DONE;
1003 } else {
1004 return charAt(position);
1005 }
1006 }
1007
1008 // update the information about the current run
1009 private void updateRunInfo() {
1010 if (currentIndex == endIndex) {
1011 currentRunStart = currentRunLimit = endIndex;
1012 currentRunIndex = -1;
1013 } else {
1014 synchronized (AttributedString.this) {
1015 int runIndex = -1;
1016 while (runIndex < runCount - 1 && runStarts[runIndex + 1] <= currentIndex)
1017 runIndex++;
1018 currentRunIndex = runIndex;
1019 if (runIndex >= 0) {
1020 currentRunStart = runStarts[runIndex];
1021 if (currentRunStart < beginIndex)
1022 currentRunStart = beginIndex;
1023 }
1024 else {
1025 currentRunStart = beginIndex;
1026 }
1027 if (runIndex < runCount - 1) {
1028 currentRunLimit = runStarts[runIndex + 1];
1029 if (currentRunLimit > endIndex)
1030 currentRunLimit = endIndex;
1031 }
1032 else {
1033 currentRunLimit = endIndex;
1034 }
1035 }
1036 }
1037 }
1038
1039 }
1040
1041 // the map class associated with this string class, giving access to the attributes of one run
1042
1043 final private class AttributeMap extends AbstractMap<Attribute,Object> {
1044
1045 int runIndex;
1046 int beginIndex;
1047 int endIndex;
1048
1049 AttributeMap(int runIndex, int beginIndex, int endIndex) {
1050 this.runIndex = runIndex;
1051 this.beginIndex = beginIndex;
1052 this.endIndex = endIndex;
1053 }
1054
1055 public Set entrySet() {
1056 HashSet set = new HashSet();
1057 synchronized (AttributedString.this) {
1058 int size = runAttributes[runIndex].size();
1059 for (int i = 0; i < size; i++) {
1060 Attribute key = (Attribute) runAttributes[runIndex].get(i);
1061 Object value = runAttributeValues[runIndex].get(i);
1062 if (value instanceof Annotation) {
1063 value = AttributedString.this.getAttributeCheckRange(key,
1064 runIndex, beginIndex, endIndex);
1065 if (value == null) {
1066 continue;
1067 }
1068 }
1069 Map.Entry entry = new AttributeEntry(key, value);
1070 set.add(entry);
1071 }
1072 }
1073 return set;
1074 }
1075
1076 public Object get(Object key) {
1077 return AttributedString.this.getAttributeCheckRange((Attribute) key, runIndex, beginIndex, endIndex);
1078 }
1079 }
1080}
1081
1082class AttributeEntry implements Map.Entry {
1083
1084 private Attribute key;
1085 private Object value;
1086
1087 AttributeEntry(Attribute key, Object value) {
1088 this.key = key;
1089 this.value = value;
1090 }
1091
1092 public boolean equals(Object o) {
1093 if (!(o instanceof AttributeEntry)) {
1094 return false;
1095 }
1096 AttributeEntry other = (AttributeEntry) o;
1097 return other.key.equals(key) &&
1098 (value == null ? other.value == null : other.value.equals(value));
1099 }
1100
1101 public Object getKey() {
1102 return key;
1103 }
1104
1105 public Object getValue() {
1106 return value;
1107 }
1108
1109 public Object setValue(Object newValue) {
1110 throw new UnsupportedOperationException();
1111 }
1112
1113 public int hashCode() {
1114 return key.hashCode() ^ (value==null ? 0 : value.hashCode());
1115 }
1116
1117 public String toString() {
1118 return key.toString()+"="+value.toString();
1119 }
1120}
java/pkg/java/text/Bidi.class+0−0

Binary file not shown.

java/pkg/java/text/Bidi.java+328−0
@@ -0,0 +1,328 @@
1/*
2 * Copyright (c) 2000, 2009, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26/*
27 * (C) Copyright IBM Corp. 1999-2003 - All Rights Reserved
28 *
29 * The original version of this source code and documentation is
30 * copyrighted and owned by IBM. These materials are provided
31 * under terms of a License Agreement between IBM and Sun.
32 * This technology is protected by multiple US and International
33 * patents. This notice and attribution to IBM may not be removed.
34 */
35
36package java.text;
37
38import sun.text.bidi.BidiBase;
39
40/**
41 * This class implements the Unicode Bidirectional Algorithm.
42 * <p>
43 * A Bidi object provides information on the bidirectional reordering of the text
44 * used to create it. This is required, for example, to properly display Arabic
45 * or Hebrew text. These languages are inherently mixed directional, as they order
46 * numbers from left-to-right while ordering most other text from right-to-left.
47 * <p>
48 * Once created, a Bidi object can be queried to see if the text it represents is
49 * all left-to-right or all right-to-left. Such objects are very lightweight and
50 * this text is relatively easy to process.
51 * <p>
52 * If there are multiple runs of text, information about the runs can be accessed
53 * by indexing to get the start, limit, and level of a run. The level represents
54 * both the direction and the 'nesting level' of a directional run. Odd levels
55 * are right-to-left, while even levels are left-to-right. So for example level
56 * 0 represents left-to-right text, while level 1 represents right-to-left text, and
57 * level 2 represents left-to-right text embedded in a right-to-left run.
58 *
59 * @since 1.4
60 */
61public final class Bidi {
62
63 /** Constant indicating base direction is left-to-right. */
64 public static final int DIRECTION_LEFT_TO_RIGHT = 0;
65
66 /** Constant indicating base direction is right-to-left. */
67 public static final int DIRECTION_RIGHT_TO_LEFT = 1;
68
69 /**
70 * Constant indicating that the base direction depends on the first strong
71 * directional character in the text according to the Unicode
72 * Bidirectional Algorithm. If no strong directional character is present,
73 * the base direction is left-to-right.
74 */
75 public static final int DIRECTION_DEFAULT_LEFT_TO_RIGHT = -2;
76
77 /**
78 * Constant indicating that the base direction depends on the first strong
79 * directional character in the text according to the Unicode
80 * Bidirectional Algorithm. If no strong directional character is present,
81 * the base direction is right-to-left.
82 */
83 public static final int DIRECTION_DEFAULT_RIGHT_TO_LEFT = -1;
84
85 private BidiBase bidiBase;
86
87 /**
88 * Create Bidi from the given paragraph of text and base direction.
89 * @param paragraph a paragraph of text
90 * @param flags a collection of flags that control the algorithm. The
91 * algorithm understands the flags DIRECTION_LEFT_TO_RIGHT, DIRECTION_RIGHT_TO_LEFT,
92 * DIRECTION_DEFAULT_LEFT_TO_RIGHT, and DIRECTION_DEFAULT_RIGHT_TO_LEFT.
93 * Other values are reserved.
94 */
95 public Bidi(String paragraph, int flags) {
96 if (paragraph == null) {
97 throw new IllegalArgumentException("paragraph is null");
98 }
99
100 bidiBase = new BidiBase(paragraph.toCharArray(), 0, null, 0, paragraph.length(), flags);
101 }
102
103 /**
104 * Create Bidi from the given paragraph of text.
105 * <p>
106 * The RUN_DIRECTION attribute in the text, if present, determines the base
107 * direction (left-to-right or right-to-left). If not present, the base
108 * direction is computes using the Unicode Bidirectional Algorithm, defaulting to left-to-right
109 * if there are no strong directional characters in the text. This attribute, if
110 * present, must be applied to all the text in the paragraph.
111 * <p>
112 * The BIDI_EMBEDDING attribute in the text, if present, represents embedding level
113 * information. Negative values from -1 to -62 indicate overrides at the absolute value
114 * of the level. Positive values from 1 to 62 indicate embeddings. Where values are
115 * zero or not defined, the base embedding level as determined by the base direction
116 * is assumed.
117 * <p>
118 * The NUMERIC_SHAPING attribute in the text, if present, converts European digits to
119 * other decimal digits before running the bidi algorithm. This attribute, if present,
120 * must be applied to all the text in the paragraph.
121 *
122 * @param paragraph a paragraph of text with optional character and paragraph attribute information
123 *
124 * @see java.awt.font.TextAttribute#BIDI_EMBEDDING
125 * @see java.awt.font.TextAttribute#NUMERIC_SHAPING
126 * @see java.awt.font.TextAttribute#RUN_DIRECTION
127 */
128 public Bidi(AttributedCharacterIterator paragraph) {
129 if (paragraph == null) {
130 throw new IllegalArgumentException("paragraph is null");
131 }
132
133 bidiBase = new BidiBase(0, 0);
134 bidiBase.setPara(paragraph);
135 }
136
137 /**
138 * Create Bidi from the given text, embedding, and direction information.
139 * The embeddings array may be null. If present, the values represent embedding level
140 * information. Negative values from -1 to -61 indicate overrides at the absolute value
141 * of the level. Positive values from 1 to 61 indicate embeddings. Where values are
142 * zero, the base embedding level as determined by the base direction is assumed.
143 * @param text an array containing the paragraph of text to process.
144 * @param textStart the index into the text array of the start of the paragraph.
145 * @param embeddings an array containing embedding values for each character in the paragraph.
146 * This can be null, in which case it is assumed that there is no external embedding information.
147 * @param embStart the index into the embedding array of the start of the paragraph.
148 * @param paragraphLength the length of the paragraph in the text and embeddings arrays.
149 * @param flags a collection of flags that control the algorithm. The
150 * algorithm understands the flags DIRECTION_LEFT_TO_RIGHT, DIRECTION_RIGHT_TO_LEFT,
151 * DIRECTION_DEFAULT_LEFT_TO_RIGHT, and DIRECTION_DEFAULT_RIGHT_TO_LEFT.
152 * Other values are reserved.
153 */
154 public Bidi(char[] text, int textStart, byte[] embeddings, int embStart, int paragraphLength, int flags) {
155 if (text == null) {
156 throw new IllegalArgumentException("text is null");
157 }
158 if (paragraphLength < 0) {
159 throw new IllegalArgumentException("bad length: " + paragraphLength);
160 }
161 if (textStart < 0 || paragraphLength > text.length - textStart) {
162 throw new IllegalArgumentException("bad range: " + textStart +
163 " length: " + paragraphLength +
164 " for text of length: " + text.length);
165 }
166 if (embeddings != null && (embStart < 0 || paragraphLength > embeddings.length - embStart)) {
167 throw new IllegalArgumentException("bad range: " + embStart +
168 " length: " + paragraphLength +
169 " for embeddings of length: " + text.length);
170 }
171
172 bidiBase = new BidiBase(text, textStart, embeddings, embStart, paragraphLength, flags);
173 }
174
175 /**
176 * Create a Bidi object representing the bidi information on a line of text within
177 * the paragraph represented by the current Bidi. This call is not required if the
178 * entire paragraph fits on one line.
179 * @param lineStart the offset from the start of the paragraph to the start of the line.
180 * @param lineLimit the offset from the start of the paragraph to the limit of the line.
181 */
182 public Bidi createLineBidi(int lineStart, int lineLimit) {
183 AttributedString astr = new AttributedString("");
184 Bidi newBidi = new Bidi(astr.getIterator());
185
186 return bidiBase.setLine(this, bidiBase, newBidi, newBidi.bidiBase,lineStart, lineLimit);
187 }
188
189 /**
190 * Return true if the line is not left-to-right or right-to-left. This means it either has mixed runs of left-to-right
191 * and right-to-left text, or the base direction differs from the direction of the only run of text.
192 * @return true if the line is not left-to-right or right-to-left.
193 */
194 public boolean isMixed() {
195 return bidiBase.isMixed();
196 }
197
198 /**
199 * Return true if the line is all left-to-right text and the base direction is left-to-right.
200 * @return true if the line is all left-to-right text and the base direction is left-to-right
201 */
202 public boolean isLeftToRight() {
203 return bidiBase.isLeftToRight();
204 }
205
206 /**
207 * Return true if the line is all right-to-left text, and the base direction is right-to-left.
208 * @return true if the line is all right-to-left text, and the base direction is right-to-left
209 */
210 public boolean isRightToLeft() {
211 return bidiBase.isRightToLeft();
212 }
213
214 /**
215 * Return the length of text in the line.
216 * @return the length of text in the line
217 */
218 public int getLength() {
219 return bidiBase.getLength();
220 }
221
222 /**
223 * Return true if the base direction is left-to-right.
224 * @return true if the base direction is left-to-right
225 */
226 public boolean baseIsLeftToRight() {
227 return bidiBase.baseIsLeftToRight();
228 }
229
230 /**
231 * Return the base level (0 if left-to-right, 1 if right-to-left).
232 * @return the base level
233 */
234 public int getBaseLevel() {
235 return bidiBase.getParaLevel();
236 }
237
238 /**
239 * Return the resolved level of the character at offset. If offset is <0 or >=
240 * the length of the line, return the base direction level.
241 * @param offset the index of the character for which to return the level
242 * @return the resolved level of the character at offset
243 */
244 public int getLevelAt(int offset) {
245 return bidiBase.getLevelAt(offset);
246 }
247
248 /**
249 * Return the number of level runs.
250 * @return the number of level runs
251 */
252 public int getRunCount() {
253 return bidiBase.countRuns();
254 }
255
256 /**
257 * Return the level of the nth logical run in this line.
258 * @param run the index of the run, between 0 and <code>getRunCount()</code>
259 * @return the level of the run
260 */
261 public int getRunLevel(int run) {
262 return bidiBase.getRunLevel(run);
263 }
264
265 /**
266 * Return the index of the character at the start of the nth logical run in this line, as
267 * an offset from the start of the line.
268 * @param run the index of the run, between 0 and <code>getRunCount()</code>
269 * @return the start of the run
270 */
271 public int getRunStart(int run) {
272 return bidiBase.getRunStart(run);
273 }
274
275 /**
276 * Return the index of the character past the end of the nth logical run in this line, as
277 * an offset from the start of the line. For example, this will return the length
278 * of the line for the last run on the line.
279 * @param run the index of the run, between 0 and <code>getRunCount()</code>
280 * @return limit the limit of the run
281 */
282 public int getRunLimit(int run) {
283 return bidiBase.getRunLimit(run);
284 }
285
286 /**
287 * Return true if the specified text requires bidi analysis. If this returns false,
288 * the text will display left-to-right. Clients can then avoid constructing a Bidi object.
289 * Text in the Arabic Presentation Forms area of Unicode is presumed to already be shaped
290 * and ordered for display, and so will not cause this function to return true.
291 *
292 * @param text the text containing the characters to test
293 * @param start the start of the range of characters to test
294 * @param limit the limit of the range of characters to test
295 * @return true if the range of characters requires bidi analysis
296 */
297 public static boolean requiresBidi(char[] text, int start, int limit) {
298 return BidiBase.requiresBidi(text, start, limit);
299 }
300
301 /**
302 * Reorder the objects in the array into visual order based on their levels.
303 * This is a utility function to use when you have a collection of objects
304 * representing runs of text in logical order, each run containing text
305 * at a single level. The elements at <code>index</code> from
306 * <code>objectStart</code> up to <code>objectStart + count</code>
307 * in the objects array will be reordered into visual order assuming
308 * each run of text has the level indicated by the corresponding element
309 * in the levels array (at <code>index - objectStart + levelStart</code>).
310 *
311 * @param levels an array representing the bidi level of each object
312 * @param levelStart the start position in the levels array
313 * @param objects the array of objects to be reordered into visual order
314 * @param objectStart the start position in the objects array
315 * @param count the number of objects to reorder
316 */
317 public static void reorderVisually(byte[] levels, int levelStart, Object[] objects, int objectStart, int count) {
318 BidiBase.reorderVisually(levels, levelStart, objects, objectStart, count);
319 }
320
321 /**
322 * Display the bidi internal state, used in debugging.
323 */
324 public String toString() {
325 return bidiBase.toString();
326 }
327
328}
java/pkg/java/text/BreakDictionary$1.class+0−0

Binary file not shown.

java/pkg/java/text/BreakDictionary.class+0−0

Binary file not shown.

java/pkg/java/text/BreakDictionary.java+341−0
@@ -0,0 +1,341 @@
1/*
2 * Copyright (c) 1999, 2003, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26/*
27 *
28 * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
29 * (C) Copyright IBM Corp. 1996 - 2002 - All Rights Reserved
30 *
31 * The original version of this source code and documentation
32 * is copyrighted and owned by Taligent, Inc., a wholly-owned
33 * subsidiary of IBM. These materials are provided under terms
34 * of a License Agreement between Taligent and Sun. This technology
35 * is protected by multiple US and International patents.
36 *
37 * This notice and attribution to Taligent may not be removed.
38 * Taligent is a registered trademark of Taligent, Inc.
39 */
40package java.text;
41
42import java.io.*;
43import java.security.AccessController;
44import java.security.PrivilegedActionException;
45import java.security.PrivilegedExceptionAction;
46import java.util.MissingResourceException;
47import sun.text.CompactByteArray;
48import sun.text.SupplementaryCharacterData;
49
50/**
51 * This is the class that represents the list of known words used by
52 * DictionaryBasedBreakIterator. The conceptual data structure used
53 * here is a trie: there is a node hanging off the root node for every
54 * letter that can start a word. Each of these nodes has a node hanging
55 * off of it for every letter that can be the second letter of a word
56 * if this node is the first letter, and so on. The trie is represented
57 * as a two-dimensional array that can be treated as a table of state
58 * transitions. Indexes are used to compress this array, taking
59 * advantage of the fact that this array will always be very sparse.
60 */
61class BreakDictionary {
62
63 //=========================================================================
64 // data members
65 //=========================================================================
66
67 /**
68 * The version of the dictionary that was read in.
69 */
70 private static int supportedVersion = 1;
71
72 /**
73 * Maps from characters to column numbers. The main use of this is to
74 * avoid making room in the array for empty columns.
75 */
76 private CompactByteArray columnMap = null;
77 private SupplementaryCharacterData supplementaryCharColumnMap = null;
78
79 /**
80 * The number of actual columns in the table
81 */
82 private int numCols;
83
84 /**
85 * Columns are organized into groups of 32. This says how many
86 * column groups. (We could calculate this, but we store the
87 * value to avoid having to repeatedly calculate it.)
88 */
89 private int numColGroups;
90
91 /**
92 * The actual compressed state table. Each conceptual row represents
93 * a state, and the cells in it contain the row numbers of the states
94 * to transition to for each possible letter. 0 is used to indicate
95 * an illegal combination of letters (i.e., the error state). The
96 * table is compressed by eliminating all the unpopulated (i.e., zero)
97 * cells. Multiple conceptual rows can then be doubled up in a single
98 * physical row by sliding them up and possibly shifting them to one
99 * side or the other so the populated cells don't collide. Indexes
100 * are used to identify unpopulated cells and to locate populated cells.
101 */
102 private short[] table = null;
103
104 /**
105 * This index maps logical row numbers to physical row numbers
106 */
107 private short[] rowIndex = null;
108
109 /**
110 * A bitmap is used to tell which cells in the comceptual table are
111 * populated. This array contains all the unique bit combinations
112 * in that bitmap. If the table is more than 32 columns wide,
113 * successive entries in this array are used for a single row.
114 */
115 private int[] rowIndexFlags = null;
116
117 /**
118 * This index maps from a logical row number into the bitmap table above.
119 * (This keeps us from storing duplicate bitmap combinations.) Since there
120 * are a lot of rows with only one populated cell, instead of wasting space
121 * in the bitmap table, we just store a negative number in this index for
122 * rows with one populated cell. The absolute value of that number is
123 * the column number of the populated cell.
124 */
125 private short[] rowIndexFlagsIndex = null;
126
127 /**
128 * For each logical row, this index contains a constant that is added to
129 * the logical column number to get the physical column number
130 */
131 private byte[] rowIndexShifts = null;
132
133 //=========================================================================
134 // deserialization
135 //=========================================================================
136
137 public BreakDictionary(String dictionaryName)
138 throws IOException, MissingResourceException {
139
140 readDictionaryFile(dictionaryName);
141 }
142
143 private void readDictionaryFile(final String dictionaryName)
144 throws IOException, MissingResourceException {
145
146 BufferedInputStream in;
147 try {
148 in = (BufferedInputStream)AccessController.doPrivileged(
149 new PrivilegedExceptionAction() {
150 public Object run() throws Exception {
151 return new BufferedInputStream(getClass().getResourceAsStream("/sun/text/resources/" + dictionaryName));
152 }
153 }
154 );
155 }
156 catch (PrivilegedActionException e) {
157 throw new InternalError(e.toString());
158 }
159
160 byte[] buf = new byte[8];
161 if (in.read(buf) != 8) {
162 throw new MissingResourceException("Wrong data length",
163 dictionaryName, "");
164 }
165
166 // check vesion
167 int version = BreakIterator.getInt(buf, 0);
168 if (version != supportedVersion) {
169 throw new MissingResourceException("Dictionary version(" + version + ") is unsupported",
170 dictionaryName, "");
171 }
172
173 // get data size
174 int len = BreakIterator.getInt(buf, 4);
175 buf = new byte[len];
176 if (in.read(buf) != len) {
177 throw new MissingResourceException("Wrong data length",
178 dictionaryName, "");
179 }
180
181 // close the stream
182 in.close();
183
184 int l;
185 int offset = 0;
186
187 // read in the column map for BMP characteres (this is serialized in
188 // its internal form: an index array followed by a data array)
189 l = BreakIterator.getInt(buf, offset);
190 offset += 4;
191 short[] temp = new short[l];
192 for (int i = 0; i < l; i++, offset+=2) {
193 temp[i] = BreakIterator.getShort(buf, offset);
194 }
195 l = BreakIterator.getInt(buf, offset);
196 offset += 4;
197 byte[] temp2 = new byte[l];
198 for (int i = 0; i < l; i++, offset++) {
199 temp2[i] = buf[offset];
200 }
201 columnMap = new CompactByteArray(temp, temp2);
202
203 // read in numCols and numColGroups
204 numCols = BreakIterator.getInt(buf, offset);
205 offset += 4;
206 numColGroups = BreakIterator.getInt(buf, offset);
207 offset += 4;
208
209 // read in the row-number index
210 l = BreakIterator.getInt(buf, offset);
211 offset += 4;
212 rowIndex = new short[l];
213 for (int i = 0; i < l; i++, offset+=2) {
214 rowIndex[i] = BreakIterator.getShort(buf, offset);
215 }
216
217 // load in the populated-cells bitmap: index first, then bitmap list
218 l = BreakIterator.getInt(buf, offset);
219 offset += 4;
220 rowIndexFlagsIndex = new short[l];
221 for (int i = 0; i < l; i++, offset+=2) {
222 rowIndexFlagsIndex[i] = BreakIterator.getShort(buf, offset);
223 }
224 l = BreakIterator.getInt(buf, offset);
225 offset += 4;
226 rowIndexFlags = new int[l];
227 for (int i = 0; i < l; i++, offset+=4) {
228 rowIndexFlags[i] = BreakIterator.getInt(buf, offset);
229 }
230
231 // load in the row-shift index
232 l = BreakIterator.getInt(buf, offset);
233 offset += 4;
234 rowIndexShifts = new byte[l];
235 for (int i = 0; i < l; i++, offset++) {
236 rowIndexShifts[i] = buf[offset];
237 }
238
239 // load in the actual state table
240 l = BreakIterator.getInt(buf, offset);
241 offset += 4;
242 table = new short[l];
243 for (int i = 0; i < l; i++, offset+=2) {
244 table[i] = BreakIterator.getShort(buf, offset);
245 }
246
247 // finally, prepare the column map for supplementary characters
248 l = BreakIterator.getInt(buf, offset);
249 offset += 4;
250 int[] temp3 = new int[l];
251 for (int i = 0; i < l; i++, offset+=4) {
252 temp3[i] = BreakIterator.getInt(buf, offset);
253 }
254 supplementaryCharColumnMap = new SupplementaryCharacterData(temp3);
255 }
256
257 //=========================================================================
258 // access to the words
259 //=========================================================================
260
261 /**
262 * Uses the column map to map the character to a column number, then
263 * passes the row and column number to getNextState()
264 * @param row The current state
265 * @param ch The character whose column we're interested in
266 * @return The new state to transition to
267 */
268 public final short getNextStateFromCharacter(int row, int ch) {
269 int col;
270 if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) {
271 col = columnMap.elementAt((char)ch);
272 } else {
273 col = supplementaryCharColumnMap.getValue(ch);
274 }
275 return getNextState(row, col);
276 }
277
278 /**
279 * Returns the value in the cell with the specified (logical) row and
280 * column numbers. In DictionaryBasedBreakIterator, the row number is
281 * a state number, the column number is an input, and the return value
282 * is the row number of the new state to transition to. (0 is the
283 * "error" state, and -1 is the "end of word" state in a dictionary)
284 * @param row The row number of the current state
285 * @param col The column number of the input character (0 means "not a
286 * dictionary character")
287 * @return The row number of the new state to transition to
288 */
289 public final short getNextState(int row, int col) {
290 if (cellIsPopulated(row, col)) {
291 // we map from logical to physical row number by looking up the
292 // mapping in rowIndex; we map from logical column number to
293 // physical column number by looking up a shift value for this
294 // logical row and offsetting the logical column number by
295 // the shift amount. Then we can use internalAt() to actually
296 // get the value out of the table.
297 return internalAt(rowIndex[row], col + rowIndexShifts[row]);
298 }
299 else {
300 return 0;
301 }
302 }
303
304 /**
305 * Given (logical) row and column numbers, returns true if the
306 * cell in that position is populated
307 */
308 private final boolean cellIsPopulated(int row, int col) {
309 // look up the entry in the bitmap index for the specified row.
310 // If it's a negative number, it's the column number of the only
311 // populated cell in the row
312 if (rowIndexFlagsIndex[row] < 0) {
313 return col == -rowIndexFlagsIndex[row];
314 }
315
316 // if it's a positive number, it's the offset of an entry in the bitmap
317 // list. If the table is more than 32 columns wide, the bitmap is stored
318 // successive entries in the bitmap list, so we have to divide the column
319 // number by 32 and offset the number we got out of the index by the result.
320 // Once we have the appropriate piece of the bitmap, test the appropriate
321 // bit and return the result.
322 else {
323 int flags = rowIndexFlags[rowIndexFlagsIndex[row] + (col >> 5)];
324 return (flags & (1 << (col & 0x1f))) != 0;
325 }
326 }
327
328 /**
329 * Implementation of getNextState() when we know the specified cell is
330 * populated.
331 * @param row The PHYSICAL row number of the cell
332 * @param col The PHYSICAL column number of the cell
333 * @return The value stored in the cell
334 */
335 private final short internalAt(int row, int col) {
336 // the table is a one-dimensional array, so this just does the math necessary
337 // to treat it as a two-dimensional array (we don't just use a two-dimensional
338 // array because two-dimensional arrays are inefficient in Java)
339 return table[row * numCols + col];
340 }
341}
java/pkg/java/text/BreakIterator$1.class+0−0

Binary file not shown.

java/pkg/java/text/BreakIterator$BreakIteratorCache.class+0−0

Binary file not shown.

java/pkg/java/text/BreakIterator$BreakIteratorGetter.class+0−0

Binary file not shown.

java/pkg/java/text/BreakIterator.class+0−0

Binary file not shown.

java/pkg/java/text/BreakIterator.java+715−0
@@ -0,0 +1,715 @@
1/*
2 * Copyright (c) 1996, 2006, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26/*
27 * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
28 * (C) Copyright IBM Corp. 1996 - 1998 - All Rights Reserved
29 *
30 * The original version of this source code and documentation
31 * is copyrighted and owned by Taligent, Inc., a wholly-owned
32 * subsidiary of IBM. These materials are provided under terms
33 * of a License Agreement between Taligent and Sun. This technology
34 * is protected by multiple US and International patents.
35 *
36 * This notice and attribution to Taligent may not be removed.
37 * Taligent is a registered trademark of Taligent, Inc.
38 *
39 */
40
41package java.text;
42
43import java.lang.ref.SoftReference;
44import java.net.URL;
45import java.io.InputStream;
46import java.io.IOException;
47import java.security.AccessController;
48import java.security.PrivilegedAction;
49import java.text.CharacterIterator;
50import java.text.StringCharacterIterator;
51import java.text.spi.BreakIteratorProvider;
52import java.util.Locale;
53import java.util.MissingResourceException;
54import java.util.ResourceBundle;
55import java.util.spi.LocaleServiceProvider;
56import sun.util.LocaleServiceProviderPool;
57import sun.util.resources.LocaleData;
58
59
60/**
61 * The <code>BreakIterator</code> class implements methods for finding
62 * the location of boundaries in text. Instances of <code>BreakIterator</code>
63 * maintain a current position and scan over text
64 * returning the index of characters where boundaries occur.
65 * Internally, <code>BreakIterator</code> scans text using a
66 * <code>CharacterIterator</code>, and is thus able to scan text held
67 * by any object implementing that protocol. A <code>StringCharacterIterator</code>
68 * is used to scan <code>String</code> objects passed to <code>setText</code>.
69 *
70 * <p>
71 * You use the factory methods provided by this class to create
72 * instances of various types of break iterators. In particular,
73 * use <code>getWordInstance</code>, <code>getLineInstance</code>,
74 * <code>getSentenceInstance</code>, and <code>getCharacterInstance</code>
75 * to create <code>BreakIterator</code>s that perform
76 * word, line, sentence, and character boundary analysis respectively.
77 * A single <code>BreakIterator</code> can work only on one unit
78 * (word, line, sentence, and so on). You must use a different iterator
79 * for each unit boundary analysis you wish to perform.
80 *
81 * <p><a name="line"></a>
82 * Line boundary analysis determines where a text string can be
83 * broken when line-wrapping. The mechanism correctly handles
84 * punctuation and hyphenated words. Actual line breaking needs
85 * to also consider the available line width and is handled by
86 * higher-level software.
87 *
88 * <p><a name="sentence"></a>
89 * Sentence boundary analysis allows selection with correct interpretation
90 * of periods within numbers and abbreviations, and trailing punctuation
91 * marks such as quotation marks and parentheses.
92 *
93 * <p><a name="word"></a>
94 * Word boundary analysis is used by search and replace functions, as
95 * well as within text editing applications that allow the user to
96 * select words with a double click. Word selection provides correct
97 * interpretation of punctuation marks within and following
98 * words. Characters that are not part of a word, such as symbols
99 * or punctuation marks, have word-breaks on both sides.
100 *
101 * <p><a name="character"></a>
102 * Character boundary analysis allows users to interact with characters
103 * as they expect to, for example, when moving the cursor through a text
104 * string. Character boundary analysis provides correct navigation
105 * through character strings, regardless of how the character is stored.
106 * The boundaries returned may be those of supplementary characters,
107 * combining character sequences, or ligature clusters.
108 * For example, an accented character might be stored as a base character
109 * and a diacritical mark. What users consider to be a character can
110 * differ between languages.
111 *
112 * <p>
113 * The <code>BreakIterator</code> instances returned by the factory methods
114 * of this class are intended for use with natural languages only, not for
115 * programming language text. It is however possible to define subclasses
116 * that tokenize a programming language.
117 *
118 * <P>
119 * <strong>Examples</strong>:<P>
120 * Creating and using text boundaries:
121 * <blockquote>
122 * <pre>
123 * public static void main(String args[]) {
124 * if (args.length == 1) {
125 * String stringToExamine = args[0];
126 * //print each word in order
127 * BreakIterator boundary = BreakIterator.getWordInstance();
128 * boundary.setText(stringToExamine);
129 * printEachForward(boundary, stringToExamine);
130 * //print each sentence in reverse order
131 * boundary = BreakIterator.getSentenceInstance(Locale.US);
132 * boundary.setText(stringToExamine);
133 * printEachBackward(boundary, stringToExamine);
134 * printFirst(boundary, stringToExamine);
135 * printLast(boundary, stringToExamine);
136 * }
137 * }
138 * </pre>
139 * </blockquote>
140 *
141 * Print each element in order:
142 * <blockquote>
143 * <pre>
144 * public static void printEachForward(BreakIterator boundary, String source) {
145 * int start = boundary.first();
146 * for (int end = boundary.next();
147 * end != BreakIterator.DONE;
148 * start = end, end = boundary.next()) {
149 * System.out.println(source.substring(start,end));
150 * }
151 * }
152 * </pre>
153 * </blockquote>
154 *
155 * Print each element in reverse order:
156 * <blockquote>
157 * <pre>
158 * public static void printEachBackward(BreakIterator boundary, String source) {
159 * int end = boundary.last();
160 * for (int start = boundary.previous();
161 * start != BreakIterator.DONE;
162 * end = start, start = boundary.previous()) {
163 * System.out.println(source.substring(start,end));
164 * }
165 * }
166 * </pre>
167 * </blockquote>
168 *
169 * Print first element:
170 * <blockquote>
171 * <pre>
172 * public static void printFirst(BreakIterator boundary, String source) {
173 * int start = boundary.first();
174 * int end = boundary.next();
175 * System.out.println(source.substring(start,end));
176 * }
177 * </pre>
178 * </blockquote>
179 *
180 * Print last element:
181 * <blockquote>
182 * <pre>
183 * public static void printLast(BreakIterator boundary, String source) {
184 * int end = boundary.last();
185 * int start = boundary.previous();
186 * System.out.println(source.substring(start,end));
187 * }
188 * </pre>
189 * </blockquote>
190 *
191 * Print the element at a specified position:
192 * <blockquote>
193 * <pre>
194 * public static void printAt(BreakIterator boundary, int pos, String source) {
195 * int end = boundary.following(pos);
196 * int start = boundary.previous();
197 * System.out.println(source.substring(start,end));
198 * }
199 * </pre>
200 * </blockquote>
201 *
202 * Find the next word:
203 * <blockquote>
204 * <pre>
205 * public static int nextWordStartAfter(int pos, String text) {
206 * BreakIterator wb = BreakIterator.getWordInstance();
207 * wb.setText(text);
208 * int last = wb.following(pos);
209 * int current = wb.next();
210 * while (current != BreakIterator.DONE) {
211 * for (int p = last; p < current; p++) {
212 * if (Character.isLetter(text.codePointAt(p)))
213 * return last;
214 * }
215 * last = current;
216 * current = wb.next();
217 * }
218 * return BreakIterator.DONE;
219 * }
220 * </pre>
221 * (The iterator returned by BreakIterator.getWordInstance() is unique in that
222 * the break positions it returns don't represent both the start and end of the
223 * thing being iterated over. That is, a sentence-break iterator returns breaks
224 * that each represent the end of one sentence and the beginning of the next.
225 * With the word-break iterator, the characters between two boundaries might be a
226 * word, or they might be the punctuation or whitespace between two words. The
227 * above code uses a simple heuristic to determine which boundary is the beginning
228 * of a word: If the characters between this boundary and the next boundary
229 * include at least one letter (this can be an alphabetical letter, a CJK ideograph,
230 * a Hangul syllable, a Kana character, etc.), then the text between this boundary
231 * and the next is a word; otherwise, it's the material between words.)
232 * </blockquote>
233 *
234 * @see CharacterIterator
235 *
236 */
237
238public abstract class BreakIterator implements Cloneable
239{
240 /**
241 * Constructor. BreakIterator is stateless and has no default behavior.
242 */
243 protected BreakIterator()
244 {
245 }
246
247 /**
248 * Create a copy of this iterator
249 * @return A copy of this
250 */
251 public Object clone()
252 {
253 try {
254 return super.clone();
255 }
256 catch (CloneNotSupportedException e) {
257 throw new InternalError();
258 }
259 }
260
261 /**
262 * DONE is returned by previous(), next(), next(int), preceding(int)
263 * and following(int) when either the first or last text boundary has been
264 * reached.
265 */
266 public static final int DONE = -1;
267
268 /**
269 * Returns the first boundary. The iterator's current position is set
270 * to the first text boundary.
271 * @return The character index of the first text boundary.
272 */
273 public abstract int first();
274
275 /**
276 * Returns the last boundary. The iterator's current position is set
277 * to the last text boundary.
278 * @return The character index of the last text boundary.
279 */
280 public abstract int last();
281
282 /**
283 * Returns the nth boundary from the current boundary. If either
284 * the first or last text boundary has been reached, it returns
285 * <code>BreakIterator.DONE</code> and the current position is set to either
286 * the first or last text boundary depending on which one is reached. Otherwise,
287 * the iterator's current position is set to the new boundary.
288 * For example, if the iterator's current position is the mth text boundary
289 * and three more boundaries exist from the current boundary to the last text
290 * boundary, the next(2) call will return m + 2. The new text position is set
291 * to the (m + 2)th text boundary. A next(4) call would return
292 * <code>BreakIterator.DONE</code> and the last text boundary would become the
293 * new text position.
294 * @param n which boundary to return. A value of 0
295 * does nothing. Negative values move to previous boundaries
296 * and positive values move to later boundaries.
297 * @return The character index of the nth boundary from the current position
298 * or <code>BreakIterator.DONE</code> if either first or last text boundary
299 * has been reached.
300 */
301 public abstract int next(int n);
302
303 /**
304 * Returns the boundary following the current boundary. If the current boundary
305 * is the last text boundary, it returns <code>BreakIterator.DONE</code> and
306 * the iterator's current position is unchanged. Otherwise, the iterator's
307 * current position is set to the boundary following the current boundary.
308 * @return The character index of the next text boundary or
309 * <code>BreakIterator.DONE</code> if the current boundary is the last text
310 * boundary.
311 * Equivalent to next(1).
312 * @see #next(int)
313 */
314 public abstract int next();
315
316 /**
317 * Returns the boundary preceding the current boundary. If the current boundary
318 * is the first text boundary, it returns <code>BreakIterator.DONE</code> and
319 * the iterator's current position is unchanged. Otherwise, the iterator's
320 * current position is set to the boundary preceding the current boundary.
321 * @return The character index of the previous text boundary or
322 * <code>BreakIterator.DONE</code> if the current boundary is the first text
323 * boundary.
324 */
325 public abstract int previous();
326
327 /**
328 * Returns the first boundary following the specified character offset. If the
329 * specified offset equals to the last text boundary, it returns
330 * <code>BreakIterator.DONE</code> and the iterator's current position is unchanged.
331 * Otherwise, the iterator's current position is set to the returned boundary.
332 * The value returned is always greater than the offset or the value
333 * <code>BreakIterator.DONE</code>.
334 * @param offset the character offset to begin scanning.
335 * @return The first boundary after the specified offset or
336 * <code>BreakIterator.DONE</code> if the last text boundary is passed in
337 * as the offset.
338 * @exception IllegalArgumentException if the specified offset is less than
339 * the first text boundary or greater than the last text boundary.
340 */
341 public abstract int following(int offset);
342
343 /**
344 * Returns the last boundary preceding the specified character offset. If the
345 * specified offset equals to the first text boundary, it returns
346 * <code>BreakIterator.DONE</code> and the iterator's current position is unchanged.
347 * Otherwise, the iterator's current position is set to the returned boundary.
348 * The value returned is always less than the offset or the value
349 * <code>BreakIterator.DONE</code>.
350 * @param offset the characater offset to begin scanning.
351 * @return The last boundary before the specified offset or
352 * <code>BreakIterator.DONE</code> if the first text boundary is passed in
353 * as the offset.
354 * @exception IllegalArgumentException if the specified offset is less than
355 * the first text boundary or greater than the last text boundary.
356 * @since 1.2
357 */
358 public int preceding(int offset) {
359 // NOTE: This implementation is here solely because we can't add new
360 // abstract methods to an existing class. There is almost ALWAYS a
361 // better, faster way to do this.
362 int pos = following(offset);
363 while (pos >= offset && pos != DONE)
364 pos = previous();
365 return pos;
366 }
367
368 /**
369 * Returns true if the specified character offset is a text boundary.
370 * @param offset the character offset to check.
371 * @return <code>true</code> if "offset" is a boundary position,
372 * <code>false</code> otherwise.
373 * @exception IllegalArgumentException if the specified offset is less than
374 * the first text boundary or greater than the last text boundary.
375 * @since 1.2
376 */
377 public boolean isBoundary(int offset) {
378 // NOTE: This implementation probably is wrong for most situations
379 // because it fails to take into account the possibility that a
380 // CharacterIterator passed to setText() may not have a begin offset
381 // of 0. But since the abstract BreakIterator doesn't have that
382 // knowledge, it assumes the begin offset is 0. If you subclass
383 // BreakIterator, copy the SimpleTextBoundary implementation of this
384 // function into your subclass. [This should have been abstract at
385 // this level, but it's too late to fix that now.]
386 if (offset == 0) {
387 return true;
388 }
389 int boundary = following(offset - 1);
390 if (boundary == DONE) {
391 throw new IllegalArgumentException();
392 }
393 return boundary == offset;
394 }
395
396 /**
397 * Returns character index of the text boundary that was most
398 * recently returned by next(), next(int), previous(), first(), last(),
399 * following(int) or preceding(int). If any of these methods returns
400 * <code>BreakIterator.DONE</code> because either first or last text boundary
401 * has been reached, it returns the first or last text boundary depending on
402 * which one is reached.
403 * @return The text boundary returned from the above methods, first or last
404 * text boundary.
405 * @see #next()
406 * @see #next(int)
407 * @see #previous()
408 * @see #first()
409 * @see #last()
410 * @see #following(int)
411 * @see #preceding(int)
412 */
413 public abstract int current();
414
415 /**
416 * Get the text being scanned
417 * @return the text being scanned
418 */
419 public abstract CharacterIterator getText();
420
421 /**
422 * Set a new text string to be scanned. The current scan
423 * position is reset to first().
424 * @param newText new text to scan.
425 */
426 public void setText(String newText)
427 {
428 setText(new StringCharacterIterator(newText));
429 }
430
431 /**
432 * Set a new text for scanning. The current scan
433 * position is reset to first().
434 * @param newText new text to scan.
435 */
436 public abstract void setText(CharacterIterator newText);
437
438 private static final int CHARACTER_INDEX = 0;
439 private static final int WORD_INDEX = 1;
440 private static final int LINE_INDEX = 2;
441 private static final int SENTENCE_INDEX = 3;
442 private static final SoftReference[] iterCache = new SoftReference[4];
443
444 /**
445 * Returns a new <code>BreakIterator</code> instance
446 * for <a href="#word">word breaks</a>
447 * for the {@linkplain Locale#getDefault() default locale}.
448 * @return A break iterator for word breaks
449 */
450 public static BreakIterator getWordInstance()
451 {
452 return getWordInstance(Locale.getDefault());
453 }
454
455 /**
456 * Returns a new <code>BreakIterator</code> instance
457 * for <a href="#word">word breaks</a>
458 * for the given locale.
459 * @param locale the desired locale
460 * @return A break iterator for word breaks
461 * @exception NullPointerException if <code>locale</code> is null
462 */
463 public static BreakIterator getWordInstance(Locale locale)
464 {
465 return getBreakInstance(locale,
466 WORD_INDEX,
467 "WordData",
468 "WordDictionary");
469 }
470
471 /**
472 * Returns a new <code>BreakIterator</code> instance
473 * for <a href="#line">line breaks</a>
474 * for the {@linkplain Locale#getDefault() default locale}.
475 * @return A break iterator for line breaks
476 */
477 public static BreakIterator getLineInstance()
478 {
479 return getLineInstance(Locale.getDefault());
480 }
481
482 /**
483 * Returns a new <code>BreakIterator</code> instance
484 * for <a href="#line">line breaks</a>
485 * for the given locale.
486 * @param locale the desired locale
487 * @return A break iterator for line breaks
488 * @exception NullPointerException if <code>locale</code> is null
489 */
490 public static BreakIterator getLineInstance(Locale locale)
491 {
492 return getBreakInstance(locale,
493 LINE_INDEX,
494 "LineData",
495 "LineDictionary");
496 }
497
498 /**
499 * Returns a new <code>BreakIterator</code> instance
500 * for <a href="#character">character breaks</a>
501 * for the {@linkplain Locale#getDefault() default locale}.
502 * @return A break iterator for character breaks
503 */
504 public static BreakIterator getCharacterInstance()
505 {
506 return getCharacterInstance(Locale.getDefault());
507 }
508
509 /**
510 * Returns a new <code>BreakIterator</code> instance
511 * for <a href="#character">character breaks</a>
512 * for the given locale.
513 * @param locale the desired locale
514 * @return A break iterator for character breaks
515 * @exception NullPointerException if <code>locale</code> is null
516 */
517 public static BreakIterator getCharacterInstance(Locale locale)
518 {
519 return getBreakInstance(locale,
520 CHARACTER_INDEX,
521 "CharacterData",
522 "CharacterDictionary");
523 }
524
525 /**
526 * Returns a new <code>BreakIterator</code> instance
527 * for <a href="#sentence">sentence breaks</a>
528 * for the {@linkplain Locale#getDefault() default locale}.
529 * @return A break iterator for sentence breaks
530 */
531 public static BreakIterator getSentenceInstance()
532 {
533 return getSentenceInstance(Locale.getDefault());
534 }
535
536 /**
537 * Returns a new <code>BreakIterator</code> instance
538 * for <a href="#sentence">sentence breaks</a>
539 * for the given locale.
540 * @param locale the desired locale
541 * @return A break iterator for sentence breaks
542 * @exception NullPointerException if <code>locale</code> is null
543 */
544 public static BreakIterator getSentenceInstance(Locale locale)
545 {
546 return getBreakInstance(locale,
547 SENTENCE_INDEX,
548 "SentenceData",
549 "SentenceDictionary");
550 }
551
552 private static BreakIterator getBreakInstance(Locale locale,
553 int type,
554 String dataName,
555 String dictionaryName) {
556 if (iterCache[type] != null) {
557 BreakIteratorCache cache = (BreakIteratorCache) iterCache[type].get();
558 if (cache != null) {
559 if (cache.getLocale().equals(locale)) {
560 return cache.createBreakInstance();
561 }
562 }
563 }
564
565 BreakIterator result = createBreakInstance(locale,
566 type,
567 dataName,
568 dictionaryName);
569 BreakIteratorCache cache = new BreakIteratorCache(locale, result);
570 iterCache[type] = new SoftReference(cache);
571 return result;
572 }
573
574 private static ResourceBundle getBundle(final String baseName, final Locale locale) {
575 return (ResourceBundle) AccessController.doPrivileged(new PrivilegedAction() {
576 public Object run() {
577 return ResourceBundle.getBundle(baseName, locale);
578 }
579 });
580 }
581
582 private static BreakIterator createBreakInstance(Locale locale,
583 int type,
584 String dataName,
585 String dictionaryName) {
586
587 // Check whether a provider can provide an implementation that's closer
588 // to the requested locale than what the Java runtime itself can provide.
589 LocaleServiceProviderPool pool =
590 LocaleServiceProviderPool.getPool(BreakIteratorProvider.class);
591 if (pool.hasProviders()) {
592 BreakIterator providersInstance = pool.getLocalizedObject(
593 BreakIteratorGetter.INSTANCE,
594 locale, type);
595 if (providersInstance != null) {
596 return providersInstance;
597 }
598 }
599
600 ResourceBundle bundle = getBundle(
601 "sun.text.resources.BreakIteratorInfo", locale);
602 String[] classNames = bundle.getStringArray("BreakIteratorClasses");
603
604 String dataFile = bundle.getString(dataName);
605
606 try {
607 if (classNames[type].equals("RuleBasedBreakIterator")) {
608 return new RuleBasedBreakIterator(dataFile);
609 }
610 else if (classNames[type].equals("DictionaryBasedBreakIterator")) {
611 String dictionaryFile = bundle.getString(dictionaryName);
612 return new DictionaryBasedBreakIterator(dataFile, dictionaryFile);
613 }
614 else {
615 throw new IllegalArgumentException("Invalid break iterator class \"" +
616 classNames[type] + "\"");
617 }
618 }
619 catch (Exception e) {
620 throw new InternalError(e.toString());
621 }
622 }
623
624 /**
625 * Returns an array of all locales for which the
626 * <code>get*Instance</code> methods of this class can return
627 * localized instances.
628 * The returned array represents the union of locales supported by the Java
629 * runtime and by installed
630 * {@link java.text.spi.BreakIteratorProvider BreakIteratorProvider} implementations.
631 * It must contain at least a <code>Locale</code>
632 * instance equal to {@link java.util.Locale#US Locale.US}.
633 *
634 * @return An array of locales for which localized
635 * <code>BreakIterator</code> instances are available.
636 */
637 public static synchronized Locale[] getAvailableLocales()
638 {
639 LocaleServiceProviderPool pool =
640 LocaleServiceProviderPool.getPool(BreakIteratorProvider.class);
641 return pool.getAvailableLocales();
642 }
643
644 private static final class BreakIteratorCache {
645
646 private BreakIterator iter;
647 private Locale locale;
648
649 BreakIteratorCache(Locale locale, BreakIterator iter) {
650 this.locale = locale;
651 this.iter = (BreakIterator) iter.clone();
652 }
653
654 Locale getLocale() {
655 return locale;
656 }
657
658 BreakIterator createBreakInstance() {
659 return (BreakIterator) iter.clone();
660 }
661 }
662
663 static long getLong(byte[] buf, int offset) {
664 long num = buf[offset]&0xFF;
665 for (int i = 1; i < 8; i++) {
666 num = num<<8 | (buf[offset+i]&0xFF);
667 }
668 return num;
669 }
670
671 static int getInt(byte[] buf, int offset) {
672 int num = buf[offset]&0xFF;
673 for (int i = 1; i < 4; i++) {
674 num = num<<8 | (buf[offset+i]&0xFF);
675 }
676 return num;
677 }
678
679 static short getShort(byte[] buf, int offset) {
680 short num = (short)(buf[offset]&0xFF);
681 num = (short)(num<<8 | (buf[offset+1]&0xFF));
682 return num;
683 }
684
685 /**
686 * Obtains a BreakIterator instance from a BreakIteratorProvider
687 * implementation.
688 */
689 private static class BreakIteratorGetter
690 implements LocaleServiceProviderPool.LocalizedObjectGetter<BreakIteratorProvider, BreakIterator> {
691 private static final BreakIteratorGetter INSTANCE =
692 new BreakIteratorGetter();
693
694 public BreakIterator getObject(BreakIteratorProvider breakIteratorProvider,
695 Locale locale,
696 String key,
697 Object... params) {
698 assert params.length == 1;
699
700 switch ((Integer)params[0]) {
701 case CHARACTER_INDEX:
702 return breakIteratorProvider.getCharacterInstance(locale);
703 case WORD_INDEX:
704 return breakIteratorProvider.getWordInstance(locale);
705 case LINE_INDEX:
706 return breakIteratorProvider.getLineInstance(locale);
707 case SENTENCE_INDEX:
708 return breakIteratorProvider.getSentenceInstance(locale);
709 default:
710 assert false : "should not happen";
711 }
712 return null;
713 }
714 }
715}
java/pkg/java/text/CalendarBuilder.class+0−0

Binary file not shown.

java/pkg/java/text/CalendarBuilder.java+170−0
@@ -0,0 +1,170 @@
1/*
2 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package java.text;
27
28import java.util.Calendar;
29import static java.util.GregorianCalendar.*;
30
31/**
32 * {@code CalendarBuilder} keeps field-value pairs for setting
33 * the calendar fields of the given {@code Calendar}. It has the
34 * {@link Calendar#FIELD_COUNT FIELD_COUNT}-th field for the week year
35 * support. Also {@code ISO_DAY_OF_WEEK} is used to specify
36 * {@code DAY_OF_WEEK} in the ISO day of week numbering.
37 *
38 * <p>{@code CalendarBuilder} retains the semantic of the pseudo
39 * timestamp for fields. {@code CalendarBuilder} uses a single
40 * int array combining fields[] and stamp[] of {@code Calendar}.
41 *
42 * @author Masayoshi Okutsu
43 */
44class CalendarBuilder {
45 /*
46 * Pseudo time stamp constants used in java.util.Calendar
47 */
48 private static final int UNSET = 0;
49 private static final int COMPUTED = 1;
50 private static final int MINIMUM_USER_STAMP = 2;
51
52 private static final int MAX_FIELD = FIELD_COUNT + 1;
53
54 public static final int WEEK_YEAR = FIELD_COUNT;
55 public static final int ISO_DAY_OF_WEEK = 1000; // pseudo field index
56
57 // stamp[] (lower half) and field[] (upper half) combined
58 private final int[] field;
59 private int nextStamp;
60 private int maxFieldIndex;
61
62 CalendarBuilder() {
63 field = new int[MAX_FIELD * 2];
64 nextStamp = MINIMUM_USER_STAMP;
65 maxFieldIndex = -1;
66 }
67
68 CalendarBuilder set(int index, int value) {
69 if (index == ISO_DAY_OF_WEEK) {
70 index = DAY_OF_WEEK;
71 value = toCalendarDayOfWeek(value);
72 }
73 field[index] = nextStamp++;
74 field[MAX_FIELD + index] = value;
75 if (index > maxFieldIndex && index < FIELD_COUNT) {
76 maxFieldIndex = index;
77 }
78 return this;
79 }
80
81 CalendarBuilder addYear(int value) {
82 field[MAX_FIELD + YEAR] += value;
83 field[MAX_FIELD + WEEK_YEAR] += value;
84 return this;
85 }
86
87 boolean isSet(int index) {
88 if (index == ISO_DAY_OF_WEEK) {
89 index = DAY_OF_WEEK;
90 }
91 return field[index] > UNSET;
92 }
93
94 Calendar establish(Calendar cal) {
95 boolean weekDate = isSet(WEEK_YEAR)
96 && field[WEEK_YEAR] > field[YEAR];
97 if (weekDate && !cal.isWeekDateSupported()) {
98 // Use YEAR instead
99 if (!isSet(YEAR)) {
100 set(YEAR, field[MAX_FIELD + WEEK_YEAR]);
101 }
102 weekDate = false;
103 }
104
105 cal.clear();
106 // Set the fields from the min stamp to the max stamp so that
107 // the field resolution works in the Calendar.
108 for (int stamp = MINIMUM_USER_STAMP; stamp < nextStamp; stamp++) {
109 for (int index = 0; index <= maxFieldIndex; index++) {
110 if (field[index] == stamp) {
111 cal.set(index, field[MAX_FIELD + index]);
112 break;
113 }
114 }
115 }
116
117 if (weekDate) {
118 int weekOfYear = isSet(WEEK_OF_YEAR) ? field[MAX_FIELD + WEEK_OF_YEAR] : 1;
119 int dayOfWeek = isSet(DAY_OF_WEEK) ?
120 field[MAX_FIELD + DAY_OF_WEEK] : cal.getFirstDayOfWeek();
121 if (!isValidDayOfWeek(dayOfWeek) && cal.isLenient()) {
122 if (dayOfWeek >= 8) {
123 dayOfWeek--;
124 weekOfYear += dayOfWeek / 7;
125 dayOfWeek = (dayOfWeek % 7) + 1;
126 } else {
127 while (dayOfWeek <= 0) {
128 dayOfWeek += 7;
129 weekOfYear--;
130 }
131 }
132 dayOfWeek = toCalendarDayOfWeek(dayOfWeek);
133 }
134 cal.setWeekDate(field[MAX_FIELD + WEEK_YEAR], weekOfYear, dayOfWeek);
135 }
136 return cal;
137 }
138
139 public String toString() {
140 StringBuilder sb = new StringBuilder();
141 sb.append("CalendarBuilder:[");
142 for (int i = 0; i < field.length; i++) {
143 if (isSet(i)) {
144 sb.append(i).append('=').append(field[MAX_FIELD + i]).append(',');
145 }
146 }
147 int lastIndex = sb.length() - 1;
148 if (sb.charAt(lastIndex) == ',') {
149 sb.setLength(lastIndex);
150 }
151 sb.append(']');
152 return sb.toString();
153 }
154
155 static int toISODayOfWeek(int calendarDayOfWeek) {
156 return calendarDayOfWeek == SUNDAY ? 7 : calendarDayOfWeek - 1;
157 }
158
159 static int toCalendarDayOfWeek(int isoDayOfWeek) {
160 if (!isValidDayOfWeek(isoDayOfWeek)) {
161 // adjust later for lenient mode
162 return isoDayOfWeek;
163 }
164 return isoDayOfWeek == 7 ? SUNDAY : isoDayOfWeek + 1;
165 }
166
167 static boolean isValidDayOfWeek(int dayOfWeek) {
168 return dayOfWeek > 0 && dayOfWeek <= 7;
169 }
170}
java/pkg/java/text/CharacterIterator.class+0−0

Binary file not shown.

java/pkg/java/text/CharacterIterator.java+193−0
@@ -0,0 +1,193 @@
1/*
2 * Copyright (c) 1996, 2000, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26/*
27 * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
28 * (C) Copyright IBM Corp. 1996 - 1998 - All Rights Reserved
29 *
30 * The original version of this source code and documentation
31 * is copyrighted and owned by Taligent, Inc., a wholly-owned
32 * subsidiary of IBM. These materials are provided under terms
33 * of a License Agreement between Taligent and Sun. This technology
34 * is protected by multiple US and International patents.
35 *
36 * This notice and attribution to Taligent may not be removed.
37 * Taligent is a registered trademark of Taligent, Inc.
38 *
39 */
40
41package java.text;
42
43
44/**
45 * This interface defines a protocol for bidirectional iteration over text.
46 * The iterator iterates over a bounded sequence of characters. Characters
47 * are indexed with values beginning with the value returned by getBeginIndex() and
48 * continuing through the value returned by getEndIndex()-1.
49 * <p>
50 * Iterators maintain a current character index, whose valid range is from
51 * getBeginIndex() to getEndIndex(); the value getEndIndex() is included to allow
52 * handling of zero-length text ranges and for historical reasons.
53 * The current index can be retrieved by calling getIndex() and set directly
54 * by calling setIndex(), first(), and last().
55 * <p>
56 * The methods previous() and next() are used for iteration. They return DONE if
57 * they would move outside the range from getBeginIndex() to getEndIndex() -1,
58 * signaling that the iterator has reached the end of the sequence. DONE is
59 * also returned by other methods to indicate that the current index is
60 * outside this range.
61 *
62 * <P>Examples:<P>
63 *
64 * Traverse the text from start to finish
65 * <pre>
66 * public void traverseForward(CharacterIterator iter) {
67 * for(char c = iter.first(); c != CharacterIterator.DONE; c = iter.next()) {
68 * processChar(c);
69 * }
70 * }
71 * </pre>
72 *
73 * Traverse the text backwards, from end to start
74 * <pre>
75 * public void traverseBackward(CharacterIterator iter) {
76 * for(char c = iter.last(); c != CharacterIterator.DONE; c = iter.previous()) {
77 * processChar(c);
78 * }
79 * }
80 * </pre>
81 *
82 * Traverse both forward and backward from a given position in the text.
83 * Calls to notBoundary() in this example represents some
84 * additional stopping criteria.
85 * <pre>
86 * public void traverseOut(CharacterIterator iter, int pos) {
87 * for (char c = iter.setIndex(pos);
88 * c != CharacterIterator.DONE && notBoundary(c);
89 * c = iter.next()) {
90 * }
91 * int end = iter.getIndex();
92 * for (char c = iter.setIndex(pos);
93 * c != CharacterIterator.DONE && notBoundary(c);
94 * c = iter.previous()) {
95 * }
96 * int start = iter.getIndex();
97 * processSection(start, end);
98 * }
99 * </pre>
100 *
101 * @see StringCharacterIterator
102 * @see AttributedCharacterIterator
103 */
104
105public interface CharacterIterator extends Cloneable
106{
107
108 /**
109 * Constant that is returned when the iterator has reached either the end
110 * or the beginning of the text. The value is '\\uFFFF', the "not a
111 * character" value which should not occur in any valid Unicode string.
112 */
113 public static final char DONE = '\uFFFF';
114
115 /**
116 * Sets the position to getBeginIndex() and returns the character at that
117 * position.
118 * @return the first character in the text, or DONE if the text is empty
119 * @see #getBeginIndex()
120 */
121 public char first();
122
123 /**
124 * Sets the position to getEndIndex()-1 (getEndIndex() if the text is empty)
125 * and returns the character at that position.
126 * @return the last character in the text, or DONE if the text is empty
127 * @see #getEndIndex()
128 */
129 public char last();
130
131 /**
132 * Gets the character at the current position (as returned by getIndex()).
133 * @return the character at the current position or DONE if the current
134 * position is off the end of the text.
135 * @see #getIndex()
136 */
137 public char current();
138
139 /**
140 * Increments the iterator's index by one and returns the character
141 * at the new index. If the resulting index is greater or equal
142 * to getEndIndex(), the current index is reset to getEndIndex() and
143 * a value of DONE is returned.
144 * @return the character at the new position or DONE if the new
145 * position is off the end of the text range.
146 */
147 public char next();
148
149 /**
150 * Decrements the iterator's index by one and returns the character
151 * at the new index. If the current index is getBeginIndex(), the index
152 * remains at getBeginIndex() and a value of DONE is returned.
153 * @return the character at the new position or DONE if the current
154 * position is equal to getBeginIndex().
155 */
156 public char previous();
157
158 /**
159 * Sets the position to the specified position in the text and returns that
160 * character.
161 * @param position the position within the text. Valid values range from
162 * getBeginIndex() to getEndIndex(). An IllegalArgumentException is thrown
163 * if an invalid value is supplied.
164 * @return the character at the specified position or DONE if the specified position is equal to getEndIndex()
165 */
166 public char setIndex(int position);
167
168 /**
169 * Returns the start index of the text.
170 * @return the index at which the text begins.
171 */
172 public int getBeginIndex();
173
174 /**
175 * Returns the end index of the text. This index is the index of the first
176 * character following the end of the text.
177 * @return the index after the last character in the text
178 */
179 public int getEndIndex();
180
181 /**
182 * Returns the current index.
183 * @return the current index.
184 */
185 public int getIndex();
186
187 /**
188 * Create a copy of this iterator
189 * @return A copy of this
190 */
191 public Object clone();
192
193}
java/pkg/java/text/CharacterIteratorFieldDelegate.class+0−0

Binary file not shown.

java/pkg/java/text/CharacterIteratorFieldDelegate.java+124−0
@@ -0,0 +1,124 @@
1/*
2 * Copyright (c) 2000, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25package java.text;
26
27import java.util.ArrayList;
28
29/**
30 * CharacterIteratorFieldDelegate combines the notifications from a Format
31 * into a resulting <code>AttributedCharacterIterator</code>. The resulting
32 * <code>AttributedCharacterIterator</code> can be retrieved by way of
33 * the <code>getIterator</code> method.
34 *
35 */
36class CharacterIteratorFieldDelegate implements Format.FieldDelegate {
37 /**
38 * Array of AttributeStrings. Whenever <code>formatted</code> is invoked
39 * for a region > size, a new instance of AttributedString is added to
40 * attributedStrings. Subsequent invocations of <code>formatted</code>
41 * for existing regions result in invoking addAttribute on the existing
42 * AttributedStrings.
43 */
44 private ArrayList attributedStrings;
45 /**
46 * Running count of the number of characters that have
47 * been encountered.
48 */
49 private int size;
50
51
52 CharacterIteratorFieldDelegate() {
53 attributedStrings = new ArrayList();
54 }
55
56 public void formatted(Format.Field attr, Object value, int start, int end,
57 StringBuffer buffer) {
58 if (start != end) {
59 if (start < size) {
60 // Adjust attributes of existing runs
61 int index = size;
62 int asIndex = attributedStrings.size() - 1;
63
64 while (start < index) {
65 AttributedString as = (AttributedString)attributedStrings.
66 get(asIndex--);
67 int newIndex = index - as.length();
68 int aStart = Math.max(0, start - newIndex);
69
70 as.addAttribute(attr, value, aStart, Math.min(
71 end - start, as.length() - aStart) +
72 aStart);
73 index = newIndex;
74 }
75 }
76 if (size < start) {
77 // Pad attributes
78 attributedStrings.add(new AttributedString(
79 buffer.substring(size, start)));
80 size = start;
81 }
82 if (size < end) {
83 // Add new string
84 int aStart = Math.max(start, size);
85 AttributedString string = new AttributedString(
86 buffer.substring(aStart, end));
87
88 string.addAttribute(attr, value);
89 attributedStrings.add(string);
90 size = end;
91 }
92 }
93 }
94
95 public void formatted(int fieldID, Format.Field attr, Object value,
96 int start, int end, StringBuffer buffer) {
97 formatted(attr, value, start, end, buffer);
98 }
99
100 /**
101 * Returns an <code>AttributedCharacterIterator</code> that can be used
102 * to iterate over the resulting formatted String.
103 *
104 * @pararm string Result of formatting.
105 */
106 public AttributedCharacterIterator getIterator(String string) {
107 // Add the last AttributedCharacterIterator if necessary
108 // assert(size <= string.length());
109 if (string.length() > size) {
110 attributedStrings.add(new AttributedString(
111 string.substring(size)));
112 size = string.length();
113 }
114 int iCount = attributedStrings.size();
115 AttributedCharacterIterator iterators[] = new
116 AttributedCharacterIterator[iCount];
117
118 for (int counter = 0; counter < iCount; counter++) {
119 iterators[counter] = ((AttributedString)attributedStrings.
120 get(counter)).getIterator();
121 }
122 return new AttributedString(iterators).getIterator();
123 }
124}
java/pkg/java/text/ChoiceFormat.class+0−0

Binary file not shown.

java/pkg/java/text/ChoiceFormat.java+619−0
@@ -0,0 +1,619 @@
1/*
2 * Copyright (c) 1996, 2005, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26/*
27 * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
28 * (C) Copyright IBM Corp. 1996 - 1998 - All Rights Reserved
29 *
30 * The original version of this source code and documentation is copyrighted
31 * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
32 * materials are provided under terms of a License Agreement between Taligent
33 * and Sun. This technology is protected by multiple US and International
34 * patents. This notice and attribution to Taligent may not be removed.
35 * Taligent is a registered trademark of Taligent, Inc.
36 *
37 */
38
39package java.text;
40
41import java.io.InvalidObjectException;
42import java.io.IOException;
43import java.io.ObjectInputStream;
44import java.util.Arrays;
45
46/**
47 * A <code>ChoiceFormat</code> allows you to attach a format to a range of numbers.
48 * It is generally used in a <code>MessageFormat</code> for handling plurals.
49 * The choice is specified with an ascending list of doubles, where each item
50 * specifies a half-open interval up to the next item:
51 * <blockquote>
52 * <pre>
53 * X matches j if and only if limit[j] &lt;= X &lt; limit[j+1]
54 * </pre>
55 * </blockquote>
56 * If there is no match, then either the first or last index is used, depending
57 * on whether the number (X) is too low or too high. If the limit array is not
58 * in ascending order, the results of formatting will be incorrect. ChoiceFormat
59 * also accepts <code>&#92;u221E</code> as equivalent to infinity(INF).
60 *
61 * <p>
62 * <strong>Note:</strong>
63 * <code>ChoiceFormat</code> differs from the other <code>Format</code>
64 * classes in that you create a <code>ChoiceFormat</code> object with a
65 * constructor (not with a <code>getInstance</code> style factory
66 * method). The factory methods aren't necessary because <code>ChoiceFormat</code>
67 * doesn't require any complex setup for a given locale. In fact,
68 * <code>ChoiceFormat</code> doesn't implement any locale specific behavior.
69 *
70 * <p>
71 * When creating a <code>ChoiceFormat</code>, you must specify an array of formats
72 * and an array of limits. The length of these arrays must be the same.
73 * For example,
74 * <ul>
75 * <li>
76 * <em>limits</em> = {1,2,3,4,5,6,7}<br>
77 * <em>formats</em> = {"Sun","Mon","Tue","Wed","Thur","Fri","Sat"}
78 * <li>
79 * <em>limits</em> = {0, 1, ChoiceFormat.nextDouble(1)}<br>
80 * <em>formats</em> = {"no files", "one file", "many files"}<br>
81 * (<code>nextDouble</code> can be used to get the next higher double, to
82 * make the half-open interval.)
83 * </ul>
84 *
85 * <p>
86 * Here is a simple example that shows formatting and parsing:
87 * <blockquote>
88 * <pre>
89 * double[] limits = {1,2,3,4,5,6,7};
90 * String[] dayOfWeekNames = {"Sun","Mon","Tue","Wed","Thur","Fri","Sat"};
91 * ChoiceFormat form = new ChoiceFormat(limits, dayOfWeekNames);
92 * ParsePosition status = new ParsePosition(0);
93 * for (double i = 0.0; i &lt;= 8.0; ++i) {
94 * status.setIndex(0);
95 * System.out.println(i + " -&gt; " + form.format(i) + " -&gt; "
96 * + form.parse(form.format(i),status));
97 * }
98 * </pre>
99 * </blockquote>
100 * Here is a more complex example, with a pattern format:
101 * <blockquote>
102 * <pre>
103 * double[] filelimits = {0,1,2};
104 * String[] filepart = {"are no files","is one file","are {2} files"};
105 * ChoiceFormat fileform = new ChoiceFormat(filelimits, filepart);
106 * Format[] testFormats = {fileform, null, NumberFormat.getInstance()};
107 * MessageFormat pattform = new MessageFormat("There {0} on {1}");
108 * pattform.setFormats(testFormats);
109 * Object[] testArgs = {null, "ADisk", null};
110 * for (int i = 0; i &lt; 4; ++i) {
111 * testArgs[0] = new Integer(i);
112 * testArgs[2] = testArgs[0];
113 * System.out.println(pattform.format(testArgs));
114 * }
115 * </pre>
116 * </blockquote>
117 * <p>
118 * Specifying a pattern for ChoiceFormat objects is fairly straightforward.
119 * For example:
120 * <blockquote>
121 * <pre>
122 * ChoiceFormat fmt = new ChoiceFormat(
123 * "-1#is negative| 0#is zero or fraction | 1#is one |1.0&lt;is 1+ |2#is two |2&lt;is more than 2.");
124 * System.out.println("Formatter Pattern : " + fmt.toPattern());
125 *
126 * System.out.println("Format with -INF : " + fmt.format(Double.NEGATIVE_INFINITY));
127 * System.out.println("Format with -1.0 : " + fmt.format(-1.0));
128 * System.out.println("Format with 0 : " + fmt.format(0));
129 * System.out.println("Format with 0.9 : " + fmt.format(0.9));
130 * System.out.println("Format with 1.0 : " + fmt.format(1));
131 * System.out.println("Format with 1.5 : " + fmt.format(1.5));
132 * System.out.println("Format with 2 : " + fmt.format(2));
133 * System.out.println("Format with 2.1 : " + fmt.format(2.1));
134 * System.out.println("Format with NaN : " + fmt.format(Double.NaN));
135 * System.out.println("Format with +INF : " + fmt.format(Double.POSITIVE_INFINITY));
136 * </pre>
137 * </blockquote>
138 * And the output result would be like the following:
139 * <blockquote>
140 * <pre>
141 * Format with -INF : is negative
142 * Format with -1.0 : is negative
143 * Format with 0 : is zero or fraction
144 * Format with 0.9 : is zero or fraction
145 * Format with 1.0 : is one
146 * Format with 1.5 : is 1+
147 * Format with 2 : is two
148 * Format with 2.1 : is more than 2.
149 * Format with NaN : is negative
150 * Format with +INF : is more than 2.
151 * </pre>
152 * </blockquote>
153 *
154 * <h4><a name="synchronization">Synchronization</a></h4>
155 *
156 * <p>
157 * Choice formats are not synchronized.
158 * It is recommended to create separate format instances for each thread.
159 * If multiple threads access a format concurrently, it must be synchronized
160 * externally.
161 *
162 *
163 * @see DecimalFormat
164 * @see MessageFormat
165 * @author Mark Davis
166 */
167public class ChoiceFormat extends NumberFormat {
168
169 // Proclaim serial compatibility with 1.1 FCS
170 private static final long serialVersionUID = 1795184449645032964L;
171
172 /**
173 * Sets the pattern.
174 * @param newPattern See the class description.
175 */
176 public void applyPattern(String newPattern) {
177 StringBuffer[] segments = new StringBuffer[2];
178 for (int i = 0; i < segments.length; ++i) {
179 segments[i] = new StringBuffer();
180 }
181 double[] newChoiceLimits = new double[30];
182 String[] newChoiceFormats = new String[30];
183 int count = 0;
184 int part = 0;
185 double startValue = 0;
186 double oldStartValue = Double.NaN;
187 boolean inQuote = false;
188 for (int i = 0; i < newPattern.length(); ++i) {
189 char ch = newPattern.charAt(i);
190 if (ch=='\'') {
191 // Check for "''" indicating a literal quote
192 if ((i+1)<newPattern.length() && newPattern.charAt(i+1)==ch) {
193 segments[part].append(ch);
194 ++i;
195 } else {
196 inQuote = !inQuote;
197 }
198 } else if (inQuote) {
199 segments[part].append(ch);
200 } else if (ch == '<' || ch == '#' || ch == '\u2264') {
201 if (segments[0].length() == 0) {
202 throw new IllegalArgumentException();
203 }
204 try {
205 String tempBuffer = segments[0].toString();
206 if (tempBuffer.equals("\u221E")) {
207 startValue = Double.POSITIVE_INFINITY;
208 } else if (tempBuffer.equals("-\u221E")) {
209 startValue = Double.NEGATIVE_INFINITY;
210 } else {
211 startValue = Double.valueOf(segments[0].toString()).doubleValue();
212 }
213 } catch (Exception e) {
214 throw new IllegalArgumentException();
215 }
216 if (ch == '<' && startValue != Double.POSITIVE_INFINITY &&
217 startValue != Double.NEGATIVE_INFINITY) {
218 startValue = nextDouble(startValue);
219 }
220 if (startValue <= oldStartValue) {
221 throw new IllegalArgumentException();
222 }
223 segments[0].setLength(0);
224 part = 1;
225 } else if (ch == '|') {
226 if (count == newChoiceLimits.length) {
227 newChoiceLimits = doubleArraySize(newChoiceLimits);
228 newChoiceFormats = doubleArraySize(newChoiceFormats);
229 }
230 newChoiceLimits[count] = startValue;
231 newChoiceFormats[count] = segments[1].toString();
232 ++count;
233 oldStartValue = startValue;
234 segments[1].setLength(0);
235 part = 0;
236 } else {
237 segments[part].append(ch);
238 }
239 }
240 // clean up last one
241 if (part == 1) {
242 if (count == newChoiceLimits.length) {
243 newChoiceLimits = doubleArraySize(newChoiceLimits);
244 newChoiceFormats = doubleArraySize(newChoiceFormats);
245 }
246 newChoiceLimits[count] = startValue;
247 newChoiceFormats[count] = segments[1].toString();
248 ++count;
249 }
250 choiceLimits = new double[count];
251 System.arraycopy(newChoiceLimits, 0, choiceLimits, 0, count);
252 choiceFormats = new String[count];
253 System.arraycopy(newChoiceFormats, 0, choiceFormats, 0, count);
254 }
255
256 /**
257 * Gets the pattern.
258 */
259 public String toPattern() {
260 StringBuffer result = new StringBuffer();
261 for (int i = 0; i < choiceLimits.length; ++i) {
262 if (i != 0) {
263 result.append('|');
264 }
265 // choose based upon which has less precision
266 // approximate that by choosing the closest one to an integer.
267 // could do better, but it's not worth it.
268 double less = previousDouble(choiceLimits[i]);
269 double tryLessOrEqual = Math.abs(Math.IEEEremainder(choiceLimits[i], 1.0d));
270 double tryLess = Math.abs(Math.IEEEremainder(less, 1.0d));
271
272 if (tryLessOrEqual < tryLess) {
273 result.append(""+choiceLimits[i]);
274 result.append('#');
275 } else {
276 if (choiceLimits[i] == Double.POSITIVE_INFINITY) {
277 result.append("\u221E");
278 } else if (choiceLimits[i] == Double.NEGATIVE_INFINITY) {
279 result.append("-\u221E");
280 } else {
281 result.append(""+less);
282 }
283 result.append('<');
284 }
285 // Append choiceFormats[i], using quotes if there are special characters.
286 // Single quotes themselves must be escaped in either case.
287 String text = choiceFormats[i];
288 boolean needQuote = text.indexOf('<') >= 0
289 || text.indexOf('#') >= 0
290 || text.indexOf('\u2264') >= 0
291 || text.indexOf('|') >= 0;
292 if (needQuote) result.append('\'');
293 if (text.indexOf('\'') < 0) result.append(text);
294 else {
295 for (int j=0; j<text.length(); ++j) {
296 char c = text.charAt(j);
297 result.append(c);
298 if (c == '\'') result.append(c);
299 }
300 }
301 if (needQuote) result.append('\'');
302 }
303 return result.toString();
304 }
305
306 /**
307 * Constructs with limits and corresponding formats based on the pattern.
308 * @see #applyPattern
309 */
310 public ChoiceFormat(String newPattern) {
311 applyPattern(newPattern);
312 }
313
314 /**
315 * Constructs with the limits and the corresponding formats.
316 * @see #setChoices
317 */
318 public ChoiceFormat(double[] limits, String[] formats) {
319 setChoices(limits, formats);
320 }
321
322 /**
323 * Set the choices to be used in formatting.
324 * @param limits contains the top value that you want
325 * parsed with that format,and should be in ascending sorted order. When
326 * formatting X, the choice will be the i, where
327 * limit[i] &lt;= X &lt; limit[i+1].
328 * If the limit array is not in ascending order, the results of formatting
329 * will be incorrect.
330 * @param formats are the formats you want to use for each limit.
331 * They can be either Format objects or Strings.
332 * When formatting with object Y,
333 * if the object is a NumberFormat, then ((NumberFormat) Y).format(X)
334 * is called. Otherwise Y.toString() is called.
335 */
336 public void setChoices(double[] limits, String formats[]) {
337 if (limits.length != formats.length) {
338 throw new IllegalArgumentException(
339 "Array and limit arrays must be of the same length.");
340 }
341 choiceLimits = limits;
342 choiceFormats = formats;
343 }
344
345 /**
346 * Get the limits passed in the constructor.
347 * @return the limits.
348 */
349 public double[] getLimits() {
350 return choiceLimits;
351 }
352
353 /**
354 * Get the formats passed in the constructor.
355 * @return the formats.
356 */
357 public Object[] getFormats() {
358 return choiceFormats;
359 }
360
361 // Overrides
362
363 /**
364 * Specialization of format. This method really calls
365 * <code>format(double, StringBuffer, FieldPosition)</code>
366 * thus the range of longs that are supported is only equal to
367 * the range that can be stored by double. This will never be
368 * a practical limitation.
369 */
370 public StringBuffer format(long number, StringBuffer toAppendTo,
371 FieldPosition status) {
372 return format((double)number, toAppendTo, status);
373 }
374
375 /**
376 * Returns pattern with formatted double.
377 * @param number number to be formatted & substituted.
378 * @param toAppendTo where text is appended.
379 * @param status ignore no useful status is returned.
380 */
381 public StringBuffer format(double number, StringBuffer toAppendTo,
382 FieldPosition status) {
383 // find the number
384 int i;
385 for (i = 0; i < choiceLimits.length; ++i) {
386 if (!(number >= choiceLimits[i])) {
387 // same as number < choiceLimits, except catchs NaN
388 break;
389 }
390 }
391 --i;
392 if (i < 0) i = 0;
393 // return either a formatted number, or a string
394 return toAppendTo.append(choiceFormats[i]);
395 }
396
397 /**
398 * Parses a Number from the input text.
399 * @param text the source text.
400 * @param status an input-output parameter. On input, the
401 * status.index field indicates the first character of the
402 * source text that should be parsed. On exit, if no error
403 * occured, status.index is set to the first unparsed character
404 * in the source text. On exit, if an error did occur,
405 * status.index is unchanged and status.errorIndex is set to the
406 * first index of the character that caused the parse to fail.
407 * @return A Number representing the value of the number parsed.
408 */
409 public Number parse(String text, ParsePosition status) {
410 // find the best number (defined as the one with the longest parse)
411 int start = status.index;
412 int furthest = start;
413 double bestNumber = Double.NaN;
414 double tempNumber = 0.0;
415 for (int i = 0; i < choiceFormats.length; ++i) {
416 String tempString = choiceFormats[i];
417 if (text.regionMatches(start, tempString, 0, tempString.length())) {
418 status.index = start + tempString.length();
419 tempNumber = choiceLimits[i];
420 if (status.index > furthest) {
421 furthest = status.index;
422 bestNumber = tempNumber;
423 if (furthest == text.length()) break;
424 }
425 }
426 }
427 status.index = furthest;
428 if (status.index == start) {
429 status.errorIndex = furthest;
430 }
431 return new Double(bestNumber);
432 }
433
434 /**
435 * Finds the least double greater than d.
436 * If NaN, returns same value.
437 * <p>Used to make half-open intervals.
438 * @see #previousDouble
439 */
440 public static final double nextDouble (double d) {
441 return nextDouble(d,true);
442 }
443
444 /**
445 * Finds the greatest double less than d.
446 * If NaN, returns same value.
447 * @see #nextDouble
448 */
449 public static final double previousDouble (double d) {
450 return nextDouble(d,false);
451 }
452
453 /**
454 * Overrides Cloneable
455 */
456 public Object clone()
457 {
458 ChoiceFormat other = (ChoiceFormat) super.clone();
459 // for primitives or immutables, shallow clone is enough
460 other.choiceLimits = (double[]) choiceLimits.clone();
461 other.choiceFormats = (String[]) choiceFormats.clone();
462 return other;
463 }
464
465 /**
466 * Generates a hash code for the message format object.
467 */
468 public int hashCode() {
469 int result = choiceLimits.length;
470 if (choiceFormats.length > 0) {
471 // enough for reasonable distribution
472 result ^= choiceFormats[choiceFormats.length-1].hashCode();
473 }
474 return result;
475 }
476
477 /**
478 * Equality comparision between two
479 */
480 public boolean equals(Object obj) {
481 if (obj == null) return false;
482 if (this == obj) // quick check
483 return true;
484 if (getClass() != obj.getClass())
485 return false;
486 ChoiceFormat other = (ChoiceFormat) obj;
487 return (Arrays.equals(choiceLimits, other.choiceLimits)
488 && Arrays.equals(choiceFormats, other.choiceFormats));
489 }
490
491 /**
492 * After reading an object from the input stream, do a simple verification
493 * to maintain class invariants.
494 * @throws InvalidObjectException if the objects read from the stream is invalid.
495 */
496 private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
497 in.defaultReadObject();
498 if (choiceLimits.length != choiceFormats.length) {
499 throw new InvalidObjectException(
500 "limits and format arrays of different length.");
501 }
502 }
503
504 // ===============privates===========================
505
506 /**
507 * A list of lower bounds for the choices. The formatter will return
508 * <code>choiceFormats[i]</code> if the number being formatted is greater than or equal to
509 * <code>choiceLimits[i]</code> and less than <code>choiceLimits[i+1]</code>.
510 * @serial
511 */
512 private double[] choiceLimits;
513
514 /**
515 * A list of choice strings. The formatter will return
516 * <code>choiceFormats[i]</code> if the number being formatted is greater than or equal to
517 * <code>choiceLimits[i]</code> and less than <code>choiceLimits[i+1]</code>.
518 * @serial
519 */
520 private String[] choiceFormats;
521
522 /*
523 static final long SIGN = 0x8000000000000000L;
524 static final long EXPONENT = 0x7FF0000000000000L;
525 static final long SIGNIFICAND = 0x000FFFFFFFFFFFFFL;
526
527 private static double nextDouble (double d, boolean positive) {
528 if (Double.isNaN(d) || Double.isInfinite(d)) {
529 return d;
530 }
531 long bits = Double.doubleToLongBits(d);
532 long significand = bits & SIGNIFICAND;
533 if (bits < 0) {
534 significand |= (SIGN | EXPONENT);
535 }
536 long exponent = bits & EXPONENT;
537 if (positive) {
538 significand += 1;
539 // FIXME fix overflow & underflow
540 } else {
541 significand -= 1;
542 // FIXME fix overflow & underflow
543 }
544 bits = exponent | (significand & ~EXPONENT);
545 return Double.longBitsToDouble(bits);
546 }
547 */
548
549 static final long SIGN = 0x8000000000000000L;
550 static final long EXPONENT = 0x7FF0000000000000L;
551 static final long POSITIVEINFINITY = 0x7FF0000000000000L;
552
553 /**
554 * Finds the least double greater than d (if positive == true),
555 * or the greatest double less than d (if positive == false).
556 * If NaN, returns same value.
557 *
558 * Does not affect floating-point flags,
559 * provided these member functions do not:
560 * Double.longBitsToDouble(long)
561 * Double.doubleToLongBits(double)
562 * Double.isNaN(double)
563 */
564 public static double nextDouble (double d, boolean positive) {
565
566 /* filter out NaN's */
567 if (Double.isNaN(d)) {
568 return d;
569 }
570
571 /* zero's are also a special case */
572 if (d == 0.0) {
573 double smallestPositiveDouble = Double.longBitsToDouble(1L);
574 if (positive) {
575 return smallestPositiveDouble;
576 } else {
577 return -smallestPositiveDouble;
578 }
579 }
580
581 /* if entering here, d is a nonzero value */
582
583 /* hold all bits in a long for later use */
584 long bits = Double.doubleToLongBits(d);
585
586 /* strip off the sign bit */
587 long magnitude = bits & ~SIGN;
588
589 /* if next double away from zero, increase magnitude */
590 if ((bits > 0) == positive) {
591 if (magnitude != POSITIVEINFINITY) {
592 magnitude += 1;
593 }
594 }
595 /* else decrease magnitude */
596 else {
597 magnitude -= 1;
598 }
599
600 /* restore sign bit and return */
601 long signbit = bits & SIGN;
602 return Double.longBitsToDouble (magnitude | signbit);
603 }
604
605 private static double[] doubleArraySize(double[] array) {
606 int oldSize = array.length;
607 double[] newArray = new double[oldSize * 2];
608 System.arraycopy(array, 0, newArray, 0, oldSize);
609 return newArray;
610 }
611
612 private String[] doubleArraySize(String[] array) {
613 int oldSize = array.length;
614 String[] newArray = new String[oldSize * 2];
615 System.arraycopy(array, 0, newArray, 0, oldSize);
616 return newArray;
617 }
618
619}
java/pkg/java/text/CollationElementIterator.class+0−0

Binary file not shown.

java/pkg/java/text/CollationElementIterator.java+777−0
@@ -0,0 +1,777 @@
1/*
2 * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26/*
27 * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
28 * (C) Copyright IBM Corp. 1996-1998 - All Rights Reserved
29 *
30 * The original version of this source code and documentation is copyrighted
31 * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
32 * materials are provided under terms of a License Agreement between Taligent
33 * and Sun. This technology is protected by multiple US and International
34 * patents. This notice and attribution to Taligent may not be removed.
35 * Taligent is a registered trademark of Taligent, Inc.
36 *
37 */
38
39package java.text;
40
41import java.lang.Character;
42import java.util.Vector;
43import sun.text.CollatorUtilities;
44import sun.text.normalizer.NormalizerBase;
45
46/**
47 * The <code>CollationElementIterator</code> class is used as an iterator
48 * to walk through each character of an international string. Use the iterator
49 * to return the ordering priority of the positioned character. The ordering
50 * priority of a character, which we refer to as a key, defines how a character
51 * is collated in the given collation object.
52 *
53 * <p>
54 * For example, consider the following in Spanish:
55 * <blockquote>
56 * <pre>
57 * "ca" -> the first key is key('c') and second key is key('a').
58 * "cha" -> the first key is key('ch') and second key is key('a').
59 * </pre>
60 * </blockquote>
61 * And in German,
62 * <blockquote>
63 * <pre>
64 * "\u00e4b"-> the first key is key('a'), the second key is key('e'), and
65 * the third key is key('b').
66 * </pre>
67 * </blockquote>
68 * The key of a character is an integer composed of primary order(short),
69 * secondary order(byte), and tertiary order(byte). Java strictly defines
70 * the size and signedness of its primitive data types. Therefore, the static
71 * functions <code>primaryOrder</code>, <code>secondaryOrder</code>, and
72 * <code>tertiaryOrder</code> return <code>int</code>, <code>short</code>,
73 * and <code>short</code> respectively to ensure the correctness of the key
74 * value.
75 *
76 * <p>
77 * Example of the iterator usage,
78 * <blockquote>
79 * <pre>
80 *
81 * String testString = "This is a test";
82 * Collator col = Collator.getInstance();
83 * if (col instanceof RuleBasedCollator) {
84 * RuleBasedCollator ruleBasedCollator = (RuleBasedCollator)col;
85 * CollationElementIterator collationElementIterator = ruleBasedCollator.getCollationElementIterator(testString);
86 * int primaryOrder = CollationElementIterator.primaryOrder(collationElementIterator.next());
87 * :
88 * }
89 * </pre>
90 * </blockquote>
91 *
92 * <p>
93 * <code>CollationElementIterator.next</code> returns the collation order
94 * of the next character. A collation order consists of primary order,
95 * secondary order and tertiary order. The data type of the collation
96 * order is <strong>int</strong>. The first 16 bits of a collation order
97 * is its primary order; the next 8 bits is the secondary order and the
98 * last 8 bits is the tertiary order.
99 *
100 * <p><b>Note:</b> <code>CollationElementIterator</code> is a part of
101 * <code>RuleBasedCollator</code> implementation. It is only usable
102 * with <code>RuleBasedCollator</code> instances.
103 *
104 * @see Collator
105 * @see RuleBasedCollator
106 * @author Helena Shih, Laura Werner, Richard Gillam
107 */
108public final class CollationElementIterator
109{
110 /**
111 * Null order which indicates the end of string is reached by the
112 * cursor.
113 */
114 public final static int NULLORDER = 0xffffffff;
115
116 /**
117 * CollationElementIterator constructor. This takes the source string and
118 * the collation object. The cursor will walk thru the source string based
119 * on the predefined collation rules. If the source string is empty,
120 * NULLORDER will be returned on the calls to next().
121 * @param sourceText the source string.
122 * @param order the collation object.
123 */
124 CollationElementIterator(String sourceText, RuleBasedCollator owner) {
125 this.owner = owner;
126 ordering = owner.getTables();
127 if ( sourceText.length() != 0 ) {
128 NormalizerBase.Mode mode =
129 CollatorUtilities.toNormalizerMode(owner.getDecomposition());
130 text = new NormalizerBase(sourceText, mode);
131 }
132 }
133
134 /**
135 * CollationElementIterator constructor. This takes the source string and
136 * the collation object. The cursor will walk thru the source string based
137 * on the predefined collation rules. If the source string is empty,
138 * NULLORDER will be returned on the calls to next().
139 * @param sourceText the source string.
140 * @param order the collation object.
141 */
142 CollationElementIterator(CharacterIterator sourceText, RuleBasedCollator owner) {
143 this.owner = owner;
144 ordering = owner.getTables();
145 NormalizerBase.Mode mode =
146 CollatorUtilities.toNormalizerMode(owner.getDecomposition());
147 text = new NormalizerBase(sourceText, mode);
148 }
149
150 /**
151 * Resets the cursor to the beginning of the string. The next call
152 * to next() will return the first collation element in the string.
153 */
154 public void reset()
155 {
156 if (text != null) {
157 text.reset();
158 NormalizerBase.Mode mode =
159 CollatorUtilities.toNormalizerMode(owner.getDecomposition());
160 text.setMode(mode);
161 }
162 buffer = null;
163 expIndex = 0;
164 swapOrder = 0;
165 }
166
167 /**
168 * Get the next collation element in the string. <p>This iterator iterates
169 * over a sequence of collation elements that were built from the string.
170 * Because there isn't necessarily a one-to-one mapping from characters to
171 * collation elements, this doesn't mean the same thing as "return the
172 * collation element [or ordering priority] of the next character in the
173 * string".</p>
174 * <p>This function returns the collation element that the iterator is currently
175 * pointing to and then updates the internal pointer to point to the next element.
176 * previous() updates the pointer first and then returns the element. This
177 * means that when you change direction while iterating (i.e., call next() and
178 * then call previous(), or call previous() and then call next()), you'll get
179 * back the same element twice.</p>
180 */
181 public int next()
182 {
183 if (text == null) {
184 return NULLORDER;
185 }
186 NormalizerBase.Mode textMode = text.getMode();
187 // convert the owner's mode to something the Normalizer understands
188 NormalizerBase.Mode ownerMode =
189 CollatorUtilities.toNormalizerMode(owner.getDecomposition());
190 if (textMode != ownerMode) {
191 text.setMode(ownerMode);
192 }
193
194 // if buffer contains any decomposed char values
195 // return their strength orders before continuing in
196 // the Normalizer's CharacterIterator.
197 if (buffer != null) {
198 if (expIndex < buffer.length) {
199 return strengthOrder(buffer[expIndex++]);
200 } else {
201 buffer = null;
202 expIndex = 0;
203 }
204 } else if (swapOrder != 0) {
205 if (Character.isSupplementaryCodePoint(swapOrder)) {
206 char[] chars = Character.toChars(swapOrder);
207 swapOrder = chars[1];
208 return chars[0] << 16;
209 }
210 int order = swapOrder << 16;
211 swapOrder = 0;
212 return order;
213 }
214 int ch = text.next();
215
216 // are we at the end of Normalizer's text?
217 if (ch == NormalizerBase.DONE) {
218 return NULLORDER;
219 }
220
221 int value = ordering.getUnicodeOrder(ch);
222 if (value == RuleBasedCollator.UNMAPPED) {
223 swapOrder = ch;
224 return UNMAPPEDCHARVALUE;
225 }
226 else if (value >= RuleBasedCollator.CONTRACTCHARINDEX) {
227 value = nextContractChar(ch);
228 }
229 if (value >= RuleBasedCollator.EXPANDCHARINDEX) {
230 buffer = ordering.getExpandValueList(value);
231 expIndex = 0;
232 value = buffer[expIndex++];
233 }
234
235 if (ordering.isSEAsianSwapping()) {
236 int consonant;
237 if (isThaiPreVowel(ch)) {
238 consonant = text.next();
239 if (isThaiBaseConsonant(consonant)) {
240 buffer = makeReorderedBuffer(consonant, value, buffer, true);
241 value = buffer[0];
242 expIndex = 1;
243 } else if (consonant != NormalizerBase.DONE) {
244 text.previous();
245 }
246 }
247 if (isLaoPreVowel(ch)) {
248 consonant = text.next();
249 if (isLaoBaseConsonant(consonant)) {
250 buffer = makeReorderedBuffer(consonant, value, buffer, true);
251 value = buffer[0];
252 expIndex = 1;
253 } else if (consonant != NormalizerBase.DONE) {
254 text.previous();
255 }
256 }
257 }
258
259 return strengthOrder(value);
260 }
261
262 /**
263 * Get the previous collation element in the string. <p>This iterator iterates
264 * over a sequence of collation elements that were built from the string.
265 * Because there isn't necessarily a one-to-one mapping from characters to
266 * collation elements, this doesn't mean the same thing as "return the
267 * collation element [or ordering priority] of the previous character in the
268 * string".</p>
269 * <p>This function updates the iterator's internal pointer to point to the
270 * collation element preceding the one it's currently pointing to and then
271 * returns that element, while next() returns the current element and then
272 * updates the pointer. This means that when you change direction while
273 * iterating (i.e., call next() and then call previous(), or call previous()
274 * and then call next()), you'll get back the same element twice.</p>
275 * @since 1.2
276 */
277 public int previous()
278 {
279 if (text == null) {
280 return NULLORDER;
281 }
282 NormalizerBase.Mode textMode = text.getMode();
283 // convert the owner's mode to something the Normalizer understands
284 NormalizerBase.Mode ownerMode =
285 CollatorUtilities.toNormalizerMode(owner.getDecomposition());
286 if (textMode != ownerMode) {
287 text.setMode(ownerMode);
288 }
289 if (buffer != null) {
290 if (expIndex > 0) {
291 return strengthOrder(buffer[--expIndex]);
292 } else {
293 buffer = null;
294 expIndex = 0;
295 }
296 } else if (swapOrder != 0) {
297 if (Character.isSupplementaryCodePoint(swapOrder)) {
298 char[] chars = Character.toChars(swapOrder);
299 swapOrder = chars[1];
300 return chars[0] << 16;
301 }
302 int order = swapOrder << 16;
303 swapOrder = 0;
304 return order;
305 }
306 int ch = text.previous();
307 if (ch == NormalizerBase.DONE) {
308 return NULLORDER;
309 }
310
311 int value = ordering.getUnicodeOrder(ch);
312
313 if (value == RuleBasedCollator.UNMAPPED) {
314 swapOrder = UNMAPPEDCHARVALUE;
315 return ch;
316 } else if (value >= RuleBasedCollator.CONTRACTCHARINDEX) {
317 value = prevContractChar(ch);
318 }
319 if (value >= RuleBasedCollator.EXPANDCHARINDEX) {
320 buffer = ordering.getExpandValueList(value);
321 expIndex = buffer.length;
322 value = buffer[--expIndex];
323 }
324
325 if (ordering.isSEAsianSwapping()) {
326 int vowel;
327 if (isThaiBaseConsonant(ch)) {
328 vowel = text.previous();
329 if (isThaiPreVowel(vowel)) {
330 buffer = makeReorderedBuffer(vowel, value, buffer, false);
331 expIndex = buffer.length - 1;
332 value = buffer[expIndex];
333 } else {
334 text.next();
335 }
336 }
337 if (isLaoBaseConsonant(ch)) {
338 vowel = text.previous();
339 if (isLaoPreVowel(vowel)) {
340 buffer = makeReorderedBuffer(vowel, value, buffer, false);
341 expIndex = buffer.length - 1;
342 value = buffer[expIndex];
343 } else {
344 text.next();
345 }
346 }
347 }
348
349 return strengthOrder(value);
350 }
351
352 /**
353 * Return the primary component of a collation element.
354 * @param order the collation element
355 * @return the element's primary component
356 */
357 public final static int primaryOrder(int order)
358 {
359 order &= RBCollationTables.PRIMARYORDERMASK;
360 return (order >>> RBCollationTables.PRIMARYORDERSHIFT);
361 }
362 /**
363 * Return the secondary component of a collation element.
364 * @param order the collation element
365 * @return the element's secondary component
366 */
367 public final static short secondaryOrder(int order)
368 {
369 order = order & RBCollationTables.SECONDARYORDERMASK;
370 return ((short)(order >> RBCollationTables.SECONDARYORDERSHIFT));
371 }
372 /**
373 * Return the tertiary component of a collation element.
374 * @param order the collation element
375 * @return the element's tertiary component
376 */
377 public final static short tertiaryOrder(int order)
378 {
379 return ((short)(order &= RBCollationTables.TERTIARYORDERMASK));
380 }
381
382 /**
383 * Get the comparison order in the desired strength. Ignore the other
384 * differences.
385 * @param order The order value
386 */
387 final int strengthOrder(int order)
388 {
389 int s = owner.getStrength();
390 if (s == Collator.PRIMARY)
391 {
392 order &= RBCollationTables.PRIMARYDIFFERENCEONLY;
393 } else if (s == Collator.SECONDARY)
394 {
395 order &= RBCollationTables.SECONDARYDIFFERENCEONLY;
396 }
397 return order;
398 }
399
400 /**
401 * Sets the iterator to point to the collation element corresponding to
402 * the specified character (the parameter is a CHARACTER offset in the
403 * original string, not an offset into its corresponding sequence of
404 * collation elements). The value returned by the next call to next()
405 * will be the collation element corresponding to the specified position
406 * in the text. If that position is in the middle of a contracting
407 * character sequence, the result of the next call to next() is the
408 * collation element for that sequence. This means that getOffset()
409 * is not guaranteed to return the same value as was passed to a preceding
410 * call to setOffset().
411 *
412 * @param newOffset The new character offset into the original text.
413 * @since 1.2
414 */
415 public void setOffset(int newOffset)
416 {
417 if (text != null) {
418 if (newOffset < text.getBeginIndex()
419 || newOffset >= text.getEndIndex()) {
420 text.setIndexOnly(newOffset);
421 } else {
422 int c = text.setIndex(newOffset);
423
424 // if the desired character isn't used in a contracting character
425 // sequence, bypass all the backing-up logic-- we're sitting on
426 // the right character already
427 if (ordering.usedInContractSeq(c)) {
428 // walk backwards through the string until we see a character
429 // that DOESN'T participate in a contracting character sequence
430 while (ordering.usedInContractSeq(c)) {
431 c = text.previous();
432 }
433 // now walk forward using this object's next() method until
434 // we pass the starting point and set our current position
435 // to the beginning of the last "character" before or at
436 // our starting position
437 int last = text.getIndex();
438 while (text.getIndex() <= newOffset) {
439 last = text.getIndex();
440 next();
441 }
442 text.setIndexOnly(last);
443 // we don't need this, since last is the last index
444 // that is the starting of the contraction which encompass
445 // newOffset
446 // text.previous();
447 }
448 }
449 }
450 buffer = null;
451 expIndex = 0;
452 swapOrder = 0;
453 }
454
455 /**
456 * Returns the character offset in the original text corresponding to the next
457 * collation element. (That is, getOffset() returns the position in the text
458 * corresponding to the collation element that will be returned by the next
459 * call to next().) This value will always be the index of the FIRST character
460 * corresponding to the collation element (a contracting character sequence is
461 * when two or more characters all correspond to the same collation element).
462 * This means if you do setOffset(x) followed immediately by getOffset(), getOffset()
463 * won't necessarily return x.
464 *
465 * @return The character offset in the original text corresponding to the collation
466 * element that will be returned by the next call to next().
467 * @since 1.2
468 */
469 public int getOffset()
470 {
471 return (text != null) ? text.getIndex() : 0;
472 }
473
474
475 /**
476 * Return the maximum length of any expansion sequences that end
477 * with the specified comparison order.
478 * @param order a collation order returned by previous or next.
479 * @return the maximum length of any expansion sequences ending
480 * with the specified order.
481 * @since 1.2
482 */
483 public int getMaxExpansion(int order)
484 {
485 return ordering.getMaxExpansion(order);
486 }
487
488 /**
489 * Set a new string over which to iterate.
490 *
491 * @param source the new source text
492 * @since 1.2
493 */
494 public void setText(String source)
495 {
496 buffer = null;
497 swapOrder = 0;
498 expIndex = 0;
499 NormalizerBase.Mode mode =
500 CollatorUtilities.toNormalizerMode(owner.getDecomposition());
501 if (text == null) {
502 text = new NormalizerBase(source, mode);
503 } else {
504 text.setMode(mode);
505 text.setText(source);
506 }
507 }
508
509 /**
510 * Set a new string over which to iterate.
511 *
512 * @param source the new source text.
513 * @since 1.2
514 */
515 public void setText(CharacterIterator source)
516 {
517 buffer = null;
518 swapOrder = 0;
519 expIndex = 0;
520 NormalizerBase.Mode mode =
521 CollatorUtilities.toNormalizerMode(owner.getDecomposition());
522 if (text == null) {
523 text = new NormalizerBase(source, mode);
524 } else {
525 text.setMode(mode);
526 text.setText(source);
527 }
528 }
529
530 //============================================================
531 // privates
532 //============================================================
533
534 /**
535 * Determine if a character is a Thai vowel (which sorts after
536 * its base consonant).
537 */
538 private final static boolean isThaiPreVowel(int ch) {
539 return (ch >= 0x0e40) && (ch <= 0x0e44);
540 }
541
542 /**
543 * Determine if a character is a Thai base consonant
544 */
545 private final static boolean isThaiBaseConsonant(int ch) {
546 return (ch >= 0x0e01) && (ch <= 0x0e2e);
547 }
548
549 /**
550 * Determine if a character is a Lao vowel (which sorts after
551 * its base consonant).
552 */
553 private final static boolean isLaoPreVowel(int ch) {
554 return (ch >= 0x0ec0) && (ch <= 0x0ec4);
555 }
556
557 /**
558 * Determine if a character is a Lao base consonant
559 */
560 private final static boolean isLaoBaseConsonant(int ch) {
561 return (ch >= 0x0e81) && (ch <= 0x0eae);
562 }
563
564 /**
565 * This method produces a buffer which contains the collation
566 * elements for the two characters, with colFirst's values preceding
567 * another character's. Presumably, the other character precedes colFirst
568 * in logical order (otherwise you wouldn't need this method would you?).
569 * The assumption is that the other char's value(s) have already been
570 * computed. If this char has a single element it is passed to this
571 * method as lastValue, and lastExpansion is null. If it has an
572 * expansion it is passed in lastExpansion, and colLastValue is ignored.
573 */
574 private int[] makeReorderedBuffer(int colFirst,
575 int lastValue,
576 int[] lastExpansion,
577 boolean forward) {
578
579 int[] result;
580
581 int firstValue = ordering.getUnicodeOrder(colFirst);
582 if (firstValue >= RuleBasedCollator.CONTRACTCHARINDEX) {
583 firstValue = forward? nextContractChar(colFirst) : prevContractChar(colFirst);
584 }
585
586 int[] firstExpansion = null;
587 if (firstValue >= RuleBasedCollator.EXPANDCHARINDEX) {
588 firstExpansion = ordering.getExpandValueList(firstValue);
589 }
590
591 if (!forward) {
592 int temp1 = firstValue;
593 firstValue = lastValue;
594 lastValue = temp1;
595 int[] temp2 = firstExpansion;
596 firstExpansion = lastExpansion;
597 lastExpansion = temp2;
598 }
599
600 if (firstExpansion == null && lastExpansion == null) {
601 result = new int [2];
602 result[0] = firstValue;
603 result[1] = lastValue;
604 }
605 else {
606 int firstLength = firstExpansion==null? 1 : firstExpansion.length;
607 int lastLength = lastExpansion==null? 1 : lastExpansion.length;
608 result = new int[firstLength + lastLength];
609
610 if (firstExpansion == null) {
611 result[0] = firstValue;
612 }
613 else {
614 System.arraycopy(firstExpansion, 0, result, 0, firstLength);
615 }
616
617 if (lastExpansion == null) {
618 result[firstLength] = lastValue;
619 }
620 else {
621 System.arraycopy(lastExpansion, 0, result, firstLength, lastLength);
622 }
623 }
624
625 return result;
626 }
627
628 /**
629 * Check if a comparison order is ignorable.
630 * @return true if a character is ignorable, false otherwise.
631 */
632 final static boolean isIgnorable(int order)
633 {
634 return ((primaryOrder(order) == 0) ? true : false);
635 }
636
637 /**
638 * Get the ordering priority of the next contracting character in the
639 * string.
640 * @param ch the starting character of a contracting character token
641 * @return the next contracting character's ordering. Returns NULLORDER
642 * if the end of string is reached.
643 */
644 private int nextContractChar(int ch)
645 {
646 // First get the ordering of this single character,
647 // which is always the first element in the list
648 Vector list = ordering.getContractValues(ch);
649 EntryPair pair = (EntryPair)list.firstElement();
650 int order = pair.value;
651
652 // find out the length of the longest contracting character sequence in the list.
653 // There's logic in the builder code to make sure the longest sequence is always
654 // the last.
655 pair = (EntryPair)list.lastElement();
656 int maxLength = pair.entryName.length();
657
658 // (the Normalizer is cloned here so that the seeking we do in the next loop
659 // won't affect our real position in the text)
660 NormalizerBase tempText = (NormalizerBase)text.clone();
661
662 // extract the next maxLength characters in the string (we have to do this using the
663 // Normalizer to ensure that our offsets correspond to those the rest of the
664 // iterator is using) and store it in "fragment".
665 tempText.previous();
666 key.setLength(0);
667 int c = tempText.next();
668 while (maxLength > 0 && c != NormalizerBase.DONE) {
669 if (Character.isSupplementaryCodePoint(c)) {
670 key.append(Character.toChars(c));
671 maxLength -= 2;
672 } else {
673 key.append((char)c);
674 --maxLength;
675 }
676 c = tempText.next();
677 }
678 String fragment = key.toString();
679 // now that we have that fragment, iterate through this list looking for the
680 // longest sequence that matches the characters in the actual text. (maxLength
681 // is used here to keep track of the length of the longest sequence)
682 // Upon exit from this loop, maxLength will contain the length of the matching
683 // sequence and order will contain the collation-element value corresponding
684 // to this sequence
685 maxLength = 1;
686 for (int i = list.size() - 1; i > 0; i--) {
687 pair = (EntryPair)list.elementAt(i);
688 if (!pair.fwd)
689 continue;
690
691 if (fragment.startsWith(pair.entryName) && pair.entryName.length()
692 > maxLength) {
693 maxLength = pair.entryName.length();
694 order = pair.value;
695 }
696 }
697
698 // seek our current iteration position to the end of the matching sequence
699 // and return the appropriate collation-element value (if there was no matching
700 // sequence, we're already seeked to the right position and order already contains
701 // the correct collation-element value for the single character)
702 while (maxLength > 1) {
703 c = text.next();
704 maxLength -= Character.charCount(c);
705 }
706 return order;
707 }
708
709 /**
710 * Get the ordering priority of the previous contracting character in the
711 * string.
712 * @param ch the starting character of a contracting character token
713 * @return the next contracting character's ordering. Returns NULLORDER
714 * if the end of string is reached.
715 */
716 private int prevContractChar(int ch)
717 {
718 // This function is identical to nextContractChar(), except that we've
719 // switched things so that the next() and previous() calls on the Normalizer
720 // are switched and so that we skip entry pairs with the fwd flag turned on
721 // rather than off. Notice that we still use append() and startsWith() when
722 // working on the fragment. This is because the entry pairs that are used
723 // in reverse iteration have their names reversed already.
724 Vector list = ordering.getContractValues(ch);
725 EntryPair pair = (EntryPair)list.firstElement();
726 int order = pair.value;
727
728 pair = (EntryPair)list.lastElement();
729 int maxLength = pair.entryName.length();
730
731 NormalizerBase tempText = (NormalizerBase)text.clone();
732
733 tempText.next();
734 key.setLength(0);
735 int c = tempText.previous();
736 while (maxLength > 0 && c != NormalizerBase.DONE) {
737 if (Character.isSupplementaryCodePoint(c)) {
738 key.append(Character.toChars(c));
739 maxLength -= 2;
740 } else {
741 key.append((char)c);
742 --maxLength;
743 }
744 c = tempText.previous();
745 }
746 String fragment = key.toString();
747
748 maxLength = 1;
749 for (int i = list.size() - 1; i > 0; i--) {
750 pair = (EntryPair)list.elementAt(i);
751 if (pair.fwd)
752 continue;
753
754 if (fragment.startsWith(pair.entryName) && pair.entryName.length()
755 > maxLength) {
756 maxLength = pair.entryName.length();
757 order = pair.value;
758 }
759 }
760
761 while (maxLength > 1) {
762 c = text.previous();
763 maxLength -= Character.charCount(c);
764 }
765 return order;
766 }
767
768 final static int UNMAPPEDCHARVALUE = 0x7FFF0000;
769
770 private NormalizerBase text = null;
771 private int[] buffer = null;
772 private int expIndex = 0;
773 private StringBuffer key = new StringBuffer(5);
774 private int swapOrder = 0;
775 private RBCollationTables ordering;
776 private RuleBasedCollator owner;
777}
java/pkg/java/text/CollationKey.class+0−0

Binary file not shown.

java/pkg/java/text/CollationKey.java+145−0
@@ -0,0 +1,145 @@
1/*
2 * Copyright (c) 1997, 2005, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26/*
27 * (C) Copyright Taligent, Inc. 1996 - All Rights Reserved
28 * (C) Copyright IBM Corp. 1996 - All Rights Reserved
29 *
30 * The original version of this source code and documentation is copyrighted
31 * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
32 * materials are provided under terms of a License Agreement between Taligent
33 * and Sun. This technology is protected by multiple US and International
34 * patents. This notice and attribution to Taligent may not be removed.
35 * Taligent is a registered trademark of Taligent, Inc.
36 *
37 */
38
39package java.text;
40
41/**
42 * A <code>CollationKey</code> represents a <code>String</code> under the
43 * rules of a specific <code>Collator</code> object. Comparing two
44 * <code>CollationKey</code>s returns the relative order of the
45 * <code>String</code>s they represent. Using <code>CollationKey</code>s
46 * to compare <code>String</code>s is generally faster than using
47 * <code>Collator.compare</code>. Thus, when the <code>String</code>s
48 * must be compared multiple times, for example when sorting a list
49 * of <code>String</code>s. It's more efficient to use <code>CollationKey</code>s.
50 *
51 * <p>
52 * You can not create <code>CollationKey</code>s directly. Rather,
53 * generate them by calling <code>Collator.getCollationKey</code>.
54 * You can only compare <code>CollationKey</code>s generated from
55 * the same <code>Collator</code> object.
56 *
57 * <p>
58 * Generating a <code>CollationKey</code> for a <code>String</code>
59 * involves examining the entire <code>String</code>
60 * and converting it to series of bits that can be compared bitwise. This
61 * allows fast comparisons once the keys are generated. The cost of generating
62 * keys is recouped in faster comparisons when <code>String</code>s need
63 * to be compared many times. On the other hand, the result of a comparison
64 * is often determined by the first couple of characters of each <code>String</code>.
65 * <code>Collator.compare</code> examines only as many characters as it needs which
66 * allows it to be faster when doing single comparisons.
67 * <p>
68 * The following example shows how <code>CollationKey</code>s might be used
69 * to sort a list of <code>String</code>s.
70 * <blockquote>
71 * <pre>
72 * // Create an array of CollationKeys for the Strings to be sorted.
73 * Collator myCollator = Collator.getInstance();
74 * CollationKey[] keys = new CollationKey[3];
75 * keys[0] = myCollator.getCollationKey("Tom");
76 * keys[1] = myCollator.getCollationKey("Dick");
77 * keys[2] = myCollator.getCollationKey("Harry");
78 * sort( keys );
79 * <br>
80 * //...
81 * <br>
82 * // Inside body of sort routine, compare keys this way
83 * if( keys[i].compareTo( keys[j] ) > 0 )
84 * // swap keys[i] and keys[j]
85 * <br>
86 * //...
87 * <br>
88 * // Finally, when we've returned from sort.
89 * System.out.println( keys[0].getSourceString() );
90 * System.out.println( keys[1].getSourceString() );
91 * System.out.println( keys[2].getSourceString() );
92 * </pre>
93 * </blockquote>
94 *
95 * @see Collator
96 * @see RuleBasedCollator
97 * @author Helena Shih
98 */
99
100public abstract class CollationKey implements Comparable<CollationKey> {
101 /**
102 * Compare this CollationKey to the target CollationKey. The collation rules of the
103 * Collator object which created these keys are applied. <strong>Note:</strong>
104 * CollationKeys created by different Collators can not be compared.
105 * @param target target CollationKey
106 * @return Returns an integer value. Value is less than zero if this is less
107 * than target, value is zero if this and target are equal and value is greater than
108 * zero if this is greater than target.
109 * @see java.text.Collator#compare
110 */
111 abstract public int compareTo(CollationKey target);
112
113 /**
114 * Returns the String that this CollationKey represents.
115 */
116 public String getSourceString() {
117 return source;
118 }
119
120
121 /**
122 * Converts the CollationKey to a sequence of bits. If two CollationKeys
123 * could be legitimately compared, then one could compare the byte arrays
124 * for each of those keys to obtain the same result. Byte arrays are
125 * organized most significant byte first.
126 */
127 abstract public byte[] toByteArray();
128
129
130 /**
131 * CollationKey constructor.
132 *
133 * @param source - the source string.
134 * @exception NullPointerException if <code>source</code> is null.
135 * @since 1.6
136 */
137 protected CollationKey(String source) {
138 if (source==null){
139 throw new NullPointerException();
140 }
141 this.source = source;
142 }
143
144 final private String source;
145}
java/pkg/java/text/CollationRules.class+0−0

Binary file not shown.

java/pkg/java/text/CollationRules.java+274−0
@@ -0,0 +1,274 @@
1/*
2 * Copyright (c) 1996, 2001, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26/*
27 * (C) Copyright Taligent, Inc. 1996,1997 - All Rights Reserved
28 * (C) Copyright IBM Corp. 1996, 1997 - All Rights Reserved
29 *
30 * The original version of this source code and documentation is copyrighted
31 * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
32 * materials are provided under terms of a License Agreement between Taligent
33 * and Sun. This technology is protected by multiple US and International
34 * patents. This notice and attribution to Taligent may not be removed.
35 * Taligent is a registered trademark of Taligent, Inc.
36 *
37 */
38
39package java.text;
40/**
41 * CollationRules contains the default en_US collation rules as a base
42 * for building other collation tables.
43 * <p>Note that decompositions are done before these rules are used,
44 * so they do not have to contain accented characters, such as A-grave.
45 * @see RuleBasedCollator
46 * @see LocaleElements
47 * @author Helena Shih, Mark Davis
48 */
49final class CollationRules {
50 final static String DEFAULTRULES = new String(
51 "" // no FRENCH accent order by default, add in French Delta
52 // IGNORABLES (up to first < character)
53 // COMPLETELY IGNORE format characters
54 + "='\u200B'=\u200C=\u200D=\u200E=\u200F"
55 // Control Characters
56 + "=\u0000 =\u0001 =\u0002 =\u0003 =\u0004" //null, .. eot
57 + "=\u0005 =\u0006 =\u0007 =\u0008 ='\u0009'" //enq, ...
58 + "='\u000b' =\u000e" //vt,, so
59 + "=\u000f ='\u0010' =\u0011 =\u0012 =\u0013" //si, dle, dc1, dc2, dc3
60 + "=\u0014 =\u0015 =\u0016 =\u0017 =\u0018" //dc4, nak, syn, etb, can
61 + "=\u0019 =\u001a =\u001b =\u001c =\u001d" //em, sub, esc, fs, gs
62 + "=\u001e =\u001f =\u007f" //rs, us, del
63 //....then the C1 Latin 1 reserved control codes
64 + "=\u0080 =\u0081 =\u0082 =\u0083 =\u0084 =\u0085"
65 + "=\u0086 =\u0087 =\u0088 =\u0089 =\u008a =\u008b"
66 + "=\u008c =\u008d =\u008e =\u008f =\u0090 =\u0091"
67 + "=\u0092 =\u0093 =\u0094 =\u0095 =\u0096 =\u0097"
68 + "=\u0098 =\u0099 =\u009a =\u009b =\u009c =\u009d"
69 + "=\u009e =\u009f"
70 // IGNORE except for secondary, tertiary difference
71 // Spaces
72 + ";'\u0020';'\u00A0'" // spaces
73 + ";'\u2000';'\u2001';'\u2002';'\u2003';'\u2004'" // spaces
74 + ";'\u2005';'\u2006';'\u2007';'\u2008';'\u2009'" // spaces
75 + ";'\u200A';'\u3000';'\uFEFF'" // spaces
76 + ";'\r' ;'\t' ;'\n';'\f';'\u000b'" // whitespace
77
78 // Non-spacing accents
79
80 + ";\u0301" // non-spacing acute accent
81 + ";\u0300" // non-spacing grave accent
82 + ";\u0306" // non-spacing breve accent
83 + ";\u0302" // non-spacing circumflex accent
84 + ";\u030c" // non-spacing caron/hacek accent
85 + ";\u030a" // non-spacing ring above accent
86 + ";\u030d" // non-spacing vertical line above
87 + ";\u0308" // non-spacing diaeresis accent
88 + ";\u030b" // non-spacing double acute accent
89 + ";\u0303" // non-spacing tilde accent
90 + ";\u0307" // non-spacing dot above/overdot accent
91 + ";\u0304" // non-spacing macron accent
92 + ";\u0337" // non-spacing short slash overlay (overstruck diacritic)
93 + ";\u0327" // non-spacing cedilla accent
94 + ";\u0328" // non-spacing ogonek accent
95 + ";\u0323" // non-spacing dot-below/underdot accent
96 + ";\u0332" // non-spacing underscore/underline accent
97 // with the rest of the general diacritical marks in binary order
98 + ";\u0305" // non-spacing overscore/overline
99 + ";\u0309" // non-spacing hook above
100 + ";\u030e" // non-spacing double vertical line above
101 + ";\u030f" // non-spacing double grave
102 + ";\u0310" // non-spacing chandrabindu
103 + ";\u0311" // non-spacing inverted breve
104 + ";\u0312" // non-spacing turned comma above/cedilla above
105 + ";\u0313" // non-spacing comma above
106 + ";\u0314" // non-spacing reversed comma above
107 + ";\u0315" // non-spacing comma above right
108 + ";\u0316" // non-spacing grave below
109 + ";\u0317" // non-spacing acute below
110 + ";\u0318" // non-spacing left tack below
111 + ";\u0319" // non-spacing tack below
112 + ";\u031a" // non-spacing left angle above
113 + ";\u031b" // non-spacing horn
114 + ";\u031c" // non-spacing left half ring below
115 + ";\u031d" // non-spacing up tack below
116 + ";\u031e" // non-spacing down tack below
117 + ";\u031f" // non-spacing plus sign below
118 + ";\u0320" // non-spacing minus sign below
119 + ";\u0321" // non-spacing palatalized hook below
120 + ";\u0322" // non-spacing retroflex hook below
121 + ";\u0324" // non-spacing double dot below
122 + ";\u0325" // non-spacing ring below
123 + ";\u0326" // non-spacing comma below
124 + ";\u0329" // non-spacing vertical line below
125 + ";\u032a" // non-spacing bridge below
126 + ";\u032b" // non-spacing inverted double arch below
127 + ";\u032c" // non-spacing hacek below
128 + ";\u032d" // non-spacing circumflex below
129 + ";\u032e" // non-spacing breve below
130 + ";\u032f" // non-spacing inverted breve below
131 + ";\u0330" // non-spacing tilde below
132 + ";\u0331" // non-spacing macron below
133 + ";\u0333" // non-spacing double underscore
134 + ";\u0334" // non-spacing tilde overlay
135 + ";\u0335" // non-spacing short bar overlay
136 + ";\u0336" // non-spacing long bar overlay
137 + ";\u0338" // non-spacing long slash overlay
138 + ";\u0339" // non-spacing right half ring below
139 + ";\u033a" // non-spacing inverted bridge below
140 + ";\u033b" // non-spacing square below
141 + ";\u033c" // non-spacing seagull below
142 + ";\u033d" // non-spacing x above
143 + ";\u033e" // non-spacing vertical tilde
144 + ";\u033f" // non-spacing double overscore
145 //+ ";\u0340" // non-spacing grave tone mark == \u0300
146 //+ ";\u0341" // non-spacing acute tone mark == \u0301
147 + ";\u0342;"
148 //+ "\u0343;" // == \u0313
149 + "\u0344;\u0345;\u0360;\u0361" // newer
150 + ";\u0483;\u0484;\u0485;\u0486" // Cyrillic accents
151
152 + ";\u20D0;\u20D1;\u20D2" // symbol accents
153 + ";\u20D3;\u20D4;\u20D5" // symbol accents
154 + ";\u20D6;\u20D7;\u20D8" // symbol accents
155 + ";\u20D9;\u20DA;\u20DB" // symbol accents
156 + ";\u20DC;\u20DD;\u20DE" // symbol accents
157 + ";\u20DF;\u20E0;\u20E1" // symbol accents
158
159 + ",'\u002D';\u00AD" // dashes
160 + ";\u2010;\u2011;\u2012" // dashes
161 + ";\u2013;\u2014;\u2015" // dashes
162 + ";\u2212" // dashes
163
164 // other punctuation
165
166 + "<'\u005f'" // underline/underscore (spacing)
167 + "<\u00af" // overline or macron (spacing)
168 + "<'\u002c'" // comma (spacing)
169 + "<'\u003b'" // semicolon
170 + "<'\u003a'" // colon
171 + "<'\u0021'" // exclamation point
172 + "<\u00a1" // inverted exclamation point
173 + "<'\u003f'" // question mark
174 + "<\u00bf" // inverted question mark
175 + "<'\u002f'" // slash
176 + "<'\u002e'" // period/full stop
177 + "<\u00b4" // acute accent (spacing)
178 + "<'\u0060'" // grave accent (spacing)
179 + "<'\u005e'" // circumflex accent (spacing)
180 + "<\u00a8" // diaresis/umlaut accent (spacing)
181 + "<'\u007e'" // tilde accent (spacing)
182 + "<\u00b7" // middle dot (spacing)
183 + "<\u00b8" // cedilla accent (spacing)
184 + "<'\u0027'" // apostrophe
185 + "<'\"'" // quotation marks
186 + "<\u00ab" // left angle quotes
187 + "<\u00bb" // right angle quotes
188 + "<'\u0028'" // left parenthesis
189 + "<'\u0029'" // right parenthesis
190 + "<'\u005b'" // left bracket
191 + "<'\u005d'" // right bracket
192 + "<'\u007b'" // left brace
193 + "<'\u007d'" // right brace
194 + "<\u00a7" // section symbol
195 + "<\u00b6" // paragraph symbol
196 + "<\u00a9" // copyright symbol
197 + "<\u00ae" // registered trademark symbol
198 + "<'\u0040'" // at sign
199 + "<\u00a4" // international currency symbol
200 + "<\u0e3f" // baht sign
201 + "<\u00a2" // cent sign
202 + "<\u20a1" // colon sign
203 + "<\u20a2" // cruzeiro sign
204 + "<'\u0024'" // dollar sign
205 + "<\u20ab" // dong sign
206 + "<\u20ac" // euro sign
207 + "<\u20a3" // franc sign
208 + "<\u20a4" // lira sign
209 + "<\u20a5" // mill sign
210 + "<\u20a6" // naira sign
211 + "<\u20a7" // peseta sign
212 + "<\u00a3" // pound-sterling sign
213 + "<\u20a8" // rupee sign
214 + "<\u20aa" // new shekel sign
215 + "<\u20a9" // won sign
216 + "<\u00a5" // yen sign
217 + "<'\u002a'" // asterisk
218 + "<'\\'" // backslash
219 + "<'\u0026'" // ampersand
220 + "<'\u0023'" // number sign
221 + "<'\u0025'" // percent sign
222 + "<'\u002b'" // plus sign
223 + "<\u00b1" // plus-or-minus sign
224 + "<\u00f7" // divide sign
225 + "<\u00d7" // multiply sign
226 + "<'\u003c'" // less-than sign
227 + "<'\u003d'" // equal sign
228 + "<'\u003e'" // greater-than sign
229 + "<\u00ac" // end of line symbol/logical NOT symbol
230 + "<'\u007c'" // vertical line/logical OR symbol
231 + "<\u00a6" // broken vertical line
232 + "<\u00b0" // degree symbol
233 + "<\u00b5" // micro symbol
234
235 // NUMERICS
236
237 + "<0<1<2<3<4<5<6<7<8<9"
238 + "<\u00bc<\u00bd<\u00be" // 1/4,1/2,3/4 fractions
239
240 // NON-IGNORABLES
241 + "<a,A"
242 + "<b,B"
243 + "<c,C"
244 + "<d,D"
245 + "<\u00F0,\u00D0" // eth
246 + "<e,E"
247 + "<f,F"
248 + "<g,G"
249 + "<h,H"
250 + "<i,I"
251 + "<j,J"
252 + "<k,K"
253 + "<l,L"
254 + "<m,M"
255 + "<n,N"
256 + "<o,O"
257 + "<p,P"
258 + "<q,Q"
259 + "<r,R"
260 + "<s, S & SS,\u00DF" // s-zet
261 + "<t,T"
262 + "& TH, \u00DE &TH, \u00FE " // thorn
263 + "<u,U"
264 + "<v,V"
265 + "<w,W"
266 + "<x,X"
267 + "<y,Y"
268 + "<z,Z"
269 + "&AE,\u00C6" // ae & AE ligature
270 + "&AE,\u00E6"
271 + "&OE,\u0152" // oe & OE ligature
272 + "&OE,\u0153"
273 );
274}
java/pkg/java/text/Collator$CollatorGetter.class+0−0

Binary file not shown.

java/pkg/java/text/Collator.class+0−0

Binary file not shown.

java/pkg/java/text/Collator.java+553−0
@@ -0,0 +1,553 @@
1/*
2 * Copyright (c) 1997, 2006, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26/*
27 * (C) Copyright Taligent, Inc. 1996-1998 - All Rights Reserved
28 * (C) Copyright IBM Corp. 1996-1998 - All Rights Reserved
29 *
30 * The original version of this source code and documentation is copyrighted
31 * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
32 * materials are provided under terms of a License Agreement between Taligent
33 * and Sun. This technology is protected by multiple US and International
34 * patents. This notice and attribution to Taligent may not be removed.
35 * Taligent is a registered trademark of Taligent, Inc.
36 *
37 */
38
39package java.text;
40
41import java.text.spi.CollatorProvider;
42import java.util.Locale;
43import java.util.MissingResourceException;
44import java.util.ResourceBundle;
45import java.util.spi.LocaleServiceProvider;
46import sun.misc.SoftCache;
47import sun.util.resources.LocaleData;
48import sun.util.LocaleServiceProviderPool;
49
50
51/**
52 * The <code>Collator</code> class performs locale-sensitive
53 * <code>String</code> comparison. You use this class to build
54 * searching and sorting routines for natural language text.
55 *
56 * <p>
57 * <code>Collator</code> is an abstract base class. Subclasses
58 * implement specific collation strategies. One subclass,
59 * <code>RuleBasedCollator</code>, is currently provided with
60 * the Java Platform and is applicable to a wide set of languages. Other
61 * subclasses may be created to handle more specialized needs.
62 *
63 * <p>
64 * Like other locale-sensitive classes, you can use the static
65 * factory method, <code>getInstance</code>, to obtain the appropriate
66 * <code>Collator</code> object for a given locale. You will only need
67 * to look at the subclasses of <code>Collator</code> if you need
68 * to understand the details of a particular collation strategy or
69 * if you need to modify that strategy.
70 *
71 * <p>
72 * The following example shows how to compare two strings using
73 * the <code>Collator</code> for the default locale.
74 * <blockquote>
75 * <pre>
76 * // Compare two strings in the default locale
77 * Collator myCollator = Collator.getInstance();
78 * if( myCollator.compare("abc", "ABC") < 0 )
79 * System.out.println("abc is less than ABC");
80 * else
81 * System.out.println("abc is greater than or equal to ABC");
82 * </pre>
83 * </blockquote>
84 *
85 * <p>
86 * You can set a <code>Collator</code>'s <em>strength</em> property
87 * to determine the level of difference considered significant in
88 * comparisons. Four strengths are provided: <code>PRIMARY</code>,
89 * <code>SECONDARY</code>, <code>TERTIARY</code>, and <code>IDENTICAL</code>.
90 * The exact assignment of strengths to language features is
91 * locale dependant. For example, in Czech, "e" and "f" are considered
92 * primary differences, while "e" and "&#283;" are secondary differences,
93 * "e" and "E" are tertiary differences and "e" and "e" are identical.
94 * The following shows how both case and accents could be ignored for
95 * US English.
96 * <blockquote>
97 * <pre>
98 * //Get the Collator for US English and set its strength to PRIMARY
99 * Collator usCollator = Collator.getInstance(Locale.US);
100 * usCollator.setStrength(Collator.PRIMARY);
101 * if( usCollator.compare("abc", "ABC") == 0 ) {
102 * System.out.println("Strings are equivalent");
103 * }
104 * </pre>
105 * </blockquote>
106 * <p>
107 * For comparing <code>String</code>s exactly once, the <code>compare</code>
108 * method provides the best performance. When sorting a list of
109 * <code>String</code>s however, it is generally necessary to compare each
110 * <code>String</code> multiple times. In this case, <code>CollationKey</code>s
111 * provide better performance. The <code>CollationKey</code> class converts
112 * a <code>String</code> to a series of bits that can be compared bitwise
113 * against other <code>CollationKey</code>s. A <code>CollationKey</code> is
114 * created by a <code>Collator</code> object for a given <code>String</code>.
115 * <br>
116 * <strong>Note:</strong> <code>CollationKey</code>s from different
117 * <code>Collator</code>s can not be compared. See the class description
118 * for {@link CollationKey}
119 * for an example using <code>CollationKey</code>s.
120 *
121 * @see RuleBasedCollator
122 * @see CollationKey
123 * @see CollationElementIterator
124 * @see Locale
125 * @author Helena Shih, Laura Werner, Richard Gillam
126 */
127
128public abstract class Collator
129 implements java.util.Comparator<Object>, Cloneable
130{
131 /**
132 * Collator strength value. When set, only PRIMARY differences are
133 * considered significant during comparison. The assignment of strengths
134 * to language features is locale dependant. A common example is for
135 * different base letters ("a" vs "b") to be considered a PRIMARY difference.
136 * @see java.text.Collator#setStrength
137 * @see java.text.Collator#getStrength
138 */
139 public final static int PRIMARY = 0;
140 /**
141 * Collator strength value. When set, only SECONDARY and above differences are
142 * considered significant during comparison. The assignment of strengths
143 * to language features is locale dependant. A common example is for
144 * different accented forms of the same base letter ("a" vs "\u00E4") to be
145 * considered a SECONDARY difference.
146 * @see java.text.Collator#setStrength
147 * @see java.text.Collator#getStrength
148 */
149 public final static int SECONDARY = 1;
150 /**
151 * Collator strength value. When set, only TERTIARY and above differences are
152 * considered significant during comparison. The assignment of strengths
153 * to language features is locale dependant. A common example is for
154 * case differences ("a" vs "A") to be considered a TERTIARY difference.
155 * @see java.text.Collator#setStrength
156 * @see java.text.Collator#getStrength
157 */
158 public final static int TERTIARY = 2;
159
160 /**
161 * Collator strength value. When set, all differences are
162 * considered significant during comparison. The assignment of strengths
163 * to language features is locale dependant. A common example is for control
164 * characters ("&#092;u0001" vs "&#092;u0002") to be considered equal at the
165 * PRIMARY, SECONDARY, and TERTIARY levels but different at the IDENTICAL
166 * level. Additionally, differences between pre-composed accents such as
167 * "&#092;u00C0" (A-grave) and combining accents such as "A&#092;u0300"
168 * (A, combining-grave) will be considered significant at the IDENTICAL
169 * level if decomposition is set to NO_DECOMPOSITION.
170 */
171 public final static int IDENTICAL = 3;
172
173 /**
174 * Decomposition mode value. With NO_DECOMPOSITION
175 * set, accented characters will not be decomposed for collation. This
176 * is the default setting and provides the fastest collation but
177 * will only produce correct results for languages that do not use accents.
178 * @see java.text.Collator#getDecomposition
179 * @see java.text.Collator#setDecomposition
180 */
181 public final static int NO_DECOMPOSITION = 0;
182
183 /**
184 * Decomposition mode value. With CANONICAL_DECOMPOSITION
185 * set, characters that are canonical variants according to Unicode
186 * standard will be decomposed for collation. This should be used to get
187 * correct collation of accented characters.
188 * <p>
189 * CANONICAL_DECOMPOSITION corresponds to Normalization Form D as
190 * described in
191 * <a href="http://www.unicode.org/unicode/reports/tr15/tr15-23.html">Unicode
192 * Technical Report #15</a>.
193 * @see java.text.Collator#getDecomposition
194 * @see java.text.Collator#setDecomposition
195 */
196 public final static int CANONICAL_DECOMPOSITION = 1;
197
198 /**
199 * Decomposition mode value. With FULL_DECOMPOSITION
200 * set, both Unicode canonical variants and Unicode compatibility variants
201 * will be decomposed for collation. This causes not only accented
202 * characters to be collated, but also characters that have special formats
203 * to be collated with their norminal form. For example, the half-width and
204 * full-width ASCII and Katakana characters are then collated together.
205 * FULL_DECOMPOSITION is the most complete and therefore the slowest
206 * decomposition mode.
207 * <p>
208 * FULL_DECOMPOSITION corresponds to Normalization Form KD as
209 * described in
210 * <a href="http://www.unicode.org/unicode/reports/tr15/tr15-23.html">Unicode
211 * Technical Report #15</a>.
212 * @see java.text.Collator#getDecomposition
213 * @see java.text.Collator#setDecomposition
214 */
215 public final static int FULL_DECOMPOSITION = 2;
216
217 /**
218 * Gets the Collator for the current default locale.
219 * The default locale is determined by java.util.Locale.getDefault.
220 * @return the Collator for the default locale.(for example, en_US)
221 * @see java.util.Locale#getDefault
222 */
223 public static synchronized Collator getInstance() {
224 return getInstance(Locale.getDefault());
225 }
226
227 /**
228 * Gets the Collator for the desired locale.
229 * @param desiredLocale the desired locale.
230 * @return the Collator for the desired locale.
231 * @see java.util.Locale
232 * @see java.util.ResourceBundle
233 */
234 public static synchronized
235 Collator getInstance(Locale desiredLocale)
236 {
237 Collator result = (Collator) cache.get(desiredLocale);
238 if (result != null) {
239 return (Collator)result.clone(); // make the world safe
240 }
241
242 // Check whether a provider can provide an implementation that's closer
243 // to the requested locale than what the Java runtime itself can provide.
244 LocaleServiceProviderPool pool =
245 LocaleServiceProviderPool.getPool(CollatorProvider.class);
246 if (pool.hasProviders()) {
247 Collator providersInstance = pool.getLocalizedObject(
248 CollatorGetter.INSTANCE,
249 desiredLocale,
250 desiredLocale);
251 if (providersInstance != null) {
252 return providersInstance;
253 }
254 }
255
256 // Load the resource of the desired locale from resource
257 // manager.
258 String colString = "";
259 try {
260 ResourceBundle resource = LocaleData.getCollationData(desiredLocale);
261
262 colString = resource.getString("Rule");
263 } catch (MissingResourceException e) {
264 // Use default values
265 }
266 try
267 {
268 result = new RuleBasedCollator( CollationRules.DEFAULTRULES +
269 colString,
270 CANONICAL_DECOMPOSITION );
271 }
272 catch(ParseException foo)
273 {
274 // predefined tables should contain correct grammar
275 try {
276 result = new RuleBasedCollator( CollationRules.DEFAULTRULES );
277 } catch (ParseException bar) {
278 // do nothing
279 }
280 }
281 // Now that RuleBasedCollator adds expansions for pre-composed characters
282 // into their decomposed equivalents, the default collators don't need
283 // to have decomposition turned on. Laura, 5/5/98, bug 4114077
284 result.setDecomposition(NO_DECOMPOSITION);
285
286 cache.put(desiredLocale,result);
287 return (Collator)result.clone();
288 }
289
290 /**
291 * Compares the source string to the target string according to the
292 * collation rules for this Collator. Returns an integer less than,
293 * equal to or greater than zero depending on whether the source String is
294 * less than, equal to or greater than the target string. See the Collator
295 * class description for an example of use.
296 * <p>
297 * For a one time comparison, this method has the best performance. If a
298 * given String will be involved in multiple comparisons, CollationKey.compareTo
299 * has the best performance. See the Collator class description for an example
300 * using CollationKeys.
301 * @param source the source string.
302 * @param target the target string.
303 * @return Returns an integer value. Value is less than zero if source is less than
304 * target, value is zero if source and target are equal, value is greater than zero
305 * if source is greater than target.
306 * @see java.text.CollationKey
307 * @see java.text.Collator#getCollationKey
308 */
309 public abstract int compare(String source, String target);
310
311 /**
312 * Compares its two arguments for order. Returns a negative integer,
313 * zero, or a positive integer as the first argument is less than, equal
314 * to, or greater than the second.
315 * <p>
316 * This implementation merely returns
317 * <code> compare((String)o1, (String)o2) </code>.
318 *
319 * @return a negative integer, zero, or a positive integer as the
320 * first argument is less than, equal to, or greater than the
321 * second.
322 * @exception ClassCastException the arguments cannot be cast to Strings.
323 * @see java.util.Comparator
324 * @since 1.2
325 */
326 public int compare(Object o1, Object o2) {
327 return compare((String)o1, (String)o2);
328 }
329
330 /**
331 * Transforms the String into a series of bits that can be compared bitwise
332 * to other CollationKeys. CollationKeys provide better performance than
333 * Collator.compare when Strings are involved in multiple comparisons.
334 * See the Collator class description for an example using CollationKeys.
335 * @param source the string to be transformed into a collation key.
336 * @return the CollationKey for the given String based on this Collator's collation
337 * rules. If the source String is null, a null CollationKey is returned.
338 * @see java.text.CollationKey
339 * @see java.text.Collator#compare
340 */
341 public abstract CollationKey getCollationKey(String source);
342
343 /**
344 * Convenience method for comparing the equality of two strings based on
345 * this Collator's collation rules.
346 * @param source the source string to be compared with.
347 * @param target the target string to be compared with.
348 * @return true if the strings are equal according to the collation
349 * rules. false, otherwise.
350 * @see java.text.Collator#compare
351 */
352 public boolean equals(String source, String target)
353 {
354 return (compare(source, target) == Collator.EQUAL);
355 }
356
357 /**
358 * Returns this Collator's strength property. The strength property determines
359 * the minimum level of difference considered significant during comparison.
360 * See the Collator class description for an example of use.
361 * @return this Collator's current strength property.
362 * @see java.text.Collator#setStrength
363 * @see java.text.Collator#PRIMARY
364 * @see java.text.Collator#SECONDARY
365 * @see java.text.Collator#TERTIARY
366 * @see java.text.Collator#IDENTICAL
367 */
368 public synchronized int getStrength()
369 {
370 return strength;
371 }
372
373 /**
374 * Sets this Collator's strength property. The strength property determines
375 * the minimum level of difference considered significant during comparison.
376 * See the Collator class description for an example of use.
377 * @param newStrength the new strength value.
378 * @see java.text.Collator#getStrength
379 * @see java.text.Collator#PRIMARY
380 * @see java.text.Collator#SECONDARY
381 * @see java.text.Collator#TERTIARY
382 * @see java.text.Collator#IDENTICAL
383 * @exception IllegalArgumentException If the new strength value is not one of
384 * PRIMARY, SECONDARY, TERTIARY or IDENTICAL.
385 */
386 public synchronized void setStrength(int newStrength) {
387 if ((newStrength != PRIMARY) &&
388 (newStrength != SECONDARY) &&
389 (newStrength != TERTIARY) &&
390 (newStrength != IDENTICAL))
391 throw new IllegalArgumentException("Incorrect comparison level.");
392 strength = newStrength;
393 }
394
395 /**
396 * Get the decomposition mode of this Collator. Decomposition mode
397 * determines how Unicode composed characters are handled. Adjusting
398 * decomposition mode allows the user to select between faster and more
399 * complete collation behavior.
400 * <p>The three values for decomposition mode are:
401 * <UL>
402 * <LI>NO_DECOMPOSITION,
403 * <LI>CANONICAL_DECOMPOSITION
404 * <LI>FULL_DECOMPOSITION.
405 * </UL>
406 * See the documentation for these three constants for a description
407 * of their meaning.
408 * @return the decomposition mode
409 * @see java.text.Collator#setDecomposition
410 * @see java.text.Collator#NO_DECOMPOSITION
411 * @see java.text.Collator#CANONICAL_DECOMPOSITION
412 * @see java.text.Collator#FULL_DECOMPOSITION
413 */
414 public synchronized int getDecomposition()
415 {
416 return decmp;
417 }
418 /**
419 * Set the decomposition mode of this Collator. See getDecomposition
420 * for a description of decomposition mode.
421 * @param decompositionMode the new decomposition mode.
422 * @see java.text.Collator#getDecomposition
423 * @see java.text.Collator#NO_DECOMPOSITION
424 * @see java.text.Collator#CANONICAL_DECOMPOSITION
425 * @see java.text.Collator#FULL_DECOMPOSITION
426 * @exception IllegalArgumentException If the given value is not a valid decomposition
427 * mode.
428 */
429 public synchronized void setDecomposition(int decompositionMode) {
430 if ((decompositionMode != NO_DECOMPOSITION) &&
431 (decompositionMode != CANONICAL_DECOMPOSITION) &&
432 (decompositionMode != FULL_DECOMPOSITION))
433 throw new IllegalArgumentException("Wrong decomposition mode.");
434 decmp = decompositionMode;
435 }
436
437 /**
438 * Returns an array of all locales for which the
439 * <code>getInstance</code> methods of this class can return
440 * localized instances.
441 * The returned array represents the union of locales supported
442 * by the Java runtime and by installed
443 * {@link java.text.spi.CollatorProvider CollatorProvider} implementations.
444 * It must contain at least a Locale instance equal to
445 * {@link java.util.Locale#US Locale.US}.
446 *
447 * @return An array of locales for which localized
448 * <code>Collator</code> instances are available.
449 */
450 public static synchronized Locale[] getAvailableLocales() {
451 LocaleServiceProviderPool pool =
452 LocaleServiceProviderPool.getPool(CollatorProvider.class);
453 return pool.getAvailableLocales();
454 }
455
456 /**
457 * Overrides Cloneable
458 */
459 public Object clone()
460 {
461 try {
462 return (Collator)super.clone();
463 } catch (CloneNotSupportedException e) {
464 throw new InternalError();
465 }
466 }
467
468 /**
469 * Compares the equality of two Collators.
470 * @param that the Collator to be compared with this.
471 * @return true if this Collator is the same as that Collator;
472 * false otherwise.
473 */
474 public boolean equals(Object that)
475 {
476 if (this == that) return true;
477 if (that == null) return false;
478 if (getClass() != that.getClass()) return false;
479 Collator other = (Collator) that;
480 return ((strength == other.strength) &&
481 (decmp == other.decmp));
482 }
483
484 /**
485 * Generates the hash code for this Collator.
486 */
487 abstract public int hashCode();
488
489 /**
490 * Default constructor. This constructor is
491 * protected so subclasses can get access to it. Users typically create
492 * a Collator sub-class by calling the factory method getInstance.
493 * @see java.text.Collator#getInstance
494 */
495 protected Collator()
496 {
497 strength = TERTIARY;
498 decmp = CANONICAL_DECOMPOSITION;
499 }
500
501 private int strength = 0;
502 private int decmp = 0;
503 private static SoftCache cache = new SoftCache();
504
505 //
506 // FIXME: These three constants should be removed.
507 //
508 /**
509 * LESS is returned if source string is compared to be less than target
510 * string in the compare() method.
511 * @see java.text.Collator#compare
512 */
513 final static int LESS = -1;
514 /**
515 * EQUAL is returned if source string is compared to be equal to target
516 * string in the compare() method.
517 * @see java.text.Collator#compare
518 */
519 final static int EQUAL = 0;
520 /**
521 * GREATER is returned if source string is compared to be greater than
522 * target string in the compare() method.
523 * @see java.text.Collator#compare
524 */
525 final static int GREATER = 1;
526
527 /**
528 * Obtains a Collator instance from a CollatorProvider
529 * implementation.
530 */
531 private static class CollatorGetter
532 implements LocaleServiceProviderPool.LocalizedObjectGetter<CollatorProvider, Collator> {
533 private static final CollatorGetter INSTANCE = new CollatorGetter();
534
535 public Collator getObject(CollatorProvider collatorProvider,
536 Locale locale,
537 String key,
538 Object... params) {
539 assert params.length == 1;
540 Collator result = collatorProvider.getInstance(locale);
541 if (result != null) {
542 // put this Collator instance in the cache for two locales, one
543 // is for the desired locale, and the other is for the actual
544 // locale where the provider is found, which may be a fall back locale.
545 cache.put((Locale)params[0], result);
546 cache.put(locale, result);
547 return (Collator)result.clone();
548 }
549
550 return null;
551 }
552 }
553 }
java/pkg/java/text/DateFormat$DateFormatGetter.class+0−0

Binary file not shown.

java/pkg/java/text/DateFormat$Field.class+0−0

Binary file not shown.

java/pkg/java/text/DateFormat.class+0−0

Binary file not shown.

java/pkg/java/text/DateFormat.java+1030−0
@@ -0,0 +1,1030 @@
1/*
2 * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26/*
27 * (C) Copyright Taligent, Inc. 1996 - All Rights Reserved
28 * (C) Copyright IBM Corp. 1996 - All Rights Reserved
29 *
30 * The original version of this source code and documentation is copyrighted
31 * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
32 * materials are provided under terms of a License Agreement between Taligent
33 * and Sun. This technology is protected by multiple US and International
34 * patents. This notice and attribution to Taligent may not be removed.
35 * Taligent is a registered trademark of Taligent, Inc.
36 *
37 */
38
39package java.text;
40
41import java.io.InvalidObjectException;
42import java.text.spi.DateFormatProvider;
43import java.util.Calendar;
44import java.util.Date;
45import java.util.GregorianCalendar;
46import java.util.HashMap;
47import java.util.Locale;
48import java.util.Map;
49import java.util.MissingResourceException;
50import java.util.ResourceBundle;
51import java.util.TimeZone;
52import java.util.spi.LocaleServiceProvider;
53import sun.util.LocaleServiceProviderPool;
54
55/**
56 * {@code DateFormat} is an abstract class for date/time formatting subclasses which
57 * formats and parses dates or time in a language-independent manner.
58 * The date/time formatting subclass, such as {@link SimpleDateFormat}, allows for
59 * formatting (i.e., date -> text), parsing (text -> date), and
60 * normalization. The date is represented as a <code>Date</code> object or
61 * as the milliseconds since January 1, 1970, 00:00:00 GMT.
62 *
63 * <p>{@code DateFormat} provides many class methods for obtaining default date/time
64 * formatters based on the default or a given locale and a number of formatting
65 * styles. The formatting styles include {@link #FULL}, {@link #LONG}, {@link #MEDIUM}, and {@link #SHORT}. More
66 * detail and examples of using these styles are provided in the method
67 * descriptions.
68 *
69 * <p>{@code DateFormat} helps you to format and parse dates for any locale.
70 * Your code can be completely independent of the locale conventions for
71 * months, days of the week, or even the calendar format: lunar vs. solar.
72 *
73 * <p>To format a date for the current Locale, use one of the
74 * static factory methods:
75 * <pre>
76 * myString = DateFormat.getDateInstance().format(myDate);
77 * </pre>
78 * <p>If you are formatting multiple dates, it is
79 * more efficient to get the format and use it multiple times so that
80 * the system doesn't have to fetch the information about the local
81 * language and country conventions multiple times.
82 * <pre>
83 * DateFormat df = DateFormat.getDateInstance();
84 * for (int i = 0; i < myDate.length; ++i) {
85 * output.println(df.format(myDate[i]) + "; ");
86 * }
87 * </pre>
88 * <p>To format a date for a different Locale, specify it in the
89 * call to {@link #getDateInstance(int, Locale) getDateInstance()}.
90 * <pre>
91 * DateFormat df = DateFormat.getDateInstance(DateFormat.LONG, Locale.FRANCE);
92 * </pre>
93 * <p>You can use a DateFormat to parse also.
94 * <pre>
95 * myDate = df.parse(myString);
96 * </pre>
97 * <p>Use {@code getDateInstance} to get the normal date format for that country.
98 * There are other static factory methods available.
99 * Use {@code getTimeInstance} to get the time format for that country.
100 * Use {@code getDateTimeInstance} to get a date and time format. You can pass in
101 * different options to these factory methods to control the length of the
102 * result; from {@link #SHORT} to {@link #MEDIUM} to {@link #LONG} to {@link #FULL}. The exact result depends
103 * on the locale, but generally:
104 * <ul><li>{@link #SHORT} is completely numeric, such as {@code 12.13.52} or {@code 3:30pm}
105 * <li>{@link #MEDIUM} is longer, such as {@code Jan 12, 1952}
106 * <li>{@link #LONG} is longer, such as {@code January 12, 1952} or {@code 3:30:32pm}
107 * <li>{@link #FULL} is pretty completely specified, such as
108 * {@code Tuesday, April 12, 1952 AD or 3:30:42pm PST}.
109 * </ul>
110 *
111 * <p>You can also set the time zone on the format if you wish.
112 * If you want even more control over the format or parsing,
113 * (or want to give your users more control),
114 * you can try casting the {@code DateFormat} you get from the factory methods
115 * to a {@link SimpleDateFormat}. This will work for the majority
116 * of countries; just remember to put it in a {@code try} block in case you
117 * encounter an unusual one.
118 *
119 * <p>You can also use forms of the parse and format methods with
120 * {@link ParsePosition} and {@link FieldPosition} to
121 * allow you to
122 * <ul><li>progressively parse through pieces of a string.
123 * <li>align any particular field, or find out where it is for selection
124 * on the screen.
125 * </ul>
126 *
127 * <h4><a name="synchronization">Synchronization</a></h4>
128 *
129 * <p>
130 * Date formats are not synchronized.
131 * It is recommended to create separate format instances for each thread.
132 * If multiple threads access a format concurrently, it must be synchronized
133 * externally.
134 *
135 * @see Format
136 * @see NumberFormat
137 * @see SimpleDateFormat
138 * @see java.util.Calendar
139 * @see java.util.GregorianCalendar
140 * @see java.util.TimeZone
141 * @author Mark Davis, Chen-Lieh Huang, Alan Liu
142 */
143public abstract class DateFormat extends Format {
144
145 /**
146 * The {@link Calendar} instance used for calculating the date-time fields
147 * and the instant of time. This field is used for both formatting and
148 * parsing.
149 *
150 * <p>Subclasses should initialize this field to a {@link Calendar}
151 * appropriate for the {@link Locale} associated with this
152 * <code>DateFormat</code>.
153 * @serial
154 */
155 protected Calendar calendar;
156
157 /**
158 * The number formatter that <code>DateFormat</code> uses to format numbers
159 * in dates and times. Subclasses should initialize this to a number format
160 * appropriate for the locale associated with this <code>DateFormat</code>.
161 * @serial
162 */
163 protected NumberFormat numberFormat;
164
165 /**
166 * Useful constant for ERA field alignment.
167 * Used in FieldPosition of date/time formatting.
168 */
169 public final static int ERA_FIELD = 0;
170 /**
171 * Useful constant for YEAR field alignment.
172 * Used in FieldPosition of date/time formatting.
173 */
174 public final static int YEAR_FIELD = 1;
175 /**
176 * Useful constant for MONTH field alignment.
177 * Used in FieldPosition of date/time formatting.
178 */
179 public final static int MONTH_FIELD = 2;
180 /**
181 * Useful constant for DATE field alignment.
182 * Used in FieldPosition of date/time formatting.
183 */
184 public final static int DATE_FIELD = 3;
185 /**
186 * Useful constant for one-based HOUR_OF_DAY field alignment.
187 * Used in FieldPosition of date/time formatting.
188 * HOUR_OF_DAY1_FIELD is used for the one-based 24-hour clock.
189 * For example, 23:59 + 01:00 results in 24:59.
190 */
191 public final static int HOUR_OF_DAY1_FIELD = 4;
192 /**
193 * Useful constant for zero-based HOUR_OF_DAY field alignment.
194 * Used in FieldPosition of date/time formatting.
195 * HOUR_OF_DAY0_FIELD is used for the zero-based 24-hour clock.
196 * For example, 23:59 + 01:00 results in 00:59.
197 */
198 public final static int HOUR_OF_DAY0_FIELD = 5;
199 /**
200 * Useful constant for MINUTE field alignment.
201 * Used in FieldPosition of date/time formatting.
202 */
203 public final static int MINUTE_FIELD = 6;
204 /**
205 * Useful constant for SECOND field alignment.
206 * Used in FieldPosition of date/time formatting.
207 */
208 public final static int SECOND_FIELD = 7;
209 /**
210 * Useful constant for MILLISECOND field alignment.
211 * Used in FieldPosition of date/time formatting.
212 */
213 public final static int MILLISECOND_FIELD = 8;
214 /**
215 * Useful constant for DAY_OF_WEEK field alignment.
216 * Used in FieldPosition of date/time formatting.
217 */
218 public final static int DAY_OF_WEEK_FIELD = 9;
219 /**
220 * Useful constant for DAY_OF_YEAR field alignment.
221 * Used in FieldPosition of date/time formatting.
222 */
223 public final static int DAY_OF_YEAR_FIELD = 10;
224 /**
225 * Useful constant for DAY_OF_WEEK_IN_MONTH field alignment.
226 * Used in FieldPosition of date/time formatting.
227 */
228 public final static int DAY_OF_WEEK_IN_MONTH_FIELD = 11;
229 /**
230 * Useful constant for WEEK_OF_YEAR field alignment.
231 * Used in FieldPosition of date/time formatting.
232 */
233 public final static int WEEK_OF_YEAR_FIELD = 12;
234 /**
235 * Useful constant for WEEK_OF_MONTH field alignment.
236 * Used in FieldPosition of date/time formatting.
237 */
238 public final static int WEEK_OF_MONTH_FIELD = 13;
239 /**
240 * Useful constant for AM_PM field alignment.
241 * Used in FieldPosition of date/time formatting.
242 */
243 public final static int AM_PM_FIELD = 14;
244 /**
245 * Useful constant for one-based HOUR field alignment.
246 * Used in FieldPosition of date/time formatting.
247 * HOUR1_FIELD is used for the one-based 12-hour clock.
248 * For example, 11:30 PM + 1 hour results in 12:30 AM.
249 */
250 public final static int HOUR1_FIELD = 15;
251 /**
252 * Useful constant for zero-based HOUR field alignment.
253 * Used in FieldPosition of date/time formatting.
254 * HOUR0_FIELD is used for the zero-based 12-hour clock.
255 * For example, 11:30 PM + 1 hour results in 00:30 AM.
256 */
257 public final static int HOUR0_FIELD = 16;
258 /**
259 * Useful constant for TIMEZONE field alignment.
260 * Used in FieldPosition of date/time formatting.
261 */
262 public final static int TIMEZONE_FIELD = 17;
263
264 // Proclaim serial compatibility with 1.1 FCS
265 private static final long serialVersionUID = 7218322306649953788L;
266
267 /**
268 * Overrides Format.
269 * Formats a time object into a time string. Examples of time objects
270 * are a time value expressed in milliseconds and a Date object.
271 * @param obj must be a Number or a Date.
272 * @param toAppendTo the string buffer for the returning time string.
273 * @return the string buffer passed in as toAppendTo, with formatted text appended.
274 * @param fieldPosition keeps track of the position of the field
275 * within the returned string.
276 * On input: an alignment field,
277 * if desired. On output: the offsets of the alignment field. For
278 * example, given a time text "1996.07.10 AD at 15:08:56 PDT",
279 * if the given fieldPosition is DateFormat.YEAR_FIELD, the
280 * begin index and end index of fieldPosition will be set to
281 * 0 and 4, respectively.
282 * Notice that if the same time field appears
283 * more than once in a pattern, the fieldPosition will be set for the first
284 * occurrence of that time field. For instance, formatting a Date to
285 * the time string "1 PM PDT (Pacific Daylight Time)" using the pattern
286 * "h a z (zzzz)" and the alignment field DateFormat.TIMEZONE_FIELD,
287 * the begin index and end index of fieldPosition will be set to
288 * 5 and 8, respectively, for the first occurrence of the timezone
289 * pattern character 'z'.
290 * @see java.text.Format
291 */
292 public final StringBuffer format(Object obj, StringBuffer toAppendTo,
293 FieldPosition fieldPosition)
294 {
295 if (obj instanceof Date)
296 return format( (Date)obj, toAppendTo, fieldPosition );
297 else if (obj instanceof Number)
298 return format( new Date(((Number)obj).longValue()),
299 toAppendTo, fieldPosition );
300 else
301 throw new IllegalArgumentException("Cannot format given Object as a Date");
302 }
303
304 /**
305 * Formats a Date into a date/time string.
306 * @param date a Date to be formatted into a date/time string.
307 * @param toAppendTo the string buffer for the returning date/time string.
308 * @param fieldPosition keeps track of the position of the field
309 * within the returned string.
310 * On input: an alignment field,
311 * if desired. On output: the offsets of the alignment field. For
312 * example, given a time text "1996.07.10 AD at 15:08:56 PDT",
313 * if the given fieldPosition is DateFormat.YEAR_FIELD, the
314 * begin index and end index of fieldPosition will be set to
315 * 0 and 4, respectively.
316 * Notice that if the same time field appears
317 * more than once in a pattern, the fieldPosition will be set for the first
318 * occurrence of that time field. For instance, formatting a Date to
319 * the time string "1 PM PDT (Pacific Daylight Time)" using the pattern
320 * "h a z (zzzz)" and the alignment field DateFormat.TIMEZONE_FIELD,
321 * the begin index and end index of fieldPosition will be set to
322 * 5 and 8, respectively, for the first occurrence of the timezone
323 * pattern character 'z'.
324 * @return the string buffer passed in as toAppendTo, with formatted text appended.
325 */
326 public abstract StringBuffer format(Date date, StringBuffer toAppendTo,
327 FieldPosition fieldPosition);
328
329 /**
330 * Formats a Date into a date/time string.
331 * @param date the time value to be formatted into a time string.
332 * @return the formatted time string.
333 */
334 public final String format(Date date)
335 {
336 return format(date, new StringBuffer(),
337 DontCareFieldPosition.INSTANCE).toString();
338 }
339
340 /**
341 * Parses text from the beginning of the given string to produce a date.
342 * The method may not use the entire text of the given string.
343 * <p>
344 * See the {@link #parse(String, ParsePosition)} method for more information
345 * on date parsing.
346 *
347 * @param source A <code>String</code> whose beginning should be parsed.
348 * @return A <code>Date</code> parsed from the string.
349 * @exception ParseException if the beginning of the specified string
350 * cannot be parsed.
351 */
352 public Date parse(String source) throws ParseException
353 {
354 ParsePosition pos = new ParsePosition(0);
355 Date result = parse(source, pos);
356 if (pos.index == 0)
357 throw new ParseException("Unparseable date: \"" + source + "\"" ,
358 pos.errorIndex);
359 return result;
360 }
361
362 /**
363 * Parse a date/time string according to the given parse position. For
364 * example, a time text {@code "07/10/96 4:5 PM, PDT"} will be parsed into a {@code Date}
365 * that is equivalent to {@code Date(837039900000L)}.
366 *
367 * <p> By default, parsing is lenient: If the input is not in the form used
368 * by this object's format method but can still be parsed as a date, then
369 * the parse succeeds. Clients may insist on strict adherence to the
370 * format by calling {@link #setLenient(boolean) setLenient(false)}.
371 *
372 * <p>This parsing operation uses the {@link #calendar} to produce
373 * a {@code Date}. As a result, the {@code calendar}'s date-time
374 * fields and the {@code TimeZone} value may have been
375 * overwritten, depending on subclass implementations. Any {@code
376 * TimeZone} value that has previously been set by a call to
377 * {@link #setTimeZone(java.util.TimeZone) setTimeZone} may need
378 * to be restored for further operations.
379 *
380 * @param source The date/time string to be parsed
381 *
382 * @param pos On input, the position at which to start parsing; on
383 * output, the position at which parsing terminated, or the
384 * start position if the parse failed.
385 *
386 * @return A {@code Date}, or {@code null} if the input could not be parsed
387 */
388 public abstract Date parse(String source, ParsePosition pos);
389
390 /**
391 * Parses text from a string to produce a <code>Date</code>.
392 * <p>
393 * The method attempts to parse text starting at the index given by
394 * <code>pos</code>.
395 * If parsing succeeds, then the index of <code>pos</code> is updated
396 * to the index after the last character used (parsing does not necessarily
397 * use all characters up to the end of the string), and the parsed
398 * date is returned. The updated <code>pos</code> can be used to
399 * indicate the starting point for the next call to this method.
400 * If an error occurs, then the index of <code>pos</code> is not
401 * changed, the error index of <code>pos</code> is set to the index of
402 * the character where the error occurred, and null is returned.
403 * <p>
404 * See the {@link #parse(String, ParsePosition)} method for more information
405 * on date parsing.
406 *
407 * @param source A <code>String</code>, part of which should be parsed.
408 * @param pos A <code>ParsePosition</code> object with index and error
409 * index information as described above.
410 * @return A <code>Date</code> parsed from the string. In case of
411 * error, returns null.
412 * @exception NullPointerException if <code>pos</code> is null.
413 */
414 public Object parseObject(String source, ParsePosition pos) {
415 return parse(source, pos);
416 }
417
418 /**
419 * Constant for full style pattern.
420 */
421 public static final int FULL = 0;
422 /**
423 * Constant for long style pattern.
424 */
425 public static final int LONG = 1;
426 /**
427 * Constant for medium style pattern.
428 */
429 public static final int MEDIUM = 2;
430 /**
431 * Constant for short style pattern.
432 */
433 public static final int SHORT = 3;
434 /**
435 * Constant for default style pattern. Its value is MEDIUM.
436 */
437 public static final int DEFAULT = MEDIUM;
438
439 /**
440 * Gets the time formatter with the default formatting style
441 * for the default locale.
442 * @return a time formatter.
443 */
444 public final static DateFormat getTimeInstance()
445 {
446 return get(DEFAULT, 0, 1, Locale.getDefault(Locale.Category.FORMAT));
447 }
448
449 /**
450 * Gets the time formatter with the given formatting style
451 * for the default locale.
452 * @param style the given formatting style. For example,
453 * SHORT for "h:mm a" in the US locale.
454 * @return a time formatter.
455 */
456 public final static DateFormat getTimeInstance(int style)
457 {
458 return get(style, 0, 1, Locale.getDefault(Locale.Category.FORMAT));
459 }
460
461 /**
462 * Gets the time formatter with the given formatting style
463 * for the given locale.
464 * @param style the given formatting style. For example,
465 * SHORT for "h:mm a" in the US locale.
466 * @param aLocale the given locale.
467 * @return a time formatter.
468 */
469 public final static DateFormat getTimeInstance(int style,
470 Locale aLocale)
471 {
472 return get(style, 0, 1, aLocale);
473 }
474
475 /**
476 * Gets the date formatter with the default formatting style
477 * for the default locale.
478 * @return a date formatter.
479 */
480 public final static DateFormat getDateInstance()
481 {
482 return get(0, DEFAULT, 2, Locale.getDefault(Locale.Category.FORMAT));
483 }
484
485 /**
486 * Gets the date formatter with the given formatting style
487 * for the default locale.
488 * @param style the given formatting style. For example,
489 * SHORT for "M/d/yy" in the US locale.
490 * @return a date formatter.
491 */
492 public final static DateFormat getDateInstance(int style)
493 {
494 return get(0, style, 2, Locale.getDefault(Locale.Category.FORMAT));
495 }
496
497 /**
498 * Gets the date formatter with the given formatting style
499 * for the given locale.
500 * @param style the given formatting style. For example,
501 * SHORT for "M/d/yy" in the US locale.
502 * @param aLocale the given locale.
503 * @return a date formatter.
504 */
505 public final static DateFormat getDateInstance(int style,
506 Locale aLocale)
507 {
508 return get(0, style, 2, aLocale);
509 }
510
511 /**
512 * Gets the date/time formatter with the default formatting style
513 * for the default locale.
514 * @return a date/time formatter.
515 */
516 public final static DateFormat getDateTimeInstance()
517 {
518 return get(DEFAULT, DEFAULT, 3, Locale.getDefault(Locale.Category.FORMAT));
519 }
520
521 /**
522 * Gets the date/time formatter with the given date and time
523 * formatting styles for the default locale.
524 * @param dateStyle the given date formatting style. For example,
525 * SHORT for "M/d/yy" in the US locale.
526 * @param timeStyle the given time formatting style. For example,
527 * SHORT for "h:mm a" in the US locale.
528 * @return a date/time formatter.
529 */
530 public final static DateFormat getDateTimeInstance(int dateStyle,
531 int timeStyle)
532 {
533 return get(timeStyle, dateStyle, 3, Locale.getDefault(Locale.Category.FORMAT));
534 }
535
536 /**
537 * Gets the date/time formatter with the given formatting styles
538 * for the given locale.
539 * @param dateStyle the given date formatting style.
540 * @param timeStyle the given time formatting style.
541 * @param aLocale the given locale.
542 * @return a date/time formatter.
543 */
544 public final static DateFormat
545 getDateTimeInstance(int dateStyle, int timeStyle, Locale aLocale)
546 {
547 return get(timeStyle, dateStyle, 3, aLocale);
548 }
549
550 /**
551 * Get a default date/time formatter that uses the SHORT style for both the
552 * date and the time.
553 */
554 public final static DateFormat getInstance() {
555 return getDateTimeInstance(SHORT, SHORT);
556 }
557
558 /**
559 * Returns an array of all locales for which the
560 * <code>get*Instance</code> methods of this class can return
561 * localized instances.
562 * The returned array represents the union of locales supported by the Java
563 * runtime and by installed
564 * {@link java.text.spi.DateFormatProvider DateFormatProvider} implementations.
565 * It must contain at least a <code>Locale</code> instance equal to
566 * {@link java.util.Locale#US Locale.US}.
567 *
568 * @return An array of locales for which localized
569 * <code>DateFormat</code> instances are available.
570 */
571 public static Locale[] getAvailableLocales()
572 {
573 LocaleServiceProviderPool pool =
574 LocaleServiceProviderPool.getPool(DateFormatProvider.class);
575 return pool.getAvailableLocales();
576 }
577
578 /**
579 * Set the calendar to be used by this date format. Initially, the default
580 * calendar for the specified or default locale is used.
581 *
582 * <p>Any {@link java.util.TimeZone TimeZone} and {@linkplain
583 * #isLenient() leniency} values that have previously been set are
584 * overwritten by {@code newCalendar}'s values.
585 *
586 * @param newCalendar the new {@code Calendar} to be used by the date format
587 */
588 public void setCalendar(Calendar newCalendar)
589 {
590 this.calendar = newCalendar;
591 }
592
593 /**
594 * Gets the calendar associated with this date/time formatter.
595 *
596 * @return the calendar associated with this date/time formatter.
597 */
598 public Calendar getCalendar()
599 {
600 return calendar;
601 }
602
603 /**
604 * Allows you to set the number formatter.
605 * @param newNumberFormat the given new NumberFormat.
606 */
607 public void setNumberFormat(NumberFormat newNumberFormat)
608 {
609 this.numberFormat = newNumberFormat;
610 }
611
612 /**
613 * Gets the number formatter which this date/time formatter uses to
614 * format and parse a time.
615 * @return the number formatter which this date/time formatter uses.
616 */
617 public NumberFormat getNumberFormat()
618 {
619 return numberFormat;
620 }
621
622 /**
623 * Sets the time zone for the calendar of this {@code DateFormat} object.
624 * This method is equivalent to the following call.
625 * <blockquote><pre>
626 * getCalendar().setTimeZone(zone)
627 * </pre></blockquote>
628 *
629 * <p>The {@code TimeZone} set by this method is overwritten by a
630 * {@link #setCalendar(java.util.Calendar) setCalendar} call.
631 *
632 * <p>The {@code TimeZone} set by this method may be overwritten as
633 * a result of a call to the parse method.
634 *
635 * @param zone the given new time zone.
636 */
637 public void setTimeZone(TimeZone zone)
638 {
639 calendar.setTimeZone(zone);
640 }
641
642 /**
643 * Gets the time zone.
644 * This method is equivalent to the following call.
645 * <blockquote><pre>
646 * getCalendar().getTimeZone()
647 * </pre></blockquote>
648 *
649 * @return the time zone associated with the calendar of DateFormat.
650 */
651 public TimeZone getTimeZone()
652 {
653 return calendar.getTimeZone();
654 }
655
656 /**
657 * Specify whether or not date/time parsing is to be lenient. With
658 * lenient parsing, the parser may use heuristics to interpret inputs that
659 * do not precisely match this object's format. With strict parsing,
660 * inputs must match this object's format.
661 *
662 * <p>This method is equivalent to the following call.
663 * <blockquote><pre>
664 * getCalendar().setLenient(lenient)
665 * </pre></blockquote>
666 *
667 * <p>This leniency value is overwritten by a call to {@link
668 * #setCalendar(java.util.Calendar) setCalendar()}.
669 *
670 * @param lenient when {@code true}, parsing is lenient
671 * @see java.util.Calendar#setLenient(boolean)
672 */
673 public void setLenient(boolean lenient)
674 {
675 calendar.setLenient(lenient);
676 }
677
678 /**
679 * Tell whether date/time parsing is to be lenient.
680 * This method is equivalent to the following call.
681 * <blockquote><pre>
682 * getCalendar().isLenient()
683 * </pre></blockquote>
684 *
685 * @return {@code true} if the {@link #calendar} is lenient;
686 * {@code false} otherwise.
687 * @see java.util.Calendar#isLenient()
688 */
689 public boolean isLenient()
690 {
691 return calendar.isLenient();
692 }
693
694 /**
695 * Overrides hashCode
696 */
697 public int hashCode() {
698 return numberFormat.hashCode();
699 // just enough fields for a reasonable distribution
700 }
701
702 /**
703 * Overrides equals
704 */
705 public boolean equals(Object obj) {
706 if (this == obj) return true;
707 if (obj == null || getClass() != obj.getClass()) return false;
708 DateFormat other = (DateFormat) obj;
709 return (// calendar.equivalentTo(other.calendar) // THIS API DOESN'T EXIST YET!
710 calendar.getFirstDayOfWeek() == other.calendar.getFirstDayOfWeek() &&
711 calendar.getMinimalDaysInFirstWeek() == other.calendar.getMinimalDaysInFirstWeek() &&
712 calendar.isLenient() == other.calendar.isLenient() &&
713 calendar.getTimeZone().equals(other.calendar.getTimeZone()) &&
714 numberFormat.equals(other.numberFormat));
715 }
716
717 /**
718 * Overrides Cloneable
719 */
720 public Object clone()
721 {
722 DateFormat other = (DateFormat) super.clone();
723 other.calendar = (Calendar) calendar.clone();
724 other.numberFormat = (NumberFormat) numberFormat.clone();
725 return other;
726 }
727
728 /**
729 * Creates a DateFormat with the given time and/or date style in the given
730 * locale.
731 * @param timeStyle a value from 0 to 3 indicating the time format,
732 * ignored if flags is 2
733 * @param dateStyle a value from 0 to 3 indicating the time format,
734 * ignored if flags is 1
735 * @param flags either 1 for a time format, 2 for a date format,
736 * or 3 for a date/time format
737 * @param loc the locale for the format
738 */
739 private static DateFormat get(int timeStyle, int dateStyle,
740 int flags, Locale loc) {
741 if ((flags & 1) != 0) {
742 if (timeStyle < 0 || timeStyle > 3) {
743 throw new IllegalArgumentException("Illegal time style " + timeStyle);
744 }
745 } else {
746 timeStyle = -1;
747 }
748 if ((flags & 2) != 0) {
749 if (dateStyle < 0 || dateStyle > 3) {
750 throw new IllegalArgumentException("Illegal date style " + dateStyle);
751 }
752 } else {
753 dateStyle = -1;
754 }
755 try {
756 // Check whether a provider can provide an implementation that's closer
757 // to the requested locale than what the Java runtime itself can provide.
758 LocaleServiceProviderPool pool =
759 LocaleServiceProviderPool.getPool(DateFormatProvider.class);
760 if (pool.hasProviders()) {
761 DateFormat providersInstance = pool.getLocalizedObject(
762 DateFormatGetter.INSTANCE,
763 loc,
764 timeStyle,
765 dateStyle,
766 flags);
767 if (providersInstance != null) {
768 return providersInstance;
769 }
770 }
771
772 return new SimpleDateFormat(timeStyle, dateStyle, loc);
773 } catch (MissingResourceException e) {
774 return new SimpleDateFormat("M/d/yy h:mm a");
775 }
776 }
777
778 /**
779 * Create a new date format.
780 */
781 protected DateFormat() {}
782
783 /**
784 * Defines constants that are used as attribute keys in the
785 * <code>AttributedCharacterIterator</code> returned
786 * from <code>DateFormat.formatToCharacterIterator</code> and as
787 * field identifiers in <code>FieldPosition</code>.
788 * <p>
789 * The class also provides two methods to map
790 * between its constants and the corresponding Calendar constants.
791 *
792 * @since 1.4
793 * @see java.util.Calendar
794 */
795 public static class Field extends Format.Field {
796
797 // Proclaim serial compatibility with 1.4 FCS
798 private static final long serialVersionUID = 7441350119349544720L;
799
800 // table of all instances in this class, used by readResolve
801 private static final Map instanceMap = new HashMap(18);
802 // Maps from Calendar constant (such as Calendar.ERA) to Field
803 // constant (such as Field.ERA).
804 private static final Field[] calendarToFieldMapping =
805 new Field[Calendar.FIELD_COUNT];
806
807 /** Calendar field. */
808 private int calendarField;
809
810 /**
811 * Returns the <code>Field</code> constant that corresponds to
812 * the <code>Calendar</code> constant <code>calendarField</code>.
813 * If there is no direct mapping between the <code>Calendar</code>
814 * constant and a <code>Field</code>, null is returned.
815 *
816 * @throws IllegalArgumentException if <code>calendarField</code> is
817 * not the value of a <code>Calendar</code> field constant.
818 * @param calendarField Calendar field constant
819 * @return Field instance representing calendarField.
820 * @see java.util.Calendar
821 */
822 public static Field ofCalendarField(int calendarField) {
823 if (calendarField < 0 || calendarField >=
824 calendarToFieldMapping.length) {
825 throw new IllegalArgumentException("Unknown Calendar constant "
826 + calendarField);
827 }
828 return calendarToFieldMapping[calendarField];
829 }
830
831 /**
832 * Creates a <code>Field</code>.
833 *
834 * @param name the name of the <code>Field</code>
835 * @param calendarField the <code>Calendar</code> constant this
836 * <code>Field</code> corresponds to; any value, even one
837 * outside the range of legal <code>Calendar</code> values may
838 * be used, but <code>-1</code> should be used for values
839 * that don't correspond to legal <code>Calendar</code> values
840 */
841 protected Field(String name, int calendarField) {
842 super(name);
843 this.calendarField = calendarField;
844 if (this.getClass() == DateFormat.Field.class) {
845 instanceMap.put(name, this);
846 if (calendarField >= 0) {
847 // assert(calendarField < Calendar.FIELD_COUNT);
848 calendarToFieldMapping[calendarField] = this;
849 }
850 }
851 }
852
853 /**
854 * Returns the <code>Calendar</code> field associated with this
855 * attribute. For example, if this represents the hours field of
856 * a <code>Calendar</code>, this would return
857 * <code>Calendar.HOUR</code>. If there is no corresponding
858 * <code>Calendar</code> constant, this will return -1.
859 *
860 * @return Calendar constant for this field
861 * @see java.util.Calendar
862 */
863 public int getCalendarField() {
864 return calendarField;
865 }
866
867 /**
868 * Resolves instances being deserialized to the predefined constants.
869 *
870 * @throws InvalidObjectException if the constant could not be
871 * resolved.
872 * @return resolved DateFormat.Field constant
873 */
874 protected Object readResolve() throws InvalidObjectException {
875 if (this.getClass() != DateFormat.Field.class) {
876 throw new InvalidObjectException("subclass didn't correctly implement readResolve");
877 }
878
879 Object instance = instanceMap.get(getName());
880 if (instance != null) {
881 return instance;
882 } else {
883 throw new InvalidObjectException("unknown attribute name");
884 }
885 }
886
887 //
888 // The constants
889 //
890
891 /**
892 * Constant identifying the era field.
893 */
894 public final static Field ERA = new Field("era", Calendar.ERA);
895
896 /**
897 * Constant identifying the year field.
898 */
899 public final static Field YEAR = new Field("year", Calendar.YEAR);
900
901 /**
902 * Constant identifying the month field.
903 */
904 public final static Field MONTH = new Field("month", Calendar.MONTH);
905
906 /**
907 * Constant identifying the day of month field.
908 */
909 public final static Field DAY_OF_MONTH = new
910 Field("day of month", Calendar.DAY_OF_MONTH);
911
912 /**
913 * Constant identifying the hour of day field, where the legal values
914 * are 1 to 24.
915 */
916 public final static Field HOUR_OF_DAY1 = new Field("hour of day 1",-1);
917
918 /**
919 * Constant identifying the hour of day field, where the legal values
920 * are 0 to 23.
921 */
922 public final static Field HOUR_OF_DAY0 = new
923 Field("hour of day", Calendar.HOUR_OF_DAY);
924
925 /**
926 * Constant identifying the minute field.
927 */
928 public final static Field MINUTE =new Field("minute", Calendar.MINUTE);
929
930 /**
931 * Constant identifying the second field.
932 */
933 public final static Field SECOND =new Field("second", Calendar.SECOND);
934
935 /**
936 * Constant identifying the millisecond field.
937 */
938 public final static Field MILLISECOND = new
939 Field("millisecond", Calendar.MILLISECOND);
940
941 /**
942 * Constant identifying the day of week field.
943 */
944 public final static Field DAY_OF_WEEK = new
945 Field("day of week", Calendar.DAY_OF_WEEK);
946
947 /**
948 * Constant identifying the day of year field.
949 */
950 public final static Field DAY_OF_YEAR = new
951 Field("day of year", Calendar.DAY_OF_YEAR);
952
953 /**
954 * Constant identifying the day of week field.
955 */
956 public final static Field DAY_OF_WEEK_IN_MONTH =
957 new Field("day of week in month",
958 Calendar.DAY_OF_WEEK_IN_MONTH);
959
960 /**
961 * Constant identifying the week of year field.
962 */
963 public final static Field WEEK_OF_YEAR = new
964 Field("week of year", Calendar.WEEK_OF_YEAR);
965
966 /**
967 * Constant identifying the week of month field.
968 */
969 public final static Field WEEK_OF_MONTH = new
970 Field("week of month", Calendar.WEEK_OF_MONTH);
971
972 /**
973 * Constant identifying the time of day indicator
974 * (e.g. "a.m." or "p.m.") field.
975 */
976 public final static Field AM_PM = new
977 Field("am pm", Calendar.AM_PM);
978
979 /**
980 * Constant identifying the hour field, where the legal values are
981 * 1 to 12.
982 */
983 public final static Field HOUR1 = new Field("hour 1", -1);
984
985 /**
986 * Constant identifying the hour field, where the legal values are
987 * 0 to 11.
988 */
989 public final static Field HOUR0 = new
990 Field("hour", Calendar.HOUR);
991
992 /**
993 * Constant identifying the time zone field.
994 */
995 public final static Field TIME_ZONE = new Field("time zone", -1);
996 }
997
998 /**
999 * Obtains a DateFormat instance from a DateFormatProvider
1000 * implementation.
1001 */
1002 private static class DateFormatGetter
1003 implements LocaleServiceProviderPool.LocalizedObjectGetter<DateFormatProvider, DateFormat> {
1004 private static final DateFormatGetter INSTANCE = new DateFormatGetter();
1005
1006 public DateFormat getObject(DateFormatProvider dateFormatProvider,
1007 Locale locale,
1008 String key,
1009 Object... params) {
1010 assert params.length == 3;
1011
1012 int timeStyle = (Integer)params[0];
1013 int dateStyle = (Integer)params[1];
1014 int flags = (Integer)params[2];
1015
1016 switch (flags) {
1017 case 1:
1018 return dateFormatProvider.getTimeInstance(timeStyle, locale);
1019 case 2:
1020 return dateFormatProvider.getDateInstance(dateStyle, locale);
1021 case 3:
1022 return dateFormatProvider.getDateTimeInstance(dateStyle, timeStyle, locale);
1023 default:
1024 assert false : "should not happen";
1025 }
1026
1027 return null;
1028 }
1029 }
1030}
java/pkg/java/text/DateFormatSymbols$DateFormatSymbolsGetter.class+0−0

Binary file not shown.

java/pkg/java/text/DateFormatSymbols.class+0−0

Binary file not shown.

java/pkg/java/text/DateFormatSymbols.java+794−0
@@ -0,0 +1,794 @@
1/*
2 * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26/*
27 * (C) Copyright Taligent, Inc. 1996 - All Rights Reserved
28 * (C) Copyright IBM Corp. 1996 - All Rights Reserved
29 *
30 * The original version of this source code and documentation is copyrighted
31 * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
32 * materials are provided under terms of a License Agreement between Taligent
33 * and Sun. This technology is protected by multiple US and International
34 * patents. This notice and attribution to Taligent may not be removed.
35 * Taligent is a registered trademark of Taligent, Inc.
36 *
37 */
38
39package java.text;
40
41import java.io.IOException;
42import java.io.ObjectOutputStream;
43import java.io.Serializable;
44import java.lang.ref.SoftReference;
45import java.text.spi.DateFormatSymbolsProvider;
46import java.util.Arrays;
47import java.util.List;
48import java.util.Locale;
49import java.util.ResourceBundle;
50import java.util.TimeZone;
51import java.util.concurrent.ConcurrentHashMap;
52import java.util.concurrent.ConcurrentMap;
53import java.util.spi.LocaleServiceProvider;
54import sun.util.LocaleServiceProviderPool;
55import sun.util.TimeZoneNameUtility;
56import sun.util.calendar.ZoneInfo;
57import sun.util.resources.LocaleData;
58
59/**
60 * <code>DateFormatSymbols</code> is a public class for encapsulating
61 * localizable date-time formatting data, such as the names of the
62 * months, the names of the days of the week, and the time zone data.
63 * <code>DateFormat</code> and <code>SimpleDateFormat</code> both use
64 * <code>DateFormatSymbols</code> to encapsulate this information.
65 *
66 * <p>
67 * Typically you shouldn't use <code>DateFormatSymbols</code> directly.
68 * Rather, you are encouraged to create a date-time formatter with the
69 * <code>DateFormat</code> class's factory methods: <code>getTimeInstance</code>,
70 * <code>getDateInstance</code>, or <code>getDateTimeInstance</code>.
71 * These methods automatically create a <code>DateFormatSymbols</code> for
72 * the formatter so that you don't have to. After the
73 * formatter is created, you may modify its format pattern using the
74 * <code>setPattern</code> method. For more information about
75 * creating formatters using <code>DateFormat</code>'s factory methods,
76 * see {@link DateFormat}.
77 *
78 * <p>
79 * If you decide to create a date-time formatter with a specific
80 * format pattern for a specific locale, you can do so with:
81 * <blockquote>
82 * <pre>
83 * new SimpleDateFormat(aPattern, DateFormatSymbols.getInstance(aLocale)).
84 * </pre>
85 * </blockquote>
86 *
87 * <p>
88 * <code>DateFormatSymbols</code> objects are cloneable. When you obtain
89 * a <code>DateFormatSymbols</code> object, feel free to modify the
90 * date-time formatting data. For instance, you can replace the localized
91 * date-time format pattern characters with the ones that you feel easy
92 * to remember. Or you can change the representative cities
93 * to your favorite ones.
94 *
95 * <p>
96 * New <code>DateFormatSymbols</code> subclasses may be added to support
97 * <code>SimpleDateFormat</code> for date-time formatting for additional locales.
98
99 * @see DateFormat
100 * @see SimpleDateFormat
101 * @see java.util.SimpleTimeZone
102 * @author Chen-Lieh Huang
103 */
104public class DateFormatSymbols implements Serializable, Cloneable {
105
106 /**
107 * Construct a DateFormatSymbols object by loading format data from
108 * resources for the default locale. This constructor can only
109 * construct instances for the locales supported by the Java
110 * runtime environment, not for those supported by installed
111 * {@link java.text.spi.DateFormatSymbolsProvider DateFormatSymbolsProvider}
112 * implementations. For full locale coverage, use the
113 * {@link #getInstance(Locale) getInstance} method.
114 *
115 * @see #getInstance()
116 * @exception java.util.MissingResourceException
117 * if the resources for the default locale cannot be
118 * found or cannot be loaded.
119 */
120 public DateFormatSymbols()
121 {
122 initializeData(Locale.getDefault(Locale.Category.FORMAT));
123 }
124
125 /**
126 * Construct a DateFormatSymbols object by loading format data from
127 * resources for the given locale. This constructor can only
128 * construct instances for the locales supported by the Java
129 * runtime environment, not for those supported by installed
130 * {@link java.text.spi.DateFormatSymbolsProvider DateFormatSymbolsProvider}
131 * implementations. For full locale coverage, use the
132 * {@link #getInstance(Locale) getInstance} method.
133 *
134 * @see #getInstance(Locale)
135 * @exception java.util.MissingResourceException
136 * if the resources for the specified locale cannot be
137 * found or cannot be loaded.
138 */
139 public DateFormatSymbols(Locale locale)
140 {
141 initializeData(locale);
142 }
143
144 /**
145 * Era strings. For example: "AD" and "BC". An array of 2 strings,
146 * indexed by <code>Calendar.BC</code> and <code>Calendar.AD</code>.
147 * @serial
148 */
149 String eras[] = null;
150
151 /**
152 * Month strings. For example: "January", "February", etc. An array
153 * of 13 strings (some calendars have 13 months), indexed by
154 * <code>Calendar.JANUARY</code>, <code>Calendar.FEBRUARY</code>, etc.
155 * @serial
156 */
157 String months[] = null;
158
159 /**
160 * Short month strings. For example: "Jan", "Feb", etc. An array of
161 * 13 strings (some calendars have 13 months), indexed by
162 * <code>Calendar.JANUARY</code>, <code>Calendar.FEBRUARY</code>, etc.
163
164 * @serial
165 */
166 String shortMonths[] = null;
167
168 /**
169 * Weekday strings. For example: "Sunday", "Monday", etc. An array
170 * of 8 strings, indexed by <code>Calendar.SUNDAY</code>,
171 * <code>Calendar.MONDAY</code>, etc.
172 * The element <code>weekdays[0]</code> is ignored.
173 * @serial
174 */
175 String weekdays[] = null;
176
177 /**
178 * Short weekday strings. For example: "Sun", "Mon", etc. An array
179 * of 8 strings, indexed by <code>Calendar.SUNDAY</code>,
180 * <code>Calendar.MONDAY</code>, etc.
181 * The element <code>shortWeekdays[0]</code> is ignored.
182 * @serial
183 */
184 String shortWeekdays[] = null;
185
186 /**
187 * AM and PM strings. For example: "AM" and "PM". An array of
188 * 2 strings, indexed by <code>Calendar.AM</code> and
189 * <code>Calendar.PM</code>.
190 * @serial
191 */
192 String ampms[] = null;
193
194 /**
195 * Localized names of time zones in this locale. This is a
196 * two-dimensional array of strings of size <em>n</em> by <em>m</em>,
197 * where <em>m</em> is at least 5. Each of the <em>n</em> rows is an
198 * entry containing the localized names for a single <code>TimeZone</code>.
199 * Each such row contains (with <code>i</code> ranging from
200 * 0..<em>n</em>-1):
201 * <ul>
202 * <li><code>zoneStrings[i][0]</code> - time zone ID</li>
203 * <li><code>zoneStrings[i][1]</code> - long name of zone in standard
204 * time</li>
205 * <li><code>zoneStrings[i][2]</code> - short name of zone in
206 * standard time</li>
207 * <li><code>zoneStrings[i][3]</code> - long name of zone in daylight
208 * saving time</li>
209 * <li><code>zoneStrings[i][4]</code> - short name of zone in daylight
210 * saving time</li>
211 * </ul>
212 * The zone ID is <em>not</em> localized; it's one of the valid IDs of
213 * the {@link java.util.TimeZone TimeZone} class that are not
214 * <a href="../java/util/TimeZone.html#CustomID">custom IDs</a>.
215 * All other entries are localized names.
216 * @see java.util.TimeZone
217 * @serial
218 */
219 String zoneStrings[][] = null;
220
221 /**
222 * Indicates that zoneStrings is set externally with setZoneStrings() method.
223 */
224 transient boolean isZoneStringsSet = false;
225
226 /**
227 * Unlocalized date-time pattern characters. For example: 'y', 'd', etc.
228 * All locales use the same these unlocalized pattern characters.
229 */
230 static final String patternChars = "GyMdkHmsSEDFwWahKzZYuX";
231
232 static final int PATTERN_ERA = 0; // G
233 static final int PATTERN_YEAR = 1; // y
234 static final int PATTERN_MONTH = 2; // M
235 static final int PATTERN_DAY_OF_MONTH = 3; // d
236 static final int PATTERN_HOUR_OF_DAY1 = 4; // k
237 static final int PATTERN_HOUR_OF_DAY0 = 5; // H
238 static final int PATTERN_MINUTE = 6; // m
239 static final int PATTERN_SECOND = 7; // s
240 static final int PATTERN_MILLISECOND = 8; // S
241 static final int PATTERN_DAY_OF_WEEK = 9; // E
242 static final int PATTERN_DAY_OF_YEAR = 10; // D
243 static final int PATTERN_DAY_OF_WEEK_IN_MONTH = 11; // F
244 static final int PATTERN_WEEK_OF_YEAR = 12; // w
245 static final int PATTERN_WEEK_OF_MONTH = 13; // W
246 static final int PATTERN_AM_PM = 14; // a
247 static final int PATTERN_HOUR1 = 15; // h
248 static final int PATTERN_HOUR0 = 16; // K
249 static final int PATTERN_ZONE_NAME = 17; // z
250 static final int PATTERN_ZONE_VALUE = 18; // Z
251 static final int PATTERN_WEEK_YEAR = 19; // Y
252 static final int PATTERN_ISO_DAY_OF_WEEK = 20; // u
253 static final int PATTERN_ISO_ZONE = 21; // X
254
255 /**
256 * Localized date-time pattern characters. For example, a locale may
257 * wish to use 'u' rather than 'y' to represent years in its date format
258 * pattern strings.
259 * This string must be exactly 18 characters long, with the index of
260 * the characters described by <code>DateFormat.ERA_FIELD</code>,
261 * <code>DateFormat.YEAR_FIELD</code>, etc. Thus, if the string were
262 * "Xz...", then localized patterns would use 'X' for era and 'z' for year.
263 * @serial
264 */
265 String localPatternChars = null;
266
267 /**
268 * The locale which is used for initializing this DateFormatSymbols object.
269 *
270 * @since 1.6
271 * @serial
272 */
273 Locale locale = null;
274
275 /* use serialVersionUID from JDK 1.1.4 for interoperability */
276 static final long serialVersionUID = -5987973545549424702L;
277
278 /**
279 * Returns an array of all locales for which the
280 * <code>getInstance</code> methods of this class can return
281 * localized instances.
282 * The returned array represents the union of locales supported by the
283 * Java runtime and by installed
284 * {@link java.text.spi.DateFormatSymbolsProvider DateFormatSymbolsProvider}
285 * implementations. It must contain at least a <code>Locale</code>
286 * instance equal to {@link java.util.Locale#US Locale.US}.
287 *
288 * @return An array of locales for which localized
289 * <code>DateFormatSymbols</code> instances are available.
290 * @since 1.6
291 */
292 public static Locale[] getAvailableLocales() {
293 LocaleServiceProviderPool pool=
294 LocaleServiceProviderPool.getPool(DateFormatSymbolsProvider.class);
295 return pool.getAvailableLocales();
296 }
297
298 /**
299 * Gets the <code>DateFormatSymbols</code> instance for the default
300 * locale. This method provides access to <code>DateFormatSymbols</code>
301 * instances for locales supported by the Java runtime itself as well
302 * as for those supported by installed
303 * {@link java.text.spi.DateFormatSymbolsProvider DateFormatSymbolsProvider}
304 * implementations.
305 * @return a <code>DateFormatSymbols</code> instance.
306 * @since 1.6
307 */
308 public static final DateFormatSymbols getInstance() {
309 return getInstance(Locale.getDefault(Locale.Category.FORMAT));
310 }
311
312 /**
313 * Gets the <code>DateFormatSymbols</code> instance for the specified
314 * locale. This method provides access to <code>DateFormatSymbols</code>
315 * instances for locales supported by the Java runtime itself as well
316 * as for those supported by installed
317 * {@link java.text.spi.DateFormatSymbolsProvider DateFormatSymbolsProvider}
318 * implementations.
319 * @param locale the given locale.
320 * @return a <code>DateFormatSymbols</code> instance.
321 * @exception NullPointerException if <code>locale</code> is null
322 * @since 1.6
323 */
324 public static final DateFormatSymbols getInstance(Locale locale) {
325 DateFormatSymbols dfs = getProviderInstance(locale);
326 if (dfs != null) {
327 return dfs;
328 }
329 return (DateFormatSymbols) getCachedInstance(locale).clone();
330 }
331
332 /**
333 * Returns a DateFormatSymbols provided by a provider or found in
334 * the cache. Note that this method returns a cached instance,
335 * not its clone. Therefore, the instance should never be given to
336 * an application.
337 */
338 static final DateFormatSymbols getInstanceRef(Locale locale) {
339 DateFormatSymbols dfs = getProviderInstance(locale);
340 if (dfs != null) {
341 return dfs;
342 }
343 return getCachedInstance(locale);
344 }
345
346 private static DateFormatSymbols getProviderInstance(Locale locale) {
347 DateFormatSymbols providersInstance = null;
348
349 // Check whether a provider can provide an implementation that's closer
350 // to the requested locale than what the Java runtime itself can provide.
351 LocaleServiceProviderPool pool =
352 LocaleServiceProviderPool.getPool(DateFormatSymbolsProvider.class);
353 if (pool.hasProviders()) {
354 providersInstance = pool.getLocalizedObject(
355 DateFormatSymbolsGetter.INSTANCE, locale);
356 }
357 return providersInstance;
358 }
359
360 /**
361 * Returns a cached DateFormatSymbols if it's found in the
362 * cache. Otherwise, this method returns a newly cached instance
363 * for the given locale.
364 */
365 private static DateFormatSymbols getCachedInstance(Locale locale) {
366 SoftReference<DateFormatSymbols> ref = cachedInstances.get(locale);
367 DateFormatSymbols dfs = null;
368 if (ref == null || (dfs = ref.get()) == null) {
369 dfs = new DateFormatSymbols(locale);
370 ref = new SoftReference<DateFormatSymbols>(dfs);
371 SoftReference<DateFormatSymbols> x = cachedInstances.putIfAbsent(locale, ref);
372 if (x != null) {
373 DateFormatSymbols y = x.get();
374 if (y != null) {
375 dfs = y;
376 } else {
377 // Replace the empty SoftReference with ref.
378 cachedInstances.put(locale, ref);
379 }
380 }
381 }
382 return dfs;
383 }
384
385 /**
386 * Gets era strings. For example: "AD" and "BC".
387 * @return the era strings.
388 */
389 public String[] getEras() {
390 return Arrays.copyOf(eras, eras.length);
391 }
392
393 /**
394 * Sets era strings. For example: "AD" and "BC".
395 * @param newEras the new era strings.
396 */
397 public void setEras(String[] newEras) {
398 eras = Arrays.copyOf(newEras, newEras.length);
399 }
400
401 /**
402 * Gets month strings. For example: "January", "February", etc.
403 * @return the month strings.
404 */
405 public String[] getMonths() {
406 return Arrays.copyOf(months, months.length);
407 }
408
409 /**
410 * Sets month strings. For example: "January", "February", etc.
411 * @param newMonths the new month strings.
412 */
413 public void setMonths(String[] newMonths) {
414 months = Arrays.copyOf(newMonths, newMonths.length);
415 }
416
417 /**
418 * Gets short month strings. For example: "Jan", "Feb", etc.
419 * @return the short month strings.
420 */
421 public String[] getShortMonths() {
422 return Arrays.copyOf(shortMonths, shortMonths.length);
423 }
424
425 /**
426 * Sets short month strings. For example: "Jan", "Feb", etc.
427 * @param newShortMonths the new short month strings.
428 */
429 public void setShortMonths(String[] newShortMonths) {
430 shortMonths = Arrays.copyOf(newShortMonths, newShortMonths.length);
431 }
432
433 /**
434 * Gets weekday strings. For example: "Sunday", "Monday", etc.
435 * @return the weekday strings. Use <code>Calendar.SUNDAY</code>,
436 * <code>Calendar.MONDAY</code>, etc. to index the result array.
437 */
438 public String[] getWeekdays() {
439 return Arrays.copyOf(weekdays, weekdays.length);
440 }
441
442 /**
443 * Sets weekday strings. For example: "Sunday", "Monday", etc.
444 * @param newWeekdays the new weekday strings. The array should
445 * be indexed by <code>Calendar.SUNDAY</code>,
446 * <code>Calendar.MONDAY</code>, etc.
447 */
448 public void setWeekdays(String[] newWeekdays) {
449 weekdays = Arrays.copyOf(newWeekdays, newWeekdays.length);
450 }
451
452 /**
453 * Gets short weekday strings. For example: "Sun", "Mon", etc.
454 * @return the short weekday strings. Use <code>Calendar.SUNDAY</code>,
455 * <code>Calendar.MONDAY</code>, etc. to index the result array.
456 */
457 public String[] getShortWeekdays() {
458 return Arrays.copyOf(shortWeekdays, shortWeekdays.length);
459 }
460
461 /**
462 * Sets short weekday strings. For example: "Sun", "Mon", etc.
463 * @param newShortWeekdays the new short weekday strings. The array should
464 * be indexed by <code>Calendar.SUNDAY</code>,
465 * <code>Calendar.MONDAY</code>, etc.
466 */
467 public void setShortWeekdays(String[] newShortWeekdays) {
468 shortWeekdays = Arrays.copyOf(newShortWeekdays, newShortWeekdays.length);
469 }
470
471 /**
472 * Gets ampm strings. For example: "AM" and "PM".
473 * @return the ampm strings.
474 */
475 public String[] getAmPmStrings() {
476 return Arrays.copyOf(ampms, ampms.length);
477 }
478
479 /**
480 * Sets ampm strings. For example: "AM" and "PM".
481 * @param newAmpms the new ampm strings.
482 */
483 public void setAmPmStrings(String[] newAmpms) {
484 ampms = Arrays.copyOf(newAmpms, newAmpms.length);
485 }
486
487 /**
488 * Gets time zone strings. Use of this method is discouraged; use
489 * {@link java.util.TimeZone#getDisplayName() TimeZone.getDisplayName()}
490 * instead.
491 * <p>
492 * The value returned is a
493 * two-dimensional array of strings of size <em>n</em> by <em>m</em>,
494 * where <em>m</em> is at least 5. Each of the <em>n</em> rows is an
495 * entry containing the localized names for a single <code>TimeZone</code>.
496 * Each such row contains (with <code>i</code> ranging from
497 * 0..<em>n</em>-1):
498 * <ul>
499 * <li><code>zoneStrings[i][0]</code> - time zone ID</li>
500 * <li><code>zoneStrings[i][1]</code> - long name of zone in standard
501 * time</li>
502 * <li><code>zoneStrings[i][2]</code> - short name of zone in
503 * standard time</li>
504 * <li><code>zoneStrings[i][3]</code> - long name of zone in daylight
505 * saving time</li>
506 * <li><code>zoneStrings[i][4]</code> - short name of zone in daylight
507 * saving time</li>
508 * </ul>
509 * The zone ID is <em>not</em> localized; it's one of the valid IDs of
510 * the {@link java.util.TimeZone TimeZone} class that are not
511 * <a href="../util/TimeZone.html#CustomID">custom IDs</a>.
512 * All other entries are localized names. If a zone does not implement
513 * daylight saving time, the daylight saving time names should not be used.
514 * <p>
515 * If {@link #setZoneStrings(String[][]) setZoneStrings} has been called
516 * on this <code>DateFormatSymbols</code> instance, then the strings
517 * provided by that call are returned. Otherwise, the returned array
518 * contains names provided by the Java runtime and by installed
519 * {@link java.util.spi.TimeZoneNameProvider TimeZoneNameProvider}
520 * implementations.
521 *
522 * @return the time zone strings.
523 * @see #setZoneStrings(String[][])
524 */
525 public String[][] getZoneStrings() {
526 return getZoneStringsImpl(true);
527 }
528
529 /**
530 * Sets time zone strings. The argument must be a
531 * two-dimensional array of strings of size <em>n</em> by <em>m</em>,
532 * where <em>m</em> is at least 5. Each of the <em>n</em> rows is an
533 * entry containing the localized names for a single <code>TimeZone</code>.
534 * Each such row contains (with <code>i</code> ranging from
535 * 0..<em>n</em>-1):
536 * <ul>
537 * <li><code>zoneStrings[i][0]</code> - time zone ID</li>
538 * <li><code>zoneStrings[i][1]</code> - long name of zone in standard
539 * time</li>
540 * <li><code>zoneStrings[i][2]</code> - short name of zone in
541 * standard time</li>
542 * <li><code>zoneStrings[i][3]</code> - long name of zone in daylight
543 * saving time</li>
544 * <li><code>zoneStrings[i][4]</code> - short name of zone in daylight
545 * saving time</li>
546 * </ul>
547 * The zone ID is <em>not</em> localized; it's one of the valid IDs of
548 * the {@link java.util.TimeZone TimeZone} class that are not
549 * <a href="../util/TimeZone.html#CustomID">custom IDs</a>.
550 * All other entries are localized names.
551 *
552 * @param newZoneStrings the new time zone strings.
553 * @exception IllegalArgumentException if the length of any row in
554 * <code>newZoneStrings</code> is less than 5
555 * @exception NullPointerException if <code>newZoneStrings</code> is null
556 * @see #getZoneStrings()
557 */
558 public void setZoneStrings(String[][] newZoneStrings) {
559 String[][] aCopy = new String[newZoneStrings.length][];
560 for (int i = 0; i < newZoneStrings.length; ++i) {
561 int len = newZoneStrings[i].length;
562 if (len < 5) {
563 throw new IllegalArgumentException();
564 }
565 aCopy[i] = Arrays.copyOf(newZoneStrings[i], len);
566 }
567 zoneStrings = aCopy;
568 isZoneStringsSet = true;
569 }
570
571 /**
572 * Gets localized date-time pattern characters. For example: 'u', 't', etc.
573 * @return the localized date-time pattern characters.
574 */
575 public String getLocalPatternChars() {
576 return localPatternChars;
577 }
578
579 /**
580 * Sets localized date-time pattern characters. For example: 'u', 't', etc.
581 * @param newLocalPatternChars the new localized date-time
582 * pattern characters.
583 */
584 public void setLocalPatternChars(String newLocalPatternChars) {
585 // Call toString() to throw an NPE in case the argument is null
586 localPatternChars = newLocalPatternChars.toString();
587 }
588
589 /**
590 * Overrides Cloneable
591 */
592 public Object clone()
593 {
594 try
595 {
596 DateFormatSymbols other = (DateFormatSymbols)super.clone();
597 copyMembers(this, other);
598 return other;
599 } catch (CloneNotSupportedException e) {
600 throw new InternalError();
601 }
602 }
603
604 /**
605 * Override hashCode.
606 * Generates a hash code for the DateFormatSymbols object.
607 */
608 public int hashCode() {
609 int hashcode = 0;
610 String[][] zoneStrings = getZoneStringsWrapper();
611 for (int index = 0; index < zoneStrings[0].length; ++index)
612 hashcode ^= zoneStrings[0][index].hashCode();
613 return hashcode;
614 }
615
616 /**
617 * Override equals
618 */
619 public boolean equals(Object obj)
620 {
621 if (this == obj) return true;
622 if (obj == null || getClass() != obj.getClass()) return false;
623 DateFormatSymbols that = (DateFormatSymbols) obj;
624 return (Arrays.equals(eras, that.eras)
625 && Arrays.equals(months, that.months)
626 && Arrays.equals(shortMonths, that.shortMonths)
627 && Arrays.equals(weekdays, that.weekdays)
628 && Arrays.equals(shortWeekdays, that.shortWeekdays)
629 && Arrays.equals(ampms, that.ampms)
630 && Arrays.deepEquals(getZoneStringsWrapper(), that.getZoneStringsWrapper())
631 && ((localPatternChars != null
632 && localPatternChars.equals(that.localPatternChars))
633 || (localPatternChars == null
634 && that.localPatternChars == null)));
635 }
636
637 // =======================privates===============================
638
639 /**
640 * Useful constant for defining time zone offsets.
641 */
642 static final int millisPerHour = 60*60*1000;
643
644 /**
645 * Cache to hold DateFormatSymbols instances per Locale.
646 */
647 private static final ConcurrentMap<Locale, SoftReference<DateFormatSymbols>> cachedInstances
648 = new ConcurrentHashMap<Locale, SoftReference<DateFormatSymbols>>(3);
649
650 private void initializeData(Locale desiredLocale) {
651 locale = desiredLocale;
652
653 // Copy values of a cached instance if any.
654 SoftReference<DateFormatSymbols> ref = cachedInstances.get(locale);
655 DateFormatSymbols dfs;
656 if (ref != null && (dfs = ref.get()) != null) {
657 copyMembers(dfs, this);
658 return;
659 }
660
661 // Initialize the fields from the ResourceBundle for locale.
662 ResourceBundle resource = LocaleData.getDateFormatData(locale);
663
664 eras = resource.getStringArray("Eras");
665 months = resource.getStringArray("MonthNames");
666 shortMonths = resource.getStringArray("MonthAbbreviations");
667 ampms = resource.getStringArray("AmPmMarkers");
668 localPatternChars = resource.getString("DateTimePatternChars");
669
670 // Day of week names are stored in a 1-based array.
671 weekdays = toOneBasedArray(resource.getStringArray("DayNames"));
672 shortWeekdays = toOneBasedArray(resource.getStringArray("DayAbbreviations"));
673 }
674
675 private static String[] toOneBasedArray(String[] src) {
676 int len = src.length;
677 String[] dst = new String[len + 1];
678 dst[0] = "";
679 for (int i = 0; i < len; i++) {
680 dst[i + 1] = src[i];
681 }
682 return dst;
683 }
684
685 /**
686 * Package private: used by SimpleDateFormat
687 * Gets the index for the given time zone ID to obtain the time zone
688 * strings for formatting. The time zone ID is just for programmatic
689 * lookup. NOT LOCALIZED!!!
690 * @param ID the given time zone ID.
691 * @return the index of the given time zone ID. Returns -1 if
692 * the given time zone ID can't be located in the DateFormatSymbols object.
693 * @see java.util.SimpleTimeZone
694 */
695 final int getZoneIndex(String ID)
696 {
697 String[][] zoneStrings = getZoneStringsWrapper();
698 for (int index=0; index<zoneStrings.length; index++)
699 {
700 if (ID.equals(zoneStrings[index][0])) return index;
701 }
702
703 return -1;
704 }
705
706 /**
707 * Wrapper method to the getZoneStrings(), which is called from inside
708 * the java.text package and not to mutate the returned arrays, so that
709 * it does not need to create a defensive copy.
710 */
711 final String[][] getZoneStringsWrapper() {
712 if (isSubclassObject()) {
713 return getZoneStrings();
714 } else {
715 return getZoneStringsImpl(false);
716 }
717 }
718
719 private final String[][] getZoneStringsImpl(boolean needsCopy) {
720 if (zoneStrings == null) {
721 zoneStrings = TimeZoneNameUtility.getZoneStrings(locale);
722 }
723
724 if (!needsCopy) {
725 return zoneStrings;
726 }
727
728 int len = zoneStrings.length;
729 String[][] aCopy = new String[len][];
730 for (int i = 0; i < len; i++) {
731 aCopy[i] = Arrays.copyOf(zoneStrings[i], zoneStrings[i].length);
732 }
733 return aCopy;
734 }
735
736 private final boolean isSubclassObject() {
737 return !getClass().getName().equals("java.text.DateFormatSymbols");
738 }
739
740 /**
741 * Clones all the data members from the source DateFormatSymbols to
742 * the target DateFormatSymbols. This is only for subclasses.
743 * @param src the source DateFormatSymbols.
744 * @param dst the target DateFormatSymbols.
745 */
746 private final void copyMembers(DateFormatSymbols src, DateFormatSymbols dst)
747 {
748 dst.eras = Arrays.copyOf(src.eras, src.eras.length);
749 dst.months = Arrays.copyOf(src.months, src.months.length);
750 dst.shortMonths = Arrays.copyOf(src.shortMonths, src.shortMonths.length);
751 dst.weekdays = Arrays.copyOf(src.weekdays, src.weekdays.length);
752 dst.shortWeekdays = Arrays.copyOf(src.shortWeekdays, src.shortWeekdays.length);
753 dst.ampms = Arrays.copyOf(src.ampms, src.ampms.length);
754 if (src.zoneStrings != null) {
755 dst.zoneStrings = src.getZoneStringsImpl(true);
756 } else {
757 dst.zoneStrings = null;
758 }
759 dst.localPatternChars = src.localPatternChars;
760 }
761
762 /**
763 * Write out the default serializable data, after ensuring the
764 * <code>zoneStrings</code> field is initialized in order to make
765 * sure the backward compatibility.
766 *
767 * @since 1.6
768 */
769 private void writeObject(ObjectOutputStream stream) throws IOException {
770 if (zoneStrings == null) {
771 zoneStrings = TimeZoneNameUtility.getZoneStrings(locale);
772 }
773 stream.defaultWriteObject();
774 }
775
776 /**
777 * Obtains a DateFormatSymbols instance from a DateFormatSymbolsProvider
778 * implementation.
779 */
780 private static class DateFormatSymbolsGetter
781 implements LocaleServiceProviderPool.LocalizedObjectGetter<DateFormatSymbolsProvider,
782 DateFormatSymbols> {
783 private static final DateFormatSymbolsGetter INSTANCE =
784 new DateFormatSymbolsGetter();
785
786 public DateFormatSymbols getObject(DateFormatSymbolsProvider dateFormatSymbolsProvider,
787 Locale locale,
788 String key,
789 Object... params) {
790 assert params.length == 0;
791 return dateFormatSymbolsProvider.getInstance(locale);
792 }
793 }
794}
java/pkg/java/text/DecimalFormat.class+0−0

Binary file not shown.

java/pkg/java/text/DecimalFormat.java+3278−0
@@ -0,0 +1,3278 @@
1/*
2 * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26/*
27 * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
28 * (C) Copyright IBM Corp. 1996 - 1998 - All Rights Reserved
29 *
30 * The original version of this source code and documentation is copyrighted
31 * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
32 * materials are provided under terms of a License Agreement between Taligent
33 * and Sun. This technology is protected by multiple US and International
34 * patents. This notice and attribution to Taligent may not be removed.
35 * Taligent is a registered trademark of Taligent, Inc.
36 *
37 */
38
39package java.text;
40
41import java.io.InvalidObjectException;
42import java.io.IOException;
43import java.io.ObjectInputStream;
44import java.math.BigDecimal;
45import java.math.BigInteger;
46import java.math.RoundingMode;
47import java.util.ArrayList;
48import java.util.Currency;
49import java.util.Locale;
50import java.util.ResourceBundle;
51import java.util.concurrent.ConcurrentHashMap;
52import java.util.concurrent.ConcurrentMap;
53import java.util.concurrent.atomic.AtomicInteger;
54import java.util.concurrent.atomic.AtomicLong;
55import sun.util.resources.LocaleData;
56
57/**
58 * <code>DecimalFormat</code> is a concrete subclass of
59 * <code>NumberFormat</code> that formats decimal numbers. It has a variety of
60 * features designed to make it possible to parse and format numbers in any
61 * locale, including support for Western, Arabic, and Indic digits. It also
62 * supports different kinds of numbers, including integers (123), fixed-point
63 * numbers (123.4), scientific notation (1.23E4), percentages (12%), and
64 * currency amounts ($123). All of these can be localized.
65 *
66 * <p>To obtain a <code>NumberFormat</code> for a specific locale, including the
67 * default locale, call one of <code>NumberFormat</code>'s factory methods, such
68 * as <code>getInstance()</code>. In general, do not call the
69 * <code>DecimalFormat</code> constructors directly, since the
70 * <code>NumberFormat</code> factory methods may return subclasses other than
71 * <code>DecimalFormat</code>. If you need to customize the format object, do
72 * something like this:
73 *
74 * <blockquote><pre>
75 * NumberFormat f = NumberFormat.getInstance(loc);
76 * if (f instanceof DecimalFormat) {
77 * ((DecimalFormat) f).setDecimalSeparatorAlwaysShown(true);
78 * }
79 * </pre></blockquote>
80 *
81 * <p>A <code>DecimalFormat</code> comprises a <em>pattern</em> and a set of
82 * <em>symbols</em>. The pattern may be set directly using
83 * <code>applyPattern()</code>, or indirectly using the API methods. The
84 * symbols are stored in a <code>DecimalFormatSymbols</code> object. When using
85 * the <code>NumberFormat</code> factory methods, the pattern and symbols are
86 * read from localized <code>ResourceBundle</code>s.
87 *
88 * <h4>Patterns</h4>
89 *
90 * <code>DecimalFormat</code> patterns have the following syntax:
91 * <blockquote><pre>
92 * <i>Pattern:</i>
93 * <i>PositivePattern</i>
94 * <i>PositivePattern</i> ; <i>NegativePattern</i>
95 * <i>PositivePattern:</i>
96 * <i>Prefix<sub>opt</sub></i> <i>Number</i> <i>Suffix<sub>opt</sub></i>
97 * <i>NegativePattern:</i>
98 * <i>Prefix<sub>opt</sub></i> <i>Number</i> <i>Suffix<sub>opt</sub></i>
99 * <i>Prefix:</i>
100 * any Unicode characters except &#92;uFFFE, &#92;uFFFF, and special characters
101 * <i>Suffix:</i>
102 * any Unicode characters except &#92;uFFFE, &#92;uFFFF, and special characters
103 * <i>Number:</i>
104 * <i>Integer</i> <i>Exponent<sub>opt</sub></i>
105 * <i>Integer</i> . <i>Fraction</i> <i>Exponent<sub>opt</sub></i>
106 * <i>Integer:</i>
107 * <i>MinimumInteger</i>
108 * #
109 * # <i>Integer</i>
110 * # , <i>Integer</i>
111 * <i>MinimumInteger:</i>
112 * 0
113 * 0 <i>MinimumInteger</i>
114 * 0 , <i>MinimumInteger</i>
115 * <i>Fraction:</i>
116 * <i>MinimumFraction<sub>opt</sub></i> <i>OptionalFraction<sub>opt</sub></i>
117 * <i>MinimumFraction:</i>
118 * 0 <i>MinimumFraction<sub>opt</sub></i>
119 * <i>OptionalFraction:</i>
120 * # <i>OptionalFraction<sub>opt</sub></i>
121 * <i>Exponent:</i>
122 * E <i>MinimumExponent</i>
123 * <i>MinimumExponent:</i>
124 * 0 <i>MinimumExponent<sub>opt</sub></i>
125 * </pre></blockquote>
126 *
127 * <p>A <code>DecimalFormat</code> pattern contains a positive and negative
128 * subpattern, for example, <code>"#,##0.00;(#,##0.00)"</code>. Each
129 * subpattern has a prefix, numeric part, and suffix. The negative subpattern
130 * is optional; if absent, then the positive subpattern prefixed with the
131 * localized minus sign (<code>'-'</code> in most locales) is used as the
132 * negative subpattern. That is, <code>"0.00"</code> alone is equivalent to
133 * <code>"0.00;-0.00"</code>. If there is an explicit negative subpattern, it
134 * serves only to specify the negative prefix and suffix; the number of digits,
135 * minimal digits, and other characteristics are all the same as the positive
136 * pattern. That means that <code>"#,##0.0#;(#)"</code> produces precisely
137 * the same behavior as <code>"#,##0.0#;(#,##0.0#)"</code>.
138 *
139 * <p>The prefixes, suffixes, and various symbols used for infinity, digits,
140 * thousands separators, decimal separators, etc. may be set to arbitrary
141 * values, and they will appear properly during formatting. However, care must
142 * be taken that the symbols and strings do not conflict, or parsing will be
143 * unreliable. For example, either the positive and negative prefixes or the
144 * suffixes must be distinct for <code>DecimalFormat.parse()</code> to be able
145 * to distinguish positive from negative values. (If they are identical, then
146 * <code>DecimalFormat</code> will behave as if no negative subpattern was
147 * specified.) Another example is that the decimal separator and thousands
148 * separator should be distinct characters, or parsing will be impossible.
149 *
150 * <p>The grouping separator is commonly used for thousands, but in some
151 * countries it separates ten-thousands. The grouping size is a constant number
152 * of digits between the grouping characters, such as 3 for 100,000,000 or 4 for
153 * 1,0000,0000. If you supply a pattern with multiple grouping characters, the
154 * interval between the last one and the end of the integer is the one that is
155 * used. So <code>"#,##,###,####"</code> == <code>"######,####"</code> ==
156 * <code>"##,####,####"</code>.
157 *
158 * <h4>Special Pattern Characters</h4>
159 *
160 * <p>Many characters in a pattern are taken literally; they are matched during
161 * parsing and output unchanged during formatting. Special characters, on the
162 * other hand, stand for other characters, strings, or classes of characters.
163 * They must be quoted, unless noted otherwise, if they are to appear in the
164 * prefix or suffix as literals.
165 *
166 * <p>The characters listed here are used in non-localized patterns. Localized
167 * patterns use the corresponding characters taken from this formatter's
168 * <code>DecimalFormatSymbols</code> object instead, and these characters lose
169 * their special status. Two exceptions are the currency sign and quote, which
170 * are not localized.
171 *
172 * <blockquote>
173 * <table border=0 cellspacing=3 cellpadding=0 summary="Chart showing symbol,
174 * location, localized, and meaning.">
175 * <tr bgcolor="#ccccff">
176 * <th align=left>Symbol
177 * <th align=left>Location
178 * <th align=left>Localized?
179 * <th align=left>Meaning
180 * <tr valign=top>
181 * <td><code>0</code>
182 * <td>Number
183 * <td>Yes
184 * <td>Digit
185 * <tr valign=top bgcolor="#eeeeff">
186 * <td><code>#</code>
187 * <td>Number
188 * <td>Yes
189 * <td>Digit, zero shows as absent
190 * <tr valign=top>
191 * <td><code>.</code>
192 * <td>Number
193 * <td>Yes
194 * <td>Decimal separator or monetary decimal separator
195 * <tr valign=top bgcolor="#eeeeff">
196 * <td><code>-</code>
197 * <td>Number
198 * <td>Yes
199 * <td>Minus sign
200 * <tr valign=top>
201 * <td><code>,</code>
202 * <td>Number
203 * <td>Yes
204 * <td>Grouping separator
205 * <tr valign=top bgcolor="#eeeeff">
206 * <td><code>E</code>
207 * <td>Number
208 * <td>Yes
209 * <td>Separates mantissa and exponent in scientific notation.
210 * <em>Need not be quoted in prefix or suffix.</em>
211 * <tr valign=top>
212 * <td><code>;</code>
213 * <td>Subpattern boundary
214 * <td>Yes
215 * <td>Separates positive and negative subpatterns
216 * <tr valign=top bgcolor="#eeeeff">
217 * <td><code>%</code>
218 * <td>Prefix or suffix
219 * <td>Yes
220 * <td>Multiply by 100 and show as percentage
221 * <tr valign=top>
222 * <td><code>&#92;u2030</code>
223 * <td>Prefix or suffix
224 * <td>Yes
225 * <td>Multiply by 1000 and show as per mille value
226 * <tr valign=top bgcolor="#eeeeff">
227 * <td><code>&#164;</code> (<code>&#92;u00A4</code>)
228 * <td>Prefix or suffix
229 * <td>No
230 * <td>Currency sign, replaced by currency symbol. If
231 * doubled, replaced by international currency symbol.
232 * If present in a pattern, the monetary decimal separator
233 * is used instead of the decimal separator.
234 * <tr valign=top>
235 * <td><code>'</code>
236 * <td>Prefix or suffix
237 * <td>No
238 * <td>Used to quote special characters in a prefix or suffix,
239 * for example, <code>"'#'#"</code> formats 123 to
240 * <code>"#123"</code>. To create a single quote
241 * itself, use two in a row: <code>"# o''clock"</code>.
242 * </table>
243 * </blockquote>
244 *
245 * <h4>Scientific Notation</h4>
246 *
247 * <p>Numbers in scientific notation are expressed as the product of a mantissa
248 * and a power of ten, for example, 1234 can be expressed as 1.234 x 10^3. The
249 * mantissa is often in the range 1.0 <= x < 10.0, but it need not be.
250 * <code>DecimalFormat</code> can be instructed to format and parse scientific
251 * notation <em>only via a pattern</em>; there is currently no factory method
252 * that creates a scientific notation format. In a pattern, the exponent
253 * character immediately followed by one or more digit characters indicates
254 * scientific notation. Example: <code>"0.###E0"</code> formats the number
255 * 1234 as <code>"1.234E3"</code>.
256 *
257 * <ul>
258 * <li>The number of digit characters after the exponent character gives the
259 * minimum exponent digit count. There is no maximum. Negative exponents are
260 * formatted using the localized minus sign, <em>not</em> the prefix and suffix
261 * from the pattern. This allows patterns such as <code>"0.###E0 m/s"</code>.
262 *
263 * <li>The minimum and maximum number of integer digits are interpreted
264 * together:
265 *
266 * <ul>
267 * <li>If the maximum number of integer digits is greater than their minimum number
268 * and greater than 1, it forces the exponent to be a multiple of the maximum
269 * number of integer digits, and the minimum number of integer digits to be
270 * interpreted as 1. The most common use of this is to generate
271 * <em>engineering notation</em>, in which the exponent is a multiple of three,
272 * e.g., <code>"##0.#####E0"</code>. Using this pattern, the number 12345
273 * formats to <code>"12.345E3"</code>, and 123456 formats to
274 * <code>"123.456E3"</code>.
275 *
276 * <li>Otherwise, the minimum number of integer digits is achieved by adjusting the
277 * exponent. Example: 0.00123 formatted with <code>"00.###E0"</code> yields
278 * <code>"12.3E-4"</code>.
279 * </ul>
280 *
281 * <li>The number of significant digits in the mantissa is the sum of the
282 * <em>minimum integer</em> and <em>maximum fraction</em> digits, and is
283 * unaffected by the maximum integer digits. For example, 12345 formatted with
284 * <code>"##0.##E0"</code> is <code>"12.3E3"</code>. To show all digits, set
285 * the significant digits count to zero. The number of significant digits
286 * does not affect parsing.
287 *
288 * <li>Exponential patterns may not contain grouping separators.
289 * </ul>
290 *
291 * <h4>Rounding</h4>
292 *
293 * <code>DecimalFormat</code> provides rounding modes defined in
294 * {@link java.math.RoundingMode} for formatting. By default, it uses
295 * {@link java.math.RoundingMode#HALF_EVEN RoundingMode.HALF_EVEN}.
296 *
297 * <h4>Digits</h4>
298 *
299 * For formatting, <code>DecimalFormat</code> uses the ten consecutive
300 * characters starting with the localized zero digit defined in the
301 * <code>DecimalFormatSymbols</code> object as digits. For parsing, these
302 * digits as well as all Unicode decimal digits, as defined by
303 * {@link Character#digit Character.digit}, are recognized.
304 *
305 * <h4>Special Values</h4>
306 *
307 * <p><code>NaN</code> is formatted as a string, which typically has a single character
308 * <code>&#92;uFFFD</code>. This string is determined by the
309 * <code>DecimalFormatSymbols</code> object. This is the only value for which
310 * the prefixes and suffixes are not used.
311 *
312 * <p>Infinity is formatted as a string, which typically has a single character
313 * <code>&#92;u221E</code>, with the positive or negative prefixes and suffixes
314 * applied. The infinity string is determined by the
315 * <code>DecimalFormatSymbols</code> object.
316 *
317 * <p>Negative zero (<code>"-0"</code>) parses to
318 * <ul>
319 * <li><code>BigDecimal(0)</code> if <code>isParseBigDecimal()</code> is
320 * true,
321 * <li><code>Long(0)</code> if <code>isParseBigDecimal()</code> is false
322 * and <code>isParseIntegerOnly()</code> is true,
323 * <li><code>Double(-0.0)</code> if both <code>isParseBigDecimal()</code>
324 * and <code>isParseIntegerOnly()</code> are false.
325 * </ul>
326 *
327 * <h4><a name="synchronization">Synchronization</a></h4>
328 *
329 * <p>
330 * Decimal formats are generally not synchronized.
331 * It is recommended to create separate format instances for each thread.
332 * If multiple threads access a format concurrently, it must be synchronized
333 * externally.
334 *
335 * <h4>Example</h4>
336 *
337 * <blockquote><pre>
338 * <strong>// Print out a number using the localized number, integer, currency,
339 * // and percent format for each locale</strong>
340 * Locale[] locales = NumberFormat.getAvailableLocales();
341 * double myNumber = -1234.56;
342 * NumberFormat form;
343 * for (int j=0; j<4; ++j) {
344 * System.out.println("FORMAT");
345 * for (int i = 0; i < locales.length; ++i) {
346 * if (locales[i].getCountry().length() == 0) {
347 * continue; // Skip language-only locales
348 * }
349 * System.out.print(locales[i].getDisplayName());
350 * switch (j) {
351 * case 0:
352 * form = NumberFormat.getInstance(locales[i]); break;
353 * case 1:
354 * form = NumberFormat.getIntegerInstance(locales[i]); break;
355 * case 2:
356 * form = NumberFormat.getCurrencyInstance(locales[i]); break;
357 * default:
358 * form = NumberFormat.getPercentInstance(locales[i]); break;
359 * }
360 * if (form instanceof DecimalFormat) {
361 * System.out.print(": " + ((DecimalFormat) form).toPattern());
362 * }
363 * System.out.print(" -> " + form.format(myNumber));
364 * try {
365 * System.out.println(" -> " + form.parse(form.format(myNumber)));
366 * } catch (ParseException e) {}
367 * }
368 * }
369 * </pre></blockquote>
370 *
371 * @see <a href="http://java.sun.com/docs/books/tutorial/i18n/format/decimalFormat.html">Java Tutorial</a>
372 * @see NumberFormat
373 * @see DecimalFormatSymbols
374 * @see ParsePosition
375 * @author Mark Davis
376 * @author Alan Liu
377 */
378public class DecimalFormat extends NumberFormat {
379
380 /**
381 * Creates a DecimalFormat using the default pattern and symbols
382 * for the default locale. This is a convenient way to obtain a
383 * DecimalFormat when internationalization is not the main concern.
384 * <p>
385 * To obtain standard formats for a given locale, use the factory methods
386 * on NumberFormat such as getNumberInstance. These factories will
387 * return the most appropriate sub-class of NumberFormat for a given
388 * locale.
389 *
390 * @see java.text.NumberFormat#getInstance
391 * @see java.text.NumberFormat#getNumberInstance
392 * @see java.text.NumberFormat#getCurrencyInstance
393 * @see java.text.NumberFormat#getPercentInstance
394 */
395 public DecimalFormat() {
396 Locale def = Locale.getDefault(Locale.Category.FORMAT);
397 // try to get the pattern from the cache
398 String pattern = cachedLocaleData.get(def);
399 if (pattern == null) { /* cache miss */
400 // Get the pattern for the default locale.
401 ResourceBundle rb = LocaleData.getNumberFormatData(def);
402 String[] all = rb.getStringArray("NumberPatterns");
403 pattern = all[0];
404 /* update cache */
405 cachedLocaleData.putIfAbsent(def, pattern);
406 }
407
408 // Always applyPattern after the symbols are set
409 this.symbols = new DecimalFormatSymbols(def);
410 applyPattern(pattern, false);
411 }
412
413
414 /**
415 * Creates a DecimalFormat using the given pattern and the symbols
416 * for the default locale. This is a convenient way to obtain a
417 * DecimalFormat when internationalization is not the main concern.
418 * <p>
419 * To obtain standard formats for a given locale, use the factory methods
420 * on NumberFormat such as getNumberInstance. These factories will
421 * return the most appropriate sub-class of NumberFormat for a given
422 * locale.
423 *
424 * @param pattern A non-localized pattern string.
425 * @exception NullPointerException if <code>pattern</code> is null
426 * @exception IllegalArgumentException if the given pattern is invalid.
427 * @see java.text.NumberFormat#getInstance
428 * @see java.text.NumberFormat#getNumberInstance
429 * @see java.text.NumberFormat#getCurrencyInstance
430 * @see java.text.NumberFormat#getPercentInstance
431 */
432 public DecimalFormat(String pattern) {
433 // Always applyPattern after the symbols are set
434 this.symbols = new DecimalFormatSymbols(Locale.getDefault(Locale.Category.FORMAT));
435 applyPattern(pattern, false);
436 }
437
438
439 /**
440 * Creates a DecimalFormat using the given pattern and symbols.
441 * Use this constructor when you need to completely customize the
442 * behavior of the format.
443 * <p>
444 * To obtain standard formats for a given
445 * locale, use the factory methods on NumberFormat such as
446 * getInstance or getCurrencyInstance. If you need only minor adjustments
447 * to a standard format, you can modify the format returned by
448 * a NumberFormat factory method.
449 *
450 * @param pattern a non-localized pattern string
451 * @param symbols the set of symbols to be used
452 * @exception NullPointerException if any of the given arguments is null
453 * @exception IllegalArgumentException if the given pattern is invalid
454 * @see java.text.NumberFormat#getInstance
455 * @see java.text.NumberFormat#getNumberInstance
456 * @see java.text.NumberFormat#getCurrencyInstance
457 * @see java.text.NumberFormat#getPercentInstance
458 * @see java.text.DecimalFormatSymbols
459 */
460 public DecimalFormat (String pattern, DecimalFormatSymbols symbols) {
461 // Always applyPattern after the symbols are set
462 this.symbols = (DecimalFormatSymbols)symbols.clone();
463 applyPattern(pattern, false);
464 }
465
466
467 // Overrides
468 /**
469 * Formats a number and appends the resulting text to the given string
470 * buffer.
471 * The number can be of any subclass of {@link java.lang.Number}.
472 * <p>
473 * This implementation uses the maximum precision permitted.
474 * @param number the number to format
475 * @param toAppendTo the <code>StringBuffer</code> to which the formatted
476 * text is to be appended
477 * @param pos On input: an alignment field, if desired.
478 * On output: the offsets of the alignment field.
479 * @return the value passed in as <code>toAppendTo</code>
480 * @exception IllegalArgumentException if <code>number</code> is
481 * null or not an instance of <code>Number</code>.
482 * @exception NullPointerException if <code>toAppendTo</code> or
483 * <code>pos</code> is null
484 * @exception ArithmeticException if rounding is needed with rounding
485 * mode being set to RoundingMode.UNNECESSARY
486 * @see java.text.FieldPosition
487 */
488 public final StringBuffer format(Object number,
489 StringBuffer toAppendTo,
490 FieldPosition pos) {
491 if (number instanceof Long || number instanceof Integer ||
492 number instanceof Short || number instanceof Byte ||
493 number instanceof AtomicInteger ||
494 number instanceof AtomicLong ||
495 (number instanceof BigInteger &&
496 ((BigInteger)number).bitLength () < 64)) {
497 return format(((Number)number).longValue(), toAppendTo, pos);
498 } else if (number instanceof BigDecimal) {
499 return format((BigDecimal)number, toAppendTo, pos);
500 } else if (number instanceof BigInteger) {
501 return format((BigInteger)number, toAppendTo, pos);
502 } else if (number instanceof Number) {
503 return format(((Number)number).doubleValue(), toAppendTo, pos);
504 } else {
505 throw new IllegalArgumentException("Cannot format given Object as a Number");
506 }
507 }
508
509 /**
510 * Formats a double to produce a string.
511 * @param number The double to format
512 * @param result where the text is to be appended
513 * @param fieldPosition On input: an alignment field, if desired.
514 * On output: the offsets of the alignment field.
515 * @exception ArithmeticException if rounding is needed with rounding
516 * mode being set to RoundingMode.UNNECESSARY
517 * @return The formatted number string
518 * @see java.text.FieldPosition
519 */
520 public StringBuffer format(double number, StringBuffer result,
521 FieldPosition fieldPosition) {
522 fieldPosition.setBeginIndex(0);
523 fieldPosition.setEndIndex(0);
524
525 return format(number, result, fieldPosition.getFieldDelegate());
526 }
527
528 /**
529 * Formats a double to produce a string.
530 * @param number The double to format
531 * @param result where the text is to be appended
532 * @param delegate notified of locations of sub fields
533 * @exception ArithmeticException if rounding is needed with rounding
534 * mode being set to RoundingMode.UNNECESSARY
535 * @return The formatted number string
536 */
537 private StringBuffer format(double number, StringBuffer result,
538 FieldDelegate delegate) {
539 if (Double.isNaN(number) ||
540 (Double.isInfinite(number) && multiplier == 0)) {
541 int iFieldStart = result.length();
542 result.append(symbols.getNaN());
543 delegate.formatted(INTEGER_FIELD, Field.INTEGER, Field.INTEGER,
544 iFieldStart, result.length(), result);
545 return result;
546 }
547
548 /* Detecting whether a double is negative is easy with the exception of
549 * the value -0.0. This is a double which has a zero mantissa (and
550 * exponent), but a negative sign bit. It is semantically distinct from
551 * a zero with a positive sign bit, and this distinction is important
552 * to certain kinds of computations. However, it's a little tricky to
553 * detect, since (-0.0 == 0.0) and !(-0.0 < 0.0). How then, you may
554 * ask, does it behave distinctly from +0.0? Well, 1/(-0.0) ==
555 * -Infinity. Proper detection of -0.0 is needed to deal with the
556 * issues raised by bugs 4106658, 4106667, and 4147706. Liu 7/6/98.
557 */
558 boolean isNegative = ((number < 0.0) || (number == 0.0 && 1/number < 0.0)) ^ (multiplier < 0);
559
560 if (multiplier != 1) {
561 number *= multiplier;
562 }
563
564 if (Double.isInfinite(number)) {
565 if (isNegative) {
566 append(result, negativePrefix, delegate,
567 getNegativePrefixFieldPositions(), Field.SIGN);
568 } else {
569 append(result, positivePrefix, delegate,
570 getPositivePrefixFieldPositions(), Field.SIGN);
571 }
572
573 int iFieldStart = result.length();
574 result.append(symbols.getInfinity());
575 delegate.formatted(INTEGER_FIELD, Field.INTEGER, Field.INTEGER,
576 iFieldStart, result.length(), result);
577
578 if (isNegative) {
579 append(result, negativeSuffix, delegate,
580 getNegativeSuffixFieldPositions(), Field.SIGN);
581 } else {
582 append(result, positiveSuffix, delegate,
583 getPositiveSuffixFieldPositions(), Field.SIGN);
584 }
585
586 return result;
587 }
588
589 if (isNegative) {
590 number = -number;
591 }
592
593 // at this point we are guaranteed a nonnegative finite number.
594 assert(number >= 0 && !Double.isInfinite(number));
595
596 synchronized(digitList) {
597 int maxIntDigits = super.getMaximumIntegerDigits();
598 int minIntDigits = super.getMinimumIntegerDigits();
599 int maxFraDigits = super.getMaximumFractionDigits();
600 int minFraDigits = super.getMinimumFractionDigits();
601
602 digitList.set(isNegative, number, useExponentialNotation ?
603 maxIntDigits + maxFraDigits : maxFraDigits,
604 !useExponentialNotation);
605 return subformat(result, delegate, isNegative, false,
606 maxIntDigits, minIntDigits, maxFraDigits, minFraDigits);
607 }
608 }
609
610 /**
611 * Format a long to produce a string.
612 * @param number The long to format
613 * @param result where the text is to be appended
614 * @param fieldPosition On input: an alignment field, if desired.
615 * On output: the offsets of the alignment field.
616 * @exception ArithmeticException if rounding is needed with rounding
617 * mode being set to RoundingMode.UNNECESSARY
618 * @return The formatted number string
619 * @see java.text.FieldPosition
620 */
621 public StringBuffer format(long number, StringBuffer result,
622 FieldPosition fieldPosition) {
623 fieldPosition.setBeginIndex(0);
624 fieldPosition.setEndIndex(0);
625
626 return format(number, result, fieldPosition.getFieldDelegate());
627 }
628
629 /**
630 * Format a long to produce a string.
631 * @param number The long to format
632 * @param result where the text is to be appended
633 * @param delegate notified of locations of sub fields
634 * @return The formatted number string
635 * @exception ArithmeticException if rounding is needed with rounding
636 * mode being set to RoundingMode.UNNECESSARY
637 * @see java.text.FieldPosition
638 */
639 private StringBuffer format(long number, StringBuffer result,
640 FieldDelegate delegate) {
641 boolean isNegative = (number < 0);
642 if (isNegative) {
643 number = -number;
644 }
645
646 // In general, long values always represent real finite numbers, so
647 // we don't have to check for +/- Infinity or NaN. However, there
648 // is one case we have to be careful of: The multiplier can push
649 // a number near MIN_VALUE or MAX_VALUE outside the legal range. We
650 // check for this before multiplying, and if it happens we use
651 // BigInteger instead.
652 boolean useBigInteger = false;
653 if (number < 0) { // This can only happen if number == Long.MIN_VALUE.
654 if (multiplier != 0) {
655 useBigInteger = true;
656 }
657 } else if (multiplier != 1 && multiplier != 0) {
658 long cutoff = Long.MAX_VALUE / multiplier;
659 if (cutoff < 0) {
660 cutoff = -cutoff;
661 }
662 useBigInteger = (number > cutoff);
663 }
664
665 if (useBigInteger) {
666 if (isNegative) {
667 number = -number;
668 }
669 BigInteger bigIntegerValue = BigInteger.valueOf(number);
670 return format(bigIntegerValue, result, delegate, true);
671 }
672
673 number *= multiplier;
674 if (number == 0) {
675 isNegative = false;
676 } else {
677 if (multiplier < 0) {
678 number = -number;
679 isNegative = !isNegative;
680 }
681 }
682
683 synchronized(digitList) {
684 int maxIntDigits = super.getMaximumIntegerDigits();
685 int minIntDigits = super.getMinimumIntegerDigits();
686 int maxFraDigits = super.getMaximumFractionDigits();
687 int minFraDigits = super.getMinimumFractionDigits();
688
689 digitList.set(isNegative, number,
690 useExponentialNotation ? maxIntDigits + maxFraDigits : 0);
691
692 return subformat(result, delegate, isNegative, true,
693 maxIntDigits, minIntDigits, maxFraDigits, minFraDigits);
694 }
695 }
696
697 /**
698 * Formats a BigDecimal to produce a string.
699 * @param number The BigDecimal to format
700 * @param result where the text is to be appended
701 * @param fieldPosition On input: an alignment field, if desired.
702 * On output: the offsets of the alignment field.
703 * @return The formatted number string
704 * @exception ArithmeticException if rounding is needed with rounding
705 * mode being set to RoundingMode.UNNECESSARY
706 * @see java.text.FieldPosition
707 */
708 private StringBuffer format(BigDecimal number, StringBuffer result,
709 FieldPosition fieldPosition) {
710 fieldPosition.setBeginIndex(0);
711 fieldPosition.setEndIndex(0);
712 return format(number, result, fieldPosition.getFieldDelegate());
713 }
714
715 /**
716 * Formats a BigDecimal to produce a string.
717 * @param number The BigDecimal to format
718 * @param result where the text is to be appended
719 * @param delegate notified of locations of sub fields
720 * @exception ArithmeticException if rounding is needed with rounding
721 * mode being set to RoundingMode.UNNECESSARY
722 * @return The formatted number string
723 */
724 private StringBuffer format(BigDecimal number, StringBuffer result,
725 FieldDelegate delegate) {
726 if (multiplier != 1) {
727 number = number.multiply(getBigDecimalMultiplier());
728 }
729 boolean isNegative = number.signum() == -1;
730 if (isNegative) {
731 number = number.negate();
732 }
733
734 synchronized(digitList) {
735 int maxIntDigits = getMaximumIntegerDigits();
736 int minIntDigits = getMinimumIntegerDigits();
737 int maxFraDigits = getMaximumFractionDigits();
738 int minFraDigits = getMinimumFractionDigits();
739 int maximumDigits = maxIntDigits + maxFraDigits;
740
741 digitList.set(isNegative, number, useExponentialNotation ?
742 ((maximumDigits < 0) ? Integer.MAX_VALUE : maximumDigits) :
743 maxFraDigits, !useExponentialNotation);
744
745 return subformat(result, delegate, isNegative, false,
746 maxIntDigits, minIntDigits, maxFraDigits, minFraDigits);
747 }
748 }
749
750 /**
751 * Format a BigInteger to produce a string.
752 * @param number The BigInteger to format
753 * @param result where the text is to be appended
754 * @param fieldPosition On input: an alignment field, if desired.
755 * On output: the offsets of the alignment field.
756 * @return The formatted number string
757 * @exception ArithmeticException if rounding is needed with rounding
758 * mode being set to RoundingMode.UNNECESSARY
759 * @see java.text.FieldPosition
760 */
761 private StringBuffer format(BigInteger number, StringBuffer result,
762 FieldPosition fieldPosition) {
763 fieldPosition.setBeginIndex(0);
764 fieldPosition.setEndIndex(0);
765
766 return format(number, result, fieldPosition.getFieldDelegate(), false);
767 }
768
769 /**
770 * Format a BigInteger to produce a string.
771 * @param number The BigInteger to format
772 * @param result where the text is to be appended
773 * @param delegate notified of locations of sub fields
774 * @return The formatted number string
775 * @exception ArithmeticException if rounding is needed with rounding
776 * mode being set to RoundingMode.UNNECESSARY
777 * @see java.text.FieldPosition
778 */
779 private StringBuffer format(BigInteger number, StringBuffer result,
780 FieldDelegate delegate, boolean formatLong) {
781 if (multiplier != 1) {
782 number = number.multiply(getBigIntegerMultiplier());
783 }
784 boolean isNegative = number.signum() == -1;
785 if (isNegative) {
786 number = number.negate();
787 }
788
789 synchronized(digitList) {
790 int maxIntDigits, minIntDigits, maxFraDigits, minFraDigits, maximumDigits;
791 if (formatLong) {
792 maxIntDigits = super.getMaximumIntegerDigits();
793 minIntDigits = super.getMinimumIntegerDigits();
794 maxFraDigits = super.getMaximumFractionDigits();
795 minFraDigits = super.getMinimumFractionDigits();
796 maximumDigits = maxIntDigits + maxFraDigits;
797 } else {
798 maxIntDigits = getMaximumIntegerDigits();
799 minIntDigits = getMinimumIntegerDigits();
800 maxFraDigits = getMaximumFractionDigits();
801 minFraDigits = getMinimumFractionDigits();
802 maximumDigits = maxIntDigits + maxFraDigits;
803 if (maximumDigits < 0) {
804 maximumDigits = Integer.MAX_VALUE;
805 }
806 }
807
808 digitList.set(isNegative, number,
809 useExponentialNotation ? maximumDigits : 0);
810
811 return subformat(result, delegate, isNegative, true,
812 maxIntDigits, minIntDigits, maxFraDigits, minFraDigits);
813 }
814 }
815
816 /**
817 * Formats an Object producing an <code>AttributedCharacterIterator</code>.
818 * You can use the returned <code>AttributedCharacterIterator</code>
819 * to build the resulting String, as well as to determine information
820 * about the resulting String.
821 * <p>
822 * Each attribute key of the AttributedCharacterIterator will be of type
823 * <code>NumberFormat.Field</code>, with the attribute value being the
824 * same as the attribute key.
825 *
826 * @exception NullPointerException if obj is null.
827 * @exception IllegalArgumentException when the Format cannot format the
828 * given object.
829 * @exception ArithmeticException if rounding is needed with rounding
830 * mode being set to RoundingMode.UNNECESSARY
831 * @param obj The object to format
832 * @return AttributedCharacterIterator describing the formatted value.
833 * @since 1.4
834 */
835 public AttributedCharacterIterator formatToCharacterIterator(Object obj) {
836 CharacterIteratorFieldDelegate delegate =
837 new CharacterIteratorFieldDelegate();
838 StringBuffer sb = new StringBuffer();
839
840 if (obj instanceof Double || obj instanceof Float) {
841 format(((Number)obj).doubleValue(), sb, delegate);
842 } else if (obj instanceof Long || obj instanceof Integer ||
843 obj instanceof Short || obj instanceof Byte ||
844 obj instanceof AtomicInteger || obj instanceof AtomicLong) {
845 format(((Number)obj).longValue(), sb, delegate);
846 } else if (obj instanceof BigDecimal) {
847 format((BigDecimal)obj, sb, delegate);
848 } else if (obj instanceof BigInteger) {
849 format((BigInteger)obj, sb, delegate, false);
850 } else if (obj == null) {
851 throw new NullPointerException(
852 "formatToCharacterIterator must be passed non-null object");
853 } else {
854 throw new IllegalArgumentException(
855 "Cannot format given Object as a Number");
856 }
857 return delegate.getIterator(sb.toString());
858 }
859
860 /**
861 * Complete the formatting of a finite number. On entry, the digitList must
862 * be filled in with the correct digits.
863 */
864 private StringBuffer subformat(StringBuffer result, FieldDelegate delegate,
865 boolean isNegative, boolean isInteger,
866 int maxIntDigits, int minIntDigits,
867 int maxFraDigits, int minFraDigits) {
868 // NOTE: This isn't required anymore because DigitList takes care of this.
869 //
870 // // The negative of the exponent represents the number of leading
871 // // zeros between the decimal and the first non-zero digit, for
872 // // a value < 0.1 (e.g., for 0.00123, -fExponent == 2). If this
873 // // is more than the maximum fraction digits, then we have an underflow
874 // // for the printed representation. We recognize this here and set
875 // // the DigitList representation to zero in this situation.
876 //
877 // if (-digitList.decimalAt >= getMaximumFractionDigits())
878 // {
879 // digitList.count = 0;
880 // }
881
882 char zero = symbols.getZeroDigit();
883 int zeroDelta = zero - '0'; // '0' is the DigitList representation of zero
884 char grouping = symbols.getGroupingSeparator();
885 char decimal = isCurrencyFormat ?
886 symbols.getMonetaryDecimalSeparator() :
887 symbols.getDecimalSeparator();
888
889 /* Per bug 4147706, DecimalFormat must respect the sign of numbers which
890 * format as zero. This allows sensible computations and preserves
891 * relations such as signum(1/x) = signum(x), where x is +Infinity or
892 * -Infinity. Prior to this fix, we always formatted zero values as if
893 * they were positive. Liu 7/6/98.
894 */
895 if (digitList.isZero()) {
896 digitList.decimalAt = 0; // Normalize
897 }
898
899 if (isNegative) {
900 append(result, negativePrefix, delegate,
901 getNegativePrefixFieldPositions(), Field.SIGN);
902 } else {
903 append(result, positivePrefix, delegate,
904 getPositivePrefixFieldPositions(), Field.SIGN);
905 }
906
907 if (useExponentialNotation) {
908 int iFieldStart = result.length();
909 int iFieldEnd = -1;
910 int fFieldStart = -1;
911
912 // Minimum integer digits are handled in exponential format by
913 // adjusting the exponent. For example, 0.01234 with 3 minimum
914 // integer digits is "123.4E-4".
915
916 // Maximum integer digits are interpreted as indicating the
917 // repeating range. This is useful for engineering notation, in
918 // which the exponent is restricted to a multiple of 3. For
919 // example, 0.01234 with 3 maximum integer digits is "12.34e-3".
920 // If maximum integer digits are > 1 and are larger than
921 // minimum integer digits, then minimum integer digits are
922 // ignored.
923 int exponent = digitList.decimalAt;
924 int repeat = maxIntDigits;
925 int minimumIntegerDigits = minIntDigits;
926 if (repeat > 1 && repeat > minIntDigits) {
927 // A repeating range is defined; adjust to it as follows.
928 // If repeat == 3, we have 6,5,4=>3; 3,2,1=>0; 0,-1,-2=>-3;
929 // -3,-4,-5=>-6, etc. This takes into account that the
930 // exponent we have here is off by one from what we expect;
931 // it is for the format 0.MMMMMx10^n.
932 if (exponent >= 1) {
933 exponent = ((exponent - 1) / repeat) * repeat;
934 } else {
935 // integer division rounds towards 0
936 exponent = ((exponent - repeat) / repeat) * repeat;
937 }
938 minimumIntegerDigits = 1;
939 } else {
940 // No repeating range is defined; use minimum integer digits.
941 exponent -= minimumIntegerDigits;
942 }
943
944 // We now output a minimum number of digits, and more if there
945 // are more digits, up to the maximum number of digits. We
946 // place the decimal point after the "integer" digits, which
947 // are the first (decimalAt - exponent) digits.
948 int minimumDigits = minIntDigits + minFraDigits;
949 if (minimumDigits < 0) { // overflow?
950 minimumDigits = Integer.MAX_VALUE;
951 }
952
953 // The number of integer digits is handled specially if the number
954 // is zero, since then there may be no digits.
955 int integerDigits = digitList.isZero() ? minimumIntegerDigits :
956 digitList.decimalAt - exponent;
957 if (minimumDigits < integerDigits) {
958 minimumDigits = integerDigits;
959 }
960 int totalDigits = digitList.count;
961 if (minimumDigits > totalDigits) {
962 totalDigits = minimumDigits;
963 }
964 boolean addedDecimalSeparator = false;
965
966 for (int i=0; i<totalDigits; ++i) {
967 if (i == integerDigits) {
968 // Record field information for caller.
969 iFieldEnd = result.length();
970
971 result.append(decimal);
972 addedDecimalSeparator = true;
973
974 // Record field information for caller.
975 fFieldStart = result.length();
976 }
977 result.append((i < digitList.count) ?
978 (char)(digitList.digits[i] + zeroDelta) :
979 zero);
980 }
981
982 if (decimalSeparatorAlwaysShown && totalDigits == integerDigits) {
983 // Record field information for caller.
984 iFieldEnd = result.length();
985
986 result.append(decimal);
987 addedDecimalSeparator = true;
988
989 // Record field information for caller.
990 fFieldStart = result.length();
991 }
992
993 // Record field information
994 if (iFieldEnd == -1) {
995 iFieldEnd = result.length();
996 }
997 delegate.formatted(INTEGER_FIELD, Field.INTEGER, Field.INTEGER,
998 iFieldStart, iFieldEnd, result);
999 if (addedDecimalSeparator) {
1000 delegate.formatted(Field.DECIMAL_SEPARATOR,
1001 Field.DECIMAL_SEPARATOR,
1002 iFieldEnd, fFieldStart, result);
1003 }
1004 if (fFieldStart == -1) {
1005 fFieldStart = result.length();
1006 }
1007 delegate.formatted(FRACTION_FIELD, Field.FRACTION, Field.FRACTION,
1008 fFieldStart, result.length(), result);
1009
1010 // The exponent is output using the pattern-specified minimum
1011 // exponent digits. There is no maximum limit to the exponent
1012 // digits, since truncating the exponent would result in an
1013 // unacceptable inaccuracy.
1014 int fieldStart = result.length();
1015
1016 result.append(symbols.getExponentSeparator());
1017
1018 delegate.formatted(Field.EXPONENT_SYMBOL, Field.EXPONENT_SYMBOL,
1019 fieldStart, result.length(), result);
1020
1021 // For zero values, we force the exponent to zero. We
1022 // must do this here, and not earlier, because the value
1023 // is used to determine integer digit count above.
1024 if (digitList.isZero()) {
1025 exponent = 0;
1026 }
1027
1028 boolean negativeExponent = exponent < 0;
1029 if (negativeExponent) {
1030 exponent = -exponent;
1031 fieldStart = result.length();
1032 result.append(symbols.getMinusSign());
1033 delegate.formatted(Field.EXPONENT_SIGN, Field.EXPONENT_SIGN,
1034 fieldStart, result.length(), result);
1035 }
1036 digitList.set(negativeExponent, exponent);
1037
1038 int eFieldStart = result.length();
1039
1040 for (int i=digitList.decimalAt; i<minExponentDigits; ++i) {
1041 result.append(zero);
1042 }
1043 for (int i=0; i<digitList.decimalAt; ++i) {
1044 result.append((i < digitList.count) ?
1045 (char)(digitList.digits[i] + zeroDelta) : zero);
1046 }
1047 delegate.formatted(Field.EXPONENT, Field.EXPONENT, eFieldStart,
1048 result.length(), result);
1049 } else {
1050 int iFieldStart = result.length();
1051
1052 // Output the integer portion. Here 'count' is the total
1053 // number of integer digits we will display, including both
1054 // leading zeros required to satisfy getMinimumIntegerDigits,
1055 // and actual digits present in the number.
1056 int count = minIntDigits;
1057 int digitIndex = 0; // Index into digitList.fDigits[]
1058 if (digitList.decimalAt > 0 && count < digitList.decimalAt) {
1059 count = digitList.decimalAt;
1060 }
1061
1062 // Handle the case where getMaximumIntegerDigits() is smaller
1063 // than the real number of integer digits. If this is so, we
1064 // output the least significant max integer digits. For example,
1065 // the value 1997 printed with 2 max integer digits is just "97".
1066 if (count > maxIntDigits) {
1067 count = maxIntDigits;
1068 digitIndex = digitList.decimalAt - count;
1069 }
1070
1071 int sizeBeforeIntegerPart = result.length();
1072 for (int i=count-1; i>=0; --i) {
1073 if (i < digitList.decimalAt && digitIndex < digitList.count) {
1074 // Output a real digit
1075 result.append((char)(digitList.digits[digitIndex++] + zeroDelta));
1076 } else {
1077 // Output a leading zero
1078 result.append(zero);
1079 }
1080
1081 // Output grouping separator if necessary. Don't output a
1082 // grouping separator if i==0 though; that's at the end of
1083 // the integer part.
1084 if (isGroupingUsed() && i>0 && (groupingSize != 0) &&
1085 (i % groupingSize == 0)) {
1086 int gStart = result.length();
1087 result.append(grouping);
1088 delegate.formatted(Field.GROUPING_SEPARATOR,
1089 Field.GROUPING_SEPARATOR, gStart,
1090 result.length(), result);
1091 }
1092 }
1093
1094 // Determine whether or not there are any printable fractional
1095 // digits. If we've used up the digits we know there aren't.
1096 boolean fractionPresent = (minFraDigits > 0) ||
1097 (!isInteger && digitIndex < digitList.count);
1098
1099 // If there is no fraction present, and we haven't printed any
1100 // integer digits, then print a zero. Otherwise we won't print
1101 // _any_ digits, and we won't be able to parse this string.
1102 if (!fractionPresent && result.length() == sizeBeforeIntegerPart) {
1103 result.append(zero);
1104 }
1105
1106 delegate.formatted(INTEGER_FIELD, Field.INTEGER, Field.INTEGER,
1107 iFieldStart, result.length(), result);
1108
1109 // Output the decimal separator if we always do so.
1110 int sStart = result.length();
1111 if (decimalSeparatorAlwaysShown || fractionPresent) {
1112 result.append(decimal);
1113 }
1114
1115 if (sStart != result.length()) {
1116 delegate.formatted(Field.DECIMAL_SEPARATOR,
1117 Field.DECIMAL_SEPARATOR,
1118 sStart, result.length(), result);
1119 }
1120 int fFieldStart = result.length();
1121
1122 for (int i=0; i < maxFraDigits; ++i) {
1123 // Here is where we escape from the loop. We escape if we've
1124 // output the maximum fraction digits (specified in the for
1125 // expression above).
1126 // We also stop when we've output the minimum digits and either:
1127 // we have an integer, so there is no fractional stuff to
1128 // display, or we're out of significant digits.
1129 if (i >= minFraDigits &&
1130 (isInteger || digitIndex >= digitList.count)) {
1131 break;
1132 }
1133
1134 // Output leading fractional zeros. These are zeros that come
1135 // after the decimal but before any significant digits. These
1136 // are only output if abs(number being formatted) < 1.0.
1137 if (-1-i > (digitList.decimalAt-1)) {
1138 result.append(zero);
1139 continue;
1140 }
1141
1142 // Output a digit, if we have any precision left, or a
1143 // zero if we don't. We don't want to output noise digits.
1144 if (!isInteger && digitIndex < digitList.count) {
1145 result.append((char)(digitList.digits[digitIndex++] + zeroDelta));
1146 } else {
1147 result.append(zero);
1148 }
1149 }
1150
1151 // Record field information for caller.
1152 delegate.formatted(FRACTION_FIELD, Field.FRACTION, Field.FRACTION,
1153 fFieldStart, result.length(), result);
1154 }
1155
1156 if (isNegative) {
1157 append(result, negativeSuffix, delegate,
1158 getNegativeSuffixFieldPositions(), Field.SIGN);
1159 }
1160 else {
1161 append(result, positiveSuffix, delegate,
1162 getPositiveSuffixFieldPositions(), Field.SIGN);
1163 }
1164
1165 return result;
1166 }
1167
1168 /**
1169 * Appends the String <code>string</code> to <code>result</code>.
1170 * <code>delegate</code> is notified of all the
1171 * <code>FieldPosition</code>s in <code>positions</code>.
1172 * <p>
1173 * If one of the <code>FieldPosition</code>s in <code>positions</code>
1174 * identifies a <code>SIGN</code> attribute, it is mapped to
1175 * <code>signAttribute</code>. This is used
1176 * to map the <code>SIGN</code> attribute to the <code>EXPONENT</code>
1177 * attribute as necessary.
1178 * <p>
1179 * This is used by <code>subformat</code> to add the prefix/suffix.
1180 */
1181 private void append(StringBuffer result, String string,
1182 FieldDelegate delegate,
1183 FieldPosition[] positions,
1184 Format.Field signAttribute) {
1185 int start = result.length();
1186
1187 if (string.length() > 0) {
1188 result.append(string);
1189 for (int counter = 0, max = positions.length; counter < max;
1190 counter++) {
1191 FieldPosition fp = positions[counter];
1192 Format.Field attribute = fp.getFieldAttribute();
1193
1194 if (attribute == Field.SIGN) {
1195 attribute = signAttribute;
1196 }
1197 delegate.formatted(attribute, attribute,
1198 start + fp.getBeginIndex(),
1199 start + fp.getEndIndex(), result);
1200 }
1201 }
1202 }
1203
1204 /**
1205 * Parses text from a string to produce a <code>Number</code>.
1206 * <p>
1207 * The method attempts to parse text starting at the index given by
1208 * <code>pos</code>.
1209 * If parsing succeeds, then the index of <code>pos</code> is updated
1210 * to the index after the last character used (parsing does not necessarily
1211 * use all characters up to the end of the string), and the parsed
1212 * number is returned. The updated <code>pos</code> can be used to
1213 * indicate the starting point for the next call to this method.
1214 * If an error occurs, then the index of <code>pos</code> is not
1215 * changed, the error index of <code>pos</code> is set to the index of
1216 * the character where the error occurred, and null is returned.
1217 * <p>
1218 * The subclass returned depends on the value of {@link #isParseBigDecimal}
1219 * as well as on the string being parsed.
1220 * <ul>
1221 * <li>If <code>isParseBigDecimal()</code> is false (the default),
1222 * most integer values are returned as <code>Long</code>
1223 * objects, no matter how they are written: <code>"17"</code> and
1224 * <code>"17.000"</code> both parse to <code>Long(17)</code>.
1225 * Values that cannot fit into a <code>Long</code> are returned as
1226 * <code>Double</code>s. This includes values with a fractional part,
1227 * infinite values, <code>NaN</code>, and the value -0.0.
1228 * <code>DecimalFormat</code> does <em>not</em> decide whether to
1229 * return a <code>Double</code> or a <code>Long</code> based on the
1230 * presence of a decimal separator in the source string. Doing so
1231 * would prevent integers that overflow the mantissa of a double,
1232 * such as <code>"-9,223,372,036,854,775,808.00"</code>, from being
1233 * parsed accurately.
1234 * <p>
1235 * Callers may use the <code>Number</code> methods
1236 * <code>doubleValue</code>, <code>longValue</code>, etc., to obtain
1237 * the type they want.
1238 * <li>If <code>isParseBigDecimal()</code> is true, values are returned
1239 * as <code>BigDecimal</code> objects. The values are the ones
1240 * constructed by {@link java.math.BigDecimal#BigDecimal(String)}
1241 * for corresponding strings in locale-independent format. The
1242 * special cases negative and positive infinity and NaN are returned
1243 * as <code>Double</code> instances holding the values of the
1244 * corresponding <code>Double</code> constants.
1245 * </ul>
1246 * <p>
1247 * <code>DecimalFormat</code> parses all Unicode characters that represent
1248 * decimal digits, as defined by <code>Character.digit()</code>. In
1249 * addition, <code>DecimalFormat</code> also recognizes as digits the ten
1250 * consecutive characters starting with the localized zero digit defined in
1251 * the <code>DecimalFormatSymbols</code> object.
1252 *
1253 * @param text the string to be parsed
1254 * @param pos A <code>ParsePosition</code> object with index and error
1255 * index information as described above.
1256 * @return the parsed value, or <code>null</code> if the parse fails
1257 * @exception NullPointerException if <code>text</code> or
1258 * <code>pos</code> is null.
1259 */
1260 public Number parse(String text, ParsePosition pos) {
1261 // special case NaN
1262 if (text.regionMatches(pos.index, symbols.getNaN(), 0, symbols.getNaN().length())) {
1263 pos.index = pos.index + symbols.getNaN().length();
1264 return new Double(Double.NaN);
1265 }
1266
1267 boolean[] status = new boolean[STATUS_LENGTH];
1268 if (!subparse(text, pos, positivePrefix, negativePrefix, digitList, false, status)) {
1269 return null;
1270 }
1271
1272 // special case INFINITY
1273 if (status[STATUS_INFINITE]) {
1274 if (status[STATUS_POSITIVE] == (multiplier >= 0)) {
1275 return new Double(Double.POSITIVE_INFINITY);
1276 } else {
1277 return new Double(Double.NEGATIVE_INFINITY);
1278 }
1279 }
1280
1281 if (multiplier == 0) {
1282 if (digitList.isZero()) {
1283 return new Double(Double.NaN);
1284 } else if (status[STATUS_POSITIVE]) {
1285 return new Double(Double.POSITIVE_INFINITY);
1286 } else {
1287 return new Double(Double.NEGATIVE_INFINITY);
1288 }
1289 }
1290
1291 if (isParseBigDecimal()) {
1292 BigDecimal bigDecimalResult = digitList.getBigDecimal();
1293
1294 if (multiplier != 1) {
1295 try {
1296 bigDecimalResult = bigDecimalResult.divide(getBigDecimalMultiplier());
1297 }
1298 catch (ArithmeticException e) { // non-terminating decimal expansion
1299 bigDecimalResult = bigDecimalResult.divide(getBigDecimalMultiplier(), roundingMode);
1300 }
1301 }
1302
1303 if (!status[STATUS_POSITIVE]) {
1304 bigDecimalResult = bigDecimalResult.negate();
1305 }
1306 return bigDecimalResult;
1307 } else {
1308 boolean gotDouble = true;
1309 boolean gotLongMinimum = false;
1310 double doubleResult = 0.0;
1311 long longResult = 0;
1312
1313 // Finally, have DigitList parse the digits into a value.
1314 if (digitList.fitsIntoLong(status[STATUS_POSITIVE], isParseIntegerOnly())) {
1315 gotDouble = false;
1316 longResult = digitList.getLong();
1317 if (longResult < 0) { // got Long.MIN_VALUE
1318 gotLongMinimum = true;
1319 }
1320 } else {
1321 doubleResult = digitList.getDouble();
1322 }
1323
1324 // Divide by multiplier. We have to be careful here not to do
1325 // unneeded conversions between double and long.
1326 if (multiplier != 1) {
1327 if (gotDouble) {
1328 doubleResult /= multiplier;
1329 } else {
1330 // Avoid converting to double if we can
1331 if (longResult % multiplier == 0) {
1332 longResult /= multiplier;
1333 } else {
1334 doubleResult = ((double)longResult) / multiplier;
1335 gotDouble = true;
1336 }
1337 }
1338 }
1339
1340 if (!status[STATUS_POSITIVE] && !gotLongMinimum) {
1341 doubleResult = -doubleResult;
1342 longResult = -longResult;
1343 }
1344
1345 // At this point, if we divided the result by the multiplier, the
1346 // result may fit into a long. We check for this case and return
1347 // a long if possible.
1348 // We must do this AFTER applying the negative (if appropriate)
1349 // in order to handle the case of LONG_MIN; otherwise, if we do
1350 // this with a positive value -LONG_MIN, the double is > 0, but
1351 // the long is < 0. We also must retain a double in the case of
1352 // -0.0, which will compare as == to a long 0 cast to a double
1353 // (bug 4162852).
1354 if (multiplier != 1 && gotDouble) {
1355 longResult = (long)doubleResult;
1356 gotDouble = ((doubleResult != (double)longResult) ||
1357 (doubleResult == 0.0 && 1/doubleResult < 0.0)) &&
1358 !isParseIntegerOnly();
1359 }
1360
1361 return gotDouble ?
1362 (Number)new Double(doubleResult) : (Number)new Long(longResult);
1363 }
1364 }
1365
1366 /**
1367 * Return a BigInteger multiplier.
1368 */
1369 private BigInteger getBigIntegerMultiplier() {
1370 if (bigIntegerMultiplier == null) {
1371 bigIntegerMultiplier = BigInteger.valueOf(multiplier);
1372 }
1373 return bigIntegerMultiplier;
1374 }
1375 private transient BigInteger bigIntegerMultiplier;
1376
1377 /**
1378 * Return a BigDecimal multiplier.
1379 */
1380 private BigDecimal getBigDecimalMultiplier() {
1381 if (bigDecimalMultiplier == null) {
1382 bigDecimalMultiplier = new BigDecimal(multiplier);
1383 }
1384 return bigDecimalMultiplier;
1385 }
1386 private transient BigDecimal bigDecimalMultiplier;
1387
1388 private static final int STATUS_INFINITE = 0;
1389 private static final int STATUS_POSITIVE = 1;
1390 private static final int STATUS_LENGTH = 2;
1391
1392 /**
1393 * Parse the given text into a number. The text is parsed beginning at
1394 * parsePosition, until an unparseable character is seen.
1395 * @param text The string to parse.
1396 * @param parsePosition The position at which to being parsing. Upon
1397 * return, the first unparseable character.
1398 * @param digits The DigitList to set to the parsed value.
1399 * @param isExponent If true, parse an exponent. This means no
1400 * infinite values and integer only.
1401 * @param status Upon return contains boolean status flags indicating
1402 * whether the value was infinite and whether it was positive.
1403 */
1404 private final boolean subparse(String text, ParsePosition parsePosition,
1405 String positivePrefix, String negativePrefix,
1406 DigitList digits, boolean isExponent,
1407 boolean status[]) {
1408 int position = parsePosition.index;
1409 int oldStart = parsePosition.index;
1410 int backup;
1411 boolean gotPositive, gotNegative;
1412
1413 // check for positivePrefix; take longest
1414 gotPositive = text.regionMatches(position, positivePrefix, 0,
1415 positivePrefix.length());
1416 gotNegative = text.regionMatches(position, negativePrefix, 0,
1417 negativePrefix.length());
1418
1419 if (gotPositive && gotNegative) {
1420 if (positivePrefix.length() > negativePrefix.length()) {
1421 gotNegative = false;
1422 } else if (positivePrefix.length() < negativePrefix.length()) {
1423 gotPositive = false;
1424 }
1425 }
1426
1427 if (gotPositive) {
1428 position += positivePrefix.length();
1429 } else if (gotNegative) {
1430 position += negativePrefix.length();
1431 } else {
1432 parsePosition.errorIndex = position;
1433 return false;
1434 }
1435
1436 // process digits or Inf, find decimal position
1437 status[STATUS_INFINITE] = false;
1438 if (!isExponent && text.regionMatches(position,symbols.getInfinity(),0,
1439 symbols.getInfinity().length())) {
1440 position += symbols.getInfinity().length();
1441 status[STATUS_INFINITE] = true;
1442 } else {
1443 // We now have a string of digits, possibly with grouping symbols,
1444 // and decimal points. We want to process these into a DigitList.
1445 // We don't want to put a bunch of leading zeros into the DigitList
1446 // though, so we keep track of the location of the decimal point,
1447 // put only significant digits into the DigitList, and adjust the
1448 // exponent as needed.
1449
1450 digits.decimalAt = digits.count = 0;
1451 char zero = symbols.getZeroDigit();
1452 char decimal = isCurrencyFormat ?
1453 symbols.getMonetaryDecimalSeparator() :
1454 symbols.getDecimalSeparator();
1455 char grouping = symbols.getGroupingSeparator();
1456 String exponentString = symbols.getExponentSeparator();
1457 boolean sawDecimal = false;
1458 boolean sawExponent = false;
1459 boolean sawDigit = false;
1460 int exponent = 0; // Set to the exponent value, if any
1461
1462 // We have to track digitCount ourselves, because digits.count will
1463 // pin when the maximum allowable digits is reached.
1464 int digitCount = 0;
1465
1466 backup = -1;
1467 for (; position < text.length(); ++position) {
1468 char ch = text.charAt(position);
1469
1470 /* We recognize all digit ranges, not only the Latin digit range
1471 * '0'..'9'. We do so by using the Character.digit() method,
1472 * which converts a valid Unicode digit to the range 0..9.
1473 *
1474 * The character 'ch' may be a digit. If so, place its value
1475 * from 0 to 9 in 'digit'. First try using the locale digit,
1476 * which may or MAY NOT be a standard Unicode digit range. If
1477 * this fails, try using the standard Unicode digit ranges by
1478 * calling Character.digit(). If this also fails, digit will
1479 * have a value outside the range 0..9.
1480 */
1481 int digit = ch - zero;
1482 if (digit < 0 || digit > 9) {
1483 digit = Character.digit(ch, 10);
1484 }
1485
1486 if (digit == 0) {
1487 // Cancel out backup setting (see grouping handler below)
1488 backup = -1; // Do this BEFORE continue statement below!!!
1489 sawDigit = true;
1490
1491 // Handle leading zeros
1492 if (digits.count == 0) {
1493 // Ignore leading zeros in integer part of number.
1494 if (!sawDecimal) {
1495 continue;
1496 }
1497
1498 // If we have seen the decimal, but no significant
1499 // digits yet, then we account for leading zeros by
1500 // decrementing the digits.decimalAt into negative
1501 // values.
1502 --digits.decimalAt;
1503 } else {
1504 ++digitCount;
1505 digits.append((char)(digit + '0'));
1506 }
1507 } else if (digit > 0 && digit <= 9) { // [sic] digit==0 handled above
1508 sawDigit = true;
1509 ++digitCount;
1510 digits.append((char)(digit + '0'));
1511
1512 // Cancel out backup setting (see grouping handler below)
1513 backup = -1;
1514 } else if (!isExponent && ch == decimal) {
1515 // If we're only parsing integers, or if we ALREADY saw the
1516 // decimal, then don't parse this one.
1517 if (isParseIntegerOnly() || sawDecimal) {
1518 break;
1519 }
1520 digits.decimalAt = digitCount; // Not digits.count!
1521 sawDecimal = true;
1522 } else if (!isExponent && ch == grouping && isGroupingUsed()) {
1523 if (sawDecimal) {
1524 break;
1525 }
1526 // Ignore grouping characters, if we are using them, but
1527 // require that they be followed by a digit. Otherwise
1528 // we backup and reprocess them.
1529 backup = position;
1530 } else if (!isExponent && text.regionMatches(position, exponentString, 0, exponentString.length())
1531 && !sawExponent) {
1532 // Process the exponent by recursively calling this method.
1533 ParsePosition pos = new ParsePosition(position + exponentString.length());
1534 boolean[] stat = new boolean[STATUS_LENGTH];
1535 DigitList exponentDigits = new DigitList();
1536
1537 if (subparse(text, pos, "", Character.toString(symbols.getMinusSign()), exponentDigits, true, stat) &&
1538 exponentDigits.fitsIntoLong(stat[STATUS_POSITIVE], true)) {
1539 position = pos.index; // Advance past the exponent
1540 exponent = (int)exponentDigits.getLong();
1541 if (!stat[STATUS_POSITIVE]) {
1542 exponent = -exponent;
1543 }
1544 sawExponent = true;
1545 }
1546 break; // Whether we fail or succeed, we exit this loop
1547 }
1548 else {
1549 break;
1550 }
1551 }
1552
1553 if (backup != -1) {
1554 position = backup;
1555 }
1556
1557 // If there was no decimal point we have an integer
1558 if (!sawDecimal) {
1559 digits.decimalAt = digitCount; // Not digits.count!
1560 }
1561
1562 // Adjust for exponent, if any
1563 digits.decimalAt += exponent;
1564
1565 // If none of the text string was recognized. For example, parse
1566 // "x" with pattern "#0.00" (return index and error index both 0)
1567 // parse "$" with pattern "$#0.00". (return index 0 and error
1568 // index 1).
1569 if (!sawDigit && digitCount == 0) {
1570 parsePosition.index = oldStart;
1571 parsePosition.errorIndex = oldStart;
1572 return false;
1573 }
1574 }
1575
1576 // check for suffix
1577 if (!isExponent) {
1578 if (gotPositive) {
1579 gotPositive = text.regionMatches(position,positiveSuffix,0,
1580 positiveSuffix.length());
1581 }
1582 if (gotNegative) {
1583 gotNegative = text.regionMatches(position,negativeSuffix,0,
1584 negativeSuffix.length());
1585 }
1586
1587 // if both match, take longest
1588 if (gotPositive && gotNegative) {
1589 if (positiveSuffix.length() > negativeSuffix.length()) {
1590 gotNegative = false;
1591 } else if (positiveSuffix.length() < negativeSuffix.length()) {
1592 gotPositive = false;
1593 }
1594 }
1595
1596 // fail if neither or both
1597 if (gotPositive == gotNegative) {
1598 parsePosition.errorIndex = position;
1599 return false;
1600 }
1601
1602 parsePosition.index = position +
1603 (gotPositive ? positiveSuffix.length() : negativeSuffix.length()); // mark success!
1604 } else {
1605 parsePosition.index = position;
1606 }
1607
1608 status[STATUS_POSITIVE] = gotPositive;
1609 if (parsePosition.index == oldStart) {
1610 parsePosition.errorIndex = position;
1611 return false;
1612 }
1613 return true;
1614 }
1615
1616 /**
1617 * Returns a copy of the decimal format symbols, which is generally not
1618 * changed by the programmer or user.
1619 * @return a copy of the desired DecimalFormatSymbols
1620 * @see java.text.DecimalFormatSymbols
1621 */
1622 public DecimalFormatSymbols getDecimalFormatSymbols() {
1623 try {
1624 // don't allow multiple references
1625 return (DecimalFormatSymbols) symbols.clone();
1626 } catch (Exception foo) {
1627 return null; // should never happen
1628 }
1629 }
1630
1631
1632 /**
1633 * Sets the decimal format symbols, which is generally not changed
1634 * by the programmer or user.
1635 * @param newSymbols desired DecimalFormatSymbols
1636 * @see java.text.DecimalFormatSymbols
1637 */
1638 public void setDecimalFormatSymbols(DecimalFormatSymbols newSymbols) {
1639 try {
1640 // don't allow multiple references
1641 symbols = (DecimalFormatSymbols) newSymbols.clone();
1642 expandAffixes();
1643 } catch (Exception foo) {
1644 // should never happen
1645 }
1646 }
1647
1648 /**
1649 * Get the positive prefix.
1650 * <P>Examples: +123, $123, sFr123
1651 */
1652 public String getPositivePrefix () {
1653 return positivePrefix;
1654 }
1655
1656 /**
1657 * Set the positive prefix.
1658 * <P>Examples: +123, $123, sFr123
1659 */
1660 public void setPositivePrefix (String newValue) {
1661 positivePrefix = newValue;
1662 posPrefixPattern = null;
1663 positivePrefixFieldPositions = null;
1664 }
1665
1666 /**
1667 * Returns the FieldPositions of the fields in the prefix used for
1668 * positive numbers. This is not used if the user has explicitly set
1669 * a positive prefix via <code>setPositivePrefix</code>. This is
1670 * lazily created.
1671 *
1672 * @return FieldPositions in positive prefix
1673 */
1674 private FieldPosition[] getPositivePrefixFieldPositions() {
1675 if (positivePrefixFieldPositions == null) {
1676 if (posPrefixPattern != null) {
1677 positivePrefixFieldPositions = expandAffix(posPrefixPattern);
1678 }
1679 else {
1680 positivePrefixFieldPositions = EmptyFieldPositionArray;
1681 }
1682 }
1683 return positivePrefixFieldPositions;
1684 }
1685
1686 /**
1687 * Get the negative prefix.
1688 * <P>Examples: -123, ($123) (with negative suffix), sFr-123
1689 */
1690 public String getNegativePrefix () {
1691 return negativePrefix;
1692 }
1693
1694 /**
1695 * Set the negative prefix.
1696 * <P>Examples: -123, ($123) (with negative suffix), sFr-123
1697 */
1698 public void setNegativePrefix (String newValue) {
1699 negativePrefix = newValue;
1700 negPrefixPattern = null;
1701 }
1702
1703 /**
1704 * Returns the FieldPositions of the fields in the prefix used for
1705 * negative numbers. This is not used if the user has explicitly set
1706 * a negative prefix via <code>setNegativePrefix</code>. This is
1707 * lazily created.
1708 *
1709 * @return FieldPositions in positive prefix
1710 */
1711 private FieldPosition[] getNegativePrefixFieldPositions() {
1712 if (negativePrefixFieldPositions == null) {
1713 if (negPrefixPattern != null) {
1714 negativePrefixFieldPositions = expandAffix(negPrefixPattern);
1715 }
1716 else {
1717 negativePrefixFieldPositions = EmptyFieldPositionArray;
1718 }
1719 }
1720 return negativePrefixFieldPositions;
1721 }
1722
1723 /**
1724 * Get the positive suffix.
1725 * <P>Example: 123%
1726 */
1727 public String getPositiveSuffix () {
1728 return positiveSuffix;
1729 }
1730
1731 /**
1732 * Set the positive suffix.
1733 * <P>Example: 123%
1734 */
1735 public void setPositiveSuffix (String newValue) {
1736 positiveSuffix = newValue;
1737 posSuffixPattern = null;
1738 }
1739
1740 /**
1741 * Returns the FieldPositions of the fields in the suffix used for
1742 * positive numbers. This is not used if the user has explicitly set
1743 * a positive suffix via <code>setPositiveSuffix</code>. This is
1744 * lazily created.
1745 *
1746 * @return FieldPositions in positive prefix
1747 */
1748 private FieldPosition[] getPositiveSuffixFieldPositions() {
1749 if (positiveSuffixFieldPositions == null) {
1750 if (posSuffixPattern != null) {
1751 positiveSuffixFieldPositions = expandAffix(posSuffixPattern);
1752 }
1753 else {
1754 positiveSuffixFieldPositions = EmptyFieldPositionArray;
1755 }
1756 }
1757 return positiveSuffixFieldPositions;
1758 }
1759
1760 /**
1761 * Get the negative suffix.
1762 * <P>Examples: -123%, ($123) (with positive suffixes)
1763 */
1764 public String getNegativeSuffix () {
1765 return negativeSuffix;
1766 }
1767
1768 /**
1769 * Set the negative suffix.
1770 * <P>Examples: 123%
1771 */
1772 public void setNegativeSuffix (String newValue) {
1773 negativeSuffix = newValue;
1774 negSuffixPattern = null;
1775 }
1776
1777 /**
1778 * Returns the FieldPositions of the fields in the suffix used for
1779 * negative numbers. This is not used if the user has explicitly set
1780 * a negative suffix via <code>setNegativeSuffix</code>. This is
1781 * lazily created.
1782 *
1783 * @return FieldPositions in positive prefix
1784 */
1785 private FieldPosition[] getNegativeSuffixFieldPositions() {
1786 if (negativeSuffixFieldPositions == null) {
1787 if (negSuffixPattern != null) {
1788 negativeSuffixFieldPositions = expandAffix(negSuffixPattern);
1789 }
1790 else {
1791 negativeSuffixFieldPositions = EmptyFieldPositionArray;
1792 }
1793 }
1794 return negativeSuffixFieldPositions;
1795 }
1796
1797 /**
1798 * Gets the multiplier for use in percent, per mille, and similar
1799 * formats.
1800 *
1801 * @see #setMultiplier(int)
1802 */
1803 public int getMultiplier () {
1804 return multiplier;
1805 }
1806
1807 /**
1808 * Sets the multiplier for use in percent, per mille, and similar
1809 * formats.
1810 * For a percent format, set the multiplier to 100 and the suffixes to
1811 * have '%' (for Arabic, use the Arabic percent sign).
1812 * For a per mille format, set the multiplier to 1000 and the suffixes to
1813 * have '&#92;u2030'.
1814 *
1815 * <P>Example: with multiplier 100, 1.23 is formatted as "123", and
1816 * "123" is parsed into 1.23.
1817 *
1818 * @see #getMultiplier
1819 */
1820 public void setMultiplier (int newValue) {
1821 multiplier = newValue;
1822 bigDecimalMultiplier = null;
1823 bigIntegerMultiplier = null;
1824 }
1825
1826 /**
1827 * Return the grouping size. Grouping size is the number of digits between
1828 * grouping separators in the integer portion of a number. For example,
1829 * in the number "123,456.78", the grouping size is 3.
1830 * @see #setGroupingSize
1831 * @see java.text.NumberFormat#isGroupingUsed
1832 * @see java.text.DecimalFormatSymbols#getGroupingSeparator
1833 */
1834 public int getGroupingSize () {
1835 return groupingSize;
1836 }
1837
1838 /**
1839 * Set the grouping size. Grouping size is the number of digits between
1840 * grouping separators in the integer portion of a number. For example,
1841 * in the number "123,456.78", the grouping size is 3.
1842 * <br>
1843 * The value passed in is converted to a byte, which may lose information.
1844 * @see #getGroupingSize
1845 * @see java.text.NumberFormat#setGroupingUsed
1846 * @see java.text.DecimalFormatSymbols#setGroupingSeparator
1847 */
1848 public void setGroupingSize (int newValue) {
1849 groupingSize = (byte)newValue;
1850 }
1851
1852 /**
1853 * Allows you to get the behavior of the decimal separator with integers.
1854 * (The decimal separator will always appear with decimals.)
1855 * <P>Example: Decimal ON: 12345 -> 12345.; OFF: 12345 -> 12345
1856 */
1857 public boolean isDecimalSeparatorAlwaysShown() {
1858 return decimalSeparatorAlwaysShown;
1859 }
1860
1861 /**
1862 * Allows you to set the behavior of the decimal separator with integers.
1863 * (The decimal separator will always appear with decimals.)
1864 * <P>Example: Decimal ON: 12345 -> 12345.; OFF: 12345 -> 12345
1865 */
1866 public void setDecimalSeparatorAlwaysShown(boolean newValue) {
1867 decimalSeparatorAlwaysShown = newValue;
1868 }
1869
1870 /**
1871 * Returns whether the {@link #parse(java.lang.String, java.text.ParsePosition)}
1872 * method returns <code>BigDecimal</code>. The default value is false.
1873 * @see #setParseBigDecimal
1874 * @since 1.5
1875 */
1876 public boolean isParseBigDecimal() {
1877 return parseBigDecimal;
1878 }
1879
1880 /**
1881 * Sets whether the {@link #parse(java.lang.String, java.text.ParsePosition)}
1882 * method returns <code>BigDecimal</code>.
1883 * @see #isParseBigDecimal
1884 * @since 1.5
1885 */
1886 public void setParseBigDecimal(boolean newValue) {
1887 parseBigDecimal = newValue;
1888 }
1889
1890 /**
1891 * Standard override; no change in semantics.
1892 */
1893 public Object clone() {
1894 try {
1895 DecimalFormat other = (DecimalFormat) super.clone();
1896 other.symbols = (DecimalFormatSymbols) symbols.clone();
1897 other.digitList = (DigitList) digitList.clone();
1898 return other;
1899 } catch (Exception e) {
1900 throw new InternalError();
1901 }
1902 }
1903
1904 /**
1905 * Overrides equals
1906 */
1907 public boolean equals(Object obj)
1908 {
1909 if (obj == null) return false;
1910 if (!super.equals(obj)) return false; // super does class check
1911 DecimalFormat other = (DecimalFormat) obj;
1912 return ((posPrefixPattern == other.posPrefixPattern &&
1913 positivePrefix.equals(other.positivePrefix))
1914 || (posPrefixPattern != null &&
1915 posPrefixPattern.equals(other.posPrefixPattern)))
1916 && ((posSuffixPattern == other.posSuffixPattern &&
1917 positiveSuffix.equals(other.positiveSuffix))
1918 || (posSuffixPattern != null &&
1919 posSuffixPattern.equals(other.posSuffixPattern)))
1920 && ((negPrefixPattern == other.negPrefixPattern &&
1921 negativePrefix.equals(other.negativePrefix))
1922 || (negPrefixPattern != null &&
1923 negPrefixPattern.equals(other.negPrefixPattern)))
1924 && ((negSuffixPattern == other.negSuffixPattern &&
1925 negativeSuffix.equals(other.negativeSuffix))
1926 || (negSuffixPattern != null &&
1927 negSuffixPattern.equals(other.negSuffixPattern)))
1928 && multiplier == other.multiplier
1929 && groupingSize == other.groupingSize
1930 && decimalSeparatorAlwaysShown == other.decimalSeparatorAlwaysShown
1931 && parseBigDecimal == other.parseBigDecimal
1932 && useExponentialNotation == other.useExponentialNotation
1933 && (!useExponentialNotation ||
1934 minExponentDigits == other.minExponentDigits)
1935 && maximumIntegerDigits == other.maximumIntegerDigits
1936 && minimumIntegerDigits == other.minimumIntegerDigits
1937 && maximumFractionDigits == other.maximumFractionDigits
1938 && minimumFractionDigits == other.minimumFractionDigits
1939 && roundingMode == other.roundingMode
1940 && symbols.equals(other.symbols);
1941 }
1942
1943 /**
1944 * Overrides hashCode
1945 */
1946 public int hashCode() {
1947 return super.hashCode() * 37 + positivePrefix.hashCode();
1948 // just enough fields for a reasonable distribution
1949 }
1950
1951 /**
1952 * Synthesizes a pattern string that represents the current state
1953 * of this Format object.
1954 * @see #applyPattern
1955 */
1956 public String toPattern() {
1957 return toPattern( false );
1958 }
1959
1960 /**
1961 * Synthesizes a localized pattern string that represents the current
1962 * state of this Format object.
1963 * @see #applyPattern
1964 */
1965 public String toLocalizedPattern() {
1966 return toPattern( true );
1967 }
1968
1969 /**
1970 * Expand the affix pattern strings into the expanded affix strings. If any
1971 * affix pattern string is null, do not expand it. This method should be
1972 * called any time the symbols or the affix patterns change in order to keep
1973 * the expanded affix strings up to date.
1974 */
1975 private void expandAffixes() {
1976 // Reuse one StringBuffer for better performance
1977 StringBuffer buffer = new StringBuffer();
1978 if (posPrefixPattern != null) {
1979 positivePrefix = expandAffix(posPrefixPattern, buffer);
1980 positivePrefixFieldPositions = null;
1981 }
1982 if (posSuffixPattern != null) {
1983 positiveSuffix = expandAffix(posSuffixPattern, buffer);
1984 positiveSuffixFieldPositions = null;
1985 }
1986 if (negPrefixPattern != null) {
1987 negativePrefix = expandAffix(negPrefixPattern, buffer);
1988 negativePrefixFieldPositions = null;
1989 }
1990 if (negSuffixPattern != null) {
1991 negativeSuffix = expandAffix(negSuffixPattern, buffer);
1992 negativeSuffixFieldPositions = null;
1993 }
1994 }
1995
1996 /**
1997 * Expand an affix pattern into an affix string. All characters in the
1998 * pattern are literal unless prefixed by QUOTE. The following characters
1999 * after QUOTE are recognized: PATTERN_PERCENT, PATTERN_PER_MILLE,
2000 * PATTERN_MINUS, and CURRENCY_SIGN. If CURRENCY_SIGN is doubled (QUOTE +
2001 * CURRENCY_SIGN + CURRENCY_SIGN), it is interpreted as an ISO 4217
2002 * currency code. Any other character after a QUOTE represents itself.
2003 * QUOTE must be followed by another character; QUOTE may not occur by
2004 * itself at the end of the pattern.
2005 *
2006 * @param pattern the non-null, possibly empty pattern
2007 * @param buffer a scratch StringBuffer; its contents will be lost
2008 * @return the expanded equivalent of pattern
2009 */
2010 private String expandAffix(String pattern, StringBuffer buffer) {
2011 buffer.setLength(0);
2012 for (int i=0; i<pattern.length(); ) {
2013 char c = pattern.charAt(i++);
2014 if (c == QUOTE) {
2015 c = pattern.charAt(i++);
2016 switch (c) {
2017 case CURRENCY_SIGN:
2018 if (i<pattern.length() &&
2019 pattern.charAt(i) == CURRENCY_SIGN) {
2020 ++i;
2021 buffer.append(symbols.getInternationalCurrencySymbol());
2022 } else {
2023 buffer.append(symbols.getCurrencySymbol());
2024 }
2025 continue;
2026 case PATTERN_PERCENT:
2027 c = symbols.getPercent();
2028 break;
2029 case PATTERN_PER_MILLE:
2030 c = symbols.getPerMill();
2031 break;
2032 case PATTERN_MINUS:
2033 c = symbols.getMinusSign();
2034 break;
2035 }
2036 }
2037 buffer.append(c);
2038 }
2039 return buffer.toString();
2040 }
2041
2042 /**
2043 * Expand an affix pattern into an array of FieldPositions describing
2044 * how the pattern would be expanded.
2045 * All characters in the
2046 * pattern are literal unless prefixed by QUOTE. The following characters
2047 * after QUOTE are recognized: PATTERN_PERCENT, PATTERN_PER_MILLE,
2048 * PATTERN_MINUS, and CURRENCY_SIGN. If CURRENCY_SIGN is doubled (QUOTE +
2049 * CURRENCY_SIGN + CURRENCY_SIGN), it is interpreted as an ISO 4217
2050 * currency code. Any other character after a QUOTE represents itself.
2051 * QUOTE must be followed by another character; QUOTE may not occur by
2052 * itself at the end of the pattern.
2053 *
2054 * @param pattern the non-null, possibly empty pattern
2055 * @return FieldPosition array of the resulting fields.
2056 */
2057 private FieldPosition[] expandAffix(String pattern) {
2058 ArrayList positions = null;
2059 int stringIndex = 0;
2060 for (int i=0; i<pattern.length(); ) {
2061 char c = pattern.charAt(i++);
2062 if (c == QUOTE) {
2063 int field = -1;
2064 Format.Field fieldID = null;
2065 c = pattern.charAt(i++);
2066 switch (c) {
2067 case CURRENCY_SIGN:
2068 String string;
2069 if (i<pattern.length() &&
2070 pattern.charAt(i) == CURRENCY_SIGN) {
2071 ++i;
2072 string = symbols.getInternationalCurrencySymbol();
2073 } else {
2074 string = symbols.getCurrencySymbol();
2075 }
2076 if (string.length() > 0) {
2077 if (positions == null) {
2078 positions = new ArrayList(2);
2079 }
2080 FieldPosition fp = new FieldPosition(Field.CURRENCY);
2081 fp.setBeginIndex(stringIndex);
2082 fp.setEndIndex(stringIndex + string.length());
2083 positions.add(fp);
2084 stringIndex += string.length();
2085 }
2086 continue;
2087 case PATTERN_PERCENT:
2088 c = symbols.getPercent();
2089 field = -1;
2090 fieldID = Field.PERCENT;
2091 break;
2092 case PATTERN_PER_MILLE:
2093 c = symbols.getPerMill();
2094 field = -1;
2095 fieldID = Field.PERMILLE;
2096 break;
2097 case PATTERN_MINUS:
2098 c = symbols.getMinusSign();
2099 field = -1;
2100 fieldID = Field.SIGN;
2101 break;
2102 }
2103 if (fieldID != null) {
2104 if (positions == null) {
2105 positions = new ArrayList(2);
2106 }
2107 FieldPosition fp = new FieldPosition(fieldID, field);
2108 fp.setBeginIndex(stringIndex);
2109 fp.setEndIndex(stringIndex + 1);
2110 positions.add(fp);
2111 }
2112 }
2113 stringIndex++;
2114 }
2115 if (positions != null) {
2116 return (FieldPosition[])positions.toArray(EmptyFieldPositionArray);
2117 }
2118 return EmptyFieldPositionArray;
2119 }
2120
2121 /**
2122 * Appends an affix pattern to the given StringBuffer, quoting special
2123 * characters as needed. Uses the internal affix pattern, if that exists,
2124 * or the literal affix, if the internal affix pattern is null. The
2125 * appended string will generate the same affix pattern (or literal affix)
2126 * when passed to toPattern().
2127 *
2128 * @param buffer the affix string is appended to this
2129 * @param affixPattern a pattern such as posPrefixPattern; may be null
2130 * @param expAffix a corresponding expanded affix, such as positivePrefix.
2131 * Ignored unless affixPattern is null. If affixPattern is null, then
2132 * expAffix is appended as a literal affix.
2133 * @param localized true if the appended pattern should contain localized
2134 * pattern characters; otherwise, non-localized pattern chars are appended
2135 */
2136 private void appendAffix(StringBuffer buffer, String affixPattern,
2137 String expAffix, boolean localized) {
2138 if (affixPattern == null) {
2139 appendAffix(buffer, expAffix, localized);
2140 } else {
2141 int i;
2142 for (int pos=0; pos<affixPattern.length(); pos=i) {
2143 i = affixPattern.indexOf(QUOTE, pos);
2144 if (i < 0) {
2145 appendAffix(buffer, affixPattern.substring(pos), localized);
2146 break;
2147 }
2148 if (i > pos) {
2149 appendAffix(buffer, affixPattern.substring(pos, i), localized);
2150 }
2151 char c = affixPattern.charAt(++i);
2152 ++i;
2153 if (c == QUOTE) {
2154 buffer.append(c);
2155 // Fall through and append another QUOTE below
2156 } else if (c == CURRENCY_SIGN &&
2157 i<affixPattern.length() &&
2158 affixPattern.charAt(i) == CURRENCY_SIGN) {
2159 ++i;
2160 buffer.append(c);
2161 // Fall through and append another CURRENCY_SIGN below
2162 } else if (localized) {
2163 switch (c) {
2164 case PATTERN_PERCENT:
2165 c = symbols.getPercent();
2166 break;
2167 case PATTERN_PER_MILLE:
2168 c = symbols.getPerMill();
2169 break;
2170 case PATTERN_MINUS:
2171 c = symbols.getMinusSign();
2172 break;
2173 }
2174 }
2175 buffer.append(c);
2176 }
2177 }
2178 }
2179
2180 /**
2181 * Append an affix to the given StringBuffer, using quotes if
2182 * there are special characters. Single quotes themselves must be
2183 * escaped in either case.
2184 */
2185 private void appendAffix(StringBuffer buffer, String affix, boolean localized) {
2186 boolean needQuote;
2187 if (localized) {
2188 needQuote = affix.indexOf(symbols.getZeroDigit()) >= 0
2189 || affix.indexOf(symbols.getGroupingSeparator()) >= 0
2190 || affix.indexOf(symbols.getDecimalSeparator()) >= 0
2191 || affix.indexOf(symbols.getPercent()) >= 0
2192 || affix.indexOf(symbols.getPerMill()) >= 0
2193 || affix.indexOf(symbols.getDigit()) >= 0
2194 || affix.indexOf(symbols.getPatternSeparator()) >= 0
2195 || affix.indexOf(symbols.getMinusSign()) >= 0
2196 || affix.indexOf(CURRENCY_SIGN) >= 0;
2197 }
2198 else {
2199 needQuote = affix.indexOf(PATTERN_ZERO_DIGIT) >= 0
2200 || affix.indexOf(PATTERN_GROUPING_SEPARATOR) >= 0
2201 || affix.indexOf(PATTERN_DECIMAL_SEPARATOR) >= 0
2202 || affix.indexOf(PATTERN_PERCENT) >= 0
2203 || affix.indexOf(PATTERN_PER_MILLE) >= 0
2204 || affix.indexOf(PATTERN_DIGIT) >= 0
2205 || affix.indexOf(PATTERN_SEPARATOR) >= 0
2206 || affix.indexOf(PATTERN_MINUS) >= 0
2207 || affix.indexOf(CURRENCY_SIGN) >= 0;
2208 }
2209 if (needQuote) buffer.append('\'');
2210 if (affix.indexOf('\'') < 0) buffer.append(affix);
2211 else {
2212 for (int j=0; j<affix.length(); ++j) {
2213 char c = affix.charAt(j);
2214 buffer.append(c);
2215 if (c == '\'') buffer.append(c);
2216 }
2217 }
2218 if (needQuote) buffer.append('\'');
2219 }
2220
2221 /**
2222 * Does the real work of generating a pattern. */
2223 private String toPattern(boolean localized) {
2224 StringBuffer result = new StringBuffer();
2225 for (int j = 1; j >= 0; --j) {
2226 if (j == 1)
2227 appendAffix(result, posPrefixPattern, positivePrefix, localized);
2228 else appendAffix(result, negPrefixPattern, negativePrefix, localized);
2229 int i;
2230 int digitCount = useExponentialNotation
2231 ? getMaximumIntegerDigits()
2232 : Math.max(groupingSize, getMinimumIntegerDigits())+1;
2233 for (i = digitCount; i > 0; --i) {
2234 if (i != digitCount && isGroupingUsed() && groupingSize != 0 &&
2235 i % groupingSize == 0) {
2236 result.append(localized ? symbols.getGroupingSeparator() :
2237 PATTERN_GROUPING_SEPARATOR);
2238 }
2239 result.append(i <= getMinimumIntegerDigits()
2240 ? (localized ? symbols.getZeroDigit() : PATTERN_ZERO_DIGIT)
2241 : (localized ? symbols.getDigit() : PATTERN_DIGIT));
2242 }
2243 if (getMaximumFractionDigits() > 0 || decimalSeparatorAlwaysShown)
2244 result.append(localized ? symbols.getDecimalSeparator() :
2245 PATTERN_DECIMAL_SEPARATOR);
2246 for (i = 0; i < getMaximumFractionDigits(); ++i) {
2247 if (i < getMinimumFractionDigits()) {
2248 result.append(localized ? symbols.getZeroDigit() :
2249 PATTERN_ZERO_DIGIT);
2250 } else {
2251 result.append(localized ? symbols.getDigit() :
2252 PATTERN_DIGIT);
2253 }
2254 }
2255 if (useExponentialNotation)
2256 {
2257 result.append(localized ? symbols.getExponentSeparator() :
2258 PATTERN_EXPONENT);
2259 for (i=0; i<minExponentDigits; ++i)
2260 result.append(localized ? symbols.getZeroDigit() :
2261 PATTERN_ZERO_DIGIT);
2262 }
2263 if (j == 1) {
2264 appendAffix(result, posSuffixPattern, positiveSuffix, localized);
2265 if ((negSuffixPattern == posSuffixPattern && // n == p == null
2266 negativeSuffix.equals(positiveSuffix))
2267 || (negSuffixPattern != null &&
2268 negSuffixPattern.equals(posSuffixPattern))) {
2269 if ((negPrefixPattern != null && posPrefixPattern != null &&
2270 negPrefixPattern.equals("'-" + posPrefixPattern)) ||
2271 (negPrefixPattern == posPrefixPattern && // n == p == null
2272 negativePrefix.equals(symbols.getMinusSign() + positivePrefix)))
2273 break;
2274 }
2275 result.append(localized ? symbols.getPatternSeparator() :
2276 PATTERN_SEPARATOR);
2277 } else appendAffix(result, negSuffixPattern, negativeSuffix, localized);
2278 }
2279 return result.toString();
2280 }
2281
2282 /**
2283 * Apply the given pattern to this Format object. A pattern is a
2284 * short-hand specification for the various formatting properties.
2285 * These properties can also be changed individually through the
2286 * various setter methods.
2287 * <p>
2288 * There is no limit to integer digits set
2289 * by this routine, since that is the typical end-user desire;
2290 * use setMaximumInteger if you want to set a real value.
2291 * For negative numbers, use a second pattern, separated by a semicolon
2292 * <P>Example <code>"#,#00.0#"</code> -> 1,234.56
2293 * <P>This means a minimum of 2 integer digits, 1 fraction digit, and
2294 * a maximum of 2 fraction digits.
2295 * <p>Example: <code>"#,#00.0#;(#,#00.0#)"</code> for negatives in
2296 * parentheses.
2297 * <p>In negative patterns, the minimum and maximum counts are ignored;
2298 * these are presumed to be set in the positive pattern.
2299 *
2300 * @exception NullPointerException if <code>pattern</code> is null
2301 * @exception IllegalArgumentException if the given pattern is invalid.
2302 */
2303 public void applyPattern(String pattern) {
2304 applyPattern(pattern, false);
2305 }
2306
2307 /**
2308 * Apply the given pattern to this Format object. The pattern
2309 * is assumed to be in a localized notation. A pattern is a
2310 * short-hand specification for the various formatting properties.
2311 * These properties can also be changed individually through the
2312 * various setter methods.
2313 * <p>
2314 * There is no limit to integer digits set
2315 * by this routine, since that is the typical end-user desire;
2316 * use setMaximumInteger if you want to set a real value.
2317 * For negative numbers, use a second pattern, separated by a semicolon
2318 * <P>Example <code>"#,#00.0#"</code> -> 1,234.56
2319 * <P>This means a minimum of 2 integer digits, 1 fraction digit, and
2320 * a maximum of 2 fraction digits.
2321 * <p>Example: <code>"#,#00.0#;(#,#00.0#)"</code> for negatives in
2322 * parentheses.
2323 * <p>In negative patterns, the minimum and maximum counts are ignored;
2324 * these are presumed to be set in the positive pattern.
2325 *
2326 * @exception NullPointerException if <code>pattern</code> is null
2327 * @exception IllegalArgumentException if the given pattern is invalid.
2328 */
2329 public void applyLocalizedPattern(String pattern) {
2330 applyPattern(pattern, true);
2331 }
2332
2333 /**
2334 * Does the real work of applying a pattern.
2335 */
2336 private void applyPattern(String pattern, boolean localized) {
2337 char zeroDigit = PATTERN_ZERO_DIGIT;
2338 char groupingSeparator = PATTERN_GROUPING_SEPARATOR;
2339 char decimalSeparator = PATTERN_DECIMAL_SEPARATOR;
2340 char percent = PATTERN_PERCENT;
2341 char perMill = PATTERN_PER_MILLE;
2342 char digit = PATTERN_DIGIT;
2343 char separator = PATTERN_SEPARATOR;
2344 String exponent = PATTERN_EXPONENT;
2345 char minus = PATTERN_MINUS;
2346 if (localized) {
2347 zeroDigit = symbols.getZeroDigit();
2348 groupingSeparator = symbols.getGroupingSeparator();
2349 decimalSeparator = symbols.getDecimalSeparator();
2350 percent = symbols.getPercent();
2351 perMill = symbols.getPerMill();
2352 digit = symbols.getDigit();
2353 separator = symbols.getPatternSeparator();
2354 exponent = symbols.getExponentSeparator();
2355 minus = symbols.getMinusSign();
2356 }
2357 boolean gotNegative = false;
2358 decimalSeparatorAlwaysShown = false;
2359 isCurrencyFormat = false;
2360 useExponentialNotation = false;
2361
2362 // Two variables are used to record the subrange of the pattern
2363 // occupied by phase 1. This is used during the processing of the
2364 // second pattern (the one representing negative numbers) to ensure
2365 // that no deviation exists in phase 1 between the two patterns.
2366 int phaseOneStart = 0;
2367 int phaseOneLength = 0;
2368
2369 int start = 0;
2370 for (int j = 1; j >= 0 && start < pattern.length(); --j) {
2371 boolean inQuote = false;
2372 StringBuffer prefix = new StringBuffer();
2373 StringBuffer suffix = new StringBuffer();
2374 int decimalPos = -1;
2375 int multiplier = 1;
2376 int digitLeftCount = 0, zeroDigitCount = 0, digitRightCount = 0;
2377 byte groupingCount = -1;
2378
2379 // The phase ranges from 0 to 2. Phase 0 is the prefix. Phase 1 is
2380 // the section of the pattern with digits, decimal separator,
2381 // grouping characters. Phase 2 is the suffix. In phases 0 and 2,
2382 // percent, per mille, and currency symbols are recognized and
2383 // translated. The separation of the characters into phases is
2384 // strictly enforced; if phase 1 characters are to appear in the
2385 // suffix, for example, they must be quoted.
2386 int phase = 0;
2387
2388 // The affix is either the prefix or the suffix.
2389 StringBuffer affix = prefix;
2390
2391 for (int pos = start; pos < pattern.length(); ++pos) {
2392 char ch = pattern.charAt(pos);
2393 switch (phase) {
2394 case 0:
2395 case 2:
2396 // Process the prefix / suffix characters
2397 if (inQuote) {
2398 // A quote within quotes indicates either the closing
2399 // quote or two quotes, which is a quote literal. That
2400 // is, we have the second quote in 'do' or 'don''t'.
2401 if (ch == QUOTE) {
2402 if ((pos+1) < pattern.length() &&
2403 pattern.charAt(pos+1) == QUOTE) {
2404 ++pos;
2405 affix.append("''"); // 'don''t'
2406 } else {
2407 inQuote = false; // 'do'
2408 }
2409 continue;
2410 }
2411 } else {
2412 // Process unquoted characters seen in prefix or suffix
2413 // phase.
2414 if (ch == digit ||
2415 ch == zeroDigit ||
2416 ch == groupingSeparator ||
2417 ch == decimalSeparator) {
2418 phase = 1;
2419 if (j == 1) {
2420 phaseOneStart = pos;
2421 }
2422 --pos; // Reprocess this character
2423 continue;
2424 } else if (ch == CURRENCY_SIGN) {
2425 // Use lookahead to determine if the currency sign
2426 // is doubled or not.
2427 boolean doubled = (pos + 1) < pattern.length() &&
2428 pattern.charAt(pos + 1) == CURRENCY_SIGN;
2429 if (doubled) { // Skip over the doubled character
2430 ++pos;
2431 }
2432 isCurrencyFormat = true;
2433 affix.append(doubled ? "'\u00A4\u00A4" : "'\u00A4");
2434 continue;
2435 } else if (ch == QUOTE) {
2436 // A quote outside quotes indicates either the
2437 // opening quote or two quotes, which is a quote
2438 // literal. That is, we have the first quote in 'do'
2439 // or o''clock.
2440 if (ch == QUOTE) {
2441 if ((pos+1) < pattern.length() &&
2442 pattern.charAt(pos+1) == QUOTE) {
2443 ++pos;
2444 affix.append("''"); // o''clock
2445 } else {
2446 inQuote = true; // 'do'
2447 }
2448 continue;
2449 }
2450 } else if (ch == separator) {
2451 // Don't allow separators before we see digit
2452 // characters of phase 1, and don't allow separators
2453 // in the second pattern (j == 0).
2454 if (phase == 0 || j == 0) {
2455 throw new IllegalArgumentException("Unquoted special character '" +
2456 ch + "' in pattern \"" + pattern + '"');
2457 }
2458 start = pos + 1;
2459 pos = pattern.length();
2460 continue;
2461 }
2462
2463 // Next handle characters which are appended directly.
2464 else if (ch == percent) {
2465 if (multiplier != 1) {
2466 throw new IllegalArgumentException("Too many percent/per mille characters in pattern \"" +
2467 pattern + '"');
2468 }
2469 multiplier = 100;
2470 affix.append("'%");
2471 continue;
2472 } else if (ch == perMill) {
2473 if (multiplier != 1) {
2474 throw new IllegalArgumentException("Too many percent/per mille characters in pattern \"" +
2475 pattern + '"');
2476 }
2477 multiplier = 1000;
2478 affix.append("'\u2030");
2479 continue;
2480 } else if (ch == minus) {
2481 affix.append("'-");
2482 continue;
2483 }
2484 }
2485 // Note that if we are within quotes, or if this is an
2486 // unquoted, non-special character, then we usually fall
2487 // through to here.
2488 affix.append(ch);
2489 break;
2490
2491 case 1:
2492 // Phase one must be identical in the two sub-patterns. We
2493 // enforce this by doing a direct comparison. While
2494 // processing the first sub-pattern, we just record its
2495 // length. While processing the second, we compare
2496 // characters.
2497 if (j == 1) {
2498 ++phaseOneLength;
2499 } else {
2500 if (--phaseOneLength == 0) {
2501 phase = 2;
2502 affix = suffix;
2503 }
2504 continue;
2505 }
2506
2507 // Process the digits, decimal, and grouping characters. We
2508 // record five pieces of information. We expect the digits
2509 // to occur in the pattern ####0000.####, and we record the
2510 // number of left digits, zero (central) digits, and right
2511 // digits. The position of the last grouping character is
2512 // recorded (should be somewhere within the first two blocks
2513 // of characters), as is the position of the decimal point,
2514 // if any (should be in the zero digits). If there is no
2515 // decimal point, then there should be no right digits.
2516 if (ch == digit) {
2517 if (zeroDigitCount > 0) {
2518 ++digitRightCount;
2519 } else {
2520 ++digitLeftCount;
2521 }
2522 if (groupingCount >= 0 && decimalPos < 0) {
2523 ++groupingCount;
2524 }
2525 } else if (ch == zeroDigit) {
2526 if (digitRightCount > 0) {
2527 throw new IllegalArgumentException("Unexpected '0' in pattern \"" +
2528 pattern + '"');
2529 }
2530 ++zeroDigitCount;
2531 if (groupingCount >= 0 && decimalPos < 0) {
2532 ++groupingCount;
2533 }
2534 } else if (ch == groupingSeparator) {
2535 groupingCount = 0;
2536 } else if (ch == decimalSeparator) {
2537 if (decimalPos >= 0) {
2538 throw new IllegalArgumentException("Multiple decimal separators in pattern \"" +
2539 pattern + '"');
2540 }
2541 decimalPos = digitLeftCount + zeroDigitCount + digitRightCount;
2542 } else if (pattern.regionMatches(pos, exponent, 0, exponent.length())){
2543 if (useExponentialNotation) {
2544 throw new IllegalArgumentException("Multiple exponential " +
2545 "symbols in pattern \"" + pattern + '"');
2546 }
2547 useExponentialNotation = true;
2548 minExponentDigits = 0;
2549
2550 // Use lookahead to parse out the exponential part
2551 // of the pattern, then jump into phase 2.
2552 pos = pos+exponent.length();
2553 while (pos < pattern.length() &&
2554 pattern.charAt(pos) == zeroDigit) {
2555 ++minExponentDigits;
2556 ++phaseOneLength;
2557 ++pos;
2558 }
2559
2560 if ((digitLeftCount + zeroDigitCount) < 1 ||
2561 minExponentDigits < 1) {
2562 throw new IllegalArgumentException("Malformed exponential " +
2563 "pattern \"" + pattern + '"');
2564 }
2565
2566 // Transition to phase 2
2567 phase = 2;
2568 affix = suffix;
2569 --pos;
2570 continue;
2571 } else {
2572 phase = 2;
2573 affix = suffix;
2574 --pos;
2575 --phaseOneLength;
2576 continue;
2577 }
2578 break;
2579 }
2580 }
2581
2582 // Handle patterns with no '0' pattern character. These patterns
2583 // are legal, but must be interpreted. "##.###" -> "#0.###".
2584 // ".###" -> ".0##".
2585 /* We allow patterns of the form "####" to produce a zeroDigitCount
2586 * of zero (got that?); although this seems like it might make it
2587 * possible for format() to produce empty strings, format() checks
2588 * for this condition and outputs a zero digit in this situation.
2589 * Having a zeroDigitCount of zero yields a minimum integer digits
2590 * of zero, which allows proper round-trip patterns. That is, we
2591 * don't want "#" to become "#0" when toPattern() is called (even
2592 * though that's what it really is, semantically).
2593 */
2594 if (zeroDigitCount == 0 && digitLeftCount > 0 && decimalPos >= 0) {
2595 // Handle "###.###" and "###." and ".###"
2596 int n = decimalPos;
2597 if (n == 0) { // Handle ".###"
2598 ++n;
2599 }
2600 digitRightCount = digitLeftCount - n;
2601 digitLeftCount = n - 1;
2602 zeroDigitCount = 1;
2603 }
2604
2605 // Do syntax checking on the digits.
2606 if ((decimalPos < 0 && digitRightCount > 0) ||
2607 (decimalPos >= 0 && (decimalPos < digitLeftCount ||
2608 decimalPos > (digitLeftCount + zeroDigitCount))) ||
2609 groupingCount == 0 || inQuote) {
2610 throw new IllegalArgumentException("Malformed pattern \"" +
2611 pattern + '"');
2612 }
2613
2614 if (j == 1) {
2615 posPrefixPattern = prefix.toString();
2616 posSuffixPattern = suffix.toString();
2617 negPrefixPattern = posPrefixPattern; // assume these for now
2618 negSuffixPattern = posSuffixPattern;
2619 int digitTotalCount = digitLeftCount + zeroDigitCount + digitRightCount;
2620 /* The effectiveDecimalPos is the position the decimal is at or
2621 * would be at if there is no decimal. Note that if decimalPos<0,
2622 * then digitTotalCount == digitLeftCount + zeroDigitCount.
2623 */
2624 int effectiveDecimalPos = decimalPos >= 0 ?
2625 decimalPos : digitTotalCount;
2626 setMinimumIntegerDigits(effectiveDecimalPos - digitLeftCount);
2627 setMaximumIntegerDigits(useExponentialNotation ?
2628 digitLeftCount + getMinimumIntegerDigits() :
2629 MAXIMUM_INTEGER_DIGITS);
2630 setMaximumFractionDigits(decimalPos >= 0 ?
2631 (digitTotalCount - decimalPos) : 0);
2632 setMinimumFractionDigits(decimalPos >= 0 ?
2633 (digitLeftCount + zeroDigitCount - decimalPos) : 0);
2634 setGroupingUsed(groupingCount > 0);
2635 this.groupingSize = (groupingCount > 0) ? groupingCount : 0;
2636 this.multiplier = multiplier;
2637 setDecimalSeparatorAlwaysShown(decimalPos == 0 ||
2638 decimalPos == digitTotalCount);
2639 } else {
2640 negPrefixPattern = prefix.toString();
2641 negSuffixPattern = suffix.toString();
2642 gotNegative = true;
2643 }
2644 }
2645
2646 if (pattern.length() == 0) {
2647 posPrefixPattern = posSuffixPattern = "";
2648 setMinimumIntegerDigits(0);
2649 setMaximumIntegerDigits(MAXIMUM_INTEGER_DIGITS);
2650 setMinimumFractionDigits(0);
2651 setMaximumFractionDigits(MAXIMUM_FRACTION_DIGITS);
2652 }
2653
2654 // If there was no negative pattern, or if the negative pattern is
2655 // identical to the positive pattern, then prepend the minus sign to
2656 // the positive pattern to form the negative pattern.
2657 if (!gotNegative ||
2658 (negPrefixPattern.equals(posPrefixPattern)
2659 && negSuffixPattern.equals(posSuffixPattern))) {
2660 negSuffixPattern = posSuffixPattern;
2661 negPrefixPattern = "'-" + posPrefixPattern;
2662 }
2663
2664 expandAffixes();
2665 }
2666
2667 /**
2668 * Sets the maximum number of digits allowed in the integer portion of a
2669 * number.
2670 * For formatting numbers other than <code>BigInteger</code> and
2671 * <code>BigDecimal</code> objects, the lower of <code>newValue</code> and
2672 * 309 is used. Negative input values are replaced with 0.
2673 * @see NumberFormat#setMaximumIntegerDigits
2674 */
2675 public void setMaximumIntegerDigits(int newValue) {
2676 maximumIntegerDigits = Math.min(Math.max(0, newValue), MAXIMUM_INTEGER_DIGITS);
2677 super.setMaximumIntegerDigits((maximumIntegerDigits > DOUBLE_INTEGER_DIGITS) ?
2678 DOUBLE_INTEGER_DIGITS : maximumIntegerDigits);
2679 if (minimumIntegerDigits > maximumIntegerDigits) {
2680 minimumIntegerDigits = maximumIntegerDigits;
2681 super.setMinimumIntegerDigits((minimumIntegerDigits > DOUBLE_INTEGER_DIGITS) ?
2682 DOUBLE_INTEGER_DIGITS : minimumIntegerDigits);
2683 }
2684 }
2685
2686 /**
2687 * Sets the minimum number of digits allowed in the integer portion of a
2688 * number.
2689 * For formatting numbers other than <code>BigInteger</code> and
2690 * <code>BigDecimal</code> objects, the lower of <code>newValue</code> and
2691 * 309 is used. Negative input values are replaced with 0.
2692 * @see NumberFormat#setMinimumIntegerDigits
2693 */
2694 public void setMinimumIntegerDigits(int newValue) {
2695 minimumIntegerDigits = Math.min(Math.max(0, newValue), MAXIMUM_INTEGER_DIGITS);
2696 super.setMinimumIntegerDigits((minimumIntegerDigits > DOUBLE_INTEGER_DIGITS) ?
2697 DOUBLE_INTEGER_DIGITS : minimumIntegerDigits);
2698 if (minimumIntegerDigits > maximumIntegerDigits) {
2699 maximumIntegerDigits = minimumIntegerDigits;
2700 super.setMaximumIntegerDigits((maximumIntegerDigits > DOUBLE_INTEGER_DIGITS) ?
2701 DOUBLE_INTEGER_DIGITS : maximumIntegerDigits);
2702 }
2703 }
2704
2705 /**
2706 * Sets the maximum number of digits allowed in the fraction portion of a
2707 * number.
2708 * For formatting numbers other than <code>BigInteger</code> and
2709 * <code>BigDecimal</code> objects, the lower of <code>newValue</code> and
2710 * 340 is used. Negative input values are replaced with 0.
2711 * @see NumberFormat#setMaximumFractionDigits
2712 */
2713 public void setMaximumFractionDigits(int newValue) {
2714 maximumFractionDigits = Math.min(Math.max(0, newValue), MAXIMUM_FRACTION_DIGITS);
2715 super.setMaximumFractionDigits((maximumFractionDigits > DOUBLE_FRACTION_DIGITS) ?
2716 DOUBLE_FRACTION_DIGITS : maximumFractionDigits);
2717 if (minimumFractionDigits > maximumFractionDigits) {
2718 minimumFractionDigits = maximumFractionDigits;
2719 super.setMinimumFractionDigits((minimumFractionDigits > DOUBLE_FRACTION_DIGITS) ?
2720 DOUBLE_FRACTION_DIGITS : minimumFractionDigits);
2721 }
2722 }
2723
2724 /**
2725 * Sets the minimum number of digits allowed in the fraction portion of a
2726 * number.
2727 * For formatting numbers other than <code>BigInteger</code> and
2728 * <code>BigDecimal</code> objects, the lower of <code>newValue</code> and
2729 * 340 is used. Negative input values are replaced with 0.
2730 * @see NumberFormat#setMinimumFractionDigits
2731 */
2732 public void setMinimumFractionDigits(int newValue) {
2733 minimumFractionDigits = Math.min(Math.max(0, newValue), MAXIMUM_FRACTION_DIGITS);
2734 super.setMinimumFractionDigits((minimumFractionDigits > DOUBLE_FRACTION_DIGITS) ?
2735 DOUBLE_FRACTION_DIGITS : minimumFractionDigits);
2736 if (minimumFractionDigits > maximumFractionDigits) {
2737 maximumFractionDigits = minimumFractionDigits;
2738 super.setMaximumFractionDigits((maximumFractionDigits > DOUBLE_FRACTION_DIGITS) ?
2739 DOUBLE_FRACTION_DIGITS : maximumFractionDigits);
2740 }
2741 }
2742
2743 /**
2744 * Gets the maximum number of digits allowed in the integer portion of a
2745 * number.
2746 * For formatting numbers other than <code>BigInteger</code> and
2747 * <code>BigDecimal</code> objects, the lower of the return value and
2748 * 309 is used.
2749 * @see #setMaximumIntegerDigits
2750 */
2751 public int getMaximumIntegerDigits() {
2752 return maximumIntegerDigits;
2753 }
2754
2755 /**
2756 * Gets the minimum number of digits allowed in the integer portion of a
2757 * number.
2758 * For formatting numbers other than <code>BigInteger</code> and
2759 * <code>BigDecimal</code> objects, the lower of the return value and
2760 * 309 is used.
2761 * @see #setMinimumIntegerDigits
2762 */
2763 public int getMinimumIntegerDigits() {
2764 return minimumIntegerDigits;
2765 }
2766
2767 /**
2768 * Gets the maximum number of digits allowed in the fraction portion of a
2769 * number.
2770 * For formatting numbers other than <code>BigInteger</code> and
2771 * <code>BigDecimal</code> objects, the lower of the return value and
2772 * 340 is used.
2773 * @see #setMaximumFractionDigits
2774 */
2775 public int getMaximumFractionDigits() {
2776 return maximumFractionDigits;
2777 }
2778
2779 /**
2780 * Gets the minimum number of digits allowed in the fraction portion of a
2781 * number.
2782 * For formatting numbers other than <code>BigInteger</code> and
2783 * <code>BigDecimal</code> objects, the lower of the return value and
2784 * 340 is used.
2785 * @see #setMinimumFractionDigits
2786 */
2787 public int getMinimumFractionDigits() {
2788 return minimumFractionDigits;
2789 }
2790
2791 /**
2792 * Gets the currency used by this decimal format when formatting
2793 * currency values.
2794 * The currency is obtained by calling
2795 * {@link DecimalFormatSymbols#getCurrency DecimalFormatSymbols.getCurrency}
2796 * on this number format's symbols.
2797 *
2798 * @return the currency used by this decimal format, or <code>null</code>
2799 * @since 1.4
2800 */
2801 public Currency getCurrency() {
2802 return symbols.getCurrency();
2803 }
2804
2805 /**
2806 * Sets the currency used by this number format when formatting
2807 * currency values. This does not update the minimum or maximum
2808 * number of fraction digits used by the number format.
2809 * The currency is set by calling
2810 * {@link DecimalFormatSymbols#setCurrency DecimalFormatSymbols.setCurrency}
2811 * on this number format's symbols.
2812 *
2813 * @param currency the new currency to be used by this decimal format
2814 * @exception NullPointerException if <code>currency</code> is null
2815 * @since 1.4
2816 */
2817 public void setCurrency(Currency currency) {
2818 if (currency != symbols.getCurrency()) {
2819 symbols.setCurrency(currency);
2820 if (isCurrencyFormat) {
2821 expandAffixes();
2822 }
2823 }
2824 }
2825
2826 /**
2827 * Gets the {@link java.math.RoundingMode} used in this DecimalFormat.
2828 *
2829 * @return The <code>RoundingMode</code> used for this DecimalFormat.
2830 * @see #setRoundingMode(RoundingMode)
2831 * @since 1.6
2832 */
2833 public RoundingMode getRoundingMode() {
2834 return roundingMode;
2835 }
2836
2837 /**
2838 * Sets the {@link java.math.RoundingMode} used in this DecimalFormat.
2839 *
2840 * @param roundingMode The <code>RoundingMode</code> to be used
2841 * @see #getRoundingMode()
2842 * @exception NullPointerException if <code>roundingMode</code> is null.
2843 * @since 1.6
2844 */
2845 public void setRoundingMode(RoundingMode roundingMode) {
2846 if (roundingMode == null) {
2847 throw new NullPointerException();
2848 }
2849
2850 this.roundingMode = roundingMode;
2851 digitList.setRoundingMode(roundingMode);
2852 }
2853
2854 /**
2855 * Adjusts the minimum and maximum fraction digits to values that
2856 * are reasonable for the currency's default fraction digits.
2857 */
2858 void adjustForCurrencyDefaultFractionDigits() {
2859 Currency currency = symbols.getCurrency();
2860 if (currency == null) {
2861 try {
2862 currency = Currency.getInstance(symbols.getInternationalCurrencySymbol());
2863 } catch (IllegalArgumentException e) {
2864 }
2865 }
2866 if (currency != null) {
2867 int digits = currency.getDefaultFractionDigits();
2868 if (digits != -1) {
2869 int oldMinDigits = getMinimumFractionDigits();
2870 // Common patterns are "#.##", "#.00", "#".
2871 // Try to adjust all of them in a reasonable way.
2872 if (oldMinDigits == getMaximumFractionDigits()) {
2873 setMinimumFractionDigits(digits);
2874 setMaximumFractionDigits(digits);
2875 } else {
2876 setMinimumFractionDigits(Math.min(digits, oldMinDigits));
2877 setMaximumFractionDigits(digits);
2878 }
2879 }
2880 }
2881 }
2882
2883 /**
2884 * Reads the default serializable fields from the stream and performs
2885 * validations and adjustments for older serialized versions. The
2886 * validations and adjustments are:
2887 * <ol>
2888 * <li>
2889 * Verify that the superclass's digit count fields correctly reflect
2890 * the limits imposed on formatting numbers other than
2891 * <code>BigInteger</code> and <code>BigDecimal</code> objects. These
2892 * limits are stored in the superclass for serialization compatibility
2893 * with older versions, while the limits for <code>BigInteger</code> and
2894 * <code>BigDecimal</code> objects are kept in this class.
2895 * If, in the superclass, the minimum or maximum integer digit count is
2896 * larger than <code>DOUBLE_INTEGER_DIGITS</code> or if the minimum or
2897 * maximum fraction digit count is larger than
2898 * <code>DOUBLE_FRACTION_DIGITS</code>, then the stream data is invalid
2899 * and this method throws an <code>InvalidObjectException</code>.
2900 * <li>
2901 * If <code>serialVersionOnStream</code> is less than 4, initialize
2902 * <code>roundingMode</code> to {@link java.math.RoundingMode#HALF_EVEN
2903 * RoundingMode.HALF_EVEN}. This field is new with version 4.
2904 * <li>
2905 * If <code>serialVersionOnStream</code> is less than 3, then call
2906 * the setters for the minimum and maximum integer and fraction digits with
2907 * the values of the corresponding superclass getters to initialize the
2908 * fields in this class. The fields in this class are new with version 3.
2909 * <li>
2910 * If <code>serialVersionOnStream</code> is less than 1, indicating that
2911 * the stream was written by JDK 1.1, initialize
2912 * <code>useExponentialNotation</code>
2913 * to false, since it was not present in JDK 1.1.
2914 * <li>
2915 * Set <code>serialVersionOnStream</code> to the maximum allowed value so
2916 * that default serialization will work properly if this object is streamed
2917 * out again.
2918 * </ol>
2919 *
2920 * <p>Stream versions older than 2 will not have the affix pattern variables
2921 * <code>posPrefixPattern</code> etc. As a result, they will be initialized
2922 * to <code>null</code>, which means the affix strings will be taken as
2923 * literal values. This is exactly what we want, since that corresponds to
2924 * the pre-version-2 behavior.
2925 */
2926 private void readObject(ObjectInputStream stream)
2927 throws IOException, ClassNotFoundException
2928 {
2929 stream.defaultReadObject();
2930 digitList = new DigitList();
2931
2932 if (serialVersionOnStream < 4) {
2933 setRoundingMode(RoundingMode.HALF_EVEN);
2934 }
2935 // We only need to check the maximum counts because NumberFormat
2936 // .readObject has already ensured that the maximum is greater than the
2937 // minimum count.
2938 if (super.getMaximumIntegerDigits() > DOUBLE_INTEGER_DIGITS ||
2939 super.getMaximumFractionDigits() > DOUBLE_FRACTION_DIGITS) {
2940 throw new InvalidObjectException("Digit count out of range");
2941 }
2942 if (serialVersionOnStream < 3) {
2943 setMaximumIntegerDigits(super.getMaximumIntegerDigits());
2944 setMinimumIntegerDigits(super.getMinimumIntegerDigits());
2945 setMaximumFractionDigits(super.getMaximumFractionDigits());
2946 setMinimumFractionDigits(super.getMinimumFractionDigits());
2947 }
2948 if (serialVersionOnStream < 1) {
2949 // Didn't have exponential fields
2950 useExponentialNotation = false;
2951 }
2952 serialVersionOnStream = currentSerialVersion;
2953 }
2954
2955 //----------------------------------------------------------------------
2956 // INSTANCE VARIABLES
2957 //----------------------------------------------------------------------
2958
2959 private transient DigitList digitList = new DigitList();
2960
2961 /**
2962 * The symbol used as a prefix when formatting positive numbers, e.g. "+".
2963 *
2964 * @serial
2965 * @see #getPositivePrefix
2966 */
2967 private String positivePrefix = "";
2968
2969 /**
2970 * The symbol used as a suffix when formatting positive numbers.
2971 * This is often an empty string.
2972 *
2973 * @serial
2974 * @see #getPositiveSuffix
2975 */
2976 private String positiveSuffix = "";
2977
2978 /**
2979 * The symbol used as a prefix when formatting negative numbers, e.g. "-".
2980 *
2981 * @serial
2982 * @see #getNegativePrefix
2983 */
2984 private String negativePrefix = "-";
2985
2986 /**
2987 * The symbol used as a suffix when formatting negative numbers.
2988 * This is often an empty string.
2989 *
2990 * @serial
2991 * @see #getNegativeSuffix
2992 */
2993 private String negativeSuffix = "";
2994
2995 /**
2996 * The prefix pattern for non-negative numbers. This variable corresponds
2997 * to <code>positivePrefix</code>.
2998 *
2999 * <p>This pattern is expanded by the method <code>expandAffix()</code> to
3000 * <code>positivePrefix</code> to update the latter to reflect changes in
3001 * <code>symbols</code>. If this variable is <code>null</code> then
3002 * <code>positivePrefix</code> is taken as a literal value that does not
3003 * change when <code>symbols</code> changes. This variable is always
3004 * <code>null</code> for <code>DecimalFormat</code> objects older than
3005 * stream version 2 restored from stream.
3006 *
3007 * @serial
3008 * @since 1.3
3009 */
3010 private String posPrefixPattern;
3011
3012 /**
3013 * The suffix pattern for non-negative numbers. This variable corresponds
3014 * to <code>positiveSuffix</code>. This variable is analogous to
3015 * <code>posPrefixPattern</code>; see that variable for further
3016 * documentation.
3017 *
3018 * @serial
3019 * @since 1.3
3020 */
3021 private String posSuffixPattern;
3022
3023 /**
3024 * The prefix pattern for negative numbers. This variable corresponds
3025 * to <code>negativePrefix</code>. This variable is analogous to
3026 * <code>posPrefixPattern</code>; see that variable for further
3027 * documentation.
3028 *
3029 * @serial
3030 * @since 1.3
3031 */
3032 private String negPrefixPattern;
3033
3034 /**
3035 * The suffix pattern for negative numbers. This variable corresponds
3036 * to <code>negativeSuffix</code>. This variable is analogous to
3037 * <code>posPrefixPattern</code>; see that variable for further
3038 * documentation.
3039 *
3040 * @serial
3041 * @since 1.3
3042 */
3043 private String negSuffixPattern;
3044
3045 /**
3046 * The multiplier for use in percent, per mille, etc.
3047 *
3048 * @serial
3049 * @see #getMultiplier
3050 */
3051 private int multiplier = 1;
3052
3053 /**
3054 * The number of digits between grouping separators in the integer
3055 * portion of a number. Must be greater than 0 if
3056 * <code>NumberFormat.groupingUsed</code> is true.
3057 *
3058 * @serial
3059 * @see #getGroupingSize
3060 * @see java.text.NumberFormat#isGroupingUsed
3061 */
3062 private byte groupingSize = 3; // invariant, > 0 if useThousands
3063
3064 /**
3065 * If true, forces the decimal separator to always appear in a formatted
3066 * number, even if the fractional part of the number is zero.
3067 *
3068 * @serial
3069 * @see #isDecimalSeparatorAlwaysShown
3070 */
3071 private boolean decimalSeparatorAlwaysShown = false;
3072
3073 /**
3074 * If true, parse returns BigDecimal wherever possible.
3075 *
3076 * @serial
3077 * @see #isParseBigDecimal
3078 * @since 1.5
3079 */
3080 private boolean parseBigDecimal = false;
3081
3082
3083 /**
3084 * True if this object represents a currency format. This determines
3085 * whether the monetary decimal separator is used instead of the normal one.
3086 */
3087 private transient boolean isCurrencyFormat = false;
3088
3089 /**
3090 * The <code>DecimalFormatSymbols</code> object used by this format.
3091 * It contains the symbols used to format numbers, e.g. the grouping separator,
3092 * decimal separator, and so on.
3093 *
3094 * @serial
3095 * @see #setDecimalFormatSymbols
3096 * @see java.text.DecimalFormatSymbols
3097 */
3098 private DecimalFormatSymbols symbols = null; // LIU new DecimalFormatSymbols();
3099
3100 /**
3101 * True to force the use of exponential (i.e. scientific) notation when formatting
3102 * numbers.
3103 *
3104 * @serial
3105 * @since 1.2
3106 */
3107 private boolean useExponentialNotation; // Newly persistent in the Java 2 platform v.1.2
3108
3109 /**
3110 * FieldPositions describing the positive prefix String. This is
3111 * lazily created. Use <code>getPositivePrefixFieldPositions</code>
3112 * when needed.
3113 */
3114 private transient FieldPosition[] positivePrefixFieldPositions;
3115
3116 /**
3117 * FieldPositions describing the positive suffix String. This is
3118 * lazily created. Use <code>getPositiveSuffixFieldPositions</code>
3119 * when needed.
3120 */
3121 private transient FieldPosition[] positiveSuffixFieldPositions;
3122
3123 /**
3124 * FieldPositions describing the negative prefix String. This is
3125 * lazily created. Use <code>getNegativePrefixFieldPositions</code>
3126 * when needed.
3127 */
3128 private transient FieldPosition[] negativePrefixFieldPositions;
3129
3130 /**
3131 * FieldPositions describing the negative suffix String. This is
3132 * lazily created. Use <code>getNegativeSuffixFieldPositions</code>
3133 * when needed.
3134 */
3135 private transient FieldPosition[] negativeSuffixFieldPositions;
3136
3137 /**
3138 * The minimum number of digits used to display the exponent when a number is
3139 * formatted in exponential notation. This field is ignored if
3140 * <code>useExponentialNotation</code> is not true.
3141 *
3142 * @serial
3143 * @since 1.2
3144 */
3145 private byte minExponentDigits; // Newly persistent in the Java 2 platform v.1.2
3146
3147 /**
3148 * The maximum number of digits allowed in the integer portion of a
3149 * <code>BigInteger</code> or <code>BigDecimal</code> number.
3150 * <code>maximumIntegerDigits</code> must be greater than or equal to
3151 * <code>minimumIntegerDigits</code>.
3152 *
3153 * @serial
3154 * @see #getMaximumIntegerDigits
3155 * @since 1.5
3156 */
3157 private int maximumIntegerDigits = super.getMaximumIntegerDigits();
3158
3159 /**
3160 * The minimum number of digits allowed in the integer portion of a
3161 * <code>BigInteger</code> or <code>BigDecimal</code> number.
3162 * <code>minimumIntegerDigits</code> must be less than or equal to
3163 * <code>maximumIntegerDigits</code>.
3164 *
3165 * @serial
3166 * @see #getMinimumIntegerDigits
3167 * @since 1.5
3168 */
3169 private int minimumIntegerDigits = super.getMinimumIntegerDigits();
3170
3171 /**
3172 * The maximum number of digits allowed in the fractional portion of a
3173 * <code>BigInteger</code> or <code>BigDecimal</code> number.
3174 * <code>maximumFractionDigits</code> must be greater than or equal to
3175 * <code>minimumFractionDigits</code>.
3176 *
3177 * @serial
3178 * @see #getMaximumFractionDigits
3179 * @since 1.5
3180 */
3181 private int maximumFractionDigits = super.getMaximumFractionDigits();
3182
3183 /**
3184 * The minimum number of digits allowed in the fractional portion of a
3185 * <code>BigInteger</code> or <code>BigDecimal</code> number.
3186 * <code>minimumFractionDigits</code> must be less than or equal to
3187 * <code>maximumFractionDigits</code>.
3188 *
3189 * @serial
3190 * @see #getMinimumFractionDigits
3191 * @since 1.5
3192 */
3193 private int minimumFractionDigits = super.getMinimumFractionDigits();
3194
3195 /**
3196 * The {@link java.math.RoundingMode} used in this DecimalFormat.
3197 *
3198 * @serial
3199 * @since 1.6
3200 */
3201 private RoundingMode roundingMode = RoundingMode.HALF_EVEN;
3202
3203 //----------------------------------------------------------------------
3204
3205 static final int currentSerialVersion = 4;
3206
3207 /**
3208 * The internal serial version which says which version was written.
3209 * Possible values are:
3210 * <ul>
3211 * <li><b>0</b> (default): versions before the Java 2 platform v1.2
3212 * <li><b>1</b>: version for 1.2, which includes the two new fields
3213 * <code>useExponentialNotation</code> and
3214 * <code>minExponentDigits</code>.
3215 * <li><b>2</b>: version for 1.3 and later, which adds four new fields:
3216 * <code>posPrefixPattern</code>, <code>posSuffixPattern</code>,
3217 * <code>negPrefixPattern</code>, and <code>negSuffixPattern</code>.
3218 * <li><b>3</b>: version for 1.5 and later, which adds five new fields:
3219 * <code>maximumIntegerDigits</code>,
3220 * <code>minimumIntegerDigits</code>,
3221 * <code>maximumFractionDigits</code>,
3222 * <code>minimumFractionDigits</code>, and
3223 * <code>parseBigDecimal</code>.
3224 * <li><b>4</b>: version for 1.6 and later, which adds one new field:
3225 * <code>roundingMode</code>.
3226 * </ul>
3227 * @since 1.2
3228 * @serial
3229 */
3230 private int serialVersionOnStream = currentSerialVersion;
3231
3232 //----------------------------------------------------------------------
3233 // CONSTANTS
3234 //----------------------------------------------------------------------
3235
3236 // Constants for characters used in programmatic (unlocalized) patterns.
3237 private static final char PATTERN_ZERO_DIGIT = '0';
3238 private static final char PATTERN_GROUPING_SEPARATOR = ',';
3239 private static final char PATTERN_DECIMAL_SEPARATOR = '.';
3240 private static final char PATTERN_PER_MILLE = '\u2030';
3241 private static final char PATTERN_PERCENT = '%';
3242 private static final char PATTERN_DIGIT = '#';
3243 private static final char PATTERN_SEPARATOR = ';';
3244 private static final String PATTERN_EXPONENT = "E";
3245 private static final char PATTERN_MINUS = '-';
3246
3247 /**
3248 * The CURRENCY_SIGN is the standard Unicode symbol for currency. It
3249 * is used in patterns and substituted with either the currency symbol,
3250 * or if it is doubled, with the international currency symbol. If the
3251 * CURRENCY_SIGN is seen in a pattern, then the decimal separator is
3252 * replaced with the monetary decimal separator.
3253 *
3254 * The CURRENCY_SIGN is not localized.
3255 */
3256 private static final char CURRENCY_SIGN = '\u00A4';
3257
3258 private static final char QUOTE = '\'';
3259
3260 private static FieldPosition[] EmptyFieldPositionArray = new FieldPosition[0];
3261
3262 // Upper limit on integer and fraction digits for a Java double
3263 static final int DOUBLE_INTEGER_DIGITS = 309;
3264 static final int DOUBLE_FRACTION_DIGITS = 340;
3265
3266 // Upper limit on integer and fraction digits for BigDecimal and BigInteger
3267 static final int MAXIMUM_INTEGER_DIGITS = Integer.MAX_VALUE;
3268 static final int MAXIMUM_FRACTION_DIGITS = Integer.MAX_VALUE;
3269
3270 // Proclaim JDK 1.1 serial compatibility.
3271 static final long serialVersionUID = 864413376551465018L;
3272
3273 /**
3274 * Cache to hold the NumberPattern of a Locale.
3275 */
3276 private static final ConcurrentMap<Locale, String> cachedLocaleData
3277 = new ConcurrentHashMap<Locale, String>(3);
3278}
java/pkg/java/text/DecimalFormatSymbols$DecimalFormatSymbolsGetter.class+0−0

Binary file not shown.

java/pkg/java/text/DecimalFormatSymbols.class+0−0

Binary file not shown.

java/pkg/java/text/DecimalFormatSymbols.java+837−0
@@ -0,0 +1,837 @@
1/*
2 * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26/*
27 * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
28 * (C) Copyright IBM Corp. 1996 - 1998 - All Rights Reserved
29 *
30 * The original version of this source code and documentation is copyrighted
31 * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
32 * materials are provided under terms of a License Agreement between Taligent
33 * and Sun. This technology is protected by multiple US and International
34 * patents. This notice and attribution to Taligent may not be removed.
35 * Taligent is a registered trademark of Taligent, Inc.
36 *
37 */
38
39package java.text;
40
41import java.io.IOException;
42import java.io.ObjectInputStream;
43import java.io.Serializable;
44import java.text.spi.DecimalFormatSymbolsProvider;
45import java.util.Currency;
46import java.util.Locale;
47import java.util.ResourceBundle;
48import java.util.concurrent.ConcurrentHashMap;
49
50import sun.util.LocaleServiceProviderPool;
51import sun.util.resources.LocaleData;
52
53/**
54 * This class represents the set of symbols (such as the decimal separator,
55 * the grouping separator, and so on) needed by <code>DecimalFormat</code>
56 * to format numbers. <code>DecimalFormat</code> creates for itself an instance of
57 * <code>DecimalFormatSymbols</code> from its locale data. If you need to change any
58 * of these symbols, you can get the <code>DecimalFormatSymbols</code> object from
59 * your <code>DecimalFormat</code> and modify it.
60 *
61 * @see java.util.Locale
62 * @see DecimalFormat
63 * @author Mark Davis
64 * @author Alan Liu
65 */
66
67public class DecimalFormatSymbols implements Cloneable, Serializable {
68
69 /**
70 * Create a DecimalFormatSymbols object for the default locale.
71 * This constructor can only construct instances for the locales
72 * supported by the Java runtime environment, not for those
73 * supported by installed
74 * {@link java.text.spi.DecimalFormatSymbolsProvider DecimalFormatSymbolsProvider}
75 * implementations. For full locale coverage, use the
76 * {@link #getInstance(Locale) getInstance} method.
77 */
78 public DecimalFormatSymbols() {
79 initialize( Locale.getDefault(Locale.Category.FORMAT) );
80 }
81
82 /**
83 * Create a DecimalFormatSymbols object for the given locale.
84 * This constructor can only construct instances for the locales
85 * supported by the Java runtime environment, not for those
86 * supported by installed
87 * {@link java.text.spi.DecimalFormatSymbolsProvider DecimalFormatSymbolsProvider}
88 * implementations. For full locale coverage, use the
89 * {@link #getInstance(Locale) getInstance} method.
90 *
91 * @exception NullPointerException if <code>locale</code> is null
92 */
93 public DecimalFormatSymbols( Locale locale ) {
94 initialize( locale );
95 }
96
97 /**
98 * Returns an array of all locales for which the
99 * <code>getInstance</code> methods of this class can return
100 * localized instances.
101 * The returned array represents the union of locales supported by the Java
102 * runtime and by installed
103 * {@link java.text.spi.DecimalFormatSymbolsProvider DecimalFormatSymbolsProvider}
104 * implementations. It must contain at least a <code>Locale</code>
105 * instance equal to {@link java.util.Locale#US Locale.US}.
106 *
107 * @return An array of locales for which localized
108 * <code>DecimalFormatSymbols</code> instances are available.
109 * @since 1.6
110 */
111 public static Locale[] getAvailableLocales() {
112 LocaleServiceProviderPool pool =
113 LocaleServiceProviderPool.getPool(DecimalFormatSymbolsProvider.class);
114 return pool.getAvailableLocales();
115 }
116
117 /**
118 * Gets the <code>DecimalFormatSymbols</code> instance for the default
119 * locale. This method provides access to <code>DecimalFormatSymbols</code>
120 * instances for locales supported by the Java runtime itself as well
121 * as for those supported by installed
122 * {@link java.text.spi.DecimalFormatSymbolsProvider
123 * DecimalFormatSymbolsProvider} implementations.
124 * @return a <code>DecimalFormatSymbols</code> instance.
125 * @since 1.6
126 */
127 public static final DecimalFormatSymbols getInstance() {
128 return getInstance(Locale.getDefault(Locale.Category.FORMAT));
129 }
130
131 /**
132 * Gets the <code>DecimalFormatSymbols</code> instance for the specified
133 * locale. This method provides access to <code>DecimalFormatSymbols</code>
134 * instances for locales supported by the Java runtime itself as well
135 * as for those supported by installed
136 * {@link java.text.spi.DecimalFormatSymbolsProvider
137 * DecimalFormatSymbolsProvider} implementations.
138 * @param locale the desired locale.
139 * @return a <code>DecimalFormatSymbols</code> instance.
140 * @exception NullPointerException if <code>locale</code> is null
141 * @since 1.6
142 */
143 public static final DecimalFormatSymbols getInstance(Locale locale) {
144
145 // Check whether a provider can provide an implementation that's closer
146 // to the requested locale than what the Java runtime itself can provide.
147 LocaleServiceProviderPool pool =
148 LocaleServiceProviderPool.getPool(DecimalFormatSymbolsProvider.class);
149 if (pool.hasProviders()) {
150 DecimalFormatSymbols providersInstance = pool.getLocalizedObject(
151 DecimalFormatSymbolsGetter.INSTANCE, locale);
152 if (providersInstance != null) {
153 return providersInstance;
154 }
155 }
156
157 return new DecimalFormatSymbols(locale);
158 }
159
160 /**
161 * Gets the character used for zero. Different for Arabic, etc.
162 */
163 public char getZeroDigit() {
164 return zeroDigit;
165 }
166
167 /**
168 * Sets the character used for zero. Different for Arabic, etc.
169 */
170 public void setZeroDigit(char zeroDigit) {
171 this.zeroDigit = zeroDigit;
172 }
173
174 /**
175 * Gets the character used for thousands separator. Different for French, etc.
176 */
177 public char getGroupingSeparator() {
178 return groupingSeparator;
179 }
180
181 /**
182 * Sets the character used for thousands separator. Different for French, etc.
183 */
184 public void setGroupingSeparator(char groupingSeparator) {
185 this.groupingSeparator = groupingSeparator;
186 }
187
188 /**
189 * Gets the character used for decimal sign. Different for French, etc.
190 */
191 public char getDecimalSeparator() {
192 return decimalSeparator;
193 }
194
195 /**
196 * Sets the character used for decimal sign. Different for French, etc.
197 */
198 public void setDecimalSeparator(char decimalSeparator) {
199 this.decimalSeparator = decimalSeparator;
200 }
201
202 /**
203 * Gets the character used for per mille sign. Different for Arabic, etc.
204 */
205 public char getPerMill() {
206 return perMill;
207 }
208
209 /**
210 * Sets the character used for per mille sign. Different for Arabic, etc.
211 */
212 public void setPerMill(char perMill) {
213 this.perMill = perMill;
214 }
215
216 /**
217 * Gets the character used for percent sign. Different for Arabic, etc.
218 */
219 public char getPercent() {
220 return percent;
221 }
222
223 /**
224 * Sets the character used for percent sign. Different for Arabic, etc.
225 */
226 public void setPercent(char percent) {
227 this.percent = percent;
228 }
229
230 /**
231 * Gets the character used for a digit in a pattern.
232 */
233 public char getDigit() {
234 return digit;
235 }
236
237 /**
238 * Sets the character used for a digit in a pattern.
239 */
240 public void setDigit(char digit) {
241 this.digit = digit;
242 }
243
244 /**
245 * Gets the character used to separate positive and negative subpatterns
246 * in a pattern.
247 */
248 public char getPatternSeparator() {
249 return patternSeparator;
250 }
251
252 /**
253 * Sets the character used to separate positive and negative subpatterns
254 * in a pattern.
255 */
256 public void setPatternSeparator(char patternSeparator) {
257 this.patternSeparator = patternSeparator;
258 }
259
260 /**
261 * Gets the string used to represent infinity. Almost always left
262 * unchanged.
263 */
264 public String getInfinity() {
265 return infinity;
266 }
267
268 /**
269 * Sets the string used to represent infinity. Almost always left
270 * unchanged.
271 */
272 public void setInfinity(String infinity) {
273 this.infinity = infinity;
274 }
275
276 /**
277 * Gets the string used to represent "not a number". Almost always left
278 * unchanged.
279 */
280 public String getNaN() {
281 return NaN;
282 }
283
284 /**
285 * Sets the string used to represent "not a number". Almost always left
286 * unchanged.
287 */
288 public void setNaN(String NaN) {
289 this.NaN = NaN;
290 }
291
292 /**
293 * Gets the character used to represent minus sign. If no explicit
294 * negative format is specified, one is formed by prefixing
295 * minusSign to the positive format.
296 */
297 public char getMinusSign() {
298 return minusSign;
299 }
300
301 /**
302 * Sets the character used to represent minus sign. If no explicit
303 * negative format is specified, one is formed by prefixing
304 * minusSign to the positive format.
305 */
306 public void setMinusSign(char minusSign) {
307 this.minusSign = minusSign;
308 }
309
310 /**
311 * Returns the currency symbol for the currency of these
312 * DecimalFormatSymbols in their locale.
313 * @since 1.2
314 */
315 public String getCurrencySymbol()
316 {
317 return currencySymbol;
318 }
319
320 /**
321 * Sets the currency symbol for the currency of these
322 * DecimalFormatSymbols in their locale.
323 * @since 1.2
324 */
325 public void setCurrencySymbol(String currency)
326 {
327 currencySymbol = currency;
328 }
329
330 /**
331 * Returns the ISO 4217 currency code of the currency of these
332 * DecimalFormatSymbols.
333 * @since 1.2
334 */
335 public String getInternationalCurrencySymbol()
336 {
337 return intlCurrencySymbol;
338 }
339
340 /**
341 * Sets the ISO 4217 currency code of the currency of these
342 * DecimalFormatSymbols.
343 * If the currency code is valid (as defined by
344 * {@link java.util.Currency#getInstance(java.lang.String) Currency.getInstance}),
345 * this also sets the currency attribute to the corresponding Currency
346 * instance and the currency symbol attribute to the currency's symbol
347 * in the DecimalFormatSymbols' locale. If the currency code is not valid,
348 * then the currency attribute is set to null and the currency symbol
349 * attribute is not modified.
350 *
351 * @see #setCurrency
352 * @see #setCurrencySymbol
353 * @since 1.2
354 */
355 public void setInternationalCurrencySymbol(String currencyCode)
356 {
357 intlCurrencySymbol = currencyCode;
358 currency = null;
359 if (currencyCode != null) {
360 try {
361 currency = Currency.getInstance(currencyCode);
362 currencySymbol = currency.getSymbol();
363 } catch (IllegalArgumentException e) {
364 }
365 }
366 }
367
368 /**
369 * Gets the currency of these DecimalFormatSymbols. May be null if the
370 * currency symbol attribute was previously set to a value that's not
371 * a valid ISO 4217 currency code.
372 *
373 * @return the currency used, or null
374 * @since 1.4
375 */
376 public Currency getCurrency() {
377 return currency;
378 }
379
380 /**
381 * Sets the currency of these DecimalFormatSymbols.
382 * This also sets the currency symbol attribute to the currency's symbol
383 * in the DecimalFormatSymbols' locale, and the international currency
384 * symbol attribute to the currency's ISO 4217 currency code.
385 *
386 * @param currency the new currency to be used
387 * @exception NullPointerException if <code>currency</code> is null
388 * @since 1.4
389 * @see #setCurrencySymbol
390 * @see #setInternationalCurrencySymbol
391 */
392 public void setCurrency(Currency currency) {
393 if (currency == null) {
394 throw new NullPointerException();
395 }
396 this.currency = currency;
397 intlCurrencySymbol = currency.getCurrencyCode();
398 currencySymbol = currency.getSymbol(locale);
399 }
400
401
402 /**
403 * Returns the monetary decimal separator.
404 * @since 1.2
405 */
406 public char getMonetaryDecimalSeparator()
407 {
408 return monetarySeparator;
409 }
410
411 /**
412 * Sets the monetary decimal separator.
413 * @since 1.2
414 */
415 public void setMonetaryDecimalSeparator(char sep)
416 {
417 monetarySeparator = sep;
418 }
419
420 //------------------------------------------------------------
421 // BEGIN Package Private methods ... to be made public later
422 //------------------------------------------------------------
423
424 /**
425 * Returns the character used to separate the mantissa from the exponent.
426 */
427 char getExponentialSymbol()
428 {
429 return exponential;
430 }
431 /**
432 * Returns the string used to separate the mantissa from the exponent.
433 * Examples: "x10^" for 1.23x10^4, "E" for 1.23E4.
434 *
435 * @return the exponent separator string
436 * @see #setExponentSeparator(java.lang.String)
437 * @since 1.6
438 */
439 public String getExponentSeparator()
440 {
441 return exponentialSeparator;
442 }
443
444 /**
445 * Sets the character used to separate the mantissa from the exponent.
446 */
447 void setExponentialSymbol(char exp)
448 {
449 exponential = exp;
450 }
451
452 /**
453 * Sets the string used to separate the mantissa from the exponent.
454 * Examples: "x10^" for 1.23x10^4, "E" for 1.23E4.
455 *
456 * @param exp the exponent separator string
457 * @exception NullPointerException if <code>exp</code> is null
458 * @see #getExponentSeparator()
459 * @since 1.6
460 */
461 public void setExponentSeparator(String exp)
462 {
463 if (exp == null) {
464 throw new NullPointerException();
465 }
466 exponentialSeparator = exp;
467 }
468
469
470 //------------------------------------------------------------
471 // END Package Private methods ... to be made public later
472 //------------------------------------------------------------
473
474 /**
475 * Standard override.
476 */
477 public Object clone() {
478 try {
479 return (DecimalFormatSymbols)super.clone();
480 // other fields are bit-copied
481 } catch (CloneNotSupportedException e) {
482 throw new InternalError();
483 }
484 }
485
486 /**
487 * Override equals.
488 */
489 public boolean equals(Object obj) {
490 if (obj == null) return false;
491 if (this == obj) return true;
492 if (getClass() != obj.getClass()) return false;
493 DecimalFormatSymbols other = (DecimalFormatSymbols) obj;
494 return (zeroDigit == other.zeroDigit &&
495 groupingSeparator == other.groupingSeparator &&
496 decimalSeparator == other.decimalSeparator &&
497 percent == other.percent &&
498 perMill == other.perMill &&
499 digit == other.digit &&
500 minusSign == other.minusSign &&
501 patternSeparator == other.patternSeparator &&
502 infinity.equals(other.infinity) &&
503 NaN.equals(other.NaN) &&
504 currencySymbol.equals(other.currencySymbol) &&
505 intlCurrencySymbol.equals(other.intlCurrencySymbol) &&
506 currency == other.currency &&
507 monetarySeparator == other.monetarySeparator &&
508 exponentialSeparator.equals(other.exponentialSeparator) &&
509 locale.equals(other.locale));
510 }
511
512 /**
513 * Override hashCode.
514 */
515 public int hashCode() {
516 int result = zeroDigit;
517 result = result * 37 + groupingSeparator;
518 result = result * 37 + decimalSeparator;
519 return result;
520 }
521
522 /**
523 * Initializes the symbols from the FormatData resource bundle.
524 */
525 private void initialize( Locale locale ) {
526 this.locale = locale;
527
528 // get resource bundle data - try the cache first
529 boolean needCacheUpdate = false;
530 Object[] data = cachedLocaleData.get(locale);
531 if (data == null) { /* cache miss */
532 // When numbering system is thai (Locale's extension contains u-nu-thai),
533 // we read the data from th_TH_TH.
534 Locale lookupLocale = locale;
535 String numberType = locale.getUnicodeLocaleType("nu");
536 if (numberType != null && numberType.equals("thai")) {
537 lookupLocale = new Locale("th", "TH", "TH");
538 }
539 data = new Object[3];
540 ResourceBundle rb = LocaleData.getNumberFormatData(lookupLocale);
541 data[0] = rb.getStringArray("NumberElements");
542 needCacheUpdate = true;
543 }
544
545 String[] numberElements = (String[]) data[0];
546
547 decimalSeparator = numberElements[0].charAt(0);
548 groupingSeparator = numberElements[1].charAt(0);
549 patternSeparator = numberElements[2].charAt(0);
550 percent = numberElements[3].charAt(0);
551 zeroDigit = numberElements[4].charAt(0); //different for Arabic,etc.
552 digit = numberElements[5].charAt(0);
553 minusSign = numberElements[6].charAt(0);
554 exponential = numberElements[7].charAt(0);
555 exponentialSeparator = numberElements[7]; //string representation new since 1.6
556 perMill = numberElements[8].charAt(0);
557 infinity = numberElements[9];
558 NaN = numberElements[10];
559
560 // Try to obtain the currency used in the locale's country.
561 // Check for empty country string separately because it's a valid
562 // country ID for Locale (and used for the C locale), but not a valid
563 // ISO 3166 country code, and exceptions are expensive.
564 if (!"".equals(locale.getCountry())) {
565 try {
566 currency = Currency.getInstance(locale);
567 } catch (IllegalArgumentException e) {
568 // use default values below for compatibility
569 }
570 }
571 if (currency != null) {
572 intlCurrencySymbol = currency.getCurrencyCode();
573 if (data[1] != null && data[1] == intlCurrencySymbol) {
574 currencySymbol = (String) data[2];
575 } else {
576 currencySymbol = currency.getSymbol(locale);
577 data[1] = intlCurrencySymbol;
578 data[2] = currencySymbol;
579 needCacheUpdate = true;
580 }
581 } else {
582 // default values
583 intlCurrencySymbol = "XXX";
584 try {
585 currency = Currency.getInstance(intlCurrencySymbol);
586 } catch (IllegalArgumentException e) {
587 }
588 currencySymbol = "\u00A4";
589 }
590 // Currently the monetary decimal separator is the same as the
591 // standard decimal separator for all locales that we support.
592 // If that changes, add a new entry to NumberElements.
593 monetarySeparator = decimalSeparator;
594
595 if (needCacheUpdate) {
596 cachedLocaleData.putIfAbsent(locale, data);
597 }
598 }
599
600 /**
601 * Reads the default serializable fields, provides default values for objects
602 * in older serial versions, and initializes non-serializable fields.
603 * If <code>serialVersionOnStream</code>
604 * is less than 1, initializes <code>monetarySeparator</code> to be
605 * the same as <code>decimalSeparator</code> and <code>exponential</code>
606 * to be 'E'.
607 * If <code>serialVersionOnStream</code> is less than 2,
608 * initializes <code>locale</code>to the root locale, and initializes
609 * If <code>serialVersionOnStream</code> is less than 3, it initializes
610 * <code>exponentialSeparator</code> using <code>exponential</code>.
611 * Sets <code>serialVersionOnStream</code> back to the maximum allowed value so that
612 * default serialization will work properly if this object is streamed out again.
613 * Initializes the currency from the intlCurrencySymbol field.
614 *
615 * @since JDK 1.1.6
616 */
617 private void readObject(ObjectInputStream stream)
618 throws IOException, ClassNotFoundException {
619 stream.defaultReadObject();
620 if (serialVersionOnStream < 1) {
621 // Didn't have monetarySeparator or exponential field;
622 // use defaults.
623 monetarySeparator = decimalSeparator;
624 exponential = 'E';
625 }
626 if (serialVersionOnStream < 2) {
627 // didn't have locale; use root locale
628 locale = Locale.ROOT;
629 }
630 if (serialVersionOnStream < 3) {
631 // didn't have exponentialSeparator. Create one using exponential
632 exponentialSeparator = Character.toString(exponential);
633 }
634 serialVersionOnStream = currentSerialVersion;
635
636 if (intlCurrencySymbol != null) {
637 try {
638 currency = Currency.getInstance(intlCurrencySymbol);
639 } catch (IllegalArgumentException e) {
640 }
641 }
642 }
643
644 /**
645 * Character used for zero.
646 *
647 * @serial
648 * @see #getZeroDigit
649 */
650 private char zeroDigit;
651
652 /**
653 * Character used for thousands separator.
654 *
655 * @serial
656 * @see #getGroupingSeparator
657 */
658 private char groupingSeparator;
659
660 /**
661 * Character used for decimal sign.
662 *
663 * @serial
664 * @see #getDecimalSeparator
665 */
666 private char decimalSeparator;
667
668 /**
669 * Character used for per mille sign.
670 *
671 * @serial
672 * @see #getPerMill
673 */
674 private char perMill;
675
676 /**
677 * Character used for percent sign.
678 * @serial
679 * @see #getPercent
680 */
681 private char percent;
682
683 /**
684 * Character used for a digit in a pattern.
685 *
686 * @serial
687 * @see #getDigit
688 */
689 private char digit;
690
691 /**
692 * Character used to separate positive and negative subpatterns
693 * in a pattern.
694 *
695 * @serial
696 * @see #getPatternSeparator
697 */
698 private char patternSeparator;
699
700 /**
701 * String used to represent infinity.
702 * @serial
703 * @see #getInfinity
704 */
705 private String infinity;
706
707 /**
708 * String used to represent "not a number".
709 * @serial
710 * @see #getNaN
711 */
712 private String NaN;
713
714 /**
715 * Character used to represent minus sign.
716 * @serial
717 * @see #getMinusSign
718 */
719 private char minusSign;
720
721 /**
722 * String denoting the local currency, e.g. "$".
723 * @serial
724 * @see #getCurrencySymbol
725 */
726 private String currencySymbol;
727
728 /**
729 * ISO 4217 currency code denoting the local currency, e.g. "USD".
730 * @serial
731 * @see #getInternationalCurrencySymbol
732 */
733 private String intlCurrencySymbol;
734
735 /**
736 * The decimal separator used when formatting currency values.
737 * @serial
738 * @since JDK 1.1.6
739 * @see #getMonetaryDecimalSeparator
740 */
741 private char monetarySeparator; // Field new in JDK 1.1.6
742
743 /**
744 * The character used to distinguish the exponent in a number formatted
745 * in exponential notation, e.g. 'E' for a number such as "1.23E45".
746 * <p>
747 * Note that the public API provides no way to set this field,
748 * even though it is supported by the implementation and the stream format.
749 * The intent is that this will be added to the API in the future.
750 *
751 * @serial
752 * @since JDK 1.1.6
753 */
754 private char exponential; // Field new in JDK 1.1.6
755
756 /**
757 * The string used to separate the mantissa from the exponent.
758 * Examples: "x10^" for 1.23x10^4, "E" for 1.23E4.
759 * <p>
760 * If both <code>exponential</code> and <code>exponentialSeparator</code>
761 * exist, this <code>exponentialSeparator</code> has the precedence.
762 *
763 * @serial
764 * @since 1.6
765 */
766 private String exponentialSeparator; // Field new in JDK 1.6
767
768 /**
769 * The locale of these currency format symbols.
770 *
771 * @serial
772 * @since 1.4
773 */
774 private Locale locale;
775
776 // currency; only the ISO code is serialized.
777 private transient Currency currency;
778
779 // Proclaim JDK 1.1 FCS compatibility
780 static final long serialVersionUID = 5772796243397350300L;
781
782 // The internal serial version which says which version was written
783 // - 0 (default) for version up to JDK 1.1.5
784 // - 1 for version from JDK 1.1.6, which includes two new fields:
785 // monetarySeparator and exponential.
786 // - 2 for version from J2SE 1.4, which includes locale field.
787 // - 3 for version from J2SE 1.6, which includes exponentialSeparator field.
788 private static final int currentSerialVersion = 3;
789
790 /**
791 * Describes the version of <code>DecimalFormatSymbols</code> present on the stream.
792 * Possible values are:
793 * <ul>
794 * <li><b>0</b> (or uninitialized): versions prior to JDK 1.1.6.
795 *
796 * <li><b>1</b>: Versions written by JDK 1.1.6 or later, which include
797 * two new fields: <code>monetarySeparator</code> and <code>exponential</code>.
798 * <li><b>2</b>: Versions written by J2SE 1.4 or later, which include a
799 * new <code>locale</code> field.
800 * <li><b>3</b>: Versions written by J2SE 1.6 or later, which include a
801 * new <code>exponentialSeparator</code> field.
802 * </ul>
803 * When streaming out a <code>DecimalFormatSymbols</code>, the most recent format
804 * (corresponding to the highest allowable <code>serialVersionOnStream</code>)
805 * is always written.
806 *
807 * @serial
808 * @since JDK 1.1.6
809 */
810 private int serialVersionOnStream = currentSerialVersion;
811
812 /**
813 * cache to hold the NumberElements and the Currency
814 * of a Locale.
815 */
816 private static final ConcurrentHashMap<Locale, Object[]> cachedLocaleData = new ConcurrentHashMap<Locale, Object[]>(3);
817
818 /**
819 * Obtains a DecimalFormatSymbols instance from a DecimalFormatSymbolsProvider
820 * implementation.
821 */
822 private static class DecimalFormatSymbolsGetter
823 implements LocaleServiceProviderPool.LocalizedObjectGetter<DecimalFormatSymbolsProvider,
824 DecimalFormatSymbols> {
825 private static final DecimalFormatSymbolsGetter INSTANCE =
826 new DecimalFormatSymbolsGetter();
827
828 public DecimalFormatSymbols getObject(
829 DecimalFormatSymbolsProvider decimalFormatSymbolsProvider,
830 Locale locale,
831 String key,
832 Object... params) {
833 assert params.length == 0;
834 return decimalFormatSymbolsProvider.getInstance(locale);
835 }
836 }
837}
java/pkg/java/text/DictionaryBasedBreakIterator.class+0−0

Binary file not shown.

java/pkg/java/text/DictionaryBasedBreakIterator.java+514−0
@@ -0,0 +1,514 @@
1/*
2 * Copyright (c) 1999, 2008, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26/*
27 *
28 * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
29 * (C) Copyright IBM Corp. 1996 - 2002 - All Rights Reserved
30 *
31 * The original version of this source code and documentation
32 * is copyrighted and owned by Taligent, Inc., a wholly-owned
33 * subsidiary of IBM. These materials are provided under terms
34 * of a License Agreement between Taligent and Sun. This technology
35 * is protected by multiple US and International patents.
36 *
37 * This notice and attribution to Taligent may not be removed.
38 * Taligent is a registered trademark of Taligent, Inc.
39 */
40
41package java.text;
42
43import java.util.Vector;
44import java.util.Stack;
45import java.util.Hashtable;
46import java.text.CharacterIterator;
47import java.io.InputStream;
48import java.io.IOException;
49
50/**
51 * A subclass of RuleBasedBreakIterator that adds the ability to use a dictionary
52 * to further subdivide ranges of text beyond what is possible using just the
53 * state-table-based algorithm. This is necessary, for example, to handle
54 * word and line breaking in Thai, which doesn't use spaces between words. The
55 * state-table-based algorithm used by RuleBasedBreakIterator is used to divide
56 * up text as far as possible, and then contiguous ranges of letters are
57 * repeatedly compared against a list of known words (i.e., the dictionary)
58 * to divide them up into words.
59 *
60 * DictionaryBasedBreakIterator uses the same rule language as RuleBasedBreakIterator,
61 * but adds one more special substitution name: &lt;dictionary&gt;. This substitution
62 * name is used to identify characters in words in the dictionary. The idea is that
63 * if the iterator passes over a chunk of text that includes two or more characters
64 * in a row that are included in &lt;dictionary&gt;, it goes back through that range and
65 * derives additional break positions (if possible) using the dictionary.
66 *
67 * DictionaryBasedBreakIterator is also constructed with the filename of a dictionary
68 * file. It follows a prescribed search path to locate the dictionary (right now,
69 * it looks for it in /com/ibm/text/resources in each directory in the classpath,
70 * and won't find it in JAR files, but this location is likely to change). The
71 * dictionary file is in a serialized binary format. We have a very primitive (and
72 * slow) BuildDictionaryFile utility for creating dictionary files, but aren't
73 * currently making it public. Contact us for help.
74 */
75class DictionaryBasedBreakIterator extends RuleBasedBreakIterator {
76
77 /**
78 * a list of known words that is used to divide up contiguous ranges of letters,
79 * stored in a compressed, indexed, format that offers fast access
80 */
81 private BreakDictionary dictionary;
82
83 /**
84 * a list of flags indicating which character categories are contained in
85 * the dictionary file (this is used to determine which ranges of characters
86 * to apply the dictionary to)
87 */
88 private boolean[] categoryFlags;
89
90 /**
91 * a temporary hiding place for the number of dictionary characters in the
92 * last range passed over by next()
93 */
94 private int dictionaryCharCount;
95
96 /**
97 * when a range of characters is divided up using the dictionary, the break
98 * positions that are discovered are stored here, preventing us from having
99 * to use either the dictionary or the state table again until the iterator
100 * leaves this range of text
101 */
102 private int[] cachedBreakPositions;
103
104 /**
105 * if cachedBreakPositions is not null, this indicates which item in the
106 * cache the current iteration position refers to
107 */
108 private int positionInCache;
109
110 /**
111 * Constructs a DictionaryBasedBreakIterator.
112 * @param description Same as the description parameter on RuleBasedBreakIterator,
113 * except for the special meaning of "<dictionary>". This parameter is just
114 * passed through to RuleBasedBreakIterator's constructor.
115 * @param dictionaryFilename The filename of the dictionary file to use
116 */
117 public DictionaryBasedBreakIterator(String dataFile, String dictionaryFile)
118 throws IOException {
119 super(dataFile);
120 byte[] tmp = super.getAdditionalData();
121 if (tmp != null) {
122 prepareCategoryFlags(tmp);
123 super.setAdditionalData(null);
124 }
125 dictionary = new BreakDictionary(dictionaryFile);
126 }
127
128 private void prepareCategoryFlags(byte[] data) {
129 categoryFlags = new boolean[data.length];
130 for (int i = 0; i < data.length; i++) {
131 categoryFlags[i] = (data[i] == (byte)1) ? true : false;
132 }
133 }
134
135 public void setText(CharacterIterator newText) {
136 super.setText(newText);
137 cachedBreakPositions = null;
138 dictionaryCharCount = 0;
139 positionInCache = 0;
140 }
141
142 /**
143 * Sets the current iteration position to the beginning of the text.
144 * (i.e., the CharacterIterator's starting offset).
145 * @return The offset of the beginning of the text.
146 */
147 public int first() {
148 cachedBreakPositions = null;
149 dictionaryCharCount = 0;
150 positionInCache = 0;
151 return super.first();
152 }
153
154 /**
155 * Sets the current iteration position to the end of the text.
156 * (i.e., the CharacterIterator's ending offset).
157 * @return The text's past-the-end offset.
158 */
159 public int last() {
160 cachedBreakPositions = null;
161 dictionaryCharCount = 0;
162 positionInCache = 0;
163 return super.last();
164 }
165
166 /**
167 * Advances the iterator one step backwards.
168 * @return The position of the last boundary position before the
169 * current iteration position
170 */
171 public int previous() {
172 CharacterIterator text = getText();
173
174 // if we have cached break positions and we're still in the range
175 // covered by them, just move one step backward in the cache
176 if (cachedBreakPositions != null && positionInCache > 0) {
177 --positionInCache;
178 text.setIndex(cachedBreakPositions[positionInCache]);
179 return cachedBreakPositions[positionInCache];
180 }
181
182 // otherwise, dump the cache and use the inherited previous() method to move
183 // backward. This may fill up the cache with new break positions, in which
184 // case we have to mark our position in the cache
185 else {
186 cachedBreakPositions = null;
187 int result = super.previous();
188 if (cachedBreakPositions != null) {
189 positionInCache = cachedBreakPositions.length - 2;
190 }
191 return result;
192 }
193 }
194
195 /**
196 * Sets the current iteration position to the last boundary position
197 * before the specified position.
198 * @param offset The position to begin searching from
199 * @return The position of the last boundary before "offset"
200 */
201 public int preceding(int offset) {
202 CharacterIterator text = getText();
203 checkOffset(offset, text);
204
205 // if we have no cached break positions, or "offset" is outside the
206 // range covered by the cache, we can just call the inherited routine
207 // (which will eventually call other routines in this class that may
208 // refresh the cache)
209 if (cachedBreakPositions == null || offset <= cachedBreakPositions[0] ||
210 offset > cachedBreakPositions[cachedBreakPositions.length - 1]) {
211 cachedBreakPositions = null;
212 return super.preceding(offset);
213 }
214
215 // on the other hand, if "offset" is within the range covered by the cache,
216 // then all we have to do is search the cache for the last break position
217 // before "offset"
218 else {
219 positionInCache = 0;
220 while (positionInCache < cachedBreakPositions.length
221 && offset > cachedBreakPositions[positionInCache]) {
222 ++positionInCache;
223 }
224 --positionInCache;
225 text.setIndex(cachedBreakPositions[positionInCache]);
226 return text.getIndex();
227 }
228 }
229
230 /**
231 * Sets the current iteration position to the first boundary position after
232 * the specified position.
233 * @param offset The position to begin searching forward from
234 * @return The position of the first boundary after "offset"
235 */
236 public int following(int offset) {
237 CharacterIterator text = getText();
238 checkOffset(offset, text);
239
240 // if we have no cached break positions, or if "offset" is outside the
241 // range covered by the cache, then dump the cache and call our
242 // inherited following() method. This will call other methods in this
243 // class that may refresh the cache.
244 if (cachedBreakPositions == null || offset < cachedBreakPositions[0] ||
245 offset >= cachedBreakPositions[cachedBreakPositions.length - 1]) {
246 cachedBreakPositions = null;
247 return super.following(offset);
248 }
249
250 // on the other hand, if "offset" is within the range covered by the
251 // cache, then just search the cache for the first break position
252 // after "offset"
253 else {
254 positionInCache = 0;
255 while (positionInCache < cachedBreakPositions.length
256 && offset >= cachedBreakPositions[positionInCache]) {
257 ++positionInCache;
258 }
259 text.setIndex(cachedBreakPositions[positionInCache]);
260 return text.getIndex();
261 }
262 }
263
264 /**
265 * This is the implementation function for next().
266 */
267 protected int handleNext() {
268 CharacterIterator text = getText();
269
270 // if there are no cached break positions, or if we've just moved
271 // off the end of the range covered by the cache, we have to dump
272 // and possibly regenerate the cache
273 if (cachedBreakPositions == null ||
274 positionInCache == cachedBreakPositions.length - 1) {
275
276 // start by using the inherited handleNext() to find a tentative return
277 // value. dictionaryCharCount tells us how many dictionary characters
278 // we passed over on our way to the tentative return value
279 int startPos = text.getIndex();
280 dictionaryCharCount = 0;
281 int result = super.handleNext();
282
283 // if we passed over more than one dictionary character, then we use
284 // divideUpDictionaryRange() to regenerate the cached break positions
285 // for the new range
286 if (dictionaryCharCount > 1 && result - startPos > 1) {
287 divideUpDictionaryRange(startPos, result);
288 }
289
290 // otherwise, the value we got back from the inherited fuction
291 // is our return value, and we can dump the cache
292 else {
293 cachedBreakPositions = null;
294 return result;
295 }
296 }
297
298 // if the cache of break positions has been regenerated (or existed all
299 // along), then just advance to the next break position in the cache
300 // and return it
301 if (cachedBreakPositions != null) {
302 ++positionInCache;
303 text.setIndex(cachedBreakPositions[positionInCache]);
304 return cachedBreakPositions[positionInCache];
305 }
306 return -9999; // SHOULD NEVER GET HERE!
307 }
308
309 /**
310 * Looks up a character category for a character.
311 */
312 protected int lookupCategory(int c) {
313 // this override of lookupCategory() exists only to keep track of whether we've
314 // passed over any dictionary characters. It calls the inherited lookupCategory()
315 // to do the real work, and then checks whether its return value is one of the
316 // categories represented in the dictionary. If it is, bump the dictionary-
317 // character count.
318 int result = super.lookupCategory(c);
319 if (result != RuleBasedBreakIterator.IGNORE && categoryFlags[result]) {
320 ++dictionaryCharCount;
321 }
322 return result;
323 }
324
325 /**
326 * This is the function that actually implements the dictionary-based
327 * algorithm. Given the endpoints of a range of text, it uses the
328 * dictionary to determine the positions of any boundaries in this
329 * range. It stores all the boundary positions it discovers in
330 * cachedBreakPositions so that we only have to do this work once
331 * for each time we enter the range.
332 */
333 private void divideUpDictionaryRange(int startPos, int endPos) {
334 CharacterIterator text = getText();
335
336 // the range we're dividing may begin or end with non-dictionary characters
337 // (i.e., for line breaking, we may have leading or trailing punctuation
338 // that needs to be kept with the word). Seek from the beginning of the
339 // range to the first dictionary character
340 text.setIndex(startPos);
341 int c = getCurrent();
342 int category = lookupCategory(c);
343 while (category == IGNORE || !categoryFlags[category]) {
344 c = getNext();
345 category = lookupCategory(c);
346 }
347
348 // initialize. We maintain two stacks: currentBreakPositions contains
349 // the list of break positions that will be returned if we successfully
350 // finish traversing the whole range now. possibleBreakPositions lists
351 // all other possible word ends we've passed along the way. (Whenever
352 // we reach an error [a sequence of characters that can't begin any word
353 // in the dictionary], we back up, possibly delete some breaks from
354 // currentBreakPositions, move a break from possibleBreakPositions
355 // to currentBreakPositions, and start over from there. This process
356 // continues in this way until we either successfully make it all the way
357 // across the range, or exhaust all of our combinations of break
358 // positions.)
359 Stack currentBreakPositions = new Stack();
360 Stack possibleBreakPositions = new Stack();
361 Vector wrongBreakPositions = new Vector();
362
363 // the dictionary is implemented as a trie, which is treated as a state
364 // machine. -1 represents the end of a legal word. Every word in the
365 // dictionary is represented by a path from the root node to -1. A path
366 // that ends in state 0 is an illegal combination of characters.
367 int state = 0;
368
369 // these two variables are used for error handling. We keep track of the
370 // farthest we've gotten through the range being divided, and the combination
371 // of breaks that got us that far. If we use up all possible break
372 // combinations, the text contains an error or a word that's not in the
373 // dictionary. In this case, we "bless" the break positions that got us the
374 // farthest as real break positions, and then start over from scratch with
375 // the character where the error occurred.
376 int farthestEndPoint = text.getIndex();
377 Stack bestBreakPositions = null;
378
379 // initialize (we always exit the loop with a break statement)
380 c = getCurrent();
381 while (true) {
382
383 // if we can transition to state "-1" from our current state, we're
384 // on the last character of a legal word. Push that position onto
385 // the possible-break-positions stack
386 if (dictionary.getNextState(state, 0) == -1) {
387 possibleBreakPositions.push(Integer.valueOf(text.getIndex()));
388 }
389
390 // look up the new state to transition to in the dictionary
391 state = dictionary.getNextStateFromCharacter(state, c);
392
393 // if the character we're sitting on causes us to transition to
394 // the "end of word" state, then it was a non-dictionary character
395 // and we've successfully traversed the whole range. Drop out
396 // of the loop.
397 if (state == -1) {
398 currentBreakPositions.push(Integer.valueOf(text.getIndex()));
399 break;
400 }
401
402 // if the character we're sitting on causes us to transition to
403 // the error state, or if we've gone off the end of the range
404 // without transitioning to the "end of word" state, we've hit
405 // an error...
406 else if (state == 0 || text.getIndex() >= endPos) {
407
408 // if this is the farthest we've gotten, take note of it in
409 // case there's an error in the text
410 if (text.getIndex() > farthestEndPoint) {
411 farthestEndPoint = text.getIndex();
412 bestBreakPositions = (Stack)(currentBreakPositions.clone());
413 }
414
415 // wrongBreakPositions is a list of all break positions
416 // we've tried starting that didn't allow us to traverse
417 // all the way through the text. Every time we pop a
418 //break position off of currentBreakPositions, we put it
419 // into wrongBreakPositions to avoid trying it again later.
420 // If we make it to this spot, we're either going to back
421 // up to a break in possibleBreakPositions and try starting
422 // over from there, or we've exhausted all possible break
423 // positions and are going to do the fallback procedure.
424 // This loop prevents us from messing with anything in
425 // possibleBreakPositions that didn't work as a starting
426 // point the last time we tried it (this is to prevent a bunch of
427 // repetitive checks from slowing down some extreme cases)
428 Integer newStartingSpot = null;
429 while (!possibleBreakPositions.isEmpty() && wrongBreakPositions.contains(
430 possibleBreakPositions.peek())) {
431 possibleBreakPositions.pop();
432 }
433
434 // if we've used up all possible break-position combinations, there's
435 // an error or an unknown word in the text. In this case, we start
436 // over, treating the farthest character we've reached as the beginning
437 // of the range, and "blessing" the break positions that got us that
438 // far as real break positions
439 if (possibleBreakPositions.isEmpty()) {
440 if (bestBreakPositions != null) {
441 currentBreakPositions = bestBreakPositions;
442 if (farthestEndPoint < endPos) {
443 text.setIndex(farthestEndPoint + 1);
444 }
445 else {
446 break;
447 }
448 }
449 else {
450 if ((currentBreakPositions.size() == 0 ||
451 ((Integer)(currentBreakPositions.peek())).intValue() != text.getIndex())
452 && text.getIndex() != startPos) {
453 currentBreakPositions.push(new Integer(text.getIndex()));
454 }
455 getNext();
456 currentBreakPositions.push(new Integer(text.getIndex()));
457 }
458 }
459
460 // if we still have more break positions we can try, then promote the
461 // last break in possibleBreakPositions into currentBreakPositions,
462 // and get rid of all entries in currentBreakPositions that come after
463 // it. Then back up to that position and start over from there (i.e.,
464 // treat that position as the beginning of a new word)
465 else {
466 Integer temp = (Integer)possibleBreakPositions.pop();
467 Object temp2 = null;
468 while (!currentBreakPositions.isEmpty() && temp.intValue() <
469 ((Integer)currentBreakPositions.peek()).intValue()) {
470 temp2 = currentBreakPositions.pop();
471 wrongBreakPositions.addElement(temp2);
472 }
473 currentBreakPositions.push(temp);
474 text.setIndex(((Integer)currentBreakPositions.peek()).intValue());
475 }
476
477 // re-sync "c" for the next go-round, and drop out of the loop if
478 // we've made it off the end of the range
479 c = getCurrent();
480 if (text.getIndex() >= endPos) {
481 break;
482 }
483 }
484
485 // if we didn't hit any exceptional conditions on this last iteration,
486 // just advance to the next character and loop
487 else {
488 c = getNext();
489 }
490 }
491
492 // dump the last break position in the list, and replace it with the actual
493 // end of the range (which may be the same character, or may be further on
494 // because the range actually ended with non-dictionary characters we want to
495 // keep with the word)
496 if (!currentBreakPositions.isEmpty()) {
497 currentBreakPositions.pop();
498 }
499 currentBreakPositions.push(Integer.valueOf(endPos));
500
501 // create a regular array to hold the break positions and copy
502 // the break positions from the stack to the array (in addition,
503 // our starting position goes into this array as a break position).
504 // This array becomes the cache of break positions used by next()
505 // and previous(), so this is where we actually refresh the cache.
506 cachedBreakPositions = new int[currentBreakPositions.size() + 1];
507 cachedBreakPositions[0] = startPos;
508
509 for (int i = 0; i < currentBreakPositions.size(); i++) {
510 cachedBreakPositions[i + 1] = ((Integer)currentBreakPositions.elementAt(i)).intValue();
511 }
512 positionInCache = 0;
513 }
514}
java/pkg/java/text/DigitList$1.class+0−0

Binary file not shown.

java/pkg/java/text/DigitList.class+0−0

Binary file not shown.

java/pkg/java/text/DigitList.java+715−0
@@ -0,0 +1,715 @@
1/*
2 * Copyright (c) 1996, 2006, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26/*
27 * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
28 * (C) Copyright IBM Corp. 1996 - 1998 - All Rights Reserved
29 *
30 * The original version of this source code and documentation is copyrighted
31 * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
32 * materials are provided under terms of a License Agreement between Taligent
33 * and Sun. This technology is protected by multiple US and International
34 * patents. This notice and attribution to Taligent may not be removed.
35 * Taligent is a registered trademark of Taligent, Inc.
36 *
37 */
38
39package java.text;
40
41import java.math.BigDecimal;
42import java.math.BigInteger;
43import java.math.RoundingMode;
44
45/**
46 * Digit List. Private to DecimalFormat.
47 * Handles the transcoding
48 * between numeric values and strings of characters. Only handles
49 * non-negative numbers. The division of labor between DigitList and
50 * DecimalFormat is that DigitList handles the radix 10 representation
51 * issues; DecimalFormat handles the locale-specific issues such as
52 * positive/negative, grouping, decimal point, currency, and so on.
53 *
54 * A DigitList is really a representation of a floating point value.
55 * It may be an integer value; we assume that a double has sufficient
56 * precision to represent all digits of a long.
57 *
58 * The DigitList representation consists of a string of characters,
59 * which are the digits radix 10, from '0' to '9'. It also has a radix
60 * 10 exponent associated with it. The value represented by a DigitList
61 * object can be computed by mulitplying the fraction f, where 0 <= f < 1,
62 * derived by placing all the digits of the list to the right of the
63 * decimal point, by 10^exponent.
64 *
65 * @see Locale
66 * @see Format
67 * @see NumberFormat
68 * @see DecimalFormat
69 * @see ChoiceFormat
70 * @see MessageFormat
71 * @author Mark Davis, Alan Liu
72 */
73final class DigitList implements Cloneable {
74 /**
75 * The maximum number of significant digits in an IEEE 754 double, that
76 * is, in a Java double. This must not be increased, or garbage digits
77 * will be generated, and should not be decreased, or accuracy will be lost.
78 */
79 public static final int MAX_COUNT = 19; // == Long.toString(Long.MAX_VALUE).length()
80
81 /**
82 * These data members are intentionally public and can be set directly.
83 *
84 * The value represented is given by placing the decimal point before
85 * digits[decimalAt]. If decimalAt is < 0, then leading zeros between
86 * the decimal point and the first nonzero digit are implied. If decimalAt
87 * is > count, then trailing zeros between the digits[count-1] and the
88 * decimal point are implied.
89 *
90 * Equivalently, the represented value is given by f * 10^decimalAt. Here
91 * f is a value 0.1 <= f < 1 arrived at by placing the digits in Digits to
92 * the right of the decimal.
93 *
94 * DigitList is normalized, so if it is non-zero, figits[0] is non-zero. We
95 * don't allow denormalized numbers because our exponent is effectively of
96 * unlimited magnitude. The count value contains the number of significant
97 * digits present in digits[].
98 *
99 * Zero is represented by any DigitList with count == 0 or with each digits[i]
100 * for all i <= count == '0'.
101 */
102 public int decimalAt = 0;
103 public int count = 0;
104 public char[] digits = new char[MAX_COUNT];
105
106 private char[] data;
107 private RoundingMode roundingMode = RoundingMode.HALF_EVEN;
108 private boolean isNegative = false;
109
110 /**
111 * Return true if the represented number is zero.
112 */
113 boolean isZero() {
114 for (int i=0; i < count; ++i) {
115 if (digits[i] != '0') {
116 return false;
117 }
118 }
119 return true;
120 }
121
122 /**
123 * Set the rounding mode
124 */
125 void setRoundingMode(RoundingMode r) {
126 roundingMode = r;
127 }
128
129 /**
130 * Clears out the digits.
131 * Use before appending them.
132 * Typically, you set a series of digits with append, then at the point
133 * you hit the decimal point, you set myDigitList.decimalAt = myDigitList.count;
134 * then go on appending digits.
135 */
136 public void clear () {
137 decimalAt = 0;
138 count = 0;
139 }
140
141 /**
142 * Appends a digit to the list, extending the list when necessary.
143 */
144 public void append(char digit) {
145 if (count == digits.length) {
146 char[] data = new char[count + 100];
147 System.arraycopy(digits, 0, data, 0, count);
148 digits = data;
149 }
150 digits[count++] = digit;
151 }
152
153 /**
154 * Utility routine to get the value of the digit list
155 * If (count == 0) this throws a NumberFormatException, which
156 * mimics Long.parseLong().
157 */
158 public final double getDouble() {
159 if (count == 0) {
160 return 0.0;
161 }
162
163 StringBuffer temp = getStringBuffer();
164 temp.append('.');
165 temp.append(digits, 0, count);
166 temp.append('E');
167 temp.append(decimalAt);
168 return Double.parseDouble(temp.toString());
169 }
170
171 /**
172 * Utility routine to get the value of the digit list.
173 * If (count == 0) this returns 0, unlike Long.parseLong().
174 */
175 public final long getLong() {
176 // for now, simple implementation; later, do proper IEEE native stuff
177
178 if (count == 0) {
179 return 0;
180 }
181
182 // We have to check for this, because this is the one NEGATIVE value
183 // we represent. If we tried to just pass the digits off to parseLong,
184 // we'd get a parse failure.
185 if (isLongMIN_VALUE()) {
186 return Long.MIN_VALUE;
187 }
188
189 StringBuffer temp = getStringBuffer();
190 temp.append(digits, 0, count);
191 for (int i = count; i < decimalAt; ++i) {
192 temp.append('0');
193 }
194 return Long.parseLong(temp.toString());
195 }
196
197 public final BigDecimal getBigDecimal() {
198 if (count == 0) {
199 if (decimalAt == 0) {
200 return BigDecimal.ZERO;
201 } else {
202 return new BigDecimal("0E" + decimalAt);
203 }
204 }
205
206 if (decimalAt == count) {
207 return new BigDecimal(digits, 0, count);
208 } else {
209 return new BigDecimal(digits, 0, count).scaleByPowerOfTen(decimalAt - count);
210 }
211 }
212
213 /**
214 * Return true if the number represented by this object can fit into
215 * a long.
216 * @param isPositive true if this number should be regarded as positive
217 * @param ignoreNegativeZero true if -0 should be regarded as identical to
218 * +0; otherwise they are considered distinct
219 * @return true if this number fits into a Java long
220 */
221 boolean fitsIntoLong(boolean isPositive, boolean ignoreNegativeZero) {
222 // Figure out if the result will fit in a long. We have to
223 // first look for nonzero digits after the decimal point;
224 // then check the size. If the digit count is 18 or less, then
225 // the value can definitely be represented as a long. If it is 19
226 // then it may be too large.
227
228 // Trim trailing zeros. This does not change the represented value.
229 while (count > 0 && digits[count - 1] == '0') {
230 --count;
231 }
232
233 if (count == 0) {
234 // Positive zero fits into a long, but negative zero can only
235 // be represented as a double. - bug 4162852
236 return isPositive || ignoreNegativeZero;
237 }
238
239 if (decimalAt < count || decimalAt > MAX_COUNT) {
240 return false;
241 }
242
243 if (decimalAt < MAX_COUNT) return true;
244
245 // At this point we have decimalAt == count, and count == MAX_COUNT.
246 // The number will overflow if it is larger than 9223372036854775807
247 // or smaller than -9223372036854775808.
248 for (int i=0; i<count; ++i) {
249 char dig = digits[i], max = LONG_MIN_REP[i];
250 if (dig > max) return false;
251 if (dig < max) return true;
252 }
253
254 // At this point the first count digits match. If decimalAt is less
255 // than count, then the remaining digits are zero, and we return true.
256 if (count < decimalAt) return true;
257
258 // Now we have a representation of Long.MIN_VALUE, without the leading
259 // negative sign. If this represents a positive value, then it does
260 // not fit; otherwise it fits.
261 return !isPositive;
262 }
263
264 /**
265 * Set the digit list to a representation of the given double value.
266 * This method supports fixed-point notation.
267 * @param isNegative Boolean value indicating whether the number is negative.
268 * @param source Value to be converted; must not be Inf, -Inf, Nan,
269 * or a value <= 0.
270 * @param maximumFractionDigits The most fractional digits which should
271 * be converted.
272 */
273 public final void set(boolean isNegative, double source, int maximumFractionDigits) {
274 set(isNegative, source, maximumFractionDigits, true);
275 }
276
277 /**
278 * Set the digit list to a representation of the given double value.
279 * This method supports both fixed-point and exponential notation.
280 * @param isNegative Boolean value indicating whether the number is negative.
281 * @param source Value to be converted; must not be Inf, -Inf, Nan,
282 * or a value <= 0.
283 * @param maximumDigits The most fractional or total digits which should
284 * be converted.
285 * @param fixedPoint If true, then maximumDigits is the maximum
286 * fractional digits to be converted. If false, total digits.
287 */
288 final void set(boolean isNegative, double source, int maximumDigits, boolean fixedPoint) {
289 set(isNegative, Double.toString(source), maximumDigits, fixedPoint);
290 }
291
292 /**
293 * Generate a representation of the form DDDDD, DDDDD.DDDDD, or
294 * DDDDDE+/-DDDDD.
295 */
296 final void set(boolean isNegative, String s, int maximumDigits, boolean fixedPoint) {
297 this.isNegative = isNegative;
298 int len = s.length();
299 char[] source = getDataChars(len);
300 s.getChars(0, len, source, 0);
301
302 decimalAt = -1;
303 count = 0;
304 int exponent = 0;
305 // Number of zeros between decimal point and first non-zero digit after
306 // decimal point, for numbers < 1.
307 int leadingZerosAfterDecimal = 0;
308 boolean nonZeroDigitSeen = false;
309
310 for (int i = 0; i < len; ) {
311 char c = source[i++];
312 if (c == '.') {
313 decimalAt = count;
314 } else if (c == 'e' || c == 'E') {
315 exponent = parseInt(source, i, len);
316 break;
317 } else {
318 if (!nonZeroDigitSeen) {
319 nonZeroDigitSeen = (c != '0');
320 if (!nonZeroDigitSeen && decimalAt != -1)
321 ++leadingZerosAfterDecimal;
322 }
323 if (nonZeroDigitSeen) {
324 digits[count++] = c;
325 }
326 }
327 }
328 if (decimalAt == -1) {
329 decimalAt = count;
330 }
331 if (nonZeroDigitSeen) {
332 decimalAt += exponent - leadingZerosAfterDecimal;
333 }
334
335 if (fixedPoint) {
336 // The negative of the exponent represents the number of leading
337 // zeros between the decimal and the first non-zero digit, for
338 // a value < 0.1 (e.g., for 0.00123, -decimalAt == 2). If this
339 // is more than the maximum fraction digits, then we have an underflow
340 // for the printed representation.
341 if (-decimalAt > maximumDigits) {
342 // Handle an underflow to zero when we round something like
343 // 0.0009 to 2 fractional digits.
344 count = 0;
345 return;
346 } else if (-decimalAt == maximumDigits) {
347 // If we round 0.0009 to 3 fractional digits, then we have to
348 // create a new one digit in the least significant location.
349 if (shouldRoundUp(0)) {
350 count = 1;
351 ++decimalAt;
352 digits[0] = '1';
353 } else {
354 count = 0;
355 }
356 return;
357 }
358 // else fall through
359 }
360
361 // Eliminate trailing zeros.
362 while (count > 1 && digits[count - 1] == '0') {
363 --count;
364 }
365
366 // Eliminate digits beyond maximum digits to be displayed.
367 // Round up if appropriate.
368 round(fixedPoint ? (maximumDigits + decimalAt) : maximumDigits);
369 }
370
371 /**
372 * Round the representation to the given number of digits.
373 * @param maximumDigits The maximum number of digits to be shown.
374 * Upon return, count will be less than or equal to maximumDigits.
375 */
376 private final void round(int maximumDigits) {
377 // Eliminate digits beyond maximum digits to be displayed.
378 // Round up if appropriate.
379 if (maximumDigits >= 0 && maximumDigits < count) {
380 if (shouldRoundUp(maximumDigits)) {
381 // Rounding up involved incrementing digits from LSD to MSD.
382 // In most cases this is simple, but in a worst case situation
383 // (9999..99) we have to adjust the decimalAt value.
384 for (;;) {
385 --maximumDigits;
386 if (maximumDigits < 0) {
387 // We have all 9's, so we increment to a single digit
388 // of one and adjust the exponent.
389 digits[0] = '1';
390 ++decimalAt;
391 maximumDigits = 0; // Adjust the count
392 break;
393 }
394
395 ++digits[maximumDigits];
396 if (digits[maximumDigits] <= '9') break;
397 // digits[maximumDigits] = '0'; // Unnecessary since we'll truncate this
398 }
399 ++maximumDigits; // Increment for use as count
400 }
401 count = maximumDigits;
402
403 // Eliminate trailing zeros.
404 while (count > 1 && digits[count-1] == '0') {
405 --count;
406 }
407 }
408 }
409
410
411 /**
412 * Return true if truncating the representation to the given number
413 * of digits will result in an increment to the last digit. This
414 * method implements the rounding modes defined in the
415 * java.math.RoundingMode class.
416 * [bnf]
417 * @param maximumDigits the number of digits to keep, from 0 to
418 * <code>count-1</code>. If 0, then all digits are rounded away, and
419 * this method returns true if a one should be generated (e.g., formatting
420 * 0.09 with "#.#").
421 * @exception ArithmeticException if rounding is needed with rounding
422 * mode being set to RoundingMode.UNNECESSARY
423 * @return true if digit <code>maximumDigits-1</code> should be
424 * incremented
425 */
426 private boolean shouldRoundUp(int maximumDigits) {
427 if (maximumDigits < count) {
428 switch(roundingMode) {
429 case UP:
430 for (int i=maximumDigits; i<count; ++i) {
431 if (digits[i] != '0') {
432 return true;
433 }
434 }
435 break;
436 case DOWN:
437 break;
438 case CEILING:
439 for (int i=maximumDigits; i<count; ++i) {
440 if (digits[i] != '0') {
441 return !isNegative;
442 }
443 }
444 break;
445 case FLOOR:
446 for (int i=maximumDigits; i<count; ++i) {
447 if (digits[i] != '0') {
448 return isNegative;
449 }
450 }
451 break;
452 case HALF_UP:
453 if (digits[maximumDigits] >= '5') {
454 return true;
455 }
456 break;
457 case HALF_DOWN:
458 if (digits[maximumDigits] > '5') {
459 return true;
460 } else if (digits[maximumDigits] == '5' ) {
461 for (int i=maximumDigits+1; i<count; ++i) {
462 if (digits[i] != '0') {
463 return true;
464 }
465 }
466 }
467 break;
468 case HALF_EVEN:
469 // Implement IEEE half-even rounding
470 if (digits[maximumDigits] > '5') {
471 return true;
472 } else if (digits[maximumDigits] == '5' ) {
473 for (int i=maximumDigits+1; i<count; ++i) {
474 if (digits[i] != '0') {
475 return true;
476 }
477 }
478 return maximumDigits > 0 && (digits[maximumDigits-1] % 2 != 0);
479 }
480 break;
481 case UNNECESSARY:
482 for (int i=maximumDigits; i<count; ++i) {
483 if (digits[i] != '0') {
484 throw new ArithmeticException(
485 "Rounding needed with the rounding mode being set to RoundingMode.UNNECESSARY");
486 }
487 }
488 break;
489 default:
490 assert false;
491 }
492 }
493 return false;
494 }
495
496 /**
497 * Utility routine to set the value of the digit list from a long
498 */
499 public final void set(boolean isNegative, long source) {
500 set(isNegative, source, 0);
501 }
502
503 /**
504 * Set the digit list to a representation of the given long value.
505 * @param isNegative Boolean value indicating whether the number is negative.
506 * @param source Value to be converted; must be >= 0 or ==
507 * Long.MIN_VALUE.
508 * @param maximumDigits The most digits which should be converted.
509 * If maximumDigits is lower than the number of significant digits
510 * in source, the representation will be rounded. Ignored if <= 0.
511 */
512 public final void set(boolean isNegative, long source, int maximumDigits) {
513 this.isNegative = isNegative;
514
515 // This method does not expect a negative number. However,
516 // "source" can be a Long.MIN_VALUE (-9223372036854775808),
517 // if the number being formatted is a Long.MIN_VALUE. In that
518 // case, it will be formatted as -Long.MIN_VALUE, a number
519 // which is outside the legal range of a long, but which can
520 // be represented by DigitList.
521 if (source <= 0) {
522 if (source == Long.MIN_VALUE) {
523 decimalAt = count = MAX_COUNT;
524 System.arraycopy(LONG_MIN_REP, 0, digits, 0, count);
525 } else {
526 decimalAt = count = 0; // Values <= 0 format as zero
527 }
528 } else {
529 // Rewritten to improve performance. I used to call
530 // Long.toString(), which was about 4x slower than this code.
531 int left = MAX_COUNT;
532 int right;
533 while (source > 0) {
534 digits[--left] = (char)('0' + (source % 10));
535 source /= 10;
536 }
537 decimalAt = MAX_COUNT - left;
538 // Don't copy trailing zeros. We are guaranteed that there is at
539 // least one non-zero digit, so we don't have to check lower bounds.
540 for (right = MAX_COUNT - 1; digits[right] == '0'; --right)
541 ;
542 count = right - left + 1;
543 System.arraycopy(digits, left, digits, 0, count);
544 }
545 if (maximumDigits > 0) round(maximumDigits);
546 }
547
548 /**
549 * Set the digit list to a representation of the given BigDecimal value.
550 * This method supports both fixed-point and exponential notation.
551 * @param isNegative Boolean value indicating whether the number is negative.
552 * @param source Value to be converted; must not be a value <= 0.
553 * @param maximumDigits The most fractional or total digits which should
554 * be converted.
555 * @param fixedPoint If true, then maximumDigits is the maximum
556 * fractional digits to be converted. If false, total digits.
557 */
558 final void set(boolean isNegative, BigDecimal source, int maximumDigits, boolean fixedPoint) {
559 String s = source.toString();
560 extendDigits(s.length());
561
562 set(isNegative, s, maximumDigits, fixedPoint);
563 }
564
565 /**
566 * Set the digit list to a representation of the given BigInteger value.
567 * @param isNegative Boolean value indicating whether the number is negative.
568 * @param source Value to be converted; must be >= 0.
569 * @param maximumDigits The most digits which should be converted.
570 * If maximumDigits is lower than the number of significant digits
571 * in source, the representation will be rounded. Ignored if <= 0.
572 */
573 final void set(boolean isNegative, BigInteger source, int maximumDigits) {
574 this.isNegative = isNegative;
575 String s = source.toString();
576 int len = s.length();
577 extendDigits(len);
578 s.getChars(0, len, digits, 0);
579
580 decimalAt = len;
581 int right;
582 for (right = len - 1; right >= 0 && digits[right] == '0'; --right)
583 ;
584 count = right + 1;
585
586 if (maximumDigits > 0) {
587 round(maximumDigits);
588 }
589 }
590
591 /**
592 * equality test between two digit lists.
593 */
594 public boolean equals(Object obj) {
595 if (this == obj) // quick check
596 return true;
597 if (!(obj instanceof DigitList)) // (1) same object?
598 return false;
599 DigitList other = (DigitList) obj;
600 if (count != other.count ||
601 decimalAt != other.decimalAt)
602 return false;
603 for (int i = 0; i < count; i++)
604 if (digits[i] != other.digits[i])
605 return false;
606 return true;
607 }
608
609 /**
610 * Generates the hash code for the digit list.
611 */
612 public int hashCode() {
613 int hashcode = decimalAt;
614
615 for (int i = 0; i < count; i++) {
616 hashcode = hashcode * 37 + digits[i];
617 }
618
619 return hashcode;
620 }
621
622 /**
623 * Creates a copy of this object.
624 * @return a clone of this instance.
625 */
626 public Object clone() {
627 try {
628 DigitList other = (DigitList) super.clone();
629 char[] newDigits = new char[digits.length];
630 System.arraycopy(digits, 0, newDigits, 0, digits.length);
631 other.digits = newDigits;
632 other.tempBuffer = null;
633 return other;
634 } catch (CloneNotSupportedException e) {
635 throw new InternalError();
636 }
637 }
638
639 /**
640 * Returns true if this DigitList represents Long.MIN_VALUE;
641 * false, otherwise. This is required so that getLong() works.
642 */
643 private boolean isLongMIN_VALUE() {
644 if (decimalAt != count || count != MAX_COUNT) {
645 return false;
646 }
647
648 for (int i = 0; i < count; ++i) {
649 if (digits[i] != LONG_MIN_REP[i]) return false;
650 }
651
652 return true;
653 }
654
655 private static final int parseInt(char[] str, int offset, int strLen) {
656 char c;
657 boolean positive = true;
658 if ((c = str[offset]) == '-') {
659 positive = false;
660 offset++;
661 } else if (c == '+') {
662 offset++;
663 }
664
665 int value = 0;
666 while (offset < strLen) {
667 c = str[offset++];
668 if (c >= '0' && c <= '9') {
669 value = value * 10 + (c - '0');
670 } else {
671 break;
672 }
673 }
674 return positive ? value : -value;
675 }
676
677 // The digit part of -9223372036854775808L
678 private static final char[] LONG_MIN_REP = "9223372036854775808".toCharArray();
679
680 public String toString() {
681 if (isZero()) {
682 return "0";
683 }
684 StringBuffer buf = getStringBuffer();
685 buf.append("0.");
686 buf.append(digits, 0, count);
687 buf.append("x10^");
688 buf.append(decimalAt);
689 return buf.toString();
690 }
691
692 private StringBuffer tempBuffer;
693
694 private StringBuffer getStringBuffer() {
695 if (tempBuffer == null) {
696 tempBuffer = new StringBuffer(MAX_COUNT);
697 } else {
698 tempBuffer.setLength(0);
699 }
700 return tempBuffer;
701 }
702
703 private void extendDigits(int len) {
704 if (len > digits.length) {
705 digits = new char[len];
706 }
707 }
708
709 private final char[] getDataChars(int length) {
710 if (data == null || data.length < length) {
711 data = new char[length];
712 }
713 return data;
714 }
715}
java/pkg/java/text/DontCareFieldPosition$1.class+0−0

Binary file not shown.

java/pkg/java/text/DontCareFieldPosition.class+0−0

Binary file not shown.

java/pkg/java/text/DontCareFieldPosition.java+53−0
@@ -0,0 +1,53 @@
1/*
2 * Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package java.text;
27
28/**
29 * DontCareFieldPosition defines no-op FieldDelegate. Its
30 * singleton is used for the format methods that don't take a
31 * FieldPosition.
32 */
33class DontCareFieldPosition extends FieldPosition {
34 // The singleton of DontCareFieldPosition.
35 static final FieldPosition INSTANCE = new DontCareFieldPosition();
36
37 private final Format.FieldDelegate noDelegate = new Format.FieldDelegate() {
38 public void formatted(Format.Field attr, Object value, int start,
39 int end, StringBuffer buffer) {
40 }
41 public void formatted(int fieldID, Format.Field attr, Object value,
42 int start, int end, StringBuffer buffer) {
43 }
44 };
45
46 private DontCareFieldPosition() {
47 super(0);
48 }
49
50 Format.FieldDelegate getFieldDelegate() {
51 return noDelegate;
52 }
53}
java/pkg/java/text/EntryPair.class+0−0

Binary file not shown.

java/pkg/java/text/EntryPair.java+60−0
@@ -0,0 +1,60 @@
1/*
2 * Copyright (c) 1996, 1998, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26/*
27 * (C) Copyright Taligent, Inc. 1996 - All Rights Reserved
28 * (C) Copyright IBM Corp. 1996 - All Rights Reserved
29 *
30 * The original version of this source code and documentation is copyrighted
31 * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
32 * materials are provided under terms of a License Agreement between Taligent
33 * and Sun. This technology is protected by multiple US and International
34 * patents. This notice and attribution to Taligent may not be removed.
35 * Taligent is a registered trademark of Taligent, Inc.
36 *
37 */
38
39package java.text;
40
41/**
42 * This is used for building contracting character tables. entryName
43 * is the contracting character name and value is its collation
44 * order.
45 */
46final class EntryPair
47{
48 public String entryName;
49 public int value;
50 public boolean fwd;
51
52 public EntryPair(String name, int value) {
53 this(name, value, true);
54 }
55 public EntryPair(String name, int value, boolean fwd) {
56 this.entryName = name;
57 this.value = value;
58 this.fwd = fwd;
59 }
60}
java/pkg/java/text/FieldPosition$1.class+0−0

Binary file not shown.

java/pkg/java/text/FieldPosition$Delegate.class+0−0

Binary file not shown.

java/pkg/java/text/FieldPosition.class+0−0

Binary file not shown.

java/pkg/java/text/FieldPosition.java+303−0
@@ -0,0 +1,303 @@
1/*
2 * Copyright (c) 1996, 2002, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26/*
27 * (C) Copyright Taligent, Inc. 1996 - All Rights Reserved
28 * (C) Copyright IBM Corp. 1996 - All Rights Reserved
29 *
30 * The original version of this source code and documentation is copyrighted
31 * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
32 * materials are provided under terms of a License Agreement between Taligent
33 * and Sun. This technology is protected by multiple US and International
34 * patents. This notice and attribution to Taligent may not be removed.
35 * Taligent is a registered trademark of Taligent, Inc.
36 *
37 */
38
39package java.text;
40
41/**
42 * <code>FieldPosition</code> is a simple class used by <code>Format</code>
43 * and its subclasses to identify fields in formatted output. Fields can
44 * be identified in two ways:
45 * <ul>
46 * <li>By an integer constant, whose names typically end with
47 * <code>_FIELD</code>. The constants are defined in the various
48 * subclasses of <code>Format</code>.
49 * <li>By a <code>Format.Field</code> constant, see <code>ERA_FIELD</code>
50 * and its friends in <code>DateFormat</code> for an example.
51 * </ul>
52 * <p>
53 * <code>FieldPosition</code> keeps track of the position of the
54 * field within the formatted output with two indices: the index
55 * of the first character of the field and the index of the last
56 * character of the field.
57 *
58 * <p>
59 * One version of the <code>format</code> method in the various
60 * <code>Format</code> classes requires a <code>FieldPosition</code>
61 * object as an argument. You use this <code>format</code> method
62 * to perform partial formatting or to get information about the
63 * formatted output (such as the position of a field).
64 *
65 * <p>
66 * If you are interested in the positions of all attributes in the
67 * formatted string use the <code>Format</code> method
68 * <code>formatToCharacterIterator</code>.
69 *
70 * @author Mark Davis
71 * @see java.text.Format
72 */
73public class FieldPosition {
74
75 /**
76 * Input: Desired field to determine start and end offsets for.
77 * The meaning depends on the subclass of Format.
78 */
79 int field = 0;
80
81 /**
82 * Output: End offset of field in text.
83 * If the field does not occur in the text, 0 is returned.
84 */
85 int endIndex = 0;
86
87 /**
88 * Output: Start offset of field in text.
89 * If the field does not occur in the text, 0 is returned.
90 */
91 int beginIndex = 0;
92
93 /**
94 * Desired field this FieldPosition is for.
95 */
96 private Format.Field attribute;
97
98 /**
99 * Creates a FieldPosition object for the given field. Fields are
100 * identified by constants, whose names typically end with _FIELD,
101 * in the various subclasses of Format.
102 *
103 * @see java.text.NumberFormat#INTEGER_FIELD
104 * @see java.text.NumberFormat#FRACTION_FIELD
105 * @see java.text.DateFormat#YEAR_FIELD
106 * @see java.text.DateFormat#MONTH_FIELD
107 */
108 public FieldPosition(int field) {
109 this.field = field;
110 }
111
112 /**
113 * Creates a FieldPosition object for the given field constant. Fields are
114 * identified by constants defined in the various <code>Format</code>
115 * subclasses. This is equivalent to calling
116 * <code>new FieldPosition(attribute, -1)</code>.
117 *
118 * @param attribute Format.Field constant identifying a field
119 * @since 1.4
120 */
121 public FieldPosition(Format.Field attribute) {
122 this(attribute, -1);
123 }
124
125 /**
126 * Creates a <code>FieldPosition</code> object for the given field.
127 * The field is identified by an attribute constant from one of the
128 * <code>Field</code> subclasses as well as an integer field ID
129 * defined by the <code>Format</code> subclasses. <code>Format</code>
130 * subclasses that are aware of <code>Field</code> should give precedence
131 * to <code>attribute</code> and ignore <code>fieldID</code> if
132 * <code>attribute</code> is not null. However, older <code>Format</code>
133 * subclasses may not be aware of <code>Field</code> and rely on
134 * <code>fieldID</code>. If the field has no corresponding integer
135 * constant, <code>fieldID</code> should be -1.
136 *
137 * @param attribute Format.Field constant identifying a field
138 * @param fieldID integer constantce identifying a field
139 * @since 1.4
140 */
141 public FieldPosition(Format.Field attribute, int fieldID) {
142 this.attribute = attribute;
143 this.field = fieldID;
144 }
145
146 /**
147 * Returns the field identifier as an attribute constant
148 * from one of the <code>Field</code> subclasses. May return null if
149 * the field is specified only by an integer field ID.
150 *
151 * @return Identifier for the field
152 * @since 1.4
153 */
154 public Format.Field getFieldAttribute() {
155 return attribute;
156 }
157
158 /**
159 * Retrieves the field identifier.
160 */
161 public int getField() {
162 return field;
163 }
164
165 /**
166 * Retrieves the index of the first character in the requested field.
167 */
168 public int getBeginIndex() {
169 return beginIndex;
170 }
171
172 /**
173 * Retrieves the index of the character following the last character in the
174 * requested field.
175 */
176 public int getEndIndex() {
177 return endIndex;
178 }
179
180 /**
181 * Sets the begin index. For use by subclasses of Format.
182 * @since 1.2
183 */
184 public void setBeginIndex(int bi) {
185 beginIndex = bi;
186 }
187
188 /**
189 * Sets the end index. For use by subclasses of Format.
190 * @since 1.2
191 */
192 public void setEndIndex(int ei) {
193 endIndex = ei;
194 }
195
196 /**
197 * Returns a <code>Format.FieldDelegate</code> instance that is associated
198 * with the FieldPosition. When the delegate is notified of the same
199 * field the FieldPosition is associated with, the begin/end will be
200 * adjusted.
201 */
202 Format.FieldDelegate getFieldDelegate() {
203 return new Delegate();
204 }
205
206 /**
207 * Overrides equals
208 */
209 public boolean equals(Object obj)
210 {
211 if (obj == null) return false;
212 if (!(obj instanceof FieldPosition))
213 return false;
214 FieldPosition other = (FieldPosition) obj;
215 if (attribute == null) {
216 if (other.attribute != null) {
217 return false;
218 }
219 }
220 else if (!attribute.equals(other.attribute)) {
221 return false;
222 }
223 return (beginIndex == other.beginIndex
224 && endIndex == other.endIndex
225 && field == other.field);
226 }
227
228 /**
229 * Returns a hash code for this FieldPosition.
230 * @return a hash code value for this object
231 */
232 public int hashCode() {
233 return (field << 24) | (beginIndex << 16) | endIndex;
234 }
235
236 /**
237 * Return a string representation of this FieldPosition.
238 * @return a string representation of this object
239 */
240 public String toString() {
241 return getClass().getName() +
242 "[field=" + field + ",attribute=" + attribute +
243 ",beginIndex=" + beginIndex +
244 ",endIndex=" + endIndex + ']';
245 }
246
247
248 /**
249 * Return true if the receiver wants a <code>Format.Field</code> value and
250 * <code>attribute</code> is equal to it.
251 */
252 private boolean matchesField(Format.Field attribute) {
253 if (this.attribute != null) {
254 return this.attribute.equals(attribute);
255 }
256 return false;
257 }
258
259 /**
260 * Return true if the receiver wants a <code>Format.Field</code> value and
261 * <code>attribute</code> is equal to it, or true if the receiver
262 * represents an inteter constant and <code>field</code> equals it.
263 */
264 private boolean matchesField(Format.Field attribute, int field) {
265 if (this.attribute != null) {
266 return this.attribute.equals(attribute);
267 }
268 return (field == this.field);
269 }
270
271
272 /**
273 * An implementation of FieldDelegate that will adjust the begin/end
274 * of the FieldPosition if the arguments match the field of
275 * the FieldPosition.
276 */
277 private class Delegate implements Format.FieldDelegate {
278 /**
279 * Indicates whether the field has been encountered before. If this
280 * is true, and <code>formatted</code> is invoked, the begin/end
281 * are not updated.
282 */
283 private boolean encounteredField;
284
285 public void formatted(Format.Field attr, Object value, int start,
286 int end, StringBuffer buffer) {
287 if (!encounteredField && matchesField(attr)) {
288 setBeginIndex(start);
289 setEndIndex(end);
290 encounteredField = (start != end);
291 }
292 }
293
294 public void formatted(int fieldID, Format.Field attr, Object value,
295 int start, int end, StringBuffer buffer) {
296 if (!encounteredField && matchesField(attr, fieldID)) {
297 setBeginIndex(start);
298 setEndIndex(end);
299 encounteredField = (start != end);
300 }
301 }
302 }
303}
java/pkg/java/text/Format$Field.class+0−0

Binary file not shown.

java/pkg/java/text/Format$FieldDelegate.class+0−0

Binary file not shown.

java/pkg/java/text/Format.class+0−0

Binary file not shown.

java/pkg/java/text/Format.java+406−0
@@ -0,0 +1,406 @@
1/*
2 * Copyright (c) 1996, 2005, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26/*
27 * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
28 * (C) Copyright IBM Corp. 1996 - 1998 - All Rights Reserved
29 *
30 * The original version of this source code and documentation is copyrighted
31 * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
32 * materials are provided under terms of a License Agreement between Taligent
33 * and Sun. This technology is protected by multiple US and International
34 * patents. This notice and attribution to Taligent may not be removed.
35 * Taligent is a registered trademark of Taligent, Inc.
36 *
37 */
38
39package java.text;
40
41import java.io.Serializable;
42
43/**
44 * <code>Format</code> is an abstract base class for formatting locale-sensitive
45 * information such as dates, messages, and numbers.
46 *
47 * <p>
48 * <code>Format</code> defines the programming interface for formatting
49 * locale-sensitive objects into <code>String</code>s (the
50 * <code>format</code> method) and for parsing <code>String</code>s back
51 * into objects (the <code>parseObject</code> method).
52 *
53 * <p>
54 * Generally, a format's <code>parseObject</code> method must be able to parse
55 * any string formatted by its <code>format</code> method. However, there may
56 * be exceptional cases where this is not possible. For example, a
57 * <code>format</code> method might create two adjacent integer numbers with
58 * no separator in between, and in this case the <code>parseObject</code> could
59 * not tell which digits belong to which number.
60 *
61 * <h4>Subclassing</h4>
62 *
63 * <p>
64 * The Java Platform provides three specialized subclasses of <code>Format</code>--
65 * <code>DateFormat</code>, <code>MessageFormat</code>, and
66 * <code>NumberFormat</code>--for formatting dates, messages, and numbers,
67 * respectively.
68 * <p>
69 * Concrete subclasses must implement three methods:
70 * <ol>
71 * <li> <code>format(Object obj, StringBuffer toAppendTo, FieldPosition pos)</code>
72 * <li> <code>formatToCharacterIterator(Object obj)</code>
73 * <li> <code>parseObject(String source, ParsePosition pos)</code>
74 * </ol>
75 * These general methods allow polymorphic parsing and formatting of objects
76 * and are used, for example, by <code>MessageFormat</code>.
77 * Subclasses often also provide additional <code>format</code> methods for
78 * specific input types as well as <code>parse</code> methods for specific
79 * result types. Any <code>parse</code> method that does not take a
80 * <code>ParsePosition</code> argument should throw <code>ParseException</code>
81 * when no text in the required format is at the beginning of the input text.
82 *
83 * <p>
84 * Most subclasses will also implement the following factory methods:
85 * <ol>
86 * <li>
87 * <code>getInstance</code> for getting a useful format object appropriate
88 * for the current locale
89 * <li>
90 * <code>getInstance(Locale)</code> for getting a useful format
91 * object appropriate for the specified locale
92 * </ol>
93 * In addition, some subclasses may also implement other
94 * <code>getXxxxInstance</code> methods for more specialized control. For
95 * example, the <code>NumberFormat</code> class provides
96 * <code>getPercentInstance</code> and <code>getCurrencyInstance</code>
97 * methods for getting specialized number formatters.
98 *
99 * <p>
100 * Subclasses of <code>Format</code> that allow programmers to create objects
101 * for locales (with <code>getInstance(Locale)</code> for example)
102 * must also implement the following class method:
103 * <blockquote>
104 * <pre>
105 * public static Locale[] getAvailableLocales()
106 * </pre>
107 * </blockquote>
108 *
109 * <p>
110 * And finally subclasses may define a set of constants to identify the various
111 * fields in the formatted output. These constants are used to create a FieldPosition
112 * object which identifies what information is contained in the field and its
113 * position in the formatted result. These constants should be named
114 * <code><em>item</em>_FIELD</code> where <code><em>item</em></code> identifies
115 * the field. For examples of these constants, see <code>ERA_FIELD</code> and its
116 * friends in {@link DateFormat}.
117 *
118 * <h4><a name="synchronization">Synchronization</a></h4>
119 *
120 * <p>
121 * Formats are generally not synchronized.
122 * It is recommended to create separate format instances for each thread.
123 * If multiple threads access a format concurrently, it must be synchronized
124 * externally.
125 *
126 * @see java.text.ParsePosition
127 * @see java.text.FieldPosition
128 * @see java.text.NumberFormat
129 * @see java.text.DateFormat
130 * @see java.text.MessageFormat
131 * @author Mark Davis
132 */
133public abstract class Format implements Serializable, Cloneable {
134
135 private static final long serialVersionUID = -299282585814624189L;
136
137 /**
138 * Sole constructor. (For invocation by subclass constructors, typically
139 * implicit.)
140 */
141 protected Format() {
142 }
143
144 /**
145 * Formats an object to produce a string. This is equivalent to
146 * <blockquote>
147 * {@link #format(Object, StringBuffer, FieldPosition) format}<code>(obj,
148 * new StringBuffer(), new FieldPosition(0)).toString();</code>
149 * </blockquote>
150 *
151 * @param obj The object to format
152 * @return Formatted string.
153 * @exception IllegalArgumentException if the Format cannot format the given
154 * object
155 */
156 public final String format (Object obj) {
157 return format(obj, new StringBuffer(), new FieldPosition(0)).toString();
158 }
159
160 /**
161 * Formats an object and appends the resulting text to a given string
162 * buffer.
163 * If the <code>pos</code> argument identifies a field used by the format,
164 * then its indices are set to the beginning and end of the first such
165 * field encountered.
166 *
167 * @param obj The object to format
168 * @param toAppendTo where the text is to be appended
169 * @param pos A <code>FieldPosition</code> identifying a field
170 * in the formatted text
171 * @return the string buffer passed in as <code>toAppendTo</code>,
172 * with formatted text appended
173 * @exception NullPointerException if <code>toAppendTo</code> or
174 * <code>pos</code> is null
175 * @exception IllegalArgumentException if the Format cannot format the given
176 * object
177 */
178 public abstract StringBuffer format(Object obj,
179 StringBuffer toAppendTo,
180 FieldPosition pos);
181
182 /**
183 * Formats an Object producing an <code>AttributedCharacterIterator</code>.
184 * You can use the returned <code>AttributedCharacterIterator</code>
185 * to build the resulting String, as well as to determine information
186 * about the resulting String.
187 * <p>
188 * Each attribute key of the AttributedCharacterIterator will be of type
189 * <code>Field</code>. It is up to each <code>Format</code> implementation
190 * to define what the legal values are for each attribute in the
191 * <code>AttributedCharacterIterator</code>, but typically the attribute
192 * key is also used as the attribute value.
193 * <p>The default implementation creates an
194 * <code>AttributedCharacterIterator</code> with no attributes. Subclasses
195 * that support fields should override this and create an
196 * <code>AttributedCharacterIterator</code> with meaningful attributes.
197 *
198 * @exception NullPointerException if obj is null.
199 * @exception IllegalArgumentException when the Format cannot format the
200 * given object.
201 * @param obj The object to format
202 * @return AttributedCharacterIterator describing the formatted value.
203 * @since 1.4
204 */
205 public AttributedCharacterIterator formatToCharacterIterator(Object obj) {
206 return createAttributedCharacterIterator(format(obj));
207 }
208
209 /**
210 * Parses text from a string to produce an object.
211 * <p>
212 * The method attempts to parse text starting at the index given by
213 * <code>pos</code>.
214 * If parsing succeeds, then the index of <code>pos</code> is updated
215 * to the index after the last character used (parsing does not necessarily
216 * use all characters up to the end of the string), and the parsed
217 * object is returned. The updated <code>pos</code> can be used to
218 * indicate the starting point for the next call to this method.
219 * If an error occurs, then the index of <code>pos</code> is not
220 * changed, the error index of <code>pos</code> is set to the index of
221 * the character where the error occurred, and null is returned.
222 *
223 * @param source A <code>String</code>, part of which should be parsed.
224 * @param pos A <code>ParsePosition</code> object with index and error
225 * index information as described above.
226 * @return An <code>Object</code> parsed from the string. In case of
227 * error, returns null.
228 * @exception NullPointerException if <code>pos</code> is null.
229 */
230 public abstract Object parseObject (String source, ParsePosition pos);
231
232 /**
233 * Parses text from the beginning of the given string to produce an object.
234 * The method may not use the entire text of the given string.
235 *
236 * @param source A <code>String</code> whose beginning should be parsed.
237 * @return An <code>Object</code> parsed from the string.
238 * @exception ParseException if the beginning of the specified string
239 * cannot be parsed.
240 */
241 public Object parseObject(String source) throws ParseException {
242 ParsePosition pos = new ParsePosition(0);
243 Object result = parseObject(source, pos);
244 if (pos.index == 0) {
245 throw new ParseException("Format.parseObject(String) failed",
246 pos.errorIndex);
247 }
248 return result;
249 }
250
251 /**
252 * Creates and returns a copy of this object.
253 *
254 * @return a clone of this instance.
255 */
256 public Object clone() {
257 try {
258 return super.clone();
259 } catch (CloneNotSupportedException e) {
260 // will never happen
261 return null;
262 }
263 }
264
265 //
266 // Convenience methods for creating AttributedCharacterIterators from
267 // different parameters.
268 //
269
270 /**
271 * Creates an <code>AttributedCharacterIterator</code> for the String
272 * <code>s</code>.
273 *
274 * @param s String to create AttributedCharacterIterator from
275 * @return AttributedCharacterIterator wrapping s
276 */
277 AttributedCharacterIterator createAttributedCharacterIterator(String s) {
278 AttributedString as = new AttributedString(s);
279
280 return as.getIterator();
281 }
282
283 /**
284 * Creates an <code>AttributedCharacterIterator</code> containg the
285 * concatenated contents of the passed in
286 * <code>AttributedCharacterIterator</code>s.
287 *
288 * @param iterators AttributedCharacterIterators used to create resulting
289 * AttributedCharacterIterators
290 * @return AttributedCharacterIterator wrapping passed in
291 * AttributedCharacterIterators
292 */
293 AttributedCharacterIterator createAttributedCharacterIterator(
294 AttributedCharacterIterator[] iterators) {
295 AttributedString as = new AttributedString(iterators);
296
297 return as.getIterator();
298 }
299
300 /**
301 * Returns an AttributedCharacterIterator with the String
302 * <code>string</code> and additional key/value pair <code>key</code>,
303 * <code>value</code>.
304 *
305 * @param string String to create AttributedCharacterIterator from
306 * @param key Key for AttributedCharacterIterator
307 * @param value Value associated with key in AttributedCharacterIterator
308 * @return AttributedCharacterIterator wrapping args
309 */
310 AttributedCharacterIterator createAttributedCharacterIterator(
311 String string, AttributedCharacterIterator.Attribute key,
312 Object value) {
313 AttributedString as = new AttributedString(string);
314
315 as.addAttribute(key, value);
316 return as.getIterator();
317 }
318
319 /**
320 * Creates an AttributedCharacterIterator with the contents of
321 * <code>iterator</code> and the additional attribute <code>key</code>
322 * <code>value</code>.
323 *
324 * @param iterator Initial AttributedCharacterIterator to add arg to
325 * @param key Key for AttributedCharacterIterator
326 * @param value Value associated with key in AttributedCharacterIterator
327 * @return AttributedCharacterIterator wrapping args
328 */
329 AttributedCharacterIterator createAttributedCharacterIterator(
330 AttributedCharacterIterator iterator,
331 AttributedCharacterIterator.Attribute key, Object value) {
332 AttributedString as = new AttributedString(iterator);
333
334 as.addAttribute(key, value);
335 return as.getIterator();
336 }
337
338
339 /**
340 * Defines constants that are used as attribute keys in the
341 * <code>AttributedCharacterIterator</code> returned
342 * from <code>Format.formatToCharacterIterator</code> and as
343 * field identifiers in <code>FieldPosition</code>.
344 *
345 * @since 1.4
346 */
347 public static class Field extends AttributedCharacterIterator.Attribute {
348
349 // Proclaim serial compatibility with 1.4 FCS
350 private static final long serialVersionUID = 276966692217360283L;
351
352 /**
353 * Creates a Field with the specified name.
354 *
355 * @param name Name of the attribute
356 */
357 protected Field(String name) {
358 super(name);
359 }
360 }
361
362
363 /**
364 * FieldDelegate is notified by the various <code>Format</code>
365 * implementations as they are formatting the Objects. This allows for
366 * storage of the individual sections of the formatted String for
367 * later use, such as in a <code>FieldPosition</code> or for an
368 * <code>AttributedCharacterIterator</code>.
369 * <p>
370 * Delegates should NOT assume that the <code>Format</code> will notify
371 * the delegate of fields in any particular order.
372 *
373 * @see FieldPosition.Delegate
374 * @see CharacterIteratorFieldDelegate
375 */
376 interface FieldDelegate {
377 /**
378 * Notified when a particular region of the String is formatted. This
379 * method will be invoked if there is no corresponding integer field id
380 * matching <code>attr</code>.
381 *
382 * @param attr Identifies the field matched
383 * @param value Value associated with the field
384 * @param start Beginning location of the field, will be >= 0
385 * @param end End of the field, will be >= start and <= buffer.length()
386 * @param buffer Contains current formatted value, receiver should
387 * NOT modify it.
388 */
389 public void formatted(Format.Field attr, Object value, int start,
390 int end, StringBuffer buffer);
391
392 /**
393 * Notified when a particular region of the String is formatted.
394 *
395 * @param fieldID Identifies the field by integer
396 * @param attr Identifies the field matched
397 * @param value Value associated with the field
398 * @param start Beginning location of the field, will be >= 0
399 * @param end End of the field, will be >= start and <= buffer.length()
400 * @param buffer Contains current formatted value, receiver should
401 * NOT modify it.
402 */
403 public void formatted(int fieldID, Format.Field attr, Object value,
404 int start, int end, StringBuffer buffer);
405 }
406}
java/pkg/java/text/MergeCollation.class+0−0

Binary file not shown.

java/pkg/java/text/MergeCollation.java+342−0
@@ -0,0 +1,342 @@
1/*
2 * Copyright (c) 1996, 1999, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26/*
27 * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
28 * (C) Copyright IBM Corp. 1996, 1997 - All Rights Reserved
29 *
30 * The original version of this source code and documentation is copyrighted
31 * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
32 * materials are provided under terms of a License Agreement between Taligent
33 * and Sun. This technology is protected by multiple US and International
34 * patents. This notice and attribution to Taligent may not be removed.
35 * Taligent is a registered trademark of Taligent, Inc.
36 *
37 */
38
39package java.text;
40
41import java.util.ArrayList;
42
43/**
44 * Utility class for normalizing and merging patterns for collation.
45 * Patterns are strings of the form <entry>*, where <entry> has the
46 * form:
47 * <pattern> := <entry>*
48 * <entry> := <separator><chars>{"/"<extension>}
49 * <separator> := "=", ",", ";", "<", "&"
50 * <chars>, and <extension> are both arbitrary strings.
51 * unquoted whitespaces are ignored.
52 * 'xxx' can be used to quote characters
53 * One difference from Collator is that & is used to reset to a current
54 * point. Or, in other words, it introduces a new sequence which is to
55 * be added to the old.
56 * That is: "a < b < c < d" is the same as "a < b & b < c & c < d" OR
57 * "a < b < d & b < c"
58 * XXX: make '' be a single quote.
59 * @see PatternEntry
60 * @author Mark Davis, Helena Shih
61 */
62
63final class MergeCollation {
64
65 /**
66 * Creates from a pattern
67 * @exception ParseException If the input pattern is incorrect.
68 */
69 public MergeCollation(String pattern) throws ParseException
70 {
71 for (int i = 0; i < statusArray.length; i++)
72 statusArray[i] = 0;
73 setPattern(pattern);
74 }
75
76 /**
77 * recovers current pattern
78 */
79 public String getPattern() {
80 return getPattern(true);
81 }
82
83 /**
84 * recovers current pattern.
85 * @param withWhiteSpace puts spacing around the entries, and \n
86 * before & and <
87 */
88 public String getPattern(boolean withWhiteSpace) {
89 StringBuffer result = new StringBuffer();
90 PatternEntry tmp = null;
91 ArrayList extList = null;
92 int i;
93 for (i = 0; i < patterns.size(); ++i) {
94 PatternEntry entry = (PatternEntry) patterns.get(i);
95 if (entry.extension.length() != 0) {
96 if (extList == null)
97 extList = new ArrayList();
98 extList.add(entry);
99 } else {
100 if (extList != null) {
101 PatternEntry last = findLastWithNoExtension(i-1);
102 for (int j = extList.size() - 1; j >= 0 ; j--) {
103 tmp = (PatternEntry)(extList.get(j));
104 tmp.addToBuffer(result, false, withWhiteSpace, last);
105 }
106 extList = null;
107 }
108 entry.addToBuffer(result, false, withWhiteSpace, null);
109 }
110 }
111 if (extList != null) {
112 PatternEntry last = findLastWithNoExtension(i-1);
113 for (int j = extList.size() - 1; j >= 0 ; j--) {
114 tmp = (PatternEntry)(extList.get(j));
115 tmp.addToBuffer(result, false, withWhiteSpace, last);
116 }
117 extList = null;
118 }
119 return result.toString();
120 }
121
122 private final PatternEntry findLastWithNoExtension(int i) {
123 for (--i;i >= 0; --i) {
124 PatternEntry entry = (PatternEntry) patterns.get(i);
125 if (entry.extension.length() == 0) {
126 return entry;
127 }
128 }
129 return null;
130 }
131
132 /**
133 * emits the pattern for collation builder.
134 * @return emits the string in the format understable to the collation
135 * builder.
136 */
137 public String emitPattern() {
138 return emitPattern(true);
139 }
140
141 /**
142 * emits the pattern for collation builder.
143 * @param withWhiteSpace puts spacing around the entries, and \n
144 * before & and <
145 * @return emits the string in the format understable to the collation
146 * builder.
147 */
148 public String emitPattern(boolean withWhiteSpace) {
149 StringBuffer result = new StringBuffer();
150 for (int i = 0; i < patterns.size(); ++i)
151 {
152 PatternEntry entry = (PatternEntry) patterns.get(i);
153 if (entry != null) {
154 entry.addToBuffer(result, true, withWhiteSpace, null);
155 }
156 }
157 return result.toString();
158 }
159
160 /**
161 * sets the pattern.
162 */
163 public void setPattern(String pattern) throws ParseException
164 {
165 patterns.clear();
166 addPattern(pattern);
167 }
168
169 /**
170 * adds a pattern to the current one.
171 * @param pattern the new pattern to be added
172 */
173 public void addPattern(String pattern) throws ParseException
174 {
175 if (pattern == null)
176 return;
177
178 PatternEntry.Parser parser = new PatternEntry.Parser(pattern);
179
180 PatternEntry entry = parser.next();
181 while (entry != null) {
182 fixEntry(entry);
183 entry = parser.next();
184 }
185 }
186
187 /**
188 * gets count of separate entries
189 * @return the size of pattern entries
190 */
191 public int getCount() {
192 return patterns.size();
193 }
194
195 /**
196 * gets count of separate entries
197 * @param index the offset of the desired pattern entry
198 * @return the requested pattern entry
199 */
200 public PatternEntry getItemAt(int index) {
201 return (PatternEntry) patterns.get(index);
202 }
203
204 //============================================================
205 // privates
206 //============================================================
207 ArrayList patterns = new ArrayList(); // a list of PatternEntries
208
209 private transient PatternEntry saveEntry = null;
210 private transient PatternEntry lastEntry = null;
211
212 // This is really used as a local variable inside fixEntry, but we cache
213 // it here to avoid newing it up every time the method is called.
214 private transient StringBuffer excess = new StringBuffer();
215
216 //
217 // When building a MergeCollation, we need to do lots of searches to see
218 // whether a given entry is already in the table. Since we're using an
219 // array, this would make the algorithm O(N*N). To speed things up, we
220 // use this bit array to remember whether the array contains any entries
221 // starting with each Unicode character. If not, we can avoid the search.
222 // Using BitSet would make this easier, but it's significantly slower.
223 //
224 private transient byte[] statusArray = new byte[8192];
225 private final byte BITARRAYMASK = (byte)0x1;
226 private final int BYTEPOWER = 3;
227 private final int BYTEMASK = (1 << BYTEPOWER) - 1;
228
229 /*
230 If the strength is RESET, then just change the lastEntry to
231 be the current. (If the current is not in patterns, signal an error).
232 If not, then remove the current entry, and add it after lastEntry
233 (which is usually at the end).
234 */
235 private final void fixEntry(PatternEntry newEntry) throws ParseException
236 {
237 // check to see whether the new entry has the same characters as the previous
238 // entry did (this can happen when a pattern declaring a difference between two
239 // strings that are canonically equivalent is normalized). If so, and the strength
240 // is anything other than IDENTICAL or RESET, throw an exception (you can't
241 // declare a string to be unequal to itself). --rtg 5/24/99
242 if (lastEntry != null && newEntry.chars.equals(lastEntry.chars)
243 && newEntry.extension.equals(lastEntry.extension)) {
244 if (newEntry.strength != Collator.IDENTICAL
245 && newEntry.strength != PatternEntry.RESET) {
246 throw new ParseException("The entries " + lastEntry + " and "
247 + newEntry + " are adjacent in the rules, but have conflicting "
248 + "strengths: A character can't be unequal to itself.", -1);
249 } else {
250 // otherwise, just skip this entry and behave as though you never saw it
251 return;
252 }
253 }
254
255 boolean changeLastEntry = true;
256 if (newEntry.strength != PatternEntry.RESET) {
257 int oldIndex = -1;
258
259 if ((newEntry.chars.length() == 1)) {
260
261 char c = newEntry.chars.charAt(0);
262 int statusIndex = c >> BYTEPOWER;
263 byte bitClump = statusArray[statusIndex];
264 byte setBit = (byte)(BITARRAYMASK << (c & BYTEMASK));
265
266 if (bitClump != 0 && (bitClump & setBit) != 0) {
267 oldIndex = patterns.lastIndexOf(newEntry);
268 } else {
269 // We're going to add an element that starts with this
270 // character, so go ahead and set its bit.
271 statusArray[statusIndex] = (byte)(bitClump | setBit);
272 }
273 } else {
274 oldIndex = patterns.lastIndexOf(newEntry);
275 }
276 if (oldIndex != -1) {
277 patterns.remove(oldIndex);
278 }
279
280 excess.setLength(0);
281 int lastIndex = findLastEntry(lastEntry, excess);
282
283 if (excess.length() != 0) {
284 newEntry.extension = excess + newEntry.extension;
285 if (lastIndex != patterns.size()) {
286 lastEntry = saveEntry;
287 changeLastEntry = false;
288 }
289 }
290 if (lastIndex == patterns.size()) {
291 patterns.add(newEntry);
292 saveEntry = newEntry;
293 } else {
294 patterns.add(lastIndex, newEntry);
295 }
296 }
297 if (changeLastEntry) {
298 lastEntry = newEntry;
299 }
300 }
301
302 private final int findLastEntry(PatternEntry entry,
303 StringBuffer excessChars) throws ParseException
304 {
305 if (entry == null)
306 return 0;
307
308 if (entry.strength != PatternEntry.RESET) {
309 // Search backwards for string that contains this one;
310 // most likely entry is last one
311
312 int oldIndex = -1;
313 if ((entry.chars.length() == 1)) {
314 int index = entry.chars.charAt(0) >> BYTEPOWER;
315 if ((statusArray[index] &
316 (BITARRAYMASK << (entry.chars.charAt(0) & BYTEMASK))) != 0) {
317 oldIndex = patterns.lastIndexOf(entry);
318 }
319 } else {
320 oldIndex = patterns.lastIndexOf(entry);
321 }
322 if ((oldIndex == -1))
323 throw new ParseException("couldn't find last entry: "
324 + entry, oldIndex);
325 return oldIndex + 1;
326 } else {
327 int i;
328 for (i = patterns.size() - 1; i >= 0; --i) {
329 PatternEntry e = (PatternEntry) patterns.get(i);
330 if (e.chars.regionMatches(0,entry.chars,0,
331 e.chars.length())) {
332 excessChars.append(entry.chars.substring(e.chars.length(),
333 entry.chars.length()));
334 break;
335 }
336 }
337 if (i == -1)
338 throw new ParseException("couldn't find: " + entry, i);
339 return i + 1;
340 }
341 }
342}
java/pkg/java/text/MessageFormat$Field.class+0−0

Binary file not shown.

java/pkg/java/text/MessageFormat.class+0−0

Binary file not shown.

java/pkg/java/text/MessageFormat.java+1594−0
@@ -0,0 +1,1594 @@
1/*
2 * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26/*
27 * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
28 * (C) Copyright IBM Corp. 1996 - 1998 - All Rights Reserved
29 *
30 * The original version of this source code and documentation is copyrighted
31 * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
32 * materials are provided under terms of a License Agreement between Taligent
33 * and Sun. This technology is protected by multiple US and International
34 * patents. This notice and attribution to Taligent may not be removed.
35 * Taligent is a registered trademark of Taligent, Inc.
36 *
37 */
38
39package java.text;
40
41import java.io.InvalidObjectException;
42import java.io.IOException;
43import java.io.ObjectInputStream;
44import java.text.DecimalFormat;
45import java.util.ArrayList;
46import java.util.Arrays;
47import java.util.Date;
48import java.util.List;
49import java.util.Locale;
50
51
52/**
53 * <code>MessageFormat</code> provides a means to produce concatenated
54 * messages in a language-neutral way. Use this to construct messages
55 * displayed for end users.
56 *
57 * <p>
58 * <code>MessageFormat</code> takes a set of objects, formats them, then
59 * inserts the formatted strings into the pattern at the appropriate places.
60 *
61 * <p>
62 * <strong>Note:</strong>
63 * <code>MessageFormat</code> differs from the other <code>Format</code>
64 * classes in that you create a <code>MessageFormat</code> object with one
65 * of its constructors (not with a <code>getInstance</code> style factory
66 * method). The factory methods aren't necessary because <code>MessageFormat</code>
67 * itself doesn't implement locale specific behavior. Any locale specific
68 * behavior is defined by the pattern that you provide as well as the
69 * subformats used for inserted arguments.
70 *
71 * <h4><a name="patterns">Patterns and Their Interpretation</a></h4>
72 *
73 * <code>MessageFormat</code> uses patterns of the following form:
74 * <blockquote><pre>
75 * <i>MessageFormatPattern:</i>
76 * <i>String</i>
77 * <i>MessageFormatPattern</i> <i>FormatElement</i> <i>String</i>
78 *
79 * <i>FormatElement:</i>
80 * { <i>ArgumentIndex</i> }
81 * { <i>ArgumentIndex</i> , <i>FormatType</i> }
82 * { <i>ArgumentIndex</i> , <i>FormatType</i> , <i>FormatStyle</i> }
83 *
84 * <i>FormatType: one of </i>
85 * number date time choice
86 *
87 * <i>FormatStyle:</i>
88 * short
89 * medium
90 * long
91 * full
92 * integer
93 * currency
94 * percent
95 * <i>SubformatPattern</i>
96 * </pre></blockquote>
97 *
98 * <p>Within a <i>String</i>, a pair of single quotes can be used to
99 * quote any arbitrary characters except single quotes. For example,
100 * pattern string <code>"'{0}'"</code> represents string
101 * <code>"{0}"</code>, not a <i>FormatElement</i>. A single quote itself
102 * must be represented by doubled single quotes {@code ''} throughout a
103 * <i>String</i>. For example, pattern string <code>"'{''}'"</code> is
104 * interpreted as a sequence of <code>'{</code> (start of quoting and a
105 * left curly brace), <code>''</code> (a single quote), and
106 * <code>}'</code> (a right curly brace and end of quoting),
107 * <em>not</em> <code>'{'</code> and <code>'}'</code> (quoted left and
108 * right curly braces): representing string <code>"{'}"</code>,
109 * <em>not</em> <code>"{}"</code>.
110 *
111 * <p>A <i>SubformatPattern</i> is interpreted by its corresponding
112 * subformat, and subformat-dependent pattern rules apply. For example,
113 * pattern string <code>"{1,number,<u>$'#',##</u>}"</code>
114 * (<i>SubformatPattern</i> with underline) will produce a number format
115 * with the pound-sign quoted, with a result such as: {@code
116 * "$#31,45"}. Refer to each {@code Format} subclass documentation for
117 * details.
118 *
119 * <p>Any unmatched quote is treated as closed at the end of the given
120 * pattern. For example, pattern string {@code "'{0}"} is treated as
121 * pattern {@code "'{0}'"}.
122 *
123 * <p>Any curly braces within an unquoted pattern must be balanced. For
124 * example, <code>"ab {0} de"</code> and <code>"ab '}' de"</code> are
125 * valid patterns, but <code>"ab {0'}' de"</code>, <code>"ab } de"</code>
126 * and <code>"''{''"</code> are not.
127 *
128 * <p>
129 * <dl><dt><b>Warning:</b><dd>The rules for using quotes within message
130 * format patterns unfortunately have shown to be somewhat confusing.
131 * In particular, it isn't always obvious to localizers whether single
132 * quotes need to be doubled or not. Make sure to inform localizers about
133 * the rules, and tell them (for example, by using comments in resource
134 * bundle source files) which strings will be processed by {@code MessageFormat}.
135 * Note that localizers may need to use single quotes in translated
136 * strings where the original version doesn't have them.
137 * </dl>
138 * <p>
139 * The <i>ArgumentIndex</i> value is a non-negative integer written
140 * using the digits {@code '0'} through {@code '9'}, and represents an index into the
141 * {@code arguments} array passed to the {@code format} methods
142 * or the result array returned by the {@code parse} methods.
143 * <p>
144 * The <i>FormatType</i> and <i>FormatStyle</i> values are used to create
145 * a {@code Format} instance for the format element. The following
146 * table shows how the values map to {@code Format} instances. Combinations not
147 * shown in the table are illegal. A <i>SubformatPattern</i> must
148 * be a valid pattern string for the {@code Format} subclass used.
149 * <p>
150 * <table border=1 summary="Shows how FormatType and FormatStyle values map to Format instances">
151 * <tr>
152 * <th id="ft" class="TableHeadingColor">FormatType
153 * <th id="fs" class="TableHeadingColor">FormatStyle
154 * <th id="sc" class="TableHeadingColor">Subformat Created
155 * <tr>
156 * <td headers="ft"><i>(none)</i>
157 * <td headers="fs"><i>(none)</i>
158 * <td headers="sc"><code>null</code>
159 * <tr>
160 * <td headers="ft" rowspan=5><code>number</code>
161 * <td headers="fs"><i>(none)</i>
162 * <td headers="sc">{@link NumberFormat#getInstance(Locale) NumberFormat.getInstance}{@code (getLocale())}
163 * <tr>
164 * <td headers="fs"><code>integer</code>
165 * <td headers="sc">{@link NumberFormat#getIntegerInstance(Locale) NumberFormat.getIntegerInstance}{@code (getLocale())}
166 * <tr>
167 * <td headers="fs"><code>currency</code>
168 * <td headers="sc">{@link NumberFormat#getCurrencyInstance(Locale) NumberFormat.getCurrencyInstance}{@code (getLocale())}
169 * <tr>
170 * <td headers="fs"><code>percent</code>
171 * <td headers="sc">{@link NumberFormat#getPercentInstance(Locale) NumberFormat.getPercentInstance}{@code (getLocale())}
172 * <tr>
173 * <td headers="fs"><i>SubformatPattern</i>
174 * <td headers="sc">{@code new} {@link DecimalFormat#DecimalFormat(String,DecimalFormatSymbols) DecimalFormat}{@code (subformatPattern,} {@link DecimalFormatSymbols#getInstance(Locale) DecimalFormatSymbols.getInstance}{@code (getLocale()))}
175 * <tr>
176 * <td headers="ft" rowspan=6><code>date</code>
177 * <td headers="fs"><i>(none)</i>
178 * <td headers="sc">{@link DateFormat#getDateInstance(int,Locale) DateFormat.getDateInstance}{@code (}{@link DateFormat#DEFAULT}{@code , getLocale())}
179 * <tr>
180 * <td headers="fs"><code>short</code>
181 * <td headers="sc">{@link DateFormat#getDateInstance(int,Locale) DateFormat.getDateInstance}{@code (}{@link DateFormat#SHORT}{@code , getLocale())}
182 * <tr>
183 * <td headers="fs"><code>medium</code>
184 * <td headers="sc">{@link DateFormat#getDateInstance(int,Locale) DateFormat.getDateInstance}{@code (}{@link DateFormat#DEFAULT}{@code , getLocale())}
185 * <tr>
186 * <td headers="fs"><code>long</code>
187 * <td headers="sc">{@link DateFormat#getDateInstance(int,Locale) DateFormat.getDateInstance}{@code (}{@link DateFormat#LONG}{@code , getLocale())}
188 * <tr>
189 * <td headers="fs"><code>full</code>
190 * <td headers="sc">{@link DateFormat#getDateInstance(int,Locale) DateFormat.getDateInstance}{@code (}{@link DateFormat#FULL}{@code , getLocale())}
191 * <tr>
192 * <td headers="fs"><i>SubformatPattern</i>
193 * <td headers="sc">{@code new} {@link SimpleDateFormat#SimpleDateFormat(String,Locale) SimpleDateFormat}{@code (subformatPattern, getLocale())}
194 * <tr>
195 * <td headers="ft" rowspan=6><code>time</code>
196 * <td headers="fs"><i>(none)</i>
197 * <td headers="sc">{@link DateFormat#getTimeInstance(int,Locale) DateFormat.getTimeInstance}{@code (}{@link DateFormat#DEFAULT}{@code , getLocale())}
198 * <tr>
199 * <td headers="fs"><code>short</code>
200 * <td headers="sc">{@link DateFormat#getTimeInstance(int,Locale) DateFormat.getTimeInstance}{@code (}{@link DateFormat#SHORT}{@code , getLocale())}
201 * <tr>
202 * <td headers="fs"><code>medium</code>
203 * <td headers="sc">{@link DateFormat#getTimeInstance(int,Locale) DateFormat.getTimeInstance}{@code (}{@link DateFormat#DEFAULT}{@code , getLocale())}
204 * <tr>
205 * <td headers="fs"><code>long</code>
206 * <td headers="sc">{@link DateFormat#getTimeInstance(int,Locale) DateFormat.getTimeInstance}{@code (}{@link DateFormat#LONG}{@code , getLocale())}
207 * <tr>
208 * <td headers="fs"><code>full</code>
209 * <td headers="sc">{@link DateFormat#getTimeInstance(int,Locale) DateFormat.getTimeInstance}{@code (}{@link DateFormat#FULL}{@code , getLocale())}
210 * <tr>
211 * <td headers="fs"><i>SubformatPattern</i>
212 * <td headers="sc">{@code new} {@link SimpleDateFormat#SimpleDateFormat(String,Locale) SimpleDateFormat}{@code (subformatPattern, getLocale())}
213 * <tr>
214 * <td headers="ft"><code>choice</code>
215 * <td headers="fs"><i>SubformatPattern</i>
216 * <td headers="sc">{@code new} {@link ChoiceFormat#ChoiceFormat(String) ChoiceFormat}{@code (subformatPattern)}
217 * </table>
218 * <p>
219 *
220 * <h4>Usage Information</h4>
221 *
222 * <p>
223 * Here are some examples of usage.
224 * In real internationalized programs, the message format pattern and other
225 * static strings will, of course, be obtained from resource bundles.
226 * Other parameters will be dynamically determined at runtime.
227 * <p>
228 * The first example uses the static method <code>MessageFormat.format</code>,
229 * which internally creates a <code>MessageFormat</code> for one-time use:
230 * <blockquote><pre>
231 * int planet = 7;
232 * String event = "a disturbance in the Force";
233 *
234 * String result = MessageFormat.format(
235 * "At {1,time} on {1,date}, there was {2} on planet {0,number,integer}.",
236 * planet, new Date(), event);
237 * </pre></blockquote>
238 * The output is:
239 * <blockquote><pre>
240 * At 12:30 PM on Jul 3, 2053, there was a disturbance in the Force on planet 7.
241 * </pre></blockquote>
242 *
243 * <p>
244 * The following example creates a <code>MessageFormat</code> instance that
245 * can be used repeatedly:
246 * <blockquote><pre>
247 * int fileCount = 1273;
248 * String diskName = "MyDisk";
249 * Object[] testArgs = {new Long(fileCount), diskName};
250 *
251 * MessageFormat form = new MessageFormat(
252 * "The disk \"{1}\" contains {0} file(s).");
253 *
254 * System.out.println(form.format(testArgs));
255 * </pre></blockquote>
256 * The output with different values for <code>fileCount</code>:
257 * <blockquote><pre>
258 * The disk "MyDisk" contains 0 file(s).
259 * The disk "MyDisk" contains 1 file(s).
260 * The disk "MyDisk" contains 1,273 file(s).
261 * </pre></blockquote>
262 *
263 * <p>
264 * For more sophisticated patterns, you can use a <code>ChoiceFormat</code>
265 * to produce correct forms for singular and plural:
266 * <blockquote><pre>
267 * MessageFormat form = new MessageFormat("The disk \"{1}\" contains {0}.");
268 * double[] filelimits = {0,1,2};
269 * String[] filepart = {"no files","one file","{0,number} files"};
270 * ChoiceFormat fileform = new ChoiceFormat(filelimits, filepart);
271 * form.setFormatByArgumentIndex(0, fileform);
272 *
273 * int fileCount = 1273;
274 * String diskName = "MyDisk";
275 * Object[] testArgs = {new Long(fileCount), diskName};
276 *
277 * System.out.println(form.format(testArgs));
278 * </pre></blockquote>
279 * The output with different values for <code>fileCount</code>:
280 * <blockquote><pre>
281 * The disk "MyDisk" contains no files.
282 * The disk "MyDisk" contains one file.
283 * The disk "MyDisk" contains 1,273 files.
284 * </pre></blockquote>
285 *
286 * <p>
287 * You can create the <code>ChoiceFormat</code> programmatically, as in the
288 * above example, or by using a pattern. See {@link ChoiceFormat}
289 * for more information.
290 * <blockquote><pre>
291 * form.applyPattern(
292 * "There {0,choice,0#are no files|1#is one file|1&lt;are {0,number,integer} files}.");
293 * </pre></blockquote>
294 *
295 * <p>
296 * <strong>Note:</strong> As we see above, the string produced
297 * by a <code>ChoiceFormat</code> in <code>MessageFormat</code> is treated as special;
298 * occurrences of '{' are used to indicate subformats, and cause recursion.
299 * If you create both a <code>MessageFormat</code> and <code>ChoiceFormat</code>
300 * programmatically (instead of using the string patterns), then be careful not to
301 * produce a format that recurses on itself, which will cause an infinite loop.
302 * <p>
303 * When a single argument is parsed more than once in the string, the last match
304 * will be the final result of the parsing. For example,
305 * <blockquote><pre>
306 * MessageFormat mf = new MessageFormat("{0,number,#.##}, {0,number,#.#}");
307 * Object[] objs = {new Double(3.1415)};
308 * String result = mf.format( objs );
309 * // result now equals "3.14, 3.1"
310 * objs = null;
311 * objs = mf.parse(result, new ParsePosition(0));
312 * // objs now equals {new Double(3.1)}
313 * </pre></blockquote>
314 *
315 * <p>
316 * Likewise, parsing with a {@code MessageFormat} object using patterns containing
317 * multiple occurrences of the same argument would return the last match. For
318 * example,
319 * <blockquote><pre>
320 * MessageFormat mf = new MessageFormat("{0}, {0}, {0}");
321 * String forParsing = "x, y, z";
322 * Object[] objs = mf.parse(forParsing, new ParsePosition(0));
323 * // result now equals {new String("z")}
324 * </pre></blockquote>
325 *
326 * <h4><a name="synchronization">Synchronization</a></h4>
327 *
328 * <p>
329 * Message formats are not synchronized.
330 * It is recommended to create separate format instances for each thread.
331 * If multiple threads access a format concurrently, it must be synchronized
332 * externally.
333 *
334 * @see java.util.Locale
335 * @see Format
336 * @see NumberFormat
337 * @see DecimalFormat
338 * @see DecimalFormatSymbols
339 * @see ChoiceFormat
340 * @see DateFormat
341 * @see SimpleDateFormat
342 *
343 * @author Mark Davis
344 */
345
346public class MessageFormat extends Format {
347
348 private static final long serialVersionUID = 6479157306784022952L;
349
350 /**
351 * Constructs a MessageFormat for the default locale and the
352 * specified pattern.
353 * The constructor first sets the locale, then parses the pattern and
354 * creates a list of subformats for the format elements contained in it.
355 * Patterns and their interpretation are specified in the
356 * <a href="#patterns">class description</a>.
357 *
358 * @param pattern the pattern for this message format
359 * @exception IllegalArgumentException if the pattern is invalid
360 */
361 public MessageFormat(String pattern) {
362 this.locale = Locale.getDefault(Locale.Category.FORMAT);
363 applyPattern(pattern);
364 }
365
366 /**
367 * Constructs a MessageFormat for the specified locale and
368 * pattern.
369 * The constructor first sets the locale, then parses the pattern and
370 * creates a list of subformats for the format elements contained in it.
371 * Patterns and their interpretation are specified in the
372 * <a href="#patterns">class description</a>.
373 *
374 * @param pattern the pattern for this message format
375 * @param locale the locale for this message format
376 * @exception IllegalArgumentException if the pattern is invalid
377 * @since 1.4
378 */
379 public MessageFormat(String pattern, Locale locale) {
380 this.locale = locale;
381 applyPattern(pattern);
382 }
383
384 /**
385 * Sets the locale to be used when creating or comparing subformats.
386 * This affects subsequent calls
387 * <ul>
388 * <li>to the {@link #applyPattern applyPattern}
389 * and {@link #toPattern toPattern} methods if format elements specify
390 * a format type and therefore have the subformats created in the
391 * <code>applyPattern</code> method, as well as
392 * <li>to the <code>format</code> and
393 * {@link #formatToCharacterIterator formatToCharacterIterator} methods
394 * if format elements do not specify a format type and therefore have
395 * the subformats created in the formatting methods.
396 * </ul>
397 * Subformats that have already been created are not affected.
398 *
399 * @param locale the locale to be used when creating or comparing subformats
400 */
401 public void setLocale(Locale locale) {
402 this.locale = locale;
403 }
404
405 /**
406 * Gets the locale that's used when creating or comparing subformats.
407 *
408 * @return the locale used when creating or comparing subformats
409 */
410 public Locale getLocale() {
411 return locale;
412 }
413
414
415 /**
416 * Sets the pattern used by this message format.
417 * The method parses the pattern and creates a list of subformats
418 * for the format elements contained in it.
419 * Patterns and their interpretation are specified in the
420 * <a href="#patterns">class description</a>.
421 *
422 * @param pattern the pattern for this message format
423 * @exception IllegalArgumentException if the pattern is invalid
424 */
425 public void applyPattern(String pattern) {
426 StringBuilder[] segments = new StringBuilder[4];
427 // Allocate only segments[SEG_RAW] here. The rest are
428 // allocated on demand.
429 segments[SEG_RAW] = new StringBuilder();
430
431 int part = SEG_RAW;
432 int formatNumber = 0;
433 boolean inQuote = false;
434 int braceStack = 0;
435 maxOffset = -1;
436 for (int i = 0; i < pattern.length(); ++i) {
437 char ch = pattern.charAt(i);
438 if (part == SEG_RAW) {
439 if (ch == '\'') {
440 if (i + 1 < pattern.length()
441 && pattern.charAt(i+1) == '\'') {
442 segments[part].append(ch); // handle doubles
443 ++i;
444 } else {
445 inQuote = !inQuote;
446 }
447 } else if (ch == '{' && !inQuote) {
448 part = SEG_INDEX;
449 if (segments[SEG_INDEX] == null) {
450 segments[SEG_INDEX] = new StringBuilder();
451 }
452 } else {
453 segments[part].append(ch);
454 }
455 } else {
456 if (inQuote) { // just copy quotes in parts
457 segments[part].append(ch);
458 if (ch == '\'') {
459 inQuote = false;
460 }
461 } else {
462 switch (ch) {
463 case ',':
464 if (part < SEG_MODIFIER) {
465 if (segments[++part] == null) {
466 segments[part] = new StringBuilder();
467 }
468 } else {
469 segments[part].append(ch);
470 }
471 break;
472 case '{':
473 ++braceStack;
474 segments[part].append(ch);
475 break;
476 case '}':
477 if (braceStack == 0) {
478 part = SEG_RAW;
479 makeFormat(i, formatNumber, segments);
480 formatNumber++;
481 // throw away other segments
482 segments[SEG_INDEX] = null;
483 segments[SEG_TYPE] = null;
484 segments[SEG_MODIFIER] = null;
485 } else {
486 --braceStack;
487 segments[part].append(ch);
488 }
489 break;
490 case ' ':
491 // Skip any leading space chars for SEG_TYPE.
492 if (part != SEG_TYPE || segments[SEG_TYPE].length() > 0) {
493 segments[part].append(ch);
494 }
495 break;
496 case '\'':
497 inQuote = true;
498 // fall through, so we keep quotes in other parts
499 default:
500 segments[part].append(ch);
501 break;
502 }
503 }
504 }
505 }
506 if (braceStack == 0 && part != 0) {
507 maxOffset = -1;
508 throw new IllegalArgumentException("Unmatched braces in the pattern.");
509 }
510 this.pattern = segments[0].toString();
511 }
512
513
514 /**
515 * Returns a pattern representing the current state of the message format.
516 * The string is constructed from internal information and therefore
517 * does not necessarily equal the previously applied pattern.
518 *
519 * @return a pattern representing the current state of the message format
520 */
521 public String toPattern() {
522 // later, make this more extensible
523 int lastOffset = 0;
524 StringBuilder result = new StringBuilder();
525 for (int i = 0; i <= maxOffset; ++i) {
526 copyAndFixQuotes(pattern, lastOffset, offsets[i], result);
527 lastOffset = offsets[i];
528 result.append('{').append(argumentNumbers[i]);
529 Format fmt = formats[i];
530 if (fmt == null) {
531 // do nothing, string format
532 } else if (fmt instanceof NumberFormat) {
533 if (fmt.equals(NumberFormat.getInstance(locale))) {
534 result.append(",number");
535 } else if (fmt.equals(NumberFormat.getCurrencyInstance(locale))) {
536 result.append(",number,currency");
537 } else if (fmt.equals(NumberFormat.getPercentInstance(locale))) {
538 result.append(",number,percent");
539 } else if (fmt.equals(NumberFormat.getIntegerInstance(locale))) {
540 result.append(",number,integer");
541 } else {
542 if (fmt instanceof DecimalFormat) {
543 result.append(",number,").append(((DecimalFormat)fmt).toPattern());
544 } else if (fmt instanceof ChoiceFormat) {
545 result.append(",choice,").append(((ChoiceFormat)fmt).toPattern());
546 } else {
547 // UNKNOWN
548 }
549 }
550 } else if (fmt instanceof DateFormat) {
551 int index;
552 for (index = MODIFIER_DEFAULT; index < DATE_TIME_MODIFIERS.length; index++) {
553 DateFormat df = DateFormat.getDateInstance(DATE_TIME_MODIFIERS[index],
554 locale);
555 if (fmt.equals(df)) {
556 result.append(",date");
557 break;
558 }
559 df = DateFormat.getTimeInstance(DATE_TIME_MODIFIERS[index],
560 locale);
561 if (fmt.equals(df)) {
562 result.append(",time");
563 break;
564 }
565 }
566 if (index >= DATE_TIME_MODIFIERS.length) {
567 if (fmt instanceof SimpleDateFormat) {
568 result.append(",date,").append(((SimpleDateFormat)fmt).toPattern());
569 } else {
570 // UNKNOWN
571 }
572 } else if (index != MODIFIER_DEFAULT) {
573 result.append(',').append(DATE_TIME_MODIFIER_KEYWORDS[index]);
574 }
575 } else {
576 //result.append(", unknown");
577 }
578 result.append('}');
579 }
580 copyAndFixQuotes(pattern, lastOffset, pattern.length(), result);
581 return result.toString();
582 }
583
584 /**
585 * Sets the formats to use for the values passed into
586 * <code>format</code> methods or returned from <code>parse</code>
587 * methods. The indices of elements in <code>newFormats</code>
588 * correspond to the argument indices used in the previously set
589 * pattern string.
590 * The order of formats in <code>newFormats</code> thus corresponds to
591 * the order of elements in the <code>arguments</code> array passed
592 * to the <code>format</code> methods or the result array returned
593 * by the <code>parse</code> methods.
594 * <p>
595 * If an argument index is used for more than one format element
596 * in the pattern string, then the corresponding new format is used
597 * for all such format elements. If an argument index is not used
598 * for any format element in the pattern string, then the
599 * corresponding new format is ignored. If fewer formats are provided
600 * than needed, then only the formats for argument indices less
601 * than <code>newFormats.length</code> are replaced.
602 *
603 * @param newFormats the new formats to use
604 * @exception NullPointerException if <code>newFormats</code> is null
605 * @since 1.4
606 */
607 public void setFormatsByArgumentIndex(Format[] newFormats) {
608 for (int i = 0; i <= maxOffset; i++) {
609 int j = argumentNumbers[i];
610 if (j < newFormats.length) {
611 formats[i] = newFormats[j];
612 }
613 }
614 }
615
616 /**
617 * Sets the formats to use for the format elements in the
618 * previously set pattern string.
619 * The order of formats in <code>newFormats</code> corresponds to
620 * the order of format elements in the pattern string.
621 * <p>
622 * If more formats are provided than needed by the pattern string,
623 * the remaining ones are ignored. If fewer formats are provided
624 * than needed, then only the first <code>newFormats.length</code>
625 * formats are replaced.
626 * <p>
627 * Since the order of format elements in a pattern string often
628 * changes during localization, it is generally better to use the
629 * {@link #setFormatsByArgumentIndex setFormatsByArgumentIndex}
630 * method, which assumes an order of formats corresponding to the
631 * order of elements in the <code>arguments</code> array passed to
632 * the <code>format</code> methods or the result array returned by
633 * the <code>parse</code> methods.
634 *
635 * @param newFormats the new formats to use
636 * @exception NullPointerException if <code>newFormats</code> is null
637 */
638 public void setFormats(Format[] newFormats) {
639 int runsToCopy = newFormats.length;
640 if (runsToCopy > maxOffset + 1) {
641 runsToCopy = maxOffset + 1;
642 }
643 for (int i = 0; i < runsToCopy; i++) {
644 formats[i] = newFormats[i];
645 }
646 }
647
648 /**
649 * Sets the format to use for the format elements within the
650 * previously set pattern string that use the given argument
651 * index.
652 * The argument index is part of the format element definition and
653 * represents an index into the <code>arguments</code> array passed
654 * to the <code>format</code> methods or the result array returned
655 * by the <code>parse</code> methods.
656 * <p>
657 * If the argument index is used for more than one format element
658 * in the pattern string, then the new format is used for all such
659 * format elements. If the argument index is not used for any format
660 * element in the pattern string, then the new format is ignored.
661 *
662 * @param argumentIndex the argument index for which to use the new format
663 * @param newFormat the new format to use
664 * @since 1.4
665 */
666 public void setFormatByArgumentIndex(int argumentIndex, Format newFormat) {
667 for (int j = 0; j <= maxOffset; j++) {
668 if (argumentNumbers[j] == argumentIndex) {
669 formats[j] = newFormat;
670 }
671 }
672 }
673
674 /**
675 * Sets the format to use for the format element with the given
676 * format element index within the previously set pattern string.
677 * The format element index is the zero-based number of the format
678 * element counting from the start of the pattern string.
679 * <p>
680 * Since the order of format elements in a pattern string often
681 * changes during localization, it is generally better to use the
682 * {@link #setFormatByArgumentIndex setFormatByArgumentIndex}
683 * method, which accesses format elements based on the argument
684 * index they specify.
685 *
686 * @param formatElementIndex the index of a format element within the pattern
687 * @param newFormat the format to use for the specified format element
688 * @exception ArrayIndexOutOfBoundsException if {@code formatElementIndex} is equal to or
689 * larger than the number of format elements in the pattern string
690 */
691 public void setFormat(int formatElementIndex, Format newFormat) {
692 formats[formatElementIndex] = newFormat;
693 }
694
695 /**
696 * Gets the formats used for the values passed into
697 * <code>format</code> methods or returned from <code>parse</code>
698 * methods. The indices of elements in the returned array
699 * correspond to the argument indices used in the previously set
700 * pattern string.
701 * The order of formats in the returned array thus corresponds to
702 * the order of elements in the <code>arguments</code> array passed
703 * to the <code>format</code> methods or the result array returned
704 * by the <code>parse</code> methods.
705 * <p>
706 * If an argument index is used for more than one format element
707 * in the pattern string, then the format used for the last such
708 * format element is returned in the array. If an argument index
709 * is not used for any format element in the pattern string, then
710 * null is returned in the array.
711 *
712 * @return the formats used for the arguments within the pattern
713 * @since 1.4
714 */
715 public Format[] getFormatsByArgumentIndex() {
716 int maximumArgumentNumber = -1;
717 for (int i = 0; i <= maxOffset; i++) {
718 if (argumentNumbers[i] > maximumArgumentNumber) {
719 maximumArgumentNumber = argumentNumbers[i];
720 }
721 }
722 Format[] resultArray = new Format[maximumArgumentNumber + 1];
723 for (int i = 0; i <= maxOffset; i++) {
724 resultArray[argumentNumbers[i]] = formats[i];
725 }
726 return resultArray;
727 }
728
729 /**
730 * Gets the formats used for the format elements in the
731 * previously set pattern string.
732 * The order of formats in the returned array corresponds to
733 * the order of format elements in the pattern string.
734 * <p>
735 * Since the order of format elements in a pattern string often
736 * changes during localization, it's generally better to use the
737 * {@link #getFormatsByArgumentIndex getFormatsByArgumentIndex}
738 * method, which assumes an order of formats corresponding to the
739 * order of elements in the <code>arguments</code> array passed to
740 * the <code>format</code> methods or the result array returned by
741 * the <code>parse</code> methods.
742 *
743 * @return the formats used for the format elements in the pattern
744 */
745 public Format[] getFormats() {
746 Format[] resultArray = new Format[maxOffset + 1];
747 System.arraycopy(formats, 0, resultArray, 0, maxOffset + 1);
748 return resultArray;
749 }
750
751 /**
752 * Formats an array of objects and appends the <code>MessageFormat</code>'s
753 * pattern, with format elements replaced by the formatted objects, to the
754 * provided <code>StringBuffer</code>.
755 * <p>
756 * The text substituted for the individual format elements is derived from
757 * the current subformat of the format element and the
758 * <code>arguments</code> element at the format element's argument index
759 * as indicated by the first matching line of the following table. An
760 * argument is <i>unavailable</i> if <code>arguments</code> is
761 * <code>null</code> or has fewer than argumentIndex+1 elements.
762 * <p>
763 * <table border=1 summary="Examples of subformat,argument,and formatted text">
764 * <tr>
765 * <th>Subformat
766 * <th>Argument
767 * <th>Formatted Text
768 * <tr>
769 * <td><i>any</i>
770 * <td><i>unavailable</i>
771 * <td><code>"{" + argumentIndex + "}"</code>
772 * <tr>
773 * <td><i>any</i>
774 * <td><code>null</code>
775 * <td><code>"null"</code>
776 * <tr>
777 * <td><code>instanceof ChoiceFormat</code>
778 * <td><i>any</i>
779 * <td><code>subformat.format(argument).indexOf('{') >= 0 ?<br>
780 * (new MessageFormat(subformat.format(argument), getLocale())).format(argument) :
781 * subformat.format(argument)</code>
782 * <tr>
783 * <td><code>!= null</code>
784 * <td><i>any</i>
785 * <td><code>subformat.format(argument)</code>
786 * <tr>
787 * <td><code>null</code>
788 * <td><code>instanceof Number</code>
789 * <td><code>NumberFormat.getInstance(getLocale()).format(argument)</code>
790 * <tr>
791 * <td><code>null</code>
792 * <td><code>instanceof Date</code>
793 * <td><code>DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, getLocale()).format(argument)</code>
794 * <tr>
795 * <td><code>null</code>
796 * <td><code>instanceof String</code>
797 * <td><code>argument</code>
798 * <tr>
799 * <td><code>null</code>
800 * <td><i>any</i>
801 * <td><code>argument.toString()</code>
802 * </table>
803 * <p>
804 * If <code>pos</code> is non-null, and refers to
805 * <code>Field.ARGUMENT</code>, the location of the first formatted
806 * string will be returned.
807 *
808 * @param arguments an array of objects to be formatted and substituted.
809 * @param result where text is appended.
810 * @param pos On input: an alignment field, if desired.
811 * On output: the offsets of the alignment field.
812 * @exception IllegalArgumentException if an argument in the
813 * <code>arguments</code> array is not of the type
814 * expected by the format element(s) that use it.
815 */
816 public final StringBuffer format(Object[] arguments, StringBuffer result,
817 FieldPosition pos)
818 {
819 return subformat(arguments, result, pos, null);
820 }
821
822 /**
823 * Creates a MessageFormat with the given pattern and uses it
824 * to format the given arguments. This is equivalent to
825 * <blockquote>
826 * <code>(new {@link #MessageFormat(String) MessageFormat}(pattern)).{@link #format(java.lang.Object[], java.lang.StringBuffer, java.text.FieldPosition) format}(arguments, new StringBuffer(), null).toString()</code>
827 * </blockquote>
828 *
829 * @exception IllegalArgumentException if the pattern is invalid,
830 * or if an argument in the <code>arguments</code> array
831 * is not of the type expected by the format element(s)
832 * that use it.
833 */
834 public static String format(String pattern, Object ... arguments) {
835 MessageFormat temp = new MessageFormat(pattern);
836 return temp.format(arguments);
837 }
838
839 // Overrides
840 /**
841 * Formats an array of objects and appends the <code>MessageFormat</code>'s
842 * pattern, with format elements replaced by the formatted objects, to the
843 * provided <code>StringBuffer</code>.
844 * This is equivalent to
845 * <blockquote>
846 * <code>{@link #format(java.lang.Object[], java.lang.StringBuffer, java.text.FieldPosition) format}((Object[]) arguments, result, pos)</code>
847 * </blockquote>
848 *
849 * @param arguments an array of objects to be formatted and substituted.
850 * @param result where text is appended.
851 * @param pos On input: an alignment field, if desired.
852 * On output: the offsets of the alignment field.
853 * @exception IllegalArgumentException if an argument in the
854 * <code>arguments</code> array is not of the type
855 * expected by the format element(s) that use it.
856 */
857 public final StringBuffer format(Object arguments, StringBuffer result,
858 FieldPosition pos)
859 {
860 return subformat((Object[]) arguments, result, pos, null);
861 }
862
863 /**
864 * Formats an array of objects and inserts them into the
865 * <code>MessageFormat</code>'s pattern, producing an
866 * <code>AttributedCharacterIterator</code>.
867 * You can use the returned <code>AttributedCharacterIterator</code>
868 * to build the resulting String, as well as to determine information
869 * about the resulting String.
870 * <p>
871 * The text of the returned <code>AttributedCharacterIterator</code> is
872 * the same that would be returned by
873 * <blockquote>
874 * <code>{@link #format(java.lang.Object[], java.lang.StringBuffer, java.text.FieldPosition) format}(arguments, new StringBuffer(), null).toString()</code>
875 * </blockquote>
876 * <p>
877 * In addition, the <code>AttributedCharacterIterator</code> contains at
878 * least attributes indicating where text was generated from an
879 * argument in the <code>arguments</code> array. The keys of these attributes are of
880 * type <code>MessageFormat.Field</code>, their values are
881 * <code>Integer</code> objects indicating the index in the <code>arguments</code>
882 * array of the argument from which the text was generated.
883 * <p>
884 * The attributes/value from the underlying <code>Format</code>
885 * instances that <code>MessageFormat</code> uses will also be
886 * placed in the resulting <code>AttributedCharacterIterator</code>.
887 * This allows you to not only find where an argument is placed in the
888 * resulting String, but also which fields it contains in turn.
889 *
890 * @param arguments an array of objects to be formatted and substituted.
891 * @return AttributedCharacterIterator describing the formatted value.
892 * @exception NullPointerException if <code>arguments</code> is null.
893 * @exception IllegalArgumentException if an argument in the
894 * <code>arguments</code> array is not of the type
895 * expected by the format element(s) that use it.
896 * @since 1.4
897 */
898 public AttributedCharacterIterator formatToCharacterIterator(Object arguments) {
899 StringBuffer result = new StringBuffer();
900 ArrayList iterators = new ArrayList();
901
902 if (arguments == null) {
903 throw new NullPointerException(
904 "formatToCharacterIterator must be passed non-null object");
905 }
906 subformat((Object[]) arguments, result, null, iterators);
907 if (iterators.size() == 0) {
908 return createAttributedCharacterIterator("");
909 }
910 return createAttributedCharacterIterator(
911 (AttributedCharacterIterator[])iterators.toArray(
912 new AttributedCharacterIterator[iterators.size()]));
913 }
914
915 /**
916 * Parses the string.
917 *
918 * <p>Caveats: The parse may fail in a number of circumstances.
919 * For example:
920 * <ul>
921 * <li>If one of the arguments does not occur in the pattern.
922 * <li>If the format of an argument loses information, such as
923 * with a choice format where a large number formats to "many".
924 * <li>Does not yet handle recursion (where
925 * the substituted strings contain {n} references.)
926 * <li>Will not always find a match (or the correct match)
927 * if some part of the parse is ambiguous.
928 * For example, if the pattern "{1},{2}" is used with the
929 * string arguments {"a,b", "c"}, it will format as "a,b,c".
930 * When the result is parsed, it will return {"a", "b,c"}.
931 * <li>If a single argument is parsed more than once in the string,
932 * then the later parse wins.
933 * </ul>
934 * When the parse fails, use ParsePosition.getErrorIndex() to find out
935 * where in the string the parsing failed. The returned error
936 * index is the starting offset of the sub-patterns that the string
937 * is comparing with. For example, if the parsing string "AAA {0} BBB"
938 * is comparing against the pattern "AAD {0} BBB", the error index is
939 * 0. When an error occurs, the call to this method will return null.
940 * If the source is null, return an empty array.
941 */
942 public Object[] parse(String source, ParsePosition pos) {
943 if (source == null) {
944 Object[] empty = {};
945 return empty;
946 }
947
948 int maximumArgumentNumber = -1;
949 for (int i = 0; i <= maxOffset; i++) {
950 if (argumentNumbers[i] > maximumArgumentNumber) {
951 maximumArgumentNumber = argumentNumbers[i];
952 }
953 }
954 Object[] resultArray = new Object[maximumArgumentNumber + 1];
955
956 int patternOffset = 0;
957 int sourceOffset = pos.index;
958 ParsePosition tempStatus = new ParsePosition(0);
959 for (int i = 0; i <= maxOffset; ++i) {
960 // match up to format
961 int len = offsets[i] - patternOffset;
962 if (len == 0 || pattern.regionMatches(patternOffset,
963 source, sourceOffset, len)) {
964 sourceOffset += len;
965 patternOffset += len;
966 } else {
967 pos.errorIndex = sourceOffset;
968 return null; // leave index as is to signal error
969 }
970
971 // now use format
972 if (formats[i] == null) { // string format
973 // if at end, use longest possible match
974 // otherwise uses first match to intervening string
975 // does NOT recursively try all possibilities
976 int tempLength = (i != maxOffset) ? offsets[i+1] : pattern.length();
977
978 int next;
979 if (patternOffset >= tempLength) {
980 next = source.length();
981 }else{
982 next = source.indexOf(pattern.substring(patternOffset, tempLength),
983 sourceOffset);
984 }
985
986 if (next < 0) {
987 pos.errorIndex = sourceOffset;
988 return null; // leave index as is to signal error
989 } else {
990 String strValue= source.substring(sourceOffset,next);
991 if (!strValue.equals("{"+argumentNumbers[i]+"}"))
992 resultArray[argumentNumbers[i]]
993 = source.substring(sourceOffset,next);
994 sourceOffset = next;
995 }
996 } else {
997 tempStatus.index = sourceOffset;
998 resultArray[argumentNumbers[i]]
999 = formats[i].parseObject(source,tempStatus);
1000 if (tempStatus.index == sourceOffset) {
1001 pos.errorIndex = sourceOffset;
1002 return null; // leave index as is to signal error
1003 }
1004 sourceOffset = tempStatus.index; // update
1005 }
1006 }
1007 int len = pattern.length() - patternOffset;
1008 if (len == 0 || pattern.regionMatches(patternOffset,
1009 source, sourceOffset, len)) {
1010 pos.index = sourceOffset + len;
1011 } else {
1012 pos.errorIndex = sourceOffset;
1013 return null; // leave index as is to signal error
1014 }
1015 return resultArray;
1016 }
1017
1018 /**
1019 * Parses text from the beginning of the given string to produce an object
1020 * array.
1021 * The method may not use the entire text of the given string.
1022 * <p>
1023 * See the {@link #parse(String, ParsePosition)} method for more information
1024 * on message parsing.
1025 *
1026 * @param source A <code>String</code> whose beginning should be parsed.
1027 * @return An <code>Object</code> array parsed from the string.
1028 * @exception ParseException if the beginning of the specified string
1029 * cannot be parsed.
1030 */
1031 public Object[] parse(String source) throws ParseException {
1032 ParsePosition pos = new ParsePosition(0);
1033 Object[] result = parse(source, pos);
1034 if (pos.index == 0) // unchanged, returned object is null
1035 throw new ParseException("MessageFormat parse error!", pos.errorIndex);
1036
1037 return result;
1038 }
1039
1040 /**
1041 * Parses text from a string to produce an object array.
1042 * <p>
1043 * The method attempts to parse text starting at the index given by
1044 * <code>pos</code>.
1045 * If parsing succeeds, then the index of <code>pos</code> is updated
1046 * to the index after the last character used (parsing does not necessarily
1047 * use all characters up to the end of the string), and the parsed
1048 * object array is returned. The updated <code>pos</code> can be used to
1049 * indicate the starting point for the next call to this method.
1050 * If an error occurs, then the index of <code>pos</code> is not
1051 * changed, the error index of <code>pos</code> is set to the index of
1052 * the character where the error occurred, and null is returned.
1053 * <p>
1054 * See the {@link #parse(String, ParsePosition)} method for more information
1055 * on message parsing.
1056 *
1057 * @param source A <code>String</code>, part of which should be parsed.
1058 * @param pos A <code>ParsePosition</code> object with index and error
1059 * index information as described above.
1060 * @return An <code>Object</code> array parsed from the string. In case of
1061 * error, returns null.
1062 * @exception NullPointerException if <code>pos</code> is null.
1063 */
1064 public Object parseObject(String source, ParsePosition pos) {
1065 return parse(source, pos);
1066 }
1067
1068 /**
1069 * Creates and returns a copy of this object.
1070 *
1071 * @return a clone of this instance.
1072 */
1073 public Object clone() {
1074 MessageFormat other = (MessageFormat) super.clone();
1075
1076 // clone arrays. Can't do with utility because of bug in Cloneable
1077 other.formats = (Format[]) formats.clone(); // shallow clone
1078 for (int i = 0; i < formats.length; ++i) {
1079 if (formats[i] != null)
1080 other.formats[i] = (Format)formats[i].clone();
1081 }
1082 // for primitives or immutables, shallow clone is enough
1083 other.offsets = (int[]) offsets.clone();
1084 other.argumentNumbers = (int[]) argumentNumbers.clone();
1085
1086 return other;
1087 }
1088
1089 /**
1090 * Equality comparison between two message format objects
1091 */
1092 public boolean equals(Object obj) {
1093 if (this == obj) // quick check
1094 return true;
1095 if (obj == null || getClass() != obj.getClass())
1096 return false;
1097 MessageFormat other = (MessageFormat) obj;
1098 return (maxOffset == other.maxOffset
1099 && pattern.equals(other.pattern)
1100 && ((locale != null && locale.equals(other.locale))
1101 || (locale == null && other.locale == null))
1102 && Arrays.equals(offsets,other.offsets)
1103 && Arrays.equals(argumentNumbers,other.argumentNumbers)
1104 && Arrays.equals(formats,other.formats));
1105 }
1106
1107 /**
1108 * Generates a hash code for the message format object.
1109 */
1110 public int hashCode() {
1111 return pattern.hashCode(); // enough for reasonable distribution
1112 }
1113
1114
1115 /**
1116 * Defines constants that are used as attribute keys in the
1117 * <code>AttributedCharacterIterator</code> returned
1118 * from <code>MessageFormat.formatToCharacterIterator</code>.
1119 *
1120 * @since 1.4
1121 */
1122 public static class Field extends Format.Field {
1123
1124 // Proclaim serial compatibility with 1.4 FCS
1125 private static final long serialVersionUID = 7899943957617360810L;
1126
1127 /**
1128 * Creates a Field with the specified name.
1129 *
1130 * @param name Name of the attribute
1131 */
1132 protected Field(String name) {
1133 super(name);
1134 }
1135
1136 /**
1137 * Resolves instances being deserialized to the predefined constants.
1138 *
1139 * @throws InvalidObjectException if the constant could not be
1140 * resolved.
1141 * @return resolved MessageFormat.Field constant
1142 */
1143 protected Object readResolve() throws InvalidObjectException {
1144 if (this.getClass() != MessageFormat.Field.class) {
1145 throw new InvalidObjectException("subclass didn't correctly implement readResolve");
1146 }
1147
1148 return ARGUMENT;
1149 }
1150
1151 //
1152 // The constants
1153 //
1154
1155 /**
1156 * Constant identifying a portion of a message that was generated
1157 * from an argument passed into <code>formatToCharacterIterator</code>.
1158 * The value associated with the key will be an <code>Integer</code>
1159 * indicating the index in the <code>arguments</code> array of the
1160 * argument from which the text was generated.
1161 */
1162 public final static Field ARGUMENT =
1163 new Field("message argument field");
1164 }
1165
1166 // ===========================privates============================
1167
1168 /**
1169 * The locale to use for formatting numbers and dates.
1170 * @serial
1171 */
1172 private Locale locale;
1173
1174 /**
1175 * The string that the formatted values are to be plugged into. In other words, this
1176 * is the pattern supplied on construction with all of the {} expressions taken out.
1177 * @serial
1178 */
1179 private String pattern = "";
1180
1181 /** The initially expected number of subformats in the format */
1182 private static final int INITIAL_FORMATS = 10;
1183
1184 /**
1185 * An array of formatters, which are used to format the arguments.
1186 * @serial
1187 */
1188 private Format[] formats = new Format[INITIAL_FORMATS];
1189
1190 /**
1191 * The positions where the results of formatting each argument are to be inserted
1192 * into the pattern.
1193 * @serial
1194 */
1195 private int[] offsets = new int[INITIAL_FORMATS];
1196
1197 /**
1198 * The argument numbers corresponding to each formatter. (The formatters are stored
1199 * in the order they occur in the pattern, not in the order in which the arguments
1200 * are specified.)
1201 * @serial
1202 */
1203 private int[] argumentNumbers = new int[INITIAL_FORMATS];
1204
1205 /**
1206 * One less than the number of entries in <code>offsets</code>. Can also be thought of
1207 * as the index of the highest-numbered element in <code>offsets</code> that is being used.
1208 * All of these arrays should have the same number of elements being used as <code>offsets</code>
1209 * does, and so this variable suffices to tell us how many entries are in all of them.
1210 * @serial
1211 */
1212 private int maxOffset = -1;
1213
1214 /**
1215 * Internal routine used by format. If <code>characterIterators</code> is
1216 * non-null, AttributedCharacterIterator will be created from the
1217 * subformats as necessary. If <code>characterIterators</code> is null
1218 * and <code>fp</code> is non-null and identifies
1219 * <code>Field.MESSAGE_ARGUMENT</code>, the location of
1220 * the first replaced argument will be set in it.
1221 *
1222 * @exception IllegalArgumentException if an argument in the
1223 * <code>arguments</code> array is not of the type
1224 * expected by the format element(s) that use it.
1225 */
1226 private StringBuffer subformat(Object[] arguments, StringBuffer result,
1227 FieldPosition fp, List characterIterators) {
1228 // note: this implementation assumes a fast substring & index.
1229 // if this is not true, would be better to append chars one by one.
1230 int lastOffset = 0;
1231 int last = result.length();
1232 for (int i = 0; i <= maxOffset; ++i) {
1233 result.append(pattern.substring(lastOffset, offsets[i]));
1234 lastOffset = offsets[i];
1235 int argumentNumber = argumentNumbers[i];
1236 if (arguments == null || argumentNumber >= arguments.length) {
1237 result.append('{').append(argumentNumber).append('}');
1238 continue;
1239 }
1240 // int argRecursion = ((recursionProtection >> (argumentNumber*2)) & 0x3);
1241 if (false) { // if (argRecursion == 3){
1242 // prevent loop!!!
1243 result.append('\uFFFD');
1244 } else {
1245 Object obj = arguments[argumentNumber];
1246 String arg = null;
1247 Format subFormatter = null;
1248 if (obj == null) {
1249 arg = "null";
1250 } else if (formats[i] != null) {
1251 subFormatter = formats[i];
1252 if (subFormatter instanceof ChoiceFormat) {
1253 arg = formats[i].format(obj);
1254 if (arg.indexOf('{') >= 0) {
1255 subFormatter = new MessageFormat(arg, locale);
1256 obj = arguments;
1257 arg = null;
1258 }
1259 }
1260 } else if (obj instanceof Number) {
1261 // format number if can
1262 subFormatter = NumberFormat.getInstance(locale);
1263 } else if (obj instanceof Date) {
1264 // format a Date if can
1265 subFormatter = DateFormat.getDateTimeInstance(
1266 DateFormat.SHORT, DateFormat.SHORT, locale);//fix
1267 } else if (obj instanceof String) {
1268 arg = (String) obj;
1269
1270 } else {
1271 arg = obj.toString();
1272 if (arg == null) arg = "null";
1273 }
1274
1275 // At this point we are in two states, either subFormatter
1276 // is non-null indicating we should format obj using it,
1277 // or arg is non-null and we should use it as the value.
1278
1279 if (characterIterators != null) {
1280 // If characterIterators is non-null, it indicates we need
1281 // to get the CharacterIterator from the child formatter.
1282 if (last != result.length()) {
1283 characterIterators.add(
1284 createAttributedCharacterIterator(result.substring
1285 (last)));
1286 last = result.length();
1287 }
1288 if (subFormatter != null) {
1289 AttributedCharacterIterator subIterator =
1290 subFormatter.formatToCharacterIterator(obj);
1291
1292 append(result, subIterator);
1293 if (last != result.length()) {
1294 characterIterators.add(
1295 createAttributedCharacterIterator(
1296 subIterator, Field.ARGUMENT,
1297 Integer.valueOf(argumentNumber)));
1298 last = result.length();
1299 }
1300 arg = null;
1301 }
1302 if (arg != null && arg.length() > 0) {
1303 result.append(arg);
1304 characterIterators.add(
1305 createAttributedCharacterIterator(
1306 arg, Field.ARGUMENT,
1307 Integer.valueOf(argumentNumber)));
1308 last = result.length();
1309 }
1310 }
1311 else {
1312 if (subFormatter != null) {
1313 arg = subFormatter.format(obj);
1314 }
1315 last = result.length();
1316 result.append(arg);
1317 if (i == 0 && fp != null && Field.ARGUMENT.equals(
1318 fp.getFieldAttribute())) {
1319 fp.setBeginIndex(last);
1320 fp.setEndIndex(result.length());
1321 }
1322 last = result.length();
1323 }
1324 }
1325 }
1326 result.append(pattern.substring(lastOffset, pattern.length()));
1327 if (characterIterators != null && last != result.length()) {
1328 characterIterators.add(createAttributedCharacterIterator(
1329 result.substring(last)));
1330 }
1331 return result;
1332 }
1333
1334 /**
1335 * Convenience method to append all the characters in
1336 * <code>iterator</code> to the StringBuffer <code>result</code>.
1337 */
1338 private void append(StringBuffer result, CharacterIterator iterator) {
1339 if (iterator.first() != CharacterIterator.DONE) {
1340 char aChar;
1341
1342 result.append(iterator.first());
1343 while ((aChar = iterator.next()) != CharacterIterator.DONE) {
1344 result.append(aChar);
1345 }
1346 }
1347 }
1348
1349 // Indices for segments
1350 private static final int SEG_RAW = 0;
1351 private static final int SEG_INDEX = 1;
1352 private static final int SEG_TYPE = 2;
1353 private static final int SEG_MODIFIER = 3; // modifier or subformat
1354
1355 // Indices for type keywords
1356 private static final int TYPE_NULL = 0;
1357 private static final int TYPE_NUMBER = 1;
1358 private static final int TYPE_DATE = 2;
1359 private static final int TYPE_TIME = 3;
1360 private static final int TYPE_CHOICE = 4;
1361
1362 private static final String[] TYPE_KEYWORDS = {
1363 "",
1364 "number",
1365 "date",
1366 "time",
1367 "choice"
1368 };
1369
1370 // Indices for number modifiers
1371 private static final int MODIFIER_DEFAULT = 0; // common in number and date-time
1372 private static final int MODIFIER_CURRENCY = 1;
1373 private static final int MODIFIER_PERCENT = 2;
1374 private static final int MODIFIER_INTEGER = 3;
1375
1376 private static final String[] NUMBER_MODIFIER_KEYWORDS = {
1377 "",
1378 "currency",
1379 "percent",
1380 "integer"
1381 };
1382
1383 // Indices for date-time modifiers
1384 private static final int MODIFIER_SHORT = 1;
1385 private static final int MODIFIER_MEDIUM = 2;
1386 private static final int MODIFIER_LONG = 3;
1387 private static final int MODIFIER_FULL = 4;
1388
1389 private static final String[] DATE_TIME_MODIFIER_KEYWORDS = {
1390 "",
1391 "short",
1392 "medium",
1393 "long",
1394 "full"
1395 };
1396
1397 // Date-time style values corresponding to the date-time modifiers.
1398 private static final int[] DATE_TIME_MODIFIERS = {
1399 DateFormat.DEFAULT,
1400 DateFormat.SHORT,
1401 DateFormat.MEDIUM,
1402 DateFormat.LONG,
1403 DateFormat.FULL,
1404 };
1405
1406 private void makeFormat(int position, int offsetNumber,
1407 StringBuilder[] textSegments)
1408 {
1409 String[] segments = new String[textSegments.length];
1410 for (int i = 0; i < textSegments.length; i++) {
1411 StringBuilder oneseg = textSegments[i];
1412 segments[i] = (oneseg != null) ? oneseg.toString() : "";
1413 }
1414
1415 // get the argument number
1416 int argumentNumber;
1417 try {
1418 argumentNumber = Integer.parseInt(segments[SEG_INDEX]); // always unlocalized!
1419 } catch (NumberFormatException e) {
1420 throw new IllegalArgumentException("can't parse argument number: "
1421 + segments[SEG_INDEX], e);
1422 }
1423 if (argumentNumber < 0) {
1424 throw new IllegalArgumentException("negative argument number: "
1425 + argumentNumber);
1426 }
1427
1428 // resize format information arrays if necessary
1429 if (offsetNumber >= formats.length) {
1430 int newLength = formats.length * 2;
1431 Format[] newFormats = new Format[newLength];
1432 int[] newOffsets = new int[newLength];
1433 int[] newArgumentNumbers = new int[newLength];
1434 System.arraycopy(formats, 0, newFormats, 0, maxOffset + 1);
1435 System.arraycopy(offsets, 0, newOffsets, 0, maxOffset + 1);
1436 System.arraycopy(argumentNumbers, 0, newArgumentNumbers, 0, maxOffset + 1);
1437 formats = newFormats;
1438 offsets = newOffsets;
1439 argumentNumbers = newArgumentNumbers;
1440 }
1441 int oldMaxOffset = maxOffset;
1442 maxOffset = offsetNumber;
1443 offsets[offsetNumber] = segments[SEG_RAW].length();
1444 argumentNumbers[offsetNumber] = argumentNumber;
1445
1446 // now get the format
1447 Format newFormat = null;
1448 if (segments[SEG_TYPE].length() != 0) {
1449 int type = findKeyword(segments[SEG_TYPE], TYPE_KEYWORDS);
1450 switch (type) {
1451 case TYPE_NULL:
1452 // Type "" is allowed. e.g., "{0,}", "{0,,}", and "{0,,#}"
1453 // are treated as "{0}".
1454 break;
1455
1456 case TYPE_NUMBER:
1457 switch (findKeyword(segments[SEG_MODIFIER], NUMBER_MODIFIER_KEYWORDS)) {
1458 case MODIFIER_DEFAULT:
1459 newFormat = NumberFormat.getInstance(locale);
1460 break;
1461 case MODIFIER_CURRENCY:
1462 newFormat = NumberFormat.getCurrencyInstance(locale);
1463 break;
1464 case MODIFIER_PERCENT:
1465 newFormat = NumberFormat.getPercentInstance(locale);
1466 break;
1467 case MODIFIER_INTEGER:
1468 newFormat = NumberFormat.getIntegerInstance(locale);
1469 break;
1470 default: // DecimalFormat pattern
1471 try {
1472 newFormat = new DecimalFormat(segments[SEG_MODIFIER],
1473 DecimalFormatSymbols.getInstance(locale));
1474 } catch (IllegalArgumentException e) {
1475 maxOffset = oldMaxOffset;
1476 throw e;
1477 }
1478 break;
1479 }
1480 break;
1481
1482 case TYPE_DATE:
1483 case TYPE_TIME:
1484 int mod = findKeyword(segments[SEG_MODIFIER], DATE_TIME_MODIFIER_KEYWORDS);
1485 if (mod >= 0 && mod < DATE_TIME_MODIFIER_KEYWORDS.length) {
1486 if (type == TYPE_DATE) {
1487 newFormat = DateFormat.getDateInstance(DATE_TIME_MODIFIERS[mod],
1488 locale);
1489 } else {
1490 newFormat = DateFormat.getTimeInstance(DATE_TIME_MODIFIERS[mod],
1491 locale);
1492 }
1493 } else {
1494 // SimpleDateFormat pattern
1495 try {
1496 newFormat = new SimpleDateFormat(segments[SEG_MODIFIER], locale);
1497 } catch (IllegalArgumentException e) {
1498 maxOffset = oldMaxOffset;
1499 throw e;
1500 }
1501 }
1502 break;
1503
1504 case TYPE_CHOICE:
1505 try {
1506 // ChoiceFormat pattern
1507 newFormat = new ChoiceFormat(segments[SEG_MODIFIER]);
1508 } catch (Exception e) {
1509 maxOffset = oldMaxOffset;
1510 throw new IllegalArgumentException("Choice Pattern incorrect: "
1511 + segments[SEG_MODIFIER], e);
1512 }
1513 break;
1514
1515 default:
1516 maxOffset = oldMaxOffset;
1517 throw new IllegalArgumentException("unknown format type: " +
1518 segments[SEG_TYPE]);
1519 }
1520 }
1521 formats[offsetNumber] = newFormat;
1522 }
1523
1524 private static final int findKeyword(String s, String[] list) {
1525 for (int i = 0; i < list.length; ++i) {
1526 if (s.equals(list[i]))
1527 return i;
1528 }
1529
1530 // Try trimmed lowercase.
1531 String ls = s.trim().toLowerCase(Locale.ROOT);
1532 if (ls != s) {
1533 for (int i = 0; i < list.length; ++i) {
1534 if (ls.equals(list[i]))
1535 return i;
1536 }
1537 }
1538 return -1;
1539 }
1540
1541 private static final void copyAndFixQuotes(String source, int start, int end,
1542 StringBuilder target) {
1543 boolean quoted = false;
1544
1545 for (int i = start; i < end; ++i) {
1546 char ch = source.charAt(i);
1547 if (ch == '{') {
1548 if (!quoted) {
1549 target.append('\'');
1550 quoted = true;
1551 }
1552 target.append(ch);
1553 } else if (ch == '\'') {
1554 target.append("''");
1555 } else {
1556 if (quoted) {
1557 target.append('\'');
1558 quoted = false;
1559 }
1560 target.append(ch);
1561 }
1562 }
1563 if (quoted) {
1564 target.append('\'');
1565 }
1566 }
1567
1568 /**
1569 * After reading an object from the input stream, do a simple verification
1570 * to maintain class invariants.
1571 * @throws InvalidObjectException if the objects read from the stream is invalid.
1572 */
1573 private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
1574 in.defaultReadObject();
1575 boolean isValid = maxOffset >= -1
1576 && formats.length > maxOffset
1577 && offsets.length > maxOffset
1578 && argumentNumbers.length > maxOffset;
1579 if (isValid) {
1580 int lastOffset = pattern.length() + 1;
1581 for (int i = maxOffset; i >= 0; --i) {
1582 if ((offsets[i] < 0) || (offsets[i] > lastOffset)) {
1583 isValid = false;
1584 break;
1585 } else {
1586 lastOffset = offsets[i];
1587 }
1588 }
1589 }
1590 if (!isValid) {
1591 throw new InvalidObjectException("Could not reconstruct MessageFormat from corrupt stream.");
1592 }
1593 }
1594}
java/pkg/java/text/Normalizer$Form.class+0−0

Binary file not shown.

java/pkg/java/text/Normalizer.class+0−0

Binary file not shown.

java/pkg/java/text/Normalizer.java+183−0
@@ -0,0 +1,183 @@
1/*
2 * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26/*
27 *******************************************************************************
28 * (C) Copyright IBM Corp. 1996-2005 - All Rights Reserved *
29 * *
30 * The original version of this source code and documentation is copyrighted *
31 * and owned by IBM, These materials are provided under terms of a License *
32 * Agreement between IBM and Sun. This technology is protected by multiple *
33 * US and International patents. This notice and attribution to IBM may not *
34 * to removed. *
35 *******************************************************************************
36 */
37
38package java.text;
39
40import sun.text.normalizer.NormalizerBase;
41import sun.text.normalizer.NormalizerImpl;
42
43/**
44 * This class provides the method <code>normalize</code> which transforms Unicode
45 * text into an equivalent composed or decomposed form, allowing for easier
46 * sorting and searching of text.
47 * The <code>normalize</code> method supports the standard normalization forms
48 * described in
49 * <a href="http://www.unicode.org/unicode/reports/tr15/tr15-23.html">
50 * Unicode Standard Annex #15 &mdash; Unicode Normalization Forms</a>.
51 * <p>
52 * Characters with accents or other adornments can be encoded in
53 * several different ways in Unicode. For example, take the character A-acute.
54 * In Unicode, this can be encoded as a single character (the "composed" form):
55 *
56 * <p><pre>
57 * U+00C1 LATIN CAPITAL LETTER A WITH ACUTE</pre>
58 * </p>
59 *
60 * or as two separate characters (the "decomposed" form):
61 *
62 * <p><pre>
63 * U+0041 LATIN CAPITAL LETTER A
64 * U+0301 COMBINING ACUTE ACCENT</pre>
65 * </p>
66 *
67 * To a user of your program, however, both of these sequences should be
68 * treated as the same "user-level" character "A with acute accent". When you
69 * are searching or comparing text, you must ensure that these two sequences are
70 * treated as equivalent. In addition, you must handle characters with more than
71 * one accent. Sometimes the order of a character's combining accents is
72 * significant, while in other cases accent sequences in different orders are
73 * really equivalent.
74 * <p>
75 * Similarly, the string "ffi" can be encoded as three separate letters:
76 *
77 * <p><pre>
78 * U+0066 LATIN SMALL LETTER F
79 * U+0066 LATIN SMALL LETTER F
80 * U+0069 LATIN SMALL LETTER I</pre>
81 * </p>
82 *
83 * or as the single character
84 *
85 * <p><pre>
86 * U+FB03 LATIN SMALL LIGATURE FFI</pre>
87 * </p>
88 *
89 * The ffi ligature is not a distinct semantic character, and strictly speaking
90 * it shouldn't be in Unicode at all, but it was included for compatibility
91 * with existing character sets that already provided it. The Unicode standard
92 * identifies such characters by giving them "compatibility" decompositions
93 * into the corresponding semantic characters. When sorting and searching, you
94 * will often want to use these mappings.
95 * <p>
96 * The <code>normalize</code> method helps solve these problems by transforming
97 * text into the canonical composed and decomposed forms as shown in the first
98 * example above. In addition, you can have it perform compatibility
99 * decompositions so that you can treat compatibility characters the same as
100 * their equivalents.
101 * Finally, the <code>normalize</code> method rearranges accents into the
102 * proper canonical order, so that you do not have to worry about accent
103 * rearrangement on your own.
104 * <p>
105 * The W3C generally recommends to exchange texts in NFC.
106 * Note also that most legacy character encodings use only precomposed forms and
107 * often do not encode any combining marks by themselves. For conversion to such
108 * character encodings the Unicode text needs to be normalized to NFC.
109 * For more usage examples, see the Unicode Standard Annex.
110 *
111 * @since 1.6
112 */
113public final class Normalizer {
114
115 private Normalizer() {};
116
117 /**
118 * This enum provides constants of the four Unicode normalization forms
119 * that are described in
120 * <a href="http://www.unicode.org/unicode/reports/tr15/tr15-23.html">
121 * Unicode Standard Annex #15 &mdash; Unicode Normalization Forms</a>
122 * and two methods to access them.
123 *
124 * @since 1.6
125 */
126 public static enum Form {
127
128 /**
129 * Canonical decomposition.
130 */
131 NFD,
132
133 /**
134 * Canonical decomposition, followed by canonical composition.
135 */
136 NFC,
137
138 /**
139 * Compatibility decomposition.
140 */
141 NFKD,
142
143 /**
144 * Compatibility decomposition, followed by canonical composition.
145 */
146 NFKC
147 }
148
149 /**
150 * Normalize a sequence of char values.
151 * The sequence will be normalized according to the specified normalization
152 * from.
153 * @param src The sequence of char values to normalize.
154 * @param form The normalization form; one of
155 * {@link java.text.Normalizer.Form#NFC},
156 * {@link java.text.Normalizer.Form#NFD},
157 * {@link java.text.Normalizer.Form#NFKC},
158 * {@link java.text.Normalizer.Form#NFKD}
159 * @return The normalized String
160 * @throws NullPointerException If <code>src</code> or <code>form</code>
161 * is null.
162 */
163 public static String normalize(CharSequence src, Form form) {
164 return NormalizerBase.normalize(src.toString(), form);
165 }
166
167 /**
168 * Determines if the given sequence of char values is normalized.
169 * @param src The sequence of char values to be checked.
170 * @param form The normalization form; one of
171 * {@link java.text.Normalizer.Form#NFC},
172 * {@link java.text.Normalizer.Form#NFD},
173 * {@link java.text.Normalizer.Form#NFKC},
174 * {@link java.text.Normalizer.Form#NFKD}
175 * @return true if the sequence of char values is normalized;
176 * false otherwise.
177 * @throws NullPointerException If <code>src</code> or <code>form</code>
178 * is null.
179 */
180 public static boolean isNormalized(CharSequence src, Form form) {
181 return NormalizerBase.isNormalized(src.toString(), form);
182 }
183}
java/pkg/java/text/NumberFormat$Field.class+0−0

Binary file not shown.

java/pkg/java/text/NumberFormat$NumberFormatGetter.class+0−0

Binary file not shown.

java/pkg/java/text/NumberFormat.class+0−0

Binary file not shown.

java/pkg/java/text/NumberFormat.java+1162−0
@@ -0,0 +1,1162 @@
1/*
2 * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26/*
27 * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
28 * (C) Copyright IBM Corp. 1996 - 1998 - All Rights Reserved
29 *
30 * The original version of this source code and documentation is copyrighted
31 * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
32 * materials are provided under terms of a License Agreement between Taligent
33 * and Sun. This technology is protected by multiple US and International
34 * patents. This notice and attribution to Taligent may not be removed.
35 * Taligent is a registered trademark of Taligent, Inc.
36 *
37 */
38
39package java.text;
40
41import java.io.InvalidObjectException;
42import java.io.IOException;
43import java.io.ObjectInputStream;
44import java.io.ObjectOutputStream;
45import java.math.BigInteger;
46import java.math.RoundingMode;
47import java.text.spi.NumberFormatProvider;
48import java.util.Currency;
49import java.util.HashMap;
50import java.util.Hashtable;
51import java.util.Locale;
52import java.util.Map;
53import java.util.ResourceBundle;
54import java.util.concurrent.atomic.AtomicInteger;
55import java.util.concurrent.atomic.AtomicLong;
56import java.util.spi.LocaleServiceProvider;
57import sun.util.LocaleServiceProviderPool;
58import sun.util.resources.LocaleData;
59
60/**
61 * <code>NumberFormat</code> is the abstract base class for all number
62 * formats. This class provides the interface for formatting and parsing
63 * numbers. <code>NumberFormat</code> also provides methods for determining
64 * which locales have number formats, and what their names are.
65 *
66 * <p>
67 * <code>NumberFormat</code> helps you to format and parse numbers for any locale.
68 * Your code can be completely independent of the locale conventions for
69 * decimal points, thousands-separators, or even the particular decimal
70 * digits used, or whether the number format is even decimal.
71 *
72 * <p>
73 * To format a number for the current Locale, use one of the factory
74 * class methods:
75 * <blockquote>
76 * <pre>
77 * myString = NumberFormat.getInstance().format(myNumber);
78 * </pre>
79 * </blockquote>
80 * If you are formatting multiple numbers, it is
81 * more efficient to get the format and use it multiple times so that
82 * the system doesn't have to fetch the information about the local
83 * language and country conventions multiple times.
84 * <blockquote>
85 * <pre>
86 * NumberFormat nf = NumberFormat.getInstance();
87 * for (int i = 0; i < myNumber.length; ++i) {
88 * output.println(nf.format(myNumber[i]) + "; ");
89 * }
90 * </pre>
91 * </blockquote>
92 * To format a number for a different Locale, specify it in the
93 * call to <code>getInstance</code>.
94 * <blockquote>
95 * <pre>
96 * NumberFormat nf = NumberFormat.getInstance(Locale.FRENCH);
97 * </pre>
98 * </blockquote>
99 * You can also use a <code>NumberFormat</code> to parse numbers:
100 * <blockquote>
101 * <pre>
102 * myNumber = nf.parse(myString);
103 * </pre>
104 * </blockquote>
105 * Use <code>getInstance</code> or <code>getNumberInstance</code> to get the
106 * normal number format. Use <code>getIntegerInstance</code> to get an
107 * integer number format. Use <code>getCurrencyInstance</code> to get the
108 * currency number format. And use <code>getPercentInstance</code> to get a
109 * format for displaying percentages. With this format, a fraction like
110 * 0.53 is displayed as 53%.
111 *
112 * <p>
113 * You can also control the display of numbers with such methods as
114 * <code>setMinimumFractionDigits</code>.
115 * If you want even more control over the format or parsing,
116 * or want to give your users more control,
117 * you can try casting the <code>NumberFormat</code> you get from the factory methods
118 * to a <code>DecimalFormat</code>. This will work for the vast majority
119 * of locales; just remember to put it in a <code>try</code> block in case you
120 * encounter an unusual one.
121 *
122 * <p>
123 * NumberFormat and DecimalFormat are designed such that some controls
124 * work for formatting and others work for parsing. The following is
125 * the detailed description for each these control methods,
126 * <p>
127 * setParseIntegerOnly : only affects parsing, e.g.
128 * if true, "3456.78" -> 3456 (and leaves the parse position just after index 6)
129 * if false, "3456.78" -> 3456.78 (and leaves the parse position just after index 8)
130 * This is independent of formatting. If you want to not show a decimal point
131 * where there might be no digits after the decimal point, use
132 * setDecimalSeparatorAlwaysShown.
133 * <p>
134 * setDecimalSeparatorAlwaysShown : only affects formatting, and only where
135 * there might be no digits after the decimal point, such as with a pattern
136 * like "#,##0.##", e.g.,
137 * if true, 3456.00 -> "3,456."
138 * if false, 3456.00 -> "3456"
139 * This is independent of parsing. If you want parsing to stop at the decimal
140 * point, use setParseIntegerOnly.
141 *
142 * <p>
143 * You can also use forms of the <code>parse</code> and <code>format</code>
144 * methods with <code>ParsePosition</code> and <code>FieldPosition</code> to
145 * allow you to:
146 * <ul>
147 * <li> progressively parse through pieces of a string
148 * <li> align the decimal point and other areas
149 * </ul>
150 * For example, you can align numbers in two ways:
151 * <ol>
152 * <li> If you are using a monospaced font with spacing for alignment,
153 * you can pass the <code>FieldPosition</code> in your format call, with
154 * <code>field</code> = <code>INTEGER_FIELD</code>. On output,
155 * <code>getEndIndex</code> will be set to the offset between the
156 * last character of the integer and the decimal. Add
157 * (desiredSpaceCount - getEndIndex) spaces at the front of the string.
158 *
159 * <li> If you are using proportional fonts,
160 * instead of padding with spaces, measure the width
161 * of the string in pixels from the start to <code>getEndIndex</code>.
162 * Then move the pen by
163 * (desiredPixelWidth - widthToAlignmentPoint) before drawing the text.
164 * It also works where there is no decimal, but possibly additional
165 * characters at the end, e.g., with parentheses in negative
166 * numbers: "(12)" for -12.
167 * </ol>
168 *
169 * <h4><a name="synchronization">Synchronization</a></h4>
170 *
171 * <p>
172 * Number formats are generally not synchronized.
173 * It is recommended to create separate format instances for each thread.
174 * If multiple threads access a format concurrently, it must be synchronized
175 * externally.
176 *
177 * @see DecimalFormat
178 * @see ChoiceFormat
179 * @author Mark Davis
180 * @author Helena Shih
181 */
182public abstract class NumberFormat extends Format {
183
184 /**
185 * Field constant used to construct a FieldPosition object. Signifies that
186 * the position of the integer part of a formatted number should be returned.
187 * @see java.text.FieldPosition
188 */
189 public static final int INTEGER_FIELD = 0;
190
191 /**
192 * Field constant used to construct a FieldPosition object. Signifies that
193 * the position of the fraction part of a formatted number should be returned.
194 * @see java.text.FieldPosition
195 */
196 public static final int FRACTION_FIELD = 1;
197
198 /**
199 * Sole constructor. (For invocation by subclass constructors, typically
200 * implicit.)
201 */
202 protected NumberFormat() {
203 }
204
205 /**
206 * Formats a number and appends the resulting text to the given string
207 * buffer.
208 * The number can be of any subclass of {@link java.lang.Number}.
209 * <p>
210 * This implementation extracts the number's value using
211 * {@link java.lang.Number#longValue()} for all integral type values that
212 * can be converted to <code>long</code> without loss of information,
213 * including <code>BigInteger</code> values with a
214 * {@link java.math.BigInteger#bitLength() bit length} of less than 64,
215 * and {@link java.lang.Number#doubleValue()} for all other types. It
216 * then calls
217 * {@link #format(long,java.lang.StringBuffer,java.text.FieldPosition)}
218 * or {@link #format(double,java.lang.StringBuffer,java.text.FieldPosition)}.
219 * This may result in loss of magnitude information and precision for
220 * <code>BigInteger</code> and <code>BigDecimal</code> values.
221 * @param number the number to format
222 * @param toAppendTo the <code>StringBuffer</code> to which the formatted
223 * text is to be appended
224 * @param pos On input: an alignment field, if desired.
225 * On output: the offsets of the alignment field.
226 * @return the value passed in as <code>toAppendTo</code>
227 * @exception IllegalArgumentException if <code>number</code> is
228 * null or not an instance of <code>Number</code>.
229 * @exception NullPointerException if <code>toAppendTo</code> or
230 * <code>pos</code> is null
231 * @exception ArithmeticException if rounding is needed with rounding
232 * mode being set to RoundingMode.UNNECESSARY
233 * @see java.text.FieldPosition
234 */
235 public StringBuffer format(Object number,
236 StringBuffer toAppendTo,
237 FieldPosition pos) {
238 if (number instanceof Long || number instanceof Integer ||
239 number instanceof Short || number instanceof Byte ||
240 number instanceof AtomicInteger || number instanceof AtomicLong ||
241 (number instanceof BigInteger &&
242 ((BigInteger)number).bitLength() < 64)) {
243 return format(((Number)number).longValue(), toAppendTo, pos);
244 } else if (number instanceof Number) {
245 return format(((Number)number).doubleValue(), toAppendTo, pos);
246 } else {
247 throw new IllegalArgumentException("Cannot format given Object as a Number");
248 }
249 }
250
251 /**
252 * Parses text from a string to produce a <code>Number</code>.
253 * <p>
254 * The method attempts to parse text starting at the index given by
255 * <code>pos</code>.
256 * If parsing succeeds, then the index of <code>pos</code> is updated
257 * to the index after the last character used (parsing does not necessarily
258 * use all characters up to the end of the string), and the parsed
259 * number is returned. The updated <code>pos</code> can be used to
260 * indicate the starting point for the next call to this method.
261 * If an error occurs, then the index of <code>pos</code> is not
262 * changed, the error index of <code>pos</code> is set to the index of
263 * the character where the error occurred, and null is returned.
264 * <p>
265 * See the {@link #parse(String, ParsePosition)} method for more information
266 * on number parsing.
267 *
268 * @param source A <code>String</code>, part of which should be parsed.
269 * @param pos A <code>ParsePosition</code> object with index and error
270 * index information as described above.
271 * @return A <code>Number</code> parsed from the string. In case of
272 * error, returns null.
273 * @exception NullPointerException if <code>pos</code> is null.
274 */
275 public final Object parseObject(String source, ParsePosition pos) {
276 return parse(source, pos);
277 }
278
279 /**
280 * Specialization of format.
281 * @exception ArithmeticException if rounding is needed with rounding
282 * mode being set to RoundingMode.UNNECESSARY
283 * @see java.text.Format#format
284 */
285 public final String format(double number) {
286 return format(number, new StringBuffer(),
287 DontCareFieldPosition.INSTANCE).toString();
288 }
289
290 /**
291 * Specialization of format.
292 * @exception ArithmeticException if rounding is needed with rounding
293 * mode being set to RoundingMode.UNNECESSARY
294 * @see java.text.Format#format
295 */
296 public final String format(long number) {
297 return format(number, new StringBuffer(),
298 DontCareFieldPosition.INSTANCE).toString();
299 }
300
301 /**
302 * Specialization of format.
303 * @exception ArithmeticException if rounding is needed with rounding
304 * mode being set to RoundingMode.UNNECESSARY
305 * @see java.text.Format#format
306 */
307 public abstract StringBuffer format(double number,
308 StringBuffer toAppendTo,
309 FieldPosition pos);
310
311 /**
312 * Specialization of format.
313 * @exception ArithmeticException if rounding is needed with rounding
314 * mode being set to RoundingMode.UNNECESSARY
315 * @see java.text.Format#format
316 */
317 public abstract StringBuffer format(long number,
318 StringBuffer toAppendTo,
319 FieldPosition pos);
320
321 /**
322 * Returns a Long if possible (e.g., within the range [Long.MIN_VALUE,
323 * Long.MAX_VALUE] and with no decimals), otherwise a Double.
324 * If IntegerOnly is set, will stop at a decimal
325 * point (or equivalent; e.g., for rational numbers "1 2/3", will stop
326 * after the 1).
327 * Does not throw an exception; if no object can be parsed, index is
328 * unchanged!
329 * @see java.text.NumberFormat#isParseIntegerOnly
330 * @see java.text.Format#parseObject
331 */
332 public abstract Number parse(String source, ParsePosition parsePosition);
333
334 /**
335 * Parses text from the beginning of the given string to produce a number.
336 * The method may not use the entire text of the given string.
337 * <p>
338 * See the {@link #parse(String, ParsePosition)} method for more information
339 * on number parsing.
340 *
341 * @param source A <code>String</code> whose beginning should be parsed.
342 * @return A <code>Number</code> parsed from the string.
343 * @exception ParseException if the beginning of the specified string
344 * cannot be parsed.
345 */
346 public Number parse(String source) throws ParseException {
347 ParsePosition parsePosition = new ParsePosition(0);
348 Number result = parse(source, parsePosition);
349 if (parsePosition.index == 0) {
350 throw new ParseException("Unparseable number: \"" + source + "\"",
351 parsePosition.errorIndex);
352 }
353 return result;
354 }
355
356 /**
357 * Returns true if this format will parse numbers as integers only.
358 * For example in the English locale, with ParseIntegerOnly true, the
359 * string "1234." would be parsed as the integer value 1234 and parsing
360 * would stop at the "." character. Of course, the exact format accepted
361 * by the parse operation is locale dependant and determined by sub-classes
362 * of NumberFormat.
363 */
364 public boolean isParseIntegerOnly() {
365 return parseIntegerOnly;
366 }
367
368 /**
369 * Sets whether or not numbers should be parsed as integers only.
370 * @see #isParseIntegerOnly
371 */
372 public void setParseIntegerOnly(boolean value) {
373 parseIntegerOnly = value;
374 }
375
376 //============== Locale Stuff =====================
377
378 /**
379 * Returns a general-purpose number format for the current default locale.
380 * This is the same as calling
381 * {@link #getNumberInstance() getNumberInstance()}.
382 */
383 public final static NumberFormat getInstance() {
384 return getInstance(Locale.getDefault(Locale.Category.FORMAT), NUMBERSTYLE);
385 }
386
387 /**
388 * Returns a general-purpose number format for the specified locale.
389 * This is the same as calling
390 * {@link #getNumberInstance(java.util.Locale) getNumberInstance(inLocale)}.
391 */
392 public static NumberFormat getInstance(Locale inLocale) {
393 return getInstance(inLocale, NUMBERSTYLE);
394 }
395
396 /**
397 * Returns a general-purpose number format for the current default locale.
398 */
399 public final static NumberFormat getNumberInstance() {
400 return getInstance(Locale.getDefault(Locale.Category.FORMAT), NUMBERSTYLE);
401 }
402
403 /**
404 * Returns a general-purpose number format for the specified locale.
405 */
406 public static NumberFormat getNumberInstance(Locale inLocale) {
407 return getInstance(inLocale, NUMBERSTYLE);
408 }
409
410 /**
411 * Returns an integer number format for the current default locale. The
412 * returned number format is configured to round floating point numbers
413 * to the nearest integer using half-even rounding (see {@link
414 * java.math.RoundingMode#HALF_EVEN RoundingMode.HALF_EVEN}) for formatting,
415 * and to parse only the integer part of an input string (see {@link
416 * #isParseIntegerOnly isParseIntegerOnly}).
417 *
418 * @see #getRoundingMode()
419 * @return a number format for integer values
420 * @since 1.4
421 */
422 public final static NumberFormat getIntegerInstance() {
423 return getInstance(Locale.getDefault(Locale.Category.FORMAT), INTEGERSTYLE);
424 }
425
426 /**
427 * Returns an integer number format for the specified locale. The
428 * returned number format is configured to round floating point numbers
429 * to the nearest integer using half-even rounding (see {@link
430 * java.math.RoundingMode#HALF_EVEN RoundingMode.HALF_EVEN}) for formatting,
431 * and to parse only the integer part of an input string (see {@link
432 * #isParseIntegerOnly isParseIntegerOnly}).
433 *
434 * @see #getRoundingMode()
435 * @return a number format for integer values
436 * @since 1.4
437 */
438 public static NumberFormat getIntegerInstance(Locale inLocale) {
439 return getInstance(inLocale, INTEGERSTYLE);
440 }
441
442 /**
443 * Returns a currency format for the current default locale.
444 */
445 public final static NumberFormat getCurrencyInstance() {
446 return getInstance(Locale.getDefault(Locale.Category.FORMAT), CURRENCYSTYLE);
447 }
448
449 /**
450 * Returns a currency format for the specified locale.
451 */
452 public static NumberFormat getCurrencyInstance(Locale inLocale) {
453 return getInstance(inLocale, CURRENCYSTYLE);
454 }
455
456 /**
457 * Returns a percentage format for the current default locale.
458 */
459 public final static NumberFormat getPercentInstance() {
460 return getInstance(Locale.getDefault(Locale.Category.FORMAT), PERCENTSTYLE);
461 }
462
463 /**
464 * Returns a percentage format for the specified locale.
465 */
466 public static NumberFormat getPercentInstance(Locale inLocale) {
467 return getInstance(inLocale, PERCENTSTYLE);
468 }
469
470 /**
471 * Returns a scientific format for the current default locale.
472 */
473 /*public*/ final static NumberFormat getScientificInstance() {
474 return getInstance(Locale.getDefault(Locale.Category.FORMAT), SCIENTIFICSTYLE);
475 }
476
477 /**
478 * Returns a scientific format for the specified locale.
479 */
480 /*public*/ static NumberFormat getScientificInstance(Locale inLocale) {
481 return getInstance(inLocale, SCIENTIFICSTYLE);
482 }
483
484 /**
485 * Returns an array of all locales for which the
486 * <code>get*Instance</code> methods of this class can return
487 * localized instances.
488 * The returned array represents the union of locales supported by the Java
489 * runtime and by installed
490 * {@link java.text.spi.NumberFormatProvider NumberFormatProvider} implementations.
491 * It must contain at least a <code>Locale</code> instance equal to
492 * {@link java.util.Locale#US Locale.US}.
493 *
494 * @return An array of locales for which localized
495 * <code>NumberFormat</code> instances are available.
496 */
497 public static Locale[] getAvailableLocales() {
498 LocaleServiceProviderPool pool =
499 LocaleServiceProviderPool.getPool(NumberFormatProvider.class);
500 return pool.getAvailableLocales();
501 }
502
503 /**
504 * Overrides hashCode
505 */
506 public int hashCode() {
507 return maximumIntegerDigits * 37 + maxFractionDigits;
508 // just enough fields for a reasonable distribution
509 }
510
511 /**
512 * Overrides equals
513 */
514 public boolean equals(Object obj) {
515 if (obj == null) {
516 return false;
517 }
518 if (this == obj) {
519 return true;
520 }
521 if (getClass() != obj.getClass()) {
522 return false;
523 }
524 NumberFormat other = (NumberFormat) obj;
525 return (maximumIntegerDigits == other.maximumIntegerDigits
526 && minimumIntegerDigits == other.minimumIntegerDigits
527 && maximumFractionDigits == other.maximumFractionDigits
528 && minimumFractionDigits == other.minimumFractionDigits
529 && groupingUsed == other.groupingUsed
530 && parseIntegerOnly == other.parseIntegerOnly);
531 }
532
533 /**
534 * Overrides Cloneable
535 */
536 public Object clone() {
537 NumberFormat other = (NumberFormat) super.clone();
538 return other;
539 }
540
541 /**
542 * Returns true if grouping is used in this format. For example, in the
543 * English locale, with grouping on, the number 1234567 might be formatted
544 * as "1,234,567". The grouping separator as well as the size of each group
545 * is locale dependant and is determined by sub-classes of NumberFormat.
546 * @see #setGroupingUsed
547 */
548 public boolean isGroupingUsed() {
549 return groupingUsed;
550 }
551
552 /**
553 * Set whether or not grouping will be used in this format.
554 * @see #isGroupingUsed
555 */
556 public void setGroupingUsed(boolean newValue) {
557 groupingUsed = newValue;
558 }
559
560 /**
561 * Returns the maximum number of digits allowed in the integer portion of a
562 * number.
563 * @see #setMaximumIntegerDigits
564 */
565 public int getMaximumIntegerDigits() {
566 return maximumIntegerDigits;
567 }
568
569 /**
570 * Sets the maximum number of digits allowed in the integer portion of a
571 * number. maximumIntegerDigits must be >= minimumIntegerDigits. If the
572 * new value for maximumIntegerDigits is less than the current value
573 * of minimumIntegerDigits, then minimumIntegerDigits will also be set to
574 * the new value.
575 * @param newValue the maximum number of integer digits to be shown; if
576 * less than zero, then zero is used. The concrete subclass may enforce an
577 * upper limit to this value appropriate to the numeric type being formatted.
578 * @see #getMaximumIntegerDigits
579 */
580 public void setMaximumIntegerDigits(int newValue) {
581 maximumIntegerDigits = Math.max(0,newValue);
582 if (minimumIntegerDigits > maximumIntegerDigits) {
583 minimumIntegerDigits = maximumIntegerDigits;
584 }
585 }
586
587 /**
588 * Returns the minimum number of digits allowed in the integer portion of a
589 * number.
590 * @see #setMinimumIntegerDigits
591 */
592 public int getMinimumIntegerDigits() {
593 return minimumIntegerDigits;
594 }
595
596 /**
597 * Sets the minimum number of digits allowed in the integer portion of a
598 * number. minimumIntegerDigits must be <= maximumIntegerDigits. If the
599 * new value for minimumIntegerDigits exceeds the current value
600 * of maximumIntegerDigits, then maximumIntegerDigits will also be set to
601 * the new value
602 * @param newValue the minimum number of integer digits to be shown; if
603 * less than zero, then zero is used. The concrete subclass may enforce an
604 * upper limit to this value appropriate to the numeric type being formatted.
605 * @see #getMinimumIntegerDigits
606 */
607 public void setMinimumIntegerDigits(int newValue) {
608 minimumIntegerDigits = Math.max(0,newValue);
609 if (minimumIntegerDigits > maximumIntegerDigits) {
610 maximumIntegerDigits = minimumIntegerDigits;
611 }
612 }
613
614 /**
615 * Returns the maximum number of digits allowed in the fraction portion of a
616 * number.
617 * @see #setMaximumFractionDigits
618 */
619 public int getMaximumFractionDigits() {
620 return maximumFractionDigits;
621 }
622
623 /**
624 * Sets the maximum number of digits allowed in the fraction portion of a
625 * number. maximumFractionDigits must be >= minimumFractionDigits. If the
626 * new value for maximumFractionDigits is less than the current value
627 * of minimumFractionDigits, then minimumFractionDigits will also be set to
628 * the new value.
629 * @param newValue the maximum number of fraction digits to be shown; if
630 * less than zero, then zero is used. The concrete subclass may enforce an
631 * upper limit to this value appropriate to the numeric type being formatted.
632 * @see #getMaximumFractionDigits
633 */
634 public void setMaximumFractionDigits(int newValue) {
635 maximumFractionDigits = Math.max(0,newValue);
636 if (maximumFractionDigits < minimumFractionDigits) {
637 minimumFractionDigits = maximumFractionDigits;
638 }
639 }
640
641 /**
642 * Returns the minimum number of digits allowed in the fraction portion of a
643 * number.
644 * @see #setMinimumFractionDigits
645 */
646 public int getMinimumFractionDigits() {
647 return minimumFractionDigits;
648 }
649
650 /**
651 * Sets the minimum number of digits allowed in the fraction portion of a
652 * number. minimumFractionDigits must be <= maximumFractionDigits. If the
653 * new value for minimumFractionDigits exceeds the current value
654 * of maximumFractionDigits, then maximumIntegerDigits will also be set to
655 * the new value
656 * @param newValue the minimum number of fraction digits to be shown; if
657 * less than zero, then zero is used. The concrete subclass may enforce an
658 * upper limit to this value appropriate to the numeric type being formatted.
659 * @see #getMinimumFractionDigits
660 */
661 public void setMinimumFractionDigits(int newValue) {
662 minimumFractionDigits = Math.max(0,newValue);
663 if (maximumFractionDigits < minimumFractionDigits) {
664 maximumFractionDigits = minimumFractionDigits;
665 }
666 }
667
668 /**
669 * Gets the currency used by this number format when formatting
670 * currency values. The initial value is derived in a locale dependent
671 * way. The returned value may be null if no valid
672 * currency could be determined and no currency has been set using
673 * {@link #setCurrency(java.util.Currency) setCurrency}.
674 * <p>
675 * The default implementation throws
676 * <code>UnsupportedOperationException</code>.
677 *
678 * @return the currency used by this number format, or <code>null</code>
679 * @exception UnsupportedOperationException if the number format class
680 * doesn't implement currency formatting
681 * @since 1.4
682 */
683 public Currency getCurrency() {
684 throw new UnsupportedOperationException();
685 }
686
687 /**
688 * Sets the currency used by this number format when formatting
689 * currency values. This does not update the minimum or maximum
690 * number of fraction digits used by the number format.
691 * <p>
692 * The default implementation throws
693 * <code>UnsupportedOperationException</code>.
694 *
695 * @param currency the new currency to be used by this number format
696 * @exception UnsupportedOperationException if the number format class
697 * doesn't implement currency formatting
698 * @exception NullPointerException if <code>currency</code> is null
699 * @since 1.4
700 */
701 public void setCurrency(Currency currency) {
702 throw new UnsupportedOperationException();
703 }
704
705 /**
706 * Gets the {@link java.math.RoundingMode} used in this NumberFormat.
707 * The default implementation of this method in NumberFormat
708 * always throws {@link java.lang.UnsupportedOperationException}.
709 * Subclasses which handle different rounding modes should override
710 * this method.
711 *
712 * @exception UnsupportedOperationException The default implementation
713 * always throws this exception
714 * @return The <code>RoundingMode</code> used for this NumberFormat.
715 * @see #setRoundingMode(RoundingMode)
716 * @since 1.6
717 */
718 public RoundingMode getRoundingMode() {
719 throw new UnsupportedOperationException();
720 }
721
722 /**
723 * Sets the {@link java.math.RoundingMode} used in this NumberFormat.
724 * The default implementation of this method in NumberFormat always
725 * throws {@link java.lang.UnsupportedOperationException}.
726 * Subclasses which handle different rounding modes should override
727 * this method.
728 *
729 * @exception UnsupportedOperationException The default implementation
730 * always throws this exception
731 * @exception NullPointerException if <code>roundingMode</code> is null
732 * @param roundingMode The <code>RoundingMode</code> to be used
733 * @see #getRoundingMode()
734 * @since 1.6
735 */
736 public void setRoundingMode(RoundingMode roundingMode) {
737 throw new UnsupportedOperationException();
738 }
739
740 // =======================privates===============================
741
742 private static NumberFormat getInstance(Locale desiredLocale,
743 int choice) {
744 // Check whether a provider can provide an implementation that's closer
745 // to the requested locale than what the Java runtime itself can provide.
746 LocaleServiceProviderPool pool =
747 LocaleServiceProviderPool.getPool(NumberFormatProvider.class);
748 if (pool.hasProviders()) {
749 NumberFormat providersInstance = pool.getLocalizedObject(
750 NumberFormatGetter.INSTANCE,
751 desiredLocale,
752 choice);
753 if (providersInstance != null) {
754 return providersInstance;
755 }
756 }
757
758 /* try the cache first */
759 String[] numberPatterns = (String[])cachedLocaleData.get(desiredLocale);
760 if (numberPatterns == null) { /* cache miss */
761 ResourceBundle resource = LocaleData.getNumberFormatData(desiredLocale);
762 numberPatterns = resource.getStringArray("NumberPatterns");
763 /* update cache */
764 cachedLocaleData.put(desiredLocale, numberPatterns);
765 }
766
767 DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(desiredLocale);
768 int entry = (choice == INTEGERSTYLE) ? NUMBERSTYLE : choice;
769 DecimalFormat format = new DecimalFormat(numberPatterns[entry], symbols);
770
771 if (choice == INTEGERSTYLE) {
772 format.setMaximumFractionDigits(0);
773 format.setDecimalSeparatorAlwaysShown(false);
774 format.setParseIntegerOnly(true);
775 } else if (choice == CURRENCYSTYLE) {
776 format.adjustForCurrencyDefaultFractionDigits();
777 }
778
779 return format;
780 }
781
782 /**
783 * First, read in the default serializable data.
784 *
785 * Then, if <code>serialVersionOnStream</code> is less than 1, indicating that
786 * the stream was written by JDK 1.1,
787 * set the <code>int</code> fields such as <code>maximumIntegerDigits</code>
788 * to be equal to the <code>byte</code> fields such as <code>maxIntegerDigits</code>,
789 * since the <code>int</code> fields were not present in JDK 1.1.
790 * Finally, set serialVersionOnStream back to the maximum allowed value so that
791 * default serialization will work properly if this object is streamed out again.
792 *
793 * <p>If <code>minimumIntegerDigits</code> is greater than
794 * <code>maximumIntegerDigits</code> or <code>minimumFractionDigits</code>
795 * is greater than <code>maximumFractionDigits</code>, then the stream data
796 * is invalid and this method throws an <code>InvalidObjectException</code>.
797 * In addition, if any of these values is negative, then this method throws
798 * an <code>InvalidObjectException</code>.
799 *
800 * @since 1.2
801 */
802 private void readObject(ObjectInputStream stream)
803 throws IOException, ClassNotFoundException
804 {
805 stream.defaultReadObject();
806 if (serialVersionOnStream < 1) {
807 // Didn't have additional int fields, reassign to use them.
808 maximumIntegerDigits = maxIntegerDigits;
809 minimumIntegerDigits = minIntegerDigits;
810 maximumFractionDigits = maxFractionDigits;
811 minimumFractionDigits = minFractionDigits;
812 }
813 if (minimumIntegerDigits > maximumIntegerDigits ||
814 minimumFractionDigits > maximumFractionDigits ||
815 minimumIntegerDigits < 0 || minimumFractionDigits < 0) {
816 throw new InvalidObjectException("Digit count range invalid");
817 }
818 serialVersionOnStream = currentSerialVersion;
819 }
820
821 /**
822 * Write out the default serializable data, after first setting
823 * the <code>byte</code> fields such as <code>maxIntegerDigits</code> to be
824 * equal to the <code>int</code> fields such as <code>maximumIntegerDigits</code>
825 * (or to <code>Byte.MAX_VALUE</code>, whichever is smaller), for compatibility
826 * with the JDK 1.1 version of the stream format.
827 *
828 * @since 1.2
829 */
830 private void writeObject(ObjectOutputStream stream)
831 throws IOException
832 {
833 maxIntegerDigits = (maximumIntegerDigits > Byte.MAX_VALUE) ?
834 Byte.MAX_VALUE : (byte)maximumIntegerDigits;
835 minIntegerDigits = (minimumIntegerDigits > Byte.MAX_VALUE) ?
836 Byte.MAX_VALUE : (byte)minimumIntegerDigits;
837 maxFractionDigits = (maximumFractionDigits > Byte.MAX_VALUE) ?
838 Byte.MAX_VALUE : (byte)maximumFractionDigits;
839 minFractionDigits = (minimumFractionDigits > Byte.MAX_VALUE) ?
840 Byte.MAX_VALUE : (byte)minimumFractionDigits;
841 stream.defaultWriteObject();
842 }
843
844 /**
845 * Cache to hold the NumberPatterns of a Locale.
846 */
847 private static final Hashtable cachedLocaleData = new Hashtable(3);
848
849 // Constants used by factory methods to specify a style of format.
850 private static final int NUMBERSTYLE = 0;
851 private static final int CURRENCYSTYLE = 1;
852 private static final int PERCENTSTYLE = 2;
853 private static final int SCIENTIFICSTYLE = 3;
854 private static final int INTEGERSTYLE = 4;
855
856 /**
857 * True if the grouping (i.e. thousands) separator is used when
858 * formatting and parsing numbers.
859 *
860 * @serial
861 * @see #isGroupingUsed
862 */
863 private boolean groupingUsed = true;
864
865 /**
866 * The maximum number of digits allowed in the integer portion of a
867 * number. <code>maxIntegerDigits</code> must be greater than or equal to
868 * <code>minIntegerDigits</code>.
869 * <p>
870 * <strong>Note:</strong> This field exists only for serialization
871 * compatibility with JDK 1.1. In Java platform 2 v1.2 and higher, the new
872 * <code>int</code> field <code>maximumIntegerDigits</code> is used instead.
873 * When writing to a stream, <code>maxIntegerDigits</code> is set to
874 * <code>maximumIntegerDigits</code> or <code>Byte.MAX_VALUE</code>,
875 * whichever is smaller. When reading from a stream, this field is used
876 * only if <code>serialVersionOnStream</code> is less than 1.
877 *
878 * @serial
879 * @see #getMaximumIntegerDigits
880 */
881 private byte maxIntegerDigits = 40;
882
883 /**
884 * The minimum number of digits allowed in the integer portion of a
885 * number. <code>minimumIntegerDigits</code> must be less than or equal to
886 * <code>maximumIntegerDigits</code>.
887 * <p>
888 * <strong>Note:</strong> This field exists only for serialization
889 * compatibility with JDK 1.1. In Java platform 2 v1.2 and higher, the new
890 * <code>int</code> field <code>minimumIntegerDigits</code> is used instead.
891 * When writing to a stream, <code>minIntegerDigits</code> is set to
892 * <code>minimumIntegerDigits</code> or <code>Byte.MAX_VALUE</code>,
893 * whichever is smaller. When reading from a stream, this field is used
894 * only if <code>serialVersionOnStream</code> is less than 1.
895 *
896 * @serial
897 * @see #getMinimumIntegerDigits
898 */
899 private byte minIntegerDigits = 1;
900
901 /**
902 * The maximum number of digits allowed in the fractional portion of a
903 * number. <code>maximumFractionDigits</code> must be greater than or equal to
904 * <code>minimumFractionDigits</code>.
905 * <p>
906 * <strong>Note:</strong> This field exists only for serialization
907 * compatibility with JDK 1.1. In Java platform 2 v1.2 and higher, the new
908 * <code>int</code> field <code>maximumFractionDigits</code> is used instead.
909 * When writing to a stream, <code>maxFractionDigits</code> is set to
910 * <code>maximumFractionDigits</code> or <code>Byte.MAX_VALUE</code>,
911 * whichever is smaller. When reading from a stream, this field is used
912 * only if <code>serialVersionOnStream</code> is less than 1.
913 *
914 * @serial
915 * @see #getMaximumFractionDigits
916 */
917 private byte maxFractionDigits = 3; // invariant, >= minFractionDigits
918
919 /**
920 * The minimum number of digits allowed in the fractional portion of a
921 * number. <code>minimumFractionDigits</code> must be less than or equal to
922 * <code>maximumFractionDigits</code>.
923 * <p>
924 * <strong>Note:</strong> This field exists only for serialization
925 * compatibility with JDK 1.1. In Java platform 2 v1.2 and higher, the new
926 * <code>int</code> field <code>minimumFractionDigits</code> is used instead.
927 * When writing to a stream, <code>minFractionDigits</code> is set to
928 * <code>minimumFractionDigits</code> or <code>Byte.MAX_VALUE</code>,
929 * whichever is smaller. When reading from a stream, this field is used
930 * only if <code>serialVersionOnStream</code> is less than 1.
931 *
932 * @serial
933 * @see #getMinimumFractionDigits
934 */
935 private byte minFractionDigits = 0;
936
937 /**
938 * True if this format will parse numbers as integers only.
939 *
940 * @serial
941 * @see #isParseIntegerOnly
942 */
943 private boolean parseIntegerOnly = false;
944
945 // new fields for 1.2. byte is too small for integer digits.
946
947 /**
948 * The maximum number of digits allowed in the integer portion of a
949 * number. <code>maximumIntegerDigits</code> must be greater than or equal to
950 * <code>minimumIntegerDigits</code>.
951 *
952 * @serial
953 * @since 1.2
954 * @see #getMaximumIntegerDigits
955 */
956 private int maximumIntegerDigits = 40;
957
958 /**
959 * The minimum number of digits allowed in the integer portion of a
960 * number. <code>minimumIntegerDigits</code> must be less than or equal to
961 * <code>maximumIntegerDigits</code>.
962 *
963 * @serial
964 * @since 1.2
965 * @see #getMinimumIntegerDigits
966 */
967 private int minimumIntegerDigits = 1;
968
969 /**
970 * The maximum number of digits allowed in the fractional portion of a
971 * number. <code>maximumFractionDigits</code> must be greater than or equal to
972 * <code>minimumFractionDigits</code>.
973 *
974 * @serial
975 * @since 1.2
976 * @see #getMaximumFractionDigits
977 */
978 private int maximumFractionDigits = 3; // invariant, >= minFractionDigits
979
980 /**
981 * The minimum number of digits allowed in the fractional portion of a
982 * number. <code>minimumFractionDigits</code> must be less than or equal to
983 * <code>maximumFractionDigits</code>.
984 *
985 * @serial
986 * @since 1.2
987 * @see #getMinimumFractionDigits
988 */
989 private int minimumFractionDigits = 0;
990
991 static final int currentSerialVersion = 1;
992
993 /**
994 * Describes the version of <code>NumberFormat</code> present on the stream.
995 * Possible values are:
996 * <ul>
997 * <li><b>0</b> (or uninitialized): the JDK 1.1 version of the stream format.
998 * In this version, the <code>int</code> fields such as
999 * <code>maximumIntegerDigits</code> were not present, and the <code>byte</code>
1000 * fields such as <code>maxIntegerDigits</code> are used instead.
1001 *
1002 * <li><b>1</b>: the 1.2 version of the stream format. The values of the
1003 * <code>byte</code> fields such as <code>maxIntegerDigits</code> are ignored,
1004 * and the <code>int</code> fields such as <code>maximumIntegerDigits</code>
1005 * are used instead.
1006 * </ul>
1007 * When streaming out a <code>NumberFormat</code>, the most recent format
1008 * (corresponding to the highest allowable <code>serialVersionOnStream</code>)
1009 * is always written.
1010 *
1011 * @serial
1012 * @since 1.2
1013 */
1014 private int serialVersionOnStream = currentSerialVersion;
1015
1016 // Removed "implements Cloneable" clause. Needs to update serialization
1017 // ID for backward compatibility.
1018 static final long serialVersionUID = -2308460125733713944L;
1019
1020
1021 //
1022 // class for AttributedCharacterIterator attributes
1023 //
1024 /**
1025 * Defines constants that are used as attribute keys in the
1026 * <code>AttributedCharacterIterator</code> returned
1027 * from <code>NumberFormat.formatToCharacterIterator</code> and as
1028 * field identifiers in <code>FieldPosition</code>.
1029 *
1030 * @since 1.4
1031 */
1032 public static class Field extends Format.Field {
1033
1034 // Proclaim serial compatibility with 1.4 FCS
1035 private static final long serialVersionUID = 7494728892700160890L;
1036
1037 // table of all instances in this class, used by readResolve
1038 private static final Map instanceMap = new HashMap(11);
1039
1040 /**
1041 * Creates a Field instance with the specified
1042 * name.
1043 *
1044 * @param name Name of the attribute
1045 */
1046 protected Field(String name) {
1047 super(name);
1048 if (this.getClass() == NumberFormat.Field.class) {
1049 instanceMap.put(name, this);
1050 }
1051 }
1052
1053 /**
1054 * Resolves instances being deserialized to the predefined constants.
1055 *
1056 * @throws InvalidObjectException if the constant could not be resolved.
1057 * @return resolved NumberFormat.Field constant
1058 */
1059 protected Object readResolve() throws InvalidObjectException {
1060 if (this.getClass() != NumberFormat.Field.class) {
1061 throw new InvalidObjectException("subclass didn't correctly implement readResolve");
1062 }
1063
1064 Object instance = instanceMap.get(getName());
1065 if (instance != null) {
1066 return instance;
1067 } else {
1068 throw new InvalidObjectException("unknown attribute name");
1069 }
1070 }
1071
1072 /**
1073 * Constant identifying the integer field.
1074 */
1075 public static final Field INTEGER = new Field("integer");
1076
1077 /**
1078 * Constant identifying the fraction field.
1079 */
1080 public static final Field FRACTION = new Field("fraction");
1081
1082 /**
1083 * Constant identifying the exponent field.
1084 */
1085 public static final Field EXPONENT = new Field("exponent");
1086
1087 /**
1088 * Constant identifying the decimal separator field.
1089 */
1090 public static final Field DECIMAL_SEPARATOR =
1091 new Field("decimal separator");
1092
1093 /**
1094 * Constant identifying the sign field.
1095 */
1096 public static final Field SIGN = new Field("sign");
1097
1098 /**
1099 * Constant identifying the grouping separator field.
1100 */
1101 public static final Field GROUPING_SEPARATOR =
1102 new Field("grouping separator");
1103
1104 /**
1105 * Constant identifying the exponent symbol field.
1106 */
1107 public static final Field EXPONENT_SYMBOL = new
1108 Field("exponent symbol");
1109
1110 /**
1111 * Constant identifying the percent field.
1112 */
1113 public static final Field PERCENT = new Field("percent");
1114
1115 /**
1116 * Constant identifying the permille field.
1117 */
1118 public static final Field PERMILLE = new Field("per mille");
1119
1120 /**
1121 * Constant identifying the currency field.
1122 */
1123 public static final Field CURRENCY = new Field("currency");
1124
1125 /**
1126 * Constant identifying the exponent sign field.
1127 */
1128 public static final Field EXPONENT_SIGN = new Field("exponent sign");
1129 }
1130
1131 /**
1132 * Obtains a NumberFormat instance from a NumberFormatProvider implementation.
1133 */
1134 private static class NumberFormatGetter
1135 implements LocaleServiceProviderPool.LocalizedObjectGetter<NumberFormatProvider,
1136 NumberFormat> {
1137 private static final NumberFormatGetter INSTANCE = new NumberFormatGetter();
1138
1139 public NumberFormat getObject(NumberFormatProvider numberFormatProvider,
1140 Locale locale,
1141 String key,
1142 Object... params) {
1143 assert params.length == 1;
1144 int choice = (Integer)params[0];
1145
1146 switch (choice) {
1147 case NUMBERSTYLE:
1148 return numberFormatProvider.getNumberInstance(locale);
1149 case PERCENTSTYLE:
1150 return numberFormatProvider.getPercentInstance(locale);
1151 case CURRENCYSTYLE:
1152 return numberFormatProvider.getCurrencyInstance(locale);
1153 case INTEGERSTYLE:
1154 return numberFormatProvider.getIntegerInstance(locale);
1155 default:
1156 assert false : choice;
1157 }
1158
1159 return null;
1160 }
1161 }
1162}
java/pkg/java/text/ParseException.class+0−0

Binary file not shown.

java/pkg/java/text/ParseException.java+78−0
@@ -0,0 +1,78 @@
1/*
2 * Copyright (c) 1996, 1998, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26/*
27 * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
28 * (C) Copyright IBM Corp. 1996 - 1998 - All Rights Reserved
29 *
30 * The original version of this source code and documentation is copyrighted
31 * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
32 * materials are provided under terms of a License Agreement between Taligent
33 * and Sun. This technology is protected by multiple US and International
34 * patents. This notice and attribution to Taligent may not be removed.
35 * Taligent is a registered trademark of Taligent, Inc.
36 *
37 */
38
39package java.text;
40
41/**
42 * Signals that an error has been reached unexpectedly
43 * while parsing.
44 * @see java.lang.Exception
45 * @see java.text.Format
46 * @see java.text.FieldPosition
47 * @author Mark Davis
48 */
49public
50class ParseException extends Exception {
51
52 /**
53 * Constructs a ParseException with the specified detail message and
54 * offset.
55 * A detail message is a String that describes this particular exception.
56 * @param s the detail message
57 * @param errorOffset the position where the error is found while parsing.
58 */
59 public ParseException(String s, int errorOffset) {
60 super(s);
61 this.errorOffset = errorOffset;
62 }
63
64 /**
65 * Returns the position where the error was found.
66 */
67 public int getErrorOffset () {
68 return errorOffset;
69 }
70
71 //============ privates ============
72 /**
73 * The zero-based character offset into the string being parsed at which
74 * the error was found during parsing.
75 * @serial
76 */
77 private int errorOffset;
78}
java/pkg/java/text/ParsePosition.class+0−0

Binary file not shown.

java/pkg/java/text/ParsePosition.java+139−0
@@ -0,0 +1,139 @@
1/*
2 * Copyright (c) 1996, 2002, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26/*
27 * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
28 * (C) Copyright IBM Corp. 1996 - 1998 - All Rights Reserved
29 *
30 * The original version of this source code and documentation is copyrighted
31 * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
32 * materials are provided under terms of a License Agreement between Taligent
33 * and Sun. This technology is protected by multiple US and International
34 * patents. This notice and attribution to Taligent may not be removed.
35 * Taligent is a registered trademark of Taligent, Inc.
36 *
37 */
38
39package java.text;
40
41
42/**
43 * <code>ParsePosition</code> is a simple class used by <code>Format</code>
44 * and its subclasses to keep track of the current position during parsing.
45 * The <code>parseObject</code> method in the various <code>Format</code>
46 * classes requires a <code>ParsePosition</code> object as an argument.
47 *
48 * <p>
49 * By design, as you parse through a string with different formats,
50 * you can use the same <code>ParsePosition</code>, since the index parameter
51 * records the current position.
52 *
53 * @author Mark Davis
54 * @see java.text.Format
55 */
56
57public class ParsePosition {
58
59 /**
60 * Input: the place you start parsing.
61 * <br>Output: position where the parse stopped.
62 * This is designed to be used serially,
63 * with each call setting index up for the next one.
64 */
65 int index = 0;
66 int errorIndex = -1;
67
68 /**
69 * Retrieve the current parse position. On input to a parse method, this
70 * is the index of the character at which parsing will begin; on output, it
71 * is the index of the character following the last character parsed.
72 */
73 public int getIndex() {
74 return index;
75 }
76
77 /**
78 * Set the current parse position.
79 */
80 public void setIndex(int index) {
81 this.index = index;
82 }
83
84 /**
85 * Create a new ParsePosition with the given initial index.
86 */
87 public ParsePosition(int index) {
88 this.index = index;
89 }
90 /**
91 * Set the index at which a parse error occurred. Formatters
92 * should set this before returning an error code from their
93 * parseObject method. The default value is -1 if this is not set.
94 * @since 1.2
95 */
96 public void setErrorIndex(int ei)
97 {
98 errorIndex = ei;
99 }
100
101 /**
102 * Retrieve the index at which an error occurred, or -1 if the
103 * error index has not been set.
104 * @since 1.2
105 */
106 public int getErrorIndex()
107 {
108 return errorIndex;
109 }
110 /**
111 * Overrides equals
112 */
113 public boolean equals(Object obj)
114 {
115 if (obj == null) return false;
116 if (!(obj instanceof ParsePosition))
117 return false;
118 ParsePosition other = (ParsePosition) obj;
119 return (index == other.index && errorIndex == other.errorIndex);
120 }
121
122 /**
123 * Returns a hash code for this ParsePosition.
124 * @return a hash code value for this object
125 */
126 public int hashCode() {
127 return (errorIndex << 16) | index;
128 }
129
130 /**
131 * Return a string representation of this ParsePosition.
132 * @return a string representation of this object
133 */
134 public String toString() {
135 return getClass().getName() +
136 "[index=" + index +
137 ",errorIndex=" + errorIndex + ']';
138 }
139}
java/pkg/java/text/PatternEntry$Parser.class+0−0

Binary file not shown.

java/pkg/java/text/PatternEntry.class+0−0

Binary file not shown.

java/pkg/java/text/PatternEntry.java+306−0
@@ -0,0 +1,306 @@
1/*
2 * Copyright (c) 1996, 2000, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26/*
27 * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
28 * (C) Copyright IBM Corp. 1996, 1997 - All Rights Reserved
29 *
30 * The original version of this source code and documentation is copyrighted
31 * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
32 * materials are provided under terms of a License Agreement between Taligent
33 * and Sun. This technology is protected by multiple US and International
34 * patents. This notice and attribution to Taligent may not be removed.
35 * Taligent is a registered trademark of Taligent, Inc.
36 *
37 */
38
39package java.text;
40
41import java.lang.Character;
42
43/**
44 * Utility class for normalizing and merging patterns for collation.
45 * This is to be used with MergeCollation for adding patterns to an
46 * existing rule table.
47 * @see MergeCollation
48 * @author Mark Davis, Helena Shih
49 */
50
51class PatternEntry {
52 /**
53 * Gets the current extension, quoted
54 */
55 public void appendQuotedExtension(StringBuffer toAddTo) {
56 appendQuoted(extension,toAddTo);
57 }
58
59 /**
60 * Gets the current chars, quoted
61 */
62 public void appendQuotedChars(StringBuffer toAddTo) {
63 appendQuoted(chars,toAddTo);
64 }
65
66 /**
67 * WARNING this is used for searching in a Vector.
68 * Because Vector.indexOf doesn't take a comparator,
69 * this method is ill-defined and ignores strength.
70 */
71 public boolean equals(Object obj) {
72 if (obj == null) return false;
73 PatternEntry other = (PatternEntry) obj;
74 boolean result = chars.equals(other.chars);
75 return result;
76 }
77
78 public int hashCode() {
79 return chars.hashCode();
80 }
81
82 /**
83 * For debugging.
84 */
85 public String toString() {
86 StringBuffer result = new StringBuffer();
87 addToBuffer(result, true, false, null);
88 return result.toString();
89 }
90
91 /**
92 * Gets the strength of the entry.
93 */
94 final int getStrength() {
95 return strength;
96 }
97
98 /**
99 * Gets the expanding characters of the entry.
100 */
101 final String getExtension() {
102 return extension;
103 }
104
105 /**
106 * Gets the core characters of the entry.
107 */
108 final String getChars() {
109 return chars;
110 }
111
112 // ===== privates =====
113
114 void addToBuffer(StringBuffer toAddTo,
115 boolean showExtension,
116 boolean showWhiteSpace,
117 PatternEntry lastEntry)
118 {
119 if (showWhiteSpace && toAddTo.length() > 0)
120 if (strength == Collator.PRIMARY || lastEntry != null)
121 toAddTo.append('\n');
122 else
123 toAddTo.append(' ');
124 if (lastEntry != null) {
125 toAddTo.append('&');
126 if (showWhiteSpace)
127 toAddTo.append(' ');
128 lastEntry.appendQuotedChars(toAddTo);
129 appendQuotedExtension(toAddTo);
130 if (showWhiteSpace)
131 toAddTo.append(' ');
132 }
133 switch (strength) {
134 case Collator.IDENTICAL: toAddTo.append('='); break;
135 case Collator.TERTIARY: toAddTo.append(','); break;
136 case Collator.SECONDARY: toAddTo.append(';'); break;
137 case Collator.PRIMARY: toAddTo.append('<'); break;
138 case RESET: toAddTo.append('&'); break;
139 case UNSET: toAddTo.append('?'); break;
140 }
141 if (showWhiteSpace)
142 toAddTo.append(' ');
143 appendQuoted(chars,toAddTo);
144 if (showExtension && extension.length() != 0) {
145 toAddTo.append('/');
146 appendQuoted(extension,toAddTo);
147 }
148 }
149
150 static void appendQuoted(String chars, StringBuffer toAddTo) {
151 boolean inQuote = false;
152 char ch = chars.charAt(0);
153 if (Character.isSpaceChar(ch)) {
154 inQuote = true;
155 toAddTo.append('\'');
156 } else {
157 if (PatternEntry.isSpecialChar(ch)) {
158 inQuote = true;
159 toAddTo.append('\'');
160 } else {
161 switch (ch) {
162 case 0x0010: case '\f': case '\r':
163 case '\t': case '\n': case '@':
164 inQuote = true;
165 toAddTo.append('\'');
166 break;
167 case '\'':
168 inQuote = true;
169 toAddTo.append('\'');
170 break;
171 default:
172 if (inQuote) {
173 inQuote = false; toAddTo.append('\'');
174 }
175 break;
176 }
177 }
178 }
179 toAddTo.append(chars);
180 if (inQuote)
181 toAddTo.append('\'');
182 }
183
184 //========================================================================
185 // Parsing a pattern into a list of PatternEntries....
186 //========================================================================
187
188 PatternEntry(int strength,
189 StringBuffer chars,
190 StringBuffer extension)
191 {
192 this.strength = strength;
193 this.chars = chars.toString();
194 this.extension = (extension.length() > 0) ? extension.toString()
195 : "";
196 }
197
198 static class Parser {
199 private String pattern;
200 private int i;
201
202 public Parser(String pattern) {
203 this.pattern = pattern;
204 this.i = 0;
205 }
206
207 public PatternEntry next() throws ParseException {
208 int newStrength = UNSET;
209
210 newChars.setLength(0);
211 newExtension.setLength(0);
212
213 boolean inChars = true;
214 boolean inQuote = false;
215 mainLoop:
216 while (i < pattern.length()) {
217 char ch = pattern.charAt(i);
218 if (inQuote) {
219 if (ch == '\'') {
220 inQuote = false;
221 } else {
222 if (newChars.length() == 0) newChars.append(ch);
223 else if (inChars) newChars.append(ch);
224 else newExtension.append(ch);
225 }
226 } else switch (ch) {
227 case '=': if (newStrength != UNSET) break mainLoop;
228 newStrength = Collator.IDENTICAL; break;
229 case ',': if (newStrength != UNSET) break mainLoop;
230 newStrength = Collator.TERTIARY; break;
231 case ';': if (newStrength != UNSET) break mainLoop;
232 newStrength = Collator.SECONDARY; break;
233 case '<': if (newStrength != UNSET) break mainLoop;
234 newStrength = Collator.PRIMARY; break;
235 case '&': if (newStrength != UNSET) break mainLoop;
236 newStrength = RESET; break;
237 case '\t':
238 case '\n':
239 case '\f':
240 case '\r':
241 case ' ': break; // skip whitespace TODO use Character
242 case '/': inChars = false; break;
243 case '\'':
244 inQuote = true;
245 ch = pattern.charAt(++i);
246 if (newChars.length() == 0) newChars.append(ch);
247 else if (inChars) newChars.append(ch);
248 else newExtension.append(ch);
249 break;
250 default:
251 if (newStrength == UNSET) {
252 throw new ParseException
253 ("missing char (=,;<&) : " +
254 pattern.substring(i,
255 (i+10 < pattern.length()) ?
256 i+10 : pattern.length()),
257 i);
258 }
259 if (PatternEntry.isSpecialChar(ch) && (inQuote == false))
260 throw new ParseException
261 ("Unquoted punctuation character : " + Integer.toString(ch, 16), i);
262 if (inChars) {
263 newChars.append(ch);
264 } else {
265 newExtension.append(ch);
266 }
267 break;
268 }
269 i++;
270 }
271 if (newStrength == UNSET)
272 return null;
273 if (newChars.length() == 0) {
274 throw new ParseException
275 ("missing chars (=,;<&): " +
276 pattern.substring(i,
277 (i+10 < pattern.length()) ?
278 i+10 : pattern.length()),
279 i);
280 }
281
282 return new PatternEntry(newStrength, newChars, newExtension);
283 }
284
285 // We re-use these objects in order to improve performance
286 private StringBuffer newChars = new StringBuffer();
287 private StringBuffer newExtension = new StringBuffer();
288
289 }
290
291 static boolean isSpecialChar(char ch) {
292 return ((ch == '\u0020') ||
293 ((ch <= '\u002F') && (ch >= '\u0022')) ||
294 ((ch <= '\u003F') && (ch >= '\u003A')) ||
295 ((ch <= '\u0060') && (ch >= '\u005B')) ||
296 ((ch <= '\u007E') && (ch >= '\u007B')));
297 }
298
299
300 static final int RESET = -2;
301 static final int UNSET = -1;
302
303 int strength = UNSET;
304 String chars = "";
305 String extension = "";
306}
java/pkg/java/text/RBCollationTables$1.class+0−0

Binary file not shown.

java/pkg/java/text/RBCollationTables$BuildAPI.class+0−0

Binary file not shown.

java/pkg/java/text/RBCollationTables.class+0−0

Binary file not shown.

java/pkg/java/text/RBCollationTables.java+303−0
@@ -0,0 +1,303 @@
1/*
2 * Copyright (c) 1999, 2003, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26/*
27 * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
28 * (C) Copyright IBM Corp. 1996-1998 - All Rights Reserved
29 *
30 * The original version of this source code and documentation is copyrighted
31 * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
32 * materials are provided under terms of a License Agreement between Taligent
33 * and Sun. This technology is protected by multiple US and International
34 * patents. This notice and attribution to Taligent may not be removed.
35 * Taligent is a registered trademark of Taligent, Inc.
36 *
37 */
38
39package java.text;
40
41import java.util.Vector;
42import sun.text.UCompactIntArray;
43import sun.text.IntHashtable;
44
45/**
46 * This class contains the static state of a RuleBasedCollator: The various
47 * tables that are used by the collation routines. Several RuleBasedCollators
48 * can share a single RBCollationTables object, easing memory requirements and
49 * improving performance.
50 */
51final class RBCollationTables {
52 //===========================================================================================
53 // The following diagram shows the data structure of the RBCollationTables object.
54 // Suppose we have the rule, where 'o-umlaut' is the unicode char 0x00F6.
55 // "a, A < b, B < c, C, ch, cH, Ch, CH < d, D ... < o, O; 'o-umlaut'/E, 'O-umlaut'/E ...".
56 // What the rule says is, sorts 'ch'ligatures and 'c' only with tertiary difference and
57 // sorts 'o-umlaut' as if it's always expanded with 'e'.
58 //
59 // mapping table contracting list expanding list
60 // (contains all unicode char
61 // entries) ___ ____________ _________________________
62 // ________ +>|_*_|->|'c' |v('c') | +>|v('o')|v('umlaut')|v('e')|
63 // |_\u0001_|-> v('\u0001') | |_:_| |------------| | |-------------------------|
64 // |_\u0002_|-> v('\u0002') | |_:_| |'ch'|v('ch')| | | : |
65 // |____:___| | |_:_| |------------| | |-------------------------|
66 // |____:___| | |'cH'|v('cH')| | | : |
67 // |__'a'___|-> v('a') | |------------| | |-------------------------|
68 // |__'b'___|-> v('b') | |'Ch'|v('Ch')| | | : |
69 // |____:___| | |------------| | |-------------------------|
70 // |____:___| | |'CH'|v('CH')| | | : |
71 // |___'c'__|---------------- ------------ | |-------------------------|
72 // |____:___| | | : |
73 // |o-umlaut|---------------------------------------- |_________________________|
74 // |____:___|
75 //
76 // Noted by Helena Shih on 6/23/97
77 //============================================================================================
78
79 public RBCollationTables(String rules, int decmp) throws ParseException {
80 this.rules = rules;
81
82 RBTableBuilder builder = new RBTableBuilder(new BuildAPI());
83 builder.build(rules, decmp); // this object is filled in through
84 // the BuildAPI object
85 }
86
87 final class BuildAPI {
88 /**
89 * Private constructor. Prevents anyone else besides RBTableBuilder
90 * from gaining direct access to the internals of this class.
91 */
92 private BuildAPI() {
93 }
94
95 /**
96 * This function is used by RBTableBuilder to fill in all the members of this
97 * object. (Effectively, the builder class functions as a "friend" of this
98 * class, but to avoid changing too much of the logic, it carries around "shadow"
99 * copies of all these variables until the end of the build process and then
100 * copies them en masse into the actual tables object once all the construction
101 * logic is complete. This function does that "copying en masse".
102 * @param f2ary The value for frenchSec (the French-secondary flag)
103 * @param swap The value for SE Asian swapping rule
104 * @param map The collator's character-mapping table (the value for mapping)
105 * @param cTbl The collator's contracting-character table (the value for contractTable)
106 * @param eTbl The collator's expanding-character table (the value for expandTable)
107 * @param cFlgs The hash table of characters that participate in contracting-
108 * character sequences (the value for contractFlags)
109 * @param mso The value for maxSecOrder
110 * @param mto The value for maxTerOrder
111 */
112 void fillInTables(boolean f2ary,
113 boolean swap,
114 UCompactIntArray map,
115 Vector cTbl,
116 Vector eTbl,
117 IntHashtable cFlgs,
118 short mso,
119 short mto) {
120 frenchSec = f2ary;
121 seAsianSwapping = swap;
122 mapping = map;
123 contractTable = cTbl;
124 expandTable = eTbl;
125 contractFlags = cFlgs;
126 maxSecOrder = mso;
127 maxTerOrder = mto;
128 }
129 }
130
131 /**
132 * Gets the table-based rules for the collation object.
133 * @return returns the collation rules that the table collation object
134 * was created from.
135 */
136 public String getRules()
137 {
138 return rules;
139 }
140
141 public boolean isFrenchSec() {
142 return frenchSec;
143 }
144
145 public boolean isSEAsianSwapping() {
146 return seAsianSwapping;
147 }
148
149 // ==============================================================
150 // internal (for use by CollationElementIterator)
151 // ==============================================================
152
153 /**
154 * Get the entry of hash table of the contracting string in the collation
155 * table.
156 * @param ch the starting character of the contracting string
157 */
158 Vector getContractValues(int ch)
159 {
160 int index = mapping.elementAt(ch);
161 return getContractValuesImpl(index - CONTRACTCHARINDEX);
162 }
163
164 //get contract values from contractTable by index
165 private Vector getContractValuesImpl(int index)
166 {
167 if (index >= 0)
168 {
169 return (Vector)contractTable.elementAt(index);
170 }
171 else // not found
172 {
173 return null;
174 }
175 }
176
177 /**
178 * Returns true if this character appears anywhere in a contracting
179 * character sequence. (Used by CollationElementIterator.setOffset().)
180 */
181 boolean usedInContractSeq(int c) {
182 return contractFlags.get(c) == 1;
183 }
184
185 /**
186 * Return the maximum length of any expansion sequences that end
187 * with the specified comparison order.
188 *
189 * @param order a collation order returned by previous or next.
190 * @return the maximum length of any expansion seuences ending
191 * with the specified order.
192 *
193 * @see CollationElementIterator#getMaxExpansion
194 */
195 int getMaxExpansion(int order)
196 {
197 int result = 1;
198
199 if (expandTable != null) {
200 // Right now this does a linear search through the entire
201 // expandsion table. If a collator had a large number of expansions,
202 // this could cause a performance problem, but in practise that
203 // rarely happens
204 for (int i = 0; i < expandTable.size(); i++) {
205 int[] valueList = (int [])expandTable.elementAt(i);
206 int length = valueList.length;
207
208 if (length > result && valueList[length-1] == order) {
209 result = length;
210 }
211 }
212 }
213
214 return result;
215 }
216
217 /**
218 * Get the entry of hash table of the expanding string in the collation
219 * table.
220 * @param idx the index of the expanding string value list
221 */
222 final int[] getExpandValueList(int order) {
223 return (int[])expandTable.elementAt(order - EXPANDCHARINDEX);
224 }
225
226 /**
227 * Get the comarison order of a character from the collation table.
228 * @return the comparison order of a character.
229 */
230 int getUnicodeOrder(int ch)
231 {
232 return mapping.elementAt(ch);
233 }
234
235 short getMaxSecOrder() {
236 return maxSecOrder;
237 }
238
239 short getMaxTerOrder() {
240 return maxTerOrder;
241 }
242
243 /**
244 * Reverse a string.
245 */
246 //shemran/Note: this is used for secondary order value reverse, no
247 // need to consider supplementary pair.
248 static void reverse (StringBuffer result, int from, int to)
249 {
250 int i = from;
251 char swap;
252
253 int j = to - 1;
254 while (i < j) {
255 swap = result.charAt(i);
256 result.setCharAt(i, result.charAt(j));
257 result.setCharAt(j, swap);
258 i++;
259 j--;
260 }
261 }
262
263 final static int getEntry(Vector list, String name, boolean fwd) {
264 for (int i = 0; i < list.size(); i++) {
265 EntryPair pair = (EntryPair)list.elementAt(i);
266 if (pair.fwd == fwd && pair.entryName.equals(name)) {
267 return i;
268 }
269 }
270 return UNMAPPED;
271 }
272
273 // ==============================================================
274 // constants
275 // ==============================================================
276 //sherman/Todo: is the value big enough?????
277 final static int EXPANDCHARINDEX = 0x7E000000; // Expand index follows
278 final static int CONTRACTCHARINDEX = 0x7F000000; // contract indexes follow
279 final static int UNMAPPED = 0xFFFFFFFF;
280
281 final static int PRIMARYORDERMASK = 0xffff0000;
282 final static int SECONDARYORDERMASK = 0x0000ff00;
283 final static int TERTIARYORDERMASK = 0x000000ff;
284 final static int PRIMARYDIFFERENCEONLY = 0xffff0000;
285 final static int SECONDARYDIFFERENCEONLY = 0xffffff00;
286 final static int PRIMARYORDERSHIFT = 16;
287 final static int SECONDARYORDERSHIFT = 8;
288
289 // ==============================================================
290 // instance variables
291 // ==============================================================
292 private String rules = null;
293 private boolean frenchSec = false;
294 private boolean seAsianSwapping = false;
295
296 private UCompactIntArray mapping = null;
297 private Vector contractTable = null;
298 private Vector expandTable = null;
299 private IntHashtable contractFlags = null;
300
301 private short maxSecOrder = 0;
302 private short maxTerOrder = 0;
303}
java/pkg/java/text/RBTableBuilder.class+0−0

Binary file not shown.

java/pkg/java/text/RBTableBuilder.java+618−0
@@ -0,0 +1,618 @@
1/*
2 * Copyright (c) 1999, 2005, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26/*
27 * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
28 * (C) Copyright IBM Corp. 1996-1998 - All Rights Reserved
29 *
30 * The original version of this source code and documentation is copyrighted
31 * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
32 * materials are provided under terms of a License Agreement between Taligent
33 * and Sun. This technology is protected by multiple US and International
34 * patents. This notice and attribution to Taligent may not be removed.
35 * Taligent is a registered trademark of Taligent, Inc.
36 *
37 */
38
39package java.text;
40
41import java.util.Vector;
42import sun.text.UCompactIntArray;
43import sun.text.IntHashtable;
44import sun.text.ComposedCharIter;
45import sun.text.CollatorUtilities;
46import sun.text.normalizer.NormalizerImpl;
47
48/**
49 * This class contains all the code to parse a RuleBasedCollator pattern
50 * and build a RBCollationTables object from it. A particular instance
51 * of tis class exists only during the actual build process-- once an
52 * RBCollationTables object has been built, the RBTableBuilder object
53 * goes away. This object carries all of the state which is only needed
54 * during the build process, plus a "shadow" copy of all of the state
55 * that will go into the tables object itself. This object communicates
56 * with RBCollationTables through a separate class, RBCollationTables.BuildAPI,
57 * this is an inner class of RBCollationTables and provides a separate
58 * private API for communication with RBTableBuilder.
59 * This class isn't just an inner class of RBCollationTables itself because
60 * of its large size. For source-code readability, it seemed better for the
61 * builder to have its own source file.
62 */
63final class RBTableBuilder {
64
65 public RBTableBuilder(RBCollationTables.BuildAPI tables) {
66 this.tables = tables;
67 }
68
69 /**
70 * Create a table-based collation object with the given rules.
71 * This is the main function that actually builds the tables and
72 * stores them back in the RBCollationTables object. It is called
73 * ONLY by the RBCollationTables constructor.
74 * @see java.util.RuleBasedCollator#RuleBasedCollator
75 * @exception ParseException If the rules format is incorrect.
76 */
77
78 public void build(String pattern, int decmp) throws ParseException
79 {
80 boolean isSource = true;
81 int i = 0;
82 String expChars;
83 String groupChars;
84 if (pattern.length() == 0)
85 throw new ParseException("Build rules empty.", 0);
86
87 // This array maps Unicode characters to their collation ordering
88 mapping = new UCompactIntArray((int)RBCollationTables.UNMAPPED);
89 // Normalize the build rules. Find occurances of all decomposed characters
90 // and normalize the rules before feeding into the builder. By "normalize",
91 // we mean that all precomposed Unicode characters must be converted into
92 // a base character and one or more combining characters (such as accents).
93 // When there are multiple combining characters attached to a base character,
94 // the combining characters must be in their canonical order
95 //
96 // sherman/Note:
97 //(1)decmp will be NO_DECOMPOSITION only in ko locale to prevent decompose
98 //hangual syllables to jamos, so we can actually just call decompose with
99 //normalizer's IGNORE_HANGUL option turned on
100 //
101 //(2)just call the "special version" in NormalizerImpl directly
102 //pattern = Normalizer.decompose(pattern, false, Normalizer.IGNORE_HANGUL, true);
103 //
104 //Normalizer.Mode mode = CollatorUtilities.toNormalizerMode(decmp);
105 //pattern = Normalizer.normalize(pattern, mode, 0, true);
106
107 pattern = NormalizerImpl.canonicalDecomposeWithSingleQuotation(pattern);
108
109 // Build the merged collation entries
110 // Since rules can be specified in any order in the string
111 // (e.g. "c , C < d , D < e , E .... C < CH")
112 // this splits all of the rules in the string out into separate
113 // objects and then sorts them. In the above example, it merges the
114 // "C < CH" rule in just before the "C < D" rule.
115 //
116
117 mPattern = new MergeCollation(pattern);
118
119 int order = 0;
120
121 // Now walk though each entry and add it to my own tables
122 for (i = 0; i < mPattern.getCount(); ++i)
123 {
124 PatternEntry entry = mPattern.getItemAt(i);
125 if (entry != null) {
126 groupChars = entry.getChars();
127 if (groupChars.length() > 1) {
128 switch(groupChars.charAt(groupChars.length()-1)) {
129 case '@':
130 frenchSec = true;
131 groupChars = groupChars.substring(0, groupChars.length()-1);
132 break;
133 case '!':
134 seAsianSwapping = true;
135 groupChars = groupChars.substring(0, groupChars.length()-1);
136 break;
137 }
138 }
139
140 order = increment(entry.getStrength(), order);
141 expChars = entry.getExtension();
142
143 if (expChars.length() != 0) {
144 addExpandOrder(groupChars, expChars, order);
145 } else if (groupChars.length() > 1) {
146 char ch = groupChars.charAt(0);
147 if (Character.isHighSurrogate(ch) && groupChars.length() == 2) {
148 addOrder(Character.toCodePoint(ch, groupChars.charAt(1)), order);
149 } else {
150 addContractOrder(groupChars, order);
151 }
152 } else {
153 char ch = groupChars.charAt(0);
154 addOrder(ch, order);
155 }
156 }
157 }
158 addComposedChars();
159
160 commit();
161 mapping.compact();
162 /*
163 System.out.println("mappingSize=" + mapping.getKSize());
164 for (int j = 0; j < 0xffff; j++) {
165 int value = mapping.elementAt(j);
166 if (value != RBCollationTables.UNMAPPED)
167 System.out.println("index=" + Integer.toString(j, 16)
168 + ", value=" + Integer.toString(value, 16));
169 }
170 */
171 tables.fillInTables(frenchSec, seAsianSwapping, mapping, contractTable, expandTable,
172 contractFlags, maxSecOrder, maxTerOrder);
173 }
174
175 /** Add expanding entries for pre-composed unicode characters so that this
176 * collator can be used reasonably well with decomposition turned off.
177 */
178 private void addComposedChars() throws ParseException {
179 // Iterate through all of the pre-composed characters in Unicode
180 ComposedCharIter iter = new ComposedCharIter();
181 int c;
182 while ((c = iter.next()) != ComposedCharIter.DONE) {
183 if (getCharOrder(c) == RBCollationTables.UNMAPPED) {
184 //
185 // We don't already have an ordering for this pre-composed character.
186 //
187 // First, see if the decomposed string is already in our
188 // tables as a single contracting-string ordering.
189 // If so, just map the precomposed character to that order.
190 //
191 // TODO: What we should really be doing here is trying to find the
192 // longest initial substring of the decomposition that is present
193 // in the tables as a contracting character sequence, and find its
194 // ordering. Then do this recursively with the remaining chars
195 // so that we build a list of orderings, and add that list to
196 // the expansion table.
197 // That would be more correct but also significantly slower, so
198 // I'm not totally sure it's worth doing.
199 //
200 String s = iter.decomposition();
201
202 //sherman/Note: if this is 1 character decomposed string, the
203 //only thing need to do is to check if this decomposed character
204 //has an entry in our order table, this order is not necessary
205 //to be a contraction order, if it does have one, add an entry
206 //for the precomposed character by using the same order, the
207 //previous impl unnecessarily adds a single character expansion
208 //entry.
209 if (s.length() == 1) {
210 int order = getCharOrder(s.charAt(0));
211 if (order != RBCollationTables.UNMAPPED) {
212 addOrder(c, order);
213 }
214 continue;
215 } else if (s.length() == 2) {
216 char ch0 = s.charAt(0);
217 if (Character.isHighSurrogate(ch0)) {
218 int order = getCharOrder(s.codePointAt(0));
219 if (order != RBCollationTables.UNMAPPED) {
220 addOrder(c, order);
221 }
222 continue;
223 }
224 }
225 int contractOrder = getContractOrder(s);
226 if (contractOrder != RBCollationTables.UNMAPPED) {
227 addOrder(c, contractOrder);
228 } else {
229 //
230 // We don't have a contracting ordering for the entire string
231 // that results from the decomposition, but if we have orders
232 // for each individual character, we can add an expanding
233 // table entry for the pre-composed character
234 //
235 boolean allThere = true;
236 for (int i = 0; i < s.length(); i++) {
237 if (getCharOrder(s.charAt(i)) == RBCollationTables.UNMAPPED) {
238 allThere = false;
239 break;
240 }
241 }
242 if (allThere) {
243 addExpandOrder(c, s, RBCollationTables.UNMAPPED);
244 }
245 }
246 }
247 }
248 }
249
250 /**
251 * Look up for unmapped values in the expanded character table.
252 *
253 * When the expanding character tables are built by addExpandOrder,
254 * it doesn't know what the final ordering of each character
255 * in the expansion will be. Instead, it just puts the raw character
256 * code into the table, adding CHARINDEX as a flag. Now that we've
257 * finished building the mapping table, we can go back and look up
258 * that character to see what its real collation order is and
259 * stick that into the expansion table. That lets us avoid doing
260 * a two-stage lookup later.
261 */
262 private final void commit()
263 {
264 if (expandTable != null) {
265 for (int i = 0; i < expandTable.size(); i++) {
266 int[] valueList = (int [])expandTable.elementAt(i);
267 for (int j = 0; j < valueList.length; j++) {
268 int order = valueList[j];
269 if (order < RBCollationTables.EXPANDCHARINDEX && order > CHARINDEX) {
270 // found a expanding character that isn't filled in yet
271 int ch = order - CHARINDEX;
272
273 // Get the real values for the non-filled entry
274 int realValue = getCharOrder(ch);
275
276 if (realValue == RBCollationTables.UNMAPPED) {
277 // The real value is still unmapped, maybe it's ignorable
278 valueList[j] = IGNORABLEMASK & ch;
279 } else {
280 // just fill in the value
281 valueList[j] = realValue;
282 }
283 }
284 }
285 }
286 }
287 }
288 /**
289 * Increment of the last order based on the comparison level.
290 */
291 private final int increment(int aStrength, int lastValue)
292 {
293 switch(aStrength)
294 {
295 case Collator.PRIMARY:
296 // increment priamry order and mask off secondary and tertiary difference
297 lastValue += PRIMARYORDERINCREMENT;
298 lastValue &= RBCollationTables.PRIMARYORDERMASK;
299 isOverIgnore = true;
300 break;
301 case Collator.SECONDARY:
302 // increment secondary order and mask off tertiary difference
303 lastValue += SECONDARYORDERINCREMENT;
304 lastValue &= RBCollationTables.SECONDARYDIFFERENCEONLY;
305 // record max # of ignorable chars with secondary difference
306 if (!isOverIgnore)
307 maxSecOrder++;
308 break;
309 case Collator.TERTIARY:
310 // increment tertiary order
311 lastValue += TERTIARYORDERINCREMENT;
312 // record max # of ignorable chars with tertiary difference
313 if (!isOverIgnore)
314 maxTerOrder++;
315 break;
316 }
317 return lastValue;
318 }
319
320 /**
321 * Adds a character and its designated order into the collation table.
322 */
323 private final void addOrder(int ch, int anOrder)
324 {
325 // See if the char already has an order in the mapping table
326 int order = mapping.elementAt(ch);
327
328 if (order >= RBCollationTables.CONTRACTCHARINDEX) {
329 // There's already an entry for this character that points to a contracting
330 // character table. Instead of adding the character directly to the mapping
331 // table, we must add it to the contract table instead.
332 int length = 1;
333 if (Character.isSupplementaryCodePoint(ch)) {
334 length = Character.toChars(ch, keyBuf, 0);
335 } else {
336 keyBuf[0] = (char)ch;
337 }
338 addContractOrder(new String(keyBuf, 0, length), anOrder);
339 } else {
340 // add the entry to the mapping table,
341 // the same later entry replaces the previous one
342 mapping.setElementAt(ch, anOrder);
343 }
344 }
345
346 private final void addContractOrder(String groupChars, int anOrder) {
347 addContractOrder(groupChars, anOrder, true);
348 }
349
350 /**
351 * Adds the contracting string into the collation table.
352 */
353 private final void addContractOrder(String groupChars, int anOrder,
354 boolean fwd)
355 {
356 if (contractTable == null) {
357 contractTable = new Vector(INITIALTABLESIZE);
358 }
359
360 //initial character
361 int ch = groupChars.codePointAt(0);
362 /*
363 char ch0 = groupChars.charAt(0);
364 int ch = Character.isHighSurrogate(ch0)?
365 Character.toCodePoint(ch0, groupChars.charAt(1)):ch0;
366 */
367 // See if the initial character of the string already has a contract table.
368 int entry = mapping.elementAt(ch);
369 Vector entryTable = getContractValuesImpl(entry - RBCollationTables.CONTRACTCHARINDEX);
370
371 if (entryTable == null) {
372 // We need to create a new table of contract entries for this base char
373 int tableIndex = RBCollationTables.CONTRACTCHARINDEX + contractTable.size();
374 entryTable = new Vector(INITIALTABLESIZE);
375 contractTable.addElement(entryTable);
376
377 // Add the initial character's current ordering first. then
378 // update its mapping to point to this contract table
379 entryTable.addElement(new EntryPair(groupChars.substring(0,Character.charCount(ch)), entry));
380 mapping.setElementAt(ch, tableIndex);
381 }
382
383 // Now add (or replace) this string in the table
384 int index = RBCollationTables.getEntry(entryTable, groupChars, fwd);
385 if (index != RBCollationTables.UNMAPPED) {
386 EntryPair pair = (EntryPair) entryTable.elementAt(index);
387 pair.value = anOrder;
388 } else {
389 EntryPair pair = (EntryPair)entryTable.lastElement();
390
391 // NOTE: This little bit of logic is here to speed CollationElementIterator
392 // .nextContractChar(). This code ensures that the longest sequence in
393 // this list is always the _last_ one in the list. This keeps
394 // nextContractChar() from having to search the entire list for the longest
395 // sequence.
396 if (groupChars.length() > pair.entryName.length()) {
397 entryTable.addElement(new EntryPair(groupChars, anOrder, fwd));
398 } else {
399 entryTable.insertElementAt(new EntryPair(groupChars, anOrder,
400 fwd), entryTable.size() - 1);
401 }
402 }
403
404 // If this was a forward mapping for a contracting string, also add a
405 // reverse mapping for it, so that CollationElementIterator.previous
406 // can work right
407 if (fwd && groupChars.length() > 1) {
408 addContractFlags(groupChars);
409 addContractOrder(new StringBuffer(groupChars).reverse().toString(),
410 anOrder, false);
411 }
412 }
413
414 /**
415 * If the given string has been specified as a contracting string
416 * in this collation table, return its ordering.
417 * Otherwise return UNMAPPED.
418 */
419 private int getContractOrder(String groupChars)
420 {
421 int result = RBCollationTables.UNMAPPED;
422 if (contractTable != null) {
423 int ch = groupChars.codePointAt(0);
424 /*
425 char ch0 = groupChars.charAt(0);
426 int ch = Character.isHighSurrogate(ch0)?
427 Character.toCodePoint(ch0, groupChars.charAt(1)):ch0;
428 */
429 Vector entryTable = getContractValues(ch);
430 if (entryTable != null) {
431 int index = RBCollationTables.getEntry(entryTable, groupChars, true);
432 if (index != RBCollationTables.UNMAPPED) {
433 EntryPair pair = (EntryPair) entryTable.elementAt(index);
434 result = pair.value;
435 }
436 }
437 }
438 return result;
439 }
440
441 private final int getCharOrder(int ch) {
442 int order = mapping.elementAt(ch);
443
444 if (order >= RBCollationTables.CONTRACTCHARINDEX) {
445 Vector groupList = getContractValuesImpl(order - RBCollationTables.CONTRACTCHARINDEX);
446 EntryPair pair = (EntryPair)groupList.firstElement();
447 order = pair.value;
448 }
449 return order;
450 }
451
452 /**
453 * Get the entry of hash table of the contracting string in the collation
454 * table.
455 * @param ch the starting character of the contracting string
456 */
457 private Vector getContractValues(int ch)
458 {
459 int index = mapping.elementAt(ch);
460 return getContractValuesImpl(index - RBCollationTables.CONTRACTCHARINDEX);
461 }
462
463 private Vector getContractValuesImpl(int index)
464 {
465 if (index >= 0)
466 {
467 return (Vector)contractTable.elementAt(index);
468 }
469 else // not found
470 {
471 return null;
472 }
473 }
474
475 /**
476 * Adds the expanding string into the collation table.
477 */
478 private final void addExpandOrder(String contractChars,
479 String expandChars,
480 int anOrder) throws ParseException
481 {
482 // Create an expansion table entry
483 int tableIndex = addExpansion(anOrder, expandChars);
484
485 // And add its index into the main mapping table
486 if (contractChars.length() > 1) {
487 char ch = contractChars.charAt(0);
488 if (Character.isHighSurrogate(ch) && contractChars.length() == 2) {
489 char ch2 = contractChars.charAt(1);
490 if (Character.isLowSurrogate(ch2)) {
491 //only add into table when it is a legal surrogate
492 addOrder(Character.toCodePoint(ch, ch2), tableIndex);
493 }
494 } else {
495 addContractOrder(contractChars, tableIndex);
496 }
497 } else {
498 addOrder(contractChars.charAt(0), tableIndex);
499 }
500 }
501
502 private final void addExpandOrder(int ch, String expandChars, int anOrder)
503 throws ParseException
504 {
505 int tableIndex = addExpansion(anOrder, expandChars);
506 addOrder(ch, tableIndex);
507 }
508
509 /**
510 * Create a new entry in the expansion table that contains the orderings
511 * for the given characers. If anOrder is valid, it is added to the
512 * beginning of the expanded list of orders.
513 */
514 private int addExpansion(int anOrder, String expandChars) {
515 if (expandTable == null) {
516 expandTable = new Vector(INITIALTABLESIZE);
517 }
518
519 // If anOrder is valid, we want to add it at the beginning of the list
520 int offset = (anOrder == RBCollationTables.UNMAPPED) ? 0 : 1;
521
522 int[] valueList = new int[expandChars.length() + offset];
523 if (offset == 1) {
524 valueList[0] = anOrder;
525 }
526
527 int j = offset;
528 for (int i = 0; i < expandChars.length(); i++) {
529 char ch0 = expandChars.charAt(i);
530 char ch1;
531 int ch;
532 if (Character.isHighSurrogate(ch0)) {
533 if (++i == expandChars.length() ||
534 !Character.isLowSurrogate(ch1=expandChars.charAt(i))) {
535 //ether we are missing the low surrogate or the next char
536 //is not a legal low surrogate, so stop loop
537 break;
538 }
539 ch = Character.toCodePoint(ch0, ch1);
540
541 } else {
542 ch = ch0;
543 }
544
545 int mapValue = getCharOrder(ch);
546
547 if (mapValue != RBCollationTables.UNMAPPED) {
548 valueList[j++] = mapValue;
549 } else {
550 // can't find it in the table, will be filled in by commit().
551 valueList[j++] = CHARINDEX + ch;
552 }
553 }
554 if (j < valueList.length) {
555 //we had at least one supplementary character, the size of valueList
556 //is bigger than it really needs...
557 int[] tmpBuf = new int[j];
558 while (--j >= 0) {
559 tmpBuf[j] = valueList[j];
560 }
561 valueList = tmpBuf;
562 }
563 // Add the expanding char list into the expansion table.
564 int tableIndex = RBCollationTables.EXPANDCHARINDEX + expandTable.size();
565 expandTable.addElement(valueList);
566
567 return tableIndex;
568 }
569
570 private void addContractFlags(String chars) {
571 char c0;
572 int c;
573 int len = chars.length();
574 for (int i = 0; i < len; i++) {
575 c0 = chars.charAt(i);
576 c = Character.isHighSurrogate(c0)
577 ?Character.toCodePoint(c0, chars.charAt(++i))
578 :c0;
579 contractFlags.put(c, 1);
580 }
581 }
582
583 // ==============================================================
584 // constants
585 // ==============================================================
586 final static int CHARINDEX = 0x70000000; // need look up in .commit()
587
588 private final static int IGNORABLEMASK = 0x0000ffff;
589 private final static int PRIMARYORDERINCREMENT = 0x00010000;
590 private final static int SECONDARYORDERINCREMENT = 0x00000100;
591 private final static int TERTIARYORDERINCREMENT = 0x00000001;
592 private final static int INITIALTABLESIZE = 20;
593 private final static int MAXKEYSIZE = 5;
594
595 // ==============================================================
596 // instance variables
597 // ==============================================================
598
599 // variables used by the build process
600 private RBCollationTables.BuildAPI tables = null;
601 private MergeCollation mPattern = null;
602 private boolean isOverIgnore = false;
603 private char[] keyBuf = new char[MAXKEYSIZE];
604 private IntHashtable contractFlags = new IntHashtable(100);
605
606 // "shadow" copies of the instance variables in RBCollationTables
607 // (the values in these variables are copied back into RBCollationTables
608 // at the end of the build process)
609 private boolean frenchSec = false;
610 private boolean seAsianSwapping = false;
611
612 private UCompactIntArray mapping = null;
613 private Vector contractTable = null;
614 private Vector expandTable = null;
615
616 private short maxSecOrder = 0;
617 private short maxTerOrder = 0;
618}
java/pkg/java/text/RuleBasedBreakIterator$1.class+0−0

Binary file not shown.

java/pkg/java/text/RuleBasedBreakIterator$SafeCharIterator.class+0−0

Binary file not shown.

java/pkg/java/text/RuleBasedBreakIterator.class+0−0

Binary file not shown.

java/pkg/java/text/RuleBasedBreakIterator.java+1148−0
@@ -0,0 +1,1148 @@
1/*
2 * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26/*
27 *
28 * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
29 * (C) Copyright IBM Corp. 1996 - 2002 - All Rights Reserved
30 *
31 * The original version of this source code and documentation
32 * is copyrighted and owned by Taligent, Inc., a wholly-owned
33 * subsidiary of IBM. These materials are provided under terms
34 * of a License Agreement between Taligent and Sun. This technology
35 * is protected by multiple US and International patents.
36 *
37 * This notice and attribution to Taligent may not be removed.
38 * Taligent is a registered trademark of Taligent, Inc.
39 */
40
41package java.text;
42
43import java.io.BufferedInputStream;
44import java.io.IOException;
45import java.security.AccessController;
46import java.security.PrivilegedActionException;
47import java.security.PrivilegedExceptionAction;
48import java.util.Vector;
49import java.util.Stack;
50import java.util.Hashtable;
51import java.util.Enumeration;
52import java.util.MissingResourceException;
53import java.text.CharacterIterator;
54import java.text.StringCharacterIterator;
55import sun.text.CompactByteArray;
56import sun.text.SupplementaryCharacterData;
57
58/**
59 * <p>A subclass of BreakIterator whose behavior is specified using a list of rules.</p>
60 *
61 * <p>There are two kinds of rules, which are separated by semicolons: <i>substitutions</i>
62 * and <i>regular expressions.</i></p>
63 *
64 * <p>A substitution rule defines a name that can be used in place of an expression. It
65 * consists of a name, which is a string of characters contained in angle brackets, an equals
66 * sign, and an expression. (There can be no whitespace on either side of the equals sign.)
67 * To keep its syntactic meaning intact, the expression must be enclosed in parentheses or
68 * square brackets. A substitution is visible after its definition, and is filled in using
69 * simple textual substitution. Substitution definitions can contain other substitutions, as
70 * long as those substitutions have been defined first. Substitutions are generally used to
71 * make the regular expressions (which can get quite complex) shorted and easier to read.
72 * They typically define either character categories or commonly-used subexpressions.</p>
73 *
74 * <p>There is one special substitution.&nbsp; If the description defines a substitution
75 * called &quot;&lt;ignore&gt;&quot;, the expression must be a [] expression, and the
76 * expression defines a set of characters (the &quot;<em>ignore characters</em>&quot;) that
77 * will be transparent to the BreakIterator.&nbsp; A sequence of characters will break the
78 * same way it would if any ignore characters it contains are taken out.&nbsp; Break
79 * positions never occur befoer ignore characters.</p>
80 *
81 * <p>A regular expression uses a subset of the normal Unix regular-expression syntax, and
82 * defines a sequence of characters to be kept together. With one significant exception, the
83 * iterator uses a longest-possible-match algorithm when matching text to regular
84 * expressions. The iterator also treats descriptions containing multiple regular expressions
85 * as if they were ORed together (i.e., as if they were separated by |).</p>
86 *
87 * <p>The special characters recognized by the regular-expression parser are as follows:</p>
88 *
89 * <blockquote>
90 * <table border="1" width="100%">
91 * <tr>
92 * <td width="6%">*</td>
93 * <td width="94%">Specifies that the expression preceding the asterisk may occur any number
94 * of times (including not at all).</td>
95 * </tr>
96 * <tr>
97 * <td width="6%">{}</td>
98 * <td width="94%">Encloses a sequence of characters that is optional.</td>
99 * </tr>
100 * <tr>
101 * <td width="6%">()</td>
102 * <td width="94%">Encloses a sequence of characters.&nbsp; If followed by *, the sequence
103 * repeats.&nbsp; Otherwise, the parentheses are just a grouping device and a way to delimit
104 * the ends of expressions containing |.</td>
105 * </tr>
106 * <tr>
107 * <td width="6%">|</td>
108 * <td width="94%">Separates two alternative sequences of characters.&nbsp; Either one
109 * sequence or the other, but not both, matches this expression.&nbsp; The | character can
110 * only occur inside ().</td>
111 * </tr>
112 * <tr>
113 * <td width="6%">.</td>
114 * <td width="94%">Matches any character.</td>
115 * </tr>
116 * <tr>
117 * <td width="6%">*?</td>
118 * <td width="94%">Specifies a non-greedy asterisk.&nbsp; *? works the same way as *, except
119 * when there is overlap between the last group of characters in the expression preceding the
120 * * and the first group of characters following the *.&nbsp; When there is this kind of
121 * overlap, * will match the longest sequence of characters that match the expression before
122 * the *, and *? will match the shortest sequence of characters matching the expression
123 * before the *?.&nbsp; For example, if you have &quot;xxyxyyyxyxyxxyxyxyy&quot; in the text,
124 * &quot;x[xy]*x&quot; will match through to the last x (i.e., &quot;<strong>xxyxyyyxyxyxxyxyx</strong>yy&quot;,
125 * but &quot;x[xy]*?x&quot; will only match the first two xes (&quot;<strong>xx</strong>yxyyyxyxyxxyxyxyy&quot;).</td>
126 * </tr>
127 * <tr>
128 * <td width="6%">[]</td>
129 * <td width="94%">Specifies a group of alternative characters.&nbsp; A [] expression will
130 * match any single character that is specified in the [] expression.&nbsp; For more on the
131 * syntax of [] expressions, see below.</td>
132 * </tr>
133 * <tr>
134 * <td width="6%">/</td>
135 * <td width="94%">Specifies where the break position should go if text matches this
136 * expression.&nbsp; (e.g., &quot;[a-z]&#42;/[:Zs:]*[1-0]&quot; will match if the iterator sees a run
137 * of letters, followed by a run of whitespace, followed by a digit, but the break position
138 * will actually go before the whitespace).&nbsp; Expressions that don't contain / put the
139 * break position at the end of the matching text.</td>
140 * </tr>
141 * <tr>
142 * <td width="6%">\</td>
143 * <td width="94%">Escape character.&nbsp; The \ itself is ignored, but causes the next
144 * character to be treated as literal character.&nbsp; This has no effect for many
145 * characters, but for the characters listed above, this deprives them of their special
146 * meaning.&nbsp; (There are no special escape sequences for Unicode characters, or tabs and
147 * newlines; these are all handled by a higher-level protocol.&nbsp; In a Java string,
148 * &quot;\n&quot; will be converted to a literal newline character by the time the
149 * regular-expression parser sees it.&nbsp; Of course, this means that \ sequences that are
150 * visible to the regexp parser must be written as \\ when inside a Java string.)&nbsp; All
151 * characters in the ASCII range except for letters, digits, and control characters are
152 * reserved characters to the parser and must be preceded by \ even if they currently don't
153 * mean anything.</td>
154 * </tr>
155 * <tr>
156 * <td width="6%">!</td>
157 * <td width="94%">If ! appears at the beginning of a regular expression, it tells the regexp
158 * parser that this expression specifies the backwards-iteration behavior of the iterator,
159 * and not its normal iteration behavior.&nbsp; This is generally only used in situations
160 * where the automatically-generated backwards-iteration brhavior doesn't produce
161 * satisfactory results and must be supplemented with extra client-specified rules.</td>
162 * </tr>
163 * <tr>
164 * <td width="6%"><em>(all others)</em></td>
165 * <td width="94%">All other characters are treated as literal characters, which must match
166 * the corresponding character(s) in the text exactly.</td>
167 * </tr>
168 * </table>
169 * </blockquote>
170 *
171 * <p>Within a [] expression, a number of other special characters can be used to specify
172 * groups of characters:</p>
173 *
174 * <blockquote>
175 * <table border="1" width="100%">
176 * <tr>
177 * <td width="6%">-</td>
178 * <td width="94%">Specifies a range of matching characters.&nbsp; For example
179 * &quot;[a-p]&quot; matches all lowercase Latin letters from a to p (inclusive).&nbsp; The -
180 * sign specifies ranges of continuous Unicode numeric values, not ranges of characters in a
181 * language's alphabetical order: &quot;[a-z]&quot; doesn't include capital letters, nor does
182 * it include accented letters such as a-umlaut.</td>
183 * </tr>
184 * <tr>
185 * <td width="6%">::</td>
186 * <td width="94%">A pair of colons containing a one- or two-letter code matches all
187 * characters in the corresponding Unicode category.&nbsp; The two-letter codes are the same
188 * as the two-letter codes in the Unicode database (for example, &quot;[:Sc::Sm:]&quot;
189 * matches all currency symbols and all math symbols).&nbsp; Specifying a one-letter code is
190 * the same as specifying all two-letter codes that begin with that letter (for example,
191 * &quot;[:L:]&quot; matches all letters, and is equivalent to
192 * &quot;[:Lu::Ll::Lo::Lm::Lt:]&quot;).&nbsp; Anything other than a valid two-letter Unicode
193 * category code or a single letter that begins a Unicode category code is illegal within
194 * colons.</td>
195 * </tr>
196 * <tr>
197 * <td width="6%">[]</td>
198 * <td width="94%">[] expressions can nest.&nbsp; This has no effect, except when used in
199 * conjunction with the ^ token.</td>
200 * </tr>
201 * <tr>
202 * <td width="6%">^</td>
203 * <td width="94%">Excludes the character (or the characters in the [] expression) following
204 * it from the group of characters.&nbsp; For example, &quot;[a-z^p]&quot; matches all Latin
205 * lowercase letters except p.&nbsp; &quot;[:L:^[&#92;u4e00-&#92;u9fff]]&quot; matches all letters
206 * except the Han ideographs.</td>
207 * </tr>
208 * <tr>
209 * <td width="6%"><em>(all others)</em></td>
210 * <td width="94%">All other characters are treated as literal characters.&nbsp; (For
211 * example, &quot;[aeiou]&quot; specifies just the letters a, e, i, o, and u.)</td>
212 * </tr>
213 * </table>
214 * </blockquote>
215 *
216 * <p>For a more complete explanation, see <a
217 * href="http://www.ibm.com/java/education/boundaries/boundaries.html">http://www.ibm.com/java/education/boundaries/boundaries.html</a>.
218 * &nbsp; For examples, see the resource data (which is annotated).</p>
219 *
220 * @author Richard Gillam
221 */
222class RuleBasedBreakIterator extends BreakIterator {
223
224 /**
225 * A token used as a character-category value to identify ignore characters
226 */
227 protected static final byte IGNORE = -1;
228
229 /**
230 * The state number of the starting state
231 */
232 private static final short START_STATE = 1;
233
234 /**
235 * The state-transition value indicating "stop"
236 */
237 private static final short STOP_STATE = 0;
238
239 /**
240 * Magic number for the BreakIterator data file format.
241 */
242 static final byte[] LABEL = {
243 (byte)'B', (byte)'I', (byte)'d', (byte)'a', (byte)'t', (byte)'a',
244 (byte)'\0'
245 };
246 static final int LABEL_LENGTH = LABEL.length;
247
248 /**
249 * Version number of the dictionary that was read in.
250 */
251 static final byte supportedVersion = 1;
252
253 /**
254 * Header size in byte count
255 */
256 private static final int HEADER_LENGTH = 36;
257
258 /**
259 * An array length of indices for BMP characters
260 */
261 private static final int BMP_INDICES_LENGTH = 512;
262
263 /**
264 * Tables that indexes from character values to character category numbers
265 */
266 private CompactByteArray charCategoryTable = null;
267 private SupplementaryCharacterData supplementaryCharCategoryTable = null;
268
269 /**
270 * The table of state transitions used for forward iteration
271 */
272 private short[] stateTable = null;
273
274 /**
275 * The table of state transitions used to sync up the iterator with the
276 * text in backwards and random-access iteration
277 */
278 private short[] backwardsStateTable = null;
279
280 /**
281 * A list of flags indicating which states in the state table are accepting
282 * ("end") states
283 */
284 private boolean[] endStates = null;
285
286 /**
287 * A list of flags indicating which states in the state table are
288 * lookahead states (states which turn lookahead on and off)
289 */
290 private boolean[] lookaheadStates = null;
291
292 /**
293 * A table for additional data. May be used by a subclass of
294 * RuleBasedBreakIterator.
295 */
296 private byte[] additionalData = null;
297
298 /**
299 * The number of character categories (and, thus, the number of columns in
300 * the state tables)
301 */
302 private int numCategories;
303
304 /**
305 * The character iterator through which this BreakIterator accesses the text
306 */
307 private CharacterIterator text = null;
308
309 /**
310 * A CRC32 value of all data in datafile
311 */
312 private long checksum;
313
314 //=======================================================================
315 // constructors
316 //=======================================================================
317
318 /**
319 * Constructs a RuleBasedBreakIterator according to the datafile
320 * provided.
321 */
322 public RuleBasedBreakIterator(String datafile)
323 throws IOException, MissingResourceException {
324 readTables(datafile);
325 }
326
327 /**
328 * Read datafile. The datafile's format is as follows:
329 * <pre>
330 * BreakIteratorData {
331 * u1 magic[7];
332 * u1 version;
333 * u4 totalDataSize;
334 * header_info header;
335 * body value;
336 * }
337 * </pre>
338 * <code>totalDataSize</code> is the summation of the size of
339 * <code>header_info</code> and <code>body</code> in byte count.
340 * <p>
341 * In <code>header</code>, each field except for checksum implies the
342 * length of each field. Since <code>BMPdataLength</code> is a fixed-length
343 * data(512 entries), its length isn't included in <code>header</code>.
344 * <code>checksum</code> is a CRC32 value of all in <code>body</code>.
345 * <pre>
346 * header_info {
347 * u4 stateTableLength;
348 * u4 backwardsStateTableLength;
349 * u4 endStatesLength;
350 * u4 lookaheadStatesLength;
351 * u4 BMPdataLength;
352 * u4 nonBMPdataLength;
353 * u4 additionalDataLength;
354 * u8 checksum;
355 * }
356 * </pre>
357 * <p>
358 *
359 * Finally, <code>BMPindices</code> and <code>BMPdata</code> are set to
360 * <code>charCategoryTable</code>. <code>nonBMPdata</code> is set to
361 * <code>supplementaryCharCategoryTable</code>.
362 * <pre>
363 * body {
364 * u2 stateTable[stateTableLength];
365 * u2 backwardsStateTable[backwardsStateTableLength];
366 * u1 endStates[endStatesLength];
367 * u1 lookaheadStates[lookaheadStatesLength];
368 * u2 BMPindices[512];
369 * u1 BMPdata[BMPdataLength];
370 * u4 nonBMPdata[numNonBMPdataLength];
371 * u1 additionalData[additionalDataLength];
372 * }
373 * </pre>
374 */
375 protected void readTables(String datafile)
376 throws IOException, MissingResourceException {
377
378 byte[] buffer = readFile(datafile);
379
380 /* Read header_info. */
381 int stateTableLength = BreakIterator.getInt(buffer, 0);
382 int backwardsStateTableLength = BreakIterator.getInt(buffer, 4);
383 int endStatesLength = BreakIterator.getInt(buffer, 8);
384 int lookaheadStatesLength = BreakIterator.getInt(buffer, 12);
385 int BMPdataLength = BreakIterator.getInt(buffer, 16);
386 int nonBMPdataLength = BreakIterator.getInt(buffer, 20);
387 int additionalDataLength = BreakIterator.getInt(buffer, 24);
388 checksum = BreakIterator.getLong(buffer, 28);
389
390 /* Read stateTable[numCategories * numRows] */
391 stateTable = new short[stateTableLength];
392 int offset = HEADER_LENGTH;
393 for (int i = 0; i < stateTableLength; i++, offset+=2) {
394 stateTable[i] = BreakIterator.getShort(buffer, offset);
395 }
396
397 /* Read backwardsStateTable[numCategories * numRows] */
398 backwardsStateTable = new short[backwardsStateTableLength];
399 for (int i = 0; i < backwardsStateTableLength; i++, offset+=2) {
400 backwardsStateTable[i] = BreakIterator.getShort(buffer, offset);
401 }
402
403 /* Read endStates[numRows] */
404 endStates = new boolean[endStatesLength];
405 for (int i = 0; i < endStatesLength; i++, offset++) {
406 endStates[i] = buffer[offset] == 1;
407 }
408
409 /* Read lookaheadStates[numRows] */
410 lookaheadStates = new boolean[lookaheadStatesLength];
411 for (int i = 0; i < lookaheadStatesLength; i++, offset++) {
412 lookaheadStates[i] = buffer[offset] == 1;
413 }
414
415 /* Read a category table and indices for BMP characters. */
416 short[] temp1 = new short[BMP_INDICES_LENGTH]; // BMPindices
417 for (int i = 0; i < BMP_INDICES_LENGTH; i++, offset+=2) {
418 temp1[i] = BreakIterator.getShort(buffer, offset);
419 }
420 byte[] temp2 = new byte[BMPdataLength]; // BMPdata
421 System.arraycopy(buffer, offset, temp2, 0, BMPdataLength);
422 offset += BMPdataLength;
423 charCategoryTable = new CompactByteArray(temp1, temp2);
424
425 /* Read a category table for non-BMP characters. */
426 int[] temp3 = new int[nonBMPdataLength];
427 for (int i = 0; i < nonBMPdataLength; i++, offset+=4) {
428 temp3[i] = BreakIterator.getInt(buffer, offset);
429 }
430 supplementaryCharCategoryTable = new SupplementaryCharacterData(temp3);
431
432 /* Read additional data */
433 if (additionalDataLength > 0) {
434 additionalData = new byte[additionalDataLength];
435 System.arraycopy(buffer, offset, additionalData, 0, additionalDataLength);
436 }
437
438 /* Set numCategories */
439 numCategories = stateTable.length / endStates.length;
440 }
441
442 protected byte[] readFile(final String datafile)
443 throws IOException, MissingResourceException {
444
445 BufferedInputStream is;
446 try {
447 is = (BufferedInputStream)AccessController.doPrivileged(
448 new PrivilegedExceptionAction() {
449 public Object run() throws Exception {
450 return new BufferedInputStream(getClass().getResourceAsStream("/sun/text/resources/" + datafile));
451 }
452 }
453 );
454 }
455 catch (PrivilegedActionException e) {
456 throw new InternalError(e.toString());
457 }
458
459 int offset = 0;
460
461 /* First, read magic, version, and header_info. */
462 int len = LABEL_LENGTH + 5;
463 byte[] buf = new byte[len];
464 if (is.read(buf) != len) {
465 throw new MissingResourceException("Wrong header length",
466 datafile, "");
467 }
468
469 /* Validate the magic number. */
470 for (int i = 0; i < LABEL_LENGTH; i++, offset++) {
471 if (buf[offset] != LABEL[offset]) {
472 throw new MissingResourceException("Wrong magic number",
473 datafile, "");
474 }
475 }
476
477 /* Validate the version number. */
478 if (buf[offset] != supportedVersion) {
479 throw new MissingResourceException("Unsupported version(" + buf[offset] + ")",
480 datafile, "");
481 }
482
483 /* Read data: totalDataSize + 8(for checksum) */
484 len = BreakIterator.getInt(buf, ++offset);
485 buf = new byte[len];
486 if (is.read(buf) != len) {
487 throw new MissingResourceException("Wrong data length",
488 datafile, "");
489 }
490
491 is.close();
492
493 return buf;
494 }
495
496 byte[] getAdditionalData() {
497 return additionalData;
498 }
499
500 void setAdditionalData(byte[] b) {
501 additionalData = b;
502 }
503
504 //=======================================================================
505 // boilerplate
506 //=======================================================================
507 /**
508 * Clones this iterator.
509 * @return A newly-constructed RuleBasedBreakIterator with the same
510 * behavior as this one.
511 */
512 public Object clone() {
513 RuleBasedBreakIterator result = (RuleBasedBreakIterator) super.clone();
514 if (text != null) {
515 result.text = (CharacterIterator) text.clone();
516 }
517 return result;
518 }
519
520 /**
521 * Returns true if both BreakIterators are of the same class, have the same
522 * rules, and iterate over the same text.
523 */
524 public boolean equals(Object that) {
525 try {
526 if (that == null) {
527 return false;
528 }
529
530 RuleBasedBreakIterator other = (RuleBasedBreakIterator) that;
531 if (checksum != other.checksum) {
532 return false;
533 }
534 if (text == null) {
535 return other.text == null;
536 } else {
537 return text.equals(other.text);
538 }
539 }
540 catch(ClassCastException e) {
541 return false;
542 }
543 }
544
545 /**
546 * Returns text
547 */
548 public String toString() {
549 StringBuffer sb = new StringBuffer();
550 sb.append('[');
551 sb.append("checksum=0x" + Long.toHexString(checksum));
552 sb.append(']');
553 return sb.toString();
554 }
555
556 /**
557 * Compute a hashcode for this BreakIterator
558 * @return A hash code
559 */
560 public int hashCode() {
561 return (int)checksum;
562 }
563
564 //=======================================================================
565 // BreakIterator overrides
566 //=======================================================================
567
568 /**
569 * Sets the current iteration position to the beginning of the text.
570 * (i.e., the CharacterIterator's starting offset).
571 * @return The offset of the beginning of the text.
572 */
573 public int first() {
574 CharacterIterator t = getText();
575
576 t.first();
577 return t.getIndex();
578 }
579
580 /**
581 * Sets the current iteration position to the end of the text.
582 * (i.e., the CharacterIterator's ending offset).
583 * @return The text's past-the-end offset.
584 */
585 public int last() {
586 CharacterIterator t = getText();
587
588 // I'm not sure why, but t.last() returns the offset of the last character,
589 // rather than the past-the-end offset
590 t.setIndex(t.getEndIndex());
591 return t.getIndex();
592 }
593
594 /**
595 * Advances the iterator either forward or backward the specified number of steps.
596 * Negative values move backward, and positive values move forward. This is
597 * equivalent to repeatedly calling next() or previous().
598 * @param n The number of steps to move. The sign indicates the direction
599 * (negative is backwards, and positive is forwards).
600 * @return The character offset of the boundary position n boundaries away from
601 * the current one.
602 */
603 public int next(int n) {
604 int result = current();
605 while (n > 0) {
606 result = handleNext();
607 --n;
608 }
609 while (n < 0) {
610 result = previous();
611 ++n;
612 }
613 return result;
614 }
615
616 /**
617 * Advances the iterator to the next boundary position.
618 * @return The position of the first boundary after this one.
619 */
620 public int next() {
621 return handleNext();
622 }
623
624 private int cachedLastKnownBreak = BreakIterator.DONE;
625
626 /**
627 * Advances the iterator backwards, to the last boundary preceding this one.
628 * @return The position of the last boundary position preceding this one.
629 */
630 public int previous() {
631 // if we're already sitting at the beginning of the text, return DONE
632 CharacterIterator text = getText();
633 if (current() == text.getBeginIndex()) {
634 return BreakIterator.DONE;
635 }
636
637 // set things up. handlePrevious() will back us up to some valid
638 // break position before the current position (we back our internal
639 // iterator up one step to prevent handlePrevious() from returning
640 // the current position), but not necessarily the last one before
641 // where we started
642 int start = current();
643 int lastResult = cachedLastKnownBreak;
644 if (lastResult >= start || lastResult <= BreakIterator.DONE) {
645 getPrevious();
646 lastResult = handlePrevious();
647 } else {
648 //it might be better to check if handlePrevious() give us closer
649 //safe value but handlePrevious() is slow too
650 //So, this has to be done carefully
651 text.setIndex(lastResult);
652 }
653 int result = lastResult;
654
655 // iterate forward from the known break position until we pass our
656 // starting point. The last break position before the starting
657 // point is our return value
658 while (result != BreakIterator.DONE && result < start) {
659 lastResult = result;
660 result = handleNext();
661 }
662
663 // set the current iteration position to be the last break position
664 // before where we started, and then return that value
665 text.setIndex(lastResult);
666 cachedLastKnownBreak = lastResult;
667 return lastResult;
668 }
669
670 /**
671 * Returns previous character
672 */
673 private int getPrevious() {
674 char c2 = text.previous();
675 if (Character.isLowSurrogate(c2) &&
676 text.getIndex() > text.getBeginIndex()) {
677 char c1 = text.previous();
678 if (Character.isHighSurrogate(c1)) {
679 return Character.toCodePoint(c1, c2);
680 } else {
681 text.next();
682 }
683 }
684 return (int)c2;
685 }
686
687 /**
688 * Returns current character
689 */
690 int getCurrent() {
691 char c1 = text.current();
692 if (Character.isHighSurrogate(c1) &&
693 text.getIndex() < text.getEndIndex()) {
694 char c2 = text.next();
695 text.previous();
696 if (Character.isLowSurrogate(c2)) {
697 return Character.toCodePoint(c1, c2);
698 }
699 }
700 return (int)c1;
701 }
702
703 /**
704 * Returns the count of next character.
705 */
706 private int getCurrentCodePointCount() {
707 char c1 = text.current();
708 if (Character.isHighSurrogate(c1) &&
709 text.getIndex() < text.getEndIndex()) {
710 char c2 = text.next();
711 text.previous();
712 if (Character.isLowSurrogate(c2)) {
713 return 2;
714 }
715 }
716 return 1;
717 }
718
719 /**
720 * Returns next character
721 */
722 int getNext() {
723 int index = text.getIndex();
724 int endIndex = text.getEndIndex();
725 if (index == endIndex ||
726 (index = index + getCurrentCodePointCount()) >= endIndex) {
727 return CharacterIterator.DONE;
728 }
729 text.setIndex(index);
730 return getCurrent();
731 }
732
733 /**
734 * Returns the position of next character.
735 */
736 private int getNextIndex() {
737 int index = text.getIndex() + getCurrentCodePointCount();
738 int endIndex = text.getEndIndex();
739 if (index > endIndex) {
740 return endIndex;
741 } else {
742 return index;
743 }
744 }
745
746 /**
747 * Throw IllegalArgumentException unless begin <= offset < end.
748 */
749 protected static final void checkOffset(int offset, CharacterIterator text) {
750 if (offset < text.getBeginIndex() || offset > text.getEndIndex()) {
751 throw new IllegalArgumentException("offset out of bounds");
752 }
753 }
754
755 /**
756 * Sets the iterator to refer to the first boundary position following
757 * the specified position.
758 * @offset The position from which to begin searching for a break position.
759 * @return The position of the first break after the current position.
760 */
761 public int following(int offset) {
762
763 CharacterIterator text = getText();
764 checkOffset(offset, text);
765
766 // Set our internal iteration position (temporarily)
767 // to the position passed in. If this is the _beginning_ position,
768 // then we can just use next() to get our return value
769 text.setIndex(offset);
770 if (offset == text.getBeginIndex()) {
771 cachedLastKnownBreak = handleNext();
772 return cachedLastKnownBreak;
773 }
774
775 // otherwise, we have to sync up first. Use handlePrevious() to back
776 // us up to a known break position before the specified position (if
777 // we can determine that the specified position is a break position,
778 // we don't back up at all). This may or may not be the last break
779 // position at or before our starting position. Advance forward
780 // from here until we've passed the starting position. The position
781 // we stop on will be the first break position after the specified one.
782 int result = cachedLastKnownBreak;
783 if (result >= offset || result <= BreakIterator.DONE) {
784 result = handlePrevious();
785 } else {
786 //it might be better to check if handlePrevious() give us closer
787 //safe value but handlePrevious() is slow too
788 //So, this has to be done carefully
789 text.setIndex(result);
790 }
791 while (result != BreakIterator.DONE && result <= offset) {
792 result = handleNext();
793 }
794 cachedLastKnownBreak = result;
795 return result;
796 }
797
798 /**
799 * Sets the iterator to refer to the last boundary position before the
800 * specified position.
801 * @offset The position to begin searching for a break from.
802 * @return The position of the last boundary before the starting position.
803 */
804 public int preceding(int offset) {
805 // if we start by updating the current iteration position to the
806 // position specified by the caller, we can just use previous()
807 // to carry out this operation
808 CharacterIterator text = getText();
809 checkOffset(offset, text);
810 text.setIndex(offset);
811 return previous();
812 }
813
814 /**
815 * Returns true if the specfied position is a boundary position. As a side
816 * effect, leaves the iterator pointing to the first boundary position at
817 * or after "offset".
818 * @param offset the offset to check.
819 * @return True if "offset" is a boundary position.
820 */
821 public boolean isBoundary(int offset) {
822 CharacterIterator text = getText();
823 checkOffset(offset, text);
824 if (offset == text.getBeginIndex()) {
825 return true;
826 }
827
828 // to check whether this is a boundary, we can use following() on the
829 // position before the specified one and return true if the position we
830 // get back is the one the user specified
831 else {
832 return following(offset - 1) == offset;
833 }
834 }
835
836 /**
837 * Returns the current iteration position.
838 * @return The current iteration position.
839 */
840 public int current() {
841 return getText().getIndex();
842 }
843
844 /**
845 * Return a CharacterIterator over the text being analyzed. This version
846 * of this method returns the actual CharacterIterator we're using internally.
847 * Changing the state of this iterator can have undefined consequences. If
848 * you need to change it, clone it first.
849 * @return An iterator over the text being analyzed.
850 */
851 public CharacterIterator getText() {
852 // The iterator is initialized pointing to no text at all, so if this
853 // function is called while we're in that state, we have to fudge an
854 // iterator to return.
855 if (text == null) {
856 text = new StringCharacterIterator("");
857 }
858 return text;
859 }
860
861 /**
862 * Set the iterator to analyze a new piece of text. This function resets
863 * the current iteration position to the beginning of the text.
864 * @param newText An iterator over the text to analyze.
865 */
866 public void setText(CharacterIterator newText) {
867 // Test iterator to see if we need to wrap it in a SafeCharIterator.
868 // The correct behavior for CharacterIterators is to allow the
869 // position to be set to the endpoint of the iterator. Many
870 // CharacterIterators do not uphold this, so this is a workaround
871 // to permit them to use this class.
872 int end = newText.getEndIndex();
873 boolean goodIterator;
874 try {
875 newText.setIndex(end); // some buggy iterators throw an exception here
876 goodIterator = newText.getIndex() == end;
877 }
878 catch(IllegalArgumentException e) {
879 goodIterator = false;
880 }
881
882 if (goodIterator) {
883 text = newText;
884 }
885 else {
886 text = new SafeCharIterator(newText);
887 }
888 text.first();
889
890 cachedLastKnownBreak = BreakIterator.DONE;
891 }
892
893
894 //=======================================================================
895 // implementation
896 //=======================================================================
897
898 /**
899 * This method is the actual implementation of the next() method. All iteration
900 * vectors through here. This method initializes the state machine to state 1
901 * and advances through the text character by character until we reach the end
902 * of the text or the state machine transitions to state 0. We update our return
903 * value every time the state machine passes through a possible end state.
904 */
905 protected int handleNext() {
906 // if we're already at the end of the text, return DONE.
907 CharacterIterator text = getText();
908 if (text.getIndex() == text.getEndIndex()) {
909 return BreakIterator.DONE;
910 }
911
912 // no matter what, we always advance at least one character forward
913 int result = getNextIndex();
914 int lookaheadResult = 0;
915
916 // begin in state 1
917 int state = START_STATE;
918 int category;
919 int c = getCurrent();
920
921 // loop until we reach the end of the text or transition to state 0
922 while (c != CharacterIterator.DONE && state != STOP_STATE) {
923
924 // look up the current character's character category (which tells us
925 // which column in the state table to look at)
926 category = lookupCategory(c);
927
928 // if the character isn't an ignore character, look up a state
929 // transition in the state table
930 if (category != IGNORE) {
931 state = lookupState(state, category);
932 }
933
934 // if the state we've just transitioned to is a lookahead state,
935 // (but not also an end state), save its position. If it's
936 // both a lookahead state and an end state, update the break position
937 // to the last saved lookup-state position
938 if (lookaheadStates[state]) {
939 if (endStates[state]) {
940 result = lookaheadResult;
941 }
942 else {
943 lookaheadResult = getNextIndex();
944 }
945 }
946
947 // otherwise, if the state we've just transitioned to is an accepting
948 // state, update the break position to be the current iteration position
949 else {
950 if (endStates[state]) {
951 result = getNextIndex();
952 }
953 }
954
955 c = getNext();
956 }
957
958 // if we've run off the end of the text, and the very last character took us into
959 // a lookahead state, advance the break position to the lookahead position
960 // (the theory here is that if there are no characters at all after the lookahead
961 // position, that always matches the lookahead criteria)
962 if (c == CharacterIterator.DONE && lookaheadResult == text.getEndIndex()) {
963 result = lookaheadResult;
964 }
965
966 text.setIndex(result);
967 return result;
968 }
969
970 /**
971 * This method backs the iterator back up to a "safe position" in the text.
972 * This is a position that we know, without any context, must be a break position.
973 * The various calling methods then iterate forward from this safe position to
974 * the appropriate position to return. (For more information, see the description
975 * of buildBackwardsStateTable() in RuleBasedBreakIterator.Builder.)
976 */
977 protected int handlePrevious() {
978 CharacterIterator text = getText();
979 int state = START_STATE;
980 int category = 0;
981 int lastCategory = 0;
982 int c = getCurrent();
983
984 // loop until we reach the beginning of the text or transition to state 0
985 while (c != CharacterIterator.DONE && state != STOP_STATE) {
986
987 // save the last character's category and look up the current
988 // character's category
989 lastCategory = category;
990 category = lookupCategory(c);
991
992 // if the current character isn't an ignore character, look up a
993 // state transition in the backwards state table
994 if (category != IGNORE) {
995 state = lookupBackwardState(state, category);
996 }
997
998 // then advance one character backwards
999 c = getPrevious();
1000 }
1001
1002 // if we didn't march off the beginning of the text, we're either one or two
1003 // positions away from the real break position. (One because of the call to
1004 // previous() at the end of the loop above, and another because the character
1005 // that takes us into the stop state will always be the character BEFORE
1006 // the break position.)
1007 if (c != CharacterIterator.DONE) {
1008 if (lastCategory != IGNORE) {
1009 getNext();
1010 getNext();
1011 }
1012 else {
1013 getNext();
1014 }
1015 }
1016 return text.getIndex();
1017 }
1018
1019 /**
1020 * Looks up a character's category (i.e., its category for breaking purposes,
1021 * not its Unicode category)
1022 */
1023 protected int lookupCategory(int c) {
1024 if (c < Character.MIN_SUPPLEMENTARY_CODE_POINT) {
1025 return charCategoryTable.elementAt((char)c);
1026 } else {
1027 return supplementaryCharCategoryTable.getValue(c);
1028 }
1029 }
1030
1031 /**
1032 * Given a current state and a character category, looks up the
1033 * next state to transition to in the state table.
1034 */
1035 protected int lookupState(int state, int category) {
1036 return stateTable[state * numCategories + category];
1037 }
1038
1039 /**
1040 * Given a current state and a character category, looks up the
1041 * next state to transition to in the backwards state table.
1042 */
1043 protected int lookupBackwardState(int state, int category) {
1044 return backwardsStateTable[state * numCategories + category];
1045 }
1046
1047 /*
1048 * This class exists to work around a bug in incorrect implementations
1049 * of CharacterIterator, which incorrectly handle setIndex(endIndex).
1050 * This iterator relies only on base.setIndex(n) where n is less than
1051 * endIndex.
1052 *
1053 * One caveat: if the base iterator's begin and end indices change
1054 * the change will not be reflected by this wrapper. Does that matter?
1055 */
1056 private static final class SafeCharIterator implements CharacterIterator,
1057 Cloneable {
1058
1059 private CharacterIterator base;
1060 private int rangeStart;
1061 private int rangeLimit;
1062 private int currentIndex;
1063
1064 SafeCharIterator(CharacterIterator base) {
1065 this.base = base;
1066 this.rangeStart = base.getBeginIndex();
1067 this.rangeLimit = base.getEndIndex();
1068 this.currentIndex = base.getIndex();
1069 }
1070
1071 public char first() {
1072 return setIndex(rangeStart);
1073 }
1074
1075 public char last() {
1076 return setIndex(rangeLimit - 1);
1077 }
1078
1079 public char current() {
1080 if (currentIndex < rangeStart || currentIndex >= rangeLimit) {
1081 return DONE;
1082 }
1083 else {
1084 return base.setIndex(currentIndex);
1085 }
1086 }
1087
1088 public char next() {
1089
1090 currentIndex++;
1091 if (currentIndex >= rangeLimit) {
1092 currentIndex = rangeLimit;
1093 return DONE;
1094 }
1095 else {
1096 return base.setIndex(currentIndex);
1097 }
1098 }
1099
1100 public char previous() {
1101
1102 currentIndex--;
1103 if (currentIndex < rangeStart) {
1104 currentIndex = rangeStart;
1105 return DONE;
1106 }
1107 else {
1108 return base.setIndex(currentIndex);
1109 }
1110 }
1111
1112 public char setIndex(int i) {
1113
1114 if (i < rangeStart || i > rangeLimit) {
1115 throw new IllegalArgumentException("Invalid position");
1116 }
1117 currentIndex = i;
1118 return current();
1119 }
1120
1121 public int getBeginIndex() {
1122 return rangeStart;
1123 }
1124
1125 public int getEndIndex() {
1126 return rangeLimit;
1127 }
1128
1129 public int getIndex() {
1130 return currentIndex;
1131 }
1132
1133 public Object clone() {
1134
1135 SafeCharIterator copy = null;
1136 try {
1137 copy = (SafeCharIterator) super.clone();
1138 }
1139 catch(CloneNotSupportedException e) {
1140 throw new Error("Clone not supported: " + e);
1141 }
1142
1143 CharacterIterator copyOfBase = (CharacterIterator) base.clone();
1144 copy.base = copyOfBase;
1145 return copy;
1146 }
1147 }
1148}
java/pkg/java/text/RuleBasedCollationKey.class+0−0

Binary file not shown.

java/pkg/java/text/RuleBasedCollationKey.java+123−0
@@ -0,0 +1,123 @@
1/*
2 * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26/*
27 * (C) Copyright Taligent, Inc. 1996 - All Rights Reserved
28 * (C) Copyright IBM Corp. 1996 - All Rights Reserved
29 *
30 * The original version of this source code and documentation is copyrighted
31 * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
32 * materials are provided under terms of a License Agreement between Taligent
33 * and Sun. This technology is protected by multiple US and International
34 * patents. This notice and attribution to Taligent may not be removed.
35 * Taligent is a registered trademark of Taligent, Inc.
36 *
37 */
38
39package java.text;
40
41/**
42 * A RuleBasedCollationKey is a concrete implementation of CollationKey class.
43 * The RuleBasedCollationKey class is used by the RuleBasedCollator class.
44 */
45
46final class RuleBasedCollationKey extends CollationKey {
47 /**
48 * Compare this RuleBasedCollationKey to target. The collation rules of the
49 * Collator object which created these keys are applied. <strong>Note:</strong>
50 * RuleBasedCollationKeys created by different Collators can not be compared.
51 * @param target target RuleBasedCollationKey
52 * @return Returns an integer value. Value is less than zero if this is less
53 * than target, value is zero if this and target are equal and value is greater than
54 * zero if this is greater than target.
55 * @see java.text.Collator#compare
56 */
57 public int compareTo(CollationKey target)
58 {
59 int result = key.compareTo(((RuleBasedCollationKey)(target)).key);
60 if (result <= Collator.LESS)
61 return Collator.LESS;
62 else if (result >= Collator.GREATER)
63 return Collator.GREATER;
64 return Collator.EQUAL;
65 }
66
67 /**
68 * Compare this RuleBasedCollationKey and the target for equality.
69 * The collation rules of the Collator object which created these keys are applied.
70 * <strong>Note:</strong> RuleBasedCollationKeys created by different Collators can not be
71 * compared.
72 * @param target the RuleBasedCollationKey to compare to.
73 * @return Returns true if two objects are equal, false otherwise.
74 */
75 public boolean equals(Object target) {
76 if (this == target) return true;
77 if (target == null || !getClass().equals(target.getClass())) {
78 return false;
79 }
80 RuleBasedCollationKey other = (RuleBasedCollationKey)target;
81 return key.equals(other.key);
82 }
83
84 /**
85 * Creates a hash code for this RuleBasedCollationKey. The hash value is calculated on the
86 * key itself, not the String from which the key was created. Thus
87 * if x and y are RuleBasedCollationKeys, then x.hashCode(x) == y.hashCode() if
88 * x.equals(y) is true. This allows language-sensitive comparison in a hash table.
89 * See the CollatinKey class description for an example.
90 * @return the hash value based on the string's collation order.
91 */
92 public int hashCode() {
93 return (key.hashCode());
94 }
95
96 /**
97 * Converts the RuleBasedCollationKey to a sequence of bits. If two RuleBasedCollationKeys
98 * could be legitimately compared, then one could compare the byte arrays
99 * for each of those keys to obtain the same result. Byte arrays are
100 * organized most significant byte first.
101 */
102 public byte[] toByteArray() {
103
104 char[] src = key.toCharArray();
105 byte[] dest = new byte[ 2*src.length ];
106 int j = 0;
107 for( int i=0; i<src.length; i++ ) {
108 dest[j++] = (byte)(src[i] >>> 8);
109 dest[j++] = (byte)(src[i] & 0x00ff);
110 }
111 return dest;
112 }
113
114 /**
115 * A RuleBasedCollationKey can only be generated by Collator objects.
116 */
117 RuleBasedCollationKey(String source, String key) {
118 super(source);
119 this.key = key;
120 }
121 private String key = null;
122
123}
java/pkg/java/text/RuleBasedCollator.class+0−0

Binary file not shown.

java/pkg/java/text/RuleBasedCollator.java+763−0
@@ -0,0 +1,763 @@
1/*
2 * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26/*
27 * (C) Copyright Taligent, Inc. 1996, 1997 - All Rights Reserved
28 * (C) Copyright IBM Corp. 1996-1998 - All Rights Reserved
29 *
30 * The original version of this source code and documentation is copyrighted
31 * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These
32 * materials are provided under terms of a License Agreement between Taligent
33 * and Sun. This technology is protected by multiple US and International
34 * patents. This notice and attribution to Taligent may not be removed.
35 * Taligent is a registered trademark of Taligent, Inc.
36 *
37 */
38
39package java.text;
40
41import java.text.Normalizer;
42import java.util.Vector;
43import java.util.Locale;
44
45/**
46 * The <code>RuleBasedCollator</code> class is a concrete subclass of
47 * <code>Collator</code> that provides a simple, data-driven, table
48 * collator. With this class you can create a customized table-based
49 * <code>Collator</code>. <code>RuleBasedCollator</code> maps
50 * characters to sort keys.
51 *
52 * <p>
53 * <code>RuleBasedCollator</code> has the following restrictions
54 * for efficiency (other subclasses may be used for more complex languages) :
55 * <ol>
56 * <li>If a special collation rule controlled by a &lt;modifier&gt; is
57 specified it applies to the whole collator object.
58 * <li>All non-mentioned characters are at the end of the
59 * collation order.
60 * </ol>
61 *
62 * <p>
63 * The collation table is composed of a list of collation rules, where each
64 * rule is of one of three forms:
65 * <pre>
66 * &lt;modifier&gt;
67 * &lt;relation&gt; &lt;text-argument&gt;
68 * &lt;reset&gt; &lt;text-argument&gt;
69 * </pre>
70 * The definitions of the rule elements is as follows:
71 * <UL Type=disc>
72 * <LI><strong>Text-Argument</strong>: A text-argument is any sequence of
73 * characters, excluding special characters (that is, common
74 * whitespace characters [0009-000D, 0020] and rule syntax characters
75 * [0021-002F, 003A-0040, 005B-0060, 007B-007E]). If those
76 * characters are desired, you can put them in single quotes
77 * (e.g. ampersand => '&'). Note that unquoted white space characters
78 * are ignored; e.g. <code>b c</code> is treated as <code>bc</code>.
79 * <LI><strong>Modifier</strong>: There are currently two modifiers that
80 * turn on special collation rules.
81 * <UL Type=square>
82 * <LI>'@' : Turns on backwards sorting of accents (secondary
83 * differences), as in French.
84 * <LI>'!' : Turns on Thai/Lao vowel-consonant swapping. If this
85 * rule is in force when a Thai vowel of the range
86 * &#92;U0E40-&#92;U0E44 precedes a Thai consonant of the range
87 * &#92;U0E01-&#92;U0E2E OR a Lao vowel of the range &#92;U0EC0-&#92;U0EC4
88 * precedes a Lao consonant of the range &#92;U0E81-&#92;U0EAE then
89 * the vowel is placed after the consonant for collation
90 * purposes.
91 * </UL>
92 * <p>'@' : Indicates that accents are sorted backwards, as in French.
93 * <LI><strong>Relation</strong>: The relations are the following:
94 * <UL Type=square>
95 * <LI>'&lt;' : Greater, as a letter difference (primary)
96 * <LI>';' : Greater, as an accent difference (secondary)
97 * <LI>',' : Greater, as a case difference (tertiary)
98 * <LI>'=' : Equal
99 * </UL>
100 * <LI><strong>Reset</strong>: There is a single reset
101 * which is used primarily for contractions and expansions, but which
102 * can also be used to add a modification at the end of a set of rules.
103 * <p>'&' : Indicates that the next rule follows the position to where
104 * the reset text-argument would be sorted.
105 * </UL>
106 *
107 * <p>
108 * This sounds more complicated than it is in practice. For example, the
109 * following are equivalent ways of expressing the same thing:
110 * <blockquote>
111 * <pre>
112 * a &lt; b &lt; c
113 * a &lt; b &amp; b &lt; c
114 * a &lt; c &amp; a &lt; b
115 * </pre>
116 * </blockquote>
117 * Notice that the order is important, as the subsequent item goes immediately
118 * after the text-argument. The following are not equivalent:
119 * <blockquote>
120 * <pre>
121 * a &lt; b &amp; a &lt; c
122 * a &lt; c &amp; a &lt; b
123 * </pre>
124 * </blockquote>
125 * Either the text-argument must already be present in the sequence, or some
126 * initial substring of the text-argument must be present. (e.g. "a &lt; b &amp; ae &lt;
127 * e" is valid since "a" is present in the sequence before "ae" is reset). In
128 * this latter case, "ae" is not entered and treated as a single character;
129 * instead, "e" is sorted as if it were expanded to two characters: "a"
130 * followed by an "e". This difference appears in natural languages: in
131 * traditional Spanish "ch" is treated as though it contracts to a single
132 * character (expressed as "c &lt; ch &lt; d"), while in traditional German
133 * a-umlaut is treated as though it expanded to two characters
134 * (expressed as "a,A &lt; b,B ... &amp;ae;&#92;u00e3&amp;AE;&#92;u00c3").
135 * [&#92;u00e3 and &#92;u00c3 are, of course, the escape sequences for a-umlaut.]
136 * <p>
137 * <strong>Ignorable Characters</strong>
138 * <p>
139 * For ignorable characters, the first rule must start with a relation (the
140 * examples we have used above are really fragments; "a &lt; b" really should be
141 * "&lt; a &lt; b"). If, however, the first relation is not "&lt;", then all the all
142 * text-arguments up to the first "&lt;" are ignorable. For example, ", - &lt; a &lt; b"
143 * makes "-" an ignorable character, as we saw earlier in the word
144 * "black-birds". In the samples for different languages, you see that most
145 * accents are ignorable.
146 *
147 * <p><strong>Normalization and Accents</strong>
148 * <p>
149 * <code>RuleBasedCollator</code> automatically processes its rule table to
150 * include both pre-composed and combining-character versions of
151 * accented characters. Even if the provided rule string contains only
152 * base characters and separate combining accent characters, the pre-composed
153 * accented characters matching all canonical combinations of characters from
154 * the rule string will be entered in the table.
155 * <p>
156 * This allows you to use a RuleBasedCollator to compare accented strings
157 * even when the collator is set to NO_DECOMPOSITION. There are two caveats,
158 * however. First, if the strings to be collated contain combining
159 * sequences that may not be in canonical order, you should set the collator to
160 * CANONICAL_DECOMPOSITION or FULL_DECOMPOSITION to enable sorting of
161 * combining sequences. Second, if the strings contain characters with
162 * compatibility decompositions (such as full-width and half-width forms),
163 * you must use FULL_DECOMPOSITION, since the rule tables only include
164 * canonical mappings.
165 *
166 * <p><strong>Errors</strong>
167 * <p>
168 * The following are errors:
169 * <UL Type=disc>
170 * <LI>A text-argument contains unquoted punctuation symbols
171 * (e.g. "a &lt; b-c &lt; d").
172 * <LI>A relation or reset character not followed by a text-argument
173 * (e.g. "a &lt; ,b").
174 * <LI>A reset where the text-argument (or an initial substring of the
175 * text-argument) is not already in the sequence.
176 * (e.g. "a &lt; b &amp; e &lt; f")
177 * </UL>
178 * If you produce one of these errors, a <code>RuleBasedCollator</code> throws
179 * a <code>ParseException</code>.
180 *
181 * <p><strong>Examples</strong>
182 * <p>Simple: "&lt; a &lt; b &lt; c &lt; d"
183 * <p>Norwegian: "&lt; a, A &lt; b, B &lt; c, C &lt; d, D &lt; e, E &lt; f, F
184 * &lt; g, G &lt; h, H &lt; i, I &lt; j, J &lt; k, K &lt; l, L
185 * &lt; m, M &lt; n, N &lt; o, O &lt; p, P &lt; q, Q &lt; r, R
186 * &lt; s, S &lt; t, T &lt; u, U &lt; v, V &lt; w, W &lt; x, X
187 * &lt; y, Y &lt; z, Z
188 * &lt; &#92;u00E6, &#92;u00C6
189 * &lt; &#92;u00F8, &#92;u00D8
190 * &lt; &#92;u00E5 = a&#92;u030A, &#92;u00C5 = A&#92;u030A;
191 * aa, AA"
192 *
193 * <p>
194 * To create a <code>RuleBasedCollator</code> object with specialized
195 * rules tailored to your needs, you construct the <code>RuleBasedCollator</code>
196 * with the rules contained in a <code>String</code> object. For example:
197 * <blockquote>
198 * <pre>
199 * String simple = "&lt; a&lt; b&lt; c&lt; d";
200 * RuleBasedCollator mySimple = new RuleBasedCollator(simple);
201 * </pre>
202 * </blockquote>
203 * Or:
204 * <blockquote>
205 * <pre>
206 * String Norwegian = "&lt; a, A &lt; b, B &lt; c, C &lt; d, D &lt; e, E &lt; f, F &lt; g, G &lt; h, H &lt; i, I" +
207 * "&lt; j, J &lt; k, K &lt; l, L &lt; m, M &lt; n, N &lt; o, O &lt; p, P &lt; q, Q &lt; r, R" +
208 * "&lt; s, S &lt; t, T &lt; u, U &lt; v, V &lt; w, W &lt; x, X &lt; y, Y &lt; z, Z" +
209 * "&lt; &#92;u00E6, &#92;u00C6" + // Latin letter ae & AE
210 * "&lt; &#92;u00F8, &#92;u00D8" + // Latin letter o & O with stroke
211 * "&lt; &#92;u00E5 = a&#92;u030A," + // Latin letter a with ring above
212 * " &#92;u00C5 = A&#92;u030A;" + // Latin letter A with ring above
213 * " aa, AA";
214 * RuleBasedCollator myNorwegian = new RuleBasedCollator(Norwegian);
215 * </pre>
216 * </blockquote>
217 *
218 * <p>
219 * A new collation rules string can be created by concatenating rules
220 * strings. For example, the rules returned by {@link #getRules()} could
221 * be concatenated to combine multiple <code>RuleBasedCollator</code>s.
222 *
223 * <p>
224 * The following example demonstrates how to change the order of
225 * non-spacing accents,
226 * <blockquote>
227 * <pre>
228 * // old rule
229 * String oldRules = "=&#92;u0301;&#92;u0300;&#92;u0302;&#92;u0308" // main accents
230 * + ";&#92;u0327;&#92;u0303;&#92;u0304;&#92;u0305" // main accents
231 * + ";&#92;u0306;&#92;u0307;&#92;u0309;&#92;u030A" // main accents
232 * + ";&#92;u030B;&#92;u030C;&#92;u030D;&#92;u030E" // main accents
233 * + ";&#92;u030F;&#92;u0310;&#92;u0311;&#92;u0312" // main accents
234 * + "&lt; a , A ; ae, AE ; &#92;u00e6 , &#92;u00c6"
235 * + "&lt; b , B &lt; c, C &lt; e, E & C &lt; d, D";
236 * // change the order of accent characters
237 * String addOn = "& &#92;u0300 ; &#92;u0308 ; &#92;u0302";
238 * RuleBasedCollator myCollator = new RuleBasedCollator(oldRules + addOn);
239 * </pre>
240 * </blockquote>
241 *
242 * @see Collator
243 * @see CollationElementIterator
244 * @author Helena Shih, Laura Werner, Richard Gillam
245 */
246public class RuleBasedCollator extends Collator{
247 // IMPLEMENTATION NOTES: The implementation of the collation algorithm is
248 // divided across three classes: RuleBasedCollator, RBCollationTables, and
249 // CollationElementIterator. RuleBasedCollator contains the collator's
250 // transient state and includes the code that uses the other classes to
251 // implement comparison and sort-key building. RuleBasedCollator also
252 // contains the logic to handle French secondary accent sorting.
253 // A RuleBasedCollator has two CollationElementIterators. State doesn't
254 // need to be preserved in these objects between calls to compare() or
255 // getCollationKey(), but the objects persist anyway to avoid wasting extra
256 // creation time. compare() and getCollationKey() are synchronized to ensure
257 // thread safety with this scheme. The CollationElementIterator is responsible
258 // for generating collation elements from strings and returning one element at
259 // a time (sometimes there's a one-to-many or many-to-one mapping between
260 // characters and collation elements-- this class handles that).
261 // CollationElementIterator depends on RBCollationTables, which contains the
262 // collator's static state. RBCollationTables contains the actual data
263 // tables specifying the collation order of characters for a particular locale
264 // or use. It also contains the base logic that CollationElementIterator
265 // uses to map from characters to collation elements. A single RBCollationTables
266 // object is shared among all RuleBasedCollators for the same locale, and
267 // thus by all the CollationElementIterators they create.
268
269 /**
270 * RuleBasedCollator constructor. This takes the table rules and builds
271 * a collation table out of them. Please see RuleBasedCollator class
272 * description for more details on the collation rule syntax.
273 * @see java.util.Locale
274 * @param rules the collation rules to build the collation table from.
275 * @exception ParseException A format exception
276 * will be thrown if the build process of the rules fails. For
277 * example, build rule "a < ? < d" will cause the constructor to
278 * throw the ParseException because the '?' is not quoted.
279 */
280 public RuleBasedCollator(String rules) throws ParseException {
281 this(rules, Collator.CANONICAL_DECOMPOSITION);

482 more lines aren’t shown here.

java/pkg/java/text/SimpleDateFormat.class+0−0

Binary file not shown.

java/pkg/java/text/SimpleDateFormat.java+2350−0

This diff is large: its 2351 lines aren’t shown here.

java/pkg/java/text/StringCharacterIterator.class+0−0

Binary file not shown.

java/pkg/java/text/StringCharacterIterator.java+279−0

This diff is large: its 280 lines aren’t shown here.

java/pkg/java/text/mkfile+49−0

This diff is large: its 50 lines aren’t shown here.

java/pkg/java/text/package.html+65−0

This diff is large: its 66 lines aren’t shown here.

java/pkg/java/text/spi/BreakIteratorProvider.class+0−0

Binary file not shown.

java/pkg/java/text/spi/BreakIteratorProvider.java+107−0

This diff is large: its 108 lines aren’t shown here.

java/pkg/java/text/spi/CollatorProvider.class+0−0

Binary file not shown.

java/pkg/java/text/spi/CollatorProvider.java+61−0

This diff is large: its 62 lines aren’t shown here.

java/pkg/java/text/spi/DateFormatProvider.class+0−0

Binary file not shown.

java/pkg/java/text/spi/DateFormatProvider.java+114−0

This diff is large: its 115 lines aren’t shown here.

java/pkg/java/text/spi/DateFormatSymbolsProvider.class+0−0

Binary file not shown.

java/pkg/java/text/spi/DateFormatSymbolsProvider.java+62−0

This diff is large: its 63 lines aren’t shown here.

java/pkg/java/text/spi/DecimalFormatSymbolsProvider.class+0−0

Binary file not shown.

java/pkg/java/text/spi/DecimalFormatSymbolsProvider.java+62−0

This diff is large: its 63 lines aren’t shown here.

java/pkg/java/text/spi/NumberFormatProvider.class+0−0

Binary file not shown.

java/pkg/java/text/spi/NumberFormatProvider.java+113−0

This diff is large: its 114 lines aren’t shown here.

java/pkg/java/text/spi/mkfile+13−0

This diff is large: its 14 lines aren’t shown here.

java/pkg/java/text/spi/package.html+50−0

This diff is large: its 51 lines aren’t shown here.