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