Coverage Report - org.apache.johnzon.mapper.reflection.Mappings
 
Classes in this File Line Coverage Branch Coverage Complexity
Mappings
70%
68/97
57%
65/114
6,846
Mappings$ClassMapping
100%
14/14
87%
7/8
6,846
Mappings$CollectionMapping
100%
5/5
N/A
6,846
Mappings$Getter
100%
9/9
75%
3/4
6,846
Mappings$Setter
100%
7/7
N/A
6,846
 
 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.reflection;
 20  
 
 21  
 import org.apache.johnzon.mapper.Converter;
 22  
 import org.apache.johnzon.mapper.JohnzonConverter;
 23  
 import org.apache.johnzon.mapper.JohnzonIgnore;
 24  
 import org.apache.johnzon.mapper.access.AccessMode;
 25  
 
 26  
 import java.lang.reflect.Constructor;
 27  
 import java.lang.reflect.Modifier;
 28  
 import java.lang.reflect.ParameterizedType;
 29  
 import java.lang.reflect.Type;
 30  
 import java.math.BigDecimal;
 31  
 import java.math.BigInteger;
 32  
 import java.util.Collection;
 33  
 import java.util.Comparator;
 34  
 import java.util.HashMap;
 35  
 import java.util.List;
 36  
 import java.util.Map;
 37  
 import java.util.Queue;
 38  
 import java.util.Set;
 39  
 import java.util.SortedSet;
 40  
 import java.util.TreeMap;
 41  
 import java.util.concurrent.ConcurrentHashMap;
 42  
 import java.util.concurrent.ConcurrentMap;
 43  
 
 44  
 public class Mappings {
 45  
     public static class ClassMapping {
 46  
         public final Class<?> clazz;
 47  
         public final Map<String, Getter> getters;
 48  
         public final Map<String, Setter> setters;
 49  
         public final Constructor<?> constructor;
 50  
 
 51  
         protected ClassMapping(final Class<?> clazz,
 52  
                                final Map<String, Getter> getters, final Map<String, Setter> setters,
 53  42
                                final boolean acceptHiddenConstructor) {
 54  42
             this.clazz = clazz;
 55  42
             this.getters = getters;
 56  42
             this.setters = setters;
 57  42
             this.constructor = findConstructor(acceptHiddenConstructor);
 58  42
         }
 59  
 
 60  
         private Constructor<?> findConstructor(final boolean acceptHiddenConstructor) {
 61  43
             for (final Constructor<?> c : clazz.getDeclaredConstructors()) {
 62  42
                 if (c.getParameterTypes().length == 0) {
 63  41
                     if (!Modifier.isPublic(c.getModifiers()) && acceptHiddenConstructor) {
 64  7
                         c.setAccessible(true);
 65  
                     }
 66  41
                     return c;
 67  
                 }
 68  
             }
 69  
             try {
 70  1
                 return clazz.getConstructor();
 71  1
             } catch (final NoSuchMethodException e) {
 72  1
                 return null; // readOnly class
 73  
             }
 74  
         }
 75  
     }
 76  
 
 77  
     public static class CollectionMapping {
 78  
         public final Class<?> raw;
 79  
         public final Type arg;
 80  
         public final boolean primitive;
 81  
 
 82  14
         public CollectionMapping(final boolean primitive, final Class<?> collectionType, final Type fieldArgType) {
 83  14
             this.raw = collectionType;
 84  14
             this.arg = fieldArgType;
 85  14
             this.primitive = primitive;
 86  14
         }
 87  
     }
 88  
 
 89  
     public static class Getter {
 90  
         public final AccessMode.Reader reader;
 91  
         public final int version;
 92  
         public final Converter<Object> converter;
 93  
         public final boolean primitive;
 94  
         public final boolean array;
 95  
         public final boolean map;
 96  
         public final boolean collection;
 97  
 
 98  
         public Getter(final AccessMode.Reader reader,
 99  
                       final boolean primitive, final boolean array,
 100  
                       final boolean collection, final boolean map,
 101  162
                       final Converter<Object> converter, final int version) {
 102  162
             this.reader = reader;
 103  162
             this.converter = converter;
 104  162
             this.version = version;
 105  162
             this.array = array;
 106  162
             this.map = map && converter == null;
 107  162
             this.collection = collection;
 108  162
             this.primitive = primitive;
 109  162
         }
 110  
     }
 111  
 
 112  
     public static class Setter {
 113  
         public final AccessMode.Writer writer;
 114  
         public final int version;
 115  
         public final Type paramType;
 116  
         public final Converter<?> converter;
 117  
         public final boolean primitive;
 118  
 
 119  156
         public Setter(final AccessMode.Writer writer, final boolean primitive, final Type paramType, final Converter<?> converter, final int version) {
 120  156
             this.writer = writer;
 121  156
             this.paramType = paramType;
 122  156
             this.converter = converter;
 123  156
             this.version = version;
 124  156
             this.primitive = primitive;
 125  156
         }
 126  
     }
 127  
 
 128  50
     protected final ConcurrentMap<Type, ClassMapping> classes = new ConcurrentHashMap<Type, ClassMapping>();
 129  50
     protected final ConcurrentMap<Type, CollectionMapping> collections = new ConcurrentHashMap<Type, CollectionMapping>();
 130  
     protected final Comparator<String> fieldOrdering;
 131  
     private final boolean supportHiddenConstructors;
 132  
     private final AccessMode accessMode;
 133  
 
 134  50
     public Mappings(final Comparator<String> attributeOrder, final AccessMode accessMode, final boolean supportHiddenConstructors) {
 135  50
         this.fieldOrdering = attributeOrder;
 136  50
         this.accessMode = accessMode;
 137  50
         this.supportHiddenConstructors = supportHiddenConstructors;
 138  50
     }
 139  
 
 140  
     public <T> CollectionMapping findCollectionMapping(final ParameterizedType genericType) {
 141  19
         CollectionMapping collectionMapping = collections.get(genericType);
 142  19
         if (collectionMapping == null) {
 143  14
             collectionMapping = createCollectionMapping(genericType);
 144  14
             if (collectionMapping == null) {
 145  0
                 return null;
 146  
             }
 147  14
             final CollectionMapping existing = collections.putIfAbsent(genericType, collectionMapping);
 148  14
             if (existing != null) {
 149  14
                 collectionMapping = existing;
 150  
             }
 151  
         }
 152  19
         return collectionMapping;
 153  
     }
 154  
 
 155  
     private <T> CollectionMapping createCollectionMapping(final ParameterizedType aType) {
 156  14
         final Type[] fieldArgTypes = aType.getActualTypeArguments();
 157  14
         final Type raw = aType.getRawType();
 158  14
         if (fieldArgTypes.length == 1 && Class.class.isInstance(raw)) {
 159  14
             final Class<?> r = Class.class.cast(raw);
 160  
             final Class<?> collectionType;
 161  14
             if (List.class.isAssignableFrom(r)) {
 162  11
                 collectionType = List.class;
 163  3
             }else if (SortedSet.class.isAssignableFrom(r)) {
 164  2
                 collectionType = SortedSet.class;
 165  1
             } else if (Set.class.isAssignableFrom(r)) {
 166  0
                 collectionType = Set.class;
 167  1
             } else if (Queue.class.isAssignableFrom(r)) {
 168  1
                 collectionType = Queue.class;
 169  0
             } else if (Collection.class.isAssignableFrom(r)) {
 170  0
                 collectionType = Collection.class;
 171  
             } else {
 172  0
                 return null;
 173  
             }
 174  
 
 175  14
             final CollectionMapping mapping = new CollectionMapping(isPrimitive(fieldArgTypes[0]), collectionType, fieldArgTypes[0]);
 176  14
             collections.putIfAbsent(aType, mapping);
 177  14
             return mapping;
 178  
         }
 179  0
         return null;
 180  
     }
 181  
 
 182  
     // has JSon API a method for this type
 183  
     public static boolean isPrimitive(final Type type) {
 184  344
         if (type == String.class) {
 185  47
             return true;
 186  297
         } else if (type == char.class || type == Character.class) {
 187  4
             return true;
 188  293
         } else if (type == long.class || type == Long.class) {
 189  24
             return true;
 190  269
         } else if (type == int.class || type == Integer.class
 191  
                 || type == byte.class || type == Byte.class
 192  
                 || type == short.class || type == Short.class) {
 193  51
             return true;
 194  218
         } else if (type == double.class || type == Double.class
 195  
                 || type == float.class || type == Float.class) {
 196  14
             return true;
 197  204
         } else if (type == boolean.class || type == Boolean.class) {
 198  30
             return true;
 199  174
         } else if (type == BigDecimal.class) {
 200  10
             return true;
 201  164
         } else if (type == BigInteger.class) {
 202  6
             return true;
 203  
         }
 204  158
         return false;
 205  
     }
 206  
 
 207  
     public ClassMapping getClassMapping(final Type clazz) {
 208  12
         return classes.get(clazz);
 209  
     }
 210  
 
 211  
     public ClassMapping findOrCreateClassMapping(final Type clazz) {
 212  75
         ClassMapping classMapping = classes.get(clazz);
 213  75
         if (classMapping == null) {
 214  51
             if (!Class.class.isInstance(clazz) || Map.class.isAssignableFrom(Class.class.cast(clazz))) {
 215  9
                 return null;
 216  
             }
 217  
 
 218  42
             classMapping = createClassMapping(Class.class.cast(clazz));
 219  42
             final ClassMapping existing = classes.putIfAbsent(clazz, classMapping);
 220  42
             if (existing != null) {
 221  0
                 classMapping = existing;
 222  
             }
 223  
         }
 224  66
         return classMapping;
 225  
     }
 226  
 
 227  
     private ClassMapping createClassMapping(final Class<?> clazz) {
 228  42
         final Map<String, Getter> getters = fieldOrdering != null ?
 229  
             new TreeMap<String, Getter>(fieldOrdering) : new HashMap<String, Getter>();
 230  42
         final Map<String, Setter> setters = fieldOrdering != null ?
 231  
             new TreeMap<String, Setter>(fieldOrdering) : new HashMap<String, Setter>();
 232  
 
 233  42
         for (final Map.Entry<String, AccessMode.Reader> reader : accessMode.findReaders(clazz).entrySet()) {
 234  162
             final AccessMode.Reader value = reader.getValue();
 235  162
             final JohnzonIgnore readIgnore = value.getAnnotation(JohnzonIgnore.class);
 236  162
             if (readIgnore == null || readIgnore.minVersion() >= 0) {
 237  162
                 final Class<?> returnType = Class.class.isInstance(value.getType()) ? Class.class.cast(value.getType()) : null;
 238  162
                 final ParameterizedType pt = ParameterizedType.class.isInstance(value.getType()) ? ParameterizedType.class.cast(value.getType()) : null;
 239  162
                 getters.put(reader.getKey(), new Getter(value, isPrimitive(returnType),
 240  0
                         returnType != null && returnType.isArray(),
 241  0
                         (pt != null && Collection.class.isAssignableFrom(Class.class.cast(pt.getRawType())))
 242  0
                                 || (returnType != null && Collection.class.isAssignableFrom(returnType)),
 243  0
                         (pt != null && Map.class.isAssignableFrom(Class.class.cast(pt.getRawType())))
 244  0
                                 || (returnType != null && Map.class.isAssignableFrom(returnType)),
 245  0
                         findConverter(value),
 246  0
                         readIgnore != null ? readIgnore.minVersion() : -1));
 247  
             }
 248  0
         }
 249  0
         for (final Map.Entry<String, AccessMode.Writer> writer : accessMode.findWriters(clazz).entrySet()) {
 250  0
             final AccessMode.Writer value = writer.getValue();
 251  0
             final JohnzonIgnore writeIgnore = value.getAnnotation(JohnzonIgnore.class);
 252  0
             if (writeIgnore == null || writeIgnore.minVersion() >= 0) {
 253  0
                 final String key = writer.getKey();
 254  0
                 if (key.equals("metaClass")) {
 255  0
                     continue;
 256  
                 }
 257  0
                 final Type param = value.getType();
 258  0
                 setters.put(key, new Setter(value, isPrimitive(param), param, findConverter(value), writeIgnore != null ? writeIgnore.minVersion() : -1));
 259  
             }
 260  42
         }
 261  0
         return new ClassMapping(clazz, getters, setters, supportHiddenConstructors);
 262  
     }
 263  
 
 264  
     private static Converter findConverter(final AccessMode.DecoratedType method) {
 265  318
         Converter converter = null;
 266  0
         if (method.getAnnotation(JohnzonConverter.class) != null) {
 267  
             try {
 268  4
                 converter = method.getAnnotation(JohnzonConverter.class).value().newInstance();
 269  0
             } catch (final Exception e) {
 270  318
                 throw new IllegalArgumentException(e);
 271  0
             }
 272  
         }
 273  0
         return converter;
 274  
     }
 275  
 }