Skip to content

Commit 1463fa8

Browse files
authored
Merge pull request #176 from laurgarn/jdk9_immutable
Jdk9 immutable
2 parents 015d584 + 76bc620 commit 1463fa8

File tree

8 files changed

+393
-19
lines changed

8 files changed

+393
-19
lines changed

pom.xml

+3-3
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
<groupId>com.cedarsoftware</groupId>
77
<artifactId>json-io</artifactId>
88
<packaging>bundle</packaging>
9-
<version>4.14.0</version>
9+
<version>4.14.1-SNAPSHOT</version>
1010
<description>Java JSON serialization</description>
1111
<url>https://github.com/jdereg/json-io</url>
1212

@@ -137,8 +137,8 @@
137137
<artifactId>maven-compiler-plugin</artifactId>
138138
<version>3.8.1</version>
139139
<configuration>
140-
<source>1.8</source>
141-
<target>1.8</target>
140+
<source>11</source>
141+
<target>11</target>
142142
<!-- <release>1.8</release>-->
143143
<compilerId>groovy-eclipse-compiler</compilerId>
144144
<!-- <verbose>true</verbose> -->

src/main/java/com/cedarsoftware/util/io/JsonObject.java

+15-1
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,11 @@ public class JsonObject<K, V> extends LinkedHashMap<K, V>
7272
primitiveWrappers.add("java.lang.Short");
7373
}
7474

75+
public String toString()
76+
{
77+
return "mLen:" + getLenientSize() + " type:" + type + " l,c:" + line + "," + col + " id:" + id;
78+
}
79+
7580

7681
public long getId()
7782
{
@@ -234,6 +239,15 @@ public Object[] getArray()
234239

235240
public int getLength()
236241
{
242+
Integer items = getLenientSize();
243+
if (items != null)
244+
{
245+
return items;
246+
}
247+
throw new JsonIoException("getLength() called on a non-collection, line " + line + ", col " + col);
248+
}
249+
250+
private Integer getLenientSize() {
237251
if (isArray())
238252
{
239253
if (target == null)
@@ -248,7 +262,7 @@ public int getLength()
248262
Object[] items = (Object[]) get(ITEMS);
249263
return items == null ? 0 : items.length;
250264
}
251-
throw new JsonIoException("getLength() called on a non-collection, line " + line + ", col " + col);
265+
return null;
252266
}
253267

254268
public Class getComponentType()

src/main/java/com/cedarsoftware/util/io/JsonReader.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -795,7 +795,7 @@ public Object getRefTarget(JsonObject jObj)
795795
*/
796796
public Object readObject()
797797
{
798-
JsonParser parser = new JsonParser(input, objsRead, getArgs());
798+
JsonParser parser = new JsonParser(input, objsRead, getArgs(), maxParseDepth);
799799
JsonObject<String, Object> root = new JsonObject();
800800
Object o;
801801
try

src/main/java/com/cedarsoftware/util/io/MetaUtils.java

+14-2
Original file line numberDiff line numberDiff line change
@@ -627,7 +627,11 @@ public int compare(Constructor c1, Constructor c2)
627627
// Try each constructor (public, protected, private, package-private) with null values for non-primitives.
628628
for (Constructor constructor : constructorList)
629629
{
630-
constructor.setAccessible(true);
630+
try {
631+
constructor.setAccessible(true);
632+
} catch (Exception ignore) {
633+
continue;
634+
}
631635
Class[] argTypes = constructor.getParameterTypes();
632636
Object[] values = fillArgs(argTypes, true);
633637
try
@@ -641,7 +645,11 @@ public int compare(Constructor c1, Constructor c2)
641645
// Try each constructor (public, protected, private, package-private) with non-null values for non-primitives.
642646
for (Constructor constructor : constructorList)
643647
{
644-
constructor.setAccessible(true);
648+
try {
649+
constructor.setAccessible(true);
650+
} catch (Exception e) {
651+
continue;
652+
}
645653
Class[] argTypes = constructor.getParameterTypes();
646654
Object[] values = fillArgs(argTypes, false);
647655
try
@@ -809,6 +817,10 @@ else if (argType == Object.class)
809817
{
810818
values[i] = new Object();
811819
}
820+
else if (argType.isArray())
821+
{
822+
values[i] = new Object[0];
823+
}
812824
else
813825
{
814826
values[i] = null;

src/main/java/com/cedarsoftware/util/io/ObjectResolver.java

+61-7
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@
66
import java.lang.reflect.Type;
77
import java.lang.reflect.TypeVariable;
88
import java.util.ArrayDeque;
9+
import java.util.ArrayList;
910
import java.util.Arrays;
1011
import java.util.Collection;
1112
import java.util.Deque;
1213
import java.util.Iterator;
1314
import java.util.List;
1415
import java.util.Map;
16+
import java.util.Set;
1517

1618
import static com.cedarsoftware.util.io.JsonObject.ITEMS;
1719
import static com.cedarsoftware.util.io.JsonObject.KEYS;
@@ -57,6 +59,7 @@
5759
* See the License for the specific language governing permissions and
5860
* limitations under the License.
5961
*/
62+
@SuppressWarnings({ "rawtypes", "unchecked", "Convert2Diamond" })
6063
public class ObjectResolver extends Resolver
6164
{
6265
private final ClassLoader classLoader;
@@ -208,8 +211,8 @@ else if (rhs.getClass().isArray())
208211
}
209212
else if (rhs instanceof JsonObject)
210213
{
211-
final JsonObject<String, Object> jObj = (JsonObject) rhs;
212-
final Long ref = jObj.getReferenceId();
214+
final JsonObject<String, Object> jsRhs = (JsonObject) rhs;
215+
final Long ref = jsRhs.getReferenceId();
213216

214217
if (ref != null)
215218
{ // Correct field references
@@ -226,10 +229,19 @@ else if (rhs instanceof JsonObject)
226229
}
227230
else
228231
{ // Assign ObjectMap's to Object (or derived) fields
229-
field.set(target, createJavaObjectInstance(fieldType, jObj));
230-
if (!MetaUtils.isLogicalPrimitive(jObj.getTargetClass()))
232+
Object fieldObject = createJavaObjectInstance(fieldType, jsRhs);
233+
field.set(target, fieldObject);
234+
if (!MetaUtils.isLogicalPrimitive(jsRhs.getTargetClass()))
231235
{
232-
stack.addFirst((JsonObject) rhs);
236+
// GOTCHA : if the field is an immutable collection,
237+
// "work instance", where one can accumulate items in (ArrayList)
238+
// and "final instance' (say List.of() ) can _not_ be the same.
239+
// So, the later the assignment, the better.
240+
Object javaObj = convertMapsToObjects(jsRhs);
241+
if (javaObj != fieldObject)
242+
{
243+
field.set(target, javaObj);
244+
}
233245
}
234246
}
235247
}
@@ -390,12 +402,20 @@ private static String safeToString(Object o)
390402
*/
391403
protected void traverseCollection(final Deque<JsonObject<String, Object>> stack, final JsonObject<String, Object> jsonObj)
392404
{
405+
final String className = jsonObj.type;
393406
final Object[] items = jsonObj.getArray();
394407
if (items == null || items.length == 0)
395408
{
409+
if (className != null && className.startsWith("java.util.Immutable"))
410+
if (className.contains("Set")) {
411+
jsonObj.target = Set.of();
412+
} else if (className.contains("List")) {
413+
jsonObj.target = List.of();
414+
}
396415
return;
397416
}
398-
final Collection col = (Collection) jsonObj.target;
417+
final boolean isImmutable = className != null && className.startsWith("java.util.Immutable");
418+
final Collection col = isImmutable ? new ArrayList() : (Collection) jsonObj.target;
399419
final boolean isList = col instanceof List;
400420
int idx = 0;
401421

@@ -462,9 +482,43 @@ else if (element.getClass().isArray())
462482
idx++;
463483
}
464484

485+
//if (isImmutable) {
486+
reconciliateCollection(jsonObj, col);
487+
//}
488+
465489
jsonObj.remove(ITEMS); // Reduce memory required during processing
466490
}
467491

492+
static public void reconciliateCollection(JsonObject jsonObj, Collection col)
493+
{
494+
final String className = jsonObj.type;
495+
final boolean isImmutable = className != null && className.startsWith("java.util.Immutable");
496+
if (!isImmutable) return;
497+
498+
if (col == null && jsonObj.target instanceof Collection) col = (Collection) jsonObj.target;
499+
if (col == null) return;
500+
501+
if (className.contains("List"))
502+
{
503+
if (col.stream().noneMatch(c -> c == null || c instanceof JsonObject))
504+
{
505+
jsonObj.target = List.of(col.toArray());
506+
}
507+
else
508+
{
509+
jsonObj.target = col;
510+
}
511+
}
512+
else if (className.contains("Set"))
513+
{
514+
jsonObj.target = Set.of(col.toArray());
515+
}
516+
else
517+
{
518+
jsonObj.target = col;
519+
}
520+
}
521+
468522
/**
469523
* Traverse the JsonObject associated to an array (of any type). Convert and
470524
* assign the list of items in the JsonObject (stored in the @items field)
@@ -699,7 +753,7 @@ protected Object readIfMatching(final Object o, final Class compType, final Dequ
699753
{
700754
read = ((JsonReader.JsonClassReader)closestReader).read(o, stack);
701755
}
702-
return read;
756+
return read;
703757
}
704758

705759
private void markUntypedObjects(final Type type, final Object rhs, final Map<String, Field> classFields)

src/main/java/com/cedarsoftware/util/io/Resolver.java

+32-4
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
* See the License for the specific language governing permissions and
3535
* limitations under the License.*
3636
*/
37+
@SuppressWarnings({ "rawtypes", "unchecked" })
3738
abstract class Resolver
3839
{
3940
final Collection<UnresolvedReference> unresolvedRefs = new ArrayList<>();
@@ -98,6 +99,7 @@ static final class UnresolvedReference
9899
/**
99100
* stores missing fields information to notify client after the complete deserialization resolution
100101
*/
102+
@SuppressWarnings("FieldMayBeFinal")
101103
protected static class Missingfields
102104
{
103105
private Object target;
@@ -146,7 +148,7 @@ protected JsonReader getReader()
146148
*/
147149
protected Object convertMapsToObjects(final JsonObject<String, Object> root)
148150
{
149-
final Deque<JsonObject<String, Object>> stack = new ArrayDeque<JsonObject<String, Object>>();
151+
final Deque<JsonObject<String, Object>> stack = new ArrayDeque<>();
150152
stack.addFirst(root);
151153

152154
while (!stack.isEmpty())
@@ -389,10 +391,18 @@ else if (singletonMap.isAssignableFrom(c))
389391
Object value = jsonObj.values().iterator().next();
390392
mate = Collections.singletonMap(key, value);
391393
}
392-
else
394+
else if (!c.getName().startsWith("java.util.Immutable"))
393395
{
394396
mate = newInstance(c, jsonObj);
395397
}
398+
else if (c.getName().contains("Set"))
399+
{
400+
mate = new ArrayList<>();
401+
}
402+
else if (c.getName().contains("List"))
403+
{
404+
mate = new ArrayList<>();
405+
}
396406
}
397407
}
398408
else
@@ -577,11 +587,29 @@ protected void patchUnresolvedReferences()
577587
{ // Patch up Indexable Collections
578588
List list = (List) objToFix;
579589
list.set(ref.index, objReferenced.target);
590+
String containingTypeName = ref.referencingObj.type;
591+
if (containingTypeName != null && containingTypeName.startsWith("java.util.Immutable") && containingTypeName.contains("List"))
592+
{
593+
if (list.stream().noneMatch(c -> c == null || c instanceof JsonObject))
594+
{
595+
list = List.of(list.toArray());
596+
ref.referencingObj.target = list;
597+
}
598+
}
580599
}
581600
else if (objToFix instanceof Collection)
582-
{ // Add element (since it was not indexable, add it to collection)
601+
{
602+
String containingTypeName = ref.referencingObj.type;
583603
Collection col = (Collection) objToFix;
584-
col.add(objReferenced.target);
604+
if (containingTypeName != null && containingTypeName.startsWith("java.util.Immutable") && containingTypeName.contains("Set"))
605+
{
606+
throw new JsonIoException("Error setting set entry of ImmutableSet '" + ref.referencingObj.type + "', @ref = " + ref.refId);
607+
}
608+
else
609+
{
610+
// Add element (since it was not indexable, add it to collection)
611+
col.add(objReferenced.target);
612+
}
585613
}
586614
else
587615
{

src/test/groovy/com/cedarsoftware/util/io/TestCollection.groovy

+7-1
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ class TestCollection {
7979
private Set _strs_c;
8080
private Set _strs_d;
8181
private HashSet _typedSet;
82+
private List<String> _imm_lst_0;
83+
private List<String> _imm_lst_1;
8284

8385
private void init() {
8486
Collection array = new ArrayList()
@@ -191,6 +193,10 @@ class TestCollection {
191193
_typedSet.add(true)
192194
_typedSet.add(17.76)
193195
_typedSet.add(TimeZone.getTimeZone("PST"))
196+
197+
_imm_lst_0 = List.of()
198+
_imm_lst_1 = List.of("One")
199+
194200
}
195201
}
196202

@@ -655,4 +661,4 @@ class TestCollection {
655661
TestUtil.printLine(json)
656662
assertTrue(json.contains('list":[]'))
657663
}
658-
}
664+
}

0 commit comments

Comments
 (0)