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
021package org.granite.messaging.jmf;
022
023import java.io.IOException;
024import java.lang.reflect.Field;
025import java.util.ArrayList;
026import java.util.Arrays;
027import java.util.HashMap;
028import java.util.List;
029import java.util.Map;
030
031import org.granite.messaging.jmf.codec.BijectiveCodec;
032import org.granite.messaging.jmf.codec.ConditionalObjectCodec;
033import org.granite.messaging.jmf.codec.ExtendedObjectCodec;
034import org.granite.messaging.jmf.codec.PrimitiveCodec;
035import org.granite.messaging.jmf.codec.StandardCodec;
036import org.granite.messaging.jmf.codec.std.BooleanCodec;
037import org.granite.messaging.jmf.codec.std.ByteCodec;
038import org.granite.messaging.jmf.codec.std.CharacterCodec;
039import org.granite.messaging.jmf.codec.std.DoubleCodec;
040import org.granite.messaging.jmf.codec.std.FloatCodec;
041import org.granite.messaging.jmf.codec.std.IntegerCodec;
042import org.granite.messaging.jmf.codec.std.LongCodec;
043import org.granite.messaging.jmf.codec.std.NullCodec;
044import org.granite.messaging.jmf.codec.std.ShortCodec;
045import org.granite.messaging.jmf.codec.std.StringCodec;
046import org.granite.messaging.jmf.codec.std.impl.ArrayCodecImpl;
047import org.granite.messaging.jmf.codec.std.impl.ArrayListCodecImpl;
048import org.granite.messaging.jmf.codec.std.impl.BigDecimalCodecImpl;
049import org.granite.messaging.jmf.codec.std.impl.BigIntegerCodecImpl;
050import org.granite.messaging.jmf.codec.std.impl.BooleanCodecImpl;
051import org.granite.messaging.jmf.codec.std.impl.ByteCodecImpl;
052import org.granite.messaging.jmf.codec.std.impl.CharacterCodecImpl;
053import org.granite.messaging.jmf.codec.std.impl.ClassCodecImpl;
054import org.granite.messaging.jmf.codec.std.impl.DateCodecImpl;
055import org.granite.messaging.jmf.codec.std.impl.DoubleCodecImpl;
056import org.granite.messaging.jmf.codec.std.impl.EnumCodecImpl;
057import org.granite.messaging.jmf.codec.std.impl.FloatCodecImpl;
058import org.granite.messaging.jmf.codec.std.impl.HashMapCodecImpl;
059import org.granite.messaging.jmf.codec.std.impl.HashSetCodecImpl;
060import org.granite.messaging.jmf.codec.std.impl.IntegerCodecImpl;
061import org.granite.messaging.jmf.codec.std.impl.LongCodecImpl;
062import org.granite.messaging.jmf.codec.std.impl.NullCodecImpl;
063import org.granite.messaging.jmf.codec.std.impl.ObjectCodecImpl;
064import org.granite.messaging.jmf.codec.std.impl.ShortCodecImpl;
065import org.granite.messaging.jmf.codec.std.impl.StringCodecImpl;
066
067/**
068 * @author Franck WOLFF
069 */
070public class DefaultCodecRegistry implements CodecRegistry {
071
072        private static final int[] UNPARAMETERIZED_JMF_TYPES = new int[256];
073        static {
074                for (int parameterizedJmfType = 0; parameterizedJmfType < 256; parameterizedJmfType++) {
075                        int jmfType;
076                        
077                        if ((parameterizedJmfType & 0x08) == 0x00)
078                                jmfType = (parameterizedJmfType & 0x07);
079                        else if ((parameterizedJmfType & 0x18) == 0x08)
080                                jmfType = (parameterizedJmfType & 0x0F);
081                        else if ((parameterizedJmfType & 0x38) == 0x18)
082                                jmfType = (parameterizedJmfType & 0x1F);
083                        else if ((parameterizedJmfType & 0x78) == 0x38)
084                                jmfType = (parameterizedJmfType & 0x3F);
085                        else
086                                jmfType = parameterizedJmfType;
087                        
088                        UNPARAMETERIZED_JMF_TYPES[parameterizedJmfType] = jmfType;
089                }
090        }
091
092        private NullCodec nullCodec;
093
094        private BooleanCodec booleanCodec;
095        private CharacterCodec characterCodec;
096        private ByteCodec byteCodec;
097        private ShortCodec shortCodec;
098        private IntegerCodec integerCodec;
099        private LongCodec longCodec;
100        private FloatCodec floatCodec;
101        private DoubleCodec doubleCodec;
102        private StringCodec stringCodec;
103        
104        private final Map<Integer, StandardCodec<?>> typeToCodec = new HashMap<Integer, StandardCodec<?>>();
105        private final Map<Class<?>, StandardCodec<?>> classToCodec = new HashMap<Class<?>, StandardCodec<?>>();
106        private final List<ConditionalObjectCodec> conditionalObjectCodecs = new ArrayList<ConditionalObjectCodec>();
107        private final Map<Class<?>, PrimitiveFieldCodec> primitiveFieldCodecs = new HashMap<Class<?>, PrimitiveFieldCodec>();
108
109        private final List<ExtendedObjectCodec> extendedCodecs;
110        
111        public DefaultCodecRegistry() {
112                this(null);
113        }
114                
115        public DefaultCodecRegistry(List<ExtendedObjectCodec> extendedCodecs) {
116                this.extendedCodecs = (extendedCodecs != null ? extendedCodecs : new ArrayList<ExtendedObjectCodec>());
117
118                List<StandardCodec<?>> standardCodecs = getStandardCodecs();
119                for (StandardCodec<?> codec : standardCodecs) {
120                        
121                        if (codec instanceof BijectiveCodec) {
122                                if (codec instanceof PrimitiveCodec) {
123                                        assertNull(classToCodec.put(((PrimitiveCodec<?>)codec).getPrimitiveClass(), codec));
124                                        assertNull(typeToCodec.put(((PrimitiveCodec<?>)codec).getPrimitiveType(), codec));
125                                        
126                                        switch (((PrimitiveCodec<?>)codec).getPrimitiveType()) {
127                                                case JMF_BOOLEAN: initBooleanCodec((BooleanCodec)codec); break;
128                                                case JMF_CHARACTER: initCharacterCodec((CharacterCodec)codec); break;
129                                                case JMF_BYTE: initByteCodec((ByteCodec)codec); break;
130                                                case JMF_SHORT: initShortCodec((ShortCodec)codec); break;
131                                                case JMF_INTEGER: initIntegerCodec((IntegerCodec)codec); break;
132                                                case JMF_LONG: initLongCodec((LongCodec)codec); break;
133                                                case JMF_FLOAT: initFloatCodec((FloatCodec)codec); break;
134                                                case JMF_DOUBLE: initDoubleCodec((DoubleCodec)codec); break;
135                                        }
136                                }
137                                
138                                assertNull(classToCodec.put(((BijectiveCodec<?>)codec).getObjectClass(), codec));
139                                assertNull(typeToCodec.put(codec.getObjectType(), codec));
140                                
141                                if (codec.getObjectType() == JMF_STRING)
142                                        initStringCodec((StringCodec)codec);
143                                else if (codec.getObjectType() == JMF_NULL)
144                                        initNullCodec((NullCodec)codec);
145                        }
146                        else if (codec instanceof ConditionalObjectCodec) {
147                                assertNull(typeToCodec.put(codec.getObjectType(), codec));
148                                conditionalObjectCodecs.add((ConditionalObjectCodec)codec);
149                        }
150                        else
151                                throw new JMFConfigurationException("Codec must implement BijectiveCodec or ConditionalObjectCodec: " + codec);
152                }
153                
154                checkPrimitiveCodecs();
155        }
156
157        public NullCodec getNullCodec() {
158                return nullCodec;
159        }
160
161        public BooleanCodec getBooleanCodec() {
162                return booleanCodec;
163        }
164
165        public CharacterCodec getCharacterCodec() {
166                return characterCodec;
167        }
168
169        public ByteCodec getByteCodec() {
170                return byteCodec;
171        }
172
173        public ShortCodec getShortCodec() {
174                return shortCodec;
175        }
176
177        public IntegerCodec getIntegerCodec() {
178                return integerCodec;
179        }
180
181        public LongCodec getLongCodec() {
182                return longCodec;
183        }
184
185        public FloatCodec getFloatCodec() {
186                return floatCodec;
187        }
188
189        public DoubleCodec getDoubleCodec() {
190                return doubleCodec;
191        }
192
193        public StringCodec getStringCodec() {
194                return stringCodec;
195        }
196
197        @SuppressWarnings("unchecked")
198        public <T> StandardCodec<T> getCodec(int jmfType) {
199                return (StandardCodec<T>)typeToCodec.get(jmfType);
200        }
201
202        @SuppressWarnings("unchecked")
203        public <T> StandardCodec<T> getCodec(Object v) {
204                Class<?> cls = (v != null ? v.getClass() : null);
205                StandardCodec<T> codec = (StandardCodec<T>)classToCodec.get(cls);
206                if (codec == null) {
207                        for (ConditionalObjectCodec condCodec : conditionalObjectCodecs) {
208                                if (condCodec.canEncode(v)) {
209                                        codec = (StandardCodec<T>)condCodec;
210                                        break;
211                                }
212                        }
213                }
214                return codec;
215        }
216
217        public ExtendedObjectCodec findExtendedEncoder(ExtendedObjectOutput out, Object v) {
218                for (ExtendedObjectCodec c : extendedCodecs) {
219                        if (c.canEncode(out, v))
220                                return c;
221                }
222                return null;
223        }
224
225        public ExtendedObjectCodec findExtendedDecoder(ExtendedObjectInput in, String className)
226                throws ClassNotFoundException {
227                
228                for (ExtendedObjectCodec c : extendedCodecs) {
229                        if (c.canDecode(in, className))
230                                return c;
231                }
232                return null;
233        }
234
235        public PrimitiveFieldCodec getPrimitiveFieldCodec(Class<?> fieldCls) {
236                return primitiveFieldCodecs.get(fieldCls);
237        }
238        
239        public int extractJmfType(int parameterizedJmfType) {
240                return UNPARAMETERIZED_JMF_TYPES[parameterizedJmfType];
241        }
242
243        public int jmfTypeOfPrimitiveClass(Class<?> cls) {
244                if (!cls.isPrimitive())
245                        return -1;
246                StandardCodec<?> codec = classToCodec.get(cls);
247                return (codec instanceof PrimitiveCodec ? ((PrimitiveCodec<?>)codec).getPrimitiveType() : -1);
248        }
249
250        public Class<?> primitiveClassOfJmfType(int jmfType) {
251                StandardCodec<?> codec = typeToCodec.get(Integer.valueOf(jmfType));
252                return (codec instanceof PrimitiveCodec && ((PrimitiveCodec<?>)codec).getPrimitiveType() == jmfType ? ((PrimitiveCodec<?>)codec).getPrimitiveClass() : null);
253        }
254        
255        protected List<StandardCodec<?>> getStandardCodecs() {
256                return Arrays.asList((StandardCodec<?>)
257                        new NullCodecImpl(),
258                                
259                        new BooleanCodecImpl(),
260                        new CharacterCodecImpl(),
261                        new ByteCodecImpl(),
262                        new ShortCodecImpl(),
263                        new IntegerCodecImpl(),
264                        new LongCodecImpl(),
265                        new FloatCodecImpl(),
266                        new DoubleCodecImpl(),
267
268                        new BigIntegerCodecImpl(),
269                        new BigDecimalCodecImpl(),
270
271                        new StringCodecImpl(),
272
273                        new DateCodecImpl(),
274
275                        new ArrayListCodecImpl(),
276                        new HashSetCodecImpl(),
277                        new HashMapCodecImpl(),
278
279                        new EnumCodecImpl(),
280                        new ArrayCodecImpl(),
281                        new ClassCodecImpl(),
282                        new ObjectCodecImpl()
283                );
284        }
285        
286        private void assertNull(StandardCodec<?> codec) {
287                if (codec != null)
288                        throw new JMFConfigurationException("Codec conflict with: " + codec);
289        }
290        
291        private void checkPrimitiveCodecs() {
292                if (nullCodec == null)
293                        throw new JMFConfigurationException("No Null codec");
294                
295                if (booleanCodec == null)
296                        throw new JMFConfigurationException("No Boolean codec");
297                if (characterCodec == null)
298                        throw new JMFConfigurationException("No Character codec");
299                if (byteCodec == null)
300                        throw new JMFConfigurationException("No Byte codec");
301                if (shortCodec == null)
302                        throw new JMFConfigurationException("No Short codec");
303                if (integerCodec == null)
304                        throw new JMFConfigurationException("No Integer codec");
305                if (longCodec == null)
306                        throw new JMFConfigurationException("No Long codec");
307                if (floatCodec == null)
308                        throw new JMFConfigurationException("No Float codec");
309                if (doubleCodec == null)
310                        throw new JMFConfigurationException("No Double codec");
311                
312                if (stringCodec == null)
313                        throw new JMFConfigurationException("No String codec");
314        }
315
316        private void initBooleanCodec(BooleanCodec codec) {
317                booleanCodec = codec;
318                primitiveFieldCodecs.put(booleanCodec.getPrimitiveClass(), new PrimitiveFieldCodec() {
319                        public void encodePrimitive(OutputContext ctx, Object v, Field field) throws IllegalAccessException, IOException {
320                                booleanCodec.encodePrimitive(ctx, field.getBoolean(v));
321                        }
322                        public void decodePrimitive(InputContext ctx, Object v, Field field) throws IllegalAccessException, IOException {
323                                field.setBoolean(v, booleanCodec.decodePrimitive(ctx));
324                        }
325                });
326        }
327
328        private void initCharacterCodec(CharacterCodec codec) {
329                characterCodec = codec;
330                primitiveFieldCodecs.put(characterCodec.getPrimitiveClass(), new PrimitiveFieldCodec() {
331                        public void encodePrimitive(OutputContext ctx, Object v, Field field) throws IllegalAccessException, IOException {
332                                characterCodec.encodePrimitive(ctx, field.getChar(v));
333                        }
334                        public void decodePrimitive(InputContext ctx, Object v, Field field) throws IllegalAccessException, IOException {
335                                field.setChar(v, characterCodec.decodePrimitive(ctx));
336                        }
337                });
338        }
339
340        private void initByteCodec(ByteCodec codec) {
341                byteCodec = codec;
342                primitiveFieldCodecs.put(byteCodec.getPrimitiveClass(), new PrimitiveFieldCodec() {
343                        public void encodePrimitive(OutputContext ctx, Object v, Field field) throws IllegalAccessException, IOException {
344                                byteCodec.encodePrimitive(ctx, field.getByte(v));
345                        }
346                        public void decodePrimitive(InputContext ctx, Object v, Field field) throws IllegalAccessException, IOException {
347                                field.setByte(v, byteCodec.decodePrimitive(ctx));
348                        }
349                });
350        }
351
352        private void initShortCodec(ShortCodec codec) {
353                shortCodec = codec;
354                primitiveFieldCodecs.put(shortCodec.getPrimitiveClass(), new PrimitiveFieldCodec() {
355                        public void encodePrimitive(OutputContext ctx, Object v, Field field) throws IllegalAccessException, IOException {
356                                shortCodec.encodePrimitive(ctx, field.getShort(v));
357                        }
358                        public void decodePrimitive(InputContext ctx, Object v, Field field) throws IllegalAccessException, IOException {
359                                field.setShort(v, shortCodec.decodePrimitive(ctx));
360                        }
361                });
362        }
363
364        private void initIntegerCodec(IntegerCodec codec) {
365                integerCodec = codec;
366                primitiveFieldCodecs.put(integerCodec.getPrimitiveClass(), new PrimitiveFieldCodec() {
367                        public void encodePrimitive(OutputContext ctx, Object v, Field field) throws IllegalAccessException, IOException {
368                                integerCodec.encodePrimitive(ctx, field.getInt(v));
369                        }
370                        public void decodePrimitive(InputContext ctx, Object v, Field field) throws IllegalAccessException, IOException {
371                                field.setInt(v, integerCodec.decodePrimitive(ctx));
372                        }
373                });
374        }
375
376        private void initLongCodec(LongCodec codec) {
377                longCodec = codec;
378                primitiveFieldCodecs.put(longCodec.getPrimitiveClass(), new PrimitiveFieldCodec() {
379                        public void encodePrimitive(OutputContext ctx, Object v, Field field) throws IllegalAccessException, IOException {
380                                longCodec.encodePrimitive(ctx, field.getLong(v));
381                        }
382                        public void decodePrimitive(InputContext ctx, Object v, Field field) throws IllegalAccessException, IOException {
383                                field.setLong(v, longCodec.decodePrimitive(ctx));
384                        }
385                });
386        }
387
388        private void initFloatCodec(FloatCodec codec) {
389                floatCodec = codec;
390                primitiveFieldCodecs.put(floatCodec.getPrimitiveClass(), new PrimitiveFieldCodec() {
391                        public void encodePrimitive(OutputContext ctx, Object v, Field field) throws IllegalAccessException, IOException {
392                                floatCodec.encodePrimitive(ctx, field.getFloat(v));
393                        }
394                        public void decodePrimitive(InputContext ctx, Object v, Field field) throws IllegalAccessException, IOException {
395                                field.setFloat(v, floatCodec.decodePrimitive(ctx));
396                        }
397                });
398        }
399
400        private void initDoubleCodec(DoubleCodec codec) {
401                doubleCodec = codec;
402                primitiveFieldCodecs.put(doubleCodec.getPrimitiveClass(), new PrimitiveFieldCodec() {
403                        public void encodePrimitive(OutputContext ctx, Object v, Field field) throws IllegalAccessException, IOException {
404                                doubleCodec.encodePrimitive(ctx, field.getDouble(v));
405                        }
406                        public void decodePrimitive(InputContext ctx, Object v, Field field) throws IllegalAccessException, IOException {
407                                field.setDouble(v, doubleCodec.decodePrimitive(ctx));
408                        }
409                });
410        }
411
412        private void initStringCodec(StringCodec codec) {
413                stringCodec = codec;
414        }
415
416        private void initNullCodec(NullCodec codec) {
417                nullCodec = codec;
418        }
419}