diff --git a/src/main/java/org/meeuw/i18n/languages/ISO_639.java b/src/main/java/org/meeuw/i18n/languages/ISO_639.java index 21a8eb7..64ac8ea 100644 --- a/src/main/java/org/meeuw/i18n/languages/ISO_639.java +++ b/src/main/java/org/meeuw/i18n/languages/ISO_639.java @@ -19,6 +19,16 @@ */ public class ISO_639 { + static ThreadLocal ignoreNotFound = ThreadLocal.withInitial(() -> Boolean.FALSE); + + public static void setIgnoreNotFound() { + ignoreNotFound.set(Boolean.TRUE); + } + + public static void removeIgnoreNotFound() { + ignoreNotFound.remove(); + } + private ISO_639() { } /** @@ -198,8 +208,12 @@ public static Optional get(String code) { * @throws IllegalArgumentException if not found */ public static ISO_639_Code iso639(String code) { - return get(code) - .orElseThrow(() -> new IllegalArgumentException("Unknown language code '" + code + "'")); + if (ignoreNotFound.get()) { + return get(code).orElse(LanguageCode.NOTFOUND); + } else { + return get(code) + .orElseThrow(() -> new IllegalArgumentException("Unknown language code '" + code + "'")); + } } diff --git a/src/main/java/org/meeuw/i18n/languages/ISO_639_3_Code.java b/src/main/java/org/meeuw/i18n/languages/ISO_639_3_Code.java index 4ea56bb..ea519ac 100644 --- a/src/main/java/org/meeuw/i18n/languages/ISO_639_3_Code.java +++ b/src/main/java/org/meeuw/i18n/languages/ISO_639_3_Code.java @@ -288,7 +288,7 @@ public List nameRecords() { } @Size - public NameRecord nameRecord(Locale locale) { + public NameRecord name(Locale locale) { if (locale.getLanguage().equals("en")) { return names.get(0); } else { diff --git a/src/main/java/org/meeuw/i18n/languages/ISO_639_Code.java b/src/main/java/org/meeuw/i18n/languages/ISO_639_Code.java index 47a99b4..a7a0f88 100644 --- a/src/main/java/org/meeuw/i18n/languages/ISO_639_Code.java +++ b/src/main/java/org/meeuw/i18n/languages/ISO_639_Code.java @@ -16,6 +16,7 @@ @XmlJavaTypeAdapter(LanguageCodeAdapter.class) public interface ISO_639_Code extends Serializable { + /** * The code associated with this language or language family. * diff --git a/src/main/java/org/meeuw/i18n/languages/LanguageCode.java b/src/main/java/org/meeuw/i18n/languages/LanguageCode.java index 71a8da8..af147fd 100644 --- a/src/main/java/org/meeuw/i18n/languages/LanguageCode.java +++ b/src/main/java/org/meeuw/i18n/languages/LanguageCode.java @@ -17,7 +17,7 @@ /** * A language with a ISO 639-3 language code (of three letters). Also, aware of the ISO-630-1 2-letter codes if that exists. *

- * Annotated with {@link XmlJavaTypeAdapter}, so it will automatically be marshalled and unmarshalled in XML's. + * Annotated with {@link XmlJavaTypeAdapter}, so it will automatically be marshalled and unmarshalled in XMLs. *

* Also annotated with jackson annotation, to be marshalled and unmarshalled in JSON as the code. *

@@ -26,6 +26,11 @@ @XmlJavaTypeAdapter(LanguageCodeAdapter.class) public interface LanguageCode extends ISO_639_Code { + UserDefinedLanguage UNKNOWN = new UserDefinedLanguage("UNKNOWN", null, "unknown language", "the language for some reason is unknown or unrecognized"); + + UserDefinedLanguage NOTFOUND = new UserDefinedLanguage("NOTFOUND", null, "language not found", "the language is not found"); + + /** * A stream with all known {@link ISO_639_Code language codes}. * @@ -195,8 +200,12 @@ static Optional get(String code) { * @throws IllegalArgumentException if not found */ static LanguageCode languageCode(String code) { - return get(code) - .orElseThrow(() -> new IllegalArgumentException("Unknown language code '" + code + "'")); + if (ISO_639.ignoreNotFound.get()) { + return get(code).orElse(NOTFOUND); + } else { + return get(code) + .orElseThrow(() -> new IllegalArgumentException("Unknown language code '" + code + "'")); + } } /** @@ -351,7 +360,7 @@ default Locale toLocale() { return new Locale(code()); } - default NameRecord nameRecord(Locale locale) { + default NameRecord name(Locale locale) { if (locale.getLanguage().equals("en")) { return nameRecords().get(0); } else { diff --git a/src/main/java/org/meeuw/i18n/languages/UserDefinedLanguage.java b/src/main/java/org/meeuw/i18n/languages/UserDefinedLanguage.java new file mode 100644 index 0000000..77f468e --- /dev/null +++ b/src/main/java/org/meeuw/i18n/languages/UserDefinedLanguage.java @@ -0,0 +1,83 @@ +package org.meeuw.i18n.languages; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class UserDefinedLanguage implements LanguageCode { + + private static final Map registered = new ConcurrentHashMap<>(); + + private final String code; + private final Type type; + private final String refName; + private final String comment; + + public UserDefinedLanguage(String code, Type type, String refName, String comment) { + this.code = code; + this.type = type; + this.refName = refName; + this.comment = comment; + } + + + @Override + public String code() { + return code; + } + + @Override + public String part3() { + return null; + } + + @Override + public String part2B() { + return null; + } + + @Override + public String part2T() { + return null; + } + + @Override + public String part1() { + return null; + } + + @Override + public Scope scope() { + return null; + } + + @Override + public Type languageType() { + return type; + } + + @Override + public String refName() { + return refName; + } + + @Override + public String comment() { + return comment; + } + + @Override + public List nameRecords() { + return List.of(); + } + + @Override + public List macroLanguages() { + return List.of(); + } + + @Override + public List individualLanguages() { + return List.of(); + } +} diff --git a/src/test/java/org/meeuw/i18n/languages/test/SerializationTest.java b/src/test/java/org/meeuw/i18n/languages/test/SerializationTest.java index 267c7be..0368c64 100644 --- a/src/test/java/org/meeuw/i18n/languages/test/SerializationTest.java +++ b/src/test/java/org/meeuw/i18n/languages/test/SerializationTest.java @@ -1,17 +1,27 @@ package org.meeuw.i18n.languages.test; -import com.fasterxml.jackson.databind.ObjectMapper; -import jakarta.xml.bind.JAXB; import java.io.*; -import static org.assertj.core.api.Assertions.assertThat; + +import jakarta.xml.bind.JAXB; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; -import org.meeuw.i18n.languages.ISO_639_Code; +import org.meeuw.i18n.languages.*; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.exc.ValueInstantiationException; + +import static org.assertj.core.api.Assertions.assertThat; import static org.meeuw.i18n.languages.ISO_639.iso639; -import org.meeuw.i18n.languages.LanguageCode; +import static org.meeuw.i18n.languages.LanguageCode.NOTFOUND; +import static org.meeuw.i18n.languages.LanguageCode.UNKNOWN; + public class SerializationTest { - + /** * Test serializing/deserializing to XML */ @@ -23,12 +33,44 @@ public void xml(String code) { new A(iso639(code), LanguageCode.languageCode("be")), writer); System.out.println(writer); A a = JAXB.unmarshal(new StringReader(writer.toString()), A.class); - - + + assertThat(a.isoCode).isSameAs(iso639(code)); } - - + + /** + * Test deserializing from XML (erroneous) + */ + @Test + public void xmlErroneous() { + { + A a = JAXB.unmarshal(new StringReader(""), A.class); + + assertThat(a.languageCode).isNull(); + } + try { + ISO_639.setIgnoreNotFound(); + A a = JAXB.unmarshal(new StringReader(""), A.class); + assertThat(a.languageCode).isEqualTo(NOTFOUND); + } finally { + ISO_639.removeIgnoreNotFound(); + } + + try { + ISO_639.registerFallback("zz", UNKNOWN); + A a = JAXB.unmarshal(new StringReader(""), A.class); + assertThat(a.languageCode).isEqualTo(UNKNOWN); + } finally { + ISO_639.resetFallBacks(); + } + + + + + } + + + /** * Test serializing/deserializing to XML */ @@ -38,20 +80,52 @@ public void json(String code) throws IOException { ObjectMapper objectMapper = new ObjectMapper(); String targetCode = iso639(code).code(); //objectMapper.registerModule(new org.meeuw.i18n.languages.jackson.LanguageModule(true)); - + String s = objectMapper.writeValueAsString(new A(iso639(code), LanguageCode.languageCode("be"))); System.out.println(s); - + assertThat(s).isEqualTo("{\"isoCode\":\"" + targetCode + "\",\"languageCode\":\"be\"}"); - + A rounded = objectMapper.readValue(s, A.class); - assertThat(rounded.isoCode.code()).isEqualTo(targetCode); + assertThat(rounded.isoCode.code()).isEqualTo(targetCode); + } + + /** + * Test deserializing from XML (erroneous) + */ + @Test + public void jsonErroneous() throws JsonProcessingException { + String code = "zz"; + ObjectMapper objectMapper = new ObjectMapper(); + + + Assertions.assertThatThrownBy(() -> { + A a = objectMapper.readValue("{\"languageCode\": \"" + code + "\"}", A.class); + }).isInstanceOf(ValueInstantiationException.class); + + try { + ISO_639.setIgnoreNotFound(); + A a = objectMapper.readValue("{\"languageCode\": \"" + code + "\"}", A.class); + assertThat(a.languageCode).isEqualTo(NOTFOUND); + } finally { + ISO_639.removeIgnoreNotFound(); + } + try { + ISO_639.registerFallback("zz", UNKNOWN); + A a = objectMapper.readValue("{\"languageCode\": \"" + code + "\"}", A.class); + assertThat(a.languageCode).isEqualTo(UNKNOWN); + } finally { + ISO_639.resetFallBacks(); + } } - - - + + + + + + /** - * Test java serializing/deserializing + * Test java serializing/deserializing */ @ParameterizedTest @ValueSource(strings = {"nld", "gem"}) @@ -59,17 +133,16 @@ public void serialize(String code) throws IOException, ClassNotFoundException { ISO_639_Code looked = iso639(code); A a = new A(looked, LanguageCode.languageCode("be")); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); - ObjectOutputStream objectOutputStream + ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream); objectOutputStream.writeObject(a); - - + + ObjectInputStream inputStream = new ObjectInputStream(new ByteArrayInputStream(byteArrayOutputStream.toByteArray())); A deserialized = (A) inputStream.readObject(); assertThat(deserialized.isoCode.code()).isEqualTo(looked.code()); assertThat(deserialized.isoCode).isSameAs(looked); - - - } + + }