diff --git a/boon/src/main/java/org/boon/core/Conversions.java b/boon/src/main/java/org/boon/core/Conversions.java index bd7b8d63..a849310a 100644 --- a/boon/src/main/java/org/boon/core/Conversions.java +++ b/boon/src/main/java/org/boon/core/Conversions.java @@ -547,6 +547,8 @@ public static T coerce(TypeType coerceTo, Class clz, Object value) { case TIME_ZONE: return (T) toTimeZone( value); + case LOCALE: + return (T) toLocale( value); @@ -747,6 +749,8 @@ public static T coerceWithFlag(TypeType coerceTo, Class clz, boolean [] f case TIME_ZONE: return (T) toTimeZone(flag, value); + case LOCALE: + return (T) toLocale(value); case UUID: return (T) toUUID(flag, value); @@ -779,6 +783,54 @@ public static TimeZone toTimeZone(Object value) { return TimeZone.getTimeZone(id); } + public static Locale toLocale(Object value) { + String locId = value.toString(); + return toLocale(locId); + } + + //(C) LocaleUtils from commons-lang + public static Locale toLocale(String str) { + if(str == null) { + return null; + } else { + int len = str.length(); + if(len != 2 && len != 5 && len < 7) { + throw new IllegalArgumentException("Invalid locale format: " + str); + } else { + char ch0 = str.charAt(0); + char ch1 = str.charAt(1); + if(ch0 >= 97 && ch0 <= 122 && ch1 >= 97 && ch1 <= 122) { + if(len == 2) { + return new Locale(str, ""); + } else if(str.charAt(2) != 95) { + throw new IllegalArgumentException("Invalid locale format: " + str); + } else { + char ch3 = str.charAt(3); + if(ch3 == 95) { + return new Locale(str.substring(0, 2), "", str.substring(4)); + } else { + char ch4 = str.charAt(4); + if(ch3 >= 65 && ch3 <= 90 && ch4 >= 65 && ch4 <= 90) { + if(len == 5) { + return new Locale(str.substring(0, 2), str.substring(3, 5)); + } else if(str.charAt(5) != 95) { + throw new IllegalArgumentException("Invalid locale format: " + str); + } else { + return new Locale(str.substring(0, 2), str.substring(3, 5), str.substring(6)); + } + } else { + throw new IllegalArgumentException("Invalid locale format: " + str); + } + } + } + } else { + throw new IllegalArgumentException("Invalid locale format: " + str); + } + } + } + } + + @SuppressWarnings("unchecked") public static T coerceClassic(Class clz, Object value) { diff --git a/boon/src/main/java/org/boon/core/reflection/Annotations.java b/boon/src/main/java/org/boon/core/reflection/Annotations.java index ff182c03..bec0b776 100644 --- a/boon/src/main/java/org/boon/core/reflection/Annotations.java +++ b/boon/src/main/java/org/boon/core/reflection/Annotations.java @@ -300,7 +300,7 @@ private static Annotation[] extractAllAnnotationsForProperty( Class clazz, St /* In the land of dynamic proxied AOP classes, * this class could be a proxy. This seems like a bug * waiting to happen. So far it has worked... */ - if ( annotations.length == 0 ) { + if ( annotations.length == 0 && clazz.getSuperclass() != null) { annotations = findPropertyAnnotations( clazz.getSuperclass(), propertyName, useRead ); } return annotations; diff --git a/boon/src/main/java/org/boon/core/reflection/MapperComplex.java b/boon/src/main/java/org/boon/core/reflection/MapperComplex.java index 6b7bc42f..f282ef82 100644 --- a/boon/src/main/java/org/boon/core/reflection/MapperComplex.java +++ b/boon/src/main/java/org/boon/core/reflection/MapperComplex.java @@ -180,14 +180,15 @@ public List convertListOfMapsToObjects(List list, Class componen /** * fromMap converts a map into a java object * @param map map to create the object from. - * @param cls class type of new object + * @param c class type of new object * @param map to create teh object from. * @return new object of type cls */ @Override - public T fromMap(final Map map, final Class cls) { + public T fromMap(final Map map, final Class c) { + final Class cls = MapperSimple.handleAbstractReplacement(map, c); T toObject = Reflection.newInstance( cls ); Map fields = fieldsAccessor.getFields( toObject.getClass() ); Set> mapKeyValuesEntrySet = map.entrySet(); @@ -1171,14 +1172,15 @@ public Object fromValueMap(final Map valueMap * This does some special handling to take advantage of us using the value map so it avoids creating * a bunch of array objects and collections. Things you have to worry about when writing a * high-speed JSON serializer. - * @param cls the new type + * @param c the new type * @return new object from value map */ @Override @SuppressWarnings("unchecked") public T fromValueMap(final Map valueMap, - final Class cls) { + final Class c) { + final Class cls = MapperSimple.handleAbstractReplacement(valueMap, c); T newInstance = Reflection.newInstance( cls ); ValueMap map = ( ValueMap ) ( Map ) valueMap; @@ -1372,8 +1374,18 @@ private void fromValueMapHandleValueCase( evalue = ((ValueContainer) evalue).toValue(); } + + key = Conversions.coerce( keyType, key ); - evalue = Conversions.coerce( valueType, evalue ); + + + Class actualType = valueType; + if ( valueType.isInterface() || Typ.isAbstract( valueType ) && evalue instanceof Map && ((Map)evalue).containsKey("class")) { + String className = ((Map)evalue) + .get("class").toString(); + actualType = Reflection.loadClass( className ); + } + evalue = Conversions.coerce( actualType, evalue ); newMap.put( key, evalue ); } @@ -1842,4 +1854,4 @@ public List toList(Object object) { } -} \ No newline at end of file +} diff --git a/boon/src/main/java/org/boon/core/reflection/MapperSimple.java b/boon/src/main/java/org/boon/core/reflection/MapperSimple.java index 87fe1eab..80a64332 100644 --- a/boon/src/main/java/org/boon/core/reflection/MapperSimple.java +++ b/boon/src/main/java/org/boon/core/reflection/MapperSimple.java @@ -79,19 +79,36 @@ public List convertListOfMapsToObjects(List list, Class componen return ( List ) newList; } + static Class handleAbstractReplacement(final Map map, final Class cls) { + if (cls.isInterface() && Typ.isAbstract(cls)) { + if(map.containsKey("class")) { + String className = map.get("class").toString(); + if (className != null) { + Class c = Reflection.loadClass(className); + if (cls.isAssignableFrom(c)) { + return c; + } + } + } + } + return cls; + + } - /** - * fromMap converts a map into a java object - * @param map map to create the object from. - * @param cls class type of new object - * @param map to create teh object from. - * @return new object of type cls - */ - @Override - public T fromMap(final Map map, final Class cls) { + + + /** + * fromMap converts a map into a java object + * @param map map to create the object from. + * @param map to create teh object from. + * @return new object of type cls + */ + @Override + public T fromMap(final Map map, final Class c) { + final Class cls = handleAbstractReplacement(map, c); T toObject = Reflection.newInstance( cls ); Map fields = fieldsAccessor.getFields( toObject.getClass() ); Set> mapKeyValuesEntrySet = map.entrySet(); @@ -1042,8 +1059,7 @@ private void handleCollectionOfValues( */ @Override @SuppressWarnings("unchecked") - public Object fromValueMap(final Map valueMap - ) { + public Object fromValueMap(final Map valueMap) { try { @@ -1063,14 +1079,15 @@ public Object fromValueMap(final Map valueMap * This does some special handling to take advantage of us using the value map so it avoids creating * a bunch of array objects and collections. Things you have to worry about when writing a * high-speed JSON serializer. - * @param cls the new type + * @param c the new type * @return new object from value map */ @Override @SuppressWarnings("unchecked") public T fromValueMap(final Map valueMap, - final Class cls) { + final Class c) { + final Class cls = handleAbstractReplacement(valueMap, c); T newInstance = Reflection.newInstance( cls ); ValueMap map = ( ValueMap ) ( Map ) valueMap; @@ -1259,7 +1276,13 @@ private void fromValueMapHandleValueCase( } key = Conversions.coerce( keyType, key ); - evalue = Conversions.coerce( valueType, evalue ); + Class actualType = valueType; + if ( valueType.isInterface() || Typ.isAbstract( valueType ) && evalue instanceof Map && ((Map)evalue).containsKey("class")) { + String className = ((Map)evalue) + .get("class").toString(); + actualType = Reflection.loadClass( className ); + } + evalue = Conversions.coerce( actualType, evalue ); newMap.put( key, evalue ); } @@ -1352,7 +1375,13 @@ private void setFieldValueFromMap( final Object parentObject, } key = Conversions.coerce(keyType, key); - evalue = Conversions.coerce( valueType, evalue ); + Class actualType = valueType; + if ( valueType.isInterface() || Typ.isAbstract( valueType ) && evalue instanceof Map && ((Map)evalue).containsKey("class")) { + String className = ((Map)evalue) + .get("class").toString(); + actualType = Reflection.loadClass( className ); + } + evalue = Conversions.coerce( actualType, evalue ); newMap.put( key, evalue ); } diff --git a/boon/src/test/java/org/boon/bugs/BugReport165AndIssue169.java b/boon/src/test/java/org/boon/bugs/BugReport165AndIssue169.java index ebf3e88b..e109b525 100644 --- a/boon/src/test/java/org/boon/bugs/BugReport165AndIssue169.java +++ b/boon/src/test/java/org/boon/bugs/BugReport165AndIssue169.java @@ -29,6 +29,7 @@ +import org.boon.Boon; import org.boon.IO; import org.boon.json.JsonFactory; import org.boon.json.ObjectMapper; @@ -112,10 +113,10 @@ public Sample() { e.printStackTrace(); } - file =new File("/usr/local/bin/vertx"); + //file =new File("/usr/local/bin/vertx"); date = new Date(); - path = file.toPath(); + //path = file.toPath(); locale = Locale.CANADA; timeZone = TimeZone.getTimeZone("PST"); } @@ -194,6 +195,8 @@ public void test4() { puts(json); puts (sample1); + puts(Boon.toJson(Locale.CANADA_FRENCH)); + ok = json.contains("\"effectiveDate\"") || die(); Sample sample2 = JsonFactory.fromJson(json, Sample.class); @@ -202,7 +205,7 @@ public void test4() { puts ("sample2", JsonFactory.toJson(sample2)); - ok = sample1.equals(sample2); + ok = sample1.equals(sample2) || die(); } diff --git a/boon/src/test/java/org/boon/json/code/InterfaceB.java b/boon/src/test/java/org/boon/json/code/InterfaceB.java new file mode 100644 index 00000000..9d76faee --- /dev/null +++ b/boon/src/test/java/org/boon/json/code/InterfaceB.java @@ -0,0 +1,13 @@ +package org.boon.json.code; + + +/** + * Created by piyush.goyal on 9/22/16. + */ +public interface InterfaceB { + + String getPiyush(); + + void setPiyush(String piyush); + +} diff --git a/boon/src/test/java/org/boon/json/code/JsonAbstractClassesTest.java b/boon/src/test/java/org/boon/json/code/JsonAbstractClassesTest.java new file mode 100644 index 00000000..103d16f6 --- /dev/null +++ b/boon/src/test/java/org/boon/json/code/JsonAbstractClassesTest.java @@ -0,0 +1,88 @@ +package org.boon.json.code; + +import org.boon.json.JsonFactory; +import org.boon.json.JsonParserFactory; +import org.boon.json.JsonSerializer; +import org.boon.json.JsonSerializerFactory; +import org.boon.json.ObjectMapper; +import org.junit.Before; +import org.junit.Test; +import static org.junit.Assert.*; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Created by piyush.goyal on 8/17/17. + */ +public class JsonAbstractClassesTest { + + + + private ObjectMapper mapper; + + @Test + public void testAbstracTClassesWSimpleMapper() { + mapper = JsonFactory.create(new JsonParserFactory().useAnnotations(), + new JsonSerializerFactory().useAnnotations()); + testAbstracTClasses(); + } + @Test + public void testAbstracTClassesWComplexMapper() { + mapper = JsonFactory.create(new JsonParserFactory().setRespectIgnore(false).useAnnotations(), + new JsonSerializerFactory().useAnnotations()); + testAbstracTClasses(); + } + + public void testAbstracTClasses() { + + + JsonClassC c = new JsonClassC(); + c.setPiyush("Tosheer"); + + JsonClassD d = new JsonClassD(); + d.setPiyush("VIvek"); + + Map map = new HashMap<>(); + map.put("c", c); + map.put("d", d); + + List list = new ArrayList<>(); + list.add(c); + list.add(d); + + JsonClassA jsonClassA = new JsonClassA(); + + jsonClassA.setTextValue("aa"); + jsonClassA.setXx(map); + jsonClassA.setYy(list); + + JsonSerializer serializer = new JsonSerializerFactory().setOutputType(true).create(); + + String s = serializer.serialize(jsonClassA).toString(); + System.out.println(s); + + //String json = "{\"class\":\"com.akqa.some.code.JsonClassA\",\"xx\":{\"c\":{\"class\":\"com.akqa.some.code.JsonClassC\",\"piyush\":\"Tosheer\"},\"d\":{\"class\":\"com.akqa.some.code.JsonClassD\",\"piyush\":\"VIvek\"}},\"textValue\":\"aa\"}"; + String json = s; + JsonClassA jsonClassA1 = mapper.fromJson(json, JsonClassA.class); + + + assertEquals("aa", jsonClassA1.getTextValue()); + Map xx = jsonClassA1.getXx(); + InterfaceB c1 = xx.get("c"); + InterfaceB d1 = xx.get("d"); + assertEquals("Tosheer", c1.getPiyush()); + assertEquals("VIvek", d1.getPiyush()); + assertEquals(c.getClass(), c1.getClass()); + assertEquals(d.getClass(), d1.getClass()); + List list1= jsonClassA1.getYy(); + c1 = list1.get(0); + d1 = list1.get(1); + assertEquals("Tosheer", c1.getPiyush()); + assertEquals("VIvek", d1.getPiyush()); + assertEquals(c.getClass(), c1.getClass()); + assertEquals(d.getClass(), d1.getClass()); + } +} diff --git a/boon/src/test/java/org/boon/json/code/JsonClassA.java b/boon/src/test/java/org/boon/json/code/JsonClassA.java new file mode 100644 index 00000000..034d44c3 --- /dev/null +++ b/boon/src/test/java/org/boon/json/code/JsonClassA.java @@ -0,0 +1,42 @@ +package org.boon.json.code; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Created by piyush.goyal on 9/22/16. + */ +public class JsonClassA { + + + private Map xx = new HashMap<>(); + + private List yy = new ArrayList<>(); + private String textValue; + + public String getTextValue() { + return textValue; + } + + public void setTextValue(String textValue) { + this.textValue = textValue; + } + + public Map getXx() { + return xx; + } + + public void setXx(Map xx) { + this.xx = xx; + } + + public List getYy() { + return yy; + } + + public void setYy(List yy) { + this.yy = yy; + } +} diff --git a/boon/src/test/java/org/boon/json/code/JsonClassC.java b/boon/src/test/java/org/boon/json/code/JsonClassC.java new file mode 100644 index 00000000..02bf42dd --- /dev/null +++ b/boon/src/test/java/org/boon/json/code/JsonClassC.java @@ -0,0 +1,22 @@ +package org.boon.json.code; + +/** + * Created by piyush.goyal on 9/22/16. + */ +public class JsonClassC implements InterfaceB { + + protected JsonClassC(){} + + + private String piyush; + + @Override + public String getPiyush() { + return piyush; + } + + @Override + public void setPiyush(String piyush) { + this.piyush = piyush; + } +} diff --git a/boon/src/test/java/org/boon/json/code/JsonClassD.java b/boon/src/test/java/org/boon/json/code/JsonClassD.java new file mode 100644 index 00000000..ff102ad8 --- /dev/null +++ b/boon/src/test/java/org/boon/json/code/JsonClassD.java @@ -0,0 +1,19 @@ +package org.boon.json.code; + +/** + * Created by piyush.goyal on 9/22/16. + */ +public class JsonClassD implements InterfaceB { + + private String piyush; + + @Override + public String getPiyush() { + return piyush; + } + + @Override + public void setPiyush(String piyush) { + this.piyush = piyush; + } +}