@Override
  public void findAndAddVirtualProperties(
      MapperConfig<?> config, AnnotatedClass ac, List<BeanPropertyWriter> properties) {
    JsonAppend ann = _findAnnotation(ac, JsonAppend.class);
    if (ann == null) {
      return;
    }
    final boolean prepend = ann.prepend();
    JavaType propType = null;

    // First: any attribute-backed properties?
    JsonAppend.Attr[] attrs = ann.attrs();
    for (int i = 0, len = attrs.length; i < len; ++i) {
      if (propType == null) {
        propType = config.constructType(Object.class);
      }
      BeanPropertyWriter bpw = _constructVirtualProperty(attrs[i], config, ac, propType);
      if (prepend) {
        properties.add(i, bpw);
      } else {
        properties.add(bpw);
      }
    }

    // Then: general-purpose virtual properties?
    JsonAppend.Prop[] props = ann.props();
    for (int i = 0, len = props.length; i < len; ++i) {
      BeanPropertyWriter bpw = _constructVirtualProperty(props[i], config, ac);
      if (prepend) {
        properties.add(i, bpw);
      } else {
        properties.add(bpw);
      }
    }
  }
 private void _verifyProperty(BeanDescription beanDesc, boolean verifyDesc, boolean verifyIndex) {
   assertNotNull(beanDesc);
   List<BeanPropertyDefinition> props = beanDesc.findProperties();
   assertEquals(2, props.size());
   for (BeanPropertyDefinition prop : props) {
     String name = prop.getName();
     final PropertyMetadata md = prop.getMetadata();
     if ("a".equals(name)) {
       assertFalse(md.isRequired());
       assertNull(md.getRequired());
       if (verifyDesc) {
         assertEquals(PropDescBean.A_DESC, md.getDescription());
       }
       if (verifyIndex) {
         assertNull(md.getIndex());
       }
     } else if ("b".equals(name)) {
       assertTrue(md.isRequired());
       assertEquals(Boolean.TRUE, md.getRequired());
       if (verifyDesc) {
         assertNull(md.getDescription());
       }
       if (verifyIndex) {
         assertEquals(Integer.valueOf(PropDescBean.B_INDEX), md.getIndex());
       }
     } else {
       fail("Unrecognized property '" + name + "'");
     }
   }
 }
 public void testSimpleOrderingForDeserialization() {
   POJOPropertiesCollector coll = collector(mapper, SortedProperties.class, false);
   List<BeanPropertyDefinition> props = coll.getProperties();
   assertEquals(4, props.size());
   assertEquals("a", props.get(0).getName());
   assertEquals("b", props.get(1).getName());
   assertEquals("c", props.get(2).getName());
   assertEquals("d", props.get(3).getName());
 }
 private final void serializeContents(
     List<String> value, JsonGenerator jgen, SerializerProvider provider)
     throws IOException, JsonGenerationException {
   int i = 0;
   try {
     final int len = value.size();
     for (; i < len; ++i) {
       String str = value.get(i);
       if (str == null) {
         provider.defaultSerializeNull(jgen);
       } else {
         jgen.writeString(str);
       }
     }
   } catch (Exception e) {
     wrapAndThrow(provider, e, value, i);
   }
 }
 private final void serializeUsingCustom(
     List<String> value, JsonGenerator jgen, SerializerProvider provider)
     throws IOException, JsonGenerationException {
   int i = 0;
   try {
     final int len = value.size();
     final JsonSerializer<String> ser = _serializer;
     for (i = 0; i < len; ++i) {
       String str = value.get(i);
       if (str == null) {
         provider.defaultSerializeNull(jgen);
       } else {
         ser.serialize(str, jgen, provider);
       }
     }
   } catch (Exception e) {
     wrapAndThrow(provider, e, value, i);
   }
 }
 @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;
 }
 @Override
 public void serializeWithType(
     List<String> value, JsonGenerator jgen, SerializerProvider provider, TypeSerializer typeSer)
     throws IOException, JsonGenerationException {
   final int len = value.size();
   typeSer.writeTypePrefixForArray(value, jgen);
   if (_serializer == null) {
     serializeContents(value, jgen, provider, len);
   } else {
     serializeUsingCustom(value, jgen, provider, len);
   }
   typeSer.writeTypeSuffixForArray(value, jgen);
 }
  @Override
  public void serialize(List<String> value, JsonGenerator jgen, SerializerProvider provider)
      throws IOException, JsonGenerationException {
    final int len = value.size();
    // [JACKSON-805]
    if ((len == 1) && provider.isEnabled(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED)) {
      _serializeUnwrapped(value, jgen, provider);
      return;
    }

    jgen.writeStartArray();
    if (_serializer == null) {
      serializeContents(value, jgen, provider, len);
    } else {
      serializeUsingCustom(value, jgen, provider, len);
    }
    jgen.writeEndArray();
  }
  public void testSimpleWithType() {
    // first for serialization; should base choice on getter
    POJOPropertiesCollector coll = collector(mapper, TypeTestBean.class, true);
    List<BeanPropertyDefinition> props = coll.getProperties();
    assertEquals(1, props.size());
    assertEquals("value", props.get(0).getName());
    AnnotatedMember m = props.get(0).getAccessor();
    assertTrue(m instanceof AnnotatedMethod);
    assertEquals(Integer.class, m.getRawType());

    // then for deserialization; prefer ctor param
    coll = collector(mapper, TypeTestBean.class, false);
    props = coll.getProperties();
    assertEquals(1, props.size());
    assertEquals("value", props.get(0).getName());
    m = props.get(0).getMutator();
    assertEquals(AnnotatedParameter.class, m.getClass());
    assertEquals(String.class, m.getRawType());
  }
  /**
   * Helper method called to filter out explicit ignored properties, as well as properties that have
   * "ignorable types". Note that this will not remove properties that have no setters.
   */
  protected List<BeanPropertyDefinition> filterBeanProps(
      DeserializationContext ctxt,
      BeanDescription beanDesc,
      BeanDeserializerBuilder builder,
      List<BeanPropertyDefinition> propDefsIn,
      Set<String> ignored)
      throws JsonMappingException {
    ArrayList<BeanPropertyDefinition> result =
        new ArrayList<BeanPropertyDefinition>(Math.max(4, propDefsIn.size()));
    HashMap<Class<?>, Boolean> ignoredTypes = new HashMap<Class<?>, Boolean>();
    // These are all valid setters, but we do need to introspect bit more
    for (BeanPropertyDefinition property : propDefsIn) {
      String name = property.getName();
      if (ignored.contains(
          name)) { // explicit ignoral using @JsonIgnoreProperties needs to block entries
        continue;
      }
      if (!property.hasConstructorParameter()) { // never skip constructor params
        Class<?> rawPropertyType = null;
        if (property.hasSetter()) {
          rawPropertyType = property.getSetter().getRawParameterType(0);
        } else if (property.hasField()) {
          rawPropertyType = property.getField().getRawType();
        }

        // [JACKSON-429] Some types are declared as ignorable as well
        if ((rawPropertyType != null)
            && (isIgnorableType(ctxt.getConfig(), beanDesc, rawPropertyType, ignoredTypes))) {
          // important: make ignorable, to avoid errors if value is actually seen
          builder.addIgnorable(name);
          continue;
        }
      }
      result.add(property);
    }
    return result;
  }
 {
   location.add(new FoodOrgLocation());
 }