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