001/** 002 * GRANITE DATA SERVICES 003 * Copyright (C) 2006-2013 GRANITE DATA SERVICES S.A.S. 004 * 005 * This file is part of the Granite Data Services Platform. 006 * 007 * Granite Data Services is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * Granite Data Services is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 015 * General Public License for more details. 016 * 017 * You should have received a copy of the GNU Lesser General Public 018 * License along with this library; if not, write to the Free Software 019 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 020 * USA, or see <http://www.gnu.org/licenses/>. 021 */ 022package org.granite.messaging.jmf; 023 024import java.io.IOException; 025import java.io.OutputStream; 026import java.lang.reflect.InvocationTargetException; 027import java.util.Comparator; 028import java.util.HashMap; 029import java.util.IdentityHashMap; 030import java.util.Map; 031import java.util.Map.Entry; 032import java.util.TreeSet; 033 034import org.granite.messaging.jmf.codec.StandardCodec; 035import org.granite.messaging.reflect.Property; 036import org.granite.messaging.reflect.Reflection; 037 038/** 039 * @author Franck WOLFF 040 */ 041public class JMFSerializer implements OutputContext { 042 043 /////////////////////////////////////////////////////////////////////////// 044 // Fields 045 046 protected final Map<String, Integer> classNames = new HashMap<String, Integer>(256); 047 protected final Map<String, Integer> strings = new HashMap<String, Integer>(256); 048 protected final Map<Object, Integer> objects = new IdentityHashMap<Object, Integer>(256); 049 050 protected final OutputStream outputStream; 051 protected final SharedContext context; 052 053 protected final CodecRegistry codecRegistry; 054 055 /////////////////////////////////////////////////////////////////////////// 056 // Initialization 057 058 public JMFSerializer(OutputStream outputStream, SharedContext context) { 059 this.outputStream = outputStream; 060 this.codecRegistry = context.getCodecRegistry(); 061 this.context = context; 062 063 for (String s : context.getInitialClassNameDictionary()) 064 addToClassNames(s); 065 } 066 067 /////////////////////////////////////////////////////////////////////////// 068 // ObjectOutput implementation 069 070 public void writeBoolean(boolean v) throws IOException { 071 codecRegistry.getBooleanCodec().encodePrimitive(this, v); 072 } 073 074 public void writeByte(int v) throws IOException { 075 codecRegistry.getByteCodec().encodePrimitive(this, v); 076 } 077 078 public void writeShort(int v) throws IOException { 079 codecRegistry.getShortCodec().encodePrimitive(this, v); 080 } 081 082 public void writeChar(int v) throws IOException { 083 codecRegistry.getCharacterCodec().encodePrimitive(this, v); 084 } 085 086 public void writeInt(int v) throws IOException { 087 codecRegistry.getIntegerCodec().encodePrimitive(this, v); 088 } 089 090 public void writeLong(long v) throws IOException { 091 codecRegistry.getLongCodec().encodePrimitive(this, v); 092 } 093 094 public void writeFloat(float v) throws IOException { 095 codecRegistry.getFloatCodec().encodePrimitive(this, v); 096 } 097 098 public void writeDouble(double v) throws IOException { 099 codecRegistry.getDoubleCodec().encodePrimitive(this, v); 100 } 101 102 public void writeUTF(String s) throws IOException { 103 if (s == null) 104 codecRegistry.getNullCodec().encode(this, s); 105 else 106 codecRegistry.getStringCodec().encode(this, s); 107 } 108 109 public void writeObject(Object obj) throws IOException { 110 StandardCodec<Object> codec = codecRegistry.getCodec(obj); 111 if (codec == null) 112 throw new JMFEncodingException("Unsupported Java class: " + obj); 113 114 try { 115 codec.encode(this, obj); 116 } 117 catch (IllegalAccessException e) { 118 throw new IOException(e); 119 } 120 catch (InvocationTargetException e) { 121 throw new IOException(e); 122 } 123 } 124 125 public void flush() throws IOException { 126 outputStream.flush(); 127 } 128 129 public void close() throws IOException { 130 outputStream.close(); 131 } 132 133 /////////////////////////////////////////////////////////////////////////// 134 // ObjectOutput implementation (unsupported, marked at deprecated) 135 136 @Deprecated 137 public void write(int b) throws IOException { 138 throw new UnsupportedOperationException("Use writeByte(b)"); 139 } 140 141 @Deprecated 142 public void write(byte[] b) throws IOException { 143 throw new UnsupportedOperationException("Use writeObject(b)"); 144 } 145 146 @Deprecated 147 public void write(byte[] b, int off, int len) throws IOException { 148 throw new UnsupportedOperationException("Use writeObject(Arrays.copyOfRange(b, off, off+len))"); 149 } 150 151 @Deprecated 152 public void writeBytes(String s) throws IOException { 153 throw new UnsupportedOperationException("Use writeUTF(s)"); 154 } 155 156 @Deprecated 157 public void writeChars(String s) throws IOException { 158 throw new UnsupportedOperationException("Use writeUTF(s)"); 159 } 160 161 /////////////////////////////////////////////////////////////////////////// 162 // OutputContext implementation 163 164 public SharedContext getSharedContext() { 165 return context; 166 } 167 168 public OutputStream getOutputStream() { 169 return outputStream; 170 } 171 172 @Override 173 public void addToClassNames(String className) { 174 if (className != null && !classNames.containsKey(className)) { 175 Integer index = Integer.valueOf(classNames.size()); 176 classNames.put(className, index); 177 } 178 } 179 180 @Override 181 public int indexOfClassName(String className) { 182 if (className != null) { 183 Integer index = classNames.get(className); 184 if (index != null) 185 return index.intValue(); 186 } 187 return -1; 188 } 189 190 public void addToStrings(String s) { 191 if (s != null && !strings.containsKey(s)) { 192 Integer index = Integer.valueOf(strings.size()); 193 strings.put(s, index); 194 } 195 } 196 197 public int indexOfString(String s) { 198 if (s != null) { 199 Integer index = strings.get(s); 200 if (index != null) 201 return index.intValue(); 202 } 203 return -1; 204 } 205 206 public void addToObjects(Object o) { 207 if (o != null && !objects.containsKey(o)) { 208 Integer index = Integer.valueOf(objects.size()); 209 objects.put(o, index); 210 } 211 } 212 213 public int indexOfObject(Object o) { 214 if (o != null) { 215 Integer index = objects.get(o); 216 if (index != null) 217 return index.intValue(); 218 } 219 return -1; 220 } 221 222 /////////////////////////////////////////////////////////////////////////// 223 // ExtendedObjectOutput implementation 224 225 public Reflection getReflection() { 226 return context.getReflection(); 227 } 228 229 public String getAlias(String className) { 230 return context.getRemoteAlias(className); 231 } 232 233 public void getAndWriteProperty(Object obj, Property property) throws IOException, IllegalAccessException, InvocationTargetException { 234 if (property.getType().isPrimitive()) 235 codecRegistry.getPrimitivePropertyCodec(property.getType()).encodePrimitive(this, obj, property); 236 else 237 writeObject(property.getObject(obj)); 238 } 239 240 /////////////////////////////////////////////////////////////////////////// 241 // Debug 242 243 public String toDumpString() { 244 final Comparator<Map.Entry<?, Integer>> comparator = new Comparator<Map.Entry<?, Integer>>() { 245 @Override 246 public int compare(Entry<?, Integer> o1, Entry<?, Integer> o2) { 247 return o1.getValue().compareTo(o2.getValue()); 248 } 249 }; 250 251 StringBuilder sb = new StringBuilder(getClass().getName()); 252 sb.append(" {\n"); 253 254 TreeSet<Map.Entry<String, Integer>> setStringInteger = new TreeSet<Map.Entry<String, Integer>>(comparator); 255 setStringInteger.addAll(classNames.entrySet()); 256 sb.append(" classNames=[\n"); 257 for (Map.Entry<String, Integer> entry : setStringInteger) 258 sb.append(" ").append(entry.getValue()).append(": \"").append(entry.getKey()).append("\"\n"); 259 sb.append(" ],\n"); 260 261 setStringInteger = new TreeSet<Map.Entry<String, Integer>>(comparator); 262 setStringInteger.addAll(strings.entrySet()); 263 sb.append(" strings=[\n"); 264 for (Map.Entry<String, Integer> entry : setStringInteger) 265 sb.append(" ").append(entry.getValue()).append(": \"").append(entry.getKey()).append("\"\n"); 266 sb.append(" ],\n"); 267 268 TreeSet<Map.Entry<Object, Integer>> setObjectInteger = new TreeSet<Map.Entry<Object, Integer>>(comparator); 269 setObjectInteger.addAll(objects.entrySet()); 270 sb.append(" objects=[\n"); 271 for (Map.Entry<Object, Integer> entry : setObjectInteger) { 272 sb.append(" ").append(entry.getValue()).append(": ").append(entry.getKey().getClass().getName()) 273 .append("@").append(System.identityHashCode(entry.getKey())).append("\n"); 274 } 275 sb.append(" ]\n"); 276 277 sb.append("}"); 278 return sb.toString(); 279 } 280}