Add ability to allocate memory for an object without the use of the default constructor.
This commit is contained in:
parent
b649f2768c
commit
451a9dbe66
@ -16,11 +16,15 @@
|
|||||||
|
|
||||||
package com.google.gson;
|
package com.google.gson;
|
||||||
|
|
||||||
import java.lang.reflect.AccessibleObject;
|
import sun.misc.Unsafe;
|
||||||
|
|
||||||
import java.lang.reflect.Array;
|
import java.lang.reflect.Array;
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
|
import java.security.AccessController;
|
||||||
|
import java.security.PrivilegedAction;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
@ -36,6 +40,20 @@ import java.util.logging.Logger;
|
|||||||
*/
|
*/
|
||||||
final class MappedObjectConstructor implements ObjectConstructor {
|
final class MappedObjectConstructor implements ObjectConstructor {
|
||||||
private static final Logger log = Logger.getLogger(MappedObjectConstructor.class.getName());
|
private static final Logger log = Logger.getLogger(MappedObjectConstructor.class.getName());
|
||||||
|
private static final Unsafe THE_UNSAFE = AccessController.doPrivileged(
|
||||||
|
new PrivilegedAction<Unsafe>() {
|
||||||
|
public Unsafe run() {
|
||||||
|
try {
|
||||||
|
Field f = Unsafe.class.getDeclaredField("theUnsafe");
|
||||||
|
f.setAccessible(true);
|
||||||
|
return (Unsafe) f.get(null);
|
||||||
|
} catch (NoSuchFieldException e) {
|
||||||
|
throw new Error();
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new Error();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
private final ParameterizedTypeHandlerMap<InstanceCreator<?>> instanceCreatorMap;
|
private final ParameterizedTypeHandlerMap<InstanceCreator<?>> instanceCreatorMap;
|
||||||
|
|
||||||
@ -57,12 +75,13 @@ final class MappedObjectConstructor implements ObjectConstructor {
|
|||||||
return Array.newInstance(Types.getRawType(type), length);
|
return Array.newInstance(Types.getRawType(type), length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings({"unchecked", "cast"})
|
||||||
private <T> T constructWithNoArgConstructor(Type typeOfT) {
|
private <T> T constructWithNoArgConstructor(Type typeOfT) {
|
||||||
try {
|
try {
|
||||||
Constructor<T> constructor = getNoArgsConstructor(typeOfT);
|
Class<T> clazz = (Class<T>) Types.getRawType(typeOfT);
|
||||||
|
Constructor<T> constructor = getNoArgsConstructor(clazz);
|
||||||
if (constructor == null) {
|
if (constructor == null) {
|
||||||
throw new RuntimeException(("No-args constructor for " + typeOfT + " does not exist. "
|
return (T) THE_UNSAFE.allocateInstance(clazz);
|
||||||
+ "Register an InstanceCreator with Gson for this type to fix this problem."));
|
|
||||||
}
|
}
|
||||||
return constructor.newInstance();
|
return constructor.newInstance();
|
||||||
} catch (InstantiationException e) {
|
} catch (InstantiationException e) {
|
||||||
@ -77,17 +96,14 @@ final class MappedObjectConstructor implements ObjectConstructor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings({"unchecked", "cast"})
|
private <T> Constructor<T> getNoArgsConstructor(Class<T> clazz) {
|
||||||
private <T> Constructor<T> getNoArgsConstructor(Type typeOfT) {
|
try {
|
||||||
Class<?> clazz = Types.getRawType(typeOfT);
|
Constructor<T> declaredConstructor = clazz.getDeclaredConstructor();
|
||||||
Constructor<T>[] declaredConstructors = (Constructor<T>[]) clazz.getDeclaredConstructors();
|
declaredConstructor.setAccessible(true);
|
||||||
AccessibleObject.setAccessible(declaredConstructors, true);
|
return declaredConstructor;
|
||||||
for (Constructor<T> constructor : declaredConstructors) {
|
} catch (Exception e) {
|
||||||
if (constructor.getParameterTypes().length == 0) {
|
return null;
|
||||||
return constructor;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -0,0 +1,89 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 Google Inc.
|
||||||
|
*
|
||||||
|
* 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 com.google.gson;
|
||||||
|
|
||||||
|
import com.google.gson.common.TestTypes.CrazyLongTypeAdapter;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unit tests for the {@link MappedObjectConstructor} class.
|
||||||
|
*
|
||||||
|
* @author Joel Leitch
|
||||||
|
*/
|
||||||
|
public class MappedObjectConstructorTest extends TestCase {
|
||||||
|
private ParameterizedTypeHandlerMap<InstanceCreator<?>> creatorMap;
|
||||||
|
private MappedObjectConstructor constructor;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setUp() throws Exception {
|
||||||
|
super.setUp();
|
||||||
|
creatorMap = new ParameterizedTypeHandlerMap<InstanceCreator<?>>();
|
||||||
|
constructor = new MappedObjectConstructor(creatorMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testInstanceCreatorTakesTopPrecedence() throws Exception {
|
||||||
|
creatorMap.register(ObjectWithDefaultConstructor.class, new MyInstanceCreator());
|
||||||
|
ObjectWithDefaultConstructor obj =
|
||||||
|
constructor.construct(ObjectWithDefaultConstructor.class);
|
||||||
|
assertEquals("instanceCreator", obj.stringValue);
|
||||||
|
assertEquals(10, obj.intValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testNoInstanceCreatorInvokesDefaultConstructor() throws Exception {
|
||||||
|
ObjectWithDefaultConstructor expected = new ObjectWithDefaultConstructor();
|
||||||
|
ObjectWithDefaultConstructor obj =
|
||||||
|
constructor.construct(ObjectWithDefaultConstructor.class);
|
||||||
|
assertEquals(expected.stringValue, obj.stringValue);
|
||||||
|
assertEquals(expected.intValue, obj.intValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testNoDefaultConstructor() throws Exception {
|
||||||
|
ObjectNoDefaultConstructor obj = constructor.construct(ObjectNoDefaultConstructor.class);
|
||||||
|
assertNull(obj.stringValue);
|
||||||
|
assertEquals(0, obj.intValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class MyInstanceCreator
|
||||||
|
implements InstanceCreator<ObjectWithDefaultConstructor> {
|
||||||
|
public ObjectWithDefaultConstructor createInstance(Type type) {
|
||||||
|
return new ObjectWithDefaultConstructor("instanceCreator", 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ObjectWithDefaultConstructor {
|
||||||
|
public final String stringValue;
|
||||||
|
public final int intValue;
|
||||||
|
|
||||||
|
private ObjectWithDefaultConstructor() {
|
||||||
|
this("default", 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObjectWithDefaultConstructor(String stringValue, int intValue) {
|
||||||
|
this.stringValue = stringValue;
|
||||||
|
this.intValue = intValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ObjectNoDefaultConstructor extends ObjectWithDefaultConstructor {
|
||||||
|
public ObjectNoDefaultConstructor(String stringValue, int intValue) {
|
||||||
|
super(stringValue, intValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user