Move the field cloning reponsibility to FieldCloner.

This commit is contained in:
Kristian S. Stangeland 2012-12-27 10:32:26 +01:00
parent 8b91e3034d
commit dea725e1e3
5 changed files with 49 additions and 37 deletions

View File

@ -213,13 +213,16 @@ class InjectedServerConnection {
*/
private static final long serialVersionUID = 2070481080950500367L;
// Object writer we'll use
private final ObjectWriter writer = new ObjectWriter();
@Override
protected void onReplacing(Object inserting, Object replacement) {
// Is this a normal Minecraft object?
if (!(inserting instanceof Factory)) {
// If so, copy the content of the old element to the new
try {
ObjectWriter.copyTo(inserting, replacement, inserting.getClass());
writer.copyTo(inserting, replacement, inserting.getClass());
} catch (Throwable e) {
reporter.reportDetailed(InjectedServerConnection.this, "Cannot copy old " + inserting +
" to new.", e, inserting, replacement);

View File

@ -66,6 +66,9 @@ public class NetworkServerInjector extends PlayerInjector {
// Whether or not the player has disconnected
private boolean hasDisconnected;
// Used to copy fields
private final ObjectWriter writer = new ObjectWriter();
public NetworkServerInjector(
ClassLoader classLoader, ErrorReporter reporter, Player player,
ListenerInvoker invoker, IntegerSet sendingFilters,
@ -248,7 +251,7 @@ public class NetworkServerInjector extends PlayerInjector {
@Override
protected void cleanHook() {
if (serverHandlerRef != null && serverHandlerRef.isCurrentSet()) {
ObjectWriter.copyTo(serverHandlerRef.getValue(), serverHandlerRef.getOldValue(), serverHandler.getClass());
writer.copyTo(serverHandlerRef.getValue(), serverHandlerRef.getOldValue(), serverHandler.getClass());
serverHandlerRef.revertValue();
try {

View File

@ -23,8 +23,6 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import com.comphenix.protocol.injector.StructureCache;
import com.comphenix.protocol.reflect.cloning.Cloner;
import com.comphenix.protocol.reflect.cloning.IdentityCloner;
import com.comphenix.protocol.utility.MinecraftReflection;
/**
@ -38,11 +36,6 @@ public class ObjectWriter {
private static ConcurrentMap<Class, StructureModifier<Object>> cache =
new ConcurrentHashMap<Class, StructureModifier<Object>>();
/**
* The default value cloner to use.
*/
private static final Cloner DEFAULT_CLONER = new IdentityCloner();
/**
* Retrieve a usable structure modifier for the given object type.
* <p>
@ -50,7 +43,7 @@ public class ObjectWriter {
* @param type - the type of the object we are modifying.
* @return A structure modifier for the given type.
*/
private static StructureModifier<Object> getModifier(Class<?> type) {
private StructureModifier<Object> getModifier(Class<?> type) {
Class<?> packetClass = MinecraftReflection.getPacketClass();
// Handle subclasses of the packet class with our custom structure cache
@ -82,26 +75,24 @@ public class ObjectWriter {
* @param destination - fields to copy to.
* @param commonType - type containing each field to copy.
*/
public static void copyTo(Object source, Object destination, Class<?> commonType) {
public void copyTo(Object source, Object destination, Class<?> commonType) {
// Note that we indicate that public fields will be copied the first time around
copyToInternal(source, destination, commonType, DEFAULT_CLONER, true);
copyToInternal(source, destination, commonType, true);
}
/**
* Copy every field in object A to object B. Each value is copied using the supplied cloner.
* <p>
* The two objects must have the same number of fields of the same type.
* @param source - fields to copy.
* @param destination - fields to copy to.
* @param commonType - type containing each field to copy.
* @param valueCloner - a object responsible for copying the content of each field.
* Called for every non-static field that will be copied.
* @param modifierSource - modifier for the original object.
* @param modifierDest - modifier for the new cloned object.
* @param fieldIndex - the current field index.
*/
public static void copyTo(Object source, Object destination, Class<?> commonType, Cloner valueCloner) {
copyToInternal(source, destination, commonType, valueCloner, true);
protected void transformField(StructureModifier<Object> modifierSource, StructureModifier<Object> modifierDest, int fieldIndex) {
Object value = modifierSource.read(fieldIndex);
modifierDest.write(fieldIndex, value);
}
// Internal method that will actually implement the recursion
private static void copyToInternal(Object source, Object destination, Class<?> commonType, Cloner valueCloner, boolean copyPublic) {
private void copyToInternal(Object source, Object destination, Class<?> commonType, boolean copyPublic) {
if (source == null)
throw new IllegalArgumentException("Source cannot be NULL");
if (destination == null)
@ -119,10 +110,9 @@ public class ObjectWriter {
Field field = modifierSource.getField(i);
int mod = field.getModifiers();
// Skip static fields. We also get the "public" field fairly often, so we'll skip that.
// Skip static fields. We also get the "public" fields fairly often, so we'll skip that.
if (!Modifier.isStatic(mod) && (!Modifier.isPublic(mod) || copyPublic)) {
Object value = modifierSource.read(i);
modifierDest.write(i, valueCloner.clone(value));
transformField(modifierSource, modifierDest, i);
}
}
@ -130,7 +120,7 @@ public class ObjectWriter {
Class<?> superclass = commonType.getSuperclass();
if (!superclass.equals(Object.class)) {
copyToInternal(source, destination, superclass, valueCloner, false);
copyToInternal(source, destination, superclass, false);
}
} catch (FieldAccessException e) {

View File

@ -1,6 +1,7 @@
package com.comphenix.protocol.reflect.cloning;
import com.comphenix.protocol.reflect.ObjectWriter;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.reflect.instances.InstanceProvider;
/**
@ -12,6 +13,9 @@ public class FieldCloner implements Cloner {
private final Cloner defaultCloner;
private final InstanceProvider instanceProvider;
// Used to clone objects
private final ObjectWriter writer;
/**
* Constructs a field cloner that copies objects by reading and writing the internal fields directly.
* @param defaultCloner - the default cloner used while copying fields.
@ -20,6 +24,16 @@ public class FieldCloner implements Cloner {
public FieldCloner(Cloner defaultCloner, InstanceProvider instanceProvider) {
this.defaultCloner = defaultCloner;
this.instanceProvider = instanceProvider;
// Remember to clone the value too
this.writer = new ObjectWriter() {
@Override
protected void transformField(StructureModifier<Object> modifierSource,
StructureModifier<Object> modifierDest, int fieldIndex) {
Object value = modifierSource.read(fieldIndex);
modifierDest.write(fieldIndex, getDefaultCloner().clone(value));
}
};
}
@Override
@ -38,8 +52,8 @@ public class FieldCloner implements Cloner {
Object copy = instanceProvider.create(source.getClass());
// Copy public and private fields alike. Skip static and transient fields.
ObjectWriter.copyTo(source, copy, source.getClass(), defaultCloner);
// Copy public and private fields alike. Skip static fields.
writer.copyTo(source, copy, source.getClass());
return copy;
}

View File

@ -369,6 +369,8 @@ public class PacketContainerTest {
private Object[] getArray(Object val) {
if (val instanceof Object[])
return (Object[]) val;
if (val == null)
return null;
int arrlength = Array.getLength(val);
Object[] outputArray = new Object[arrlength];