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     */
022    package org.granite.messaging.jmf;
023    
024    import java.io.IOException;
025    import java.lang.reflect.InvocationTargetException;
026    import java.util.ArrayList;
027    import java.util.Arrays;
028    import java.util.HashMap;
029    import java.util.List;
030    import java.util.Map;
031    
032    import org.granite.messaging.jmf.codec.BijectiveCodec;
033    import org.granite.messaging.jmf.codec.ConditionalObjectCodec;
034    import org.granite.messaging.jmf.codec.ExtendedObjectCodec;
035    import org.granite.messaging.jmf.codec.PrimitiveCodec;
036    import org.granite.messaging.jmf.codec.StandardCodec;
037    import org.granite.messaging.jmf.codec.std.BooleanCodec;
038    import org.granite.messaging.jmf.codec.std.ByteCodec;
039    import org.granite.messaging.jmf.codec.std.CharacterCodec;
040    import org.granite.messaging.jmf.codec.std.DoubleCodec;
041    import org.granite.messaging.jmf.codec.std.FloatCodec;
042    import org.granite.messaging.jmf.codec.std.IntegerCodec;
043    import org.granite.messaging.jmf.codec.std.LongCodec;
044    import org.granite.messaging.jmf.codec.std.NullCodec;
045    import org.granite.messaging.jmf.codec.std.ShortCodec;
046    import org.granite.messaging.jmf.codec.std.StringCodec;
047    import org.granite.messaging.jmf.codec.std.impl.ArrayCodecImpl;
048    import org.granite.messaging.jmf.codec.std.impl.ArrayListCodecImpl;
049    import org.granite.messaging.jmf.codec.std.impl.BigDecimalCodecImpl;
050    import org.granite.messaging.jmf.codec.std.impl.BigIntegerCodecImpl;
051    import org.granite.messaging.jmf.codec.std.impl.BooleanCodecImpl;
052    import org.granite.messaging.jmf.codec.std.impl.ByteCodecImpl;
053    import org.granite.messaging.jmf.codec.std.impl.CharacterCodecImpl;
054    import org.granite.messaging.jmf.codec.std.impl.ClassCodecImpl;
055    import org.granite.messaging.jmf.codec.std.impl.DateCodecImpl;
056    import org.granite.messaging.jmf.codec.std.impl.DoubleCodecImpl;
057    import org.granite.messaging.jmf.codec.std.impl.EnumCodecImpl;
058    import org.granite.messaging.jmf.codec.std.impl.FloatCodecImpl;
059    import org.granite.messaging.jmf.codec.std.impl.HashMapCodecImpl;
060    import org.granite.messaging.jmf.codec.std.impl.HashSetCodecImpl;
061    import org.granite.messaging.jmf.codec.std.impl.IntegerCodecImpl;
062    import org.granite.messaging.jmf.codec.std.impl.LongCodecImpl;
063    import org.granite.messaging.jmf.codec.std.impl.NullCodecImpl;
064    import org.granite.messaging.jmf.codec.std.impl.ObjectCodecImpl;
065    import org.granite.messaging.jmf.codec.std.impl.ShortCodecImpl;
066    import org.granite.messaging.jmf.codec.std.impl.SqlDateCodecImpl;
067    import org.granite.messaging.jmf.codec.std.impl.SqlTimeCodecImpl;
068    import org.granite.messaging.jmf.codec.std.impl.SqlTimestampCodecImpl;
069    import org.granite.messaging.jmf.codec.std.impl.StringCodecImpl;
070    import org.granite.messaging.reflect.Property;
071    
072    /**
073     * @author Franck WOLFF
074     */
075    public 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    }