001 /*
002 GRANITE DATA SERVICES
003 Copyright (C) 2013 GRANITE DATA SERVICES S.A.S.
004
005 This file is part of Granite Data Services.
006
007 Granite Data Services is free software; you can redistribute it and/or modify
008 it under the terms of the GNU Library General Public License as published by
009 the Free Software Foundation; either version 2 of the License, or (at your
010 option) any later version.
011
012 Granite Data Services is distributed in the hope that it will be useful, but
013 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
014 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
015 for more details.
016
017 You should have received a copy of the GNU Library General Public License
018 along with this library; if not, see <http://www.gnu.org/licenses/>.
019 */
020
021 package org.granite.messaging.jmf;
022
023 import java.io.EOFException;
024 import java.io.IOException;
025 import java.io.InputStream;
026 import java.lang.reflect.InvocationTargetException;
027 import java.util.ArrayList;
028 import java.util.List;
029
030 import org.granite.messaging.annotations.Include;
031 import org.granite.messaging.jmf.codec.StandardCodec;
032 import org.granite.messaging.reflect.NoopWritableProperty;
033 import org.granite.messaging.reflect.Property;
034 import org.granite.messaging.reflect.Reflection;
035
036 /**
037 * @author Franck WOLFF
038 */
039 public class JMFDeserializer implements InputContext {
040
041 ///////////////////////////////////////////////////////////////////////////
042 // Fields
043
044 protected final List<String> storedStrings = new ArrayList<String>(256);
045 protected final List<Object> storedObjects = new ArrayList<Object>(256);
046
047 protected final InputStream inputStream;
048 protected final SharedContext context;
049
050 protected final CodecRegistry codecRegistry;
051
052 ///////////////////////////////////////////////////////////////////////////
053 // Initialization
054
055 public JMFDeserializer(InputStream is, SharedContext context) {
056 this.inputStream = is;
057 this.context = context;
058 this.codecRegistry = context.getCodecRegistry();
059
060 this.storedStrings.addAll(context.getDefaultStoredStrings());
061 }
062
063 ///////////////////////////////////////////////////////////////////////////
064 // ObjectInput implementation
065
066 public boolean readBoolean() throws IOException {
067 return codecRegistry.getBooleanCodec().decodePrimitive(this);
068 }
069
070 public byte readByte() throws IOException {
071 return codecRegistry.getByteCodec().decodePrimitive(this);
072 }
073
074 public int readUnsignedByte() throws IOException {
075 return readByte() & 0xFF;
076 }
077
078 public short readShort() throws IOException {
079 return codecRegistry.getShortCodec().decodePrimitive(this);
080 }
081
082 public int readUnsignedShort() throws IOException {
083 return readShort() & 0xFFFF;
084 }
085
086 public char readChar() throws IOException {
087 return codecRegistry.getCharacterCodec().decodePrimitive(this);
088 }
089
090 public int readInt() throws IOException {
091 return codecRegistry.getIntegerCodec().decodePrimitive(this);
092 }
093
094 public long readLong() throws IOException {
095 return codecRegistry.getLongCodec().decodePrimitive(this);
096 }
097
098 public float readFloat() throws IOException {
099 return codecRegistry.getFloatCodec().decodePrimitive(this);
100 }
101
102 public double readDouble() throws IOException {
103 return codecRegistry.getDoubleCodec().decodePrimitive(this);
104 }
105
106 public String readUTF() throws IOException {
107 int parameterizedJmfType = safeRead();
108
109 if (parameterizedJmfType == JMF_NULL)
110 return (String)codecRegistry.getNullCodec().decode(this, parameterizedJmfType);
111
112 return codecRegistry.getStringCodec().decode(this, parameterizedJmfType);
113 }
114
115 public Object readObject() throws ClassNotFoundException, IOException {
116 int parameterizedJmfType = safeRead();
117 int jmfType = codecRegistry.extractJmfType(parameterizedJmfType);
118
119 StandardCodec<?> codec = codecRegistry.getCodec(jmfType);
120 if (codec == null)
121 throw new JMFEncodingException("Unsupported JMF type: " + jmfType);
122
123 try {
124 return codec.decode(this, parameterizedJmfType);
125 }
126 catch (InvocationTargetException e) {
127 throw new IOException(e.getTargetException());
128 }
129 catch (IllegalAccessException e) {
130 throw new IOException(e);
131 }
132 catch (InstantiationException e) {
133 throw new IOException(e);
134 }
135 catch (NoSuchMethodException e) {
136 throw new IOException(e);
137 }
138 }
139
140 public int available() throws IOException {
141 return inputStream.available();
142 }
143
144 public void close() throws IOException {
145 inputStream.close();
146 }
147
148 ///////////////////////////////////////////////////////////////////////////
149 // ObjectInput implementation (unsupported, marked at deprecated)
150
151 @Deprecated
152 public int read() throws IOException {
153 throw new UnsupportedOperationException("Use readByte()");
154 }
155
156 @Deprecated
157 public int read(byte[] b) throws IOException {
158 throw new UnsupportedOperationException("Use (byte[])readObject()");
159 }
160
161 @Deprecated
162 public int read(byte[] b, int off, int len) throws IOException {
163 throw new UnsupportedOperationException("Use (byte[])readObject()");
164 }
165
166 @Deprecated
167 public void readFully(byte[] b) throws IOException {
168 throw new UnsupportedOperationException("Use (byte[])readObject()");
169 }
170
171 @Deprecated
172 public void readFully(byte[] b, int off, int len) throws IOException {
173 throw new UnsupportedOperationException("Use (byte[])readObject()");
174 }
175
176 @Deprecated
177 public String readLine() throws IOException {
178 throw new UnsupportedOperationException("Use readUTF()");
179 }
180
181 @Deprecated
182 public int skipBytes(int n) throws IOException {
183 throw new UnsupportedOperationException();
184 }
185
186 @Deprecated
187 public long skip(long n) throws IOException {
188 throw new UnsupportedOperationException();
189 }
190
191 ///////////////////////////////////////////////////////////////////////////
192 // InputContext implementation
193
194 public SharedContext getSharedContext() {
195 return context;
196 }
197
198 public InputStream getInputStream() {
199 return inputStream;
200 }
201
202 public int safeRead() throws IOException {
203 int b = inputStream.read();
204 if (b == -1)
205 throw new EOFException();
206 return b;
207 }
208
209 public void safeReadFully(byte[] b) throws IOException {
210 safeReadFully(b, 0, b.length);
211 }
212
213 public void safeReadFully(byte[] b, int off, int len) throws IOException {
214 if (off < 0 || len < 0 || off + len > b.length)
215 throw new IndexOutOfBoundsException("b.length=" + b.length + ", off=" + off + ", len" + len);
216
217 if (len == 0)
218 return;
219
220 do {
221 int read = inputStream.read(b, off, len);
222 if (read == -1)
223 throw new EOFException();
224 off += read;
225 len -= read;
226 }
227 while (len > 0);
228 }
229
230 public void safeSkip(long n) throws IOException {
231 while (n > 0) {
232 if (inputStream.read() == -1)
233 throw new EOFException();
234 n--;
235 }
236 }
237
238 public int addSharedString(String s) {
239 int index = storedStrings.size();
240 storedStrings.add(index, s);
241 return index;
242 }
243
244 public String getSharedString(int index) {
245 return storedStrings.get(index);
246 }
247
248 public int addSharedObject(Object o) {
249 int index = storedObjects.size();
250 storedObjects.add(index, o);
251 return index;
252 }
253
254 public Object getSharedObject(int index) {
255 Object o = storedObjects.get(index);
256 if (o instanceof UnresolvedSharedObject)
257 throw new JMFUnresolvedSharedObjectException("Unresolved shared object: " + o);
258 return o;
259 }
260
261 public int addUnresolvedSharedObject(String className) {
262 int index = storedObjects.size();
263 storedObjects.add(index, new UnresolvedSharedObject(className, index));
264 return index;
265 }
266
267 public Object setUnresolvedSharedObject(int index, Object o) {
268 Object uso = storedObjects.set(index, o);
269 if (!(uso instanceof UnresolvedSharedObject))
270 throw new JMFUnresolvedSharedObjectException("Not an unresolved shared object: " + uso);
271 return uso;
272 }
273
274 ///////////////////////////////////////////////////////////////////////////
275 // ExtendedObjectInput implementation
276
277 public Reflection getReflection() {
278 return context.getReflection();
279 }
280
281 public String getAlias(String remoteAlias) {
282 return context.getClassName(remoteAlias);
283 }
284
285 public void readAndSetProperty(Object obj, Property property) throws IOException, ClassNotFoundException, IllegalAccessException, InvocationTargetException {
286 if (property.isAnnotationPresent(Include.class) && !property.isWritable())
287 property = new NoopWritableProperty(property.getName(), property.getType());
288
289 if (property.getType().isPrimitive())
290 codecRegistry.getPrimitivePropertyCodec(property.getType()).decodePrimitive(this, obj, property);
291 else
292 property.setObject(obj, readObject());
293 }
294
295 static class UnresolvedSharedObject {
296
297 private final String className;
298 private final int index;
299
300 public UnresolvedSharedObject(String className, int index) {
301 this.className = className;
302 this.index = index;
303 }
304
305 public String getClassName() {
306 return className;
307 }
308
309 public int getIndex() {
310 return index;
311 }
312
313 @Override
314 public String toString() {
315 return UnresolvedSharedObject.class.getName() + " {className=" + className + ", index=" + index + "}";
316 }
317 }
318 }