1 package com.fasterxml.jackson.databind.deser.impl; 2 3 import java.io.IOException; 4 import java.util.*; 5 6 import com.fasterxml.jackson.core.JsonParser; 7 8 import com.fasterxml.jackson.databind.*; 9 import com.fasterxml.jackson.databind.deser.SettableBeanProperty; 10 import com.fasterxml.jackson.databind.deser.ValueInstantiator; 11 12 /** 13 * Object that is used to collect arguments for non-default creator 14 * (non-default-constructor, or argument-taking factory method) 15 * before creator can be called. 16 * Since ordering of JSON properties is not guaranteed, this may 17 * require buffering of values other than ones being passed to 18 * creator. 19 */ 20 public final class PropertyBasedCreator 21 { 22 /** 23 * Number of properties: usually same as size of {@link #_propertyLookup}, 24 * but not necessarily, when we have unnamed injectable properties. 25 */ 26 protected final int _propertyCount; 27 28 /** 29 * Helper object that knows how to actually construct the instance by 30 * invoking creator method with buffered arguments. 31 */ 32 protected final ValueInstantiator _valueInstantiator; 33 34 /** 35 * Map that contains property objects for either constructor or factory 36 * method (whichever one is null: one property for each 37 * parameter for that one), keyed by logical property name 38 */ 39 protected final HashMap<String, SettableBeanProperty> _propertyLookup; 40 41 /** 42 * Array that contains properties that expect value to inject, if any; 43 * null if no injectable values are expected. 44 */ 45 protected final SettableBeanProperty[] _allProperties; 46 47 /* 48 /********************************************************** 49 /* Construction, initialization 50 /********************************************************** 51 */ 52 PropertyBasedCreator(DeserializationContext ctxt, ValueInstantiator valueInstantiator, SettableBeanProperty[] creatorProps, boolean caseInsensitive, boolean addAliases)53 protected PropertyBasedCreator(DeserializationContext ctxt, 54 ValueInstantiator valueInstantiator, 55 SettableBeanProperty[] creatorProps, 56 boolean caseInsensitive, 57 boolean addAliases) 58 { 59 _valueInstantiator = valueInstantiator; 60 if (caseInsensitive) { 61 _propertyLookup = CaseInsensitiveMap.construct(ctxt.getConfig().getLocale()); 62 } else { 63 _propertyLookup = new HashMap<String, SettableBeanProperty>(); 64 } 65 final int len = creatorProps.length; 66 _propertyCount = len; 67 _allProperties = new SettableBeanProperty[len]; 68 69 // 26-Feb-2017, tatu: Let's start by aliases, so that there is no 70 // possibility of accidental override of primary names 71 if (addAliases) { 72 final DeserializationConfig config = ctxt.getConfig(); 73 for (SettableBeanProperty prop : creatorProps) { 74 // 22-Jan-2018, tatu: ignorable entries should be ignored, even if got aliases 75 if (!prop.isIgnorable()) { 76 List<PropertyName> aliases = prop.findAliases(config); 77 if (!aliases.isEmpty()) { 78 for (PropertyName pn : aliases) { 79 _propertyLookup.put(pn.getSimpleName(), prop); 80 } 81 } 82 } 83 } 84 } 85 for (int i = 0; i < len; ++i) { 86 SettableBeanProperty prop = creatorProps[i]; 87 _allProperties[i] = prop; 88 // 22-Jan-2018, tatu: ignorable entries should be skipped 89 if (!prop.isIgnorable()) { 90 _propertyLookup.put(prop.getName(), prop); 91 } 92 } 93 } 94 95 /** 96 * Factory method used for building actual instances to be used with POJOS: 97 * resolves deserializers, checks for "null values". 98 * 99 * @since 2.9 100 */ construct(DeserializationContext ctxt, ValueInstantiator valueInstantiator, SettableBeanProperty[] srcCreatorProps, BeanPropertyMap allProperties)101 public static PropertyBasedCreator construct(DeserializationContext ctxt, 102 ValueInstantiator valueInstantiator, SettableBeanProperty[] srcCreatorProps, 103 BeanPropertyMap allProperties) 104 throws JsonMappingException 105 { 106 final int len = srcCreatorProps.length; 107 SettableBeanProperty[] creatorProps = new SettableBeanProperty[len]; 108 for (int i = 0; i < len; ++i) { 109 SettableBeanProperty prop = srcCreatorProps[i]; 110 if (!prop.hasValueDeserializer()) { 111 // 15-Apr-2020, tatu: [databind#962] Avoid getting deserializer for Inject-only 112 // cases 113 if (!prop.isInjectionOnly()) { 114 prop = prop.withValueDeserializer(ctxt.findContextualValueDeserializer(prop.getType(), prop)); 115 } 116 } 117 creatorProps[i] = prop; 118 } 119 return new PropertyBasedCreator(ctxt, valueInstantiator, creatorProps, 120 allProperties.isCaseInsensitive(), 121 // 05-Sep-2019, tatu: As per [databind#2378] looks like not all aliases get merged into 122 // `allProperties` so force lookup anyway. 123 // allProperties.hasAliases() 124 true); 125 } 126 127 /** 128 * Factory method used for building actual instances to be used with types 129 * OTHER than POJOs. 130 * resolves deserializers and checks for "null values". 131 * 132 * @since 2.9 133 */ construct(DeserializationContext ctxt, ValueInstantiator valueInstantiator, SettableBeanProperty[] srcCreatorProps, boolean caseInsensitive)134 public static PropertyBasedCreator construct(DeserializationContext ctxt, 135 ValueInstantiator valueInstantiator, SettableBeanProperty[] srcCreatorProps, 136 boolean caseInsensitive) 137 throws JsonMappingException 138 { 139 final int len = srcCreatorProps.length; 140 SettableBeanProperty[] creatorProps = new SettableBeanProperty[len]; 141 for (int i = 0; i < len; ++i) { 142 SettableBeanProperty prop = srcCreatorProps[i]; 143 if (!prop.hasValueDeserializer()) { 144 prop = prop.withValueDeserializer(ctxt.findContextualValueDeserializer(prop.getType(), prop)); 145 } 146 creatorProps[i] = prop; 147 } 148 return new PropertyBasedCreator(ctxt, valueInstantiator, creatorProps, 149 caseInsensitive, false); 150 } 151 152 @Deprecated // since 2.9 construct(DeserializationContext ctxt, ValueInstantiator valueInstantiator, SettableBeanProperty[] srcCreatorProps)153 public static PropertyBasedCreator construct(DeserializationContext ctxt, 154 ValueInstantiator valueInstantiator, SettableBeanProperty[] srcCreatorProps) 155 throws JsonMappingException 156 { 157 return construct(ctxt, valueInstantiator, srcCreatorProps, 158 ctxt.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES)); 159 } 160 161 /* 162 /********************************************************** 163 /* Accessors 164 /********************************************************** 165 */ 166 properties()167 public Collection<SettableBeanProperty> properties() { 168 return _propertyLookup.values(); 169 } 170 findCreatorProperty(String name)171 public SettableBeanProperty findCreatorProperty(String name) { 172 return _propertyLookup.get(name); 173 } 174 findCreatorProperty(int propertyIndex)175 public SettableBeanProperty findCreatorProperty(int propertyIndex) { 176 for (SettableBeanProperty prop : _propertyLookup.values()) { 177 if (prop.getPropertyIndex() == propertyIndex) { 178 return prop; 179 } 180 } 181 return null; 182 } 183 184 /* 185 /********************************************************** 186 /* Building process 187 /********************************************************** 188 */ 189 190 /** 191 * Method called when starting to build a bean instance. 192 * 193 * @since 2.1 (added ObjectIdReader parameter -- existed in previous versions without) 194 */ startBuilding(JsonParser p, DeserializationContext ctxt, ObjectIdReader oir)195 public PropertyValueBuffer startBuilding(JsonParser p, DeserializationContext ctxt, 196 ObjectIdReader oir) { 197 return new PropertyValueBuffer(p, ctxt, _propertyCount, oir); 198 } 199 build(DeserializationContext ctxt, PropertyValueBuffer buffer)200 public Object build(DeserializationContext ctxt, PropertyValueBuffer buffer) throws IOException 201 { 202 Object bean = _valueInstantiator.createFromObjectWith(ctxt, 203 _allProperties, buffer); 204 // returning null isn't quite legal, but let's let caller deal with that 205 if (bean != null) { 206 // Object Id to handle? 207 bean = buffer.handleIdValue(ctxt, bean); 208 209 // Anything buffered? 210 for (PropertyValue pv = buffer.buffered(); pv != null; pv = pv.next) { 211 pv.assign(bean); 212 } 213 } 214 return bean; 215 } 216 217 /* 218 /********************************************************** 219 /* Helper classes 220 /********************************************************** 221 */ 222 223 /** 224 * Simple override of standard {@link java.util.HashMap} to support 225 * case-insensitive access to creator properties 226 * 227 * @since 2.8.5 228 */ 229 static class CaseInsensitiveMap extends HashMap<String, SettableBeanProperty> 230 { 231 private static final long serialVersionUID = 1L; 232 233 /** 234 * Lower-casing can have Locale-specific minor variations. 235 * 236 * @since 2.11 237 */ 238 protected final Locale _locale; 239 240 @Deprecated // since 2.11 CaseInsensitiveMap()241 public CaseInsensitiveMap() { 242 this(Locale.getDefault()); 243 } 244 245 // @since 2.11 CaseInsensitiveMap(Locale l)246 public CaseInsensitiveMap(Locale l) { 247 _locale = l; 248 } 249 250 // @since 2.11 construct(Locale l)251 public static CaseInsensitiveMap construct(Locale l) { 252 return new CaseInsensitiveMap(l); 253 } 254 255 @Override get(Object key0)256 public SettableBeanProperty get(Object key0) { 257 return super.get(((String) key0).toLowerCase(_locale)); 258 } 259 260 @Override put(String key, SettableBeanProperty value)261 public SettableBeanProperty put(String key, SettableBeanProperty value) { 262 key = key.toLowerCase(_locale); 263 return super.put(key, value); 264 } 265 } 266 } 267