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.Field;
027    import java.lang.reflect.InvocationTargetException;
028    import java.util.ArrayList;
029    import java.util.List;
030    
031    import org.granite.messaging.jmf.codec.StandardCodec;
032    import org.granite.messaging.jmf.reflect.Reflection;
033    
034    /**
035     * @author Franck WOLFF
036     */
037    public 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    }