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
021package org.granite.messaging.jmf;
022
023import java.io.EOFException;
024import java.io.IOException;
025import java.io.InputStream;
026import java.lang.reflect.Field;
027import java.lang.reflect.InvocationTargetException;
028import java.util.ArrayList;
029import java.util.List;
030
031import org.granite.messaging.jmf.codec.StandardCodec;
032import org.granite.messaging.jmf.reflect.Reflection;
033
034/**
035 * @author Franck WOLFF
036 */
037public class JMFDeserializer implements InputContext {
038        
039        ///////////////////////////////////////////////////////////////////////////
040        // Fields
041        
042        protected final List<String> storedStrings = new ArrayList<String>(256);
043        protected final List<Object> storedObjects = new ArrayList<Object>(256);
044        
045    protected final InputStream inputStream;
046    protected final SharedContext context;
047    
048    protected final CodecRegistry codecRegistry;
049        
050        ///////////////////////////////////////////////////////////////////////////
051        // Initialization
052        
053        public JMFDeserializer(InputStream is, SharedContext context) {
054                this.inputStream = is;
055                this.context = context;
056                this.codecRegistry = context.getCodecRegistry();
057                
058                this.storedStrings.addAll(context.getDefaultStoredStrings());
059        }
060        
061        ///////////////////////////////////////////////////////////////////////////
062        // ObjectInput implementation
063
064        public boolean readBoolean() throws IOException {
065                return codecRegistry.getBooleanCodec().decodePrimitive(this);
066        }
067
068        public byte readByte() throws IOException {
069                return codecRegistry.getByteCodec().decodePrimitive(this);
070        }
071
072        public int readUnsignedByte() throws IOException {
073                return readByte() & 0xFF;
074        }
075
076        public short readShort() throws IOException {
077                return codecRegistry.getShortCodec().decodePrimitive(this);
078        }
079
080        public int readUnsignedShort() throws IOException {
081                return readShort() & 0xFFFF;
082        }
083
084        public char readChar() throws IOException {
085                return codecRegistry.getCharacterCodec().decodePrimitive(this);
086        }
087
088        public int readInt() throws IOException {
089                return codecRegistry.getIntegerCodec().decodePrimitive(this);
090        }
091
092        public long readLong() throws IOException {
093                return codecRegistry.getLongCodec().decodePrimitive(this);
094        }
095
096        public float readFloat() throws IOException {
097                return codecRegistry.getFloatCodec().decodePrimitive(this);
098        }
099
100        public double readDouble() throws IOException {
101                return codecRegistry.getDoubleCodec().decodePrimitive(this);
102        }
103
104        public String readUTF() throws IOException {
105                int parameterizedJmfType = safeRead();
106                
107                if (parameterizedJmfType == JMF_NULL)
108                        return (String)codecRegistry.getNullCodec().decode(this, parameterizedJmfType);
109                
110                return codecRegistry.getStringCodec().decode(this, parameterizedJmfType);
111        }
112
113        public Object readObject() throws ClassNotFoundException, IOException {
114                int parameterizedJmfType = safeRead();
115                int jmfType = codecRegistry.extractJmfType(parameterizedJmfType);
116                
117                StandardCodec<?> codec = codecRegistry.getCodec(jmfType);
118                if (codec == null)
119                        throw new JMFEncodingException("Unsupported JMF type: " + jmfType);
120                
121                try {
122                        return codec.decode(this, parameterizedJmfType);
123                }
124                catch (InvocationTargetException e) {
125                        throw new IOException(e.getTargetException());
126                }
127                catch (IllegalAccessException e) {
128                        throw new IOException(e);
129                }
130                catch (InstantiationException e) {
131                        throw new IOException(e);
132                }
133                catch (NoSuchMethodException e) {
134                        throw new IOException(e);
135                }
136        }
137
138        public int available() throws IOException {
139                return inputStream.available();
140        }
141
142        public void close() throws IOException {
143                inputStream.close();
144        }
145        
146        ///////////////////////////////////////////////////////////////////////////
147        // ObjectInput implementation (unsupported, marked at deprecated)
148
149        @Deprecated
150        public int read() throws IOException {
151                throw new UnsupportedOperationException("Use readByte()");
152        }
153
154        @Deprecated
155        public int read(byte[] b) throws IOException {
156                throw new UnsupportedOperationException("Use (byte[])readObject()");
157        }
158
159        @Deprecated
160        public int read(byte[] b, int off, int len) throws IOException {
161                throw new UnsupportedOperationException("Use (byte[])readObject()");
162        }
163        
164        @Deprecated
165        public void readFully(byte[] b) throws IOException {
166                throw new UnsupportedOperationException("Use (byte[])readObject()");
167        }
168
169        @Deprecated
170        public void readFully(byte[] b, int off, int len) throws IOException {
171                throw new UnsupportedOperationException("Use (byte[])readObject()");
172        }
173        
174        @Deprecated
175        public String readLine() throws IOException {
176                throw new UnsupportedOperationException("Use readUTF()");
177        }
178
179        @Deprecated
180        public int skipBytes(int n) throws IOException {
181                throw new UnsupportedOperationException();
182        }
183
184        @Deprecated
185        public long skip(long n) throws IOException {
186                throw new UnsupportedOperationException();
187        }
188        
189        ///////////////////////////////////////////////////////////////////////////
190        // InputContext implementation
191
192        public SharedContext getSharedContext() {
193                return context;
194        }
195        
196        public InputStream getInputStream() {
197                return inputStream;
198        }
199
200        public int safeRead() throws IOException {
201                int b = inputStream.read();
202                if (b == -1)
203                        throw new EOFException();
204                return b;
205        }
206        
207        public void safeReadFully(byte[] b) throws IOException {
208                safeReadFully(b, 0, b.length);
209        }
210
211        public void safeReadFully(byte[] b, int off, int len) throws IOException {
212                if (off < 0 || len < 0 || off + len > b.length)
213                        throw new IndexOutOfBoundsException("b.length=" + b.length + ", off=" + off + ", len" + len);
214                
215                if (len == 0)
216                        return;
217                
218                do {
219                        int read = inputStream.read(b, off, len);
220                        if (read == -1)
221                                throw new EOFException();
222                        off += read;
223                        len -= read;
224                }
225                while (len > 0);
226        }
227
228        public void safeSkip(long n) throws IOException {
229                while (n > 0) {
230                        if (inputStream.read() == -1)
231                                throw new EOFException();
232                        n--;
233                }
234        }
235
236        public int addSharedString(String s) {
237                int index = storedStrings.size();
238                storedStrings.add(index, s);
239                return index;
240        }
241        
242        public String getSharedString(int index) {
243                return storedStrings.get(index);
244        }
245        
246        public int addSharedObject(Object o) {
247                int index = storedObjects.size();
248                storedObjects.add(index, o);
249                return index;
250        }
251        
252        public Object getSharedObject(int index) {
253                Object o = storedObjects.get(index);
254                if (o instanceof UnresolvedSharedObject)
255                        throw new JMFUnresolvedSharedObjectException("Unresolved shared object: " + o);
256                return o;
257        }
258        
259        public int addUnresolvedSharedObject(String className) {
260                int index = storedObjects.size();
261                storedObjects.add(index, new UnresolvedSharedObject(className, index));
262                return index;
263        }
264        
265        public Object setUnresolvedSharedObject(int index, Object o) {
266                Object uso = storedObjects.set(index, o);
267                if (!(uso instanceof UnresolvedSharedObject))
268                        throw new JMFUnresolvedSharedObjectException("Not an unresolved shared object: " + uso);
269                return uso;
270        }
271        
272        ///////////////////////////////////////////////////////////////////////////
273        // ExtendedObjectInput implementation
274
275        public Reflection getReflection() {
276                return context.getReflection();
277        }
278
279        public String getAlias(String className) {
280                return context.getAlias(className);
281        }
282
283        public void readAndSetField(Object obj, Field field) throws IOException, ClassNotFoundException, IllegalAccessException {
284                if (field.getType().isPrimitive())
285                        codecRegistry.getPrimitiveFieldCodec(field.getType()).decodePrimitive(this, obj, field);
286                else
287                        field.set(obj, readObject());
288        }
289        
290        static class UnresolvedSharedObject {
291                
292                private final String className;
293                private final int index;
294
295                public UnresolvedSharedObject(String className, int index) {
296                        this.className = className;
297                        this.index = index;
298                }
299
300                public String getClassName() {
301                        return className;
302                }
303
304                public int getIndex() {
305                        return index;
306                }
307
308                @Override
309                public String toString() {
310                        return UnresolvedSharedObject.class.getName() + " {className=" + className + ", index=" + index + "}";
311                }
312        }
313}