Coverage Report - org.apache.johnzon.mapper.Mapper
 
Classes in this File Line Coverage Branch Coverage Complexity
Mapper
86 %
335/387
80 %
256/320
6,261
Mapper$1
N/A
N/A
6,261
Mapper$FallbackConverter
66 %
2/3
N/A
6,261
 
 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.StringReader;
 28  
 import java.io.StringWriter;
 29  
 import java.io.Writer;
 30  
 import java.lang.reflect.Array;
 31  
 import java.lang.reflect.InvocationTargetException;
 32  
 import java.lang.reflect.ParameterizedType;
 33  
 import java.lang.reflect.Type;
 34  
 import java.math.BigDecimal;
 35  
 import java.math.BigInteger;
 36  
 import java.util.ArrayList;
 37  
 import java.util.Collection;
 38  
 import java.util.Comparator;
 39  
 import java.util.HashMap;
 40  
 import java.util.HashSet;
 41  
 import java.util.List;
 42  
 import java.util.Map;
 43  
 import java.util.Queue;
 44  
 import java.util.Set;
 45  
 import java.util.SortedMap;
 46  
 import java.util.SortedSet;
 47  
 import java.util.TreeMap;
 48  
 import java.util.TreeSet;
 49  
 import java.util.concurrent.ArrayBlockingQueue;
 50  
 import java.util.concurrent.ConcurrentHashMap;
 51  
 import java.util.concurrent.ConcurrentMap;
 52  
 
 53  
 import javax.json.JsonArray;
 54  
 import javax.json.JsonNumber;
 55  
 import javax.json.JsonObject;
 56  
 import javax.json.JsonReader;
 57  
 import javax.json.JsonReaderFactory;
 58  
 import javax.json.JsonString;
 59  
 import javax.json.JsonValue;
 60  
 import javax.json.JsonValue.ValueType;
 61  
 import javax.json.stream.JsonGenerator;
 62  
 import javax.json.stream.JsonGeneratorFactory;
 63  
 import javax.xml.bind.DatatypeConverter;
 64  
 
 65  
 import org.apache.johnzon.mapper.access.AccessMode;
 66  
 import org.apache.johnzon.mapper.converter.EnumConverter;
 67  
 import org.apache.johnzon.mapper.reflection.JohnzonCollectionType;
 68  
 import org.apache.johnzon.mapper.reflection.JohnzonParameterizedType;
 69  
 import org.apache.johnzon.mapper.reflection.Mappings;
 70  
 
 71  
 public class Mapper {
 72  1
     private static final Converter<Object> FALLBACK_CONVERTER = new FallbackConverter();
 73  1
     private static final JohnzonParameterizedType ANY_LIST = new JohnzonParameterizedType(List.class, Object.class);
 74  
 
 75  
     protected final Mappings mappings;
 76  
     protected final JsonReaderFactory readerFactory;
 77  
     protected final JsonGeneratorFactory generatorFactory;
 78  
     protected final boolean close;
 79  
     protected final ConcurrentMap<Type, Converter<?>> converters;
 80  
     protected final int version;
 81  
     protected final boolean skipNull;
 82  
     protected final boolean skipEmptyArray;
 83  
     protected final boolean treatByteArrayAsBase64;
 84  
 
 85  
     // CHECKSTYLE:OFF
 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, final boolean skipNull, final boolean skipEmptyArray,
 89  
                   final AccessMode accessMode, final boolean hiddenConstructorSupported, final boolean useConstructors,
 90  63
                   final boolean treatByteArrayAsBase64) {
 91  
     // CHECKSTYLE:ON
 92  63
         this.readerFactory = readerFactory;
 93  63
         this.generatorFactory = generatorFactory;
 94  63
         this.close = doClose;
 95  63
         this.converters = new ConcurrentHashMap<Type, Converter<?>>(converters);
 96  63
         this.version = version;
 97  63
         this.mappings = new Mappings(attributeOrder, accessMode, hiddenConstructorSupported, useConstructors, version);
 98  63
         this.skipNull = skipNull;
 99  63
         this.skipEmptyArray = skipEmptyArray;
 100  63
         this.treatByteArrayAsBase64 = treatByteArrayAsBase64;
 101  63
     }
 102  
 
 103  
     private static JsonGenerator writePrimitives(final JsonGenerator generator, final Object value) {
 104  85
         if (value == null) {
 105  0
             return null; // fake a write
 106  
         }
 107  
 
 108  85
         final Class<?> type = value.getClass();
 109  85
         if (type == String.class) {
 110  19
             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  88
         if (type == String.class) {
 135  22
             return generator.write(key, value.toString());
 136  66
         } else if (type == long.class || type == Long.class) {
 137  9
             return generator.write(key, Long.class.cast(value).longValue());
 138  57
         } else if (isInt(type)) {
 139  34
             return generator.write(key, Number.class.cast(value).intValue());
 140  23
         } else if (isFloat(type)) {
 141  5
             final double doubleValue = Number.class.cast(value).doubleValue();
 142  5
             if (Double.isNaN(doubleValue)) {
 143  1
                 return generator;
 144  
             }
 145  4
             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  123
         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  36
         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  70
         final Converter<T> converter = (Converter<T>) converters.get(aClass);
 183  70
         if (converter != null) {
 184  67
             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  75
         if (Object.class == aClass) {
 199  7
             return text;
 200  
         }
 201  68
         final Converter<?> converter = findConverter(aClass);
 202  68
         if (converter == null) {
 203  1
             converters.putIfAbsent(aClass, FALLBACK_CONVERTER);
 204  1
             return FALLBACK_CONVERTER;
 205  
         }
 206  67
         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  37
         if (close) {
 250  0
             generator.close();
 251  
         } else {
 252  37
             generator.flush();
 253  
         }
 254  37
     }
 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  31
         final JsonGenerator generator = generatorFactory.createGenerator(stream);
 279  31
         doWriteHandlingNullObject(object, generator);
 280  30
     }
 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  4
         final StringWriter writer = new StringWriter();
 289  4
         writeObject(instance, writer);
 290  4
         return writer.toString();
 291  
     }
 292  
 
 293  
     private void doWriteHandlingNullObject(final Object object, final JsonGenerator generator) {
 294  32
         if (object == null) {
 295  2
             generator.writeStartObject().writeEnd().close();
 296  2
             return;
 297  
         }
 298  
 
 299  
         //JsonGenerator gen = null;
 300  
         try {
 301  
             /*gen = */
 302  30
             doWriteObject(generator, object);
 303  
         } finally {
 304  30
             doCloseOrFlush(generator);
 305  29
         }
 306  29
     }
 307  
 
 308  
     private JsonGenerator doWriteObject(final JsonGenerator generator, final Object object) {
 309  
         try {
 310  36
             JsonGenerator gen = generator;
 311  36
             if (object == null) {
 312  0
                 return generator;
 313  
             }
 314  
 
 315  36
             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  35
             gen = gen.writeStartObject();
 323  35
             gen = doWriteObjectBody(gen, object);
 324  34
             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  37
         final Class<?> objectClass = object.getClass();
 334  37
         final Mappings.ClassMapping classMapping = mappings.findOrCreateClassMapping(objectClass);
 335  37
         if (classMapping == null) {
 336  0
             throw new MapperException("No mapping for " + objectClass.getName());
 337  
         }
 338  
 
 339  37
         JsonGenerator generator = gen;
 340  37
         for (final Map.Entry<String, Mappings.Getter> getterEntry : classMapping.getters.entrySet()) {
 341  160
             final Mappings.Getter getter = getterEntry.getValue();
 342  160
             final Object value = getter.reader.read(object);
 343  160
             if (getter.version >= 0 && version >= getter.version) {
 344  2
                 continue;
 345  
             }
 346  
 
 347  158
             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  115
             final Object val = getter.converter == null ? value : getter.converter.toString(value);
 357  
 
 358  115
             generator = writeValue(generator, value.getClass(),
 359  
                     getter.primitive, getter.array,
 360  
                     getter.collection, getter.map,
 361  
                     getterEntry.getKey(),
 362  
                     val);
 363  114
         }
 364  36
         return generator;
 365  
     }
 366  
 
 367  
     private JsonGenerator writeMapBody(final Map<?, ?> object, final JsonGenerator gen) throws InvocationTargetException, IllegalAccessException {
 368  9
         JsonGenerator generator = gen;
 369  9
         for (final Map.Entry<?, ?> entry : ((Map<?, ?>) object).entrySet()) {
 370  18
             final Object value = entry.getValue();
 371  18
             final Object key = entry.getKey();
 372  
 
 373  18
             if (value == null) {
 374  2
                 if (skipNull) {
 375  1
                     continue;
 376  
                 } else {
 377  1
                     gen.writeNull(key == null ? "null" : key.toString());
 378  1
                     continue;
 379  
                 }
 380  
             }
 381  
 
 382  16
             final Class<?> valueClass = value.getClass();
 383  16
             final boolean primitive = Mappings.isPrimitive(valueClass);
 384  16
             final boolean clazz = mappings.getClassMapping(valueClass) != null;
 385  16
             final boolean array = clazz || primitive ? false : valueClass.isArray();
 386  16
             final boolean collection = clazz || primitive || array ? false : Collection.class.isAssignableFrom(valueClass);
 387  16
             final boolean map = clazz || primitive || array || collection ? false : Map.class.isAssignableFrom(valueClass);
 388  16
             generator = writeValue(generator, valueClass,
 389  
                     primitive, array, collection, map,
 390  
                     key == null ? "null" : key.toString(), value);
 391  16
         }
 392  9
         return generator;
 393  
     }
 394  
 
 395  
     private JsonGenerator writeValue(final JsonGenerator generator, final Class<?> type,
 396  
                                      final boolean primitive, final boolean array,
 397  
                                      final boolean collection, final boolean map,
 398  
                                      final String key, final Object value) throws InvocationTargetException, IllegalAccessException {
 399  131
         if (array) {
 400  24
             final int length = Array.getLength(value);
 401  24
             if (length == 0 && skipEmptyArray) {
 402  2
                 return generator;
 403  
             }
 404  
             
 405  22
             if(treatByteArrayAsBase64 && (type == byte[].class /*|| type == Byte[].class*/)) {
 406  2
                 String base64EncodedByteArray = DatatypeConverter.printBase64Binary((byte[]) value);
 407  2
                 generator.write(key, base64EncodedByteArray);
 408  2
                 return generator;
 409  
             }
 410  
 
 411  20
             JsonGenerator gen = generator.writeStartArray(key);
 412  65
             for (int i = 0; i < length; i++) {
 413  45
                 gen = writeItem(gen, Array.get(value, i));
 414  
             }
 415  20
             return gen.writeEnd();
 416  107
         } else if (collection) {
 417  9
             JsonGenerator gen = generator.writeStartArray(key);
 418  9
             for (final Object o : Collection.class.cast(value)) {
 419  15
                 gen = writeItem(gen, o);
 420  15
             }
 421  9
             return gen.writeEnd();
 422  98
         } else if (map) {
 423  8
             JsonGenerator gen = generator.writeStartObject(key);
 424  8
             gen = writeMapBody((Map<?, ?>) value, gen);
 425  8
             return gen.writeEnd();
 426  90
         } else if (primitive) {
 427  88
             return writePrimitives(generator, key, type, value);
 428  
         } else {
 429  2
             final Converter<?> converter = findConverter(type);
 430  2
             if (converter != null) {
 431  0
                 return writeValue(generator, String.class, true, false, false, false, key,
 432  
                         doConverFrom(value, (Converter<Object>) converter));
 433  
             }
 434  2
             return doWriteObjectBody(generator.writeStartObject(key), value).writeEnd();
 435  
         }
 436  
     }
 437  
 
 438  
     private JsonGenerator writeItem(final JsonGenerator generator, final Object o) {
 439  85
         JsonGenerator newGen = writePrimitives(generator, o);
 440  85
         if (newGen == null) {
 441  10
             if (Collection.class.isInstance(o)) {
 442  2
                 newGen = doWriteArray(Collection.class.cast(o), generator);
 443  8
             } else if (o != null && o.getClass().isArray()) {
 444  2
                 final int length = Array.getLength(o);
 445  2
                 if (length > 0 || !skipEmptyArray) {
 446  2
                     newGen = generator.writeStartArray();
 447  6
                     for (int i = 0; i < length; i++) {
 448  4
                         newGen = writeItem(newGen, Array.get(o, i));
 449  
                     }
 450  2
                     newGen = newGen.writeEnd();
 451  
                 }
 452  2
             } else {
 453  6
                 newGen = doWriteObject(generator, o);
 454  
             }
 455  
         }
 456  85
         return newGen;
 457  
     }
 458  
 
 459  
     public <T> T readObject(final String string, final Type clazz) {
 460  1
         return readObject(new StringReader(string), clazz);
 461  
     }
 462  
 
 463  
     public <T> T readObject(final Reader stream, final Type clazz) {
 464  9
         return mapObject(clazz, readerFactory.createReader(stream));
 465  
     }
 466  
 
 467  
     public <T> T readObject(final InputStream stream, final Type clazz) {
 468  15
         return mapObject(clazz, readerFactory.createReader(stream));
 469  
     }
 470  
 
 471  
     private <T> T mapObject(final Type clazz, final JsonReader reader) {
 472  
         try {
 473  24
             return (T) buildObject(clazz, reader.readObject());
 474  3
         } catch (final Exception e) {
 475  3
             throw new MapperException(e);
 476  
         } finally {
 477  24
             if (close) {
 478  0
                 reader.close();
 479  
             }
 480  
         }
 481  
     }
 482  
 
 483  
     public <T> Collection<T> readCollection(final InputStream stream, final ParameterizedType genericType) {
 484  2
         final JsonReader reader = readerFactory.createReader(stream);
 485  2
         final Mappings.CollectionMapping mapping = mappings.findCollectionMapping(genericType);
 486  2
         if (mapping == null) {
 487  0
             throw new UnsupportedOperationException("type " + genericType + " not supported");
 488  
         }
 489  
         try {
 490  2
             return mapCollection(mapping, reader.readArray());
 491  0
         } catch (final Exception e) {
 492  0
             throw new MapperException(e);
 493  
         } finally {
 494  2
             if (close) {
 495  0
                 reader.close();
 496  
             }
 497  
         }
 498  
     }
 499  
 
 500  
     public <T> T readJohnzonCollection(final InputStream stream, final JohnzonCollectionType<T> genericType) {
 501  1
         return (T) readCollection(stream, genericType);
 502  
     }
 503  
 
 504  
     public <T> T readJohnzonCollection(final Reader stream, final JohnzonCollectionType<T> genericType) {
 505  0
         return (T) readCollection(stream, genericType);
 506  
     }
 507  
 
 508  
     public <T> Collection<T> readCollection(final Reader stream, final ParameterizedType genericType) {
 509  1
         final JsonReader reader = readerFactory.createReader(stream);
 510  1
         final Mappings.CollectionMapping mapping = mappings.findCollectionMapping(genericType);
 511  1
         if (mapping == null) {
 512  0
             throw new UnsupportedOperationException("type " + genericType + " not supported");
 513  
         }
 514  
         try {
 515  1
             return mapCollection(mapping, reader.readArray());
 516  0
         } catch (final Exception e) {
 517  0
             throw new MapperException(e);
 518  
         } finally {
 519  1
             if (close) {
 520  0
                 reader.close();
 521  
             }
 522  
         }
 523  
     }
 524  
 
 525  
     public <T> T[] readArray(final Reader stream, final Class<T> clazz) {
 526  0
         final JsonReader reader = readerFactory.createReader(stream);
 527  0
         return mapArray(clazz, reader);
 528  
     }
 529  
 
 530  
     public <T> T[] readArray(final InputStream stream, final Class<T> clazz) {
 531  2
         final JsonReader reader = readerFactory.createReader(stream);
 532  2
         return mapArray(clazz, reader);
 533  
     }
 534  
 
 535  
     private <T> T[] mapArray(final Class<T> clazz, final JsonReader reader) {
 536  
         try {
 537  2
             return (T[]) buildArrayWithComponentType(reader.readArray(), clazz);
 538  0
         } catch (final Exception e) {
 539  0
             throw new MapperException(e);
 540  
         } finally {
 541  2
             if (close) {
 542  0
                 reader.close();
 543  
             }
 544  
         }
 545  
     }
 546  
 
 547  
     private Object buildObject(final Type inType, final JsonObject object) throws Exception {
 548  54
         Type type = inType;
 549  54
         if (inType == Object.class) {
 550  2
             type = new JohnzonParameterizedType(Map.class, String.class, Object.class);
 551  
         }
 552  
 
 553  54
         final Mappings.ClassMapping classMapping = mappings.findOrCreateClassMapping(type);
 554  
 
 555  54
         if (classMapping == null) {
 556  11
             if (ParameterizedType.class.isInstance(type)) {
 557  11
                 final ParameterizedType aType = ParameterizedType.class.cast(type);
 558  11
                 final Type[] fieldArgTypes = aType.getActualTypeArguments();
 559  11
                 if (fieldArgTypes.length >= 2) {
 560  11
                     final Class<?> raw = Class.class.cast(aType.getRawType());
 561  
 
 562  
                     final Map map;
 563  11
                     if (SortedMap.class.isAssignableFrom(raw)) {
 564  1
                         map = new TreeMap();
 565  10
                     } else if (ConcurrentMap.class.isAssignableFrom(raw)) {
 566  0
                         map = new ConcurrentHashMap(object.size());
 567  10
                     } else if (Map.class.isAssignableFrom(raw)) {
 568  10
                         map = new HashMap(object.size());
 569  
                     } else {
 570  0
                         map = null;
 571  
                     }
 572  
 
 573  11
                     if (map != null) {
 574  
 
 575  
                         Type keyType;
 576  11
                         if (ParameterizedType.class.isInstance(fieldArgTypes[0])) {
 577  2
                             keyType = fieldArgTypes[0];
 578  
                         } else {
 579  9
                             keyType = fieldArgTypes[0];
 580  
                         }
 581  
 
 582  11
                         for (final Map.Entry<String, JsonValue> value : object.entrySet()) {
 583  26
                             map.put(convertTo(keyType, value.getKey()), toObject(value.getValue(), fieldArgTypes[1]));
 584  24
                         }
 585  9
                         return map;
 586  
                     }
 587  
                 }
 588  
             }
 589  
         }
 590  43
         if (classMapping == null) {
 591  0
             throw new MapperException("Can't map " + type);
 592  
         }
 593  
 
 594  43
         if (classMapping.constructor == null) {
 595  0
             throw new IllegalArgumentException(classMapping.clazz.getName() + " can't be instantiated by Johnzon, this is a write only class");
 596  
         }
 597  
 
 598  43
         final Object t = !classMapping.constructorHasArguments ?
 599  
                 classMapping.constructor.newInstance() : classMapping.constructor.newInstance(createParameters(classMapping, object));
 600  43
         for (final Map.Entry<String, Mappings.Setter> setter : classMapping.setters.entrySet()) {
 601  282
             final JsonValue jsonValue = object.get(setter.getKey());
 602  282
             final Mappings.Setter value = setter.getValue();
 603  282
             final AccessMode.Writer setterMethod = value.writer;
 604  282
             final Object convertedValue = toValue(jsonValue, value.converter, value.paramType);
 605  
 
 606  279
             if (convertedValue != null) {
 607  134
                 setterMethod.write(t, convertedValue);
 608  
             }
 609  279
         }
 610  
 
 611  40
         return t;
 612  
     }
 613  
 
 614  
     private Object toValue(final JsonValue jsonValue, final Converter<?> converter, final Type type) throws Exception {
 615  285
         return converter == null ?
 616  
                 toObject(jsonValue, type) : jsonValue.getValueType() == ValueType.STRING ?
 617  
                 converter.fromString(JsonString.class.cast(jsonValue).getString()) :
 618  
                 converter.fromString(jsonValue.toString());
 619  
     }
 620  
 
 621  
     private Object[] createParameters(final Mappings.ClassMapping mapping, final JsonObject object) throws Exception {
 622  1
         final Object[] objects = new Object[mapping.constructorParameters.length];
 623  4
         for (int i = 0; i < mapping.constructorParameters.length; i++) {
 624  3
             objects[i] = toValue(object.get(mapping.constructorParameters[i]), mapping.constructorParameterConverters[i], mapping.constructorParameterTypes[i]);
 625  
         }
 626  1
         return objects;
 627  
     }
 628  
 
 629  
     private Object toObject(final JsonValue jsonValue, final Type type) throws Exception {
 630  398
         if (jsonValue == null || jsonValue == JsonValue.NULL) {
 631  147
             return null;
 632  
         }
 633  
 
 634  251
         if (type == Boolean.class || type == boolean.class) {
 635  18
             if (jsonValue == JsonValue.TRUE) {
 636  8
                 return true;
 637  
             }
 638  10
             if (jsonValue == JsonValue.FALSE) {
 639  8
                 return false;
 640  
             }
 641  2
             throw new MapperException("Unable to parse " + jsonValue + " to boolean");
 642  
         }
 643  
 
 644  233
         if(treatByteArrayAsBase64 && jsonValue.getValueType() == ValueType.STRING && (type == byte[].class /*|| type == Byte[].class*/)) {
 645  1
             return DatatypeConverter.parseBase64Binary(((JsonString)jsonValue).getString());
 646  
         }
 647  
 
 648  232
         if (Object.class == type) { // handling specific types here to keep exception in standard handling
 649  26
             if (jsonValue == JsonValue.TRUE) {
 650  2
                 return true;
 651  
             }
 652  24
             if (jsonValue == JsonValue.FALSE) {
 653  0
                 return false;
 654  
             }
 655  24
             if (JsonNumber.class.isInstance(jsonValue)) {
 656  10
                 final JsonNumber jsonNumber = JsonNumber.class.cast(jsonValue);
 657  10
                 if(jsonNumber.isIntegral()) {
 658  9
                     return jsonNumber.intValue();
 659  
                 }
 660  1
                 return jsonNumber.doubleValue();
 661  
             }
 662  
         }
 663  
 
 664  220
         if (type == Character.class || type == char.class) {
 665  3
             return convertTo(Class.class.cast(type), (JsonString.class.cast(jsonValue).getString()));
 666  
         }
 667  
 
 668  217
         if (JsonObject.class.isInstance(jsonValue)) {
 669  30
             return buildObject(type, JsonObject.class.cast(jsonValue));
 670  187
         } else if (JsonArray.class.isInstance(jsonValue)) {
 671  34
             return buildArray(type, JsonArray.class.cast(jsonValue));
 672  153
         } else if (JsonNumber.class.isInstance(jsonValue)) {
 673  
 
 674  107
             final JsonNumber number = JsonNumber.class.cast(jsonValue);
 675  
 
 676  107
             if (type == Integer.class || type == int.class) {
 677  53
                 return number.intValue();
 678  
             }
 679  
 
 680  54
             if (type == Long.class || type == long.class) {
 681  22
                 return number.longValue();
 682  
             }
 683  
 
 684  32
             if (type == Short.class || type == short.class) {
 685  13
                 return (short) number.intValue();
 686  
             }
 687  
 
 688  19
             if (type == Byte.class || type == byte.class) {
 689  14
                 return (byte) number.intValue();
 690  
             }
 691  
 
 692  5
             if (type == Float.class || type == float.class) {
 693  1
                 return (float) number.doubleValue();
 694  
             }
 695  
 
 696  4
             if (type == Double.class || type == double.class) {
 697  1
                 return number.doubleValue();
 698  
             }
 699  
 
 700  3
             if (type == BigInteger.class) {
 701  1
                 return number.bigIntegerValue();
 702  
             }
 703  
 
 704  2
             if (type == BigDecimal.class) {
 705  2
                 return number.bigDecimalValue();
 706  
             }
 707  0
         } else if (JsonString.class.isInstance(jsonValue) || Object.class == type) {
 708  46
             return convertTo(Class.class.cast(type), JsonString.class.cast(jsonValue).getString());
 709  
         }
 710  
 
 711  0
         throw new MapperException("Unable to parse " + jsonValue + " to " + type);
 712  
     }
 713  
 
 714  
     private Object buildArray(final Type type, final JsonArray jsonArray) throws Exception {
 715  39
         if (Class.class.isInstance(type)) {
 716  19
             final Class clazz = Class.class.cast(type);
 717  19
             if (clazz.isArray()) {
 718  14
                 final Class<?> componentType = clazz.getComponentType();
 719  14
                 return buildArrayWithComponentType(jsonArray, componentType);
 720  
             }
 721  
         }
 722  
 
 723  25
         if (ParameterizedType.class.isInstance(type)) {
 724  20
             final Mappings.CollectionMapping mapping = mappings.findCollectionMapping(ParameterizedType.class.cast(type));
 725  20
             if (mapping != null) {
 726  20
                 return mapCollection(mapping, jsonArray);
 727  
             }
 728  
         }
 729  
 
 730  5
         if (Object.class == type) {
 731  5
             return buildArray(ANY_LIST, jsonArray);
 732  
         }
 733  
 
 734  0
         throw new UnsupportedOperationException("type " + type + " not supported");
 735  
     }
 736  
 
 737  
     private <T> Collection<T> mapCollection(final Mappings.CollectionMapping mapping, final JsonArray jsonArray) throws Exception {
 738  
         final Collection collection;
 739  
 
 740  23
         if (SortedSet.class == mapping.raw) {
 741  3
             collection = new TreeSet<T>();
 742  20
         } else if (Set.class == mapping.raw) {
 743  0
             collection = new HashSet<T>(jsonArray.size());
 744  20
         } else if (Queue.class == mapping.raw) {
 745  1
             collection = new ArrayBlockingQueue<T>(jsonArray.size());
 746  19
         } else if (List.class == mapping.raw || Collection.class == mapping.raw) {
 747  19
             collection = new ArrayList<T>(jsonArray.size());
 748  
         } else {
 749  0
             throw new IllegalStateException("not supported collection type: " + mapping.raw.getName());
 750  
         }
 751  
 
 752  23
         for (final JsonValue value : jsonArray) {
 753  48
             final Object element = toObject(value, mapping.arg);
 754  48
             collection.add(element);
 755  48
         }
 756  23
         return collection;
 757  
     }
 758  
 
 759  
     private Object buildArrayWithComponentType(final JsonArray jsonArray, final Class<?> componentType) throws Exception {
 760  16
         final Object array = Array.newInstance(componentType, jsonArray.size());
 761  16
         int i = 0;
 762  16
         for (final JsonValue value : jsonArray) {
 763  42
             Array.set(array, i++, toObject(value, componentType));
 764  42
         }
 765  16
         return array;
 766  
     }
 767  
 
 768  2
     private static class FallbackConverter implements Converter<Object> {
 769  
         @Override
 770  
         public String toString(final Object instance) {
 771  0
             return instance.toString();
 772  
         }
 773  
 
 774  
         @Override
 775  
         public Object fromString(final String text) {
 776  1
             throw new UnsupportedOperationException("Using fallback converter, " +
 777  
                     "this only works in write mode but not in read. Please register a custom converter to do so.");
 778  
         }
 779  
     }
 780  
 }