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.codec.std.impl;
023    
024    import java.io.IOException;
025    import java.io.OutputStream;
026    
027    import org.granite.messaging.jmf.DumpContext;
028    import org.granite.messaging.jmf.InputContext;
029    import org.granite.messaging.jmf.OutputContext;
030    import org.granite.messaging.jmf.codec.std.DoubleCodec;
031    import org.granite.messaging.jmf.codec.std.LongCodec;
032    
033    /**
034     * @author Franck WOLFF
035     */
036    public class DoubleCodecImpl extends AbstractStandardCodec<Double> implements DoubleCodec {
037    
038            public int getObjectType() {
039                    return JMF_DOUBLE_OBJECT;
040            }
041    
042            public Class<?> getObjectClass() {
043                    return Double.class;
044            }
045    
046            public int getPrimitiveType() {
047                    return JMF_DOUBLE;
048            }
049    
050            public Class<?> getPrimitiveClass() {
051                    return Double.TYPE;
052            }
053    
054            public void encode(OutputContext ctx, Double v) throws IOException {
055                    writeDoubleData(ctx, JMF_DOUBLE_OBJECT, v.doubleValue());
056            }
057            
058            public Double decode(InputContext ctx, int parameterizedJmfType) throws IOException {
059                    int jmfType = ctx.getSharedContext().getCodecRegistry().extractJmfType(parameterizedJmfType);
060    
061                    if (jmfType != JMF_DOUBLE_OBJECT)
062                            throw newBadTypeJMFEncodingException(jmfType, parameterizedJmfType);
063                    
064                    return Double.valueOf(readDoubleData(ctx, parameterizedJmfType));
065            }
066    
067            public void encodePrimitive(OutputContext ctx, double v) throws IOException {
068                    writeDoubleData(ctx, JMF_DOUBLE, v);
069            }
070            
071            public double decodePrimitive(InputContext ctx) throws IOException {
072                    int parameterizedJmfType = ctx.safeRead();
073                    int jmfType = ctx.getSharedContext().getCodecRegistry().extractJmfType(parameterizedJmfType);
074    
075                    if (jmfType != JMF_DOUBLE)
076                            throw newBadTypeJMFEncodingException(jmfType, parameterizedJmfType);
077                    
078                    return readDoubleData(ctx, parameterizedJmfType);
079            }
080            
081            public void dump(DumpContext ctx, int parameterizedJmfType) throws IOException {
082                    int jmfType = ctx.getSharedContext().getCodecRegistry().extractJmfType(parameterizedJmfType);
083                    
084                    switch (jmfType) {
085                    case JMF_DOUBLE:
086                            ctx.indentPrintLn("double: " + readDoubleData(ctx, parameterizedJmfType));
087                            break;
088                    case JMF_DOUBLE_OBJECT:
089                            ctx.indentPrintLn(Double.class.getName() + ": " + Double.valueOf(readDoubleData(ctx, parameterizedJmfType)));
090                            break;
091                    default:
092                            throw newBadTypeJMFEncodingException(jmfType, parameterizedJmfType);
093                    }
094            }
095            
096            public static void writeDoubleData(OutputContext ctx, int jmfType, double v) throws IOException {
097                    final OutputStream os = ctx.getOutputStream();
098                    
099                    if (Double.isNaN(v))
100                            os.write(0xC0 | jmfType);
101                    else {
102                            long asLong = (long)v;
103                            LongCodec longCodec = ctx.getSharedContext().getCodecRegistry().getLongCodec();
104                            
105                            int lengthAsLong = Integer.MAX_VALUE;
106                            if (v == asLong) {
107                                    if (v == Long.MIN_VALUE)
108                                            lengthAsLong = 1;
109                                    else if (Double.doubleToRawLongBits(v) != Long.MIN_VALUE)
110                                            lengthAsLong = longCodec.lengthOfVariableAbsoluteLong(Math.abs(asLong)) + 1;
111                            }
112                            
113                            if (lengthAsLong < 4) {
114                                    os.write(0x80 | jmfType);
115                                    longCodec.writeVariableLong(ctx, asLong);
116                            }
117                            else if (v == (float)v) {
118                                    os.write(0x40 | jmfType);
119                                    
120                                    int bits = Float.floatToRawIntBits((float)v);
121                                    os.write(bits);
122                                    os.write(bits >> 8);
123                                    os.write(bits >> 16);
124                                    os.write(bits >> 24);
125                            }
126                            else if (lengthAsLong < 8) {
127                                    os.write(0x80 | jmfType);
128                                    longCodec.writeVariableLong(ctx, asLong);
129                            }
130                            else {
131                                    os.write(jmfType);
132                                    
133                                    long bits = Double.doubleToRawLongBits(v);
134                                    os.write((int)bits);
135                                    os.write((int)(bits >> 8));
136                                    os.write((int)(bits >> 16));
137                                    os.write((int)(bits >> 24));
138                                    os.write((int)(bits >> 32));
139                                    os.write((int)(bits >> 40));
140                                    os.write((int)(bits >> 48));
141                                    os.write((int)(bits >> 56));
142                            }
143                    }
144            }
145            
146            public static double readDoubleData(InputContext ctx, int type) throws IOException {
147                    double v;
148                    
149                    switch ((type >> 6) & 0x03) {
150                    case 3:
151                            v = Double.NaN;
152                            break;
153                    case 2:
154                            v = ctx.getSharedContext().getCodecRegistry().getLongCodec().readVariableLong(ctx);
155                            break;
156                    case 1:
157                            int i = ctx.safeRead();
158                            i |= ctx.safeRead() << 8;
159                            i |= ctx.safeRead() << 16;
160                            i |= ctx.safeRead() << 24;
161                            v = Float.intBitsToFloat(i);
162                            break;
163                    default: // case 0:
164                            long l = ctx.safeRead();
165                            l |= ((long)ctx.safeRead()) << 8;
166                            l |= ((long)ctx.safeRead()) << 16;
167                            l |= ((long)ctx.safeRead()) << 24;
168                            l |= ((long)ctx.safeRead()) << 32;
169                            l |= ((long)ctx.safeRead()) << 40;
170                            l |= ((long)ctx.safeRead()) << 48;
171                            l |= ((long)ctx.safeRead()) << 56;
172                            v = Double.longBitsToDouble(l);
173                            break;
174                    }
175    
176                    return v;
177            }
178    }