From eb4e74a7785ac0d00fa7bde923f8494eba4da4d8 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Thu, 28 May 2015 18:52:40 +0400 Subject: [PATCH] Add ISO 4217 parser --- .../impl/currency/CurrenciesGenerator.java | 143 ++++++++++++++++++ .../impl/currency/CurrencyHelper.java | 28 ++++ .../impl/currency/CurrencyResource.java | 36 +++++ .../teavm/classlib/java/util/TCurrency.java | 84 ++++++++++ .../classlib/java/util/CurrencyTest.java | 39 +++++ 5 files changed, 330 insertions(+) create mode 100644 teavm-classlib/src/main/java/org/teavm/classlib/impl/currency/CurrenciesGenerator.java create mode 100644 teavm-classlib/src/main/java/org/teavm/classlib/impl/currency/CurrencyHelper.java create mode 100644 teavm-classlib/src/main/java/org/teavm/classlib/impl/currency/CurrencyResource.java create mode 100644 teavm-classlib/src/main/java/org/teavm/classlib/java/util/TCurrency.java create mode 100644 teavm-tests/src/test/java/org/teavm/classlib/java/util/CurrencyTest.java diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/impl/currency/CurrenciesGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlib/impl/currency/CurrenciesGenerator.java new file mode 100644 index 000000000..e301e2bdd --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/impl/currency/CurrenciesGenerator.java @@ -0,0 +1,143 @@ +/* + * Copyright 2015 Alexey Andreev. + * + * Licensed 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.impl.currency; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Iterator; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import org.teavm.model.MethodReference; +import org.teavm.platform.metadata.MetadataGenerator; +import org.teavm.platform.metadata.MetadataGeneratorContext; +import org.teavm.platform.metadata.Resource; +import org.teavm.platform.metadata.ResourceArray; +import org.w3c.dom.*; +import org.xml.sax.SAXException; + +/** + * + * @author Alexey Andreev + */ +public class CurrenciesGenerator implements MetadataGenerator { + @Override + public Resource generateMetadata(MetadataGeneratorContext context, MethodReference method) { + Document doc; + try (InputStream input = context.getClassLoader().getResourceAsStream( + "org/teavm/classlib/impl/currency/iso4217.xml")) { + DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance(); + DocumentBuilder builder = builderFactory.newDocumentBuilder(); + doc = builder.parse(input); + } catch (IOException | ParserConfigurationException | SAXException e) { + throw new RuntimeException("Error reading ISO 4217 medata from file"); + } + + ResourceArray currencies = context.createResourceArray(); + Element root = doc.getDocumentElement(); + for (Element elem : childElements(root)) { + if (elem.getTagName().equals("CcyTbl")) { + parseCurrencies(context, elem, currencies); + } + } + return currencies; + } + + private void parseCurrencies(MetadataGeneratorContext context, Element tableElem, + ResourceArray currencies) { + for (Element currencyElem : childElements(tableElem)) { + if (!currencyElem.getTagName().equals("CcyNtry")) { + continue; + } + CurrencyResource currency = context.createResource(CurrencyResource.class); + for (Element propertyElem : childElements(currencyElem)) { + switch (propertyElem.getTagName()) { + case "Ccy": + currency.setCode(getText(propertyElem)); + break; + case "CcyNbr": + currency.setNumericCode(Integer.parseInt(getText(propertyElem))); + break; + case "CcyMnrUnts": + String value = getText(propertyElem); + if (value.equals("N.A.")) { + currency.setFractionDigits(-1); + } else { + currency.setFractionDigits(Integer.parseInt(value)); + } + break; + } + } + currencies.add(currency); + } + } + + private Iterable childElements(final Element parent) { + return new Iterable() { + NodeList nodes = parent.getChildNodes(); + @Override + public Iterator iterator() { + return new Iterator() { + int index = -1; + { + following(); + } + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + @Override + public Element next() { + Element result = (Element)nodes.item(index); + following(); + return result; + } + @Override + public boolean hasNext() { + return index < nodes.getLength(); + } + private void following() { + while (++index < nodes.getLength()) { + if (nodes.item(index).getNodeType() == Node.ELEMENT_NODE) { + break; + } + } + } + }; + } + }; + } + + private String getText(Element element) { + StringBuilder sb = new StringBuilder(); + NodeList nodes = element.getChildNodes(); + for (int i = 0; i < nodes.getLength(); ++i) { + Node child = nodes.item(i); + switch (child.getNodeType()) { + case Node.TEXT_NODE: + case Node.CDATA_SECTION_NODE: + CharacterData cdata = (CharacterData)child; + sb.append(cdata.getData()); + break; + case Node.ENTITY_REFERENCE_NODE: + EntityReference ref = (EntityReference)child; + sb.append(ref.getNodeValue()); + break; + } + } + return sb.toString(); + } +} diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/impl/currency/CurrencyHelper.java b/teavm-classlib/src/main/java/org/teavm/classlib/impl/currency/CurrencyHelper.java new file mode 100644 index 000000000..be945647e --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/impl/currency/CurrencyHelper.java @@ -0,0 +1,28 @@ +/* + * Copyright 2015 Alexey Andreev. + * + * Licensed 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.impl.currency; + +import org.teavm.platform.metadata.MetadataProvider; +import org.teavm.platform.metadata.ResourceArray; + +/** + * + * @author Alexey Andreev + */ +public final class CurrencyHelper { + @MetadataProvider(CurrenciesGenerator.class) + public static native ResourceArray getCurrencies(); +} diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/impl/currency/CurrencyResource.java b/teavm-classlib/src/main/java/org/teavm/classlib/impl/currency/CurrencyResource.java new file mode 100644 index 000000000..95d5ef0b6 --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/impl/currency/CurrencyResource.java @@ -0,0 +1,36 @@ +/* + * Copyright 2015 Alexey Andreev. + * + * Licensed 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.impl.currency; + +import org.teavm.platform.metadata.Resource; + +/** + * + * @author Alexey Andreev + */ +public interface CurrencyResource extends Resource { + String getCode(); + + void setCode(String code); + + int getNumericCode(); + + void setNumericCode(int code); + + int getFractionDigits(); + + void setFractionDigits(int fractionDigits); +} diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TCurrency.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TCurrency.java new file mode 100644 index 000000000..526ee1b6e --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/TCurrency.java @@ -0,0 +1,84 @@ +/* + * Copyright 2015 Alexey Andreev. + * + * Licensed 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.util; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import org.teavm.classlib.impl.currency.CurrencyHelper; +import org.teavm.classlib.impl.currency.CurrencyResource; +import org.teavm.classlib.java.io.TSerializable; +import org.teavm.platform.metadata.ResourceArray; + +/** + * + * @author Alexey Andreev + */ +public final class TCurrency implements TSerializable { + private static Map currencies; + private CurrencyResource resource; + + private TCurrency(CurrencyResource resource) { + this.resource = resource; + } + + private static void initCurrencies() { + if (currencies != null) { + return; + } + currencies = new HashMap<>(); + ResourceArray resources = CurrencyHelper.getCurrencies(); + for (int i = 0; i < resources.size(); ++i) { + CurrencyResource resource = resources.get(i); + currencies.put(resource.getCode(), new TCurrency(resource)); + } + } + + public static TCurrency getInstance(String currencyCode) { + if (currencyCode == null) { + throw new NullPointerException(); + } + initCurrencies(); + TCurrency currency = currencies.get(currencyCode); + if (currency == null) { + throw new IllegalArgumentException("Currency not found: " + currencyCode); + } + return currency; + } + + public static Set getAvailableCurrencies() { + initCurrencies(); + return new HashSet<>(currencies.values()); + } + + public String getCurrencyCode() { + return resource.getCode(); + } + + public int getDefaultFractionDigits() { + return resource.getFractionDigits(); + } + + public int getNumericCode() { + return resource.getNumericCode(); + } + + @Override + public String toString() { + return resource.getCode(); + } +} diff --git a/teavm-tests/src/test/java/org/teavm/classlib/java/util/CurrencyTest.java b/teavm-tests/src/test/java/org/teavm/classlib/java/util/CurrencyTest.java new file mode 100644 index 000000000..ef2c06be1 --- /dev/null +++ b/teavm-tests/src/test/java/org/teavm/classlib/java/util/CurrencyTest.java @@ -0,0 +1,39 @@ +/* + * Copyright 2015 Alexey Andreev. + * + * Licensed 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.util; + +import static org.junit.Assert.*; +import java.util.Currency; +import org.junit.Test; + +/** + * + * @author Alexey Andreev + */ +public class CurrencyTest { + @Test + public void findsByCode() { + Currency currency = Currency.getInstance("RUB"); + assertEquals("RUB", currency.getCurrencyCode()); + assertEquals(2, currency.getDefaultFractionDigits()); + assertEquals(643, currency.getNumericCode()); + } + + @Test(expected = IllegalArgumentException.class) + public void rejectsWrongCode() { + Currency.getInstance("WWW"); + } +}