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