• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.fasterxml.jackson.databind.deser.impl;
2 
3 import java.io.IOException;
4 import java.lang.reflect.InvocationTargetException;
5 import java.util.*;
6 
7 import com.fasterxml.jackson.core.JsonParser;
8 import com.fasterxml.jackson.core.JsonProcessingException;
9 
10 import com.fasterxml.jackson.databind.DeserializationContext;
11 import com.fasterxml.jackson.databind.DeserializationFeature;
12 import com.fasterxml.jackson.databind.JsonDeserializer;
13 import com.fasterxml.jackson.databind.JsonMappingException;
14 import com.fasterxml.jackson.databind.MapperFeature;
15 import com.fasterxml.jackson.databind.PropertyName;
16 import com.fasterxml.jackson.databind.cfg.MapperConfig;
17 import com.fasterxml.jackson.databind.deser.SettableBeanProperty;
18 import com.fasterxml.jackson.databind.util.ClassUtil;
19 import com.fasterxml.jackson.databind.util.IgnorePropertiesUtil;
20 import com.fasterxml.jackson.databind.util.NameTransformer;
21 
22 /**
23  * Helper class used for storing mapping from property name to
24  * {@link SettableBeanProperty} instances.
25  *<p>
26  * Note that this class is used instead of generic {@link java.util.HashMap}
27  * for bit of performance gain (and some memory savings): although default
28  * implementation is very good for generic use cases, it can be streamlined
29  * a bit for specific use case we have. Even relatively small improvements
30  * matter since this is directly on the critical path during deserialization,
31  * as it is done for each and every POJO property deserialized.
32  */
33 public class BeanPropertyMap
34     implements Iterable<SettableBeanProperty>,
35         java.io.Serializable
36 {
37     private static final long serialVersionUID = 2L;
38 
39     /**
40      * @since 2.5
41      */
42     protected final boolean _caseInsensitive;
43 
44     private int _hashMask;
45 
46     /**
47      * Number of entries stored in the hash area.
48      */
49     private int _size;
50 
51     private int _spillCount;
52 
53     /**
54      * Hash area that contains key/property pairs in adjacent elements.
55      */
56     private Object[] _hashArea;
57 
58     /**
59      * Array of properties in the exact order they were handed in. This is
60      * used by as-array serialization, deserialization.
61      */
62     private final SettableBeanProperty[] _propsInOrder;
63 
64     /**
65      * Configuration of alias mappings, indexed by unmodified property name
66      * to unmodified aliases, if any; entries only included for properties
67      * that do have aliases.
68      * This is is used for constructing actual reverse lookup mapping, if
69      * needed, taking into account possible case-insensitivity, as well
70      * as possibility of name prefixes.
71      *
72      * @since 2.9
73      */
74     private final Map<String,List<PropertyName>> _aliasDefs;
75 
76     /**
77      * Mapping from secondary names (aliases) to primary names.
78      *
79      * @since 2.9
80      */
81     private final Map<String,String> _aliasMapping;
82 
83     /**
84      * We require {@link Locale} since case changes are locale-sensitive in certain
85      * cases (see <a href="https://en.wikipedia.org/wiki/Dotted_and_dotless_I">Turkish I</a>
86      * for example)
87      *
88      * @since 2.11
89      */
90     private final Locale _locale;
91 
92     /**
93      * @since 2.11
94      */
BeanPropertyMap(boolean caseInsensitive, Collection<SettableBeanProperty> props, Map<String,List<PropertyName>> aliasDefs, Locale locale)95     public BeanPropertyMap(boolean caseInsensitive, Collection<SettableBeanProperty> props,
96             Map<String,List<PropertyName>> aliasDefs,
97             Locale locale)
98     {
99         _caseInsensitive = caseInsensitive;
100         _propsInOrder = props.toArray(new SettableBeanProperty[props.size()]);
101         _aliasDefs = aliasDefs;
102         _locale = locale;
103         _aliasMapping = _buildAliasMapping(aliasDefs, caseInsensitive, locale);
104         init(props);
105 
106     }
107 
108     /**
109      * @deprecated since 2.11
110      */
111     @Deprecated
BeanPropertyMap(boolean caseInsensitive, Collection<SettableBeanProperty> props, Map<String,List<PropertyName>> aliasDefs)112     public BeanPropertyMap(boolean caseInsensitive, Collection<SettableBeanProperty> props,
113             Map<String,List<PropertyName>> aliasDefs) {
114         this(caseInsensitive, props, aliasDefs, Locale.getDefault());
115     }
116 
117     /* Copy constructors used when a property can replace existing one
118      *
119      * @since 2.9.6
120      */
BeanPropertyMap(BeanPropertyMap src, SettableBeanProperty newProp, int hashIndex, int orderedIndex)121     private BeanPropertyMap(BeanPropertyMap src,
122             SettableBeanProperty newProp, int hashIndex, int orderedIndex)
123     {
124         // First, copy most fields as is:
125         _caseInsensitive = src._caseInsensitive;
126         _locale = src._locale;
127         _hashMask = src._hashMask;
128         _size = src._size;
129         _spillCount = src._spillCount;
130         _aliasDefs = src._aliasDefs;
131         _aliasMapping = src._aliasMapping;
132 
133         // but then make deep copy of arrays to modify
134         _hashArea = Arrays.copyOf(src._hashArea, src._hashArea.length);
135         _propsInOrder = Arrays.copyOf(src._propsInOrder, src._propsInOrder.length);
136         _hashArea[hashIndex] = newProp;
137         _propsInOrder[orderedIndex] = newProp;
138     }
139 
140     /* Copy constructors used when a property needs to be appended (can't replace)
141      *
142      * @since 2.9.6
143      */
BeanPropertyMap(BeanPropertyMap src, SettableBeanProperty newProp, String key, int slot)144     private BeanPropertyMap(BeanPropertyMap src,
145             SettableBeanProperty newProp, String key, int slot)
146     {
147         // First, copy most fields as is:
148         _caseInsensitive = src._caseInsensitive;
149         _locale = src._locale;
150         _hashMask = src._hashMask;
151         _size = src._size;
152         _spillCount = src._spillCount;
153         _aliasDefs = src._aliasDefs;
154         _aliasMapping = src._aliasMapping;
155 
156         // but then make deep copy of arrays to modify
157         _hashArea = Arrays.copyOf(src._hashArea, src._hashArea.length);
158         int last = src._propsInOrder.length;
159         // and append property at the end of ordering
160         _propsInOrder = Arrays.copyOf(src._propsInOrder, last+1);
161         _propsInOrder[last] = newProp;
162 
163         final int hashSize = _hashMask+1;
164         int ix = (slot<<1);
165 
166         // primary slot not free?
167         if (_hashArea[ix] != null) {
168             // secondary?
169             ix = (hashSize + (slot >> 1)) << 1;
170             if (_hashArea[ix] != null) {
171                 // ok, spill over.
172                 ix = ((hashSize + (hashSize >> 1) ) << 1) + _spillCount;
173                 _spillCount += 2;
174                 if (ix >= _hashArea.length) {
175                     _hashArea = Arrays.copyOf(_hashArea, _hashArea.length + 4);
176                 }
177             }
178         }
179         _hashArea[ix] = key;
180         _hashArea[ix+1] = newProp;
181     }
182 
183     /**
184      * @since 2.8
185      */
BeanPropertyMap(BeanPropertyMap base, boolean caseInsensitive)186     protected BeanPropertyMap(BeanPropertyMap base, boolean caseInsensitive)
187     {
188         _caseInsensitive = caseInsensitive;
189         _locale = base._locale;
190         _aliasDefs = base._aliasDefs;
191         _aliasMapping = base._aliasMapping;
192 
193         // 16-May-2016, tatu: Alas, not enough to just change flag, need to re-init as well.
194         _propsInOrder = Arrays.copyOf(base._propsInOrder, base._propsInOrder.length);
195         init(Arrays.asList(_propsInOrder));
196     }
197 
198     /**
199      * Mutant factory method that constructs a new instance if desired case-insensitivity
200      * state differs from the state of this instance; if states are the same, returns
201      * <code>this</code>.
202      *
203      * @since 2.8
204      */
withCaseInsensitivity(boolean state)205     public BeanPropertyMap withCaseInsensitivity(boolean state) {
206         if (_caseInsensitive == state) {
207             return this;
208         }
209         return new BeanPropertyMap(this, state);
210     }
211 
init(Collection<SettableBeanProperty> props)212     protected void init(Collection<SettableBeanProperty> props)
213     {
214         _size = props.size();
215 
216         // First: calculate size of primary hash area
217         final int hashSize = findSize(_size);
218         _hashMask = hashSize-1;
219 
220         // and allocate enough to contain primary/secondary, expand for spillovers as need be
221         int alloc = (hashSize + (hashSize>>1)) * 2;
222         Object[] hashed = new Object[alloc];
223         int spillCount = 0;
224 
225         for (SettableBeanProperty prop : props) {
226             // Due to removal, renaming, theoretically possible we'll have "holes" so:
227             if (prop == null) {
228                 continue;
229             }
230 
231             String key = getPropertyName(prop);
232             int slot = _hashCode(key);
233             int ix = (slot<<1);
234 
235             // primary slot not free?
236             if (hashed[ix] != null) {
237                 // secondary?
238                 ix = (hashSize + (slot >> 1)) << 1;
239                 if (hashed[ix] != null) {
240                     // ok, spill over.
241                     ix = ((hashSize + (hashSize >> 1) ) << 1) + spillCount;
242                     spillCount += 2;
243                     if (ix >= hashed.length) {
244                         hashed = Arrays.copyOf(hashed, hashed.length + 4);
245                     }
246                 }
247             }
248             hashed[ix] = key;
249             hashed[ix+1] = prop;
250 
251             // and aliases
252         }
253         _hashArea = hashed;
254         _spillCount = spillCount;
255     }
256 
findSize(int size)257     private final static int findSize(int size)
258     {
259         if (size <= 5) {
260             return 8;
261         }
262         if (size <= 12) {
263             return 16;
264         }
265         int needed = size + (size >> 2); // at most 80% full
266         int result = 32;
267         while (result < needed) {
268             result += result;
269         }
270         return result;
271     }
272 
273     /**
274      * @since 2.12
275      */
construct(MapperConfig<?> config, Collection<SettableBeanProperty> props, Map<String,List<PropertyName>> aliasMapping, boolean caseInsensitive)276     public static BeanPropertyMap construct(MapperConfig<?> config,
277             Collection<SettableBeanProperty> props,
278             Map<String,List<PropertyName>> aliasMapping,
279             boolean caseInsensitive) {
280         return new BeanPropertyMap(caseInsensitive,
281                 props, aliasMapping,
282                 config.getLocale());
283     }
284 
285     /**
286      * @since 2.11
287      * @deprecated since 2.12
288      */
289     @Deprecated
construct(MapperConfig<?> config, Collection<SettableBeanProperty> props, Map<String,List<PropertyName>> aliasMapping)290     public static BeanPropertyMap construct(MapperConfig<?> config,
291             Collection<SettableBeanProperty> props,
292             Map<String,List<PropertyName>> aliasMapping) {
293         return new BeanPropertyMap(config.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES),
294                 props, aliasMapping,
295                 config.getLocale());
296     }
297 
298     /**
299      * @deprecated since 2.11
300      */
301     @Deprecated
construct(Collection<SettableBeanProperty> props, boolean caseInsensitive, Map<String,List<PropertyName>> aliasMapping)302     public static BeanPropertyMap construct(Collection<SettableBeanProperty> props,
303             boolean caseInsensitive, Map<String,List<PropertyName>> aliasMapping) {
304         return new BeanPropertyMap(caseInsensitive, props, aliasMapping);
305     }
306 
307     /**
308      * Fluent copy method that creates a new instance that is a copy
309      * of this instance except for one additional property that is
310      * passed as the argument.
311      * Note that method does not modify this instance but constructs
312      * and returns a new one.
313      */
withProperty(SettableBeanProperty newProp)314     public BeanPropertyMap withProperty(SettableBeanProperty newProp)
315     {
316         // First: may be able to just replace?
317         String key = getPropertyName(newProp);
318 
319         for (int i = 1, end = _hashArea.length; i < end; i += 2) {
320             SettableBeanProperty prop = (SettableBeanProperty) _hashArea[i];
321             if ((prop != null) && prop.getName().equals(key)) {
322                 return new BeanPropertyMap(this, newProp, i, _findFromOrdered(prop));
323             }
324         }
325         // If not, append
326         final int slot = _hashCode(key);
327 
328         return new BeanPropertyMap(this, newProp, key, slot);
329     }
330 
assignIndexes()331     public BeanPropertyMap assignIndexes()
332     {
333         // order is arbitrary, but stable:
334         int index = 0;
335         for (int i = 1, end = _hashArea.length; i < end; i += 2) {
336             SettableBeanProperty prop = (SettableBeanProperty) _hashArea[i];
337             if (prop != null) {
338                 prop.assignIndex(index++);
339             }
340         }
341         return this;
342     }
343 
344     /**
345      * Mutant factory method for constructing a map where all entries use given
346      * prefix
347      */
renameAll(NameTransformer transformer)348     public BeanPropertyMap renameAll(NameTransformer transformer)
349     {
350         if (transformer == null || (transformer == NameTransformer.NOP)) {
351             return this;
352         }
353         // Try to retain insertion ordering as well
354         final int len = _propsInOrder.length;
355         ArrayList<SettableBeanProperty> newProps = new ArrayList<SettableBeanProperty>(len);
356 
357         for (int i = 0; i < len; ++i) {
358             SettableBeanProperty prop = _propsInOrder[i];
359 
360             // What to do with holes? For now, retain
361             if (prop == null) {
362                 newProps.add(prop);
363                 continue;
364             }
365             newProps.add(_rename(prop, transformer));
366         }
367         // should we try to re-index? Ordering probably changed but caller probably doesn't want changes...
368         // 26-Feb-2017, tatu: Probably SHOULD handle renaming wrt Aliases?
369         return new BeanPropertyMap(_caseInsensitive, newProps, _aliasDefs, _locale);
370     }
371 
372     /*
373     /**********************************************************
374     /* Public API, mutators
375     /**********************************************************
376      */
377 
378     /**
379      * Mutant factory method that will use this instance as the base, and
380      * construct an instance that is otherwise same except for excluding
381      * properties with specified names.
382      *
383      * @since 2.8
384      */
withoutProperties(Collection<String> toExclude)385     public BeanPropertyMap withoutProperties(Collection<String> toExclude)
386     {
387         return withoutProperties(toExclude, null);
388     }
389 
390     /**
391      * Mutant factory method that will use this instance as the base, and
392      * construct an instance that is otherwise same except for excluding
393      * properties with specified names, or including only the one marked
394      * as included
395      *
396      * @since 2.12
397      */
withoutProperties(Collection<String> toExclude, Collection<String> toInclude)398     public BeanPropertyMap withoutProperties(Collection<String> toExclude, Collection<String> toInclude)
399     {
400         if ((toExclude == null || toExclude.isEmpty()) && toInclude == null) {
401             return this;
402         }
403         final int len = _propsInOrder.length;
404         ArrayList<SettableBeanProperty> newProps = new ArrayList<SettableBeanProperty>(len);
405 
406         for (int i = 0; i < len; ++i) {
407             SettableBeanProperty prop = _propsInOrder[i];
408             // 01-May-2015, tatu: Not 100% sure if existing `null`s should be retained;
409             //   or, if entries to ignore should be retained as nulls. For now just
410             //   prune them out
411             if (prop != null) { // may contain holes, too, check.
412                 if (!IgnorePropertiesUtil.shouldIgnore(prop.getName(), toExclude, toInclude)) {
413                     newProps.add(prop);
414                 }
415             }
416         }
417         // should we try to re-index? Apparently no need
418         return new BeanPropertyMap(_caseInsensitive, newProps, _aliasDefs, _locale);
419     }
420 
421     /**
422      * Specialized method that can be used to replace an existing entry
423      * (note: entry MUST exist; otherwise exception is thrown) with
424      * specified replacement.
425      *
426      * @since 2.9.4
427      */
replace(SettableBeanProperty origProp, SettableBeanProperty newProp)428     public void replace(SettableBeanProperty origProp, SettableBeanProperty newProp)
429     {
430         int i = 1;
431         int end = _hashArea.length;
432 
433         for (;; i += 2) {
434             if (i > end) {
435                 throw new NoSuchElementException("No entry '"+origProp.getName()+"' found, can't replace");
436             }
437             if (_hashArea[i] == origProp) {
438                 _hashArea[i] = newProp;
439                 break;
440             }
441         }
442         _propsInOrder[_findFromOrdered(origProp)] = newProp;
443     }
444 
445     /**
446      * Specialized method for removing specified existing entry.
447      * NOTE: entry MUST exist, otherwise an exception is thrown.
448      */
remove(SettableBeanProperty propToRm)449     public void remove(SettableBeanProperty propToRm)
450     {
451         ArrayList<SettableBeanProperty> props = new ArrayList<SettableBeanProperty>(_size);
452         String key = getPropertyName(propToRm);
453         boolean found = false;
454 
455         for (int i = 1, end = _hashArea.length; i < end; i += 2) {
456             SettableBeanProperty prop = (SettableBeanProperty) _hashArea[i];
457             if (prop == null) {
458                 continue;
459             }
460             if (!found) {
461                 // 09-Jan-2017, tatu: Important: must check name slot and NOT property name,
462                 //   as only former is lower-case in case-insensitive case
463                 found = key.equals(_hashArea[i-1]);
464                 if (found) {
465                     // need to leave a hole here
466                     _propsInOrder[_findFromOrdered(prop)] = null;
467                     continue;
468                 }
469             }
470             props.add(prop);
471         }
472         if (!found) {
473             throw new NoSuchElementException("No entry '"+propToRm.getName()+"' found, can't remove");
474         }
475         init(props);
476     }
477 
478     /*
479     /**********************************************************
480     /* Public API, simple accessors
481     /**********************************************************
482      */
483 
size()484     public int size() { return _size; }
485 
486     /**
487      * @since 2.9
488      */
isCaseInsensitive()489     public boolean isCaseInsensitive() {
490         return _caseInsensitive;
491     }
492 
493     /**
494      * @since 2.9
495      */
hasAliases()496     public boolean hasAliases() {
497         return !_aliasDefs.isEmpty();
498     }
499 
500     /**
501      * Accessor for traversing over all contained properties.
502      */
503     @Override
iterator()504     public Iterator<SettableBeanProperty> iterator() {
505         return _properties().iterator();
506     }
507 
_properties()508     private List<SettableBeanProperty> _properties() {
509         ArrayList<SettableBeanProperty> p = new ArrayList<SettableBeanProperty>(_size);
510         for (int i = 1, end = _hashArea.length; i < end; i += 2) {
511             SettableBeanProperty prop = (SettableBeanProperty) _hashArea[i];
512             if (prop != null) {
513                 p.add(prop);
514             }
515         }
516         return p;
517     }
518 
519     /**
520      * Method that will re-create initial insertion-ordering of
521      * properties contained in this map. Note that if properties
522      * have been removed, array may contain nulls; otherwise
523      * it should be consecutive.
524      *
525      * @since 2.1
526      */
getPropertiesInInsertionOrder()527     public SettableBeanProperty[] getPropertiesInInsertionOrder() {
528         return _propsInOrder;
529     }
530 
531     // Confining this case insensitivity to this function (and the find method) in case we want to
532     // apply a particular locale to the lower case function.  For now, using the default.
getPropertyName(SettableBeanProperty prop)533     protected final String getPropertyName(SettableBeanProperty prop) {
534         return _caseInsensitive ? prop.getName().toLowerCase(_locale) : prop.getName();
535     }
536 
537     /*
538     /**********************************************************
539     /* Public API, property lookup
540     /**********************************************************
541      */
542 
543     /**
544      * @since 2.3
545      */
find(int index)546     public SettableBeanProperty find(int index)
547     {
548         // note: will scan the whole area, including primary, secondary and
549         // possible spill-area
550         for (int i = 1, end = _hashArea.length; i < end; i += 2) {
551             SettableBeanProperty prop = (SettableBeanProperty) _hashArea[i];
552             if ((prop != null) && (index == prop.getPropertyIndex())) {
553                 return prop;
554             }
555         }
556         return null;
557     }
558 
find(String key)559     public SettableBeanProperty find(String key)
560     {
561         if (key == null) {
562             throw new IllegalArgumentException("Cannot pass null property name");
563         }
564         if (_caseInsensitive) {
565             key = key.toLowerCase(_locale);
566         }
567 
568         // inlined `_hashCode(key)`
569         int slot = key.hashCode() & _hashMask;
570 //        int h = key.hashCode();
571 //        int slot = (h + (h >> 13)) & _hashMask;
572 
573         int ix = (slot<<1);
574         Object match = _hashArea[ix];
575         if ((match == key) || key.equals(match)) {
576             return (SettableBeanProperty) _hashArea[ix+1];
577         }
578         return _find2(key, slot, match);
579     }
580 
_find2(String key, int slot, Object match)581     private final SettableBeanProperty _find2(String key, int slot, Object match)
582     {
583         if (match == null) {
584             // 26-Feb-2017, tatu: Need to consider aliases
585             return _findWithAlias(_aliasMapping.get(key));
586         }
587         // no? secondary?
588         int hashSize = _hashMask+1;
589         int ix = hashSize + (slot>>1) << 1;
590         match = _hashArea[ix];
591         if (key.equals(match)) {
592             return (SettableBeanProperty) _hashArea[ix+1];
593         }
594         if (match != null) { // _findFromSpill(...)
595             int i = (hashSize + (hashSize>>1)) << 1;
596             for (int end = i + _spillCount; i < end; i += 2) {
597                 match = _hashArea[i];
598                 if ((match == key) || key.equals(match)) {
599                     return (SettableBeanProperty) _hashArea[i+1];
600                 }
601             }
602         }
603         // 26-Feb-2017, tatu: Need to consider aliases
604         return _findWithAlias(_aliasMapping.get(key));
605     }
606 
_findWithAlias(String keyFromAlias)607     private SettableBeanProperty _findWithAlias(String keyFromAlias)
608     {
609         if (keyFromAlias == null) {
610             return null;
611         }
612         // NOTE: need to inline much of handling do avoid cyclic calls via alias
613         // first, inlined main `find(String)`
614         int slot = _hashCode(keyFromAlias);
615         int ix = (slot<<1);
616         Object match = _hashArea[ix];
617         if (keyFromAlias.equals(match)) {
618             return (SettableBeanProperty) _hashArea[ix+1];
619         }
620         if (match == null) {
621             return null;
622         }
623         return _find2ViaAlias(keyFromAlias, slot, match);
624     }
625 
_find2ViaAlias(String key, int slot, Object match)626     private SettableBeanProperty _find2ViaAlias(String key, int slot, Object match)
627     {
628         // no? secondary?
629         int hashSize = _hashMask+1;
630         int ix = hashSize + (slot>>1) << 1;
631         match = _hashArea[ix];
632         if (key.equals(match)) {
633             return (SettableBeanProperty) _hashArea[ix+1];
634         }
635         if (match != null) { // _findFromSpill(...)
636             int i = (hashSize + (hashSize>>1)) << 1;
637             for (int end = i + _spillCount; i < end; i += 2) {
638                 match = _hashArea[i];
639                 if ((match == key) || key.equals(match)) {
640                     return (SettableBeanProperty) _hashArea[i+1];
641                 }
642             }
643         }
644         return null;
645     }
646 
647     /*
648     /**********************************************************
649     /* Public API, deserialization support
650     /**********************************************************
651      */
652 
653     /**
654      * Convenience method that tries to find property with given name, and
655      * if it is found, call {@link SettableBeanProperty#deserializeAndSet}
656      * on it, and return true; or, if not found, return false.
657      * Note, too, that if deserialization is attempted, possible exceptions
658      * are wrapped if and as necessary, so caller need not handle those.
659      *
660      * @since 2.5
661      */
findDeserializeAndSet(JsonParser p, DeserializationContext ctxt, Object bean, String key)662     public boolean findDeserializeAndSet(JsonParser p, DeserializationContext ctxt,
663             Object bean, String key) throws IOException
664     {
665         final SettableBeanProperty prop = find(key);
666         if (prop == null) {
667             return false;
668         }
669         try {
670             prop.deserializeAndSet(p, ctxt, bean);
671         } catch (Exception e) {
672             wrapAndThrow(e, bean, key, ctxt);
673         }
674         return true;
675     }
676 
677     /*
678     /**********************************************************
679     /* Std method overrides
680     /**********************************************************
681      */
682 
683     @Override
toString()684     public String toString()
685     {
686         StringBuilder sb = new StringBuilder();
687         sb.append("Properties=[");
688         int count = 0;
689 
690         Iterator<SettableBeanProperty> it = iterator();
691         while (it.hasNext()) {
692             SettableBeanProperty prop = it.next();
693             if (count++ > 0) {
694                 sb.append(", ");
695             }
696             sb.append(prop.getName());
697             sb.append('(');
698             sb.append(prop.getType());
699             sb.append(')');
700         }
701         sb.append(']');
702         if (!_aliasDefs.isEmpty()) {
703             sb.append("(aliases: ");
704             sb.append(_aliasDefs);
705             sb.append(")");
706         }
707         return sb.toString();
708     }
709 
710     /*
711     /**********************************************************
712     /* Helper methods
713     /**********************************************************
714      */
715 
_rename(SettableBeanProperty prop, NameTransformer xf)716     protected SettableBeanProperty _rename(SettableBeanProperty prop, NameTransformer xf)
717     {
718         if (prop == null) {
719             return prop;
720         }
721         String newName = xf.transform(prop.getName());
722         prop = prop.withSimpleName(newName);
723         JsonDeserializer<?> deser = prop.getValueDeserializer();
724         if (deser != null) {
725             @SuppressWarnings("unchecked")
726             JsonDeserializer<Object> newDeser = (JsonDeserializer<Object>)
727                 deser.unwrappingDeserializer(xf);
728             if (newDeser != deser) {
729                 prop = prop.withValueDeserializer(newDeser);
730             }
731         }
732         return prop;
733     }
734 
wrapAndThrow(Throwable t, Object bean, String fieldName, DeserializationContext ctxt)735     protected void wrapAndThrow(Throwable t, Object bean, String fieldName, DeserializationContext ctxt)
736         throws IOException
737     {
738         // inlined 'throwOrReturnThrowable'
739         while (t instanceof InvocationTargetException && t.getCause() != null) {
740             t = t.getCause();
741         }
742         // Errors to be passed as is
743         ClassUtil.throwIfError(t);
744         // StackOverflowErrors are tricky ones; need to be careful...
745         boolean wrap = (ctxt == null) || ctxt.isEnabled(DeserializationFeature.WRAP_EXCEPTIONS);
746         // Ditto for IOExceptions; except we may want to wrap JSON exceptions
747         if (t instanceof IOException) {
748             if (!wrap || !(t instanceof JsonProcessingException)) {
749                 throw (IOException) t;
750             }
751         } else if (!wrap) { // allow disabling wrapping for unchecked exceptions
752             ClassUtil.throwIfRTE(t);
753         }
754         throw JsonMappingException.wrapWithPath(t, bean, fieldName);
755     }
756 
757     /**
758      * Helper method used to find exact location of a property with name
759      * given exactly, not subject to case changes, within hash area.
760      * Expectation is that such property SHOULD exist, although no
761      * exception is thrown.
762      *
763      * @since 2.7
764      */
765     /*
766     private final int _findIndexInHash(String key)
767     {
768         final int slot = _hashCode(key);
769         int ix = (slot<<1);
770 
771         // primary match?
772         if (key.equals(_hashArea[ix])) {
773             return ix+1;
774         }
775         // no? secondary?
776         int hashSize = _hashMask+1;
777         ix = hashSize + (slot>>1) << 1;
778         if (key.equals(_hashArea[ix])) {
779             return ix+1;
780         }
781         // perhaps spill then
782         int i = (hashSize + (hashSize>>1)) << 1;
783         for (int end = i + _spillCount; i < end; i += 2) {
784             if (key.equals(_hashArea[i])) {
785                 return i+1;
786             }
787         }
788         return -1;
789     }
790     */
791 
_findFromOrdered(SettableBeanProperty prop)792     private final int _findFromOrdered(SettableBeanProperty prop) {
793         for (int i = 0, end = _propsInOrder.length; i < end; ++i) {
794             if (_propsInOrder[i] == prop) {
795                 return i;
796             }
797         }
798         throw new IllegalStateException("Illegal state: property '"+prop.getName()+"' missing from _propsInOrder");
799     }
800 
801     // Offlined version for convenience if we want to change hashing scheme
_hashCode(String key)802     private final int _hashCode(String key) {
803         // This method produces better hash, fewer collisions... yet for some
804         // reason produces slightly worse performance. Very strange.
805 
806         // 05-Aug-2015, tatu: ... still true?
807 
808         /*
809         int h = key.hashCode();
810         return (h + (h >> 13)) & _hashMask;
811         */
812         return key.hashCode() & _hashMask;
813     }
814 
815     // @since 2.9
_buildAliasMapping(Map<String,List<PropertyName>> defs, boolean caseInsensitive, Locale loc)816     private Map<String,String> _buildAliasMapping(Map<String,List<PropertyName>> defs,
817             boolean caseInsensitive, Locale loc)
818     {
819         if ((defs == null) || defs.isEmpty()) {
820             return Collections.emptyMap();
821         }
822         Map<String,String> aliases = new HashMap<>();
823         for (Map.Entry<String,List<PropertyName>> entry : defs.entrySet()) {
824             String key = entry.getKey();
825             if (caseInsensitive) {
826                 key = key.toLowerCase(loc);
827             }
828             for (PropertyName pn : entry.getValue()) {
829                 String mapped = pn.getSimpleName();
830                 if (caseInsensitive) {
831                     mapped = mapped.toLowerCase(loc);
832                 }
833                 aliases.put(mapped, key);
834             }
835         }
836         return aliases;
837     }
838 }
839