Coverage Report - org.apache.johnzon.mapper.Mapper
 
Classes in this File Line Coverage Branch Coverage Complexity
Mapper
86%
335/389
76%
242/316
6,581
Mapper$1
N/A
N/A
6,581
Mapper$FallbackConverter
66%
2/3
N/A
6,581
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one
 3  
  * or more contributor license agreements. See the NOTICE file
 4  
  * distributed with this work for additional information
 5  
  * regarding copyright ownership. The ASF licenses this file
 6  
  * to you under the Apache License, Version 2.0 (the
 7  
  * "License"); you may not use this file except in compliance
 8  
  * with the License. You may obtain a copy of the License at
 9  
  *
 10  
  * http://www.apache.org/licenses/LICENSE-2.0
 11  
  *
 12  
  * Unless required by applicable law or agreed to in writing,
 13  
  * software distributed under the License is distributed on an
 14  
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 15  
  * KIND, either express or implied. See the License for the
 16  
  * specific language governing permissions and limitations
 17  
  * under the License.
 18  
  */
 19  
 package org.apache.johnzon.mapper;
 20  
 
 21  
 import static java.util.Arrays.asList;
 22  
 
 23  
 import java.io.InputStream;
 24  
 import java.io.OutputStream;
 25  
 import java.io.OutputStreamWriter;
 26  
 import java.io.Reader;
 27  
 import java.io.StringWriter;
 28  
 import java.io.Writer;
 29  
 import java.lang.reflect.Array;
 30  
 import java.lang.reflect.InvocationTargetException;
 31  
 import java.lang.reflect.ParameterizedType;
 32  
 import java.lang.reflect.Type;
 33  
 import java.math.BigDecimal;
 34  
 import java.math.BigInteger;
 35  
 import java.util.ArrayList;
 36  
 import java.util.Collection;
 37  
 import java.util.Comparator;
 38  
 import java.util.HashMap;
 39  
 import java.util.HashSet;
 40  
 import java.util.List;
 41  
 import java.util.Map;
 42  
 import java.util.Queue;
 43  
 import java.util.Set;
 44  
 import java.util.SortedMap;
 45  
 import java.util.SortedSet;
 46  
 import java.util.TreeMap;
 47  
 import java.util.TreeSet;
 48  
 import java.util.concurrent.ArrayBlockingQueue;
 49  
 import java.util.concurrent.ConcurrentHashMap;
 50  
 import java.util.concurrent.ConcurrentMap;
 51  
 
 52  
 import javax.json.JsonArray;
 53  
 import javax.json.JsonNumber;
 54  
 import javax.json.JsonObject;
 55  
 import javax.json.JsonReader;
 56  
 import javax.json.JsonReaderFactory;
 57  
 import javax.json.JsonString;
 58  
 import javax.json.JsonValue;
 59  
 import javax.json.JsonValue.ValueType;
 60  
 import javax.json.stream.JsonGenerator;
 61  
 import javax.json.stream.JsonGeneratorFactory;
 62  
 import javax.xml.bind.DatatypeConverter;
 63  
 
 64  
 import org.apache.johnzon.mapper.access.AccessMode;
 65  
 import org.apache.johnzon.mapper.converter.EnumConverter;
 66  
 import org.apache.johnzon.mapper.reflection.JohnzonCollectionType;
 67  
 import org.apache.johnzon.mapper.reflection.JohnzonParameterizedType;
 68  
 import org.apache.johnzon.mapper.reflection.Mappings;
 69  
 
 70  
 public class Mapper {
 71  1
     private static final Converter<Object> FALLBACK_CONVERTER = new FallbackConverter();
 72  1
     private static final JohnzonParameterizedType ANY_LIST = new JohnzonParameterizedType(List.class, Object.class);
 73  
 
 74  
     protected final Mappings mappings;
 75  
     protected final JsonReaderFactory readerFactory;
 76  
     protected final JsonGeneratorFactory generatorFactory;
 77  
     protected final boolean close;
 78  
     protected final ConcurrentMap<Type, Converter<?>> converters;
 79  
     protected final int version;
 80  
     //private final boolean hiddenConstructorSupported;
 81  
     //private final AccessMode accessMode;
 82  
     protected boolean skipNull;
 83  
     protected boolean skipEmptyArray;
 84  
     protected boolean treatByteArrayAsBase64;
 85  
 
 86  
     public Mapper(final JsonReaderFactory readerFactory, final JsonGeneratorFactory generatorFactory,
 87  
                   final boolean doClose, final Map<Class<?>, Converter<?>> converters,
 88  
                   final int version, final Comparator<String> attributeOrder, boolean skipNull, boolean skipEmptyArray,
 89  56
                   final AccessMode accessMode, final boolean hiddenConstructorSupported, boolean treatByteArrayAsBase64) {
 90  56
         this.readerFactory = readerFactory;
 91  56
         this.generatorFactory = generatorFactory;
 92  56
         this.close = doClose;
 93  56
         this.converters = new ConcurrentHashMap<Type, Converter<?>>(converters);
 94  56
         this.version = version;
 95  56
         this.mappings = new Mappings(attributeOrder, accessMode, hiddenConstructorSupported);
 96  56
         this.skipNull = skipNull;
 97  56
         this.skipEmptyArray = skipEmptyArray;
 98  
         //this.accessMode = accessMode;
 99  
         //this.hiddenConstructorSupported = hiddenConstructorSupported;
 100  56
         this.treatByteArrayAsBase64 = treatByteArrayAsBase64;
 101  56
     }
 102  
 
 103  
     private static JsonGenerator writePrimitives(final JsonGenerator generator, final Object value) {
 104  81
         if (value == null) {
 105  0
             return null; // fake a write
 106  
         }
 107  
 
 108  81
         final Class<?> type = value.getClass();
 109  81
         if (type == String.class) {
 110  15
             return generator.write(value.toString());
 111  66
         } else if (type == long.class || type == Long.class) {
 112  0
             return generator.write(Long.class.cast(value).longValue());
 113  66
         } else if (isInt(type)) {
 114  53
             return generator.write(Number.class.cast(value).intValue());
 115  13
         } else if (isFloat(type)) {
 116  1
             final double doubleValue = Number.class.cast(value).doubleValue();
 117  1
             if (Double.isNaN(doubleValue)) {
 118  0
                 return generator;
 119  
             }
 120  1
             return generator.write(doubleValue);
 121  12
         } else if (type == boolean.class || type == Boolean.class) {
 122  0
             return generator.write(Boolean.class.cast(value));
 123  12
         } else if (type == BigDecimal.class) {
 124  0
             return generator.write(BigDecimal.class.cast(value));
 125  12
         } else if (type == BigInteger.class) {
 126  0
             return generator.write(BigInteger.class.cast(value));
 127  12
         } else if (type == char.class || type == Character.class) {
 128  2
             return generator.write(Character.class.cast(value).toString());
 129  
         }
 130  10
         return null;
 131  
     }
 132  
 
 133  
     private static JsonGenerator writePrimitives(final JsonGenerator generator, final String key, final Class<?> type, final Object value) {
 134  82
         if (type == String.class) {
 135  20
             return generator.write(key, value.toString());
 136  62
         } else if (type == long.class || type == Long.class) {
 137  9
             return generator.write(key, Long.class.cast(value).longValue());
 138  53
         } else if (isInt(type)) {
 139  31
             return generator.write(key, Number.class.cast(value).intValue());
 140  22
         } else if (isFloat(type)) {
 141  4
             final double doubleValue = Number.class.cast(value).doubleValue();
 142  4
             if (Double.isNaN(doubleValue)) {
 143  1
                 return generator;
 144  
             }
 145  3
             return generator.write(key, doubleValue);
 146  18
         } else if (type == boolean.class || type == Boolean.class) {
 147  11
             return generator.write(key, Boolean.class.cast(value));
 148  7
         } else if (type == BigDecimal.class) {
 149  4
             return generator.write(key, BigDecimal.class.cast(value));
 150  3
         } else if (type == BigInteger.class) {
 151  2
             return generator.write(key, BigInteger.class.cast(value));
 152  1
         } else if (type == char.class || type == Character.class) {
 153  1
             return generator.write(key, Character.class.cast(value).toString());
 154  
         }
 155  0
         return generator;
 156  
     }
 157  
 
 158  
     private static boolean isInt(final Class<?> type) {
 159  119
         return type == int.class || type == Integer.class
 160  
                 || type == byte.class || type == Byte.class
 161  
                 || type == short.class || type == Short.class;
 162  
     }
 163  
 
 164  
     private static boolean isFloat(final Class<?> type) {
 165  35
         return type == double.class || type == Double.class
 166  
                 || type == float.class || type == Float.class;
 167  
     }
 168  
 
 169  
     /*private <T> String convertFrom(final Class<T> aClass, final T value) {
 170  
         final Converter<T> converter = (Converter<T>) findConverter(aClass);
 171  
         return doConverFrom(value, converter);
 172  
     }*/
 173  
 
 174  
     private static <T> String doConverFrom(final T value, final Converter<T> converter) {
 175  0
         if (converter == null) {
 176  0
             throw new MapperException("can't convert " + value + " to String");
 177  
         }
 178  0
         return converter.toString(value);
 179  
     }
 180  
 
 181  
     private <T> Converter<T> findConverter(final Type aClass) {
 182  57
         final Converter<T> converter = (Converter<T>) converters.get(aClass);
 183  57
         if (converter != null) {
 184  54
             return converter;
 185  
         }
 186  3
         if (Class.class.isInstance(aClass)) {
 187  2
             final Class<?> clazz = Class.class.cast(aClass);
 188  2
             if (clazz.isEnum()) {
 189  0
                 final Converter<T> enumConverter = new EnumConverter(clazz);
 190  0
                 converters.putIfAbsent(clazz, enumConverter);
 191  0
                 return enumConverter;
 192  
             }
 193  
         }
 194  3
         return null;
 195  
     }
 196  
 
 197  
     private Object convertTo(final Type aClass, final String text) {
 198  60
         if (Object.class == aClass) {
 199  5
             return text;
 200  
         }
 201  55
         final Converter<?> converter = findConverter(aClass);
 202  55
         if (converter == null) {
 203  1
             converters.putIfAbsent(aClass, FALLBACK_CONVERTER);
 204  1
             return FALLBACK_CONVERTER;
 205  
         }
 206  54
         return converter.fromString(text);
 207  
     }
 208  
 
 209  
     public <T> void writeArray(final Object object, final OutputStream stream) {
 210  0
         writeArray(asList((T[]) object), stream);
 211  0
     }
 212  
 
 213  
     public <T> void writeArray(final T[] object, final OutputStream stream) {
 214  3
         writeArray(asList(object), stream);
 215  3
     }
 216  
 
 217  
     public <T> void writeArray(final T[] object, final Writer stream) {
 218  2
         writeArray(asList(object), stream);
 219  2
     }
 220  
 
 221  
     public <T> void writeArray(final Collection<T> object, final OutputStream stream) {
 222  4
         writeArray(object, new OutputStreamWriter(stream));
 223  4
     }
 224  
 
 225  
     public <T> void writeArray(final Collection<T> object, final Writer stream) {
 226  6
         JsonGenerator generator = generatorFactory.createGenerator(stream);
 227  
         try {
 228  6
             generator = doWriteArray(object, generator);
 229  
         } finally {
 230  6
             doCloseOrFlush(generator);
 231  6
         }
 232  6
     }
 233  
 
 234  
     private <T> JsonGenerator doWriteArray(final Collection<T> object, final JsonGenerator inGenerator) {
 235  8
         JsonGenerator generator = inGenerator;
 236  8
         if (object == null) {
 237  0
             generator = generator.writeStartArray().writeEnd();
 238  
         } else {
 239  8
             generator = generator.writeStartArray();
 240  8
             for (final T t : object) {
 241  18
                 generator = writeItem(generator, t);
 242  18
             }
 243  8
             generator = generator.writeEnd();
 244  
         }
 245  8
         return generator;
 246  
     }
 247  
 
 248  
     private void doCloseOrFlush(final JsonGenerator generator) {
 249  34
         if (close) {
 250  0
             generator.close();
 251  
         } else {
 252  34
             generator.flush();
 253  
         }
 254  34
     }
 255  
 
 256  
     public <T> void writeIterable(final Iterable<T> object, final OutputStream stream) {
 257  0
         writeIterable(object, new OutputStreamWriter(stream));
 258  0
     }
 259  
 
 260  
     public <T> void writeIterable(final Iterable<T> object, final Writer stream) {
 261  1
         JsonGenerator generator = generatorFactory.createGenerator(stream);
 262  
         try {
 263  1
             if (object == null) {
 264  0
                 generator = generator.writeStartArray().writeEnd();
 265  
             } else {
 266  1
                 generator.writeStartArray();
 267  1
                 for (final T t : object) {
 268  3
                     generator = writeItem(generator, t);
 269  3
                 }
 270  1
                 generator.writeEnd();
 271  
             }
 272  
         } finally {
 273  1
             doCloseOrFlush(generator);
 274  1
         }
 275  1
     }
 276  
 
 277  
     public void writeObject(final Object object, final Writer stream) {
 278  28
         final JsonGenerator generator = generatorFactory.createGenerator(stream);
 279  28
         doWriteHandlingNullObject(object, generator);
 280  27
     }
 281  
 
 282  
     public void writeObject(final Object object, final OutputStream stream) {
 283  1
         final JsonGenerator generator = generatorFactory.createGenerator(stream);
 284  1
         doWriteHandlingNullObject(object, generator);
 285  1
     }
 286  
 
 287  
     public String writeObjectAsString(final Object instance) {
 288  1
         final StringWriter writer = new StringWriter();
 289  1
         writeObject(instance, writer);
 290  1
         return writer.toString();
 291  
     }
 292  
 
 293  
     private void doWriteHandlingNullObject(final Object object, final JsonGenerator generator) {
 294  29
         if (object == null) {
 295  2
             generator.writeStartObject().writeEnd().close();
 296  2
             return;
 297  
         }
 298  
 
 299  
         //JsonGenerator gen = null;
 300  
         try {
 301  
             /*gen = */
 302  27
             doWriteObject(generator, object);
 303  
         } finally {
 304  27
             doCloseOrFlush(generator);
 305  26
         }
 306  26
     }
 307  
 
 308  
     private JsonGenerator doWriteObject(final JsonGenerator generator, final Object object) {
 309  
         try {
 310  33
             JsonGenerator gen = generator;
 311  33
             if (object == null) {
 312  0
                 return generator;
 313  
             }
 314  
 
 315  33
             if (Map.class.isInstance(object)) {
 316  1
                 gen = gen.writeStartObject();
 317  1
                 gen = writeMapBody((Map<?, ?>) object, gen);
 318  1
                 gen = gen.writeEnd();
 319  1
                 return gen;
 320  
             }
 321  
 
 322  32
             gen = gen.writeStartObject();
 323  32
             gen = doWriteObjectBody(gen, object);
 324  31
             return gen.writeEnd();
 325  0
         } catch (final InvocationTargetException e) {
 326  0
             throw new MapperException(e);
 327  0
         } catch (final IllegalAccessException e) {
 328  0
             throw new MapperException(e);
 329  
         }
 330  
     }
 331  
 
 332  
     private JsonGenerator doWriteObjectBody(final JsonGenerator gen, final Object object) throws IllegalAccessException, InvocationTargetException {
 333  34
         final Class<?> objectClass = object.getClass();
 334  34
         final Mappings.ClassMapping classMapping = mappings.findOrCreateClassMapping(objectClass);
 335  34
         if (classMapping == null) {
 336  0
             throw new MapperException("No mapping for " + objectClass.getName());
 337  
         }
 338  
 
 339  34
         JsonGenerator generator = gen;
 340  34
         for (final Map.Entry<String, Mappings.Getter> getterEntry : classMapping.getters.entrySet()) {
 341  154
             final Mappings.Getter getter = getterEntry.getValue();
 342  154
             final Object value = getter.reader.read(object);
 343  154
             if (getter.version >= 0 && version >= getter.version) {
 344  2
                 continue;
 345  
             }
 346  
 
 347  152
             if (value == null) {
 348  43
                 if (skipNull) {
 349  37
                     continue;
 350  
                 } else {
 351  6
                     gen.writeNull(getterEntry.getKey());
 352  6
                     continue;
 353  
                 }
 354  
             }
 355  
 
 356  218
             generator = writeValue(generator, value.getClass(),
 357  
                     getter.primitive, getter.array,
 358  
                     getter.collection, getter.map,
 359  109
                     getterEntry.getKey(),
 360  1
                     getter.converter == null ? value : getter.converter.toString(value));
 361  108
         }
 362  33
         return generator;
 363  
     }
 364  
 
 365  
     private JsonGenerator writeMapBody(final Map<?, ?> object, final JsonGenerator gen) throws InvocationTargetException, IllegalAccessException {
 366  7
         JsonGenerator generator = gen;
 367  7
         for (final Map.Entry<?, ?> entry : ((Map<?, ?>) object).entrySet()) {
 368  14
             final Object value = entry.getValue();
 369  14
             final Object key = entry.getKey();
 370  
 
 371  14
             if (value == null) {
 372  2
                 if (skipNull) {
 373  1
                     continue;
 374  
                 } else {
 375  1
                     gen.writeNull(key == null ? "null" : key.toString());
 376  1
                     continue;
 377  
                 }
 378  
             }
 379  
 
 380  12
             final Class<?> valueClass = value.getClass();
 381  12
             final boolean primitive = Mappings.isPrimitive(valueClass);
 382  12
             final boolean clazz = mappings.getClassMapping(valueClass) != null;
 383  12
             final boolean array = clazz || primitive ? false : valueClass.isArray();
 384  12
             final boolean collection = clazz || primitive || array ? false : Collection.class.isAssignableFrom(valueClass);
 385  12
             final boolean map = clazz || primitive || array || collection ? false : Map.class.isAssignableFrom(valueClass);
 386  24
             generator = writeValue(generator, valueClass,
 387  
                     primitive, array, collection, map,
 388  10
                     key == null ? "null" : key.toString(), value);
 389  12
         }
 390  7
         return generator;
 391  
     }
 392  
 
 393  
     private JsonGenerator writeValue(final JsonGenerator generator, final Class<?> type,
 394  
                                      final boolean primitive, final boolean array,
 395  
                                      final boolean collection, final boolean map,
 396  
                                      final String key, final Object value) throws InvocationTargetException, IllegalAccessException {
 397  121
         if (array) {
 398  23
             final int length = Array.getLength(value);
 399  23
             if (length == 0 && skipEmptyArray) {
 400  2
                 return generator;
 401  
             }
 402  
             
 403  21
             if(treatByteArrayAsBase64 && (type == byte[].class /*|| type == Byte[].class*/)) {
 404  2
                 String base64EncodedByteArray = DatatypeConverter.printBase64Binary((byte[]) value);
 405  2
                 generator.write(key, base64EncodedByteArray);
 406  2
                 return generator;
 407  
             }
 408  
 
 409  19
             JsonGenerator gen = generator.writeStartArray(key);
 410  62
             for (int i = 0; i < length; i++) {
 411  43
                 gen = writeItem(gen, Array.get(value, i));
 412  
             }
 413  19
             return gen.writeEnd();
 414  98
         } else if (collection) {
 415  8
             JsonGenerator gen = generator.writeStartArray(key);
 416  8
             for (final Object o : Collection.class.cast(value)) {
 417  13
                 gen = writeItem(gen, o);
 418  13
             }
 419  8
             return gen.writeEnd();
 420  90
         } else if (map) {
 421  6
             JsonGenerator gen = generator.writeStartObject(key);
 422  6
             gen = writeMapBody((Map<?, ?>) value, gen);
 423  6
             return gen.writeEnd();
 424  84
         } else if (primitive) {
 425  82
             return writePrimitives(generator, key, type, value);
 426  
         } else {
 427  2
             final Converter<?> converter = findConverter(type);
 428  2
             if (converter != null) {
 429  0
                 return writeValue(generator, String.class, true, false, false, false, key,
 430  0
                         doConverFrom(value, (Converter<Object>) converter));
 431  
             }
 432  2
             return doWriteObjectBody(generator.writeStartObject(key), value).writeEnd();
 433  
         }
 434  
     }
 435  
 
 436  
     private JsonGenerator writeItem(final JsonGenerator generator, final Object o) {
 437  81
         JsonGenerator newGen = writePrimitives(generator, o);
 438  81
         if (newGen == null) {
 439  10
             if (Collection.class.isInstance(o)) {
 440  2
                 newGen = doWriteArray(Collection.class.cast(o), generator);
 441  8
             } else if (o != null && o.getClass().isArray()) {
 442  2
                 final int length = Array.getLength(o);
 443  2
                 if (length > 0 || !skipEmptyArray) {
 444  2
                     newGen = generator.writeStartArray();
 445  6
                     for (int i = 0; i < length; i++) {
 446  4
                         newGen = writeItem(newGen, Array.get(o, i));
 447  
                     }
 448  2
                     newGen = newGen.writeEnd();
 449  
                 }
 450  2
             } else {
 451  6
                 newGen = doWriteObject(generator, o);
 452  
             }
 453  
         }
 454  81
         return newGen;
 455  
     }
 456  
 
 457  
     public <T> T readObject(final Reader stream, final Type clazz) {
 458  8
         final JsonReader reader = readerFactory.createReader(stream);
 459  8
         return mapObject(clazz, reader);
 460  
     }
 461  
 
 462  
     public <T> T readObject(final InputStream stream, final Type clazz) {
 463  11
         final JsonReader reader = readerFactory.createReader(stream);
 464  11
         return mapObject(clazz, reader);
 465  
     }
 466  
 
 467  
     private <T> T mapObject(final Type clazz, final JsonReader reader) {
 468  
         try {
 469  19
             return (T) buildObject(clazz, reader.readObject());
 470  3
         } catch (final Exception e) {
 471  3
             throw new MapperException(e);
 472  
         } finally {
 473  19
             if (close) {
 474  0
                 reader.close();
 475  
             }
 476  
         }
 477  
     }
 478  
 
 479  
     public <T> Collection<T> readCollection(final InputStream stream, final ParameterizedType genericType) {
 480  2
         final JsonReader reader = readerFactory.createReader(stream);
 481  2
         final Mappings.CollectionMapping mapping = mappings.findCollectionMapping(genericType);
 482  2
         if (mapping == null) {
 483  0
             throw new UnsupportedOperationException("type " + genericType + " not supported");
 484  
         }
 485  
         try {
 486  2
             return mapCollection(mapping, reader.readArray());
 487  0
         } catch (final Exception e) {
 488  0
             throw new MapperException(e);
 489  
         } finally {
 490  2
             if (close) {
 491  0
                 reader.close();
 492  
             }
 493  
         }
 494  
     }
 495  
 
 496  
     public <T> T readJohnzonCollection(final InputStream stream, final JohnzonCollectionType<T> genericType) {
 497  1
         return (T) readCollection(stream, genericType);
 498  
     }
 499  
 
 500  
     public <T> T readJohnzonCollection(final Reader stream, final JohnzonCollectionType<T> genericType) {
 501  0
         return (T) readCollection(stream, genericType);
 502  
     }
 503  
 
 504  
     public <T> Collection<T> readCollection(final Reader stream, final ParameterizedType genericType) {
 505  1
         final JsonReader reader = readerFactory.createReader(stream);
 506  1
         final Mappings.CollectionMapping mapping = mappings.findCollectionMapping(genericType);
 507  1
         if (mapping == null) {
 508  0
             throw new UnsupportedOperationException("type " + genericType + " not supported");
 509  
         }
 510  
         try {
 511  1
             return mapCollection(mapping, reader.readArray());
 512  0
         } catch (final Exception e) {
 513  0
             throw new MapperException(e);
 514  
         } finally {
 515  1
             if (close) {
 516  0
                 reader.close();
 517  
             }
 518  
         }
 519  
     }
 520  
 
 521  
     public <T> T[] readArray(final Reader stream, final Class<T> clazz) {
 522  0
         final JsonReader reader = readerFactory.createReader(stream);
 523  0
         return mapArray(clazz, reader);
 524  
     }
 525  
 
 526  
     public <T> T[] readArray(final InputStream stream, final Class<T> clazz) {
 527  2
         final JsonReader reader = readerFactory.createReader(stream);
 528  2
         return mapArray(clazz, reader);
 529  
     }
 530  
 
 531  
     private <T> T[] mapArray(final Class<T> clazz, final JsonReader reader) {
 532  
         try {
 533  2
             return (T[]) buildArrayWithComponentType(reader.readArray(), clazz);
 534  0
         } catch (final Exception e) {
 535  0
             throw new MapperException(e);
 536  
         } finally {
 537  2
             if (close) {
 538  0
                 reader.close();
 539  
             }
 540  
         }
 541  
     }
 542  
 
 543  
     private Object buildObject(final Type inType, final JsonObject object) throws Exception {
 544  47
         Type type = inType;
 545  47
         if (inType == Object.class) {
 546  1
             type = new JohnzonParameterizedType(Map.class, String.class, Object.class);
 547  
         }
 548  
 
 549  47
         final Mappings.ClassMapping classMapping = mappings.findOrCreateClassMapping(type);
 550  
 
 551  47
         if (classMapping == null) {
 552  9
             if (ParameterizedType.class.isInstance(type)) {
 553  9
                 final ParameterizedType aType = ParameterizedType.class.cast(type);
 554  9
                 final Type[] fieldArgTypes = aType.getActualTypeArguments();
 555  9
                 if (fieldArgTypes.length >= 2) {
 556  9
                     final Class<?> raw = Class.class.cast(aType.getRawType());
 557  
 
 558  
                     final Map map;
 559  9
                     if (SortedMap.class.isAssignableFrom(raw)) {
 560  1
                         map = new TreeMap();
 561  8
                     } else if (ConcurrentMap.class.isAssignableFrom(raw)) {
 562  0
                         map = new ConcurrentHashMap(object.size());
 563  8
                     } else if (Map.class.isAssignableFrom(raw)) {
 564  8
                         map = new HashMap(object.size());
 565  
                     } else {
 566  0
                         map = null;
 567  
                     }
 568  
 
 569  9
                     if (map != null) {
 570  
 
 571  
                         Type keyType;
 572  9
                         if (ParameterizedType.class.isInstance(fieldArgTypes[0])) {
 573  2
                             keyType = fieldArgTypes[0];
 574  
                         } else {
 575  7
                             keyType = fieldArgTypes[0];
 576  
                         }
 577  
 
 578  9
                         for (final Map.Entry<String, JsonValue> value : object.entrySet()) {
 579  22
                             map.put(convertTo(keyType, value.getKey()), toObject(value.getValue(), fieldArgTypes[1]));
 580  20
                         }
 581  7
                         return map;
 582  
                     }
 583  
                 }
 584  
             }
 585  
         }
 586  38
         if (classMapping == null) {
 587  0
             throw new MapperException("Can't map " + type);
 588  
         }
 589  
 
 590  38
         if (classMapping.constructor == null) {
 591  0
             throw new IllegalArgumentException(classMapping.clazz.getName() + " can't be instantiated by Johnzon, this is a write only class");
 592  
         }
 593  
 
 594  38
         final Object t = classMapping.constructor.newInstance();
 595  38
         for (final Map.Entry<String, Mappings.Setter> setter : classMapping.setters.entrySet()) {
 596  278
             final JsonValue jsonValue = object.get(setter.getKey());
 597  278
             final Mappings.Setter value = setter.getValue();
 598  278
             final AccessMode.Writer setterMethod = value.writer;
 599  278
             final Object convertedValue = value.converter == null ?
 600  277
                     toObject(jsonValue, value.paramType) : jsonValue.getValueType() == ValueType.STRING ?
 601  1
                     value.converter.fromString(JsonString.class.cast(jsonValue).getString()) :
 602  0
                     value.converter.fromString(jsonValue.toString());
 603  
 
 604  275
             if (convertedValue != null) {
 605  130
                 setterMethod.write(t, convertedValue);
 606  
             }
 607  275
         }
 608  
 
 609  35
         return t;
 610  
     }
 611  
 
 612  
     private Object toObject(final JsonValue jsonValue, final Type type) throws Exception {
 613  380
         if (jsonValue == null || jsonValue == JsonValue.NULL) {
 614  147
             return null;
 615  
         }
 616  
 
 617  233
         if (type == Boolean.class || type == boolean.class) {
 618  18
             if (jsonValue == JsonValue.TRUE) {
 619  8
                 return true;
 620  
             }
 621  10
             if (jsonValue == JsonValue.FALSE) {
 622  8
                 return false;
 623  
             }
 624  2
             throw new MapperException("Unable to parse " + jsonValue + " to boolean");
 625  
         }
 626  
 
 627  215
         if(treatByteArrayAsBase64 && jsonValue.getValueType() == ValueType.STRING && (type == byte[].class /*|| type == Byte[].class*/)) {
 628  1
             return DatatypeConverter.parseBase64Binary(((JsonString)jsonValue).getString());
 629  
         }
 630  
 
 631  214
         if (Object.class == type) { // handling specific types here to keep exception in standard handling
 632  20
             if (jsonValue == JsonValue.TRUE) {
 633  2
                 return true;
 634  
             }
 635  18
             if (jsonValue == JsonValue.FALSE) {
 636  0
                 return false;
 637  
             }
 638  18
             if (JsonNumber.class.isInstance(jsonValue)) {
 639  8
                 final JsonNumber jsonNumber = JsonNumber.class.cast(jsonValue);
 640  8
                 if(jsonNumber.isIntegral()) {
 641  7
                     return jsonNumber.intValue();
 642  
                 }
 643  1
                 return jsonNumber.doubleValue();
 644  
             }
 645  
         }
 646  
 
 647  204
         if (type == Character.class || type == char.class) {
 648  3
             return convertTo(Class.class.cast(type), (JsonString.class.cast(jsonValue).getString()));
 649  
         }
 650  
 
 651  201
         if (JsonObject.class.isInstance(jsonValue)) {
 652  28
             return buildObject(type, JsonObject.class.cast(jsonValue));
 653  173
         } else if (JsonArray.class.isInstance(jsonValue)) {
 654  30
             return buildArray(type, JsonArray.class.cast(jsonValue));
 655  143
         } else if (JsonNumber.class.isInstance(jsonValue)) {
 656  
 
 657  108
             final JsonNumber number = JsonNumber.class.cast(jsonValue);
 658  
 
 659  108
             if (type == Integer.class || type == int.class) {
 660  53
                 return number.intValue();
 661  
             }
 662  
 
 663  55
             if (type == Long.class || type == long.class) {
 664  22
                 return number.longValue();
 665  
             }
 666  
 
 667  33
             if (type == Short.class || type == short.class) {
 668  13
                 return (short) number.intValue();
 669  
             }
 670  
 
 671  20
             if (type == Byte.class || type == byte.class) {
 672  14
                 return (byte) number.intValue();
 673  
             }
 674  
 
 675  6
             if (type == Float.class || type == float.class) {
 676  1
                 return (float) number.doubleValue();
 677  
             }
 678  
 
 679  5
             if (type == Double.class || type == double.class) {
 680  1
                 return number.doubleValue();
 681  
             }
 682  
 
 683  4
             if (type == BigInteger.class) {
 684  1
                 return number.bigIntegerValue();
 685  
             }
 686  
 
 687  3
             if (type == BigDecimal.class) {
 688  3
                 return number.bigDecimalValue();
 689  
             }
 690  0
         } else if (JsonString.class.isInstance(jsonValue) || Object.class == type) {
 691  35
             return convertTo(Class.class.cast(type), JsonString.class.cast(jsonValue).getString());
 692  
         }
 693  
 
 694  0
         throw new MapperException("Unable to parse " + jsonValue + " to " + type);
 695  
     }
 696  
 
 697  
     private Object buildArray(final Type type, final JsonArray jsonArray) throws Exception {
 698  34
         if (Class.class.isInstance(type)) {
 699  18
             final Class clazz = Class.class.cast(type);
 700  18
             if (clazz.isArray()) {
 701  14
                 final Class<?> componentType = clazz.getComponentType();
 702  14
                 return buildArrayWithComponentType(jsonArray, componentType);
 703  
             }
 704  
         }
 705  
 
 706  20
         if (ParameterizedType.class.isInstance(type)) {
 707  16
             final Mappings.CollectionMapping mapping = mappings.findCollectionMapping(ParameterizedType.class.cast(type));
 708  16
             if (mapping != null) {
 709  16
                 return mapCollection(mapping, jsonArray);
 710  
             }
 711  
         }
 712  
 
 713  4
         if (Object.class == type) {
 714  4
             return buildArray(ANY_LIST, jsonArray);
 715  
         }
 716  
 
 717  0
         throw new UnsupportedOperationException("type " + type + " not supported");
 718  
     }
 719  
 
 720  
     private <T> Collection<T> mapCollection(final Mappings.CollectionMapping mapping, final JsonArray jsonArray) throws Exception {
 721  
         final Collection collection;
 722  
 
 723  19
         if (SortedSet.class == mapping.raw) {
 724  3
             collection = new TreeSet<T>();
 725  16
         } else if (Set.class == mapping.raw) {
 726  0
             collection = new HashSet<T>(jsonArray.size());
 727  16
         } else if (Queue.class == mapping.raw) {
 728  1
             collection = new ArrayBlockingQueue<T>(jsonArray.size());
 729  15
         } else if (List.class == mapping.raw || Collection.class == mapping.raw) {
 730  15
             collection = new ArrayList<T>(jsonArray.size());
 731  
         } else {
 732  0
             throw new IllegalStateException("not supported collection type: " + mapping.raw.getName());
 733  
         }
 734  
 
 735  19
         for (final JsonValue value : jsonArray) {
 736  40
             final Object element = toObject(value, mapping.arg);
 737  40
             collection.add(element);
 738  40
         }
 739  19
         return collection;
 740  
     }
 741  
 
 742  
     private Object buildArrayWithComponentType(final JsonArray jsonArray, final Class<?> componentType) throws Exception {
 743  16
         final Object array = Array.newInstance(componentType, jsonArray.size());
 744  16
         int i = 0;
 745  16
         for (final JsonValue value : jsonArray) {
 746  42
             Array.set(array, i++, toObject(value, componentType));
 747  42
         }
 748  16
         return array;
 749  
     }
 750  
 
 751  2
     private static class FallbackConverter implements Converter<Object> {
 752  
         @Override
 753  
         public String toString(final Object instance) {
 754  0
             return instance.toString();
 755  
         }
 756  
 
 757  
         @Override
 758  
         public Object fromString(final String text) {
 759  1
             throw new UnsupportedOperationException("Using fallback converter, " +
 760  
                     "this only works in write mode but not in read. Please register a custom converter to do so.");
 761  
         }
 762  
     }
 763  
 }