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