diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2008-10-21 07:00:00 -0700 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2008-10-21 07:00:00 -0700 |
commit | fdb2704414a9ed92394ada0d1395e4db86889465 (patch) | |
tree | 9b591a4a50054274a197f02b3ccb51313681879f /text | |
download | libcore-fdb2704414a9ed92394ada0d1395e4db86889465.zip libcore-fdb2704414a9ed92394ada0d1395e4db86889465.tar.gz libcore-fdb2704414a9ed92394ada0d1395e4db86889465.tar.bz2 |
Initial Contribution
Diffstat (limited to 'text')
61 files changed, 22558 insertions, 0 deletions
diff --git a/text/MODULE_LICENSE_APACHE2 b/text/MODULE_LICENSE_APACHE2 new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/text/MODULE_LICENSE_APACHE2 diff --git a/text/src/main/java/java/text/Annotation.java b/text/src/main/java/java/text/Annotation.java new file mode 100644 index 0000000..922cbd0 --- /dev/null +++ b/text/src/main/java/java/text/Annotation.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package java.text; + +/** + * Annotation + */ +public class Annotation { + + private Object value; + + public Annotation(Object attribute) { + value = attribute; + } + + public Object getValue() { + return value; + } + + @Override + public String toString() { + return getClass().getName() + "[value=" + value + ']'; //$NON-NLS-1$ + } +} diff --git a/text/src/main/java/java/text/AttributedCharacterIterator.java b/text/src/main/java/java/text/AttributedCharacterIterator.java new file mode 100644 index 0000000..c9504e8 --- /dev/null +++ b/text/src/main/java/java/text/AttributedCharacterIterator.java @@ -0,0 +1,107 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package java.text; + +import java.io.InvalidObjectException; +import java.io.Serializable; +import java.util.Map; +import java.util.Set; + +import org.apache.harmony.text.internal.nls.Messages; + +/** + * AttributedCharacterIterator + */ +public interface AttributedCharacterIterator extends CharacterIterator { + + public static class Attribute implements Serializable { + + private static final long serialVersionUID = -9142742483513960612L; + + public static final Attribute INPUT_METHOD_SEGMENT = new Attribute( + "input_method_segment"); //$NON-NLS-1$ + + public static final Attribute LANGUAGE = new Attribute("language"); //$NON-NLS-1$ + + public static final Attribute READING = new Attribute("reading"); //$NON-NLS-1$ + + private String name; + + protected Attribute(String name) { + this.name = name; + } + + @Override + public final boolean equals(Object object) { + if (object == null || !(object.getClass().equals(this.getClass()))) { + return false; + } + return name.equals(((Attribute) object).name); + } + + protected String getName() { + return name; + } + + @Override + public final int hashCode() { + return name.hashCode(); + } + + protected Object readResolve() throws InvalidObjectException { + if (this.getClass() != Attribute.class) { + // text.0C=cannot resolve subclasses + throw new InvalidObjectException(Messages.getString("text.0C")); //$NON-NLS-1$ + } + if (this.equals(INPUT_METHOD_SEGMENT)) { + return INPUT_METHOD_SEGMENT; + } + if (this.equals(LANGUAGE)) { + return LANGUAGE; + } + if (this.equals(READING)) { + return READING; + } + // text.02=Unknown attribute + throw new InvalidObjectException(Messages.getString("text.02")); //$NON-NLS-1$ + } + + @Override + public String toString() { + return getClass().getName() + '(' + getName() + ')'; + } + } + + public Set<Attribute> getAllAttributeKeys(); + + public Object getAttribute(Attribute attribute); + + public Map<Attribute, Object> getAttributes(); + + public int getRunLimit(); + + public int getRunLimit(Attribute attribute); + + public int getRunLimit(Set<? extends Attribute> attributes); + + public int getRunStart(); + + public int getRunStart(Attribute attribute); + + public int getRunStart(Set<? extends Attribute> attributes); +} diff --git a/text/src/main/java/java/text/AttributedString.java b/text/src/main/java/java/text/AttributedString.java new file mode 100644 index 0000000..6bcd8a3 --- /dev/null +++ b/text/src/main/java/java/text/AttributedString.java @@ -0,0 +1,670 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package java.text; + +import java.text.AttributedCharacterIterator.Attribute; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.Set; + +import org.apache.harmony.text.internal.nls.Messages; + +/** + * AttributedString + */ +public class AttributedString { + + String text; + + Map<AttributedCharacterIterator.Attribute, List<Range>> attributeMap; + + static class Range { + int start; + + int end; + + Object value; + + Range(int s, int e, Object v) { + start = s; + end = e; + value = v; + } + } + + static class AttributedIterator implements AttributedCharacterIterator { + + private int begin, end, offset; + + private AttributedString attrString; + + private HashSet<Attribute> attributesAllowed; + + AttributedIterator(AttributedString attrString) { + this.attrString = attrString; + begin = 0; + end = attrString.text.length(); + offset = 0; + } + + AttributedIterator(AttributedString attrString, + AttributedCharacterIterator.Attribute[] attributes, int begin, + int end) { + if (begin < 0 || end > attrString.text.length() || begin > end) { + throw new IllegalArgumentException(); + } + this.begin = begin; + this.end = end; + offset = begin; + this.attrString = attrString; + if (attributes != null) { + HashSet<Attribute> set = new HashSet<Attribute>( + (attributes.length * 4 / 3) + 1); + for (int i = attributes.length; --i >= 0;) { + set.add(attributes[i]); + } + attributesAllowed = set; + } + } + + /** + * Returns a new StringCharacterIterator with the same source String, + * begin, end, and current index as this StringCharacterIterator. + * + * @return a shallow copy of this StringCharacterIterator + * + * @see java.lang.Cloneable + */ + @Override + @SuppressWarnings("unchecked") + public Object clone() { + try { + AttributedIterator clone = (AttributedIterator) super.clone(); + if (attributesAllowed != null) { + clone.attributesAllowed = (HashSet<Attribute>) attributesAllowed + .clone(); + } + return clone; + } catch (CloneNotSupportedException e) { + return null; + } + } + + /** + * Returns the character at the current index in the source String. + * + * @return the current character, or DONE if the current index is past + * the end + */ + public char current() { + if (offset == end) { + return DONE; + } + return attrString.text.charAt(offset); + } + + /** + * Sets the current position to the begin index and returns the + * character at the begin index. + * + * @return the character at the begin index + */ + public char first() { + if (begin == end) { + return DONE; + } + offset = begin; + return attrString.text.charAt(offset); + } + + /** + * Returns the begin index in the source String. + * + * @return the index of the first character to iterate + */ + public int getBeginIndex() { + return begin; + } + + /** + * Returns the end index in the source String. + * + * @return the index one past the last character to iterate + */ + public int getEndIndex() { + return end; + } + + /** + * Returns the current index in the source String. + * + * @return the current index + */ + public int getIndex() { + return offset; + } + + private boolean inRange(Range range) { + if (!(range.value instanceof Annotation)) { + return true; + } + return range.start >= begin && range.start < end + && range.end > begin && range.end <= end; + } + + private boolean inRange(List<Range> ranges) { + Iterator<Range> it = ranges.iterator(); + while (it.hasNext()) { + Range range = it.next(); + if (range.start >= begin && range.start < end) { + return !(range.value instanceof Annotation) + || (range.end > begin && range.end <= end); + } else if (range.end > begin && range.end <= end) { + return !(range.value instanceof Annotation) + || (range.start >= begin && range.start < end); + } + } + return false; + } + + public Set<AttributedIterator.Attribute> getAllAttributeKeys() { + if (begin == 0 && end == attrString.text.length() + && attributesAllowed == null) { + return attrString.attributeMap.keySet(); + } + + Set<AttributedIterator.Attribute> result = new HashSet<Attribute>( + (attrString.attributeMap.size() * 4 / 3) + 1); + Iterator<Map.Entry<Attribute, List<Range>>> it = attrString.attributeMap + .entrySet().iterator(); + while (it.hasNext()) { + Map.Entry<Attribute, List<Range>> entry = it.next(); + if (attributesAllowed == null + || attributesAllowed.contains(entry.getKey())) { + List<Range> ranges = entry.getValue(); + if (inRange(ranges)) { + result.add(entry.getKey()); + } + } + } + return result; + } + + private Object currentValue(List<Range> ranges) { + Iterator<Range> it = ranges.iterator(); + while (it.hasNext()) { + Range range = it.next(); + if (offset >= range.start && offset < range.end) { + return inRange(range) ? range.value : null; + } + } + return null; + } + + public Object getAttribute( + AttributedCharacterIterator.Attribute attribute) { + if (attributesAllowed != null + && !attributesAllowed.contains(attribute)) { + return null; + } + ArrayList<Range> ranges = (ArrayList<Range>) attrString.attributeMap + .get(attribute); + if (ranges == null) { + return null; + } + return currentValue(ranges); + } + + public Map<Attribute, Object> getAttributes() { + Map<Attribute, Object> result = new HashMap<Attribute, Object>( + (attrString.attributeMap.size() * 4 / 3) + 1); + Iterator<Map.Entry<Attribute, List<Range>>> it = attrString.attributeMap + .entrySet().iterator(); + while (it.hasNext()) { + Map.Entry<Attribute, List<Range>> entry = it.next(); + if (attributesAllowed == null + || attributesAllowed.contains(entry.getKey())) { + Object value = currentValue(entry.getValue()); + if (value != null) { + result.put(entry.getKey(), value); + } + } + } + return result; + } + + public int getRunLimit() { + return getRunLimit(getAllAttributeKeys()); + } + + private int runLimit(List<Range> ranges) { + int result = end; + ListIterator<Range> it = ranges.listIterator(ranges.size()); + while (it.hasPrevious()) { + Range range = it.previous(); + if (range.end <= begin) { + break; + } + if (offset >= range.start && offset < range.end) { + return inRange(range) ? range.end : result; + } else if (offset >= range.end) { + break; + } + result = range.start; + } + return result; + } + + public int getRunLimit(AttributedCharacterIterator.Attribute attribute) { + if (attributesAllowed != null + && !attributesAllowed.contains(attribute)) { + return end; + } + ArrayList<Range> ranges = (ArrayList<Range>) attrString.attributeMap + .get(attribute); + if (ranges == null) { + return end; + } + return runLimit(ranges); + } + + public int getRunLimit(Set<? extends Attribute> attributes) { + int limit = end; + Iterator<? extends Attribute> it = attributes.iterator(); + while (it.hasNext()) { + AttributedCharacterIterator.Attribute attribute = it.next(); + int newLimit = getRunLimit(attribute); + if (newLimit < limit) { + limit = newLimit; + } + } + return limit; + } + + public int getRunStart() { + return getRunStart(getAllAttributeKeys()); + } + + private int runStart(List<Range> ranges) { + int result = begin; + Iterator<Range> it = ranges.iterator(); + while (it.hasNext()) { + Range range = it.next(); + if (range.start >= end) { + break; + } + if (offset >= range.start && offset < range.end) { + return inRange(range) ? range.start : result; + } else if (offset < range.start) { + break; + } + result = range.end; + } + return result; + } + + public int getRunStart(AttributedCharacterIterator.Attribute attribute) { + if (attributesAllowed != null + && !attributesAllowed.contains(attribute)) { + return begin; + } + ArrayList<Range> ranges = (ArrayList<Range>) attrString.attributeMap + .get(attribute); + if (ranges == null) { + return begin; + } + return runStart(ranges); + } + + public int getRunStart(Set<? extends Attribute> attributes) { + int start = begin; + Iterator<? extends Attribute> it = attributes.iterator(); + while (it.hasNext()) { + AttributedCharacterIterator.Attribute attribute = it.next(); + int newStart = getRunStart(attribute); + if (newStart > start) { + start = newStart; + } + } + return start; + } + + /** + * Sets the current position to the end index - 1 and returns the + * character at the current position. + * + * @return the character before the end index + */ + public char last() { + if (begin == end) { + return DONE; + } + offset = end - 1; + return attrString.text.charAt(offset); + } + + /** + * Increments the current index and returns the character at the new + * index. + * + * @return the character at the next index, or DONE if the next index is + * past the end + */ + public char next() { + if (offset >= (end - 1)) { + offset = end; + return DONE; + } + return attrString.text.charAt(++offset); + } + + /** + * Decrements the current index and returns the character at the new + * index. + * + * @return the character at the previous index, or DONE if the previous + * index is past the beginning + */ + public char previous() { + if (offset == begin) { + return DONE; + } + return attrString.text.charAt(--offset); + } + + /** + * Sets the current index in the source String. + * + * @return the character at the new index, or DONE if the index is past + * the end + * + * @exception IllegalArgumentException + * when the new index is less than the begin index or + * greater than the end index + */ + public char setIndex(int location) { + if (location < begin || location > end) { + throw new IllegalArgumentException(); + } + offset = location; + if (offset == end) { + return DONE; + } + return attrString.text.charAt(offset); + } + } + + public AttributedString(AttributedCharacterIterator iterator) { + if (iterator.getBeginIndex() > iterator.getEndIndex()) { + // text.0A=Invalid substring range + throw new IllegalArgumentException(Messages.getString("text.0A")); //$NON-NLS-1$ + } + StringBuffer buffer = new StringBuffer(); + for (int i = iterator.getBeginIndex(); i < iterator.getEndIndex(); i++) { + buffer.append(iterator.current()); + iterator.next(); + } + text = buffer.toString(); + Set<AttributedCharacterIterator.Attribute> attributes = iterator + .getAllAttributeKeys(); + if (attributes == null) { + return; + } + attributeMap = new HashMap<Attribute, List<Range>>( + (attributes.size() * 4 / 3) + 1); + + Iterator<Attribute> it = attributes.iterator(); + while (it.hasNext()) { + AttributedCharacterIterator.Attribute attribute = it.next(); + iterator.setIndex(0); + while (iterator.current() != CharacterIterator.DONE) { + int start = iterator.getRunStart(attribute); + int limit = iterator.getRunLimit(attribute); + Object value = iterator.getAttribute(attribute); + if (value != null) { + addAttribute(attribute, value, start, limit); + } + iterator.setIndex(limit); + } + } + } + + private AttributedString(AttributedCharacterIterator iterator, int start, + int end, Set<Attribute> attributes) { + if (start < iterator.getBeginIndex() || end > iterator.getEndIndex() + || start > end) { + throw new IllegalArgumentException(); + } + + if (attributes == null) { + return; + } + + StringBuffer buffer = new StringBuffer(); + iterator.setIndex(start); + while (iterator.getIndex() < end) { + buffer.append(iterator.current()); + iterator.next(); + } + text = buffer.toString(); + attributeMap = new HashMap<Attribute, List<Range>>( + (attributes.size() * 4 / 3) + 1); + + Iterator<Attribute> it = attributes.iterator(); + while (it.hasNext()) { + AttributedCharacterIterator.Attribute attribute = it.next(); + iterator.setIndex(start); + while (iterator.getIndex() < end) { + Object value = iterator.getAttribute(attribute); + int runStart = iterator.getRunStart(attribute); + int limit = iterator.getRunLimit(attribute); + if ((value instanceof Annotation && runStart >= start && limit <= end) + || (value != null && !(value instanceof Annotation))) { + addAttribute(attribute, value, (runStart < start ? start + : runStart) + - start, (limit > end ? end : limit) - start); + } + iterator.setIndex(limit); + } + } + } + + public AttributedString(AttributedCharacterIterator iterator, int start, + int end) { + this(iterator, start, end, iterator.getAllAttributeKeys()); + } + + public AttributedString(AttributedCharacterIterator iterator, int start, + int end, AttributedCharacterIterator.Attribute[] attributes) { + // BEGIN android-removed + // this(iterator, start, end, new HashSet<Attribute>(Arrays + // .asList(attributes))); + // END android-removed + // BEGIN android-added + this(iterator, start, end, (attributes == null? null : + new HashSet<Attribute>(Arrays.asList(attributes)))); + // END android-added + } + + public AttributedString(String value) { + if (value == null) { + throw new NullPointerException(); + } + text = value; + attributeMap = new HashMap<Attribute, List<Range>>(11); + } + + public AttributedString(String value, + Map<? extends AttributedCharacterIterator.Attribute, ?> attributes) { + if (value == null) { + throw new NullPointerException(); + } + if (value.length() == 0 && !attributes.isEmpty()) { + // text.0B=Cannot add attributes to empty string + throw new IllegalArgumentException(Messages.getString("text.0B")); //$NON-NLS-1$ + } + text = value; + attributeMap = new HashMap<Attribute, List<Range>>( + (attributes.size() * 4 / 3) + 1); + Iterator<?> it = attributes.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry<?, ?> entry = (Map.Entry<?, ?>) it.next(); + ArrayList<Range> ranges = new ArrayList<Range>(1); + ranges.add(new Range(0, text.length(), entry.getValue())); + attributeMap.put((AttributedCharacterIterator.Attribute) entry + .getKey(), ranges); + } + } + + public void addAttribute(AttributedCharacterIterator.Attribute attribute, + Object value) { + if (null == attribute) { + throw new NullPointerException(); + } + if (text.length() == 0) { + throw new IllegalArgumentException(); + } + + List<Range> ranges = attributeMap.get(attribute); + if (ranges == null) { + ranges = new ArrayList<Range>(1); + attributeMap.put(attribute, ranges); + } else { + ranges.clear(); + } + ranges.add(new Range(0, text.length(), value)); + } + + public void addAttribute(AttributedCharacterIterator.Attribute attribute, + Object value, int start, int end) { + if (null == attribute) { + throw new NullPointerException(); + } + if (start < 0 || end > text.length() || start >= end) { + throw new IllegalArgumentException(); + } + + if (value == null) { + return; + } + + List<Range> ranges = attributeMap.get(attribute); + if (ranges == null) { + ranges = new ArrayList<Range>(1); + ranges.add(new Range(start, end, value)); + attributeMap.put(attribute, ranges); + return; + } + ListIterator<Range> it = ranges.listIterator(); + while (it.hasNext()) { + Range range = it.next(); + if (end <= range.start) { + it.previous(); + break; + } else if (start < range.end + || (start == range.end && (value == null ? range.value == null + : value.equals(range.value)))) { + Range r1 = null, r3; + it.remove(); + r1 = new Range(range.start, start, range.value); + r3 = new Range(end, range.end, range.value); + + while (end > range.end && it.hasNext()) { + range = it.next(); + if (end <= range.end) { + if (end > range.start + || (end == range.start && (value == null ? range.value == null + : value.equals(range.value)))) { + it.remove(); + r3 = new Range(end, range.end, range.value); + break; + } + } else { + it.remove(); + } + } + + if (value == null ? r1.value == null : value.equals(r1.value)) { + if (value == null ? r3.value == null : value + .equals(r3.value)) { + it.add(new Range(r1.start < start ? r1.start : start, + r3.end > end ? r3.end : end, r1.value)); + } else { + it.add(new Range(r1.start < start ? r1.start : start, + end, r1.value)); + if (r3.start < r3.end) { + it.add(r3); + } + } + } else { + if (value == null ? r3.value == null : value + .equals(r3.value)) { + if (r1.start < r1.end) { + it.add(r1); + } + it.add(new Range(start, r3.end > end ? r3.end : end, + r3.value)); + } else { + if (r1.start < r1.end) { + it.add(r1); + } + it.add(new Range(start, end, value)); + if (r3.start < r3.end) { + it.add(r3); + } + } + } + return; + } + } + it.add(new Range(start, end, value)); + } + + public void addAttributes( + Map<? extends AttributedCharacterIterator.Attribute, ?> attributes, + int start, int end) { + Iterator<?> it = attributes.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry<?, ?> entry = (Map.Entry<?, ?>) it.next(); + addAttribute( + (AttributedCharacterIterator.Attribute) entry.getKey(), + entry.getValue(), start, end); + } + } + + public AttributedCharacterIterator getIterator() { + return new AttributedIterator(this); + } + + public AttributedCharacterIterator getIterator( + AttributedCharacterIterator.Attribute[] attributes) { + return new AttributedIterator(this, attributes, 0, text.length()); + } + + public AttributedCharacterIterator getIterator( + AttributedCharacterIterator.Attribute[] attributes, int start, + int end) { + return new AttributedIterator(this, attributes, start, end); + } +} diff --git a/text/src/main/java/java/text/Bidi.java b/text/src/main/java/java/text/Bidi.java new file mode 100644 index 0000000..04e7df8 --- /dev/null +++ b/text/src/main/java/java/text/Bidi.java @@ -0,0 +1,554 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package java.text; + +import java.awt.font.NumericShaper; +import java.awt.font.TextAttribute; +import java.util.Arrays; +import java.util.LinkedList; + +import org.apache.harmony.text.BidiRun; +import org.apache.harmony.text.BidiWrapper; +import org.apache.harmony.text.internal.nls.Messages; + +/** + * Bidi is the class providing the bidirectional algorithm. The algorithm is + * defined in the Unicode Standard Annex #9, version 13, also described in The + * Unicode Standard, Version 4.0 . + * + * Use a Bidi object to get the information on the position reordering of a + * bidirectional text, such as Arabic or Hebrew. The natural display ordering of + * horizontal text in these languages is from right to left, while they order + * numbers from left to right. + * + * If the text contains multiple runs, the information of each run can be + * obtained from the run index. The level of any particular run indicates the + * direction of the text as well as the nesting level. Left-to-right runs have + * even levels while right-to-left runs have odd levels. + * + */ +public final class Bidi { + /** + * Constant that indicates the default base level. If there is no strong + * character, then set the paragraph level to 0 (left-to-right). + */ + public static final int DIRECTION_DEFAULT_LEFT_TO_RIGHT = -2; + + /** + * Constant that indicates the default base level. If there is no strong + * character, then set the paragraph level to 1 (right-to-left). + */ + public static final int DIRECTION_DEFAULT_RIGHT_TO_LEFT = -1; + + /** + * Constant that specifies the default base level as 0 (left-to-right). + */ + public static final int DIRECTION_LEFT_TO_RIGHT = 0; + + /** + * Constant that specifies the default base level as 1 (right-to-left). + */ + public static final int DIRECTION_RIGHT_TO_LEFT = 1; + + /** + * Create a Bidi object from the AttributedCharacterIterator of a paragraph + * text. + * + * The RUN_DIRECTION attribute determines the base direction of the + * bidirectional text. If it's not specified explicitly, the algorithm uses + * DIRECTION_DEFAULT_LEFT_TO_RIGHT by default. + * + * The BIDI_EMBEDDING attribute specifies the level of embedding for each + * character. Values between -1 and -62 denote overrides at the level's + * absolute value, values from 1 to 62 indicate embeddings, and the 0 value + * indicates the level is calculated by the algorithm automatically. For the + * character with no BIDI_EMBEDDING attribute or with a improper attribute + * value, such as a null value, the algorithm treats its embedding level as + * 0. + * + * The NUMERIC_SHAPING attribute specifies the instance of NumericShaper + * used to convert European digits to other decimal digits before performing + * the bidi algorithm. + * + * @param paragraph + * + * TODO Make these proper links again (problem with core vs. framework). + * see TextAttribute.BIDI_EMBEDDING + * see TextAttribute.NUMERIC_SHAPING + * see TextAttribute.RUN_DIRECTION + */ + public Bidi(AttributedCharacterIterator paragraph) { + if (paragraph == null) { + // text.14=paragraph is null + throw new IllegalArgumentException(Messages.getString("text.14")); //$NON-NLS-1$ + } + + int begin = paragraph.getBeginIndex(); + int end = paragraph.getEndIndex(); + int length = end - begin; + char text[] = new char[length + 1]; // One more char for + // AttributedCharacterIterator.DONE + + if (length != 0) { + text[0] = paragraph.first(); + } else { + paragraph.first(); + } + + // First check the RUN_DIRECTION attribute. + int flags = DIRECTION_DEFAULT_LEFT_TO_RIGHT; + + Object direction = paragraph.getAttribute(TextAttribute.RUN_DIRECTION); + if (direction != null && direction instanceof Boolean) { + if (direction.equals(TextAttribute.RUN_DIRECTION_LTR)) { + flags = DIRECTION_LEFT_TO_RIGHT; + } else { + flags = DIRECTION_RIGHT_TO_LEFT; + } + } + + // Retrieve the text and gather BIDI_EMBEDDINGS + byte embeddings[] = null; + for (int textLimit = 1, i = 1; i < length; textLimit = paragraph + .getRunLimit(TextAttribute.BIDI_EMBEDDING) + - begin + 1) { + Object embedding = paragraph + .getAttribute(TextAttribute.BIDI_EMBEDDING); + if (embedding != null && embedding instanceof Integer) { + int embLevel = ((Integer) embedding).intValue(); + + if (embeddings == null) { + embeddings = new byte[length]; + } + + for (; i < textLimit; i++) { + text[i] = paragraph.next(); + embeddings[i - 1] = (byte) embLevel; + } + } else { + for (; i < textLimit; i++) { + text[i] = paragraph.next(); + } + } + } + + // Apply NumericShaper to the text + Object numericShaper = paragraph + .getAttribute(TextAttribute.NUMERIC_SHAPING); + if (numericShaper != null && numericShaper instanceof NumericShaper) { + ((NumericShaper) numericShaper).shape(text, 0, length); + } + + long pBidi = createUBiDi(text, 0, embeddings, 0, length, flags); + readBidiInfo(pBidi); + BidiWrapper.ubidi_close(pBidi); + } + + /** + * Create a Bidi object. + * + * @param text + * the char array of the paragraph text. + * @param textStart + * the start offset of the text array to perform the algorithm. + * @param embeddings + * the embedding level array of the paragraph text, specifying + * the embedding level information for each character. Values + * between -1 and -62 denote overrides at the level's absolute + * value, values from 1 to 62 indicate embeddings, and the 0 + * value indicates the level is calculated by the algorithm + * automatically. + * @param embStart + * the start offset of the embeddings array to perform the + * algorithm. + * @param paragraphLength + * the length of the text to perform the algorithm. It must be + * text.length >= textStart + paragraphLength, and + * embeddings.length >= embStart + paragraphLength. + * @param flags + * indicates the base direction of the bidirectional text. It is + * expected that this will be one of the direction constant + * values defined in this class. An unknown value is treated as + * DIRECTION_DEFAULT_LEFT_TO_RIGHT. + * + * @see #DIRECTION_LEFT_TO_RIGHT + * @see #DIRECTION_RIGHT_TO_LEFT + * @see #DIRECTION_DEFAULT_RIGHT_TO_LEFT + * @see #DIRECTION_DEFAULT_LEFT_TO_RIGHT + */ + public Bidi(char[] text, int textStart, byte[] embeddings, int embStart, + int paragraphLength, int flags) { + if (textStart < 0) { + // text.0D=Negative textStart value {0} + throw new IllegalArgumentException(Messages.getString( + "text.0D", textStart)); //$NON-NLS-1$ + } + if (embStart < 0) { + // text.10=Negative embStart value {0} + throw new IllegalArgumentException(Messages.getString( + "text.10", embStart)); //$NON-NLS-1$ + } + if (paragraphLength < 0) { + // text.11=Negative paragraph length {0} + throw new IllegalArgumentException(Messages.getString( + "text.11", paragraphLength)); //$NON-NLS-1$ + } + long pBidi = createUBiDi(text, textStart, embeddings, embStart, + paragraphLength, flags); + readBidiInfo(pBidi); + BidiWrapper.ubidi_close(pBidi); + } + + /** + * Create a Bidi object. + * + * @param paragraph + * the String containing the paragraph text to perform the + * algorithm. + * @param flags + * indicates the base direction of the bidirectional text. It is + * expected that this will be one of the direction constant + * values defined in this class. An unknown value is treated as + * DIRECTION_DEFAULT_LEFT_TO_RIGHT. + * + * @see #DIRECTION_LEFT_TO_RIGHT + * @see #DIRECTION_RIGHT_TO_LEFT + * @see #DIRECTION_DEFAULT_RIGHT_TO_LEFT + * @see #DIRECTION_DEFAULT_LEFT_TO_RIGHT + */ + public Bidi(String paragraph, int flags) { + this((paragraph == null ? null : paragraph.toCharArray()), 0, null, 0, + (paragraph == null ? 0 : paragraph.length()), flags); + } + + // create the native UBiDi struct, need to be closed with ubidi_close(). + private static long createUBiDi(char[] text, int textStart, + byte[] embeddings, int embStart, int paragraphLength, int flags) { + char[] realText = null; + + byte[] realEmbeddings = null; + + if (text == null || text.length - textStart < paragraphLength) { + throw new IllegalArgumentException(); + } + realText = new char[paragraphLength]; + System.arraycopy(text, textStart, realText, 0, paragraphLength); + + if (embeddings != null) { + if (embeddings.length - embStart < paragraphLength) { + throw new IllegalArgumentException(); + } + if (paragraphLength > 0) { + Bidi temp = new Bidi(text, textStart, null, 0, paragraphLength, + flags); + realEmbeddings = new byte[paragraphLength]; + System.arraycopy(temp.offsetLevel, 0, realEmbeddings, 0, + paragraphLength); + for (int i = 0; i < paragraphLength; i++) { + byte e = embeddings[i]; + if (e < 0) { + realEmbeddings[i] = (byte) (BidiWrapper.UBIDI_LEVEL_OVERRIDE - e); + } else if (e > 0) { + realEmbeddings[i] = e; + } else { + realEmbeddings[i] |= (byte) BidiWrapper.UBIDI_LEVEL_OVERRIDE; + } + } + } + } + + if (flags > 1 || flags < -2) { + flags = 0; + } + + long bidi = BidiWrapper.ubidi_open(); + BidiWrapper.ubidi_setPara(bidi, realText, paragraphLength, + (byte) flags, realEmbeddings); + return bidi; + } + + // private constructor, used by createLineBidi() + private Bidi(long pBidi) { + readBidiInfo(pBidi); + } + + // read info from the native UBiDi struct + private void readBidiInfo(long pBidi) { + + length = BidiWrapper.ubidi_getLength(pBidi); + + offsetLevel = (length == 0) ? null : BidiWrapper.ubidi_getLevels(pBidi); + + baseLevel = BidiWrapper.ubidi_getParaLevel(pBidi); + + int runCount = BidiWrapper.ubidi_countRuns(pBidi); + if (runCount == 0) { + unidirectional = true; + runs = null; + } else if (runCount < 0) { + runs = null; + } else { + runs = BidiWrapper.ubidi_getRuns(pBidi); + + // Simplified case for one run which has the base level + if (runCount == 1 && runs[0].getLevel() == baseLevel) { + unidirectional = true; + runs = null; + } + } + + direction = BidiWrapper.ubidi_getDirection(pBidi); + } + + private int baseLevel; + + private int length; + + private byte[] offsetLevel; + + private BidiRun[] runs; + + private int direction; + + private boolean unidirectional; + + /** + * Return whether the base level is from left to right. + * + * @return true if the base level is from left to right. + */ + public boolean baseIsLeftToRight() { + return baseLevel % 2 == 0 ? true : false; + } + + /** + * Create a new Bidi object containing the information of one line from this + * object. + * + * @param lineStart + * the start offset of the line. + * @param lineLimit + * the limit of the line. + * @return the new line Bidi object. In this new object, the indices will + * range from 0 to (limit - start - 1). + */ + public Bidi createLineBidi(int lineStart, int lineLimit) { + if (lineStart < 0 || lineLimit < 0 || lineLimit > length + || lineStart > lineLimit) { + // text.12=Invalid ranges (start={0}, limit={1}, length={2}) + throw new IllegalArgumentException(Messages.getString( + "text.12", new Object[] { lineStart, lineLimit, length })); //$NON-NLS-1$ + } + char[] text = new char[this.length]; + Arrays.fill(text, 'a'); + byte[] embeddings = new byte[this.length]; + for (int i = 0; i < embeddings.length; i++) { + embeddings[i] = (byte) -this.offsetLevel[i]; + } + + int dir = this.baseIsLeftToRight() ? Bidi.DIRECTION_LEFT_TO_RIGHT + : Bidi.DIRECTION_RIGHT_TO_LEFT; + + long parent = createUBiDi(text, 0, embeddings, 0, this.length, dir); + + long line = BidiWrapper.ubidi_setLine(parent, lineStart, lineLimit); + Bidi result = new Bidi(line); + BidiWrapper.ubidi_close(line); + BidiWrapper.ubidi_close(parent); + return result; + } + + /** + * Return the base level. + * + * @return the int value of the base level. + */ + public int getBaseLevel() { + return baseLevel; + } + + /** + * Return the length of the text in the Bidi object. + * + * @return the int value of the length. + */ + public int getLength() { + return length; + } + + /** + * Return the level of a specified character. + * + * @param offset + * the offset of the character. + * @return the int value of the level. + */ + public int getLevelAt(int offset) { + try { + return offsetLevel[offset] & ~BidiWrapper.UBIDI_LEVEL_OVERRIDE; + } catch (RuntimeException e) { + return baseLevel; + } + } + + /** + * Return the number of runs in the bidirectional text. + * + * @return the int value of runs, at least 1. + */ + public int getRunCount() { + return unidirectional ? 1 : runs.length; + } + + /** + * Return the level of a specified run. + * + * @param run + * the index of the run. + * @return the level of the run. + */ + public int getRunLevel(int run) { + return unidirectional ? baseLevel : runs[run].getLevel(); + } + + /** + * Return the limit offset of a specified run. + * + * @param run + * the index of the run. + * @return the limit offset of the run. + */ + public int getRunLimit(int run) { + return unidirectional ? length : runs[run].getLimit(); + } + + /** + * Return the start offset of a specified run. + * + * @param run + * the index of the run. + * @return the start offset of the run. + */ + public int getRunStart(int run) { + return unidirectional ? 0 : runs[run].getStart(); + } + + /** + * Return whether the text is from left to right, that is, both the base + * direction and the text direction is from left to right. + * + * @return true if the text is from left to right. + */ + public boolean isLeftToRight() { + return direction == BidiWrapper.UBiDiDirection_UBIDI_LTR; + } + + /** + * Return whether the text direction is mixed. + * + * @return true if the text direction is mixed. + */ + public boolean isMixed() { + return direction == BidiWrapper.UBiDiDirection_UBIDI_MIXED; + } + + /** + * Return whether the text is from right to left, that is, both the base + * direction and the text direction is from right to left. + * + * @return true if the text is from right to left. + */ + public boolean isRightToLeft() { + return direction == BidiWrapper.UBiDiDirection_UBIDI_RTL; + } + + /** + * Reorder a range of objects according to their specified levels. This is a + * convenience function that does not use a Bidi object. The range of + * objects at index from objectStart to objectStart + count will be + * reordered according to the range of levels at index from levelStart to + * levelStart + count. + * + * @param levels + * the level array, which is already determined. + * @param levelStart + * the start offset of the range of the levels. + * @param objects + * the object array to reorder. + * @param objectStart + * the start offset of the range of objects. + * @param count + * the count of the range of objects to reorder. + */ + public static void reorderVisually(byte[] levels, int levelStart, + Object[] objects, int objectStart, int count) { + if (count < 0 || levelStart < 0 || objectStart < 0 + || count > levels.length - levelStart + || count > objects.length - objectStart) { + // text.13=Invalid ranges (levels={0}, levelStart={1}, objects={2}, + // objectStart={3}, count={4}) + throw new IllegalArgumentException(Messages.getString("text.13", //$NON-NLS-1$ + new Object[] { levels.length, levelStart, objects.length, + objectStart, count })); + } + byte[] realLevels = new byte[count]; + System.arraycopy(levels, levelStart, realLevels, 0, count); + + int[] indices = BidiWrapper.ubidi_reorderVisual(realLevels, count); + + LinkedList<Object> result = new LinkedList<Object>(); + for (int i = 0; i < count; i++) { + result.addLast(objects[objectStart + indices[i]]); + } + + System.arraycopy(result.toArray(), 0, objects, objectStart, count); + } + + /** + * Return whether a range of characters of a text requires a Bidi object to + * display properly. + * + * @param text + * the char array of the text. + * @param start + * the start offset of the range of characters. + * @param limit + * the limit offset of the range of characters. + * @return true if the range of characters requires a Bidi object. + */ + public static boolean requiresBidi(char[] text, int start, int limit) { + int length = text.length; + if (limit < 0 || start < 0 || start > limit || limit > length) { + throw new IllegalArgumentException(); + } + Bidi bidi = new Bidi(text, start, null, 0, limit - start, 0); + return !bidi.isLeftToRight(); + } + + /** + * Return the internal message of the Bidi object, used in debugging. + * + * @return a string containing the internal message. + */ + @Override + public String toString() { + return super.toString() + + "[direction: " + direction + " baselevel: " + baseLevel //$NON-NLS-1$ //$NON-NLS-2$ + + " length: " + length + " runs: " + (unidirectional ? "null" : runs.toString()) + "]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + } +} diff --git a/text/src/main/java/java/text/BreakIterator.java b/text/src/main/java/java/text/BreakIterator.java new file mode 100644 index 0000000..f1eb303 --- /dev/null +++ b/text/src/main/java/java/text/BreakIterator.java @@ -0,0 +1,458 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package java.text; + +import java.util.Locale; + +/** + * This class is used to locate the boundaries of text. Instance of this class + * can be got by some factory methods: + * <ul> + * <li> + * <code>getCharacterInstance()<code> returns a BreakIterator that iterate the + * logical characters without worrying about how the character is stored. For + * example, some character may be stored in more than one Unicode code point + * according to Unicode specification, this character can handle the logical + * characters with multi code points.</li> + * <li> + * <code>getWordInstance()<code> returns a <code>BreakIterator</code> that + * iterate the word-breaks. The beginning and end of each word(including numbers) + * is treated as boundary position. Whitespace and punctuation are kept separate + * from real words.</li> + * <li> + * <code>getSentenceInstance()</code> returns a BreakIterator that iterate the + * sentence-breaks.</li> + * <li><code>getLineInstance()</code> returns a BreakIterator that iterate the + * line-breaks which can be used to wrap lines. This iterator can handle whitespaces, + * hyphens and punctuations. + * </ul> + * + * <code>BreakIterator</code> uses <code>CharacterIterator</code> to perform the + * analysis, so that any storage which provides <code>CharacterIterator</code> + * interface. + * + * @see CharacterIterator + */ +public abstract class BreakIterator implements Cloneable { + + /* + * ----------------------------------------------------------------------- + * constants + * ----------------------------------------------------------------------- + */ + /** + * This constant is returned by iterate methods like previous() or next() if + * they have returned all valid boundaries. + */ + public static final int DONE = -1; + + private static final int LONG_LENGTH = 8; + + private static final int INT_LENGTH = 4; + + private static final int SHORT_LENGTH = 2; + + /* + * ----------------------------------------------------------------------- + * variables + * ----------------------------------------------------------------------- + */ + // the wrapped ICU implementation + com.ibm.icu4jni.text.BreakIterator wrapped; + + /* + * ----------------------------------------------------------------------- + * constructors + * ----------------------------------------------------------------------- + */ + /** + * Default constructor, just for invocation by subclass. + */ + protected BreakIterator() { + super(); + } + + /* + * wrapping constructor + */ + BreakIterator(com.ibm.icu4jni.text.BreakIterator iterator) { + wrapped = iterator; + } + + /* + * ----------------------------------------------------------------------- + * methods + * ----------------------------------------------------------------------- + */ + /** + * Return all supported locales. + * + * @return all supported locales + */ + public static Locale[] getAvailableLocales() { + return com.ibm.icu4jni.text.BreakIterator.getAvailableLocales(); + } + + /** + * Return a new instance of BreakIterator used to iterate characters using + * default locale. + * + * @return a new instance of BreakIterator used to iterate characters using + * default locale. + */ + public static BreakIterator getCharacterInstance() { + return new RuleBasedBreakIterator(com.ibm.icu4jni.text.BreakIterator + .getCharacterInstance()); + } + + /** + * Return a new instance of BreakIterator used to iterate characters using + * given locale. + * + * @param where + * the given locale + * @return a new instance of BreakIterator used to iterate characters using + * given locale. + */ + public static BreakIterator getCharacterInstance(Locale where) { + if (where == null) { + throw new NullPointerException(); + } + + return new RuleBasedBreakIterator(com.ibm.icu4jni.text.BreakIterator + .getCharacterInstance(where)); + } + + /** + * Return a new instance of BreakIterator used to iterate line-breaks using + * default locale. + * + * @return a new instance of BreakIterator used to iterate line-breaks using + * default locale. + */ + public static BreakIterator getLineInstance() { + return new RuleBasedBreakIterator(com.ibm.icu4jni.text.BreakIterator + .getLineInstance()); + } + + /** + * Return a new instance of BreakIterator used to iterate line-breaks using + * given locale. + * + * @param where + * the given locale + * @return a new instance of BreakIterator used to iterate line-breaks using + * given locale. + */ + public static BreakIterator getLineInstance(Locale where) { + if (where == null) { + throw new NullPointerException(); + } + + return new RuleBasedBreakIterator(com.ibm.icu4jni.text.BreakIterator + .getLineInstance(where)); + } + + /** + * Return a new instance of BreakIterator used to iterate sentence-breaks + * using default locale. + * + * @return a new instance of BreakIterator used to iterate sentence-breaks + * using default locale. + */ + public static BreakIterator getSentenceInstance() { + return new RuleBasedBreakIterator(com.ibm.icu4jni.text.BreakIterator + .getSentenceInstance()); + } + + /** + * Return a new instance of BreakIterator used to iterate sentence-breaks + * using given locale. + * + * @param where + * the given locale + * @return a new instance of BreakIterator used to iterate sentence-breaks + * using given locale. + */ + public static BreakIterator getSentenceInstance(Locale where) { + if (where == null) { + throw new NullPointerException(); + } + + return new RuleBasedBreakIterator(com.ibm.icu4jni.text.BreakIterator + .getSentenceInstance(where)); + } + + /** + * Return a new instance of BreakIterator used to iterate word-breaks using + * default locale. + * + * @return a new instance of BreakIterator used to iterate word-breaks using + * default locale. + */ + public static BreakIterator getWordInstance() { + return new RuleBasedBreakIterator(com.ibm.icu4jni.text.BreakIterator + .getWordInstance()); + } + + /** + * Return a new instance of BreakIterator used to iterate word-breaks using + * given locale. + * + * @param where + * the given locale + * @return a new instance of BreakIterator used to iterate word-breaks using + * given locale. + */ + public static BreakIterator getWordInstance(Locale where) { + if (where == null) { + throw new NullPointerException(); + } + + return new RuleBasedBreakIterator(com.ibm.icu4jni.text.BreakIterator + .getWordInstance(where)); + } + + /** + * Return true if the given offset is a boundary position. If this method + * returns true, the current iteration position is set to the given + * position; if the function returns false, the current iteration position + * is set as though following() had been called. + * + * @param offset + * the given offset to check + * @return true if the given offset is a boundary position + */ + public boolean isBoundary(int offset) { + return wrapped.isBoundary(offset); + } + + /** + * Return the position of last boundary precede the given offset, and set + * current position to returned value, or <code>DONE</code> if the given + * offset specifies the starting position. + * <p> + * <code>IllegalArgumentException</code> will be thrown if given offset is + * invalid. + * </p> + * + * @param offset + * the given start position to be searched for + * @return the position of last boundary precede the given offset + */ + public int preceding(int offset) { + return wrapped.preceding(offset); + } + + /** + * Set the new text string to be analyzed, the current position will be + * reset to beginning of this new string, and the old string will lost. + * + * @param newText + * the new text string to be analyzed + */ + public void setText(String newText) { + wrapped.setText(newText); + } + + /* + * ----------------------------------------------------------------------- + * abstract methods + * ----------------------------------------------------------------------- + */ + /** + * Return this iterator's current position. + * + * @return this iterator's current position + */ + public abstract int current(); + + /** + * Set this iterator's current position to the first boundary, and return + * this position. + * + * @return the position of first boundary + */ + public abstract int first(); + + /** + * Set the position of the first boundary following the given offset, and + * return this position. If there is no boundary after the given offset, + * return DONE. + * <p> + * <code>IllegalArgumentException</code> will be thrown if given offset is + * invalid. + * </p> + * + * @param offset + * the given position to be searched for + * @return the position of the first boundary following the given offset + */ + public abstract int following(int offset); + + /** + * Return a <code>CharacterIterator</code> which represents the text being + * analyzed. Please note that the returned value is probably the internal + * iterator used by this object, so that if the invoker want to modify the + * status of the returned iterator, a clone operation at first is + * recommended. + * + * @return a <code>CharacterIterator</code> which represents the text + * being analyzed. + */ + public abstract CharacterIterator getText(); + + /** + * Set this iterator's current position to the last boundary, and return + * this position. + * + * @return the position of last boundary + */ + public abstract int last(); + + /** + * Set this iterator's current position to the next boundary after current + * position, and return this position. Return <code>DONE</code> if no + * boundary found after current position. + * + * @return the position of last boundary + */ + public abstract int next(); + + /** + * Set this iterator's current position to the next boundary after the given + * position, and return this position. Return <code>DONE</code> if no + * boundary found after the given position. + * + * @param n + * the given position. + * @return the position of last boundary + */ + public abstract int next(int n); + + /** + * Set this iterator's current position to the previous boundary before + * current position, and return this position. Return <code>DONE</code> if + * no boundary found before current position. + * + * @return the position of last boundary + */ + public abstract int previous(); + + /** + * Set new text to be analyzed by given <code>CharacterIterator</code>. + * The position will be reset to the beginning of the new text, and other + * status of this iterator will be kept. + * + * @param newText + * the given <code>CharacterIterator</code> refer to the text + * to be analyzed + */ + public abstract void setText(CharacterIterator newText); + + /* + * ----------------------------------------------------------------------- + * methods override Object + * ----------------------------------------------------------------------- + */ + /** + * Create copy of this iterator, all status including current position is + * kept. + * + * @return copy of this iterator + */ + @Override + public Object clone() { + try { + BreakIterator cloned = (BreakIterator) super.clone(); + cloned.wrapped = (com.ibm.icu4jni.text.BreakIterator) wrapped.clone(); + return cloned; + } catch (CloneNotSupportedException e) { + throw new InternalError(e.getMessage()); + } + } + + /** + * Get a long value from the given byte array, start from given offset. + * + * @param buf + * the bytes to be converted + * @param offset + * the start position of conversion + * @return the converted long value + */ + protected static long getLong(byte[] buf, int offset) { + if (null == buf) { + throw new NullPointerException(); + } + if (offset < 0 || buf.length - offset < LONG_LENGTH) { + throw new ArrayIndexOutOfBoundsException(); + } + long result = 0; + for (int i = offset; i < offset + LONG_LENGTH; i++) { + result = (result << 8) | (buf[i] & 0xff); + } + return result; + } + + /** + * Get an int value from the given byte array, start from given offset. + * + * @param buf + * the bytes to be converted + * @param offset + * the start position of conversion + * @return the converted int value + */ + protected static int getInt(byte[] buf, int offset) { + if (null == buf) { + throw new NullPointerException(); + } + if (offset < 0 || buf.length - INT_LENGTH < offset) { + throw new ArrayIndexOutOfBoundsException(); + } + int result = 0; + for (int i = offset; i < offset + INT_LENGTH; i++) { + result = (result << 8) | (buf[i] & 0xff); + } + return result; + } + + /** + * Get a short value from the given byte array, start from given offset. + * + * @param buf + * the bytes to be converted + * @param offset + * the start position of conversion + * @return the converted short value + */ + protected static short getShort(byte[] buf, int offset) { + if (null == buf) { + throw new NullPointerException(); + } + if (offset < 0 || buf.length - SHORT_LENGTH < offset) { + throw new ArrayIndexOutOfBoundsException(); + } + short result = 0; + for (int i = offset; i < offset + SHORT_LENGTH; i++) { + result = (short) ((result << 8) | (buf[i] & 0xff)); + } + return result; + } +} diff --git a/text/src/main/java/java/text/CharacterIterator.java b/text/src/main/java/java/text/CharacterIterator.java new file mode 100644 index 0000000..195beeb --- /dev/null +++ b/text/src/main/java/java/text/CharacterIterator.java @@ -0,0 +1,115 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package java.text; + +/** + * CharacterIterator is used to sequence over a group of characters. The + * iteration starts at the begin index in the group of character and continues + * to one index before the end index. + */ +public interface CharacterIterator extends Cloneable { + + /** + * A constant which indicates there is no character. + */ + public static final char DONE = '\uffff'; + + /** + * Returns a new CharacterIterator with the same properties. + * + * @return a shallow copy of this CharacterIterator + * + * @see java.lang.Cloneable + */ + public Object clone(); + + /** + * Returns the character at the current index. + * + * @return the current character, or DONE if the current index is past the + * end + */ + public char current(); + + /** + * Sets the current position to the begin index and returns the character at + * the begin index. + * + * @return the character at the begin index + */ + public char first(); + + /** + * Returns the begin index. + * + * @return the index of the first character to iterate + */ + public int getBeginIndex(); + + /** + * Returns the end index. + * + * @return the index one past the last character to iterate + */ + public int getEndIndex(); + + /** + * Returns the current index. + * + * @return the current index + */ + public int getIndex(); + + /** + * Sets the current position to the end index - 1 and returns the character + * at the current position. + * + * @return the character before the end index + */ + public char last(); + + /** + * Increments the current index and returns the character at the new index. + * + * @return the character at the next index, or DONE if the next index is + * past the end + */ + public char next(); + + /** + * Decrements the current index and returns the character at the new index. + * + * @return the character at the previous index, or DONE if the previous + * index is past the beginning + */ + public char previous(); + + /** + * Sets the current index. + * + * @param location The index the <code>CharacterIterator</code> is set to. + * + * @return the character at the new index, or DONE if the index is past the + * end + * + * @exception IllegalArgumentException + * when the new index is less than the begin index or greater + * than the end index + */ + public char setIndex(int location); +} diff --git a/text/src/main/java/java/text/ChoiceFormat.java b/text/src/main/java/java/text/ChoiceFormat.java new file mode 100644 index 0000000..5a6bb69 --- /dev/null +++ b/text/src/main/java/java/text/ChoiceFormat.java @@ -0,0 +1,411 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package java.text; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; + +/** + * ChoiceFormat is used to associate strings with ranges of double values. The + * strings and ranges are either specified using arrays or with a pattern which + * is parsed to determine the Strings and ranges. + */ + +public class ChoiceFormat extends NumberFormat { + + private static final long serialVersionUID = 1795184449645032964L; + + private double[] choiceLimits; + + private String[] choiceFormats; + + /** + * Constructs a new ChoiceFormat with the specified ranges and associated + * strings. + * + * @param limits + * an array of double, the ranges are greater or equal to the + * value in lower index up to less than the value in the next + * higher index. The bounds of the lowest and highest indexes are + * negative and positive infinity. + * @param formats + * the strings associated with the ranges. The lower bound of the + * associated range is at the same index as the string. + */ + public ChoiceFormat(double[] limits, String[] formats) { + setChoices(limits, formats); + } + + /** + * Constructs a new ChoiceFormat with the strings and ranges parsed from the + * specified pattern. + * + * @param template + * the pattern of strings and ranges + * + * @exception IllegalArgumentException + * then an error occurs parsing the pattern + */ + public ChoiceFormat(String template) { + applyPattern(template); + } + + /** + * Parses the pattern to determine new strings and ranges for this + * ChoiceFormat. + * + * @param template + * the pattern of strings and ranges + * + * @exception IllegalArgumentException + * then an error occurs parsing the pattern + */ + public void applyPattern(String template) { + double[] limits = new double[5]; + List<String> formats = new ArrayList<String>(); + int length = template.length(), limitCount = 0, index = 0; + StringBuffer buffer = new StringBuffer(); + NumberFormat format = NumberFormat.getInstance(Locale.US); + ParsePosition position = new ParsePosition(0); + while (true) { + index = skipWhitespace(template, index); + if (index >= length) { + if (limitCount == limits.length) { + choiceLimits = limits; + } else { + choiceLimits = new double[limitCount]; + System.arraycopy(limits, 0, choiceLimits, 0, limitCount); + } + choiceFormats = new String[formats.size()]; + for (int i = 0; i < formats.size(); i++) { + choiceFormats[i] = formats.get(i); + } + return; + } + + position.setIndex(index); + Number value = format.parse(template, position); + index = skipWhitespace(template, position.getIndex()); + if (position.getErrorIndex() != -1 || index >= length) { + // Fix Harmony 540 + choiceLimits = new double[0]; + choiceFormats = new String[0]; + return; + } + char ch = template.charAt(index++); + if (limitCount == limits.length) { + double[] newLimits = new double[limitCount * 2]; + System.arraycopy(limits, 0, newLimits, 0, limitCount); + limits = newLimits; + } + double next; + switch (ch) { + case '#': + case '\u2264': + next = value.doubleValue(); + break; + case '<': + next = nextDouble(value.doubleValue()); + break; + default: + throw new IllegalArgumentException(); + } + if (limitCount > 0 && next <= limits[limitCount - 1]) { + throw new IllegalArgumentException(); + } + buffer.setLength(0); + position.setIndex(index); + upTo(template, position, buffer, '|'); + index = position.getIndex(); + limits[limitCount++] = next; + formats.add(buffer.toString()); + } + } + + /** + * Returns a new instance of ChoiceFormat with the same ranges and strings + * as this ChoiceFormat. + * + * @return a shallow copy of this ChoiceFormat + * + * @see java.lang.Cloneable + */ + @Override + public Object clone() { + ChoiceFormat clone = (ChoiceFormat) super.clone(); + clone.choiceLimits = choiceLimits.clone(); + clone.choiceFormats = choiceFormats.clone(); + return clone; + } + + /** + * Compares the specified object to this ChoiceFormat and answer if they are + * equal. The object must be an instance of ChoiceFormat and have the same + * limits and formats. + * + * @param object + * the object to compare with this object + * @return true if the specified object is equal to this ChoiceFormat, false + * otherwise + * + * @see #hashCode + */ + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (!(object instanceof ChoiceFormat)) { + return false; + } + ChoiceFormat choice = (ChoiceFormat) object; + return Arrays.equals(choiceLimits, choice.choiceLimits) + && Arrays.equals(choiceFormats, choice.choiceFormats); + } + + /** + * Appends to the specified StringBuffer the string associated with the + * range in which the specified double value fits. + * + * @param value + * the double to format + * @param buffer + * the StringBuffer + * @param field + * a FieldPosition which is ignored + * @return the StringBuffer parameter <code>buffer</code> + */ + @Override + public StringBuffer format(double value, StringBuffer buffer, + FieldPosition field) { + for (int i = choiceLimits.length - 1; i >= 0; i--) { + if (choiceLimits[i] <= value) { + return buffer.append(choiceFormats[i]); + } + } + return choiceFormats.length == 0 ? buffer : buffer + .append(choiceFormats[0]); + } + + /** + * Appends to the specified StringBuffer the string associated with the + * range in which the specified long value fits. + * + * @param value + * the long to format + * @param buffer + * the StringBuffer + * @param field + * a FieldPosition which is ignored + * @return the StringBuffer parameter <code>buffer</code> + */ + @Override + public StringBuffer format(long value, StringBuffer buffer, + FieldPosition field) { + return format((double) value, buffer, field); + } + + /** + * Returns the Strings associated with the ranges of this ChoiceFormat. + * + * @return an array of String + */ + public Object[] getFormats() { + return choiceFormats; + } + + /** + * Returns the ranges of this ChoiceFormat. + * + * @return an array of double, the ranges are greater or equal to the value + * in lower index up to less than the value in the next higher + * index. The bounds of the lowest and highest indexes are negative + * and positive infinity. + */ + public double[] getLimits() { + return choiceLimits; + } + + /** + * Returns an integer hash code for the receiver. Objects which are equal + * answer the same value for this method. + * + * @return the receiver's hash + * + * @see #equals + */ + @Override + public int hashCode() { + int hashCode = 0; + for (int i = 0; i < choiceLimits.length; i++) { + long v = Double.doubleToLongBits(choiceLimits[i]); + hashCode += (int) (v ^ (v >>> 32)) + choiceFormats[i].hashCode(); + } + return hashCode; + } + + /** + * Returns the double value which is closest to the specified double but + * larger. + * + * @param value + * a double value + * @return the next larger double value + */ + public static final double nextDouble(double value) { + if (value == Double.POSITIVE_INFINITY) { + return value; + } + long bits; + // Handle -0.0 + if (value == 0) { + bits = 0; + } else { + bits = Double.doubleToLongBits(value); + } + return Double.longBitsToDouble(value < 0 ? bits - 1 : bits + 1); + } + + /** + * Returns the double value which is closest to the specified double but + * either larger or smaller as specified. + * + * @param value + * a double value + * @param increment + * true to get a larger value, false to get a smaller value + * @return the next larger or smaller double value + */ + public static double nextDouble(double value, boolean increment) { + return increment ? nextDouble(value) : previousDouble(value); + } + + /** + * Parse a Double from the specified String starting at the index specified + * by the ParsePosition. The String is compared to the strings of this + * ChoiceFormat and if a match occurs, the answer is the lower bound of the + * corresponding range. If the string is successfully parsed, the index of + * the ParsePosition is updated to the index following the parsed text. + * + * @param string + * the String to parse + * @param position + * the ParsePosition, updated on return with the index following + * the parsed text, or on error the index is unchanged and the + * error index is set to the index where the error occurred + * @return a Double resulting from the parse, or Double.NaN if there is an + * error + */ + @Override + public Number parse(String string, ParsePosition position) { + int offset = position.getIndex(); + for (int i = 0; i < choiceFormats.length; i++) { + if (string.startsWith(choiceFormats[i], offset)) { + position.setIndex(offset + choiceFormats[i].length()); + return new Double(choiceLimits[i]); + } + } + position.setErrorIndex(offset); + return new Double(Double.NaN); + } + + /** + * Returns the double value which is closest to the specified double but + * smaller. + * + * @param value + * a double value + * @return the next smaller double value + */ + public static final double previousDouble(double value) { + if (value == Double.NEGATIVE_INFINITY) { + return value; + } + long bits; + // Handle 0.0 + if (value == 0) { + bits = 0x8000000000000000L; + } else { + bits = Double.doubleToLongBits(value); + } + return Double.longBitsToDouble(value <= 0 ? bits + 1 : bits - 1); + } + + /** + * Sets the ranges and associated strings of this ChoiceFormat. + * + * @param limits + * an array of double, the ranges are greater or equal to the + * value in lower index up to less than the value in the next + * higher index. The bounds of the lowest and highest indexes are + * negative and positive infinity. + * @param formats + * the strings associated with the ranges. The lower bound of the + * range is at the same index as the string. + */ + public void setChoices(double[] limits, String[] formats) { + if (limits.length != formats.length) { + throw new IllegalArgumentException(); + } + choiceLimits = limits; + choiceFormats = formats; + } + + private int skipWhitespace(String string, int index) { + int length = string.length(); + while (index < length && Character.isWhitespace(string.charAt(index))) { + index++; + } + return index; + } + + /** + * Returns the pattern of this ChoiceFormat which specified the ranges and + * their associated strings. + * + * @return the pattern + */ + public String toPattern() { + StringBuffer buffer = new StringBuffer(); + for (int i = 0; i < choiceLimits.length; i++) { + if (i != 0) { + buffer.append('|'); + } + String previous = String.valueOf(previousDouble(choiceLimits[i])); + String limit = String.valueOf(choiceLimits[i]); + if (previous.length() < limit.length()) { + buffer.append(previous); + buffer.append('<'); + } else { + buffer.append(limit); + buffer.append('#'); + } + boolean quote = (choiceFormats[i].indexOf('|') != -1); + if (quote) { + buffer.append('\''); + } + buffer.append(choiceFormats[i]); + if (quote) { + buffer.append('\''); + } + } + return buffer.toString(); + } +} diff --git a/text/src/main/java/java/text/CollationElementIterator.java b/text/src/main/java/java/text/CollationElementIterator.java new file mode 100644 index 0000000..a25f554 --- /dev/null +++ b/text/src/main/java/java/text/CollationElementIterator.java @@ -0,0 +1,252 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package java.text; + +// BEGIN android-note +// Fixed Unicode escape sequences to make the file 7-bit clean. +// END android-note + +/** + * <p> + * <code>CollationElementIterator</code> is created by a + * <code>RuleBasedCollator</code> to iterate through a string. The return + * result of each iteration is a 32-bit collation element that defines the + * ordering priority of the next character or sequence of characters in the + * source string. + * </p> + * <p> + * For illustration, consider the following in Spanish: + * </p> + * + * <p> + * <code> + * "ca" -> the first collation element is collation_element('c') and second + * collation element is collation_element('a'). + * </code> + * </p> + * + * <p> + * <code> + * Since "ch" in Spanish sorts as one entity, the below example returns one + * collation element for the two characters 'c' and 'h' + * </code> + * </p> + * + * <p> + * <code> + * "cha" -> the first collation element is collation_element('ch') and second + * collation element is collation_element('a'). + * </code> + * </p> + * <p> + * And in German, + * </p> + * + * <p> + * <code> + * Since the character '\u0086' is a composed character of 'a' and 'e', the iterator + * returns two collation elements for the single character '\u0086' + * </code> + * </p> + * <p> + * <code> + * "\u0086b" -> the first + * collation element is collation_element('a'), the second collation element is + * collation_element('e'), and the third collation element is + * collation_element('b'). + * </code> + * </p> + * + */ +public final class CollationElementIterator { + + /** + * This constant is returned by the iterator in the methods + * <code>next()</code> and <code>previous()</code> when the end or the + * beginning of the source string has been reached, and there are no more + * valid collation elements to return. + */ + public static final int NULLORDER = -1; + + private com.ibm.icu4jni.text.CollationElementIterator icuIterator; + + CollationElementIterator(com.ibm.icu4jni.text.CollationElementIterator iterator) { + this.icuIterator = iterator; + } + + /** + * Obtains the maximum length of any expansion sequence that ends with the + * specified collation element. If there is no expansion with this collation + * element as the last element, returns <code>1</code>. + * + * @param order + * a collation element that has been previously obtained from a + * call to either the {@link #next()} or {@link #previous()} + * method. + * @return the maximum length of any expansion sequence ending with the + * specified collation element. + */ + public int getMaxExpansion(int order) { + return this.icuIterator.getMaxExpansion(order); + } + + /** + * Obtains the character offset in the source string corresponding to the + * next collation element. This value could be any of: <ui> + * <li>The index of the first character in the source string that matches + * the value of the next collation element. (This means that if + * setOffset(offset) sets the index in the middle of a contraction, + * getOffset() returns the index of the first character in the contraction, + * which may not be equal to the original offset that was set. Hence calling + * getOffset() immediately after setOffset(offset) does not guarantee that + * the original offset set will be returned.)</li> + * <li>If normalization is on, the index of the immediate subsequent + * character, or composite character with the first character, having a + * combining class of 0.</li> + * <li>The length of the source string, if iteration has reached the end. + * </li> + * <ui> + * + * @return The position of the collation element in the source string that + * will be returned in the next invocation of the {@link #next()} + * method. + */ + public int getOffset() { + return this.icuIterator.getOffset(); + } + + /** + * Obtains the next collation element in the source string. + * + * @return the next collation element or <code>NULLORDER</code> if the end + * of the iteration has been reached. + */ + public int next() { + return this.icuIterator.next(); + } + + /** + * Obtains the previous collation element in the source string. + * + * @return the previous collation element, or <code>NULLORDER</code> when + * the start of the iteration has been reached. + */ + public int previous() { + return this.icuIterator.previous(); + } + + /** + * Obtains the primary order of the specified collation element, i.e. the + * first 16 bits. This value is unsigned. + * + * @param order + * @return the element's 16 bits primary order. + */ + public static final int primaryOrder(int order) { + return com.ibm.icu4jni.text.CollationElementIterator.primaryOrder(order); + } + + /** + * Repositions the cursor to point at the first element of the current + * string. The next call to <code>next()</code> or <code>previous()</code> + * will return the first and last collation element in the string, + * respectively. + * <p> + * If the <code>RuleBasedCollator</code> used by this iterator has had its + * attributes changed, calling <code>reset()</code> will reinitialize the + * iterator to use the new attributes. + * </p> + */ + public void reset() { + this.icuIterator.reset(); + } + + /** + * Obtains the secondary order of the specified collation element, i.e. the + * 16th to 23th bits, inclusive. This value is unsigned. + * + * @param order + * @return the 8 bit secondary order of the element + */ + public static final short secondaryOrder(int order) { + return (short) com.ibm.icu4jni.text.CollationElementIterator + .secondaryOrder(order); + } + + /** + * Points the iterator at the collation element associated with the + * character in the source string which is found at the supplied offset. + * After this call completes, an invocation of the {@link #next()} method + * will return this collation element. + * <p> + * If <code>newOffset</code> corresponds to a character which is part of a + * sequence that maps to a single collation element the iterator is adjusted + * to the start of that sequence. As a result of this, any subsequent call + * made to <code>getOffset()</code> may not return the same value set by + * this method. + * </p> + * <p> + * If the decomposition mode is on, and offset is in the middle of a + * decomposable range of source text, the iterator may not return a correct + * result for the next forwards or backwards iteration. The user must ensure + * that the offset is not in the middle of a decomposable range. + * </p> + * + * @param newOffset + * the character offset into the original source string to set. + * Note that this is not an offset into the corresponding + * sequence of collation elements. + */ + public void setOffset(int newOffset) { + this.icuIterator.setOffset(newOffset); + } + + /** + * Sets a new source string iterator for iteration, and reset the offset to + * the beginning of the text. + * + * @param source + * the new source string iterator for iteration. + */ + public void setText(CharacterIterator source) { + this.icuIterator.setText(source); + } + + /** + * Sets a new source string for iteration, and reset the offset to the + * beginning of the text. + * + * @param source + * the new source string for iteration + */ + public void setText(String source) { + this.icuIterator.setText(source); + } + + /** + * Obtains the tertiary order of the specified collation element, i.e. the + * last 8 bits. This value is unsigned. + * + * @param order + * @return the 8 bits tertiary order of the element + */ + public static final short tertiaryOrder(int order) { + return (short) com.ibm.icu4jni.text.CollationElementIterator + .tertiaryOrder(order); + } +} diff --git a/text/src/main/java/java/text/CollationKey.java b/text/src/main/java/java/text/CollationKey.java new file mode 100644 index 0000000..45b6afa --- /dev/null +++ b/text/src/main/java/java/text/CollationKey.java @@ -0,0 +1,103 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package java.text; + +/** + * CollationKey represents the collation order of a particular String for a + * specific Collator. CollationKeys can be compared to determine the relative + * ordering of their source Strings. This is useful when the Strings must be + * compared multiple times, as in sorting. + */ +public final class CollationKey implements Comparable<CollationKey> { + + private String source; + + private com.ibm.icu4jni.text.CollationKey icuKey; + + CollationKey(String source, com.ibm.icu4jni.text.CollationKey key) { + this.source = source; + this.icuKey = key; + } + + /** + * Compare the receiver to the specified CollationKey to determine the + * relative ordering. + * + * @param value + * a CollationKey + * @return an int < 0 if this CollationKey is less than the specified + * CollationKey, 0 if they are equal, and > 0 if this CollationKey + * is greater + */ + public int compareTo(CollationKey value) { + return icuKey.compareTo(value.icuKey); + } + + /** + * Compares the specified object to this CollationKey and answer if they are + * equal. The object must be an instance of CollationKey and have the same + * source string and collation key. The instances of CollationKey must have + * been created by the same Collator. + * + * @param object + * the object to compare with this object + * @return true if the specified object is equal to this CollationKey, false + * otherwise + * + * @see #hashCode + */ + @Override + public boolean equals(Object object) { + if (!(object instanceof CollationKey)) { + return false; + } + CollationKey collationKey = (CollationKey) object; + return icuKey.equals(collationKey.icuKey); + } + + /** + * Answer the String from which this CollationKey was created. + * + * @return a String + */ + public String getSourceString() { + return this.source; + } + + /** + * Returns an integer hash code for the receiver. Objects which are equal + * answer the same value for this method. + * + * @return the receiver's hash + * + * @see #equals + */ + @Override + public int hashCode() { + return icuKey.hashCode(); + } + + /** + * Answer the collation key as a byte array. + * + * @return an array of bytes + */ + public byte[] toByteArray() { + return icuKey.toByteArray(); + } +} diff --git a/text/src/main/java/java/text/Collator.java b/text/src/main/java/java/text/Collator.java new file mode 100644 index 0000000..45c1eb1 --- /dev/null +++ b/text/src/main/java/java/text/Collator.java @@ -0,0 +1,365 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package java.text; + +import java.security.AccessController; +import java.util.Comparator; +import java.util.Locale; +import java.util.Vector; + +import org.apache.harmony.luni.util.PriviAction; + +/** + * Collator is an abstract class which is the root of classes which provide + * Locale specific String comparison to determine their ordering with respect to + * each other. + */ +public abstract class Collator implements Comparator<Object>, Cloneable { + + static final int EQUAL = 0; + + static final int GREATER = 1; + + static final int LESS = -1; + + /** + * Constant used to specify the decomposition rule. + */ + public static final int NO_DECOMPOSITION = 0; + + /** + * Constant used to specify the decomposition rule. + */ + public static final int CANONICAL_DECOMPOSITION = 1; + + /** + * Constant used to specify the decomposition rule. + */ + public static final int FULL_DECOMPOSITION = 2; + + /** + * Constant used to specify the collation strength. + */ + public static final int PRIMARY = 0; + + /** + * Constant used to specify the collation strength. + */ + public static final int SECONDARY = 1; + + /** + * Constant used to specify the collation strength. + */ + public static final int TERTIARY = 2; + + /** + * Constant used to specify the collation strength. + */ + public static final int IDENTICAL = 3; + + private static int CACHE_SIZE; + + static { + // CACHE_SIZE includes key and value, so needs to be double + String cacheSize = AccessController + .doPrivileged(new PriviAction<String>("collator.cache")); //$NON-NLS-1$ + if (cacheSize != null) { + try { + CACHE_SIZE = Integer.parseInt(cacheSize); + } catch (NumberFormatException e) { + CACHE_SIZE = 6; + } + } else { + CACHE_SIZE = 6; + } + } + + private static Vector<Collator> cache = new Vector<Collator>(CACHE_SIZE); + + // Wrapper class of ICU4J Collator + com.ibm.icu4jni.text.Collator icuColl; + + Collator(com.ibm.icu4jni.text.Collator wrapper) { + this.icuColl = wrapper; + } + + /** + * Constructs a new instance of this Collator. + */ + protected Collator() { + super(); + } + + /** + * Returns a new Collator with the same decomposition rule and strength + * value as this Collator. + * + * @return a shallow copy of this Collator + * @see java.lang.Cloneable + */ + @Override + public Object clone() { + try { + Collator clone = (Collator) super.clone(); + clone.icuColl = (com.ibm.icu4jni.text.Collator) this.icuColl.clone(); + return clone; + } catch (CloneNotSupportedException e) { + return null; + } + } + + /** + * Compares the two objects to determine their relative ordering. The + * objects must be Strings. + * + * @param object1 + * the first String to compare + * @param object2 + * the second String to compare + * @return an int < 0 if object1 is less than object2, 0 if they are equal, + * and > 0 if object1 is greater than object2 + * + * @exception ClassCastException + * when the objects are not Strings + */ + public int compare(Object object1, Object object2) { + return compare((String) object1, (String) object2); + } + + /** + * Compares the two Strings to determine their relative ordering. + * + * @param string1 + * the first String to compare + * @param string2 + * the second String to compare + * @return an int < 0 if string1 is less than string2, 0 if they are equal, + * and > 0 if string1 is greater than string2 + */ + public abstract int compare(String string1, String string2); + + /** + * Compares the specified object to this Collator and answer if they are + * equal. The object must be an instance of Collator and have the same + * strength and decomposition values. + * + * @param object + * the object to compare with this object + * @return true if the specified object is equal to this Collator, false + * otherwise + * + * @see #hashCode + */ + @Override + public boolean equals(Object object) { + if (!(object instanceof Collator)) { + return false; + } + Collator collator = (Collator) object; + return this.icuColl == null ? collator.icuColl == null : this.icuColl + .equals(collator.icuColl); + } + + /** + * Compares the two Strings using the collation rules to determine if they + * are equal. + * + * @param string1 + * the first String to compare + * @param string2 + * the second String to compare + * @return true if the strings are equal using the collation rules, false + * otherwise + */ + public boolean equals(String string1, String string2) { + return compare(string1, string2) == 0; + } + + /** + * Gets the list of installed Locales which support Collator. + * + * @return an array of Locale + */ + public static Locale[] getAvailableLocales() { + return com.ibm.icu4jni.text.Collator.getAvailableLocales(); + } + + /** + * Returns a CollationKey for the specified String for this Collator with + * the current decomposition rule and strength value. + * + * @param string + * the collation key. + * @return a CollationKey + */ + public abstract CollationKey getCollationKey(String string); + + /** + * Returns the decomposition rule for this Collator. + * + * @return the decomposition rule, either NO_DECOMPOSITION, + * CANONICAL_DECOMPOSITION or FULL_DECOMPOSITION + */ + public int getDecomposition() { + return decompositionMode_ICU_Java(this.icuColl.getDecomposition()); + } + + /** + * Returns a Collator instance which is appropriate for the default Locale. + * + * @return a Collator + */ + public static Collator getInstance() { + return getInstance(Locale.getDefault()); + } + + /** + * Returns a Collator instance which is appropriate for the specified + * Locale. + * + * @param locale + * the Locale + * @return a Collator + */ + public static Collator getInstance(Locale locale) { + String key = locale.toString(); + for (int i = cache.size() - 1; i >= 0; i -= 2) { + if (cache.elementAt(i).equals(key)) { + return (Collator) (cache.elementAt(i - 1)).clone(); + } + } + + return new RuleBasedCollator(com.ibm.icu4jni.text.Collator + .getInstance(locale)); + } + + /** + * Returns the strength value for this Collator. + * + * @return the strength value, either PRIMARY, SECONDARY, TERTIARY, or + * IDENTICAL + */ + public int getStrength() { + return strength_ICU_Java(this.icuColl.getStrength()); + } + + /** + * Returns an integer hash code for the receiver. Objects which are equal + * answer the same value for this method. + * + * @return the receiver's hash + * + * @see #equals(Object) + * @see #equals(String, String) + */ + @Override + public abstract int hashCode(); + + /** + * Sets the decomposition rule for this Collator. + * + * @param value + * the decomposition rule, either NO_DECOMPOSITION, + * CANONICAL_DECOMPOSITION or FULL_DECOMPOSITION + * + * @exception IllegalArgumentException + * when the decomposition rule is not valid + */ + public void setDecomposition(int value) { + this.icuColl.setDecomposition(decompositionMode_Java_ICU(value)); + } + + /** + * Sets the strength value for this Collator. + * + * @param value + * the strength value, either PRIMARY, SECONDARY, TERTIARY, or + * IDENTICAL + * + * @exception IllegalArgumentException + * when the strength value is not valid + */ + public void setStrength(int value) { + this.icuColl.setStrength(strength_Java_ICU(value)); + } + + private int decompositionMode_Java_ICU(int mode) { + int icuDecomp = mode; + switch (mode) { + case Collator.CANONICAL_DECOMPOSITION: + icuDecomp = com.ibm.icu4jni.text.Collator.CANONICAL_DECOMPOSITION; + break; + case Collator.NO_DECOMPOSITION: + icuDecomp = com.ibm.icu4jni.text.Collator.NO_DECOMPOSITION; + break; + } + return icuDecomp; + } + + private int decompositionMode_ICU_Java(int mode) { + int javaMode = mode; + switch (mode) { + case com.ibm.icu4jni.text.Collator.NO_DECOMPOSITION: + javaMode = Collator.NO_DECOMPOSITION; + break; + case com.ibm.icu4jni.text.Collator.CANONICAL_DECOMPOSITION: + javaMode = Collator.CANONICAL_DECOMPOSITION; + break; + } + return javaMode; + } + + private int strength_Java_ICU(int value) { + int icuValue = value; + switch (value) { + case Collator.PRIMARY: + icuValue = com.ibm.icu4jni.text.Collator.PRIMARY; + break; + case Collator.SECONDARY: + icuValue = com.ibm.icu4jni.text.Collator.SECONDARY; + break; + case Collator.TERTIARY: + icuValue = com.ibm.icu4jni.text.Collator.TERTIARY; + break; + case Collator.IDENTICAL: + icuValue = com.ibm.icu4jni.text.Collator.IDENTICAL; + break; + } + return icuValue; + + } + + private int strength_ICU_Java(int value) { + int javaValue = value; + switch (value) { + case com.ibm.icu4jni.text.Collator.PRIMARY: + javaValue = Collator.PRIMARY; + break; + case com.ibm.icu4jni.text.Collator.SECONDARY: + javaValue = Collator.SECONDARY; + break; + case com.ibm.icu4jni.text.Collator.TERTIARY: + javaValue = Collator.TERTIARY; + break; + case com.ibm.icu4jni.text.Collator.IDENTICAL: + javaValue = Collator.IDENTICAL; + break; + } + return javaValue; + } +} diff --git a/text/src/main/java/java/text/DateFormat.java b/text/src/main/java/java/text/DateFormat.java new file mode 100644 index 0000000..6fca4f6 --- /dev/null +++ b/text/src/main/java/java/text/DateFormat.java @@ -0,0 +1,803 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package java.text; + +import java.io.InvalidObjectException; +import java.util.Calendar; +import java.util.Date; +import java.util.Hashtable; +import java.util.Locale; +import java.util.ResourceBundle; +import java.util.TimeZone; + +import org.apache.harmony.text.internal.nls.Messages; + +/** + * DateFormat is the abstract superclass of formats which format and parse + * Dates. + */ +public abstract class DateFormat extends Format { + + private static final long serialVersionUID = 7218322306649953788L; + + /** + * The calendar that this <code>DateFormat</code> uses to format a number + * representing a date. + */ + protected Calendar calendar; + + /** + * The number format used to format a number. + */ + protected NumberFormat numberFormat; + + /** + * Format style constant. + */ + public final static int DEFAULT = 2; + + /** + * Format style constant. + */ + public final static int FULL = 0; + + /** + * Format style constant. + */ + public final static int LONG = 1; + + /** + * Format style constant. + */ + public final static int MEDIUM = 2; + + /** + * Format style constant. + */ + public final static int SHORT = 3; + + /** + * Field constant. + */ + public final static int ERA_FIELD = 0; + + /** + * Field constant. + */ + public final static int YEAR_FIELD = 1; + + /** + * Field constant. + */ + public final static int MONTH_FIELD = 2; + + /** + * Field constant. + */ + public final static int DATE_FIELD = 3; + + /** + * Field constant. + */ + public final static int HOUR_OF_DAY1_FIELD = 4; + + /** + * Field constant. + */ + public final static int HOUR_OF_DAY0_FIELD = 5; + + /** + * Field constant. + */ + public final static int MINUTE_FIELD = 6; + + /** + * Field constant. + */ + public final static int SECOND_FIELD = 7; + + /** + * Field constant. + */ + public final static int MILLISECOND_FIELD = 8; + + /** + * Field constant. + */ + public final static int DAY_OF_WEEK_FIELD = 9; + + /** + * Field constant. + */ + public final static int DAY_OF_YEAR_FIELD = 10; + + /** + * Field constant. + */ + public final static int DAY_OF_WEEK_IN_MONTH_FIELD = 11; + + /** + * Field constant. + */ + public final static int WEEK_OF_YEAR_FIELD = 12; + + /** + * Field constant. + */ + public final static int WEEK_OF_MONTH_FIELD = 13; + + /** + * Field constant. + */ + public final static int AM_PM_FIELD = 14; + + /** + * Field constant. + */ + public final static int HOUR1_FIELD = 15; + + /** + * Field constant. + */ + public final static int HOUR0_FIELD = 16; + + /** + * Field constant. + */ + public final static int TIMEZONE_FIELD = 17; + + /** + * Constructs a new instance of DateFormat. + * + */ + protected DateFormat() { + } + + /** + * Returns a new instance of DateFormat with the same properties. + * + * @return a shallow copy of this DateFormat + * + * @see java.lang.Cloneable + */ + @Override + public Object clone() { + DateFormat clone = (DateFormat) super.clone(); + clone.calendar = (Calendar) calendar.clone(); + clone.numberFormat = (NumberFormat) numberFormat.clone(); + return clone; + } + + /** + * Compares the specified object to this DateFormat and answer if they are + * equal. The object must be an instance of DateFormat with the same + * properties. + * + * @param object + * the object to compare with this object + * @return true if the specified object is equal to this DateFormat, false + * otherwise + * + * @see #hashCode + */ + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (!(object instanceof DateFormat)) { + return false; + } + DateFormat dateFormat = (DateFormat) object; + return numberFormat.equals(dateFormat.numberFormat) + && calendar.getTimeZone().equals( + dateFormat.calendar.getTimeZone()) + && calendar.getFirstDayOfWeek() == dateFormat.calendar + .getFirstDayOfWeek() + && calendar.getMinimalDaysInFirstWeek() == dateFormat.calendar + .getMinimalDaysInFirstWeek() + && calendar.isLenient() == dateFormat.calendar.isLenient(); + } + + /** + * Formats the specified object into the specified StringBuffer using the + * rules of this DateFormat. If the field specified by the FieldPosition is + * formatted, set the begin and end index of the formatted field in the + * FieldPosition. + * + * @param object + * the object to format, must be a Date or a Number. If the + * object is a Number, a Date is constructed using the + * <code>longValue()</code> of the Number. + * @param buffer + * the StringBuffer + * @param field + * the FieldPosition + * @return the StringBuffer parameter <code>buffer</code> + * + * @exception IllegalArgumentException + * when the object is not a Date or a Number + */ + @Override + public final StringBuffer format(Object object, StringBuffer buffer, + FieldPosition field) { + if (object instanceof Date) { + return format((Date) object, buffer, field); + } + if (object instanceof Number) { + return format(new Date(((Number) object).longValue()), buffer, + field); + } + throw new IllegalArgumentException(); + } + + /** + * Formats the specified Date using the rules of this DateFormat. + * + * @param date + * the Date to format + * @return the formatted String + */ + public final String format(Date date) { + return format(date, new StringBuffer(), new FieldPosition(0)) + .toString(); + } + + /** + * Formats the specified Date into the specified StringBuffer using the + * rules of this DateFormat. If the field specified by the FieldPosition is + * formatted, set the begin and end index of the formatted field in the + * FieldPosition. + * + * @param date + * the Date to format + * @param buffer + * the StringBuffer + * @param field + * the FieldPosition + * @return the StringBuffer parameter <code>buffer</code> + */ + public abstract StringBuffer format(Date date, StringBuffer buffer, + FieldPosition field); + + /** + * Gets the list of installed Locales which support DateFormat. + * + * @return an array of Locale + */ + public static Locale[] getAvailableLocales() { + return Locale.getAvailableLocales(); + } + + /** + * Returns the Calendar used by this DateFormat. + * + * @return a Calendar + */ + public Calendar getCalendar() { + return calendar; + } + + /** + * Returns a DateFormat instance for formatting and parsing dates in the + * DEFAULT style for the default Locale. + * + * @return a DateFormat + */ + public final static DateFormat getDateInstance() { + return getDateInstance(DEFAULT); + } + + /** + * Returns a DateFormat instance for formatting and parsing dates in the + * specified style for the default Locale. + * + * @param style + * one of SHORT, MEDIUM, LONG, FULL, or DEFAULT + * @return a DateFormat + */ + public final static DateFormat getDateInstance(int style) { + checkDateStyle(style); + return getDateInstance(style, Locale.getDefault()); + } + + /** + * Returns a DateFormat instance for formatting and parsing dates in the + * specified style for the specified Locale. + * + * @param style + * one of SHORT, MEDIUM, LONG, FULL, or DEFAULT + * @param locale + * the Locale + * @return a DateFormat + */ + public final static DateFormat getDateInstance(int style, Locale locale) { + checkDateStyle(style); + ResourceBundle bundle = getBundle(locale); + String pattern = bundle.getString("Date_" + getStyleName(style)); //$NON-NLS-1$ + return new SimpleDateFormat(pattern, locale); + } + + /** + * Returns a DateFormat instance for formatting and parsing dates and times + * in the DEFAULT style for the default Locale. + * + * @return a DateFormat + */ + public final static DateFormat getDateTimeInstance() { + return getDateTimeInstance(DEFAULT, DEFAULT); + } + + /** + * Returns a <code>DateFormat</code> instance for the formatting and + * parsing of both dates and times in the manner appropriate to the default + * Locale. + * + * @param dateStyle + * one of SHORT, MEDIUM, LONG, FULL, or DEFAULT + * @param timeStyle + * one of SHORT, MEDIUM, LONG, FULL, or DEFAULT + * @return a DateFormat + */ + public final static DateFormat getDateTimeInstance(int dateStyle, + int timeStyle) { + checkTimeStyle(timeStyle); + checkDateStyle(dateStyle); + return getDateTimeInstance(dateStyle, timeStyle, Locale.getDefault()); + } + + /** + * Returns a DateFormat instance for formatting and parsing dates and times + * in the specified styles for the specified Locale. + * + * @param dateStyle + * one of SHORT, MEDIUM, LONG, FULL, or DEFAULT + * @param timeStyle + * one of SHORT, MEDIUM, LONG, FULL, or DEFAULT + * @param locale + * the Locale + * @return a DateFormat + */ + public final static DateFormat getDateTimeInstance(int dateStyle, + int timeStyle, Locale locale) { + checkTimeStyle(timeStyle); + checkDateStyle(dateStyle); + ResourceBundle bundle = getBundle(locale); + String pattern = bundle.getString("Date_" + getStyleName(dateStyle)) //$NON-NLS-1$ + + " " + bundle.getString("Time_" + getStyleName(timeStyle)); //$NON-NLS-1$ //$NON-NLS-2$ + return new SimpleDateFormat(pattern, locale); + } + + /** + * Returns a DateFormat instance for formatting and parsing dates and times + * in the SHORT style for the default Locale. + * + * @return a DateFormat + */ + public final static DateFormat getInstance() { + return getDateTimeInstance(SHORT, SHORT); + } + + /** + * Returns the NumberFormat used by this DateFormat. + * + * @return a NumberFormat + */ + public NumberFormat getNumberFormat() { + return numberFormat; + } + + static String getStyleName(int style) { + String styleName; + switch (style) { + case SHORT: + styleName = "SHORT"; //$NON-NLS-1$ + break; + case MEDIUM: + styleName = "MEDIUM"; //$NON-NLS-1$ + break; + case LONG: + styleName = "LONG"; //$NON-NLS-1$ + break; + case FULL: + styleName = "FULL"; //$NON-NLS-1$ + break; + default: + styleName = ""; //$NON-NLS-1$ + } + return styleName; + } + + /** + * Returns a DateFormat instance for formatting and parsing times in the + * DEFAULT style for the default Locale. + * + * @return a DateFormat + */ + public final static DateFormat getTimeInstance() { + return getTimeInstance(DEFAULT); + } + + /** + * Returns a DateFormat instance for formatting and parsing times in the + * specified style for the default Locale. + * + * @param style + * one of SHORT, MEDIUM, LONG, FULL, or DEFAULT + * @return a DateFormat + */ + public final static DateFormat getTimeInstance(int style) { + checkTimeStyle(style); + return getTimeInstance(style, Locale.getDefault()); + } + + /** + * Returns a DateFormat instance for formatting and parsing times in the + * specified style for the specified Locale. + * + * @param style + * one of SHORT, MEDIUM, LONG, FULL, or DEFAULT + * @param locale + * the Locale + * @return a DateFormat + */ + public final static DateFormat getTimeInstance(int style, Locale locale) { + checkTimeStyle(style); + ResourceBundle bundle = getBundle(locale); + String pattern = bundle.getString("Time_" + getStyleName(style)); //$NON-NLS-1$ + return new SimpleDateFormat(pattern, locale); + } + + /** + * Returns the TimeZone of the Calendar used by this DateFormat. + * + * @return a TimeZone + */ + public TimeZone getTimeZone() { + return calendar.getTimeZone(); + } + + /** + * Returns an integer hash code for the receiver. Objects which are equal + * answer the same value for this method. + * + * @return the receiver's hash + * + * @see #equals + */ + @Override + public int hashCode() { + return calendar.getFirstDayOfWeek() + + calendar.getMinimalDaysInFirstWeek() + + calendar.getTimeZone().hashCode() + + (calendar.isLenient() ? 1231 : 1237) + + numberFormat.hashCode(); + } + + /** + * Returns if the Calendar used by this DateFormat is lenient. + * + * @return true when the Calendar is lenient, false otherwise + */ + public boolean isLenient() { + return calendar.isLenient(); + } + + /** + * Parse a Date from the specified String using the rules of this + * DateFormat. + * + * @param string + * the String to parse + * @return the Date resulting from the parse + * + * @exception ParseException + * when an error occurs during parsing + */ + public Date parse(String string) throws ParseException { + ParsePosition position = new ParsePosition(0); + Date date = parse(string, position); + if (position.getErrorIndex() != -1 || position.getIndex() == 0) { + // text.19=Unparseable date: {0} + throw new ParseException( + Messages.getString("text.19", string), position.getErrorIndex()); //$NON-NLS-1$ + } + return date; + } + + /** + * Parse a Date from the specified String starting at the index specified by + * the ParsePosition. If the string is successfully parsed, the index of the + * ParsePosition is updated to the index following the parsed text. + * + * @param string + * the String to parse + * @param position + * the ParsePosition, updated on return with the index following + * the parsed text, or on error the index is unchanged and the + * error index is set to the index where the error occurred + * @return the Date resulting from the parse, or null if there is an error + */ + public abstract Date parse(String string, ParsePosition position); + + /** + * Parse a Date from the specified String starting at the index specified by + * the ParsePosition. If the string is successfully parsed, the index of the + * ParsePosition is updated to the index following the parsed text. + * + * @param string + * the String to parse + * @param position + * the ParsePosition, updated on return with the index following + * the parsed text, or on error the index is unchanged and the + * error index is set to the index where the error occurred + * @return the Date resulting from the parse, or null if there is an error + */ + @Override + public Object parseObject(String string, ParsePosition position) { + return parse(string, position); + } + + /** + * Sets the Calendar used by this DateFormat. + * + * @param cal + * the Calendar + */ + public void setCalendar(Calendar cal) { + calendar = cal; + } + + /** + * Sets if the Calendar used by this DateFormat is lenient. + * + * @param value + * true to set the Calendar to be lenient, false otherwise + */ + public void setLenient(boolean value) { + calendar.setLenient(value); + } + + /** + * Sets the NumberFormat used by this DateFormat. + * + * @param format + * the NumberFormat + */ + public void setNumberFormat(NumberFormat format) { + numberFormat = format; + } + + /** + * Sets the TimeZone of the Calendar used by this DateFormat. + * + * @param timezone + * the TimeZone + */ + public void setTimeZone(TimeZone timezone) { + calendar.setTimeZone(timezone); + } + + /** + * The instances of this inner class are used as attribute keys and values + * in AttributedCharacterIterator that + * SimpleDateFormat.formatToCharacterIterator() method returns. + * <p> + * There is no public constructor to this class, the only instances are the + * constants defined here. + * <p> + */ + public static class Field extends Format.Field { + + private static final long serialVersionUID = 7441350119349544720L; + + private static Hashtable<Integer, Field> table = new Hashtable<Integer, Field>(); + + /** + * Marks the era part of a date. + */ + public final static Field ERA = new Field("era", Calendar.ERA); //$NON-NLS-1$ + + /** + * Marks the year part of a date. + */ + public final static Field YEAR = new Field("year", Calendar.YEAR); //$NON-NLS-1$ + + /** + * Marks the month part of a date. + */ + public final static Field MONTH = new Field("month", Calendar.MONTH); //$NON-NLS-1$ + + /** + * Marks the hour of the day part of a date (0-11). + */ + public final static Field HOUR_OF_DAY0 = new Field("hour of day", //$NON-NLS-1$ + Calendar.HOUR_OF_DAY); + + /** + * Marks the hour of the day part of a date (1-12). + */ + public final static Field HOUR_OF_DAY1 = new Field("hour of day 1", -1); //$NON-NLS-1$ + + /** + * Marks the minute part of a time. + */ + public final static Field MINUTE = new Field("minute", Calendar.MINUTE); //$NON-NLS-1$ + + /** + * Marks the second part of a time. + */ + public final static Field SECOND = new Field("second", Calendar.SECOND); //$NON-NLS-1$ + + /** + * Marks the millisecond part of a time. + */ + public final static Field MILLISECOND = new Field("millisecond", //$NON-NLS-1$ + Calendar.MILLISECOND); + + /** + * Marks the day of the week part of a date. + */ + public final static Field DAY_OF_WEEK = new Field("day of week", //$NON-NLS-1$ + Calendar.DAY_OF_WEEK); + + /** + * Marks the day of the month part of a date. + */ + public final static Field DAY_OF_MONTH = new Field("day of month", //$NON-NLS-1$ + Calendar.DAY_OF_MONTH); + + /** + * Marks the day of the year part of a date. + */ + public final static Field DAY_OF_YEAR = new Field("day of year", //$NON-NLS-1$ + Calendar.DAY_OF_YEAR); + + /** + * Marks the day of the week in the month part of a date. + */ + public final static Field DAY_OF_WEEK_IN_MONTH = new Field( + "day of week in month", Calendar.DAY_OF_WEEK_IN_MONTH); //$NON-NLS-1$ + + /** + * Marks the week of the year part of a date. + */ + public final static Field WEEK_OF_YEAR = new Field("week of year", //$NON-NLS-1$ + Calendar.WEEK_OF_YEAR); + + /** + * Marks the week of the month part of a date. + */ + public final static Field WEEK_OF_MONTH = new Field("week of month", //$NON-NLS-1$ + Calendar.WEEK_OF_MONTH); + + /** + * Marks the time indicator part of a date. + */ + public final static Field AM_PM = new Field("am pm", Calendar.AM_PM); //$NON-NLS-1$ + + /** + * Marks the hour part of a date (0-11). + */ + public final static Field HOUR0 = new Field("hour", Calendar.HOUR); //$NON-NLS-1$ + + /** + * Marks the hour part of a date (1-12). + */ + public final static Field HOUR1 = new Field("hour 1", -1); //$NON-NLS-1$ + + /** + * Marks the time zone part of a date. + */ + public final static Field TIME_ZONE = new Field("time zone", -1); //$NON-NLS-1$ + + /** + * The Calendar field that this Field represents. + */ + private int calendarField = -1; + + /** + * Constructs a new instance of DateFormat.Field with the given + * fieldName and calendar field. + * @param fieldName The field name. + * @param calendarField the calender field type of the field. + */ + protected Field(String fieldName, int calendarField) { + super(fieldName); + this.calendarField = calendarField; + if (calendarField != -1 + && table.get(new Integer(calendarField)) == null) { + table.put(new Integer(calendarField), this); + } + } + + /** + * Returns the Calendar field this Field represents + * + * @return int calendar field + */ + public int getCalendarField() { + return calendarField; + } + + /** + * Returns the DateFormat.Field instance for the given calendar field + * + * @param calendarField + * a calendar field constant + * @return null if there is no Field for this calendar field + */ + public static Field ofCalendarField(int calendarField) { + if (calendarField < 0 || calendarField >= Calendar.FIELD_COUNT) { + throw new IllegalArgumentException(); + } + + return table.get(new Integer(calendarField)); + } + + /** + * Serialization method resolve instances to the constant + * DateFormat.Field values + */ + @Override + protected Object readResolve() throws InvalidObjectException { + if (calendarField != -1) { + try { + Field result = ofCalendarField(calendarField); + if (result != null && this.equals(result)) { + return result; + } + } catch (IllegalArgumentException e) { + // text.02=Unknown attribute + throw new InvalidObjectException(Messages + .getString("text.02")); //$NON-NLS-1$ + } + } else { + if (this.equals(TIME_ZONE)) { + return TIME_ZONE; + } + if (this.equals(HOUR1)) { + return HOUR1; + } + if (this.equals(HOUR_OF_DAY1)) { + return HOUR_OF_DAY1; + } + } + // text.02=Unknown attribute + throw new InvalidObjectException(Messages.getString("text.02")); //$NON-NLS-1$ + } + } + + private static void checkDateStyle(int style) { + if (!(style == SHORT || style == MEDIUM || style == LONG + || style == FULL || style == DEFAULT)) { + // text.0E=Illegal date style: {0} + throw new IllegalArgumentException(Messages.getString( + "text.0E", style)); //$NON-NLS-1$ + } + } + + private static void checkTimeStyle(int style) { + if (!(style == SHORT || style == MEDIUM || style == LONG + || style == FULL || style == DEFAULT)) { + // text.0F=Illegal time style: {0} + throw new IllegalArgumentException(Messages.getString( + "text.0F", style)); //$NON-NLS-1$ + } + } +} diff --git a/text/src/main/java/java/text/DateFormatSymbols.java b/text/src/main/java/java/text/DateFormatSymbols.java new file mode 100644 index 0000000..e843334 --- /dev/null +++ b/text/src/main/java/java/text/DateFormatSymbols.java @@ -0,0 +1,429 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package java.text; + +import java.io.Serializable; +// BEGIN android-added +import java.io.IOException; +import java.io.ObjectOutputStream; +// END android-added +import java.util.Arrays; +import java.util.Locale; +import java.util.ResourceBundle; + +// BEGIN android-added +import com.ibm.icu4jni.util.Resources; +// END android-added + +/** + * DateFormatSymbols holds the Strings used in the formating and parsing of + * dates and times. + */ +public class DateFormatSymbols implements Serializable, Cloneable { + + private static final long serialVersionUID = -5987973545549424702L; + + private String localPatternChars; + + String[] ampms, eras, months, shortMonths, shortWeekdays, weekdays; + + String[][] zoneStrings; + +// BEGIN android-added + /** + * Locale, necessary to lazily load time zone strings. We force the time + * zone names to load upon serialization, so this will never be needed + * post deserialization. + */ + transient final Locale locale; + + /** + * Gets zone strings, initializing them if necessary. Does not create + * a defensive copy, so make sure you do so before exposing the returned + * arrays to clients. + */ + synchronized String[][] internalZoneStrings() { + if (zoneStrings == null) { + zoneStrings = Resources.getDisplayTimeZones(locale.toString()); + } + return zoneStrings; + } +// END android-added + + /** + * Constructs a new DateFormatSymbols containing the symbols for the default + * Locale. + */ + public DateFormatSymbols() { + this(Locale.getDefault()); + } + + /** + * Constructs a new DateFormatSymbols containing the symbols for the + * specified Locale. + * + * @param locale + * the Locale + */ + public DateFormatSymbols(Locale locale) { + ResourceBundle bundle = Format.getBundle(locale); + localPatternChars = bundle.getString("LocalPatternChars"); //$NON-NLS-1$ + ampms = bundle.getStringArray("ampm"); //$NON-NLS-1$ + eras = bundle.getStringArray("eras"); //$NON-NLS-1$ + months = bundle.getStringArray("months"); //$NON-NLS-1$ + shortMonths = bundle.getStringArray("shortMonths"); //$NON-NLS-1$ + shortWeekdays = bundle.getStringArray("shortWeekdays"); //$NON-NLS-1$ + weekdays = bundle.getStringArray("weekdays"); //$NON-NLS-1$ + +// BEGIN android-changed + // zoneStrings = (String[][]) bundle.getObject("timezones"); //$NON-NLS-1$ + this.locale = locale; + } + + @Override + public Object clone() { + try { + return super.clone(); + } catch (CloneNotSupportedException e) { + throw new AssertionError(); + } + } +// END android-changed + + /** + * Compares the specified object to this DateFormatSymbols and answer if + * they are equal. The object must be an instance of DateFormatSymbols with + * the same symbols. + * + * @param object + * the object to compare with this object + * @return true if the specified object is equal to this DateFormatSymbols, + * false otherwise + * + * @see #hashCode + */ + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (!(object instanceof DateFormatSymbols)) { + return false; + } + DateFormatSymbols obj = (DateFormatSymbols) object; + if (!localPatternChars.equals(obj.localPatternChars)) { + return false; + } + if (!Arrays.equals(ampms, obj.ampms)) { + return false; + } + if (!Arrays.equals(eras, obj.eras)) { + return false; + } + if (!Arrays.equals(months, obj.months)) { + return false; + } + if (!Arrays.equals(shortMonths, obj.shortMonths)) { + return false; + } + if (!Arrays.equals(shortWeekdays, obj.shortWeekdays)) { + return false; + } + if (!Arrays.equals(weekdays, obj.weekdays)) { + return false; + } + +// BEGIN android-changed + // Quick check that may keep us from having to load the zone strings. + if (zoneStrings == null && obj.zoneStrings == null + && !locale.equals(obj.locale)) { + return false; + } + + // Make sure zone strings are loaded. + internalZoneStrings(); + obj.internalZoneStrings(); +// END android-changed + + if (zoneStrings.length != obj.zoneStrings.length) { + return false; + } + for (String[] element : zoneStrings) { + if (element.length != element.length) { + return false; + } + for (int j = 0; j < element.length; j++) { + if (element[j] != element[j] + && !(element[j].equals(element[j]))) { + return false; + } + } + } + return true; + } + + /** + * Returns the array of Strings which represent AM and PM. Use the Calendar + * constants Calendar.AM and Calendar.PM to index into the array. + * + * @return an array of String + */ + public String[] getAmPmStrings() { + return ampms.clone(); + } + + /** + * Returns the array of Strings which represent BC and AD. Use the Calendar + * constants GregorianCalendar.BC and GregorianCalendar.AD to index into the + * array. + * + * @return an array of String + */ + public String[] getEras() { + return eras.clone(); + } + + /** + * Returns the pattern characters used by SimpleDateFormat to specify date + * and time fields. + * + * @return a String containing the pattern characters + */ + public String getLocalPatternChars() { + return localPatternChars; + } + + /** + * Returns the array of Strings containing the full names of the months. Use + * the Calendar constants Calendar.JANUARY, etc. to index into the array. + * + * @return an array of String + */ + public String[] getMonths() { + return months.clone(); + } + + /** + * Returns the array of Strings containing the abbreviated names of the + * months. Use the Calendar constants Calendar.JANUARY, etc. to index into + * the array. + * + * @return an array of String + */ + public String[] getShortMonths() { + return shortMonths.clone(); + } + + /** + * Returns the array of Strings containing the abbreviated names of the days + * of the week. Use the Calendar constants Calendar.SUNDAY, etc. to index + * into the array. + * + * @return an array of String + */ + public String[] getShortWeekdays() { + return shortWeekdays.clone(); + } + + /** + * Returns the array of Strings containing the full names of the days of the + * week. Use the Calendar constants Calendar.SUNDAY, etc. to index into the + * array. + * + * @return an array of String + */ + public String[] getWeekdays() { + return weekdays.clone(); + } + + /** + * Returns the two-dimensional array of Strings containing the names of the + * timezones. Each element in the array is an array of five Strings, the + * first is a TimeZone ID, and second and third are the full and abbreviated + * timezone names for standard time, and the fourth and fifth are the full + * and abbreviated names for daylight time. + * + * @return a two-dimensional array of String + */ + public String[][] getZoneStrings() { +// BEGIN android-added + String[][] zoneStrings = internalZoneStrings(); +// END android-added + + String[][] clone = new String[zoneStrings.length][]; + for (int i = zoneStrings.length; --i >= 0;) { + clone[i] = zoneStrings[i].clone(); + } + return clone; + } + + /** + * Returns an integer hash code for the receiver. Objects which are equal + * answer the same value for this method. + * + * @return the receiver's hash + * + * @see #equals + */ + @Override + public int hashCode() { + int hashCode; + hashCode = localPatternChars.hashCode(); + for (String element : ampms) { + hashCode += element.hashCode(); + } + for (String element : eras) { + hashCode += element.hashCode(); + } + for (String element : months) { + hashCode += element.hashCode(); + } + for (String element : shortMonths) { + hashCode += element.hashCode(); + } + for (String element : shortWeekdays) { + hashCode += element.hashCode(); + } + for (String element : weekdays) { + hashCode += element.hashCode(); + } + +// BEGIN android-added + String[][] zoneStrings = internalZoneStrings(); +// END android-added + + for (String[] element : zoneStrings) { + for (int j = 0; j < element.length; j++) { + hashCode += element[j].hashCode(); + } + } + return hashCode; + } + + /** + * Sets the array of Strings which represent AM and PM. Use the Calendar + * constants Calendar.AM and Calendar.PM to index into the array. + * + * @param data + * the array of Strings + */ + public void setAmPmStrings(String[] data) { + ampms = data.clone(); + } + + /** + * Sets the array of Strings which represent BC and AD. Use the Calendar + * constants GregorianCalendar.BC and GregorianCalendar.AD to index into the + * array. + * + * @param data + * the array of Strings + */ + public void setEras(String[] data) { + eras = data.clone(); + } + + /** + * Sets the pattern characters used by SimpleDateFormat to specify date and + * time fields. + * + * @param data + * the String containing the pattern characters + * + * @exception NullPointerException + * when the data is null + */ + public void setLocalPatternChars(String data) { + if (data == null) { + throw new NullPointerException(); + } + localPatternChars = data; + } + + /** + * Sets the array of Strings containing the full names of the months. Use + * the Calendar constants Calendar.JANUARY, etc. to index into the array. + * + * @param data + * the array of Strings + */ + public void setMonths(String[] data) { + months = data.clone(); + } + + /** + * Sets the array of Strings containing the abbreviated names of the months. + * Use the Calendar constants Calendar.JANUARY, etc. to index into the + * array. + * + * @param data + * the array of Strings + */ + public void setShortMonths(String[] data) { + shortMonths = data.clone(); + } + + /** + * Sets the array of Strings containing the abbreviated names of the days of + * the week. Use the Calendar constants Calendar.SUNDAY, etc. to index into + * the array. + * + * @param data + * the array of Strings + */ + public void setShortWeekdays(String[] data) { + shortWeekdays = data.clone(); + } + + /** + * Sets the array of Strings containing the full names of the days of the + * week. Use the Calendar constants Calendar.SUNDAY, etc. to index into the + * array. + * + * @param data + * the array of Strings + */ + public void setWeekdays(String[] data) { + weekdays = data.clone(); + } + + /** + * Sets the two-dimensional array of Strings containing the names of the + * timezones. Each element in the array is an array of five Strings, the + * first is a TimeZone ID, and second and third are the full and abbreviated + * timezone names for standard time, and the fourth and fifth are the full + * and abbreviated names for daylight time. + * + * @param data + * the two-dimensional array of Strings + */ + public void setZoneStrings(String[][] data) { + zoneStrings = data.clone(); + } + +// BEGIN android-added + private void writeObject(ObjectOutputStream out) + throws IOException { + // Ensure internal zone strings are initialized to ensure backward + // compatibility. + internalZoneStrings(); + + out.defaultWriteObject(); + } +// END android-added +} diff --git a/text/src/main/java/java/text/DecimalFormat.java b/text/src/main/java/java/text/DecimalFormat.java new file mode 100644 index 0000000..b22f6a9 --- /dev/null +++ b/text/src/main/java/java/text/DecimalFormat.java @@ -0,0 +1,952 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package java.text; + +import java.io.IOException; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.ObjectStreamField; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Currency; +import java.util.Locale; + +import org.apache.harmony.text.internal.nls.Messages; + +/** + * DecimalFormat is used to format and parse numbers, both integers and + * fractions, based on a pattern. The pattern characters used can be either + * localized or non-localized. + */ +public class DecimalFormat extends NumberFormat { + + private static final long serialVersionUID = 864413376551465018L; + + private transient boolean parseBigDecimal = false; + + private transient DecimalFormatSymbols symbols; + + private transient com.ibm.icu4jni.text.DecimalFormat dform; + + private transient com.ibm.icu4jni.text.DecimalFormatSymbols icuSymbols; + + private static final int CURRENT_SERIAL_VERTION = 3; + + private transient int serialVersionOnStream = 3; + + /** + * Constructs a new DecimalFormat for formatting and parsing numbers for the + * default Locale. + */ + public DecimalFormat() { + this(getPattern(Locale.getDefault(), "Number")); //$NON-NLS-1$ + } + + /** + * Constructs a new DecimalFormat using the specified non-localized pattern + * and the DecimalFormatSymbols for the default Locale. + * + * @param pattern + * the non-localized pattern + * + * @exception IllegalArgumentException + * when the pattern cannot be parsed + */ + public DecimalFormat(String pattern) { + this(pattern, new DecimalFormatSymbols()); + } + + /** + * Constructs a new DecimalFormat using the specified non-localized pattern + * and DecimalFormatSymbols. + * + * @param pattern + * the non-localized pattern + * @param value + * the DecimalFormatSymbols + * + * @exception IllegalArgumentException + * when the pattern cannot be parsed + */ + public DecimalFormat(String pattern, DecimalFormatSymbols value) { + symbols = (DecimalFormatSymbols) value.clone(); + Locale locale = (Locale) this.getInternalField("locale", symbols); //$NON-NLS-1$ + icuSymbols = new com.ibm.icu4jni.text.DecimalFormatSymbols(locale); + copySymbols(icuSymbols, symbols); + + dform = new com.ibm.icu4jni.text.DecimalFormat(pattern, icuSymbols); + + super.setMaximumFractionDigits(dform.getMaximumFractionDigits()); + super.setMaximumIntegerDigits(dform.getMaximumIntegerDigits()); + super.setMinimumFractionDigits(dform.getMinimumFractionDigits()); + super.setMinimumIntegerDigits(dform.getMinimumIntegerDigits()); + } + + /** + * Changes the pattern of this DecimalFormat to the specified pattern which + * uses localized pattern characters. + * + * @param pattern + * the localized pattern + * + * @exception IllegalArgumentException + * when the pattern cannot be parsed + */ + public void applyLocalizedPattern(String pattern) { + dform.applyLocalizedPattern(pattern); + } + + /** + * Changes the pattern of this SimpleDateFormat to the specified pattern + * which uses non-localized pattern characters. + * + * @param pattern + * the non-localized pattern + * + * @exception IllegalArgumentException + * when the pattern cannot be parsed + */ + public void applyPattern(String pattern) { + + dform.applyPattern(pattern); + } + + /** + * Returns a new instance of DecimalFormat with the same pattern and + * properties as this DecimalFormat. + * + * @return a shallow copy of this DecimalFormat + * + * @see java.lang.Cloneable + */ + @Override + public Object clone() { + DecimalFormat clone = (DecimalFormat) super.clone(); + clone.dform = (com.ibm.icu4jni.text.DecimalFormat) dform.clone(); + clone.symbols = (DecimalFormatSymbols) symbols.clone(); + return clone; + } + + /** + * Compares the specified object to this DecimalFormat and answer if they + * are equal. The object must be an instance of DecimalFormat with the same + * pattern and properties. + * + * @param object + * the object to compare with this object + * @return true if the specified object is equal to this DecimalFormat, + * false otherwise + * + * @see #hashCode + */ + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (!(object instanceof DecimalFormat)) { + return false; + } + DecimalFormat format = (DecimalFormat) object; + return (this.dform == null ? format.dform == null : this.dform + .equals(format.dform)); + } + + /** + * Formats the specified object using the rules of this DecimalNumberFormat + * and returns an AttributedCharacterIterator with the formatted number and + * attributes. + * + * @param object + * the object to format + * @return an AttributedCharacterIterator with the formatted number and + * attributes + * + * @exception NullPointerException + * when the object is null + * @exception IllegalArgumentException + * when the object cannot be formatted by this Format + */ + @Override + public AttributedCharacterIterator formatToCharacterIterator(Object object) { + if (object == null) { + throw new NullPointerException(); + } + return dform.formatToCharacterIterator(object); + } + + /** + * Formats the double value into the specified StringBuffer using the + * pattern of this DecimalFormat. If the field specified by the + * FieldPosition is formatted, set the begin and end index of the formatted + * field in the FieldPosition. + * + * @param value + * the double to format + * @param buffer + * the StringBuffer + * @param position + * the FieldPosition + * @return the StringBuffer parameter <code>buffer</code> + */ + @Override + public StringBuffer format(double value, StringBuffer buffer, + FieldPosition position) { + return dform.format(value, buffer, position); + } + + /** + * Formats the long value into the specified StringBuffer using the pattern + * of this DecimalFormat. If the field specified by the FieldPosition is + * formatted, set the begin and end index of the formatted field in the + * FieldPosition. + * + * @param value + * the long to format + * @param buffer + * the StringBuffer + * @param position + * the FieldPosition + * @return the StringBuffer parameter <code>buffer</code> + */ + @Override + public StringBuffer format(long value, StringBuffer buffer, + FieldPosition position) { + return dform.format(value, buffer, position); + } + + /** + * Formats the number into the specified StringBuffer using the pattern of + * this DecimalFormat. If the field specified by the FieldPosition is + * formatted, set the begin and end index of the formatted field in the + * FieldPosition. + * + * @param number + * the object to format + * @param toAppendTo + * the StringBuffer + * @param pos + * the FieldPosition + * @return the StringBuffer parameter <code>buffer</code> + * @throws IllegalArgumentException + * if the given number is not instance of <code>Number</code> + */ + @Override + public final StringBuffer format(Object number, StringBuffer toAppendTo, + FieldPosition pos) { + if (!(number instanceof Number)) { + throw new IllegalArgumentException(); + } + if (toAppendTo == null || pos == null) { + throw new NullPointerException(); + } + if (number instanceof BigInteger || number instanceof BigDecimal) { + return dform.format(number, toAppendTo, pos); + } + return super.format(number, toAppendTo, pos); + } + + /** + * Returns the DecimalFormatSymbols used by this DecimalFormat. + * + * @return a DecimalFormatSymbols + */ + public DecimalFormatSymbols getDecimalFormatSymbols() { + return (DecimalFormatSymbols) symbols.clone(); + } + + /** + * Returns the currency used by this decimal format. + * + * @return currency of DecimalFormatSymbols used by this decimal format + * @see DecimalFormatSymbols#getCurrency() + */ + @Override + public Currency getCurrency() { + final Currency cur = dform.getCurrency(); + final String code = (cur == null) ? "XXX" : cur.getCurrencyCode(); //$NON-NLS-1$ + + return Currency.getInstance(code); + } + + /** + * Returns the number of digits grouped together by the grouping separator. + * + * @return the number of digits grouped together + */ + public int getGroupingSize() { + return dform.getGroupingSize(); + } + + /** + * Returns the multiplier which is applied to the number before formatting + * or after parsing. + * + * @return the multiplier + */ + public int getMultiplier() { + return dform.getMultiplier(); + } + + /** + * Returns the prefix which is formatted or parsed before a negative number. + * + * @return the negative prefix + */ + public String getNegativePrefix() { + return dform.getNegativePrefix(); + } + + /** + * Returns the suffix which is formatted or parsed after a negative number. + * + * @return the negative suffix + */ + public String getNegativeSuffix() { + return dform.getNegativeSuffix(); + } + + /** + * Returns the prefix which is formatted or parsed before a positive number. + * + * @return the positive prefix + */ + public String getPositivePrefix() { + return dform.getPositivePrefix(); + } + + /** + * Returns the suffix which is formatted or parsed after a positive number. + * + * @return the positive suffix + */ + public String getPositiveSuffix() { + return dform.getPositiveSuffix(); + } + + /** + * Returns an integer hash code for the receiver. Objects which are equal + * answer the same value for this method. + * + * @return the receiver's hash + * + * @see #equals + */ + @Override + public int hashCode() { + return dform.hashCode(); + } + + /** + * Returns whether the decimal separator is shown when there are no + * fractional digits. + * + * @return true if the decimal separator should always be formatted, false + * otherwise + */ + public boolean isDecimalSeparatorAlwaysShown() { + return dform.isDecimalSeparatorAlwaysShown(); + } + + /** + * This value indicates whether the return object of the parse operation + * will be of type BigDecimal. This value will default to false. + * + * @return true and parse will always return BigDecimals, false and the type + * of the result will be Long or Double. + */ + public boolean isParseBigDecimal() { + return this.parseBigDecimal; + } + + /** + * When DecimalFormat is used to parsing, and this value is set to true, + * then all the resulting number will be of type + * <code>java.lang.Integer</code>. Except that, NaN, positive and + * negative infinity are still returned as <code>java.lang.Double</code> + * + * In this implementation, com.ibm.icu4jni.text.DecimalFormat is wrapped to + * fulfill most of the format and parse feature. And this method is + * delegated to the wrapped instance of com.ibm.icu4jni.text.DecimalFormat. + * + * @param value + * If set to true, all the resulting number will be of type + * java.lang.Integer except some special cases. + */ + @Override + public void setParseIntegerOnly(boolean value) { + dform.setParseIntegerOnly(value); + } + + /** + * Returns true if this <code>DecimalFormat</code>'s all resulting number + * will be of type <code>java.lang.Integer</code> + * + * @return true if this <code>DecimalFormat</code>'s all resulting number + * will be of type <code>java.lang.Integer</code> + */ + @Override + public boolean isParseIntegerOnly() { + return dform.isParseIntegerOnly(); + } + + private static final Double NEGATIVE_ZERO_DOUBLE = new Double(-0.0); + + /** + * Parse a Long or Double from the specified String starting at the index + * specified by the ParsePosition. If the string is successfully parsed, the + * index of the ParsePosition is updated to the index following the parsed + * text. + * + * @param string + * the String to parse + * @param position + * the ParsePosition, updated on return with the index following + * the parsed text, or on error the index is unchanged and the + * error index is set to the index where the error occurred + * @return a Long or Double resulting from the parse, or null if there is an + * error. The result will be a Long if the parsed number is an + * integer in the range of a long, otherwise the result is a Double. + */ + @Override + public Number parse(String string, ParsePosition position) { + Number number = dform.parse(string, position); + if (null == number) { + return null; + } + // BEGIN android-removed + // if (this.isParseBigDecimal()) { + // if (number instanceof Long) { + // return new BigDecimal(number.longValue()); + // } + // if ((number instanceof Double) && !((Double) number).isInfinite() + // && !((Double) number).isNaN()) { + // + // return new BigDecimal(number.doubleValue()); + // } + // if (number instanceof BigInteger) { + // return new BigDecimal(number.doubleValue()); + // } + // if (number instanceof com.ibm.icu.math.BigDecimal) { + // return new BigDecimal(number.toString()); + // } + // return number; + // } + // if ((number instanceof com.ibm.icu.math.BigDecimal) + // || (number instanceof BigInteger)) { + // return new Double(number.doubleValue()); + // } + // END android-removed + // BEGIN android-added + if (this.isParseBigDecimal()) { + if (number instanceof Long) { + return new BigDecimal(number.longValue()); + } + if ((number instanceof Double) && !((Double) number).isInfinite() + && !((Double) number).isNaN()) { + + return new BigDecimal(number.toString()); + } + if (number instanceof BigInteger) { + return new BigDecimal(number.toString()); + } + return number; + } + if ((number instanceof BigDecimal) || (number instanceof BigInteger)) { + return new Double(number.doubleValue()); + } + // END android-added + + if (this.isParseIntegerOnly() && number.equals(NEGATIVE_ZERO_DOUBLE)) { + return new Long(0); + } + return number; + + } + + /** + * Sets the DecimalFormatSymbols used by this DecimalFormat. + * + * @param value + * the DecimalFormatSymbols + */ + public void setDecimalFormatSymbols(DecimalFormatSymbols value) { + if (value != null) { + symbols = (DecimalFormatSymbols) value.clone(); + icuSymbols = dform.getDecimalFormatSymbols(); + copySymbols(icuSymbols, symbols); + dform.setDecimalFormatSymbols(icuSymbols); + } + } + + /** + * Sets the currency used by this decimal format. The min and max fraction + * digits remain the same. + * + * @param currency + * @see DecimalFormatSymbols#setCurrency(Currency) + */ + @Override + public void setCurrency(Currency currency) { + dform.setCurrency(Currency.getInstance(currency + .getCurrencyCode())); + symbols.setCurrency(currency); + } + + /** + * Sets whether the decimal separator is shown when there are no fractional + * digits. + * + * @param value + * true if the decimal separator should always be formatted, + * false otherwise + */ + public void setDecimalSeparatorAlwaysShown(boolean value) { + dform.setDecimalSeparatorAlwaysShown(value); + } + + /** + * Sets the number of digits grouped together by the grouping separator. + * + * @param value + * the number of digits grouped together + */ + public void setGroupingSize(int value) { + dform.setGroupingSize(value); + } + + /** + * Sets whether or not grouping will be used in this format. Grouping + * affects both parsing and formatting. + * + * @param value + * true if uses grouping,false otherwise. + * + */ + @Override + public void setGroupingUsed(boolean value) { + dform.setGroupingUsed(value); + } + + /** + * This value indicates whether grouping will be used in this format. + * + * @return true if grouping is used,false otherwise. + */ + @Override + public boolean isGroupingUsed() { + return dform.isGroupingUsed(); + } + + /** + * Sets the maximum number of fraction digits that are printed when + * formatting. If the maximum is less than the number of fraction digits, + * the least significant digits are truncated. Limit the maximum to + * DOUBLE_FRACTION_DIGITS. + * + * @param value + * the maximum number of fraction digits + */ + @Override + public void setMaximumFractionDigits(int value) { + super.setMaximumFractionDigits(value); + dform.setMaximumFractionDigits(value); + } + + /** + * Sets the maximum number of integer digits that are printed when + * formatting. If the maximum is less than the number of integer digits, the + * most significant digits are truncated. Limit the maximum to + * DOUBLE_INTEGER_DIGITS. + * + * @param value + * the maximum number of integer digits + */ + @Override + public void setMaximumIntegerDigits(int value) { + super.setMaximumIntegerDigits(value); + dform.setMaximumIntegerDigits(value); + } + + /** + * Sets the minimum number of fraction digits that are printed when + * formatting. Limit the minimum to DOUBLE_FRACTION_DIGITS. + * + * @param value + * the minimum number of fraction digits + */ + @Override + public void setMinimumFractionDigits(int value) { + super.setMinimumFractionDigits(value); + dform.setMinimumFractionDigits(value); + } + + /** + * Sets the minimum number of integer digits that are printed when + * formatting. Limit the minimum to DOUBLE_INTEGER_DIGITS. + * + * @param value + * the minimum number of integer digits + */ + @Override + public void setMinimumIntegerDigits(int value) { + super.setMinimumIntegerDigits(value); + dform.setMinimumIntegerDigits(value); + } + + /** + * Sets the multiplier which is applied to the number before formatting or + * after parsing. + * + * @param value + * the multiplier + */ + public void setMultiplier(int value) { + dform.setMultiplier(value); + } + + /** + * Sets the prefix which is formatted or parsed before a negative number. + * + * @param value + * the negative prefix + */ + public void setNegativePrefix(String value) { + dform.setNegativePrefix(value); + } + + /** + * Sets the suffix which is formatted or parsed after a negative number. + * + * @param value + * the negative suffix + */ + public void setNegativeSuffix(String value) { + dform.setNegativeSuffix(value); + } + + /** + * Sets the prefix which is formatted or parsed before a positive number. + * + * @param value + * the positive prefix + */ + public void setPositivePrefix(String value) { + dform.setPositivePrefix(value); + } + + /** + * Sets the suffix which is formatted or parsed after a positive number. + * + * @param value + * the positive suffix + */ + public void setPositiveSuffix(String value) { + dform.setPositiveSuffix(value); + } + + /** + * Let users change the behavior of a DecimalFormat, If set to true all the + * returned objects will be of type BigDecimal + * + * @param newValue + * true if all the returned objects should be type of BigDecimal + */ + public void setParseBigDecimal(boolean newValue) { + this.parseBigDecimal = newValue; + } + + /** + * Returns the pattern of this DecimalFormat using localized pattern + * characters. + * + * @return the localized pattern + */ + public String toLocalizedPattern() { + return dform.toLocalizedPattern(); + } + + /** + * Returns the pattern of this DecimalFormat using non-localized pattern + * characters. + * + * @return the non-localized pattern + */ + public String toPattern() { + return dform.toPattern(); + } + + // the fields list to be serialized + private static final ObjectStreamField[] serialPersistentFields = { + new ObjectStreamField("positivePrefix", String.class), //$NON-NLS-1$ + new ObjectStreamField("positiveSuffix", String.class), //$NON-NLS-1$ + new ObjectStreamField("negativePrefix", String.class), //$NON-NLS-1$ + new ObjectStreamField("negativeSuffix", String.class), //$NON-NLS-1$ + new ObjectStreamField("posPrefixPattern", String.class), //$NON-NLS-1$ + new ObjectStreamField("posSuffixPattern", String.class), //$NON-NLS-1$ + new ObjectStreamField("negPrefixPattern", String.class), //$NON-NLS-1$ + new ObjectStreamField("negSuffixPattern", String.class), //$NON-NLS-1$ + new ObjectStreamField("multiplier", int.class), //$NON-NLS-1$ + new ObjectStreamField("groupingSize", byte.class), //$NON-NLS-1$ + // BEGIN android-added + new ObjectStreamField("groupingUsed", boolean.class), //$NON-NLS-1$ + // END android-added + new ObjectStreamField("decimalSeparatorAlwaysShown", boolean.class), //$NON-NLS-1$ + new ObjectStreamField("parseBigDecimal", boolean.class), //$NON-NLS-1$ + new ObjectStreamField("symbols", DecimalFormatSymbols.class), //$NON-NLS-1$ + new ObjectStreamField("useExponentialNotation", boolean.class), //$NON-NLS-1$ + new ObjectStreamField("minExponentDigits", byte.class), //$NON-NLS-1$ + new ObjectStreamField("maximumIntegerDigits", int.class), //$NON-NLS-1$ + new ObjectStreamField("minimumIntegerDigits", int.class), //$NON-NLS-1$ + new ObjectStreamField("maximumFractionDigits", int.class), //$NON-NLS-1$ + new ObjectStreamField("minimumFractionDigits", int.class), //$NON-NLS-1$ + new ObjectStreamField("serialVersionOnStream", int.class), }; //$NON-NLS-1$ + + /** + * Writes serialized fields following serialized forms specified by Java + * specification. + * + * @param stream + * the output stream to write serialized bytes + * @throws IOException + * if some I/O error occurs + * @throws ClassNotFoundException + */ + private void writeObject(ObjectOutputStream stream) throws IOException, + ClassNotFoundException { + ObjectOutputStream.PutField fields = stream.putFields(); + fields.put("positivePrefix", dform.getPositivePrefix()); //$NON-NLS-1$ + fields.put("positiveSuffix", dform.getPositiveSuffix()); //$NON-NLS-1$ + fields.put("negativePrefix", dform.getNegativePrefix()); //$NON-NLS-1$ + fields.put("negativeSuffix", dform.getNegativeSuffix()); //$NON-NLS-1$ + String posPrefixPattern = (String) this.getInternalField( + "posPrefixPattern", dform); //$NON-NLS-1$ + fields.put("posPrefixPattern", posPrefixPattern); //$NON-NLS-1$ + String posSuffixPattern = (String) this.getInternalField( + "posSuffixPattern", dform); //$NON-NLS-1$ + fields.put("posSuffixPattern", posSuffixPattern); //$NON-NLS-1$ + String negPrefixPattern = (String) this.getInternalField( + "negPrefixPattern", dform); //$NON-NLS-1$ + fields.put("negPrefixPattern", negPrefixPattern); //$NON-NLS-1$ + String negSuffixPattern = (String) this.getInternalField( + "negSuffixPattern", dform); //$NON-NLS-1$ + fields.put("negSuffixPattern", negSuffixPattern); //$NON-NLS-1$ + fields.put("multiplier", dform.getMultiplier()); //$NON-NLS-1$ + fields.put("groupingSize", (byte) dform.getGroupingSize()); //$NON-NLS-1$ + // BEGIN android-added + fields.put("groupingUsed", dform.isGroupingUsed()); //$NON-NLS-1$ + // END android-added + fields.put("decimalSeparatorAlwaysShown", dform //$NON-NLS-1$ + .isDecimalSeparatorAlwaysShown()); + fields.put("parseBigDecimal", parseBigDecimal); //$NON-NLS-1$ + fields.put("symbols", symbols); //$NON-NLS-1$ + boolean useExponentialNotation = ((Boolean) this.getInternalField( + "useExponentialNotation", dform)).booleanValue(); //$NON-NLS-1$ + fields.put("useExponentialNotation", useExponentialNotation); //$NON-NLS-1$ + byte minExponentDigits = ((Byte) this.getInternalField( + "minExponentDigits", dform)).byteValue(); //$NON-NLS-1$ + fields.put("minExponentDigits", minExponentDigits); //$NON-NLS-1$ + fields.put("maximumIntegerDigits", dform.getMaximumIntegerDigits()); //$NON-NLS-1$ + fields.put("minimumIntegerDigits", dform.getMinimumIntegerDigits()); //$NON-NLS-1$ + fields.put("maximumFractionDigits", dform.getMaximumFractionDigits()); //$NON-NLS-1$ + fields.put("minimumFractionDigits", dform.getMinimumFractionDigits()); //$NON-NLS-1$ + fields.put("serialVersionOnStream", CURRENT_SERIAL_VERTION); //$NON-NLS-1$ + stream.writeFields(); + + } + + /** + * Reads serialized fields following serialized forms specified by Java + * specification. + * + * @param stream + * the input stream to read serialized bytes + * @throws IOException + * if some I/O error occurs + * @throws ClassNotFoundException + * if some class of serialized objects or fields cannot be found + */ + private void readObject(ObjectInputStream stream) throws IOException, + ClassNotFoundException { + + ObjectInputStream.GetField fields = stream.readFields(); + String positivePrefix = (String) fields.get("positivePrefix", ""); //$NON-NLS-1$ //$NON-NLS-2$ + String positiveSuffix = (String) fields.get("positiveSuffix", ""); //$NON-NLS-1$ //$NON-NLS-2$ + String negativePrefix = (String) fields.get("negativePrefix", "-"); //$NON-NLS-1$ //$NON-NLS-2$ + String negativeSuffix = (String) fields.get("negativeSuffix", ""); //$NON-NLS-1$ //$NON-NLS-2$ + + String posPrefixPattern = (String) fields.get("posPrefixPattern", ""); //$NON-NLS-1$ //$NON-NLS-2$ + String posSuffixPattern = (String) fields.get("posSuffixPattern", ""); //$NON-NLS-1$ //$NON-NLS-2$ + String negPrefixPattern = (String) fields.get("negPrefixPattern", "-"); //$NON-NLS-1$ //$NON-NLS-2$ + String negSuffixPattern = (String) fields.get("negSuffixPattern", ""); //$NON-NLS-1$ //$NON-NLS-2$ + + int multiplier = fields.get("multiplier", 1); //$NON-NLS-1$ + byte groupingSize = fields.get("groupingSize", (byte) 3); //$NON-NLS-1$ + // BEGIN android-added + boolean groupingUsed = fields.get("groupingUsed", true); //$NON-NLS-1$ + // END android-added + boolean decimalSeparatorAlwaysShown = fields.get( + "decimalSeparatorAlwaysShown", false); //$NON-NLS-1$ + boolean parseBigDecimal = fields.get("parseBigDecimal", false); //$NON-NLS-1$ + symbols = (DecimalFormatSymbols) fields.get("symbols", null); //$NON-NLS-1$ + + boolean useExponentialNotation = fields.get("useExponentialNotation", //$NON-NLS-1$ + false); + byte minExponentDigits = fields.get("minExponentDigits", (byte) 0); //$NON-NLS-1$ + + int maximumIntegerDigits = fields.get("maximumIntegerDigits", 309); //$NON-NLS-1$ + int minimumIntegerDigits = fields.get("minimumIntegerDigits", 309); //$NON-NLS-1$ + int maximumFractionDigits = fields.get("maximumFractionDigits", 340); //$NON-NLS-1$ + int minimumFractionDigits = fields.get("minimumFractionDigits", 340); //$NON-NLS-1$ + this.serialVersionOnStream = fields.get("serialVersionOnStream", 0); //$NON-NLS-1$ + + Locale locale = (Locale) getInternalField("locale", symbols); //$NON-NLS-1$ + // BEGIN android-removed + // dform = new com.ibm.icu4jni.text.DecimalFormat("", //$NON-NLS-1$ + // new com.ibm.icu4jni.text.DecimalFormatSymbols(locale)); + // END android-removed + // BEGIN android-added + icuSymbols = new com.ibm.icu4jni.text.DecimalFormatSymbols(locale); + copySymbols(icuSymbols, symbols); + dform = new com.ibm.icu4jni.text.DecimalFormat("", //$NON-NLS-1$ + icuSymbols); + // END android-added + setInternalField("useExponentialNotation", dform, new Boolean( //$NON-NLS-1$ + useExponentialNotation)); + setInternalField("minExponentDigits", dform, //$NON-NLS-1$ + new Byte(minExponentDigits)); + dform.setPositivePrefix(positivePrefix); + dform.setPositiveSuffix(positiveSuffix); + dform.setNegativePrefix(negativePrefix); + dform.setNegativeSuffix(negativeSuffix); + setInternalField("posPrefixPattern", dform, posPrefixPattern); //$NON-NLS-1$ + setInternalField("posSuffixPattern", dform, posSuffixPattern); //$NON-NLS-1$ + setInternalField("negPrefixPattern", dform, negPrefixPattern); //$NON-NLS-1$ + setInternalField("negSuffixPattern", dform, negSuffixPattern); //$NON-NLS-1$ + dform.setMultiplier(multiplier); + dform.setGroupingSize(groupingSize); + dform.setGroupingUsed(groupingUsed); + dform.setDecimalSeparatorAlwaysShown(decimalSeparatorAlwaysShown); + dform.setMinimumIntegerDigits(minimumIntegerDigits); + dform.setMaximumIntegerDigits(maximumIntegerDigits); + dform.setMinimumFractionDigits(minimumFractionDigits); + dform.setMaximumFractionDigits(maximumFractionDigits); + this.setParseBigDecimal(parseBigDecimal); + + if (super.getMaximumIntegerDigits() > Integer.MAX_VALUE + || super.getMinimumIntegerDigits() > Integer.MAX_VALUE + || super.getMaximumFractionDigits() > Integer.MAX_VALUE + || super.getMinimumIntegerDigits() > Integer.MAX_VALUE) { + // text.09=The deserialized date is invalid + throw new InvalidObjectException(Messages.getString("text.09")); //$NON-NLS-1$ + } + if (serialVersionOnStream < 3) { + setMaximumIntegerDigits(super.getMinimumIntegerDigits()); + setMinimumIntegerDigits(super.getMinimumIntegerDigits()); + setMaximumFractionDigits(super.getMaximumFractionDigits()); + setMinimumFractionDigits(super.getMinimumFractionDigits()); + } + if (serialVersionOnStream < 1) { + this.setInternalField("useExponentialNotation", dform, //$NON-NLS-1$ + Boolean.FALSE); + } + serialVersionOnStream = 3; + } + + /* + * Copies decimal format symbols from text object to ICU one. + * + * @param icu the object which receives the new values. @param dfs the + * object which contains the new values. + */ + private void copySymbols(final com.ibm.icu4jni.text.DecimalFormatSymbols icu, + final DecimalFormatSymbols dfs) { + icu.setCurrency(Currency.getInstance(dfs.getCurrency() + .getCurrencyCode())); + icu.setCurrencySymbol(dfs.getCurrencySymbol()); + icu.setDecimalSeparator(dfs.getDecimalSeparator()); + icu.setDigit(dfs.getDigit()); + icu.setGroupingSeparator(dfs.getGroupingSeparator()); + icu.setInfinity(dfs.getInfinity()); + icu + .setInternationalCurrencySymbol(dfs + .getInternationalCurrencySymbol()); + icu.setMinusSign(dfs.getMinusSign()); + icu.setMonetaryDecimalSeparator(dfs.getMonetaryDecimalSeparator()); + icu.setNaN(dfs.getNaN()); + icu.setPatternSeparator(dfs.getPatternSeparator()); + icu.setPercent(dfs.getPercent()); + icu.setPerMill(dfs.getPerMill()); + icu.setZeroDigit(dfs.getZeroDigit()); + } + + /* + * Sets private field value by reflection. + * + * @param fieldName the field name to be set @param target the object which + * field to be set @param value the value to be set + */ + private void setInternalField(final String fieldName, final Object target, + final Object value) { + AccessController + .doPrivileged(new PrivilegedAction<java.lang.reflect.Field>() { + public java.lang.reflect.Field run() { + java.lang.reflect.Field field = null; + try { + field = target.getClass().getDeclaredField( + fieldName); + field.setAccessible(true); + field.set(target, value); + } catch (Exception e) { + return null; + } + return field; + } + }); + } + + /* + * Gets private field value by reflection. + * + * @param fieldName the field name to be set @param target the object which + * field to be gotten + */ + private Object getInternalField(final String fieldName, final Object target) { + Object value = AccessController + .doPrivileged(new PrivilegedAction<Object>() { + public Object run() { + Object result = null; + java.lang.reflect.Field field = null; + try { + field = target.getClass().getDeclaredField( + fieldName); + field.setAccessible(true); + result = field.get(target); + } catch (Exception e1) { + return null; + } + return result; + } + }); + return value; + } + +} diff --git a/text/src/main/java/java/text/DecimalFormatSymbols.java b/text/src/main/java/java/text/DecimalFormatSymbols.java new file mode 100644 index 0000000..4918a1f --- /dev/null +++ b/text/src/main/java/java/text/DecimalFormatSymbols.java @@ -0,0 +1,543 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package java.text; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.ObjectStreamField; +import java.io.Serializable; +import java.util.Arrays; +import java.util.Currency; +import java.util.Locale; +import java.util.ResourceBundle; + +/** + * DecimalFormatSymbols holds the symbols used in the formating and parsing of + * numbers. + */ +public final class DecimalFormatSymbols implements Cloneable, Serializable { + + private static final long serialVersionUID = 5772796243397350300L; + + private final int ZeroDigit = 0, Digit = 1, DecimalSeparator = 2, + GroupingSeparator = 3, PatternSeparator = 4, Percent = 5, + PerMill = 6, Exponent = 7, MonetaryDecimalSeparator = 8, + MinusSign = 9; + + transient char[] patternChars; + + private transient Currency currency; + + private transient Locale locale; + + private String infinity, NaN, currencySymbol, intlCurrencySymbol; + + /** + * Constructs a new DecimalFormatSymbols containing the symbols for the + * default Locale. + */ + public DecimalFormatSymbols() { + this(Locale.getDefault()); + } + + /** + * Constructs a new DecimalFormatSymbols containing the symbols for the + * specified Locale. + * + * @param locale + * the Locale + */ + public DecimalFormatSymbols(Locale locale) { + ResourceBundle bundle = Format.getBundle(locale); + patternChars = bundle.getString("DecimalPatternChars").toCharArray(); //$NON-NLS-1$ + infinity = bundle.getString("Infinity"); //$NON-NLS-1$ + NaN = bundle.getString("NaN"); //$NON-NLS-1$ + this.locale = locale; + try { + currency = Currency.getInstance(locale); + currencySymbol = currency.getSymbol(locale); + intlCurrencySymbol = currency.getCurrencyCode(); + } catch (IllegalArgumentException e) { + currency = Currency.getInstance("XXX"); //$NON-NLS-1$ + currencySymbol = bundle.getString("CurrencySymbol"); //$NON-NLS-1$ + intlCurrencySymbol = bundle.getString("IntCurrencySymbol"); //$NON-NLS-1$ + } + } + + /** + * Returns a new DecimalFormatSymbols with the same symbols as this + * DecimalFormatSymbols. + * + * @return a shallow copy of this DecimalFormatSymbols + * + * @see java.lang.Cloneable + */ + @Override + public Object clone() { + try { + DecimalFormatSymbols symbols = (DecimalFormatSymbols) super.clone(); + symbols.patternChars = patternChars.clone(); + return symbols; + } catch (CloneNotSupportedException e) { + return null; + } + } + + /** + * Compares the specified object to this DecimalFormatSymbols and answer if + * they are equal. The object must be an instance of DecimalFormatSymbols + * with the same symbols. + * + * @param object + * the object to compare with this object + * @return true if the specified object is equal to this + * DecimalFormatSymbols, false otherwise + * + * @see #hashCode + */ + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (!(object instanceof DecimalFormatSymbols)) { + return false; + } + DecimalFormatSymbols obj = (DecimalFormatSymbols) object; + return Arrays.equals(patternChars, obj.patternChars) + && infinity.equals(obj.infinity) && NaN.equals(obj.NaN) + && currencySymbol.equals(obj.currencySymbol) + && intlCurrencySymbol.equals(obj.intlCurrencySymbol); + } + + /** + * Returns the currency. + * <p> + * <code>null<code> is returned + * if <code>setInternationalCurrencySymbol()</code> has been previously called + * with a value that is not a valid ISO 4217 currency code. + * <p> + * + * @return the currency that was set in the constructor, <code>setCurrency()</code>, + * or <code>setInternationalCurrencySymbol()</code>, or </code>null</code> + * + * @see #setCurrency(Currency) + * @see #setInternationalCurrencySymbol(String) + */ + public Currency getCurrency() { + return currency; + } + + /** + * Returns the international currency symbol. + * + * @return a String + */ + public String getInternationalCurrencySymbol() { + return intlCurrencySymbol; + } + + /** + * Returns the currency symbol. + * + * @return a String + */ + public String getCurrencySymbol() { + return currencySymbol; + } + + /** + * Returns the character which represents the decimal point in a number. + * + * @return a char + */ + public char getDecimalSeparator() { + return patternChars[DecimalSeparator]; + } + + /** + * Returns the character which represents a single digit in a format + * pattern. + * + * @return a char + */ + public char getDigit() { + return patternChars[Digit]; + } + + /** + * Returns the character used as the thousands separator in a number. + * + * @return a char + */ + public char getGroupingSeparator() { + return patternChars[GroupingSeparator]; + } + + /** + * Returns the String which represents infinity. + * + * @return a String + */ + public String getInfinity() { + return infinity; + } + + String getLocalPatternChars() { + // Don't include the MonetaryDecimalSeparator or the MinusSign + return new String(patternChars, 0, patternChars.length - 2); + } + + /** + * Returns the minus sign character. + * + * @return a char + */ + public char getMinusSign() { + return patternChars[MinusSign]; + } + + /** + * Returns the character which represents the decimal point in a monetary + * value. + * + * @return a char + */ + public char getMonetaryDecimalSeparator() { + return patternChars[MonetaryDecimalSeparator]; + } + + /** + * Returns the String which represents NaN. + * + * @return a String + */ + public String getNaN() { + return NaN; + } + + /** + * Returns the character which separates the positive and negative patterns + * in a format pattern. + * + * @return a char + */ + public char getPatternSeparator() { + return patternChars[PatternSeparator]; + } + + /** + * Returns the percent character. + * + * @return a char + */ + public char getPercent() { + return patternChars[Percent]; + } + + /** + * Returns the mille percent sign character. + * + * @return a char + */ + public char getPerMill() { + return patternChars[PerMill]; + } + + /** + * Returns the character which represents zero. + * + * @return a char + */ + public char getZeroDigit() { + return patternChars[ZeroDigit]; + } + + char getExponential() { + return patternChars[Exponent]; + } + + /** + * Returns an integer hash code for the receiver. Objects which are equal + * answer the same value for this method. + * + * @return the receiver's hash + * + * @see #equals + */ + @Override + public int hashCode() { + return new String(patternChars).hashCode() + infinity.hashCode() + + NaN.hashCode() + currencySymbol.hashCode() + + intlCurrencySymbol.hashCode(); + } + + /** + * Sets the currency. + * <p> + * The international currency symbol and currency symbol are updated, but + * the min and max number of fraction digits stay the same. + * <p> + * + * @param currency + * the new currency + * + * @throws java.lang.NullPointerException + * if currency is null + */ + public void setCurrency(Currency currency) { + if (currency == null) { + throw new NullPointerException(); + } + if (currency == this.currency) { + return; + } + this.currency = currency; + intlCurrencySymbol = currency.getCurrencyCode(); + currencySymbol = currency.getSymbol(locale); + } + + /** + * Sets the international currency symbol. + * <p> + * currency and currency symbol also are updated, if <code>value</code> is + * a valid ISO4217 currency code. + * <p> + * The min and max number of fraction digits stay the same. + * + * @param value + * currency code + */ + public void setInternationalCurrencySymbol(String value) { + if (value == null) { + currency = null; + intlCurrencySymbol = null; + return; + } + + if (value.equals(intlCurrencySymbol)) { + return; + } + + try { + currency = Currency.getInstance(value); + currencySymbol = currency.getSymbol(locale); + } catch (IllegalArgumentException e) { + currency = null; + } + intlCurrencySymbol = value; + } + + /** + * Sets the currency symbol. + * + * @param value + * a String + */ + public void setCurrencySymbol(String value) { + currencySymbol = value; + } + + /** + * Sets the character which represents the decimal point in a number. + * + * @param value + * the decimal separator character + */ + public void setDecimalSeparator(char value) { + patternChars[DecimalSeparator] = value; + } + + /** + * Sets the character which represents a single digit in a format pattern. + * + * @param value + * the digit character + */ + public void setDigit(char value) { + patternChars[Digit] = value; + } + + /** + * Sets the character used as the thousands separator in a number. + * + * @param value + * the grouping separator character + */ + public void setGroupingSeparator(char value) { + patternChars[GroupingSeparator] = value; + } + + /** + * Sets the String which represents infinity. + * + * @param value + * the String + */ + public void setInfinity(String value) { + infinity = value; + } + + /** + * Sets the minus sign character. + * + * @param value + * the minus sign character + */ + public void setMinusSign(char value) { + patternChars[MinusSign] = value; + } + + /** + * Sets the character which represents the decimal point in a monetary + * value. + * + * @param value + * the monetary decimal separator character + */ + public void setMonetaryDecimalSeparator(char value) { + patternChars[MonetaryDecimalSeparator] = value; + } + + /** + * Sets the String which represents NaN. + * + * @param value + * the String + */ + public void setNaN(String value) { + NaN = value; + } + + /** + * Sets the character which separates the positive and negative patterns in + * a format pattern. + * + * @param value + * the pattern separator character + */ + public void setPatternSeparator(char value) { + patternChars[PatternSeparator] = value; + } + + /** + * Sets the percent character. + * + * @param value + * the percent character + */ + public void setPercent(char value) { + patternChars[Percent] = value; + } + + /** + * Sets the mille percent sign character. + * + * @param value + * the mille percent character + */ + public void setPerMill(char value) { + patternChars[PerMill] = value; + } + + /** + * Sets the character which represents zero. + * + * @param value + * the zero digit character + */ + public void setZeroDigit(char value) { + patternChars[ZeroDigit] = value; + } + + void setExponential(char value) { + patternChars[Exponent] = value; + } + + private static final ObjectStreamField[] serialPersistentFields = { + new ObjectStreamField("currencySymbol", String.class), //$NON-NLS-1$ + new ObjectStreamField("decimalSeparator", Character.TYPE), //$NON-NLS-1$ + new ObjectStreamField("digit", Character.TYPE), //$NON-NLS-1$ + new ObjectStreamField("exponential", Character.TYPE), //$NON-NLS-1$ + new ObjectStreamField("groupingSeparator", Character.TYPE), //$NON-NLS-1$ + new ObjectStreamField("infinity", String.class), //$NON-NLS-1$ + new ObjectStreamField("intlCurrencySymbol", String.class), //$NON-NLS-1$ + new ObjectStreamField("minusSign", Character.TYPE), //$NON-NLS-1$ + new ObjectStreamField("monetarySeparator", Character.TYPE), //$NON-NLS-1$ + new ObjectStreamField("NaN", String.class), //$NON-NLS-1$ + new ObjectStreamField("patternSeparator", Character.TYPE), //$NON-NLS-1$ + new ObjectStreamField("percent", Character.TYPE), //$NON-NLS-1$ + new ObjectStreamField("perMill", Character.TYPE), //$NON-NLS-1$ + new ObjectStreamField("serialVersionOnStream", Integer.TYPE), //$NON-NLS-1$ + new ObjectStreamField("zeroDigit", Character.TYPE), //$NON-NLS-1$ + new ObjectStreamField("locale", Locale.class), }; //$NON-NLS-1$ + + private void writeObject(ObjectOutputStream stream) throws IOException { + ObjectOutputStream.PutField fields = stream.putFields(); + fields.put("currencySymbol", currencySymbol); //$NON-NLS-1$ + fields.put("decimalSeparator", getDecimalSeparator()); //$NON-NLS-1$ + fields.put("digit", getDigit()); //$NON-NLS-1$ + fields.put("exponential", getExponential()); //$NON-NLS-1$ + fields.put("groupingSeparator", getGroupingSeparator()); //$NON-NLS-1$ + fields.put("infinity", infinity); //$NON-NLS-1$ + fields.put("intlCurrencySymbol", intlCurrencySymbol); //$NON-NLS-1$ + fields.put("minusSign", getMinusSign()); //$NON-NLS-1$ + fields.put("monetarySeparator", getMonetaryDecimalSeparator()); //$NON-NLS-1$ + fields.put("NaN", NaN); //$NON-NLS-1$ + fields.put("patternSeparator", getPatternSeparator()); //$NON-NLS-1$ + fields.put("percent", getPercent()); //$NON-NLS-1$ + fields.put("perMill", getPerMill()); //$NON-NLS-1$ + fields.put("serialVersionOnStream", 1); //$NON-NLS-1$ + fields.put("zeroDigit", getZeroDigit()); //$NON-NLS-1$ + fields.put("locale", locale); //$NON-NLS-1$ + stream.writeFields(); + } + + private void readObject(ObjectInputStream stream) throws IOException, + ClassNotFoundException { + ObjectInputStream.GetField fields = stream.readFields(); + patternChars = new char[10]; + currencySymbol = (String) fields.get("currencySymbol", ""); //$NON-NLS-1$ //$NON-NLS-2$ + setDecimalSeparator(fields.get("decimalSeparator", '.')); //$NON-NLS-1$ + setDigit(fields.get("digit", '#')); //$NON-NLS-1$ + setGroupingSeparator(fields.get("groupingSeparator", ',')); //$NON-NLS-1$ + infinity = (String) fields.get("infinity", ""); //$NON-NLS-1$ //$NON-NLS-2$ + intlCurrencySymbol = (String) fields.get("intlCurrencySymbol", ""); //$NON-NLS-1$ //$NON-NLS-2$ + setMinusSign(fields.get("minusSign", '-')); //$NON-NLS-1$ + NaN = (String) fields.get("NaN", ""); //$NON-NLS-1$ //$NON-NLS-2$ + setPatternSeparator(fields.get("patternSeparator", ';')); //$NON-NLS-1$ + setPercent(fields.get("percent", '%')); //$NON-NLS-1$ + setPerMill(fields.get("perMill", '\u2030')); //$NON-NLS-1$ + setZeroDigit(fields.get("zeroDigit", '0')); //$NON-NLS-1$ + locale = (Locale) fields.get("locale", null); //$NON-NLS-1$ + if (fields.get("serialVersionOnStream", 0) == 0) { //$NON-NLS-1$ + setMonetaryDecimalSeparator(getDecimalSeparator()); + setExponential('E'); + } else { + setMonetaryDecimalSeparator(fields.get("monetarySeparator", '.')); //$NON-NLS-1$ + setExponential(fields.get("exponential", 'E')); //$NON-NLS-1$ + + } + try { + currency = Currency.getInstance(intlCurrencySymbol); + } catch (IllegalArgumentException e) { + currency = null; + } + } +} diff --git a/text/src/main/java/java/text/FieldPosition.java b/text/src/main/java/java/text/FieldPosition.java new file mode 100644 index 0000000..2319b8e --- /dev/null +++ b/text/src/main/java/java/text/FieldPosition.java @@ -0,0 +1,171 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package java.text; + +/** + * FieldPosition is used to identify fields in formatted Strings. + */ +public class FieldPosition { + + private int myField, beginIndex, endIndex; + + private Format.Field myAttribute; + + /** + * Constructs a new FieldPosition on the specified field. + * + * @param field + * the field to identify + */ + public FieldPosition(int field) { + myField = field; + } + + /** + * Constructs a new FieldPosition on the specified Field attribute. + * + * @param attribute + * the field attribute to identify + */ + public FieldPosition(Format.Field attribute) { + myAttribute = attribute; + myField = -1; + } + + /** + * Constructs a new FieldPosition on the specified Field attribute and field + * id. + * + * @param attribute + * the field attribute to identify + * @param field + * the field to identify + */ + public FieldPosition(Format.Field attribute, int field) { + myAttribute = attribute; + myField = field; + } + + void clear() { + beginIndex = endIndex = 0; + } + + /** + * Compares the specified object to this FieldPosition and answer if they + * are equal. The object must be an instance of FieldPosition with the same + * field, begin index and end index. + * + * @param object + * the object to compare with this object + * @return true if the specified object is equal to this fieldPosition, + * false otherwise + * + * @see #hashCode + */ + @Override + public boolean equals(Object object) { + if (!(object instanceof FieldPosition)) { + return false; + } + FieldPosition pos = (FieldPosition) object; + return myField == pos.myField && myAttribute == pos.myAttribute + && beginIndex == pos.beginIndex && endIndex == pos.endIndex; + } + + /** + * Returns the index of the beginning of the field. + * + * @return the first index of the field + */ + public int getBeginIndex() { + return beginIndex; + } + + /** + * Returns the index one past the end of the field. + * + * @return one past the index of the last character in the field + */ + public int getEndIndex() { + return endIndex; + } + + /** + * Returns the field which is being identified. + * + * @return the field + */ + public int getField() { + return myField; + } + + /** + * Returns the attribute which is being identified. + * + * @return the field + */ + public Format.Field getFieldAttribute() { + return myAttribute; + } + + /** + * Returns an integer hash code for the receiver. Objects which are equal + * answer the same value for this method. + * + * @return the receiver's hash + * + * @see #equals + */ + @Override + public int hashCode() { + int attributeHash = (myAttribute == null) ? 0 : myAttribute.hashCode(); + return attributeHash + myField * 10 + beginIndex * 100 + endIndex; + } + + /** + * Sets the index of the beginning of the field. + * + * @param index + * the index of the first character in the field + */ + public void setBeginIndex(int index) { + beginIndex = index; + } + + /** + * Sets the index of the end of the field. + * + * @param index + * one past the index of the last character in the field + */ + public void setEndIndex(int index) { + endIndex = index; + } + + /** + * Returns the string representation of this FieldPosition. + * + * @return the string representation of this FieldPosition + */ + @Override + public String toString() { + return getClass().getName() + "[attribute=" + myAttribute + ", field=" //$NON-NLS-1$ //$NON-NLS-2$ + + myField + ", beginIndex=" + beginIndex + ", endIndex=" //$NON-NLS-1$ //$NON-NLS-2$ + + endIndex + "]"; //$NON-NLS-1$ + } +} diff --git a/text/src/main/java/java/text/Format.java b/text/src/main/java/java/text/Format.java new file mode 100644 index 0000000..1b93898 --- /dev/null +++ b/text/src/main/java/java/text/Format.java @@ -0,0 +1,262 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package java.text; + +import java.io.Serializable; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Locale; +import java.util.ResourceBundle; + +import org.apache.harmony.text.internal.nls.Messages; + +/** + * Format is the abstract superclass of classes which format and parse objects + * according to Locale specific rules. + */ +public abstract class Format implements Serializable, Cloneable { + + private static final long serialVersionUID = -299282585814624189L; + + /** + * Constructs a new instance of Format. + * + */ + public Format() { + } + + /** + * Returns a copy of this Format. + * + * @return a shallow copy of this Format + * + * @see java.lang.Cloneable + */ + @Override + public Object clone() { + try { + return super.clone(); + } catch (CloneNotSupportedException e) { + return null; + } + } + + static ResourceBundle getBundle(final Locale locale) { + return AccessController + .doPrivileged(new PrivilegedAction<ResourceBundle>() { + public ResourceBundle run() { + return ResourceBundle + .getBundle( + "org.apache.harmony.luni.internal.locale.Locale", locale); //$NON-NLS-1$ + } + }); + } + + String convertPattern(String template, String fromChars, String toChars, + boolean check) { + if (!check && fromChars.equals(toChars)) { + return template; + } + boolean quote = false; + StringBuilder output = new StringBuilder(); + int length = template.length(); + for (int i = 0; i < length; i++) { + int index; + char next = template.charAt(i); + if (next == '\'') { + quote = !quote; + } + if (!quote && (index = fromChars.indexOf(next)) != -1) { + output.append(toChars.charAt(index)); + } else if (check + && !quote + && ((next >= 'a' && next <= 'z') || (next >= 'A' && next <= 'Z'))) { + // text.05=Invalid pattern char {0} in {1} + throw new IllegalArgumentException(Messages.getString( + "text.05", String.valueOf(next), template)); //$NON-NLS-1$ + } else { + output.append(next); + } + } + if (quote) { + // text.04=Unterminated quote + throw new IllegalArgumentException(Messages.getString("text.04")); //$NON-NLS-1$ + } + return output.toString(); + } + + /** + * Formats the specified object using the rules of this Format. + * + * + * @param object + * the object to format + * @return the formatted String + * + * @exception IllegalArgumentException + * when the object cannot be formatted by this Format + */ + public final String format(Object object) { + return format(object, new StringBuffer(), new FieldPosition(0)) + .toString(); + } + + /** + * Formats the specified object into the specified StringBuffer using the + * rules of this Format. If the field specified by the FieldPosition is + * formatted, set the begin and end index of the formatted field in the + * FieldPosition. + * + * @param object + * the object to format + * @param buffer + * the StringBuffer + * @param field + * the FieldPosition + * @return the StringBuffer parameter <code>buffer</code> + * + * @exception IllegalArgumentException + * when the object cannot be formatted by this Format + */ + public abstract StringBuffer format(Object object, StringBuffer buffer, + FieldPosition field); + + /** + * Formats the specified object using the rules of this format and returns + * an AttributedCharacterIterator with the formatted String and no + * attributes. + * <p> + * Subclasses should return an AttributedCharacterIterator with the + * appropriate attributes. + * + * @param object + * the object to format + * @return an AttributedCharacterIterator with the formatted object and + * attributes + * + * @exception IllegalArgumentException + * when the object cannot be formatted by this Format + */ + public AttributedCharacterIterator formatToCharacterIterator(Object object) { + return new AttributedString(format(object)).getIterator(); + } + + /** + * Parse the specified String using the rules of this Format. + * + * @param string + * the String to parse + * @return the object resulting from the parse + * + * @exception ParseException + * when an error occurs during parsing + */ + public Object parseObject(String string) throws ParseException { + ParsePosition position = new ParsePosition(0); + Object result = parseObject(string, position); + if (position.getErrorIndex() != -1 || position.getIndex() == 0) { + throw new ParseException(null, position.getErrorIndex()); + } + return result; + } + + /** + * Parse the specified String starting at the index specified by the + * ParsePosition. If the string is successfully parsed, the index of the + * ParsePosition is updated to the index following the parsed text. + * + * @param string + * the String to parse + * @param position + * the ParsePosition, updated on return with the index following + * the parsed text, or on error the index is unchanged and the + * error index is set to the index where the error occurred + * @return the object resulting from the parse, or null if there is an error + */ + public abstract Object parseObject(String string, ParsePosition position); + + static boolean upTo(String string, ParsePosition position, + StringBuffer buffer, char stop) { + int index = position.getIndex(), length = string.length(); + boolean lastQuote = false, quote = false; + while (index < length) { + char ch = string.charAt(index++); + if (ch == '\'') { + if (lastQuote) { + buffer.append('\''); + } + quote = !quote; + lastQuote = true; + } else if (ch == stop && !quote) { + position.setIndex(index); + return true; + } else { + lastQuote = false; + buffer.append(ch); + } + } + position.setIndex(index); + return false; + } + + static boolean upToWithQuotes(String string, ParsePosition position, + StringBuffer buffer, char stop, char start) { + int index = position.getIndex(), length = string.length(), count = 1; + boolean quote = false; + while (index < length) { + char ch = string.charAt(index++); + if (ch == '\'') { + quote = !quote; + } + if (!quote) { + if (ch == stop) { + count--; + } + if (count == 0) { + position.setIndex(index); + return true; + } + if (ch == start) { + count++; + } + } + buffer.append(ch); + } + // text.07=Unmatched braces in the pattern + throw new IllegalArgumentException(Messages.getString("text.07")); //$NON-NLS-1$ + } + + /** + * This inner class is used to represent Format attributes in the + * AttributedCharacterIterator that formatToCharacterIterator() method + * returns in the Format subclasses. + */ + public static class Field extends AttributedCharacterIterator.Attribute { + + private static final long serialVersionUID = 276966692217360283L; + + /** + * Constructs a new instance of Field with the given fieldName. + * + * @param fieldName The field name. + */ + protected Field(String fieldName) { + super(fieldName); + } + } +} diff --git a/text/src/main/java/java/text/MessageFormat.java b/text/src/main/java/java/text/MessageFormat.java new file mode 100644 index 0000000..40fd90c --- /dev/null +++ b/text/src/main/java/java/text/MessageFormat.java @@ -0,0 +1,1037 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package java.text; + +import java.io.IOException; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.ObjectStreamField; +import java.util.Arrays; +import java.util.Date; +import java.util.Iterator; +import java.util.Locale; +import java.util.Vector; + +import org.apache.harmony.text.internal.nls.Messages; + +/** + * MessageFormat is used to format and parse arguments based on a pattern. The + * pattern specifies how each argument will be formatted and concatenated with + * other text to produce the formatted output. + */ +public class MessageFormat extends Format { + + private static final long serialVersionUID = 6479157306784022952L; + + private Locale locale = Locale.getDefault(); + + transient private String[] strings; + + private int[] argumentNumbers; + + private Format[] formats; + + private int maxOffset; + + transient private int maxArgumentIndex; + + /** + * Constructs a new MessageFormat using the specified pattern and the + * specified Locale for Formats. + * + * @param template + * the pattern + * @param locale + * the locale + * + * @exception IllegalArgumentException + * when the pattern cannot be parsed + */ + public MessageFormat(String template, Locale locale) { + this.locale = locale; + applyPattern(template); + } + + /** + * Constructs a new MessageFormat using the specified pattern and the + * default Locale for Formats. + * + * @param template + * the pattern + * + * @exception IllegalArgumentException + * when the pattern cannot be parsed + */ + public MessageFormat(String template) { + applyPattern(template); + } + + /** + * Changes this MessageFormat to use the specified pattern. + * + * @param template + * the pattern + * + * @exception IllegalArgumentException + * when the pattern cannot be parsed + */ + public void applyPattern(String template) { + int length = template.length(); + StringBuffer buffer = new StringBuffer(); + ParsePosition position = new ParsePosition(0); + Vector<String> localStrings = new Vector<String>(); + int argCount = 0; + int[] args = new int[10]; + int maxArg = -1; + Vector<Format> localFormats = new Vector<Format>(); + while (position.getIndex() < length) { + if (Format.upTo(template, position, buffer, '{')) { + byte arg; + int offset = position.getIndex(); + if (offset >= length + || (arg = (byte) Character.digit(template + .charAt(offset++), 10)) == -1) { + // text.19=Invalid argument number + throw new IllegalArgumentException(Messages + .getString("text.19")); //$NON-NLS-1$ + } + position.setIndex(offset); + localFormats.addElement(parseVariable(template, position)); + if (argCount >= args.length) { + int[] newArgs = new int[args.length * 2]; + System.arraycopy(args, 0, newArgs, 0, args.length); + args = newArgs; + } + args[argCount++] = arg; + if (arg > maxArg) { + maxArg = arg; + } + } + localStrings.addElement(buffer.toString()); + buffer.setLength(0); + } + this.strings = new String[localStrings.size()]; + for (int i = 0; i < localStrings.size(); i++) { + this.strings[i] = localStrings.elementAt(i); + } + argumentNumbers = args; + this.formats = new Format[argCount]; + for (int i = 0; i < argCount; i++) { + this.formats[i] = localFormats.elementAt(i); + } + maxOffset = argCount - 1; + maxArgumentIndex = maxArg; + } + + /** + * Returns a new instance of MessageFormat with the same pattern and Formats + * as this MessageFormat. + * + * @return a shallow copy of this MessageFormat + * + * @see java.lang.Cloneable + */ + @Override + public Object clone() { + MessageFormat clone = (MessageFormat) super.clone(); + Format[] array = new Format[formats.length]; + for (int i = formats.length; --i >= 0;) { + if (formats[i] != null) { + array[i] = (Format) formats[i].clone(); + } + } + clone.formats = array; + return clone; + } + + /** + * Compares the specified object to this MessageFormat and answer if they + * are equal. The object must be an instance of MessageFormat and have the + * same pattern. + * + * @param object + * the object to compare with this object + * @return true if the specified object is equal to this MessageFormat, + * false otherwise + * + * @see #hashCode + */ + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (!(object instanceof MessageFormat)) { + return false; + } + MessageFormat format = (MessageFormat) object; + if (maxOffset != format.maxOffset) { + return false; + } + // Must use a loop since the lengths may be different due + // to serialization cross-loading + for (int i = 0; i <= maxOffset; i++) { + if (argumentNumbers[i] != format.argumentNumbers[i]) { + return false; + } + } + return locale.equals(format.locale) + && Arrays.equals(strings, format.strings) + && Arrays.equals(formats, format.formats); + } + + /** + * Formats the specified object using the rules of this MessageFormat and + * returns an AttributedCharacterIterator with the formatted message and + * attributes. The AttributedCharacterIterator returned also includes the + * attributes from the formats of this MessageFormat. + * + * @param object + * the object to format + * @return an AttributedCharacterIterator with the formatted message and + * attributes + * + * @exception IllegalArgumentException + * when the arguments in the object array cannot be formatted + * by this Format + */ + @Override + public AttributedCharacterIterator formatToCharacterIterator(Object object) { + if (object == null) { + throw new NullPointerException(); + } + + StringBuffer buffer = new StringBuffer(); + Vector<FieldContainer> fields = new Vector<FieldContainer>(); + + // format the message, and find fields + formatImpl((Object[]) object, buffer, new FieldPosition(0), fields); + + // create an AttributedString with the formatted buffer + AttributedString as = new AttributedString(buffer.toString()); + + // add MessageFormat field attributes and values to the AttributedString + for (int i = 0; i < fields.size(); i++) { + FieldContainer fc = fields.elementAt(i); + as.addAttribute(fc.attribute, fc.value, fc.start, fc.end); + } + + // return the CharacterIterator from AttributedString + return as.getIterator(); + } + + /** + * Formats the Object arguments into the specified StringBuffer using the + * pattern of this MessageFormat. + * <p> + * If Field Attribute of the FieldPosition supplied is + * MessageFormat.Field.ARGUMENT, then begin and end index of this field + * position is set to the location of the first occurrence of a message + * format argument. Otherwise the FieldPosition is ignored + * <p> + * + * @param objects + * the array of Objects to format + * @param buffer + * the StringBuffer + * @param field + * a FieldPosition. + * + * @return the StringBuffer parameter <code>buffer</code> + */ + public final StringBuffer format(Object[] objects, StringBuffer buffer, + FieldPosition field) { + return formatImpl(objects, buffer, field, null); + } + + private StringBuffer formatImpl(Object[] objects, StringBuffer buffer, + FieldPosition position, Vector<FieldContainer> fields) { + FieldPosition passedField = new FieldPosition(0); + for (int i = 0; i <= maxOffset; i++) { + buffer.append(strings[i]); + int begin = buffer.length(); + Object arg; + if (objects != null && argumentNumbers[i] < objects.length) { + arg = objects[argumentNumbers[i]]; + } else { + buffer.append('{'); + buffer.append(argumentNumbers[i]); + buffer.append('}'); + handleArgumentField(begin, buffer.length(), argumentNumbers[i], + position, fields); + continue; + } + Format format = formats[i]; + if (format == null || arg == null) { + if (arg instanceof Number) { + format = NumberFormat.getInstance(); + } else if (arg instanceof Date) { + format = DateFormat.getInstance(); + } else { + buffer.append(arg); + handleArgumentField(begin, buffer.length(), + argumentNumbers[i], position, fields); + continue; + } + } + if (format instanceof ChoiceFormat) { + String result = format.format(arg); + MessageFormat mf = new MessageFormat(result); + mf.setLocale(locale); + mf.format(objects, buffer, passedField); + handleArgumentField(begin, buffer.length(), argumentNumbers[i], + position, fields); + handleformat(format, arg, begin, fields); + } else { + format.format(arg, buffer, passedField); + handleArgumentField(begin, buffer.length(), argumentNumbers[i], + position, fields); + handleformat(format, arg, begin, fields); + } + } + if (maxOffset + 1 < strings.length) { + buffer.append(strings[maxOffset + 1]); + } + return buffer; + } + + /** + * Adds a new FieldContainer with MessageFormat.Field.ARGUMENT field, + * argnumber, begin and end index to the fields vector, or sets the + * position's begin and end index if it has MessageFormat.Field.ARGUMENT as + * its field attribute. + * + * @param begin + * @param end + * @param argnumber + * @param position + * @param fields + */ + private void handleArgumentField(int begin, int end, int argnumber, + FieldPosition position, Vector<FieldContainer> fields) { + if (fields != null) { + fields.add(new FieldContainer(begin, end, Field.ARGUMENT, + new Integer(argnumber))); + } else { + if (position != null + && position.getFieldAttribute() == Field.ARGUMENT + && position.getEndIndex() == 0) { + position.setBeginIndex(begin); + position.setEndIndex(end); + } + } + } + + /** + * An inner class to store attributes, values, start and end indices. + * Instances of this inner class are used as elements for the fields vector + */ + private static class FieldContainer { + int start, end; + + AttributedCharacterIterator.Attribute attribute; + + Object value; + + public FieldContainer(int start, int end, + AttributedCharacterIterator.Attribute attribute, Object value) { + this.start = start; + this.end = end; + this.attribute = attribute; + this.value = value; + } + } + + /** + * If fields vector is not null, find and add the fields of this format to + * the fields vector by iterating through its AttributedCharacterIterator + * + * @param format + * the format to find fields for + * @param arg + * object to format + * @param begin + * the index where the string this format has formatted begins + * @param fields + * fields vector, each entry in this vector are of type + * FieldContainer. + */ + private void handleformat(Format format, Object arg, int begin, + Vector<FieldContainer> fields) { + if (fields != null) { + AttributedCharacterIterator iterator = format + .formatToCharacterIterator(arg); + while (iterator.getIndex() != iterator.getEndIndex()) { + int start = iterator.getRunStart(); + int end = iterator.getRunLimit(); + + Iterator<?> it = iterator.getAttributes().keySet().iterator(); + while (it.hasNext()) { + AttributedCharacterIterator.Attribute attribute = (AttributedCharacterIterator.Attribute) it + .next(); + Object value = iterator.getAttribute(attribute); + fields.add(new FieldContainer(begin + start, begin + end, + attribute, value)); + } + iterator.setIndex(end); + } + } + } + + /** + * Formats the specified object into the specified StringBuffer using the + * pattern of this MessageFormat. + * + * @param object + * the object to format, must be an array of Object + * @param buffer + * the StringBuffer + * @param field + * a FieldPosition which is ignored + * @return the StringBuffer parameter <code>buffer</code> + * + * @exception ClassCastException + * when <code>object</code> is not an array of Object + */ + @Override + public final StringBuffer format(Object object, StringBuffer buffer, + FieldPosition field) { + return format((Object[]) object, buffer, field); + } + + /** + * Formats the Object arguments using the specified MessageFormat pattern. + * + * @param template + * the pattern + * @param objects + * the array of Objects to format + * @return the formatted result + * + * @exception IllegalArgumentException + * when the pattern cannot be parsed + */ + public static String format(String template, Object... objects) { + return new MessageFormat(template).format(objects); + } + + /** + * Returns the Formats of this MessageFormat. + * + * @return an array of Format + */ + public Format[] getFormats() { + return formats.clone(); + } + + /** + * Returns the formats used for each argument index. If an argument is + * placed more than once in the pattern string, than returns the format of + * the last one. + * + * @return an array of formats, ordered by argument index + */ + public Format[] getFormatsByArgumentIndex() { + Format[] answer = new Format[maxArgumentIndex + 1]; + for (int i = 0; i < maxOffset + 1; i++) { + answer[argumentNumbers[i]] = formats[i]; + } + return answer; + } + + /** + * Sets the format used for argument at index <code>argIndex</code>to + * <code>format</code> + * + * @param argIndex + * @param format + */ + public void setFormatByArgumentIndex(int argIndex, Format format) { + for (int i = 0; i < maxOffset + 1; i++) { + if (argumentNumbers[i] == argIndex) { + formats[i] = format; + } + } + } + + /** + * Sets the formats used for each argument <code>The formats</code> array + * elements should be in the order of the argument indices. + * + * @param formats + */ + public void setFormatsByArgumentIndex(Format[] formats) { + for (int j = 0; j < formats.length; j++) { + for (int i = 0; i < maxOffset + 1; i++) { + if (argumentNumbers[i] == j) { + this.formats[i] = formats[j]; + } + } + } + } + + /** + * Returns the Locale used when creating Formats. + * + * @return the Locale used to create Formats + */ + public Locale getLocale() { + return locale; + } + + /** + * Returns an integer hash code for the receiver. Objects which are equal + * answer the same value for this method. + * + * @return the receiver's hash + * + * @see #equals + */ + @Override + public int hashCode() { + int hashCode = 0; + for (int i = 0; i <= maxOffset; i++) { + hashCode += argumentNumbers[i] + strings[i].hashCode(); + if (formats[i] != null) { + hashCode += formats[i].hashCode(); + } + } + if (maxOffset + 1 < strings.length) { + hashCode += strings[maxOffset + 1].hashCode(); + } + if (locale != null) { + return hashCode + locale.hashCode(); + } + return hashCode; + } + + /** + * Parse the message arguments from the specified String using the rules of + * this MessageFormat. + * + * @param string + * the String to parse + * @return the array of Object arguments resulting from the parse + * + * @exception ParseException + * when an error occurs during parsing + */ + public Object[] parse(String string) throws ParseException { + ParsePosition position = new ParsePosition(0); + Object[] result = parse(string, position); + if (position.getErrorIndex() != -1 || position.getIndex() == 0) { + throw new ParseException(null, position.getErrorIndex()); + } + return result; + } + + /** + * Parse the message argument from the specified String starting at the + * index specified by the ParsePosition. If the string is successfully + * parsed, the index of the ParsePosition is updated to the index following + * the parsed text. + * + * @param string + * the String to parse + * @param position + * the ParsePosition, updated on return with the index following + * the parsed text, or on error the index is unchanged and the + * error index is set to the index where the error occurred + * @return the array of Object arguments resulting from the parse, or null + * if there is an error + */ + public Object[] parse(String string, ParsePosition position) { + if (string == null) { + return new Object[0]; + } + ParsePosition internalPos = new ParsePosition(0); + int offset = position.getIndex(); + Object[] result = new Object[maxArgumentIndex + 1]; + for (int i = 0; i <= maxOffset; i++) { + String sub = strings[i]; + if (!string.startsWith(sub, offset)) { + position.setErrorIndex(offset); + return null; + } + offset += sub.length(); + Object parse; + Format format = formats[i]; + if (format == null) { + if (i + 1 < strings.length) { + int next = string.indexOf(strings[i + 1], offset); + if (next == -1) { + position.setErrorIndex(offset); + return null; + } + parse = string.substring(offset, next); + offset = next; + } else { + parse = string.substring(offset); + offset = string.length(); + } + } else { + internalPos.setIndex(offset); + parse = format.parseObject(string, internalPos); + if (internalPos.getErrorIndex() != -1) { + position.setErrorIndex(offset); + return null; + } + offset = internalPos.getIndex(); + } + result[argumentNumbers[i]] = parse; + } + if (maxOffset + 1 < strings.length) { + String sub = strings[maxOffset + 1]; + if (!string.startsWith(sub, offset)) { + position.setErrorIndex(offset); + return null; + } + offset += sub.length(); + } + position.setIndex(offset); + return result; + } + + /** + * Parse the message argument from the specified String starting at the + * index specified by the ParsePosition. If the string is successfully + * parsed, the index of the ParsePosition is updated to the index following + * the parsed text. + * + * @param string + * the String to parse + * @param position + * the ParsePosition, updated on return with the index following + * the parsed text, or on error the index is unchanged and the + * error index is set to the index where the error occurred + * @return the array of Object arguments resulting from the parse, or null + * if there is an error + */ + @Override + public Object parseObject(String string, ParsePosition position) { + return parse(string, position); + } + + private int match(String string, ParsePosition position, boolean last, + String[] tokens) { + int length = string.length(), offset = position.getIndex(), token = -1; + while (offset < length && Character.isWhitespace(string.charAt(offset))) { + offset++; + } + for (int i = tokens.length; --i >= 0;) { + if (string.regionMatches(true, offset, tokens[i], 0, tokens[i] + .length())) { + token = i; + break; + } + } + if (token == -1) { + return -1; + } + offset += tokens[token].length(); + while (offset < length && Character.isWhitespace(string.charAt(offset))) { + offset++; + } + char ch; + if (offset < length + && ((ch = string.charAt(offset)) == '}' || (!last && ch == ','))) { + position.setIndex(offset + 1); + return token; + } + return -1; + } + + private Format parseVariable(String string, ParsePosition position) { + int length = string.length(), offset = position.getIndex(); + char ch; + if (offset >= length + || ((ch = string.charAt(offset++)) != '}' && ch != ',')) { + // text.15=Missing element format + throw new IllegalArgumentException(Messages.getString("text.15")); //$NON-NLS-1$ + } + position.setIndex(offset); + if (ch == '}') { + return null; + } + int type = match(string, position, false, new String[] { "time", //$NON-NLS-1$ + "date", "number", "choice" }); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + if (type == -1) { + // text.16=Unknown element format + throw new IllegalArgumentException(Messages.getString("text.16")); //$NON-NLS-1$ + } + StringBuffer buffer = new StringBuffer(); + ch = string.charAt(position.getIndex() - 1); + switch (type) { + case 0: // time + case 1: // date + if (ch == '}') { + return type == 1 ? DateFormat.getDateInstance( + DateFormat.DEFAULT, locale) : DateFormat + .getTimeInstance(DateFormat.DEFAULT, locale); + } + int dateStyle = match(string, position, true, new String[] { + "full", "long", "medium", "short" }); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + if (dateStyle == -1) { + Format.upToWithQuotes(string, position, buffer, '}', '{'); + return new SimpleDateFormat(buffer.toString(), locale); + } + switch (dateStyle) { + case 0: + dateStyle = DateFormat.FULL; + break; + case 1: + dateStyle = DateFormat.LONG; + break; + case 2: + dateStyle = DateFormat.MEDIUM; + break; + case 3: + dateStyle = DateFormat.SHORT; + break; + } + return type == 1 ? DateFormat + .getDateInstance(dateStyle, locale) : DateFormat + .getTimeInstance(dateStyle, locale); + case 2: // number + if (ch == '}') { + return NumberFormat.getInstance(); + } + int numberStyle = match(string, position, true, new String[] { + "currency", "percent", "integer" }); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + if (numberStyle == -1) { + Format.upToWithQuotes(string, position, buffer, '}', '{'); + return new DecimalFormat(buffer.toString(), + new DecimalFormatSymbols(locale)); + } + switch (numberStyle) { + case 0: // currency + return NumberFormat.getCurrencyInstance(locale); + case 1: // percent + return NumberFormat.getPercentInstance(locale); + } + return NumberFormat.getIntegerInstance(locale); + } + // choice + try { + Format.upToWithQuotes(string, position, buffer, '}', '{'); + } catch (IllegalArgumentException e) { + // ignored + } + return new ChoiceFormat(buffer.toString()); + } + + /** + * Sets the specified Format used by this MessageFormat. + * + * @param offset + * the format to change + * @param format + * the Format + */ + public void setFormat(int offset, Format format) { + formats[offset] = format; + } + + /** + * Sets the Formats used by this MessageFormat. + * + * @param formats + * an array of Format + */ + public void setFormats(Format[] formats) { + int min = this.formats.length; + if (formats.length < min) { + min = formats.length; + } + for (int i = 0; i < min; i++) { + this.formats[i] = formats[i]; + } + } + + /** + * Sets the Locale to use when creating Formats. + * + * @param locale + * the Locale + */ + public void setLocale(Locale locale) { + this.locale = locale; + for (int i = 0; i <= maxOffset; i++) { + Format format = formats[i]; + // BEGIN android-removed + //if (format instanceof DecimalFormat) { + // formats[i] = new DecimalFormat(((DecimalFormat) format) + // .toPattern(), new DecimalFormatSymbols(locale)); + //} else if (format instanceof SimpleDateFormat) { + // formats[i] = new SimpleDateFormat(((SimpleDateFormat) format) + // .toPattern(), locale); + //} + // END android-removed + // BEGIN android-added + // java specification undefined for null argument, change into + // a more tolerant implementation + if (format instanceof DecimalFormat) { + try { + formats[i] = new DecimalFormat(((DecimalFormat) format) + .toPattern(), new DecimalFormatSymbols(locale)); + } catch (NullPointerException npe){ + formats[i] = null; + } + } else if (format instanceof SimpleDateFormat) { + try { + formats[i] = new SimpleDateFormat(((SimpleDateFormat) format) + .toPattern(), locale); + } catch (NullPointerException npe) { + formats[i] = null; + } + } + // END android-added + } + } + + private String decodeDecimalFormat(StringBuffer buffer, Format format) { + buffer.append(",number"); //$NON-NLS-1$ + if (format.equals(NumberFormat.getNumberInstance(locale))) { + // Empty block + } else if (format.equals(NumberFormat.getIntegerInstance(locale))) { + buffer.append(",integer"); //$NON-NLS-1$ + } else if (format.equals(NumberFormat.getCurrencyInstance(locale))) { + buffer.append(",currency"); //$NON-NLS-1$ + } else if (format.equals(NumberFormat.getPercentInstance(locale))) { + buffer.append(",percent"); //$NON-NLS-1$ + } else { + buffer.append(','); + return ((DecimalFormat) format).toPattern(); + } + return null; + } + + private String decodeSimpleDateFormat(StringBuffer buffer, Format format) { + if (format.equals(DateFormat + .getTimeInstance(DateFormat.DEFAULT, locale))) { + buffer.append(",time"); //$NON-NLS-1$ + } else if (format.equals(DateFormat.getDateInstance(DateFormat.DEFAULT, + locale))) { + buffer.append(",date"); //$NON-NLS-1$ + } else if (format.equals(DateFormat.getTimeInstance(DateFormat.SHORT, + locale))) { + buffer.append(",time,short"); //$NON-NLS-1$ + } else if (format.equals(DateFormat.getDateInstance(DateFormat.SHORT, + locale))) { + buffer.append(",date,short"); //$NON-NLS-1$ + } else if (format.equals(DateFormat.getTimeInstance(DateFormat.LONG, + locale))) { + buffer.append(",time,long"); //$NON-NLS-1$ + } else if (format.equals(DateFormat.getDateInstance(DateFormat.LONG, + locale))) { + buffer.append(",date,long"); //$NON-NLS-1$ + } else if (format.equals(DateFormat.getTimeInstance(DateFormat.FULL, + locale))) { + buffer.append(",time,full"); //$NON-NLS-1$ + } else if (format.equals(DateFormat.getDateInstance(DateFormat.FULL, + locale))) { + buffer.append(",date,full"); //$NON-NLS-1$ + } else { + buffer.append(",date,"); //$NON-NLS-1$ + return ((SimpleDateFormat) format).toPattern(); + } + return null; + } + + /** + * Returns the pattern of this MessageFormat. + * + * @return the pattern + */ + public String toPattern() { + StringBuffer buffer = new StringBuffer(); + for (int i = 0; i <= maxOffset; i++) { + appendQuoted(buffer, strings[i]); + buffer.append('{'); + buffer.append(argumentNumbers[i]); + Format format = formats[i]; + String pattern = null; + if (format instanceof ChoiceFormat) { + buffer.append(",choice,"); //$NON-NLS-1$ + pattern = ((ChoiceFormat) format).toPattern(); + } else if (format instanceof DecimalFormat) { + pattern = decodeDecimalFormat(buffer, format); + } else if (format instanceof SimpleDateFormat) { + pattern = decodeSimpleDateFormat(buffer, format); + } else if (format != null) { + // text.17=Unknown format + throw new IllegalArgumentException(Messages + .getString("text.17")); //$NON-NLS-1$ + } + if (pattern != null) { + boolean quote = false; + int index = 0, length = pattern.length(), count = 0; + while (index < length) { + char ch = pattern.charAt(index++); + if (ch == '\'') { + quote = !quote; + } + if (!quote) { + if (ch == '{') { + count++; + } + if (ch == '}') { + if (count > 0) { + count--; + } else { + buffer.append("'}"); //$NON-NLS-1$ + ch = '\''; + } + } + } + buffer.append(ch); + } + } + buffer.append('}'); + } + if (maxOffset + 1 < strings.length) { + appendQuoted(buffer, strings[maxOffset + 1]); + } + return buffer.toString(); + } + + private void appendQuoted(StringBuffer buffer, String string) { + int length = string.length(); + for (int i = 0; i < length; i++) { + char ch = string.charAt(i); + if (ch == '{' || ch == '}') { + buffer.append('\''); + buffer.append(ch); + buffer.append('\''); + } else { + buffer.append(ch); + } + } + } + + private static final ObjectStreamField[] serialPersistentFields = { + new ObjectStreamField("argumentNumbers", int[].class), //$NON-NLS-1$ + new ObjectStreamField("formats", Format[].class), //$NON-NLS-1$ + new ObjectStreamField("locale", Locale.class), //$NON-NLS-1$ + new ObjectStreamField("maxOffset", Integer.TYPE), //$NON-NLS-1$ + new ObjectStreamField("offsets", int[].class), //$NON-NLS-1$ + new ObjectStreamField("pattern", String.class), }; //$NON-NLS-1$ + + private void writeObject(ObjectOutputStream stream) throws IOException { + ObjectOutputStream.PutField fields = stream.putFields(); + fields.put("argumentNumbers", argumentNumbers); //$NON-NLS-1$ + Format[] compatibleFormats = formats; + fields.put("formats", compatibleFormats); //$NON-NLS-1$ + fields.put("locale", locale); //$NON-NLS-1$ + fields.put("maxOffset", maxOffset); //$NON-NLS-1$ + int offset = 0; + int offsetsLength = maxOffset + 1; + int[] offsets = new int[offsetsLength]; + StringBuffer pattern = new StringBuffer(); + for (int i = 0; i <= maxOffset; i++) { + offset += strings[i].length(); + offsets[i] = offset; + pattern.append(strings[i]); + } + if (maxOffset + 1 < strings.length) { + pattern.append(strings[maxOffset + 1]); + } + fields.put("offsets", offsets); //$NON-NLS-1$ + fields.put("pattern", pattern.toString()); //$NON-NLS-1$ + stream.writeFields(); + } + + private void readObject(ObjectInputStream stream) throws IOException, + ClassNotFoundException { + ObjectInputStream.GetField fields = stream.readFields(); + argumentNumbers = (int[]) fields.get("argumentNumbers", null); //$NON-NLS-1$ + formats = (Format[]) fields.get("formats", null); //$NON-NLS-1$ + locale = (Locale) fields.get("locale", null); //$NON-NLS-1$ + maxOffset = fields.get("maxOffset", 0); //$NON-NLS-1$ + int[] offsets = (int[]) fields.get("offsets", null); //$NON-NLS-1$ + String pattern = (String) fields.get("pattern", null); //$NON-NLS-1$ + int length; + if (maxOffset < 0) { + length = pattern.length() > 0 ? 1 : 0; + } else { + length = maxOffset + + (offsets[maxOffset] == pattern.length() ? 1 : 2); + } + strings = new String[length]; + int last = 0; + for (int i = 0; i <= maxOffset; i++) { + strings[i] = pattern.substring(last, offsets[i]); + last = offsets[i]; + } + if (maxOffset + 1 < strings.length) { + strings[strings.length - 1] = pattern.substring(last, pattern + .length()); + } + } + + /** + * The instances of this inner class are used as attribute keys in + * AttributedCharacterIterator that + * MessageFormat.formatToCharacterIterator() method returns. + * <p> + * There is no public constructor to this class, the only instances are the + * constants defined here. + */ + public static class Field extends Format.Field { + + private static final long serialVersionUID = 7899943957617360810L; + + /** + * This constant stands for the message argument. + */ + public static final Field ARGUMENT = new Field("message argument field"); //$NON-NLS-1$ + + /** + * Constructs a new instance of MessageFormat.Field with the given field + * name. + * @param fieldName The field name. + */ + protected Field(String fieldName) { + super(fieldName); + } + + /** + * serialization method resolve instances to the constant + * MessageFormat.Field values + */ + @Override + protected Object readResolve() throws InvalidObjectException { + String name = this.getName(); + if (name == null) { + // text.18=Not a valid {0}, subclass should override + // readResolve() + throw new InvalidObjectException(Messages.getString( + "text.18", "MessageFormat.Field")); //$NON-NLS-1$ //$NON-NLS-2$ + } + + if (name.equals(ARGUMENT.getName())) { + return ARGUMENT; + } + // text.18=Not a valid {0}, subclass should override readResolve() + throw new InvalidObjectException(Messages.getString( + "text.18", "MessageFormat.Field")); //$NON-NLS-1$ //$NON-NLS-2$ + } + } + +} diff --git a/text/src/main/java/java/text/NumberFormat.java b/text/src/main/java/java/text/NumberFormat.java new file mode 100644 index 0000000..5289b69 --- /dev/null +++ b/text/src/main/java/java/text/NumberFormat.java @@ -0,0 +1,774 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package java.text; + +import java.io.IOException; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.ObjectStreamField; +import java.util.Currency; +import java.util.Locale; +import java.util.ResourceBundle; + +import org.apache.harmony.text.internal.nls.Messages; + +/** + * NumberFormat is the abstract superclass of Formats which format and parse + * Numbers. + */ +public abstract class NumberFormat extends Format { + + private static final long serialVersionUID = -2308460125733713944L; + + /** + * Field constant. + */ + public static final int INTEGER_FIELD = 0; + + /** + * Field constant. + */ + public static final int FRACTION_FIELD = 1; + + private boolean groupingUsed = true, parseIntegerOnly = false; + + private int maximumIntegerDigits = 40, minimumIntegerDigits = 1, + maximumFractionDigits = 3, minimumFractionDigits = 0; + + /** + * Constructs a new instance of DateFormat. + */ + public NumberFormat() { + } + + /** + * Returns a new NumberFormat with the same properties as this NumberFormat. + * + * @return a shallow copy of this NumberFormat + * + * @see java.lang.Cloneable + */ + @Override + public Object clone() { + return super.clone(); + } + + /** + * Compares the specified object to this NumberFormat and answer if they are + * equal. The object must be an instance of NumberFormat and have the same + * properties. + * + * @param object + * the object to compare with this object + * @return true if the specified object is equal to this NumberFormat, false + * otherwise + * + * @see #hashCode + */ + @Override + public boolean equals(Object object) { + if (object == this) { + return true; + } + if (!(object instanceof NumberFormat)) { + return false; + } + NumberFormat obj = (NumberFormat) object; + return groupingUsed == obj.groupingUsed + && parseIntegerOnly == obj.parseIntegerOnly + && maximumFractionDigits == obj.maximumFractionDigits + && maximumIntegerDigits == obj.maximumIntegerDigits + && minimumFractionDigits == obj.minimumFractionDigits + && minimumIntegerDigits == obj.minimumIntegerDigits; + } + + /** + * Formats the specified double using the rules of this NumberFormat. + * + * @param value + * the double to format + * @return the formatted String + */ + public final String format(double value) { + return format(value, new StringBuffer(), new FieldPosition(0)) + .toString(); + } + + /** + * Formats the double value into the specified StringBuffer using the rules + * of this NumberFormat. If the field specified by the FieldPosition is + * formatted, set the begin and end index of the formatted field in the + * FieldPosition. + * + * @param value + * the double to format + * @param buffer + * the StringBuffer + * @param field + * the FieldPosition + * @return the StringBuffer parameter <code>buffer</code> + */ + public abstract StringBuffer format(double value, StringBuffer buffer, + FieldPosition field); + + /** + * Formats the specified long using the rules of this NumberFormat. + * + * @param value + * the long to format + * @return the formatted String + */ + public final String format(long value) { + return format(value, new StringBuffer(), new FieldPosition(0)) + .toString(); + } + + /** + * Formats the long value into the specified StringBuffer using the rules of + * this NumberFormat. If the field specified by the FieldPosition is + * formatted, set the begin and end index of the formatted field in the + * FieldPosition. + * + * @param value + * the long to format + * @param buffer + * the StringBuffer + * @param field + * the FieldPosition + * @return the StringBuffer parameter <code>buffer</code> + */ + public abstract StringBuffer format(long value, StringBuffer buffer, + FieldPosition field); + + /** + * Formats the specified object into the specified StringBuffer using the + * rules of this DateFormat. If the field specified by the FieldPosition is + * formatted, set the begin and end index of the formatted field in the + * FieldPosition. + * + * @param object + * the object to format, must be a Number + * @param buffer + * the StringBuffer + * @param field + * the FieldPosition + * @return the StringBuffer parameter <code>buffer</code> + * + * @exception IllegalArgumentException + * when the object is not a Number + */ + @Override + public StringBuffer format(Object object, StringBuffer buffer, + FieldPosition field) { + if (object instanceof Number) { + double dv = ((Number) object).doubleValue(); + long lv = ((Number) object).longValue(); + if (dv == lv) { + return format(lv, buffer, field); + } + return format(dv, buffer, field); + } + throw new IllegalArgumentException(); + } + + /** + * Gets the list of installed Locales which support NumberFormat. + * + * @return an array of Locale + */ + public static Locale[] getAvailableLocales() { + return Locale.getAvailableLocales(); + } + + /** + * Returns the currency used by this number format + * <p> + * This implementation throws UnsupportedOperationException, concrete sub + * classes should override if they support currency formatting. + * <p> + * + * @return currency currency that was set in getInstance() or in + * setCurrency(), or null + * @throws java.lang.UnsupportedOperationException + */ + public Currency getCurrency() { + throw new UnsupportedOperationException(); + } + + /** + * Returns a NumberFormat for formatting and parsing currency for the + * default Locale. + * + * @return a NumberFormat + */ + public final static NumberFormat getCurrencyInstance() { + return getCurrencyInstance(Locale.getDefault()); + } + + /** + * Returns a NumberFormat for formatting and parsing currency for the + * specified Locale. + * + * @param locale + * the Locale + * @return a NumberFormat + */ + public static NumberFormat getCurrencyInstance(Locale locale) { + return getInstance(locale, "Currency"); //$NON-NLS-1$ + } + + /** + * Returns a NumberFormat for formatting and parsing integers for the + * default Locale. + * + * @return a NumberFormat + */ + public final static NumberFormat getIntegerInstance() { + return getIntegerInstance(Locale.getDefault()); + } + + /** + * Returns a NumberFormat for formatting and parsing integers for the + * specified Locale. + * + * @param locale + * the Locale + * @return a NumberFormat + */ + public static NumberFormat getIntegerInstance(Locale locale) { + NumberFormat format = getInstance(locale, "Integer"); //$NON-NLS-1$ + format.setParseIntegerOnly(true); + return format; + } + + /** + * Returns a NumberFormat for formatting and parsing numbers for the default + * Locale. + * + * @return a NumberFormat + */ + public final static NumberFormat getInstance() { + return getNumberInstance(); + } + + /** + * Returns a NumberFormat for formatting and parsing numbers for the + * specified Locale. + * + * @param locale + * the Locale + * @return a NumberFormat + */ + public static NumberFormat getInstance(Locale locale) { + return getNumberInstance(locale); + } + + static NumberFormat getInstance(Locale locale, String type) { + return new DecimalFormat(getPattern(locale, type), + new DecimalFormatSymbols(locale)); + } + + /** + * Returns the maximum number of fraction digits that are printed when + * formatting. If the maximum is less than the number of fraction digits, + * the least significant digits are truncated. + * + * @return the maximum number of fraction digits + */ + public int getMaximumFractionDigits() { + return maximumFractionDigits; + } + + /** + * Returns the maximum number of integer digits that are printed when + * formatting. If the maximum is less than the number of integer digits, the + * most significant digits are truncated. + * + * @return the maximum number of integer digits + */ + public int getMaximumIntegerDigits() { + return maximumIntegerDigits; + } + + /** + * Returns the minimum number of fraction digits that are printed when + * formatting. + * + * @return the minimum number of fraction digits + */ + public int getMinimumFractionDigits() { + return minimumFractionDigits; + } + + /** + * Returns the minimum number of integer digits that are printed when + * formatting. + * + * @return the minimum number of integer digits + */ + public int getMinimumIntegerDigits() { + return minimumIntegerDigits; + } + + /** + * Returns a NumberFormat for formatting and parsing numbers for the default + * Locale. + * + * @return a NumberFormat + */ + public final static NumberFormat getNumberInstance() { + return getNumberInstance(Locale.getDefault()); + } + + /** + * Returns a NumberFormat for formatting and parsing numbers for the + * specified Locale. + * + * @param locale + * the Locale + * @return a NumberFormat + */ + public static NumberFormat getNumberInstance(Locale locale) { + return getInstance(locale, "Number"); //$NON-NLS-1$ + } + + static String getPattern(Locale locale, String type) { + ResourceBundle bundle = getBundle(locale); + return bundle.getString(type); + } + + /** + * Returns a NumberFormat for formatting and parsing percentages for the + * default Locale. + * + * @return a NumberFormat + */ + public final static NumberFormat getPercentInstance() { + return getPercentInstance(Locale.getDefault()); + } + + /** + * Returns a NumberFormat for formatting and parsing percentages for the + * specified Locale. + * + * @param locale + * the Locale + * @return a NumberFormat + */ + public static NumberFormat getPercentInstance(Locale locale) { + return getInstance(locale, "Percent"); //$NON-NLS-1$ + } + + /** + * Returns an integer hash code for the receiver. Objects which are equal + * answer the same value for this method. + * + * @return the receiver's hash + * + * @see #equals + */ + @Override + public int hashCode() { + return (groupingUsed ? 1231 : 1237) + (parseIntegerOnly ? 1231 : 1237) + + maximumFractionDigits + maximumIntegerDigits + + minimumFractionDigits + minimumIntegerDigits; + } + + /** + * Returns whether this NumberFormat formats and parses numbers using a + * grouping separator. + * + * @return true when a grouping separator is used, false otherwise + */ + public boolean isGroupingUsed() { + return groupingUsed; + } + + /** + * Returns whether this NumberFormat only parses integer numbers. Parsing + * stops if a decimal separator is encountered. + * + * @return true if this NumberFormat only parses integers, false for parsing + * integers or fractions + */ + public boolean isParseIntegerOnly() { + return parseIntegerOnly; + } + + /** + * Parse a Number from the specified String using the rules of this + * NumberFormat. + * + * @param string + * the String to parse + * @return the Number resulting from the parse + * + * @exception ParseException + * when an error occurs during parsing + */ + public Number parse(String string) throws ParseException { + ParsePosition pos = new ParsePosition(0); + Number number = parse(string, pos); + if (pos.getErrorIndex() != -1 || pos.getIndex() == 0) { + throw new ParseException(null, pos.getErrorIndex()); + } + return number; + } + + /** + * Parse a Number from the specified String starting at the index specified + * by the ParsePosition. If the string is successfully parsed, the index of + * the ParsePosition is updated to the index following the parsed text. + * + * @param string + * the String to parse + * @param position + * the ParsePosition, updated on return with the index following + * the parsed text, or on error the index is unchanged and the + * error index is set to the index where the error occurred + * @return the Number resulting from the parse, or null if there is an error + */ + public abstract Number parse(String string, ParsePosition position); + + /** + * Parse a Number from the specified String starting at the index specified + * by the ParsePosition. If the string is successfully parsed, the index of + * the ParsePosition is updated to the index following the parsed text. + * + * @param string + * the String to parse + * @param position + * the ParsePosition, updated on return with the index following + * the parsed text, or on error the index is unchanged and the + * error index is set to the index where the error occurred + * @return the Number resulting from the parse, or null if there is an error + */ + @Override + public final Object parseObject(String string, ParsePosition position) { + if (position == null) { + // text.1A=position is null + throw new NullPointerException(Messages.getString("text.1A")); //$NON-NLS-1$ + } + + try { + return parse(string, position); + } catch (Exception e) { + return null; + } + } + + /** + * Sets the currency used by this number format when formatting currency + * values. + * <p> + * The min and max fraction digits remain the same. + * <p> + * This implementation throws UnsupportedOperationException, concrete sub + * classes should override if they support currency formatting. + * <p> + * + * @param currency + * the new Currency + * @throws java.lang.UnsupportedOperationException + */ + public void setCurrency(Currency currency) { + throw new UnsupportedOperationException(); + } + + /** + * Sets whether this NumberFormat formats and parses numbers using a + * grouping separator. + * + * @param value + * true when a grouping separator is used, false otherwise + */ + public void setGroupingUsed(boolean value) { + groupingUsed = value; + } + + /** + * Sets the maximum number of fraction digits that are printed when + * formatting. If the maximum is less than the number of fraction digits, + * the least significant digits are truncated. + * + * @param value + * the maximum number of fraction digits + */ + public void setMaximumFractionDigits(int value) { + maximumFractionDigits = value < 0 ? 0 : value; + if (maximumFractionDigits < minimumFractionDigits) { + minimumFractionDigits = maximumFractionDigits; + } + } + + /** + * Used to specify the new maximum count of integer digits that are printed + * when formatting. If the maximum is less than the number of integer + * digits, the most significant digits are truncated. + * + * @param value + * the new maximum number of integer numerals for display + */ + public void setMaximumIntegerDigits(int value) { + maximumIntegerDigits = value < 0 ? 0 : value; + if (maximumIntegerDigits < minimumIntegerDigits) { + minimumIntegerDigits = maximumIntegerDigits; + } + } + + /** + * Sets the minimum number of fraction digits that are printed when + * formatting. + * + * @param value + * the minimum number of fraction digits + */ + public void setMinimumFractionDigits(int value) { + minimumFractionDigits = value < 0 ? 0 : value; + if (maximumFractionDigits < minimumFractionDigits) { + maximumFractionDigits = minimumFractionDigits; + } + } + + /** + * Sets the minimum number of integer digits that are printed when + * formatting. + * + * @param value + * the minimum number of integer digits + */ + public void setMinimumIntegerDigits(int value) { + minimumIntegerDigits = value < 0 ? 0 : value; + if (maximumIntegerDigits < minimumIntegerDigits) { + maximumIntegerDigits = minimumIntegerDigits; + } + } + + /** + * Specifies if this NumberFormat should only parse numbers as integers or + * else as any kind of number. If this is called with a <code>true</code> + * value then subsequent parsing attempts will stop if a decimal separator + * is encountered. + * + * @param value + * <code>true</code> to only parse integers, <code>false</code> + * to parse integers and fractions + */ + public void setParseIntegerOnly(boolean value) { + parseIntegerOnly = value; + } + + private static final ObjectStreamField[] serialPersistentFields = { + new ObjectStreamField("groupingUsed", Boolean.TYPE), //$NON-NLS-1$ + new ObjectStreamField("maxFractionDigits", Byte.TYPE), //$NON-NLS-1$ + new ObjectStreamField("maximumFractionDigits", Integer.TYPE), //$NON-NLS-1$ + new ObjectStreamField("maximumIntegerDigits", Integer.TYPE), //$NON-NLS-1$ + new ObjectStreamField("maxIntegerDigits", Byte.TYPE), //$NON-NLS-1$ + new ObjectStreamField("minFractionDigits", Byte.TYPE), //$NON-NLS-1$ + new ObjectStreamField("minimumFractionDigits", Integer.TYPE), //$NON-NLS-1$ + new ObjectStreamField("minimumIntegerDigits", Integer.TYPE), //$NON-NLS-1$ + new ObjectStreamField("minIntegerDigits", Byte.TYPE), //$NON-NLS-1$ + new ObjectStreamField("parseIntegerOnly", Boolean.TYPE), //$NON-NLS-1$ + new ObjectStreamField("serialVersionOnStream", Integer.TYPE), }; //$NON-NLS-1$ + + private void writeObject(ObjectOutputStream stream) throws IOException { + ObjectOutputStream.PutField fields = stream.putFields(); + fields.put("groupingUsed", groupingUsed); //$NON-NLS-1$ + fields + .put( + "maxFractionDigits", //$NON-NLS-1$ + maximumFractionDigits < Byte.MAX_VALUE ? (byte) maximumFractionDigits + : Byte.MAX_VALUE); + fields.put("maximumFractionDigits", maximumFractionDigits); //$NON-NLS-1$ + fields.put("maximumIntegerDigits", maximumIntegerDigits); //$NON-NLS-1$ + fields + .put( + "maxIntegerDigits", //$NON-NLS-1$ + maximumIntegerDigits < Byte.MAX_VALUE ? (byte) maximumIntegerDigits + : Byte.MAX_VALUE); + fields + .put( + "minFractionDigits", //$NON-NLS-1$ + minimumFractionDigits < Byte.MAX_VALUE ? (byte) minimumFractionDigits + : Byte.MAX_VALUE); + fields.put("minimumFractionDigits", minimumFractionDigits); //$NON-NLS-1$ + fields.put("minimumIntegerDigits", minimumIntegerDigits); //$NON-NLS-1$ + fields + .put( + "minIntegerDigits", //$NON-NLS-1$ + minimumIntegerDigits < Byte.MAX_VALUE ? (byte) minimumIntegerDigits + : Byte.MAX_VALUE); + fields.put("parseIntegerOnly", parseIntegerOnly); //$NON-NLS-1$ + fields.put("serialVersionOnStream", 1); //$NON-NLS-1$ + stream.writeFields(); + } + + private void readObject(ObjectInputStream stream) throws IOException, + ClassNotFoundException { + ObjectInputStream.GetField fields = stream.readFields(); + groupingUsed = fields.get("groupingUsed", true); //$NON-NLS-1$ + parseIntegerOnly = fields.get("parseIntegerOnly", false); //$NON-NLS-1$ + if (fields.get("serialVersionOnStream", 0) == 0) { //$NON-NLS-1$ + maximumFractionDigits = fields.get("maxFractionDigits", (byte) 3); //$NON-NLS-1$ + maximumIntegerDigits = fields.get("maxIntegerDigits", (byte) 40); //$NON-NLS-1$ + minimumFractionDigits = fields.get("minFractionDigits", (byte) 0); //$NON-NLS-1$ + minimumIntegerDigits = fields.get("minIntegerDigits", (byte) 1); //$NON-NLS-1$ + } else { + maximumFractionDigits = fields.get("maximumFractionDigits", 3); //$NON-NLS-1$ + maximumIntegerDigits = fields.get("maximumIntegerDigits", 40); //$NON-NLS-1$ + minimumFractionDigits = fields.get("minimumFractionDigits", 0); //$NON-NLS-1$ + minimumIntegerDigits = fields.get("minimumIntegerDigits", 1); //$NON-NLS-1$ + } + if (minimumIntegerDigits > maximumIntegerDigits + || minimumFractionDigits > maximumFractionDigits) { + // text.00=min digits greater than max digits + throw new InvalidObjectException(Messages.getString("text.00")); //$NON-NLS-1$ + } + if (minimumIntegerDigits < 0 || maximumIntegerDigits < 0 + || minimumFractionDigits < 0 || maximumFractionDigits < 0) { + // text.01=min or max digits negative + throw new InvalidObjectException(Messages.getString("text.01")); //$NON-NLS-1$ + } + } + + /** + * The instances of this inner class are used as attribute keys and values + * in AttributedCharacterIterator that + * NumberFormat.formatToCharacterIterator() method returns. + * <p> + * There is no public constructor to this class, the only instances are the + * constants defined here. + * <p> + */ + public static class Field extends Format.Field { + + private static final long serialVersionUID = 7494728892700160890L; + + /** + * This constant stands for the number sign. + */ + public static final Field SIGN = new Field("sign"); //$NON-NLS-1$ + + /** + * This constant stands for the integer part of the number. + */ + public static final Field INTEGER = new Field("integer"); //$NON-NLS-1$ + + /** + * This constant stands for the fraction part of the number. + */ + public static final Field FRACTION = new Field("fraction"); //$NON-NLS-1$ + + /** + * This constant stands for the exponent part of the number. + */ + public static final Field EXPONENT = new Field("exponent"); //$NON-NLS-1$ + + /** + * This constant stands for the exponent sign symbol. + */ + public static final Field EXPONENT_SIGN = new Field("exponent sign"); //$NON-NLS-1$ + + /** + * This constant stands for the exponent symbol. + */ + public static final Field EXPONENT_SYMBOL = new Field("exponent symbol"); //$NON-NLS-1$ + + /** + * This constant stands for the decimal separator. + */ + public static final Field DECIMAL_SEPARATOR = new Field( + "decimal separator"); //$NON-NLS-1$ + + /** + * This constant stands for the grouping separator. + */ + public static final Field GROUPING_SEPARATOR = new Field( + "grouping separator"); //$NON-NLS-1$ + + /** + * This constant stands for the percent symbol. + */ + public static final Field PERCENT = new Field("percent"); //$NON-NLS-1$ + + /** + * This constant stands for the permille symbol. + */ + public static final Field PERMILLE = new Field("per mille"); //$NON-NLS-1$ + + /** + * This constant stands for the currency symbol. + */ + public static final Field CURRENCY = new Field("currency"); //$NON-NLS-1$ + + /** + * Constructs a new instance of NumberFormat.Field with the given field + * name. + * + * @param fieldName The field name. + */ + protected Field(String fieldName) { + super(fieldName); + } + + /** + * serialization method resolve instances to the constant + * NumberFormat.Field values + */ + @Override + protected Object readResolve() throws InvalidObjectException { + if (this.equals(INTEGER)) { + return INTEGER; + } + if (this.equals(FRACTION)) { + return FRACTION; + } + if (this.equals(EXPONENT)) { + return EXPONENT; + } + if (this.equals(EXPONENT_SIGN)) { + return EXPONENT_SIGN; + } + if (this.equals(EXPONENT_SYMBOL)) { + return EXPONENT_SYMBOL; + } + if (this.equals(CURRENCY)) { + return CURRENCY; + } + if (this.equals(DECIMAL_SEPARATOR)) { + return DECIMAL_SEPARATOR; + } + if (this.equals(GROUPING_SEPARATOR)) { + return GROUPING_SEPARATOR; + } + if (this.equals(PERCENT)) { + return PERCENT; + } + if (this.equals(PERMILLE)) { + return PERMILLE; + } + if (this.equals(SIGN)) { + return SIGN; + } + // text.02=Unknown attribute + throw new InvalidObjectException(Messages.getString("text.02")); //$NON-NLS-1$ + } + } + +} diff --git a/text/src/main/java/java/text/ParseException.java b/text/src/main/java/java/text/ParseException.java new file mode 100644 index 0000000..b8b4226 --- /dev/null +++ b/text/src/main/java/java/text/ParseException.java @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package java.text; + +/** + * A ParseException is thrown when the String being parsed is not in the correct + * form. + */ +public class ParseException extends Exception { + + private static final long serialVersionUID = 2703218443322787634L; + + private int errorOffset; + + /** + * Constructs a new instance of this class with its walkback, message and + * the location of the error filled in. + * + * @param detailMessage + * String The detail message for the exception. + * @param location + * int The index at which the parse exception occurred. + */ + public ParseException(String detailMessage, int location) { + super(detailMessage); + errorOffset = location; + } + + /** + * Returns the index at which the parse exception occurred. + * + * @return int The index of the parse exception. + */ + public int getErrorOffset() { + return errorOffset; + } +} diff --git a/text/src/main/java/java/text/ParsePosition.java b/text/src/main/java/java/text/ParsePosition.java new file mode 100644 index 0000000..e59b841 --- /dev/null +++ b/text/src/main/java/java/text/ParsePosition.java @@ -0,0 +1,120 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package java.text; + +/** + * ParsePosition is used to track the current position in a String being parsed. + */ +public class ParsePosition { + + private int currentPosition, errorIndex = -1; + + /** + * Constructs a new ParsePosition at the specified index. + * + * @param index + * the index to begin parsing + */ + public ParsePosition(int index) { + currentPosition = index; + } + + /** + * Compares the specified object to this ParsePosition and answer if they + * are equal. The object must be an instance of ParsePosition and have the + * same index and error index. + * + * @param object + * the object to compare with this object + * @return true if the specified object is equal to this ParsePosition, + * false otherwise + * + * @see #hashCode + */ + @Override + public boolean equals(Object object) { + if (!(object instanceof ParsePosition)) { + return false; + } + ParsePosition pos = (ParsePosition) object; + return currentPosition == pos.currentPosition + && errorIndex == pos.errorIndex; + } + + /** + * Returns the index at which the parse could not continue. + * + * @return the index of the parse error, or -1 if there is no error + */ + public int getErrorIndex() { + return errorIndex; + } + + /** + * Returns the current parse position. + * + * @return the current position + */ + public int getIndex() { + return currentPosition; + } + + /** + * Returns an integer hash code for the receiver. Objects which are equal + * answer the same value for this method. + * + * @return the receiver's hash + * + * @see #equals + */ + @Override + public int hashCode() { + return currentPosition + errorIndex; + } + + /** + * Sets the index at which the parse could not continue. + * + * @param index + * the index of the parse error + */ + public void setErrorIndex(int index) { + errorIndex = index; + } + + /** + * Sets the current parse position. + * + * @param index + * the current parse position + */ + public void setIndex(int index) { + currentPosition = index; + } + + /** + * Returns the string representation of this FieldPosition. + * + * @return the string representation of this FieldPosition + */ + @Override + public String toString() { + return getClass().getName() + "[index=" + currentPosition //$NON-NLS-1$ + + ", errorIndex=" + errorIndex + "]"; //$NON-NLS-1$ //$NON-NLS-2$ + } +} diff --git a/text/src/main/java/java/text/RuleBasedBreakIterator.java b/text/src/main/java/java/text/RuleBasedBreakIterator.java new file mode 100644 index 0000000..a08fd9f --- /dev/null +++ b/text/src/main/java/java/text/RuleBasedBreakIterator.java @@ -0,0 +1,205 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package java.text; + +/* + * Default implementation of BreakIterator, wrap + * com.ibm.icu.text.RuleBasedBreakIterator + * + */ +class RuleBasedBreakIterator extends BreakIterator { + + /* + * Wrapping construction + */ + RuleBasedBreakIterator(com.ibm.icu4jni.text.BreakIterator iterator) { + super(iterator); + } + + /* + * (non-Javadoc) + * + * @see java.text.BreakIterator#current() + */ + @Override + public int current() { + return wrapped.current(); + } + + /* + * (non-Javadoc) + * + * @see java.text.BreakIterator#first() + */ + @Override + public int first() { + return wrapped.first(); + } + + /* + * (non-Javadoc) + * + * @see java.text.BreakIterator#following(int) + */ + @Override + public int following(int offset) { + validateOffset(offset); + return wrapped.following(offset); + } + + /* + * check the offset, throw exception if it is invalid + */ + private void validateOffset(int offset) { + CharacterIterator it = wrapped.getText(); + if (offset < it.getBeginIndex() || offset >= it.getEndIndex()) { + throw new IllegalArgumentException(); + } + } + + /* + * (non-Javadoc) + * + * @see java.text.BreakIterator#getText() + */ + @Override + public CharacterIterator getText() { + return wrapped.getText(); + } + + /* + * (non-Javadoc) + * + * @see java.text.BreakIterator#last() + */ + @Override + public int last() { + return wrapped.last(); + } + + /* + * (non-Javadoc) + * + * @see java.text.BreakIterator#next() + */ + @Override + public int next() { + return wrapped.next(); + } + + /* + * (non-Javadoc) + * + * @see java.text.BreakIterator#next(int) + */ + @Override + public int next(int n) { + return wrapped.next(n); + } + + /* + * (non-Javadoc) + * + * @see java.text.BreakIterator#previous() + */ + @Override + public int previous() { + return wrapped.previous(); + } + + /* + * (non-Javadoc) + * + * @see java.text.BreakIterator#setText(java.text.CharacterIterator) + */ + @Override + public void setText(CharacterIterator newText) { + // call a method to check if null pointer + newText.current(); + wrapped.setText(newText); + } + + /* + * (non-Javadoc) + * + * @see java.text.BreakIterator#isBoundary(int) + */ + @Override + public boolean isBoundary(int offset) { + validateOffset(offset); + return wrapped.isBoundary(offset); + } + + /* + * (non-Javadoc) + * + * @see java.text.BreakIterator#preceding(int) + */ + @Override + public int preceding(int offset) { + validateOffset(offset); + return wrapped.preceding(offset); + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object o) { + if (!(o instanceof RuleBasedBreakIterator)) { + return false; + } + return wrapped.equals(((RuleBasedBreakIterator) o).wrapped); + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return wrapped.toString(); + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + return wrapped.hashCode(); + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#clone() + */ + @Override + public Object clone() { + RuleBasedBreakIterator cloned = (RuleBasedBreakIterator) super.clone(); + cloned.wrapped = (com.ibm.icu4jni.text.RuleBasedBreakIterator) wrapped + .clone(); + return cloned; + } + +} diff --git a/text/src/main/java/java/text/RuleBasedCollator.java b/text/src/main/java/java/text/RuleBasedCollator.java new file mode 100644 index 0000000..c3cd531 --- /dev/null +++ b/text/src/main/java/java/text/RuleBasedCollator.java @@ -0,0 +1,214 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package java.text; + +import org.apache.harmony.text.internal.nls.Messages; + +/** + * <code>RuleBasedCollator</code> is a concrete subclass of + * <code>Collator</code>. It allows customization of the + * <code>Collator</code> via user-specified rule sets. + * <code>RuleBasedCollator</code> is designed to be fully compliant to the <a + * href="http://www.unicode.org/unicode/reports/tr10/"> Unicode Collation + * Algorithm (UCA) </a> and conforms to ISO 14651. + * </p> + * <p> + * Create a <code>RuleBasedCollator</code> from a locale by calling the + * <code>getInstance(Locale)</code> factory method in the base class + * <code>Collator</code>.<code>Collator.getInstance(Locale)</code> creates + * a <code>RuleBasedCollator</code> object based on the collation rules + * defined by the argument locale. If a customized collation is required, use + * the <code>RuleBasedCollator(String)</code> constructor with the appropriate + * rules. The customized <code>RuleBasedCollator</code> will base its ordering + * on UCA, while re-adjusting the attributes and orders of the characters in the + * specified rule accordingly. + * </p> + * + */ +public class RuleBasedCollator extends Collator { + + RuleBasedCollator(com.ibm.icu4jni.text.Collator wrapper) { + super(wrapper); + } + + /** + * Constructs a new instance of <code>RuleBasedCollator</code> using the + * specified <code>rules</code>. + * + * @param rules + * the collation rules. + * @throws ParseException + * when the rules contains an invalid collation rule syntax. + */ + public RuleBasedCollator(String rules) throws ParseException { + if (rules == null) { + throw new NullPointerException(); + } + if (rules.length() == 0) { + // text.06=Build rules empty + throw new ParseException(Messages.getString("text.06"), 0); //$NON-NLS-1$ + } + + try { + this.icuColl = new com.ibm.icu4jni.text.RuleBasedCollator(rules); + } catch (Exception e) { + if (e instanceof ParseException) { + throw (ParseException) e; + } + /* + * -1 means it's not a ParseException. Maybe IOException thrown when + * an error occured while reading internal data. + */ + throw new ParseException(e.getMessage(), -1); + } + } + + /** + * Obtains a <code>CollationElementIterator</code> for the given + * <code>CharacterIterator</code>. The source iterator's integrity will + * be preserved since a new copy will be created for use. + * + * @param source + * the specified source + * @return a <code>CollationElementIterator</code> for the source. + */ + public CollationElementIterator getCollationElementIterator( + CharacterIterator source) { + if (source == null) { + throw new NullPointerException(); + } + return new CollationElementIterator( + ((com.ibm.icu4jni.text.RuleBasedCollator) this.icuColl) + .getCollationElementIterator(source)); + } + + /** + * Obtains a <code>CollationElementIterator</code> for the given String. + * + * @param source + * the specified source + * @return a <code>CollationElementIterator</code> for the given String + */ + public CollationElementIterator getCollationElementIterator(String source) { + if (source == null) { + throw new NullPointerException(); + } + return new CollationElementIterator( + ((com.ibm.icu4jni.text.RuleBasedCollator) this.icuColl) + .getCollationElementIterator(source)); + } + + /** + * Obtains the collation rules of the <code>RuleBasedCollator</code>. + * + * @return the collation rules. + */ + public String getRules() { + return ((com.ibm.icu4jni.text.RuleBasedCollator) this.icuColl).getRules(); + } + + /** + * Obtains the cloned object of the <code>RuleBasedCollator</code> + * + * @return the cloned object of the <code>RuleBasedCollator</code> + */ + @Override + public Object clone() { + RuleBasedCollator clone = (RuleBasedCollator) super.clone(); + return clone; + } + + /** + * Compares the <code>source</code> text <code>String</code> to the + * <code>target</code> text <code>String</code> according to the + * collation rules, strength and decomposition mode for this + * <code>RuleBasedCollator</code>. See the <code>Collator</code> class + * description for an example of use. + * <p> + * General recommendation: If comparisons are to be done to the same String + * multiple times, it would be more efficient to generate + * <code>CollationKeys</code> for the <code>String</code> s and use + * <code>CollationKey.compareTo(CollationKey)</code> for the comparisons. + * If the each Strings are compared to only once, using the method + * RuleBasedCollator.compare(String, String) will have a better performance. + * </p> + * + * @param source + * the source text + * @param target + * the target text + * @return an integer which may be a negative value, zero, or else a + * positive value depending on whether <code>source</code> is less + * than, equivalent to, or greater than <code>target</code>. + */ + @Override + public int compare(String source, String target) { + if (source == null || target == null) { + // text.08=one of arguments is null + throw new NullPointerException(Messages.getString("text.08")); //$NON-NLS-1$ + } + return this.icuColl.compare(source, target); + } + + /** + * Obtains the <code>CollationKey</code> for the given source text. + * + * @param source + * the specified source text + * @return the <code>CollationKey</code> for the given source text. + */ + @Override + public CollationKey getCollationKey(String source) { + com.ibm.icu4jni.text.CollationKey icuKey = this.icuColl + .getCollationKey(source); + if (icuKey == null) { + return null; + } + return new CollationKey(source, icuKey); + } + + /** + * Obtains a unique hash code for the <code>RuleBasedCollator</code> + * + * @return the hash code for the <code>RuleBasedCollator</code> + */ + @Override + public int hashCode() { + return ((com.ibm.icu4jni.text.RuleBasedCollator) this.icuColl).getRules() + .hashCode(); + } + + /** + * Compares the equality of two <code>RuleBasedCollator</code> objects. + * <code>RuleBasedCollator</code> objects are equal if they have the same + * collation rules and the same attributes. + * + * @param obj + * the other object. + * @return <code>true</code> if this <code>RuleBasedCollator</code> has + * exactly the same collation behaviour as obj, <code>false</code> + * otherwise. + */ + @Override + public boolean equals(Object obj) { + if (!(obj instanceof Collator)) { + return false; + } + return super.equals(obj); + } +} diff --git a/text/src/main/java/java/text/SimpleDateFormat.java b/text/src/main/java/java/text/SimpleDateFormat.java new file mode 100644 index 0000000..70bdf9b --- /dev/null +++ b/text/src/main/java/java/text/SimpleDateFormat.java @@ -0,0 +1,1087 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package java.text; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.ObjectStreamField; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.Locale; +import java.util.ResourceBundle; +import java.util.SimpleTimeZone; +import java.util.TimeZone; +import java.util.Vector; + +import org.apache.harmony.text.internal.nls.Messages; + +/** + * SimpleDateFormat is used to format and parse Gregorian calendar dates and + * times based on a pattern of date and time fields. Each date and time field is + * specified in the pattern by a specific character. The characters used can be + * either localized or non-localized. For some fields, which have both numeric + * and text representations or abbreviated as well as full names, the number of + * grouped characters specifies how the field is formatted or parsed. + */ +public class SimpleDateFormat extends DateFormat { + + private static final long serialVersionUID = 4774881970558875024L; + + private static final String patternChars = "GyMdkHmsSEDFwWahKzZ"; //$NON-NLS-1$ + + private String pattern; + + private DateFormatSymbols formatData; + + transient private int creationYear; + + private Date defaultCenturyStart; + + /** + * Constructs a new SimpleDateFormat for formatting and parsing dates and + * times in the SHORT style for the default Locale. + */ + public SimpleDateFormat() { + this(Locale.getDefault()); + pattern = defaultPattern(); + formatData = new DateFormatSymbols(Locale.getDefault()); + } + + /** + * Constructs a new SimpleDateFormat using the specified non-localized + * pattern and the DateFormatSymbols and Calendar for the default Locale. + * + * @param pattern + * the pattern + * + * @exception NullPointerException + * if a <code>null</code> value of <code>pattern</code> + * is supplied. + * @exception IllegalArgumentException + * if <code>pattern</code> is not considered to be useable + * by this formatter. + */ + public SimpleDateFormat(String pattern) { + this(pattern, Locale.getDefault()); + } + + /** + * Constructs a new SimpleDateFormat using the specified non-localized + * pattern and DateFormatSymbols and the Calendar for the default Locale. + * + * @param template + * the pattern + * @param value + * the DateFormatSymbols + * + * @exception NullPointerException + * if the pattern is null + * @exception IllegalArgumentException + * if the pattern is invalid + */ + public SimpleDateFormat(String template, DateFormatSymbols value) { + this(Locale.getDefault()); + validatePattern(template); + pattern = template; + formatData = (DateFormatSymbols) value.clone(); + } + + /** + * Constructs a new SimpleDateFormat using the specified non-localized + * pattern and the DateFormatSymbols and Calendar for the specified Locale. + * + * @param template + * the pattern + * @param locale + * the Locale + * + * @exception NullPointerException + * if the pattern is null + * @exception IllegalArgumentException + * if the pattern is invalid + */ + public SimpleDateFormat(String template, Locale locale) { + this(locale); + validatePattern(template); + pattern = template; + formatData = new DateFormatSymbols(locale); + } + + private SimpleDateFormat(Locale locale) { + numberFormat = NumberFormat.getInstance(locale); + numberFormat.setParseIntegerOnly(true); + numberFormat.setGroupingUsed(false); + calendar = new GregorianCalendar(locale); + calendar.add(Calendar.YEAR, -80); + creationYear = calendar.get(Calendar.YEAR); + defaultCenturyStart = calendar.getTime(); + } + + private void append(StringBuffer buffer, FieldPosition position, + Vector<FieldPosition> fields, char format, int count) { + int field = -1; + int index = patternChars.indexOf(format); + if (index == -1) { + // text.03=Unknown pattern character - '{0}' + throw new IllegalArgumentException(Messages.getString( + "text.03", format)); //$NON-NLS-1$ + } + + int beginPosition = buffer.length(); + Field dateFormatField = null; + + switch (index) { + case ERA_FIELD: + dateFormatField = Field.ERA; + buffer.append(formatData.eras[calendar.get(Calendar.ERA)]); + break; + case YEAR_FIELD: + dateFormatField = Field.YEAR; + int year = calendar.get(Calendar.YEAR); + if (count < 4) { + appendNumber(buffer, 2, year %= 100); + } else { + appendNumber(buffer, count, year); + } + break; + case MONTH_FIELD: + dateFormatField = Field.MONTH; + int month = calendar.get(Calendar.MONTH); + if (count <= 2) { + appendNumber(buffer, count, month + 1); + } else if (count == 3) { + buffer.append(formatData.shortMonths[month]); + } else { + buffer.append(formatData.months[month]); + } + break; + case DATE_FIELD: + dateFormatField = Field.DAY_OF_MONTH; + field = Calendar.DATE; + break; + case HOUR_OF_DAY1_FIELD: // k + dateFormatField = Field.HOUR_OF_DAY1; + int hour = calendar.get(Calendar.HOUR_OF_DAY); + appendNumber(buffer, count, hour == 0 ? 24 : hour); + break; + case HOUR_OF_DAY0_FIELD: // H + dateFormatField = Field.HOUR_OF_DAY0; + field = Calendar.HOUR_OF_DAY; + break; + case MINUTE_FIELD: + dateFormatField = Field.MINUTE; + field = Calendar.MINUTE; + break; + case SECOND_FIELD: + dateFormatField = Field.SECOND; + field = Calendar.SECOND; + break; + case MILLISECOND_FIELD: + dateFormatField = Field.MILLISECOND; + int value = calendar.get(Calendar.MILLISECOND); + appendNumber(buffer, count, value); + break; + case DAY_OF_WEEK_FIELD: + dateFormatField = Field.DAY_OF_WEEK; + int day = calendar.get(Calendar.DAY_OF_WEEK); + if (count < 4) { + buffer.append(formatData.shortWeekdays[day]); + } else { + buffer.append(formatData.weekdays[day]); + } + break; + case DAY_OF_YEAR_FIELD: + dateFormatField = Field.DAY_OF_YEAR; + field = Calendar.DAY_OF_YEAR; + break; + case DAY_OF_WEEK_IN_MONTH_FIELD: + dateFormatField = Field.DAY_OF_WEEK_IN_MONTH; + field = Calendar.DAY_OF_WEEK_IN_MONTH; + break; + case WEEK_OF_YEAR_FIELD: + dateFormatField = Field.WEEK_OF_YEAR; + field = Calendar.WEEK_OF_YEAR; + break; + case WEEK_OF_MONTH_FIELD: + dateFormatField = Field.WEEK_OF_MONTH; + field = Calendar.WEEK_OF_MONTH; + break; + case AM_PM_FIELD: + dateFormatField = Field.AM_PM; + buffer.append(formatData.ampms[calendar.get(Calendar.AM_PM)]); + break; + case HOUR1_FIELD: // h + dateFormatField = Field.HOUR1; + hour = calendar.get(Calendar.HOUR); + appendNumber(buffer, count, hour == 0 ? 12 : hour); + break; + case HOUR0_FIELD: // K + dateFormatField = Field.HOUR0; + field = Calendar.HOUR; + break; + case TIMEZONE_FIELD: // z + dateFormatField = Field.TIME_ZONE; + appendTimeZone(buffer, count, true); + break; + case (TIMEZONE_FIELD + 1): // Z + dateFormatField = Field.TIME_ZONE; + appendTimeZone(buffer, count, false); + break; + } + if (field != -1) { + appendNumber(buffer, count, calendar.get(field)); + } + + if (fields != null) { + position = new FieldPosition(dateFormatField); + position.setBeginIndex(beginPosition); + position.setEndIndex(buffer.length()); + fields.add(position); + } else { + // Set to the first occurrence + if ((position.getFieldAttribute() == dateFormatField || (position + .getFieldAttribute() == null && position.getField() == index)) + && position.getEndIndex() == 0) { + position.setBeginIndex(beginPosition); + position.setEndIndex(buffer.length()); + } + } + } + + private void appendTimeZone(StringBuffer buffer, int count, + boolean generalTimezone) { + // cannot call TimeZone.getDisplayName() because it would not use + // the DateFormatSymbols of this SimpleDateFormat + + if (generalTimezone) { + String id = calendar.getTimeZone().getID(); +// BEGIN android-changed + String[][] zones = formatData.internalZoneStrings(); +// END android-changed + String[] zone = null; + for (String[] element : zones) { + if (id.equals(element[0])) { + zone = element; + break; + } + } + if (zone == null) { + int offset = calendar.get(Calendar.ZONE_OFFSET) + + calendar.get(Calendar.DST_OFFSET); + char sign = '+'; + if (offset < 0) { + sign = '-'; + offset = -offset; + } + buffer.append("GMT"); //$NON-NLS-1$ + buffer.append(sign); + appendNumber(buffer, 2, offset / 3600000); + buffer.append(':'); + appendNumber(buffer, 2, (offset % 3600000) / 60000); + } else { + int daylight = calendar.get(Calendar.DST_OFFSET) == 0 ? 0 : 2; + if (count < 4) { + buffer.append(zone[2 + daylight]); + } else { + buffer.append(zone[1 + daylight]); + } + } + } else { + int offset = calendar.get(Calendar.ZONE_OFFSET) + + calendar.get(Calendar.DST_OFFSET); + char sign = '+'; + if (offset < 0) { + sign = '-'; + offset = -offset; + } + buffer.append(sign); + appendNumber(buffer, 2, offset / 3600000); + appendNumber(buffer, 2, (offset % 3600000) / 60000); + } + } + + private void appendNumber(StringBuffer buffer, int count, int value) { + int minimumIntegerDigits = numberFormat.getMinimumIntegerDigits(); + numberFormat.setMinimumIntegerDigits(count); + numberFormat.format(new Integer(value), buffer, new FieldPosition(0)); + numberFormat.setMinimumIntegerDigits(minimumIntegerDigits); + } + + /** + * Changes the pattern of this SimpleDateFormat to the specified pattern + * which uses localized pattern characters. + * + * @param template + * the localized pattern + */ + public void applyLocalizedPattern(String template) { + pattern = convertPattern(template, formatData.getLocalPatternChars(), + patternChars, true); + } + + /** + * Changes the pattern of this SimpleDateFormat to the specified pattern + * which uses non-localized pattern characters. + * + * @param template + * the non-localized pattern + * + * @exception NullPointerException + * if the pattern is null + * @exception IllegalArgumentException + * if the pattern is invalid + */ + public void applyPattern(String template) { + validatePattern(template); + pattern = template; + } + + /** + * Returns a new SimpleDateFormat with the same pattern and properties as + * this SimpleDateFormat. + * + * @return a shallow copy of this SimpleDateFormat + * + * @see java.lang.Cloneable + */ + @Override + public Object clone() { + SimpleDateFormat clone = (SimpleDateFormat) super.clone(); + clone.formatData = (DateFormatSymbols) formatData.clone(); + clone.defaultCenturyStart = new Date(defaultCenturyStart.getTime()); + return clone; + } + + private static String defaultPattern() { + ResourceBundle bundle = getBundle(Locale.getDefault()); + String styleName = getStyleName(SHORT); + return bundle.getString("Date_" + styleName) + " " //$NON-NLS-1$ //$NON-NLS-2$ + + bundle.getString("Time_" + styleName); //$NON-NLS-1$ + } + + /** + * Compares the specified object to this SimpleDateFormat and answer if they + * are equal. The object must be an instance of SimpleDateFormat and have + * the same DateFormat properties, pattern, DateFormatSymbols, and creation + * year. + * + * @param object + * the object to compare with this object + * @return true if the specified object is equal to this SimpleDateFormat, + * false otherwise + * + * @see #hashCode + */ + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (!(object instanceof SimpleDateFormat)) { + return false; + } + SimpleDateFormat simple = (SimpleDateFormat) object; + return super.equals(object) && pattern.equals(simple.pattern) + && formatData.equals(simple.formatData); + } + + private Date error(ParsePosition position, int offset, TimeZone zone) { + position.setErrorIndex(offset); + calendar.setTimeZone(zone); + return null; + } + + /** + * Formats the specified object using the rules of this SimpleDateFormat and + * returns an AttributedCharacterIterator with the formatted Date and + * attributes. + * + * @param object + * the object to format + * @return an AttributedCharacterIterator with the formatted date and + * attributes + * + * @exception NullPointerException + * when the object is null + * @exception IllegalArgumentException + * when the object cannot be formatted by this Format + */ + @Override + public AttributedCharacterIterator formatToCharacterIterator(Object object) { + if (object == null) { + throw new NullPointerException(); + } + if (object instanceof Date) { + return formatToCharacterIteratorImpl((Date) object); + } + if (object instanceof Number) { + return formatToCharacterIteratorImpl(new Date(((Number) object) + .longValue())); + } + throw new IllegalArgumentException(); + } + + private AttributedCharacterIterator formatToCharacterIteratorImpl(Date date) { + StringBuffer buffer = new StringBuffer(); + Vector<FieldPosition> fields = new Vector<FieldPosition>(); + + // format the date, and find fields + formatImpl(date, buffer, null, fields); + + // create and AttributedString with the formatted buffer + AttributedString as = new AttributedString(buffer.toString()); + + // add DateFormat field attributes to the AttributedString + for (int i = 0; i < fields.size(); i++) { + FieldPosition pos = fields.elementAt(i); + Format.Field attribute = pos.getFieldAttribute(); + as.addAttribute(attribute, attribute, pos.getBeginIndex(), pos + .getEndIndex()); + } + + // return the CharacterIterator from AttributedString + return as.getIterator(); + } + + /** + * Formats the specified Date into the specified StringBuffer using the + * pattern of this SimpleDateFormat. If the field specified by the + * FieldPosition is formatted, set the begin and end index of the formatted + * field in the FieldPosition. + * + * @param date + * the Date to format + * @param buffer + * the StringBuffer + * @param field + * the FieldPosition + * @return the StringBuffer parameter <code>buffer</code> + * + * @exception IllegalArgumentException + * when there are invalid characters in the pattern + */ + @Override + public StringBuffer format(Date date, StringBuffer buffer, + FieldPosition field) { + return formatImpl(date, buffer, field, null); + } + + /** + * Validate the format character. + * + * @param format + * the format character + * + * @throws IllegalArgumentException + * when the format character is invalid + */ + private void validateFormat(char format) { + int index = patternChars.indexOf(format); + if (index == -1) { + // text.03=Unknown pattern character - '{0}' + throw new IllegalArgumentException(Messages.getString( + "text.03", format)); //$NON-NLS-1$ + } + } + + /** + * Validate the pattern. + * + * @param template + * the pattern to validate. + * + * @throws NullPointerException + * if the pattern is null + * @throws IllegalArgumentException + * if the pattern is invalid + */ + private void validatePattern(String template) { + boolean quote = false; + int next, last = -1, count = 0; + + final int patternLength = template.length(); + for (int i = 0; i < patternLength; i++) { + next = (template.charAt(i)); + if (next == '\'') { + if (count > 0) { + validateFormat((char) last); + count = 0; + } + if (last == next) { + last = -1; + } else { + last = next; + } + quote = !quote; + continue; + } + if (!quote + && (last == next || (next >= 'a' && next <= 'z') || (next >= 'A' && next <= 'Z'))) { + if (last == next) { + count++; + } else { + if (count > 0) { + validateFormat((char) last); + } + last = next; + count = 1; + } + } else { + if (count > 0) { + validateFormat((char) last); + count = 0; + } + last = -1; + } + } + if (count > 0) { + validateFormat((char) last); + } + + if (quote) { + // text.04=Unterminated quote {0} + throw new IllegalArgumentException(Messages.getString("text.04")); //$NON-NLS-1$ + } + + } + + /** + * Formats the date. + * <p> + * If the FieldPosition <code>field</code> is not null, and the field + * specified by this FieldPosition is formatted, set the begin and end index + * of the formatted field in the FieldPosition. + * <p> + * If the Vector <code>fields</code> is not null, find fields of this + * date, set FieldPositions with these fields, and add them to the fields + * vector. + * + * @param date + * Date to Format + * @param buffer + * StringBuffer to store the resulting formatted String + * @param field + * FieldPosition to set begin and end index of the field + * specified, if it is part of the format for this date + * @param fields + * Vector used to store the FieldPositions for each field in this + * date + * + * @return the formatted Date + * + * @exception IllegalArgumentException + * when the object cannot be formatted by this Format + */ + private StringBuffer formatImpl(Date date, StringBuffer buffer, + FieldPosition field, Vector<FieldPosition> fields) { + + boolean quote = false; + int next, last = -1, count = 0; + calendar.setTime(date); + if (field != null) { + field.clear(); + } + + final int patternLength = pattern.length(); + for (int i = 0; i < patternLength; i++) { + next = (pattern.charAt(i)); + if (next == '\'') { + if (count > 0) { + append(buffer, field, fields, (char) last, count); + count = 0; + } + if (last == next) { + buffer.append('\''); + last = -1; + } else { + last = next; + } + quote = !quote; + continue; + } + if (!quote + && (last == next || (next >= 'a' && next <= 'z') || (next >= 'A' && next <= 'Z'))) { + if (last == next) { + count++; + } else { + if (count > 0) { + append(buffer, field, fields, (char) last, count); + } + last = next; + count = 1; + } + } else { + if (count > 0) { + append(buffer, field, fields, (char) last, count); + count = 0; + } + last = -1; + buffer.append((char) next); + } + } + if (count > 0) { + append(buffer, field, fields, (char) last, count); + } + return buffer; + } + + /** + * Returns the Date which is the start of the one hundred year period for + * two digits year values. + * + * @return a Date + */ + public Date get2DigitYearStart() { + return defaultCenturyStart; + } + + /** + * Returns the DateFormatSymbols used by this SimpleDateFormat. + * + * @return a DateFormatSymbols + */ + public DateFormatSymbols getDateFormatSymbols() { + // Return a clone so the arrays in the ResourceBundle are not modified + return (DateFormatSymbols) formatData.clone(); + } + + /** + * Returns an integer hash code for the receiver. Objects which are equal + * answer the same value for this method. + * + * @return the receiver's hash + * + * @see #equals + */ + @Override + public int hashCode() { + return super.hashCode() + pattern.hashCode() + formatData.hashCode() + + creationYear; + } + + private int parse(String string, int offset, char format, int count) { + int index = patternChars.indexOf(format); + if (index == -1) { + // text.03=Unknown pattern character - '{0}' + throw new IllegalArgumentException(Messages.getString( + "text.03", format)); //$NON-NLS-1$ + } + int field = -1; + int absolute = 0; + if (count < 0) { + count = -count; + absolute = count; + } + switch (index) { + case ERA_FIELD: + return parseText(string, offset, formatData.eras, Calendar.ERA); + case YEAR_FIELD: + if (count >= 3) { + field = Calendar.YEAR; + } else { + ParsePosition position = new ParsePosition(offset); + Number result = parseNumber(absolute, string, position); + if (result == null) { + return -position.getErrorIndex() - 1; + } + int year = result.intValue(); + // A two digit year must be exactly two digits, i.e. 01 + if ((position.getIndex() - offset) == 2 && year >= 0) { + year += creationYear / 100 * 100; + if (year < creationYear) { + year += 100; + } + } + calendar.set(Calendar.YEAR, year); + return position.getIndex(); + } + break; + case MONTH_FIELD: + if (count <= 2) { + return parseNumber(absolute, string, offset, + Calendar.MONTH, -1); + } + index = parseText(string, offset, formatData.months, + Calendar.MONTH); + if (index < 0) { + return parseText(string, offset, formatData.shortMonths, + Calendar.MONTH); + } + return index; + case DATE_FIELD: + field = Calendar.DATE; + break; + case HOUR_OF_DAY1_FIELD: + ParsePosition position = new ParsePosition(offset); + Number result = parseNumber(absolute, string, position); + if (result == null) { + return -position.getErrorIndex() - 1; + } + int hour = result.intValue(); + if (hour == 24) { + hour = 0; + } + calendar.set(Calendar.HOUR_OF_DAY, hour); + return position.getIndex(); + case HOUR_OF_DAY0_FIELD: + field = Calendar.HOUR_OF_DAY; + break; + case MINUTE_FIELD: + field = Calendar.MINUTE; + break; + case SECOND_FIELD: + field = Calendar.SECOND; + break; + case MILLISECOND_FIELD: + field = Calendar.MILLISECOND; + break; + case DAY_OF_WEEK_FIELD: + index = parseText(string, offset, formatData.weekdays, + Calendar.DAY_OF_WEEK); + if (index < 0) { + return parseText(string, offset, formatData.shortWeekdays, + Calendar.DAY_OF_WEEK); + } + return index; + case DAY_OF_YEAR_FIELD: + field = Calendar.DAY_OF_YEAR; + break; + case DAY_OF_WEEK_IN_MONTH_FIELD: + field = Calendar.DAY_OF_WEEK_IN_MONTH; + break; + case WEEK_OF_YEAR_FIELD: + field = Calendar.WEEK_OF_YEAR; + break; + case WEEK_OF_MONTH_FIELD: + field = Calendar.WEEK_OF_MONTH; + break; + case AM_PM_FIELD: + return parseText(string, offset, formatData.ampms, + Calendar.AM_PM); + case HOUR1_FIELD: + position = new ParsePosition(offset); + result = parseNumber(absolute, string, position); + if (result == null) { + return -position.getErrorIndex() - 1; + } + hour = result.intValue(); + if (hour == 12) { + hour = 0; + } + calendar.set(Calendar.HOUR, hour); + return position.getIndex(); + case HOUR0_FIELD: + field = Calendar.HOUR; + break; + case TIMEZONE_FIELD: + return parseTimeZone(string, offset); + case (TIMEZONE_FIELD + 1): + return parseTimeZone(string, offset); + } + if (field != -1) { + return parseNumber(absolute, string, offset, field, 0); + } + return offset; + } + + /** + * Parse a Date from the specified String starting at the index specified by + * the ParsePosition. If the string is successfully parsed, the index of the + * ParsePosition is updated to the index following the parsed text. + * + * @param string + * the String to parse according to the pattern of this + * SimpleDateFormat + * @param position + * the ParsePosition, updated on return with the index following + * the parsed text, or on error the index is unchanged and the + * error index is set to the index where the error occurred + * @return the Date resulting from the parse, or null if there is an error + * + * @exception IllegalArgumentException + * when there are invalid characters in the pattern + */ + @Override + public Date parse(String string, ParsePosition position) { + boolean quote = false; + int next, last = -1, count = 0, offset = position.getIndex(); + int length = string.length(); + calendar.clear(); + TimeZone zone = calendar.getTimeZone(); + final int patternLength = pattern.length(); + for (int i = 0; i < patternLength; i++) { + next = pattern.charAt(i); + if (next == '\'') { + if (count > 0) { + if ((offset = parse(string, offset, (char) last, count)) < 0) { + return error(position, -offset - 1, zone); + } + count = 0; + } + if (last == next) { + if (offset >= length || string.charAt(offset) != '\'') { + return error(position, offset, zone); + } + offset++; + last = -1; + } else { + last = next; + } + quote = !quote; + continue; + } + if (!quote + && (last == next || (next >= 'a' && next <= 'z') || (next >= 'A' && next <= 'Z'))) { + if (last == next) { + count++; + } else { + if (count > 0) { + if ((offset = parse(string, offset, (char) last, -count)) < 0) { + return error(position, -offset - 1, zone); + } + } + last = next; + count = 1; + } + } else { + if (count > 0) { + if ((offset = parse(string, offset, (char) last, count)) < 0) { + return error(position, -offset - 1, zone); + } + count = 0; + } + last = -1; + if (offset >= length || string.charAt(offset) != next) { + return error(position, offset, zone); + } + offset++; + } + } + if (count > 0) { + if ((offset = parse(string, offset, (char) last, count)) < 0) { + return error(position, -offset - 1, zone); + } + } + Date date; + try { + date = calendar.getTime(); + } catch (IllegalArgumentException e) { + return error(position, offset, zone); + } + position.setIndex(offset); + calendar.setTimeZone(zone); + return date; + } + + private Number parseNumber(int max, String string, ParsePosition position) { + int digit, length = string.length(), result = 0; + int index = position.getIndex(); + if (max > 0 && max < length - index) { + length = index + max; + } + while (index < length + && (string.charAt(index) == ' ' || string.charAt(index) == '\t')) { + index++; + } + if (max == 0) { + position.setIndex(index); + return numberFormat.parse(string, position); + } + + while (index < length + && (digit = Character.digit(string.charAt(index), 10)) != -1) { + index++; + result = result * 10 + digit; + } + if (index == position.getIndex()) { + position.setErrorIndex(index); + return null; + } + position.setIndex(index); + return new Integer(result); + } + + private int parseNumber(int max, String string, int offset, int field, + int skew) { + ParsePosition position = new ParsePosition(offset); + Number result = parseNumber(max, string, position); + if (result == null) { + return -position.getErrorIndex() - 1; + } + calendar.set(field, result.intValue() + skew); + return position.getIndex(); + } + + private int parseText(String string, int offset, String[] text, int field) { + int found = -1; + for (int i = 0; i < text.length; i++) { + if (text[i].length() == 0) { + continue; + } + if (string + .regionMatches(true, offset, text[i], 0, text[i].length())) { + // Search for the longest match, in case some fields are subsets + if (found == -1 || text[i].length() > text[found].length()) { + found = i; + } + } + } + if (found != -1) { + calendar.set(field, found); + return offset + text[found].length(); + } + return -offset - 1; + } + + private int parseTimeZone(String string, int offset) { +// BEGIN android-changed + String[][] zones = formatData.internalZoneStrings(); +// END android-changed + boolean foundGMT = string.regionMatches(offset, "GMT", 0, 3); //$NON-NLS-1$ + if (foundGMT) { + offset += 3; + } + char sign; + if (offset < string.length() + && ((sign = string.charAt(offset)) == '+' || sign == '-')) { + ParsePosition position = new ParsePosition(offset + 1); + Number result = numberFormat.parse(string, position); + if (result == null) { + return -position.getErrorIndex() - 1; + } + int hour = result.intValue(); + int raw = hour * 3600000; + int index = position.getIndex(); + if (index < string.length() && string.charAt(index) == ':') { + position.setIndex(index + 1); + result = numberFormat.parse(string, position); + if (result == null) { + return -position.getErrorIndex() - 1; + } + int minute = result.intValue(); + raw += minute * 60000; + } else if (hour >= 24) { + raw = (hour / 100 * 3600000) + (hour % 100 * 60000); + } + if (sign == '-') { + raw = -raw; + } + calendar.setTimeZone(new SimpleTimeZone(raw, "")); //$NON-NLS-1$ + return position.getIndex(); + } + if (foundGMT) { + calendar.setTimeZone(TimeZone.getTimeZone("GMT")); //$NON-NLS-1$ + return offset; + } + for (String[] element : zones) { + for (int j = 1; j < 5; j++) { + if (string.regionMatches(true, offset, element[j], 0, + element[j].length())) { + TimeZone zone = TimeZone.getTimeZone(element[0]); + if (zone == null) { + return -offset - 1; + } + int raw = zone.getRawOffset(); + if (j >= 3 && zone.useDaylightTime()) { + raw += 3600000; + } + calendar.setTimeZone(new SimpleTimeZone(raw, "")); //$NON-NLS-1$ + return offset + element[j].length(); + } + } + } + return -offset - 1; + } + + /** + * Sets the Date which is the start of the one hundred year period for two + * digits year values. + * + * @param date + * the Date + */ + public void set2DigitYearStart(Date date) { + defaultCenturyStart = date; + Calendar cal = new GregorianCalendar(); + cal.setTime(date); + creationYear = cal.get(Calendar.YEAR); + } + + /** + * Sets the DateFormatSymbols used by this SimpleDateFormat. + * + * @param value + * the DateFormatSymbols + */ + public void setDateFormatSymbols(DateFormatSymbols value) { + formatData = (DateFormatSymbols) value.clone(); + } + + /** + * Returns the pattern of this SimpleDateFormat using localized pattern + * characters. + * + * @return the localized pattern + */ + public String toLocalizedPattern() { + return convertPattern(pattern, patternChars, formatData + .getLocalPatternChars(), false); + } + + /** + * Returns the pattern of this SimpleDateFormat using non-localized pattern + * characters. + * + * @return the non-localized pattern + */ + public String toPattern() { + return pattern; + } + + private static final ObjectStreamField[] serialPersistentFields = { + new ObjectStreamField("defaultCenturyStart", Date.class), //$NON-NLS-1$ + new ObjectStreamField("formatData", DateFormatSymbols.class), //$NON-NLS-1$ + new ObjectStreamField("pattern", String.class), //$NON-NLS-1$ + new ObjectStreamField("serialVersionOnStream", Integer.TYPE), }; //$NON-NLS-1$ + + private void writeObject(ObjectOutputStream stream) throws IOException { + ObjectOutputStream.PutField fields = stream.putFields(); + fields.put("defaultCenturyStart", defaultCenturyStart); //$NON-NLS-1$ + fields.put("formatData", formatData); //$NON-NLS-1$ + fields.put("pattern", pattern); //$NON-NLS-1$ + fields.put("serialVersionOnStream", 1); //$NON-NLS-1$ + stream.writeFields(); + } + + private void readObject(ObjectInputStream stream) throws IOException, + ClassNotFoundException { + ObjectInputStream.GetField fields = stream.readFields(); + int version = fields.get("serialVersionOnStream", 0); //$NON-NLS-1$ + Date date; + if (version > 0) { + date = (Date) fields.get("defaultCenturyStart", new Date()); //$NON-NLS-1$ + } else { + date = new Date(); + } + set2DigitYearStart(date); + formatData = (DateFormatSymbols) fields.get("formatData", null); //$NON-NLS-1$ + pattern = (String) fields.get("pattern", ""); //$NON-NLS-1$ //$NON-NLS-2$ + } +} diff --git a/text/src/main/java/java/text/StringCharacterIterator.java b/text/src/main/java/java/text/StringCharacterIterator.java new file mode 100644 index 0000000..a4d3b68 --- /dev/null +++ b/text/src/main/java/java/text/StringCharacterIterator.java @@ -0,0 +1,279 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package java.text; + +/** + * StringCharacterIterator is an implementation of CharacterIterator for + * Strings. + */ +public final class StringCharacterIterator implements CharacterIterator { + + String string; + + int start, end, offset; + + /** + * Constructs a new StringCharacterIterator on the specified String. The + * begin and current indexes are set to the beginning of the String, the end + * index is set to the length of the String. + * + * @param value + * the new source String to iterate + */ + public StringCharacterIterator(String value) { + string = value; + start = offset = 0; + end = string.length(); + } + + /** + * Constructs a new StringCharacterIterator on the specified String with the + * current index set to the specified value. The begin index is set to the + * beginning of the String, the end index is set to the length of the String. + * + * @param value + * the new source String to iterate + * @param location + * the current index + * + * @exception IllegalArgumentException + * when the current index is less than zero or greater than + * the length of the String + */ + public StringCharacterIterator(String value, int location) { + string = value; + start = 0; + end = string.length(); + if (location < 0 || location > end) { + throw new IllegalArgumentException(); + } + offset = location; + } + + /** + * Constructs a new StringCharacterIterator on the specified String with the + * begin, end and current index set to the specified values. + * + * @param value + * the new source String to iterate + * @param start + * the index of the first character to iterate + * @param end + * the index one past the last character to iterate + * @param location + * the current index + * + * @exception IllegalArgumentException + * when the begin index is less than zero, the end index is + * greater than the String length, the begin index is greater + * than the end index, the current index is less than the + * begin index or greater than the end index + */ + public StringCharacterIterator(String value, int start, int end, + int location) { + string = value; + if (start < 0 || end > string.length() || start > end + || location < start || location > end) { + throw new IllegalArgumentException(); + } + this.start = start; + this.end = end; + offset = location; + } + + /** + * Returns a new StringCharacterIterator with the same source String, begin, + * end, and current index as this StringCharacterIterator. + * + * @return a shallow copy of this StringCharacterIterator + * + * @see java.lang.Cloneable + */ + @Override + public Object clone() { + try { + return super.clone(); + } catch (CloneNotSupportedException e) { + return null; + } + } + + /** + * Returns the character at the current index in the source String. + * + * @return the current character, or DONE if the current index is past the + * end + */ + public char current() { + if (offset == end) { + return DONE; + } + return string.charAt(offset); + } + + /** + * Compares the specified object to this StringCharacterIterator and answer + * if they are equal. The object must be a StringCharacterIterator iterating + * over the same sequence of characters with the same index. + * + * @param object + * the object to compare with this object + * @return true if the specified object is equal to this + * StringCharacterIterator, false otherwise + * + * @see #hashCode + */ + @Override + public boolean equals(Object object) { + if (!(object instanceof StringCharacterIterator)) { + return false; + } + StringCharacterIterator it = (StringCharacterIterator) object; + return string.equals(it.string) && start == it.start && end == it.end + && offset == it.offset; + } + + /** + * Sets the current position to the begin index and returns the character at + * the begin index. + * + * @return the character at the begin index + */ + public char first() { + if (start == end) { + return DONE; + } + offset = start; + return string.charAt(offset); + } + + /** + * Returns the begin index in the source String. + * + * @return the index of the first character to iterate + */ + public int getBeginIndex() { + return start; + } + + /** + * Returns the end index in the source String. + * + * @return the index one past the last character to iterate + */ + public int getEndIndex() { + return end; + } + + /** + * Returns the current index in the source String. + * + * @return the current index + */ + public int getIndex() { + return offset; + } + + /** + * Returns an integer hash code for the receiver. Objects which are equal + * answer the same value for this method. + * + * @return the receiver's hash + * + * @see #equals + */ + @Override + public int hashCode() { + return string.hashCode() + start + end + offset; + } + + /** + * Sets the current position to the end index - 1 and returns the character + * at the current position. + * + * @return the character before the end index + */ + public char last() { + if (start == end) { + return DONE; + } + offset = end - 1; + return string.charAt(offset); + } + + /** + * Increments the current index and returns the character at the new index. + * + * @return the character at the next index, or DONE if the next index is + * past the end + */ + public char next() { + if (offset >= (end - 1)) { + offset = end; + return DONE; + } + return string.charAt(++offset); + } + + /** + * Decrements the current index and returns the character at the new index. + * + * @return the character at the previous index, or DONE if the previous + * index is past the beginning + */ + public char previous() { + if (offset == start) { + return DONE; + } + return string.charAt(--offset); + } + + /** + * Sets the current index in the source String. + * + * @return the character at the new index, or DONE if the index is past the + * end + * + * @exception IllegalArgumentException + * when the new index is less than the begin index or greater + * than the end index + */ + public char setIndex(int location) { + if (location < start || location > end) { + throw new IllegalArgumentException(); + } + offset = location; + if (offset == end) { + return DONE; + } + return string.charAt(offset); + } + + /** + * Sets the source String to iterate. The begin and end positions are set to + * the start and end of this String. + * + * @param value + * the new source String + */ + public void setText(String value) { + string = value; + start = offset = 0; + end = value.length(); + } +} diff --git a/text/src/main/java/java/text/package.html b/text/src/main/java/java/text/package.html new file mode 100644 index 0000000..2909d52 --- /dev/null +++ b/text/src/main/java/java/text/package.html @@ -0,0 +1,16 @@ +<html> + <body> + <p> + The java.text package allows to uncouple the text in the + application from a natural language. + </p> + <p> + With this it is possible to write the application in an + unlocalized way. Like this a new localization can be provided at any + time without having to change anything in the code. Support for + localization is given for numbers, messages, dates and also + characteristics of a language like the directionality, sorting order or + enumeration of characters, words or lines. + </p> +</body> +</html> diff --git a/text/src/main/java/org/apache/harmony/text/BidiRun.java b/text/src/main/java/org/apache/harmony/text/BidiRun.java new file mode 100644 index 0000000..d1033f4 --- /dev/null +++ b/text/src/main/java/org/apache/harmony/text/BidiRun.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.text; + +/** + * TODO: type description + */ +public class BidiRun { + private final int start; + + private final int limit; + + private final int level; + + public BidiRun(int start, int limit, int level) { + this.start = start; + this.limit = limit; + this.level = level; + } + + public int getLevel() { + return level; + } + + public int getLimit() { + return limit; + } + + public int getStart() { + return start; + } + + @Override + public boolean equals(Object o) { + return o == null || o.getClass() != BidiRun.class ? false + : this.start == ((BidiRun) o).start + && this.limit == ((BidiRun) o).limit + && this.level == ((BidiRun) o).level; + } +} diff --git a/text/src/main/java/org/apache/harmony/text/BidiWrapper.java b/text/src/main/java/org/apache/harmony/text/BidiWrapper.java new file mode 100644 index 0000000..240fcdf --- /dev/null +++ b/text/src/main/java/org/apache/harmony/text/BidiWrapper.java @@ -0,0 +1,87 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.text; + +/** + * TODO: type description + */ + +public final class BidiWrapper { + + public static final int UBIDI_DEFAULT_LTR = 0xfe; + + public static final int UBIDI_DEFAULT_RTL = 0xff; + + public static final int UBIDI_MAX_EXPLICIT_LEVEL = 61; + + public static final int UBIDI_LEVEL_OVERRIDE = 0x80; + + public static final int UBIDI_KEEP_BASE_COMBINING = 1; + + public static final int UBIDI_DO_MIRRORING = 2; + + public static final int UBIDI_INSERT_LRM_FOR_NUMERIC = 4; + + public static final int UBIDI_REMOVE_BIDI_CONTROLS = 8; + + public static final int UBIDI_OUTPUT_REVERSE = 16; + + public static final int UBiDiDirection_UBIDI_LTR = 0; + + public static final int UBiDiDirection_UBIDI_RTL = 1; + + public static final int UBiDiDirection_UBIDI_MIXED = 2; + + // Allocate a UBiDi structure. + public static native long ubidi_open(); + + // ubidi_close() must be called to free the memory associated with a + // UBiDi object. + public static native void ubidi_close(long pBiDi); + + // Perform the Unicode BiDi algorithm. + public static native void ubidi_setPara(long pBiDi, char[] text, + int length, byte paraLevel, byte[] embeddingLevels); + + // ubidi_setLine() sets a UBiDi to contain the reordering information, + // especially the resolved levels, for all the characters in a line of + // text. + public static native long ubidi_setLine(final long pParaBiDi, int start, + int limit); + + // Get the directionality of the text. + public static native int ubidi_getDirection(final long pBiDi); + + // Get the length of the text. + public static native int ubidi_getLength(final long pBiDi); + + // Get the paragraph level of the text. + public static native byte ubidi_getParaLevel(final long pBiDi); + + // Get an array of levels for each character. + public static native byte[] ubidi_getLevels(long pBiDi); + + // Get the number of runs. + public static native int ubidi_countRuns(long pBiDi); + + // Get the BidiRuns + public static native BidiRun[] ubidi_getRuns(long pBidi); + + // This is a convenience function that does not use a UBiDi object + public static native int[] ubidi_reorderVisual(byte[] levels, int length); +} diff --git a/text/src/main/java/org/apache/harmony/text/internal/nls/Messages.java b/text/src/main/java/org/apache/harmony/text/internal/nls/Messages.java new file mode 100644 index 0000000..ed1f532 --- /dev/null +++ b/text/src/main/java/org/apache/harmony/text/internal/nls/Messages.java @@ -0,0 +1,124 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * THE FILE HAS BEEN AUTOGENERATED BY MSGTOOL TOOL. + * All changes made to this file manually will be overwritten + * if this tool runs again. Better make changes in the template file. + */ + +package org.apache.harmony.text.internal.nls; + +import org.apache.harmony.luni.util.MsgHelp; + +/** + * This class retrieves strings from a resource bundle and returns them, + * formatting them with MessageFormat when required. + * <p> + * It is used by the system classes to provide national language support, by + * looking up messages in the <code> + * org.apache.harmony.text.internal.nls.messages + * </code> + * resource bundle. Note that if this file is not available, or an invalid key + * is looked up, or resource bundle support is not available, the key itself + * will be returned as the associated message. This means that the <em>KEY</em> + * should a reasonable human-readable (english) string. + * + */ +public class Messages { + + private static final String sResource = + "org.apache.harmony.text.internal.nls.messages"; //$NON-NLS-1$ + + /** + * Retrieves a message which has no arguments. + * + * @param msg + * String the key to look up. + * @return String the message for that key in the system message bundle. + */ + static public String getString(String msg) { + return MsgHelp.getString(sResource, msg); + } + + /** + * Retrieves a message which takes 1 argument. + * + * @param msg + * String the key to look up. + * @param arg + * Object the object to insert in the formatted output. + * @return String the message for that key in the system message bundle. + */ + static public String getString(String msg, Object arg) { + return getString(msg, new Object[] { arg }); + } + + /** + * Retrieves a message which takes 1 integer argument. + * + * @param msg + * String the key to look up. + * @param arg + * int the integer to insert in the formatted output. + * @return String the message for that key in the system message bundle. + */ + static public String getString(String msg, int arg) { + return getString(msg, new Object[] { Integer.toString(arg) }); + } + + /** + * Retrieves a message which takes 1 character argument. + * + * @param msg + * String the key to look up. + * @param arg + * char the character to insert in the formatted output. + * @return String the message for that key in the system message bundle. + */ + static public String getString(String msg, char arg) { + return getString(msg, new Object[] { String.valueOf(arg) }); + } + + /** + * Retrieves a message which takes 2 arguments. + * + * @param msg + * String the key to look up. + * @param arg1 + * Object an object to insert in the formatted output. + * @param arg2 + * Object another object to insert in the formatted output. + * @return String the message for that key in the system message bundle. + */ + static public String getString(String msg, Object arg1, Object arg2) { + return getString(msg, new Object[] { arg1, arg2 }); + } + + /** + * Retrieves a message which takes several arguments. + * + * @param msg + * String the key to look up. + * @param args + * Object[] the objects to insert in the formatted output. + * @return String the message for that key in the system message bundle. + */ + static public String getString(String msg, Object[] args) { + return MsgHelp.getString(sResource, msg, args); + } +} diff --git a/text/src/main/java/org/apache/harmony/text/internal/nls/messages.properties b/text/src/main/java/org/apache/harmony/text/internal/nls/messages.properties new file mode 100644 index 0000000..22221a9 --- /dev/null +++ b/text/src/main/java/org/apache/harmony/text/internal/nls/messages.properties @@ -0,0 +1,46 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# messages for EN locale +text.00=min digits greater than max digits +text.01=min or max digits negative +text.02=Unknown attribute +text.03=Unknown pattern character - '{0}' +text.04=Unterminated quote +text.05=Invalid pattern char {0} in {1} +text.07=Unmatched braces in the pattern +text.06=Build rules empty +text.08=one of arguments is null +text.09=The deserialized date is invalid +text.0A=Invalid substring range +text.0B=Cannot add attributes to empty string +text.0C=cannot resolve subclasses +text.0E=Illegal date style: {0} +text.0F=Illegal time style: {0} +text.0D=Negative textStart value {0} +text.10=Negative embStart value {0} +text.11=Negative paragraph length {0} +text.12=Invalid ranges (start={0}, limit={1}, length={2}) +text.13=Invalid ranges (levels={0}, levelStart={1}, objects={2}, objectStart={3}, count={4}) +text.14=paragraph is null +text.19=Invalid argument number +text.15=Missing element format +text.16=Unknown element format +text.17=Unknown format +text.18=Not a valid {0}, subclass should override readResolve() +text.19=Unparseable date: {0} +text.1A=position is null + diff --git a/text/src/test/java/org/apache/harmony/text/tests/java/text/AllTests.java b/text/src/test/java/org/apache/harmony/text/tests/java/text/AllTests.java new file mode 100644 index 0000000..cef6191 --- /dev/null +++ b/text/src/test/java/org/apache/harmony/text/tests/java/text/AllTests.java @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package org.apache.harmony.text.tests.java.text; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class AllTests { + + public static void main(String[] args) { + junit.textui.TestRunner.run(AllTests.suite()); + } + + public static Test suite() { + TestSuite suite = new TestSuite( + "Suite org.apache.harmony.text.tests.java.text"); + //$JUnit-BEGIN$ + suite.addTestSuite(AnnotationTest.class); + suite.addTestSuite(AttributedCharacterIteratorAttributeTest.class); + suite.addTestSuite(AttributedCharacterIteratorTest.class); + suite.addTestSuite(AttributedStringTest.class); + suite.addTestSuite(BidiTest.class); + suite.addTestSuite(BreakIteratorTest.class); + suite.addTestSuite(ChoiceFormatTest.class); + suite.addTestSuite(CollationElementIteratorTest.class); + suite.addTestSuite(CollationKeyTest.class); + suite.addTestSuite(CollatorTest.class); + suite.addTestSuite(DataFormatFieldTest.class); + suite.addTestSuite(DateFormatSymbolsTest.class); + suite.addTestSuite(DateFormatTest.class); + suite.addTestSuite(DecimalFormatSymbolsTest.class); + suite.addTestSuite(DecimalFormatTest.class); + suite.addTestSuite(FieldPositionTest.class); + suite.addTestSuite(FormatFieldTest.class); + suite.addTestSuite(FormatTest.class); + suite.addTestSuite(MessageFormatFieldTest.class); + suite.addTestSuite(MessageFormatTest.class); + suite.addTestSuite(NumberFormatFieldTest.class); + suite.addTestSuite(NumberFormatTest.class); + suite.addTestSuite(ParseExceptionTest.class); + suite.addTestSuite(ParsePositionTest.class); + suite.addTestSuite(RuleBasedCollatorTest.class); + suite.addTestSuite(SimpleDateFormatTest.class); + suite.addTestSuite(StringCharacterIteratorTest.class); + //$JUnit-END$ + return suite; + } +} diff --git a/text/src/test/java/org/apache/harmony/text/tests/java/text/AnnotationTest.java b/text/src/test/java/org/apache/harmony/text/tests/java/text/AnnotationTest.java new file mode 100644 index 0000000..c7d8f84 --- /dev/null +++ b/text/src/test/java/org/apache/harmony/text/tests/java/text/AnnotationTest.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.text.tests.java.text; + +import java.text.Annotation; + +import junit.framework.TestCase; + +public class AnnotationTest extends TestCase { + + /** + * @tests java.text.Annotation(Object) + */ + public void testAnnotation() { + assertNotNull(new Annotation(null)); + assertNotNull(new Annotation("value")); + } + + /** + * @tests java.text.Annotation.getValue() + */ + public void testGetValue() { + Annotation a = new Annotation(null); + assertNull(a.getValue()); + a = new Annotation("value"); + assertEquals("value", a.getValue()); + } + + /** + * @tests java.text.Annotation.toString() + */ + public void testToString() { + Annotation ant = new Annotation("HelloWorld"); + assertEquals("toString error.", + "java.text.Annotation[value=HelloWorld]", ant.toString()); + assertNotNull(new Annotation(null).toString()); + assertNotNull(new Annotation("value").toString()); + } +} diff --git a/text/src/test/java/org/apache/harmony/text/tests/java/text/AttributedCharacterIteratorAttributeTest.java b/text/src/test/java/org/apache/harmony/text/tests/java/text/AttributedCharacterIteratorAttributeTest.java new file mode 100644 index 0000000..968de4c --- /dev/null +++ b/text/src/test/java/org/apache/harmony/text/tests/java/text/AttributedCharacterIteratorAttributeTest.java @@ -0,0 +1,170 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.harmony.text.tests.java.text; + +import java.io.InvalidObjectException; +import java.text.AttributedCharacterIterator; +import java.text.AttributedCharacterIterator.Attribute; +import junit.framework.Test; + +public class AttributedCharacterIteratorAttributeTest extends + junit.framework.TestCase { + + private class MockAttributedCharacterIteratorAttribute extends + AttributedCharacterIterator.Attribute { + + private static final long serialVersionUID = 1L; + + public MockAttributedCharacterIteratorAttribute(String name) { + super(name); + } + + @Override + public String getName() { + return super.getName(); + } + + @Override + public Object readResolve() throws InvalidObjectException { + return super.readResolve(); + } + } + + private class TestAttributedCharacterIteratorAttribute extends + AttributedCharacterIterator.Attribute { + private static final long serialVersionUID = -2917613373935785179L; + + public TestAttributedCharacterIteratorAttribute(String name) { + super(name); + } + } + + /** + * @tests java.text.AttributedCharacterIterator.Attribute#AttributedCharacterIterator.Attribute(java.lang.String) + * Test of method + * java.text.AttributedCharacterIterator.Attribute#AttributedCharacterIterator.Attribute(java.lang.String). + */ + public void test_Constructor() { + try { + new MockAttributedCharacterIteratorAttribute("test"); + } catch (Exception e) { + fail("Unexpected exception " + e.toString()); + } + } + + /** + * @tests java.text.AttributedCharacterIterator.Attribute#equals(java.lang.Object) + * Test of method + * java.text.AttributedCharacterIterator.Attribute#equals(java.lang.Object). + */ + public void test_equalsLjava_lang_Object() { + try { + MockAttributedCharacterIteratorAttribute mac1 = new MockAttributedCharacterIteratorAttribute( + "test1"); + MockAttributedCharacterIteratorAttribute mac2 = new MockAttributedCharacterIteratorAttribute( + "test2"); + + assertFalse("Attributes are equal", mac2.equals(mac1)); + + TestAttributedCharacterIteratorAttribute mac3 = new TestAttributedCharacterIteratorAttribute( + "test1"); + + assertFalse("Attributes are equal", mac3.equals(mac1)); + + AttributedCharacterIterator.Attribute mac4 = mac1; + + assertTrue("Attributes are non-equal", mac4.equals(mac1)); + } catch (Exception e) { + fail("Unexpected exception " + e.toString()); + } + } + + /** + * @tests java.text.AttributedCharacterIterator.Attribute#getName() Test of + * method java.text.AttributedCharacterIterator.Attribute#getName(). + */ + public void test_getName() { + try { + MockAttributedCharacterIteratorAttribute mac1 = new MockAttributedCharacterIteratorAttribute( + "test1"); + assertEquals("Incorrect attribute name", "test1", mac1.getName()); + } catch (Exception e) { + fail("Unexpected exception " + e.toString()); + } + } + + /** + * @tests java.text.AttributedCharacterIterator.Attribute#hashCode() + */ + public void test_hashCode() { + try { + MockAttributedCharacterIteratorAttribute mac1 = new MockAttributedCharacterIteratorAttribute( + "test1"); + TestAttributedCharacterIteratorAttribute mac2 = new TestAttributedCharacterIteratorAttribute( + "test1"); + + assertTrue("The hash codes of same attributes are not equal", mac1 + .hashCode() == mac2.hashCode()); + + MockAttributedCharacterIteratorAttribute mac3 = new MockAttributedCharacterIteratorAttribute( + "test2"); + + assertTrue("The hash codes of different attributes are equal", mac1 + .hashCode() != mac3.hashCode()); + + AttributedCharacterIterator.Attribute mac4 = mac1; + + assertTrue("The hash codes of same attributes but different hierarchy classes are not equal", + mac1.hashCode() == mac4.hashCode()); + } catch (Exception e) { + fail("Unexpected exception " + e.toString()); + } + } + + /** + * @tests java.text.AttributedCharacterIterator.Attribute#readResolve() Test + * of method + * java.text.AttributedCharacterIterator.Attribute#readResolve(). + */ + public void test_readResolve() { + MockAttributedCharacterIteratorAttribute mac1 = new MockAttributedCharacterIteratorAttribute( + "test"); + try { + mac1.readResolve(); + fail("InvalidObjectException has not been thrown"); + } catch (InvalidObjectException e) { + // expected + } + } + + /** + * @tests java.text.AttributedCharacterIterator.Attribute#toString() Test of + * method java.text.AttributedCharacterIterator.Attribute#toString(). + */ + public void test_toString() { + MockAttributedCharacterIteratorAttribute mac1 = new MockAttributedCharacterIteratorAttribute( + null); + assertEquals("Unexpected class representation string", mac1.toString(), + getClass().getName() + + "$MockAttributedCharacterIteratorAttribute(null)"); + TestAttributedCharacterIteratorAttribute mac2 = new TestAttributedCharacterIteratorAttribute( + "test1"); + assertEquals("Unexpected class representation string", mac2.toString(), + getClass().getName() + + "$TestAttributedCharacterIteratorAttribute(test1)"); + } +} diff --git a/text/src/test/java/org/apache/harmony/text/tests/java/text/AttributedCharacterIteratorTest.java b/text/src/test/java/org/apache/harmony/text/tests/java/text/AttributedCharacterIteratorTest.java new file mode 100644 index 0000000..76ab2aa --- /dev/null +++ b/text/src/test/java/org/apache/harmony/text/tests/java/text/AttributedCharacterIteratorTest.java @@ -0,0 +1,178 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.text.tests.java.text; + +import java.text.AttributedCharacterIterator; +import java.text.AttributedString; +import java.text.CharacterIterator; + +public class AttributedCharacterIteratorTest extends junit.framework.TestCase { + + /** + * @tests java.text.AttributedCharacterIterator#current() + */ + public void test_current() { + String test = "Test 23ring"; + AttributedString attrString = new AttributedString(test); + AttributedCharacterIterator it = attrString.getIterator(); + assertEquals("Wrong first", 'T', it.current()); + it.next(); + assertEquals("Wrong second", 'e', it.current()); + for (int i = 0; i < 9; i++) + it.next(); + assertEquals("Wrong last", 'g', it.current()); + it.next(); + assertTrue("Wrong final", it.current() == CharacterIterator.DONE); + + it = attrString.getIterator(null, 2, 8); + assertEquals("Wrong first2", 's', it.current()); + } + + /** + * @tests java.text.AttributedCharacterIterator#first() + */ + public void test_first() { + String test = "Test 23ring"; + AttributedString attrString = new AttributedString(test); + AttributedCharacterIterator it = attrString.getIterator(); + assertEquals("Wrong first1", 'T', it.first()); + it = attrString.getIterator(null, 0, 3); + assertEquals("Wrong first2", 'T', it.first()); + it = attrString.getIterator(null, 2, 8); + assertEquals("Wrong first3", 's', it.first()); + it = attrString.getIterator(null, 11, 11); + assertTrue("Wrong first4", it.first() == CharacterIterator.DONE); + } + + /** + * @tests java.text.AttributedCharacterIterator#getBeginIndex() + */ + public void test_getBeginIndex() { + String test = "Test 23ring"; + AttributedString attrString = new AttributedString(test); + AttributedCharacterIterator it = attrString.getIterator(null, 2, 6); + assertEquals("Wrong begin index", 2, it.getBeginIndex()); + } + + /** + * @tests java.text.AttributedCharacterIterator#getEndIndex() + */ + public void test_getEndIndex() { + String test = "Test 23ring"; + AttributedString attrString = new AttributedString(test); + AttributedCharacterIterator it = attrString.getIterator(null, 2, 6); + assertEquals("Wrong begin index", 6, it.getEndIndex()); + } + + /** + * @tests java.text.AttributedCharacterIterator#getIndex() + */ + public void test_getIndex() { + String test = "Test 23ring"; + AttributedString attrString = new AttributedString(test); + AttributedCharacterIterator it = attrString.getIterator(); + assertEquals("Wrong first", 0, it.getIndex()); + it.next(); + assertEquals("Wrong second", 1, it.getIndex()); + for (int i = 0; i < 9; i++) + it.next(); + assertEquals("Wrong last", 10, it.getIndex()); + it.next(); + assertEquals("Wrong final", 11, it.getIndex()); + } + + /** + * @tests java.text.AttributedCharacterIterator#last() + */ + public void test_last() { + String test = "Test 23ring"; + AttributedString attrString = new AttributedString(test); + AttributedCharacterIterator it = attrString.getIterator(); + assertEquals("Wrong last1", 'g', it.last()); + it = attrString.getIterator(null, 0, 3); + assertEquals("Wrong last2", 's', it.last()); + it = attrString.getIterator(null, 2, 8); + assertEquals("Wrong last3", 'r', it.last()); + it = attrString.getIterator(null, 0, 0); + assertTrue("Wrong last4", it.last() == CharacterIterator.DONE); + } + + /** + * @tests java.text.AttributedCharacterIterator#next() + */ + public void test_next() { + String test = "Test 23ring"; + AttributedString attrString = new AttributedString(test); + AttributedCharacterIterator it = attrString.getIterator(); + assertEquals("Wrong first", 'e', it.next()); + for (int i = 0; i < 8; i++) + it.next(); + assertEquals("Wrong last", 'g', it.next()); + assertTrue("Wrong final", it.next() == CharacterIterator.DONE); + + it = attrString.getIterator(null, 2, 8); + assertEquals("Wrong first2", 't', it.next()); + } + + /** + * @tests java.text.AttributedCharacterIterator#previous() + */ + public void test_previous() { + String test = "Test 23ring"; + AttributedString attrString = new AttributedString(test); + AttributedCharacterIterator it = attrString.getIterator(); + it.setIndex(11); + assertEquals("Wrong first", 'g', it.previous()); + } + + /** + * @tests java.text.AttributedCharacterIterator#setIndex(int) + */ + public void test_setIndexI() { + String test = "Test 23ring"; + AttributedString attrString = new AttributedString(test); + AttributedCharacterIterator it = attrString.getIterator(); + it.setIndex(5); + assertEquals("Wrong first", '2', it.current()); + } + + /** + * @tests java.text.AttributedCharacterIterator#getRunLimit(java.text.AttributedCharacterIterator$Attribute) + */ + public void test_getRunLimitLjava_text_AttributedCharacterIterator$Attribute() { + AttributedString as = new AttributedString("test"); + as.addAttribute(AttributedCharacterIterator.Attribute.LANGUAGE, "a", 2, + 3); + AttributedCharacterIterator it = as.getIterator(); + assertEquals("non-null value limit", + 2, it.getRunLimit(AttributedCharacterIterator.Attribute.LANGUAGE)); + + as = new AttributedString("test"); + as.addAttribute(AttributedCharacterIterator.Attribute.LANGUAGE, null, + 2, 3); + it = as.getIterator(); + assertEquals("null value limit", + 4, it.getRunLimit(AttributedCharacterIterator.Attribute.LANGUAGE)); + } + + protected void setUp() { + } + + protected void tearDown() { + } +} diff --git a/text/src/test/java/org/apache/harmony/text/tests/java/text/AttributedStringTest.java b/text/src/test/java/org/apache/harmony/text/tests/java/text/AttributedStringTest.java new file mode 100644 index 0000000..91f4313 --- /dev/null +++ b/text/src/test/java/org/apache/harmony/text/tests/java/text/AttributedStringTest.java @@ -0,0 +1,462 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.harmony.text.tests.java.text; + +import java.text.AttributedCharacterIterator; +import java.text.AttributedString; +import java.text.CharacterIterator; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; +import java.util.WeakHashMap; + +import junit.framework.Test; + +public class AttributedStringTest extends junit.framework.TestCase { + + /** + * @tests java.text.AttributedString#AttributedString(java.lang.String) + */ + public void test_ConstructorLjava_lang_String() { + String test = "Test string"; + AttributedString attrString = new AttributedString(test); + AttributedCharacterIterator it = attrString.getIterator(); + StringBuffer buf = new StringBuffer(); + buf.append(it.first()); + char ch; + while ((ch = it.next()) != CharacterIterator.DONE) + buf.append(ch); + assertTrue("Wrong string: " + buf, buf.toString().equals(test)); + } + + /** + * @tests java.text.AttributedString#AttributedString(AttributedCharacterIterator) + */ + public void test_ConstructorLAttributedCharacterIterator() { + // Regression for HARMONY-1354 + assertNotNull(new AttributedString( + new testAttributedCharacterIterator())); + } + + /** + * @tests java.text.AttributedString#AttributedString(AttributedCharacterIterator, + * int, int) Test of method + * java.text.AttributedString#AttributedString(AttributedCharacterIterator, + * int, int). Case 1: Try to consruct AttributedString. Case 2: Try + * to consruct AttributedString using incorrect beginIndex. Case 3: + * Try to consruct AttributedString using incorrect endIndex. + */ + public void test_ConstructorLAttributedCharacterIteratorII() { + // Regression for HARMONY-1355 + + // case 1: Try to consruct AttributedString. + try { + new AttributedString(new testAttributedCharacterIterator(), 0, 0); + } catch (Exception e) { + fail("Unexpected exception " + e.toString()); + } + + // case 2: Try to consruct AttributedString using incorrect beginIndex. + try { + new AttributedString(new testAttributedCharacterIterator(), -1, 0); + fail("Expected IllegalArgumentException was not thrown"); + } catch (IllegalArgumentException e) { + // expected + } + + // case 3: Try to consruct AttributedString using incorrect endIndex. + try { + new AttributedString(new testAttributedCharacterIterator(), 0, -1); + fail("Expected IllegalArgumentException was not thrown"); + } catch (IllegalArgumentException e) { + // expected + } + } + + /** + * @tests java.text.AttributedString#AttributedString(AttributedCharacterIterator, + * int, int, AttributedCharacterIterator.Attribute[]) Test of method + * java.text.AttributedString#AttributedString(AttributedCharacterIterator, + * int, int, AttributedCharacterIterator.Attribute[]). Case 1: Try to + * consruct AttributedString. Case 2: Try to consruct + * AttributedString using incorrect beginIndex. Case 3: Try to + * consruct AttributedString using incorrect endIndex. Case 4: Try to + * consruct AttributedString using specified attributes. + */ + public void test_ConstructorLAttributedCharacterIteratorII$Ljava_text_AttributedCharacterIterator$Attribute() { + // case 1: Try to consruct AttributedString. + try { + new AttributedString(new testAttributedCharacterIterator(), 0, 0, + null); + } catch (Exception e) { + fail("Unexpected exception " + e.toString()); + } + + // case 2: Try to consruct AttributedString using incorrect beginIndex. + try { + new AttributedString(new testAttributedCharacterIterator(), -1, 0, + null); + fail("Expected IllegalArgumentException was not thrown"); + } catch (IllegalArgumentException e) { + // expected + } + + // case 3: Try to consruct AttributedString using incorrect endIndex. + try { + new AttributedString(new testAttributedCharacterIterator(), 0, -1, + null); + fail("Expected IllegalArgumentException was not thrown"); + } catch (IllegalArgumentException e) { + // expected + } + + // case 4: Try to consruct AttributedString using specified attributes. + try { + AttributedCharacterIterator.Attribute[] attributes = new AttributedCharacterIterator.Attribute[1]; + attributes[0] = new TestAttributedCharacterIteratorAttribute("test"); + new AttributedString(new testAttributedCharacterIterator(), 0, 0, + attributes); + } catch (IllegalArgumentException e) { + fail("Unexpected expected " + e.toString()); + } + } + + /** + * @tests java.text.AttributedString#AttributedString(AttributedCharacterIterator, + * int, int, Map<? extends AttributedCharacterIterator.Attribute,?>) + * Test of method + * java.text.AttributedString#AttributedString(AttributedCharacterIterator, + * int, int, Map<? extends + * AttributedCharacterIterator.Attribute,?>). Case 1: Try to + * construct AttributedString. Case 2: Try to construct + * AttributedString using 0-length text and not an empty Map + * attributes. + */ + public void test_ConstructorLjava_lang_StringLjava_util_Map() { + String test = "Test string"; + + // case 1: Try to construct AttributedString + try { + AttributedString attrString = new AttributedString( + test, + new WeakHashMap<AttributedCharacterIterator.Attribute, String>()); + AttributedCharacterIterator it = attrString.getIterator(); + StringBuffer buf = new StringBuffer(); + buf.append(it.first()); + char ch; + while ((ch = it.next()) != CharacterIterator.DONE) + buf.append(ch); + assertTrue("Wrong string: " + buf, buf.toString().equals(test)); + } catch (Exception e) { + fail("Unexpected exception " + e.toString()); + } + + // case 2: Try to construct AttributedString using 0-length text and + // not an empty Map attributes. + try { + Map<AttributedCharacterIterator.Attribute, String> whm = new WeakHashMap<AttributedCharacterIterator.Attribute, String>(); + whm.put(new TestAttributedCharacterIteratorAttribute("test"), + "value"); + new AttributedString("", whm); + fail("Expected IllegalArgumentException was not thrown"); + } catch (Exception e) { + // expected + } + } + + private class TestAttributedCharacterIteratorAttribute extends + AttributedCharacterIterator.Attribute { + private static final long serialVersionUID = -2917613373935785179L; + + public TestAttributedCharacterIteratorAttribute(String name) { + super(name); + } + } + + private class testAttributedCharacterIterator implements + AttributedCharacterIterator { + public Set getAllAttributeKeys() { + return null; + } + + public Object getAttribute(AttributedCharacterIterator.Attribute p) { + return null; + } + + public Map getAttributes() { + return null; + } + + public int getRunLimit(Set p) { + return 0; + } + + public int getRunLimit(AttributedCharacterIterator.Attribute p) { + return 0; + } + + public int getRunLimit() { + return 0; + } + + public int getRunStart(Set p) { + return 0; + } + + public int getRunStart(AttributedCharacterIterator.Attribute p) { + return 0; + } + + public int getRunStart() { + return 0; + } + + public Object clone() { + return null; + } + + public int getIndex() { + return 0; + } + + public int getEndIndex() { + return 0; + } + + public int getBeginIndex() { + return 0; + } + + public char setIndex(int p) { + return 'a'; + } + + public char previous() { + return 'a'; + } + + public char next() { + return 'a'; + } + + public char current() { + return 'a'; + } + + public char last() { + return 'a'; + } + + public char first() { + return 'a'; + } + } + + public void test_addAttributeLjava_text_AttributedCharacterIterator$AttributeLjava_lang_ObjectII() { + AttributedString as = new AttributedString("test"); + as.addAttribute(AttributedCharacterIterator.Attribute.LANGUAGE, "a", 2, + 3); + AttributedCharacterIterator it = as.getIterator(); + assertEquals("non-null value limit", 2, it + .getRunLimit(AttributedCharacterIterator.Attribute.LANGUAGE)); + + as = new AttributedString("test"); + as.addAttribute(AttributedCharacterIterator.Attribute.LANGUAGE, null, + 2, 3); + it = as.getIterator(); + assertEquals("null value limit", 4, it + .getRunLimit(AttributedCharacterIterator.Attribute.LANGUAGE)); + + try { + as = new AttributedString("test"); + as.addAttribute(AttributedCharacterIterator.Attribute.LANGUAGE, + null, -1, 3); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // Expected + } + + // regression for Harmony-1244 + as = new AttributedString("123", new WeakHashMap()); + try { + as.addAttribute(null, new TreeSet(), 0, 1); + fail("should throw NullPointerException"); + } catch (NullPointerException e) { + // expected + } + + try { + as.addAttribute(null, new TreeSet(), -1, 1); + fail("should throw NullPointerException"); + } catch (NullPointerException e) { + // expected + } + } + + /** + * @tests java.text.AttributedString.addAttribute(AttributedCharacterIterator, + * Object) + */ + public void test_addAttributeLjava_text_AttributedCharacterIterator$AttributeLjava_lang_Object() { + // regression for Harmony-1244 + AttributedString as = new AttributedString("123", new WeakHashMap()); + try { + as.addAttribute(null, new TreeSet()); + fail("should throw NullPointerException"); + } catch (NullPointerException e) { + // expected + } + try { + as.addAttribute(null, null); + fail("should throw NullPointerException"); + } catch (NullPointerException e) { + // expected + } + } + + /** + * @tests java.text.AttributedString#addAttributes(Map<? extends + * AttributedCharacterIterator.Attribute,?>, int, int) Tests of + * method java.text.AttributedString#addAttributes(Map<? extends + * AttributedCharacterIterator.Attribute,?>, int, int). Case 1: Try + * to add attributes to AttributesString. Case 2: Try to add + * null-attributes to AttributesString. Case 3: Try to add attributes + * to AttributesString using incorrect index. + */ + public void test_addAttributesLjava_util_MapII() { + AttributedString as = new AttributedString("test"); + Map<AttributedCharacterIterator.Attribute, String> whm = new WeakHashMap<AttributedCharacterIterator.Attribute, String>(); + + // case 1: Try to add attributes to AttributesString. + try { + whm.put(new TestAttributedCharacterIteratorAttribute("test1"), + "value1"); + whm.put(new TestAttributedCharacterIteratorAttribute("test2"), + "value2"); + whm.put(new TestAttributedCharacterIteratorAttribute("test3"), + "value3"); + as.addAttributes(whm, 0, 3); + } catch (Exception e) { + fail("Unexpected exception " + e.toString()); + } + + // case 2: Try to add null-attributes to AttributesString. + try { + as.addAttributes(null, 0, 3); + fail("Expected NullPointerException was not thrown"); + } catch (NullPointerException e) { + // expected + } + + // case 3: Try to add attributes to AttributesString using incorrect + // index. + try { + as.addAttributes(whm, 0, 0); + fail("Expected IllegalArgumentException was not thrown"); + } catch (IllegalArgumentException e) { + // expected + } + } + + /** + * @tests java.text.AttributedString#getIterator() Test of method + * java.text.AttributedString#getIterator(). + */ + public void test_getIterator() { + String test = "Test string"; + try { + AttributedString attrString = new AttributedString(test); + AttributedCharacterIterator it = attrString.getIterator(); + assertEquals("Incorrect iteration on AttributedString", it.first(), + test.charAt(0)); + } catch (Exception e) { + fail("Unexpected exceptiption " + e.toString()); + } + } + + /** + * @tests java.text.AttributedString#getIterator(AttributedCharacterIterator.Attribute[]) + * Test of method + * java.text.AttributedString#getIterator(AttributedCharacterIterator.Attribute[]). + */ + public void test_getIterator$Ljava_text_AttributedCharacterIterator$Attribute() { + String test = "Test string"; + try { + Map<AttributedCharacterIterator.Attribute, String> hm = new HashMap<AttributedCharacterIterator.Attribute, String>(); + AttributedCharacterIterator.Attribute[] aci = new AttributedCharacterIterator.Attribute[3]; + aci[0] = new TestAttributedCharacterIteratorAttribute("att1"); + aci[1] = new TestAttributedCharacterIteratorAttribute("att2"); + aci[2] = new TestAttributedCharacterIteratorAttribute("att3"); + hm.put(aci[0], "value1"); + hm.put(aci[1], "value2"); + + AttributedString attrString = new AttributedString(test, hm); + AttributedCharacterIterator it = attrString.getIterator(aci); + + assertTrue("Incorrect iteration on AttributedString", it + .getAttribute(aci[0]).equals("value1")); + assertTrue("Incorrect iteration on AttributedString", it + .getAttribute(aci[1]).equals("value2")); + assertTrue("Incorrect iteration on AttributedString", it + .getAttribute(aci[2]) == null); + } catch (Exception e) { + fail("Unexpected exceptiption " + e.toString()); + } + } + + /** + * @tests java.text.AttributedString#getIterator(AttributedCharacterIterator.Attribute[], + * int, int) Test of method + * java.text.AttributedString#getIterator(AttributedCharacterIterator.Attribute[], + * int, int). + */ + public void test_getIterator$Ljava_text_AttributedCharacterIterator$AttributeII() { + String test = "Test string"; + try { + Map<AttributedCharacterIterator.Attribute, String> hm = new HashMap<AttributedCharacterIterator.Attribute, String>(); + AttributedCharacterIterator.Attribute[] aci = new AttributedCharacterIterator.Attribute[3]; + aci[0] = new TestAttributedCharacterIteratorAttribute("att1"); + aci[1] = new TestAttributedCharacterIteratorAttribute("att2"); + aci[2] = new TestAttributedCharacterIteratorAttribute("att3"); + hm.put(aci[0], "value1"); + hm.put(aci[1], "value2"); + + AttributedString attrString = new AttributedString(test); + attrString.addAttributes(hm, 2, 4); + AttributedCharacterIterator it = attrString.getIterator(aci, 1, 5); + + assertTrue("Incorrect iteration on AttributedString", it + .getAttribute(aci[0]) == null); + assertTrue("Incorrect iteration on AttributedString", it + .getAttribute(aci[1]) == null); + assertTrue("Incorrect iteration on AttributedString", it + .getAttribute(aci[2]) == null); + + it.next(); + + assertTrue("Incorrect iteration on AttributedString", it + .getAttribute(aci[0]).equals("value1")); + assertTrue("Incorrect iteration on AttributedString", it + .getAttribute(aci[1]).equals("value2")); + assertTrue("Incorrect iteration on AttributedString", it + .getAttribute(aci[2]) == null); + } catch (Exception e) { + fail("Unexpected exceptiption " + e.toString()); + } + } +} diff --git a/text/src/test/java/org/apache/harmony/text/tests/java/text/BidiTest.java b/text/src/test/java/org/apache/harmony/text/tests/java/text/BidiTest.java new file mode 100644 index 0000000..bbe3f1c --- /dev/null +++ b/text/src/test/java/org/apache/harmony/text/tests/java/text/BidiTest.java @@ -0,0 +1,994 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.text.tests.java.text; + +import java.text.AttributedString; +import java.text.Bidi; +import java.util.Arrays; + +import junit.framework.Test; +import junit.framework.TestCase; + +public class BidiTest extends TestCase { + + Bidi bd; + + public static void assertRunArrayEquals(int[][] expected, Bidi bidi) { + assertEquals("different length", expected.length, bidi.getRunCount()); + + FORRUN: for (int i = 0; i < bidi.getRunCount(); i++) { + int[] butWas = new int[] { bidi.getRunStart(i), + bidi.getRunLimit(i), bidi.getRunLevel(i) }; + + for (int j = 0; j < expected.length; j++) { + if (expected[j][0] == butWas[0] && expected[j][1] == butWas[1] + && expected[j][2] == butWas[2]) { + continue FORRUN; + } + } + fail("expected [" + i + "] " + " start: " + butWas[0] + " limit: " + + butWas[1] + " level: " + butWas[2]); + } + } + + public void testNullPointerConstructor() { + try { + bd = new Bidi(null, Bidi.DIRECTION_RIGHT_TO_LEFT); + fail("should throw IAE"); + } catch (IllegalArgumentException e) { + // expected + } + + try { + bd = new Bidi(null, 0, new byte[] { 0 }, 0, 0, + Bidi.DIRECTION_RIGHT_TO_LEFT); + fail("should throw IAE"); + } catch (IllegalArgumentException e) { + // expected + } + + try { + bd = new Bidi(null); + fail("should throw IAE"); + } catch (IllegalArgumentException e) { + } + + bd = new Bidi("a".toCharArray(), 0, null, 0, 1, + Bidi.DIRECTION_RIGHT_TO_LEFT); + } + + public void testBadLength() { + try { + bd = new Bidi("1".toCharArray(), 0, new byte[] { 0 }, 0, 20, + Bidi.DIRECTION_RIGHT_TO_LEFT); + fail("should throw IAE"); + } catch (IllegalArgumentException e) { + // expected + } + + try { + bd = new Bidi("1234567".toCharArray(), 0, new byte[] { 0 }, 0, 4, + Bidi.DIRECTION_RIGHT_TO_LEFT); + fail("should throw IAE"); + } catch (IllegalArgumentException e) { + // expected + } + + try { + bd = new Bidi("1234567".toCharArray(), 4, new byte[] { 0, 1, 2, 3, + 4 }, 0, 5, Bidi.DIRECTION_RIGHT_TO_LEFT); + fail("should throw IAE"); + } catch (IllegalArgumentException e) { + // expected + } + + try { + bd = new Bidi("1234567".toCharArray(), 0, new byte[] { 0, 1, 2, 3, + 4 }, 4, 5, Bidi.DIRECTION_RIGHT_TO_LEFT); + fail("should throw IAE"); + } catch (IllegalArgumentException e) { + // expected + } + + // regression for HARMONY-1031 + try { + bd = new Bidi(new char[] { 't', 't', 't' }, -1, + new byte[] { 2, 2 }, 1, 1, 1); + fail("should be IAE"); + } catch (IllegalArgumentException e) { + // expected + } + + try { + bd = new Bidi(new char[] { 't', 't', 't' }, 1, new byte[] { 2, 2 }, + -1, 1, 1); + fail("should be IAE"); + } catch (IllegalArgumentException e) { + // expected + } + + try { + bd = new Bidi(new char[] { 't', 't', 't' }, 1, new byte[] { 2, 2 }, + 1, -1, 1); + fail("should be IAE"); + } catch (IllegalArgumentException e) { + // expected + } + + try { + bd = new Bidi(new char[] {}, 5, new byte[] { 2, 2, 2, 2, 2, 2 }, 8, + Integer.MAX_VALUE, 5); + fail("should be IAE"); + } catch (IllegalArgumentException e) { + // expected + } + + try { + bd = new Bidi(null, 5, null, 8, Integer.MAX_VALUE, 5); + fail("should be IAE"); + } catch (IllegalArgumentException e) { + // expected + } + + bd = new Bidi(new char[] { 'o' }, 0, new byte[] { 2, 2 }, 2, 0, 2); + } + + public void testEmptyParagraph() { + bd = new Bidi("", Bidi.DIRECTION_DEFAULT_LEFT_TO_RIGHT); + assertTrue(bd.baseIsLeftToRight()); + assertEquals(0, bd.getBaseLevel()); + assertEquals(0, bd.getLength()); + assertEquals(0, bd.getLevelAt(0)); + assertEquals(0, bd.getLevelAt(1000)); + assertEquals(1, bd.getRunCount()); + assertRunArrayEquals(new int[][] { { 0, 0, 0 } }, bd); + assertTrue(bd.isLeftToRight()); + assertFalse(bd.isMixed()); + assertFalse(bd.isRightToLeft()); + + bd = new Bidi("", Bidi.DIRECTION_DEFAULT_RIGHT_TO_LEFT); + assertFalse(bd.baseIsLeftToRight()); + assertEquals(1, bd.getBaseLevel()); + assertEquals(0, bd.getLength()); + assertEquals(1, bd.getLevelAt(0)); + assertEquals(1, bd.getLevelAt(1000)); + assertEquals(1, bd.getRunCount()); + assertRunArrayEquals(new int[][] { { 0, 0, 1 }, }, bd); + assertFalse(bd.isLeftToRight()); + assertFalse(bd.isMixed()); + assertTrue(bd.isRightToLeft()); + + bd = new Bidi("", Bidi.DIRECTION_LEFT_TO_RIGHT); + assertTrue(bd.baseIsLeftToRight()); + assertEquals(0, bd.getBaseLevel()); + assertEquals(0, bd.getLength()); + assertEquals(0, bd.getLevelAt(0)); + assertEquals(0, bd.getLevelAt(1000)); + assertEquals(1, bd.getRunCount()); + assertRunArrayEquals(new int[][] { { 0, 0, 0 }, }, bd); + assertTrue(bd.isLeftToRight()); + assertFalse(bd.isMixed()); + assertFalse(bd.isRightToLeft()); + + bd = new Bidi("", Bidi.DIRECTION_RIGHT_TO_LEFT); + assertFalse(bd.baseIsLeftToRight()); + assertEquals(1, bd.getBaseLevel()); + assertEquals(0, bd.getLength()); + assertEquals(1, bd.getLevelAt(0)); + assertEquals(1, bd.getLevelAt(1000)); + assertEquals(1, bd.getRunCount()); + assertRunArrayEquals(new int[][] { { 0, 0, 1 }, }, bd); + assertFalse(bd.isLeftToRight()); + assertFalse(bd.isMixed()); + assertTrue(bd.isRightToLeft()); + } + + public void testSpaceParagraph() { + bd = new Bidi(" ", Bidi.DIRECTION_DEFAULT_LEFT_TO_RIGHT); + assertTrue(bd.baseIsLeftToRight()); + assertEquals(0, bd.getBaseLevel()); + assertEquals(1, bd.getLength()); + assertEquals(0, bd.getLevelAt(0)); + assertEquals(0, bd.getLevelAt(1000)); + assertEquals(1, bd.getRunCount()); + assertRunArrayEquals(new int[][] { { 0, 1, 0 }, }, bd); + assertTrue(bd.isLeftToRight()); + assertFalse(bd.isMixed()); + assertFalse(bd.isRightToLeft()); + + bd = new Bidi(" ", Bidi.DIRECTION_DEFAULT_RIGHT_TO_LEFT); + assertFalse(bd.baseIsLeftToRight()); + assertEquals(1, bd.getBaseLevel()); + assertEquals(1, bd.getLength()); + assertEquals(1, bd.getLevelAt(0)); + assertEquals(1, bd.getLevelAt(1000)); + assertEquals(1, bd.getRunCount()); + assertRunArrayEquals(new int[][] { { 0, 1, 1 }, }, bd); + assertFalse(bd.isLeftToRight()); + assertFalse(bd.isMixed()); + assertTrue(bd.isRightToLeft()); + + bd = new Bidi(" ", Bidi.DIRECTION_LEFT_TO_RIGHT); + assertTrue(bd.baseIsLeftToRight()); + assertEquals(0, bd.getBaseLevel()); + assertEquals(1, bd.getLength()); + assertEquals(0, bd.getLevelAt(0)); + assertEquals(0, bd.getLevelAt(1000)); + assertEquals(1, bd.getRunCount()); + assertRunArrayEquals(new int[][] { { 0, 1, 0 }, }, bd); + assertTrue(bd.isLeftToRight()); + assertFalse(bd.isMixed()); + assertFalse(bd.isRightToLeft()); + + bd = new Bidi(" ", Bidi.DIRECTION_RIGHT_TO_LEFT); + assertFalse(bd.baseIsLeftToRight()); + assertEquals(1, bd.getBaseLevel()); + assertEquals(1, bd.getLength()); + assertEquals(1, bd.getLevelAt(0)); + assertEquals(1, bd.getLevelAt(1000)); + assertEquals(1, bd.getRunCount()); + assertRunArrayEquals(new int[][] { { 0, 1, 1 }, }, bd); + assertFalse(bd.isLeftToRight()); + assertFalse(bd.isMixed()); + assertTrue(bd.isRightToLeft()); + } + + public void testSimpleParagraph() { + bd = new Bidi("t", Bidi.DIRECTION_DEFAULT_LEFT_TO_RIGHT); + assertTrue(bd.baseIsLeftToRight()); + assertEquals(0, bd.getBaseLevel()); + assertEquals(1, bd.getLength()); + assertEquals(0, bd.getLevelAt(0)); + assertEquals(0, bd.getLevelAt(1000)); + assertEquals(1, bd.getRunCount()); + assertRunArrayEquals(new int[][] { { 0, 1, 0 }, }, bd); + assertTrue(bd.isLeftToRight()); + assertFalse(bd.isMixed()); + assertFalse(bd.isRightToLeft()); + + bd = new Bidi("t", Bidi.DIRECTION_DEFAULT_RIGHT_TO_LEFT); + assertTrue(bd.baseIsLeftToRight()); + assertEquals(0, bd.getBaseLevel()); + assertEquals(1, bd.getLength()); + assertEquals(0, bd.getLevelAt(0)); + assertEquals(0, bd.getLevelAt(1000)); + assertEquals(1, bd.getRunCount()); + assertRunArrayEquals(new int[][] { { 0, 1, 0 }, }, bd); + assertTrue(bd.isLeftToRight()); + assertFalse(bd.isMixed()); + assertFalse(bd.isRightToLeft()); + + bd = new Bidi("t", Bidi.DIRECTION_LEFT_TO_RIGHT); + assertTrue(bd.baseIsLeftToRight()); + assertEquals(0, bd.getBaseLevel()); + assertEquals(1, bd.getLength()); + assertEquals(0, bd.getLevelAt(0)); + assertEquals(0, bd.getLevelAt(1000)); + assertEquals(1, bd.getRunCount()); + assertRunArrayEquals(new int[][] { { 0, 1, 0 }, }, bd); + assertTrue(bd.isLeftToRight()); + assertFalse(bd.isMixed()); + assertFalse(bd.isRightToLeft()); + } + + /** + * @tests java.text.Bidi#toString() Test of method java.text.Bidi#toString() + */ + public void testToString() { + try { + bd = new Bidi("bidi", 173); + assertNotNull("Bidi representation is null", bd.toString()); + } catch (Exception e) { + fail("Unexpected exception " + e.toString()); + } + } + + public void testBadFlags() { + bd = new Bidi("", 173); + assertTrue(bd.baseIsLeftToRight()); + assertEquals(0, bd.getBaseLevel()); + assertEquals(0, bd.getLength()); + assertEquals(0, bd.getLevelAt(0)); + assertEquals(0, bd.getLevelAt(1000)); + assertEquals(1, bd.getRunCount()); + assertRunArrayEquals(new int[][] { { 0, 0, 0 }, }, bd); + assertTrue(bd.isLeftToRight()); + assertFalse(bd.isMixed()); + assertFalse(bd.isRightToLeft()); + } + + public void testBadEmbeddings() { + try { + bd = new Bidi("".toCharArray(), 0, new byte[] {}, 0, 1, + Bidi.DIRECTION_RIGHT_TO_LEFT); + fail("should throw IAE"); + } catch (IllegalArgumentException e) { + // expected + } + } + + public void testOverrideEmbeddings() { + bd = new Bidi(new char[] { 's', 's', 's' }, 0, new byte[] { (byte) -7, + (byte) -2, (byte) -3 }, 0, 3, + Bidi.DIRECTION_DEFAULT_LEFT_TO_RIGHT); + assertTrue(bd.baseIsLeftToRight()); + assertEquals(0, bd.getBaseLevel()); + assertEquals(3, bd.getLength()); + assertEquals(7, bd.getLevelAt(0)); + assertEquals(2, bd.getLevelAt(1)); + assertEquals(3, bd.getLevelAt(2)); + assertEquals(0, bd.getLevelAt(1000)); + assertEquals(3, bd.getRunCount()); + assertRunArrayEquals(new int[][] { { 0, 1, 7 }, { 1, 2, 2 }, + { 2, 3, 3 }, }, bd); + assertFalse(bd.isLeftToRight()); + assertTrue(bd.isMixed()); + assertFalse(bd.isRightToLeft()); + + bd = new Bidi(new char[] { 's', 's', 's' }, 0, new byte[] { (byte) -1, + (byte) -2, (byte) -3 }, 0, 3, + Bidi.DIRECTION_DEFAULT_RIGHT_TO_LEFT); + assertTrue(bd.baseIsLeftToRight()); + assertEquals(0, bd.getBaseLevel()); + assertEquals(3, bd.getLength()); + assertEquals(1, bd.getLevelAt(0)); + assertEquals(2, bd.getLevelAt(1)); + assertEquals(3, bd.getLevelAt(2)); + assertEquals(0, bd.getLevelAt(1000)); + assertEquals(3, bd.getRunCount()); + assertRunArrayEquals(new int[][] { { 0, 1, 1 }, { 1, 2, 2 }, + { 2, 3, 3 }, }, bd); + assertFalse(bd.isLeftToRight()); + assertTrue(bd.isMixed()); + assertFalse(bd.isRightToLeft()); + + bd = new Bidi(new char[] { 's', 's', 's' }, 0, new byte[] { (byte) -1, + (byte) -2, (byte) -3 }, 0, 3, Bidi.DIRECTION_LEFT_TO_RIGHT); + assertTrue(bd.baseIsLeftToRight()); + assertEquals(0, bd.getBaseLevel()); + assertEquals(3, bd.getLength()); + assertEquals(1, bd.getLevelAt(0)); + assertEquals(2, bd.getLevelAt(1)); + assertEquals(3, bd.getLevelAt(2)); + assertEquals(0, bd.getLevelAt(1000)); + assertEquals(3, bd.getRunCount()); + assertRunArrayEquals(new int[][] { { 0, 1, 1 }, { 1, 2, 2 }, + { 2, 3, 3 }, }, bd); + assertFalse(bd.isLeftToRight()); + assertTrue(bd.isMixed()); + assertFalse(bd.isRightToLeft()); + + bd = new Bidi(new char[] { 's', 's', 's' }, 0, new byte[] { (byte) -1, + (byte) -2, (byte) -3 }, 0, 3, Bidi.DIRECTION_RIGHT_TO_LEFT); + assertFalse(bd.baseIsLeftToRight()); + assertEquals(1, bd.getBaseLevel()); + assertEquals(3, bd.getLength()); + assertEquals(1, bd.getLevelAt(0)); + assertEquals(2, bd.getLevelAt(1)); + assertEquals(3, bd.getLevelAt(2)); + assertEquals(1, bd.getLevelAt(1000)); + assertEquals(3, bd.getRunCount()); + assertRunArrayEquals(new int[][] { { 0, 1, 1 }, { 1, 2, 2 }, + { 2, 3, 3 }, }, bd); + assertFalse(bd.isLeftToRight()); + assertTrue(bd.isMixed()); + assertFalse(bd.isRightToLeft()); + } + + public void testDefaultEmbeddings() { + bd = new Bidi(new char[] { 's', 's', 's' }, 0, new byte[] { (byte) 0, + (byte) 0, (byte) 0 }, 0, 3, Bidi.DIRECTION_RIGHT_TO_LEFT); + assertFalse(bd.baseIsLeftToRight()); + assertEquals(1, bd.getBaseLevel()); + assertEquals(3, bd.getLength()); + assertEquals(2, bd.getLevelAt(0)); + assertEquals(2, bd.getLevelAt(1)); + assertEquals(2, bd.getLevelAt(2)); + assertEquals(1, bd.getLevelAt(1000)); + assertEquals(1, bd.getRunCount()); + assertRunArrayEquals(new int[][] { { 0, 3, 2 }, }, bd); + assertFalse(bd.isLeftToRight()); + assertTrue(bd.isMixed()); + assertFalse(bd.isRightToLeft()); + } + + public void testRelativeEmbeddings() { + bd = new Bidi(new char[] { 's', 's', 's' }, 0, new byte[] { (byte) 1, + (byte) 2, (byte) 3 }, 0, 3, Bidi.DIRECTION_RIGHT_TO_LEFT); + assertFalse(bd.baseIsLeftToRight()); + assertEquals(1, bd.getBaseLevel()); + assertEquals(3, bd.getLength()); + assertEquals(2, bd.getLevelAt(0)); + assertEquals(2, bd.getLevelAt(1)); + assertEquals(4, bd.getLevelAt(2)); + assertEquals(1, bd.getLevelAt(1000)); + assertEquals(2, bd.getRunCount()); + assertRunArrayEquals(new int[][] { { 0, 2, 2 }, { 2, 3, 4 }, }, bd); + assertFalse(bd.isLeftToRight()); + assertTrue(bd.isMixed()); + assertFalse(bd.isRightToLeft()); + } + + public void testSimpleHebrewParagraph() { + bd = new Bidi("\u05D0", Bidi.DIRECTION_DEFAULT_LEFT_TO_RIGHT); + assertFalse(bd.baseIsLeftToRight()); + assertEquals(1, bd.getBaseLevel()); + assertEquals(1, bd.getLength()); + assertEquals(1, bd.getLevelAt(0)); + assertEquals(1, bd.getLevelAt(1000)); + assertEquals(1, bd.getRunCount()); + assertRunArrayEquals(new int[][] { { 0, 1, 1 }, }, bd); + assertFalse(bd.isLeftToRight()); + assertFalse(bd.isMixed()); + assertTrue(bd.isRightToLeft()); + + bd = new Bidi("\u05D0", Bidi.DIRECTION_DEFAULT_RIGHT_TO_LEFT); + assertFalse(bd.baseIsLeftToRight()); + assertEquals(1, bd.getBaseLevel()); + assertEquals(1, bd.getLength()); + assertEquals(1, bd.getLevelAt(0)); + assertEquals(1, bd.getLevelAt(1000)); + assertEquals(1, bd.getRunCount()); + assertRunArrayEquals(new int[][] { { 0, 1, 1 }, }, bd); + assertFalse(bd.isLeftToRight()); + assertFalse(bd.isMixed()); + assertTrue(bd.isRightToLeft()); + + bd = new Bidi("\u05D0", Bidi.DIRECTION_RIGHT_TO_LEFT); + assertFalse(bd.baseIsLeftToRight()); + assertEquals(1, bd.getBaseLevel()); + assertEquals(1, bd.getLength()); + assertEquals(1, bd.getLevelAt(0)); + assertEquals(1, bd.getLevelAt(1000)); + assertEquals(1, bd.getRunCount()); + assertRunArrayEquals(new int[][] { { 0, 1, 1 }, }, bd); + assertFalse(bd.isLeftToRight()); + assertFalse(bd.isMixed()); + assertTrue(bd.isRightToLeft()); + } + + public void testSimpleBidiParagraph_1() { + bd = new Bidi("\u05D0a", Bidi.DIRECTION_DEFAULT_LEFT_TO_RIGHT); + assertFalse(bd.baseIsLeftToRight()); + assertEquals(1, bd.getBaseLevel()); + assertEquals(2, bd.getLength()); + assertEquals(1, bd.getLevelAt(0)); + assertEquals(2, bd.getLevelAt(1)); + assertEquals(1, bd.getLevelAt(1000)); + assertEquals(2, bd.getRunCount()); + assertRunArrayEquals(new int[][] { { 0, 1, 1 }, { 1, 2, 2 }, }, bd); + assertFalse(bd.isLeftToRight()); + assertTrue(bd.isMixed()); + assertFalse(bd.isRightToLeft()); + + bd = new Bidi("\u05D0a", Bidi.DIRECTION_DEFAULT_RIGHT_TO_LEFT); + assertFalse(bd.baseIsLeftToRight()); + assertEquals(1, bd.getBaseLevel()); + assertEquals(2, bd.getLength()); + assertEquals(1, bd.getLevelAt(0)); + assertEquals(2, bd.getLevelAt(1)); + assertEquals(1, bd.getLevelAt(1000)); + assertEquals(2, bd.getRunCount()); + assertRunArrayEquals(new int[][] { { 0, 1, 1 }, { 1, 2, 2 }, }, bd); + assertFalse(bd.isLeftToRight()); + assertTrue(bd.isMixed()); + assertFalse(bd.isRightToLeft()); + + bd = new Bidi("\u05D0a", Bidi.DIRECTION_LEFT_TO_RIGHT); + assertTrue(bd.baseIsLeftToRight()); + assertEquals(0, bd.getBaseLevel()); + assertEquals(2, bd.getLength()); + assertEquals(1, bd.getLevelAt(0)); + assertEquals(0, bd.getLevelAt(1)); + assertEquals(0, bd.getLevelAt(1000)); + assertEquals(2, bd.getRunCount()); + assertRunArrayEquals(new int[][] { { 0, 1, 1 }, { 1, 2, 0 }, }, bd); + assertFalse(bd.isLeftToRight()); + assertTrue(bd.isMixed()); + assertFalse(bd.isRightToLeft()); + + bd = new Bidi("\u05D0a", Bidi.DIRECTION_RIGHT_TO_LEFT); + assertFalse(bd.baseIsLeftToRight()); + assertEquals(1, bd.getBaseLevel()); + assertEquals(2, bd.getLength()); + assertEquals(1, bd.getLevelAt(0)); + assertEquals(2, bd.getLevelAt(1)); + assertEquals(1, bd.getLevelAt(1000)); + assertEquals(2, bd.getRunCount()); + assertRunArrayEquals(new int[][] { { 0, 1, 1 }, { 1, 2, 2 }, }, bd); + assertFalse(bd.isLeftToRight()); + assertTrue(bd.isMixed()); + assertFalse(bd.isRightToLeft()); + } + + public void testSimpleBidiParagraph_2() { + bd = new Bidi("a\u05D0", Bidi.DIRECTION_DEFAULT_LEFT_TO_RIGHT); + assertTrue(bd.baseIsLeftToRight()); + assertEquals(0, bd.getBaseLevel()); + assertEquals(2, bd.getLength()); + assertEquals(0, bd.getLevelAt(0)); + assertEquals(1, bd.getLevelAt(1)); + assertEquals(0, bd.getLevelAt(1000)); + assertEquals(2, bd.getRunCount()); + assertRunArrayEquals(new int[][] { { 0, 1, 0 }, { 1, 2, 1 }, }, bd); + assertFalse(bd.isLeftToRight()); + assertTrue(bd.isMixed()); + assertFalse(bd.isRightToLeft()); + + bd = new Bidi("a\u05D0", Bidi.DIRECTION_DEFAULT_RIGHT_TO_LEFT); + assertTrue(bd.baseIsLeftToRight()); + assertEquals(0, bd.getBaseLevel()); + assertEquals(2, bd.getLength()); + assertEquals(0, bd.getLevelAt(0)); + assertEquals(1, bd.getLevelAt(1)); + assertEquals(0, bd.getLevelAt(1000)); + assertEquals(2, bd.getRunCount()); + assertRunArrayEquals(new int[][] { { 0, 1, 0 }, { 1, 2, 1 }, }, bd); + assertFalse(bd.isLeftToRight()); + assertTrue(bd.isMixed()); + assertFalse(bd.isRightToLeft()); + + bd = new Bidi("a\u05D0", Bidi.DIRECTION_LEFT_TO_RIGHT); + assertTrue(bd.baseIsLeftToRight()); + assertEquals(0, bd.getBaseLevel()); + assertEquals(2, bd.getLength()); + assertEquals(0, bd.getLevelAt(0)); + assertEquals(1, bd.getLevelAt(1)); + assertEquals(0, bd.getLevelAt(1000)); + assertEquals(2, bd.getRunCount()); + assertRunArrayEquals(new int[][] { { 0, 1, 0 }, { 1, 2, 1 }, }, bd); + assertFalse(bd.isLeftToRight()); + assertTrue(bd.isMixed()); + assertFalse(bd.isRightToLeft()); + + bd = new Bidi("a\u05D0", Bidi.DIRECTION_RIGHT_TO_LEFT); + assertFalse(bd.baseIsLeftToRight()); + assertEquals(1, bd.getBaseLevel()); + assertEquals(2, bd.getLength()); + assertEquals(2, bd.getLevelAt(0)); + assertEquals(1, bd.getLevelAt(1)); + assertEquals(1, bd.getLevelAt(1000)); + assertEquals(2, bd.getRunCount()); + assertRunArrayEquals(new int[][] { { 0, 1, 2 }, { 1, 2, 1 }, }, bd); + assertFalse(bd.isLeftToRight()); + assertTrue(bd.isMixed()); + assertFalse(bd.isRightToLeft()); + } + + /* + * spec reads: public static final int DIRECTION_RIGHT_TO_LEFT Constant + * indicating base direction is right-to-left. according to that, the method + * baseIsLeftToRight() here should return false. however, RI doesn't act so. + */ + public void testRIBug_1() { + bd = new Bidi("t", Bidi.DIRECTION_RIGHT_TO_LEFT); + assertFalse(bd.baseIsLeftToRight()); + // the base level it the essential cause + assertEquals(1, bd.getBaseLevel()); + assertEquals(1, bd.getLength()); + assertEquals(2, bd.getLevelAt(0)); + assertEquals(1, bd.getLevelAt(1000)); + assertEquals(1, bd.getRunCount()); + assertRunArrayEquals(new int[][] { { 0, 1, 2 }, }, bd); + assertFalse(bd.isLeftToRight()); + assertTrue(bd.isMixed()); + assertFalse(bd.isRightToLeft()); + } + + // this is essentially the same bug as Bug_1 + public void testRIBug_2() { + bd = new Bidi("\u05D0", Bidi.DIRECTION_LEFT_TO_RIGHT); + assertTrue(bd.baseIsLeftToRight()); + assertEquals(0, bd.getBaseLevel()); + assertEquals(1, bd.getLength()); + assertEquals(1, bd.getLevelAt(0)); + assertEquals(0, bd.getLevelAt(1000)); + assertEquals(1, bd.getRunCount()); + assertRunArrayEquals(new int[][] { { 0, 1, 1 }, }, bd); + assertFalse(bd.isLeftToRight()); + assertTrue(bd.isMixed()); + assertFalse(bd.isRightToLeft()); + } + + public void testComplicatedBidi() { + bd = new Bidi("a\u05D0a\"a\u05D0\"\u05D0a", + Bidi.DIRECTION_RIGHT_TO_LEFT); + assertFalse(bd.baseIsLeftToRight()); + assertEquals(1, bd.getBaseLevel()); + assertEquals(9, bd.getLength()); + assertEquals(2, bd.getLevelAt(0)); + assertEquals(1, bd.getLevelAt(1)); + assertEquals(2, bd.getLevelAt(2)); + assertEquals(2, bd.getLevelAt(3)); + assertEquals(2, bd.getLevelAt(4)); + assertEquals(1, bd.getLevelAt(5)); + assertEquals(1, bd.getLevelAt(6)); + assertEquals(1, bd.getLevelAt(7)); + assertEquals(2, bd.getLevelAt(8)); + assertEquals(1, bd.getLevelAt(1000)); + assertEquals(5, bd.getRunCount()); + assertRunArrayEquals(new int[][] { { 0, 1, 2 }, { 1, 2, 1 }, + { 2, 5, 2 }, { 5, 8, 1 }, { 8, 9, 2 }, }, bd); + assertFalse(bd.isLeftToRight()); + assertTrue(bd.isMixed()); + assertFalse(bd.isRightToLeft()); + } + + public void testComplicatedOverrideBidi() { + bd = new Bidi("a\u05D0a\"a\u05D0\"\u05D0a".toCharArray(), 0, + new byte[] { 0, 0, 0, -3, -3, 2, 2, 0, 3 }, 0, 9, + Bidi.DIRECTION_RIGHT_TO_LEFT); + assertFalse(bd.baseIsLeftToRight()); + assertEquals(1, bd.getBaseLevel()); + assertEquals(9, bd.getLength()); + assertEquals(2, bd.getLevelAt(0)); + assertEquals(1, bd.getLevelAt(1)); + assertEquals(2, bd.getLevelAt(2)); + assertEquals(3, bd.getLevelAt(3)); + assertEquals(3, bd.getLevelAt(4)); + assertEquals(3, bd.getLevelAt(5)); + assertEquals(2, bd.getLevelAt(6)); + assertEquals(1, bd.getLevelAt(7)); + assertEquals(4, bd.getLevelAt(8)); + assertEquals(1, bd.getLevelAt(1000)); + assertEquals(7, bd.getRunCount()); + assertRunArrayEquals(new int[][] { { 0, 1, 2 }, { 1, 2, 1 }, + { 2, 3, 2 }, { 3, 6, 3 }, { 6, 7, 2 }, { 7, 8, 1 }, + { 8, 9, 4 }, }, bd); + assertFalse(bd.isLeftToRight()); + assertTrue(bd.isMixed()); + assertFalse(bd.isRightToLeft()); + } + + public void testRequiresBidi() { + try { + Bidi.requiresBidi(null, 0, 0); + fail("should throw NullPointerException"); + } catch (NullPointerException e) { + // expected + } + + try { + assertFalse(Bidi.requiresBidi(null, 0, 1)); + fail("should throw NPE"); + } catch (NullPointerException e) { + // expected + } + try { + assertFalse(Bidi.requiresBidi("".toCharArray(), 0, 1)); + fail("should throw IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // expected + } + try { + assertFalse(Bidi.requiresBidi("aaa".toCharArray(), -1, 1)); + fail("should throw IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // expected + } + try { + assertFalse(Bidi.requiresBidi("aaa".toCharArray(), 1, -1)); + fail("should throw IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // expected + } + try { + assertFalse(Bidi.requiresBidi("\u05D0".toCharArray(), 1, -1)); + fail("should throw IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // expected + } + try { + assertFalse(Bidi.requiresBidi("aaa".toCharArray(), 1, 0)); + fail("should throw IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // expected + } + try { + assertFalse(Bidi.requiresBidi("aaa".toCharArray(), 7, 7)); + fail("should throw IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // expected + } + try { + assertFalse(Bidi.requiresBidi("aaa".toCharArray(), 1, + Integer.MAX_VALUE)); + fail("should throw IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // expected + } + try { + assertFalse(Bidi.requiresBidi("aaa".toCharArray(), + Integer.MAX_VALUE, 1)); + fail("should throw IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // expected + } + + assertFalse(Bidi.requiresBidi("".toCharArray(), 0, 0)); + assertFalse(Bidi.requiresBidi("aaa".toCharArray(), 1, 1)); + assertFalse(Bidi.requiresBidi("aaa".toCharArray(), 0, 2)); + assertFalse(Bidi.requiresBidi("\u05D0".toCharArray(), 1, 1)); + assertTrue(Bidi.requiresBidi("\u05D0".toCharArray(), 0, 1)); + assertFalse(Bidi.requiresBidi("aa\u05D0a".toCharArray(), 0, 2)); + assertTrue(Bidi.requiresBidi("aa\u05D0a".toCharArray(), 1, 3)); + } + + public void testHebrewOverrideEmbeddings() { + bd = new Bidi(new char[] { '\u05D0', '\u05D0', '\u05D0' }, 0, + new byte[] { (byte) -1, (byte) -2, (byte) -3 }, 0, 3, + Bidi.DIRECTION_DEFAULT_LEFT_TO_RIGHT); + assertFalse(bd.baseIsLeftToRight()); + assertEquals(1, bd.getBaseLevel()); + assertEquals(3, bd.getLength()); + assertEquals(1, bd.getLevelAt(0)); + assertEquals(2, bd.getLevelAt(1)); + assertEquals(3, bd.getLevelAt(2)); + assertEquals(1, bd.getLevelAt(1000)); + assertEquals(3, bd.getRunCount()); + assertRunArrayEquals(new int[][] { { 0, 1, 1 }, { 1, 2, 2 }, + { 2, 3, 3 }, }, bd); + assertFalse(bd.isLeftToRight()); + assertTrue(bd.isMixed()); + assertFalse(bd.isRightToLeft()); + + bd = new Bidi(new char[] { '\u05D0', '\u05D0', '\u05D0' }, 0, + new byte[] { (byte) -1, (byte) -2, (byte) -3 }, 0, 3, + Bidi.DIRECTION_DEFAULT_RIGHT_TO_LEFT); + assertFalse(bd.baseIsLeftToRight()); + assertEquals(1, bd.getBaseLevel()); + assertEquals(3, bd.getLength()); + assertEquals(1, bd.getLevelAt(0)); + assertEquals(2, bd.getLevelAt(1)); + assertEquals(3, bd.getLevelAt(2)); + assertEquals(1, bd.getLevelAt(1000)); + assertEquals(3, bd.getRunCount()); + assertRunArrayEquals(new int[][] { { 0, 1, 1 }, { 1, 2, 2 }, + { 2, 3, 3 }, }, bd); + assertFalse(bd.isLeftToRight()); + assertTrue(bd.isMixed()); + assertFalse(bd.isRightToLeft()); + + bd = new Bidi(new char[] { '\u05D0', '\u05D0', '\u05D0' }, 0, + new byte[] { (byte) -1, (byte) -2, (byte) -3 }, 0, 3, + Bidi.DIRECTION_LEFT_TO_RIGHT); + assertTrue(bd.baseIsLeftToRight()); + assertEquals(0, bd.getBaseLevel()); + assertEquals(3, bd.getLength()); + assertEquals(1, bd.getLevelAt(0)); + assertEquals(2, bd.getLevelAt(1)); + assertEquals(3, bd.getLevelAt(2)); + assertEquals(0, bd.getLevelAt(1000)); + assertEquals(3, bd.getRunCount()); + assertRunArrayEquals(new int[][] { { 0, 1, 1 }, { 1, 2, 2 }, + { 2, 3, 3 }, }, bd); + assertFalse(bd.isLeftToRight()); + assertTrue(bd.isMixed()); + assertFalse(bd.isRightToLeft()); + + bd = new Bidi(new char[] { '\u05D0', '\u05D0', '\u05D0' }, 0, + new byte[] { (byte) -1, (byte) -2, (byte) -3 }, 0, 3, + Bidi.DIRECTION_RIGHT_TO_LEFT); + assertFalse(bd.baseIsLeftToRight()); + assertEquals(1, bd.getBaseLevel()); + assertEquals(3, bd.getLength()); + assertEquals(1, bd.getLevelAt(0)); + assertEquals(2, bd.getLevelAt(1)); + assertEquals(3, bd.getLevelAt(2)); + assertEquals(1, bd.getLevelAt(1000)); + assertEquals(3, bd.getRunCount()); + assertRunArrayEquals(new int[][] { { 0, 1, 1 }, { 1, 2, 2 }, + { 2, 3, 3 }, }, bd); + assertFalse(bd.isLeftToRight()); + assertTrue(bd.isMixed()); + assertFalse(bd.isRightToLeft()); + } + + public void testCreateLineBidi() { + bd = new Bidi("a\u05D0a\na\u05D0\"\u05D0a".toCharArray(), 0, + new byte[] { 0, 0, 0, -3, -3, 2, 2, 0, 3 }, 0, 9, + Bidi.DIRECTION_RIGHT_TO_LEFT); + Bidi line = bd.createLineBidi(2, 7); + assertFalse(line.baseIsLeftToRight()); + assertEquals(1, line.getBaseLevel()); + assertEquals(5, line.getLength()); + assertEquals(2, line.getLevelAt(0)); + assertEquals(1, line.getLevelAt(1)); + assertEquals(3, line.getLevelAt(2)); + assertEquals(3, line.getLevelAt(3)); + assertEquals(2, line.getLevelAt(4)); + assertEquals(1, line.getLevelAt(1000)); + assertEquals(4, line.getRunCount()); + assertRunArrayEquals(new int[][] { { 0, 1, 2 }, { 1, 2, 1 }, + { 2, 4, 3 }, { 4, 5, 2 }, }, line); + assertFalse(line.isLeftToRight()); + assertTrue(line.isMixed()); + assertFalse(line.isRightToLeft()); + } + + public void testCreateLineBidiInvalid() { + // regression for HARMONY-1050 + Bidi bidi = new Bidi("str", 1); + try { + bidi.createLineBidi(-1, 1); + fail("Expected IAE"); + } catch (IllegalArgumentException e) { + // Expected + } + + try { + bidi.createLineBidi(1, -1); + fail("Expected IAE"); + } catch (IllegalArgumentException e) { + // Expected + } + + try { + bidi.createLineBidi(-1, -1); + fail("Expected IAE"); + } catch (IllegalArgumentException e) { + // Expected + } + + try { + bidi.createLineBidi(2, 1); + fail("Expected IAE"); + } catch (IllegalArgumentException e) { + // Expected + } + + bidi.createLineBidi(2, 2); + + try { + bidi.createLineBidi(2, 4); + fail("Expected IAE"); + } catch (IllegalArgumentException e) { + // Expected + } + } + + public void testIncompatibleLineAlgorithm() { + // ICU treat a new line as in the same run, however RI does not + bd = new Bidi("aaaaa".toCharArray(), 0, + new byte[] { -2, -1, -3, -3, -2 }, 0, 5, + Bidi.DIRECTION_RIGHT_TO_LEFT); + Bidi line = bd.createLineBidi(1, 4); + assertFalse(line.baseIsLeftToRight()); + assertEquals(1, line.getBaseLevel()); + assertEquals(3, line.getLength()); + assertEquals(1, line.getLevelAt(0)); + assertEquals(1, line.getLevelAt(1)); + assertEquals(1, line.getLevelAt(2)); + assertEquals(1, line.getLevelAt(1000)); + assertEquals(1, line.getRunCount()); + assertRunArrayEquals(new int[][] { { 0, 3, 1 }, }, line); + assertFalse(line.isLeftToRight()); + assertFalse(line.isMixed()); + assertTrue(line.isRightToLeft()); + } + + public void testReorderVisually() { + String[] init = new String[] { "a", "b", "c", "d" }; + String[] s = new String[4]; + + System.arraycopy(init, 0, s, 0, s.length); + Bidi.reorderVisually(new byte[] { 2, 1, 3, 0 }, 0, s, 0, 4); + assertEquals("[c, b, a, d]", Arrays.asList(s).toString()); + + System.arraycopy(init, 0, s, 0, s.length); + Bidi.reorderVisually(new byte[] { 1, 3 }, 0, s, 1, 2); + assertEquals("[a, c, b, d]", Arrays.asList(s).toString()); + + System.arraycopy(init, 0, s, 0, s.length); + Bidi.reorderVisually(new byte[] { 2, 1, 3, 0 }, 1, s, 1, 2); + assertEquals("[a, c, b, d]", Arrays.asList(s).toString()); + + System.arraycopy(init, 0, s, 0, s.length); + Bidi.reorderVisually(new byte[] { 2, 1, 2, 1 }, 1, s, 0, 3); + assertEquals("[c, b, a, d]", Arrays.asList(s).toString()); + + System.arraycopy(init, 0, s, 0, s.length); + Bidi.reorderVisually(new byte[] { 2, 1, 0, 1 }, 1, s, 0, 3); + assertEquals("[a, b, c, d]", Arrays.asList(s).toString()); + } + + public void testBadReorderVisually() { + String[] s = new String[] { "a", "b", "c", "d" }; + + try { + Bidi.reorderVisually(new byte[] { 2, 1, 3, 0 }, 0, s, 0, 5); + fail("should throw IAE"); + } catch (IllegalArgumentException e) { + // expected + } + + try { + Bidi.reorderVisually(new byte[] { 2, 1, 3, 0 }, 0, s, -1, 1); + fail("should throw IAE"); + } catch (IllegalArgumentException e) { + // expected + } + + try { + Bidi.reorderVisually(new byte[] { 2, 1, 3, 0 }, -1, s, 0, 1); + fail("should throw IAE"); + } catch (IllegalArgumentException e) { + // expected + } + + try { + Bidi.reorderVisually(null, 0, s, 0, 1); + fail("should throw NPE"); + } catch (NullPointerException e) { + // expected + } + + try { + Bidi.reorderVisually(new byte[] { 2, 1, 3, 0 }, 0, null, 0, 1); + fail("should throw NPE"); + } catch (NullPointerException e) { + // expected + } + + try { + Bidi.reorderVisually(new byte[] { 2, 1, 3, 0 }, 1, s, 0, -1); + fail("should throw IAE"); + } catch (IllegalArgumentException e) { + // expected + } + + } + + public void testGetRuns() { + // Regression test for Harmony-1028 + + String LTR = "\u0061\u0062"; + String RTL = "\u05DC\u05DD"; + String newLine = "\n"; + String defText = LTR + newLine + RTL + LTR + RTL; + + int[][] expectedRuns = { { 0, 3 }, { 3, 5 }, { 5, 7 }, { 7, 9 }, }; + + Bidi bi = new Bidi(defText, 0); + final int count = bi.getRunCount(); + for (int i = 0; i < count; i++) { + assertEquals(expectedRuns[i][0], bi.getRunStart(i)); + assertEquals(expectedRuns[i][1], bi.getRunLimit(i)); + } + } + + public void testGetRunLimit() { + bd = new Bidi("text", Bidi.DIRECTION_LEFT_TO_RIGHT); + try { + assertTrue(4 == bd.getRunLimit(-1)); + } catch (Exception e) { + fail("Unexpected exception: " + e); + } + } + + public void testBidiConstructor_Iterator() { + AttributedString paragraph = new AttributedString("text"); + bd = new Bidi(paragraph.getIterator()); + try { + assertTrue(4 == bd.getRunLimit(1)); + } catch (Exception e) { + fail("Unexpected exception: " + e); + } + } + +} diff --git a/text/src/test/java/org/apache/harmony/text/tests/java/text/BreakIteratorTest.java b/text/src/test/java/org/apache/harmony/text/tests/java/text/BreakIteratorTest.java new file mode 100644 index 0000000..29f2b29 --- /dev/null +++ b/text/src/test/java/org/apache/harmony/text/tests/java/text/BreakIteratorTest.java @@ -0,0 +1,597 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package org.apache.harmony.text.tests.java.text; + +import java.text.BreakIterator; +import java.text.CharacterIterator; +import java.util.Collection; +import java.util.Collections; +import java.text.StringCharacterIterator; +import java.util.Locale; + +import junit.framework.Test; +import junit.framework.TestCase; + +public class BreakIteratorTest extends TestCase { + + private static final String TEXT = "a\u0308abc def, gh-12i?jkl.mno?"; + + BreakIterator iterator; + + /* + * @see TestCase#setUp() + */ + protected void setUp() throws Exception { + super.setUp(); + iterator = BreakIterator.getCharacterInstance(Locale.US); + } + + public void testConsts() { + assertEquals(-1, BreakIterator.DONE); + } + + public void testCache() { + BreakIterator newOne = BreakIterator.getCharacterInstance(Locale.US); + assertNotSame(newOne, iterator); + assertEquals(newOne, iterator); + + newOne = BreakIterator.getCharacterInstance(); + assertEquals(newOne, iterator); + + newOne = BreakIterator.getCharacterInstance(Locale.CHINA); + assertEquals(newOne, iterator); + + BreakIterator wordIterator = BreakIterator.getWordInstance(); + assertFalse(wordIterator.equals(iterator)); + + BreakIterator lineIterator = BreakIterator.getLineInstance(); + assertFalse(lineIterator.equals(iterator)); + + BreakIterator senteIterator = BreakIterator.getSentenceInstance(); + assertFalse(senteIterator.equals(iterator)); + } + + public void testClone() { + BreakIterator cloned = (BreakIterator) iterator.clone(); + assertNotSame(cloned, iterator); + assertEquals(cloned, iterator); + } + + public void testCurrent() { + assertEquals(0, iterator.current()); + iterator.setText(TEXT); + assertEquals(iterator.first(), iterator.current()); + } + + /** + * @tests java.text.BreakIterator#BreakIterator() Test of method + * java.text.BreakIterator#BreakIterator(). + */ + public void testConstructor() { + try { + new MockBreakIterator(); + } catch (Exception e) { + fail("Unexpected exception " + e.toString()); + } + } + + public void testFirst() { + assertEquals(0, iterator.first()); + iterator.setText(TEXT); + assertEquals(0, iterator.first()); + } + + public void testFollowing() { + try { + iterator.following(1); + fail("should throw illegal argument exception"); + } catch (IllegalArgumentException e) { + } + iterator.setText(TEXT); + assertEquals(2, iterator.following(1)); + try { + assertEquals(0, iterator.following(-1)); + fail("should throw illegal argument exception"); + } catch (IllegalArgumentException e) { + } + try { + iterator.following(TEXT.length()); + fail("should throw illegal argument exception"); + } catch (IllegalArgumentException e) { + } + } + + public void testIsBoundary() { + try { + iterator.isBoundary(2); + fail("should throw illegal argument exception"); + } catch (IllegalArgumentException e) { + } + iterator.setText(TEXT); + assertTrue(iterator.isBoundary(2)); + assertFalse(iterator.isBoundary(1)); + assertTrue(iterator.isBoundary(0)); + try { + iterator.isBoundary(-1); + fail("should throw illegal argument exception"); + } catch (IllegalArgumentException e) { + } + try { + iterator.isBoundary(TEXT.length()); + fail("should throw illegal argument exception"); + } catch (IllegalArgumentException e) { + } + } + + public void testLast() { + assertEquals(0, iterator.last()); + iterator.setText(TEXT); + assertEquals(TEXT.length(), iterator.last()); + } + + /* + * Class under test for int next(int) + */ + public void testNextint() { + assertEquals(BreakIterator.DONE, iterator.next(3)); + iterator.setText(TEXT); + assertEquals(4, iterator.next(3)); + assertEquals(24, iterator.next(20)); + assertEquals(23, iterator.next(-1)); + assertEquals(-1, iterator.next(TEXT.length())); + } + + public void testPreceding() { + try { + iterator.preceding(2); + fail("should throw illegal argument exception"); + } catch (IllegalArgumentException e) { + } + iterator.setText(TEXT); + assertEquals(0, iterator.preceding(2)); + assertEquals(2, iterator.preceding(3)); + assertEquals(16, iterator.preceding(17)); + assertEquals(17, iterator.preceding(18)); + assertEquals(18, iterator.preceding(19)); + try { + iterator.preceding(-1); + fail("should throw illegal argument exception"); + } catch (IllegalArgumentException e) { + } + try { + iterator.preceding(TEXT.length()); + fail("should throw illegal argument exception"); + } catch (IllegalArgumentException e) { + } + } + + public void testPrevious() { + assertEquals(-1, iterator.previous()); + iterator.setText(TEXT); + assertEquals(-1, iterator.previous()); + iterator.last(); + assertEquals(TEXT.length() - 1, iterator.previous()); + } + + /** + * @tests java.text.BreakIterator#getAvailableLocales(). Test of method + * java.text.BreakIterator#getAvailableLocales(). + */ + public void testGetAvailableLocales() { + try { + Locale[] locales = BreakIterator.getAvailableLocales(); + assertTrue("Array available locales is null", locales != null); + assertTrue("Array available locales is 0-length", + (locales != null && locales.length != 0)); + boolean found = false; + for (Locale l : locales) { + if (l.equals(Locale.US)) { + // expected + found = true; + } + } + assertTrue("At least locale " + Locale.US + " must be presented", + found); + } catch (Exception e) { + fail("Unexpected exception " + e.toString()); + } + } + + /* + * Class under test for BreakIterator getCharacterInstance() + */ + public void testGetCharacterInstance() { + BreakIterator.getCharacterInstance(); + } + + /* + * Class under test for BreakIterator getCharacterInstance(Locale) + */ + public void testGetCharacterInstanceLocale() { + BreakIterator it = BreakIterator.getCharacterInstance(Locale.US); + BreakIterator it2 = BreakIterator.getCharacterInstance(Locale.CHINA); + assertEquals(it, it2); + } + + /* + * Class under test for BreakIterator getLineInstance() + */ + public void testGetLineInstance() { + BreakIterator it = BreakIterator.getLineInstance(); + assertNotNull(it); + } + + /* + * @tests java.text.BreakIterator#getLineInstance(Locale) Class under test + * for BreakIterator getLineInstance(Locale) + */ + public void testGetLineInstanceLocale() { + try { + BreakIterator it1 = BreakIterator + .getLineInstance(Locale.CANADA_FRENCH); + assertTrue("Incorrect BreakIterator", it1 != BreakIterator + .getLineInstance()); + BreakIterator it2 = BreakIterator.getLineInstance(new Locale( + "bad locale")); + assertTrue("Incorrect BreakIterator", it2 != BreakIterator + .getLineInstance()); + } catch (Exception e) { + fail("Unexpected exception " + e.toString()); + } + } + + /* + * Class under test for BreakIterator getSentenceInstance() + */ + public void testGetSentenceInstance() { + BreakIterator it = BreakIterator.getSentenceInstance(); + assertNotNull(it); + } + + /* + * Class under test for BreakIterator getSentenceInstance(Locale) + */ + public void testGetSentenceInstanceLocale() { + BreakIterator it = BreakIterator.getSentenceInstance(Locale.US); + assertNotNull(it); + } + + public void testGetText() { + assertEquals(new StringCharacterIterator(""), iterator.getText()); + iterator.setText(TEXT); + assertEquals(new StringCharacterIterator(TEXT), iterator.getText()); + } + + /* + * Class under test for BreakIterator getWordInstance() + */ + public void testGetWordInstance() { + BreakIterator it = BreakIterator.getWordInstance(); + assertNotNull(it); + } + + /* + * @tests java.text.BreakIterator#getWordInstance(Locale) Class under test + * for BreakIterator getWordInstance(Locale) + */ + public void testGetWordInstanceLocale() { + try { + BreakIterator it1 = BreakIterator + .getWordInstance(Locale.CANADA_FRENCH); + assertTrue("Incorrect BreakIterator", it1 != BreakIterator + .getWordInstance()); + BreakIterator it2 = BreakIterator.getWordInstance(new Locale( + "bad locale")); + assertTrue("Incorrect BreakIterator", it2 != BreakIterator + .getWordInstance()); + } catch (Exception e) { + fail("Unexpected exception " + e.toString()); + } + } + + /* + * Class under test for void setText(CharacterIterator) + */ + public void testSetTextCharacterIterator() { + try { + iterator.setText((CharacterIterator) null); + fail(); + } catch (NullPointerException e) { + } + CharacterIterator it = new StringCharacterIterator("abc"); + iterator.setText(it); + assertSame(it, iterator.getText()); + } + + /* + * Class under test for void setText(String) + */ + public void testSetTextString() { + try { + iterator.setText((String) null); + fail(); + } catch (NullPointerException e) { + } + iterator.setText("abc"); + CharacterIterator it = new StringCharacterIterator("abc"); + assertEquals(it, iterator.getText()); + } + + public void test_next() { + // Regression test for HARMONY-30 + BreakIterator bi = BreakIterator.getWordInstance(Locale.US); + bi.setText("This is the test, WordInstance"); + int n = bi.first(); + n = bi.next(); + assertEquals("Assert 0: next() returns incorrect value ", 4, n); + + assertEquals(BreakIterator.DONE, iterator.next()); + iterator.setText(TEXT); + assertEquals(2, iterator.next()); + } + + /** + * @tests java.text.BreakIterator.getShort(byte[], int) + * + */ + public void test_getShort() { + try { + MockBreakIterator.publicGetShort(null, 0); + fail("should throw NPE."); + } catch (NullPointerException e) { + } + try { + MockBreakIterator.publicGetShort(null, -1); + fail("should throw NPE."); + } catch (NullPointerException e) { + } + try { + MockBreakIterator.publicGetShort(new byte[] { 0, 0, 0, 1, 1 }, -1); + fail("should throw ArrayIndexOutOfBoundsException."); + } catch (ArrayIndexOutOfBoundsException e) { + } + try { + MockBreakIterator.publicGetShort(new byte[] { 0, 0 }, 1); + fail("should throw ArrayIndexOutOfBoundsException."); + } catch (ArrayIndexOutOfBoundsException e) { + } + assertEquals(0, MockBreakIterator + .publicGetShort(new byte[] { 0, 0 }, 0)); + assertEquals(1, MockBreakIterator + .publicGetShort(new byte[] { 0, 1 }, 0)); + assertEquals(-1, MockBreakIterator.publicGetShort(new byte[] { + (byte) 0xff, (byte) 0xff }, 0)); + assertEquals(1, MockBreakIterator.publicGetShort(new byte[] { 1, 0, 1, + 0 }, 1)); + assertEquals(1, MockBreakIterator.publicGetShort(new byte[] { 0, 0, 1, + 0 }, 1)); + assertEquals(1, MockBreakIterator.publicGetShort(new byte[] { 0, 0, 1, + 1 }, 1)); + assertEquals(257, MockBreakIterator.publicGetShort( + new byte[] { 0, 1, 1 }, 1)); + + // regression for Harmony-944 + try { + MockBreakIterator.publicGetShort(new byte[] { 0, 0 }, + Integer.MAX_VALUE); + fail("should throw ArrayIndexOutOfBoundsException"); + } catch (ArrayIndexOutOfBoundsException e) { + // expected + } + + try { + MockBreakIterator.publicGetShort(new byte[] { 0, 0 }, + Short.MAX_VALUE); + fail("should throw ArrayIndexOutOfBoundsException"); + } catch (ArrayIndexOutOfBoundsException e) { + // expected + } + } + + /** + * @tests java.text.BreakIterator.getInt(byte[], int) + * + */ + public void test_getInt() { + try { + MockBreakIterator.publicGetInt(null, 0); + fail("should throw NPE."); + } catch (NullPointerException e) { + } + try { + MockBreakIterator.publicGetInt(null, -1); + fail("should throw NPE."); + } catch (NullPointerException e) { + } + try { + MockBreakIterator.publicGetInt(new byte[] { 0, 0, 0, 1, 1 }, -1); + fail("should throw ArrayIndexOutOfBoundsException."); + } catch (ArrayIndexOutOfBoundsException e) { + } + try { + MockBreakIterator.publicGetInt(new byte[] { 0, 0, 0, 0 }, 1); + fail("should throw ArrayIndexOutOfBoundsException."); + } catch (ArrayIndexOutOfBoundsException e) { + } + assertEquals(0, MockBreakIterator.publicGetInt( + new byte[] { 0, 0, 0, 0 }, 0)); + assertEquals(1, MockBreakIterator.publicGetInt( + new byte[] { 0, 0, 0, 1 }, 0)); + assertEquals(-1, MockBreakIterator.publicGetInt(new byte[] { + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff }, 0)); + assertEquals(1, MockBreakIterator.publicGetInt(new byte[] { 1, 0, 0, 0, + 1, 0 }, 1)); + assertEquals(1, MockBreakIterator.publicGetInt(new byte[] { 0, 0, 0, 0, + 1, 0 }, 1)); + assertEquals(1, MockBreakIterator.publicGetInt(new byte[] { 0, 0, 0, 0, + 1, 1 }, 1)); + assertEquals(257, MockBreakIterator.publicGetInt(new byte[] { 0, 0, 0, + 1, 1 }, 1)); + + // regression for Harmony-944 + try { + MockBreakIterator.publicGetInt(new byte[] { 0, 0 }, + Integer.MAX_VALUE); + fail("should throw ArrayIndexOutOfBoundsException"); + } catch (ArrayIndexOutOfBoundsException e) { + // expected + } + } + + /** + * @tests java.text.BreakIterator.getLong(byte[], int) + * + */ + public void test_getLong() { + try { + MockBreakIterator.publicGetLong(null, 0); + fail("should throw NPE."); + } catch (NullPointerException e) { + } + try { + MockBreakIterator.publicGetLong(null, -1); + fail("should throw NPE."); + } catch (NullPointerException e) { + } + try { + MockBreakIterator.publicGetLong( + new byte[] { 0, 0, 0, 0, 0, 0, 1, 1 }, -1); + fail("should throw ArrayIndexOutOfBoundsException."); + } catch (ArrayIndexOutOfBoundsException e) { + } + try { + MockBreakIterator.publicGetLong( + new byte[] { 0, 0, 0, 0, 0, 0, 1, 1 }, 1); + fail("should throw ArrayIndexOutOfBoundsException."); + } catch (ArrayIndexOutOfBoundsException e) { + } + assertEquals(0, MockBreakIterator.publicGetLong(new byte[] { 0, 0, 0, + 0, 0, 0, 0, 0 }, 0)); + assertEquals(1, MockBreakIterator.publicGetLong(new byte[] { 0, 0, 0, + 0, 0, 0, 0, 1 }, 0)); + assertEquals(-1, MockBreakIterator.publicGetLong(new byte[] { + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff }, 0)); + assertEquals(1, MockBreakIterator.publicGetLong(new byte[] { 1, 0, 0, + 0, 0, 0, 0, 0, 1, 0 }, 1)); + assertEquals(1, MockBreakIterator.publicGetLong(new byte[] { 0, 0, 0, + 0, 0, 0, 0, 0, 1, 0 }, 1)); + assertEquals(1, MockBreakIterator.publicGetLong(new byte[] { 0, 0, 0, + 0, 0, 0, 0, 0, 1, 1 }, 1)); + assertEquals(257, MockBreakIterator.publicGetLong(new byte[] { 0, 0, 0, + 0, 0, 0, 0, 1, 1 }, 1)); + + // regression for Harmony-944 + try { + MockBreakIterator.publicGetLong(new byte[] { 0, 1 }, + Integer.MAX_VALUE); + fail("should throw ArrayIndexOutOfBoundsException"); + } catch (ArrayIndexOutOfBoundsException e) { + // expected + } + } + + /** + * @tests java.text.BreakIterator#getCharacterInstance(Locale) + */ + public void testGetCharacterInstanceLocale_NPE() { + // Regression for HARMONY-265 + try { + BreakIterator.getCharacterInstance(null); + fail("BreakIterator.getCharacterInstance(null); should throw NullPointerException"); + } catch (NullPointerException e) { + } + } + + public void testGetLineInstanceLocale_NPE() { + try { + BreakIterator.getLineInstance(null); + fail("BreakIterator.getLineInstance(null); should throw NullPointerException"); + } catch (NullPointerException e) { + } + } + + public void testGetSentenceInstanceLocale_NPE() { + try { + BreakIterator.getSentenceInstance(null); + fail("BreakIterator.getSentenceInstance(null); should throw NullPointerException"); + } catch (NullPointerException e) { + } + } + + public void testGetWordInstanceLocale_NPE() { + try { + BreakIterator.getWordInstance(null); + fail("BreakIterator.getWordInstance(null); should throw NullPointerException"); + } catch (NullPointerException e) { + } + } + + private static class MockBreakIterator extends BreakIterator { + public MockBreakIterator() { + super(); + } + + public static int publicGetInt(byte[] buf, int offset) { + return BreakIterator.getInt(buf, offset); + } + + public static long publicGetLong(byte[] buf, int offset) { + return BreakIterator.getLong(buf, offset); + } + + public static short publicGetShort(byte[] buf, int offset) { + return BreakIterator.getShort(buf, offset); + } + + public int current() { + return 0; + } + + public int first() { + return 0; + } + + public int following(int offset) { + return 0; + } + + public CharacterIterator getText() { + return null; + } + + public int last() { + return 0; + } + + public int next() { + return 0; + } + + public int next(int n) { + return 0; + } + + public int previous() { + return 0; + } + + public void setText(CharacterIterator newText) { + } + } +} diff --git a/text/src/test/java/org/apache/harmony/text/tests/java/text/ChoiceFormatTest.java b/text/src/test/java/org/apache/harmony/text/tests/java/text/ChoiceFormatTest.java new file mode 100644 index 0000000..3891109 --- /dev/null +++ b/text/src/test/java/org/apache/harmony/text/tests/java/text/ChoiceFormatTest.java @@ -0,0 +1,477 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.text.tests.java.text; + +import java.text.ChoiceFormat; +import java.text.FieldPosition; +import java.text.MessageFormat; +import java.text.ParsePosition; + +import junit.framework.TestCase; + +public class ChoiceFormatTest extends TestCase { + + double[] limits = new double[] { 0, 1, ChoiceFormat.nextDouble(1), + ChoiceFormat.nextDouble(2) }; + + String[] formats = new String[] { "Less than one", "one", + "Between one and two", "Greater than two" }; + + ChoiceFormat f1 = new ChoiceFormat(limits, formats); + + /** + * @tests java.text.ChoiceFormat#ChoiceFormat(double[], java.lang.String[]) + */ + public void test_Constructor$D$Ljava_lang_String() { + // Test for method java.text.ChoiceFormat(double [], java.lang.String + // []) + String formattedString; + double[] appleLimits = { 1, 2, 3, 4, 5 }; + String[] appleFormats = { "Tiny Apple", "Small Apple", "Medium Apple", + "Large Apple", "Huge Apple" }; + ChoiceFormat cf = new ChoiceFormat(appleLimits, appleFormats); + + formattedString = cf.format(Double.NEGATIVE_INFINITY); + assertTrue("a) Incorrect format returned: " + formattedString, + formattedString.equals("Tiny Apple")); + formattedString = cf.format(0.5d); + assertTrue("b) Incorrect format returned: " + formattedString, + formattedString.equals("Tiny Apple")); + formattedString = cf.format(1d); + assertTrue("c) Incorrect format returned: " + formattedString, + formattedString.equals("Tiny Apple")); + formattedString = cf.format(1.5d); + assertTrue("d) Incorrect format returned: " + formattedString, + formattedString.equals("Tiny Apple")); + formattedString = cf.format(2d); + assertTrue("e) Incorrect format returned: " + formattedString, + formattedString.equals("Small Apple")); + formattedString = cf.format(2.5d); + assertTrue("f) Incorrect format returned: " + formattedString, + formattedString.equals("Small Apple")); + formattedString = cf.format(3d); + assertTrue("g) Incorrect format returned: " + formattedString, + formattedString.equals("Medium Apple")); + formattedString = cf.format(4d); + assertTrue("h) Incorrect format returned: " + formattedString, + formattedString.equals("Large Apple")); + formattedString = cf.format(5d); + assertTrue("i) Incorrect format returned: " + formattedString, + formattedString.equals("Huge Apple")); + formattedString = cf.format(5.5d); + assertTrue("j) Incorrect format returned: " + formattedString, + formattedString.equals("Huge Apple")); + formattedString = cf.format(6.0d); + assertTrue("k) Incorrect format returned: " + formattedString, + formattedString.equals("Huge Apple")); + formattedString = cf.format(Double.POSITIVE_INFINITY); + assertTrue("l) Incorrect format returned: " + formattedString, + formattedString.equals("Huge Apple")); + } + + /** + * @tests java.text.ChoiceFormat#ChoiceFormat(java.lang.String) + */ + public void test_ConstructorLjava_lang_String() { + // Test for method java.text.ChoiceFormat(java.lang.String) + String formattedString; + String patternString = "-2#Inverted Orange| 0#No Orange| 0<Almost No Orange| 1#Normal Orange| 2#Expensive Orange"; + ChoiceFormat cf = new ChoiceFormat(patternString); + + formattedString = cf.format(Double.NEGATIVE_INFINITY); + assertTrue("a) Incorrect format returned: " + formattedString, + formattedString.equals("Inverted Orange")); + formattedString = cf.format(-3); + assertTrue("b) Incorrect format returned: " + formattedString, + formattedString.equals("Inverted Orange")); + formattedString = cf.format(-2); + assertTrue("c) Incorrect format returned: " + formattedString, + formattedString.equals("Inverted Orange")); + formattedString = cf.format(-1); + assertTrue("d) Incorrect format returned: " + formattedString, + formattedString.equals("Inverted Orange")); + formattedString = cf.format(-0); + assertTrue("e) Incorrect format returned: " + formattedString, + formattedString.equals("No Orange")); + formattedString = cf.format(0); + assertTrue("f) Incorrect format returned: " + formattedString, + formattedString.equals("No Orange")); + formattedString = cf.format(0.1); + assertTrue("g) Incorrect format returned: " + formattedString, + formattedString.equals("Almost No Orange")); + formattedString = cf.format(1); + assertTrue("h) Incorrect format returned: " + formattedString, + formattedString.equals("Normal Orange")); + formattedString = cf.format(1.5); + assertTrue("i) Incorrect format returned: " + formattedString, + formattedString.equals("Normal Orange")); + formattedString = cf.format(2); + assertTrue("j) Incorrect format returned: " + formattedString, + formattedString.equals("Expensive Orange")); + formattedString = cf.format(3); + assertTrue("k) Incorrect format returned: " + formattedString, + formattedString.equals("Expensive Orange")); + formattedString = cf.format(Double.POSITIVE_INFINITY); + assertTrue("l) Incorrect format returned: " + formattedString, + formattedString.equals("Expensive Orange")); + + } + + /** + * @tests java.text.ChoiceFormat#applyPattern(java.lang.String) + */ + public void test_applyPatternLjava_lang_String() { + // Test for method void + // java.text.ChoiceFormat.applyPattern(java.lang.String) + ChoiceFormat f = (ChoiceFormat) f1.clone(); + f.applyPattern("0#0|1#1"); + assertTrue("Incorrect limits", java.util.Arrays.equals(f.getLimits(), + new double[] { 0, 1 })); + assertTrue("Incorrect formats", java.util.Arrays.equals(f.getFormats(), + new String[] { "0", "1" })); + + // Regression for Harmony 540 + double[] choiceLimits = { -1, 0, 1, ChoiceFormat.nextDouble(1) }; + String[] choiceFormats = { "is negative", "is zero or fraction", + "is one", "is more than 1" }; + + f = new ChoiceFormat(""); + f + .applyPattern("-1#is negative|0#is zero or fraction|1#is one|1<is more than 1"); + assertTrue("Incorrect limits", java.util.Arrays.equals(f.getLimits(), + choiceLimits)); + assertTrue("Incorrect formats", java.util.Arrays.equals(f.getFormats(), + choiceFormats)); + + f = new ChoiceFormat(""); + try { + f + .applyPattern("-1#is negative|0#is zero or fraction|-1#is one|1<is more than 1"); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // Expected + } + + f = new ChoiceFormat(""); + try { + f + .applyPattern("-1is negative|0#is zero or fraction|1#is one|1<is more than 1"); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // Expected + } + + f = new ChoiceFormat(""); + f + .applyPattern("-1<is negative|0#is zero or fraction|1#is one|1<is more than 1"); + choiceLimits[0] = ChoiceFormat.nextDouble(-1); + assertTrue("Incorrect limits", java.util.Arrays.equals(f.getLimits(), + choiceLimits)); + assertTrue("Incorrect formats", java.util.Arrays.equals(f.getFormats(), + choiceFormats)); + + f = new ChoiceFormat(""); + f + .applyPattern("-1#is negative|0#is zero or fraction|1#is one|1<is more than 1"); + String str = "org.apache.harmony.tests.java.text.ChoiceFormat"; + f.applyPattern(str); + String ptrn = f.toPattern(); + assertEquals("Return value should be empty string for invalid pattern", + 0, ptrn.length()); + } + + /** + * @tests java.text.ChoiceFormat#clone() + */ + public void test_clone() { + // Test for method java.lang.Object java.text.ChoiceFormat.clone() + ChoiceFormat f = (ChoiceFormat) f1.clone(); + assertTrue("Not equal", f.equals(f1)); + f.setChoices(new double[] { 0, 1, 2 }, new String[] { "0", "1", "2" }); + assertTrue("Equal", !f.equals(f1)); + } + + /** + * @tests java.text.ChoiceFormat#equals(java.lang.Object) + */ + public void test_equalsLjava_lang_Object() { + // Test for method boolean + // java.text.ChoiceFormat.equals(java.lang.Object) + + String patternString = "-2#Inverted Orange| 0#No Orange| 0<Almost No Orange| 1#Normal Orange| 2#Expensive Orange"; + double[] appleLimits = { 1, 2, 3, 4, 5 }; + String[] appleFormats = { "Tiny Apple", "Small Apple", "Medium Apple", + "Large Apple", "Huge Apple" }; + double[] orangeLimits = { -2, 0, ChoiceFormat.nextDouble(0), 1, 2 }; + String[] orangeFormats = { "Inverted Orange", "No Orange", + "Almost No Orange", "Normal Orange", "Expensive Orange" }; + + ChoiceFormat appleChoiceFormat = new ChoiceFormat(appleLimits, + appleFormats); + ChoiceFormat orangeChoiceFormat = new ChoiceFormat(orangeLimits, + orangeFormats); + ChoiceFormat orangeChoiceFormat2 = new ChoiceFormat(patternString); + ChoiceFormat hybridChoiceFormat = new ChoiceFormat(appleLimits, + orangeFormats); + + assertTrue("Apples should not equal oranges", !appleChoiceFormat + .equals(orangeChoiceFormat)); + assertTrue("Different limit list--should not appear as equal", + !orangeChoiceFormat.equals(hybridChoiceFormat)); + assertTrue("Different format list--should not appear as equal", + !appleChoiceFormat.equals(hybridChoiceFormat)); + assertTrue("Should be equal--identical format", appleChoiceFormat + .equals(appleChoiceFormat)); + assertTrue("Should be equals--same limits, same formats", + orangeChoiceFormat.equals(orangeChoiceFormat2)); + + ChoiceFormat f2 = new ChoiceFormat( + "0#Less than one|1#one|1<Between one and two|2<Greater than two"); + assertTrue("Not equal", f1.equals(f2)); + } + + /** + * @tests java.text.ChoiceFormat#format(double, java.lang.StringBuffer, + * java.text.FieldPosition) + */ + public void test_formatDLjava_lang_StringBufferLjava_text_FieldPosition() { + // Test for method java.lang.StringBuffer + // java.text.ChoiceFormat.format(double, java.lang.StringBuffer, + // java.text.FieldPosition) + FieldPosition field = new FieldPosition(0); + StringBuffer buf = new StringBuffer(); + String r = f1.format(-1, buf, field).toString(); + assertEquals("Wrong choice for -1", "Less than one", r); + buf.setLength(0); + r = f1.format(0, buf, field).toString(); + assertEquals("Wrong choice for 0", "Less than one", r); + buf.setLength(0); + r = f1.format(1, buf, field).toString(); + assertEquals("Wrong choice for 1", "one", r); + buf.setLength(0); + r = f1.format(2, buf, field).toString(); + assertEquals("Wrong choice for 2", "Between one and two", r); + buf.setLength(0); + r = f1.format(3, buf, field).toString(); + assertEquals("Wrong choice for 3", "Greater than two", r); + + // Regression test for HARMONY-1081 + assertEquals(0, new ChoiceFormat("|").format(Double.NaN, + new StringBuffer(), new FieldPosition(6)).length()); + assertEquals(0, new ChoiceFormat("|").format(1, new StringBuffer(), + new FieldPosition(6)).length()); + assertEquals("Less than one", f1.format(Double.NaN, new StringBuffer(), + field).toString()); + } + + /** + * @tests java.text.ChoiceFormat#format(long, java.lang.StringBuffer, + * java.text.FieldPosition) + */ + public void test_formatJLjava_lang_StringBufferLjava_text_FieldPosition() { + // Test for method java.lang.StringBuffer + // java.text.ChoiceFormat.format(long, java.lang.StringBuffer, + // java.text.FieldPosition) + FieldPosition field = new FieldPosition(0); + StringBuffer buf = new StringBuffer(); + String r = f1.format(0.5, buf, field).toString(); + assertEquals("Wrong choice for 0.5", "Less than one", r); + buf.setLength(0); + r = f1.format(1.5, buf, field).toString(); + assertEquals("Wrong choice for 1.5", "Between one and two", r); + buf.setLength(0); + r = f1.format(2.5, buf, field).toString(); + assertEquals("Wrong choice for 2.5", "Greater than two", r); + } + + /** + * @tests java.text.ChoiceFormat#getFormats() + */ + public void test_getFormats() { + // Test for method java.lang.Object [] + // java.text.ChoiceFormat.getFormats() + String[] orgFormats = (String[]) formats.clone(); + String[] f = (String[]) f1.getFormats(); + assertTrue("Wrong formats", f.equals(formats)); + f[0] = "Modified"; + assertTrue("Formats copied", !f.equals(orgFormats)); + } + + /** + * @tests java.text.ChoiceFormat#getLimits() + */ + public void test_getLimits() { + // Test for method double [] java.text.ChoiceFormat.getLimits() + double[] orgLimits = (double[]) limits.clone(); + double[] l = f1.getLimits(); + assertTrue("Wrong limits", l.equals(limits)); + l[0] = 3.14527; + assertTrue("Limits copied", !l.equals(orgLimits)); + } + + /** + * @tests java.text.ChoiceFormat#hashCode() + */ + public void test_hashCode() { + // Test for method int java.text.ChoiceFormat.hashCode() + ChoiceFormat f2 = new ChoiceFormat( + "0#Less than one|1#one|1<Between one and two|2<Greater than two"); + assertTrue("Different hash", f1.hashCode() == f2.hashCode()); + } + + /** + * @tests java.text.ChoiceFormat#nextDouble(double) + */ + public void test_nextDoubleD() { + // Test for method double java.text.ChoiceFormat.nextDouble(double) + assertTrue("Not greater 5", ChoiceFormat.nextDouble(5) > 5); + assertTrue("Not greater 0", ChoiceFormat.nextDouble(0) > 0); + assertTrue("Not greater -5", ChoiceFormat.nextDouble(-5) > -5); + assertTrue("Not NaN", Double.isNaN(ChoiceFormat.nextDouble(Double.NaN))); + } + + /** + * @tests java.text.ChoiceFormat#nextDouble(double, boolean) + */ + public void test_nextDoubleDZ() { + // Test for method double java.text.ChoiceFormat.nextDouble(double, + // boolean) + assertTrue("Not greater 0", ChoiceFormat.nextDouble(0, true) > 0); + assertTrue("Not less 0", ChoiceFormat.nextDouble(0, false) < 0); + } + + /** + * @tests java.text.ChoiceFormat#parse(java.lang.String, + * java.text.ParsePosition) + */ + public void test_parseLjava_lang_StringLjava_text_ParsePosition() { + // Test for method java.lang.Number + // java.text.ChoiceFormat.parse(java.lang.String, + // java.text.ParsePosition) + ChoiceFormat format = new ChoiceFormat("1#one|2#two|3#three"); + assertEquals("Case insensitive", 0, format.parse("One", + new ParsePosition(0)).intValue()); + + ParsePosition pos = new ParsePosition(0); + Number result = f1.parse("Greater than two", pos); + assertTrue("Not a Double1", result instanceof Double); + assertTrue("Wrong value ~>2", result.doubleValue() == ChoiceFormat + .nextDouble(2)); + assertEquals("Wrong position ~16", 16, pos.getIndex()); + pos = new ParsePosition(0); + assertTrue("Incorrect result", Double.isNaN(f1.parse("12one", pos) + .doubleValue())); + assertEquals("Wrong position ~0", 0, pos.getIndex()); + pos = new ParsePosition(2); + result = f1.parse("12one and two", pos); + assertTrue("Not a Double2", result instanceof Double); + assertEquals("Ignored parse position", 1.0D, result.doubleValue(), 0.0D); + assertEquals("Wrong position ~5", 5, pos.getIndex()); + } + + /** + * @tests java.text.ChoiceFormat#previousDouble(double) + */ + public void test_previousDoubleD() { + // Test for method double java.text.ChoiceFormat.previousDouble(double) + assertTrue("Not less 5", ChoiceFormat.previousDouble(5) < 5); + assertTrue("Not less 0", ChoiceFormat.previousDouble(0) < 0); + assertTrue("Not less -5", ChoiceFormat.previousDouble(-5) < -5); + assertTrue("Not NaN", Double.isNaN(ChoiceFormat + .previousDouble(Double.NaN))); + } + + /** + * @tests java.text.ChoiceFormat#setChoices(double[], java.lang.String[]) + */ + public void test_setChoices$D$Ljava_lang_String() { + // Test for method void java.text.ChoiceFormat.setChoices(double [], + // java.lang.String []) + ChoiceFormat f = (ChoiceFormat) f1.clone(); + double[] l = new double[] { 0, 1 }; + String[] fs = new String[] { "0", "1" }; + f.setChoices(l, fs); + assertTrue("Limits copied", f.getLimits() == l); + assertTrue("Formats copied", f.getFormats() == fs); + } + + /** + * @tests java.text.ChoiceFormat#toPattern() + */ + public void test_toPattern() { + // Regression for HARMONY-59 + ChoiceFormat cf = new ChoiceFormat(""); + assertEquals("", cf.toPattern()); + + cf = new ChoiceFormat("-1#NEGATIVE_ONE|0#ZERO|1#ONE|1<GREATER_THAN_ONE"); + assertEquals("-1.0#NEGATIVE_ONE|0.0#ZERO|1.0#ONE|1.0<GREATER_THAN_ONE", + cf.toPattern()); + + MessageFormat mf = new MessageFormat("CHOICE {1,choice}"); + String ptrn = mf.toPattern(); + assertEquals("Unused message format returning incorrect pattern", + "CHOICE {1,choice,}", ptrn); + + String pattern = f1.toPattern(); + assertTrue( + "Wrong pattern: " + pattern, + pattern + .equals("0.0#Less than one|1.0#one|1.0<Between one and two|2.0<Greater than two")); + + cf = new ChoiceFormat( + "-1#is negative| 0#is zero or fraction | 1#is one |1.0<is 1+|2#is two |2<is more than 2."); + String str = "org.apache.harmony.tests.java.lang.share.MyResources2"; + cf.applyPattern(str); + ptrn = cf.toPattern(); + assertEquals("Return value should be empty string for invalid pattern", + 0, ptrn.length()); + } + + /** + * @tests java.text.ChoiceFormat#format(long) + */ + public void test_formatL() { + ChoiceFormat fmt = new ChoiceFormat( + "-1#NEGATIVE_ONE|0#ZERO|1#ONE|1<GREATER_THAN_ONE"); + + assertEquals("NEGATIVE_ONE", fmt.format(Long.MIN_VALUE)); + assertEquals("NEGATIVE_ONE", fmt.format(-1)); + assertEquals("ZERO", fmt.format(0)); + assertEquals("ONE", fmt.format(1)); + assertEquals("GREATER_THAN_ONE", fmt.format(Long.MAX_VALUE)); + } + + /** + * @tests java.text.ChoiceFormat#format(double) + */ + public void test_formatD() { + ChoiceFormat fmt = new ChoiceFormat( + "-1#NEGATIVE_ONE|0#ZERO|1#ONE|1<GREATER_THAN_ONE"); + assertEquals("NEGATIVE_ONE", fmt.format(Double.NEGATIVE_INFINITY)); + assertEquals("NEGATIVE_ONE", fmt.format(-999999999D)); + assertEquals("NEGATIVE_ONE", fmt.format(-1.1)); + assertEquals("NEGATIVE_ONE", fmt.format(-1.0)); + assertEquals("NEGATIVE_ONE", fmt.format(-0.9)); + assertEquals("ZERO", fmt.format(0.0)); + assertEquals("ZERO", fmt.format(0.9)); + assertEquals("ONE", fmt.format(1.0)); + assertEquals("GREATER_THAN_ONE", fmt.format(1.1)); + assertEquals("GREATER_THAN_ONE", fmt.format(999999999D)); + assertEquals("GREATER_THAN_ONE", fmt.format(Double.POSITIVE_INFINITY)); + } +} diff --git a/text/src/test/java/org/apache/harmony/text/tests/java/text/CollationElementIteratorTest.java b/text/src/test/java/org/apache/harmony/text/tests/java/text/CollationElementIteratorTest.java new file mode 100644 index 0000000..fd945bb --- /dev/null +++ b/text/src/test/java/org/apache/harmony/text/tests/java/text/CollationElementIteratorTest.java @@ -0,0 +1,232 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.text.tests.java.text; + +import java.text.CollationElementIterator; +import java.text.Collator; +import java.text.RuleBasedCollator; +import java.text.StringCharacterIterator; +import java.util.Locale; + +import junit.framework.TestCase; + +/** + * Test CollationElementIterator + * + * Only test normal condition. + * + */ +public class CollationElementIteratorTest extends TestCase { + + private RuleBasedCollator coll; + + protected void setUp() { + coll = (RuleBasedCollator) Collator.getInstance(Locale.US); + } + + public void testGetOffset() { + String text = "abc"; + CollationElementIterator iterator = coll + .getCollationElementIterator(text); + int[] offsets = { 0, 1, 2, 3 }; + int offset = iterator.getOffset(); + int i = 0; + assertEquals(offsets[i++], offset); + while (offset != text.length()) { + iterator.next(); + offset = iterator.getOffset(); + assertEquals(offsets[i++], offset); + } + } + + public void testNext() { + String text = "abc"; + CollationElementIterator iterator = coll + .getCollationElementIterator(text); + int[] orders = new int[text.length()]; + int order = iterator.next(); + int i = 0; + while (order != CollationElementIterator.NULLORDER) { + orders[i++] = order; + order = iterator.next(); + } + + int offset = iterator.getOffset(); + assertEquals(text.length(), offset); + order = iterator.previous(); + + while (order != CollationElementIterator.NULLORDER) { + assertEquals(orders[--i], order); + order = iterator.previous(); + } + + assertEquals(0, iterator.getOffset()); + } + + /** + * @tests java.text.CollationElementIterator#previous() Test of method + * java.text.CollationElementIterator#previous(). + */ + public void testPrevious() { + String text = "abc"; + CollationElementIterator iterator = coll + .getCollationElementIterator(text); + int[] orders = new int[text.length()]; + int order = iterator.next(); + int i = 0; + while (order != CollationElementIterator.NULLORDER) { + orders[i++] = order; + order = iterator.next(); + } + + int offset = iterator.getOffset(); + assertEquals(text.length(), offset); + order = iterator.previous(); + + while (order != CollationElementIterator.NULLORDER) { + assertEquals(orders[--i], order); + order = iterator.previous(); + } + + assertEquals(0, iterator.getOffset()); + } + + public void testReset() { + String text = "abc"; + CollationElementIterator iterator = coll + .getCollationElementIterator(text); + int[] orders = new int[text.length()]; + int order = iterator.next(); + int i = 0; + while (order != CollationElementIterator.NULLORDER) { + orders[i++] = order; + order = iterator.next(); + } + + int offset = iterator.getOffset(); + assertEquals(text.length(), offset); + + iterator.reset(); + assertEquals(0, iterator.getOffset()); + } + + public void testGetMaxExpansion() { + String text = "cha"; + RuleBasedCollator rbColl = (RuleBasedCollator) Collator + .getInstance(new Locale("es", "", "TRADITIONAL")); + CollationElementIterator iterator = rbColl + .getCollationElementIterator(text); + int order = iterator.next(); + while (order != CollationElementIterator.NULLORDER) { + assertEquals(1, iterator.getMaxExpansion(order)); + order = iterator.next(); + } + + } + + public void testPrimaryOrder() { + RuleBasedCollator rbColl = (RuleBasedCollator) Collator + .getInstance(new Locale("de", "DE")); + String text = "\u00e6"; + CollationElementIterator iterator = rbColl + .getCollationElementIterator(text); + int order = iterator.next(); + int pOrder = CollationElementIterator.primaryOrder(order); + CollationElementIterator iterator2 = rbColl + .getCollationElementIterator("ae"); + int order2 = iterator2.next(); + int pOrder2 = CollationElementIterator.primaryOrder(order2); + assertEquals(pOrder, pOrder2); + } + + public void testSecondaryOrder() { + RuleBasedCollator rbColl = (RuleBasedCollator) Collator + .getInstance(new Locale("fr", "FR")); + String text = "a\u00e0"; + CollationElementIterator iterator = rbColl + .getCollationElementIterator(text); + int order = iterator.next(); + int sOrder1 = CollationElementIterator.secondaryOrder(order); + + order = iterator.next(); + int sOrder2 = CollationElementIterator.secondaryOrder(order); + + assertEquals(sOrder1, sOrder2); + } + + public void testTertiaryOrder() { + RuleBasedCollator rbColl = (RuleBasedCollator) Collator + .getInstance(new Locale("fr", "FR")); + String text = "abAB"; + CollationElementIterator iterator = rbColl + .getCollationElementIterator(text); + int order = iterator.next(); + int tOrder1 = CollationElementIterator.tertiaryOrder(order); + order = iterator.next(); + int tOrder2 = CollationElementIterator.tertiaryOrder(order); + assertEquals(tOrder1, tOrder2); + + order = iterator.next(); + tOrder1 = CollationElementIterator.tertiaryOrder(order); + order = iterator.next(); + tOrder2 = CollationElementIterator.tertiaryOrder(order); + assertEquals(tOrder1, tOrder2); + } + + public void testSetOffset() { + RuleBasedCollator rbColl = (RuleBasedCollator) Collator + .getInstance(new Locale("es", "", "TRADITIONAL")); + String text = "cha"; + CollationElementIterator iterator = rbColl + .getCollationElementIterator(text); + iterator.setOffset(1); + assertEquals(1, iterator.getOffset()); + } + + /* + * Class under test for void setText(java.lang.String) + */ + public void testSetTextString() { + RuleBasedCollator rbColl = (RuleBasedCollator) Collator + .getInstance(new Locale("es", "", "TRADITIONAL")); + String text = "caa"; + CollationElementIterator iterator = rbColl + .getCollationElementIterator(text); + iterator.setOffset(1); + assertEquals(1, iterator.getOffset()); + iterator.setText("cha"); + iterator.setOffset(1); + assertEquals(1, iterator.getOffset()); + } + + /* + * Class under test for void setText(java.text.CharacterIterator) + */ + public void testSetTextCharacterIterator() { + RuleBasedCollator rbColl = (RuleBasedCollator) Collator + .getInstance(new Locale("es", "", "TRADITIONAL")); + String text = "caa"; + CollationElementIterator iterator = rbColl + .getCollationElementIterator(text); + iterator.setOffset(1); + assertEquals(1, iterator.getOffset()); + iterator.setText(new StringCharacterIterator("cha")); + iterator.setOffset(1); + assertEquals(1, iterator.getOffset()); + } +} diff --git a/text/src/test/java/org/apache/harmony/text/tests/java/text/CollationKeyTest.java b/text/src/test/java/org/apache/harmony/text/tests/java/text/CollationKeyTest.java new file mode 100644 index 0000000..b97358a --- /dev/null +++ b/text/src/test/java/org/apache/harmony/text/tests/java/text/CollationKeyTest.java @@ -0,0 +1,117 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.harmony.text.tests.java.text; + +import java.text.CollationKey; +import java.text.Collator; +import java.text.ParseException; +import java.text.RuleBasedCollator; +import java.util.Arrays; + +public class CollationKeyTest extends junit.framework.TestCase { + + /** + * @tests java.text.CollationKey#compareTo(java.text.CollationKey) + */ + public void test_compareToLjava_text_CollationKey() { + Collator collator = Collator.getInstance(); + collator.setStrength(Collator.PRIMARY); + CollationKey key1 = collator.getCollationKey("abc"); + CollationKey key2 = collator.getCollationKey("ABC"); + assertEquals("Should be equal", 0, key1.compareTo(key2)); + } + + /** + * @tests java.text.CollationKey#compareTo(java.lang.Object) + */ + public void test_compareToLjava_lang_Object() { + // Test for method int + // java.text.CollationKey.compareTo(java.lang.Object) + Collator collator = Collator.getInstance(); + collator.setStrength(Collator.PRIMARY); + CollationKey key1 = collator.getCollationKey("abc"); + CollationKey key2 = collator.getCollationKey("ABC"); + assertEquals("Should be equal", 0, key1.compareTo(key2)); + } + + /** + * @tests java.text.CollationKey#equals(java.lang.Object) + */ + public void test_equalsLjava_lang_Object() { + Collator collator = Collator.getInstance(); + collator.setStrength(Collator.PRIMARY); + CollationKey key1 = collator.getCollationKey("abc"); + CollationKey key2 = collator.getCollationKey("ABC"); + assertTrue("Should be equal", key1.equals(key2)); + } + + /** + * @tests java.text.CollationKey#getSourceString() + */ + public void test_getSourceString() { + Collator collator = Collator.getInstance(); + collator.setStrength(Collator.PRIMARY); + assertTrue("Wrong source string1", collator.getCollationKey("abc") + .getSourceString() == "abc"); + assertTrue("Wrong source string2", collator.getCollationKey("ABC") + .getSourceString() == "ABC"); + } + + /** + * @tests java.text.CollationKey#hashCode() + */ + public void test_hashCode() { + Collator collator = Collator.getInstance(); + collator.setStrength(Collator.PRIMARY); + CollationKey key1 = collator.getCollationKey("abc"); + CollationKey key2 = collator.getCollationKey("ABC"); + assertTrue("Should be equal", key1.hashCode() == key2.hashCode()); + } + + /** + * @tests java.text.CollationKey#toByteArray() + */ + // FIXME This test fails on Harmony ClassLibrary + public void failing_test_toByteArray() { + // Test for method byte [] java.text.CollationKey.toByteArray() + Collator collator = Collator.getInstance(); + collator.setStrength(Collator.PRIMARY); + CollationKey key1 = collator.getCollationKey("abc"); + byte[] bytes = key1.toByteArray(); + assertTrue("Not enough bytes", bytes.length >= 3); + + try { + collator = new RuleBasedCollator("= 1 , 2 ; 3 , 4 < 5 ; 6 , 7"); + } catch (ParseException e) { + fail("ParseException"); + return; + } + bytes = collator.getCollationKey("1234567").toByteArray(); + /* + * CollationElementIterator it = + * ((RuleBasedCollator)collator).getCollationElementIterator("1234567"); + * int order; while ((order = it.next()) != + * CollationElementIterator.NULLORDER) { + * System.out.println(Integer.toHexString(order)); } for (int i=0; i<bytes.length; + * i+=2) { System.out.print(Integer.toHexString(bytes[i]) + + * Integer.toHexString(bytes[i+1]) + " "); } System.out.println(); + */ + byte[] result = new byte[] { 0, 2, 0, 2, 0, 2, 0, 0, 0, 3, 0, 3, 0, 1, + 0, 2, 0, 2, 0, 0, 0, 4, 0, 4, 0, 1, 0, 1, 0, 2 }; + assertTrue("Wrong bytes", Arrays.equals(bytes, result)); + } +} diff --git a/text/src/test/java/org/apache/harmony/text/tests/java/text/CollatorTest.java b/text/src/test/java/org/apache/harmony/text/tests/java/text/CollatorTest.java new file mode 100644 index 0000000..d451c37 --- /dev/null +++ b/text/src/test/java/org/apache/harmony/text/tests/java/text/CollatorTest.java @@ -0,0 +1,317 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.harmony.text.tests.java.text; + +import java.io.UnsupportedEncodingException; +import java.text.Collator; +import java.text.ParseException; +import java.text.RuleBasedCollator; +import java.util.Locale; + +public class CollatorTest extends junit.framework.TestCase { + + /** + * @tests java.text.Collator#clone() + */ + public void test_clone() { + Collator c = Collator.getInstance(Locale.GERMAN); + Collator c2 = (Collator) c.clone(); + assertTrue("Clones answered false to equals", c.equals(c2)); + assertTrue("Clones were equivalent", c != c2); + } + + /** + * @tests java.text.Collator#compare(java.lang.Object, java.lang.Object) + */ + public void test_compareLjava_lang_ObjectLjava_lang_Object() { + Collator c = Collator.getInstance(Locale.FRENCH); + Object o, o2; + + c.setStrength(Collator.IDENTICAL); + o = "E"; + o2 = "F"; + assertTrue("a) Failed on primary difference", c.compare(o, o2) < 0); + o = "e"; + o2 = "\u00e9"; + assertTrue("a) Failed on secondary difference", c.compare(o, o2) < 0); + o = "e"; + o2 = "E"; + assertTrue("a) Failed on tertiary difference", c.compare(o, o2) < 0); + o = "\u0001"; + o2 = "\u0002"; + assertTrue("a) Failed on identical", c.compare(o, o2) < 0); + o = "e"; + o2 = "e"; + assertEquals("a) Failed on equivalence", 0, c.compare(o, o2)); + assertTrue("a) Failed on primary expansion", + c.compare("\u01db", "v") < 0); + + c.setStrength(Collator.TERTIARY); + o = "E"; + o2 = "F"; + assertTrue("b) Failed on primary difference", c.compare(o, o2) < 0); + o = "e"; + o2 = "\u00e9"; + assertTrue("b) Failed on secondary difference", c.compare(o, o2) < 0); + o = "e"; + o2 = "E"; + assertTrue("b) Failed on tertiary difference", c.compare(o, o2) < 0); + o = "\u0001"; + o2 = "\u0002"; + assertEquals("b) Failed on identical", 0, c.compare(o, o2)); + o = "e"; + o2 = "e"; + assertEquals("b) Failed on equivalence", 0, c.compare(o, o2)); + + c.setStrength(Collator.SECONDARY); + o = "E"; + o2 = "F"; + assertTrue("c) Failed on primary difference", c.compare(o, o2) < 0); + o = "e"; + o2 = "\u00e9"; + assertTrue("c) Failed on secondary difference", c.compare(o, o2) < 0); + o = "e"; + o2 = "E"; + assertEquals("c) Failed on tertiary difference", 0, c.compare(o, o2)); + o = "\u0001"; + o2 = "\u0002"; + assertEquals("c) Failed on identical", 0, c.compare(o, o2)); + o = "e"; + o2 = "e"; + assertEquals("c) Failed on equivalence", 0, c.compare(o, o2)); + + c.setStrength(Collator.PRIMARY); + o = "E"; + o2 = "F"; + assertTrue("d) Failed on primary difference", c.compare(o, o2) < 0); + o = "e"; + o2 = "\u00e9"; + assertEquals("d) Failed on secondary difference", 0, c.compare(o, o2)); + o = "e"; + o2 = "E"; + assertEquals("d) Failed on tertiary difference", 0, c.compare(o, o2)); + o = "\u0001"; + o2 = "\u0002"; + assertEquals("d) Failed on identical", 0, c.compare(o, o2)); + o = "e"; + o2 = "e"; + assertEquals("d) Failed on equivalence", 0, c.compare(o, o2)); + + try { + c.compare("e", new StringBuffer("Blah")); + } catch (ClassCastException e) { + // correct + return; + } + fail("Failed to throw ClassCastException"); + } + + /** + * @tests java.text.Collator#equals(java.lang.Object) + */ + public void test_equalsLjava_lang_Object() { + Collator c = Collator.getInstance(Locale.ENGLISH); + Collator c2 = (Collator) c.clone(); + assertTrue("Cloned collators not equal", c.equals(c2)); + c2.setStrength(Collator.SECONDARY); + assertTrue("Collators with different strengths equal", !c.equals(c2)); + } + + /** + * @tests java.text.Collator#equals(java.lang.String, java.lang.String) + */ + public void test_equalsLjava_lang_StringLjava_lang_String() { + Collator c = Collator.getInstance(Locale.FRENCH); + + c.setStrength(Collator.IDENTICAL); + assertTrue("a) Failed on primary difference", !c.equals("E", "F")); + assertTrue("a) Failed on secondary difference", !c + .equals("e", "\u00e9")); + assertTrue("a) Failed on tertiary difference", !c.equals("e", "E")); + assertTrue("a) Failed on identical", !c.equals("\u0001", "\u0002")); + assertTrue("a) Failed on equivalence", c.equals("e", "e")); + + c.setStrength(Collator.TERTIARY); + assertTrue("b) Failed on primary difference", !c.equals("E", "F")); + assertTrue("b) Failed on secondary difference", !c + .equals("e", "\u00e9")); + assertTrue("b) Failed on tertiary difference", !c.equals("e", "E")); + assertTrue("b) Failed on identical", c.equals("\u0001", "\u0002")); + assertTrue("b) Failed on equivalence", c.equals("e", "e")); + + c.setStrength(Collator.SECONDARY); + assertTrue("c) Failed on primary difference", !c.equals("E", "F")); + assertTrue("c) Failed on secondary difference", !c + .equals("e", "\u00e9")); + assertTrue("c) Failed on tertiary difference", c.equals("e", "E")); + assertTrue("c) Failed on identical", c.equals("\u0001", "\u0002")); + assertTrue("c) Failed on equivalence", c.equals("e", "e")); + + c.setStrength(Collator.PRIMARY); + assertTrue("d) Failed on primary difference", !c.equals("E", "F")); + assertTrue("d) Failed on secondary difference", c.equals("e", "\u00e9")); + assertTrue("d) Failed on tertiary difference", c.equals("e", "E")); + assertTrue("d) Failed on identical", c.equals("\u0001", "\u0002")); + assertTrue("d) Failed on equivalence", c.equals("e", "e")); + } + + /** + * @tests java.text.Collator#getAvailableLocales() + */ + //FIXME This test fails on Harmony ClassLibrary + public void failing_test_getAvailableLocales() { + Locale[] locales = Collator.getAvailableLocales(); + assertTrue("No locales", locales.length > 0); + boolean english = false, german = false; + for (int i = locales.length; --i >= 0;) { + if (locales[i].equals(Locale.ENGLISH)) + english = true; + if (locales[i].equals(Locale.GERMAN)) + german = true; + // Output the working locale to help diagnose a hang + Collator c1 = Collator.getInstance(locales[i]); + assertTrue("Doesn't work", c1.compare("a", "b") < 0); + assertTrue("Wrong decomposition", + c1.getDecomposition() == Collator.NO_DECOMPOSITION); + assertTrue("Wrong strength", c1.getStrength() == Collator.TERTIARY); + if (c1 instanceof RuleBasedCollator) { + try { + new RuleBasedCollator(((RuleBasedCollator) c1).getRules()); + } catch (ParseException e) { + fail("ParseException"); + } + // assertTrue("Can't recreate: " + locales[i], temp.equals(c1)); + } + } + assertTrue("Missing locales", english && german); + } + + /** + * @tests java.text.Collator#getDecomposition() + */ + //FIXME This test fails on Harmony ClassLibrary + public void failing_test_getDecomposition() { + RuleBasedCollator collator; + try { + collator = new RuleBasedCollator("; \u0300 < a, A < b < c < d"); + } catch (ParseException e) { + fail("ParseException"); + return; + } + assertTrue("Wrong default", + collator.getDecomposition() == Collator.CANONICAL_DECOMPOSITION); + } + + /** + * @tests java.text.Collator#getInstance() + */ + public void test_getInstance() { + Collator c1 = Collator.getInstance(); + Collator c2 = Collator.getInstance(Locale.getDefault()); + assertTrue("Wrong locale", c1.equals(c2)); + } + + /** + * @tests java.text.Collator#getInstance(java.util.Locale) + */ + public void test_getInstanceLjava_util_Locale() { + assertTrue("Used to test", true); + } + + /** + * @tests java.text.Collator#getStrength() + */ + public void test_getStrength() { + RuleBasedCollator collator; + try { + collator = new RuleBasedCollator("; \u0300 < a, A < b < c < d"); + } catch (ParseException e) { + fail("ParseException"); + return; + } + assertTrue("Wrong default", collator.getStrength() == Collator.TERTIARY); + } + + /** + * @tests java.text.Collator#setDecomposition(int) + */ + //FIXME This test fails on Harmony ClassLibrary + public void failing_test_setDecompositionI() { + Collator c = Collator.getInstance(Locale.FRENCH); + c.setStrength(Collator.IDENTICAL); + c.setDecomposition(Collator.NO_DECOMPOSITION); + assertTrue("Collator should not be using decomposition", !c.equals( + "\u212B", "\u00C5")); // "ANGSTROM SIGN" and "LATIN CAPITAL + // LETTER A WITH RING ABOVE" + c.setDecomposition(Collator.CANONICAL_DECOMPOSITION); + assertTrue("Collator should be using decomposition", c.equals("\u212B", + "\u00C5")); // "ANGSTROM SIGN" and "LATIN CAPITAL LETTER A WITH + // RING ABOVE" + assertTrue("Should not be equal under canonical decomposition", !c + .equals("\u2163", "IV")); // roman number "IV" + c.setDecomposition(Collator.FULL_DECOMPOSITION); + assertTrue("Should be equal under full decomposition", c.equals( + "\u2163", "IV")); // roman number "IV" + } + + /** + * @tests java.text.Collator#setStrength(int) + */ + public void test_setStrengthI() { + assertTrue("Used to test", true); + } + + + // Regression test for Android bug + public void test_stackCorruption() { + Collator mColl = Collator.getInstance(); + mColl.setStrength(Collator.PRIMARY); + mColl.getCollationKey("2d294f2d3739433565147655394f3762f3147312d3731641452f310"); + } + + // Test to verify that very large collation keys are not truncated. + public void test_collationKeySize() { + StringBuilder b = new StringBuilder(); + for (int i = 0; i < 1024; i++) { + b.append("0123456789ABCDEF"); + } + String sixteen = b.toString(); + b.append("_THE_END"); + String sixteenplus = b.toString(); + + Collator mColl = Collator.getInstance(); + mColl.setStrength(Collator.PRIMARY); + + try { + byte [] arr = mColl.getCollationKey(sixteen).toByteArray(); + int len = arr.length; + assertTrue("Collation key not 0 terminated", arr[arr.length - 1] == 0); + len--; + String foo = new String(arr, 0, len, "iso8859-1"); + + arr = mColl.getCollationKey(sixteen).toByteArray(); + len = arr.length; + assertTrue("Collation key not 0 terminated", arr[arr.length - 1] == 0); + len--; + String bar = new String(arr, 0, len, "iso8859-1"); + + assertTrue("Collation keys should differ", foo.equals(bar)); + } catch (UnsupportedEncodingException ex) { + fail("UnsupportedEncodingException"); + } + } +} diff --git a/text/src/test/java/org/apache/harmony/text/tests/java/text/DataFormatFieldTest.java b/text/src/test/java/org/apache/harmony/text/tests/java/text/DataFormatFieldTest.java new file mode 100644 index 0000000..9abc8b9 --- /dev/null +++ b/text/src/test/java/org/apache/harmony/text/tests/java/text/DataFormatFieldTest.java @@ -0,0 +1,223 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.text.tests.java.text; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InvalidObjectException; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +import java.text.DateFormat; +import java.text.DateFormat.Field; +import java.util.Calendar; + +import junit.framework.TestCase; + +public class DataFormatFieldTest extends TestCase { + + public void test_ConstructorLjava_lang_StringLjava_lang_String() { + // Regression for HARMONY-178 + MyField field = new MyField("day of month", Calendar.ERA); + + assertEquals("field has wrong name", "day of month", field.getName()); + assertEquals("field has wrong Calendar field number", Calendar.ERA, + field.getCalendarField()); + + DateFormat.Field realField = DateFormat.Field + .ofCalendarField(Calendar.ERA); + assertSame("Modified calendar field with the same field number", + DateFormat.Field.ERA, realField); + + DateFormat.Field realField2 = DateFormat.Field + .ofCalendarField(Calendar.DAY_OF_MONTH); + assertSame("Modified calendar field with the same field number", + DateFormat.Field.DAY_OF_MONTH, realField2); + } + + static class MyField extends DateFormat.Field { + private static final long serialVersionUID = 1L; + + protected MyField(String fieldName, int calendarField) { + super(fieldName, calendarField); + } + + protected String getName() { + return super.getName(); + } + } + + /** + * @tests java.text.DateFormat$Field#Field(java.lang.String, int) + */ + public void test_ConstructorLjava_lang_StringI() { + MyField field = new MyField("a field", Calendar.DAY_OF_WEEK); + + assertEquals("field has wrong name", "a field", field.getName()); + assertEquals("field has wrong Calendar field number", + Calendar.DAY_OF_WEEK, field.getCalendarField()); + + DateFormat.Field realField = DateFormat.Field + .ofCalendarField(Calendar.DAY_OF_WEEK); + assertSame("Modified calendar field with the same field number", + DateFormat.Field.DAY_OF_WEEK, realField); + } + + /** + * @tests java.text.DateFormat$Field#Field(java.lang.String, int) + */ + public void test_Constructor2() { + MyField field = new MyField("day of month", Calendar.ERA); + + assertEquals("field has wrong name", "day of month", field.getName()); + assertEquals("field has wrong Calendar field number", Calendar.ERA, + field.getCalendarField()); + + DateFormat.Field realField = DateFormat.Field + .ofCalendarField(Calendar.ERA); + assertSame("Modified calendar field with the same field number", + DateFormat.Field.ERA, realField); + + DateFormat.Field realField2 = DateFormat.Field + .ofCalendarField(Calendar.DAY_OF_MONTH); + assertSame("Modified calendar field with the same field number", + DateFormat.Field.DAY_OF_MONTH, realField2); + } + + /** + * @tests java.text.DateFormat$Field#getCalendarField() + */ + public void test_getCalendarField() { + // Test for method int getCalendarField() + assertEquals("Field.AM_PM.getCalendarField() returned the wrong value", + Calendar.AM_PM, Field.AM_PM.getCalendarField()); + + // test special cases + assertEquals( + "Field.TIME_ZONE.getCalendarField() returned the wrong value", + -1, Field.TIME_ZONE.getCalendarField()); + assertEquals("Field.HOUR0.getCalendarField() returned the wrong value", + Calendar.HOUR, Field.HOUR0.getCalendarField()); + assertEquals("Field.HOUR1.getCalendarField() returned the wrong value", + -1, Field.HOUR1.getCalendarField()); + assertEquals( + "Field.HOUR_OF_DAY0.getCalendarField() returned the wrong value", + Calendar.HOUR_OF_DAY, Field.HOUR_OF_DAY0.getCalendarField()); + assertEquals( + "Field.HOUR_OF_DAY1.getCalendarField() returned the wrong value", + -1, Field.HOUR_OF_DAY1.getCalendarField()); + } + + /** + * @tests java.text.DateFormat$Field#ofCalendarField(int) + */ + public void test_ofCalendarFieldI() { + // Test for method static java.text.DateFormat.Field + // ofCalendarField(int) + assertSame("ofCalendarField(Calendar.AM_PM) returned the wrong value", + Field.AM_PM, Field.ofCalendarField(Calendar.AM_PM)); + + // test special cases + assertSame("ofCalendarField(Calendar.HOUR) returned the wrong value", + Field.HOUR0, Field.ofCalendarField(Calendar.HOUR)); + assertSame( + "ofCalendarField(Calendar.HOUR_OF_DAY) returned the wrong value", + Field.HOUR_OF_DAY0, Field.ofCalendarField(Calendar.HOUR_OF_DAY)); + + // test illegal args + try { + DateFormat.Field.ofCalendarField(-1); + fail("Expected IllegalArgumentException for ofCalendarField(-1)"); + } catch (IllegalArgumentException e) { + } + + try { + DateFormat.Field.ofCalendarField(Calendar.FIELD_COUNT); + fail("Expected IllegalArgumentException for ofCalendarField(Calendar.FIELD_COUNT)"); + } catch (IllegalArgumentException e) { + } + + // test Calendar fields that do not have corresponding DateFormat Fields + assertNull( + "ofCalendarField(Calendar.DST_OFFSET) returned the wrong value", + DateFormat.Field.ofCalendarField(Calendar.DST_OFFSET)); + assertNull( + "ofCalendarField(Calendar.ZONE_OFFSET) returned the wrong value", + DateFormat.Field.ofCalendarField(Calendar.ZONE_OFFSET)); + } + + /** + * @tests java.text.DateFormat$Field#readResolve() + */ + public void test_readResolve() { + // test for method java.lang.Object readResolve() + + // see serialization stress tests: + // implemented in + // SerializationStressTest4.test_writeObject_NumberFormat_Field() + + ObjectOutputStream out = null; + ObjectInputStream in = null; + try { + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + out = new ObjectOutputStream(bytes); + + DateFormat.Field dfield, dfield2; + MyField field; + + // a regular instance of DateFormat.Field + dfield = DateFormat.Field.MILLISECOND; + + // a subclass instance with null name + field = new MyField(null, Calendar.AM_PM); + + out.writeObject(dfield); + out.writeObject(field); + + in = new ObjectInputStream(new ByteArrayInputStream(bytes + .toByteArray())); + + try { + dfield2 = (Field) in.readObject(); + assertSame("resolved incorrectly", dfield, dfield2); + } catch (IllegalArgumentException e) { + fail("Unexpected IllegalArgumentException: " + e); + } + + try { + in.readObject(); + fail("Expected InvalidObjectException for subclass instance with null name"); + } catch (InvalidObjectException e) { + } + + } catch (IOException e) { + fail("unexpected IOException" + e); + } catch (ClassNotFoundException e) { + fail("unexpected ClassNotFoundException" + e); + } finally { + try { + if (out != null) + out.close(); + if (in != null) + in.close(); + } catch (IOException e) { + } + } + } +} diff --git a/text/src/test/java/org/apache/harmony/text/tests/java/text/DateFormatSymbolsTest.java b/text/src/test/java/org/apache/harmony/text/tests/java/text/DateFormatSymbolsTest.java new file mode 100644 index 0000000..2bf9dd2 --- /dev/null +++ b/text/src/test/java/org/apache/harmony/text/tests/java/text/DateFormatSymbolsTest.java @@ -0,0 +1,353 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.harmony.text.tests.java.text; + +import java.text.DateFormatSymbols; +import java.util.Arrays; +import java.util.Locale; + +public class DateFormatSymbolsTest extends junit.framework.TestCase { + + private DateFormatSymbols dfs; + + /** + * @tests java.text.DateFormatSymbols#DateFormatSymbols() + */ + public void test_Constructor() { + // Test for method java.text.DateFormatSymbols() + // Used in tests + try { + new DateFormatSymbols(); + } catch (Exception e) { + fail("Constructor failed."); + } + } + + /** + * @tests java.text.DateFormatSymbols#DateFormatSymbols(java.util.Locale) + */ + public void test_ConstructorLjava_util_Locale() { + // Test for method java.text.DateFormatSymbols(java.util.Locale) + try { + new DateFormatSymbols(new Locale("en", "us")); + } catch (Exception e) { + fail("Constructor failed."); + } + } + + /** + * @tests java.text.DateFormatSymbols#clone() + */ + public void test_clone() { + // Test for method java.lang.Object java.text.DateFormatSymbols.clone() + DateFormatSymbols symbols = new DateFormatSymbols(); + DateFormatSymbols clone = (DateFormatSymbols) symbols.clone(); + assertTrue("Not equal", symbols.equals(clone)); + } + + /** + * @tests java.text.DateFormatSymbols#equals(java.lang.Object) + */ + public void test_equalsLjava_lang_Object() { + // Test for method boolean + // java.text.DateFormatSymbols.equals(java.lang.Object) + assertTrue("Equal object returned true", dfs.equals(dfs.clone())); + dfs.setLocalPatternChars("KKKKKKKKK"); + assertTrue("Un-Equal objects returned false", !dfs + .equals(new DateFormatSymbols())); + } + + /** + * @tests java.text.DateFormatSymbols#getAmPmStrings() + */ + public void test_getAmPmStrings() { + // Test for method java.lang.String [] + // java.text.DateFormatSymbols.getAmPmStrings() + String[] retVal = dfs.getAmPmStrings(); + String[] val = { "AM", "PM" }; + if (retVal.length != val.length) + fail("Returned wrong array"); + for (int i = 0; i < val.length; i++) + assertTrue("Array values do not match", retVal[i].equals(val[i])); + } + + /** + * @tests java.text.DateFormatSymbols#getEras() + */ + public void test_getEras() { + // Test for method java.lang.String [] + // java.text.DateFormatSymbols.getEras() + String[] retVal = dfs.getEras(); + String[] val = { "BC", "AD" }; + if (retVal.length != val.length) + fail("Returned wrong array"); + for (int i = 0; i < val.length; i++) + assertTrue("Array values do not match", retVal[i].equals(val[i])); + } + + /** + * @tests java.text.DateFormatSymbols#getLocalPatternChars() + */ + public void test_getLocalPatternChars() { + // Test for method java.lang.String + // java.text.DateFormatSymbols.getLocalPatternChars() + String retVal = dfs.getLocalPatternChars(); + + String val = "GyMdkHmsSEDFwWahKzZ"; + + assertTrue("Returned incorrect pattern string", retVal.equals(val)); + } + + /** + * @tests java.text.DateFormatSymbols#getMonths() + */ + public void test_getMonths() { + // Test for method java.lang.String [] + // java.text.DateFormatSymbols.getMonths() + String[] retVal = dfs.getMonths(); + String[] val = { "January", "February", "March", "April", "May", + "June", "July", "August", "September", "October", "November", + "December", "" }; + if (retVal.length != val.length) + fail("Returned wrong array: " + retVal.length); + for (int i = 0; i < val.length; i++) + assertTrue("Array values do not match", retVal[i].equals(val[i])); + } + + /** + * @tests java.text.DateFormatSymbols#getShortMonths() + */ + public void test_getShortMonths() { + // Test for method java.lang.String [] + // java.text.DateFormatSymbols.getShortMonths() + String[] retVal = dfs.getShortMonths(); + String[] val = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", + "Aug", "Sep", "Oct", "Nov", "Dec", "" }; + if (retVal.length != val.length) + fail("Returned wrong array"); + for (int i = 0; i < val.length; i++) + assertTrue("Array values do not match", retVal[i].equals(val[i])); + } + + /** + * @tests java.text.DateFormatSymbols#getShortWeekdays() + */ + public void test_getShortWeekdays() { + // Test for method java.lang.String [] + // java.text.DateFormatSymbols.getShortWeekdays() + String[] retVal = dfs.getShortWeekdays(); + String[] val = { "", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; + if (retVal.length != val.length) + fail("Returned wrong array"); + for (int i = 0; i < val.length; i++) + assertTrue("Array values do not match", retVal[i].equals(val[i])); + } + + /** + * @tests java.text.DateFormatSymbols#getWeekdays() + */ + public void test_getWeekdays() { + // Test for method java.lang.String [] + // java.text.DateFormatSymbols.getWeekdays() + String[] retVal = dfs.getWeekdays(); + String[] val = { "", "Sunday", "Monday", "Tuesday", "Wednesday", + "Thursday", "Friday", "Saturday" }; + if (retVal.length != val.length) + fail("Returned wrong array"); + for (int i = 0; i < val.length; i++) + assertTrue("Array values do not match", retVal[i].equals(val[i])); + } + + /** + * @tests java.text.DateFormatSymbols#getZoneStrings() + */ + public void test_getZoneStrings() { + // Test for method java.lang.String [][] + // java.text.DateFormatSymbols.getZoneStrings() + String[][] val = { { "XX" }, { "YY" } }; + dfs.setZoneStrings(val); + String[][] retVal = dfs.getZoneStrings(); + if (retVal.length != val.length) + fail("Returned wrong array"); + for (int i = 0; i < val.length; i++) + assertTrue("Failed to set strings", Arrays + .equals(retVal[i], val[i])); + } + + /** + * @tests java.text.DateFormatSymbols#hashCode() + */ + public void test_hashCode() { + // Test for method int java.text.DateFormatSymbols.hashCode() + int hc1 = dfs.hashCode(); + int hc2 = dfs.hashCode(); + assertTrue("hashCode() returned inconsistent number : " + hc1 + " - " + + hc2, hc1 == hc2); + + assertTrue("hashCode() returns different values for equal() objects", + dfs.hashCode() == dfs.clone().hashCode()); + } + + /** + * @tests java.text.DateFormatSymbols#setAmPmStrings(java.lang.String[]) + */ + public void test_setAmPmStrings$Ljava_lang_String() { + // Test for method void + // java.text.DateFormatSymbols.setAmPmStrings(java.lang.String []) + String[] val = { "XX", "YY" }; + dfs.setAmPmStrings(val); + String[] retVal = dfs.getAmPmStrings(); + if (retVal.length != val.length) + fail("Returned wrong array"); + for (int i = 0; i < val.length; i++) + assertTrue("Failed to set strings", retVal[i].equals(val[i])); + } + + /** + * @tests java.text.DateFormatSymbols#setEras(java.lang.String[]) + */ + public void test_setEras$Ljava_lang_String() { + // Test for method void + // java.text.DateFormatSymbols.setEras(java.lang.String []) + String[] val = { "XX", "YY" }; + dfs.setEras(val); + String[] retVal = dfs.getEras(); + if (retVal.length != val.length) + fail("Returned wrong array"); + for (int i = 0; i < val.length; i++) + assertTrue("Failed to set strings", retVal[i].equals(val[i])); + } + + /** + * @tests java.text.DateFormatSymbols#setLocalPatternChars(java.lang.String) + */ + public void test_setLocalPatternCharsLjava_lang_String() { + // Test for method void + // java.text.DateFormatSymbols.setLocalPatternChars(java.lang.String) + dfs.setLocalPatternChars("GyMZZkHmsSEHHFwWahKz"); + String retVal = dfs.getLocalPatternChars(); + String val = "GyMZZkHmsSEHHFwWahKz"; + assertTrue("Returned incorrect pattern string", retVal.equals(val)); + + try { + // Regression for HARMONY-466 + new DateFormatSymbols().setLocalPatternChars(null); + fail("NullPointerException expected"); + } catch (NullPointerException e) { + // expected + } + } + + /** + * @tests java.text.DateFormatSymbols#setMonths(java.lang.String[]) + */ + public void test_setMonths$Ljava_lang_String() { + // Test for method void + // java.text.DateFormatSymbols.setMonths(java.lang.String []) + String[] val = { "XX", "YY" }; + dfs.setMonths(val); + String[] retVal = dfs.getMonths(); + assertTrue("Return is identical", retVal != dfs.getMonths()); + if (retVal.length != val.length) + fail("Returned wrong array"); + for (int i = 0; i < val.length; i++) + assertTrue("Failed to set strings", retVal[i].equals(val[i])); + } + + /** + * @tests java.text.DateFormatSymbols#setShortMonths(java.lang.String[]) + */ + public void test_setShortMonths$Ljava_lang_String() { + // Test for method void + // java.text.DateFormatSymbols.setShortMonths(java.lang.String []) + String[] val = { "XX", "YY" }; + dfs.setShortMonths(val); + String[] retVal = dfs.getShortMonths(); + assertTrue("Return is identical", retVal != dfs.getShortMonths()); + if (retVal.length != val.length) + fail("Returned wrong array"); + for (int i = 0; i < val.length; i++) + assertTrue("Failed to set strings", retVal[i].equals(val[i])); + } + + /** + * @tests java.text.DateFormatSymbols#setShortWeekdays(java.lang.String[]) + */ + public void test_setShortWeekdays$Ljava_lang_String() { + // Test for method void + // java.text.DateFormatSymbols.setShortWeekdays(java.lang.String []) + String[] val = { "XX", "YY" }; + dfs.setShortWeekdays(val); + String[] retVal = dfs.getShortWeekdays(); + assertTrue("Return is identical", retVal != dfs.getShortWeekdays()); + if (retVal.length != val.length) + fail("Returned wrong array"); + for (int i = 0; i < val.length; i++) + assertTrue("Failed to set strings", retVal[i].equals(val[i])); + } + + /** + * @tests java.text.DateFormatSymbols#setWeekdays(java.lang.String[]) + */ + public void test_setWeekdays$Ljava_lang_String() { + // Test for method void + // java.text.DateFormatSymbols.setWeekdays(java.lang.String []) + String[] val = { "XX", "YY" }; + dfs.setWeekdays(val); + String[] retVal = dfs.getWeekdays(); + assertTrue("Return is identical", retVal != dfs.getWeekdays()); + if (retVal.length != val.length) + fail("Returned wrong array"); + for (int i = 0; i < val.length; i++) + assertTrue("Failed to set strings", retVal[i].equals(val[i])); + } + + /** + * @tests java.text.DateFormatSymbols#setZoneStrings(java.lang.String[][]) + */ + public void test_setZoneStrings$$Ljava_lang_String() { + // Test for method void + // java.text.DateFormatSymbols.setZoneStrings(java.lang.String [][]) + String[][] val = { { "XX" }, { "YY" } }; + dfs.setZoneStrings(val); + String[][] retVal = dfs.getZoneStrings(); + assertTrue("get returns identical", retVal != dfs.getZoneStrings()); + assertTrue("get[0] returns identical", retVal[0] != dfs + .getZoneStrings()[0]); + assertTrue("get returned identical", retVal != val); + if (retVal.length != val.length) + fail("Returned wrong array"); + for (int i = 0; i < val.length; i++) + assertTrue("Failed to set strings: " + retVal[i], Arrays.equals( + retVal[i], val[i])); + } + + /** + * Sets up the fixture, for example, open a network connection. This method + * is called before a test is executed. + */ + protected void setUp() { + dfs = new DateFormatSymbols(new Locale("en", "us")); + } + + /** + * Tears down the fixture, for example, close a network connection. This + * method is called after a test is executed. + */ + protected void tearDown() { + } +} diff --git a/text/src/test/java/org/apache/harmony/text/tests/java/text/DateFormatTest.java b/text/src/test/java/org/apache/harmony/text/tests/java/text/DateFormatTest.java new file mode 100644 index 0000000..733812f --- /dev/null +++ b/text/src/test/java/org/apache/harmony/text/tests/java/text/DateFormatTest.java @@ -0,0 +1,728 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.harmony.text.tests.java.text; + +import java.text.DateFormat; +import java.text.DateFormatSymbols; +import java.text.FieldPosition; +import java.text.NumberFormat; +import java.text.ParseException; +import java.text.ParsePosition; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.Locale; +import java.util.TimeZone; + +import junit.framework.Test; + +public class DateFormatTest extends junit.framework.TestCase { + + private class MockDateFormat extends DateFormat { + + private static final long serialVersionUID = 1L; + + public MockDateFormat() { + super(); + } + + @Override + public Date parse(String source, ParsePosition pos) { + // it is a fake + return null; + } + + @Override + public StringBuffer format(Date date, StringBuffer toAppendTo, + FieldPosition fieldPosition) { + // it is a fake + return null; + } + } + + /** + * @tests java.text.DateFormat#DateFormat() Test of method + * java.text.DateFormat#DateFormat(). + */ + public void test_Constructor() { + try { + new MockDateFormat(); + } catch (Exception e) { + fail("Unexpected exception " + e.toString()); + } + } + + /** + * @tests java.text.DateFormat#equals(java.lang.Object obj) Test of + * java.text.DateFormat#equals(java.lang.Object obj). + */ + public void test_equalsLjava_lang_Object() { + try { + DateFormat format = DateFormat.getInstance(); + DateFormat clone = (DateFormat) format.clone(); + assertTrue("Clone and parent are not equaled", format.equals(clone)); + assertTrue("Clone is equal to other object", !clone + .equals(DateFormat.getTimeInstance())); + format.setCalendar(Calendar.getInstance()); + assertTrue("Clone and parent are not equaled", format.equals(clone)); + } catch (Exception e) { + fail("Unexpected exception " + e.toString()); + } + } + + /** + * @tests java.text.DateFormat#format(java.util.Date) Test of method + * java.text.DateFormat#format(java.util.Date). + */ + public void test_formatLjava_util_Date() { + try { + DateFormat format = DateFormat.getDateTimeInstance( + DateFormat.SHORT, DateFormat.SHORT, Locale.US); + Date current = new Date(); + String dtf = format.format(current); + SimpleDateFormat sdf = new SimpleDateFormat("M/d/yy h:mm a"); + assertTrue("Incorrect date format", sdf.format(current).equals(dtf)); + } catch (Exception e) { + fail("Unexpected exception " + e.toString()); + } + } + + /** + * @tests java.text.DateFormat#format(Object, StringBuffer, FieldPosition) + * Test of method java.text.DateFormat#format(Object, StringBuffer, + * FieldPosition) + */ + public void test_formatLjava_lang_ObjectLjava_lang_StringBufferLjava_text_FieldPosition() { + try { + DateFormat format = DateFormat.getDateTimeInstance( + DateFormat.SHORT, DateFormat.SHORT, Locale.US); + Date current = new Date(); + StringBuffer toAppend = new StringBuffer(); + FieldPosition fp = new FieldPosition(DateFormat.YEAR_FIELD); + StringBuffer sb = format.format(current, toAppend, fp); + SimpleDateFormat sdf = new SimpleDateFormat("M/d/yy h:mm a"); + assertTrue("Incorrect date format", sdf.format(current).equals( + sb.toString())); + assertTrue("Incorrect beginIndex of filed position", fp + .getBeginIndex() == sb.lastIndexOf("/") + 1); + assertTrue("Incorrect endIndex of filed position", + fp.getEndIndex() == sb.lastIndexOf("/") + 3); + } catch (Exception e) { + fail("Unexpected exception " + e.toString()); + } + } + + /** + * @tests java.text.DateFormat#clone() + */ + public void test_clone() { + DateFormat format = DateFormat.getInstance(); + DateFormat clone = (DateFormat) format.clone(); + assertTrue("Clone not equal", format.equals(clone)); + clone.getNumberFormat().setMinimumFractionDigits(123); + assertTrue("Clone shares NumberFormat", !format.equals(clone)); + } + + /** + * @tests java.text.DateFormat#getAvailableLocales() + */ + public void test_getAvailableLocales() { + Locale[] locales = DateFormat.getAvailableLocales(); + assertTrue("No locales", locales.length > 0); + boolean english = false, german = false; + for (int i = locales.length; --i >= 0;) { + if (locales[i].equals(Locale.ENGLISH)) + english = true; + if (locales[i].equals(Locale.GERMAN)) + german = true; + DateFormat f1 = DateFormat.getDateTimeInstance(DateFormat.SHORT, + DateFormat.SHORT, locales[i]); + assertTrue("Doesn't work", + f1.format(new Date()).getClass() == String.class); + } + assertTrue("Missing locales", english && german); + } + + /** + * @tests java.text.DateFormat#getCalendar() + */ + public void test_getCalendar() { + DateFormat format = DateFormat.getInstance(); + Calendar cal1 = format.getCalendar(); + Calendar cal2 = format.getCalendar(); + assertTrue("Calendars not identical", cal1 == cal2); + } + + /** + * @tests java.text.DateFormat#getDateInstance() + */ + public void test_getDateInstance() { + SimpleDateFormat f2 = (SimpleDateFormat) DateFormat.getDateInstance(); + assertTrue("Wrong class", f2.getClass() == SimpleDateFormat.class); + assertTrue("Wrong default", f2.equals(DateFormat.getDateInstance( + DateFormat.DEFAULT, Locale.getDefault()))); + assertTrue("Wrong symbols", f2.getDateFormatSymbols().equals( + new DateFormatSymbols())); + assertTrue("Doesn't work", + f2.format(new Date()).getClass() == String.class); + } + + /** + * @tests java.text.DateFormat#getDateInstance(int) + */ + public void test_getDateInstanceI() { + assertTrue("Default not medium", + DateFormat.DEFAULT == DateFormat.MEDIUM); + + SimpleDateFormat f2 = (SimpleDateFormat) DateFormat + .getDateInstance(DateFormat.SHORT); + assertTrue("Wrong class1", f2.getClass() == SimpleDateFormat.class); + assertTrue("Wrong default1", f2.equals(DateFormat.getDateInstance( + DateFormat.SHORT, Locale.getDefault()))); + assertTrue("Wrong symbols1", f2.getDateFormatSymbols().equals( + new DateFormatSymbols())); + assertTrue("Doesn't work1", + f2.format(new Date()).getClass() == String.class); + + f2 = (SimpleDateFormat) DateFormat.getDateInstance(DateFormat.MEDIUM); + assertTrue("Wrong class2", f2.getClass() == SimpleDateFormat.class); + assertTrue("Wrong default2", f2.equals(DateFormat.getDateInstance( + DateFormat.MEDIUM, Locale.getDefault()))); + assertTrue("Wrong symbols2", f2.getDateFormatSymbols().equals( + new DateFormatSymbols())); + assertTrue("Doesn't work2", + f2.format(new Date()).getClass() == String.class); + + f2 = (SimpleDateFormat) DateFormat.getDateInstance(DateFormat.LONG); + assertTrue("Wrong class3", f2.getClass() == SimpleDateFormat.class); + assertTrue("Wrong default3", f2.equals(DateFormat.getDateInstance( + DateFormat.LONG, Locale.getDefault()))); + assertTrue("Wrong symbols3", f2.getDateFormatSymbols().equals( + new DateFormatSymbols())); + assertTrue("Doesn't work3", + f2.format(new Date()).getClass() == String.class); + + f2 = (SimpleDateFormat) DateFormat.getDateInstance(DateFormat.FULL); + assertTrue("Wrong class4", f2.getClass() == SimpleDateFormat.class); + assertTrue("Wrong default4", f2.equals(DateFormat.getDateInstance( + DateFormat.FULL, Locale.getDefault()))); + assertTrue("Wrong symbols4", f2.getDateFormatSymbols().equals( + new DateFormatSymbols())); + assertTrue("Doesn't work4", + f2.format(new Date()).getClass() == String.class); + + // regression test for HARMONY-940 + try { + DateFormat.getDateInstance(77); + fail("Should throw IAE"); + } catch (IllegalArgumentException iae) { + // expected + } + } + + /** + * @tests java.text.DateFormat#getDateInstance(int, java.util.Locale) + */ + public void test_getDateInstanceILjava_util_Locale() { + SimpleDateFormat f2 = (SimpleDateFormat) DateFormat.getDateInstance( + DateFormat.SHORT, Locale.GERMAN); + assertTrue("Wrong class", f2.getClass() == SimpleDateFormat.class); + assertTrue("Wrong symbols", f2.getDateFormatSymbols().equals( + new DateFormatSymbols(Locale.GERMAN))); + assertTrue("Doesn't work", + f2.format(new Date()).getClass() == String.class); + + f2 = (SimpleDateFormat) DateFormat.getDateInstance(DateFormat.MEDIUM, + Locale.GERMAN); + assertTrue("Wrong class", f2.getClass() == SimpleDateFormat.class); + assertTrue("Wrong symbols", f2.getDateFormatSymbols().equals( + new DateFormatSymbols(Locale.GERMAN))); + assertTrue("Doesn't work", + f2.format(new Date()).getClass() == String.class); + + f2 = (SimpleDateFormat) DateFormat.getDateInstance(DateFormat.LONG, + Locale.GERMAN); + assertTrue("Wrong class", f2.getClass() == SimpleDateFormat.class); + assertTrue("Wrong symbols", f2.getDateFormatSymbols().equals( + new DateFormatSymbols(Locale.GERMAN))); + assertTrue("Doesn't work", + f2.format(new Date()).getClass() == String.class); + + f2 = (SimpleDateFormat) DateFormat.getDateInstance(DateFormat.FULL, + Locale.GERMAN); + assertTrue("Wrong class", f2.getClass() == SimpleDateFormat.class); + assertTrue("Wrong symbols", f2.getDateFormatSymbols().equals( + new DateFormatSymbols(Locale.GERMAN))); + assertTrue("Doesn't work", + f2.format(new Date()).getClass() == String.class); + + // regression test for HARMONY-940 + try { + DateFormat.getDateInstance(77, Locale.GERMAN); + fail("Should throw IAE"); + } catch (IllegalArgumentException iae) { + // expected + } + } + + /** + * @tests java.text.DateFormat#getDateTimeInstance() + */ + public void test_getDateTimeInstance() { + SimpleDateFormat f2 = (SimpleDateFormat) DateFormat + .getDateTimeInstance(); + assertTrue("Wrong class", f2.getClass() == SimpleDateFormat.class); + assertTrue("Wrong default", f2.equals(DateFormat.getDateTimeInstance( + DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.getDefault()))); + assertTrue("Wrong symbols", f2.getDateFormatSymbols().equals( + new DateFormatSymbols())); + assertTrue("Doesn't work", + f2.format(new Date()).getClass() == String.class); + } + + private void testDateTime(int dStyle, int tStyle) { + SimpleDateFormat f2 = (SimpleDateFormat) DateFormat + .getDateTimeInstance(dStyle, tStyle); + assertTrue("Wrong class", f2.getClass() == SimpleDateFormat.class); + SimpleDateFormat date = (SimpleDateFormat) DateFormat.getDateInstance( + dStyle, Locale.getDefault()); + SimpleDateFormat time = (SimpleDateFormat) DateFormat.getTimeInstance( + tStyle, Locale.getDefault()); + assertTrue("Wrong default", f2.toPattern().equals( + date.toPattern() + " " + time.toPattern())); + assertTrue("Wrong symbols", f2.getDateFormatSymbols().equals( + new DateFormatSymbols())); + assertTrue("Doesn't work", + f2.format(new Date()).getClass() == String.class); + } + + /** + * @tests java.text.DateFormat#getDateTimeInstance(int, int) + */ + public void test_getDateTimeInstanceII() { + testDateTime(DateFormat.SHORT, DateFormat.SHORT); + testDateTime(DateFormat.SHORT, DateFormat.MEDIUM); + testDateTime(DateFormat.SHORT, DateFormat.LONG); + testDateTime(DateFormat.SHORT, DateFormat.FULL); + + testDateTime(DateFormat.MEDIUM, DateFormat.SHORT); + testDateTime(DateFormat.MEDIUM, DateFormat.MEDIUM); + testDateTime(DateFormat.MEDIUM, DateFormat.LONG); + testDateTime(DateFormat.MEDIUM, DateFormat.FULL); + + testDateTime(DateFormat.LONG, DateFormat.SHORT); + testDateTime(DateFormat.LONG, DateFormat.MEDIUM); + testDateTime(DateFormat.LONG, DateFormat.LONG); + testDateTime(DateFormat.LONG, DateFormat.FULL); + + testDateTime(DateFormat.FULL, DateFormat.SHORT); + testDateTime(DateFormat.FULL, DateFormat.MEDIUM); + testDateTime(DateFormat.FULL, DateFormat.LONG); + testDateTime(DateFormat.FULL, DateFormat.FULL); + + // regression test for HARMONY-940 + try { + DateFormat.getDateTimeInstance(77, 66); + fail("Should throw IAE"); + } catch (IllegalArgumentException iae) { + // expected + } + } + + private void testDateTimeLocale(int dStyle, int tStyle) { + SimpleDateFormat f2 = (SimpleDateFormat) DateFormat + .getDateTimeInstance(dStyle, tStyle, Locale.GERMAN); + assertTrue("Wrong class", f2.getClass() == SimpleDateFormat.class); + SimpleDateFormat date = (SimpleDateFormat) DateFormat.getDateInstance( + dStyle, Locale.GERMAN); + SimpleDateFormat time = (SimpleDateFormat) DateFormat.getTimeInstance( + tStyle, Locale.GERMAN); + assertTrue("Wrong default", f2.toPattern().equals( + date.toPattern() + " " + time.toPattern())); + assertTrue("Wrong symbols", f2.getDateFormatSymbols().equals( + new DateFormatSymbols(Locale.GERMAN))); + assertTrue("Doesn't work", + f2.format(new Date()).getClass() == String.class); + } + + /** + * @tests java.text.DateFormat#getDateTimeInstance(int, int, + * java.util.Locale) + */ + public void test_getDateTimeInstanceIILjava_util_Locale() { + testDateTimeLocale(DateFormat.SHORT, DateFormat.SHORT); + testDateTimeLocale(DateFormat.SHORT, DateFormat.MEDIUM); + testDateTimeLocale(DateFormat.SHORT, DateFormat.LONG); + testDateTimeLocale(DateFormat.SHORT, DateFormat.FULL); + + testDateTimeLocale(DateFormat.MEDIUM, DateFormat.SHORT); + testDateTimeLocale(DateFormat.MEDIUM, DateFormat.MEDIUM); + testDateTimeLocale(DateFormat.MEDIUM, DateFormat.LONG); + testDateTimeLocale(DateFormat.MEDIUM, DateFormat.FULL); + + testDateTimeLocale(DateFormat.LONG, DateFormat.SHORT); + testDateTimeLocale(DateFormat.LONG, DateFormat.MEDIUM); + testDateTimeLocale(DateFormat.LONG, DateFormat.LONG); + testDateTimeLocale(DateFormat.LONG, DateFormat.FULL); + + testDateTimeLocale(DateFormat.FULL, DateFormat.SHORT); + testDateTimeLocale(DateFormat.FULL, DateFormat.MEDIUM); + testDateTimeLocale(DateFormat.FULL, DateFormat.LONG); + testDateTimeLocale(DateFormat.FULL, DateFormat.FULL); + + // regression test for HARMONY-940 + try { + DateFormat.getDateTimeInstance(77, 66, Locale.GERMAN); + fail("Should throw IAE"); + } catch (IllegalArgumentException iae) { + // expected + } + } + + /** + * @tests java.text.DateFormat#getInstance() + */ + public void test_getInstance() { + SimpleDateFormat f2 = (SimpleDateFormat) DateFormat.getInstance(); + assertTrue("Wrong class", f2.getClass() == SimpleDateFormat.class); + assertTrue("Wrong default", f2.equals(DateFormat.getDateTimeInstance( + DateFormat.SHORT, DateFormat.SHORT, Locale.getDefault()))); + assertTrue("Wrong symbols", f2.getDateFormatSymbols().equals( + new DateFormatSymbols())); + assertTrue("Doesn't work", + f2.format(new Date()).getClass() == String.class); + } + + /** + * @tests java.text.DateFormat#getNumberFormat() + */ + public void test_getNumberFormat() { + DateFormat format = DateFormat.getInstance(); + NumberFormat nf1 = format.getNumberFormat(); + NumberFormat nf2 = format.getNumberFormat(); + assertTrue("NumberFormats not identical", nf1 == nf2); + } + + /** + * @tests java.text.DateFormat#getTimeInstance() + */ + public void test_getTimeInstance() { + SimpleDateFormat f2 = (SimpleDateFormat) DateFormat.getTimeInstance(); + assertTrue("Wrong class", f2.getClass() == SimpleDateFormat.class); + assertTrue("Wrong default", f2.equals(DateFormat.getTimeInstance( + DateFormat.DEFAULT, Locale.getDefault()))); + assertTrue("Wrong symbols", f2.getDateFormatSymbols().equals( + new DateFormatSymbols())); + assertTrue("Doesn't work", + f2.format(new Date()).getClass() == String.class); + } + + /** + * @tests java.text.DateFormat#getTimeInstance(int) + */ + public void test_getTimeInstanceI() { + SimpleDateFormat f2 = (SimpleDateFormat) DateFormat + .getTimeInstance(DateFormat.SHORT); + assertTrue("Wrong class1", f2.getClass() == SimpleDateFormat.class); + assertTrue("Wrong default1", f2.equals(DateFormat.getTimeInstance( + DateFormat.SHORT, Locale.getDefault()))); + assertTrue("Wrong symbols1", f2.getDateFormatSymbols().equals( + new DateFormatSymbols())); + assertTrue("Doesn't work1", + f2.format(new Date()).getClass() == String.class); + + f2 = (SimpleDateFormat) DateFormat.getTimeInstance(DateFormat.MEDIUM); + assertTrue("Wrong class2", f2.getClass() == SimpleDateFormat.class); + assertTrue("Wrong default2", f2.equals(DateFormat.getTimeInstance( + DateFormat.MEDIUM, Locale.getDefault()))); + assertTrue("Wrong symbols2", f2.getDateFormatSymbols().equals( + new DateFormatSymbols())); + assertTrue("Doesn't work2", + f2.format(new Date()).getClass() == String.class); + + f2 = (SimpleDateFormat) DateFormat.getTimeInstance(DateFormat.LONG); + assertTrue("Wrong class3", f2.getClass() == SimpleDateFormat.class); + assertTrue("Wrong default3", f2.equals(DateFormat.getTimeInstance( + DateFormat.LONG, Locale.getDefault()))); + assertTrue("Wrong symbols3", f2.getDateFormatSymbols().equals( + new DateFormatSymbols())); + assertTrue("Doesn't work3", + f2.format(new Date()).getClass() == String.class); + + f2 = (SimpleDateFormat) DateFormat.getTimeInstance(DateFormat.FULL); + assertTrue("Wrong class4", f2.getClass() == SimpleDateFormat.class); + assertTrue("Wrong default4", f2.equals(DateFormat.getTimeInstance( + DateFormat.FULL, Locale.getDefault()))); + assertTrue("Wrong symbols4", f2.getDateFormatSymbols().equals( + new DateFormatSymbols())); + assertTrue("Doesn't work4", + f2.format(new Date()).getClass() == String.class); + + // regression test for HARMONY-940 + try { + DateFormat.getTimeInstance(77); + fail("Should throw IAE"); + } catch (IllegalArgumentException iae) { + // expected + } + } + + /** + * @tests java.text.DateFormat#getTimeInstance(int, java.util.Locale) + */ + public void test_getTimeInstanceILjava_util_Locale() { + SimpleDateFormat f2 = (SimpleDateFormat) DateFormat.getTimeInstance( + DateFormat.SHORT, Locale.GERMAN); + assertTrue("Wrong class", f2.getClass() == SimpleDateFormat.class); + assertTrue("Wrong symbols", f2.getDateFormatSymbols().equals( + new DateFormatSymbols(Locale.GERMAN))); + assertTrue("Doesn't work", + f2.format(new Date()).getClass() == String.class); + + f2 = (SimpleDateFormat) DateFormat.getTimeInstance(DateFormat.MEDIUM, + Locale.GERMAN); + assertTrue("Wrong class", f2.getClass() == SimpleDateFormat.class); + assertTrue("Wrong symbols", f2.getDateFormatSymbols().equals( + new DateFormatSymbols(Locale.GERMAN))); + assertTrue("Doesn't work", + f2.format(new Date()).getClass() == String.class); + + f2 = (SimpleDateFormat) DateFormat.getTimeInstance(DateFormat.LONG, + Locale.GERMAN); + assertTrue("Wrong class", f2.getClass() == SimpleDateFormat.class); + assertTrue("Wrong symbols", f2.getDateFormatSymbols().equals( + new DateFormatSymbols(Locale.GERMAN))); + assertTrue("Doesn't work", + f2.format(new Date()).getClass() == String.class); + + f2 = (SimpleDateFormat) DateFormat.getTimeInstance(DateFormat.FULL, + Locale.GERMAN); + assertTrue("Wrong class", f2.getClass() == SimpleDateFormat.class); + assertTrue("Wrong symbols", f2.getDateFormatSymbols().equals( + new DateFormatSymbols(Locale.GERMAN))); + assertTrue("Doesn't work", + f2.format(new Date()).getClass() == String.class); + + try { + DateFormat.getTimeInstance(77, Locale.GERMAN); + fail("Should throw IAE"); + } catch (IllegalArgumentException iae) { + // expected + } + } + + /** + * @tests java.text.DateFormat#getTimeZone() Test of method + * java.text.DateFormat#getTimeZone(). + */ + public void test_getTimeZone() { + try { + DateFormat format = DateFormat.getInstance(); + TimeZone tz = format.getTimeZone(); + //if(1 == 1) + // throw new Exception(tz.getClass().getName()); + // We know we are not sun.util so: + assertFalse("Incorrect zone info", tz.getClass().getName().equals( + "sun.util.calendar.ZoneInfo")); + assertTrue("Incorrect time zone", tz.equals(format.getCalendar() + .getTimeZone())); + } catch (Exception e) { + fail("Unexpected exception " + e.toString()); + } + } + + /** + * @tests java.text.DateFormat#hashCode() Test of method + * java.text.DateFormat#hashCode(). + */ + public void test_hashCode() { + try { + DateFormat df1 = DateFormat.getInstance(); + DateFormat df2 = (DateFormat) df1.clone(); + assertTrue("Hash codes of clones are not equal", + df1.hashCode() == df2.hashCode()); + assertTrue("Hash codes of different objects are the same", df1 + .hashCode() != DateFormat.getDateInstance().hashCode()); + } catch (Exception e) { + fail("Unexpected exception " + e.toString()); + } + } + + /** + * @tests java.text.DateFormat#isLenient() Test of method + * java.text.DateFormat#isLenient(). + */ + public void test_isLenient() { + DateFormat df = DateFormat.getInstance(); + Calendar c = df.getCalendar(); + if (df.isLenient()) { + try { + c.set(Calendar.DAY_OF_MONTH, 32); + c.get(Calendar.DAY_OF_MONTH); + } catch (Exception e) { + fail("Unexpected exception " + e.toString()); + } + c.setLenient(false); + try { + c.set(Calendar.DAY_OF_MONTH, 32); + c.get(Calendar.DAY_OF_MONTH); + fail("Expected IllegalArgumentException was not thrown"); + } catch (IllegalArgumentException e) { + // expected + } catch (Exception e) { + fail("Unexpected exception " + e.toString()); + } + } else { + try { + c.set(Calendar.DAY_OF_MONTH, 32); + c.get(Calendar.DAY_OF_MONTH); + fail("Expected IllegalArgumentException was not thrown"); + } catch (IllegalArgumentException e) { + // expected + } catch (Exception e) { + fail("Unexpected exception " + e.toString()); + } + c.setLenient(true); + try { + c.set(Calendar.DAY_OF_MONTH, 32); + c.get(Calendar.DAY_OF_MONTH); + } catch (Exception e) { + fail("Unexpected exception " + e.toString()); + } + } + } + + /** + * @tests java.text.DateFormat#setCalendar(java.util.Calendar) + */ + public void test_setCalendarLjava_util_Calendar() { + DateFormat format = DateFormat.getInstance(); + Calendar cal = Calendar.getInstance(); + format.setCalendar(cal); + assertTrue("Not identical Calendar", cal == format.getCalendar()); + } + + /** + * @tests java.text.DateFormat#setNumberFormat(java.text.NumberFormat) + */ + public void test_setNumberFormatLjava_text_NumberFormat() { + DateFormat format = DateFormat.getInstance(); + NumberFormat f1 = NumberFormat.getInstance(); + format.setNumberFormat(f1); + assertTrue("Not identical NumberFormat", f1 == format.getNumberFormat()); + } + + /** + * @tests java.text.DateFormat#parse(String) + */ + public void test_parse_LString() { + DateFormat format = DateFormat.getInstance(); + try { + format.parse("not a Date"); + fail("should throw ParseException first"); + } catch (ParseException e) { + assertNotNull(e.getMessage()); + } + } + + /** + * @tests java.text.DateFormat#parseObject(String, ParsePosition) Test of + * method java.text.DateFormat#parseObject(String, ParsePosition). + * Case 1: Try to parse correct data string. Case 2: Try to parse + * partialy correct data string. Case 3: Try to use argument + * ParsePosition as null. + */ + public void test_parseObjectLjava_lang_StringLjava_text_ParsePosition() { + DateFormat df = DateFormat.getInstance(); + try { + // case 1: Try to parse correct data string. + Date current = new Date(); + ParsePosition pp = new ParsePosition(0); + int parseIndex = pp.getIndex(); + Date result = (Date) df.parseObject(df.format(current), pp); + assertTrue("Parse operation return null", result != null); + assertTrue("ParseIndex is incorrect", pp.getIndex() != parseIndex); + + // case 2: Try to parse partially correct data string. + pp.setIndex(0); + char[] cur = df.format(current).toCharArray(); + cur[cur.length / 2] = 'Z'; + String partialCorrect = new String(cur); + result = (Date) df.parseObject(partialCorrect, pp); + assertTrue("Parse operation return not-null", result == null); + assertTrue("ParseIndex is incorrect", pp.getIndex() == 0); + assertTrue("ParseErrorIndex is incorrect", + pp.getErrorIndex() == cur.length / 2); + + // case 3: Try to use argument ParsePosition as null. + try { + df.parseObject(df.format(current), null); + fail("Expected NullPointerException was not thrown"); + } catch (NullPointerException e) { + // expected + } + } catch (Exception e) { + fail("Unexpected exception " + e.toString()); + } + } + + /** + * @tests java.text.DateFormat#setLenient(boolean) Test of method + * java.text.DateFormat#setLenient(boolean). + */ + public void test_setLenientZ() { + DateFormat df = DateFormat.getInstance(); + Calendar c = df.getCalendar(); + try { + c.setLenient(true); + try { + c.set(Calendar.DAY_OF_MONTH, 32); + c.get(Calendar.DAY_OF_MONTH); + } catch (Exception e) { + fail("Unexpected exception " + e.toString()); + } + c.setLenient(false); + try { + c.set(Calendar.DAY_OF_MONTH, 32); + c.get(Calendar.DAY_OF_MONTH); + fail("Expected IllegalArgumentException was not thrown"); + } catch (IllegalArgumentException e) { + // expected + } catch (Exception e) { + fail("Unexpected exception " + e.toString()); + } + } catch (Exception e) { + fail("Uexpected exception " + e.toString()); + } + } + + /** + * @tests java.text.DateFormat#setTimeZone(TimeZone) Test of method + * java.text.DateFormat#setTimeZone(TimeZone). + */ + public void test_setTimeZoneLjava_util_TimeZone() { + try { + DateFormat format = DateFormat.getInstance(); + TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles"); + format.setTimeZone(tz); + assertTrue("TimeZone is set incorrectly", tz.equals(format + .getTimeZone())); + } catch (Exception e) { + fail("Unexpected exception " + e.toString()); + } + } +} diff --git a/text/src/test/java/org/apache/harmony/text/tests/java/text/DecimalFormatSymbolsTest.java b/text/src/test/java/org/apache/harmony/text/tests/java/text/DecimalFormatSymbolsTest.java new file mode 100644 index 0000000..4108292 --- /dev/null +++ b/text/src/test/java/org/apache/harmony/text/tests/java/text/DecimalFormatSymbolsTest.java @@ -0,0 +1,536 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.text.tests.java.text; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.text.NumberFormat; +import java.util.Currency; +import java.util.Locale; + +import junit.framework.Test; +import junit.framework.TestCase; + +public class DecimalFormatSymbolsTest extends TestCase { + + DecimalFormatSymbols dfs; + + DecimalFormatSymbols dfsUS; + + /** + * @tests java.text.DecimalFormatSymbols#DecimalFormatSymbols() Test of + * method java.text.DecimalFormatSymbols#DecimalFormatSymbols(). + */ + public void test_Constructor() { + // Test for method java.text.DecimalFormatSymbols() + try { + new DecimalFormatSymbols(); + } catch (Exception e) { + fail("Unexpected exception " + e.toString()); + } + } + + /** + * @tests java.text.DecimalFormatSymbols#DecimalFormatSymbols(java.util.Locale) + */ + public void test_ConstructorLjava_util_Locale() { + DecimalFormatSymbols dfs = new DecimalFormatSymbols(new Locale("en", + "us")); + assertEquals("Returned incorrect symbols", '%', dfs.getPercent()); + } + + /** + * @tests java.text.DecimalFormatSymbols#clone() Test of method + * java.text.DecimalFormatSymbols#clone(). Case 1: Compare of + * internal variables of cloned objects. Case 2: Compare of clones. + * Case 3: Change the content of the clone. + */ + public void test_clone() { + try { + // case 1: Compare of internal variables of cloned objects + DecimalFormatSymbols fs = new DecimalFormatSymbols(Locale.US); + DecimalFormatSymbols fsc = (DecimalFormatSymbols) fs.clone(); + assertEquals(fs.getCurrency(), fsc.getCurrency()); + + // case 2: Compare of clones + fs = new DecimalFormatSymbols(); + DecimalFormatSymbols fsc2 = (DecimalFormatSymbols) (fs.clone()); + // make sure the objects are equal + assertTrue("Object's clone isn't equal!", fs.equals(fsc2)); + + // case 3: + // change the content of the clone and make sure it's not equal + // anymore + // verifies that it's data is now distinct from the original + fs.setNaN("not-a-number"); + assertTrue("Object's changed clone should not be equal!", !fs + .equals(fsc2)); + } catch (Exception e) { + fail("Unexpected exception " + e.toString()); + } + } + + /** + * @tests java.text.DecimalFormatSymbols#equals(java.lang.Object) + */ + public void test_equalsLjava_lang_Object() { + assertTrue("Equal objects returned false", dfs.equals(dfs.clone())); + dfs.setDigit('B'); + assertTrue("Un-Equal objects returned true", !dfs + .equals(new DecimalFormatSymbols())); + + } + + /** + * @tests java.text.DecimalFormatSymbols#getCurrency() + */ + public void test_getCurrency() { + Currency currency = Currency.getInstance("USD"); + assertTrue("Returned incorrect currency", + dfsUS.getCurrency() == currency); + + // BEGIN android-changed + // use cs_CZ instead + //Currency currK = Currency.getInstance("KRW"); + Currency currC = Currency.getInstance("CZK"); + Currency currX = Currency.getInstance("XXX"); + Currency currE = Currency.getInstance("EUR"); + // Currency currF = Currency.getInstance("FRF"); + + DecimalFormatSymbols dfs1 = new DecimalFormatSymbols(new Locale("cs", + "CZ")); + assertTrue("Test1: Returned incorrect currency", + dfs1.getCurrency() == currC); + assertEquals("Test1: Returned incorrect currencySymbol", "K\u010d", dfs1 + .getCurrencySymbol()); + assertEquals("Test1: Returned incorrect intlCurrencySymbol", "CZK", + dfs1.getInternationalCurrencySymbol()); + + dfs1 = new DecimalFormatSymbols(new Locale("", "CZ")); + assertTrue("Test2: Returned incorrect currency", + dfs1.getCurrency() == currC); + assertEquals("Test2: Returned incorrect currencySymbol", "CZK", dfs1 + .getCurrencySymbol()); + assertEquals("Test2: Returned incorrect intlCurrencySymbol", "CZK", + dfs1.getInternationalCurrencySymbol()); + + dfs1 = new DecimalFormatSymbols(new Locale("cs", "")); + assertEquals("Test3: Returned incorrect currency", + currX, dfs1.getCurrency()); + assertEquals("Test3: Returned incorrect currencySymbol", "\u00a4", dfs1 + .getCurrencySymbol()); + assertEquals("Test3: Returned incorrect intlCurrencySymbol", "XXX", + dfs1.getInternationalCurrencySymbol()); + + dfs1 = new DecimalFormatSymbols(new Locale("de", "AT")); + // END android-changed + assertTrue("Test4: Returned incorrect currency", + dfs1.getCurrency() == currE); + assertEquals("Test4: Returned incorrect currencySymbol", "\u20ac", dfs1 + .getCurrencySymbol()); + assertEquals("Test4: Returned incorrect intlCurrencySymbol", "EUR", + dfs1.getInternationalCurrencySymbol()); + + // RI fails these tests since it doesn't have the PREEURO variant + // dfs1 = new DecimalFormatSymbols(new Locale("fr", "FR","PREEURO")); + // assertTrue("Test5: Returned incorrect currency", dfs1.getCurrency() + // == currF); + // assertTrue("Test5: Returned incorrect currencySymbol", + // dfs1.getCurrencySymbol().equals("F")); + // assertTrue("Test5: Returned incorrect intlCurrencySymbol", + // dfs1.getInternationalCurrencySymbol().equals("FRF")); + } + + /** + * @tests java.text.DecimalFormatSymbols#getCurrencySymbol() + */ + public void test_getCurrencySymbol() { + assertEquals("Returned incorrect currencySymbol", "$", dfsUS + .getCurrencySymbol()); + } + + /** + * @tests java.text.DecimalFormatSymbols#getDecimalSeparator() + */ + public void test_getDecimalSeparator() { + dfs.setDecimalSeparator('*'); + assertEquals("Returned incorrect DecimalSeparator symbol", '*', dfs + .getDecimalSeparator()); + } + + /** + * @tests java.text.DecimalFormatSymbols#getDigit() + */ + public void test_getDigit() { + dfs.setDigit('*'); + assertEquals("Returned incorrect Digit symbol", '*', dfs.getDigit()); + } + + /** + * @tests java.text.DecimalFormatSymbols#getGroupingSeparator() + */ + public void test_getGroupingSeparator() { + dfs.setGroupingSeparator('*'); + assertEquals("Returned incorrect GroupingSeparator symbol", '*', dfs + .getGroupingSeparator()); + } + + /** + * @tests java.text.DecimalFormatSymbols#getInfinity() + */ + public void test_getInfinity() { + dfs.setInfinity("&"); + assertTrue("Returned incorrect Infinity symbol", + dfs.getInfinity() == "&"); + } + + /** + * @tests java.text.DecimalFormatSymbols#getInternationalCurrencySymbol() + */ + public void test_getInternationalCurrencySymbol() { + assertEquals("Returned incorrect InternationalCurrencySymbol", "USD", + dfsUS.getInternationalCurrencySymbol()); + } + + /** + * @tests java.text.DecimalFormatSymbols#getMinusSign() + */ + public void test_getMinusSign() { + dfs.setMinusSign('&'); + assertEquals("Returned incorrect MinusSign symbol", '&', dfs + .getMinusSign()); + } + + /** + * @tests java.text.DecimalFormatSymbols#getMonetaryDecimalSeparator() Test + * of method + * java.text.DecimalFormatSymbols#getMonetaryDecimalSeparator(). + */ + public void test_getMonetaryDecimalSeparator() { + try { + dfs.setMonetaryDecimalSeparator(','); + assertEquals("Returned incorrect MonetaryDecimalSeparator symbol", + ',', dfs.getMonetaryDecimalSeparator()); + } catch (Exception e) { + fail("Unexpected exception " + e.toString()); + } + } + + /** + * @tests java.text.DecimalFormatSymbols#getNaN() + */ + public void test_getNaN() { + dfs.setNaN("NAN!!"); + assertEquals("Returned incorrect nan symbol", "NAN!!", dfs.getNaN()); + } + + /** + * @tests java.text.DecimalFormatSymbols#getPatternSeparator() + */ + public void test_getPatternSeparator() { + dfs.setPatternSeparator('X'); + assertEquals("Returned incorrect PatternSeparator symbol", 'X', dfs + .getPatternSeparator()); + } + + /** + * @tests java.text.DecimalFormatSymbols#getPercent() + */ + public void test_getPercent() { + dfs.setPercent('*'); + assertEquals("Returned incorrect Percent symbol", '*', dfs.getPercent()); + } + + /** + * @tests java.text.DecimalFormatSymbols#getPerMill() + */ + public void test_getPerMill() { + dfs.setPerMill('#'); + assertEquals("Returned incorrect PerMill symbol", '#', dfs.getPerMill()); + } + + /** + * @tests java.text.DecimalFormatSymbols#getZeroDigit() + */ + public void test_getZeroDigit() { + dfs.setZeroDigit('*'); + assertEquals("Returned incorrect ZeroDigit symbol", '*', dfs + .getZeroDigit()); + } + + /** + * @tests java.text.DecimalFormatSymbols#hashCode() Test of method + * java.text.DecimalFormatSymbols#hashCode(). + */ + public void test_hashCode() { + try { + DecimalFormatSymbols dfs1 = new DecimalFormatSymbols(); + DecimalFormatSymbols dfs2 = (DecimalFormatSymbols) dfs1.clone(); + assertTrue("Hash codes of equal object are equal", dfs2 + .hashCode() == dfs1.hashCode()); + dfs1.setInfinity("infinity_infinity"); + // BEGIN android-changed + assertTrue("Hash codes of non-equal objects are equal", dfs2 + .hashCode() != dfs1.hashCode()); + // END android-changed + } catch (Exception e) { + fail("Unexpected exception " + e.toString()); + } + } + + /** + * @tests java.text.DecimalFormatSymbols#setCurrency(java.util.Currency) + */ + public void test_setCurrencyLjava_util_Currency() { + Locale locale = Locale.CANADA; + DecimalFormatSymbols dfs = ((DecimalFormat) NumberFormat + .getCurrencyInstance(locale)).getDecimalFormatSymbols(); + + try { + dfs.setCurrency(null); + fail("Expected NullPointerException"); + } catch (NullPointerException e) { + } + + Currency currency = Currency.getInstance("JPY"); + dfs.setCurrency(currency); + + assertTrue("Returned incorrect currency", currency == dfs.getCurrency()); + assertTrue("Returned incorrect currency symbol", currency.getSymbol( + locale).equals(dfs.getCurrencySymbol())); + assertTrue("Returned incorrect international currency symbol", currency + .getCurrencyCode().equals(dfs.getInternationalCurrencySymbol())); + } + + /** + * @tests java.text.DecimalFormatSymbols#setCurrencySymbol(java.lang.String) + * Test of method + * java.text.DecimalFormatSymbols#setCurrencySymbol(java.lang.String). + */ + public void test_setCurrencySymbolLjava_lang_String() { + try { + dfs.setCurrencySymbol("$"); + assertEquals("Returned incorrect CurrencySymbol symbol", "$", dfs + .getCurrencySymbol()); + } catch (Exception e) { + fail("Unexpected exception " + e.toString()); + } + } + + /** + * @tests java.text.DecimalFormatSymbols#setDecimalSeparator(char) + */ + public void test_setDecimalSeparatorC() { + dfs.setDecimalSeparator('*'); + assertEquals("Returned incorrect DecimalSeparator symbol", '*', dfs + .getDecimalSeparator()); + } + + /** + * @tests java.text.DecimalFormatSymbols#setDigit(char) + */ + public void test_setDigitC() { + dfs.setDigit('*'); + assertEquals("Returned incorrect Digit symbol", '*', dfs.getDigit()); + } + + /** + * @tests java.text.DecimalFormatSymbols#setGroupingSeparator(char) + */ + public void test_setGroupingSeparatorC() { + dfs.setGroupingSeparator('*'); + assertEquals("Returned incorrect GroupingSeparator symbol", '*', dfs + .getGroupingSeparator()); + } + + /** + * @tests java.text.DecimalFormatSymbols#setInfinity(java.lang.String) + */ + public void test_setInfinityLjava_lang_String() { + dfs.setInfinity("&"); + assertTrue("Returned incorrect Infinity symbol", + dfs.getInfinity() == "&"); + } + + /** + * @tests java.text.DecimalFormatSymbols#setInternationalCurrencySymbol(java.lang.String) + */ + public void test_setInternationalCurrencySymbolLjava_lang_String() { + Locale locale = Locale.CANADA; + DecimalFormatSymbols dfs = ((DecimalFormat) NumberFormat + .getCurrencyInstance(locale)).getDecimalFormatSymbols(); + Currency currency = Currency.getInstance("JPY"); + dfs.setInternationalCurrencySymbol(currency.getCurrencyCode()); + + assertTrue("Test1: Returned incorrect currency", currency == dfs + .getCurrency()); + assertTrue("Test1: Returned incorrect currency symbol", currency + .getSymbol(locale).equals(dfs.getCurrencySymbol())); + assertTrue("Test1: Returned incorrect international currency symbol", + currency.getCurrencyCode().equals( + dfs.getInternationalCurrencySymbol())); + + String symbol = dfs.getCurrencySymbol(); + dfs.setInternationalCurrencySymbol("bogus"); + assertNull("Test2: Returned incorrect currency", dfs.getCurrency()); + assertTrue("Test2: Returned incorrect currency symbol", dfs + .getCurrencySymbol().equals(symbol)); + assertEquals("Test2: Returned incorrect international currency symbol", + "bogus", dfs.getInternationalCurrencySymbol()); + } + + /** + * @tests java.text.DecimalFormatSymbols#setMinusSign(char) + */ + public void test_setMinusSignC() { + dfs.setMinusSign('&'); + assertEquals("Returned incorrect MinusSign symbol", '&', dfs + .getMinusSign()); + } + + /** + * @tests java.text.DecimalFormatSymbols#setMonetaryDecimalSeparator(char) + * Test of method + * java.text.DecimalFormatSymbols#setMonetaryDecimalSeparator(char). + */ + public void test_setMonetaryDecimalSeparatorC() { + try { + dfs.setMonetaryDecimalSeparator('#'); + assertEquals("Returned incorrect MonetaryDecimalSeparator symbol", + '#', dfs.getMonetaryDecimalSeparator()); + } catch (Exception e) { + fail("Unexpected exception " + e.toString()); + } + } + + /** + * @tests java.text.DecimalFormatSymbols#setNaN(java.lang.String) + */ + public void test_setNaNLjava_lang_String() { + dfs.setNaN("NAN!!"); + assertEquals("Returned incorrect nan symbol", "NAN!!", dfs.getNaN()); + } + + /** + * @tests java.text.DecimalFormatSymbols#setPatternSeparator(char) + */ + public void test_setPatternSeparatorC() { + dfs.setPatternSeparator('X'); + assertEquals("Returned incorrect PatternSeparator symbol", 'X', dfs + .getPatternSeparator()); + } + + /** + * @tests java.text.DecimalFormatSymbols#setPercent(char) + */ + public void test_setPercentC() { + dfs.setPercent('*'); + assertEquals("Returned incorrect Percent symbol", '*', dfs.getPercent()); + } + + /** + * @tests java.text.DecimalFormatSymbols#setPerMill(char) + */ + public void test_setPerMillC() { + dfs.setPerMill('#'); + assertEquals("Returned incorrect PerMill symbol", '#', dfs.getPerMill()); + } + + /** + * @tests java.text.DecimalFormatSymbols#setZeroDigit(char) + */ + public void test_setZeroDigitC() { + dfs.setZeroDigit('*'); + assertEquals("Set incorrect ZeroDigit symbol", '*', dfs.getZeroDigit()); + } + + /** + * Sets up the fixture, for example, open a network connection. This method + * is called before a test is executed. + */ + protected void setUp() { + dfs = new DecimalFormatSymbols(); + dfsUS = new DecimalFormatSymbols(new Locale("en", "us")); + } + + /** + * Tears down the fixture, for example, close a network connection. This + * method is called after a test is executed. + */ + protected void tearDown() { + } + + // Test serialization mechanism of DecimalFormatSymbols + public void test_serialization() throws Exception { + DecimalFormatSymbols symbols = new DecimalFormatSymbols(Locale.FRANCE); + Currency currency = symbols.getCurrency(); + assertNotNull(currency); + + // serialize + ByteArrayOutputStream byteOStream = new ByteArrayOutputStream(); + ObjectOutputStream objectOStream = new ObjectOutputStream(byteOStream); + objectOStream.writeObject(symbols); + + // and deserialize + ObjectInputStream objectIStream = new ObjectInputStream( + new ByteArrayInputStream(byteOStream.toByteArray())); + DecimalFormatSymbols symbolsD = (DecimalFormatSymbols) objectIStream + .readObject(); + + // The associated currency will not persist + currency = symbolsD.getCurrency(); + assertNotNull(currency); + } + + // Use RI to write DecimalFormatSymbols out, use Harmony to read + // DecimalFormatSymbols in. The read symbol will be equal with those + // instantiated inside Harmony. + + // This assertion will not come into existence the other way around. This is + // probably caused by different serialization mechanism used by RI and + // Harmony. + public void test_RIHarmony_compatible() throws Exception { + ObjectInputStream i = null; + try { + DecimalFormatSymbols symbols = new DecimalFormatSymbols( + Locale.FRANCE); + i = new ObjectInputStream( + getClass() + .getClassLoader() + .getResourceAsStream( + "/serialization/java/text/DecimalFormatSymbols.ser")); + DecimalFormatSymbols symbolsD = (DecimalFormatSymbols) i + .readObject(); + assertEquals(symbols, symbolsD); + } catch(NullPointerException e) { + assertNotNull("Failed to load /serialization/java/text/DecimalFormatSymbols.ser", i); + } finally { + try { + if (i != null) { + i.close(); + } + } catch (Exception e) { + // ignore + } + } + } +} diff --git a/text/src/test/java/org/apache/harmony/text/tests/java/text/DecimalFormatTest.java b/text/src/test/java/org/apache/harmony/text/tests/java/text/DecimalFormatTest.java new file mode 100644 index 0000000..f758662 --- /dev/null +++ b/text/src/test/java/org/apache/harmony/text/tests/java/text/DecimalFormatTest.java @@ -0,0 +1,1869 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.text.tests.java.text; + +import java.io.ObjectInputStream; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.text.AttributedCharacterIterator; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.text.FieldPosition; +import java.text.NumberFormat; +import java.text.ParsePosition; +import java.util.Currency; +import java.util.Locale; + +import junit.framework.TestCase; + +import org.apache.harmony.testframework.serialization.SerializationTest; + +import tests.support.Support_BitSet; +import tests.support.Support_DecimalFormat; + +public class DecimalFormatTest extends TestCase { + + public void testAttributedCharacterIterator() throws Exception { + // Regression for http://issues.apache.org/jira/browse/HARMONY-333 + AttributedCharacterIterator iterator = new DecimalFormat() + .formatToCharacterIterator(new Integer(1)); + assertNotNull(iterator); + assertFalse("attributes should exist", iterator.getAttributes() + .isEmpty()); + } + + /* + * Test the getter and setter of parseBigDecimal and parseIntegerOnly and + * test the default value of them. + */ + public void test_isParseBigDecimalLjava_lang_Boolean_isParseIntegerOnlyLjava_lang_Boolean() { + + // parseBigDecimal default to false + DecimalFormat form = (DecimalFormat) DecimalFormat + .getInstance(Locale.US); + assertFalse(form.isParseBigDecimal()); + form.setParseBigDecimal(true); + assertTrue(form.isParseBigDecimal()); + form.setParseBigDecimal(false); + assertFalse(form.isParseBigDecimal()); + + // parseIntegerOnly default to false + assertFalse(form.isParseIntegerOnly()); + } + + // Test the type of the returned object + + public void test_parseLjava_lang_String_Ljava_text_ParsePosition() { + DecimalFormat form = (DecimalFormat) DecimalFormat + .getInstance(Locale.US); + Number number = form.parse("23.1", new ParsePosition(0)); + assertTrue(number instanceof Double); + + // Test parsed object of type double when + // parseBigDecimal is set to true + + form = (DecimalFormat) DecimalFormat.getInstance(Locale.US); + number = form.parse("23.1", new ParsePosition(0)); + assertTrue(number instanceof Double); + + form.setParseBigDecimal(true); + number = form.parse("23.1", new ParsePosition(0)); + + assertTrue(number instanceof BigDecimal); + assertEquals(new BigDecimal("23.1"), number); + + // When parseIntegerOnly set to true, all float numbers will be parsed + // into Long. + // With the exception that, the value is out of the bound of Long or + // some special values such as NaN or Infinity. + + form = (DecimalFormat) DecimalFormat.getInstance(Locale.US); + form.setParseIntegerOnly(true); + number = form.parse("23.1f", new ParsePosition(0)); + + assertTrue(number instanceof Long); + + number = form.parse("23.0", new ParsePosition(0)); + assertTrue(number instanceof Long); + + number = form.parse("-0.0", new ParsePosition(0)); + assertTrue(number instanceof Long); + assertTrue(new Long(0).equals(number)); + + number = form.parse("-9,223,372,036,854,775,8080.00", + new ParsePosition(0)); + assertTrue(number instanceof Double); + + // Even if parseIntegerOnly is set to true, NaN will be parsed to Double + + form = (DecimalFormat) DecimalFormat.getInstance(Locale.US); + form.setParseIntegerOnly(true); + DecimalFormatSymbols symbols = new DecimalFormatSymbols(); + number = form.parse(symbols.getNaN(), new ParsePosition(0)); + assertTrue(number instanceof Double); + + // Even if parseIntegerOnly is set to true, Infinity will still be + // parsed to Double + + form = (DecimalFormat) DecimalFormat.getInstance(Locale.US); + form.setParseIntegerOnly(true); + symbols = new DecimalFormatSymbols(); + number = form.parse(symbols.getInfinity(), new ParsePosition(0)); + assertTrue(number instanceof Double); + + // ParseBigDecimal take precedence of parseBigInteger + + form = (DecimalFormat) DecimalFormat.getInstance(Locale.US); + form.setParseIntegerOnly(true); + form.setParseBigDecimal(true); + + number = form.parse("23.1f", new ParsePosition(0)); + + assertTrue(number instanceof BigDecimal); + + number = form.parse("23.0", new ParsePosition(0)); + assertTrue(number instanceof BigDecimal); + + number = form.parse("-9,223,372,036,854,775,8080.00", + new ParsePosition(0)); + assertFalse(number instanceof BigInteger); + assertTrue(number instanceof BigDecimal); +// BEGIN android-added + final String doubleMax2 = "359,538,626,972,463,141,629,054,847,463,408," + + "713,596,141,135,051,689,993,197,834,953,606,314,521,560,057,077," + + "521,179,117,265,533,756,343,080,917,907,028,764,928,468,642,653," + + "778,928,365,536,935,093,407,075,033,972,099,821,153,102,564,152," + + "490,980,180,778,657,888,151,737,016,910,267,884,609,166,473,806," + + "445,896,331,617,118,664,246,696,549,595,652,408,289,446,337,476," + + "354,361,838,599,762,500,808,052,368,249,716,736"; + number = form.parse(doubleMax2, new ParsePosition(0)); + assertTrue(number instanceof BigDecimal); + assertEquals(new BigDecimal(Double.MAX_VALUE).add(new BigDecimal( + Double.MAX_VALUE)), number); +// END android-added + // Test whether the parsed object is of type float. (To be specific, + // they are of type Double) + + form = (DecimalFormat) DecimalFormat.getInstance(Locale.US); + + number = form.parse("23.1f", new ParsePosition(0)); + assertTrue(number instanceof Double); + + form.setParseBigDecimal(true); + number = form.parse("23.1f", new ParsePosition(0)); + assertTrue(number instanceof BigDecimal); + assertEquals(new BigDecimal("23.1"), number); + + // Integer will be parsed to Long, unless parseBigDecimal is set to true + + form = (DecimalFormat) DecimalFormat.getInstance(Locale.US); + + number = form.parse("123", new ParsePosition(0)); + assertTrue(number instanceof Long); + + form.setParseBigDecimal(true); + number = form.parse("123", new ParsePosition(0)); + assertTrue(number instanceof BigDecimal); + assertEquals(new BigDecimal("123"), number); + + // NaN will be parsed to Double, no matter parseBigDecimal set or not. + + form = (DecimalFormat) DecimalFormat.getInstance(Locale.US); + symbols = new DecimalFormatSymbols(); + number = form.parse(symbols.getNaN() + "", new ParsePosition(0)); + assertTrue(number instanceof Double); + + form.setParseBigDecimal(true); + number = form.parse(symbols.getNaN() + "", new ParsePosition(0)); + assertTrue(number instanceof Double); + + // Infinity will be parsed to Double, no matter parseBigDecimal set or + // not. + + form = (DecimalFormat) DecimalFormat.getInstance(Locale.US); + symbols = new DecimalFormatSymbols(); + + number = form.parse(symbols.getInfinity(), new ParsePosition(0)); + + assertTrue(number instanceof Double); + assertEquals("Infinity", number.toString()); + // When set bigDecimal to true, the result of parsing infinity + + form = (DecimalFormat) DecimalFormat.getInstance(Locale.US); + symbols = new DecimalFormatSymbols(); + form.setParseBigDecimal(true); + + number = form.parse(symbols.getInfinity(), new ParsePosition(0)); + assertTrue(number instanceof Double); + assertEquals("Infinity", number.toString()); + + // Negative infinity will be parsed to double no matter parseBigDecimal + // set or not + + form = (DecimalFormat) DecimalFormat.getInstance(Locale.US); + symbols = new DecimalFormatSymbols(); + + number = form.parse("-" + symbols.getInfinity(), new ParsePosition(0)); + + assertTrue(number instanceof Double); + assertEquals("-Infinity", number.toString()); + + // When set bigDecimal to true, the result of parsing minus infinity + + form = (DecimalFormat) DecimalFormat.getInstance(Locale.US); + symbols = new DecimalFormatSymbols(); + form.setParseBigDecimal(true); + + number = form.parse("-" + symbols.getInfinity(), new ParsePosition(0)); + + assertTrue(number instanceof Double); + assertEquals("-Infinity", number.toString()); + + // -0.0 will be parsed to different type according to the combination of + // parseBigDecimal and parseIntegerOnly + + form = (DecimalFormat) DecimalFormat.getInstance(Locale.US); + + // parseBigDecimal == true; + // parseIntegerOnly == false; + form.setParseBigDecimal(true); + number = form.parse("-0", new ParsePosition(0)); + assertTrue(number instanceof BigDecimal); + + number = form.parse("-0.0", new ParsePosition(0)); + assertTrue(number instanceof BigDecimal); + + // parseBigDecimal == false; + // parseIntegerOnly == true; + form.setParseBigDecimal(false); + form.setParseIntegerOnly(true); + number = form.parse("-0", new ParsePosition(0)); + + assertTrue(number instanceof Long); + + number = form.parse("-0.0", new ParsePosition(0)); + assertTrue(number instanceof Long); + + // parseBigDecimal == false; + // parseIntegerOnly == false; + form.setParseBigDecimal(false); + form.setParseIntegerOnly(false); + number = form.parse("-0", new ParsePosition(0)); + assertTrue(number instanceof Double); + + number = form.parse("-0.0", new ParsePosition(0)); + assertTrue(number instanceof Double); + + // parseBigDecimal == true; + // parseIntegerOnly == true; + // parseBigDecimal take precedence of parseBigInteger + form.setParseBigDecimal(true); + form.setParseIntegerOnly(true); + number = form.parse("-0", new ParsePosition(0)); + assertTrue(number instanceof BigDecimal); + + number = form.parse("-0.0", new ParsePosition(0)); + assertTrue(number instanceof BigDecimal); + + number = form.parse("12.4", new ParsePosition(0)); + assertTrue(number instanceof BigDecimal); + + // When parseBigDecimal is set to false, no matter how massive the + // mantissa part of a number is, the number will be parsed into Double + + form = (DecimalFormat) DecimalFormat.getInstance(Locale.US); + + number = form.parse("9,223,372,036,854,775,808.00", + new ParsePosition(0)); + + assertTrue(number instanceof Double); + assertEquals("9.223372036854776E18", number.toString()); + + number = form.parse("-9,223,372,036,854,775,8080.00", + new ParsePosition(0)); + assertTrue(number instanceof Double); + assertEquals("-9.223372036854776E19", number.toString()); + + // When parseBigDecimal is set to true, if mantissa part of number + // exceeds Long.MAX_VALUE, the number will be parsed into BigDecimal + + form = (DecimalFormat) DecimalFormat.getInstance(Locale.US); + + form.setParseBigDecimal(true); + number = form.parse("9,223,372,036,854,775,808.00", + new ParsePosition(0)); + + assertTrue(number instanceof BigDecimal); + + assertEquals(9.223372036854776E18, number.doubleValue(), 0); + + number = form.parse("-9,223,372,036,854,775,8080.00", + new ParsePosition(0)); + + assertTrue(number instanceof BigDecimal); + assertEquals(-9.223372036854776E19, number.doubleValue(), 0); + + // The minimum value of Long will be parsed to Long when parseBigDecimal + // is not set + + ParsePosition pos = new ParsePosition(0); + DecimalFormat df = new DecimalFormat(); + pos = new ParsePosition(0); + Number nb = df.parse("" + Long.MIN_VALUE, pos); + assertTrue(nb instanceof Long); + + // The maximum value of Long will be parsed to Long when parseBigDecimal + // is set + pos = new ParsePosition(0); + df = new DecimalFormat(); + pos = new ParsePosition(0); + nb = df.parse("" + Long.MAX_VALUE, pos); + assertTrue(nb instanceof Long); + + // When parsing invalid string( which is neither consist of digits nor + // NaN/Infinity), a null will be returned. + + pos = new ParsePosition(0); + df = new DecimalFormat(); + try { + nb = df.parse("invalid", pos); + assertNull(nb); + } catch (NullPointerException e) { + fail("Should not throw NPE"); + } + } + + public void test_getMaximumFractionDigits() { + NumberFormat nform = DecimalFormat.getInstance(Locale.US); + DecimalFormat form = (DecimalFormat) nform; + + // getMaximumFractionDigits of NumberFormat default to 3 + // getMaximumFractionDigits of DecimalFormat default to 3 + assertEquals(3, nform.getMaximumFractionDigits()); + assertEquals(3, form.getMaximumFractionDigits()); + + // Greater than 340 (critical number used to distinguish + // BigInteger and BigDecimal) + nform.setMaximumFractionDigits(500); + assertEquals(500, nform.getMaximumFractionDigits()); + assertEquals(500, form.getMaximumFractionDigits()); + + form.setMaximumFractionDigits(500); + assertEquals(500, nform.getMaximumFractionDigits()); + assertEquals(500, form.getMaximumFractionDigits()); + + form.format(12.3); + assertEquals(500, nform.getMaximumFractionDigits()); + assertEquals(500, form.getMaximumFractionDigits()); + } + + public void test_getMinimumFractionDigits() { + NumberFormat nform = DecimalFormat.getInstance(Locale.US); + DecimalFormat form = (DecimalFormat) nform; + + // getMinimumFractionDigits from NumberFormat (default to 0) + // getMinimumFractionDigits from DecimalFormat (default to 0) + assertEquals(0, nform.getMinimumFractionDigits()); + assertEquals(0, form.getMinimumFractionDigits()); + + // Greater than 340 (critical number used to distinguish + // BigInteger and BigDecimal) + nform.setMinimumFractionDigits(500); + assertEquals(500, nform.getMinimumFractionDigits()); + assertEquals(500, form.getMinimumFractionDigits()); + + form.setMaximumFractionDigits(400); + assertEquals(400, nform.getMinimumFractionDigits()); + assertEquals(400, form.getMinimumFractionDigits()); + } + + // FIXME This test fails on Harmony ClassLibrary + public void test_getMaximumIntegerDigits() { + final int maxIntDigit = 309; + + // When use default locale, in this case zh_CN + // the returned instance of NumberFormat is a DecimalFormat + DecimalFormat form = new DecimalFormat("00.###E0"); + assertEquals(2, form.getMaximumIntegerDigits()); + + NumberFormat nform = DecimalFormat.getInstance(Locale.US); + form = null; + if (nform instanceof DecimalFormat) { + form = (DecimalFormat) nform; + } + + // Greater than 309 (critical number used to distinguish + // BigInteger and BigDecimal) + nform.setMaximumIntegerDigits(500); + assertEquals(500, nform.getMaximumIntegerDigits()); + assertEquals(500, form.getMaximumIntegerDigits()); + + form = new DecimalFormat("00.###E0"); + assertEquals(2, form.getMaximumIntegerDigits()); + + form.setMaximumIntegerDigits(500); + assertEquals(500, nform.getMaximumIntegerDigits()); + assertEquals(500, form.getMaximumIntegerDigits()); + form.format(12.3); + assertEquals(500, nform.getMaximumIntegerDigits()); + assertEquals(500, form.getMaximumIntegerDigits()); + + nform = DecimalFormat.getInstance(Locale.US); + form = null; + if (nform instanceof DecimalFormat) { + form = (DecimalFormat) nform; + } + // getMaximumIntegerDigits from NumberFormat default to 309 + // getMaximumIntegerDigits from DecimalFormat default to 309 + // the following 2 assertions will fail on RI implementation, since the + // implementation of ICU and RI are not identical. RI does not give + // DecimalFormat an initial bound about its maximumIntegerDigits + // (default to Integer.MAX_VALUE: 2147483647 ) + assertEquals(maxIntDigit, nform.getMaximumIntegerDigits()); + assertEquals(maxIntDigit, form.getMaximumIntegerDigits()); + + // regression test for HARMONY-878 + assertTrue(new DecimalFormat("0\t0").getMaximumIntegerDigits() > 0); + } + + public void test_getMinimumIntegerDigits() { + final int minIntDigit = 1; + NumberFormat nform = DecimalFormat.getInstance(Locale.US); + DecimalFormat form = (DecimalFormat) nform; + + // getMaximumIntegerDigits from NumberFormat (default to 1) + // getMaximumIntegerDigits from DecimalFormat (default to 1) + assertEquals(minIntDigit, nform.getMinimumIntegerDigits()); + assertEquals(minIntDigit, form.getMinimumIntegerDigits()); + + // Greater than 309 (critical number used to distinguish + // BigInteger and BigDecimal) + nform.setMinimumIntegerDigits(500); + assertEquals(500, nform.getMinimumIntegerDigits()); + assertEquals(500, form.getMinimumIntegerDigits()); + + form.setMaximumIntegerDigits(400); + assertEquals(400, nform.getMinimumIntegerDigits()); + assertEquals(400, form.getMinimumIntegerDigits()); + + } + + public void test_formatLjava_lang_Obj_Ljava_StringBuffer_Ljava_text_FieldPosition() { + NumberFormat nform = DecimalFormat.getInstance(Locale.US); + DecimalFormat form = (DecimalFormat) nform; + + // If Object(including null) is not of type Number, + // IllegalArgumentException will be thrown out + try { + form.format(new Object(), new StringBuffer(), new FieldPosition(0)); + fail("Should throw IAE"); + } catch (IllegalArgumentException e) { + // expected + } + try { + form.format(null, new StringBuffer(), new FieldPosition(0)); + fail("Should throw IAE"); + } catch (IllegalArgumentException e) { + // expected + } + + // When StringBuffer == null || FieldPosition == null + // NullPointerException will be thrown out. + try { + form.format(new Double(1.9), null, new FieldPosition(0)); + fail("Should throw NPE"); + } catch (NullPointerException e) { + // expected + } + + try { + form.format(new Double(1.3), new StringBuffer(), null); + fail("Should throw NPE"); + } catch (NullPointerException e) { + // expected + } + + try { + form.format(new Double(1.4), null, null); + fail("Should throw NPE"); + } catch (NullPointerException e) { + // expected + } + + try { + form.format(new Object(), null, null); + fail("Should throw IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // expected + } + + FieldPosition pos; + StringBuffer out; + DecimalFormat format = (DecimalFormat) NumberFormat + .getInstance(Locale.US); + + // format maxLong + pos = new FieldPosition(0); + out = format.format(new Long(Long.MAX_VALUE), new StringBuffer(), pos); + assertTrue("Wrong result L1: " + out, out.toString().equals( + "9,223,372,036,854,775,807")); + + // format minLong + pos = new FieldPosition(0); + out = format.format(new Long(Long.MIN_VALUE), new StringBuffer(), pos); + assertTrue("Wrong result L2: " + out, out.toString().equals( + "-9,223,372,036,854,775,808")); + + // format maxLong of type BigInteger + pos = new FieldPosition(0); + out = format.format(new java.math.BigInteger(String + .valueOf(Long.MAX_VALUE)), new StringBuffer(), pos); + assertTrue("Wrong result BI1: " + out, out.toString().equals( + "9,223,372,036,854,775,807")); + + // format minLong of type BigInteger + pos = new FieldPosition(0); + out = format.format(new java.math.BigInteger(String + .valueOf(Long.MIN_VALUE)), new StringBuffer(), pos); + assertTrue("Wrong result BI2: " + out, out.toString().equals( + "-9,223,372,036,854,775,808")); + + // format maxLong + 1 + java.math.BigInteger big; + pos = new FieldPosition(0); + big = new java.math.BigInteger(String.valueOf(Long.MAX_VALUE)) + .add(new java.math.BigInteger("1")); + out = format.format(big, new StringBuffer(), pos); + assertTrue("Wrong result BI3: " + out, out.toString().equals( + "9,223,372,036,854,775,808")); + + // format minLong - 1 + pos = new FieldPosition(0); + big = new java.math.BigInteger(String.valueOf(Long.MIN_VALUE)) + .add(new java.math.BigInteger("-1")); + out = format.format(big, new StringBuffer(), pos); + assertTrue("Wrong result BI4: " + out, out.toString().equals( + "-9,223,372,036,854,775,809")); + + // format big decimal + pos = new FieldPosition(0); + out = format.format(new java.math.BigDecimal("51.348"), + new StringBuffer(), pos); + assertTrue("Wrong result BD1: " + out, out.toString().equals("51.348")); + + // format big decimal + pos = new FieldPosition(0); + out = format.format(new java.math.BigDecimal("51"), new StringBuffer(), + pos); + assertTrue("Wrong result BD2: " + out, out.toString().equals("51")); + + // format big decimal Double.MAX_VALUE * 2 + java.math.BigDecimal bigDecimal; + pos = new FieldPosition(0); + final String doubleMax2 = "359,538,626,972,463,141,629,054,847,463,408," + + "713,596,141,135,051,689,993,197,834,953,606,314,521,560,057,077," + + "521,179,117,265,533,756,343,080,917,907,028,764,928,468,642,653," + + "778,928,365,536,935,093,407,075,033,972,099,821,153,102,564,152," + + "490,980,180,778,657,888,151,737,016,910,267,884,609,166,473,806," + + "445,896,331,617,118,664,246,696,549,595,652,408,289,446,337,476," + + "354,361,838,599,762,500,808,052,368,249,716,736"; + bigDecimal = new BigDecimal(Double.MAX_VALUE).add(new BigDecimal( + Double.MAX_VALUE)); + out = format.format(bigDecimal, new StringBuffer(), pos); + assertTrue("Wrong result BDmax2: " + out, out.toString().equals( + doubleMax2)); + + // format big decimal Double.MIN_VALUE + Double.MIN_VALUE + // and Double.MIN_VALUE - Double.MIN_VALUE + pos = new FieldPosition(0); + + bigDecimal = new BigDecimal(Double.MIN_VALUE).add(new BigDecimal( + Double.MIN_VALUE)); + out = format.format(bigDecimal, new StringBuffer(), pos); + + bigDecimal = new BigDecimal(Float.MAX_VALUE).add(new BigDecimal( + Float.MAX_VALUE)); + out = format.format(bigDecimal, new StringBuffer(), pos); + final String BDFloatMax2 = "680,564,693,277,057,719,623,408,366,969,033,850,880"; + assertTrue("Wrong result BDFloatMax2: " + out, out.toString().equals( + BDFloatMax2)); + // format big decimal Float.MIN_VALUE + Float.MIN_VALUE + // and Float.MIN_VALUE - Float.MIN_VALUE + bigDecimal = new BigDecimal(Float.MIN_VALUE).add(new BigDecimal( + Float.MIN_VALUE)); + out = format.format(bigDecimal, new StringBuffer(), pos); + final String BDFloatMin2 = "0"; + + bigDecimal = new BigDecimal(Float.MIN_VALUE).subtract(new BigDecimal( + Float.MIN_VALUE)); + out = format.format(bigDecimal, new StringBuffer(), pos); + + assertTrue("Wrong result BDFloatMax2: " + out, out.toString().equals( + BDFloatMin2)); + + } + + public void test_setMaximumFractionDigitsLjava_lang_Integer() { + NumberFormat nform = DecimalFormat.getInstance(Locale.US); + DecimalFormat form = (DecimalFormat) nform; + + form.setMaximumFractionDigits(-2); + assertEquals(0, form.getMaximumFractionDigits()); + + form.setMaximumFractionDigits(341); + assertEquals(341, form.getMaximumFractionDigits()); + } + + public void test_setMinimumFractionDigitsLjava_lang_Integer() { + NumberFormat nform = DecimalFormat.getInstance(Locale.US); + DecimalFormat form = (DecimalFormat) nform; + + form.setMinimumFractionDigits(-3); + assertEquals(0, form.getMinimumFractionDigits()); + + form.setMinimumFractionDigits(310); + assertEquals(310, form.getMinimumFractionDigits()); + } + + public void test_setMaximumIntegerDigitsLjava_lang_Integer() { + NumberFormat nform = DecimalFormat.getInstance(Locale.US); + DecimalFormat form = (DecimalFormat) nform; + + form.setMaximumIntegerDigits(-3); + assertEquals(0, form.getMaximumIntegerDigits()); + + form.setMaximumIntegerDigits(310); + assertEquals(310, form.getMaximumIntegerDigits()); + } + + public void test_setMinimumIntegerDigitsLjava_lang_Integer() { + NumberFormat nform = DecimalFormat.getInstance(Locale.US); + DecimalFormat form = (DecimalFormat) nform; + + form.setMinimumIntegerDigits(-3); + assertEquals(0, form.getMinimumIntegerDigits()); + + form.setMinimumIntegerDigits(310); + assertEquals(310, form.getMinimumIntegerDigits()); + } + + // When MaxFractionDigits is set first and less than MinFractionDigits, max + // will be changed to min value + public void test_setMinimumFactionDigitsLjava_lang_Integer_setMaximumFractionDigitsLjava_lang_Integer() { + NumberFormat nform = DecimalFormat.getInstance(Locale.US); + DecimalFormat form = (DecimalFormat) nform; + + form.setMaximumFractionDigits(100); + form.setMinimumFractionDigits(200); + + assertEquals(200, form.getMaximumFractionDigits()); + assertEquals(200, form.getMinimumFractionDigits()); + + form.setMaximumIntegerDigits(100); + form.setMinimumIntegerDigits(200); + + assertEquals(200, form.getMaximumIntegerDigits()); + assertEquals(200, form.getMinimumIntegerDigits()); + } + + // When MinFractionDigits is set first and less than MaxFractionDigits, min + // will be changed to max value + public void test_setMaximumFactionDigitsLjava_lang_Integer_setMinimumFractionDigitsLjava_lang_Integer() { + NumberFormat nform = DecimalFormat.getInstance(Locale.US); + DecimalFormat form = (DecimalFormat) nform; + + form.setMinimumFractionDigits(200); + form.setMaximumFractionDigits(100); + + assertEquals(100, form.getMaximumFractionDigits()); + assertEquals(100, form.getMinimumFractionDigits()); + + form.setMinimumIntegerDigits(200); + form.setMaximumIntegerDigits(100); + + assertEquals(100, form.getMaximumIntegerDigits()); + assertEquals(100, form.getMinimumIntegerDigits()); + } + + public void test_equalsLjava_lang_Object() { + DecimalFormat format = (DecimalFormat) DecimalFormat + .getInstance(Locale.US); + DecimalFormat cloned = (DecimalFormat) format.clone(); + cloned.setDecimalFormatSymbols(new DecimalFormatSymbols(Locale.US)); + assertEquals(format, cloned); + + Currency c = Currency.getInstance(Locale.US); + cloned.setCurrency(c); + + assertEquals(format, cloned); + } + + public void test_setPositivePrefixLjava_lang_String() { + DecimalFormat format = new DecimalFormat(); + assertEquals("", format.getPositivePrefix()); + } + + public void test_setPositiveSuffixLjava_lang_String() { + DecimalFormat format = new DecimalFormat(); + assertEquals("", format.getPositiveSuffix()); + } + + public void test_setNegativePrefixLjava_lang_String() { + DecimalFormat format = new DecimalFormat(); + assertEquals("-", format.getNegativePrefix()); + } + + public void test_setNegativeSuffixLjava_lang_String() { + DecimalFormat format = new DecimalFormat(); + assertEquals("", format.getNegativeSuffix()); + } + + /** + * @tests java.text.DecimalFormat#toLocalizedPattern() Test of method + * java.text.DecimalFormat#toLocalizedPattern(). + */ + public void test_toLocalizedPattern() { + DecimalFormat format = new DecimalFormat(); + try { + format.applyLocalizedPattern("#.#"); + assertEquals("Wrong pattern 1", "#.;#", format.toLocalizedPattern()); + format.applyLocalizedPattern("#."); + assertEquals("Wrong pattern 2", "#.", format.toLocalizedPattern()); + format.applyLocalizedPattern("#"); + assertEquals("Wrong pattern 3", "#", format.toLocalizedPattern()); + format.applyLocalizedPattern(".#"); + assertEquals("Wrong pattern 4", ".#", format.toLocalizedPattern()); + } catch (Exception e) { + fail("Unexpected exception " + e.toString()); + } + } + + /** + * @tests java.text.DecimalFormat#toPattern() Test of method + * java.text.DecimalFormat#toPattern(). + */ + public void test_toPattern() { + DecimalFormat format = new DecimalFormat(); + try { + format.applyPattern("#.#"); + assertEquals("Wrong pattern 1", "#0.#", format.toPattern()); + format.applyPattern("#."); + assertEquals("Wrong pattern 2", "#0.", format.toPattern()); + format.applyPattern("#"); + assertEquals("Wrong pattern 3", "#", format.toPattern()); + format.applyPattern(".#"); + assertEquals("Wrong pattern 4", "#.0", format.toPattern()); + } catch (Exception e) { + fail("Unexpected exception " + e.toString()); + } + } + + public void test_setGroupingUse() { + DecimalFormat format = new DecimalFormat(); + StringBuffer buf = new StringBuffer(); + format.setGroupingUsed(false); + format.format(new Long(1970), buf, new FieldPosition(0)); + assertEquals("1970", buf.toString()); + assertFalse(format.isGroupingUsed()); + } + + /** + * @tests java.text.DecimalFormat#DecimalFormat() Test of method + * java.text.DecimalFormat#DecimalFormat(). + */ + public void test_Constructor() { + // Test for method java.text.DecimalFormat() + // the constructor form that specifies a pattern is equal to the form + // constructed with no pattern and applying that pattern using the + // applyPattern call + try { + DecimalFormat format1 = new DecimalFormat(); + format1.applyPattern("'$'1000.0000"); + DecimalFormat format2 = new DecimalFormat(); + format2.applyPattern("'$'1000.0000"); + assertTrue( + "Constructed format did not match applied format object", + format2.equals(format1)); + DecimalFormat format3 = new DecimalFormat("'$'1000.0000"); + assertTrue( + "Constructed format did not match applied format object", + format3.equals(format1)); + DecimalFormat format4 = new DecimalFormat("'$'8000.0000"); + assertTrue( + "Constructed format did not match applied format object", + !format4.equals(format1)); + } catch (Exception e) { + fail("Unexpected exception " + e.toString()); + } + } + + /** + * @tests java.text.DecimalFormat#DecimalFormat(java.lang.String) + */ + public void test_ConstructorLjava_lang_String() { + // Test for method java.text.DecimalFormat(java.lang.String) + // the constructor form that specifies a pattern is equal to the form + // constructed with no pattern and applying that pattern using the + // applyPattern call + DecimalFormat format = new DecimalFormat("'$'0000.0000"); + DecimalFormat format1 = new DecimalFormat(); + format1.applyPattern("'$'0000.0000"); + assertTrue("Constructed format did not match applied format object", + format.equals(format1)); + } + + /** + * @tests java.text.DecimalFormat#DecimalFormat(java.lang.String, + * java.text.DecimalFormatSymbols) Test of method + * java.text.DecimalFormat#DecimalFormat(java.lang.String, + * java.text.DecimalFormatSymbols). Case 1: Try to construct object + * using correct pattern and fromat symbols. Case 2: Try to construct + * object using null arguments. Case 3: Try to construct object using + * incorrect pattern. + */ + public void test_ConstructorLjava_lang_StringLjava_text_DecimalFormatSymbols() { + try { + // case 1: Try to construct object using correct pattern and fromat + // symbols. + DecimalFormatSymbols dfs = new DecimalFormatSymbols(Locale.CANADA); + DecimalFormat format1 = new DecimalFormat("'$'1000.0000", dfs); + DecimalFormat format2 = new DecimalFormat(); + format2.applyPattern("'$'1000.0000"); + format2.setDecimalFormatSymbols(dfs); + assertTrue( + "Constructed format did not match applied format object", + format2.equals(format1)); + assertTrue( + "Constructed format did not match applied format object", + !format1.equals(new DecimalFormat("'$'1000.0000", + new DecimalFormatSymbols(Locale.CHINA)))); + + // case 2: Try to construct object using null arguments. + try { + new DecimalFormat("'$'1000.0000", null); + fail("Expected NullPointerException was not thrown"); + } catch (NullPointerException e) { + // expected + } + try { + new DecimalFormat(null, new DecimalFormatSymbols()); + fail("Expected NullPointerException was not thrown"); + } catch (NullPointerException e) { + // expected + } + try { + new DecimalFormat(null, null); + fail("Expected NullPointerException was not thrown"); + } catch (NullPointerException e) { + // expected + } + + // case 3: Try to construct object using incorrect pattern. + try { + new DecimalFormat("$'", new DecimalFormatSymbols()); + fail("Expected IllegalArgumentException was not thrown"); + } catch (IllegalArgumentException e) { + // expected + } + } catch (Exception e) { + fail("Unexpected exception " + e.toString()); + } + } + + /** + * @tests java.text.DecimalFormat#applyLocalizedPattern(java.lang.String) + * Test of method + * java.text.DecimalFormat#applyLocalizedPattern(java.lang.String). + * Case 1: Try to apply correct variants of pattern. Case 2: Try to + * apply malformed patten. Case 3: Try to apply null patern. + */ + public void test_applyLocalizedPatternLjava_lang_String() { + DecimalFormat format = new DecimalFormat(); + try { + // case 1: Try to apply correct variants of pattern. + format.applyLocalizedPattern("#.#"); + assertEquals("Wrong pattern 1", "#.;#", format.toLocalizedPattern()); + format.applyLocalizedPattern("#."); + assertEquals("Wrong pattern 2", "#.", format.toLocalizedPattern()); + format.applyLocalizedPattern("#"); + assertEquals("Wrong pattern 3", "#", format.toLocalizedPattern()); + format.applyLocalizedPattern(".#"); + assertEquals("Wrong pattern 4", ".#", format.toLocalizedPattern()); + + // case 2: Try to apply malformed patten. + try { + format.applyLocalizedPattern("#,##0.0#;(#)"); + fail("Expected IllegalArgumentException was not thrown"); + } catch (IllegalArgumentException e) { + // expected + } + + // case 3: Try to apply null patern. + try { + format.applyLocalizedPattern((String) null); + fail("Expected NullPointerException was not thrown"); + } catch (NullPointerException e) { + // expected + } + } catch (Exception e) { + fail("Unexpected exception " + e.toString()); + } + } + + /** + * @tests java.text.DecimalFormat#applyPattern(java.lang.String) + */ + public void test_applyPatternLjava_lang_String() { + DecimalFormat format = new DecimalFormat("#.#"); + assertEquals("Wrong pattern 1", "#0.#", format.toPattern()); + format = new DecimalFormat("#."); + assertEquals("Wrong pattern 2", "#0.", format.toPattern()); + format = new DecimalFormat("#"); + assertEquals("Wrong pattern 3", "#", format.toPattern()); + format = new DecimalFormat(".#"); + assertEquals("Wrong pattern 4", "#.0", format.toPattern()); + } + + /** + * @tests java.text.DecimalFormat#clone() + */ + public void test_clone() { + DecimalFormat format = (DecimalFormat) DecimalFormat + .getInstance(Locale.US); + DecimalFormat cloned = (DecimalFormat) format.clone(); + assertEquals(cloned.getDecimalFormatSymbols(), format + .getDecimalFormatSymbols()); + + format = new DecimalFormat("'$'0000.0000"); + DecimalFormat format1 = (DecimalFormat) (format.clone()); + // make sure the objects are equal + assertTrue("Object's clone isn't equal!", format.equals(format1)); + // change the content of the clone and make sure it's not equal anymore + // verifies that it's data is now distinct from the original + format1.applyPattern("'$'0000.####"); + assertTrue("Object's changed clone should not be equal!", !format + .equals(format1)); + } + + private void compare(String testName, String format, String expected) { + assertTrue(testName + " got: " + format + " expected: " + expected, + format.equals(expected)); + } + + private boolean compare(int count, String format, String expected) { + boolean result = format.equals(expected); + if (!result) + System.out.println("Failure test: " + count + " got: " + format + + " expected: " + expected); + return result; + } + + /** + * @tests java.text.DecimalFormat#format(double, java.lang.StringBuffer, + * java.text.FieldPosition) + */ + // FIXME This test fails on Harmony ClassLibrary + public void test_formatDLjava_lang_StringBufferLjava_text_FieldPosition() { + new Support_DecimalFormat( + "test_formatDLjava_lang_StringBufferLjava_text_FieldPosition") + .t_format_with_FieldPosition(); + + int failCount = 0; + Support_BitSet failures = new Support_BitSet(); + + final DecimalFormatSymbols dfs = new DecimalFormatSymbols(Locale.US); + + DecimalFormat df = new DecimalFormat("00.0#E0", dfs); + compare("00.0#E0: 0.0", df.format(0.0), "00.0E0"); + compare("00.0#E0: 1.0", df.format(1.0), "10.0E-1"); + compare("00.0#E0: 12.0", df.format(12.0), "12.0E0"); + compare("00.0#E0: 123.0", df.format(123.0), "12.3E1"); + compare("00.0#E0: 1234.0", df.format(1234.0), "12.34E2"); + compare("00.0#E0: 12346.0", df.format(12346.0), "12.35E3"); + compare("00.0#E0: 99999.0", df.format(99999.0), "10.0E4"); + compare("00.0#E0: 1.2", df.format(1.2), "12.0E-1"); + compare("00.0#E0: 12.3", df.format(12.3), "12.3E0"); + compare("00.0#E0: 123.4", df.format(123.4), "12.34E1"); + compare("00.0#E0: 1234.6", df.format(1234.6), "12.35E2"); + compare("00.0#E0: 9999.9", df.format(9999.9), "10.0E3"); + compare("00.0#E0: 0.1", df.format(0.1), "10.0E-2"); + compare("00.0#E0: 0.12", df.format(0.12), "12.0E-2"); + compare("00.0#E0: 0.123", df.format(0.123), "12.3E-2"); + compare("00.0#E0: 0.1234", df.format(0.1234), "12.34E-2"); + compare("00.0#E0: 0.12346", df.format(0.12346), "12.35E-2"); + compare("00.0#E0: 0.99999", df.format(0.99999), "10.0E-1"); + compare("00.0#E0: -0.0", df.format(-0.0), "-00.0E0"); + compare("00.0#E0: -1.0", df.format(-1.0), "-10.0E-1"); + compare("00.0#E0: -12.0", df.format(-12.0), "-12.0E0"); + compare("00.0#E0: -123.0", df.format(-123.0), "-12.3E1"); + compare("00.0#E0: -1234.0", df.format(-1234.0), "-12.34E2"); + compare("00.0#E0: -12346.0", df.format(-12346.0), "-12.35E3"); + compare("00.0#E0: -99999.0", df.format(-99999.0), "-10.0E4"); + + df = new DecimalFormat("##0.0E0", dfs); + compare("##0.0E0: -0.0", df.format(-0.0), "-0.0E0"); + compare("##0.0E0: 0.0", df.format(0.0), "0.0E0"); + compare("##0.0E0: 1.0", df.format(1.0), "1.0E0"); + compare("##0.0E0: 12.0", df.format(12.0), "12E0"); + compare("##0.0E0: 123.0", df.format(123.0), "123E0"); + compare("##0.0E0: 1234.0", df.format(1234.0), "1.234E3"); + compare("##0.0E0: 12346.0", df.format(12346.0), "12.35E3"); + // Fails in JDK 1.2.2 + if (!compare(failCount, df.format(99999.0), "100E3")) + failures.set(failCount); + failCount++; + compare("##0.0E0: 999999.0", df.format(999999.0), "1.0E6"); + + df = new DecimalFormat("#00.0##E0", dfs); + compare("#00.0##E0: 0.1", df.format(0.1), ".100E0"); + compare("#00.0##E0: 0.12", df.format(0.12), ".120E0"); + compare("#00.0##E0: 0.123", df.format(0.123), ".123E0"); + compare("#00.0##E0: 0.1234", df.format(0.1234), ".1234E0"); + compare("#00.0##E0: 0.1234567", df.format(0.1234567), ".123457E0"); + compare("#00.0##E0: 0.01", df.format(0.01), "10.0E-3"); + compare("#00.0##E0: 0.012", df.format(0.012), "12.0E-3"); + compare("#00.0##E0: 0.0123", df.format(0.0123), "12.3E-3"); + compare("#00.0##E0: 0.01234", df.format(0.01234), "12.34E-3"); + compare("#00.0##E0: 0.01234567", df.format(0.01234567), "12.3457E-3"); + compare("#00.0##E0: 0.001", df.format(0.001), "1.00E-3"); + compare("#00.0##E0: 0.0012", df.format(0.0012), "1.20E-3"); + compare("#00.0##E0: 0.00123", df.format(0.00123), "1.23E-3"); + compare("#00.0##E0: 0.001234", df.format(0.001234), "1.234E-3"); + compare("#00.0##E0: 0.001234567", df.format(0.001234567), "1.23457E-3"); + compare("#00.0##E0: 0.0001", df.format(0.0001), "100E-6"); + compare("#00.0##E0: 0.00012", df.format(0.00012), "120E-6"); + compare("#00.0##E0: 0.000123", df.format(0.000123), "123E-6"); + compare("#00.0##E0: 0.0001234", df.format(0.0001234), "123.4E-6"); + compare("#00.0##E0: 0.0001234567", df.format(0.0001234567), + "123.457E-6"); + + // Fails in JDK 1.2.2 + if (!compare(failCount, df.format(0.0), "0.00E0")) + failures.set(failCount); + failCount++; + compare("#00.0##E0: 1.0", df.format(1.0), "1.00E0"); + compare("#00.0##E0: 12.0", df.format(12.0), "12.0E0"); + compare("#00.0##E0: 123.0", df.format(123.0), "123E0"); + compare("#00.0##E0: 1234.0", df.format(1234.0), "1.234E3"); + compare("#00.0##E0: 12345.0", df.format(12345.0), "12.345E3"); + compare("#00.0##E0: 123456.0", df.format(123456.0), "123.456E3"); + compare("#00.0##E0: 1234567.0", df.format(1234567.0), "1.23457E6"); + compare("#00.0##E0: 12345678.0", df.format(12345678.0), "12.3457E6"); + compare("#00.0##E0: 99999999.0", df.format(99999999.0), "100E6"); + + df = new DecimalFormat("#.0E0", dfs); + compare("#.0E0: -0.0", df.format(-0.0), "-.0E0"); + compare("#.0E0: 0.0", df.format(0.0), ".0E0"); + compare("#.0E0: 1.0", df.format(1.0), ".1E1"); + compare("#.0E0: 12.0", df.format(12.0), ".12E2"); + compare("#.0E0: 123.0", df.format(123.0), ".12E3"); + compare("#.0E0: 1234.0", df.format(1234.0), ".12E4"); + compare("#.0E0: 9999.0", df.format(9999.0), ".1E5"); + + df = new DecimalFormat("0.#E0", dfs); + compare("0.#E0: -0.0", df.format(-0.0), "-0E0"); + compare("0.#E0: 0.0", df.format(0.0), "0E0"); + compare("0.#E0: 1.0", df.format(1.0), "1E0"); + compare("0.#E0: 12.0", df.format(12.0), "1.2E1"); + compare("0.#E0: 123.0", df.format(123.0), "1.2E2"); + compare("0.#E0: 1234.0", df.format(1234.0), "1.2E3"); + compare("0.#E0: 9999.0", df.format(9999.0), "1E4"); + + df = new DecimalFormat(".0E0", dfs); + compare(".0E0: -0.0", df.format(-0.0), "-.0E0"); + compare(".0E0: 0.0", df.format(0.0), ".0E0"); + compare(".0E0: 1.0", df.format(1.0), ".1E1"); + compare(".0E0: 12.0", df.format(12.0), ".1E2"); + compare(".0E0: 123.0", df.format(123.0), ".1E3"); + compare(".0E0: 1234.0", df.format(1234.0), ".1E4"); + compare(".0E0: 9999.0", df.format(9999.0), ".1E5"); + + df = new DecimalFormat("0.E0", dfs); + // Fails in JDK 1.2.2 + if (!compare(failCount, df.format(0.0), "0.E0")) + failures.set(failCount); + failCount++; + if (!compare(failCount, df.format(1.0), "1.E0")) + failures.set(failCount); + failCount++; + if (!compare(failCount, df.format(12.0), "1.E1")) + failures.set(failCount); + failCount++; + if (!compare(failCount, df.format(123.0), "1.E2")) + failures.set(failCount); + failCount++; + if (!compare(failCount, df.format(1234.0), "1.E3")) + failures.set(failCount); + failCount++; + if (!compare(failCount, df.format(9999.0), "1.E4")) + failures.set(failCount); + failCount++; + + df = new DecimalFormat("##0.00#E0", dfs); + compare("##0.00#E0: 0.1", df.format(0.1), ".100E0"); + compare("##0.00#E0: 0.1234567", df.format(0.1234567), ".123457E0"); + compare("##0.00#E0: 0.9999999", df.format(0.9999999), "1.00E0"); + compare("##0.00#E0: 0.01", df.format(0.01), "10.0E-3"); + compare("##0.00#E0: 0.01234567", df.format(0.01234567), "12.3457E-3"); + compare("##0.00#E0: 0.09999999", df.format(0.09999999), ".100E0"); + compare("##0.00#E0: 0.001", df.format(0.001), "1.00E-3"); + compare("##0.00#E0: 0.001234567", df.format(0.001234567), "1.23457E-3"); + compare("##0.00#E0: 0.009999999", df.format(0.009999999), "10.0E-3"); + compare("##0.00#E0: 0.0001", df.format(0.0001), "100E-6"); + compare("##0.00#E0: 0.0001234567", df.format(0.0001234567), + "123.457E-6"); + compare("##0.00#E0: 0.0009999999", df.format(0.0009999999), "1.00E-3"); + + df = new DecimalFormat("###0.00#E0", dfs); + compare("###0.00#E0: 0.1", df.format(0.1), ".100E0"); + compare("###0.00#E0: 0.12345678", df.format(0.12345678), ".1234568E0"); + compare("###0.00#E0: 0.99999999", df.format(0.99999999), "1.00E0"); + compare("###0.00#E0: 0.01", df.format(0.01), "100E-4"); + compare("###0.00#E0: 0.012345678", df.format(0.012345678), + "123.4568E-4"); + compare("###0.00#E0: 0.099999999", df.format(0.099999999), ".100E0"); + compare("###0.00#E0: 0.001", df.format(0.001), "10.0E-4"); + compare("###0.00#E0: 0.0012345678", df.format(0.0012345678), + "12.34568E-4"); + compare("###0.00#E0: 0.0099999999", df.format(0.0099999999), "100E-4"); + compare("###0.00#E0: 0.0001", df.format(0.0001), "1.00E-4"); + compare("###0.00#E0: 0.00012345678", df.format(0.00012345678), + "1.234568E-4"); + compare("###0.00#E0: 0.00099999999", df.format(0.00099999999), + "10.0E-4"); + // Fails in JDK 1.2.2 + if (!compare(failCount, df.format(0.00001), "1000E-8")) + failures.set(failCount); + failCount++; + compare("###0.00#E0: 0.000012345678", df.format(0.000012345678), + "1234.568E-8"); + compare("###0.00#E0: 0.000099999999", df.format(0.000099999999), + "1.00E-4"); + + df = new DecimalFormat("###0.0#E0", dfs); + compare("###0.0#E0: 0.1", df.format(0.1), ".10E0"); + compare("###0.0#E0: 0.1234567", df.format(0.1234567), ".123457E0"); + compare("###0.0#E0: 0.9999999", df.format(0.9999999), "1.0E0"); + // Fails in JDK 1.2.2 + if (!compare(failCount, df.format(0.01), "100E-4")) + failures.set(failCount); + failCount++; + compare("###0.0#E0: 0.01234567", df.format(0.01234567), "123.457E-4"); + compare("###0.0#E0: 0.09999999", df.format(0.09999999), ".10E0"); + compare("###0.0#E0: 0.001", df.format(0.001), "10E-4"); + compare("###0.0#E0: 0.001234567", df.format(0.001234567), "12.3457E-4"); + // Fails in JDK 1.2.2 + if (!compare(failCount, df.format(0.009999999), "100E-4")) + failures.set(failCount); + failCount++; + compare("###0.0#E0: 0.0001", df.format(0.0001), "1.0E-4"); + compare("###0.0#E0: 0.0001234567", df.format(0.0001234567), + "1.23457E-4"); + compare("###0.0#E0: 0.0009999999", df.format(0.0009999999), "10E-4"); + // Fails in JDK 1.2.2 + if (!compare(failCount, df.format(0.00001), "1000E-8")) + failures.set(failCount); + failCount++; + compare("###0.0#E0: 0.00001234567", df.format(0.00001234567), + "1234.57E-8"); + compare("###0.0#E0: 0.00009999999", df.format(0.00009999999), "1.0E-4"); + + assertTrue("Failed " + failures + " of " + failCount, + failures.length() == 0); + + String formatString = "##0.#"; + df = new DecimalFormat(formatString, dfs); + df.setMinimumFractionDigits(30); + compare(formatString + ": 0.000000000000000000000000000000", df + .format(0.0), "0.000000000000000000000000000000"); + compare(formatString + ": -0.000000000000000000000000000000", df + .format(-0.0), "-0.000000000000000000000000000000"); + compare(formatString + ": 1.000000000000000000000000000000", df + .format(1.0), "1.000000000000000000000000000000"); + compare(formatString + ": -1.000000000000000000000000000000", df + .format(-1.0), "-1.000000000000000000000000000000"); + + df = new DecimalFormat(formatString); + df.setMaximumFractionDigits(30); + compare(formatString + ": 0", df.format(0.0), "0"); + compare(formatString + ": -0", df.format(-0.0), "-0"); + compare(formatString + ": 1", df.format(1.0), "1"); + compare(formatString + ": -1", df.format(-1.0), "-1"); + } + + /** + * @tests java.text.DecimalFormat#format(long, java.lang.StringBuffer, + * java.text.FieldPosition) + */ + // FIXME This test fails on Harmony ClassLibrary + public void test_formatJLjava_lang_StringBufferLjava_text_FieldPosition() { + int failCount = 0; + Support_BitSet failures = new Support_BitSet(); + + final DecimalFormatSymbols dfs = new DecimalFormatSymbols(Locale.US); + + DecimalFormat df = new DecimalFormat("00.0#E0", dfs); + assertEquals("00.0#E0: 0", "00.0E0", df.format(0)); + assertEquals("00.0#E0: 1", "10.0E-1", df.format(1)); + assertEquals("00.0#E0: 12", "12.0E0", df.format(12)); + assertEquals("00.0#E0: 123", "12.3E1", df.format(123)); + assertEquals("00.0#E0: 1234", "12.34E2", df.format(1234)); + assertEquals("00.0#E0: 12346", "12.35E3", df.format(12346)); + assertEquals("00.0#E0: 99999", "10.0E4", df.format(99999)); + assertEquals("00.0#E0: -1", "-10.0E-1", df.format(-1)); + assertEquals("00.0#E0: -12", "-12.0E0", df.format(-12)); + assertEquals("00.0#E0: -123", "-12.3E1", df.format(-123)); + assertEquals("00.0#E0: -1234", "-12.34E2", df.format(-1234)); + assertEquals("00.0#E0: -12346", "-12.35E3", df.format(-12346)); + assertEquals("00.0#E0: -99999", "-10.0E4", df.format(-99999)); + + df = new DecimalFormat("##0.0E0", dfs); + assertEquals("##0.0E0: 0", "0.0E0", df.format(0)); + assertEquals("##0.0E0: 1", "1.0E0", df.format(1)); + assertEquals("##0.0E0: 12", "12E0", df.format(12)); + assertEquals("##0.0E0: 123", "123E0", df.format(123)); + assertEquals("##0.0E0: 1234", "1.234E3", df.format(1234)); + assertEquals("##0.0E0: 12346", "12.35E3", df.format(12346)); + // Fails in JDK 1.2.2 + if (!df.format(99999).equals("100E3")) + failures.set(failCount); + failCount++; + assertEquals("##0.0E0: 999999", "1.0E6", df.format(999999)); + + df = new DecimalFormat("#00.0##E0", dfs); + // Fails in JDK 1.2.2 + if (!df.format(0).equals("0.00E0")) + failures.set(failCount); + failCount++; + assertEquals("#00.0##E0: 1", "1.00E0", df.format(1)); + assertEquals("#00.0##E0: 12", "12.0E0", df.format(12)); + assertEquals("#00.0##E0: 123", "123E0", df.format(123)); + assertEquals("#00.0##E0: 1234", "1.234E3", df.format(1234)); + assertEquals("#00.0##E0: 12345", "12.345E3", df.format(12345)); + assertEquals("#00.0##E0: 123456", "123.456E3", df.format(123456)); + assertEquals("#00.0##E0: 1234567", "1.23457E6", df.format(1234567)); + assertEquals("#00.0##E0: 12345678", "12.3457E6", df.format(12345678)); + assertEquals("#00.0##E0: 99999999", "100E6", df.format(99999999)); + + df = new DecimalFormat("#.0E0", dfs); + assertEquals("#.0E0: 0", ".0E0", df.format(0)); + assertEquals("#.0E0: 1", ".1E1", df.format(1)); + assertEquals("#.0E0: 12", ".12E2", df.format(12)); + assertEquals("#.0E0: 123", ".12E3", df.format(123)); + assertEquals("#.0E0: 1234", ".12E4", df.format(1234)); + assertEquals("#.0E0: 9999", ".1E5", df.format(9999)); + + df = new DecimalFormat("0.#E0", dfs); + assertEquals("0.#E0: 0", "0E0", df.format(0)); + assertEquals("0.#E0: 1", "1E0", df.format(1)); + assertEquals("0.#E0: 12", "1.2E1", df.format(12)); + assertEquals("0.#E0: 123", "1.2E2", df.format(123)); + assertEquals("0.#E0: 1234", "1.2E3", df.format(1234)); + assertEquals("0.#E0: 9999", "1E4", df.format(9999)); + + assertTrue("Failed " + failures + " of " + failCount, + failures.length() == 0); + } + + /** + * @tests java.text.DecimalFormat#formatToCharacterIterator(java.lang.Object) + */ + // FIXME This test fails on Harmony ClassLibrary + public void test_formatToCharacterIteratorLjava_lang_Object() { + + try { + // Regression for HARMONY-466 + new DecimalFormat().formatToCharacterIterator(null); + fail("NullPointerException expected"); + } catch (NullPointerException e) { + // expected + } + + new Support_DecimalFormat( + "test_formatToCharacterIteratorLjava_lang_Object") + .t_formatToCharacterIterator(); + } + + /** + * @tests java.text.DecimalFormat#format(double) + */ + public void test_formatD() { + DecimalFormat format = (DecimalFormat) NumberFormat + .getInstance(Locale.ENGLISH); + format.setGroupingUsed(false); + format.setMaximumFractionDigits(400); + for (int i = 0; i < 309; i++) { + String tval = "1"; + for (int j = 0; j < i; j++) + tval += "0"; + double d = Double.parseDouble(tval); + String result = format.format(d); + assertEquals(i + ") e:" + tval + " r:" + result, tval, result); + } + for (int i = 0; i < 322; i++) { + String tval = "0."; + for (int j = 0; j < i; j++) + tval += "0"; + tval += "1"; + double d = Double.parseDouble(tval); + String result = format.format(d); + assertEquals(i + ") e:" + tval + " r:" + result, tval, result); + } + assertEquals("999999999999999", format.format(999999999999999.)); + assertEquals("1", "999999999999999.9", format.format(999999999999999.9)); + assertEquals("2", "99999999999999.98", format.format(99999999999999.99)); + assertEquals("3", "9999999999999.998", format.format(9999999999999.999)); + assertEquals("4", "999999999999.9999", format.format(999999999999.9999)); + assertEquals("5", "99999999999.99998", format.format(99999999999.99999)); + assertEquals("6", "9999999999.999998", format.format(9999999999.999999)); + assertEquals("7", "999999999.9999999", format.format(999999999.9999999)); + assertEquals("8", "99999999.99999999", format.format(99999999.99999999)); + assertEquals("9", "9999999.999999998", format.format(9999999.999999999)); + assertEquals("10", "99999.99999999999", format + .format(99999.99999999999)); + assertEquals("11", "9999.999999999998", format + .format(9999.999999999999)); + assertEquals("12", "999.9999999999999", format + .format(999.9999999999999)); + assertEquals("13", "99.99999999999999", format + .format(99.99999999999999)); + assertEquals("14", "9.999999999999998", format + .format(9.999999999999999)); + assertEquals("15", "0.9999999999999999", format + .format(.9999999999999999)); + } + + /** + * @tests java.text.DecimalFormat#getDecimalFormatSymbols() + */ + public void test_getDecimalFormatSymbols() { + DecimalFormat df = (DecimalFormat) NumberFormat + .getInstance(Locale.ENGLISH); + DecimalFormatSymbols dfs = df.getDecimalFormatSymbols(); + assertTrue("Identical symbols", dfs != df.getDecimalFormatSymbols()); + } + + /** + * @tests java.text.DecimalFormat#getCurrency() + */ + // FIXME This test fails on Harmony ClassLibrary + public void test_getCurrency() { + Currency currK = Currency.getInstance("KRW"); + Currency currX = Currency.getInstance("XXX"); + Currency currE = Currency.getInstance("EUR"); + Currency curr01; + + DecimalFormat df = (DecimalFormat) NumberFormat + .getCurrencyInstance(new Locale("ko", "KR")); + assertTrue("Test1: Returned incorrect currency", + df.getCurrency() == currK); + + df = (DecimalFormat) NumberFormat.getCurrencyInstance(new Locale("", + "KR")); + assertTrue("Test2: Returned incorrect currency", + df.getCurrency() == currK); + + df = (DecimalFormat) NumberFormat.getCurrencyInstance(new Locale("ko", + "")); + assertTrue("Test3: Returned incorrect currency", + df.getCurrency() == currX); + + df = (DecimalFormat) NumberFormat.getCurrencyInstance(new Locale("fr", + "FR")); + assertTrue("Test4: Returned incorrect currency", + df.getCurrency() == currE); + + // Regression for HARMONY-1351 + df = (DecimalFormat) NumberFormat.getCurrencyInstance(new Locale( + "QWERTY")); + assertTrue("Test5: Returned incorrect currency", + df.getCurrency() == currX); + + // JDK fails these tests since it doesn't have the PREEURO variant + // df = (DecimalFormat)NumberFormat.getCurrencyInstance(new Locale("fr", + // "FR","PREEURO")); + // assertTrue("Test5: Returned incorrect currency", df.getCurrency() == + // currF); + } + + /** + * @tests java.text.DecimalFormat#getGroupingSize() + */ + public void test_getGroupingSize() { + DecimalFormat df = new DecimalFormat("###0.##"); + assertEquals("Wrong unset size", 0, df.getGroupingSize()); + df = new DecimalFormat("#,##0.##"); + assertEquals("Wrong set size", 3, df.getGroupingSize()); + df = new DecimalFormat("#,###,###0.##"); + assertEquals("Wrong multiple set size", 4, df.getGroupingSize()); + } + + /** + * @tests java.text.DecimalFormat#getMultiplier() + */ + public void test_getMultiplier() { + final int defaultMultiplier = 1; + NumberFormat nform = DecimalFormat.getInstance(Locale.US); + DecimalFormat form = (DecimalFormat) nform; + assertEquals(defaultMultiplier, form.getMultiplier()); + + DecimalFormat df = new DecimalFormat("###0.##"); + assertEquals("Wrong unset multiplier", 1, df.getMultiplier()); + df = new DecimalFormat("###0.##%"); + assertEquals("Wrong percent multiplier", 100, df.getMultiplier()); + df = new DecimalFormat("###0.##\u2030"); + assertEquals("Wrong mille multiplier", 1000, df.getMultiplier()); + } + + /** + * @tests java.text.DecimalFormat#getNegativePrefix() Test of method + * java.text.DecimalFormat#getNegativePrefix(). + */ + public void test_getNegativePrefix() { + DecimalFormat df = new DecimalFormat(); + try { + df.setNegativePrefix("--"); + assertTrue("Incorrect negative prefix", df.getNegativePrefix() + .equals("--")); + } catch (Exception e) { + fail("Unexpected exception " + e.toString()); + } + } + + /** + * @tests java.text.DecimalFormat#getNegativeSuffix() Test of method + * java.text.DecimalFormat#getNegativeSuffix(). + */ + public void test_getNegativeSuffix() { + DecimalFormat df = new DecimalFormat(); + try { + df.setNegativeSuffix("&"); + assertTrue("Incorrect negative suffix", df.getNegativeSuffix() + .equals("&")); + } catch (Exception e) { + fail("Unexpected exception " + e.toString()); + } + } + + /** + * @tests java.text.DecimalFormat#getPositivePrefix() Test of method + * java.text.DecimalFormat#getPositivePrefix(). + */ + public void test_getPositivePrefix() { + DecimalFormat df = new DecimalFormat(); + try { + df.setPositivePrefix("++"); + assertTrue("Incorrect positive prefix", df.getPositivePrefix() + .equals("++")); + } catch (Exception e) { + fail("Unexpected exception " + e.toString()); + } + } + + /** + * @tests java.text.DecimalFormat#getPositiveSuffix() Test of method + * java.text.DecimalFormat#getPositiveSuffix(). + */ + public void test_getPositiveSuffix() { + DecimalFormat df = new DecimalFormat(); + try { + df.setPositiveSuffix("%"); + assertTrue("Incorrect positive prefix", df.getPositiveSuffix() + .equals("%")); + } catch (Exception e) { + fail("Unexpected exception " + e.toString()); + } + } + + /** + * @tests java.text.DecimalFormat#hashCode() Test of method + * java.text.DecimalFormat#hashCode(). + */ + public void test_hashCode() { + try { + DecimalFormat df1 = new DecimalFormat(); + DecimalFormat df2 = (DecimalFormat) df1.clone(); + assertTrue("Hash codes of equals object are not equal", df2 + .hashCode() == df1.hashCode()); + } catch (Exception e) { + fail("Unexpected exception " + e.toString()); + } + } + + /** + * @tests java.text.DecimalFormat#isDecimalSeparatorAlwaysShown() + */ + public void test_isDecimalSeparatorAlwaysShown() { + DecimalFormat df = new DecimalFormat("###0.##"); + assertTrue("Wrong unset value", !df.isDecimalSeparatorAlwaysShown()); + df = new DecimalFormat("###0.00"); + assertTrue("Wrong unset2 value", !df.isDecimalSeparatorAlwaysShown()); + df = new DecimalFormat("###0."); + assertTrue("Wrong set value", df.isDecimalSeparatorAlwaysShown()); + } + + /** + * @tests java.text.DecimalFormat#parse(java.lang.String, + * java.text.ParsePosition) + */ + // FIXME This test fails on Harmony ClassLibrary + public void test_parseLjava_lang_StringLjava_text_ParsePosition() { + DecimalFormat format = (DecimalFormat) NumberFormat + .getNumberInstance(Locale.ENGLISH); + ParsePosition pos = new ParsePosition(0); + Number result = format.parse("9223372036854775807", pos); + assertTrue("Wrong result type for Long.MAX_VALUE", + result.getClass() == Long.class); + assertEquals("Wrong result Long.MAX_VALUE", + Long.MAX_VALUE, result.longValue()); + pos = new ParsePosition(0); + result = format.parse("-9223372036854775808", pos); + assertTrue("Wrong result type for Long.MIN_VALUE", + result.getClass() == Long.class); + assertTrue("Wrong result Long.MIN_VALUE: " + result.longValue(), result + .longValue() == Long.MIN_VALUE); + pos = new ParsePosition(0); + result = format.parse("9223372036854775808", pos); + assertTrue("Wrong result type for Long.MAX_VALUE+1", + result.getClass() == Double.class); + assertEquals("Wrong result Long.MAX_VALUE + 1", + (double) Long.MAX_VALUE + 1, result.doubleValue()); + pos = new ParsePosition(0); + result = format.parse("-9223372036854775809", pos); + assertTrue("Wrong result type for Long.MIN_VALUE - 1", + result.getClass() == Double.class); + assertEquals("Wrong result Long.MIN_VALUE - 1", + (double) Long.MIN_VALUE - 1, result.doubleValue()); + + pos = new ParsePosition(0); + result = format.parse("18446744073709551629", pos); + assertTrue("Wrong result type for overflow", + result.getClass() == Double.class); + assertEquals("Wrong result for overflow", + 18446744073709551629d, result.doubleValue()); + + pos = new ParsePosition(0); + result = format.parse("42325917317067571199", pos); + assertTrue("Wrong result type for overflow a: " + result, result + .getClass() == Double.class); + assertTrue("Wrong result for overflow a: " + result, result + .doubleValue() == 42325917317067571199d); + pos = new ParsePosition(0); + result = format.parse("4232591731706757119E1", pos); + assertTrue("Wrong result type for overflow b: " + result, result + .getClass() == Double.class); + assertEquals("Wrong result for overflow b: " + result, + 42325917317067571190d, result.doubleValue()); + pos = new ParsePosition(0); + result = format.parse(".42325917317067571199E20", pos); + assertTrue("Wrong result type for overflow c: " + result, result + .getClass() == Double.class); + assertTrue("Wrong result for overflow c: " + result, result + .doubleValue() == 42325917317067571199d); + pos = new ParsePosition(0); + result = format.parse("922337203685477580.9E1", pos); + assertTrue("Wrong result type for overflow d: " + result, result + .getClass() == Double.class); + assertTrue("Wrong result for overflow d: " + result, result + .doubleValue() == 9223372036854775809d); + pos = new ParsePosition(0); + result = format.parse("9.223372036854775809E18", pos); + assertTrue("Wrong result type for overflow e: " + result, result + .getClass() == Double.class); + assertTrue("Wrong result for overflow e: " + result, result + .doubleValue() == 9223372036854775809d); + + // test parse with multipliers + format.setMultiplier(100); + result = format.parse("9223372036854775807", new ParsePosition(0)); + assertTrue("Wrong result type multiplier 100: " + result, result + .getClass() == Long.class); + // BEGIN android-changed + // RI on windows and linux both answer with a slightly rounded result + assertTrue("Wrong result for multiplier 100: " + result, result + .longValue() == 92233720368547760L); + // END android-changed + format.setMultiplier(1000); + result = format.parse("9223372036854775807", new ParsePosition(0)); + assertTrue("Wrong result type multiplier 1000: " + result, result + .getClass() == Long.class); + assertTrue("Wrong result for multiplier 1000: " + result, result + .longValue() == 9223372036854776L); + + format.setMultiplier(10000); + result = format.parse("9223372036854775807", new ParsePosition(0)); + assertTrue("Wrong result type multiplier 10000: " + result, result + .getClass() == Double.class); + assertTrue("Wrong result for multiplier 10000: " + result, result + .doubleValue() == 922337203685477.5807d); + + } + + /** + * @tests java.text.DecimalFormat#setDecimalFormatSymbols(java.text.DecimalFormatSymbols) + */ + public void test_setDecimalFormatSymbolsLjava_text_DecimalFormatSymbols() { + DecimalFormat df = new DecimalFormat("###0.##"); + DecimalFormatSymbols dfs = new DecimalFormatSymbols(); + dfs.setDecimalSeparator('@'); + df.setDecimalFormatSymbols(dfs); + assertTrue("Not set", df.getDecimalFormatSymbols().equals(dfs)); + assertEquals("Symbols not used", "1@2", df.format(1.2)); + + // The returned symbols may be cloned in two spots + // 1. When set + // 2. When returned + DecimalFormat format = new DecimalFormat(); + DecimalFormatSymbols symbols = new DecimalFormatSymbols(); + format.setDecimalFormatSymbols(symbols); + DecimalFormatSymbols symbolsOut = format.getDecimalFormatSymbols(); + assertNotSame(symbols, symbolsOut); + } + + /** + * @tests java.text.DecimalFormat#setDecimalSeparatorAlwaysShown(boolean) + */ + public void test_setDecimalSeparatorAlwaysShownZ() { + DecimalFormat df = new DecimalFormat("###0.##", + new DecimalFormatSymbols(Locale.US)); + assertEquals("Wrong default result", "5", df.format(5)); + df.setDecimalSeparatorAlwaysShown(true); + assertTrue("Not set", df.isDecimalSeparatorAlwaysShown()); + assertEquals("Wrong set result", "7.", df.format(7)); + } + + /** + * @tests java.text.DecimalFormat#setCurrency(java.util.Currency) + */ + public void test_setCurrencyLjava_util_Currency() { + Locale locale = Locale.CANADA; + DecimalFormat df = ((DecimalFormat) NumberFormat + .getCurrencyInstance(locale)); + + try { + df.setCurrency(null); + fail("Expected NullPointerException"); + } catch (NullPointerException e) { + } + + Currency currency = Currency.getInstance("AED"); + df.setCurrency(currency); + assertTrue("Returned incorrect currency", currency == df.getCurrency()); + assertTrue("Returned incorrect currency symbol", currency.getSymbol( + locale) + .equals(df.getDecimalFormatSymbols().getCurrencySymbol())); + assertTrue("Returned incorrect international currency symbol", currency + .getCurrencyCode().equals( + df.getDecimalFormatSymbols() + .getInternationalCurrencySymbol())); + } + + /** + * @tests java.text.DecimalFormat#setGroupingSize(int) + */ + public void test_setGroupingSizeI() { + DecimalFormat df = new DecimalFormat("###0.##", + new DecimalFormatSymbols(Locale.ENGLISH)); + df.setGroupingUsed(true); + df.setGroupingSize(2); + assertEquals("Value not set", 2, df.getGroupingSize()); + String result = df.format(123); + assertTrue("Invalid format:" + result, result.equals("1,23")); + } + + /** + * @tests java.text.DecimalFormat#setMaximumFractionDigits(int) + */ + public void test_setMaximumFractionDigitsI() { + DecimalFormat df = new DecimalFormat("###0.##", + new DecimalFormatSymbols(Locale.US)); + df.setMaximumFractionDigits(3); + assertEquals("Not set", 3, df.getMaximumFractionDigits()); + assertEquals("Wrong maximum", "1.235", df.format(1.23456)); + df.setMinimumFractionDigits(4); + assertEquals("Not changed", 4, df.getMaximumFractionDigits()); + assertEquals("Incorrect fraction", "456.0000", df.format(456)); + } + + /** + * @tests java.text.DecimalFormat#setMaximumIntegerDigits(int) + */ + public void test_setMaximumIntegerDigitsI() { + DecimalFormat df = new DecimalFormat("###0.##"); + df.setMaximumIntegerDigits(2); + assertEquals("Not set", 2, df.getMaximumIntegerDigits()); + assertEquals("Wrong maximum", "34", df.format(1234)); + df.setMinimumIntegerDigits(4); + assertEquals("Not changed", 4, df.getMaximumIntegerDigits()); + assertEquals("Incorrect integer", "0026", df.format(26)); + } + + /** + * @tests java.text.DecimalFormat#setMinimumFractionDigits(int) + */ + public void test_setMinimumFractionDigitsI() { + DecimalFormat df = new DecimalFormat("###0.##", + new DecimalFormatSymbols(Locale.US)); + df.setMinimumFractionDigits(4); + assertEquals("Not set", 4, df.getMinimumFractionDigits()); + assertEquals("Wrong minimum", "1.2300", df.format(1.23)); + df.setMaximumFractionDigits(2); + assertEquals("Not changed", 2, df.getMinimumFractionDigits()); + assertEquals("Incorrect fraction", "456.00", df.format(456)); + } + + /** + * @tests java.text.DecimalFormat#setMinimumIntegerDigits(int) + */ + public void test_setMinimumIntegerDigitsI() { + DecimalFormat df = new DecimalFormat("###0.##", + new DecimalFormatSymbols(Locale.US)); + df.setMinimumIntegerDigits(3); + assertEquals("Not set", 3, df.getMinimumIntegerDigits()); + assertEquals("Wrong minimum", "012", df.format(12)); + df.setMaximumIntegerDigits(2); + assertEquals("Not changed", 2, df.getMinimumIntegerDigits()); + assertEquals("Incorrect integer", "00.7", df.format(0.7)); + } + + /** + * @tests java.text.DecimalFormat#setMultiplier(int) + */ + // FIXME This test fails on Harmony ClassLibrary + public void test_setMultiplierI() { + DecimalFormat df = new DecimalFormat("###0.##"); + df.setMultiplier(10); + assertEquals("Wrong multiplier", 10, df.getMultiplier()); + assertEquals("Wrong format", "50", df.format(5)); + assertEquals("Wrong parse", 5, df.parse("50", new ParsePosition(0)) + .intValue()); + + // regression test for HARMONY-879 + df.setMultiplier(-1); + assertEquals("Wrong multiplier for negative value", -1, df + .getMultiplier()); + } + + /** + * @tests serialization/deserialization compatibility. + */ + public void testSerializationSelf() throws Exception { + SerializationTest.verifySelf(new DecimalFormat()); + } + + /** + * @tests serialization compatibility with RI + */ + public void test_serializationHarmonyRICompatible() { + NumberFormat nf = NumberFormat.getInstance(Locale.FRANCE); + + DecimalFormat df = null; + if (!(nf instanceof DecimalFormat)) { + throw new Error("This NumberFormat is not a DecimalFormat"); + + } + df = (DecimalFormat) nf; + + ObjectInputStream oinput = null; + + DecimalFormat deserializedDF = null; + + try { + oinput = new ObjectInputStream(this.getClass().getResource( + "/serialization/java/text/DecimalFormat.ser").openStream()); + deserializedDF = (DecimalFormat) oinput.readObject(); + } catch (Exception e) { + fail("Error occurs during deserialization"); + } finally { + try { + if (null != oinput) { + oinput.close(); + } + } catch (Exception e) { + // ignore + } + } + + assertEquals(df.getNegativePrefix(), deserializedDF.getNegativePrefix()); + assertEquals(df.getNegativeSuffix(), deserializedDF.getNegativeSuffix()); + assertEquals(df.getPositivePrefix(), deserializedDF.getPositivePrefix()); + assertEquals(df.getPositiveSuffix(), deserializedDF.getPositiveSuffix()); + assertEquals(df.getCurrency(), deserializedDF.getCurrency()); + + assertEquals(df.getDecimalFormatSymbols(), deserializedDF + .getDecimalFormatSymbols()); + + assertEquals(df.getGroupingSize(), df.getGroupingSize()); + assertEquals(df.getMaximumFractionDigits(), deserializedDF + .getMaximumFractionDigits()); + + assertEquals(df.getMaximumIntegerDigits(), deserializedDF + .getMaximumIntegerDigits()); + + assertEquals(df.getMinimumFractionDigits(), deserializedDF + .getMinimumFractionDigits()); + assertEquals(df.getMinimumIntegerDigits(), deserializedDF + .getMinimumIntegerDigits()); + assertEquals(df.getMultiplier(), deserializedDF.getMultiplier()); + + // Deliberately omitted this assertion. Since different data resource + // will cause the assertion fail. + // assertEquals(df, deserializedDF); + + } + + /** + * Test whether DecimalFormat can parse Positive infinity correctly + */ + public void testParseInfinityBigDecimalFalse() { + // Regression test for HARMONY-106 + DecimalFormat format = (DecimalFormat) DecimalFormat.getInstance(); + DecimalFormatSymbols symbols = new DecimalFormatSymbols(); + Number number = format.parse(symbols.getInfinity(), + new ParsePosition(0)); + assertTrue(number instanceof Double); + assertTrue(Double.isInfinite(number.doubleValue())); + } + + /** + * Test whether DecimalFormat can parse Negative infinity correctly + */ + public void testParseMinusInfinityBigDecimalFalse() { + // Regression test for HARMONY-106 + DecimalFormat format = (DecimalFormat) DecimalFormat.getInstance(); + DecimalFormatSymbols symbols = new DecimalFormatSymbols(); + Number number = format.parse("-" + symbols.getInfinity(), + new ParsePosition(0)); + assertTrue(number instanceof Double); + assertTrue(Double.isInfinite(number.doubleValue())); + } + + /** + * Test if setDecimalFormatSymbols method wont throw NullPointerException + * when it is called with null parameter. + */ + public void testSetDecimalFormatSymbolsAsNull() { + // Regression for HARMONY-1070 + try { + DecimalFormat format = (DecimalFormat) DecimalFormat.getInstance(); + format.setDecimalFormatSymbols(null); + } catch (Exception e) { + fail("Unexpected exception caught: " + e); + } + } +} diff --git a/text/src/test/java/org/apache/harmony/text/tests/java/text/FieldPositionTest.java b/text/src/test/java/org/apache/harmony/text/tests/java/text/FieldPositionTest.java new file mode 100644 index 0000000..a1fa81d --- /dev/null +++ b/text/src/test/java/org/apache/harmony/text/tests/java/text/FieldPositionTest.java @@ -0,0 +1,239 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.harmony.text.tests.java.text; + +import java.text.DateFormat; +import java.text.FieldPosition; + +public class FieldPositionTest extends junit.framework.TestCase { + + /** + * @tests java.text.FieldPosition#FieldPosition(int) + */ + public void test_ConstructorI() { + // Test for constructor java.text.FieldPosition(int) + FieldPosition fpos = new FieldPosition(DateFormat.MONTH_FIELD); + assertEquals("Test1: Constructor failed to set field identifier!", + DateFormat.MONTH_FIELD, fpos.getField()); + assertNull("Constructor failed to set field attribute!", fpos + .getFieldAttribute()); + } + + /** + * @tests java.text.FieldPosition#FieldPosition(java.text.Format$Field) + */ + public void test_ConstructorLjava_text_Format$Field() { + // Test for constructor java.text.FieldPosition(Format.Field) + FieldPosition fpos = new FieldPosition(DateFormat.Field.MONTH); + assertSame("Constructor failed to set field attribute!", + DateFormat.Field.MONTH, fpos.getFieldAttribute()); + assertEquals("Test1: Constructor failed to set field identifier!", -1, + fpos.getField()); + } + + /** + * @tests java.text.FieldPosition#FieldPosition(java.text.Format$Field, int) + */ + public void test_ConstructorLjava_text_Format$FieldI() { + // Test for constructor java.text.FieldPosition(Format.Field, int) + FieldPosition fpos = new FieldPosition(DateFormat.Field.MONTH, + DateFormat.MONTH_FIELD); + assertSame("Constructor failed to set field attribute!", + DateFormat.Field.MONTH, fpos.getFieldAttribute()); + assertEquals("Test1: Constructor failed to set field identifier!", + DateFormat.MONTH_FIELD, fpos.getField()); + + // test special cases + FieldPosition fpos2 = new FieldPosition(DateFormat.Field.HOUR1, + DateFormat.HOUR1_FIELD); + assertSame("Constructor failed to set field attribute!", + DateFormat.Field.HOUR1, fpos2.getFieldAttribute()); + assertEquals("Test2: Constructor failed to set field identifier!", + DateFormat.HOUR1_FIELD, fpos2.getField()); + + FieldPosition fpos3 = new FieldPosition(DateFormat.Field.TIME_ZONE, + DateFormat.MONTH_FIELD); + assertSame("Constructor failed to set field attribute!", + DateFormat.Field.TIME_ZONE, fpos3.getFieldAttribute()); + assertEquals("Test3: Constructor failed to set field identifier!", + DateFormat.MONTH_FIELD, fpos3.getField()); + } + + /** + * @tests java.text.FieldPosition#equals(java.lang.Object) + */ + public void test_equalsLjava_lang_Object() { + // Test for method boolean + // java.text.FieldPosition.equals(java.lang.Object) + FieldPosition fpos = new FieldPosition(1); + FieldPosition fpos1 = new FieldPosition(1); + assertTrue("Identical objects were not equal!", fpos.equals(fpos1)); + + FieldPosition fpos2 = new FieldPosition(2); + assertTrue("Objects with a different ID should not be equal!", !fpos + .equals(fpos2)); + + fpos.setBeginIndex(1); + fpos1.setBeginIndex(2); + assertTrue("Objects with a different beginIndex were still equal!", + !fpos.equals(fpos1)); + fpos1.setBeginIndex(1); + fpos1.setEndIndex(2); + assertTrue("Objects with a different endIndex were still equal!", !fpos + .equals(fpos1)); + + FieldPosition fpos3 = new FieldPosition(DateFormat.Field.ERA, 1); + assertTrue("Objects with a different attribute should not be equal!", + !fpos.equals(fpos3)); + FieldPosition fpos4 = new FieldPosition(DateFormat.Field.AM_PM, 1); + assertTrue("Objects with a different attribute should not be equal!", + !fpos3.equals(fpos4)); + } + + /** + * @tests java.text.FieldPosition#getBeginIndex() + */ + public void test_getBeginIndex() { + // Test for method int java.text.FieldPosition.getBeginIndex() + FieldPosition fpos = new FieldPosition(1); + fpos.setEndIndex(3); + fpos.setBeginIndex(2); + assertEquals("getBeginIndex should have returned 2", 2, fpos + .getBeginIndex()); + } + + /** + * @tests java.text.FieldPosition#getEndIndex() + */ + public void test_getEndIndex() { + // Test for method int java.text.FieldPosition.getEndIndex() + FieldPosition fpos = new FieldPosition(1); + fpos.setBeginIndex(2); + fpos.setEndIndex(3); + assertEquals("getEndIndex should have returned 3", 3, fpos + .getEndIndex()); + } + + /** + * @tests java.text.FieldPosition#getField() + */ + public void test_getField() { + // Test for method int java.text.FieldPosition.getField() + FieldPosition fpos = new FieldPosition(65); + assertEquals( + "FieldPosition(65) should have caused getField to return 65", + 65, fpos.getField()); + FieldPosition fpos2 = new FieldPosition(DateFormat.Field.MINUTE); + assertEquals( + "FieldPosition(DateFormat.Field.MINUTE) should have caused getField to return -1", + -1, fpos2.getField()); + } + + /** + * @tests java.text.FieldPosition#getFieldAttribute() + */ + public void test_getFieldAttribute() { + // Test for method int java.text.FieldPosition.getFieldAttribute() + FieldPosition fpos = new FieldPosition(DateFormat.Field.TIME_ZONE); + assertTrue( + "FieldPosition(DateFormat.Field.TIME_ZONE) should have caused getFieldAttribute to return DateFormat.Field.TIME_ZONE", + fpos.getFieldAttribute() == DateFormat.Field.TIME_ZONE); + + FieldPosition fpos2 = new FieldPosition(DateFormat.TIMEZONE_FIELD); + assertNull( + "FieldPosition(DateFormat.TIMEZONE_FIELD) should have caused getFieldAttribute to return null", + fpos2.getFieldAttribute()); + } + + /** + * @tests java.text.FieldPosition#hashCode() + */ + public void test_hashCode() { + // Test for method int java.text.FieldPosition.hashCode() + FieldPosition fpos = new FieldPosition(1); + fpos.setBeginIndex(5); + fpos.setEndIndex(110); + assertEquals("hashCode returned incorrect value", 620, fpos.hashCode()); + + FieldPosition fpos2 = new FieldPosition( + DateFormat.Field.DAY_OF_WEEK_IN_MONTH); + fpos2.setBeginIndex(5); + fpos2.setEndIndex(110); + assertEquals("hashCode returned incorrect value", 451685956, fpos2 + .hashCode()); + } + + /** + * @tests java.text.FieldPosition#setBeginIndex(int) + */ + public void test_setBeginIndexI() { + // Test for method void java.text.FieldPosition.setBeginIndex(int) + FieldPosition fpos = new FieldPosition(1); + fpos.setBeginIndex(2); + fpos.setEndIndex(3); + assertEquals("beginIndex should have been set to 2", 2, fpos + .getBeginIndex()); + } + + /** + * @tests java.text.FieldPosition#setEndIndex(int) + */ + public void test_setEndIndexI() { + // Test for method void java.text.FieldPosition.setEndIndex(int) + FieldPosition fpos = new FieldPosition(1); + fpos.setEndIndex(3); + fpos.setBeginIndex(2); + assertEquals("EndIndex should have been set to 3", 3, fpos + .getEndIndex()); + } + + /** + * @tests java.text.FieldPosition#toString() + */ + public void test_toString() { + // Test for method java.lang.String java.text.FieldPosition.toString() + FieldPosition fpos = new FieldPosition(1); + fpos.setBeginIndex(2); + fpos.setEndIndex(3); + assertEquals( + "ToString returned the wrong value:", + "java.text.FieldPosition[attribute=null, field=1, beginIndex=2, endIndex=3]", + fpos.toString()); + + FieldPosition fpos2 = new FieldPosition(DateFormat.Field.ERA); + fpos2.setBeginIndex(4); + fpos2.setEndIndex(5); + assertEquals("ToString returned the wrong value:", + "java.text.FieldPosition[attribute=" + DateFormat.Field.ERA + + ", field=-1, beginIndex=4, endIndex=5]", fpos2 + .toString()); + } + + /** + * Sets up the fixture, for example, open a network connection. This method + * is called before a test is executed. + */ + protected void setUp() { + } + + /** + * Tears down the fixture, for example, close a network connection. This + * method is called after a test is executed. + */ + protected void tearDown() { + } +} diff --git a/text/src/test/java/org/apache/harmony/text/tests/java/text/FormatFieldTest.java b/text/src/test/java/org/apache/harmony/text/tests/java/text/FormatFieldTest.java new file mode 100644 index 0000000..1bc6b39 --- /dev/null +++ b/text/src/test/java/org/apache/harmony/text/tests/java/text/FormatFieldTest.java @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.text.tests.java.text; + +import java.text.Format; + +import junit.framework.Test; +import junit.framework.TestCase; + +public class FormatFieldTest extends TestCase { + private class MockFormatField extends Format.Field { + + private static final long serialVersionUID = 1L; + + public MockFormatField(String name) { + super(name); + } + } + + /** + * @tests java.text.Format.Field#FormatField(java.lang.String) Test of + * method java.text.Format.Field#FormatField(java.lang.String). + */ + public void test_Constructor() { + try { + new MockFormatField("test"); + } catch (Exception e) { + fail("Unexpected exception " + e.toString()); + } + } +} diff --git a/text/src/test/java/org/apache/harmony/text/tests/java/text/FormatTest.java b/text/src/test/java/org/apache/harmony/text/tests/java/text/FormatTest.java new file mode 100644 index 0000000..83337c0 --- /dev/null +++ b/text/src/test/java/org/apache/harmony/text/tests/java/text/FormatTest.java @@ -0,0 +1,98 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.text.tests.java.text; + +import java.text.AttributedCharacterIterator; +import java.text.DecimalFormatSymbols; +import java.text.FieldPosition; +import java.text.Format; +import java.text.ParsePosition; +import java.util.Locale; + +import junit.framework.Test; +import junit.framework.TestCase; + +public class FormatTest extends TestCase { + private class MockFormat extends Format { + + public StringBuffer format(Object obj, StringBuffer toAppendTo, + FieldPosition pos) { + // it is a fake + if (obj == null) + throw new NullPointerException("obj is null"); + return new StringBuffer(""); + } + + public Object parseObject(String source, ParsePosition pos) { + // it is a fake + return null; + } + } + + /** + * @tests java.text.Format#format(Object) Test of method + * java.text.Format#format(Object). + */ + public void test_Constructor() { + try { + new MockFormat(); + } catch (Exception e) { + fail("Unexpected exception " + e.toString()); + } + } + + /** + * @tests java.text.Format#clone() Test of method java.text.Format#clone(). + * Compare of internal variables of cloned objects. + */ + public void test_clone() { + try { + // Compare of internal variables of cloned objects + Format fm = new MockFormat(); + Format fmc = (Format) fm.clone(); + assertEquals(fm.getClass(), fmc.getClass()); + } catch (Exception e) { + fail("Unexpected exception " + e.toString()); + } + } + + /** + * @tests java.text.Format#format(java.lang.Object) Test of method + * java.text.Format#format(java.lang.Object). + */ + public void test_formatLjava_lang_Object() { + assertTrue("It calls an abstract metod format", true); + } + + /** + * @tests java.text.Format#formatToCharacterIterator(java.lang.Object) Test + * of method + * java.text.Format#formatToCharacterIterator(java.lang.Object). + */ + public void test_formatToCharacterIteratorLjava_lang_Object() { + assertTrue("It calls an abstract metod format", true); + } + + /** + * @tests java.text.Format#parseObject(java.lang.String source) Test of + * method java.text.Format#parseObject(java.lang.String source). + */ + public void test_parseObjectLjava_lang_String() { + assertTrue("It calls an abstract metod parseObject", true); + } +} diff --git a/text/src/test/java/org/apache/harmony/text/tests/java/text/MessageFormatFieldTest.java b/text/src/test/java/org/apache/harmony/text/tests/java/text/MessageFormatFieldTest.java new file mode 100644 index 0000000..3b4161d --- /dev/null +++ b/text/src/test/java/org/apache/harmony/text/tests/java/text/MessageFormatFieldTest.java @@ -0,0 +1,110 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.harmony.text.tests.java.text; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.text.MessageFormat; + +import junit.framework.TestCase; + +public class MessageFormatFieldTest extends TestCase { + /** + * @tests java.text.MessageFormat$Field#Field(java.lang.String) + */ + public void test_ConstructorLjava_lang_String() { + // protected constructor + String name = "new Message format"; + MyMessageFormat field = new MyMessageFormat(name); + assertEquals("field has wrong name", name, field.getName()); + + field = new MyMessageFormat(null); + assertEquals("field has wrong name", null, field.getName()); + } + + /** + * @tests java.text.MessageFormat$Field#readResolve() + */ + public void test_readResolve() { + // test for method java.lang.Object readResolve() + + // see serialization stress tests: + // implemented in + // SerializationStressTest4.test_writeObject_MessageFormat_Field() + ObjectOutputStream out = null; + ObjectInputStream in = null; + try { + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + out = new ObjectOutputStream(bytes); + + MessageFormat.Field mfield, mfield2; + MyMessageFormat field; + + mfield = MessageFormat.Field.ARGUMENT; + + field = new MyMessageFormat(null); + + out.writeObject(mfield); + out.writeObject(field); + + in = new ObjectInputStream(new ByteArrayInputStream(bytes + .toByteArray())); + + try { + mfield2 = (MessageFormat.Field) in.readObject(); + assertSame("resolved incorrectly", mfield, mfield2); + } catch (IllegalArgumentException e) { + fail("Unexpected IllegalArgumentException: " + e); + } + + try { + in.readObject(); + fail("Expected InvalidObjectException for subclass instance with null name"); + } catch (InvalidObjectException e) { + } + + } catch (IOException e) { + fail("unexpected IOException" + e); + } catch (ClassNotFoundException e) { + fail("unexpected ClassNotFoundException" + e); + } finally { + try { + if (out != null) + out.close(); + if (in != null) + in.close(); + } catch (IOException e) { + } + } + } + + static class MyMessageFormat extends MessageFormat.Field { + static final long serialVersionUID = 1L; + + protected MyMessageFormat(String attr) { + super(attr); + } + + protected String getName() { + return super.getName(); + } + } +} diff --git a/text/src/test/java/org/apache/harmony/text/tests/java/text/MessageFormatTest.java b/text/src/test/java/org/apache/harmony/text/tests/java/text/MessageFormatTest.java new file mode 100644 index 0000000..6f3022e --- /dev/null +++ b/text/src/test/java/org/apache/harmony/text/tests/java/text/MessageFormatTest.java @@ -0,0 +1,1073 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.text.tests.java.text; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.text.ChoiceFormat; +import java.text.DateFormat; +import java.text.FieldPosition; +import java.text.Format; +import java.text.MessageFormat; +import java.text.NumberFormat; +import java.text.ParseException; +import java.text.ParsePosition; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.Locale; +import java.util.TimeZone; + +import junit.framework.Test; +import junit.framework.TestCase; +import tests.support.Support_MessageFormat; + +public class MessageFormatTest extends TestCase { + + private MessageFormat format1, format2, format3; + + private Locale defaultLocale; + + private void checkSerialization(MessageFormat format) { + try { + ByteArrayOutputStream ba = new ByteArrayOutputStream(); + ObjectOutputStream out = new ObjectOutputStream(ba); + out.writeObject(format); + out.close(); + ObjectInputStream in = new ObjectInputStream( + new ByteArrayInputStream(ba.toByteArray())); + MessageFormat read = (MessageFormat) in.readObject(); + assertTrue("Not equal: " + format.toPattern(), format.equals(read)); + } catch (IOException e) { + fail("Format: " + format.toPattern() + " caused IOException: " + e); + } catch (ClassNotFoundException e) { + fail("Format: " + format.toPattern() + + " caused ClassNotFoundException: " + e); + } + } + + /** + * @tests java.text.MessageFormat#MessageFormat(java.lang.String, + * java.util.Locale) + */ + public void test_ConstructorLjava_lang_StringLjava_util_Locale() { + // Test for method java.text.MessageFormat(java.lang.String, + // java.util.Locale) + Locale mk = new Locale("mk", "MK"); + MessageFormat format = new MessageFormat( + "Date: {0,date} Currency: {1, number, currency} Integer: {2, number, integer}", + mk); + + assertTrue("Wrong locale1", format.getLocale().equals(mk)); + assertTrue("Wrong locale2", format.getFormats()[0].equals(DateFormat + .getDateInstance(DateFormat.DEFAULT, mk))); + assertTrue("Wrong locale3", format.getFormats()[1].equals(NumberFormat + .getCurrencyInstance(mk))); + assertTrue("Wrong locale4", format.getFormats()[2].equals(NumberFormat + .getIntegerInstance(mk))); + } + + /** + * @tests java.text.MessageFormat#MessageFormat(java.lang.String) + */ + public void test_ConstructorLjava_lang_String() { + // Test for method java.text.MessageFormat(java.lang.String) + MessageFormat format = new MessageFormat( + "abc {4,time} def {3,date} ghi {2,number} jkl {1,choice,0#low|1#high} mnop {0}"); + assertTrue("Not a MessageFormat", + format.getClass() == MessageFormat.class); + Format[] formats = format.getFormats(); + assertNotNull("null formats", formats); + assertTrue("Wrong format count: " + formats.length, formats.length >= 5); + assertTrue("Wrong time format", formats[0].equals(DateFormat + .getTimeInstance())); + assertTrue("Wrong date format", formats[1].equals(DateFormat + .getDateInstance())); + assertTrue("Wrong number format", formats[2].equals(NumberFormat + .getInstance())); + assertTrue("Wrong choice format", formats[3].equals(new ChoiceFormat( + "0.0#low|1.0#high"))); + assertNull("Wrong string format", formats[4]); + + Date date = new Date(); + FieldPosition pos = new FieldPosition(-1); + StringBuffer buffer = new StringBuffer(); + format.format(new Object[] { "123", new Double(1.6), new Double(7.2), + date, date }, buffer, pos); + String result = buffer.toString(); + buffer.setLength(0); + buffer.append("abc "); + buffer.append(DateFormat.getTimeInstance().format(date)); + buffer.append(" def "); + buffer.append(DateFormat.getDateInstance().format(date)); + buffer.append(" ghi "); + buffer.append(NumberFormat.getInstance().format(new Double(7.2))); + buffer.append(" jkl high mnop 123"); + assertTrue("Wrong answer:\n" + result + "\n" + buffer, result + .equals(buffer.toString())); + + assertEquals("Simple string", "Test message", new MessageFormat( + "Test message").format(new Object[0])); + + try { + result = new MessageFormat("Don't").format(new Object[0]); + assertTrue("Should not throw IllegalArgumentException: " + result, + "Dont".equals(result)); + } catch (Exception e) { + fail("Unexpected exception: " + e); + } + + try { + new MessageFormat("Invalid {1,foobar} format descriptor!"); + fail("Expected test_ConstructorLjava_lang_String to throw IAE."); + } catch (IllegalArgumentException ex) { + // expected + } catch (Throwable ex) { + fail("Expected test_ConstructorLjava_lang_String to throw IAE, not a " + + ex.getClass().getName()); + } + + try { + new MessageFormat( + "Invalid {1,date,invalid-spec} format descriptor!"); + } catch (IllegalArgumentException ex) { + // expected + } catch (Throwable ex) { + fail("Expected test_ConstructorLjava_lang_String to throw IAE, not a " + + ex.getClass().getName()); + } + + checkSerialization(new MessageFormat("")); + checkSerialization(new MessageFormat("noargs")); + checkSerialization(new MessageFormat("{0}")); + checkSerialization(new MessageFormat("a{0}")); + checkSerialization(new MessageFormat("{0}b")); + checkSerialization(new MessageFormat("a{0}b")); + + // Regression for HARMONY-65 + try { + new MessageFormat("{0,number,integer"); + fail("Assert 0: Failed to detect unmatched brackets."); + } catch (IllegalArgumentException e) { + // expected + } + } + + /** + * @tests java.text.MessageFormat#applyPattern(java.lang.String) + */ + public void test_applyPatternLjava_lang_String() { + // Test for method void + // java.text.MessageFormat.applyPattern(java.lang.String) + MessageFormat format = new MessageFormat("test"); + format.applyPattern("xx {0}"); + assertEquals("Invalid number", "xx 46", format + .format(new Object[] { new Integer(46) })); + Date date = new Date(); + String result = format.format(new Object[] { date }); + String expected = "xx " + DateFormat.getInstance().format(date); + assertTrue("Invalid date:\n" + result + "\n" + expected, result + .equals(expected)); + format = new MessageFormat("{0,date}{1,time}{2,number,integer}"); + format.applyPattern("nothing"); + assertEquals("Found formats", "nothing", format.toPattern()); + + format.applyPattern("{0}"); + assertNull("Wrong format", format.getFormats()[0]); + assertEquals("Wrong pattern", "{0}", format.toPattern()); + + format.applyPattern("{0, \t\u001ftime }"); + assertTrue("Wrong time format", format.getFormats()[0] + .equals(DateFormat.getTimeInstance())); + assertEquals("Wrong time pattern", "{0,time}", format.toPattern()); + format.applyPattern("{0,Time, Short\n}"); + assertTrue("Wrong short time format", format.getFormats()[0] + .equals(DateFormat.getTimeInstance(DateFormat.SHORT))); + assertEquals("Wrong short time pattern", "{0,time,short}", format + .toPattern()); + format.applyPattern("{0,TIME,\nmedium }"); + assertTrue("Wrong medium time format", format.getFormats()[0] + .equals(DateFormat.getTimeInstance(DateFormat.MEDIUM))); + assertEquals("Wrong medium time pattern", "{0,time}", format + .toPattern()); + format.applyPattern("{0,time,LONG}"); + assertTrue("Wrong long time format", format.getFormats()[0] + .equals(DateFormat.getTimeInstance(DateFormat.LONG))); + assertEquals("Wrong long time pattern", "{0,time,long}", format + .toPattern()); + format.setLocale(Locale.FRENCH); // use French since English has the + // same LONG and FULL time patterns + format.applyPattern("{0,time, Full}"); + assertTrue("Wrong full time format", format.getFormats()[0] + .equals(DateFormat.getTimeInstance(DateFormat.FULL, + Locale.FRENCH))); + assertEquals("Wrong full time pattern", "{0,time,full}", format + .toPattern()); + format.setLocale(Locale.getDefault()); + + format.applyPattern("{0, date}"); + assertTrue("Wrong date format", format.getFormats()[0] + .equals(DateFormat.getDateInstance())); + assertEquals("Wrong date pattern", "{0,date}", format.toPattern()); + format.applyPattern("{0, date, short}"); + assertTrue("Wrong short date format", format.getFormats()[0] + .equals(DateFormat.getDateInstance(DateFormat.SHORT))); + assertEquals("Wrong short date pattern", "{0,date,short}", format + .toPattern()); + format.applyPattern("{0, date, medium}"); + assertTrue("Wrong medium date format", format.getFormats()[0] + .equals(DateFormat.getDateInstance(DateFormat.MEDIUM))); + assertEquals("Wrong medium date pattern", "{0,date}", format + .toPattern()); + format.applyPattern("{0, date, long}"); + assertTrue("Wrong long date format", format.getFormats()[0] + .equals(DateFormat.getDateInstance(DateFormat.LONG))); + assertEquals("Wrong long date pattern", "{0,date,long}", format + .toPattern()); + format.applyPattern("{0, date, full}"); + assertTrue("Wrong full date format", format.getFormats()[0] + .equals(DateFormat.getDateInstance(DateFormat.FULL))); + assertEquals("Wrong full date pattern", "{0,date,full}", format + .toPattern()); + + format.applyPattern("{0, date, MMM d {hh:mm:ss}}"); + assertEquals("Wrong time/date format", " MMM d {hh:mm:ss}", + ((SimpleDateFormat) (format.getFormats()[0])).toPattern()); + assertEquals("Wrong time/date pattern", "{0,date, MMM d {hh:mm:ss}}", + format.toPattern()); + + format.applyPattern("{0, number}"); + assertTrue("Wrong number format", format.getFormats()[0] + .equals(NumberFormat.getNumberInstance())); + assertEquals("Wrong number pattern", "{0,number}", format.toPattern()); + format.applyPattern("{0, number, currency}"); + assertTrue("Wrong currency number format", format.getFormats()[0] + .equals(NumberFormat.getCurrencyInstance())); + assertEquals("Wrong currency number pattern", "{0,number,currency}", + format.toPattern()); + format.applyPattern("{0, number, percent}"); + assertTrue("Wrong percent number format", format.getFormats()[0] + .equals(NumberFormat.getPercentInstance())); + assertEquals("Wrong percent number pattern", "{0,number,percent}", + format.toPattern()); + format.applyPattern("{0, number, integer}"); + NumberFormat nf = NumberFormat.getInstance(); + nf.setMaximumFractionDigits(0); + nf.setParseIntegerOnly(true); + assertTrue("Wrong integer number format", format.getFormats()[0] + .equals(nf)); + assertEquals("Wrong integer number pattern", "{0,number,integer}", + format.toPattern()); + + format.applyPattern("{0, number, {'#'}##0.0E0}"); + + /* + * TODO validate these assertions String actual = + * ((DecimalFormat)(format.getFormats()[0])).toPattern(); + * assertEquals("Wrong pattern number format", "' {#}'##0.0E0", actual); + * assertEquals("Wrong pattern number pattern", "{0,number,' + * {#}'##0.0E0}", format.toPattern()); + * + */ + + format.applyPattern("{0, choice,0#no|1#one|2#{1,number}}"); + assertEquals("Wrong choice format", + + "0.0#no|1.0#one|2.0#{1,number}", + ((ChoiceFormat) format.getFormats()[0]).toPattern()); + assertEquals("Wrong choice pattern", + "{0,choice,0.0#no|1.0#one|2.0#{1,number}}", format.toPattern()); + assertEquals("Wrong formatted choice", "3.6", format + .format(new Object[] { new Integer(2), new Float(3.6) })); + + try { + format.applyPattern("WRONG MESSAGE FORMAT {0,number,{}"); + fail("Expected IllegalArgumentException for invalid pattern"); + } catch (IllegalArgumentException e) { + } + + // Regression for HARMONY-65 + MessageFormat mf = new MessageFormat("{0,number,integer}"); + String badpattern = "{0,number,#"; + try { + mf.applyPattern(badpattern); + fail("Assert 0: Failed to detect unmatched brackets."); + } catch (IllegalArgumentException e) { + // expected + } + } + + /** + * @tests java.text.MessageFormat#clone() + */ + public void test_clone() { + // Test for method java.lang.Object java.text.MessageFormat.clone() + MessageFormat format = new MessageFormat("'{'choice'}'{0}"); + MessageFormat clone = (MessageFormat) format.clone(); + assertTrue("Clone not equal", format.equals(clone)); + assertEquals("Wrong answer", "{choice}{0}", format + .format(new Object[] {})); + clone.setFormat(0, DateFormat.getInstance()); + assertTrue("Clone shares format data", !format.equals(clone)); + format = (MessageFormat) clone.clone(); + Format[] formats = clone.getFormats(); + ((SimpleDateFormat) formats[0]).applyPattern("adk123"); + assertTrue("Clone shares format data", !format.equals(clone)); + } + + /** + * @tests java.text.MessageFormat#equals(java.lang.Object) + */ + public void test_equalsLjava_lang_Object() { + // Test for method boolean + // java.text.MessageFormat.equals(java.lang.Object) + MessageFormat format1 = new MessageFormat("{0}"); + MessageFormat format2 = new MessageFormat("{1}"); + assertTrue("Should not be equal", !format1.equals(format2)); + format2.applyPattern("{0}"); + assertTrue("Should be equal", format1.equals(format2)); + SimpleDateFormat date = (SimpleDateFormat) DateFormat.getTimeInstance(); + format1.setFormat(0, DateFormat.getTimeInstance()); + format2.setFormat(0, new SimpleDateFormat(date.toPattern())); + assertTrue("Should be equal2", format1.equals(format2)); + } + + /** + * @tests java.text.MessageFormat#hashCode() + */ + public void test_hashCode() { + // Test for method + // int java.text.MessageFormat.hashCode() + assertEquals("Should be equal", 3648, new MessageFormat("rr", null) + .hashCode()); + } + + /** + * @tests java.text.MessageFormat#formatToCharacterIterator(java.lang.Object) + */ + // FIXME This test fails on Harmony ClassLibrary + public void failing_test_formatToCharacterIteratorLjava_lang_Object() { + // Test for method formatToCharacterIterator(java.lang.Object) + new Support_MessageFormat( + "test_formatToCharacterIteratorLjava_lang_Object") + .t_formatToCharacterIterator(); + } + + /** + * @tests java.text.MessageFormat#format(java.lang.Object[], + * java.lang.StringBuffer, java.text.FieldPosition) + */ + public void test_format$Ljava_lang_ObjectLjava_lang_StringBufferLjava_text_FieldPosition() { + // Test for method java.lang.StringBuffer + // java.text.MessageFormat.format(java.lang.Object [], + // java.lang.StringBuffer, java.text.FieldPosition) + MessageFormat format = new MessageFormat("{1,number,integer}"); + StringBuffer buffer = new StringBuffer(); + format.format(new Object[] { "0", new Double(53.863) }, buffer, + new FieldPosition(0)); + assertEquals("Wrong result", "54", buffer.toString()); + format + .applyPattern("{0,choice,0#zero|1#one '{1,choice,2#two {2,time}}'}"); + Date date = new Date(); + String expected = "one two " + + DateFormat.getTimeInstance().format(date); + String result = format.format(new Object[] { new Double(1.6), + new Integer(3), date }); + assertTrue("Choice not recursive:\n" + expected + "\n" + result, + expected.equals(result)); + } + + /** + * @tests java.text.MessageFormat#format(java.lang.Object, + * java.lang.StringBuffer, java.text.FieldPosition) + */ + public void test_formatLjava_lang_ObjectLjava_lang_StringBufferLjava_text_FieldPosition() { + // Test for method java.lang.StringBuffer + // java.text.MessageFormat.format(java.lang.Object, + // java.lang.StringBuffer, java.text.FieldPosition) + new Support_MessageFormat( + "test_formatLjava_lang_ObjectLjava_lang_StringBufferLjava_text_FieldPosition") + .t_format_with_FieldPosition(); + } + + /** + * @tests java.text.MessageFormat#format(java.lang.String, + * java.lang.Object...) Test of method + * java.text.MessageFormat#format(java.lang.String, + * java.lang.Object...). + */ + public void test_formatLjava_lang_StringLjava_lang_Object() { + int iCurrency = 123; + int iInteger = Integer.MIN_VALUE; + + Date date = new Date(12345678); + Object[] args = { date, iCurrency, iInteger }; + String resStr = "Date: Jan 1, 1970 Currency: $" + iCurrency + + ".00 Integer: -2,147,483,648"; + String pattern = "Date: {0,date} Currency: {1, number, currency} Integer: {2, number, integer}"; + String sFormat = MessageFormat.format(pattern, (Object[]) args); + assertEquals( + "format(String, Object[]) with valid parameters returns incorrect string: case 1", + sFormat, resStr); + + pattern = "abc {4, number, integer} def {3,date} ghi {2,number} jkl {1,choice,0#low|1#high} mnop {0}"; + resStr = "abc -2,147,483,648 def Jan 1, 1970 ghi -2,147,483,648 jkl high mnop -2,147,483,648"; + Object[] args_ = { iInteger, 1, iInteger, date, iInteger }; + sFormat = MessageFormat.format(pattern, args_); + assertEquals( + "format(String, Object[]) with valid parameters returns incorrect string: case 1", + sFormat, resStr); + + try { + args = null; + MessageFormat.format(null, args); + fail("Doesn't throw IllegalArgumentException: null, null"); + } catch (Exception e) { + // expected + } + + try { + MessageFormat.format("Invalid {1,foobar} format descriptor!", + new Object[] {iInteger} ); + fail("Doesn't throw IllegalArgumentException with invalid pattern as a parameter: case 1"); + } catch (IllegalArgumentException ex) { + // expected + } + + try { + MessageFormat.format( + "Invalid {1,date,invalid-spec} format descriptor!", new Object[]{""}); + fail("Doesn't throw IllegalArgumentException with invalid pattern as a parameter: case 2"); + } catch (IllegalArgumentException ex) { + // expected + } + + try { + MessageFormat.format("{0,number,integer", new Object[] {iInteger}); + fail("Doesn't throw IllegalArgumentException, doesn't detect unmatched brackets"); + } catch (IllegalArgumentException ex) { + // expected + } + + try { + MessageFormat.format( + "Valid {1, date} format {0, number} descriptor!", new Object[]{ "" } ); + fail("Doesn't throw IllegalArgumentException with invalid Object array"); + } catch (IllegalArgumentException ex) { + // expected + } + } + + /** + * @tests java.text.MessageFormat#getFormats() + */ + public void test_getFormats() { + // Test for method java.text.Format [] + // java.text.MessageFormat.getFormats() + + // test with repeating formats and max argument index < max offset + Format[] formats = format1.getFormats(); + Format[] correctFormats = new Format[] { + NumberFormat.getCurrencyInstance(), + DateFormat.getTimeInstance(), + NumberFormat.getPercentInstance(), null, + new ChoiceFormat("0#off|1#on"), DateFormat.getDateInstance(), }; + + assertEquals("Test1:Returned wrong number of formats:", + correctFormats.length, formats.length); + for (int i = 0; i < correctFormats.length; i++) { + assertEquals("Test1:wrong format for pattern index " + i + ":", + correctFormats[i], formats[i]); + } + + // test with max argument index > max offset + formats = format2.getFormats(); + correctFormats = new Format[] { NumberFormat.getCurrencyInstance(), + DateFormat.getTimeInstance(), + NumberFormat.getPercentInstance(), null, + new ChoiceFormat("0#off|1#on"), DateFormat.getDateInstance() }; + + assertEquals("Test2:Returned wrong number of formats:", + correctFormats.length, formats.length); + for (int i = 0; i < correctFormats.length; i++) { + assertEquals("Test2:wrong format for pattern index " + i + ":", + correctFormats[i], formats[i]); + } + + // test with argument number being zero + formats = format3.getFormats(); + assertEquals("Test3: Returned wrong number of formats:", 0, + formats.length); + } + + /** + * @tests java.text.MessageFormat#getFormatsByArgumentIndex() + */ + public void test_getFormatsByArgumentIndex() { + // Test for method java.text.Format [] test_getFormatsByArgumentIndex() + + // test with repeating formats and max argument index < max offset + Format[] formats = format1.getFormatsByArgumentIndex(); + Format[] correctFormats = new Format[] { DateFormat.getDateInstance(), + new ChoiceFormat("0#off|1#on"), DateFormat.getTimeInstance(), + NumberFormat.getCurrencyInstance(), null }; + + assertEquals("Test1:Returned wrong number of formats:", + correctFormats.length, formats.length); + for (int i = 0; i < correctFormats.length; i++) { + assertEquals("Test1:wrong format for argument index " + i + ":", + correctFormats[i], formats[i]); + } + + // test with max argument index > max offset + formats = format2.getFormatsByArgumentIndex(); + correctFormats = new Format[] { DateFormat.getDateInstance(), + new ChoiceFormat("0#off|1#on"), null, + NumberFormat.getCurrencyInstance(), null, null, null, null, + DateFormat.getTimeInstance() }; + + assertEquals("Test2:Returned wrong number of formats:", + correctFormats.length, formats.length); + for (int i = 0; i < correctFormats.length; i++) { + assertEquals("Test2:wrong format for argument index " + i + ":", + correctFormats[i], formats[i]); + } + + // test with argument number being zero + formats = format3.getFormatsByArgumentIndex(); + assertEquals("Test3: Returned wrong number of formats:", 0, + formats.length); + } + + /** + * @tests java.text.MessageFormat#getLocale() Test of method + * java.text.MessageFormat#getLocale(). + */ + public void test_getLocale() { + try { + Locale[] l = { + Locale.FRANCE, + Locale.KOREA, + new Locale(Locale.FRANCE.getCountry(), Locale.FRANCE + .getLanguage()), new Locale("mk"), + new Locale("mk", "MK"), Locale.US, + new Locale("#ru", "@31230") }; + + String pattern = "getLocale test {0,number,#,####}"; + MessageFormat mf; + + for (int i = 0; i < 0; i++) { + mf = new MessageFormat(pattern, l[i]); + Locale result = mf.getLocale(); + assertEquals("Returned local: " + result + " instead of " + + l[i], l[i], result); + assertEquals("Returned language: " + result.getLanguage() + + " instead of " + l[i].getLanguage(), l[i] + .getLanguage(), result.getLanguage()); + assertEquals("Returned country: " + result.getCountry() + + " instead of " + l[i].getCountry(), + l[i].getCountry(), result.getCountry()); + } + + mf = new MessageFormat(pattern); + mf.setLocale(null); + Locale result = mf.getLocale(); + assertEquals("Returned local: " + result + " instead of null", + null, result); + } catch (Exception e) { + fail("unexpected exception " + e.toString()); + } + } + + /** + * @tests java.text.MessageFormat#setFormat(int, Format) Test of method + * java.text.MessageFormat#setFormat(int, Format). Case 1: Compare + * getFormats() results after calls to setFormat(). Case 2: Try to + * call setFormat() using incorrect index. + */ + public void test_setFormatILjava_text_Format() { + try { + // case 1: Compare getFormats() results after calls to setFormat() + MessageFormat f1 = (MessageFormat) format1.clone(); + f1.setFormat(0, DateFormat.getTimeInstance()); + f1.setFormat(1, DateFormat.getTimeInstance()); + f1.setFormat(2, NumberFormat.getInstance()); + f1.setFormat(3, new ChoiceFormat("0#off|1#on")); + f1.setFormat(4, new ChoiceFormat("1#few|2#ok|3#a lot")); + f1.setFormat(5, DateFormat.getTimeInstance()); + + Format[] formats = f1.getFormats(); + formats = f1.getFormats(); + + Format[] correctFormats = new Format[] { + DateFormat.getTimeInstance(), DateFormat.getTimeInstance(), + NumberFormat.getInstance(), new ChoiceFormat("0#off|1#on"), + new ChoiceFormat("1#few|2#ok|3#a lot"), + DateFormat.getTimeInstance() }; + + assertEquals("Test1A:Returned wrong number of formats:", + correctFormats.length, formats.length); + for (int i = 0; i < correctFormats.length; i++) { + assertEquals( + "Test1B:wrong format for pattern index " + i + ":", + correctFormats[i], formats[i]); + } + + // case 2: Try to setFormat using incorrect index + try { + f1.setFormat(-1, DateFormat.getDateInstance()); + fail("Expected ArrayIndexOutOfBoundsException was not thrown"); + f1.setFormat(f1.getFormats().length, DateFormat + .getDateInstance()); + fail("Expected ArrayIndexOutOfBoundsException was not thrown"); + } catch (ArrayIndexOutOfBoundsException e) { + // expected + } + } catch (Exception e) { + fail("Unexpected exception " + e.toString()); + } + } + + /** + * @tests java.text.MessageFormat#setFormatByArgumentIndex(int, + * java.text.Format) + */ + public void test_setFormatByArgumentIndexILjava_text_Format() { + // test for method setFormatByArgumentIndex(int, Format) + MessageFormat f1 = (MessageFormat) format1.clone(); + f1.setFormatByArgumentIndex(0, DateFormat.getTimeInstance()); + f1.setFormatByArgumentIndex(4, new ChoiceFormat("1#few|2#ok|3#a lot")); + + // test with repeating formats and max argument index < max offset + // compare getFormatsByArgumentIndex() results after calls to + // setFormatByArgumentIndex() + Format[] formats = f1.getFormatsByArgumentIndex(); + + Format[] correctFormats = new Format[] { DateFormat.getTimeInstance(), + new ChoiceFormat("0#off|1#on"), DateFormat.getTimeInstance(), + NumberFormat.getCurrencyInstance(), + new ChoiceFormat("1#few|2#ok|3#a lot") }; + + assertEquals("Test1A:Returned wrong number of formats:", + correctFormats.length, formats.length); + for (int i = 0; i < correctFormats.length; i++) { + assertEquals("Test1B:wrong format for argument index " + i + ":", + correctFormats[i], formats[i]); + } + + // compare getFormats() results after calls to + // setFormatByArgumentIndex() + formats = f1.getFormats(); + + correctFormats = new Format[] { NumberFormat.getCurrencyInstance(), + DateFormat.getTimeInstance(), DateFormat.getTimeInstance(), + new ChoiceFormat("1#few|2#ok|3#a lot"), + new ChoiceFormat("0#off|1#on"), DateFormat.getTimeInstance(), }; + + assertEquals("Test1C:Returned wrong number of formats:", + correctFormats.length, formats.length); + for (int i = 0; i < correctFormats.length; i++) { + assertEquals("Test1D:wrong format for pattern index " + i + ":", + correctFormats[i], formats[i]); + } + + // test setting argumentIndexes that are not used + MessageFormat f2 = (MessageFormat) format2.clone(); + f2.setFormatByArgumentIndex(2, NumberFormat.getPercentInstance()); + f2.setFormatByArgumentIndex(4, DateFormat.getTimeInstance()); + + formats = f2.getFormatsByArgumentIndex(); + correctFormats = format2.getFormatsByArgumentIndex(); + + assertEquals("Test2A:Returned wrong number of formats:", + correctFormats.length, formats.length); + for (int i = 0; i < correctFormats.length; i++) { + assertEquals("Test2B:wrong format for argument index " + i + ":", + correctFormats[i], formats[i]); + } + + formats = f2.getFormats(); + correctFormats = format2.getFormats(); + + assertEquals("Test2C:Returned wrong number of formats:", + correctFormats.length, formats.length); + for (int i = 0; i < correctFormats.length; i++) { + assertEquals("Test2D:wrong format for pattern index " + i + ":", + correctFormats[i], formats[i]); + } + + // test exceeding the argumentIndex number + MessageFormat f3 = (MessageFormat) format3.clone(); + f3.setFormatByArgumentIndex(1, NumberFormat.getCurrencyInstance()); + + formats = f3.getFormatsByArgumentIndex(); + assertEquals("Test3A:Returned wrong number of formats:", 0, + formats.length); + + formats = f3.getFormats(); + assertEquals("Test3B:Returned wrong number of formats:", 0, + formats.length); + } + + /** + * @tests java.text.MessageFormat#setFormats(Format[]) Test of method + * java.text.MessageFormat#setFormats(Format[]). Case 1: Test with + * repeating formats and max argument index < max offset compare + * getFormats() results after calls to setFormats(Format[]) Case 2: + * Try to pass null argument to setFormats(). + */ + public void test_setFormats$Ljava_text_Format() { + try { + MessageFormat f1 = (MessageFormat) format1.clone(); + + // case 1: Test with repeating formats and max argument index < max + // offset + // compare getFormats() results after calls to setFormats(Format[]) + Format[] correctFormats = new Format[] { + DateFormat.getTimeInstance(), + new ChoiceFormat("0#off|1#on"), + DateFormat.getTimeInstance(), + NumberFormat.getCurrencyInstance(), + new ChoiceFormat("1#few|2#ok|3#a lot") }; + + f1.setFormats(correctFormats); + Format[] formats = f1.getFormats(); + + assertTrue("Test1A:Returned wrong number of formats:", + correctFormats.length <= formats.length); + for (int i = 0; i < correctFormats.length; i++) { + assertEquals("Test1B:wrong format for argument index " + i + + ":", correctFormats[i], formats[i]); + } + + // case 2: Try to pass null argument to setFormats(). + try { + f1.setFormats(null); + fail("Expected exception NullPointerException was not thrown"); + } catch (NullPointerException e) { + // expected + } + } catch (Exception e) { + fail("Unexpected exception " + e.toString()); + } + } + + /** + * @tests java.text.MessageFormat#setFormatsByArgumentIndex(java.text.Format[]) + */ + public void test_setFormatsByArgumentIndex$Ljava_text_Format() { + // test for method setFormatByArgumentIndex(Format[]) + MessageFormat f1 = (MessageFormat) format1.clone(); + + // test with repeating formats and max argument index < max offset + // compare getFormatsByArgumentIndex() results after calls to + // setFormatsByArgumentIndex(Format[]) + Format[] correctFormats = new Format[] { DateFormat.getTimeInstance(), + new ChoiceFormat("0#off|1#on"), DateFormat.getTimeInstance(), + NumberFormat.getCurrencyInstance(), + new ChoiceFormat("1#few|2#ok|3#a lot") }; + + f1.setFormatsByArgumentIndex(correctFormats); + Format[] formats = f1.getFormatsByArgumentIndex(); + + assertEquals("Test1A:Returned wrong number of formats:", + correctFormats.length, formats.length); + for (int i = 0; i < correctFormats.length; i++) { + assertEquals("Test1B:wrong format for argument index " + i + ":", + correctFormats[i], formats[i]); + } + + // compare getFormats() results after calls to + // setFormatByArgumentIndex() + formats = f1.getFormats(); + correctFormats = new Format[] { NumberFormat.getCurrencyInstance(), + DateFormat.getTimeInstance(), DateFormat.getTimeInstance(), + new ChoiceFormat("1#few|2#ok|3#a lot"), + new ChoiceFormat("0#off|1#on"), DateFormat.getTimeInstance(), }; + + assertEquals("Test1C:Returned wrong number of formats:", + correctFormats.length, formats.length); + for (int i = 0; i < correctFormats.length; i++) { + assertEquals("Test1D:wrong format for pattern index " + i + ":", + correctFormats[i], formats[i]); + } + + // test setting argumentIndexes that are not used + MessageFormat f2 = (MessageFormat) format2.clone(); + Format[] inputFormats = new Format[] { DateFormat.getDateInstance(), + new ChoiceFormat("0#off|1#on"), + NumberFormat.getPercentInstance(), + NumberFormat.getCurrencyInstance(), + DateFormat.getTimeInstance(), null, null, null, + DateFormat.getTimeInstance() }; + f2.setFormatsByArgumentIndex(inputFormats); + + formats = f2.getFormatsByArgumentIndex(); + correctFormats = format2.getFormatsByArgumentIndex(); + + assertEquals("Test2A:Returned wrong number of formats:", + correctFormats.length, formats.length); + for (int i = 0; i < correctFormats.length; i++) { + assertEquals("Test2B:wrong format for argument index " + i + ":", + correctFormats[i], formats[i]); + } + + formats = f2.getFormats(); + correctFormats = new Format[] { NumberFormat.getCurrencyInstance(), + DateFormat.getTimeInstance(), DateFormat.getDateInstance(), + null, new ChoiceFormat("0#off|1#on"), + DateFormat.getDateInstance() }; + + assertEquals("Test2C:Returned wrong number of formats:", + correctFormats.length, formats.length); + for (int i = 0; i < correctFormats.length; i++) { + assertEquals("Test2D:wrong format for pattern index " + i + ":", + correctFormats[i], formats[i]); + } + + // test exceeding the argumentIndex number + MessageFormat f3 = (MessageFormat) format3.clone(); + f3.setFormatsByArgumentIndex(inputFormats); + + formats = f3.getFormatsByArgumentIndex(); + assertEquals("Test3A:Returned wrong number of formats:", 0, + formats.length); + + formats = f3.getFormats(); + assertEquals("Test3B:Returned wrong number of formats:", 0, + formats.length); + + } + + /** + * @tests java.text.MessageFormat#parse(java.lang.String, + * java.text.ParsePosition) + */ + public void test_parseLjava_lang_StringLjava_text_ParsePosition() { + // Test for method java.lang.Object [] + // java.text.MessageFormat.parse(java.lang.String, + // java.text.ParsePosition) + MessageFormat format = new MessageFormat("date is {0,date,MMM d, yyyy}"); + ParsePosition pos = new ParsePosition(2); + Object[] result = (Object[]) format + .parse("xxdate is Feb 28, 1999", pos); + assertTrue("No result: " + result.length, result.length >= 1); + assertTrue("Wrong answer", ((Date) result[0]) + .equals(new GregorianCalendar(1999, Calendar.FEBRUARY, 28) + .getTime())); + + MessageFormat mf = new MessageFormat("vm={0},{1},{2}"); + result = mf.parse("vm=win,foo,bar", new ParsePosition(0)); + assertTrue("Invalid parse", result[0].equals("win") + && result[1].equals("foo") && result[2].equals("bar")); + + mf = new MessageFormat("{0}; {0}; {0}"); + String parse = "a; b; c"; + result = mf.parse(parse, new ParsePosition(0)); + assertEquals("Wrong variable result", "c", result[0]); + } + + /** + * @tests java.text.MessageFormat#setLocale(java.util.Locale) + */ + public void test_setLocaleLjava_util_Locale() { + // Test for method void + // java.text.MessageFormat.setLocale(java.util.Locale) + MessageFormat format = new MessageFormat("date {0,date}"); + format.setLocale(Locale.CHINA); + assertEquals("Wrong locale1", Locale.CHINA, format.getLocale()); + format.applyPattern("{1,date}"); + assertEquals("Wrong locale3", DateFormat.getDateInstance( + DateFormat.DEFAULT, Locale.CHINA), format.getFormats()[0]); + } + + /** + * @tests java.text.MessageFormat#toPattern() + */ + public void test_toPattern() { + // Test for method java.lang.String java.text.MessageFormat.toPattern() + String pattern = "[{0}]"; + MessageFormat mf = new MessageFormat(pattern); + assertTrue("Wrong pattern", mf.toPattern().equals(pattern)); + + // Regression for HARMONY-59 + new MessageFormat("CHOICE {1,choice}").toPattern(); + } + + /** + * Sets up the fixture, for example, open a network connection. This method + * is called before a test is executed. + */ + protected void setUp() { + defaultLocale = Locale.getDefault(); + Locale.setDefault(Locale.US); + + // test with repeating formats and max argument index < max offset + String pattern = "A {3, number, currency} B {2, time} C {0, number, percent} D {4} E {1,choice,0#off|1#on} F {0, date}"; + format1 = new MessageFormat(pattern); + + // test with max argument index > max offset + pattern = "A {3, number, currency} B {8, time} C {0, number, percent} D {6} E {1,choice,0#off|1#on} F {0, date}"; + format2 = new MessageFormat(pattern); + + // test with argument number being zero + pattern = "A B C D E F"; + format3 = new MessageFormat(pattern); + } + + /** + * Tears down the fixture, for example, close a network connection. This + * method is called after a test is executed. + */ + protected void tearDown() { + Locale.setDefault(defaultLocale); + } + + /** + * @tests java.text.MessageFormat(java.util.Locale) + */ + public void test_ConstructorLjava_util_Locale() { + // Regression for HARMONY-65 + try { + new MessageFormat("{0,number,integer", Locale.US); + fail("Assert 0: Failed to detect unmatched brackets."); + } catch (IllegalArgumentException e) { + // expected + } + } + + /** + * @tests java.text.MessageFormat#parse(java.lang.String) Test of method + * java.text.MessageFormat#parse(java.lang.String). + */ + public void test_parseLjava_lang_String() throws ParseException { + String pattern = "A {3, number, currency} B {2, time} C {0, number, percent} D {4} E {1,choice,0#off|1#on} F {0, date}"; + MessageFormat mf = new MessageFormat(pattern); + String sToParse = "A $12,345.00 B 9:56:07 AM C 3,200% D 1/15/70 9:56 AM E on F Jan 1, 1970"; + Object[] result; + try { + result = mf.parse(sToParse); + + assertTrue("No result: " + result.length, result.length == 5); + assertTrue("Object 0 is not date", result[0] instanceof Date); + assertEquals("Object 1 is not stringr", result[1].toString(), "1.0"); + assertTrue("Object 2 is not date", result[2] instanceof Date); + assertEquals("Object 3 is not number", result[3].toString(), + "12345"); + assertEquals("Object 4 is not string", result[4].toString(), + "1/15/70 9:56 AM"); + + } catch (java.text.ParseException pe) { + fail("ParseException is thrown for incorrect string " + sToParse); + } + + sToParse = "xxdate is Feb 28, 1999"; + try { + result = format1.parse(sToParse); + fail("ParseException is thrown for incorrect string " + sToParse); + } catch (java.text.ParseException pe) { + // expected + } + + sToParse = "vm=Test, @3 4 6, 3 "; + mf = new MessageFormat("vm={0},{1},{2}"); + try { + result = mf.parse(sToParse); + assertTrue("No result: " + result.length, result.length == 3); + assertEquals("Object 0 is not string", result[0].toString(), "Test"); + assertEquals("Object 1 is not string", result[1].toString(), + " @3 4 6"); + assertEquals("Object 2 is not string", result[2].toString(), + " 3 "); + } catch (java.text.ParseException pe) { + fail("ParseException is thrown for correct string " + sToParse); + } + + try { + result = mf.parse(null); + fail("ParseException is not thrown for null " + sToParse); + } catch (java.text.ParseException pe) { + // expected + } + } + + /** + * @tests java.text.MessageFormat#parseObject(java.lang.String, + * java.text.ParsePosition) Test of method + * java.text.MessageFormat#parseObject(java.lang.String, + * java.text.ParsePosition). Case 1: Parsing of correct data string. + * Case 2: Parsing of partial correct data string. Case 3: Try to use + * argument ParsePosition as null. + */ + public void test_parseObjectLjava_lang_StringLjavajava_text_ParsePosition() { + MessageFormat mf = new MessageFormat("{0,number,#.##}, {0,number,#.#}"); + try { + // case 1: Try to parse correct data string. + Object[] objs = { new Double(3.1415) }; + String result = mf.format(objs); + // result now equals "3.14, 3.1" + Object[] res = null; + ParsePosition pp = new ParsePosition(0); + int parseIndex = pp.getIndex(); + res = (Object[]) mf.parseObject(result, pp); + assertTrue("Parse operation return null", res != null); + assertTrue("parse operation return array with incorrect length", + 1 == res.length); + assertTrue("ParseIndex is incorrect", pp.getIndex() != parseIndex); + assertTrue("Result object is incorrect", new Double(3.1) + .equals(res[0])); + + // case 2: Try to parse partially correct data string. + pp.setIndex(0); + char[] cur = result.toCharArray(); + cur[cur.length / 2] = 'Z'; + String partialCorrect = new String(cur); + res = (Object[]) mf.parseObject(partialCorrect, pp); + assertTrue("Parse operation return null", res == null); + assertTrue("ParseIndex is incorrect", pp.getIndex() == 0); + assertTrue("ParseErrorIndex is incorrect", + pp.getErrorIndex() == cur.length / 2); + + // case 3: Try to use argument ParsePosition as null. + try { + mf.parseObject(result, null); + fail("Expected NullPointerException was not thrown"); + } catch (NullPointerException e) { + // expected + } + } catch (Exception e) { + fail("Unexpected exception " + e.toString()); + } + } + + public void test_format_Object() { + // Regression for HARMONY-1875 + Locale.setDefault(Locale.CANADA); + TimeZone.setDefault(TimeZone.getTimeZone("UTC")); + String pat = "text here {0,date,yyyyyyyyy} and here"; + Calendar c = Calendar.getInstance(); + String etalon = "text here 00000" + c.get(Calendar.YEAR) + " and here"; + MessageFormat obj = new MessageFormat(pat); + assertEquals(etalon, obj.format(new Object[] { new Date() })); + } + +} diff --git a/text/src/test/java/org/apache/harmony/text/tests/java/text/NumberFormatFieldTest.java b/text/src/test/java/org/apache/harmony/text/tests/java/text/NumberFormatFieldTest.java new file mode 100644 index 0000000..8a95a01 --- /dev/null +++ b/text/src/test/java/org/apache/harmony/text/tests/java/text/NumberFormatFieldTest.java @@ -0,0 +1,109 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.harmony.text.tests.java.text; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.text.*; + +public class NumberFormatFieldTest extends junit.framework.TestCase { + /** + * @tests java.text.NumberFormat$Field#Field(java.lang.String) + */ + public void test_ConstructorLjava_lang_String() { + // protected constructor + String name = "new number format"; + MyNumberFormat field = new MyNumberFormat(name); + assertEquals("field has wrong name", name, field.getName()); + + field = new MyNumberFormat(null); + assertEquals("field has wrong name", null, field.getName()); + } + + /** + * @tests java.text.NumberFormat$Field#readResolve() + */ + public void test_readResolve() { + // test for method java.lang.Object readResolve() + + // see serialization stress tests: + // implemented in + // SerializationStressTest4.test_writeObject_NumberFormat_Field() + ObjectOutputStream out = null; + ObjectInputStream in = null; + try { + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + out = new ObjectOutputStream(bytes); + + NumberFormat.Field nfield, nfield2; + MyNumberFormat field; + + nfield = NumberFormat.Field.CURRENCY; + + field = new MyNumberFormat(null); + + out.writeObject(nfield); + out.writeObject(field); + + in = new ObjectInputStream(new ByteArrayInputStream(bytes + .toByteArray())); + try { + nfield2 = (NumberFormat.Field) in.readObject(); + assertSame("resolved incorrectly", nfield, nfield2); + } catch (IllegalArgumentException e) { + fail("Unexpected IllegalArgumentException: " + e); + } + + try { + in.readObject(); + fail("Expected InvalidObjectException for subclass instance with null name"); + } catch (InvalidObjectException e) { + } + + } catch (IOException e) { + fail("unexpected IOException" + e); + } catch (ClassNotFoundException e) { + fail("unexpected ClassNotFoundException" + e); + } finally { + try { + if (out != null) + out.close(); + if (in != null) + in.close(); + } catch (IOException e) { + } + } + } + + static class MyNumberFormat extends NumberFormat.Field { + static final long serialVersionUID = 1L; + + static boolean flag = false; + + protected MyNumberFormat(String attr) { + super(attr); + } + + protected String getName() { + return super.getName(); + } + } +} diff --git a/text/src/test/java/org/apache/harmony/text/tests/java/text/NumberFormatTest.java b/text/src/test/java/org/apache/harmony/text/tests/java/text/NumberFormatTest.java new file mode 100644 index 0000000..8dd5526 --- /dev/null +++ b/text/src/test/java/org/apache/harmony/text/tests/java/text/NumberFormatTest.java @@ -0,0 +1,1118 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.harmony.text.tests.java.text; + +import java.text.ChoiceFormat; +import java.text.DecimalFormat; +import java.text.FieldPosition; +import java.text.NumberFormat; +import java.text.ParseException; +import java.text.ParsePosition; +import java.util.Currency; +import java.util.Locale; +import junit.framework.TestCase; + +public class NumberFormatTest extends TestCase { + + /** + * @tests java.text.NumberFormat#format(java.lang.Object, + * java.lang.StringBuffer, java.text.FieldPosition) + */ + public void test_formatLjava_lang_ObjectLjava_lang_StringBufferLjava_text_FieldPosition() { + FieldPosition pos; + StringBuffer out; + DecimalFormat format = (DecimalFormat) NumberFormat + .getInstance(Locale.US); + + pos = new FieldPosition(0); + out = format.format(new Long(Long.MAX_VALUE), new StringBuffer(), pos); + assertEquals("Wrong result L1: " + out, "9,223,372,036,854,775,807", + out.toString()); + + pos = new FieldPosition(0); + out = format.format(new Long(Long.MIN_VALUE), new StringBuffer(), pos); + assertEquals("Wrong result L2: " + out, "-9,223,372,036,854,775,808", + out.toString()); + + pos = new FieldPosition(0); + out = format.format(new java.math.BigInteger(String + .valueOf(Long.MAX_VALUE)), new StringBuffer(), pos); + assertEquals("Wrong result BI1: " + out, "9,223,372,036,854,775,807", + out.toString()); + + pos = new FieldPosition(0); + out = format.format(new java.math.BigInteger(String + .valueOf(Long.MIN_VALUE)), new StringBuffer(), pos); + assertEquals("Wrong result BI2: " + out, "-9,223,372,036,854,775,808", + out.toString()); + + java.math.BigInteger big; + pos = new FieldPosition(0); + big = new java.math.BigInteger(String.valueOf(Long.MAX_VALUE)) + .add(new java.math.BigInteger("1")); + out = format.format(big, new StringBuffer(), pos); + assertEquals("Wrong result BI3: " + out, "9,223,372,036,854,775,808", + out.toString()); + + pos = new FieldPosition(0); + big = new java.math.BigInteger(String.valueOf(Long.MIN_VALUE)) + .add(new java.math.BigInteger("-1")); + out = format.format(big, new StringBuffer(), pos); + assertEquals("Wrong result BI4: " + out, "-9,223,372,036,854,775,809", + out.toString()); + + pos = new FieldPosition(0); + out = format.format(new java.math.BigDecimal("51.348"), + new StringBuffer(), pos); + assertEquals("Wrong result BD1: " + out, "51.348", out.toString()); + + pos = new FieldPosition(0); + out = format.format(new java.math.BigDecimal("51"), new StringBuffer(), + pos); + assertEquals("Wrong result BD2: " + out, "51", out.toString()); + + } + + /** + * @tests java.text.NumberFormat#getIntegerInstance() + */ + public void test_getIntegerInstance() throws ParseException { + // Test for method java.text.NumberFormat getIntegerInstance() + Locale origLocale = Locale.getDefault(); + Locale.setDefault(Locale.US); + + DecimalFormat format = (DecimalFormat) NumberFormat + .getIntegerInstance(); + + assertEquals( + "Test1: NumberFormat.getIntegerInstance().toPattern() returned wrong pattern", + "#,##0", format.toPattern()); + assertEquals( + "Test2: NumberFormat.getIntegerInstance().format(35.76) returned wrong value", + "36", format.format(35.76)); + assertEquals( + "Test3: NumberFormat.getIntegerInstance().parse(\"35.76\") returned wrong number", + new Long(35), format.parse("35.76")); + assertEquals( + "Test4: NumberFormat.getIntegerInstance().parseObject(\"35.76\") returned wrong number", + new Long(35), format.parseObject("35.76")); + Locale.setDefault(origLocale); + } + + /** + * @tests java.text.NumberFormat#getIntegerInstance(java.util.Locale) + */ + public void test_getIntegerInstanceLjava_util_Locale() + throws ParseException { + // Test for method java.text.NumberFormat + // getIntegerInstance(java.util.Locale) + Locale usLocale = Locale.US; + // BEGIN android-changed + // use de_CH instead + // Locale arLocale = new Locale("ar", "AE"); + Locale chLocale = new Locale("de", "CH"); + // END android-changed + + DecimalFormat format = (DecimalFormat) NumberFormat + .getIntegerInstance(usLocale); + assertEquals( + "Test1: NumberFormat.getIntegerInstance().toPattern() returned wrong pattern", + "#,##0", format.toPattern()); + assertEquals( + "Test2: NumberFormat.getIntegerInstance().format(-35.76) returned wrong value", + "-36", format.format(-35.76)); + assertEquals( + "Test3: NumberFormat.getIntegerInstance().parse(\"-36\") returned wrong number", + new Long(-36), format.parse("-36")); + assertEquals( + "Test4: NumberFormat.getIntegerInstance().parseObject(\"-36\") returned wrong number", + new Long(-36), format.parseObject("-36")); + assertEquals( + "Test5: NumberFormat.getIntegerInstance().getMaximumFractionDigits() returned wrong value", + 0, format.getMaximumFractionDigits()); + assertTrue( + "Test6: NumberFormat.getIntegerInstance().isParseIntegerOnly() returned wrong value", + format.isParseIntegerOnly()); + + // try with a locale that has a different integer pattern + // BEGIN android-changed + // use de_CH instead + // format = (DecimalFormat) NumberFormat.getIntegerInstance(arLocale); + format = (DecimalFormat) NumberFormat.getIntegerInstance(chLocale); + assertEquals( + "Test7: NumberFormat.getIntegerInstance(new Locale(\"de\", \"CH\")).toPattern() returned wrong pattern", + "#,##0", format.toPattern()); + assertEquals( + "Test8: NumberFormat.getIntegerInstance(new Locale(\"de\", \"CH\")).format(-35.76) returned wrong value", + "-36", format.format(-35.76)); + assertEquals( + "Test9: NumberFormat.getIntegerInstance(new Locale(\"de\", \"CH\")).parse(\"-36-\") returned wrong number", + new Long(-36), format.parse("-36")); + assertEquals( + "Test10: NumberFormat.getIntegerInstance(new Locale(\"de\", \"CH\")).parseObject(\"36-\") returned wrong number", + new Long(-36), format.parseObject("-36")); + + assertEquals( + "Test11: NumberFormat.getIntegerInstance(new Locale(\"de\", \"CH\")).getMaximumFractionDigits() returned wrong value", + 0, format.getMaximumFractionDigits()); + assertTrue( + "Test12: NumberFormat.getIntegerInstance(new Locale(\"de\", \"CH\")).isParseIntegerOnly() returned wrong value", + format.isParseIntegerOnly()); + // use de_CH instead + /*assertEquals( + "Test7: NumberFormat.getIntegerInstance(new Locale(\"ar\", \"AE\")).toPattern() returned wrong pattern", + "#,##0;#,##0-", format.toPattern()); + assertEquals( + "Test8: NumberFormat.getIntegerInstance(new Locale(\"ar\", \"AE\")).format(-35.76) returned wrong value", + "36-", format.format(-35.76)); + assertEquals( + "Test9: NumberFormat.getIntegerInstance(new Locale(\"ar\", \"AE\")).parse(\"-36-\") returned wrong number", + new Long(-36), format.parse("36-")); + assertEquals( + "Test10: NumberFormat.getIntegerInstance(new Locale(\"ar\", \"AE\")).parseObject(\"36-\") returned wrong number", + new Long(-36), format.parseObject("36-")); + + assertEquals( + "Test11: NumberFormat.getIntegerInstance(new Locale(\"ar\", \"AE\")).getMaximumFractionDigits() returned wrong value", + 0, format.getMaximumFractionDigits()); + assertTrue( + "Test12: NumberFormat.getIntegerInstance(new Locale(\"ar\", \"AE\")).isParseIntegerOnly() returned wrong value", + format.isParseIntegerOnly());*/ + // END android-changed + } + + /** + * @tests java.text.NumberFormat#getCurrency() + */ + public void test_getCurrency() { + // Test for method java.util.Currency getCurrency() + + // a subclass that supports currency formatting + Currency currH = Currency.getInstance("HUF"); + NumberFormat format = NumberFormat.getInstance(new Locale("hu", "HU")); + assertSame("Returned incorrect currency", currH, format.getCurrency()); + + // a subclass that doesn't support currency formatting + ChoiceFormat cformat = new ChoiceFormat( + "0#Less than one|1#one|1<Between one and two|2<Greater than two"); + try { + ((NumberFormat) cformat).getCurrency(); + fail("Expected UnsupportedOperationException"); + } catch (UnsupportedOperationException e) { + } + } + + /** + * @tests java.text.NumberFormat#setMaximumIntegerDigits() + */ + public void test_setMaximumIntegerDigits() { + NumberFormat format = NumberFormat.getInstance(); + format.setMaximumIntegerDigits(2); + assertEquals("Wrong result: case 1", "23", format.format(123)); + + format.setMaximumIntegerDigits(Integer.MIN_VALUE); + assertEquals("Wrong result: case 2", "0", format.format(123)); + } + + /** + * @tests java.text.NumberFormat#setCurrency(java.util.Currency) + */ + public void test_setCurrencyLjava_util_Currency() { + // Test for method void setCurrency(java.util.Currency) + // a subclass that supports currency formatting + Currency currA = Currency.getInstance("ARS"); + NumberFormat format = NumberFormat.getInstance(new Locale("hu", "HU")); + format.setCurrency(currA); + assertSame("Returned incorrect currency", currA, format.getCurrency()); + + // a subclass that doesn't support currency formatting + ChoiceFormat cformat = new ChoiceFormat( + "0#Less than one|1#one|1<Between one and two|2<Greater than two"); + try { + ((NumberFormat) cformat).setCurrency(currA); + fail("Expected UnsupportedOperationException"); + } catch (UnsupportedOperationException e) { + } + } + + /** + * @tests java.text.NumberFormat#parseObject(java.lang.String, + * java.text.ParsePosition) + */ + public void test_parseObjectLjava_lang_StringLjava_text_ParsePosition() { + // regression test for HARMONY-1003 + assertNull(NumberFormat.getInstance().parseObject("0", + new ParsePosition(-1))); + + // Regression for HARMONY-1685 + try { + NumberFormat.getInstance().parseObject("test", null); + fail("NullPointerException expected"); + } catch (NullPointerException e) { + // expected + } + } + + /** + * @tests java.text.NumberFormat#clone() + */ + public void test_clone() { + + int max_digits = 100; + NumberFormat nf1 = NumberFormat.getInstance(); + nf1.setMaximumIntegerDigits(max_digits); + + NumberFormat nf2 = (NumberFormat) nf1.clone(); + NumberFormat nf3 = (NumberFormat) nf1.clone(); + + assertTrue("Clonned object is not equal to object", nf2.equals(nf1)); + assertTrue("Two clonned objects are not equal", nf2.equals(nf3)); + + assertTrue("Max digits value is incorrect for clonned object", nf2 + .getMaximumIntegerDigits() == max_digits); + + nf1.setMaximumIntegerDigits(10); + assertTrue( + "Max digits value is incorrect for clonned object after changing this value for object", + nf2.getMaximumIntegerDigits() == max_digits); + } + + /** + * @tests java.text.NumberFormat#equals(Object) + */ + public void test_equals() { + + NumberFormat nf1 = NumberFormat.getInstance(); + NumberFormat nf2 = NumberFormat.getInstance(); + + assertTrue("Objects are not equal", nf1.equals(nf2)); + assertTrue("THe same Objects are not equal", nf1.equals(nf1)); + + nf2.setMaximumIntegerDigits(100); + assertFalse("Different NumberFormat are equal", nf1.equals(nf2)); + + nf2.setMaximumIntegerDigits(nf1.getMaximumIntegerDigits()); + assertTrue("THe same Objects are not equal", nf1.equals(nf2)); + + nf1 = NumberFormat.getIntegerInstance(); + nf2 = NumberFormat.getIntegerInstance(Locale.CHINA); + assertFalse("Different NumberFormat are equal", nf1.equals(nf2)); + + assertFalse("Object is equal null", nf1.equals(null)); + } + + /** + * @tests java.text.NumberFormat#format(double) + */ + public void test_formatLdouble() { + // BEGIN android-changed + NumberFormat nf1 = NumberFormat.getInstance(Locale.US); + // use de_CH instead + // NumberFormat nf2 = NumberFormat.getInstance(new Locale("ar", "AR")); + NumberFormat nf2 = NumberFormat.getInstance(new Locale("de", "CH")); + + String out = nf1.format(1234567890.0123456789); + assertEquals("Wrong result for double : " + out, "1,234,567,890.012", + out.toString()); + + out = nf1.format(-1234567890.0123456789); + assertEquals("Wrong result for double : " + out, "-1,234,567,890.012", + out.toString()); + + out = nf2.format(-1234567890.0123456789); + // use de_CH instead + // assertEquals("Wrong result for double : " + out, "1,234,567,890.012-", + // out.toString()); + assertEquals("Wrong result for double : " + out, "-1'234'567'890.012", + out.toString()); + + out = nf1.format(1.0001); + assertEquals("Wrong result for for double: " + out, "1", out.toString()); + + out = nf1.format(5.0); + assertEquals("Wrong result for for double: " + out, "5", out.toString()); + // END android-changed + } + + /** + * @tests java.text.NumberFormat#format(long) + */ + public void test_formatLlong() { + // BEGIN android-changed + NumberFormat nf1 = NumberFormat.getInstance(Locale.US); + // use de_CH instead + // NumberFormat nf2 = NumberFormat.getInstance(Locale.CANADA_FRENCH); + NumberFormat nf2 = NumberFormat.getInstance(new Locale("de", "CH")); + + String out = nf1.format(Long.MAX_VALUE); + assertEquals("Wrong result for double : " + out, + "9,223,372,036,854,775,807", out.toString()); + + out = nf1.format(Long.MIN_VALUE); + assertEquals("Wrong result for double : " + out, + "-9,223,372,036,854,775,808", out.toString()); + + out = nf2.format(-1234567890); + // use de_CH instead + // assertEquals("Wrong result for double : " + out, "-1 234 567 890", out + // .toString()); + assertEquals("Wrong result for double : " + out, "-1'234'567'890", out + .toString()); + + // the Locale data of icu uses \uc2a0 + out = nf1.format(1); + assertEquals("Wrong result for for double: " + out, "1", out.toString()); + + out = nf1.format(0); + assertEquals("Wrong result for for double: " + out, "0", out.toString()); + // END android-changed + } + + /** + * @tests java.text.NumberFormat#getAvailableLocales() + */ + public void test_getAvailableLocales() { + + Locale[] l = NumberFormat.getAvailableLocales(); + assertFalse("returned Locale array is null", l == null); + assertTrue("returned Locale length <= 0", l.length > 0); + Locale[] resl = Locale.getAvailableLocales(); + assertTrue("returned Locale arrays are different", + l.length == resl.length); + boolean isUS = false; + for (int i = 0; i < resl.length; i++) { + assertEquals("elements " + i + " are not equal: ", resl[i], l[i]); + if (l[i].equals(Locale.US)) + isUS = true; + } + assertTrue("there is no Locale.US", isUS); + } + + /** + * @tests java.text.NumberFormat#getCurrencyInstance() + */ + public void test_getCurrencyInstance() { + + NumberFormat format = NumberFormat.getCurrencyInstance(); + + assertNotSame("Instance is null", null, format); + assertTrue("Object is not instance of NumberFormat", + format instanceof NumberFormat); + + assertEquals( + "Test1: NumberFormat.getCurrencyInstance().format(35.76) returned wrong value", + "$35.76", format.format(35.76)); + assertEquals( + "Test2: NumberFormat.getCurrencyInstance().format(123456.789) returned wrong value", + "$123,456.79", format.format(123456.789)); + assertEquals( + "Test3: NumberFormat.getCurrencyInstance().format(0.1) returned wrong value", + "$0.10", format.format(0.1)); + assertEquals( + "Test4: NumberFormat.getCurrencyInstance().format(0.999) returned wrong value", + "$1.00", format.format(0.999)); + } + + /** + * @tests java.text.NumberFormat#getCurrencyInstance(java.util.Locale) + */ + public void test_getCurrencyInstanceLjava_util_Locale() { + // BEGIN android-changed + Locale usLocale = Locale.US; + // use de_AT instead + // Locale mkLocale = new Locale("mk", "MK"); + Locale atLocale = new Locale("de", "AT"); + + NumberFormat format = NumberFormat.getCurrencyInstance(usLocale); + + assertNotSame("Instance is null", null, format); + assertTrue("Object is not instance of NumberFormat", + format instanceof NumberFormat); + + assertEquals( + "Test1: NumberFormat.getCurrencyInstance(Locale.US).format(35.76) returned wrong value", + "$35.76", format.format(35.76)); + assertEquals( + "Test2: NumberFormat.getCurrencyInstance(Locale.US).format(123456.789) returned wrong value", + "$123,456.79", format.format(123456.789)); + assertEquals( + "Test3: NumberFormat.getCurrencyInstance(Locale.US).format(0.1) returned wrong value", + "$0.10", format.format(0.1)); + assertEquals( + "Test4: NumberFormat.getCurrencyInstance(Locale.US).format(0.999) returned wrong value", + "$1.00", format.format(0.999)); + + // use de_AT instead + // format = NumberFormat.getCurrencyInstance(mkLocale); + format = NumberFormat.getCurrencyInstance(atLocale); + + assertEquals( + "Test5: NumberFormat.getCurrencyInstance(new Locale(\"de\", \"AT\")).format(35.76) returned wrong value", + "\u20ac 35,76", format.format(35.76)); + assertEquals( + "Test6: NumberFormat.getCurrencyInstance(new Locale(\"de\", \"AT\")).format(123456.789) returned wrong value", + "\u20ac 123.456,79", format.format(123456.789)); + assertEquals( + "Test7: NumberFormat.getCurrencyInstance(new Locale(\"de\", \"AT\")).format(0.1) returned wrong value", + "\u20ac 0,10", format.format(0.1)); + assertEquals( + "Test8: NumberFormat.getCurrencyInstance(new Locale(\"de\", \"AT\")).format(0.999) returned wrong value", + "\u20ac 1,00", format.format(0.999)); + // use de_AT instead + /*assertEquals( + "Test5: NumberFormat.getCurrencyInstance(new Locale(\"mk\", \"MK\")).format(35.76) returned wrong value", + "Den 35,76", format.format(35.76)); + assertEquals( + "Test6: NumberFormat.getCurrencyInstance(new Locale(\"mk\", \"MK\")).format(123456.789) returned wrong value", + "Den 123.456,79", format.format(123456.789)); + assertEquals( + "Test7: NumberFormat.getCurrencyInstance(new Locale(\"mk\", \"MK\")).format(0.1) returned wrong value", + "Den 0,1", format.format(0.1)); + assertEquals( + "Test8: NumberFormat.getCurrencyInstance(new Locale(\"mk\", \"MK\")).format(0.999) returned wrong value", + "Den 1", format.format(0.999));*/ + try { + NumberFormat.getCurrencyInstance(null); + fail("java.lang.NullPointerException is not thrown"); + } catch (java.lang.NullPointerException npe) { + // expested + } + // END android-changed + } + + /** + * @tests java.text.NumberFormat#getInstance() + */ + public void test_getInstance() { + Locale.setDefault(Locale.US); + NumberFormat format = NumberFormat.getInstance(); + + assertNotSame("Instance is null", null, format); + assertTrue("Object is not instance of NumberFormat", + format instanceof NumberFormat); + + assertEquals( + "Test1: NumberFormat.getInstance().format(1234567890.0987654321) returned wrong value", + "1,234,567,890.099", format.format(1234567890.0987654321)); + assertEquals( + "Test2: ((DecimalFormat) NumberFormat.getInstance()).toPattern returned wrong value", + "#,##0.###", ((DecimalFormat) format).toPattern()); + assertEquals( + "Test3: NumberFormat.getInstance().format(123456789) returned wrong value", + "123,456,789", format.format(123456789)); + } + + /** + * @tests java.text.NumberFormat#getInstance(Locale) + */ + public void test_getInstanceLjava_util_Locale() { + // BEGIN android-changed + Locale.setDefault(Locale.US); + // use de_CH instead + // NumberFormat format = NumberFormat.getInstance(new Locale("ar", "AR")); + NumberFormat format = NumberFormat.getInstance(new Locale("de", "CH")); + + assertNotSame("Instance is null", null, format); + assertTrue("Object is not instance of NumberFormat", + format instanceof NumberFormat); + + assertEquals( + "Test1: NumberFormat.getInstance().format(1234567890.0987654321) returned wrong value", + "1'234'567'890.099", format.format(1234567890.0987654321)); + assertEquals( + "Test2: ((DecimalFormat) NumberFormat.getInstance()).toPattern returned wrong value", + "#,##0.###", ((DecimalFormat) format).toPattern()); + assertEquals( + "Test3: NumberFormat.getInstance().format(123456789) returned wrong value", + "123'456'789", format.format(123456789)); + // use de_CH instead + /*assertEquals( + "Test1: NumberFormat.getInstance().format(1234567890.0987654321) returned wrong value", + "1,234,567,890.099", format.format(1234567890.0987654321)); + assertEquals( + "Test2: ((DecimalFormat) NumberFormat.getInstance()).toPattern returned wrong value", + "#,##0.###;#,##0.###-", ((DecimalFormat) format).toPattern()); + assertEquals( + "Test3: NumberFormat.getInstance().format(123456789) returned wrong value", + "123,456,789", format.format(123456789));*/ + try { + NumberFormat.getInstance(null); + fail("java.lang.NullPointerException is not thrown"); + } catch (java.lang.NullPointerException npe) { + // expested + } + // END android-changed + } + + /** + * @tests java.text.NumberFormat#getNumberInstance() + */ + public void test_getNumberInstance() { + Locale.setDefault(Locale.US); + NumberFormat format = NumberFormat.getNumberInstance(); + + assertNotSame("Instance is null", null, format); + assertTrue("Object is not instance of NumberFormat", + format instanceof NumberFormat); + + assertEquals( + "Test1: NumberFormat.getNumberInstance().format(1234567890.0987654321) returned wrong value", + "1,234,567,890.099", format.format(1234567890.0987654321)); + assertEquals( + "Test2: ((DecimalFormat) NumberFormat.getNumberInstance()).toPattern returned wrong value", + "#,##0.###", ((DecimalFormat) format).toPattern()); + assertEquals( + "Test3: NumberFormat.getNumberInstance().format(123456789) returned wrong value", + "123,456,789", format.format(123456789)); + } + + /** + * @tests java.text.NumberFormat#getNumberInstance(Locale) + */ + public void test_getNumberInstanceLjava_util_Locale() { + // BEGIN android-changed + Locale.setDefault(Locale.US); + // use de_CH instead + NumberFormat format = NumberFormat.getNumberInstance(new Locale("de", + "CH")); + // NumberFormat format = NumberFormat.getNumberInstance(new Locale("ar", + // "AR")); + + assertNotSame("Instance is null", null, format); + assertTrue("Object is not instance of NumberFormat", + format instanceof NumberFormat); + + assertEquals( + "Test1: NumberFormat.getNumberInstance().format(-1234567890.0987654321) returned wrong value", + "-1'234'567'890.099", format.format(-1234567890.0987654321)); + assertEquals( + "Test2: ((DecimalFormat) NumberFormat.getNumberInstance()).toPattern returned wrong value", + "#,##0.###", ((DecimalFormat) format).toPattern()); + assertEquals( + "Test3: NumberFormat.getNumberInstance().format(123456789) returned wrong value", + "123'456'789", format.format(123456789)); + // use de_CH instead + /*assertEquals( + "Test1: NumberFormat.getNumberInstance().format(-1234567890.0987654321) returned wrong value", + "1,234,567,890.099-", format.format(-1234567890.0987654321)); + assertEquals( + "Test2: ((DecimalFormat) NumberFormat.getNumberInstance()).toPattern returned wrong value", + "#,##0.###;#,##0.###-", ((DecimalFormat) format).toPattern()); + assertEquals( + "Test3: NumberFormat.getNumberInstance().format(123456789) returned wrong value", + "123,456,789", format.format(123456789));*/ + try { + NumberFormat.getInstance(null); + fail("java.lang.NullPointerException is not thrown"); + } catch (java.lang.NullPointerException npe) { + // expested + } + // END android-changed + } + + /** + * @tests java.text.NumberFormat#getPercentInstance() + */ + public void test_getPercentInstance() { + Locale.setDefault(Locale.US); + NumberFormat format = NumberFormat.getPercentInstance(); + + assertNotSame("Instance is null", null, format); + assertTrue("Object is not instance of NumberFormat", + format instanceof NumberFormat); + + assertEquals( + "Test1: NumberFormat.getPercentInstance().format(1234567890.0987654321) returned wrong value", + "123,456,789,010%", format.format(1234567890.0987654321)); + assertEquals( + "Test2: ((DecimalFormat) NumberFormat.getPercentInstance()).toPattern returned wrong value", + "#,##0%", ((DecimalFormat) format).toPattern()); + assertEquals( + "Test3: NumberFormat.getPercentInstance().format(123456789) returned wrong value", + "12,345,678,900%", format.format(123456789)); + } + + /** + * @tests java.text.NumberFormat#getPercentInstance(Locale) + */ + public void test_getPercentInstanceLjava_util_Locale() { + Locale.setDefault(Locale.US); + NumberFormat format = NumberFormat.getPercentInstance(new Locale("ar", + "AR")); + + assertNotSame("Instance is null", null, format); + assertTrue("Object is not instance of NumberFormat", + format instanceof NumberFormat); + + assertEquals( + "Test1: NumberFormat.getPercentInstance().format(1234567890.0987654321) returned wrong value", + "123,456,789,010%", format.format(1234567890.0987654321)); + assertEquals( + "Test2: ((DecimalFormat) NumberFormat.getPercentInstance()).toPattern returned wrong value", + "#,##0%", ((DecimalFormat) format).toPattern()); + assertEquals( + "Test3: NumberFormat.getPercentInstance().format(123456789) returned wrong value", + "12,345,678,900%", format.format(123456789)); + try { + NumberFormat.getInstance(null); + fail("java.lang.NullPointerException is not thrown"); + } catch (java.lang.NullPointerException npe) { + // expested + } + } + + /** + * @tests java.text.NumberFormat#getMaximumFractionDigits() + */ + public void test_getMaximumFractionDigits() { + NumberFormat nf1 = NumberFormat.getInstance(); + + nf1.setMaximumFractionDigits(Integer.MAX_VALUE); + int result = nf1.getMaximumFractionDigits(); + assertTrue("getMaximumFractionDigits returns " + result + + " instead of: " + Integer.MAX_VALUE, + result == Integer.MAX_VALUE); + + nf1.setMaximumFractionDigits(0); + result = nf1.getMaximumFractionDigits(); + assertTrue("getMaximumFractionDigits returns " + result + + " instead of 0", result == 0); + + nf1.setMinimumFractionDigits(Integer.MAX_VALUE); + result = nf1.getMaximumFractionDigits(); + assertTrue("getMaximumFractionDigits returns " + result + + " instead of Integer.MAX_VALUE", result == Integer.MAX_VALUE); + } + + /** + * @tests java.text.NumberFormat#getMinimumFractionDigits() + */ + public void test_getMinimumFractionDigits() { + NumberFormat nf1 = NumberFormat.getInstance(); + nf1.setMinimumFractionDigits(Integer.MAX_VALUE); + int result = nf1.getMinimumFractionDigits(); + assertTrue("getMinimumFractionDigits returns " + result + + " instead of: " + Integer.MAX_VALUE, + result == Integer.MAX_VALUE); + + nf1.setMaximumFractionDigits(0); + result = nf1.getMinimumFractionDigits(); + assertTrue("getMinimumFractionDigits returns " + result + + " instead of 0", result == 0); + + nf1.setMinimumFractionDigits(52); + result = nf1.getMinimumFractionDigits(); + assertTrue("getMinimumFractionDigits returns " + result + + " instead of 52", result == 52); + } + + /** + * @tests java.text.NumberFormat#getMaximumIntegerDigits() + */ + public void test_getMaximumIntegerDigits() { + NumberFormat nf1 = NumberFormat.getInstance(); + nf1.setMaximumIntegerDigits(Integer.MAX_VALUE); + int result = nf1.getMaximumIntegerDigits(); + assertTrue("getMaximumIntegerDigits returns " + result + + " instead of: " + Integer.MAX_VALUE, + result == Integer.MAX_VALUE); + + nf1.setMaximumIntegerDigits(0); + result = nf1.getMaximumIntegerDigits(); + assertTrue("getMaximumIntegerDigits returns " + result + + " instead of 0", result == 0); + + nf1.setMinimumIntegerDigits(Integer.MAX_VALUE); + result = nf1.getMaximumIntegerDigits(); + assertTrue("getMaximumIntegerigits returns " + result + + " instead of Integer.MAX_VALUE", result == Integer.MAX_VALUE); + } + + /** + * @tests java.text.NumberFormat#getMinimumIntegerDigits() + */ + public void test_getMinimumIntegernDigits() { + NumberFormat nf1 = NumberFormat.getInstance(); + nf1.setMinimumIntegerDigits(Integer.MAX_VALUE); + int result = nf1.getMinimumIntegerDigits(); + assertTrue("getMinimumIntegerDigits returns " + result + + " instead of: " + Integer.MAX_VALUE, + result == Integer.MAX_VALUE); + + nf1.setMaximumIntegerDigits(0); + result = nf1.getMinimumIntegerDigits(); + assertTrue("getMinimumIntegerDigits returns " + result + + " instead of 0", result == 0); + + nf1.setMinimumIntegerDigits(0x12034); + result = nf1.getMinimumIntegerDigits(); + assertTrue("getMinimumIntegerDigits returns " + result + + " instead of 5148", result == 73780); + } + + /** + * @tests java.text.NumberFormat#hashCode() + */ + public void test_hashCode() { + + NumberFormat nf1 = NumberFormat.getInstance(); + NumberFormat nf11 = NumberFormat.getInstance(); + NumberFormat nf2 = NumberFormat.getInstance(Locale.US); + NumberFormat nf3 = NumberFormat.getPercentInstance(); + NumberFormat nf4 = NumberFormat.getCurrencyInstance(); + NumberFormat nf5 = NumberFormat + .getNumberInstance(new Locale("mk", "MK")); + NumberFormat nf6 = NumberFormat.getInstance(Locale.US); + + assertTrue("Hash codes are not equal: case 1", nf1.hashCode() == nf2 + .hashCode()); + assertTrue("Hash codes are not equal: case 2", nf1.hashCode() == nf11 + .hashCode()); + assertTrue("Hash codes are not equal: case 3", nf1.hashCode() == nf3 + .hashCode()); + assertFalse("Hash codes are equal: case 4", nf3.hashCode() == nf4 + .hashCode()); + assertFalse("Hash codes are equal: case 5", nf4.hashCode() == nf5 + .hashCode()); + assertTrue("Hash codes are not equal: case 6", nf5.hashCode() == nf6 + .hashCode()); + + nf1.setMaximumFractionDigits(0); + assertTrue("Hash codes are not equal: case 7", nf1.hashCode() == nf11 + .hashCode()); + } + + /** + * @tests java.text.NumberFormat#isGroupingUsed() + */ + public void test_isGroupingUsed() { + NumberFormat nf1 = NumberFormat.getInstance(); + assertTrue("grouping is not used for NumberFormat.getInstance", nf1 + .isGroupingUsed()); + + nf1.setGroupingUsed(false); + assertFalse( + "grouping is used for NumberFormat.getInstance after setting false", + nf1.isGroupingUsed()); + + nf1.setGroupingUsed(true); + assertTrue( + "grouping is not used for NumberFormat.getInstance after setting true", + nf1.isGroupingUsed()); + } + + /** + * @tests java.text.NumberFormat#setGroupingUsed(boolean) + */ + public void test_setGroupingUsed() { + NumberFormat nf1 = NumberFormat.getInstance(Locale.US); + nf1.setGroupingUsed(false); + + assertEquals("grouping is not used for 1234567890.1", "1234567890.1", + nf1.format(1234567890.1)); + + assertEquals("grouping is not used for -1234567890.1", "-1234567890.1", + nf1.format(-1234567890.1)); + + nf1.setGroupingUsed(false); + + assertEquals("grouping is not used for 1234567890.1", "1234567890.1", + nf1.format(1234567890.1)); + + assertEquals("grouping is not used for -1234567890.1", "-1234567890.1", + nf1.format(-1234567890.1)); + + nf1.setGroupingUsed(true); + + assertEquals("grouping is not used for 1234567890.1", + "1,234,567,890.1", nf1.format(1234567890.1)); + + assertEquals("grouping is not used for -1234567890.1", + "-1,234,567,890.1", nf1.format(-1234567890.1)); + + NumberFormat nf2 = NumberFormat.getPercentInstance(new Locale("ar", + "AR")); + nf2.setGroupingUsed(false); + assertEquals( + "Locale(\"ar\", \"AR\"): grouping is not used for 1234567890.1", + "123456789010%", nf2.format(1234567890.1)); + + assertEquals( + "Locale(\"ar\", \"AR\"): grouping is not used for -1234567890.1", + "-123456789010%", nf2.format(-1234567890.1)); + assertEquals("grouping is not used for 1234567890.1", + "1,234,567,890.1", nf1.format(1234567890.1)); + + nf2.setGroupingUsed(true); + assertEquals( + "Locale(\"ar\", \"AR\"): grouping is not used for 1234567890.1", + "123,456,789,010%", nf2.format(1234567890.1)); + + assertEquals( + "Locale(\"ar\", \"AR\"): grouping is not used for -1234567890.1", + "-123,456,789,010%", nf2.format(-1234567890.1)); + + nf2.setGroupingUsed(true); + assertEquals( + "Locale(\"ar\", \"AR\"): grouping is not used for 1234567890.1", + "123,456,789,010%", nf2.format(1234567890.1)); + + assertEquals( + "Locale(\"ar\", \"AR\"): grouping is not used for -1234567890.1", + "-123,456,789,010%", nf2.format(-1234567890.1)); + } + + /** + * @tests java.text.NumberFormat#isParseIntegerOnly() + */ + public void test_isParseIntegerOnly() { + NumberFormat nf1 = NumberFormat.getInstance(); + assertTrue("ParseIntegerOnly is not used for NumberFormat.getInstance", + nf1.isGroupingUsed()); + + nf1.setParseIntegerOnly(false); + assertFalse( + "ParseIntegerOnly is used for NumberFormat.getInstance after setting false", + nf1.isParseIntegerOnly()); + + nf1.setParseIntegerOnly(true); + assertTrue( + "ParseIntegerOnly is not used for NumberFormat.getInstance after setting true", + nf1.isParseIntegerOnly()); + } + + /** + * @tests java.text.NumberFormat#setParseIntegerOnly(boolean) + */ + public void test_setParseIntegerOnly() { + NumberFormat nf1 = NumberFormat.getInstance(Locale.US); + nf1.setParseIntegerOnly(true); + + assertEquals("ParseIntegerOnly is not used for 1234567890.1", + "1,234,567,890.1", nf1.format(1234567890.1)); + assertEquals("ParseIntegerOnly is not used for -1234567890.1", + "-1,234,567,890.1", nf1.format(-1234567890.1)); + assertEquals("ParseIntegerOnly is not used for -1234567890.", + "-1,234,567,890", nf1.format(-1234567890.)); + + nf1.setParseIntegerOnly(false); + + assertEquals("ParseIntegerOnly is not used for 1234567890.1", + "1,234,567,890.1", nf1.format(1234567890.1)); + assertEquals("ParseIntegerOnly is not used for -1234567890.1", + "-1,234,567,890.1", nf1.format(-1234567890.1)); + assertEquals("ParseIntegerOnly is not used for -1234567890.", + "-1,234,567,890", nf1.format(-1234567890.)); + } + + /** + * @tests java.text.NumberFormat#setMaximumFractionDigits(int) + */ + public void test_setMaximumFractionDigits() { + NumberFormat nf1 = NumberFormat.getInstance(Locale.US); + nf1.setMaximumFractionDigits(Integer.MAX_VALUE); + int result = nf1.getMaximumFractionDigits(); + assertTrue("setMaximumFractionDigits set " + result + + " instead of Integer.MAX_VALUE", result == Integer.MAX_VALUE); + nf1.setMaximumFractionDigits(0); + result = nf1.getMaximumFractionDigits(); + assertTrue("setMaximumFractionDigits set " + result + " instead of 0", + result == 0); + assertEquals("format of 1234567890.0987654321 returns incorrect value", + "1,234,567,890", nf1.format(1234567890.0987654321)); + nf1.setMaximumFractionDigits(5); + result = nf1.getMaximumFractionDigits(); + assertTrue("setMaximumFractionDigits set " + result + " instead of 5", + result == 5); + assertEquals( + "format of 1234567890.0987654321 returns incorrect value with MaximumFractionDigits = 5", + "1,234,567,890.09877", nf1.format(1234567890.0987654321)); + assertEquals( + "format of -1234567890 returns incorrect value with MaximumFractionDigits = 5", + "-1,234,567,890", nf1.format(-1234567890)); + nf1.setMaximumFractionDigits(Integer.MIN_VALUE); + result = nf1.getMaximumFractionDigits(); + assertTrue("setMaximumFractionDigits set " + result + + " instead of Integer.MIN_VALUE", result == 0); + assertEquals( + "format of 1234567890.0987654321 returns incorrect value with MaximumFractionDigits = 5", + "1,234,567,890", nf1.format(1234567890.0987654321)); + } + + /** + * @tests java.text.NumberFormat#setMinimumFractionDigits(int) + */ + public void test_setMinimumFractionDigits() { + + NumberFormat nf1 = NumberFormat.getInstance(Locale.US); + nf1.setMinimumFractionDigits(Integer.MAX_VALUE); + int result = nf1.getMinimumFractionDigits(); + assertTrue("setMinimumFractionDigits set " + result + + " instead of Integer.MAX_VALUE", result == Integer.MAX_VALUE); + nf1.setMinimumFractionDigits(0); + result = nf1.getMinimumFractionDigits(); + assertTrue("setMinimumFractionDigits set " + result + " instead of 0", + result == 0); + nf1.setMinimumFractionDigits(5); + result = nf1.getMinimumFractionDigits(); + assertTrue("setMinimumFractionDigits set " + result + " instead of 5", + result == 5); + assertEquals( + "format of 1234567890.0987654321 returns incorrect value with MinimumFractionDigits = 5", + "1,234,567,890.09000", nf1.format(1234567890.09)); + assertEquals( + "format of -1234567890 returns incorrect value with MinimumFractionDigits = 5", + "-1,234,567,890.00000", nf1.format(-1234567890)); + nf1.setMinimumFractionDigits(Integer.MIN_VALUE); + result = nf1.getMinimumFractionDigits(); + assertTrue("setMinimumFractionDigits set " + result + + " instead of Integer.MIN_VALUE", result == 0); + assertEquals( + "format of 1234567890.098 returns incorrect value with MinimumFractionDigits = 5", + "1,234,567,890.098", nf1.format(1234567890.098)); + } + + /** + * @tests java.text.NumberFormat#setMinimumIntegerDigits(int) + */ + public void test_setMinimumIntegerDigits() { + + NumberFormat nf1 = NumberFormat.getInstance(Locale.US); + nf1.setMinimumIntegerDigits(Integer.MAX_VALUE); + int result = nf1.getMinimumIntegerDigits(); + assertTrue("setMinimumIntegerDigits set " + result + + " instead of Integer.MAX_VALUE", result == Integer.MAX_VALUE); + nf1.setMinimumIntegerDigits(0); + result = nf1.getMinimumIntegerDigits(); + assertTrue("setMinimumIntegerDigits set " + result + " instead of 0", + result == 0); + nf1.setMinimumIntegerDigits(5); + result = nf1.getMinimumIntegerDigits(); + assertTrue("setMinimumIntegerDigits set " + result + " instead of 5", + result == 5); + assertEquals( + "format of 123.09 returns incorrect value with MinimumIntegerDigits = 5", + "00,123.09", nf1.format(123.09)); + assertEquals( + "format of -123 returns incorrect value with MinimumIntegerDigits = 5", + "-00,123", nf1.format(-123)); + nf1.setMinimumIntegerDigits(Integer.MIN_VALUE); + result = nf1.getMinimumIntegerDigits(); + assertTrue("setMinimumIntegerDigits set " + result + + " instead of Integer.MIN_VALUE", result == 0); + } + + /** + * @tests java.text.NumberFormat#parse(String) + */ + public void test_parseLjava_lang_String() { + NumberFormat nf1 = NumberFormat.getInstance(); + try { + assertEquals( + "Test1: NumberFormat.getInstance().parse(\"1234567890.1\") returned wrong number", + new Double(1234567890.1), nf1.parse("1234567890.1")); + } catch (java.text.ParseException pe) { + fail("java.text.ParseException is thrown for 1234567890.1"); + } + + try { + assertEquals( + "Test2: NumberFormat.getInstance().parse(\"-1234567890.1\") returned wrong number", + new Double(-1234567890.1), nf1.parse("-1,234,567,890.1")); + } catch (java.text.ParseException pe) { + fail("java.text.ParseException is thrown for -1,234,567,890.1"); + } + + try { + nf1.parse("@1,234,567,8901"); + fail("java.text.ParseException is not thrown for 1,234,567,890z1"); + } catch (java.text.ParseException pe) { + // expected + } + + nf1 = NumberFormat.getPercentInstance(); + try { + assertEquals( + "Test3: NumberFormat.getPercentInstance().parse(\"-123%\") returned wrong number", + new Double(-1.23), nf1.parse("-123%")); + } catch (java.text.ParseException pe) { + fail("java.text.ParseException is thrown for -123%"); + } + + nf1 = NumberFormat.getCurrencyInstance(); + try { + assertEquals( + "Test4: NumberFormat.getCurrencyInstance().parse(\"$123\") returned wrong number", + new Long(123), nf1.parse("$123")); + } catch (java.text.ParseException pe) { + fail("java.text.ParseException is thrown for $123"); + } + + try { + assertEquals( + "Test4: NumberFormat.getCurrencyInstance().parse(\"$123abc\") returned wrong number", + new Long(123), nf1.parse("$123abc")); + } catch (java.text.ParseException pe) { + fail("java.text.ParseException is thrown for $123"); + } + + nf1 = NumberFormat.getIntegerInstance(); + try { + assertEquals( + "Test5: NumberFormat.getIntegerInstance().parse(\"-123.123\") returned wrong number", + nf1.parseObject("-123.123"), nf1.parse("-123.123")); + } catch (java.text.ParseException pe) { + fail("java.text.ParseException is thrown for $123"); + } + } + + /** + * @tests java.text.NumberFormat#NumberFormat() + */ + public void test_constructor() { + MyNumberFormat mf = new MyNumberFormat(); + assertFalse("Greated NumberFormat object is null", mf == null); + assertTrue( + "Greated NumberFormat object is not instance of NumberFormat", + mf instanceof NumberFormat); + } + + class MyNumberFormat extends NumberFormat { + static final long serialVersionUID = 1L; + + public MyNumberFormat() { + super(); + } + + public StringBuffer format(double number, StringBuffer toAppendTo, + FieldPosition pos) { + + return new StringBuffer(); + } + + public Number parse(String source, ParsePosition parsePosition) { + + return new Double(0); + } + + public StringBuffer format(long number, StringBuffer toAppendTo, + FieldPosition pos) { + return new StringBuffer(); + } + + } +} diff --git a/text/src/test/java/org/apache/harmony/text/tests/java/text/ParseExceptionTest.java b/text/src/test/java/org/apache/harmony/text/tests/java/text/ParseExceptionTest.java new file mode 100644 index 0000000..5e2fa88 --- /dev/null +++ b/text/src/test/java/org/apache/harmony/text/tests/java/text/ParseExceptionTest.java @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.harmony.text.tests.java.text; + +import java.text.DateFormat; +import java.text.ParseException; + +public class ParseExceptionTest extends junit.framework.TestCase { + + /** + * @tests java.text.ParseException#ParseException(java.lang.String, int) + */ + public void test_ConstructorLjava_lang_StringI() { + // Test for method java.text.ParseException(java.lang.String, int) + // SM + try { + DateFormat df = DateFormat.getInstance(); + df.parse("HelloWorld"); + } catch (ParseException e) { + return; + } + fail("ParseException not created/thrown."); + } + + /** + * @tests java.text.ParseException#getErrorOffset() + */ + public void test_getErrorOffset() { + // Test for method int java.text.ParseException.getErrorOffset() + // SM + try { + DateFormat df = DateFormat.getInstance(); + df.parse("1999HelloWorld"); + } catch (ParseException e) { + assertEquals("getErrorOffsetFailed.", 4, e.getErrorOffset()); + } + } + + /** + * Sets up the fixture, for example, open a network connection. This method + * is called before a test is executed. + */ + protected void setUp() { + } + + /** + * Tears down the fixture, for example, close a network connection. This + * method is called after a test is executed. + */ + protected void tearDown() { + } +} diff --git a/text/src/test/java/org/apache/harmony/text/tests/java/text/ParsePositionTest.java b/text/src/test/java/org/apache/harmony/text/tests/java/text/ParsePositionTest.java new file mode 100644 index 0000000..b8b2983 --- /dev/null +++ b/text/src/test/java/org/apache/harmony/text/tests/java/text/ParsePositionTest.java @@ -0,0 +1,124 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.harmony.text.tests.java.text; + +import java.text.ParsePosition; + +public class ParsePositionTest extends junit.framework.TestCase { + + ParsePosition pp; + + /** + * @tests java.text.ParsePosition#ParsePosition(int) + */ + public void test_ConstructorI() { + // Test for method java.text.ParsePosition(int) + try { + ParsePosition pp1 = new ParsePosition(Integer.MIN_VALUE); + assertTrue("Initialization failed.", + pp1.getIndex() == Integer.MIN_VALUE); + assertEquals("Initialization failed.", -1, pp1.getErrorIndex()); + } catch (Exception e) { + fail("Constructor failed."); + } + + } + + /** + * @tests java.text.ParsePosition#equals(java.lang.Object) + */ + public void test_equalsLjava_lang_Object() { + // Test for method boolean + // java.text.ParsePosition.equals(java.lang.Object) + ParsePosition pp2 = new ParsePosition(43); + pp2.setErrorIndex(56); + assertTrue("equals failed.", !pp.equals(pp2)); + pp.setErrorIndex(56); + pp.setIndex(43); + assertTrue("equals failed.", pp.equals(pp2)); + } + + /** + * @tests java.text.ParsePosition#getErrorIndex() + */ + public void test_getErrorIndex() { + // Test for method int java.text.ParsePosition.getErrorIndex() + pp.setErrorIndex(56); + assertEquals("getErrorIndex failed.", 56, pp.getErrorIndex()); + } + + /** + * @tests java.text.ParsePosition#getIndex() + */ + public void test_getIndex() { + // Test for method int java.text.ParsePosition.getIndex() + assertTrue("getIndex failed.", pp.getIndex() == Integer.MAX_VALUE); + } + + /** + * @tests java.text.ParsePosition#hashCode() + */ + public void test_hashCode() { + // Test for method int java.text.ParsePosition.hashCode() + assertTrue("Wrong hashCode returned", (pp.hashCode() == pp.getIndex() + + pp.getErrorIndex())); + } + + /** + * @tests java.text.ParsePosition#setErrorIndex(int) + */ + public void test_setErrorIndexI() { + // Test for method void java.text.ParsePosition.setErrorIndex(int) + pp.setErrorIndex(4564); + assertEquals("setErrorIndex failed.", 4564, pp.getErrorIndex()); + } + + /** + * @tests java.text.ParsePosition#setIndex(int) + */ + public void test_setIndexI() { + // Test for method void java.text.ParsePosition.setIndex(int) + pp.setIndex(4564); + assertEquals("setErrorIndex failed.", 4564, pp.getIndex()); + } + + /** + * @tests java.text.ParsePosition#toString() + */ + public void test_toString() { + // Test for method java.lang.String java.text.ParsePosition.toString() + assertEquals("toString failed.", + "java.text.ParsePosition[index=2147483647, errorIndex=-1]", pp + .toString()); + } + + /** + * Sets up the fixture, for example, open a network connection. This method + * is called before a test is executed. + */ + protected void setUp() { + + pp = new ParsePosition(Integer.MAX_VALUE); + } + + /** + * Tears down the fixture, for example, close a network connection. This + * method is called after a test is executed. + */ + protected void tearDown() { + } +} diff --git a/text/src/test/java/org/apache/harmony/text/tests/java/text/RuleBasedCollatorTest.java b/text/src/test/java/org/apache/harmony/text/tests/java/text/RuleBasedCollatorTest.java new file mode 100644 index 0000000..1960892 --- /dev/null +++ b/text/src/test/java/org/apache/harmony/text/tests/java/text/RuleBasedCollatorTest.java @@ -0,0 +1,350 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package org.apache.harmony.text.tests.java.text; + +import java.text.CharacterIterator; +import java.text.CollationElementIterator; +import java.text.CollationKey; +import java.text.Collator; +import java.text.ParseException; +import java.text.RuleBasedCollator; +import java.text.StringCharacterIterator; +import java.util.Locale; + +import junit.framework.TestCase; + +public class RuleBasedCollatorTest extends TestCase { + + /** + * @tests java.text.RuleBasedCollator#RuleBasedCollator(String) + */ + public void test_constrLRuleBasedCollatorLjava_lang_String() { + RuleBasedCollator rbc; + try { + rbc = new RuleBasedCollator("<a< b< c< d"); + assertNotSame("RuleBasedCollator object is null", null, rbc); + } catch (java.text.ParseException pe) { + fail("java.text.ParseException is thrown for correct string"); + } + + try { + rbc = new RuleBasedCollator("<a< '&'b< \u0301< d"); + assertNotSame("RuleBasedCollator object is null", null, rbc); + } catch (java.text.ParseException pe) { + fail("java.text.ParseException is thrown for correct string"); + } + + try { + new RuleBasedCollator(null); + fail("No Exception is thrown for correct string"); + } catch (java.text.ParseException pe) { + fail("java.lang.NullPointerException is not thrown for correct string"); + } catch (java.lang.NullPointerException npe) { + + } + + try { + new RuleBasedCollator(""); + fail("java.text.ParseException is not thrown for empty string"); + } catch (java.text.ParseException pe) { + } + + try { + new RuleBasedCollator("1234567%$#845"); + fail("java.text.ParseException is not thrown for wrong rules"); + } catch (java.text.ParseException pe) { + } + } + + public void test_getCollationKeyLjava_lang_String() { + // Regression test for HARMONY-28 + String source = null; + RuleBasedCollator rbc = null; + try { + String Simple = "< a< b< c< d"; + rbc = new RuleBasedCollator(Simple); + } catch (ParseException e) { + fail("Assert 0: Unexpected format exception " + e); + } + CollationKey ck = rbc.getCollationKey(source); + assertNull("Assert 1: getCollationKey (null) does not return null", ck); + } + + public void testHashCode() throws ParseException { + { + String rule = "< a < b < c < d"; + RuleBasedCollator coll = new RuleBasedCollator(rule); + assertEquals(rule.hashCode(), coll.hashCode()); + } + + { + String rule = "< a < b < c < d < e"; + RuleBasedCollator coll = new RuleBasedCollator(rule); + assertEquals(rule.hashCode(), coll.hashCode()); + } + + } + + public void testClone() throws ParseException { + RuleBasedCollator coll = (RuleBasedCollator) Collator + .getInstance(Locale.US); + RuleBasedCollator clone = (RuleBasedCollator) coll.clone(); + assertNotSame(coll, clone); + assertEquals(coll.getRules(), clone.getRules()); + assertEquals(coll.getDecomposition(), clone.getDecomposition()); + assertEquals(coll.getStrength(), clone.getStrength()); + } + + /* + * Class under test for boolean equals(java.lang.Object) + */ + public void testEqualsObject() throws ParseException { + String rule = "< a < b < c < d < e"; + RuleBasedCollator coll = new RuleBasedCollator(rule); + + assertEquals(Collator.TERTIARY, coll.getStrength()); + assertEquals(Collator.NO_DECOMPOSITION, coll.getDecomposition()); + RuleBasedCollator other = new RuleBasedCollator(rule); + assertTrue(coll.equals(other)); + + coll.setStrength(Collator.PRIMARY); + assertFalse(coll.equals(other)); + + coll.setStrength(Collator.TERTIARY); + coll.setDecomposition(Collator.CANONICAL_DECOMPOSITION); + assertFalse(coll.equals(other)); + } + + /* + * Class under test for int compare(java.lang.String, java.lang.String) + */ + public void testCompareStringString() throws ParseException { + String rule = "< c < b < a"; + RuleBasedCollator coll = new RuleBasedCollator(rule); + assertEquals(-1, coll.compare("c", "a")); + } + + public void testGetCollationKey() { + RuleBasedCollator coll = (RuleBasedCollator) Collator + .getInstance(Locale.GERMAN); + String source = "abc"; + CollationKey key1 = coll.getCollationKey(source); + assertEquals(source, key1.getSourceString()); + String source2 = "abb"; + CollationKey key2 = coll.getCollationKey(source2); + assertEquals(source2, key2.getSourceString()); + assertTrue(key1.compareTo(key2) > 0); + assertTrue(coll.compare(source, source2) > 0); + + } + + public void testGetRules() throws ParseException { + String rule = "< a = b < c"; + RuleBasedCollator coll = new RuleBasedCollator(rule); + assertEquals(rule, coll.getRules()); + } + + /* + * Class under test for java.text.CollationElementIterator + * getCollationElementIterator(java.lang.String) + */ + public void testGetCollationElementIteratorString() throws Exception { + { + Locale locale = new Locale("es", "", "TRADITIONAL"); + RuleBasedCollator coll = (RuleBasedCollator) Collator + .getInstance(locale); + String source = "cha"; + CollationElementIterator iterator = coll + .getCollationElementIterator(source); + int[] e_offset = { 0, 2, 3 }; + int offset = iterator.getOffset(); + int i = 0; + assertEquals(e_offset[i++], offset); + while (offset != source.length()) { + iterator.next(); + offset = iterator.getOffset(); + assertEquals(e_offset[i++], offset); + } + } + + { + Locale locale = new Locale("de", "DE"); + RuleBasedCollator coll = (RuleBasedCollator) Collator + .getInstance(locale); + String source = "\u00E6b"; + CollationElementIterator iterator = coll + .getCollationElementIterator(source); + int[] e_offset = { 0, 1, 1, 2 }; + int offset = iterator.getOffset(); + int i = 0; + assertEquals(e_offset[i++], offset); + while (offset != source.length()) { + iterator.next(); + offset = iterator.getOffset(); + assertEquals(e_offset[i++], offset); + } + } + // Regression for HARMONY-1352 + try { + new RuleBasedCollator("< a< b< c< d").getCollationElementIterator((String)null); + fail("NullPointerException expected"); + } catch (NullPointerException e) { + // expected + } + } + + /* + * Class under test for java.text.CollationElementIterator + * getCollationElementIterator(java.text.CharacterIterator) + */ + public void testGetCollationElementIteratorCharacterIterator() throws Exception { + { + Locale locale = new Locale("es", "", "TRADITIONAL"); + RuleBasedCollator coll = (RuleBasedCollator) Collator + .getInstance(locale); + String text = "cha"; + StringCharacterIterator source = new StringCharacterIterator(text); + CollationElementIterator iterator = coll + .getCollationElementIterator(source); + int[] e_offset = { 0, 2, 3 }; + int offset = iterator.getOffset(); + int i = 0; + assertEquals(e_offset[i++], offset); + while (offset != text.length()) { + iterator.next(); + offset = iterator.getOffset(); + // System.out.println(offset); + assertEquals(e_offset[i++], offset); + } + } + + { + Locale locale = new Locale("de", "DE"); + RuleBasedCollator coll = (RuleBasedCollator) Collator + .getInstance(locale); + String text = "\u00E6b"; + StringCharacterIterator source = new StringCharacterIterator(text); + CollationElementIterator iterator = coll + .getCollationElementIterator(source); + int[] e_offset = { 0, 1, 1, 2 }; + int offset = iterator.getOffset(); + int i = 0; + assertEquals(e_offset[i++], offset); + while (offset != text.length()) { + iterator.next(); + offset = iterator.getOffset(); + assertEquals(e_offset[i++], offset); + } + } + // Regression for HARMONY-1352 + try { + new RuleBasedCollator("< a< b< c< d").getCollationElementIterator((CharacterIterator)null); + fail("NullPointerException expected"); + } catch (NullPointerException e) { + // expected + } + } + + public void testStrength() { + RuleBasedCollator coll = (RuleBasedCollator) Collator + .getInstance(Locale.US); + for (int i = 0; i < 4; i++) { + coll.setStrength(i); + assertEquals(i, coll.getStrength()); + } + + } + + public void testDecomposition() { + RuleBasedCollator coll = (RuleBasedCollator) Collator + .getInstance(Locale.US); + for (int i = 0; i < 2; i++) { + coll.setDecomposition(i); + assertEquals(i, coll.getDecomposition()); + } + } + + public void testCollator_GetInstance() { + Collator coll = Collator.getInstance(); + Object obj1 = "a"; + Object obj2 = "b"; + assertEquals(-1, coll.compare(obj1, obj2)); + + Collator.getInstance(); + assertFalse(coll.equals("A", "\uFF21")); + } + + public void testGetAvaiableLocales() { + // Locale[] locales = Collator.getAvailableLocales(); + // for (int i = 0; i < locales.length; i++) { + // Locale locale = locales[i]; + // } + } + + // Test CollationKey + public void testCollationKey() { + Collator coll = Collator.getInstance(Locale.US); + String text = "abc"; + CollationKey key = coll.getCollationKey(text); + key.hashCode(); + + CollationKey key2 = coll.getCollationKey("abc"); + + assertEquals(0, key.compareTo(key2)); + } + + /** + * @tests java.text.RuleBasedCollator.RuleBasedCollator(java.lang.String) + */ + public void testNullPointerException() throws Exception { + // Regression for HARMONY-241 + try { + new RuleBasedCollator(null); + fail("Constructor RuleBasedCollator(null) " + + "should throw NullPointerException"); + } catch (NullPointerException e) {} + } + + /** + * @tests java.text.RuleBasedCollator.compare(java.lang.String, java.lang.String) + */ + public void testCompareNull() throws Exception { + // Regression for HARMONY-836 + try { + new RuleBasedCollator("< a").compare(null, null); + fail("RuleBasedCollator.compare(null, null) " + + "should throw NullPointerException"); + } catch (NullPointerException e) {} + } + + /** + * @tests java.text.RuleBasedCollator.RuleBasedCollator(java.lang.String) + */ + public void testEmptyStringException() { + // Regression for HARMONY-241 + try { + new RuleBasedCollator(""); + fail("Constructor RuleBasedCollator(\"\") " + + "should throw ParseException"); + } catch (ParseException e) { + assertEquals("java.text.ParseException", e.getClass().getName()); + assertEquals(0, e.getErrorOffset()); + } + } + +} diff --git a/text/src/test/java/org/apache/harmony/text/tests/java/text/SimpleDateFormatTest.java b/text/src/test/java/org/apache/harmony/text/tests/java/text/SimpleDateFormatTest.java new file mode 100644 index 0000000..c5e94e1 --- /dev/null +++ b/text/src/test/java/org/apache/harmony/text/tests/java/text/SimpleDateFormatTest.java @@ -0,0 +1,964 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.harmony.text.tests.java.text; + +import java.text.DateFormat; +import java.text.DateFormatSymbols; +import java.text.FieldPosition; +import java.text.ParseException; +import java.text.ParsePosition; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.Locale; +import java.util.SimpleTimeZone; +import java.util.TimeZone; + +import tests.support.Support_SimpleDateFormat; + +public class SimpleDateFormatTest extends junit.framework.TestCase { + + static SimpleDateFormat format = new SimpleDateFormat("", Locale.ENGLISH); + + static SimpleDateFormat pFormat = new SimpleDateFormat("", Locale.ENGLISH); + + static class TestFormat extends junit.framework.TestCase { + boolean testsFailed = false; + + public TestFormat(String name) { + super(name); + } + + public void test(String pattern, Calendar cal, String expected, + int field) { + StringBuffer buffer = new StringBuffer(); + FieldPosition position = new FieldPosition(field); + format.applyPattern(pattern); + format.format(cal.getTime(), buffer, position); + String result = buffer.toString(); + if (!System.getProperty("java.vendor", "None").substring(0, 3) + .equals("Sun")) { + assertTrue("Wrong format: \"" + pattern + "\" expected: " + + expected + " result: " + result, result + .equals(expected)); + assertTrue("Wrong begin position: " + pattern + " expected: " + + expected + " field: " + field, position + .getBeginIndex() == 1); + assertTrue("Wrong end position: " + pattern + " expected: " + + expected + " field: " + field, + position.getEndIndex() == result.length()); + } else { + // Print the failure but don't use assert as this + // will stop subsequent tests from running + if (!result.equals(expected)) { + System.out + .println("Wrong format: \"" + pattern + + "\" expected: " + expected + " result: " + + result); + testsFailed = true; + } + } + } + + public boolean testsFailed() { + return testsFailed; + } + + public void parse(String pattern, String input, Date expected, + int start, int end) { + pFormat.applyPattern(pattern); + ParsePosition position = new ParsePosition(start); + Date result = pFormat.parse(input, position); + assertTrue("Wrong result: " + pattern + " input: " + input + + " expected: " + expected + " result: " + result, expected + .equals(result)); + assertTrue("Wrong end position: " + pattern + " input: " + input, + position.getIndex() == end); + } + + public void verifyFormatTimezone(String timeZoneId, String expected1, + String expected2, Date date) { + format.setTimeZone(SimpleTimeZone.getTimeZone(timeZoneId)); + format.applyPattern("z, zzzz"); + assertEquals("Test z for TimeZone : " + timeZoneId, expected1, + format.format(date)); + + format.applyPattern("Z, ZZZZ"); + assertEquals("Test Z for TimeZone : " + timeZoneId, expected2, + format.format(date)); + } + } + + /** + * @tests java.text.SimpleDateFormat#SimpleDateFormat() + */ + public void test_Constructor() { + // Test for method java.text.SimpleDateFormat() + SimpleDateFormat f2 = new SimpleDateFormat(); + assertTrue("Wrong class", f2.getClass() == SimpleDateFormat.class); + assertTrue("Wrong default", f2.equals(DateFormat.getDateTimeInstance( + DateFormat.SHORT, DateFormat.SHORT, Locale.getDefault()))); + assertTrue("Wrong symbols", f2.getDateFormatSymbols().equals( + new DateFormatSymbols())); + assertTrue("Doesn't work", + f2.format(new Date()).getClass() == String.class); + } + + /** + * @tests java.text.SimpleDateFormat#SimpleDateFormat(java.lang.String) + */ + public void test_ConstructorLjava_lang_String() { + // Test for method java.text.SimpleDateFormat(java.lang.String) + SimpleDateFormat f2 = new SimpleDateFormat("yyyy"); + assertTrue("Wrong class", f2.getClass() == SimpleDateFormat.class); + assertEquals("Wrong pattern", "yyyy", f2.toPattern()); + assertTrue("Wrong locale", f2.equals(new SimpleDateFormat("yyyy", + Locale.getDefault()))); + assertTrue("Wrong symbols", f2.getDateFormatSymbols().equals( + new DateFormatSymbols())); + assertTrue("Doesn't work", + f2.format(new Date()).getClass() == String.class); + + // Invalid constructor value. + try { + new SimpleDateFormat( + "this is an invalid simple date format"); + fail("Expected test_ConstructorLjava_lang_String to throw IAE."); + } catch (IllegalArgumentException ex) { + // expected + } catch (Throwable ex) { + fail("Expected test_ConstructorLjava_lang_String to throw IAE, not " + + ex.getClass().getName()); + } + + // Null string value + try { + new SimpleDateFormat(null); + fail("Expected test_ConstructorLjava_lang_String to throw NPE."); + } catch (NullPointerException ex) { + // expected + } catch (Throwable ex) { + fail("Expected test_ConstructorLjava_lang_String to throw NPE, not " + + ex.getClass().getName()); + } + } + + /** + * @tests java.text.SimpleDateFormat#SimpleDateFormat(java.lang.String, + * java.text.DateFormatSymbols) + */ + public void test_ConstructorLjava_lang_StringLjava_text_DateFormatSymbols() { + // Test for method java.text.SimpleDateFormat(java.lang.String, + // java.text.DateFormatSymbols) + DateFormatSymbols symbols = new DateFormatSymbols(Locale.ENGLISH); + symbols.setEras(new String[] { "Before", "After" }); + SimpleDateFormat f2 = new SimpleDateFormat("y'y'yy", symbols); + assertTrue("Wrong class", f2.getClass() == SimpleDateFormat.class); + assertEquals("Wrong pattern", "y'y'yy", f2.toPattern()); + assertTrue("Wrong symbols", f2.getDateFormatSymbols().equals(symbols)); + assertTrue("Doesn't work", + f2.format(new Date()).getClass() == String.class); + } + + /** + * @tests java.text.SimpleDateFormat#SimpleDateFormat(java.lang.String, + * java.util.Locale) + */ + public void test_ConstructorLjava_lang_StringLjava_util_Locale() { + // Test for method java.text.SimpleDateFormat(java.lang.String, + // java.util.Locale) + SimpleDateFormat f2 = new SimpleDateFormat("'yyyy' MM yy", + Locale.GERMAN); + assertTrue("Wrong class", f2.getClass() == SimpleDateFormat.class); + assertEquals("Wrong pattern", "'yyyy' MM yy", f2.toPattern()); + assertTrue("Wrong symbols", f2.getDateFormatSymbols().equals( + new DateFormatSymbols(Locale.GERMAN))); + assertTrue("Doesn't work", + f2.format(new Date()).getClass() == String.class); + } + + /** + * @tests java.text.SimpleDateFormat#applyLocalizedPattern(java.lang.String) + */ + public void test_applyLocalizedPatternLjava_lang_String() { + // Test for method void + // java.text.SimpleDateFormat.applyLocalizedPattern(java.lang.String) + SimpleDateFormat f2 = new SimpleDateFormat("y", new Locale("de", "CH")); + // BEGIN android-removed + // This test doesn't work like this. The cause lies inside of icu + // that doesn't support localized pattern characters anymore. So this + // test fails because the pattern template contains characters that are + // not part of the standard pattern returned for every locale. + // The default pattern characters are: GyMdkHmsSEDFwWahKzZ + // + // f2.applyLocalizedPattern("GuMtkHmsSEDFwWahKz"); + // String pattern = f2.toPattern(); + // assertTrue("Wrong pattern: " + pattern, pattern + // .equals("GyMdkHmsSEDFwWahKz")); + // + // test the new "Z" pattern char + // f2 = new SimpleDateFormat("y", new Locale("de", "CH")); + // f2.applyLocalizedPattern("G u M t Z"); + // pattern = f2.toPattern(); + // assertTrue("Wrong pattern: " + pattern, pattern.equals("G y M d Z")); + // END android-removed + + // test invalid patterns + try { + f2.applyLocalizedPattern("b"); + fail("Expected IllegalArgumentException for pattern with invalid pattern letter: b"); + } catch (IllegalArgumentException e) { + } + + try { + // BEGIN android-canged + f2.applyLocalizedPattern("u"); + fail("Expected IllegalArgumentException for pattern with invalid pattern letter: u"); + // END android-changed + } catch (IllegalArgumentException e) { + } + + try { + f2.applyLocalizedPattern("a '"); + fail("Expected IllegalArgumentException for pattern with unterminated quote: a '"); + } catch (IllegalArgumentException e) { + } + + try { + f2.applyLocalizedPattern(null); + fail("Expected NullPointerException for null pattern"); + } catch (NullPointerException e) { + } + } + + /** + * @tests java.text.SimpleDateFormat#applyPattern(java.lang.String) + */ + public void test_applyPatternLjava_lang_String() { + // Test for method void + // java.text.SimpleDateFormat.applyPattern(java.lang.String) + SimpleDateFormat f2 = new SimpleDateFormat("y", new Locale("de", "CH")); + // BEGIN android-changed + f2.applyPattern("GyMdkHmsSEDFwWahKzZ"); + assertEquals("Wrong pattern", "GyMdkHmsSEDFwWahKzZ", f2.toPattern()); + // END android-changed + + // test invalid patterns + try { + f2.applyPattern("b"); + fail("Expected IllegalArgumentException for pattern with invalid patter letter: b"); + } catch (IllegalArgumentException e) { + } + + try { + f2.applyPattern("u"); + fail("Expected IllegalArgumentException for pattern with invalid patter letter: u"); + } catch (IllegalArgumentException e) { + } + + try { + f2.applyPattern("a '"); + fail("Expected IllegalArgumentException for pattern with unterminated quote: a '"); + } catch (IllegalArgumentException e) { + } + + try { + f2.applyPattern(null); + fail("Expected NullPointerException for null pattern"); + } catch (NullPointerException e) { + } + } + + /** + * @tests java.text.SimpleDateFormat#clone() + */ + public void test_clone() { + // Test for method java.lang.Object java.text.SimpleDateFormat.clone() + SimpleDateFormat f2 = new SimpleDateFormat(); + SimpleDateFormat clone = (SimpleDateFormat) f2.clone(); + assertTrue("Invalid clone", f2.equals(clone)); + clone.applyPattern("y"); + assertTrue("Format modified", !f2.equals(clone)); + clone = (SimpleDateFormat) f2.clone(); + // Date date = clone.get2DigitYearStart(); + // date.setTime(0); + // assertTrue("Equal after date change: " + + // f2.get2DigitYearStart().getTime() + " " + + // clone.get2DigitYearStart().getTime(), !f2.equals(clone)); + } + + /** + * @tests java.text.SimpleDateFormat#equals(java.lang.Object) + */ + public void test_equalsLjava_lang_Object() { + // Test for method boolean + // java.text.SimpleDateFormat.equals(java.lang.Object) + SimpleDateFormat format = (SimpleDateFormat) DateFormat.getInstance(); + SimpleDateFormat clone = (SimpleDateFormat) format.clone(); + assertTrue("clone not equal", format.equals(clone)); + format.format(new Date()); + assertTrue("not equal after format", format.equals(clone)); + } + + /** + * @tests java.text.SimpleDateFormat#hashCode() + */ + public void test_hashCode() { + SimpleDateFormat format = (SimpleDateFormat) DateFormat.getInstance(); + SimpleDateFormat clone = (SimpleDateFormat) format.clone(); + assertTrue("clone has not equal hash code", clone.hashCode() == format + .hashCode()); + format.format(new Date()); + assertTrue("clone has not equal hash code after format", clone + .hashCode() == format.hashCode()); + DateFormatSymbols symbols = new DateFormatSymbols(Locale.ENGLISH); + symbols.setEras(new String[] { "Before", "After" }); + SimpleDateFormat format2 = new SimpleDateFormat("y'y'yy", symbols); + assertFalse("objects has equal hash code", format2.hashCode() == format + .hashCode()); + } + + public void test_equals_afterFormat() { + // Regression test for HARMONY-209 + SimpleDateFormat df = new SimpleDateFormat(); + df.format(new Date()); + assertEquals(df, new SimpleDateFormat()); + } + + /** + * @tests java.text.SimpleDateFormat#formatToCharacterIterator(java.lang.Object) + */ + public void test_formatToCharacterIteratorLjava_lang_Object() { + + try { + // Regression for HARMONY-466 + new SimpleDateFormat().formatToCharacterIterator(null); + fail("NullPointerException expected"); + } catch (NullPointerException e) { + // expected + } + + // Test for method formatToCharacterIterator(java.lang.Object) + new Support_SimpleDateFormat( + "test_formatToCharacterIteratorLjava_lang_Object") + .t_formatToCharacterIterator(); + } + + /** + * @tests java.text.SimpleDateFormat#format(java.util.Date, + * java.lang.StringBuffer, java.text.FieldPosition) + */ + public void test_formatLjava_util_DateLjava_lang_StringBufferLjava_text_FieldPosition() { + // Test for method java.lang.StringBuffer + // java.text.SimpleDateFormat.format(java.util.Date, + // java.lang.StringBuffer, java.text.FieldPosition) + + new Support_SimpleDateFormat( + "test_formatLjava_util_DateLjava_lang_StringBufferLjava_text_FieldPosition") + .t_format_with_FieldPosition(); + + TestFormat test = new TestFormat( + "test_formatLjava_util_DateLjava_lang_StringBufferLjava_text_FieldPosition"); + + Calendar cal = new GregorianCalendar(1999, Calendar.JUNE, 2, 15, 3, 6); + test.test(" G", cal, " AD", DateFormat.ERA_FIELD); + test.test(" GG", cal, " AD", DateFormat.ERA_FIELD); + test.test(" GGG", cal, " AD", DateFormat.ERA_FIELD); + test.test(" G", new GregorianCalendar(-1999, Calendar.JUNE, 2), " BC", + DateFormat.ERA_FIELD); + + test.test(" y", cal, " 99", DateFormat.YEAR_FIELD); + test.test(" yy", cal, " 99", DateFormat.YEAR_FIELD); + test.test(" yy", new GregorianCalendar(2001, Calendar.JUNE, 2), " 01", + DateFormat.YEAR_FIELD); + test.test(" yy", new GregorianCalendar(2000, Calendar.JUNE, 2), " 00", + DateFormat.YEAR_FIELD); + test.test(" yyy", new GregorianCalendar(2000, Calendar.JUNE, 2), " 00", + DateFormat.YEAR_FIELD); + test.test(" yyy", cal, " 99", DateFormat.YEAR_FIELD); + test.test(" yyyy", cal, " 1999", DateFormat.YEAR_FIELD); + test.test(" yyyyy", cal, " 01999", DateFormat.YEAR_FIELD); + + test.test(" M", cal, " 6", DateFormat.MONTH_FIELD); + test.test(" M", new GregorianCalendar(1999, Calendar.NOVEMBER, 2), + " 11", DateFormat.MONTH_FIELD); + test.test(" MM", cal, " 06", DateFormat.MONTH_FIELD); + test.test(" MMM", cal, " Jun", DateFormat.MONTH_FIELD); + test.test(" MMMM", cal, " June", DateFormat.MONTH_FIELD); + test.test(" MMMMM", cal, " June", DateFormat.MONTH_FIELD); + + test.test(" d", cal, " 2", DateFormat.DATE_FIELD); + test.test(" d", new GregorianCalendar(1999, Calendar.NOVEMBER, 12), + " 12", DateFormat.DATE_FIELD); + test.test(" dd", cal, " 02", DateFormat.DATE_FIELD); + test.test(" dddd", cal, " 0002", DateFormat.DATE_FIELD); + + test.test(" h", cal, " 3", DateFormat.HOUR1_FIELD); + test.test(" h", new GregorianCalendar(1999, Calendar.NOVEMBER, 12), + " 12", DateFormat.HOUR1_FIELD); + test.test(" hh", cal, " 03", DateFormat.HOUR1_FIELD); + test.test(" hhhh", cal, " 0003", DateFormat.HOUR1_FIELD); + + test.test(" H", cal, " 15", DateFormat.HOUR_OF_DAY0_FIELD); + test.test(" H", + new GregorianCalendar(1999, Calendar.NOVEMBER, 12, 4, 0), " 4", + DateFormat.HOUR_OF_DAY0_FIELD); + test.test(" H", new GregorianCalendar(1999, Calendar.NOVEMBER, 12, 12, + 0), " 12", DateFormat.HOUR_OF_DAY0_FIELD); + test.test(" H", new GregorianCalendar(1999, Calendar.NOVEMBER, 12), + " 0", DateFormat.HOUR_OF_DAY0_FIELD); + test.test(" HH", cal, " 15", DateFormat.HOUR_OF_DAY0_FIELD); + test.test(" HHHH", cal, " 0015", DateFormat.HOUR_OF_DAY0_FIELD); + + test.test(" m", cal, " 3", DateFormat.MINUTE_FIELD); + test.test(" m", new GregorianCalendar(1999, Calendar.NOVEMBER, 12, 4, + 47), " 47", DateFormat.MINUTE_FIELD); + test.test(" mm", cal, " 03", DateFormat.MINUTE_FIELD); + test.test(" mmmm", cal, " 0003", DateFormat.MINUTE_FIELD); + + test.test(" s", cal, " 6", DateFormat.SECOND_FIELD); + test.test(" s", new GregorianCalendar(1999, Calendar.NOVEMBER, 12, 4, + 47, 13), " 13", DateFormat.SECOND_FIELD); + test.test(" ss", cal, " 06", DateFormat.SECOND_FIELD); + test.test(" ssss", cal, " 0006", DateFormat.SECOND_FIELD); + + test.test(" S", cal, " 0", DateFormat.MILLISECOND_FIELD); + Calendar temp = new GregorianCalendar(); + temp.set(Calendar.MILLISECOND, 961); + + test.test(" SS", temp, " 961", DateFormat.MILLISECOND_FIELD); + test.test(" SSSS", cal, " 0000", DateFormat.MILLISECOND_FIELD); + + test.test(" SS", cal, " 00", DateFormat.MILLISECOND_FIELD); + + test.test(" E", cal, " Wed", DateFormat.DAY_OF_WEEK_FIELD); + test.test(" EE", cal, " Wed", DateFormat.DAY_OF_WEEK_FIELD); + test.test(" EEE", cal, " Wed", DateFormat.DAY_OF_WEEK_FIELD); + test.test(" EEEE", cal, " Wednesday", DateFormat.DAY_OF_WEEK_FIELD); + test.test(" EEEEE", cal, " Wednesday", DateFormat.DAY_OF_WEEK_FIELD); + + test.test(" D", cal, " 153", DateFormat.DAY_OF_YEAR_FIELD); + test.test(" DD", cal, " 153", DateFormat.DAY_OF_YEAR_FIELD); + test.test(" DDDD", cal, " 0153", DateFormat.DAY_OF_YEAR_FIELD); + + test.test(" F", cal, " 1", DateFormat.DAY_OF_WEEK_IN_MONTH_FIELD); + test.test(" F", new GregorianCalendar(1999, Calendar.NOVEMBER, 14), + " 2", DateFormat.DAY_OF_WEEK_IN_MONTH_FIELD); + test.test(" FF", cal, " 01", DateFormat.DAY_OF_WEEK_IN_MONTH_FIELD); + test.test(" FFFF", cal, " 0001", DateFormat.DAY_OF_WEEK_IN_MONTH_FIELD); + + test.test(" w", cal, " 23", DateFormat.WEEK_OF_YEAR_FIELD); + test.test(" ww", cal, " 23", DateFormat.WEEK_OF_YEAR_FIELD); + test.test(" wwww", cal, " 0023", DateFormat.WEEK_OF_YEAR_FIELD); + + test.test(" W", cal, " 1", DateFormat.WEEK_OF_MONTH_FIELD); + test.test(" W", new GregorianCalendar(1999, Calendar.NOVEMBER, 14), + " 3", DateFormat.WEEK_OF_MONTH_FIELD); + test.test(" WW", cal, " 01", DateFormat.WEEK_OF_MONTH_FIELD); + test.test(" WWWW", cal, " 0001", DateFormat.WEEK_OF_MONTH_FIELD); + + test.test(" a", cal, " PM", DateFormat.AM_PM_FIELD); + test.test(" a", new GregorianCalendar(1999, Calendar.NOVEMBER, 14), + " AM", DateFormat.AM_PM_FIELD); + test.test(" a", new GregorianCalendar(1999, Calendar.NOVEMBER, 14, 12, + 0), " PM", DateFormat.AM_PM_FIELD); + test.test(" aa", cal, " PM", DateFormat.AM_PM_FIELD); + test.test(" aaa", cal, " PM", DateFormat.AM_PM_FIELD); + test.test(" aaaa", cal, " PM", DateFormat.AM_PM_FIELD); + test.test(" aaaaa", cal, " PM", DateFormat.AM_PM_FIELD); + + test.test(" k", cal, " 15", DateFormat.HOUR_OF_DAY1_FIELD); + test.test(" k", + new GregorianCalendar(1999, Calendar.NOVEMBER, 12, 4, 0), " 4", + DateFormat.HOUR_OF_DAY1_FIELD); + test.test(" k", new GregorianCalendar(1999, Calendar.NOVEMBER, 12, 12, + 0), " 12", DateFormat.HOUR_OF_DAY1_FIELD); + test.test(" k", new GregorianCalendar(1999, Calendar.NOVEMBER, 12), + " 24", DateFormat.HOUR_OF_DAY1_FIELD); + test.test(" kk", cal, " 15", DateFormat.HOUR_OF_DAY1_FIELD); + test.test(" kkkk", cal, " 0015", DateFormat.HOUR_OF_DAY1_FIELD); + + test.test(" K", cal, " 3", DateFormat.HOUR0_FIELD); + test.test(" K", new GregorianCalendar(1999, Calendar.NOVEMBER, 12), + " 0", DateFormat.HOUR0_FIELD); + test.test(" KK", cal, " 03", DateFormat.HOUR0_FIELD); + test.test(" KKKK", cal, " 0003", DateFormat.HOUR0_FIELD); + + format.setTimeZone(TimeZone.getTimeZone("EST")); + test.test(" z", cal, " EDT", DateFormat.TIMEZONE_FIELD); + Calendar temp2 = new GregorianCalendar(1999, Calendar.JANUARY, 12); + test.test(" z", temp2, " EST", DateFormat.TIMEZONE_FIELD); + test.test(" zz", cal, " EDT", DateFormat.TIMEZONE_FIELD); + test.test(" zzz", cal, " EDT", DateFormat.TIMEZONE_FIELD); + test.test(" zzzz", cal, " Eastern Daylight Time", + DateFormat.TIMEZONE_FIELD); + test.test(" zzzz", temp2, " Eastern Standard Time", + DateFormat.TIMEZONE_FIELD); + test.test(" zzzzz", cal, " Eastern Daylight Time", + DateFormat.TIMEZONE_FIELD); + + format.setTimeZone(new SimpleTimeZone(60000, "ONE MINUTE")); + test.test(" z", cal, " GMT+00:01", DateFormat.TIMEZONE_FIELD); + test.test(" zzzz", cal, " GMT+00:01", DateFormat.TIMEZONE_FIELD); + format.setTimeZone(new SimpleTimeZone(5400000, "ONE HOUR, THIRTY")); + test.test(" z", cal, " GMT+01:30", DateFormat.TIMEZONE_FIELD); + format + .setTimeZone(new SimpleTimeZone(-5400000, + "NEG ONE HOUR, THIRTY")); + test.test(" z", cal, " GMT-01:30", DateFormat.TIMEZONE_FIELD); + + format.applyPattern("'Mkz''':.@5"); + assertEquals("Wrong output", "Mkz':.@5", format.format(new Date())); + + assertTrue("Tests failed", !test.testsFailed()); + + // Test invalid args to format. + SimpleDateFormat dateFormat = new SimpleDateFormat(); + try { + dateFormat.format(null, new StringBuffer(), new FieldPosition(1)); + fail("Expected test to throw NPE."); + } catch (NullPointerException ex) { + // expected + } catch (Throwable ex) { + fail("Expected test to throw NPE, not " + ex.getClass().getName()); + } + } + + /** + * @tests java.text.SimpleDateFormat#format(java.util.Date) + */ + public void test_timeZoneFormatting() { + // tests specific to formatting of timezones + Date summerDate = new GregorianCalendar(1999, Calendar.JUNE, 2, 15, 3, + 6).getTime(); + Date winterDate = new GregorianCalendar(1999, Calendar.JANUARY, 12) + .getTime(); + + TestFormat test = new TestFormat( + "test_formatLjava_util_DateLjava_lang_StringBufferLjava_text_FieldPosition"); + + test.verifyFormatTimezone("PST", "PDT, Pacific Daylight Time", + "-0700, -0700", summerDate); + test.verifyFormatTimezone("PST", "PST, Pacific Standard Time", + "-0800, -0800", winterDate); + + test.verifyFormatTimezone("GMT-7", "GMT-07:00, GMT-07:00", + "-0700, -0700", summerDate); + test.verifyFormatTimezone("GMT-7", "GMT-07:00, GMT-07:00", + "-0700, -0700", winterDate); + + // Pacific/Kiritimati is one of the timezones supported only in mJava + test.verifyFormatTimezone("Pacific/Kiritimati", "LINT, Line Is. Time", + "+1400, +1400", summerDate); + test.verifyFormatTimezone("Pacific/Kiritimati", "LINT, Line Is. Time", + "+1400, +1400", winterDate); + + test.verifyFormatTimezone("EST", "EDT, Eastern Daylight Time", + "-0400, -0400", summerDate); + test.verifyFormatTimezone("EST", "EST, Eastern Standard Time", + "-0500, -0500", winterDate); + + test.verifyFormatTimezone("GMT+14", "GMT+14:00, GMT+14:00", + "+1400, +1400", summerDate); + test.verifyFormatTimezone("GMT+14", "GMT+14:00, GMT+14:00", + "+1400, +1400", winterDate); + } + + /** + * @tests java.text.SimpleDateFormat#get2DigitYearStart() + */ + public void test_get2DigitYearStart() { + // Test for method java.util.Date + // java.text.SimpleDateFormat.get2DigitYearStart() + SimpleDateFormat f1 = new SimpleDateFormat("y"); + Date date = f1.get2DigitYearStart(); + Calendar cal = new GregorianCalendar(); + int year = cal.get(Calendar.YEAR); + cal.setTime(date); + assertTrue("Wrong default year start", + cal.get(Calendar.YEAR) == (year - 80)); + } + + /** + * @tests java.text.SimpleDateFormat#getDateFormatSymbols() + */ + public void test_getDateFormatSymbols() { + // Test for method java.text.DateFormatSymbols + // java.text.SimpleDateFormat.getDateFormatSymbols() + SimpleDateFormat df = (SimpleDateFormat) DateFormat.getInstance(); + DateFormatSymbols dfs = df.getDateFormatSymbols(); + assertTrue("Symbols identical", dfs != df.getDateFormatSymbols()); + } + + /** + * @tests java.text.SimpleDateFormat#parse(java.lang.String, + * java.text.ParsePosition) + */ + public void test_parseLjava_lang_StringLjava_text_ParsePosition() { + // Test for method java.util.Date + // java.text.SimpleDateFormat.parse(java.lang.String, + // java.text.ParsePosition) + TestFormat test = new TestFormat( + "test_formatLjava_util_DateLjava_lang_StringBufferLjava_text_FieldPosition"); + + Calendar cal = new GregorianCalendar(1970, Calendar.JANUARY, 1); + Date time = cal.getTime(); + test.parse("h", " 12", time, 1, 3); + test.parse("H", " 0", time, 1, 2); + test.parse("k", " 24", time, 1, 3); + test.parse("K", " 0", time, 1, 2); + + cal = new GregorianCalendar(1970, Calendar.JANUARY, 1, 1, 0); + time = cal.getTime(); + test.parse("h", "1", time, 0, 1); + test.parse("H", "1 ", time, 0, 1); + test.parse("k", "1", time, 0, 1); + test.parse("K", "1", time, 0, 1); + + cal = new GregorianCalendar(1970, Calendar.JANUARY, 1, 11, 0); + time = cal.getTime(); + test.parse("h", "0011 ", time, 0, 4); + test.parse("K", "11", time, 0, 2); + cal = new GregorianCalendar(1970, Calendar.JANUARY, 1, 23, 0); + time = cal.getTime(); + test.parse("H", "23", time, 0, 2); + test.parse("k", "23", time, 0, 2); + + test.parse("h a", " 3 AM", new GregorianCalendar(1970, + Calendar.JANUARY, 1, 3, 0).getTime(), 1, 5); + test.parse("K a", " 3 pm ", new GregorianCalendar(1970, + Calendar.JANUARY, 1, 15, 0).getTime(), 1, 5); + test.parse("m:s", "0:59 ", new GregorianCalendar(1970, + Calendar.JANUARY, 1, 0, 0, 59).getTime(), 0, 4); + test.parse("m:s", "59:0", new GregorianCalendar(1970, Calendar.JANUARY, + 1, 0, 59, 0).getTime(), 0, 4); + test.parse("ms", "059", new GregorianCalendar(1970, Calendar.JANUARY, + 1, 0, 0, 59).getTime(), 0, 3); + + cal = new GregorianCalendar(1970, Calendar.JANUARY, 1); + test.parse("S", "0", cal.getTime(), 0, 1); + cal.setTimeZone(TimeZone.getTimeZone("HST")); + cal.set(Calendar.MILLISECOND, 999); + test.parse("S z", "999 HST", cal.getTime(), 0, 7); + + cal = new GregorianCalendar(1970, Calendar.JANUARY, 1); + cal.set(Calendar.ERA, GregorianCalendar.BC); + test.parse("G", "Bc ", cal.getTime(), 0, 2); + + test.parse("y", "00", new GregorianCalendar(2000, Calendar.JANUARY, 1) + .getTime(), 0, 2); + test.parse("y", "99", new GregorianCalendar(1999, Calendar.JANUARY, 1) + .getTime(), 0, 2); + test.parse("y", "1", new GregorianCalendar(1, Calendar.JANUARY, 1) + .getTime(), 0, 1); + test.parse("y", "-1", new GregorianCalendar(-1, Calendar.JANUARY, 1) + .getTime(), 0, 2); + test.parse("y", "001", new GregorianCalendar(1, Calendar.JANUARY, 1) + .getTime(), 0, 3); + test.parse("y", "2005", + new GregorianCalendar(2005, Calendar.JANUARY, 1).getTime(), 0, + 4); + test.parse("yy", "00", new GregorianCalendar(2000, Calendar.JANUARY, 1) + .getTime(), 0, 2); + test.parse("yy", "99", new GregorianCalendar(1999, Calendar.JANUARY, 1) + .getTime(), 0, 2); + test.parse("yy", "1", new GregorianCalendar(1, Calendar.JANUARY, 1) + .getTime(), 0, 1); + test.parse("yy", "-1", new GregorianCalendar(-1, Calendar.JANUARY, 1) + .getTime(), 0, 2); + test.parse("yy", "001", new GregorianCalendar(1, Calendar.JANUARY, 1) + .getTime(), 0, 3); + test.parse("yy", "2005", new GregorianCalendar(2005, Calendar.JANUARY, + 1).getTime(), 0, 4); + test.parse("yyy", "99", new GregorianCalendar(99, Calendar.JANUARY, 1) + .getTime(), 0, 2); + test.parse("yyy", "1", new GregorianCalendar(1, Calendar.JANUARY, 1) + .getTime(), 0, 1); + test.parse("yyy", "-1", new GregorianCalendar(-1, Calendar.JANUARY, 1) + .getTime(), 0, 2); + test.parse("yyy", "001", new GregorianCalendar(1, Calendar.JANUARY, 1) + .getTime(), 0, 3); + test.parse("yyy", "2005", new GregorianCalendar(2005, Calendar.JANUARY, + 1).getTime(), 0, 4); + test.parse("yyyy", "99", new GregorianCalendar(99, Calendar.JANUARY, 1) + .getTime(), 0, 2); + test.parse("yyyy", " 1999", new GregorianCalendar(1999, + Calendar.JANUARY, 1).getTime(), 2, 6); + test.parse("MM'M'", "4M", + new GregorianCalendar(1970, Calendar.APRIL, 1).getTime(), 0, 2); + test.parse("MMM", "Feb", new GregorianCalendar(1970, Calendar.FEBRUARY, + 1).getTime(), 0, 3); + test.parse("MMMM d", "April 14 ", new GregorianCalendar(1970, + Calendar.APRIL, 14).getTime(), 0, 8); + test.parse("MMMMd", "April14 ", new GregorianCalendar(1970, + Calendar.APRIL, 14).getTime(), 0, 7); + test.parse("E w", "Mon 12", new GregorianCalendar(1970, Calendar.MARCH, + 16).getTime(), 0, 6); + test.parse("Ew", "Mon12", new GregorianCalendar(1970, Calendar.MARCH, + 16).getTime(), 0, 5); + test.parse("M EE ''W", "5 Tue '2", new GregorianCalendar(1970, + Calendar.MAY, 5).getTime(), 0, 8); + test.parse("MEE''W", "5Tue'2", new GregorianCalendar(1970, + Calendar.MAY, 5).getTime(), 0, 6); + test.parse("MMM EEE F", " JUL Sunday 3", new GregorianCalendar(1970, + Calendar.JULY, 19).getTime(), 1, 13); + test.parse("MMMEEEF", " JULSunday3", new GregorianCalendar(1970, + Calendar.JULY, 19).getTime(), 1, 11); + + cal = new GregorianCalendar(1970, Calendar.JANUARY, 1); + cal.setTimeZone(TimeZone.getTimeZone("GMT+0:1")); + cal.set(Calendar.DAY_OF_YEAR, 243); + test.parse("D z", "243 GMT+0:0", cal.getTime(), 0, 11); + cal.setTimeZone(TimeZone.getTimeZone("EST")); + cal.set(1970, Calendar.JANUARY, 1, 4, 30); + test.parse("h:m z", "4:30 GMT-5 ", cal.getTime(), 0, 10); + test.parse("h z", "14 GMT-24 ", new Date(51840000), 0, 9); + test.parse("h z", "14 GMT-23 ", new Date(133200000), 0, 9); + test.parse("h z", "14 GMT-0001 ", new Date(54000000), 0, 11); + test.parse("h z", "14 GMT+24 ", new Date(48960000), 0, 9); + test.parse("h z", "14 GMT+23 ", new Date(-32400000), 0, 9); + test.parse("h z", "14 GMT+0001 ", new Date(46800000), 0, 11); + test.parse("h z", "14 +0001 ", new Date(46800000), 0, 8); + test.parse("h z", "14 -0001 ", new Date(54000000), 0, 8); + + test.parse("yyyyMMddHHmmss", "19990913171901", new GregorianCalendar( + 1999, Calendar.SEPTEMBER, 13, 17, 19, 01).getTime(), 0, 14); + + Date d = new Date(1015822800000L); + SimpleDateFormat df = new SimpleDateFormat("", new Locale("en", "US")); + df.setTimeZone(TimeZone.getTimeZone("EST")); + + try { + df.applyPattern("dd MMMM yyyy EEEE"); + String output = df.format(d); + Date date = df.parse(output); + assertTrue("Invalid result 1: " + date, d.equals(date)); + + df.applyPattern("dd MMMM yyyy F"); + output = df.format(d); + date = df.parse(output); + assertTrue("Invalid result 2: " + date, d.equals(date)); + + df.applyPattern("dd MMMM yyyy w"); + output = df.format(d); + date = df.parse(output); + assertTrue("Invalid result 3: " + date, d.equals(date)); + + df.applyPattern("dd MMMM yyyy W"); + output = df.format(d); + date = df.parse(output); + assertTrue("Invalid result 4: " + date, d.equals(date)); + + df.applyPattern("dd MMMM yyyy D"); + date = df.parse("5 January 2002 70"); + assertTrue("Invalid result 5: " + date, d.equals(date)); + + df.applyPattern("W w dd MMMM yyyy EEEE"); + output = df.format(d); + date = df.parse("3 12 5 March 2002 Monday"); + assertTrue("Invalid result 6: " + date, d.equals(date)); + + df.applyPattern("w W dd MMMM yyyy EEEE"); + output = df.format(d); + date = df.parse("12 3 5 March 2002 Monday"); + assertTrue("Invalid result 6a: " + date, d.equals(date)); + + df.applyPattern("F dd MMMM yyyy EEEE"); + output = df.format(d); + date = df.parse("2 5 March 2002 Monday"); + assertTrue("Invalid result 7: " + date, d.equals(date)); + + df.applyPattern("w dd MMMM yyyy EEEE"); + output = df.format(d); + date = df.parse("11 5 January 2002 Monday"); + assertTrue("Invalid result 8: " + date, d.equals(date)); + + df.applyPattern("w dd yyyy EEEE MMMM"); + output = df.format(d); + date = df.parse("11 5 2002 Monday January"); + assertTrue("Invalid result 9: " + date, d.equals(date)); + + df.applyPattern("w yyyy EEEE MMMM dd"); + output = df.format(d); + date = df.parse("17 2002 Monday March 11"); + assertTrue("Invalid result 10: " + date, d.equals(date)); + + df.applyPattern("dd D yyyy MMMM"); + output = df.format(d); + date = df.parse("5 70 2002 January"); + assertTrue("Invalid result 11: " + date, d.equals(date)); + + df.applyPattern("D dd yyyy MMMM"); + output = df.format(d); + date = df.parse("240 11 2002 March"); + assertTrue("Invalid result 12: " + date, d.equals(date)); + } catch (ParseException e) { + fail("unexpected: " + e); + } + } + + /** + * @tests java.text.SimpleDateFormat#set2DigitYearStart(java.util.Date) + */ + public void test_set2DigitYearStartLjava_util_Date() { + // Test for method void + // java.text.SimpleDateFormat.set2DigitYearStart(java.util.Date) + SimpleDateFormat f1 = new SimpleDateFormat("yy"); + f1.set2DigitYearStart(new GregorianCalendar(1950, Calendar.JANUARY, 1) + .getTime()); + Calendar cal = new GregorianCalendar(); + try { + cal.setTime(f1.parse("49")); + assertEquals("Incorrect year 2049", 2049, cal.get(Calendar.YEAR)); + cal.setTime(f1.parse("50")); + int year = cal.get(Calendar.YEAR); + assertTrue("Incorrect year 1950: " + year, year == 1950); + f1.applyPattern("y"); + cal.setTime(f1.parse("00")); + assertEquals("Incorrect year 2000", 2000, cal.get(Calendar.YEAR)); + f1.applyPattern("yyy"); + cal.setTime(f1.parse("50")); + assertEquals("Incorrect year 50", 50, cal.get(Calendar.YEAR)); + } catch (ParseException e) { + fail("ParseException"); + } + } + + /** + * @tests java.text.SimpleDateFormat#setDateFormatSymbols(java.text.DateFormatSymbols) + */ + public void test_setDateFormatSymbolsLjava_text_DateFormatSymbols() { + // Test for method void + // java.text.SimpleDateFormat.setDateFormatSymbols(java.text.DateFormatSymbols) + SimpleDateFormat f1 = new SimpleDateFormat("a"); + DateFormatSymbols symbols = new DateFormatSymbols(); + symbols.setAmPmStrings(new String[] { "morning", "night" }); + f1.setDateFormatSymbols(symbols); + DateFormatSymbols newSym = f1.getDateFormatSymbols(); + assertTrue("Set incorrectly", newSym.equals(symbols)); + assertTrue("Not a clone", f1.getDateFormatSymbols() != symbols); + String result = f1.format(new GregorianCalendar(1999, Calendar.JUNE, + 12, 3, 0).getTime()); + assertEquals("Incorrect symbols used", "morning", result); + symbols.setEras(new String[] { "before", "after" }); + assertTrue("Identical symbols", !f1.getDateFormatSymbols().equals( + symbols)); + } + + /** + * @tests java.text.SimpleDateFormat#toLocalizedPattern() + */ + public void test_toLocalizedPattern() { + // BEGIN android-changed + // Test for method java.lang.String + // java.text.SimpleDateFormat.toLocalizedPattern() + SimpleDateFormat f2 = new SimpleDateFormat("GyMdkHmsSEDFwWahKzZ", + new Locale("de", "CH")); + String pattern = f2.toLocalizedPattern(); + // the default localized pattern characters are the same for all locales + // since icu has droped support for this. the default pattern characters + // are these: GyMdkHmsSEDFwWahKz + // + // assertTrue("Wrong pattern: " + pattern, pattern + // .equals("GuMtkHmsSEDFwWahKz")); + assertTrue("Wrong pattern: " + pattern, pattern + .equals("GyMdkHmsSEDFwWahKzZ")); + + + // test the new "Z" pattern char + f2 = new SimpleDateFormat("G y M d Z", new Locale("de", "CH")); + pattern = f2.toLocalizedPattern(); + // assertTrue("Wrong pattern: " + pattern, pattern.equals("G u M t Z")); + assertTrue("Wrong pattern: " + pattern, pattern.equals("G y M d Z")); + // END android-changed + } + + /** + * @tests java.text.SimpleDateFormat#toPattern() + */ + public void test_toPattern() { + String pattern = "yyyy mm dd"; + SimpleDateFormat f = new SimpleDateFormat(pattern); + assertEquals("Wrong pattern: " + pattern, pattern, f.toPattern()); + + pattern = "GyMdkHmsSEDFwWahKz"; + f = new SimpleDateFormat("GyMdkHmsSEDFwWahKz", new Locale("de", "CH")); + assertTrue("Wrong pattern: " + pattern, f.toPattern().equals(pattern)); + + pattern = "G y M d Z"; + f = new SimpleDateFormat(pattern, new Locale("de", "CH")); + pattern = f.toPattern(); + assertTrue("Wrong pattern: " + pattern, f.toPattern().equals(pattern)); + } + + /** + * @tests java.text.SimpleDateFormat#parse(java.lang.String, + * java.text.ParsePosition) + */ + public void test_parse_with_spaces() { + // Regression for HARMONY-502 + SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss"); + df.setLenient(false); + + char allowed_chars[] = { 0x9, 0x20 }; + String allowed_char_names[] = { "tab", "space" }; + for (int i = 0; i < allowed_chars.length; i++) { + Date expected = new GregorianCalendar(1970, Calendar.JANUARY, 1, 9, + 7, 6).getTime(); + ParsePosition pp = new ParsePosition(0); + Date d = df.parse(allowed_chars[i] + "9:07:06", pp); + assertNotNull("hour may be prefixed by " + allowed_char_names[i], d); + assertEquals(expected, d); + + pp = new ParsePosition(0); + d = df.parse("09:" + allowed_chars[i] + "7:06", pp); + assertNotNull("minute may be prefixed by " + allowed_char_names[i], + d); + assertEquals(expected, d); + + pp = new ParsePosition(0); + d = df.parse("09:07:" + allowed_chars[i] + "6", pp); + assertNotNull("second may be prefixed by " + allowed_char_names[i], + d); + assertEquals(expected, d); + } + + char not_allowed_chars[] = { + // whitespace + 0x1c, 0x1d, 0x1e, 0x1f, 0xa, 0xb, 0xc, 0xd, 0x2001, 0x2002, + 0x2003, 0x2004, 0x2005, 0x2006, 0x2008, 0x2009, 0x200a, 0x200b, + 0x2028, 0x2029, 0x3000, + // non-breaking space + 0xA0, 0x2007, 0x202F }; + + for (int i = 0; i < not_allowed_chars.length; i++) { + + ParsePosition pp = new ParsePosition(0); + Date d = df.parse(not_allowed_chars[i] + "9:07", pp); + assertNull(d); + + pp = new ParsePosition(0); + d = df.parse("09:" + not_allowed_chars[i] + "7", pp); + assertNull(d); + + pp = new ParsePosition(0); + d = df.parse("09:07:" + not_allowed_chars[i] + "6", pp); + assertNull(d); + } + } +} diff --git a/text/src/test/java/org/apache/harmony/text/tests/java/text/StringCharacterIteratorTest.java b/text/src/test/java/org/apache/harmony/text/tests/java/text/StringCharacterIteratorTest.java new file mode 100644 index 0000000..68293fb --- /dev/null +++ b/text/src/test/java/org/apache/harmony/text/tests/java/text/StringCharacterIteratorTest.java @@ -0,0 +1,521 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.harmony.text.tests.java.text; + +import java.text.CharacterIterator; +import java.text.StringCharacterIterator; + +import junit.framework.TestCase; + +public class StringCharacterIteratorTest extends TestCase { + + /** + * @tests java.text.StringCharacterIterator.StringCharacterIterator(String, + * int) + */ + public void test_ConstructorI() { + assertNotNull(new StringCharacterIterator("value", 0)); + assertNotNull(new StringCharacterIterator("value", "value".length())); + assertNotNull(new StringCharacterIterator("", 0)); + try { + new StringCharacterIterator(null, 0); + fail("Assert 0: no null pointer"); + } catch (NullPointerException e) { + // expected + } + + try { + new StringCharacterIterator("value", -1); + fail("Assert 1: no illegal argument"); + } catch (IllegalArgumentException e) { + // expected + } + + try { + new StringCharacterIterator("value", "value".length() + 1); + fail("Assert 2: no illegal argument"); + } catch (IllegalArgumentException e) { + // expected + } + } + + /** + * @tests java.text.StringCharacterIterator(String, int, int, int) + */ + public void test_ConstructorIII() { + assertNotNull(new StringCharacterIterator("value", 0, "value".length(), + 0)); + assertNotNull(new StringCharacterIterator("value", 0, "value".length(), + 1)); + assertNotNull(new StringCharacterIterator("", 0, 0, 0)); + + try { + new StringCharacterIterator(null, 0, 0, 0); + fail("no null pointer"); + } catch (NullPointerException e) { + // Expected + } + + try { + new StringCharacterIterator("value", -1, "value".length(), 0); + fail("no illegal argument: invalid begin"); + } catch (IllegalArgumentException e) { + // Expected + } + + try { + new StringCharacterIterator("value", 0, "value".length() + 1, 0); + fail("no illegal argument: invalid end"); + } catch (IllegalArgumentException e) { + // Expected + } + + try { + new StringCharacterIterator("value", 2, 1, 0); + fail("no illegal argument: start greater than end"); + } catch (IllegalArgumentException e) { + // Expected + } + + try { + new StringCharacterIterator("value", 2, 1, 2); + fail("no illegal argument: start greater than end"); + } catch (IllegalArgumentException e) { + // Expected + } + + try { + new StringCharacterIterator("value", 2, 4, 1); + fail("no illegal argument: location greater than start"); + } catch (IllegalArgumentException e) { + // Expected + } + + try { + new StringCharacterIterator("value", 0, 2, 3); + fail("no illegal argument: location greater than start"); + } catch (IllegalArgumentException e) { + // Expected + } + } + + /** + * @tests java.text.StringCharacterIterator.equals(Object) + */ + public void test_equalsLjava_lang_Object() { + StringCharacterIterator sci0 = new StringCharacterIterator("fixture"); + assertEquals(sci0, sci0); + assertFalse(sci0.equals(null)); + assertFalse(sci0.equals("fixture")); + + StringCharacterIterator sci1 = new StringCharacterIterator("fixture"); + assertEquals(sci0, sci1); + + sci1.next(); + assertFalse(sci0.equals(sci1)); + sci0.next(); + assertEquals(sci0, sci1); + + StringCharacterIterator it1 = new StringCharacterIterator("testing", 2, + 6, 4); + StringCharacterIterator it2 = new StringCharacterIterator("xxstinx", 2, + 6, 4); + assertTrue("Range is equal", !it1.equals(it2)); + StringCharacterIterator it3 = new StringCharacterIterator("testing", 2, + 6, 2); + it3.setIndex(4); + assertTrue("Not equal", it1.equals(it3)); + } + + /** + * @tests java.text.StringCharacterIterator.clone() + */ + public void test_clone() { + StringCharacterIterator sci0 = new StringCharacterIterator("fixture"); + assertSame(sci0, sci0); + StringCharacterIterator sci1 = (StringCharacterIterator) sci0.clone(); + assertNotSame(sci0, sci1); + assertEquals(sci0, sci1); + + StringCharacterIterator it = new StringCharacterIterator("testing", 2, + 6, 4); + StringCharacterIterator clone = (StringCharacterIterator) it.clone(); + assertTrue("Clone not equal", it.equals(clone)); + } + + /** + * @tests java.text.StringCharacterIterator.current() + */ + public void test_current() { + StringCharacterIterator fixture = new StringCharacterIterator("fixture"); + assertEquals('f', fixture.current()); + fixture.next(); + assertEquals('i', fixture.current()); + + StringCharacterIterator it = new StringCharacterIterator("testing", 2, + 6, 4); + assertEquals("Wrong current char", 'i', it.current()); + } + + /** + * @tests java.text.StringCharacterIterator.first() + */ + public void test_first() { + StringCharacterIterator fixture = new StringCharacterIterator("fixture"); + assertEquals('f', fixture.first()); + fixture.next(); + assertEquals('f', fixture.first()); + fixture = new StringCharacterIterator("fixture", 1); + assertEquals('f', fixture.first()); + fixture = new StringCharacterIterator("fixture", 1, "fixture".length(), + 2); + assertEquals('i', fixture.first()); + + StringCharacterIterator it1 = new StringCharacterIterator("testing", 2, + 6, 4); + assertEquals("Wrong first char", 's', it1.first()); + assertEquals("Wrong next char", 't', it1.next()); + it1 = new StringCharacterIterator("testing", 2, 2, 2); + assertTrue("Not DONE", it1.first() == CharacterIterator.DONE); + } + + /** + * @tests java.text.StringCharacterIterator.getBeginIndex() + */ + public void test_getBeginIndex() { + StringCharacterIterator fixture = new StringCharacterIterator("fixture"); + assertEquals(0, fixture.getBeginIndex()); + fixture = new StringCharacterIterator("fixture", 1); + assertEquals(0, fixture.getBeginIndex()); + fixture = new StringCharacterIterator("fixture", 1, "fixture".length(), + 2); + assertEquals(1, fixture.getBeginIndex()); + + StringCharacterIterator it1 = new StringCharacterIterator("testing", 2, + 6, 4); + assertEquals("Wrong begin index 2", 2, it1.getBeginIndex()); + } + + /** + * @tests java.text.StringCharacterIterator.getEndIndex() + */ + public void test_getEndIndex() { + StringCharacterIterator fixture = new StringCharacterIterator("fixture"); + assertEquals("fixture".length(), fixture.getEndIndex()); + fixture = new StringCharacterIterator("fixture", 1); + assertEquals("fixture".length(), fixture.getEndIndex()); + fixture = new StringCharacterIterator("fixture", 1, "fixture".length(), + 2); + assertEquals("fixture".length(), fixture.getEndIndex()); + fixture = new StringCharacterIterator("fixture", 1, 4, 2); + assertEquals(4, fixture.getEndIndex()); + + StringCharacterIterator it1 = new StringCharacterIterator("testing", 2, + 6, 4); + assertEquals("Wrong end index 6", 6, it1.getEndIndex()); + } + + /** + * @tests java.text.StringCharacterIterator.getIndex() + */ + public void testGetIndex() { + StringCharacterIterator fixture = new StringCharacterIterator("fixture"); + assertEquals(0, fixture.getIndex()); + fixture = new StringCharacterIterator("fixture", 1); + assertEquals(1, fixture.getIndex()); + fixture = new StringCharacterIterator("fixture", 1, "fixture".length(), + 2); + assertEquals(2, fixture.getIndex()); + fixture = new StringCharacterIterator("fixture", 1, 4, 2); + assertEquals(2, fixture.getIndex()); + } + + /** + * @tests java.text.StringCharacterIterator.last() + */ + public void testLast() { + StringCharacterIterator fixture = new StringCharacterIterator("fixture"); + assertEquals('e', fixture.last()); + fixture.next(); + assertEquals('e', fixture.last()); + fixture = new StringCharacterIterator("fixture", 1); + assertEquals('e', fixture.last()); + fixture = new StringCharacterIterator("fixture", 1, "fixture".length(), + 2); + assertEquals('e', fixture.last()); + fixture = new StringCharacterIterator("fixture", 1, 4, 2); + assertEquals('t', fixture.last()); + } + + /** + * @tests java.text.StringCharacterIterator.next() + */ + public void test_next() { + StringCharacterIterator fixture = new StringCharacterIterator("fixture"); + assertEquals(0, fixture.getIndex()); + assertEquals('i', fixture.next()); + assertEquals(1, fixture.getIndex()); + assertEquals('x', fixture.next()); + assertEquals(2, fixture.getIndex()); + assertEquals('t', fixture.next()); + assertEquals(3, fixture.getIndex()); + assertEquals('u', fixture.next()); + assertEquals(4, fixture.getIndex()); + assertEquals('r', fixture.next()); + assertEquals(5, fixture.getIndex()); + assertEquals('e', fixture.next()); + assertEquals(6, fixture.getIndex()); + assertEquals(CharacterIterator.DONE, fixture.next()); + assertEquals(7, fixture.getIndex()); + assertEquals(CharacterIterator.DONE, fixture.next()); + assertEquals(7, fixture.getIndex()); + assertEquals(CharacterIterator.DONE, fixture.next()); + assertEquals(7, fixture.getIndex()); + + StringCharacterIterator it1 = new StringCharacterIterator("testing", 2, + 6, 3); + char result = it1.next(); + assertEquals("Wrong next char1", 'i', result); + assertEquals("Wrong next char2", 'n', it1.next()); + assertTrue("Wrong next char3", it1.next() == CharacterIterator.DONE); + assertTrue("Wrong next char4", it1.next() == CharacterIterator.DONE); + int index = it1.getIndex(); + assertEquals("Wrong index", 6, index); + assertTrue("Wrong current char", + it1.current() == CharacterIterator.DONE); + } + + /** + * @tests java.text.StringCharacterIterator.previous() + */ + public void test_previous() { + StringCharacterIterator fixture = new StringCharacterIterator("fixture"); + assertEquals(CharacterIterator.DONE, fixture.previous()); + assertEquals('i', fixture.next()); + assertEquals('x', fixture.next()); + assertEquals('t', fixture.next()); + assertEquals('u', fixture.next()); + assertEquals('r', fixture.next()); + assertEquals('e', fixture.next()); + assertEquals(CharacterIterator.DONE, fixture.next()); + assertEquals(CharacterIterator.DONE, fixture.next()); + assertEquals(CharacterIterator.DONE, fixture.next()); + assertEquals(7, fixture.getIndex()); + assertEquals('e', fixture.previous()); + assertEquals(6, fixture.getIndex()); + assertEquals('r', fixture.previous()); + assertEquals(5, fixture.getIndex()); + assertEquals('u', fixture.previous()); + assertEquals(4, fixture.getIndex()); + assertEquals('t', fixture.previous()); + assertEquals(3, fixture.getIndex()); + assertEquals('x', fixture.previous()); + assertEquals(2, fixture.getIndex()); + assertEquals('i', fixture.previous()); + assertEquals(1, fixture.getIndex()); + assertEquals('f', fixture.previous()); + assertEquals(0, fixture.getIndex()); + assertEquals(CharacterIterator.DONE, fixture.previous()); + assertEquals(0, fixture.getIndex()); + + StringCharacterIterator it1 = new StringCharacterIterator("testing", 2, + 6, 4); + assertEquals("Wrong previous char1", 't', it1.previous()); + assertEquals("Wrong previous char2", 's', it1.previous()); + assertTrue("Wrong previous char3", + it1.previous() == CharacterIterator.DONE); + assertTrue("Wrong previous char4", + it1.previous() == CharacterIterator.DONE); + assertEquals("Wrong index", 2, it1.getIndex()); + assertEquals("Wrong current char", 's', it1.current()); + } + + /** + * @tests java.text.StringCharacterIterator.setIndex(int) + */ + public void test_setIndex() { + StringCharacterIterator fixture = new StringCharacterIterator("fixture"); + while (fixture.next() != CharacterIterator.DONE) { + // empty + } + assertEquals("fixture".length(), fixture.getIndex()); + fixture.setIndex(0); + assertEquals(0, fixture.getIndex()); + assertEquals('f', fixture.current()); + fixture.setIndex("fixture".length() - 1); + assertEquals('e', fixture.current()); + try { + fixture.setIndex(-1); + fail("no illegal argument"); + } catch (IllegalArgumentException e) { + // expected + } + + try { + fixture.setIndex("fixture".length() + 1); + fail("no illegal argument"); + } catch (IllegalArgumentException e) { + // expected + } + } + + /** + * @tests java.text.StringCharacterIterator.setText(String) + */ + public void test_setText() { + StringCharacterIterator fixture = new StringCharacterIterator("fixture"); + fixture.setText("fix"); + assertEquals('f', fixture.current()); + assertEquals('x', fixture.last()); + + try { + fixture.setText(null); + fail("no null pointer"); + } catch (NullPointerException e) { + // expected + } + } + + /** + * @tests java.text.StringCharacterIterator#StringCharacterIterator(java.lang.String) + */ + public void test_ConstructorLjava_lang_String() { + assertNotNull(new StringCharacterIterator("value")); + assertNotNull(new StringCharacterIterator("")); + try { + new StringCharacterIterator(null); + fail("Assert 0: no null pointer"); + } catch (NullPointerException e) { + // expected + } + + StringCharacterIterator it = new StringCharacterIterator("testing"); + assertEquals("Wrong begin index", 0, it.getBeginIndex()); + assertEquals("Wrong end index", 7, it.getEndIndex()); + assertEquals("Wrong current index", 0, it.getIndex()); + assertEquals("Wrong current char", 't', it.current()); + assertEquals("Wrong next char", 'e', it.next()); + } + + /** + * @tests java.text.StringCharacterIterator#StringCharacterIterator(java.lang.String, + * int) + */ + public void test_ConstructorLjava_lang_StringI() { + StringCharacterIterator it = new StringCharacterIterator("testing", 3); + assertEquals("Wrong begin index", 0, it.getBeginIndex()); + assertEquals("Wrong end index", 7, it.getEndIndex()); + assertEquals("Wrong current index", 3, it.getIndex()); + assertEquals("Wrong current char", 't', it.current()); + assertEquals("Wrong next char", 'i', it.next()); + } + + /** + * @tests java.text.StringCharacterIterator#StringCharacterIterator(java.lang.String, + * int, int, int) + */ + public void test_ConstructorLjava_lang_StringIII() { + StringCharacterIterator it = new StringCharacterIterator("testing", 2, + 6, 4); + assertEquals("Wrong begin index", 2, it.getBeginIndex()); + assertEquals("Wrong end index", 6, it.getEndIndex()); + assertEquals("Wrong current index", 4, it.getIndex()); + assertEquals("Wrong current char", 'i', it.current()); + assertEquals("Wrong next char", 'n', it.next()); + } + + /** + * @tests java.text.StringCharacterIterator#getIndex() + */ + public void test_getIndex() { + StringCharacterIterator it1 = new StringCharacterIterator("testing", 2, + 6, 4); + assertEquals("Wrong index 4", 4, it1.getIndex()); + it1.next(); + assertEquals("Wrong index 5", 5, it1.getIndex()); + it1.last(); + assertEquals("Wrong index 4/2", 5, it1.getIndex()); + } + + /** + * @tests java.text.StringCharacterIterator#hashCode() + */ + public void test_hashCode() { + StringCharacterIterator it1 = new StringCharacterIterator("testing", 2, + 6, 4); + StringCharacterIterator it2 = new StringCharacterIterator("xxstinx", 2, + 6, 4); + assertTrue("Hash is equal", it1.hashCode() != it2.hashCode()); + StringCharacterIterator it3 = new StringCharacterIterator("testing", 2, + 6, 2); + assertTrue("Hash equal1", it1.hashCode() != it3.hashCode()); + it3 = new StringCharacterIterator("testing", 0, 6, 4); + assertTrue("Hash equal2", it1.hashCode() != it3.hashCode()); + it3 = new StringCharacterIterator("testing", 2, 5, 4); + assertTrue("Hash equal3", it1.hashCode() != it3.hashCode()); + it3 = new StringCharacterIterator("froging", 2, 6, 4); + assertTrue("Hash equal4", it1.hashCode() != it3.hashCode()); + + StringCharacterIterator sci0 = new StringCharacterIterator("fixture"); + assertEquals(sci0.hashCode(), sci0.hashCode()); + + StringCharacterIterator sci1 = new StringCharacterIterator("fixture"); + assertEquals(sci0.hashCode(), sci1.hashCode()); + + sci1.next(); + sci0.next(); + assertEquals(sci0.hashCode(), sci1.hashCode()); + } + + /** + * @tests java.text.StringCharacterIterator#last() + */ + public void test_last() { + StringCharacterIterator it1 = new StringCharacterIterator("testing", 2, + 6, 3); + assertEquals("Wrong last char", 'n', it1.last()); + assertEquals("Wrong previous char", 'i', it1.previous()); + it1 = new StringCharacterIterator("testing", 2, 2, 2); + assertTrue("Not DONE", it1.last() == CharacterIterator.DONE); + } + + /** + * @tests java.text.StringCharacterIterator#setIndex(int) + */ + public void test_setIndexI() { + StringCharacterIterator it1 = new StringCharacterIterator("testing", 2, + 6, 4); + assertEquals("Wrong result1", 's', it1.setIndex(2)); + char result = it1.next(); + assertTrue("Wrong next char: " + result, result == 't'); + assertTrue("Wrong result2", it1.setIndex(6) == CharacterIterator.DONE); + assertEquals("Wrong previous char", 'n', it1.previous()); + } + + /** + * @tests java.text.StringCharacterIterator#setText(java.lang.String) + */ + public void test_setTextLjava_lang_String() { + StringCharacterIterator it1 = new StringCharacterIterator("testing", 2, + 6, 4); + it1.setText("frog"); + assertEquals("Wrong begin index", 0, it1.getBeginIndex()); + assertEquals("Wrong end index", 4, it1.getEndIndex()); + assertEquals("Wrong current index", 0, it1.getIndex()); + } +} diff --git a/text/src/test/java/tests/text/AllTests.java b/text/src/test/java/tests/text/AllTests.java new file mode 100644 index 0000000..7162c4d --- /dev/null +++ b/text/src/test/java/tests/text/AllTests.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tests.text; + +import junit.framework.Test; +import junit.framework.TestSuite; + +/** + * Test suite that includes all tests for the Text project. + * + */ +public class AllTests { + + public static void main(String[] args) { + junit.textui.TestRunner.run(AllTests.suite()); + } + + public static Test suite() { + TestSuite suite = new TestSuite("All Text test suites"); + // $JUnit-BEGIN$ + suite.addTest(org.apache.harmony.text.tests.java.text.AllTests.suite()); + // $JUnit-END$ + return suite; + } +} diff --git a/text/src/test/resources/serialization/java/text/DecimalFormat.ser b/text/src/test/resources/serialization/java/text/DecimalFormat.ser Binary files differnew file mode 100644 index 0000000..c20fa78 --- /dev/null +++ b/text/src/test/resources/serialization/java/text/DecimalFormat.ser diff --git a/text/src/test/resources/serialization/java/text/DecimalFormatSymbols.ser b/text/src/test/resources/serialization/java/text/DecimalFormatSymbols.ser Binary files differnew file mode 100644 index 0000000..6e086af --- /dev/null +++ b/text/src/test/resources/serialization/java/text/DecimalFormatSymbols.ser |