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 import com.fasterxml.jackson.databind.DeserializationContext;
10 import com.fasterxml.jackson.databind.DeserializationFeature;
11 import com.fasterxml.jackson.databind.JsonDeserializer;
12 import com.fasterxml.jackson.databind.JsonMappingException;
13 import com.fasterxml.jackson.databind.MapperFeature;
14 import com.fasterxml.jackson.databind.PropertyName;
15 import com.fasterxml.jackson.databind.cfg.MapperConfig;
16 import com.fasterxml.jackson.databind.deser.SettableBeanProperty;
17 import com.fasterxml.jackson.databind.util.ClassUtil;
18 import com.fasterxml.jackson.databind.util.NameTransformer;
19
20 /**
21  * Helper class used for storing mapping from property name to
22  * {@link SettableBeanProperty} instances.
23  *<p>
24  * Note that this class is used instead of generic {@link java.util.HashMap}
25  * for bit of performance gain (and some memory savings): although default
26  * implementation is very good for generic use cases, it can be streamlined
27  * a bit for specific use case we have. Even relatively small improvements
28  * matter since this is directly on the critical path during deserialization,
29  * as it is done for each and every POJO property deserialized.
30  */

31 public class BeanPropertyMap
32     implements Iterable<SettableBeanProperty>,
33         java.io.Serializable
34 {
35     private static final long serialVersionUID = 2L;
36
37     /**
38      * @since 2.5
39      */

40     protected final boolean _caseInsensitive;
41
42     private int _hashMask;
43
44     /**
45      * Number of entries stored in the hash area.
46      */

47     private int _size;
48
49     private int _spillCount;
50
51     /**
52      * Hash area that contains key/property pairs in adjacent elements.
53      */

54     private Object[] _hashArea;
55
56     /**
57      * Array of properties in the exact order they were handed in. This is
58      * used by as-array serialization, deserialization.
59      */

60     private final SettableBeanProperty[] _propsInOrder;
61
62     /**
63      * Configuration of alias mappings, indexed by unmodified property name
64      * to unmodified aliases, if any; entries only included for properties
65      * that do have aliases.
66      * This is is used for constructing actual reverse lookup mapping, if
67      * needed, taking into account possible case-insensitivity, as well
68      * as possibility of name prefixes.
69      *
70      * @since 2.9
71      */

72     private final Map<String,List<PropertyName>> _aliasDefs;
73
74     /**
75      * Mapping from secondary names (aliases) to primary names.
76      *
77      * @since 2.9
78      */

79     private final Map<String,String> _aliasMapping;
80
81     /**
82      * We require {@link Locale} since case changes are locale-sensitive in certain
83      * cases (see <a href="https://en.wikipedia.org/wiki/Dotted_and_dotless_I">Turkish I</a>
84      * for example)
85      *
86      * @since 2.11
87      */

88     private final Locale _locale;
89
90     /**
91      * @since 2.11
92      */

93     public BeanPropertyMap(boolean caseInsensitive, Collection<SettableBeanProperty> props,
94             Map<String,List<PropertyName>> aliasDefs,
95             Locale locale)
96     {
97         _caseInsensitive = caseInsensitive;
98         _propsInOrder = props.toArray(new SettableBeanProperty[props.size()]);
99         _aliasDefs = aliasDefs;
100         _locale = locale;
101         _aliasMapping = _buildAliasMapping(aliasDefs, caseInsensitive, locale);
102         init(props);
103
104     }
105
106     /**
107      * @deprecated since 2.11
108      */

109     @Deprecated
110     public BeanPropertyMap(boolean caseInsensitive, Collection<SettableBeanProperty> props,
111             Map<String,List<PropertyName>> aliasDefs) {
112         this(caseInsensitive, props, aliasDefs, Locale.getDefault());
113     }
114
115     /* Copy constructors used when a property can replace existing one
116      *
117      * @since 2.9.6
118      */

119     private BeanPropertyMap(BeanPropertyMap src,
120             SettableBeanProperty newProp, int hashIndex, int orderedIndex)
121     {
122         // First, copy most fields as is:
123         _caseInsensitive = src._caseInsensitive;
124         _locale = src._locale;
125         _hashMask = src._hashMask;
126         _size = src._size;
127         _spillCount = src._spillCount;
128         _aliasDefs = src._aliasDefs;
129         _aliasMapping = src._aliasMapping;
130
131         // but then make deep copy of arrays to modify
132         _hashArea = Arrays.copyOf(src._hashArea, src._hashArea.length);
133         _propsInOrder = Arrays.copyOf(src._propsInOrder, src._propsInOrder.length);
134         _hashArea[hashIndex] = newProp;
135         _propsInOrder[orderedIndex] = newProp;
136     }
137
138     /* Copy constructors used when a property needs to be appended (can't replace)
139      *
140      * @since 2.9.6
141      */

142     private BeanPropertyMap(BeanPropertyMap src,
143             SettableBeanProperty newProp, String key, int slot)
144     {
145         // First, copy most fields as is:
146         _caseInsensitive = src._caseInsensitive;
147         _locale = src._locale;
148         _hashMask = src._hashMask;
149         _size = src._size;
150         _spillCount = src._spillCount;
151         _aliasDefs = src._aliasDefs;
152         _aliasMapping = src._aliasMapping;
153
154         // but then make deep copy of arrays to modify
155         _hashArea = Arrays.copyOf(src._hashArea, src._hashArea.length);
156         int last = src._propsInOrder.length;
157         // and append property at the end of ordering
158         _propsInOrder = Arrays.copyOf(src._propsInOrder, last+1);
159         _propsInOrder[last] = newProp;
160
161         final int hashSize = _hashMask+1;
162         int ix = (slot<<1);
163
164         // primary slot not free?
165         if (_hashArea[ix] != null) {
166             // secondary?
167             ix = (hashSize + (slot >> 1)) << 1;
168             if (_hashArea[ix] != null) {
169                 // ok, spill over.
170                 ix = ((hashSize + (hashSize >> 1) ) << 1) + _spillCount;
171                 _spillCount += 2;
172                 if (ix >= _hashArea.length) {
173                     _hashArea = Arrays.copyOf(_hashArea, _hashArea.length + 4);
174                 }
175             }
176         }
177         _hashArea[ix] = key;
178         _hashArea[ix+1] = newProp;
179     }
180
181     @Deprecated // since 2.8
182     public BeanPropertyMap(boolean caseInsensitive, Collection<SettableBeanProperty> props)
183     {
184         this(caseInsensitive, props, Collections.<String,List<PropertyName>>emptyMap(),
185                 Locale.getDefault());
186     }
187
188     /**
189      * @since 2.8
190      */

191     protected BeanPropertyMap(BeanPropertyMap base, boolean caseInsensitive)
192     {
193         _caseInsensitive = caseInsensitive;
194         _locale = base._locale;
195         _aliasDefs = base._aliasDefs;
196         _aliasMapping = base._aliasMapping;
197
198         // 16-May-2016, tatu: Alas, not enough to just change flag, need to re-init as well.
199         _propsInOrder = Arrays.copyOf(base._propsInOrder, base._propsInOrder.length);
200         init(Arrays.asList(_propsInOrder));
201     }
202
203     /**
204      * Mutant factory method that constructs a new instance if desired case-insensitivity
205      * state differs from the state of this instance; if states are the same, returns
206      * <code>this</code>.
207      *
208      * @since 2.8
209      */

210     public BeanPropertyMap withCaseInsensitivity(boolean state) {
211         if (_caseInsensitive == state) {
212             return this;
213         }
214         return new BeanPropertyMap(this, state);
215     }
216
217     protected void init(Collection<SettableBeanProperty> props)
218     {
219         _size = props.size();
220
221         // First: calculate size of primary hash area
222         final int hashSize = findSize(_size);
223         _hashMask = hashSize-1;
224
225         // and allocate enough to contain primary/secondary, expand for spillovers as need be
226         int alloc = (hashSize + (hashSize>>1)) * 2;
227         Object[] hashed = new Object[alloc];
228         int spillCount = 0;
229
230         for (SettableBeanProperty prop : props) {
231             // Due to removal, renaming, theoretically possible we'll have "holes" so:
232             if (prop == null) {
233                 continue;
234             }
235
236             String key = getPropertyName(prop);
237             int slot = _hashCode(key);
238             int ix = (slot<<1);
239
240             // primary slot not free?
241             if (hashed[ix] != null) {
242                 // secondary?
243                 ix = (hashSize + (slot >> 1)) << 1;
244                 if (hashed[ix] != null) {
245                     // ok, spill over.
246                     ix = ((hashSize + (hashSize >> 1) ) << 1) + spillCount;
247                     spillCount += 2;
248                     if (ix >= hashed.length) {
249                         hashed = Arrays.copyOf(hashed, hashed.length + 4);
250                     }
251                 }
252             }
253             hashed[ix] = key;
254             hashed[ix+1] = prop;
255
256             // and aliases
257         }
258         _hashArea = hashed;
259         _spillCount = spillCount;
260     }
261
262     private final static int findSize(int size)
263     {
264         if (size <= 5) {
265             return 8;
266         }
267         if (size <= 12) {
268             return 16;
269         }
270         int needed = size + (size >> 2); // at most 80% full
271         int result = 32;
272         while (result < needed) {
273             result += result;
274         }
275         return result;
276     }
277
278     /**
279      * @since 2.11
280      */

281     public static BeanPropertyMap construct(MapperConfig<?> config,
282             Collection<SettableBeanProperty> props,
283             Map<String,List<PropertyName>> aliasMapping) {
284         return new BeanPropertyMap(config.isEnabled(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES),
285                 props, aliasMapping,
286                 config.getLocale());
287     }
288
289     /**
290      * @deprecated since 2.11
291      */

292     @Deprecated
293     public static BeanPropertyMap construct(Collection<SettableBeanProperty> props,
294             boolean caseInsensitive, Map<String,List<PropertyName>> aliasMapping) {
295         return new BeanPropertyMap(caseInsensitive, props, aliasMapping);
296     }
297
298     @Deprecated // since 2.9
299     public static BeanPropertyMap construct(Collection<SettableBeanProperty> props, boolean caseInsensitive) {
300         return construct(props, caseInsensitive,
301                 Collections.<String,List<PropertyName>>emptyMap());
302     }
303
304     /**
305      * Fluent copy method that creates a new instance that is a copy
306      * of this instance except for one additional property that is
307      * passed as the argument.
308      * Note that method does not modify this instance but constructs
309      * and returns a new one.
310      */

311     public BeanPropertyMap withProperty(SettableBeanProperty newProp)
312     {
313         // First: may be able to just replace?
314         String key = getPropertyName(newProp);
315
316         for (int i = 1, end = _hashArea.length; i < end; i += 2) {
317             SettableBeanProperty prop = (SettableBeanProperty) _hashArea[i];
318             if ((prop != null) && prop.getName().equals(key)) {
319                 return new BeanPropertyMap(this, newProp, i, _findFromOrdered(prop));
320             }
321         }
322         // If not, append
323         final int slot = _hashCode(key);
324
325         return new BeanPropertyMap(this, newProp, key, slot);
326     }
327
328     public BeanPropertyMap assignIndexes()
329     {
330         // order is arbitrary, but stable:
331         int index = 0;
332         for (int i = 1, end = _hashArea.length; i < end; i += 2) {
333             SettableBeanProperty prop = (SettableBeanProperty) _hashArea[i];
334             if (prop != null) {
335                 prop.assignIndex(index++);
336             }
337         }
338         return this;
339     }
340
341     /**
342      * Mutant factory method for constructing a map where all entries use given
343      * prefix
344      */

345     public BeanPropertyMap renameAll(NameTransformer transformer)
346     {
347         if (transformer == null || (transformer == NameTransformer.NOP)) {
348             return this;
349         }
350         // Try to retain insertion ordering as well
351         final int len = _propsInOrder.length;
352         ArrayList<SettableBeanProperty> newProps = new ArrayList<SettableBeanProperty>(len);
353
354         for (int i = 0; i < len; ++i) {
355             SettableBeanProperty prop = _propsInOrder[i];
356             
357             // What to do with holes? For now, retain
358             if (prop == null) {
359                 newProps.add(prop);
360                 continue;
361             }
362             newProps.add(_rename(prop, transformer));
363         }
364         // should we try to re-index? Ordering probably changed but caller probably doesn't want changes...
365         // 26-Feb-2017, tatu: Probably SHOULD handle renaming wrt Aliases?
366         return new BeanPropertyMap(_caseInsensitive, newProps, _aliasDefs, _locale);
367     }
368
369     /*
370     /**********************************************************
371     /* Public API, mutators
372     /**********************************************************
373      */

374
375     /**
376      * Mutant factory method that will use this instance as the base, and
377      * construct an instance that is otherwise same except for excluding
378      * properties with specified names.
379      *
380      * @since 2.8
381      */

382     public BeanPropertyMap withoutProperties(Collection<String> toExclude)
383     {
384         if (toExclude.isEmpty()) {
385             return this;
386         }
387         final int len = _propsInOrder.length;
388         ArrayList<SettableBeanProperty> newProps = new ArrayList<SettableBeanProperty>(len);
389
390         for (int i = 0; i < len; ++i) {
391             SettableBeanProperty prop = _propsInOrder[i];
392             // 01-May-2015, tatu: Not 100% sure if existing `null`s should be retained;
393             //   or, if entries to ignore should be retained as nulls. For now just
394             //   prune them out
395             if (prop != null) { // may contain holes, too, check.
396                 if (!toExclude.contains(prop.getName())) {
397                     newProps.add(prop);
398                 }
399             }
400         }
401         // should we try to re-index? Apparently no need
402         return new BeanPropertyMap(_caseInsensitive, newProps, _aliasDefs, _locale);
403     }
404
405     @Deprecated // in 2.9.4 -- must call method that takes old and new property to avoid mismatch
406     public void replace(SettableBeanProperty newProp)
407     {
408         String key = getPropertyName(newProp);
409         int ix = _findIndexInHash(key);
410         if (ix < 0) {
411             throw new NoSuchElementException("No entry '"+key+"' found, can't replace");
412         }
413         SettableBeanProperty prop = (SettableBeanProperty) _hashArea[ix];
414         _hashArea[ix] = newProp;
415         // also, replace in in-order
416         _propsInOrder[_findFromOrdered(prop)] = newProp;
417     }
418
419     /**
420      * Specialized method that can be used to replace an existing entry
421      * (note: entry MUST exist; otherwise exception is thrown) with
422      * specified replacement.
423      *
424      * @since 2.9.4
425      */

426     public void replace(SettableBeanProperty origProp, SettableBeanProperty newProp)
427     {
428         int i = 1;
429         int end = _hashArea.length;
430
431         for (;; i += 2) {
432             if (i > end) {
433                 throw new NoSuchElementException("No entry '"+origProp.getName()+"' found, can't replace");
434             }
435             if (_hashArea[i] == origProp) {
436                 _hashArea[i] = newProp;
437                 break;
438             }
439         }
440         _propsInOrder[_findFromOrdered(origProp)] = newProp;
441     }
442
443     /**
444      * Specialized method for removing specified existing entry.
445      * NOTE: entry MUST exist, otherwise an exception is thrown.
446      */

447     public void remove(SettableBeanProperty propToRm)
448     {
449         ArrayList<SettableBeanProperty> props = new ArrayList<SettableBeanProperty>(_size);
450         String key = getPropertyName(propToRm);
451         boolean found = false;
452
453         for (int i = 1, end = _hashArea.length; i < end; i += 2) {
454             SettableBeanProperty prop = (SettableBeanProperty) _hashArea[i];
455             if (prop == null) {
456                 continue;
457             }
458             if (!found) {
459                 // 09-Jan-2017, tatu: Important: must check name slot and NOT property name,
460                 //   as only former is lower-case in case-insensitive case
461                 found = key.equals(_hashArea[i-1]);
462                 if (found) {
463                     // need to leave a hole here
464                     _propsInOrder[_findFromOrdered(prop)] = null;
465                     continue;
466                 }
467             }
468             props.add(prop);
469         }
470         if (!found) {
471             throw new NoSuchElementException("No entry '"+propToRm.getName()+"' found, can't remove");
472         }
473         init(props);
474     }
475
476     /*
477     /**********************************************************
478     /* Public API, simple accessors
479     /**********************************************************
480      */

481
482     public int size() { return _size; }
483
484     /**
485      * @since 2.9
486      */

487     public boolean isCaseInsensitive() {
488         return _caseInsensitive;
489     }
490
491     /**
492      * @since 2.9
493      */

494     public boolean hasAliases() {
495         return !_aliasDefs.isEmpty();
496     }
497
498     /**
499      * Accessor for traversing over all contained properties.
500      */

501     @Override
502     public Iterator<SettableBeanProperty> iterator() {
503         return _properties().iterator();
504     }
505
506     private List<SettableBeanProperty> _properties() {
507         ArrayList<SettableBeanProperty> p = new ArrayList<SettableBeanProperty>(_size);
508         for (int i = 1, end = _hashArea.length; i < end; i += 2) {
509             SettableBeanProperty prop = (SettableBeanProperty) _hashArea[i];
510             if (prop != null) {
511                 p.add(prop);
512             }
513         }
514         return p;
515     }
516     
517     /**
518      * Method that will re-create initial insertion-ordering of
519      * properties contained in this map. Note that if properties
520      * have been removed, array may contain nulls; otherwise
521      * it should be consecutive.
522      * 
523      * @since 2.1
524      */

525     public SettableBeanProperty[] getPropertiesInInsertionOrder() {
526         return _propsInOrder;
527     }
528
529     // Confining this case insensitivity to this function (and the find method) in case we want to
530     // apply a particular locale to the lower case function.  For now, using the default.
531     protected final String getPropertyName(SettableBeanProperty prop) {
532         return _caseInsensitive ? prop.getName().toLowerCase(_locale) : prop.getName();
533     }
534
535     /*
536     /**********************************************************
537     /* Public API, property lookup
538     /**********************************************************
539      */

540     
541     /**
542      * @since 2.3
543      */

544     public SettableBeanProperty find(int index)
545     {
546         // note: will scan the whole area, including primary, secondary and
547         // possible spill-area
548         for (int i = 1, end = _hashArea.length; i < end; i += 2) {
549             SettableBeanProperty prop = (SettableBeanProperty) _hashArea[i];
550             if ((prop != null) && (index == prop.getPropertyIndex())) {
551                 return prop;
552             }
553         }
554         return null;
555     }
556
557     public SettableBeanProperty find(String key)
558     {
559         if (key == null) {
560             throw new IllegalArgumentException("Cannot pass null property name");
561         }
562         if (_caseInsensitive) {
563             key = key.toLowerCase(_locale);
564         }
565
566         // inlined `_hashCode(key)`
567         int slot = key.hashCode() & _hashMask;
568 //        int h = key.hashCode();
569 //        int slot = (h + (h >> 13)) & _hashMask;
570
571         int ix = (slot<<1);
572         Object match = _hashArea[ix];
573         if ((match == key) || key.equals(match)) {
574             return (SettableBeanProperty) _hashArea[ix+1];
575         }
576         return _find2(key, slot, match);
577     }
578
579     private final SettableBeanProperty _find2(String key, int slot, Object match)
580     {
581         if (match == null) {
582             // 26-Feb-2017, tatu: Need to consider aliases
583             return _findWithAlias(_aliasMapping.get(key));
584         }
585         // no? secondary?
586         int hashSize = _hashMask+1;
587         int ix = hashSize + (slot>>1) << 1;
588         match = _hashArea[ix];
589         if (key.equals(match)) {
590             return (SettableBeanProperty) _hashArea[ix+1];
591         }
592         if (match != null) { // _findFromSpill(...)
593             int i = (hashSize + (hashSize>>1)) << 1;
594             for (int end = i + _spillCount; i < end; i += 2) {
595                 match = _hashArea[i];
596                 if ((match == key) || key.equals(match)) {
597                     return (SettableBeanProperty) _hashArea[i+1];
598                 }
599             }
600         }
601         // 26-Feb-2017, tatu: Need to consider aliases
602         return _findWithAlias(_aliasMapping.get(key));
603     }
604
605     private SettableBeanProperty _findWithAlias(String keyFromAlias)
606     {
607         if (keyFromAlias == null) {
608             return null;
609         }
610         // NOTE: need to inline much of handling do avoid cyclic calls via alias
611         // first, inlined main `find(String)`
612         int slot = _hashCode(keyFromAlias);
613         int ix = (slot<<1);
614         Object match = _hashArea[ix];
615         if (keyFromAlias.equals(match)) {
616             return (SettableBeanProperty) _hashArea[ix+1];
617         }
618         if (match == null) {
619             return null;
620         }
621         return _find2ViaAlias(keyFromAlias, slot, match);
622     }
623
624     private SettableBeanProperty _find2ViaAlias(String key, int slot, Object match)
625     {
626         // no? secondary?
627         int hashSize = _hashMask+1;
628         int ix = hashSize + (slot>>1) << 1;
629         match = _hashArea[ix];
630         if (key.equals(match)) {
631             return (SettableBeanProperty) _hashArea[ix+1];
632         }
633         if (match != null) { // _findFromSpill(...)
634             int i = (hashSize + (hashSize>>1)) << 1;
635             for (int end = i + _spillCount; i < end; i += 2) {
636                 match = _hashArea[i];
637                 if ((match == key) || key.equals(match)) {
638                     return (SettableBeanProperty) _hashArea[i+1];
639                 }
640             }
641         }
642         return null;
643     }
644
645     /*
646     /**********************************************************
647     /* Public API, deserialization support
648     /**********************************************************
649      */

650
651     /**
652      * Convenience method that tries to find property with given name, and
653      * if it is found, call {@link SettableBeanProperty#deserializeAndSet}
654      * on it, and return true; or, if not found, return false.
655      * Note, too, that if deserialization is attempted, possible exceptions
656      * are wrapped if and as necessary, so caller need not handle those.
657      * 
658      * @since 2.5
659      */

660     public boolean findDeserializeAndSet(JsonParser p, DeserializationContext ctxt,
661             Object bean, String key) throws IOException
662     {
663         final SettableBeanProperty prop = find(key);
664         if (prop == null) {
665             return false;
666         }
667         try {
668             prop.deserializeAndSet(p, ctxt, bean);
669         } catch (Exception e) {
670             wrapAndThrow(e, bean, key, ctxt);
671         }
672         return true;
673     }
674
675     /*
676     /**********************************************************
677     /* Std method overrides
678     /**********************************************************
679      */

680     
681     @Override
682     public String toString()
683     {
684         StringBuilder sb = new StringBuilder();
685         sb.append("Properties=[");
686         int count = 0;
687
688         Iterator<SettableBeanProperty> it = iterator();
689         while (it.hasNext()) {
690             SettableBeanProperty prop = it.next();
691             if (count++ > 0) {
692                 sb.append(", ");
693             }
694             sb.append(prop.getName());
695             sb.append('(');
696             sb.append(prop.getType());
697             sb.append(')');
698         }
699         sb.append(']');
700         if (!_aliasDefs.isEmpty()) {
701             sb.append("(aliases: ");
702             sb.append(_aliasDefs);
703             sb.append(")");
704         }
705         return sb.toString();
706     }
707     
708     /*
709     /**********************************************************
710     /* Helper methods
711     /**********************************************************
712      */

713
714     protected SettableBeanProperty _rename(SettableBeanProperty prop, NameTransformer xf)
715     {
716         if (prop == null) {
717             return prop;
718         }
719         String newName = xf.transform(prop.getName());
720         prop = prop.withSimpleName(newName);
721         JsonDeserializer<?> deser = prop.getValueDeserializer();
722         if (deser != null) {
723             @SuppressWarnings("unchecked")
724             JsonDeserializer<Object> newDeser = (JsonDeserializer<Object>)
725                 deser.unwrappingDeserializer(xf);
726             if (newDeser != deser) {
727                 prop = prop.withValueDeserializer(newDeser);
728             }
729         }
730         return prop;
731     }
732
733     protected void wrapAndThrow(Throwable t, Object bean, String fieldName, DeserializationContext ctxt)
734         throws IOException
735     {
736         // inlined 'throwOrReturnThrowable'
737         while (t instanceof InvocationTargetException && t.getCause() != null) {
738             t = t.getCause();
739         }
740         // Errors to be passed as is
741         ClassUtil.throwIfError(t);
742         // StackOverflowErrors are tricky ones; need to be careful...
743         boolean wrap = (ctxt == null) || ctxt.isEnabled(DeserializationFeature.WRAP_EXCEPTIONS);
744         // Ditto for IOExceptions; except we may want to wrap JSON exceptions
745         if (t instanceof IOException) {
746             if (!wrap || !(t instanceof JsonProcessingException)) {
747                 throw (IOException) t;
748             }
749         } else if (!wrap) { // allow disabling wrapping for unchecked exceptions
750             ClassUtil.throwIfRTE(t);
751         }
752         throw JsonMappingException.wrapWithPath(t, bean, fieldName);
753     }
754
755     /**
756      * Helper method used to find exact location of a property with name
757      * given exactly, not subject to case changes, within hash area.
758      * Expectation is that such property SHOULD exist, although no
759      * exception is thrown.
760      *
761      * @since 2.7
762      */

763     private final int _findIndexInHash(String key)
764     {
765         final int slot = _hashCode(key);
766         int ix = (slot<<1);
767         
768         // primary match?
769         if (key.equals(_hashArea[ix])) {
770             return ix+1;
771         }
772         // no? secondary?
773         int hashSize = _hashMask+1;
774         ix = hashSize + (slot>>1) << 1;
775         if (key.equals(_hashArea[ix])) {
776             return ix+1;
777         }
778         // perhaps spill then
779         int i = (hashSize + (hashSize>>1)) << 1;
780         for (int end = i + _spillCount; i < end; i += 2) {
781             if (key.equals(_hashArea[i])) {
782                 return i+1;
783             }
784         }
785         return -1;
786     }
787     
788     private final int _findFromOrdered(SettableBeanProperty prop) {
789         for (int i = 0, end = _propsInOrder.length; i < end; ++i) {
790             if (_propsInOrder[i] == prop) {
791                 return i;
792             }
793         }
794         throw new IllegalStateException("Illegal state: property '"+prop.getName()+"' missing from _propsInOrder");
795     }
796
797     // Offlined version for convenience if we want to change hashing scheme
798     private final int _hashCode(String key) {
799         // This method produces better hash, fewer collisions... yet for some
800         // reason produces slightly worse performance. Very strange.
801
802         // 05-Aug-2015, tatu: ... still true?
803
804         /*
805         int h = key.hashCode();
806         return (h + (h >> 13)) & _hashMask;
807         */

808         return key.hashCode() & _hashMask;
809     }
810
811     // @since 2.9
812     private Map<String,String> _buildAliasMapping(Map<String,List<PropertyName>> defs,
813             boolean caseInsensitive, Locale loc)
814     {
815         if ((defs == null) || defs.isEmpty()) {
816             return Collections.emptyMap();
817         }
818         Map<String,String> aliases = new HashMap<>();
819         for (Map.Entry<String,List<PropertyName>> entry : defs.entrySet()) {
820             String key = entry.getKey();
821             if (caseInsensitive) {
822                 key = key.toLowerCase(loc);
823             }
824             for (PropertyName pn : entry.getValue()) {
825                 String mapped = pn.getSimpleName();
826                 if (caseInsensitive) {
827                     mapped = mapped.toLowerCase(loc);
828                 }
829                 aliases.put(mapped, key);
830             }
831         }
832         return aliases;
833     }
834 }
835