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