1 package com.fasterxml.jackson.databind.introspect; 2 3 import java.lang.reflect.Modifier; 4 import java.util.*; 5 6 import com.fasterxml.jackson.annotation.JacksonInject; 7 import com.fasterxml.jackson.annotation.JsonCreator; 8 9 import com.fasterxml.jackson.databind.*; 10 11 import com.fasterxml.jackson.databind.cfg.HandlerInstantiator; 12 import com.fasterxml.jackson.databind.cfg.MapperConfig; 13 import com.fasterxml.jackson.databind.util.BeanUtil; 14 import com.fasterxml.jackson.databind.util.ClassUtil; 15 16 /** 17 * Helper class used for aggregating information about all possible 18 * properties of a POJO. 19 */ 20 public class POJOPropertiesCollector 21 { 22 /* 23 /********************************************************** 24 /* Configuration 25 /********************************************************** 26 */ 27 28 /** 29 * Configuration settings 30 */ 31 protected final MapperConfig<?> _config; 32 33 /** 34 * True if introspection is done for serialization (giving 35 * precedence for serialization annotations), or not (false, deserialization) 36 */ 37 protected final boolean _forSerialization; 38 39 /** 40 * @since 2.5 41 */ 42 protected final boolean _stdBeanNaming; 43 44 /** 45 * Type of POJO for which properties are being collected. 46 */ 47 protected final JavaType _type; 48 49 /** 50 * Low-level introspected class information (methods, fields etc) 51 */ 52 protected final AnnotatedClass _classDef; 53 54 protected final VisibilityChecker<?> _visibilityChecker; 55 56 protected final AnnotationIntrospector _annotationIntrospector; 57 58 /** 59 * @since 2.9 60 */ 61 protected final boolean _useAnnotations; 62 63 /** 64 * Prefix used by auto-detected mutators ("setters"): usually "set", 65 * but differs for builder objects ("with" by default). 66 */ 67 protected final String _mutatorPrefix; 68 69 /* 70 /********************************************************** 71 /* Collected property information 72 /********************************************************** 73 */ 74 75 /** 76 * State flag we keep to indicate whether actual property information 77 * has been collected or not. 78 */ 79 protected boolean _collected; 80 81 /** 82 * Set of logical property information collected so far. 83 *<p> 84 * Since 2.6, this has been constructed (more) lazily, to defer 85 * throwing of exceptions for potential conflicts in cases where 86 * this may not be an actual problem. 87 */ 88 protected LinkedHashMap<String, POJOPropertyBuilder> _properties; 89 90 protected LinkedList<POJOPropertyBuilder> _creatorProperties; 91 92 /** 93 * A set of "field renamings" that have been discovered, indicating 94 * intended renaming of other accesors: key is the implicit original 95 * name and value intended name to use instead. 96 *<p> 97 * Note that these renamings are applied earlier than "regular" (explicit) 98 * renamings and affect implicit name: their effect may be changed by 99 * further renaming based on explicit indicators. 100 * The main use case is to effectively relink accessors based on fields 101 * discovered, and used to sort of correct otherwise missing linkage between 102 * fields and other accessors. 103 * 104 * @since 2.11 105 */ 106 protected Map<PropertyName, PropertyName> _fieldRenameMappings; 107 108 protected LinkedList<AnnotatedMember> _anyGetters; 109 110 protected LinkedList<AnnotatedMethod> _anySetters; 111 112 protected LinkedList<AnnotatedMember> _anySetterField; 113 114 /** 115 * Method(s) marked with 'JsonValue' annotation 116 *<p> 117 * NOTE: before 2.9, was `AnnotatedMethod`; with 2.9 allows fields too 118 */ 119 protected LinkedList<AnnotatedMember> _jsonValueAccessors; 120 121 /** 122 * Lazily collected list of properties that can be implicitly 123 * ignored during serialization; only updated when collecting 124 * information for deserialization purposes 125 */ 126 protected HashSet<String> _ignoredPropertyNames; 127 128 /** 129 * Lazily collected list of members that were annotated to 130 * indicate that they represent mutators for deserializer 131 * value injection. 132 */ 133 protected LinkedHashMap<Object, AnnotatedMember> _injectables; 134 135 /* 136 /********************************************************** 137 /* Life-cycle 138 /********************************************************** 139 */ 140 POJOPropertiesCollector(MapperConfig<?> config, boolean forSerialization, JavaType type, AnnotatedClass classDef, String mutatorPrefix)141 protected POJOPropertiesCollector(MapperConfig<?> config, boolean forSerialization, 142 JavaType type, AnnotatedClass classDef, String mutatorPrefix) 143 { 144 _config = config; 145 _stdBeanNaming = config.isEnabled(MapperFeature.USE_STD_BEAN_NAMING); 146 _forSerialization = forSerialization; 147 _type = type; 148 _classDef = classDef; 149 _mutatorPrefix = (mutatorPrefix == null) ? "set" : mutatorPrefix; 150 if (config.isAnnotationProcessingEnabled()) { 151 _useAnnotations = true; 152 _annotationIntrospector = _config.getAnnotationIntrospector(); 153 } else { 154 _useAnnotations = false; 155 _annotationIntrospector = AnnotationIntrospector.nopInstance(); 156 } 157 _visibilityChecker = _config.getDefaultVisibilityChecker(type.getRawClass(), 158 classDef); 159 } 160 161 /* 162 /********************************************************** 163 /* Public API 164 /********************************************************** 165 */ 166 getConfig()167 public MapperConfig<?> getConfig() { 168 return _config; 169 } 170 getType()171 public JavaType getType() { 172 return _type; 173 } 174 getClassDef()175 public AnnotatedClass getClassDef() { 176 return _classDef; 177 } 178 getAnnotationIntrospector()179 public AnnotationIntrospector getAnnotationIntrospector() { 180 return _annotationIntrospector; 181 } 182 getProperties()183 public List<BeanPropertyDefinition> getProperties() { 184 // make sure we return a copy, so caller can remove entries if need be: 185 Map<String, POJOPropertyBuilder> props = getPropertyMap(); 186 return new ArrayList<BeanPropertyDefinition>(props.values()); 187 } 188 getInjectables()189 public Map<Object, AnnotatedMember> getInjectables() { 190 if (!_collected) { 191 collectAll(); 192 } 193 return _injectables; 194 } 195 196 /** 197 * @since 2.9 198 */ getJsonValueAccessor()199 public AnnotatedMember getJsonValueAccessor() 200 { 201 if (!_collected) { 202 collectAll(); 203 } 204 // If @JsonValue defined, must have a single one 205 if (_jsonValueAccessors != null) { 206 if (_jsonValueAccessors.size() > 1) { 207 reportProblem("Multiple 'as-value' properties defined (%s vs %s)", 208 _jsonValueAccessors.get(0), 209 _jsonValueAccessors.get(1)); 210 } 211 // otherwise we won't greatly care 212 return _jsonValueAccessors.get(0); 213 } 214 return null; 215 } 216 getAnyGetter()217 public AnnotatedMember getAnyGetter() 218 { 219 if (!_collected) { 220 collectAll(); 221 } 222 if (_anyGetters != null) { 223 if (_anyGetters.size() > 1) { 224 reportProblem("Multiple 'any-getters' defined (%s vs %s)", 225 _anyGetters.get(0), _anyGetters.get(1)); 226 } 227 return _anyGetters.getFirst(); 228 } 229 return null; 230 } 231 getAnySetterField()232 public AnnotatedMember getAnySetterField() 233 { 234 if (!_collected) { 235 collectAll(); 236 } 237 if (_anySetterField != null) { 238 if (_anySetterField.size() > 1) { 239 reportProblem("Multiple 'any-setter' fields defined (%s vs %s)", 240 _anySetterField.get(0), _anySetterField.get(1)); 241 } 242 return _anySetterField.getFirst(); 243 } 244 return null; 245 } 246 getAnySetterMethod()247 public AnnotatedMethod getAnySetterMethod() 248 { 249 if (!_collected) { 250 collectAll(); 251 } 252 if (_anySetters != null) { 253 if (_anySetters.size() > 1) { 254 reportProblem("Multiple 'any-setter' methods defined (%s vs %s)", 255 _anySetters.get(0), _anySetters.get(1)); 256 } 257 return _anySetters.getFirst(); 258 } 259 return null; 260 } 261 262 /** 263 * Accessor for set of properties that are explicitly marked to be ignored 264 * via per-property markers (but NOT class annotations). 265 */ getIgnoredPropertyNames()266 public Set<String> getIgnoredPropertyNames() { 267 return _ignoredPropertyNames; 268 } 269 270 /** 271 * Accessor to find out whether type specified requires inclusion 272 * of Object Identifier. 273 */ getObjectIdInfo()274 public ObjectIdInfo getObjectIdInfo() 275 { 276 ObjectIdInfo info = _annotationIntrospector.findObjectIdInfo(_classDef); 277 if (info != null) { // 2.1: may also have different defaults for refs: 278 info = _annotationIntrospector.findObjectReferenceInfo(_classDef, info); 279 } 280 return info; 281 } 282 283 // for unit tests: getPropertyMap()284 protected Map<String, POJOPropertyBuilder> getPropertyMap() { 285 if (!_collected) { 286 collectAll(); 287 } 288 return _properties; 289 } 290 291 @Deprecated // since 2.9 getJsonValueMethod()292 public AnnotatedMethod getJsonValueMethod() { 293 AnnotatedMember m = getJsonValueAccessor(); 294 if (m instanceof AnnotatedMethod) { 295 return (AnnotatedMethod) m; 296 } 297 return null; 298 } 299 300 @Deprecated // since 2.11 (not used by anything at this point) findPOJOBuilderClass()301 public Class<?> findPOJOBuilderClass() { 302 return _annotationIntrospector.findPOJOBuilder(_classDef); 303 } 304 305 /* 306 /********************************************************** 307 /* Public API: main-level collection 308 /********************************************************** 309 */ 310 311 /** 312 * Internal method that will collect actual property information. 313 * 314 * @since 2.6 315 */ collectAll()316 protected void collectAll() 317 { 318 LinkedHashMap<String, POJOPropertyBuilder> props = new LinkedHashMap<String, POJOPropertyBuilder>(); 319 320 // First: gather basic data 321 _addFields(props); // note: populates _fieldRenameMappings 322 _addMethods(props); 323 // 25-Jan-2016, tatu: Avoid introspecting (constructor-)creators for non-static 324 // inner classes, see [databind#1502] 325 if (!_classDef.isNonStaticInnerClass()) { 326 _addCreators(props); 327 } 328 329 // Remove ignored properties, first; this MUST precede annotation merging 330 // since logic relies on knowing exactly which accessor has which annotation 331 _removeUnwantedProperties(props); 332 // and then remove unneeded accessors (wrt read-only, read-write) 333 _removeUnwantedAccessor(props); 334 335 // Rename remaining properties 336 _renameProperties(props); 337 338 // and now add injectables, but taking care to avoid overlapping ones 339 // via creator and regular properties 340 _addInjectables(props); 341 342 // then merge annotations, to simplify further processing 343 // 26-Sep-2017, tatu: Before 2.9.2 was done earlier but that prevented some of 344 // annotations from getting properly merged 345 for (POJOPropertyBuilder property : props.values()) { 346 property.mergeAnnotations(_forSerialization); 347 } 348 349 // And use custom naming strategy, if applicable... 350 PropertyNamingStrategy naming = _findNamingStrategy(); 351 if (naming != null) { 352 _renameUsing(props, naming); 353 } 354 355 // Sort by visibility (explicit over implicit); drop all but first of member 356 // type (getter, setter etc) if there is visibility difference 357 for (POJOPropertyBuilder property : props.values()) { 358 property.trimByVisibility(); 359 } 360 361 // and, if required, apply wrapper name: note, MUST be done after 362 // annotations are merged. 363 if (_config.isEnabled(MapperFeature.USE_WRAPPER_NAME_AS_PROPERTY_NAME)) { 364 _renameWithWrappers(props); 365 } 366 367 // well, almost last: there's still ordering... 368 _sortProperties(props); 369 _properties = props; 370 _collected = true; 371 } 372 373 /* 374 /********************************************************** 375 /* Overridable internal methods, adding members 376 /********************************************************** 377 */ 378 379 /** 380 * Method for collecting basic information on all fields found 381 */ _addFields(Map<String, POJOPropertyBuilder> props)382 protected void _addFields(Map<String, POJOPropertyBuilder> props) 383 { 384 final AnnotationIntrospector ai = _annotationIntrospector; 385 /* 28-Mar-2013, tatu: For deserialization we may also want to remove 386 * final fields, as often they won't make very good mutators... 387 * (although, maybe surprisingly, JVM _can_ force setting of such fields!) 388 */ 389 final boolean pruneFinalFields = !_forSerialization && !_config.isEnabled(MapperFeature.ALLOW_FINAL_FIELDS_AS_MUTATORS); 390 final boolean transientAsIgnoral = _config.isEnabled(MapperFeature.PROPAGATE_TRANSIENT_MARKER); 391 392 for (AnnotatedField f : _classDef.fields()) { 393 // @JsonValue? 394 if (Boolean.TRUE.equals(ai.hasAsValue(f))) { 395 if (_jsonValueAccessors == null) { 396 _jsonValueAccessors = new LinkedList<>(); 397 } 398 _jsonValueAccessors.add(f); 399 continue; 400 } 401 // @JsonAnySetter? 402 if (Boolean.TRUE.equals(ai.hasAnySetter(f))) { 403 if (_anySetterField == null) { 404 _anySetterField = new LinkedList<AnnotatedMember>(); 405 } 406 _anySetterField.add(f); 407 continue; 408 } 409 String implName = ai.findImplicitPropertyName(f); 410 if (implName == null) { 411 implName = f.getName(); 412 } 413 final PropertyName implNameP = _propNameFromSimple(implName); 414 415 // [databind#2527: Field-based renaming can be applied early (here), 416 // or at a later point, but probably must be done before pruning 417 // final fields. So let's do it early here 418 final PropertyName rename = ai.findRenameByField(_config, f, implNameP); 419 if ((rename != null) && !rename.equals(implNameP)) { 420 if (_fieldRenameMappings == null) { 421 _fieldRenameMappings = new HashMap<>(); 422 } 423 _fieldRenameMappings.put(rename, implNameP); 424 // todo 425 } 426 427 PropertyName pn; 428 429 if (_forSerialization) { 430 // 18-Aug-2011, tatu: As per existing unit tests, we should only 431 // use serialization annotation (@JsonSerialize) when serializing 432 // fields, and similarly for deserialize-only annotations... so 433 // no fallbacks in this particular case. 434 pn = ai.findNameForSerialization(f); 435 } else { 436 pn = ai.findNameForDeserialization(f); 437 } 438 boolean hasName = (pn != null); 439 boolean nameExplicit = hasName; 440 441 if (nameExplicit && pn.isEmpty()) { // empty String meaning "use default name", here just means "same as field name" 442 pn = _propNameFromSimple(implName); 443 nameExplicit = false; 444 } 445 // having explicit name means that field is visible; otherwise need to check the rules 446 boolean visible = (pn != null); 447 if (!visible) { 448 visible = _visibilityChecker.isFieldVisible(f); 449 } 450 // and finally, may also have explicit ignoral 451 boolean ignored = ai.hasIgnoreMarker(f); 452 453 // 13-May-2015, tatu: Moved from earlier place (AnnotatedClass) in 2.6 454 if (f.isTransient()) { 455 // 20-May-2016, tatu: as per [databind#1184] explicit annotation should override 456 // "default" `transient` 457 if (!hasName) { 458 visible = false; 459 if (transientAsIgnoral) { 460 ignored = true; 461 } 462 } 463 } 464 /* [databind#190]: this is the place to prune final fields, if they are not 465 * to be used as mutators. Must verify they are not explicitly included. 466 * Also: if 'ignored' is set, need to include until a later point, to 467 * avoid losing ignoral information. 468 */ 469 if (pruneFinalFields && (pn == null) && !ignored 470 && Modifier.isFinal(f.getModifiers())) { 471 continue; 472 } 473 _property(props, implName).addField(f, pn, nameExplicit, visible, ignored); 474 } 475 } 476 477 /** 478 * Method for collecting basic information on constructor(s) found 479 */ _addCreators(Map<String, POJOPropertyBuilder> props)480 protected void _addCreators(Map<String, POJOPropertyBuilder> props) 481 { 482 // can be null if annotation processing is disabled... 483 if (!_useAnnotations) { 484 return; 485 } 486 for (AnnotatedConstructor ctor : _classDef.getConstructors()) { 487 if (_creatorProperties == null) { 488 _creatorProperties = new LinkedList<POJOPropertyBuilder>(); 489 } 490 for (int i = 0, len = ctor.getParameterCount(); i < len; ++i) { 491 _addCreatorParam(props, ctor.getParameter(i)); 492 } 493 } 494 for (AnnotatedMethod factory : _classDef.getFactoryMethods()) { 495 if (_creatorProperties == null) { 496 _creatorProperties = new LinkedList<POJOPropertyBuilder>(); 497 } 498 for (int i = 0, len = factory.getParameterCount(); i < len; ++i) { 499 _addCreatorParam(props, factory.getParameter(i)); 500 } 501 } 502 } 503 504 /** 505 * @since 2.4 506 */ _addCreatorParam(Map<String, POJOPropertyBuilder> props, AnnotatedParameter param)507 protected void _addCreatorParam(Map<String, POJOPropertyBuilder> props, 508 AnnotatedParameter param) 509 { 510 // JDK 8, paranamer, Scala can give implicit name 511 String impl = _annotationIntrospector.findImplicitPropertyName(param); 512 if (impl == null) { 513 impl = ""; 514 } 515 PropertyName pn = _annotationIntrospector.findNameForDeserialization(param); 516 boolean expl = (pn != null && !pn.isEmpty()); 517 if (!expl) { 518 if (impl.isEmpty()) { 519 // Important: if neither implicit nor explicit name, cannot make use of 520 // this creator parameter -- may or may not be a problem, verified at a later point. 521 return; 522 } 523 // Also: if this occurs, there MUST be explicit annotation on creator itself 524 JsonCreator.Mode creatorMode = _annotationIntrospector.findCreatorAnnotation(_config, 525 param.getOwner()); 526 if ((creatorMode == null) || (creatorMode == JsonCreator.Mode.DISABLED)) { 527 return; 528 } 529 pn = PropertyName.construct(impl); 530 } 531 532 // 27-Dec-2019, tatu: [databind#2527] may need to rename according to field 533 impl = _checkRenameByField(impl); 534 535 // shouldn't need to worry about @JsonIgnore, since creators only added 536 // if so annotated 537 538 /* 13-May-2015, tatu: We should try to start with implicit name, similar to how 539 * fields and methods work; but unlike those, we don't necessarily have 540 * implicit name to use (pre-Java8 at least). So: 541 */ 542 POJOPropertyBuilder prop = (expl && impl.isEmpty()) 543 ? _property(props, pn) : _property(props, impl); 544 prop.addCtor(param, pn, expl, true, false); 545 _creatorProperties.add(prop); 546 } 547 548 /** 549 * Method for collecting basic information on all fields found 550 */ _addMethods(Map<String, POJOPropertyBuilder> props)551 protected void _addMethods(Map<String, POJOPropertyBuilder> props) 552 { 553 final AnnotationIntrospector ai = _annotationIntrospector; 554 for (AnnotatedMethod m : _classDef.memberMethods()) { 555 // For methods, handling differs between getters and setters; and 556 // we will also only consider entries that either follow the bean 557 // naming convention or are explicitly marked: just being visible 558 // is not enough (unlike with fields) 559 560 int argCount = m.getParameterCount(); 561 if (argCount == 0) { // getters (including 'any getter') 562 _addGetterMethod(props, m, ai); 563 } else if (argCount == 1) { // setters 564 _addSetterMethod(props, m, ai); 565 } else if (argCount == 2) { // any getter? 566 if (ai != null) { 567 if (Boolean.TRUE.equals(ai.hasAnySetter(m))) { 568 if (_anySetters == null) { 569 _anySetters = new LinkedList<AnnotatedMethod>(); 570 } 571 _anySetters.add(m); 572 } 573 } 574 } 575 } 576 } 577 _addGetterMethod(Map<String, POJOPropertyBuilder> props, AnnotatedMethod m, AnnotationIntrospector ai)578 protected void _addGetterMethod(Map<String, POJOPropertyBuilder> props, 579 AnnotatedMethod m, AnnotationIntrospector ai) 580 { 581 // Very first thing: skip if not returning any value 582 // 06-May-2020, tatu: [databind#2675] changes handling slightly... 583 { 584 final Class<?> rt = m.getRawReturnType(); 585 if ((rt == Void.TYPE) || 586 ((rt == Void.class) && !_config.isEnabled(MapperFeature.ALLOW_VOID_VALUED_PROPERTIES))) { 587 return; 588 } 589 } 590 591 // any getter? 592 // @JsonAnyGetter? 593 if (Boolean.TRUE.equals(ai.hasAnyGetter(m))) { 594 if (_anyGetters == null) { 595 _anyGetters = new LinkedList<AnnotatedMember>(); 596 } 597 _anyGetters.add(m); 598 return; 599 } 600 // @JsonValue? 601 if (Boolean.TRUE.equals(ai.hasAsValue(m))) { 602 if (_jsonValueAccessors == null) { 603 _jsonValueAccessors = new LinkedList<>(); 604 } 605 _jsonValueAccessors.add(m); 606 return; 607 } 608 String implName; // from naming convention 609 boolean visible; 610 611 PropertyName pn = ai.findNameForSerialization(m); 612 boolean nameExplicit = (pn != null); 613 614 if (!nameExplicit) { // no explicit name; must consider implicit 615 implName = ai.findImplicitPropertyName(m); 616 if (implName == null) { 617 implName = BeanUtil.okNameForRegularGetter(m, m.getName(), _stdBeanNaming); 618 } 619 if (implName == null) { // if not, must skip 620 implName = BeanUtil.okNameForIsGetter(m, m.getName(), _stdBeanNaming); 621 if (implName == null) { 622 return; 623 } 624 visible = _visibilityChecker.isIsGetterVisible(m); 625 } else { 626 visible = _visibilityChecker.isGetterVisible(m); 627 } 628 } else { // explicit indication of inclusion, but may be empty 629 // we still need implicit name to link with other pieces 630 implName = ai.findImplicitPropertyName(m); 631 if (implName == null) { 632 implName = BeanUtil.okNameForGetter(m, _stdBeanNaming); 633 } 634 // if not regular getter name, use method name as is 635 if (implName == null) { 636 implName = m.getName(); 637 } 638 if (pn.isEmpty()) { 639 // !!! TODO: use PropertyName for implicit names too 640 pn = _propNameFromSimple(implName); 641 nameExplicit = false; 642 } 643 visible = true; 644 } 645 // 27-Dec-2019, tatu: [databind#2527] may need to rename according to field 646 implName = _checkRenameByField(implName); 647 boolean ignore = ai.hasIgnoreMarker(m); 648 _property(props, implName).addGetter(m, pn, nameExplicit, visible, ignore); 649 } 650 _addSetterMethod(Map<String, POJOPropertyBuilder> props, AnnotatedMethod m, AnnotationIntrospector ai)651 protected void _addSetterMethod(Map<String, POJOPropertyBuilder> props, 652 AnnotatedMethod m, AnnotationIntrospector ai) 653 { 654 String implName; // from naming convention 655 boolean visible; 656 PropertyName pn = (ai == null) ? null : ai.findNameForDeserialization(m); 657 boolean nameExplicit = (pn != null); 658 if (!nameExplicit) { // no explicit name; must follow naming convention 659 implName = (ai == null) ? null : ai.findImplicitPropertyName(m); 660 if (implName == null) { 661 implName = BeanUtil.okNameForMutator(m, _mutatorPrefix, _stdBeanNaming); 662 } 663 if (implName == null) { // if not, must skip 664 return; 665 } 666 visible = _visibilityChecker.isSetterVisible(m); 667 } else { // explicit indication of inclusion, but may be empty 668 // we still need implicit name to link with other pieces 669 implName = (ai == null) ? null : ai.findImplicitPropertyName(m); 670 if (implName == null) { 671 implName = BeanUtil.okNameForMutator(m, _mutatorPrefix, _stdBeanNaming); 672 } 673 // if not regular getter name, use method name as is 674 if (implName == null) { 675 implName = m.getName(); 676 } 677 if (pn.isEmpty()) { 678 // !!! TODO: use PropertyName for implicit names too 679 pn = _propNameFromSimple(implName); 680 nameExplicit = false; 681 } 682 visible = true; 683 } 684 // 27-Dec-2019, tatu: [databind#2527] may need to rename according to field 685 implName = _checkRenameByField(implName); 686 boolean ignore = (ai == null) ? false : ai.hasIgnoreMarker(m); 687 _property(props, implName).addSetter(m, pn, nameExplicit, visible, ignore); 688 } 689 _addInjectables(Map<String, POJOPropertyBuilder> props)690 protected void _addInjectables(Map<String, POJOPropertyBuilder> props) 691 { 692 final AnnotationIntrospector ai = _annotationIntrospector; 693 // first fields, then methods, to allow overriding 694 for (AnnotatedField f : _classDef.fields()) { 695 _doAddInjectable(ai.findInjectableValue(f), f); 696 } 697 698 for (AnnotatedMethod m : _classDef.memberMethods()) { 699 // for now, only allow injection of a single arg (to be changed in future?) 700 if (m.getParameterCount() != 1) { 701 continue; 702 } 703 _doAddInjectable(ai.findInjectableValue(m), m); 704 } 705 } 706 _doAddInjectable(JacksonInject.Value injectable, AnnotatedMember m)707 protected void _doAddInjectable(JacksonInject.Value injectable, AnnotatedMember m) 708 { 709 if (injectable == null) { 710 return; 711 } 712 Object id = injectable.getId(); 713 if (_injectables == null) { 714 _injectables = new LinkedHashMap<Object, AnnotatedMember>(); 715 } 716 AnnotatedMember prev = _injectables.put(id, m); 717 if (prev != null) { 718 // 12-Apr-2017, tatu: Let's allow masking of Field by Method 719 if (prev.getClass() == m.getClass()) { 720 String type = id.getClass().getName(); 721 throw new IllegalArgumentException("Duplicate injectable value with id '" 722 +String.valueOf(id)+"' (of type "+type+")"); 723 } 724 } 725 } 726 _propNameFromSimple(String simpleName)727 private PropertyName _propNameFromSimple(String simpleName) { 728 return PropertyName.construct(simpleName, null); 729 } 730 731 // @since 2.11 _checkRenameByField(String implName)732 private String _checkRenameByField(String implName) { 733 if (_fieldRenameMappings != null) { 734 PropertyName p = _fieldRenameMappings.get(_propNameFromSimple(implName)); 735 if (p != null) { 736 implName = p.getSimpleName(); 737 return implName; 738 739 } 740 } 741 return implName; 742 } 743 744 /* 745 /********************************************************** 746 /* Internal methods; removing ignored properties 747 /********************************************************** 748 */ 749 750 /** 751 * Method called to get rid of candidate properties that are marked 752 * as ignored. 753 */ _removeUnwantedProperties(Map<String, POJOPropertyBuilder> props)754 protected void _removeUnwantedProperties(Map<String, POJOPropertyBuilder> props) 755 { 756 Iterator<POJOPropertyBuilder> it = props.values().iterator(); 757 while (it.hasNext()) { 758 POJOPropertyBuilder prop = it.next(); 759 760 // First: if nothing visible, just remove altogether 761 if (!prop.anyVisible()) { 762 it.remove(); 763 continue; 764 } 765 // Otherwise, check ignorals 766 if (prop.anyIgnorals()) { 767 // first: if one or more ignorals, and no explicit markers, remove the whole thing 768 if (!prop.isExplicitlyIncluded()) { 769 it.remove(); 770 _collectIgnorals(prop.getName()); 771 continue; 772 } 773 // otherwise just remove ones marked to be ignored 774 prop.removeIgnored(); 775 if (!prop.couldDeserialize()) { 776 _collectIgnorals(prop.getName()); 777 } 778 } 779 } 780 } 781 782 /** 783 * Method called to further get rid of unwanted individual accessors, 784 * based on read/write settings and rules for "pulling in" accessors 785 * (or not). 786 */ _removeUnwantedAccessor(Map<String, POJOPropertyBuilder> props)787 protected void _removeUnwantedAccessor(Map<String, POJOPropertyBuilder> props) 788 { 789 final boolean inferMutators = _config.isEnabled(MapperFeature.INFER_PROPERTY_MUTATORS); 790 Iterator<POJOPropertyBuilder> it = props.values().iterator(); 791 792 while (it.hasNext()) { 793 POJOPropertyBuilder prop = it.next(); 794 // 26-Jan-2017, tatu: [databind#935]: need to denote removal of 795 // 16-May-2020, tatu: [databind#2719]: need to pass `this` to allow 796 // addition of ignorals wrt explicit name 797 prop.removeNonVisible(inferMutators, _forSerialization ? null : this); 798 } 799 } 800 801 /** 802 * Helper method called to add explicitly ignored properties to a list 803 * of known ignored properties; this helps in proper reporting of 804 * errors. 805 */ _collectIgnorals(String name)806 protected void _collectIgnorals(String name) 807 { 808 if (!_forSerialization && (name != null)) { 809 if (_ignoredPropertyNames == null) { 810 _ignoredPropertyNames = new HashSet<String>(); 811 } 812 _ignoredPropertyNames.add(name); 813 } 814 } 815 816 /* 817 /********************************************************** 818 /* Internal methods; renaming properties 819 /********************************************************** 820 */ 821 _renameProperties(Map<String, POJOPropertyBuilder> props)822 protected void _renameProperties(Map<String, POJOPropertyBuilder> props) 823 { 824 // With renaming need to do in phases: first, find properties to rename 825 Iterator<Map.Entry<String,POJOPropertyBuilder>> it = props.entrySet().iterator(); 826 LinkedList<POJOPropertyBuilder> renamed = null; 827 while (it.hasNext()) { 828 Map.Entry<String, POJOPropertyBuilder> entry = it.next(); 829 POJOPropertyBuilder prop = entry.getValue(); 830 831 Collection<PropertyName> l = prop.findExplicitNames(); 832 833 // no explicit names? Implicit one is fine as is 834 if (l.isEmpty()) { 835 continue; 836 } 837 it.remove(); // need to replace with one or more renamed 838 if (renamed == null) { 839 renamed = new LinkedList<POJOPropertyBuilder>(); 840 } 841 // simple renaming? Just do it 842 if (l.size() == 1) { 843 PropertyName n = l.iterator().next(); 844 renamed.add(prop.withName(n)); 845 continue; 846 } 847 // but this may be problematic... 848 renamed.addAll(prop.explode(l)); 849 850 /* 851 String newName = prop.findNewName(); 852 if (newName != null) { 853 if (renamed == null) { 854 renamed = new LinkedList<POJOPropertyBuilder>(); 855 } 856 prop = prop.withSimpleName(newName); 857 renamed.add(prop); 858 it.remove(); 859 } 860 */ 861 } 862 863 // and if any were renamed, merge back in... 864 if (renamed != null) { 865 for (POJOPropertyBuilder prop : renamed) { 866 String name = prop.getName(); 867 POJOPropertyBuilder old = props.get(name); 868 if (old == null) { 869 props.put(name, prop); 870 } else { 871 old.addAll(prop); 872 } 873 // replace the creatorProperty too, if there is one 874 if (_updateCreatorProperty(prop, _creatorProperties)) { 875 // [databind#2001]: New name of property was ignored previously? Remove from ignored 876 // 01-May-2018, tatu: I have a feeling this will need to be revisited at some point, 877 // to avoid removing some types of removals, possibly. But will do for now. 878 879 // 16-May-2020, tatu: ... and so the day came, [databind#2118] failed 880 // when explicit rename added to ignorals (for READ_ONLY) was suddenly 881 // removed from ignoral list. So, added a guard statement above so that 882 // ignoral is ONLY removed if there was matching creator property. 883 // 884 // Chances are this is not the last tweak we need but... that bridge then etc 885 if (_ignoredPropertyNames != null) { 886 _ignoredPropertyNames.remove(name); 887 } 888 } 889 } 890 } 891 } 892 _renameUsing(Map<String, POJOPropertyBuilder> propMap, PropertyNamingStrategy naming)893 protected void _renameUsing(Map<String, POJOPropertyBuilder> propMap, 894 PropertyNamingStrategy naming) 895 { 896 POJOPropertyBuilder[] props = propMap.values().toArray(new POJOPropertyBuilder[propMap.size()]); 897 propMap.clear(); 898 for (POJOPropertyBuilder prop : props) { 899 PropertyName fullName = prop.getFullName(); 900 String rename = null; 901 // As per [databind#428] need to skip renaming if property has 902 // explicitly defined name, unless feature is enabled 903 if (!prop.isExplicitlyNamed() || _config.isEnabled(MapperFeature.ALLOW_EXPLICIT_PROPERTY_RENAMING)) { 904 if (_forSerialization) { 905 if (prop.hasGetter()) { 906 rename = naming.nameForGetterMethod(_config, prop.getGetter(), fullName.getSimpleName()); 907 } else if (prop.hasField()) { 908 rename = naming.nameForField(_config, prop.getField(), fullName.getSimpleName()); 909 } 910 } else { 911 if (prop.hasSetter()) { 912 rename = naming.nameForSetterMethod(_config, prop.getSetter(), fullName.getSimpleName()); 913 } else if (prop.hasConstructorParameter()) { 914 rename = naming.nameForConstructorParameter(_config, prop.getConstructorParameter(), fullName.getSimpleName()); 915 } else if (prop.hasField()) { 916 rename = naming.nameForField(_config, prop.getField(), fullName.getSimpleName()); 917 } else if (prop.hasGetter()) { 918 /* Plus, when getter-as-setter is used, need to convert that too.. 919 * (should we verify that's enabled? For now, assume it's ok always) 920 */ 921 rename = naming.nameForGetterMethod(_config, prop.getGetter(), fullName.getSimpleName()); 922 } 923 } 924 } 925 final String simpleName; 926 if ((rename != null) && !fullName.hasSimpleName(rename)) { 927 prop = prop.withSimpleName(rename); 928 simpleName = rename; 929 } else { 930 simpleName = fullName.getSimpleName(); 931 } 932 // Need to consider case where there may already be something in there... 933 POJOPropertyBuilder old = propMap.get(simpleName); 934 if (old == null) { 935 propMap.put(simpleName, prop); 936 } else { 937 old.addAll(prop); 938 } 939 940 // replace the creatorProperty too, if there is one 941 _updateCreatorProperty(prop, _creatorProperties); 942 } 943 } 944 _renameWithWrappers(Map<String, POJOPropertyBuilder> props)945 protected void _renameWithWrappers(Map<String, POJOPropertyBuilder> props) 946 { 947 // 11-Sep-2012, tatu: To support 'MapperFeature.USE_WRAPPER_NAME_AS_PROPERTY_NAME', 948 // need another round of renaming... 949 Iterator<Map.Entry<String,POJOPropertyBuilder>> it = props.entrySet().iterator(); 950 LinkedList<POJOPropertyBuilder> renamed = null; 951 while (it.hasNext()) { 952 Map.Entry<String, POJOPropertyBuilder> entry = it.next(); 953 POJOPropertyBuilder prop = entry.getValue(); 954 AnnotatedMember member = prop.getPrimaryMember(); 955 if (member == null) { 956 continue; 957 } 958 PropertyName wrapperName = _annotationIntrospector.findWrapperName(member); 959 // One trickier part (wrt [#24] of JAXB annotations: wrapper that 960 // indicates use of actual property... But hopefully has been taken care 961 // of previously 962 if (wrapperName == null || !wrapperName.hasSimpleName()) { 963 continue; 964 } 965 if (!wrapperName.equals(prop.getFullName())) { 966 if (renamed == null) { 967 renamed = new LinkedList<POJOPropertyBuilder>(); 968 } 969 prop = prop.withName(wrapperName); 970 renamed.add(prop); 971 it.remove(); 972 } 973 } 974 // and if any were renamed, merge back in... 975 if (renamed != null) { 976 for (POJOPropertyBuilder prop : renamed) { 977 String name = prop.getName(); 978 POJOPropertyBuilder old = props.get(name); 979 if (old == null) { 980 props.put(name, prop); 981 } else { 982 old.addAll(prop); 983 } 984 } 985 } 986 } 987 988 /* 989 /********************************************************** 990 /* Overridable internal methods, sorting, other stuff 991 /********************************************************** 992 */ 993 994 // First, order by(explicit ordering and/or alphabetic), 995 // then by (optional) index (if any) 996 // and then implicitly order creator properties before others) 997 _sortProperties(Map<String, POJOPropertyBuilder> props)998 protected void _sortProperties(Map<String, POJOPropertyBuilder> props) 999 { 1000 // Then how about explicit ordering? 1001 final AnnotationIntrospector intr = _annotationIntrospector; 1002 Boolean alpha = intr.findSerializationSortAlphabetically((Annotated) _classDef); 1003 final boolean sort = (alpha == null) 1004 ? _config.shouldSortPropertiesAlphabetically() 1005 : alpha.booleanValue(); 1006 final boolean indexed = _anyIndexed(props.values()); 1007 1008 String[] propertyOrder = intr.findSerializationPropertyOrder(_classDef); 1009 1010 // no sorting? no need to shuffle, then 1011 if (!sort && !indexed && (_creatorProperties == null) && (propertyOrder == null)) { 1012 return; 1013 } 1014 int size = props.size(); 1015 Map<String, POJOPropertyBuilder> all; 1016 // Need to (re)sort alphabetically? 1017 if (sort) { 1018 all = new TreeMap<String,POJOPropertyBuilder>(); 1019 } else { 1020 all = new LinkedHashMap<String,POJOPropertyBuilder>(size+size); 1021 } 1022 1023 for (POJOPropertyBuilder prop : props.values()) { 1024 all.put(prop.getName(), prop); 1025 } 1026 Map<String,POJOPropertyBuilder> ordered = new LinkedHashMap<>(size+size); 1027 // Ok: primarily by explicit order 1028 if (propertyOrder != null) { 1029 for (String name : propertyOrder) { 1030 POJOPropertyBuilder w = all.remove(name); 1031 if (w == null) { // will also allow use of "implicit" names for sorting 1032 for (POJOPropertyBuilder prop : props.values()) { 1033 if (name.equals(prop.getInternalName())) { 1034 w = prop; 1035 // plus re-map to external name, to avoid dups: 1036 name = prop.getName(); 1037 break; 1038 } 1039 } 1040 } 1041 if (w != null) { 1042 ordered.put(name, w); 1043 } 1044 } 1045 } 1046 1047 // Second (starting with 2.11): index, if any: 1048 if (indexed) { 1049 Map<Integer,POJOPropertyBuilder> byIndex = new TreeMap<>(); 1050 Iterator<Map.Entry<String,POJOPropertyBuilder>> it = all.entrySet().iterator(); 1051 while (it.hasNext()) { 1052 Map.Entry<String,POJOPropertyBuilder> entry = it.next(); 1053 POJOPropertyBuilder prop = entry.getValue(); 1054 Integer index = prop.getMetadata().getIndex(); 1055 if (index != null) { 1056 byIndex.put(index, prop); 1057 it.remove(); 1058 } 1059 } 1060 for (POJOPropertyBuilder prop : byIndex.values()) { 1061 ordered.put(prop.getName(), prop); 1062 } 1063 } 1064 1065 // Third by sorting Creator properties before other unordered properties 1066 if (_creatorProperties != null) { 1067 /* As per [databind#311], this is bit delicate; but if alphabetic ordering 1068 * is mandated, at least ensure creator properties are in alphabetic 1069 * order. Related question of creator vs non-creator is punted for now, 1070 * so creator properties still fully predate non-creator ones. 1071 */ 1072 Collection<POJOPropertyBuilder> cr; 1073 if (sort) { 1074 TreeMap<String, POJOPropertyBuilder> sorted = 1075 new TreeMap<String,POJOPropertyBuilder>(); 1076 for (POJOPropertyBuilder prop : _creatorProperties) { 1077 sorted.put(prop.getName(), prop); 1078 } 1079 cr = sorted.values(); 1080 } else { 1081 cr = _creatorProperties; 1082 } 1083 for (POJOPropertyBuilder prop : cr) { 1084 // 16-Jan-2016, tatu: Related to [databind#1317], make sure not to accidentally 1085 // add back pruned creator properties! 1086 String name = prop.getName(); 1087 // 27-Nov-2019, tatu: Not sure why, but we should NOT remove it from `all` tho: 1088 // if (all.remove(name) != null) { 1089 if (all.containsKey(name)) { 1090 ordered.put(name, prop); 1091 } 1092 } 1093 } 1094 // And finally whatever is left (trying to put again will not change ordering) 1095 ordered.putAll(all); 1096 props.clear(); 1097 props.putAll(ordered); 1098 } 1099 _anyIndexed(Collection<POJOPropertyBuilder> props)1100 private boolean _anyIndexed(Collection<POJOPropertyBuilder> props) { 1101 for (POJOPropertyBuilder prop : props) { 1102 if (prop.getMetadata().hasIndex()) { 1103 return true; 1104 } 1105 } 1106 return false; 1107 } 1108 1109 /* 1110 /********************************************************** 1111 /* Internal methods; helpers 1112 /********************************************************** 1113 */ 1114 reportProblem(String msg, Object... args)1115 protected void reportProblem(String msg, Object... args) { 1116 if (args.length > 0) { 1117 msg = String.format(msg, args); 1118 } 1119 throw new IllegalArgumentException("Problem with definition of "+_classDef+": "+msg); 1120 } 1121 _property(Map<String, POJOPropertyBuilder> props, PropertyName name)1122 protected POJOPropertyBuilder _property(Map<String, POJOPropertyBuilder> props, 1123 PropertyName name) { 1124 String simpleName = name.getSimpleName(); 1125 POJOPropertyBuilder prop = props.get(simpleName); 1126 if (prop == null) { 1127 prop = new POJOPropertyBuilder(_config, _annotationIntrospector, 1128 _forSerialization, name); 1129 props.put(simpleName, prop); 1130 } 1131 return prop; 1132 } 1133 1134 // !!! TODO: deprecate, require use of PropertyName _property(Map<String, POJOPropertyBuilder> props, String implName)1135 protected POJOPropertyBuilder _property(Map<String, POJOPropertyBuilder> props, 1136 String implName) 1137 { 1138 POJOPropertyBuilder prop = props.get(implName); 1139 if (prop == null) { 1140 prop = new POJOPropertyBuilder(_config, _annotationIntrospector, _forSerialization, 1141 PropertyName.construct(implName)); 1142 props.put(implName, prop); 1143 } 1144 return prop; 1145 } 1146 _findNamingStrategy()1147 private PropertyNamingStrategy _findNamingStrategy() 1148 { 1149 Object namingDef = _annotationIntrospector.findNamingStrategy(_classDef); 1150 if (namingDef == null) { 1151 return _config.getPropertyNamingStrategy(); 1152 } 1153 if (namingDef instanceof PropertyNamingStrategy) { 1154 return (PropertyNamingStrategy) namingDef; 1155 } 1156 /* Alas, there's no way to force return type of "either class 1157 * X or Y" -- need to throw an exception after the fact 1158 */ 1159 if (!(namingDef instanceof Class)) { 1160 throw new IllegalStateException("AnnotationIntrospector returned PropertyNamingStrategy definition of type " 1161 +namingDef.getClass().getName()+"; expected type PropertyNamingStrategy or Class<PropertyNamingStrategy> instead"); 1162 } 1163 Class<?> namingClass = (Class<?>)namingDef; 1164 // 09-Nov-2015, tatu: Need to consider pseudo-value of STD, which means "use default" 1165 if (namingClass == PropertyNamingStrategy.class) { 1166 return null; 1167 } 1168 1169 if (!PropertyNamingStrategy.class.isAssignableFrom(namingClass)) { 1170 throw new IllegalStateException("AnnotationIntrospector returned Class " 1171 +namingClass.getName()+"; expected Class<PropertyNamingStrategy>"); 1172 } 1173 HandlerInstantiator hi = _config.getHandlerInstantiator(); 1174 if (hi != null) { 1175 PropertyNamingStrategy pns = hi.namingStrategyInstance(_config, _classDef, namingClass); 1176 if (pns != null) { 1177 return pns; 1178 } 1179 } 1180 return (PropertyNamingStrategy) ClassUtil.createInstance(namingClass, 1181 _config.canOverrideAccessModifiers()); 1182 } 1183 _updateCreatorProperty(POJOPropertyBuilder prop, List<POJOPropertyBuilder> creatorProperties)1184 protected boolean _updateCreatorProperty(POJOPropertyBuilder prop, List<POJOPropertyBuilder> creatorProperties) { 1185 1186 if (creatorProperties != null) { 1187 final String intName = prop.getInternalName(); 1188 for (int i = 0, len = creatorProperties.size(); i < len; ++i) { 1189 if (creatorProperties.get(i).getInternalName().equals(intName)) { 1190 creatorProperties.set(i, prop); 1191 return true; 1192 } 1193 } 1194 } 1195 return false; 1196 } 1197 } 1198