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