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 */
022package org.granite.messaging.jmf;
023
024import java.io.EOFException;
025import java.io.IOException;
026import java.io.InputStream;
027import java.lang.reflect.InvocationTargetException;
028import java.util.ArrayList;
029import java.util.List;
030
031import org.granite.messaging.annotations.Include;
032import org.granite.messaging.jmf.codec.StandardCodec;
033import org.granite.messaging.reflect.NoopWritableProperty;
034import org.granite.messaging.reflect.Property;
035import org.granite.messaging.reflect.Reflection;
036
037/**
038 * @author Franck WOLFF
039 */
040public 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}