diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/impl/unicode/CLDRHelper.java b/teavm-classlib/src/main/java/org/teavm/classlib/impl/unicode/CLDRHelper.java index c68a9d690..1d6fef75a 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/impl/unicode/CLDRHelper.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/impl/unicode/CLDRHelper.java @@ -39,4 +39,20 @@ public class CLDRHelper { @GeneratedBy(CLDRHelperNativeGenerator.class) @PluggableDependency(CLDRHelperNativeGenerator.class) private static native String getLikelySubtagsImpl(String localeCode); + + public static String[] resolveEras(String localeCode) { + readErasFromCLDR(); + String[] eras = getEras(localeCode); + if (eras == null) { + eras = getEras("root"); + } + return eras; + } + + // Defined by JCLPlugin + private static native void readErasFromCLDR(); + + @GeneratedBy(CLDRHelperNativeGenerator.class) + @PluggableDependency(CLDRHelperNativeGenerator.class) + private static native String[] getEras(String localeCode); } diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/impl/unicode/CLDRHelperNativeGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlib/impl/unicode/CLDRHelperNativeGenerator.java index e91a15a95..7254e7059 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/impl/unicode/CLDRHelperNativeGenerator.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/impl/unicode/CLDRHelperNativeGenerator.java @@ -35,6 +35,10 @@ public class CLDRHelperNativeGenerator implements Generator, DependencyPlugin { case "getLikelySubtagsImpl": method.getResult().propagate("java.lang.String"); break; + case "getEras": + method.getResult().propagate("[java.lang.String;"); + method.getResult().getArrayItem().propagate("java.lang.String"); + break; } } @@ -46,6 +50,23 @@ public class CLDRHelperNativeGenerator implements Generator, DependencyPlugin { .append(context.getParameterName(1)).append(")];").softNewLine(); writer.append("return data ? $rt_str(data) : null;").softNewLine(); break; + case "getEras": + generateGetArray(context, writer, "eras"); + break; } } + + private void generateGetArray(GeneratorContext context, SourceWriter writer, String name) throws IOException { + writer.append("var data = ").appendClass("java.util.Locale").append(".$CLDR.").append(name) + .append("[$rt_ustr(").append(context.getParameterName(1)).append(")];").softNewLine(); + writer.append("if (!data) {").indent().softNewLine(); + writer.append("return null;"); + writer.outdent().append("}").softNewLine(); + writer.append("var result = $rt_createArray(").appendClass("java.lang.String)") + .append(", data.length);").softNewLine(); + writer.append("for (var i = 0; i < data.length; ++i) {").indent().softNewLine(); + writer.append("result.data[i] = $rt_str(data[i])").softNewLine(); + writer.outdent().append("}").softNewLine(); + writer.append("return result;").softNewLine(); + } } diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/text/Annotation.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/text/Annotation.java new file mode 100644 index 000000000..98013ad47 --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/text/Annotation.java @@ -0,0 +1,35 @@ +/* + * 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.teavm.classlib.java.text; + +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 + ']'; + } +} diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/text/AttributedCharacterIterator.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/text/AttributedCharacterIterator.java new file mode 100644 index 000000000..6c6096e9c --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/text/AttributedCharacterIterator.java @@ -0,0 +1,144 @@ +/* + * 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.teavm.classlib.java.text; + +import org.teavm.classlib.java.io.TSerializable; +import org.teavm.classlib.java.util.TMap; +import org.teavm.classlib.java.util.TSet; + +public interface AttributedCharacterIterator extends CharacterIterator { + + public static class Attribute implements TSerializable { + public static final Attribute INPUT_METHOD_SEGMENT = new Attribute( + "input_method_segment"); + + public static final Attribute LANGUAGE = new Attribute("language"); + + public static final Attribute READING = new Attribute("reading"); + + private String name; + + protected Attribute(String name) { + this.name = name; + } + + @Override + public final boolean equals(Object object) { + return this == object; + } + + protected String getName() { + return name; + } + + @Override + public final int hashCode() { + return super.hashCode(); + } + + @Override + public String toString() { + return getClass().getName() + '(' + getName() + ')'; + } + } + + /** + * Returns a set of attributes present in the {@code + * AttributedCharacterIterator}. An empty set is returned if no attributes + * were defined. + * + * @return a set of attribute keys; may be empty. + */ + public TSet getAllAttributeKeys(); + + /** + * Returns the value stored in the attribute for the current character. If + * the attribute was not defined then {@code null} is returned. + * + * @param attribute the attribute for which the value should be returned. + * @return the value of the requested attribute for the current character or + * {@code null} if it was not defined. + */ + public Object getAttribute(Attribute attribute); + + /** + * Returns a map of all attributes of the current character. If no + * attributes were defined for the current character then an empty map is + * returned. + * + * @return a map of all attributes for the current character or an empty + * map. + */ + public TMap getAttributes(); + + /** + * Returns the index of the last character in the run having the same + * attributes as the current character. + * + * @return the index of the last character of the current run. + */ + public int getRunLimit(); + + /** + * Returns the index of the last character in the run that has the same + * attribute value for the given attribute as the current character. + * + * @param attribute + * the attribute which the run is based on. + * @return the index of the last character of the current run. + */ + public int getRunLimit(Attribute attribute); + + /** + * Returns the index of the last character in the run that has the same + * attribute values for the attributes in the set as the current character. + * + * @param attributes + * the set of attributes which the run is based on. + * @return the index of the last character of the current run. + */ + public int getRunLimit(TSet attributes); + + /** + * Returns the index of the first character in the run that has the same + * attributes as the current character. + * + * @return the index of the last character of the current run. + */ + public int getRunStart(); + + /** + * Returns the index of the first character in the run that has the same + * attribute value for the given attribute as the current character. + * + * @param attribute + * the attribute which the run is based on. + * @return the index of the last character of the current run. + */ + public int getRunStart(Attribute attribute); + + /** + * Returns the index of the first character in the run that has the same + * attribute values for the attributes in the set as the current character. + * + * @param attributes + * the set of attributes which the run is based on. + * @return the index of the last character of the current run. + */ + public int getRunStart(TSet attributes); +} diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/text/AttributedString.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/text/AttributedString.java new file mode 100644 index 000000000..3b9e6f7a4 --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/text/AttributedString.java @@ -0,0 +1,630 @@ +/* + * 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.teavm.classlib.java.text; + +import org.teavm.classlib.java.text.AttributedCharacterIterator.Attribute; +import org.teavm.classlib.java.util.*; + +public class AttributedString { + + String text; + + TMap> 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 THashSet 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) { + THashSet set = new THashSet<>((attributes.length * 4 / 3) + 1); + for (int i = attributes.length; --i >= 0;) { + set.add(attributes[i]); + } + attributesAllowed = set; + } + } + + @Override + @SuppressWarnings("unchecked") + public Object clone() { + try { + AttributedIterator clone = (AttributedIterator) super.clone(); + if (attributesAllowed != null) { + clone.attributesAllowed = (THashSet) attributesAllowed.clone(); + } + return clone; + } catch (CloneNotSupportedException e) { + return null; + } + } + + @Override + public char current() { + if (offset == end) { + return DONE; + } + return attrString.text.charAt(offset); + } + + @Override + public char first() { + if (begin == end) { + return DONE; + } + offset = begin; + return attrString.text.charAt(offset); + } + + @Override + public int getBeginIndex() { + return begin; + } + + @Override + public int getEndIndex() { + return end; + } + + @Override + 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(TList ranges) { + TIterator 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; + } + + @Override + public TSet getAllAttributeKeys() { + if (begin == 0 && end == attrString.text.length() && attributesAllowed == null) { + return attrString.attributeMap.keySet(); + } + + TSet result = new THashSet<>((attrString.attributeMap.size() * 4 / 3) + 1); + TIterator>> it = attrString.attributeMap.entrySet().iterator(); + while (it.hasNext()) { + TMap.Entry> entry = it.next(); + if (attributesAllowed == null || attributesAllowed.contains(entry.getKey())) { + TList ranges = entry.getValue(); + if (inRange(ranges)) { + result.add(entry.getKey()); + } + } + } + return result; + } + + private Object currentValue(TList ranges) { + TIterator 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; + } + + @Override + public Object getAttribute(AttributedCharacterIterator.Attribute attribute) { + if (attributesAllowed != null && !attributesAllowed.contains(attribute)) { + return null; + } + TArrayList ranges = (TArrayList) attrString.attributeMap.get(attribute); + if (ranges == null) { + return null; + } + return currentValue(ranges); + } + + @Override + public TMap getAttributes() { + TMap result = new THashMap<>((attrString.attributeMap.size() * 4 / 3) + 1); + TIterator>> it = attrString.attributeMap.entrySet().iterator(); + while (it.hasNext()) { + TMap.Entry> 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; + } + + @Override + public int getRunLimit() { + return getRunLimit(getAllAttributeKeys()); + } + + private int runLimit(TList ranges) { + int result = end; + TListIterator 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; + } + + @Override + public int getRunLimit(AttributedCharacterIterator.Attribute attribute) { + if (attributesAllowed != null && !attributesAllowed.contains(attribute)) { + return end; + } + TArrayList ranges = (TArrayList) attrString.attributeMap.get(attribute); + if (ranges == null) { + return end; + } + return runLimit(ranges); + } + + @Override + public int getRunLimit(TSet attributes) { + int limit = end; + TIterator it = attributes.iterator(); + while (it.hasNext()) { + AttributedCharacterIterator.Attribute attribute = it.next(); + int newLimit = getRunLimit(attribute); + if (newLimit < limit) { + limit = newLimit; + } + } + return limit; + } + + @Override + public int getRunStart() { + return getRunStart(getAllAttributeKeys()); + } + + private int runStart(TList ranges) { + int result = begin; + TIterator 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; + } + + @Override + public int getRunStart(AttributedCharacterIterator.Attribute attribute) { + if (attributesAllowed != null && !attributesAllowed.contains(attribute)) { + return begin; + } + TArrayList ranges = (TArrayList) attrString.attributeMap.get(attribute); + if (ranges == null) { + return begin; + } + return runStart(ranges); + } + + @Override + public int getRunStart(TSet attributes) { + int start = begin; + TIterator it = attributes.iterator(); + while (it.hasNext()) { + AttributedCharacterIterator.Attribute attribute = it.next(); + int newStart = getRunStart(attribute); + if (newStart > start) { + start = newStart; + } + } + return start; + } + + @Override + public char last() { + if (begin == end) { + return DONE; + } + offset = end - 1; + return attrString.text.charAt(offset); + } + + @Override + public char next() { + if (offset >= (end - 1)) { + offset = end; + return DONE; + } + return attrString.text.charAt(++offset); + } + + @Override + public char previous() { + if (offset == begin) { + return DONE; + } + return attrString.text.charAt(--offset); + } + + @Override + 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()) { + throw new IllegalArgumentException("Invalid substring range"); + } + StringBuilder buffer = new StringBuilder(); + for (int i = iterator.getBeginIndex(); i < iterator.getEndIndex(); i++) { + buffer.append(iterator.current()); + iterator.next(); + } + text = buffer.toString(); + TSet attributes = iterator.getAllAttributeKeys(); + if (attributes == null) { + return; + } + attributeMap = new THashMap<>((attributes.size() * 4 / 3) + 1); + + TIterator 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, TSet attributes) { + if (start < iterator.getBeginIndex() || end > iterator.getEndIndex() || start > end) { + throw new IllegalArgumentException(); + } + + if (attributes == null) { + return; + } + + StringBuilder buffer = new StringBuilder(); + iterator.setIndex(start); + while (iterator.getIndex() < end) { + buffer.append(iterator.current()); + iterator.next(); + } + text = buffer.toString(); + attributeMap = new THashMap<>((attributes.size() * 4 / 3) + 1); + + TIterator 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) { + this(iterator, start, end, new THashSet<>(TArrays.asList(attributes))); + } + + public AttributedString(String value) { + if (value == null) { + throw new NullPointerException(); + } + text = value; + attributeMap = new THashMap<>(11); + } + + public AttributedString(String value, TMap attributes) { + if (value == null) { + throw new NullPointerException(); + } + if (value.length() == 0 && !attributes.isEmpty()) { + throw new IllegalArgumentException("Cannot add attributes to empty string"); + } + text = value; + attributeMap = new THashMap<>((attributes.size() * 4 / 3) + 1); + TIterator it = attributes.entrySet().iterator(); + while (it.hasNext()) { + TMap.Entry entry = (TMap.Entry) it.next(); + TArrayList ranges = new TArrayList<>(1); + ranges.add(new Range(0, text.length(), entry.getValue())); + attributeMap.put((AttributedCharacterIterator.Attribute) entry.getKey(), ranges); + } + } + + /** + * Applies a given attribute to this string. + * + * @param attribute + * the attribute that will be applied to this string. + * @param value + * the value of the attribute that will be applied to this + * string. + * @throws IllegalArgumentException + * if the length of this attributed string is 0. + * @throws NullPointerException + * if {@code attribute} is {@code null}. + */ + public void addAttribute(AttributedCharacterIterator.Attribute attribute, Object value) { + if (null == attribute) { + throw new NullPointerException(); + } + if (text.length() == 0) { + throw new IllegalArgumentException(); + } + + TList ranges = attributeMap.get(attribute); + if (ranges == null) { + ranges = new TArrayList<>(1); + attributeMap.put(attribute, ranges); + } else { + ranges.clear(); + } + ranges.add(new Range(0, text.length(), value)); + } + + /** + * Applies a given attribute to the given range of this string. + * + * @param attribute + * the attribute that will be applied to this string. + * @param value + * the value of the attribute that will be applied to this + * string. + * @param start + * the start of the range where the attribute will be applied. + * @param end + * the end of the range where the attribute will be applied. + * @throws IllegalArgumentException + * if {@code start < 0}, {@code end} is greater than the length + * of this string, or if {@code start >= end}. + * @throws NullPointerException + * if {@code attribute} is {@code null}. + */ + 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; + } + + TList ranges = attributeMap.get(attribute); + if (ranges == null) { + ranges = new TArrayList<>(1); + ranges.add(new Range(start, end, value)); + attributeMap.put(attribute, ranges); + return; + } + TListIterator 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.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.equals(range.value))) { + it.remove(); + r3 = new Range(end, range.end, range.value); + break; + } + } else { + it.remove(); + } + } + + if (value.equals(r1.value)) { + if (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.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)); + } + + /** + * Applies a given set of attributes to the given range of the string. + * + * @param attributes + * the set of attributes that will be applied to this string. + * @param start + * the start of the range where the attribute will be applied. + * @param end + * the end of the range where the attribute will be applied. + * @throws IllegalArgumentException + * if {@code start < 0}, {@code end} is greater than the length + * of this string, or if {@code start >= end}. + */ + public void addAttributes(TMap attributes, int start, int end) { + TIterator it = attributes.entrySet().iterator(); + while (it.hasNext()) { + TMap.Entry entry = (TMap.Entry) it.next(); + addAttribute((AttributedCharacterIterator.Attribute) entry.getKey(), entry.getValue(), start, end); + } + } + + /** + * Returns an {@code AttributedCharacterIterator} that gives access to the + * complete content of this attributed string. + * + * @return the newly created {@code AttributedCharacterIterator}. + */ + public AttributedCharacterIterator getIterator() { + return new AttributedIterator(this); + } + + /** + * Returns an {@code AttributedCharacterIterator} that gives access to the + * complete content of this attributed string. Only attributes contained in + * {@code attributes} are available from this iterator if they are defined + * for this text. + * + * @param attributes + * the array containing attributes that will be in the new + * iterator if they are defined for this text. + * @return the newly created {@code AttributedCharacterIterator}. + */ + public AttributedCharacterIterator getIterator(AttributedCharacterIterator.Attribute[] attributes) { + return new AttributedIterator(this, attributes, 0, text.length()); + } + + /** + * Returns an {@code AttributedCharacterIterator} that gives access to the + * contents of this attributed string starting at index {@code start} up to + * index {@code end}. Only attributes contained in {@code attributes} are + * available from this iterator if they are defined for this text. + * + * @param attributes + * the array containing attributes that will be in the new + * iterator if they are defined for this text. + * @param start + * the start index of the iterator on the underlying text. + * @param end + * the end index of the iterator on the underlying text. + * @return the newly created {@code AttributedCharacterIterator}. + */ + public AttributedCharacterIterator getIterator(AttributedCharacterIterator.Attribute[] attributes, int start, + int end) { + return new AttributedIterator(this, attributes, start, end); + } +} diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/text/CharacterIterator.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/text/CharacterIterator.java new file mode 100644 index 000000000..c3f0e6434 --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/text/CharacterIterator.java @@ -0,0 +1,42 @@ +/* + * 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.teavm.classlib.java.text; + +public interface CharacterIterator extends Cloneable { + public static final char DONE = '\uffff'; + + public Object clone(); + + public char current(); + + public char first(); + + public int getBeginIndex(); + + public int getEndIndex(); + + public int getIndex(); + + public char last(); + + public char next(); + + public char previous(); + + public char setIndex(int location); +} diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/text/DateFormat.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/text/DateFormat.java new file mode 100644 index 000000000..ed9bb38fd --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/text/DateFormat.java @@ -0,0 +1,283 @@ +/* + * 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.teavm.classlib.java.text; + +import org.teavm.classlib.java.util.*; + +public abstract class DateFormat extends Format { + protected TCalendar calendar; + //protected NumberFormat numberFormat; + public final static int DEFAULT = 2; + public final static int FULL = 0; + public final static int LONG = 1; + public final static int MEDIUM = 2; + public final static int SHORT = 3; + public final static int ERA_FIELD = 0; + public final static int YEAR_FIELD = 1; + public final static int MONTH_FIELD = 2; + public final static int DATE_FIELD = 3; + public final static int HOUR_OF_DAY1_FIELD = 4; + public final static int HOUR_OF_DAY0_FIELD = 5; + public final static int MINUTE_FIELD = 6; + public final static int SECOND_FIELD = 7; + public final static int MILLISECOND_FIELD = 8; + public final static int DAY_OF_WEEK_FIELD = 9; + public final static int DAY_OF_YEAR_FIELD = 10; + public final static int DAY_OF_WEEK_IN_MONTH_FIELD = 11; + public final static int WEEK_OF_YEAR_FIELD = 12; + public final static int WEEK_OF_MONTH_FIELD = 13; + public final static int AM_PM_FIELD = 14; + public final static int HOUR1_FIELD = 15; + public final static int HOUR0_FIELD = 16; + public final static int TIMEZONE_FIELD = 17; + + protected DateFormat() { + } + + @Override + public Object clone() { + DateFormat clone = (DateFormat) super.clone(); + clone.calendar = (TCalendar) calendar.clone(); + // TODO: implement + //clone.numberFormat = (NumberFormat) numberFormat.clone(); + return clone; + } + + @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.getFirstDayOfWeek() == dateFormat.calendar.getFirstDayOfWeek() && + calendar.getMinimalDaysInFirstWeek() == dateFormat.calendar.getMinimalDaysInFirstWeek() && + calendar.isLenient() == dateFormat.calendar.isLenient();*/ + // TODO: implement + return false; + } + + @Override + public final StringBuffer format(Object object, StringBuffer buffer, FieldPosition field) { + if (object instanceof TDate) { + return format((TDate) object, buffer, field); + } + if (object instanceof Number) { + return format(new TDate(((Number) object).longValue()), buffer, field); + } + throw new IllegalArgumentException(); + } + + public final String format(TDate date) { + return format(date, new StringBuffer(), new FieldPosition(0)).toString(); + } + + public abstract StringBuffer format(TDate date, StringBuffer buffer, FieldPosition field); + + public static TLocale[] getAvailableLocales() { + return TLocale.getAvailableLocales(); + } + + public TCalendar getCalendar() { + return calendar; + } + + public final static DateFormat getDateInstance() { + return getDateInstance(DEFAULT); + } + + public final static DateFormat getDateInstance(int style) { + checkDateStyle(style); + return getDateInstance(style, TLocale.getDefault()); + } + + public final static DateFormat getDateInstance(int style, TLocale locale) { + /*checkDateStyle(style); + com.ibm.icu.text.DateFormat icuFormat = com.ibm.icu.text.DateFormat.getDateInstance(style, locale); + return new SimpleDateFormat(locale, (com.ibm.icu.text.SimpleDateFormat) icuFormat);*/ + // TODO: implement + return null; + } + + public final static DateFormat getDateTimeInstance() { + return getDateTimeInstance(DEFAULT, DEFAULT); + } + + public final static DateFormat getDateTimeInstance(int dateStyle, int timeStyle) { + checkTimeStyle(timeStyle); + checkDateStyle(dateStyle); + return getDateTimeInstance(dateStyle, timeStyle, TLocale.getDefault()); + } + + public final static DateFormat getDateTimeInstance(int dateStyle, int timeStyle, TLocale locale) { + /*checkTimeStyle(timeStyle); + checkDateStyle(dateStyle); + com.ibm.icu.text.DateFormat icuFormat = com.ibm.icu.text.DateFormat.getDateTimeInstance(dateStyle, timeStyle, + locale); + return new SimpleDateFormat(locale, (com.ibm.icu.text.SimpleDateFormat) icuFormat);*/ + // TODO: implement + return null; + } + + public final static DateFormat getInstance() { + return getDateTimeInstance(SHORT, SHORT); + } + + /*public NumberFormat getNumberFormat() { + return numberFormat; + }*/ + + static String getStyleName(int style) { + String styleName; + switch (style) { + case SHORT: + styleName = "SHORT"; + break; + case MEDIUM: + styleName = "MEDIUM"; + break; + case LONG: + styleName = "LONG"; + break; + case FULL: + styleName = "FULL"; + break; + default: + styleName = ""; + } + return styleName; + } + + public final static DateFormat getTimeInstance() { + return getTimeInstance(DEFAULT); + } + + public final static DateFormat getTimeInstance(int style) { + checkTimeStyle(style); + return getTimeInstance(style, TLocale.getDefault()); + } + + public final static DateFormat getTimeInstance(int style, TLocale locale) { + /*checkTimeStyle(style); + com.ibm.icu.text.DateFormat icuFormat = com.ibm.icu.text.DateFormat.getTimeInstance(style, locale); + return new SimpleDateFormat(locale, (com.ibm.icu.text.SimpleDateFormat) icuFormat);*/ + // TODO: implement + return null; + } + + + @Override + public int hashCode() { + /*return calendar.getFirstDayOfWeek() + calendar.getMinimalDaysInFirstWeek() + + (calendar.isLenient() ? 1231 : 1237) + numberFormat.hashCode();*/ + // TODO: implement + return 0; + } + + public boolean isLenient() { + return calendar.isLenient(); + } + + public TDate parse(String string) throws ParseException { + ParsePosition position = new ParsePosition(0); + TDate date = parse(string, position); + if (position.getIndex() == 0) { + throw new ParseException("Unparseable date" + string, position.getErrorIndex()); + } + return date; + } + + public abstract TDate parse(String string, ParsePosition position); + + @Override + public Object parseObject(String string, ParsePosition position) { + return parse(string, position); + } + + public void setCalendar(TCalendar cal) { + calendar = cal; + } + + public void setLenient(boolean value) { + calendar.setLenient(value); + } + + /*public void setNumberFormat(NumberFormat format) { + numberFormat = format; + }*/ + + public static class Field extends Format.Field { + private static THashMap table = new THashMap<>(); + public final static Field ERA = new Field("era", TCalendar.ERA); + public final static Field YEAR = new Field("year", TCalendar.YEAR); + public final static Field MONTH = new Field("month", TCalendar.MONTH); + public final static Field HOUR_OF_DAY0 = new Field("hour of day", TCalendar.HOUR_OF_DAY); + public final static Field HOUR_OF_DAY1 = new Field("hour of day 1", -1); + public final static Field MINUTE = new Field("minute", TCalendar.MINUTE); + public final static Field SECOND = new Field("second", TCalendar.SECOND); + public final static Field MILLISECOND = new Field("millisecond", TCalendar.MILLISECOND); + public final static Field DAY_OF_WEEK = new Field("day of week", TCalendar.DAY_OF_WEEK); + public final static Field DAY_OF_MONTH = new Field("day of month", TCalendar.DAY_OF_MONTH); + public final static Field DAY_OF_YEAR = new Field("day of year", TCalendar.DAY_OF_YEAR); + public final static Field DAY_OF_WEEK_IN_MONTH = new Field("day of week in month", + TCalendar.DAY_OF_WEEK_IN_MONTH); + public final static Field WEEK_OF_YEAR = new Field("week of year", TCalendar.WEEK_OF_YEAR); + public final static Field WEEK_OF_MONTH = new Field("week of month", TCalendar.WEEK_OF_MONTH); + public final static Field AM_PM = new Field("am pm", TCalendar.AM_PM); + public final static Field HOUR0 = new Field("hour", TCalendar.HOUR); + public final static Field HOUR1 = new Field("hour 1", -1); + public final static Field TIME_ZONE = new Field("time zone", -1); + private int calendarField = -1; + + 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); + } + } + + public int getCalendarField() { + return calendarField; + } + + public static Field ofCalendarField(int calendarField) { + if (calendarField < 0 || calendarField >= TCalendar.FIELD_COUNT) { + throw new IllegalArgumentException(); + } + + return table.get(new Integer(calendarField)); + } + } + + private static void checkDateStyle(int style) { + if (!(style == SHORT || style == MEDIUM || style == LONG || style == FULL || style == DEFAULT)) { + throw new IllegalArgumentException("Illegal date style: " + style); + } + } + + 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("Illegal time style: " + style); + } + } +} diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/text/DateFormatSymbols.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/text/DateFormatSymbols.java new file mode 100644 index 000000000..95cbfe583 --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/text/DateFormatSymbols.java @@ -0,0 +1,226 @@ +/* + * 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.teavm.classlib.java.text; + +import java.util.Arrays; +import org.teavm.classlib.impl.unicode.CLDRHelper; +import org.teavm.classlib.java.io.TSerializable; +import org.teavm.classlib.java.lang.TCloneable; +import org.teavm.classlib.java.util.TLocale; + +public class DateFormatSymbols implements TSerializable, TCloneable { + private TLocale locale; + private String localPatternChars; + String[] ampms, eras, months, shortMonths, shortWeekdays, weekdays; + String[][] zoneStrings; + + + public DateFormatSymbols() { + this(TLocale.getDefault()); + } + + public DateFormatSymbols(TLocale locale) { + this.locale = locale; + } + + @Override + public Object clone() { + DateFormatSymbols symbols = new DateFormatSymbols(locale); + if (ampms != null) { + symbols.ampms = Arrays.copyOf(ampms, ampms.length); + } + if (eras != null) { + symbols.eras = Arrays.copyOf(eras, eras.length); + } + if (months != null) { + symbols.months = Arrays.copyOf(months, months.length); + } + if (shortMonths != null) { + symbols.shortMonths = Arrays.copyOf(shortMonths, shortMonths.length); + } + if (shortWeekdays != null) { + symbols.shortWeekdays = Arrays.copyOf(shortWeekdays.clone(), shortWeekdays.length); + } + if (weekdays != null) { + symbols.weekdays = Arrays.copyOf(weekdays, weekdays.length); + } + if (zoneStrings != null) { + symbols.zoneStrings = new String[zoneStrings.length][]; + for (int i = 0; i < zoneStrings.length; i++) { + symbols.zoneStrings[i] = Arrays.copyOf(zoneStrings[i], zoneStrings[i].length); + } + } + return symbols; + } + + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (!(object instanceof DateFormatSymbols)) { + return false; + } + + DateFormatSymbols obj = (DateFormatSymbols) object; + if (!locale.equals(obj.locale)) { + return false; + } + 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; + } + 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; + } + + public String[] getAmPmStrings() { + return ampms.clone(); + } + + public String[] getEras() { + if (eras == null) { + eras = CLDRHelper.resolveEras(CLDRHelper.getCode(locale.getLanguage(), locale.getCountry())); + } + return eras.clone(); + } + + public String getLocalPatternChars() { + return localPatternChars; + } + + public String[] getMonths() { + return months.clone(); + } + + public String[] getShortMonths() { + return shortMonths.clone(); + } + + public String[] getShortWeekdays() { + return shortWeekdays.clone(); + } + + public String[] getWeekdays() { + return weekdays.clone(); + } + + public String[][] getZoneStrings() { + String[][] clone = new String[zoneStrings.length][]; + for (int i = zoneStrings.length; --i >= 0;) { + clone[i] = zoneStrings[i].clone(); + } + return clone; + } + + @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(); + } + for (String[] element : zoneStrings) { + for (int j = 0; j < element.length; j++) { + if (element[j] != null) { + hashCode += element[j].hashCode(); + } + } + } + return hashCode; + } + + public void setAmPmStrings(String[] data) { + ampms = data.clone(); + } + + public void setEras(String[] data) { + eras = data.clone(); + } + + public void setLocalPatternChars(String data) { + if (data == null) { + throw new NullPointerException(); + } + localPatternChars = data; + } + + public void setMonths(String[] data) { + months = data.clone(); + } + + public void setShortMonths(String[] data) { + shortMonths = data.clone(); + } + + public void setShortWeekdays(String[] data) { + shortWeekdays = data.clone(); + } + + public void setWeekdays(String[] data) { + weekdays = data.clone(); + } + + public void setZoneStrings(String[][] data) { + zoneStrings = data.clone(); + } +} diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/text/FieldPosition.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/text/FieldPosition.java new file mode 100644 index 000000000..de14cc1ba --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/text/FieldPosition.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.teavm.classlib.java.text; + +public class FieldPosition { + private int myField, beginIndex, endIndex; + private Format.Field myAttribute; + + public FieldPosition(int field) { + myField = field; + } + + public FieldPosition(Format.Field attribute) { + myAttribute = attribute; + myField = -1; + } + + public FieldPosition(Format.Field attribute, int field) { + myAttribute = attribute; + myField = field; + } + + void clear() { + beginIndex = endIndex = 0; + } + + @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; + } + + public int getBeginIndex() { + return beginIndex; + } + + public int getEndIndex() { + return endIndex; + } + + public int getField() { + return myField; + } + + public Format.Field getFieldAttribute() { + return myAttribute; + } + + @Override + public int hashCode() { + int attributeHash = (myAttribute == null) ? 0 : myAttribute.hashCode(); + return attributeHash + myField * 10 + beginIndex * 100 + endIndex; + } + + public void setBeginIndex(int index) { + beginIndex = index; + } + + public void setEndIndex(int index) { + endIndex = index; + } + + @Override + public String toString() { + return getClass().getName() + "[attribute=" + myAttribute + ", field=" + myField + ", beginIndex=" + + beginIndex + ", endIndex=" + endIndex + "]"; + } +} diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/text/Format.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/text/Format.java new file mode 100644 index 000000000..9d9f3f093 --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/text/Format.java @@ -0,0 +1,137 @@ +/* + * 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.teavm.classlib.java.text; + +import org.teavm.classlib.java.io.TSerializable; +import org.teavm.classlib.java.lang.TCloneable; + +public abstract class Format implements TSerializable, TCloneable { + public Format() { + } + + @Override + public Object clone() { + try { + return super.clone(); + } catch (CloneNotSupportedException e) { + return null; + } + } + + 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'))) { + throw new IllegalArgumentException("Invalid pattern char" + next + " in " + template); + } else { + output.append(next); + } + } + if (quote) { + throw new IllegalArgumentException("Unterminated quote"); + } + return output.toString(); + } + + public final String format(Object object) { + return format(object, new StringBuffer(), new FieldPosition(0)).toString(); + } + + public abstract StringBuffer format(Object object, StringBuffer buffer, FieldPosition field); + + public AttributedCharacterIterator formatToCharacterIterator(Object object) { + return new AttributedString(format(object)).getIterator(); + } + + public Object parseObject(String string) throws ParseException { + ParsePosition position = new ParsePosition(0); + Object result = parseObject(string, position); + if (position.getIndex() == 0) { + throw new ParseException("Format.parseObject(String) parse failure", position.getErrorIndex()); + } + return result; + } + + 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); + } + throw new IllegalArgumentException("Unmatched braces in the pattern"); + } + + public static class Field extends AttributedCharacterIterator.Attribute { + protected Field(String fieldName) { + super(fieldName); + } + } +} diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/text/ParseException.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/text/ParseException.java new file mode 100644 index 000000000..fa1c7af84 --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/text/ParseException.java @@ -0,0 +1,51 @@ +/* + * 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.teavm.classlib.java.text; + +/** + * 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 stack trace, detail + * message and the location of the error filled in. + * + * @param detailMessage + * the detail message for this exception. + * @param location + * the index at which the parse exception occurred. + */ + public ParseException(String detailMessage, int location) { + super(detailMessage); + errorOffset = location; + } + + /** + * Returns the index at which this parse exception occurred. + * + * @return the location of this exception in the parsed string. + */ + public int getErrorOffset() { + return errorOffset; + } +} diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/text/ParsePosition.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/text/ParsePosition.java new file mode 100644 index 000000000..2c5adb00c --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/text/ParsePosition.java @@ -0,0 +1,62 @@ +/* + * 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.teavm.classlib.java.text; + +public class ParsePosition { + + private int currentPosition, errorIndex = -1; + + public ParsePosition(int index) { + currentPosition = index; + } + + @Override + public boolean equals(Object object) { + if (!(object instanceof ParsePosition)) { + return false; + } + ParsePosition pos = (ParsePosition) object; + return currentPosition == pos.currentPosition && errorIndex == pos.errorIndex; + } + + public int getErrorIndex() { + return errorIndex; + } + + public int getIndex() { + return currentPosition; + } + + @Override + public int hashCode() { + return currentPosition + errorIndex; + } + + public void setErrorIndex(int index) { + errorIndex = index; + } + + public void setIndex(int index) { + currentPosition = index; + } + + @Override + public String toString() { + return getClass().getName() + "[index=" + currentPosition + ", errorIndex=" + errorIndex + "]"; + } +} diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/LocaleSettingsNativeGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/LocaleSettingsNativeGenerator.java index 68e224ae5..bce04cef7 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/LocaleSettingsNativeGenerator.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/LocaleSettingsNativeGenerator.java @@ -100,7 +100,7 @@ public class LocaleSettingsNativeGenerator implements Generator { if (localeName.startsWith("/")) { localeName = localeName.substring(1); } - if (!availableLocales.contains(localeName)) { + if (!localeName.equals("root") && !availableLocales.contains(localeName)) { continue; } LocaleInfo localeInfo = knownLocales.get(localeName); @@ -115,6 +115,9 @@ public class LocaleSettingsNativeGenerator implements Generator { case "territories.json": readCountries(localeName, localeInfo, input); break; + case "ca-gregorian.json": + readEras(localeName, localeInfo, input); + break; } } } catch (IOException e) { @@ -146,6 +149,16 @@ public class LocaleSettingsNativeGenerator implements Generator { } } + private void readEras(String localeCode, LocaleInfo locale, InputStream input) { + JsonObject root = (JsonObject)new JsonParser().parse(new InputStreamReader(input)); + JsonObject erasJson = root.get("main").getAsJsonObject().get(localeCode).getAsJsonObject() + .get("calendars").getAsJsonObject().get("gregorian").getAsJsonObject() + .get("eras").getAsJsonObject().get("eraNames").getAsJsonObject(); + String am = erasJson.get("0").getAsString(); + String pm = erasJson.get("1").getAsString(); + locale.eras = new String[] { am, pm }; + } + private void readWeekData(InputStream input) { JsonObject root = (JsonObject)new JsonParser().parse(new InputStreamReader(input)); JsonObject weekJson = root.get("supplemental").getAsJsonObject().get("weekData").getAsJsonObject(); @@ -338,5 +351,6 @@ public class LocaleSettingsNativeGenerator implements Generator { static class LocaleInfo { Map languages = new LinkedHashMap<>(); Map territories = new LinkedHashMap<>(); + String[] eras; } } diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/THashSet.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/THashSet.java index 91095e927..e87bf7f42 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/THashSet.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/THashSet.java @@ -181,4 +181,9 @@ public class THashSet extends TAbstractSet implements TCloneable, TSeriali THashMap> createBackingMap(int capacity, float loadFactor) { return new THashMap<>(capacity, loadFactor); } + + @Override + public Object clone() { + return new THashSet<>(this); + } }