001    /**
002     * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.kernel.io;
016    
017    import com.liferay.portal.kernel.util.CharBufferPool;
018    import com.liferay.portal.kernel.util.ClassLoaderPool;
019    import com.liferay.portal.kernel.util.ClassResolverUtil;
020    
021    import java.io.IOException;
022    import java.io.InputStream;
023    import java.io.ObjectInputStream;
024    import java.io.Serializable;
025    
026    import java.nio.ByteBuffer;
027    
028    /**
029     * @author Shuyang Zhou
030     * @see    Serializer
031     */
032    public class Deserializer {
033    
034            public Deserializer(ByteBuffer byteBuffer) {
035                    buffer = byteBuffer.array();
036                    index = byteBuffer.arrayOffset();
037                    limit = index + byteBuffer.remaining();
038            }
039    
040            public boolean readBoolean() {
041                    detectBufferUnderflow(1);
042    
043                    return BigEndianCodec.getBoolean(buffer, index++);
044            }
045    
046            public byte readByte() {
047                    detectBufferUnderflow(1);
048    
049                    return buffer[index++];
050            }
051    
052            public char readChar() {
053                    detectBufferUnderflow(2);
054    
055                    char c = BigEndianCodec.getChar(buffer, index);
056    
057                    index += 2;
058    
059                    return c;
060            }
061    
062            public double readDouble() {
063                    detectBufferUnderflow(8);
064    
065                    double d = BigEndianCodec.getDouble(buffer, index);
066    
067                    index += 8;
068    
069                    return d;
070            }
071    
072            public float readFloat() {
073                    detectBufferUnderflow(4);
074    
075                    float f = BigEndianCodec.getFloat(buffer, index);
076    
077                    index += 4;
078    
079                    return f;
080            }
081    
082            public int readInt() {
083                    detectBufferUnderflow(4);
084    
085                    int i = BigEndianCodec.getInt(buffer, index);
086    
087                    index += 4;
088    
089                    return i;
090            }
091    
092            public long readLong() {
093                    detectBufferUnderflow(8);
094    
095                    long l = BigEndianCodec.getLong(buffer, index);
096    
097                    index += 8;
098    
099                    return l;
100            }
101    
102            public <T extends Serializable> T readObject()
103                    throws ClassNotFoundException {
104    
105                    byte tcByte = buffer[index++];
106    
107                    switch (tcByte) {
108                            case SerializationConstants.TC_BOOLEAN:
109                                    return (T)Boolean.valueOf(readBoolean());
110    
111                            case SerializationConstants.TC_BYTE:
112                                    return (T)Byte.valueOf(readByte());
113    
114                            case SerializationConstants.TC_CHARACTER:
115                                    return (T)Character.valueOf(readChar());
116    
117                            case SerializationConstants.TC_CLASS:
118                                    String contextName = readString();
119                                    String className = readString();
120    
121                                    ClassLoader classLoader = ClassLoaderPool.getClassLoader(
122                                            contextName);
123    
124                                    return (T)ClassResolverUtil.resolve(className, classLoader);
125    
126                            case SerializationConstants.TC_DOUBLE:
127                                    return (T)Double.valueOf(readDouble());
128    
129                            case SerializationConstants.TC_FLOAT:
130                                    return (T)Float.valueOf(readFloat());
131    
132                            case SerializationConstants.TC_INTEGER:
133                                    return (T)Integer.valueOf(readInt());
134    
135                            case SerializationConstants.TC_LONG:
136                                    return (T)Long.valueOf(readLong());
137    
138                            case SerializationConstants.TC_NULL:
139                                    return null;
140    
141                            case SerializationConstants.TC_SHORT:
142                                    return (T)Short.valueOf(readShort());
143    
144                            case SerializationConstants.TC_STRING:
145                                    return (T)readString();
146    
147                            case SerializationConstants.TC_OBJECT:
148                                    try {
149                                            ObjectInputStream objectInpputStream =
150                                                    new AnnotatedObjectInputStream(new BufferInputStream());
151    
152                                            T t = (T)objectInpputStream.readObject();
153    
154                                            objectInpputStream.close();
155    
156                                            return t;
157                                    }
158                                    catch (IOException ioe) {
159                                            throw new RuntimeException(ioe);
160                                    }
161    
162                            default :
163                                    throw new IllegalStateException("Unkown TC code " + tcByte);
164                    }
165            }
166    
167            public short readShort() {
168                    detectBufferUnderflow(2);
169    
170                    short s = BigEndianCodec.getShort(buffer, index);
171    
172                    index += 2;
173    
174                    return s;
175            }
176    
177            public String readString() {
178                    detectBufferUnderflow(5);
179    
180                    boolean asciiCode = BigEndianCodec.getBoolean(buffer, index++);
181    
182                    int length = BigEndianCodec.getInt(buffer, index);
183    
184                    index += 4;
185    
186                    if (asciiCode) {
187                            detectBufferUnderflow(length);
188    
189                            char[] chars = CharBufferPool.borrow(length);
190    
191                            for (int i = 0; i < length; i++) {
192                                    chars[i] = (char)buffer[index++];
193                            }
194    
195                            return new String(chars, 0, length);
196                    }
197                    else {
198                            detectBufferUnderflow(length * 2);
199    
200                            char[] chars = CharBufferPool.borrow(length);
201    
202                            for (int i = 0; i < length; i++) {
203                                    chars[i] = BigEndianCodec.getChar(buffer, index);
204    
205                                    index += 2;
206                            }
207    
208                            return new String(chars, 0, length);
209                    }
210            }
211    
212            /**
213             * This method is final so that JIT can inline it.
214             */
215            protected final void detectBufferUnderflow(int availableBytes) {
216                    if ((index + availableBytes) > limit) {
217                            throw new IllegalStateException("Buffer underflow");
218                    }
219            }
220    
221            protected byte[] buffer;
222            protected int index;
223            protected int limit;
224    
225            protected class BufferInputStream extends InputStream {
226    
227                    @Override
228                    public int read() {
229                            return buffer[index++];
230                    }
231    
232                    @Override
233                    public int read(byte[] bytes) {
234                            return read(bytes, 0, bytes.length);
235                    }
236    
237                    @Override
238                    public int read(byte[] bytes, int offset, int length) {
239                            int remain = limit - index;
240    
241                            if (remain < length) {
242                                    length = remain;
243                            }
244    
245                            System.arraycopy(buffer, index, bytes, offset, length);
246    
247                            index += length;
248    
249                            return length;
250                    }
251    
252            }
253    
254    }