aboutsummaryrefslogtreecommitdiff
path: root/engine/src/networking/com/jme3/network/serializing/serializers/FieldSerializer.java
diff options
context:
space:
mode:
Diffstat (limited to 'engine/src/networking/com/jme3/network/serializing/serializers/FieldSerializer.java')
-rw-r--r--engine/src/networking/com/jme3/network/serializing/serializers/FieldSerializer.java191
1 files changed, 191 insertions, 0 deletions
diff --git a/engine/src/networking/com/jme3/network/serializing/serializers/FieldSerializer.java b/engine/src/networking/com/jme3/network/serializing/serializers/FieldSerializer.java
new file mode 100644
index 0000000..3f13c50
--- /dev/null
+++ b/engine/src/networking/com/jme3/network/serializing/serializers/FieldSerializer.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine, Java Game Networking
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.jme3.network.serializing.serializers;
+
+import com.jme3.network.serializing.Serializer;
+import com.jme3.network.serializing.SerializerException;
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.nio.BufferOverflowException;
+import java.nio.ByteBuffer;
+import java.util.*;
+import java.util.logging.Level;
+
+/**
+ * The field serializer is the default serializer used for custom class.
+ *
+ * @author Lars Wesselius, Nathan Sweet
+ */
+public class FieldSerializer extends Serializer {
+ private static Map<Class, SavedField[]> savedFields = new HashMap<Class, SavedField[]>();
+
+ protected void checkClass(Class clazz) {
+
+ // See if the class has a public no-arg constructor
+ try {
+ clazz.getConstructor();
+ } catch( NoSuchMethodException e ) {
+ throw new RuntimeException( "Registration error: no-argument constructor not found on:" + clazz );
+ }
+ }
+
+ public void initialize(Class clazz) {
+
+ checkClass(clazz);
+
+ List<Field> fields = new ArrayList<Field>();
+
+ Class processingClass = clazz;
+ while (processingClass != Object.class ) {
+ Collections.addAll(fields, processingClass.getDeclaredFields());
+ processingClass = processingClass.getSuperclass();
+ }
+
+ List<SavedField> cachedFields = new ArrayList<SavedField>(fields.size());
+ for (Field field : fields) {
+ int modifiers = field.getModifiers();
+ if (Modifier.isTransient(modifiers)) continue;
+ if (Modifier.isFinal(modifiers)) continue;
+ if (Modifier.isStatic(modifiers)) continue;
+ if (field.isSynthetic()) continue;
+ field.setAccessible(true);
+
+ SavedField cachedField = new SavedField();
+ cachedField.field = field;
+
+ if (Modifier.isFinal(field.getType().getModifiers())) {
+ // The type of this field is implicit in the outer class
+ // definition and because the type is final, it can confidently
+ // be determined on the other end.
+ // Note: passing false to this method has the side-effect that field.getType()
+ // will be registered as a real class that can then be read/written
+ // directly as any other registered class. It should be safe to take
+ // an ID like this because Serializer.initialize() is only called
+ // during registration... so this is like nested registration and
+ // doesn't have any ordering problems.
+ // ...well, as long as the order of fields is consistent from one
+ // end to the next.
+ cachedField.serializer = Serializer.getSerializer(field.getType(), false);
+ }
+
+ cachedFields.add(cachedField);
+ }
+
+ Collections.sort(cachedFields, new Comparator<SavedField>() {
+ public int compare (SavedField o1, SavedField o2) {
+ return o1.field.getName().compareTo(o2.field.getName());
+ }
+ });
+ savedFields.put(clazz, cachedFields.toArray(new SavedField[cachedFields.size()]));
+
+
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T> T readObject(ByteBuffer data, Class<T> c) throws IOException {
+
+ // Read the null/non-null marker
+ if (data.get() == 0x0)
+ return null;
+
+ SavedField[] fields = savedFields.get(c);
+
+ T object;
+ try {
+ object = c.newInstance();
+ } catch (Exception e) {
+ throw new SerializerException( "Error creating object of type:" + c, e );
+ }
+
+ for (SavedField savedField : fields) {
+ Field field = savedField.field;
+ Serializer serializer = savedField.serializer;
+ Object value;
+
+ if (serializer != null) {
+ value = serializer.readObject(data, field.getType());
+ } else {
+ value = Serializer.readClassAndObject(data);
+ }
+ try {
+ field.set(object, value);
+ } catch (IllegalAccessException e) {
+ throw new SerializerException( "Error reading object", e);
+ }
+ }
+ return object;
+ }
+
+ public void writeObject(ByteBuffer buffer, Object object) throws IOException {
+
+ // Add the null/non-null marker
+ buffer.put( (byte)(object != null ? 0x1 : 0x0) );
+ if (object == null) {
+ // Nothing left to do
+ return;
+ }
+
+ SavedField[] fields = savedFields.get(object.getClass());
+ if (fields == null)
+ throw new IOException("The " + object.getClass() + " is not registered"
+ + " in the serializer!");
+
+ for (SavedField savedField : fields) {
+ Object val = null;
+ try {
+ val = savedField.field.get(object);
+ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ }
+ Serializer serializer = savedField.serializer;
+
+ try {
+ if (serializer != null) {
+ serializer.writeObject(buffer, val);
+ } else {
+ Serializer.writeClassAndObject(buffer, val);
+ }
+ } catch (BufferOverflowException boe) {
+ throw boe;
+ } catch (Exception e) {
+ throw new SerializerException( "Error writing object for field:" + savedField.field, e );
+ }
+ }
+ }
+
+ private final class SavedField {
+ public Field field;
+ public Serializer serializer;
+ }
+}