From c802ef83b12411c5aa19068e9834c4a5ac2ced72 Mon Sep 17 00:00:00 2001 From: Thomas A Cellucci Date: Fri, 5 Aug 2016 16:35:02 -0700 Subject: [PATCH 1/3] add handling for java 8 time classes and miscellaneous others --- .../com/netflix/archaius/DefaultDecoder.java | 99 ++++++++++++------- .../netflix/archaius/DefaultDecoderTest.java | 35 +++++++ 2 files changed, 100 insertions(+), 34 deletions(-) diff --git a/archaius2-core/src/main/java/com/netflix/archaius/DefaultDecoder.java b/archaius2-core/src/main/java/com/netflix/archaius/DefaultDecoder.java index 4a5c2baba..a69431424 100644 --- a/archaius2-core/src/main/java/com/netflix/archaius/DefaultDecoder.java +++ b/archaius2-core/src/main/java/com/netflix/archaius/DefaultDecoder.java @@ -23,58 +23,89 @@ import java.lang.reflect.Method; import java.math.BigDecimal; import java.math.BigInteger; +import java.nio.LongBuffer; +import java.time.Duration; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.OffsetDateTime; +import java.time.OffsetTime; +import java.time.Period; +import java.time.ZonedDateTime; +import java.util.BitSet; +import java.util.Currency; +import java.util.Date; +import java.util.IdentityHashMap; +import java.util.Map; +import java.util.function.Function; import javax.inject.Singleton; +import javax.xml.bind.DatatypeConverter; /** * @author Spencer Gibb */ @Singleton public class DefaultDecoder implements Decoder { + private Map, Function> decoderRegistry; public static DefaultDecoder INSTANCE = new DefaultDecoder(); + { + decoderRegistry = new IdentityHashMap<>(75); + decoderRegistry.put(String.class, v->v); + decoderRegistry.put(boolean.class, v->{ + if (v.equalsIgnoreCase("true") || v.equalsIgnoreCase("yes") || v.equalsIgnoreCase("on")) { + return Boolean.TRUE; + } + else if (v.equalsIgnoreCase("false") || v.equalsIgnoreCase("no") || v.equalsIgnoreCase("off")) { + return Boolean.FALSE; + } + throw new ParseException("Error parsing value '" + v, new Exception("Expected one of [true, yes, on, false, no, off]")); + + }); + decoderRegistry.put(Boolean.class, decoderRegistry.get(boolean.class)); + decoderRegistry.put(Integer.class, Integer::valueOf); + decoderRegistry.put(int.class, Integer::valueOf); + decoderRegistry.put(long.class, Long::valueOf); + decoderRegistry.put(Long.class, Long::valueOf); + decoderRegistry.put(short.class, Short::valueOf); + decoderRegistry.put(Short.class, Short::valueOf); + decoderRegistry.put(byte.class, Byte::valueOf); + decoderRegistry.put(Byte.class, Byte::valueOf); + decoderRegistry.put(double.class, Double::valueOf); + decoderRegistry.put(Double.class, Double::valueOf); + decoderRegistry.put(float.class, Float::valueOf); + decoderRegistry.put(Float.class, Float::valueOf); + decoderRegistry.put(BigInteger.class, BigInteger::new); + decoderRegistry.put(BigDecimal.class, BigDecimal::new); + decoderRegistry.put(Duration.class, Duration::parse); + decoderRegistry.put(Period.class, Period::parse); + decoderRegistry.put(LocalDateTime.class, LocalDateTime::parse); + decoderRegistry.put(LocalDate.class, LocalDate::parse); + decoderRegistry.put(LocalTime.class, LocalTime::parse); + decoderRegistry.put(OffsetDateTime.class, OffsetDateTime::parse); + decoderRegistry.put(OffsetTime.class, OffsetTime::parse); + decoderRegistry.put(ZonedDateTime.class, ZonedDateTime::parse); + decoderRegistry.put(Instant.class, v->Instant.from(OffsetDateTime.parse(v))); + decoderRegistry.put(Date.class, v->new Date(Long.parseLong(v))); + decoderRegistry.put(Currency.class, Currency::getInstance); + decoderRegistry.put(BitSet.class, v->BitSet.valueOf(DatatypeConverter.parseHexBinary(v))); + } + + @SuppressWarnings("unchecked") @Override public T decode(Class type, String encoded) { if (encoded == null) { return null; } - // Try primitives first - if (type.equals(String.class)) { - return (T) encoded; - } - else if (type.equals(boolean.class) || type.equals(Boolean.class)) { - if (encoded.equalsIgnoreCase("true") || encoded.equalsIgnoreCase("yes") || encoded.equalsIgnoreCase("on")) { - return (T) Boolean.TRUE; - } - else if (encoded.equalsIgnoreCase("false") || encoded.equalsIgnoreCase("no") || encoded.equalsIgnoreCase("off")) { - return (T) Boolean.FALSE; - } - throw new ParseException("Error parsing value '" + encoded, new Exception("Expected one of [true, yes, on, false, no, off]")); - } - else if (type.equals(int.class) || type.equals(Integer.class)) { - return (T) Integer.valueOf(encoded); - } - else if (type.equals(long.class) || type.equals(Long.class)) { - return (T) Long.valueOf(encoded); - } - else if (type.equals(short.class) || type.equals(Short.class)) { - return (T) Short.valueOf(encoded); - } - else if (type.equals(double.class) || type.equals(Double.class)) { - return (T) Double.valueOf(encoded); - } - else if (type.equals(float.class) || type.equals(Float.class)) { - return (T) Float.valueOf(encoded); - } - else if (type.equals(BigInteger.class)) { - return (T) new BigInteger(encoded); - } - else if (type.equals(BigDecimal.class)) { - return (T) new BigDecimal(encoded); + if (decoderRegistry.containsKey(type)) { + return (T)decoderRegistry.get(type).apply(encoded); } - else if (type.isArray()) { + + if (type.isArray()) { String[] elements = encoded.split(","); T[] ar = (T[]) Array.newInstance(type.getComponentType(), elements.length); for (int i = 0; i < elements.length; i++) { diff --git a/archaius2-core/src/test/java/com/netflix/archaius/DefaultDecoderTest.java b/archaius2-core/src/test/java/com/netflix/archaius/DefaultDecoderTest.java index a36d95283..a0de15bb5 100644 --- a/archaius2-core/src/test/java/com/netflix/archaius/DefaultDecoderTest.java +++ b/archaius2-core/src/test/java/com/netflix/archaius/DefaultDecoderTest.java @@ -16,6 +16,23 @@ package com.netflix.archaius; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.time.Duration; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.OffsetDateTime; +import java.time.OffsetTime; +import java.time.Period; +import java.time.ZonedDateTime; +import java.util.BitSet; +import java.util.Currency; +import java.util.Date; + +import javax.xml.bind.DatatypeConverter; + import org.junit.Assert; import org.junit.Test; @@ -30,5 +47,23 @@ public void testPrimitives() { Assert.assertEquals(true, flag); int int_value = decoder.decode(int.class, "123"); Assert.assertEquals(123, int_value); + + Assert.assertEquals(Byte.valueOf(Byte.MAX_VALUE), decoder.decode(Byte.class, String.valueOf(Byte.MAX_VALUE))); + Assert.assertEquals(Short.valueOf(Short.MAX_VALUE), decoder.decode(Short.class, String.valueOf(Short.MAX_VALUE))); + Assert.assertEquals(BigInteger.valueOf(Long.MAX_VALUE), decoder.decode(BigInteger.class, String.valueOf(Long.MAX_VALUE))); + Assert.assertEquals(BigDecimal.valueOf(Double.MAX_VALUE), decoder.decode(BigDecimal.class, String.valueOf(Double.MAX_VALUE))); + + Assert.assertEquals(Duration.parse("PT20M30S"), decoder.decode(Duration.class, "PT20M30S")); + Assert.assertEquals(Period.of(1, 2, 25), decoder.decode(Period.class, "P1Y2M3W4D")); + Assert.assertEquals(OffsetDateTime.parse("2016-08-03T10:15:30+07:00"), decoder.decode(OffsetDateTime.class, "2016-08-03T10:15:30+07:00")); + Assert.assertEquals(OffsetTime.parse("10:15:30+18:00"), decoder.decode(OffsetTime.class, "10:15:30+18:00")); + Assert.assertEquals(ZonedDateTime.parse("2016-08-03T10:15:30+01:00[Europe/Paris]"), decoder.decode(ZonedDateTime.class, "2016-08-03T10:15:30+01:00[Europe/Paris]")); + Assert.assertEquals(LocalDate.parse("2016-08-03"), decoder.decode(LocalDate.class, "2016-08-03")); + Assert.assertEquals(LocalTime.parse("10:15:30"), decoder.decode(LocalTime.class, "10:15:30")); + Assert.assertEquals(Instant.from(OffsetDateTime.parse("2016-08-03T10:15:30+07:00")), decoder.decode(Instant.class, "2016-08-03T10:15:30+07:00")); + Date newDate = new Date(); + Assert.assertEquals(newDate, decoder.decode(Date.class, String.valueOf(newDate.getTime()))); + Assert.assertEquals(Currency.getInstance("USD"), decoder.decode(Currency.class, "USD")); + Assert.assertEquals(BitSet.valueOf(DatatypeConverter.parseHexBinary("DEADBEEF00DEADBEEF")), decoder.decode(BitSet.class, "DEADBEEF00DEADBEEF")); } } From 0b3cd970536f4eee22f623c8dfebc6d13d987a6d Mon Sep 17 00:00:00 2001 From: Thomas A Cellucci Date: Fri, 5 Aug 2016 16:38:16 -0700 Subject: [PATCH 2/3] remove unwanted import --- .../src/main/java/com/netflix/archaius/DefaultDecoder.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/archaius2-core/src/main/java/com/netflix/archaius/DefaultDecoder.java b/archaius2-core/src/main/java/com/netflix/archaius/DefaultDecoder.java index a69431424..defdb7836 100644 --- a/archaius2-core/src/main/java/com/netflix/archaius/DefaultDecoder.java +++ b/archaius2-core/src/main/java/com/netflix/archaius/DefaultDecoder.java @@ -15,15 +15,11 @@ */ package com.netflix.archaius; -import com.netflix.archaius.api.Decoder; -import com.netflix.archaius.exceptions.ParseException; - import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.math.BigDecimal; import java.math.BigInteger; -import java.nio.LongBuffer; import java.time.Duration; import java.time.Instant; import java.time.LocalDate; @@ -43,6 +39,9 @@ import javax.inject.Singleton; import javax.xml.bind.DatatypeConverter; +import com.netflix.archaius.api.Decoder; +import com.netflix.archaius.exceptions.ParseException; + /** * @author Spencer Gibb */ From 81c29b0ffe987b9aa94699821829223234baa8c4 Mon Sep 17 00:00:00 2001 From: Thomas A Cellucci Date: Mon, 8 Aug 2016 14:38:38 -0700 Subject: [PATCH 3/3] update decoder and build for java 8 --- .../com/netflix/archaius/DefaultDecoder.java | 4 +++ .../netflix/archaius/DefaultDecoderTest.java | 25 +++++++++++++++++-- build.gradle | 4 +-- 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/archaius2-core/src/main/java/com/netflix/archaius/DefaultDecoder.java b/archaius2-core/src/main/java/com/netflix/archaius/DefaultDecoder.java index defdb7836..d7ba5ffda 100644 --- a/archaius2-core/src/main/java/com/netflix/archaius/DefaultDecoder.java +++ b/archaius2-core/src/main/java/com/netflix/archaius/DefaultDecoder.java @@ -34,6 +34,8 @@ import java.util.Date; import java.util.IdentityHashMap; import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; import java.util.function.Function; import javax.inject.Singleton; @@ -79,6 +81,8 @@ else if (v.equalsIgnoreCase("false") || v.equalsIgnoreCase("no") || v.equalsIgno decoderRegistry.put(Float.class, Float::valueOf); decoderRegistry.put(BigInteger.class, BigInteger::new); decoderRegistry.put(BigDecimal.class, BigDecimal::new); + decoderRegistry.put(AtomicInteger.class, s->new AtomicInteger(Integer.parseInt(s))); + decoderRegistry.put(AtomicLong.class, s->new AtomicLong(Long.parseLong(s))); decoderRegistry.put(Duration.class, Duration::parse); decoderRegistry.put(Period.class, Period::parse); decoderRegistry.put(LocalDateTime.class, LocalDateTime::parse); diff --git a/archaius2-core/src/test/java/com/netflix/archaius/DefaultDecoderTest.java b/archaius2-core/src/test/java/com/netflix/archaius/DefaultDecoderTest.java index a0de15bb5..0cc150873 100644 --- a/archaius2-core/src/test/java/com/netflix/archaius/DefaultDecoderTest.java +++ b/archaius2-core/src/test/java/com/netflix/archaius/DefaultDecoderTest.java @@ -30,6 +30,8 @@ import java.util.BitSet; import java.util.Currency; import java.util.Date; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; import javax.xml.bind.DatatypeConverter; @@ -40,7 +42,7 @@ public class DefaultDecoderTest { @Test - public void testPrimitives() { + public void testJavaNumbers() { DefaultDecoder decoder = new DefaultDecoder(); boolean flag = decoder.decode(boolean.class, "true"); @@ -50,20 +52,39 @@ public void testPrimitives() { Assert.assertEquals(Byte.valueOf(Byte.MAX_VALUE), decoder.decode(Byte.class, String.valueOf(Byte.MAX_VALUE))); Assert.assertEquals(Short.valueOf(Short.MAX_VALUE), decoder.decode(Short.class, String.valueOf(Short.MAX_VALUE))); + Assert.assertEquals(Long.valueOf(Long.MAX_VALUE), decoder.decode(Long.class, String.valueOf(Long.MAX_VALUE))); + Assert.assertEquals(Integer.valueOf(Integer.MAX_VALUE), decoder.decode(Integer.class, String.valueOf(Integer.MAX_VALUE))); + Assert.assertEquals(Float.valueOf(Float.MAX_VALUE), decoder.decode(Float.class, String.valueOf(Float.MAX_VALUE))); + Assert.assertEquals(Double.valueOf(Double.MAX_VALUE), decoder.decode(Double.class, String.valueOf(Double.MAX_VALUE))); Assert.assertEquals(BigInteger.valueOf(Long.MAX_VALUE), decoder.decode(BigInteger.class, String.valueOf(Long.MAX_VALUE))); Assert.assertEquals(BigDecimal.valueOf(Double.MAX_VALUE), decoder.decode(BigDecimal.class, String.valueOf(Double.MAX_VALUE))); - + Assert.assertEquals(new AtomicInteger(Integer.MAX_VALUE).intValue(), decoder.decode(AtomicInteger.class, String.valueOf(Integer.MAX_VALUE)).intValue()); + Assert.assertEquals(new AtomicLong(Long.MAX_VALUE).longValue(), decoder.decode(AtomicLong.class, String.valueOf(Long.MAX_VALUE)).longValue()); + } + + @Test + public void testJavaDateTime() { + DefaultDecoder decoder = new DefaultDecoder(); + Assert.assertEquals(Duration.parse("PT20M30S"), decoder.decode(Duration.class, "PT20M30S")); Assert.assertEquals(Period.of(1, 2, 25), decoder.decode(Period.class, "P1Y2M3W4D")); Assert.assertEquals(OffsetDateTime.parse("2016-08-03T10:15:30+07:00"), decoder.decode(OffsetDateTime.class, "2016-08-03T10:15:30+07:00")); Assert.assertEquals(OffsetTime.parse("10:15:30+18:00"), decoder.decode(OffsetTime.class, "10:15:30+18:00")); Assert.assertEquals(ZonedDateTime.parse("2016-08-03T10:15:30+01:00[Europe/Paris]"), decoder.decode(ZonedDateTime.class, "2016-08-03T10:15:30+01:00[Europe/Paris]")); + Assert.assertEquals(LocalDateTime.parse("2016-08-03T10:15:30"), decoder.decode(LocalDateTime.class, "2016-08-03T10:15:30")); Assert.assertEquals(LocalDate.parse("2016-08-03"), decoder.decode(LocalDate.class, "2016-08-03")); Assert.assertEquals(LocalTime.parse("10:15:30"), decoder.decode(LocalTime.class, "10:15:30")); Assert.assertEquals(Instant.from(OffsetDateTime.parse("2016-08-03T10:15:30+07:00")), decoder.decode(Instant.class, "2016-08-03T10:15:30+07:00")); Date newDate = new Date(); Assert.assertEquals(newDate, decoder.decode(Date.class, String.valueOf(newDate.getTime()))); + } + + @Test + public void testJavaMiscellaneous() { + DefaultDecoder decoder = new DefaultDecoder(); Assert.assertEquals(Currency.getInstance("USD"), decoder.decode(Currency.class, "USD")); Assert.assertEquals(BitSet.valueOf(DatatypeConverter.parseHexBinary("DEADBEEF00DEADBEEF")), decoder.decode(BitSet.class, "DEADBEEF00DEADBEEF")); + Assert.assertEquals("testString", decoder.decode(String.class, "testString")); } + } diff --git a/build.gradle b/build.gradle index 14aeee477..c8dc31e57 100644 --- a/build.gradle +++ b/build.gradle @@ -11,8 +11,8 @@ subprojects { apply plugin: 'nebula.provided-base' apply plugin: 'java' - sourceCompatibility = 1.7 - targetCompatibility = 1.7 + sourceCompatibility = 1.8 + targetCompatibility = 1.8 repositories { jcenter()