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