1 package com.fasterxml.jackson.databind.introspect; 2 3 import java.lang.reflect.Constructor; 4 import java.lang.reflect.Method; 5 import java.util.*; 6 7 import com.fasterxml.jackson.annotation.JsonCreator; 8 import com.fasterxml.jackson.annotation.JsonFormat; 9 import com.fasterxml.jackson.annotation.JsonInclude; 10 11 import com.fasterxml.jackson.databind.*; 12 import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; 13 import com.fasterxml.jackson.databind.cfg.HandlerInstantiator; 14 import com.fasterxml.jackson.databind.cfg.MapperConfig; 15 import com.fasterxml.jackson.databind.type.TypeBindings; 16 import com.fasterxml.jackson.databind.util.Annotations; 17 import com.fasterxml.jackson.databind.util.ClassUtil; 18 import com.fasterxml.jackson.databind.util.Converter; 19 20 /** 21 * Default {@link BeanDescription} implementation used by Jackson. 22 *<p> 23 * Although sub-classing is a theoretical possibility there are no known 24 * use cases for that, nor is such usage tested or supported. 25 * Separation from API is mostly to isolate some implementation details 26 * here and keep API simple. 27 */ 28 public class BasicBeanDescription extends BeanDescription 29 { 30 // since 2.9 31 private final static Class<?>[] NO_VIEWS = new Class<?>[0]; 32 33 /* 34 /********************************************************** 35 /* General configuration 36 /********************************************************** 37 */ 38 39 /** 40 * We will hold a reference to the collector in cases where 41 * information is lazily accessed and constructed; properties 42 * are only accessed when they are actually needed. 43 */ 44 final protected POJOPropertiesCollector _propCollector; 45 46 final protected MapperConfig<?> _config; 47 48 final protected AnnotationIntrospector _annotationIntrospector; 49 50 /* 51 /********************************************************** 52 /* Information about type itself 53 /********************************************************** 54 */ 55 56 /** 57 * Information collected about the class introspected. 58 */ 59 final protected AnnotatedClass _classInfo; 60 61 /** 62 * @since 2.9 63 */ 64 protected Class<?>[] _defaultViews; 65 66 /** 67 * @since 2.9 68 */ 69 protected boolean _defaultViewsResolved; 70 71 /* 72 /********************************************************** 73 /* Member information 74 /********************************************************** 75 */ 76 77 /** 78 * Properties collected for the POJO; initialized as needed. 79 */ 80 protected List<BeanPropertyDefinition> _properties; 81 82 /** 83 * Details of Object Id to include, if any 84 */ 85 protected ObjectIdInfo _objectIdInfo; 86 87 /* 88 /********************************************************** 89 /* Life-cycle 90 /********************************************************** 91 */ 92 BasicBeanDescription(POJOPropertiesCollector coll, JavaType type, AnnotatedClass classDef)93 protected BasicBeanDescription(POJOPropertiesCollector coll, 94 JavaType type, AnnotatedClass classDef) 95 { 96 super(type); 97 _propCollector = coll; 98 _config = coll.getConfig(); 99 // NOTE: null config only for some pre-constructed types 100 if (_config == null) { 101 _annotationIntrospector = null; 102 } else { 103 _annotationIntrospector = _config.getAnnotationIntrospector(); 104 } 105 _classInfo = classDef; 106 } 107 108 /** 109 * Alternate constructor used in cases where property information is not needed, 110 * only class info. 111 */ BasicBeanDescription(MapperConfig<?> config, JavaType type, AnnotatedClass classDef, List<BeanPropertyDefinition> props)112 protected BasicBeanDescription(MapperConfig<?> config, 113 JavaType type, AnnotatedClass classDef, List<BeanPropertyDefinition> props) 114 { 115 super(type); 116 _propCollector = null; 117 _config = config; 118 // NOTE: null config only for some pre-constructed types 119 if (_config == null) { 120 _annotationIntrospector = null; 121 } else { 122 _annotationIntrospector = _config.getAnnotationIntrospector(); 123 } 124 _classInfo = classDef; 125 _properties = props; 126 } 127 BasicBeanDescription(POJOPropertiesCollector coll)128 protected BasicBeanDescription(POJOPropertiesCollector coll) 129 { 130 this(coll, coll.getType(), coll.getClassDef()); 131 _objectIdInfo = coll.getObjectIdInfo(); 132 } 133 134 /** 135 * Factory method to use for constructing an instance to use for building 136 * deserializers. 137 */ forDeserialization(POJOPropertiesCollector coll)138 public static BasicBeanDescription forDeserialization(POJOPropertiesCollector coll) { 139 return new BasicBeanDescription(coll); 140 } 141 142 /** 143 * Factory method to use for constructing an instance to use for building 144 * serializers. 145 */ forSerialization(POJOPropertiesCollector coll)146 public static BasicBeanDescription forSerialization(POJOPropertiesCollector coll) { 147 return new BasicBeanDescription(coll); 148 } 149 150 /** 151 * Factory method to use for constructing an instance to use for purposes 152 * other than building serializers or deserializers; will only have information 153 * on class, not on properties. 154 */ forOtherUse(MapperConfig<?> config, JavaType type, AnnotatedClass ac)155 public static BasicBeanDescription forOtherUse(MapperConfig<?> config, 156 JavaType type, AnnotatedClass ac) 157 { 158 return new BasicBeanDescription(config, type, 159 ac, Collections.<BeanPropertyDefinition>emptyList()); 160 } 161 _properties()162 protected List<BeanPropertyDefinition> _properties() { 163 if (_properties == null) { 164 _properties = _propCollector.getProperties(); 165 } 166 return _properties; 167 } 168 169 /* 170 /********************************************************** 171 /* Limited modifications by core databind functionality 172 /********************************************************** 173 */ 174 175 /** 176 * Method that can be used to prune unwanted properties, during 177 * construction of serializers and deserializers. 178 * Use with utmost care, if at all... 179 * 180 * @since 2.1 181 */ removeProperty(String propName)182 public boolean removeProperty(String propName) 183 { 184 Iterator<BeanPropertyDefinition> it = _properties().iterator(); 185 while (it.hasNext()) { 186 BeanPropertyDefinition prop = it.next(); 187 if (prop.getName().equals(propName)) { 188 it.remove(); 189 return true; 190 } 191 } 192 return false; 193 } 194 addProperty(BeanPropertyDefinition def)195 public boolean addProperty(BeanPropertyDefinition def) 196 { 197 // first: ensure we do not have such property 198 if (hasProperty(def.getFullName())) { 199 return false; 200 } 201 _properties().add(def); 202 return true; 203 } 204 205 /** 206 * @since 2.6 207 */ hasProperty(PropertyName name)208 public boolean hasProperty(PropertyName name) { 209 return findProperty(name) != null; 210 } 211 212 /** 213 * @since 2.6 214 */ findProperty(PropertyName name)215 public BeanPropertyDefinition findProperty(PropertyName name) 216 { 217 for (BeanPropertyDefinition prop : _properties()) { 218 if (prop.hasName(name)) { 219 return prop; 220 } 221 } 222 return null; 223 } 224 225 /* 226 /********************************************************** 227 /* Simple accessors from BeanDescription 228 /********************************************************** 229 */ 230 231 @Override getClassInfo()232 public AnnotatedClass getClassInfo() { return _classInfo; } 233 234 @Override getObjectIdInfo()235 public ObjectIdInfo getObjectIdInfo() { return _objectIdInfo; } 236 237 @Override findProperties()238 public List<BeanPropertyDefinition> findProperties() { 239 return _properties(); 240 } 241 242 @Override 243 @Deprecated // since 2.9 findJsonValueMethod()244 public AnnotatedMethod findJsonValueMethod() { 245 return (_propCollector == null) ? null 246 : _propCollector.getJsonValueMethod(); 247 } 248 249 @Override // since 2.9 findJsonValueAccessor()250 public AnnotatedMember findJsonValueAccessor() { 251 return (_propCollector == null) ? null 252 : _propCollector.getJsonValueAccessor(); 253 } 254 255 @Override getIgnoredPropertyNames()256 public Set<String> getIgnoredPropertyNames() { 257 Set<String> ign = (_propCollector == null) ? null 258 : _propCollector.getIgnoredPropertyNames(); 259 if (ign == null) { 260 return Collections.emptySet(); 261 } 262 return ign; 263 } 264 265 @Override hasKnownClassAnnotations()266 public boolean hasKnownClassAnnotations() { 267 return _classInfo.hasAnnotations(); 268 } 269 270 @Override getClassAnnotations()271 public Annotations getClassAnnotations() { 272 return _classInfo.getAnnotations(); 273 } 274 275 @Override 276 @Deprecated // since 2.7 bindingsForBeanType()277 public TypeBindings bindingsForBeanType() { 278 return _type.getBindings(); 279 } 280 281 @Override 282 @Deprecated // since 2.8 resolveType(java.lang.reflect.Type jdkType)283 public JavaType resolveType(java.lang.reflect.Type jdkType) { 284 if (jdkType == null) { 285 return null; 286 } 287 return _config.getTypeFactory().constructType(jdkType, _type.getBindings()); 288 } 289 290 @Override findDefaultConstructor()291 public AnnotatedConstructor findDefaultConstructor() { 292 return _classInfo.getDefaultConstructor(); 293 } 294 295 @Override findAnySetterAccessor()296 public AnnotatedMember findAnySetterAccessor() throws IllegalArgumentException 297 { 298 if (_propCollector != null) { 299 AnnotatedMethod anyMethod = _propCollector.getAnySetterMethod(); 300 if (anyMethod != null) { 301 // Also, let's be somewhat strict on how field name is to be 302 // passed; String, Object make sense, others not so much. 303 304 /* !!! 18-May-2009, tatu: how about enums? Can add support if 305 * requested; easy enough for devs to add support within method. 306 */ 307 Class<?> type = anyMethod.getRawParameterType(0); 308 if ((type != String.class) && (type != Object.class)) { 309 throw new IllegalArgumentException(String.format( 310 "Invalid 'any-setter' annotation on method '%s()': first argument not of type String or Object, but %s", 311 anyMethod.getName(), type.getName())); 312 } 313 return anyMethod; 314 } 315 AnnotatedMember anyField = _propCollector.getAnySetterField(); 316 if (anyField != null) { 317 // For now let's require a Map; in future can add support for other 318 // types like perhaps Iterable<Map.Entry>? 319 Class<?> type = anyField.getRawType(); 320 if (!Map.class.isAssignableFrom(type)) { 321 throw new IllegalArgumentException(String.format( 322 "Invalid 'any-setter' annotation on field '%s': type is not instance of java.util.Map", 323 anyField.getName())); 324 } 325 return anyField; 326 } 327 } 328 return null; 329 } 330 331 @Override findInjectables()332 public Map<Object, AnnotatedMember> findInjectables() { 333 if (_propCollector != null) { 334 return _propCollector.getInjectables(); 335 } 336 return Collections.emptyMap(); 337 } 338 339 @Override getConstructors()340 public List<AnnotatedConstructor> getConstructors() { 341 return _classInfo.getConstructors(); 342 } 343 344 @Override instantiateBean(boolean fixAccess)345 public Object instantiateBean(boolean fixAccess) { 346 AnnotatedConstructor ac = _classInfo.getDefaultConstructor(); 347 if (ac == null) { 348 return null; 349 } 350 if (fixAccess) { 351 ac.fixAccess(_config.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS)); 352 } 353 try { 354 return ac.getAnnotated().newInstance(); 355 } catch (Exception e) { 356 Throwable t = e; 357 while (t.getCause() != null) { 358 t = t.getCause(); 359 } 360 ClassUtil.throwIfError(t); 361 ClassUtil.throwIfRTE(t); 362 throw new IllegalArgumentException("Failed to instantiate bean of type " 363 +_classInfo.getAnnotated().getName()+": ("+t.getClass().getName()+") " 364 +ClassUtil.exceptionMessage(t), t); 365 } 366 } 367 368 /* 369 /********************************************************** 370 /* Simple accessors, extended 371 /********************************************************** 372 */ 373 374 @Override findMethod(String name, Class<?>[] paramTypes)375 public AnnotatedMethod findMethod(String name, Class<?>[] paramTypes) { 376 return _classInfo.findMethod(name, paramTypes); 377 } 378 379 /* 380 /********************************************************** 381 /* General per-class annotation introspection 382 /********************************************************** 383 */ 384 385 @Override findExpectedFormat(JsonFormat.Value defValue)386 public JsonFormat.Value findExpectedFormat(JsonFormat.Value defValue) 387 { 388 // 15-Apr-2016, tatu: Let's check both per-type defaults and annotations; per-type 389 // defaults having higher precedence, so start with that 390 if (_annotationIntrospector != null) { 391 JsonFormat.Value v = _annotationIntrospector.findFormat(_classInfo); 392 if (v != null) { 393 if (defValue == null) { 394 defValue = v; 395 } else { 396 defValue = defValue.withOverrides(v); 397 } 398 } 399 } 400 JsonFormat.Value v = _config.getDefaultPropertyFormat(_classInfo.getRawType()); 401 if (v != null) { 402 if (defValue == null) { 403 defValue = v; 404 } else { 405 defValue = defValue.withOverrides(v); 406 } 407 } 408 return defValue; 409 } 410 411 @Override // since 2.9 findDefaultViews()412 public Class<?>[] findDefaultViews() 413 { 414 if (!_defaultViewsResolved) { 415 _defaultViewsResolved = true; 416 Class<?>[] def = (_annotationIntrospector == null) ? null 417 : _annotationIntrospector.findViews(_classInfo); 418 // one more twist: if default inclusion disabled, need to force empty set of views 419 if (def == null) { 420 if (!_config.isEnabled(MapperFeature.DEFAULT_VIEW_INCLUSION)) { 421 def = NO_VIEWS; 422 } 423 } 424 _defaultViews = def; 425 } 426 return _defaultViews; 427 } 428 429 /* 430 /********************************************************** 431 /* Introspection for serialization 432 /********************************************************** 433 */ 434 435 @Override findSerializationConverter()436 public Converter<Object,Object> findSerializationConverter() 437 { 438 if (_annotationIntrospector == null) { 439 return null; 440 } 441 return _createConverter(_annotationIntrospector.findSerializationConverter(_classInfo)); 442 } 443 444 /** 445 * Method for determining whether null properties should be written 446 * out for a Bean of introspected type. This is based on global 447 * feature (lowest priority, passed as argument) 448 * and per-class annotation (highest priority). 449 */ 450 @Override findPropertyInclusion(JsonInclude.Value defValue)451 public JsonInclude.Value findPropertyInclusion(JsonInclude.Value defValue) { 452 if (_annotationIntrospector != null) { 453 JsonInclude.Value incl = _annotationIntrospector.findPropertyInclusion(_classInfo); 454 if (incl != null) { 455 return (defValue == null) ? incl : defValue.withOverrides(incl); 456 } 457 } 458 return defValue; 459 } 460 461 /** 462 * Method used to locate the method of introspected class that 463 * implements {@link com.fasterxml.jackson.annotation.JsonAnyGetter}. 464 * If no such method exists null is returned. 465 * If more than one are found, an exception is thrown. 466 */ 467 @Override findAnyGetter()468 public AnnotatedMember findAnyGetter() throws IllegalArgumentException 469 { 470 AnnotatedMember anyGetter = (_propCollector == null) ? null 471 : _propCollector.getAnyGetter(); 472 if (anyGetter != null) { 473 /* For now let's require a Map; in future can add support for other 474 * types like perhaps Iterable<Map.Entry>? 475 */ 476 Class<?> type = anyGetter.getRawType(); 477 if (!Map.class.isAssignableFrom(type)) { 478 throw new IllegalArgumentException("Invalid 'any-getter' annotation on method "+anyGetter.getName()+"(): return type is not instance of java.util.Map"); 479 } 480 } 481 return anyGetter; 482 } 483 484 @Override findBackReferences()485 public List<BeanPropertyDefinition> findBackReferences() 486 { 487 List<BeanPropertyDefinition> result = null; 488 HashSet<String> names = null; 489 for (BeanPropertyDefinition property : _properties()) { 490 AnnotationIntrospector.ReferenceProperty refDef = property.findReferenceType(); 491 if ((refDef == null) || !refDef.isBackReference()) { 492 continue; 493 } 494 final String refName = refDef.getName(); 495 if (result == null) { 496 result = new ArrayList<BeanPropertyDefinition>(); 497 names = new HashSet<>(); 498 names.add(refName); 499 } else { 500 if (!names.add(refName)) { 501 throw new IllegalArgumentException("Multiple back-reference properties with name '"+refName+"'"); 502 } 503 } 504 result.add(property); 505 } 506 return result; 507 } 508 509 @Deprecated // since 2.9 510 @Override findBackReferenceProperties()511 public Map<String,AnnotatedMember> findBackReferenceProperties() 512 { 513 List<BeanPropertyDefinition> props = findBackReferences(); 514 if (props == null) { 515 return null; 516 } 517 Map<String,AnnotatedMember> result = new HashMap<>(); 518 for (BeanPropertyDefinition prop : props) { 519 result.put(prop.getName(), prop.getMutator()); 520 } 521 return result; 522 } 523 524 /* 525 /********************************************************** 526 /* Introspection for deserialization, factories 527 /********************************************************** 528 */ 529 530 @Override getFactoryMethods()531 public List<AnnotatedMethod> getFactoryMethods() 532 { 533 // must filter out anything that clearly is not a factory method 534 List<AnnotatedMethod> candidates = _classInfo.getFactoryMethods(); 535 if (candidates.isEmpty()) { 536 return candidates; 537 } 538 List<AnnotatedMethod> result = null; 539 for (AnnotatedMethod am : candidates) { 540 if (isFactoryMethod(am)) { 541 if (result == null) { 542 result = new ArrayList<AnnotatedMethod>(); 543 } 544 result.add(am); 545 } 546 } 547 if (result == null) { 548 return Collections.emptyList(); 549 } 550 return result; 551 } 552 553 @Override findSingleArgConstructor(Class<?>.... argTypes)554 public Constructor<?> findSingleArgConstructor(Class<?>... argTypes) 555 { 556 for (AnnotatedConstructor ac : _classInfo.getConstructors()) { 557 // This list is already filtered to only include accessible 558 /* (note: for now this is a redundant check; but in future 559 * that may change; thus leaving here for now) 560 */ 561 if (ac.getParameterCount() == 1) { 562 Class<?> actArg = ac.getRawParameterType(0); 563 for (Class<?> expArg : argTypes) { 564 if (expArg == actArg) { 565 return ac.getAnnotated(); 566 } 567 } 568 } 569 } 570 return null; 571 } 572 573 @Override findFactoryMethod(Class<?>.... expArgTypes)574 public Method findFactoryMethod(Class<?>... expArgTypes) 575 { 576 // So, of all single-arg static methods: 577 for (AnnotatedMethod am : _classInfo.getFactoryMethods()) { 578 // 24-Oct-2016, tatu: Better ensure it only takes 1 arg, no matter what 579 if (isFactoryMethod(am) && am.getParameterCount() == 1) { 580 // And must take one of expected arg types (or supertype) 581 Class<?> actualArgType = am.getRawParameterType(0); 582 for (Class<?> expArgType : expArgTypes) { 583 // And one that matches what we would pass in 584 if (actualArgType.isAssignableFrom(expArgType)) { 585 return am.getAnnotated(); 586 } 587 } 588 } 589 } 590 return null; 591 } 592 isFactoryMethod(AnnotatedMethod am)593 protected boolean isFactoryMethod(AnnotatedMethod am) 594 { 595 // First: return type must be compatible with the introspected class 596 // (i.e. allowed to be sub-class, although usually is the same class) 597 Class<?> rt = am.getRawReturnType(); 598 if (!getBeanClass().isAssignableFrom(rt)) { 599 return false; 600 } 601 /* Also: must be a recognized factory method, meaning: 602 * (a) marked with @JsonCreator annotation, or 603 * (b) "valueOf" (at this point, need not be public) 604 */ 605 JsonCreator.Mode mode = _annotationIntrospector.findCreatorAnnotation(_config, am); 606 if ((mode != null) && (mode != JsonCreator.Mode.DISABLED)) { 607 return true; 608 } 609 final String name = am.getName(); 610 // 24-Oct-2016, tatu: As per [databind#1429] must ensure takes exactly one arg 611 if ("valueOf".equals(name)) { 612 if (am.getParameterCount() == 1) { 613 return true; 614 } 615 } 616 // [databind#208] Also accept "fromString()", if takes String or CharSequence 617 if ("fromString".equals(name)) { 618 if (am.getParameterCount() == 1) { 619 Class<?> cls = am.getRawParameterType(0); 620 if (cls == String.class || CharSequence.class.isAssignableFrom(cls)) { 621 return true; 622 } 623 } 624 } 625 return false; 626 } 627 628 /** 629 * @deprecated since 2.8 630 */ 631 @Deprecated // since 2.8, not used at least since 2.7 _findCreatorPropertyName(AnnotatedParameter param)632 protected PropertyName _findCreatorPropertyName(AnnotatedParameter param) 633 { 634 PropertyName name = _annotationIntrospector.findNameForDeserialization(param); 635 if (name == null || name.isEmpty()) { 636 String str = _annotationIntrospector.findImplicitPropertyName(param); 637 if (str != null && !str.isEmpty()) { 638 name = PropertyName.construct(str); 639 } 640 } 641 return name; 642 } 643 644 /* 645 /********************************************************** 646 /* Introspection for deserialization, other 647 /********************************************************** 648 */ 649 650 @Override findPOJOBuilder()651 public Class<?> findPOJOBuilder() { 652 return (_annotationIntrospector == null) ? 653 null : _annotationIntrospector.findPOJOBuilder(_classInfo); 654 } 655 656 @Override findPOJOBuilderConfig()657 public JsonPOJOBuilder.Value findPOJOBuilderConfig() 658 { 659 return (_annotationIntrospector == null) ? 660 null : _annotationIntrospector.findPOJOBuilderConfig(_classInfo); 661 } 662 663 @Override findDeserializationConverter()664 public Converter<Object,Object> findDeserializationConverter() 665 { 666 if (_annotationIntrospector == null) { 667 return null; 668 } 669 return _createConverter(_annotationIntrospector.findDeserializationConverter(_classInfo)); 670 } 671 672 @Override findClassDescription()673 public String findClassDescription() { 674 return (_annotationIntrospector == null) ? 675 null : _annotationIntrospector.findClassDescription(_classInfo); 676 } 677 678 /* 679 /********************************************************** 680 /* Helper methods for field introspection 681 /********************************************************** 682 */ 683 684 /** 685 * @param ignoredProperties (optional) names of properties to ignore; 686 * any fields that would be recognized as one of these properties 687 * is ignored. 688 * @param forSerialization If true, will collect serializable property 689 * fields; if false, deserializable 690 * 691 * @return Ordered Map with logical property name as key, and 692 * matching field as value. 693 * 694 * @deprecated Since 2.7.2, does not seem to be used? 695 */ 696 @Deprecated _findPropertyFields( Collection<String> ignoredProperties, boolean forSerialization)697 public LinkedHashMap<String,AnnotatedField> _findPropertyFields( 698 Collection<String> ignoredProperties, boolean forSerialization) 699 { 700 LinkedHashMap<String,AnnotatedField> results = new LinkedHashMap<String,AnnotatedField>(); 701 for (BeanPropertyDefinition property : _properties()) { 702 AnnotatedField f = property.getField(); 703 if (f != null) { 704 String name = property.getName(); 705 if (ignoredProperties != null) { 706 if (ignoredProperties.contains(name)) { 707 continue; 708 } 709 } 710 results.put(name, f); 711 } 712 } 713 return results; 714 } 715 716 /* 717 /********************************************************** 718 /* Helper methods, other 719 /********************************************************** 720 */ 721 722 @SuppressWarnings("unchecked") _createConverter(Object converterDef)723 protected Converter<Object,Object> _createConverter(Object converterDef) 724 { 725 if (converterDef == null) { 726 return null; 727 } 728 if (converterDef instanceof Converter<?,?>) { 729 return (Converter<Object,Object>) converterDef; 730 } 731 if (!(converterDef instanceof Class)) { 732 throw new IllegalStateException("AnnotationIntrospector returned Converter definition of type " 733 +converterDef.getClass().getName()+"; expected type Converter or Class<Converter> instead"); 734 } 735 Class<?> converterClass = (Class<?>)converterDef; 736 // there are some known "no class" markers to consider too: 737 if (converterClass == Converter.None.class || ClassUtil.isBogusClass(converterClass)) { 738 return null; 739 } 740 if (!Converter.class.isAssignableFrom(converterClass)) { 741 throw new IllegalStateException("AnnotationIntrospector returned Class " 742 +converterClass.getName()+"; expected Class<Converter>"); 743 } 744 HandlerInstantiator hi = _config.getHandlerInstantiator(); 745 Converter<?,?> conv = (hi == null) ? null : hi.converterInstance(_config, _classInfo, converterClass); 746 if (conv == null) { 747 conv = (Converter<?,?>) ClassUtil.createInstance(converterClass, 748 _config.canOverrideAccessModifiers()); 749 } 750 return (Converter<Object,Object>) conv; 751 } 752 } 753