• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
checkClass(Class clazz)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 
initialize(Class clazz)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")
readObject(ByteBuffer data, Class<T> c)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 
writeObject(ByteBuffer buffer, Object object)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