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