Home | History | Annotate | Download | only in serializers
      1 /*
      2  * Copyright (c) 2009-2010 jMonkeyEngine, Java Game Networking
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are
      7  * met:
      8  *
      9  * * Redistributions of source code must retain the above copyright
     10  *   notice, this list of conditions and the following disclaimer.
     11  *
     12  * * Redistributions in binary form must reproduce the above copyright
     13  *   notice, this list of conditions and the following disclaimer in the
     14  *   documentation and/or other materials provided with the distribution.
     15  *
     16  * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
     17  *   may be used to endorse or promote products derived from this software
     18  *   without specific prior written permission.
     19  *
     20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     23  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     27  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
     28  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
     29  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
     30  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     31  */
     32 
     33 package com.jme3.network.serializing.serializers;
     34 
     35 import com.jme3.network.serializing.Serializer;
     36 import com.jme3.network.serializing.SerializerException;
     37 import java.io.IOException;
     38 import java.lang.reflect.Field;
     39 import java.lang.reflect.Modifier;
     40 import java.nio.BufferOverflowException;
     41 import java.nio.ByteBuffer;
     42 import java.util.*;
     43 import java.util.logging.Level;
     44 
     45 /**
     46  * The field serializer is the default serializer used for custom class.
     47  *
     48  * @author Lars Wesselius, Nathan Sweet
     49  */
     50 public class FieldSerializer extends Serializer {
     51     private static Map<Class, SavedField[]> savedFields = new HashMap<Class, SavedField[]>();
     52 
     53     protected void checkClass(Class clazz) {
     54 
     55         // See if the class has a public no-arg constructor
     56         try {
     57             clazz.getConstructor();
     58         } catch( NoSuchMethodException e ) {
     59             throw new RuntimeException( "Registration error: no-argument constructor not found on:" + clazz );
     60         }
     61     }
     62 
     63     public void initialize(Class clazz) {
     64 
     65         checkClass(clazz);
     66 
     67         List<Field> fields = new ArrayList<Field>();
     68 
     69         Class processingClass = clazz;
     70         while (processingClass != Object.class ) {
     71             Collections.addAll(fields, processingClass.getDeclaredFields());
     72             processingClass = processingClass.getSuperclass();
     73         }
     74 
     75         List<SavedField> cachedFields = new ArrayList<SavedField>(fields.size());
     76         for (Field field : fields) {
     77             int modifiers = field.getModifiers();
     78             if (Modifier.isTransient(modifiers)) continue;
     79             if (Modifier.isFinal(modifiers)) continue;
     80             if (Modifier.isStatic(modifiers)) continue;
     81             if (field.isSynthetic()) continue;
     82             field.setAccessible(true);
     83 
     84             SavedField cachedField = new SavedField();
     85             cachedField.field = field;
     86 
     87             if (Modifier.isFinal(field.getType().getModifiers())) {
     88                 // The type of this field is implicit in the outer class
     89                 // definition and because the type is final, it can confidently
     90                 // be determined on the other end.
     91                 // Note: passing false to this method has the side-effect that field.getType()
     92                 // will be registered as a real class that can then be read/written
     93                 // directly as any other registered class.  It should be safe to take
     94                 // an ID like this because Serializer.initialize() is only called
     95                 // during registration... so this is like nested registration and
     96                 // doesn't have any ordering problems.
     97                 // ...well, as long as the order of fields is consistent from one
     98                 // end to the next.
     99                 cachedField.serializer = Serializer.getSerializer(field.getType(), false);
    100             }
    101 
    102             cachedFields.add(cachedField);
    103         }
    104 
    105         Collections.sort(cachedFields, new Comparator<SavedField>() {
    106             public int compare (SavedField o1, SavedField o2) {
    107                     return o1.field.getName().compareTo(o2.field.getName());
    108             }
    109         });
    110         savedFields.put(clazz, cachedFields.toArray(new SavedField[cachedFields.size()]));
    111 
    112 
    113     }
    114 
    115     @SuppressWarnings("unchecked")
    116     public <T> T readObject(ByteBuffer data, Class<T> c) throws IOException {
    117 
    118         // Read the null/non-null marker
    119         if (data.get() == 0x0)
    120             return null;
    121 
    122         SavedField[] fields = savedFields.get(c);
    123 
    124         T object;
    125         try {
    126             object = c.newInstance();
    127         } catch (Exception e) {
    128             throw new SerializerException( "Error creating object of type:" + c, e );
    129         }
    130 
    131         for (SavedField savedField : fields) {
    132             Field field = savedField.field;
    133             Serializer serializer = savedField.serializer;
    134             Object value;
    135 
    136             if (serializer != null) {
    137                 value = serializer.readObject(data, field.getType());
    138             } else {
    139                 value = Serializer.readClassAndObject(data);
    140             }
    141             try {
    142                 field.set(object, value);
    143             } catch (IllegalAccessException e) {
    144                 throw new SerializerException( "Error reading object", e);
    145             }
    146         }
    147         return object;
    148     }
    149 
    150     public void writeObject(ByteBuffer buffer, Object object) throws IOException {
    151 
    152         // Add the null/non-null marker
    153         buffer.put( (byte)(object != null ? 0x1 : 0x0) );
    154         if (object == null) {
    155             // Nothing left to do
    156             return;
    157         }
    158 
    159         SavedField[] fields = savedFields.get(object.getClass());
    160         if (fields == null)
    161             throw new IOException("The " + object.getClass() + " is not registered"
    162                                 + " in the serializer!");
    163 
    164         for (SavedField savedField : fields) {
    165             Object val = null;
    166             try {
    167                 val = savedField.field.get(object);
    168             } catch (IllegalAccessException e) {
    169                 e.printStackTrace();
    170             }
    171             Serializer serializer = savedField.serializer;
    172 
    173             try {
    174                 if (serializer != null) {
    175                     serializer.writeObject(buffer, val);
    176                 } else {
    177                     Serializer.writeClassAndObject(buffer, val);
    178                 }
    179             } catch (BufferOverflowException boe) {
    180                 throw boe;
    181             } catch (Exception e) {
    182                 throw new SerializerException( "Error writing object for field:" + savedField.field, e );
    183             }
    184         }
    185     }
    186 
    187     private final class SavedField {
    188         public Field field;
    189         public Serializer serializer;
    190     }
    191 }
    192