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    }