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