From fa707e9fda47c7951f2b2e67b6ea3882be7f547b Mon Sep 17 00:00:00 2001 From: Robinson Date: Mon, 26 Jun 2023 00:31:22 +0200 Subject: [PATCH] Unmodifiable and Synchronized collections/maps now use injected class bytes --- .../SynchronizedCollectionJavaAccessor.java | 36 +++ .../SynchronizedCollectionsSerializer.java | 181 ++++++++++++--- .../UnmodifiableCollectionJavaAccessor.java | 36 +++ .../UnmodifiableCollectionsSerializer.java | 206 ++++++++++++++---- 4 files changed, 390 insertions(+), 69 deletions(-) create mode 100644 src/dorkbox/serializers/SynchronizedCollectionJavaAccessor.java create mode 100644 src/dorkbox/serializers/UnmodifiableCollectionJavaAccessor.java diff --git a/src/dorkbox/serializers/SynchronizedCollectionJavaAccessor.java b/src/dorkbox/serializers/SynchronizedCollectionJavaAccessor.java new file mode 100644 index 0000000..61b75d9 --- /dev/null +++ b/src/dorkbox/serializers/SynchronizedCollectionJavaAccessor.java @@ -0,0 +1,36 @@ +/* + * Copyright 2023 dorkbox, llc + * + * 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 dorkbox.serializers; + +import java.lang.reflect.Field; + +public +class SynchronizedCollectionJavaAccessor { + public static final Field SOURCE_MAP_FIELD = null; + + // the class methods here are rewritten using javassist. + public static Object SynchronizedCollection_Field(Object nativeSynchronizedCollection) { + return null; + } + + public static void initSynchronizedMap_Field() { + } + + public static Object SynchronizedMap_Field(Object nativeSynchronizedMap) { + return null; + } +} diff --git a/src/dorkbox/serializers/SynchronizedCollectionsSerializer.java b/src/dorkbox/serializers/SynchronizedCollectionsSerializer.java index 8edde2c..5cbb102 100644 --- a/src/dorkbox/serializers/SynchronizedCollectionsSerializer.java +++ b/src/dorkbox/serializers/SynchronizedCollectionsSerializer.java @@ -1,3 +1,19 @@ +/* + * Copyright 2023 dorkbox, llc + * + * 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. + */ + /* * Copyright 2010 Martin Grotzke * @@ -37,29 +53,112 @@ import com.esotericsoftware.kryo.Serializer; import com.esotericsoftware.kryo.io.Input; import com.esotericsoftware.kryo.io.Output; +import dorkbox.jna.ClassUtils; +import javassist.ClassPool; +import javassist.CtClass; +import javassist.CtMethod; +import javassist.CtNewMethod; +import javassist.Modifier; + /** * A kryo {@link Serializer} for synchronized {@link Collection}s and {@link Map}s * created via {@link Collections}. * * @author Martin Grotzke + * @author Dorkbox llc */ public class SynchronizedCollectionsSerializer extends Serializer { - - private static final Field SOURCE_COLLECTION_FIELD; - private static final Field SOURCE_MAP_FIELD; - - static { - try { - SOURCE_COLLECTION_FIELD = Class.forName("java.util.Collections$SynchronizedCollection" ) - .getDeclaredField( "c" ); - SOURCE_COLLECTION_FIELD.setAccessible( true ); - SOURCE_MAP_FIELD = Class.forName("java.util.Collections$SynchronizedMap" ) - .getDeclaredField( "m" ); - SOURCE_MAP_FIELD.setAccessible( true ); + /** + * Gets the version number. + */ + public static + String getVersion() { + return "3.0"; + } + + static { + dorkbox.updates.Updates.INSTANCE.add(SynchronizedCollectionsSerializer.class, "9e3f0fdbd0ac4e4ba887964733fe110a", getVersion()); + + try { + ClassPool pool = ClassPool.getDefault(); + + // allow non-reflection access to java.util.Collections...() + { + CtClass dynamicClass = pool.makeClass("java.util.SynchronizedCollectionsAccessory"); + CtMethod method = CtNewMethod.make( + "public static Object getSynchronizedCollectionField(Object nativeComp) { " + + // "java.lang.System.err.println(\"Getting sync collection field!\" + ((java.util.Collections$SynchronizedCollection)nativeComp).c);" + + "return ((java.util.Collections$SynchronizedCollection)nativeComp).c;" + + "}", dynamicClass); + dynamicClass.addMethod(method); + + method = CtNewMethod.make( + "public static java.lang.reflect.Field initSynchronizedMapField() { " + + "java.lang.reflect.Field field = Class.forName(\"java.util.Collections$SynchronizedMap\").getDeclaredField( \"m\" );" + + "field.setAccessible( true );" + + // "java.lang.System.err.println(\"updating sync map field!\");" + + "return field;" + + "}", dynamicClass); + dynamicClass.addMethod(method); + + method = CtNewMethod.make( + "public static Object getSynchronizedMapField(java.lang.reflect.Field field, Object nativeComp) { " + + // "java.lang.System.err.println(\"Getting sync map field!\" + field.get(nativeComp));" + + "return field.get(nativeComp);" + + "}", dynamicClass); + dynamicClass.addMethod(method); + + dynamicClass.setModifiers(dynamicClass.getModifiers() & ~Modifier.STATIC); + + dynamicClass.getDeclaredMethods(); + + final byte[] dynamicClassBytes = dynamicClass.toBytecode(); + ClassUtils.defineClass(null, dynamicClassBytes); + } + + // fix the accessor class to point to the generated proxy/accessory class + { + CtClass classFixer = pool.get("dorkbox.serializers.SynchronizedCollectionJavaAccessor"); + + CtMethod ctMethod = classFixer.getDeclaredMethod("SynchronizedCollection_Field"); + ctMethod.setBody("{" + + "return java.util.SynchronizedCollectionsAccessory.getSynchronizedCollectionField($1);" + + "}"); + // perform pre-verification for the modified method + ctMethod.getMethodInfo().rebuildStackMapForME(pool); + + + ctMethod = classFixer.getDeclaredMethod("initSynchronizedMap_Field"); + ctMethod.setBody("{" + + "dorkbox.serializers.SynchronizedCollectionJavaAccessor.SOURCE_MAP_FIELD = java.util.SynchronizedCollectionsAccessory.initSynchronizedMapField();" + + "}"); + // perform pre-verification for the modified method + ctMethod.getMethodInfo().rebuildStackMapForME(pool); + + + ctMethod = classFixer.getDeclaredMethod("SynchronizedMap_Field"); + ctMethod.setBody("{" + + "return java.util.SynchronizedCollectionsAccessory.getSynchronizedMapField(dorkbox.serializers.SynchronizedCollectionJavaAccessor.SOURCE_MAP_FIELD, $1);" + + "}"); + // perform pre-verification for the modified method + ctMethod.getMethodInfo().rebuildStackMapForME(pool); + + + // perform pre-verification for the modified method + ctMethod.getMethodInfo().rebuildStackMapForME(pool); + + + final byte[] classFixerBytes = classFixer.toBytecode(); + ClassUtils.defineClass(ClassLoader.getSystemClassLoader(), classFixerBytes); + } + + + // setup reflection, but it's done from INSIDE THE SAME PACKAGE (so no warnings/etc) + SynchronizedCollectionJavaAccessor.initSynchronizedMap_Field(); + } catch ( final Exception e ) { - throw new RuntimeException( "Could not access source collection" + - " field in java.util.Collections$SynchronizedCollection.", e ); + throw new RuntimeException( "Could not modify SynchronizedCollection", e ); } } @@ -78,7 +177,7 @@ public class SynchronizedCollectionsSerializer extends Serializer { // the ordinal could be replaced by something else (e.g. a explicitly managed "id") output.writeInt( collection.ordinal(), true ); synchronized (object) { - kryo.writeClassAndObject( output, collection.sourceCollectionField.get( object ) ); + kryo.writeClassAndObject( output, collection.getValue( object ) ); } } catch ( final RuntimeException e ) { // Don't eat and wrap RuntimeExceptions because the ObjectBuffer.write... @@ -93,7 +192,7 @@ public class SynchronizedCollectionsSerializer extends Serializer { public Object copy(Kryo kryo, Object original) { try { final SynchronizedCollection collection = SynchronizedCollection.valueOfType( original.getClass() ); - Object sourceCollectionCopy = kryo.copy(collection.sourceCollectionField.get(original)); + Object sourceCollectionCopy = kryo.copy(collection.getValue(original)); return collection.create( sourceCollectionCopy ); } catch ( final RuntimeException e ) { // Don't eat and wrap RuntimeExceptions @@ -104,63 +203,89 @@ public class SynchronizedCollectionsSerializer extends Serializer { } private static enum SynchronizedCollection { - COLLECTION( Collections.synchronizedCollection( Arrays.asList( "" ) ).getClass(), SOURCE_COLLECTION_FIELD ){ + COLLECTION( Collections.synchronizedCollection( Arrays.asList( "" ) ).getClass() ){ @Override public Object create( final Object sourceCollection ) { return Collections.synchronizedCollection( (Collection) sourceCollection ); } + @Override + public Object getValue( final Object sourceCollection ) { + return SynchronizedCollectionJavaAccessor.SynchronizedCollection_Field(sourceCollection ); + } }, - RANDOM_ACCESS_LIST( Collections.synchronizedList( new ArrayList() ).getClass(), SOURCE_COLLECTION_FIELD ){ + RANDOM_ACCESS_LIST( Collections.synchronizedList( new ArrayList() ).getClass() ){ @Override public Object create( final Object sourceCollection ) { return Collections.synchronizedList( (List) sourceCollection ); } + @Override + public Object getValue( final Object sourceCollection ) { + return SynchronizedCollectionJavaAccessor.SynchronizedCollection_Field(sourceCollection ); + } }, - LIST( Collections.synchronizedList( new LinkedList() ).getClass(), SOURCE_COLLECTION_FIELD ){ + LIST( Collections.synchronizedList( new LinkedList() ).getClass() ){ @Override public Object create( final Object sourceCollection ) { return Collections.synchronizedList( (List) sourceCollection ); } + @Override + public Object getValue( final Object sourceCollection ) { + return SynchronizedCollectionJavaAccessor.SynchronizedCollection_Field(sourceCollection ); + } }, - SET( Collections.synchronizedSet( new HashSet() ).getClass(), SOURCE_COLLECTION_FIELD ){ + SET( Collections.synchronizedSet( new HashSet() ).getClass() ){ @Override public Object create( final Object sourceCollection ) { return Collections.synchronizedSet( (Set) sourceCollection ); } + @Override + public Object getValue( final Object sourceCollection ) { + return SynchronizedCollectionJavaAccessor.SynchronizedCollection_Field(sourceCollection ); + } }, - SORTED_SET( Collections.synchronizedSortedSet( new TreeSet() ).getClass(), SOURCE_COLLECTION_FIELD ){ + SORTED_SET( Collections.synchronizedSortedSet( new TreeSet() ).getClass() ){ @Override public Object create( final Object sourceCollection ) { return Collections.synchronizedSortedSet( (SortedSet) sourceCollection ); } + @Override + public Object getValue( final Object sourceCollection ) { + return SynchronizedCollectionJavaAccessor.SynchronizedCollection_Field(sourceCollection ); + } }, - MAP( Collections.synchronizedMap( new HashMap() ).getClass(), SOURCE_MAP_FIELD ) { + MAP( Collections.synchronizedMap( new HashMap() ).getClass() ) { @Override public Object create( final Object sourceCollection ) { return Collections.synchronizedMap( (Map) sourceCollection ); } - + @Override + public Object getValue( final Object sourceCollection ) { + return SynchronizedCollectionJavaAccessor.SynchronizedMap_Field(sourceCollection ); + } }, - SORTED_MAP( Collections.synchronizedSortedMap( new TreeMap() ).getClass(), SOURCE_MAP_FIELD ) { + SORTED_MAP( Collections.synchronizedSortedMap( new TreeMap() ).getClass() ) { @Override public Object create( final Object sourceCollection ) { return Collections.synchronizedSortedMap( (SortedMap) sourceCollection ); } + @Override + public Object getValue( final Object sourceCollection ) { + return SynchronizedCollectionJavaAccessor.SynchronizedMap_Field(sourceCollection ); + } }; private final Class type; - private final Field sourceCollectionField; - - private SynchronizedCollection( final Class type, final Field sourceCollectionField ) { + + private SynchronizedCollection( final Class type ) { this.type = type; - this.sourceCollectionField = sourceCollectionField; } /** * @param sourceCollection */ public abstract Object create( Object sourceCollection ); + public abstract Object getValue( Object sourceCollection ); static SynchronizedCollection valueOfType( final Class type ) { for( final SynchronizedCollection item : values() ) { diff --git a/src/dorkbox/serializers/UnmodifiableCollectionJavaAccessor.java b/src/dorkbox/serializers/UnmodifiableCollectionJavaAccessor.java new file mode 100644 index 0000000..ffd3f7e --- /dev/null +++ b/src/dorkbox/serializers/UnmodifiableCollectionJavaAccessor.java @@ -0,0 +1,36 @@ +/* + * Copyright 2023 dorkbox, llc + * + * 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 dorkbox.serializers; + +import java.lang.reflect.Field; + +public +class UnmodifiableCollectionJavaAccessor { + public static final Field SOURCE_MAP_FIELD = null; + + // the class methods here are rewritten using javassist. + public static Object UnmodifiableCollection_Field(Object nativeUnmodifiableCollection) { + return null; + } + + public static void initUnmodifiableMap_Field() { + } + + public static Object UnmodifiableMap_Field(Object nativeUnmodifiableMap) { + return null; + } +} diff --git a/src/dorkbox/serializers/UnmodifiableCollectionsSerializer.java b/src/dorkbox/serializers/UnmodifiableCollectionsSerializer.java index ff05f5c..e4e27ed 100644 --- a/src/dorkbox/serializers/UnmodifiableCollectionsSerializer.java +++ b/src/dorkbox/serializers/UnmodifiableCollectionsSerializer.java @@ -1,3 +1,19 @@ +/* + * Copyright 2023 dorkbox, llc + * + * 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. + */ + /* * Copyright 2010 Martin Grotzke * @@ -16,9 +32,7 @@ */ package dorkbox.serializers; -import java.lang.reflect.Field; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -37,31 +51,114 @@ import com.esotericsoftware.kryo.Serializer; import com.esotericsoftware.kryo.io.Input; import com.esotericsoftware.kryo.io.Output; +import dorkbox.jna.ClassUtils; +import javassist.ClassPool; +import javassist.CtClass; +import javassist.CtMethod; +import javassist.CtNewMethod; +import javassist.Modifier; + /** * A kryo {@link Serializer} for unmodifiable {@link Collection}s and {@link Map}s * created via {@link Collections}. * * @author Martin Grotzke + * @author Dorkbox llc */ public class UnmodifiableCollectionsSerializer extends Serializer { - - private static final Field SOURCE_COLLECTION_FIELD; - private static final Field SOURCE_MAP_FIELD; - - static { - try { - SOURCE_COLLECTION_FIELD = Class.forName("java.util.Collections$UnmodifiableCollection" ) - .getDeclaredField( "c" ); - SOURCE_COLLECTION_FIELD.setAccessible( true ); - - SOURCE_MAP_FIELD = Class.forName("java.util.Collections$UnmodifiableMap" ) - .getDeclaredField( "m" ); - SOURCE_MAP_FIELD.setAccessible( true ); + /** + * Gets the version number. + */ + public static + String getVersion() { + return "3.0"; + } + + + static { + // Add this project to the updates system, which verifies this class + UUID + version information + dorkbox.updates.Updates.INSTANCE.add(UnmodifiableCollectionsSerializer.class, "316353f5338341a8a3edc01d702703f8", getVersion()); + + try { + ClassPool pool = ClassPool.getDefault(); + + // allow non-reflection access to java.util.Collections...() + { + CtClass dynamicClass = pool.makeClass("java.util.UnmodifiableCollectionsAccessory"); + CtMethod method = CtNewMethod.make( + "public static Object getUnmodifiableCollectionField(Object nativeComp) { " + + // "java.lang.System.err.println(\"Getting collection field!\" + ((java.util.Collections$UnmodifiableCollection)nativeComp).c);" + + "return ((java.util.Collections$UnmodifiableCollection)nativeComp).c;" + + "}", dynamicClass); + dynamicClass.addMethod(method); + + method = CtNewMethod.make( + "public static java.lang.reflect.Field initUnmodifiableMapField() { " + + "java.lang.reflect.Field field = Class.forName(\"java.util.Collections$UnmodifiableMap\").getDeclaredField( \"m\" );" + + "field.setAccessible( true );" + + // "java.lang.System.err.println(\"updating map field!\");" + + "return field;" + + "}", dynamicClass); + dynamicClass.addMethod(method); + + method = CtNewMethod.make( + "public static Object getUnmodifiableMapField(java.lang.reflect.Field field, Object nativeComp) { " + + // "java.lang.System.err.println(\"Getting map field!\" + field.get(nativeComp));" + + "return field.get(nativeComp);" + + "}", dynamicClass); + dynamicClass.addMethod(method); + + dynamicClass.setModifiers(dynamicClass.getModifiers() & ~Modifier.STATIC); + + dynamicClass.getDeclaredMethods(); + + final byte[] dynamicClassBytes = dynamicClass.toBytecode(); + ClassUtils.defineClass(null, dynamicClassBytes); + } + + // fix the accessor class to point to the generated proxy/accessory class + { + CtClass classFixer = pool.get("dorkbox.serializers.UnmodifiableCollectionJavaAccessor"); + + CtMethod ctMethod = classFixer.getDeclaredMethod("UnmodifiableCollection_Field"); + ctMethod.setBody("{" + + "return java.util.UnmodifiableCollectionsAccessory.getUnmodifiableCollectionField($1);" + + "}"); + // perform pre-verification for the modified method + ctMethod.getMethodInfo().rebuildStackMapForME(pool); + + + ctMethod = classFixer.getDeclaredMethod("initUnmodifiableMap_Field"); + ctMethod.setBody("{" + + "dorkbox.serializers.UnmodifiableCollectionJavaAccessor.SOURCE_MAP_FIELD = java.util.UnmodifiableCollectionsAccessory.initUnmodifiableMapField();" + + "}"); + // perform pre-verification for the modified method + ctMethod.getMethodInfo().rebuildStackMapForME(pool); + + + ctMethod = classFixer.getDeclaredMethod("UnmodifiableMap_Field"); + ctMethod.setBody("{" + + "return java.util.UnmodifiableCollectionsAccessory.getUnmodifiableMapField(dorkbox.serializers.UnmodifiableCollectionJavaAccessor.SOURCE_MAP_FIELD, $1);" + + "}"); + // perform pre-verification for the modified method + ctMethod.getMethodInfo().rebuildStackMapForME(pool); + + + // perform pre-verification for the modified method + ctMethod.getMethodInfo().rebuildStackMapForME(pool); + + + final byte[] classFixerBytes = classFixer.toBytecode(); + ClassUtils.defineClass(ClassLoader.getSystemClassLoader(), classFixerBytes); + } + + + // setup reflection, but it's done from INSIDE THE SAME PACKAGE (so no warnings/etc) + UnmodifiableCollectionJavaAccessor.initUnmodifiableMap_Field(); } catch ( final Exception e ) { - throw new RuntimeException( "Could not access source collection" + - " field in java.util.Collections$UnmodifiableCollection.", e ); + throw new RuntimeException( "Could not modify UnmodifiableCollection", e ); } } @@ -78,9 +175,9 @@ public class UnmodifiableCollectionsSerializer extends Serializer { try { final UnmodifiableCollection unmodifiableCollection = UnmodifiableCollection.valueOfType( object.getClass() ); - // the ordinal could be replaced by something else (e.g. a explicitly managed "id") + // the ordinal could be replaced by something else (e.g. an explicitly managed "id") output.writeInt( unmodifiableCollection.ordinal(), true ); - kryo.writeClassAndObject( output, unmodifiableCollection.sourceCollectionField.get( object ) ); + kryo.writeClassAndObject( output, unmodifiableCollection.getValue(object) ); } catch ( final RuntimeException e ) { // Don't eat and wrap RuntimeExceptions because the ObjectBuffer.write... // handles SerializationException specifically (resizing the buffer)... @@ -92,76 +189,103 @@ public class UnmodifiableCollectionsSerializer extends Serializer { @Override public Object copy(Kryo kryo, Object original) { - try { - final UnmodifiableCollection unmodifiableCollection = UnmodifiableCollection.valueOfType( original.getClass() ); - Object sourceCollectionCopy = kryo.copy(unmodifiableCollection.sourceCollectionField.get(original)); - return unmodifiableCollection.create( sourceCollectionCopy ); - } catch ( final RuntimeException e ) { - // Don't eat and wrap RuntimeExceptions - throw e; - } catch ( final Exception e ) { - throw new RuntimeException( e ); - } + try { + final UnmodifiableCollection unmodifiableCollection = UnmodifiableCollection.valueOfType( original.getClass() ); + Object sourceCollectionCopy = kryo.copy(unmodifiableCollection.getValue(original)); + return unmodifiableCollection.create( sourceCollectionCopy ); + } catch ( final RuntimeException e ) { + // Don't eat and wrap RuntimeExceptions + throw e; + } catch ( final Exception e ) { + throw new RuntimeException( e ); + } } private enum UnmodifiableCollection { - COLLECTION( Collections.unmodifiableCollection( Arrays.asList( "" ) ).getClass(), SOURCE_COLLECTION_FIELD ){ + COLLECTION( Collections.unmodifiableCollection(Collections.singletonList("")).getClass() ){ @Override public Object create( final Object sourceCollection ) { return Collections.unmodifiableCollection( (Collection) sourceCollection ); } + @Override + public Object getValue( final Object sourceCollection ) { + return UnmodifiableCollectionJavaAccessor.UnmodifiableCollection_Field(sourceCollection); + } }, - RANDOM_ACCESS_LIST( Collections.unmodifiableList( new ArrayList() ).getClass(), SOURCE_COLLECTION_FIELD ){ + RANDOM_ACCESS_LIST( Collections.unmodifiableList( new ArrayList() ).getClass() ){ @Override public Object create( final Object sourceCollection ) { return Collections.unmodifiableList( (List) sourceCollection ); } + @Override + public Object getValue( final Object sourceCollection ) { + return UnmodifiableCollectionJavaAccessor.UnmodifiableCollection_Field(sourceCollection); + } }, - LIST( Collections.unmodifiableList( new LinkedList() ).getClass(), SOURCE_COLLECTION_FIELD ){ + LIST( Collections.unmodifiableList( new LinkedList() ).getClass() ){ @Override public Object create( final Object sourceCollection ) { return Collections.unmodifiableList( (List) sourceCollection ); } + @Override + public Object getValue( final Object sourceCollection ) { + return UnmodifiableCollectionJavaAccessor.UnmodifiableCollection_Field(sourceCollection); + } }, - SET( Collections.unmodifiableSet( new HashSet() ).getClass(), SOURCE_COLLECTION_FIELD ){ + SET( Collections.unmodifiableSet( new HashSet() ).getClass() ){ @Override public Object create( final Object sourceCollection ) { return Collections.unmodifiableSet( (Set) sourceCollection ); } + @Override + public Object getValue( final Object sourceCollection ) { + return UnmodifiableCollectionJavaAccessor.UnmodifiableCollection_Field(sourceCollection); + } }, - SORTED_SET( Collections.unmodifiableSortedSet( new TreeSet() ).getClass(), SOURCE_COLLECTION_FIELD ){ + SORTED_SET( Collections.unmodifiableSortedSet( new TreeSet() ).getClass() ){ @Override public Object create( final Object sourceCollection ) { return Collections.unmodifiableSortedSet( (SortedSet) sourceCollection ); } + @Override + public Object getValue( final Object sourceCollection ) { + return UnmodifiableCollectionJavaAccessor.UnmodifiableCollection_Field(sourceCollection); + } }, - MAP( Collections.unmodifiableMap( new HashMap() ).getClass(), SOURCE_MAP_FIELD ) { + MAP( Collections.unmodifiableMap( new HashMap() ).getClass() ) { @Override public Object create( final Object sourceCollection ) { return Collections.unmodifiableMap( (Map) sourceCollection ); } - + @Override + public Object getValue( final Object sourceCollection ) { + return UnmodifiableCollectionJavaAccessor.UnmodifiableMap_Field(sourceCollection); + } }, - SORTED_MAP( Collections.unmodifiableSortedMap( new TreeMap() ).getClass(), SOURCE_MAP_FIELD ) { + SORTED_MAP( Collections.unmodifiableSortedMap( new TreeMap() ).getClass() ) { @Override public Object create( final Object sourceCollection ) { return Collections.unmodifiableSortedMap( (SortedMap) sourceCollection ); } + @Override + public Object getValue( final Object sourceCollection ) { + return UnmodifiableCollectionJavaAccessor.UnmodifiableMap_Field(sourceCollection); + } }; private final Class type; - private final Field sourceCollectionField; - - UnmodifiableCollection( final Class type, final Field sourceCollectionField ) { + + UnmodifiableCollection( final Class type) { this.type = type; - this.sourceCollectionField = sourceCollectionField; } /** * @param sourceCollection */ public abstract Object create( Object sourceCollection ); + public abstract Object getValue( Object sourceCollection ); + static UnmodifiableCollection valueOfType( final Class type ) { for( final UnmodifiableCollection item : values() ) {