/** Method that will construct a regular bean property setter using the given setter method. */
  protected SettableBeanProperty constructSetterlessProperty(
      DeserializationContext ctxt, BeanDescription beanDesc, BeanPropertyDefinition propDef)
      throws JsonMappingException {
    final AnnotatedMethod getter = propDef.getGetter();
    // need to ensure it is callable now:
    if (ctxt.canOverrideAccessModifiers()) {
      getter.fixAccess();
    }

    /* 26-Jan-2012, tatu: Alas, this complication is still needed to handle
     *   (or at least work around) local type declarations...
     */
    JavaType type = getter.getType(beanDesc.bindingsForBeanType());
    /* First: does the Method specify the deserializer to use?
     * If so, let's use it.
     */
    JsonDeserializer<Object> propDeser = findDeserializerFromAnnotation(ctxt, getter);
    type = modifyTypeByAnnotation(ctxt, getter, type);
    TypeDeserializer typeDeser = type.getTypeHandler();
    SettableBeanProperty prop =
        new SetterlessProperty(propDef, type, typeDeser, beanDesc.getClassAnnotations(), getter);
    if (propDeser != null) {
      prop = prop.withValueDeserializer(propDeser);
    }
    return prop;
  }
  /**
   * Method that will construct a regular bean property setter using the given setter method.
   *
   * @return Property constructed, if any; or null to indicate that there should be no property
   *     based on given definitions.
   */
  protected SettableBeanProperty constructSettableProperty(
      DeserializationContext ctxt,
      BeanDescription beanDesc,
      BeanPropertyDefinition propDef,
      Type jdkType)
      throws JsonMappingException {
    // need to ensure method is callable (for non-public)
    AnnotatedMember mutator = propDef.getMutator();
    if (ctxt.canOverrideAccessModifiers()) {
      mutator.fixAccess();
    }
    // note: this works since we know there's exactly one argument for methods
    JavaType t0 = beanDesc.resolveType(jdkType);

    BeanProperty.Std property =
        new BeanProperty.Std(propDef.getName(), t0, beanDesc.getClassAnnotations(), mutator);
    JavaType type = resolveType(ctxt, beanDesc, t0, mutator);
    // did type change?
    if (type != t0) {
      property = property.withType(type);
    }

    /* First: does the Method specify the deserializer to use?
     * If so, let's use it.
     */
    JsonDeserializer<Object> propDeser = findDeserializerFromAnnotation(ctxt, mutator);
    type = modifyTypeByAnnotation(ctxt, mutator, type);
    TypeDeserializer typeDeser = type.getTypeHandler();
    SettableBeanProperty prop;
    if (mutator instanceof AnnotatedMethod) {
      prop =
          new MethodProperty(
              propDef, type, typeDeser, beanDesc.getClassAnnotations(), (AnnotatedMethod) mutator);
    } else {
      prop =
          new FieldProperty(
              propDef, type, typeDeser, beanDesc.getClassAnnotations(), (AnnotatedField) mutator);
    }
    if (propDeser != null) {
      prop = prop.withValueDeserializer(propDeser);
    }
    // [JACKSON-235]: need to retain name of managed forward references:
    AnnotationIntrospector.ReferenceProperty ref = propDef.findReferenceType();
    if (ref != null && ref.isManagedReference()) {
      prop.setManagedReferenceName(ref.getName());
    }
    return prop;
  }
  protected void addObjectIdReader(
      DeserializationContext ctxt, BeanDescription beanDesc, BeanDeserializerBuilder builder)
      throws JsonMappingException {
    ObjectIdInfo objectIdInfo = beanDesc.getObjectIdInfo();
    if (objectIdInfo == null) {
      return;
    }
    Class<?> implClass = objectIdInfo.getGeneratorType();
    JavaType idType;
    SettableBeanProperty idProp;
    ObjectIdGenerator<?> gen;

    // Just one special case: Property-based generator is trickier
    if (implClass
        == ObjectIdGenerators.PropertyGenerator.class) { // most special one, needs extra work
      String propName = objectIdInfo.getPropertyName();
      idProp = builder.findProperty(propName);
      if (idProp == null) {
        throw new IllegalArgumentException(
            "Invalid Object Id definition for "
                + beanDesc.getBeanClass().getName()
                + ": can not find property with name '"
                + propName
                + "'");
      }
      idType = idProp.getType();
      gen = new PropertyBasedObjectIdGenerator(objectIdInfo.getScope());
    } else {
      JavaType type = ctxt.constructType(implClass);
      idType = ctxt.getTypeFactory().findTypeParameters(type, ObjectIdGenerator.class)[0];
      idProp = null;
      gen = ctxt.objectIdGeneratorInstance(beanDesc.getClassInfo(), objectIdInfo);
    }
    // also: unlike with value deserializers, let's just resolve one we need here
    JsonDeserializer<?> deser = ctxt.findRootValueDeserializer(idType);
    builder.setObjectIdReader(
        ObjectIdReader.construct(idType, objectIdInfo.getPropertyName(), gen, deser, idProp));
  }
  /**
   * Method called to figure out settable properties for the bean deserializer to use.
   *
   * <p>Note: designed to be overridable, and effort is made to keep interface similar between
   * versions.
   */
  protected void addBeanProps(
      DeserializationContext ctxt, BeanDescription beanDesc, BeanDeserializerBuilder builder)
      throws JsonMappingException {
    final SettableBeanProperty[] creatorProps =
        builder.getValueInstantiator().getFromObjectArguments(ctxt.getConfig());

    // Things specified as "ok to ignore"? [JACKSON-77]
    AnnotationIntrospector intr = ctxt.getAnnotationIntrospector();
    boolean ignoreAny = false;
    {
      Boolean B = intr.findIgnoreUnknownProperties(beanDesc.getClassInfo());
      if (B != null) {
        ignoreAny = B.booleanValue();
        builder.setIgnoreUnknownProperties(ignoreAny);
      }
    }
    // Or explicit/implicit definitions?
    Set<String> ignored =
        ArrayBuilders.arrayToSet(intr.findPropertiesToIgnore(beanDesc.getClassInfo()));
    for (String propName : ignored) {
      builder.addIgnorable(propName);
    }
    // Also, do we have a fallback "any" setter?
    AnnotatedMethod anySetter = beanDesc.findAnySetter();
    if (anySetter != null) {
      builder.setAnySetter(constructAnySetter(ctxt, beanDesc, anySetter));
    }
    // NOTE: we do NOT add @JsonIgnore'd properties into blocked ones if there's any setter
    // Implicit ones via @JsonIgnore and equivalent?
    if (anySetter == null) {
      Collection<String> ignored2 = beanDesc.getIgnoredPropertyNames();
      if (ignored2 != null) {
        for (String propName : ignored2) {
          // allow ignoral of similarly named JSON property, but do not force;
          // latter means NOT adding this to 'ignored':
          builder.addIgnorable(propName);
        }
      }
    }
    final boolean useGettersAsSetters =
        (ctxt.isEnabled(MapperFeature.USE_GETTERS_AS_SETTERS)
            && ctxt.isEnabled(MapperFeature.AUTO_DETECT_GETTERS));

    // Ok: let's then filter out property definitions
    List<BeanPropertyDefinition> propDefs =
        filterBeanProps(ctxt, beanDesc, builder, beanDesc.findProperties(), ignored);

    // After which we can let custom code change the set
    if (_factoryConfig.hasDeserializerModifiers()) {
      for (BeanDeserializerModifier mod : _factoryConfig.deserializerModifiers()) {
        propDefs = mod.updateProperties(ctxt.getConfig(), beanDesc, propDefs);
      }
    }

    // At which point we still have all kinds of properties; not all with mutators:
    for (BeanPropertyDefinition propDef : propDefs) {
      SettableBeanProperty prop = null;
      if (propDef.hasConstructorParameter()) {
        /* [JACKSON-700] If property is passed via constructor parameter, we must
         *   handle things in special way. Not sure what is the most optimal way...
         *   for now, let's just call a (new) method in builder, which does nothing.
         */
        // but let's call a method just to allow custom builders to be aware...
        final String name = propDef.getName();
        for (SettableBeanProperty cp : creatorProps) {
          if (name.equals(cp.getName())) {
            prop = cp;
            break;
          }
        }
        if (prop == null) {
          throw ctxt.mappingException("Could not find creator property with name '" + name + "'");
        }
        builder.addCreatorProperty(prop);
        continue;
      }
      if (propDef.hasSetter()) {
        Type propertyType = propDef.getSetter().getGenericParameterType(0);
        prop = constructSettableProperty(ctxt, beanDesc, propDef, propertyType);
      } else if (propDef.hasField()) {
        Type propertyType = propDef.getField().getGenericType();
        prop = constructSettableProperty(ctxt, beanDesc, propDef, propertyType);
      } else if (useGettersAsSetters && propDef.hasGetter()) {
        /* As per [JACKSON-88], may also need to consider getters
         * for Map/Collection properties; but with lowest precedence
         */
        AnnotatedMethod getter = propDef.getGetter();
        // should only consider Collections and Maps, for now?
        Class<?> rawPropertyType = getter.getRawType();
        if (Collection.class.isAssignableFrom(rawPropertyType)
            || Map.class.isAssignableFrom(rawPropertyType)) {
          prop = constructSetterlessProperty(ctxt, beanDesc, propDef);
        }
      }
      if (prop != null) {
        Class<?>[] views = propDef.findViews();
        if (views == null) {
          // one more twist: if default inclusion disabled, need to force empty set of views
          if (!ctxt.isEnabled(MapperFeature.DEFAULT_VIEW_INCLUSION)) {
            views = NO_VIEWS;
          }
        }
        // one more thing before adding to builder: copy any metadata
        prop.setViews(views);
        builder.addProperty(prop);
      }
    }
  }