@Override
 public List<BeanPropertyDefinition> updateProperties(
     DeserializationConfig config,
     BeanDescription beanDesc,
     List<BeanPropertyDefinition> propDefs) {
   final AnnotationIntrospector intr = config.getAnnotationIntrospector();
   int changed = 0;
   for (int i = 0, len = propDefs.size(); i < len; ++i) {
     BeanPropertyDefinition prop = propDefs.get(i);
     AnnotatedMember acc = prop.getAccessor();
     // should not be null, but just in case:
     if (acc == null) {
       continue;
     }
     // first: do we need to handle wrapping (for Lists)?
     QName wrapperName = AnnotationUtil.findWrapperName(intr, acc);
     if (wrapperName != null) {
       String localName = wrapperName.getLocalPart();
       if ((localName != null && localName.length() >= 0) && !localName.equals(prop.getName())) {
         // make copy-on-write as necessary
         if (changed == 0) {
           propDefs = new ArrayList<BeanPropertyDefinition>(propDefs);
         }
         ++changed;
         propDefs.set(i, prop.withName(localName));
         continue;
       }
     } else {
       /* If not, how about "as text" unwrapping? Such properties
        * are exposed as values of 'unnamed' fields; so one way to
        * map them is to rename property to have name ""... (and
        * hope this does not break other parts...)
        */
       Boolean b = AnnotationUtil.findIsTextAnnotation(intr, acc);
       if (b != null && b.booleanValue()) {
         // unwrapped properties will appear as 'unnamed' (empty String)
         propDefs.set(i, prop.withName(""));
         continue;
       }
     }
   }
   return propDefs;
 }
  /**
   * 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);
      }
    }
  }
  @Override
  public JsonSerializer<?> createContextual(SerializerProvider provider, BeanProperty property)
      throws JsonMappingException {
    JsonSerializer<?> ser = null;
    JsonSerializer<?> keySer = null;
    final AnnotationIntrospector intr = provider.getAnnotationIntrospector();
    final AnnotatedMember propertyAcc = (property == null) ? null : property.getMember();

    // First: if we have a property, may have property-annotation overrides
    if ((propertyAcc != null) && (intr != null)) {
      Object serDef = intr.findKeySerializer(propertyAcc);
      if (serDef != null) {
        keySer = provider.serializerInstance(propertyAcc, serDef);
      }
      serDef = intr.findContentSerializer(propertyAcc);
      if (serDef != null) {
        ser = provider.serializerInstance(propertyAcc, serDef);
      }
    }
    if (ser == null) {
      ser = _valueSerializer;
    }
    // [databind#124]: May have a content converter
    ser = findContextualConvertingSerializer(provider, property, ser);
    if (ser == null) {
      // 30-Sep-2012, tatu: One more thing -- if explicit content type is annotated,
      //   we can consider it a static case as well.
      // 20-Aug-2013, tatu: Need to avoid trying to access serializer for java.lang.Object tho
      if (_valueTypeIsStatic && !_valueType.isJavaLangObject()) {
        ser = provider.findValueSerializer(_valueType, property);
      }
    }
    if (keySer == null) {
      keySer = _keySerializer;
    }
    if (keySer == null) {
      keySer = provider.findKeySerializer(_keyType, property);
    } else {
      keySer = provider.handleSecondaryContextualization(keySer, property);
    }
    Set<String> ignored = _ignoredEntries;
    boolean sortKeys = false;
    if ((intr != null) && (propertyAcc != null)) {
      JsonIgnoreProperties.Value ignorals = intr.findPropertyIgnorals(propertyAcc);
      if (ignorals != null) {
        Set<String> newIgnored = ignorals.findIgnoredForSerialization();
        if ((newIgnored != null) && !newIgnored.isEmpty()) {
          ignored = (ignored == null) ? new HashSet<String>() : new HashSet<String>(ignored);
          for (String str : newIgnored) {
            ignored.add(str);
          }
        }
      }
      Boolean b = intr.findSerializationSortAlphabetically(propertyAcc);
      sortKeys = (b != null) && b.booleanValue();
    }
    JsonFormat.Value format = findFormatOverrides(provider, property, Map.class);
    if (format != null) {
      Boolean B = format.getFeature(JsonFormat.Feature.WRITE_SORTED_MAP_ENTRIES);
      if (B != null) {
        sortKeys = B.booleanValue();
      }
    }
    MapSerializer mser = withResolved(property, keySer, ser, ignored, sortKeys);

    // [databind#307]: allow filtering
    if (property != null) {
      AnnotatedMember m = property.getMember();
      if (m != null) {
        Object filterId = intr.findFilterId(m);
        if (filterId != null) {
          mser = mser.withFilterId(filterId);
        }
      }
      JsonInclude.Value inclV = property.findPropertyInclusion(provider.getConfig(), null);
      if (inclV != null) {
        JsonInclude.Include incl = inclV.getContentInclusion();

        if (incl != JsonInclude.Include.USE_DEFAULTS) {
          Object valueToSuppress;
          boolean suppressNulls;
          switch (incl) {
            case NON_DEFAULT:
              valueToSuppress = BeanUtil.getDefaultValue(_valueType);
              suppressNulls = true;
              if (valueToSuppress != null) {
                if (valueToSuppress.getClass().isArray()) {
                  valueToSuppress = ArrayBuilders.getArrayComparator(valueToSuppress);
                }
              }
              break;
            case NON_ABSENT:
              suppressNulls = true;
              valueToSuppress = _valueType.isReferenceType() ? MARKER_FOR_EMPTY : null;
              break;
            case NON_EMPTY:
              suppressNulls = true;
              valueToSuppress = MARKER_FOR_EMPTY;
              break;
            case CUSTOM:
              valueToSuppress = provider.includeFilterInstance(null, inclV.getContentFilter());
              if (valueToSuppress == null) { // is this legal?
                suppressNulls = true;
              } else {
                suppressNulls = provider.includeFilterSuppressNulls(valueToSuppress);
              }
              break;
            case NON_NULL:
              valueToSuppress = null;
              suppressNulls = true;
              break;
            case ALWAYS: // default
            default:
              valueToSuppress = null;
              // 30-Sep-2016, tatu: Should not need to check global flags here,
              //   if inclusion forced to be ALWAYS
              suppressNulls = false;
              break;
          }
          mser = mser.withContentInclusion(valueToSuppress, suppressNulls);
        }
      }
    }
    return mser;
  }