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.codec.std.impl;
023
024import java.io.IOException;
025import java.io.OutputStream;
026
027import org.granite.messaging.jmf.DumpContext;
028import org.granite.messaging.jmf.InputContext;
029import org.granite.messaging.jmf.JMFEncodingException;
030import org.granite.messaging.jmf.OutputContext;
031import org.granite.messaging.jmf.codec.std.DoubleCodec;
032import org.granite.messaging.jmf.codec.std.impl.util.DoubleUtil;
033import org.granite.messaging.jmf.codec.std.impl.util.LongUtil;
034import org.granite.messaging.jmf.codec.std.impl.util.DoubleUtil.DoubleAsLong;
035
036/**
037 * @author Franck WOLFF
038 */
039public class DoubleCodecImpl extends AbstractStandardCodec<Double> implements DoubleCodec {
040
041        protected static final int POW_10_OFFSET = 4;
042
043        public int getObjectType() {
044                return JMF_DOUBLE_OBJECT;
045        }
046
047        public Class<?> getObjectClass() {
048                return Double.class;
049        }
050
051        public int getPrimitiveType() {
052                return JMF_DOUBLE;
053        }
054
055        public Class<?> getPrimitiveClass() {
056                return double.class;
057        }
058
059        public void encode(OutputContext ctx, Double v) throws IOException {
060                writeDoubleData(ctx, JMF_DOUBLE_OBJECT, v.doubleValue());
061        }
062        
063        public Double decode(InputContext ctx, int parameterizedJmfType) throws IOException {
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                return readDoubleData(ctx, parameterizedJmfType);
074        }
075        
076        public void dump(DumpContext ctx, int parameterizedJmfType) throws IOException {
077                int jmfType = ctx.getSharedContext().getCodecRegistry().extractJmfType(parameterizedJmfType);
078                
079                switch (jmfType) {
080                case JMF_DOUBLE:
081                        ctx.indentPrintLn("double: " + readDoubleData(ctx, parameterizedJmfType));
082                        break;
083                case JMF_DOUBLE_OBJECT:
084                        ctx.indentPrintLn(Double.class.getName() + ": " + Double.valueOf(readDoubleData(ctx, parameterizedJmfType)));
085                        break;
086                default:
087                        throw newBadTypeJMFEncodingException(jmfType, parameterizedJmfType);
088                }
089        }
090        
091        public static void writeDoubleData(OutputContext ctx, int jmfType, double v) throws IOException {
092                final OutputStream os = ctx.getOutputStream();
093                
094                long bits = Double.doubleToLongBits(v);
095                
096                // v isn't NaN, +-Infinity or -0.0
097                if ((bits & 0x7FF0000000000000L) != 0x7FF0000000000000L && bits != 0x8000000000000000L) {
098                        
099                        DoubleAsLong asLong = DoubleUtil.doubleAsLong04(v);
100                        if (asLong != null &&
101                                asLong.longValue >= LongUtil.MIN_7_BYTES_VARIABLE_LONG &&
102                                asLong.longValue <= LongUtil.MAX_7_BYTES_VARIABLE_LONG) {
103                                
104                                os.write(0x80 | (asLong.pow10 << POW_10_OFFSET) | jmfType);
105                                LongUtil.encodeVariableLong(ctx, asLong.longValue);
106                                return;
107                        }
108                }
109                
110                os.write(jmfType);
111                LongUtil.encodeLong(ctx, bits);
112        }
113        
114        public static double readDoubleData(InputContext ctx, int parameterizedJmfType) throws IOException {
115                if ((parameterizedJmfType & 0x80) != 0) {
116                        long asLong = LongUtil.decodeVariableLong(ctx);
117                        int pow10 = ((parameterizedJmfType >>> POW_10_OFFSET) & 0x07);
118                        
119                        switch (pow10) {
120                        case 0:
121                                return asLong;
122                        case 2:
123                                return asLong / 100.0;
124                        case 4:
125                                return asLong / 10000.0;
126                        default:
127                                throw new JMFEncodingException("Unsupported power of 10: " + pow10);
128                        }
129                }
130                
131                return Double.longBitsToDouble(LongUtil.decodeLong(ctx));
132        }
133}