/**
  * Apply mapping for a single @Field annotation
  *
  * @param xContentBuilder
  * @param field
  * @param fieldAnnotation
  * @throws IOException
  */
 private static void addSingleFieldMapping(
     XContentBuilder xContentBuilder, java.lang.reflect.Field field, Field fieldAnnotation)
     throws IOException {
   xContentBuilder.startObject(field.getName());
   xContentBuilder.field(FIELD_STORE, fieldAnnotation.store());
   if (FieldType.Auto != fieldAnnotation.type()) {
     xContentBuilder.field(FIELD_TYPE, fieldAnnotation.type().name().toLowerCase());
     if (FieldType.Date == fieldAnnotation.type() && DateFormat.none != fieldAnnotation.format()) {
       xContentBuilder.field(
           FIELD_FORMAT,
           DateFormat.custom == fieldAnnotation.format()
               ? fieldAnnotation.pattern()
               : fieldAnnotation.format());
     }
   }
   if (FieldIndex.not_analyzed == fieldAnnotation.index()) {
     xContentBuilder.field(FIELD_INDEX, fieldAnnotation.index().name().toLowerCase());
   }
   if (isNotBlank(fieldAnnotation.searchAnalyzer())) {
     xContentBuilder.field(FIELD_SEARCH_ANALYZER, fieldAnnotation.searchAnalyzer());
   }
   if (isNotBlank(fieldAnnotation.indexAnalyzer())) {
     xContentBuilder.field(FIELD_INDEX_ANALYZER, fieldAnnotation.indexAnalyzer());
   }
   xContentBuilder.endObject();
 }
 private static boolean isInIgnoreFields(java.lang.reflect.Field field) {
   Field fieldAnnotation = field.getAnnotation(Field.class);
   if (null != fieldAnnotation) {
     String[] ignoreFields = fieldAnnotation.ignoreFields();
     return Arrays.asList(ignoreFields).contains(field.getName());
   }
   return false;
 }
 private static void applyDefaultIdFieldMapping(
     XContentBuilder xContentBuilder, java.lang.reflect.Field field) throws IOException {
   xContentBuilder
       .startObject(field.getName())
       .field(FIELD_TYPE, TYPE_VALUE_STRING)
       .field(FIELD_INDEX, INDEX_VALUE_NOT_ANALYZED);
   xContentBuilder.endObject();
 }
 /**
  * Multi field mappings for string type fields, support for sorts and facets
  *
  * @param builder
  * @param field
  * @param annotation
  * @throws IOException
  */
 private static void addMultiFieldMapping(
     XContentBuilder builder, java.lang.reflect.Field field, MultiField annotation)
     throws IOException {
   builder.startObject(field.getName());
   builder.field(FIELD_TYPE, "multi_field");
   builder.startObject("fields");
   // add standard field
   addSingleFieldMapping(builder, field, annotation.mainField());
   for (NestedField nestedField : annotation.otherFields()) {
     addNestedFieldMapping(builder, field, nestedField);
   }
   builder.endObject();
   builder.endObject();
 }
  private static void mapEntity(
      XContentBuilder xContentBuilder,
      Class clazz,
      boolean isRootObject,
      String idFieldName,
      String nestedObjectFieldName)
      throws IOException {

    java.lang.reflect.Field[] fields = clazz.getDeclaredFields();

    if (!isRootObject && isAnyPropertyAnnotatedAsField(fields)) {
      xContentBuilder
          .startObject(nestedObjectFieldName)
          .field(FIELD_TYPE, TYPE_VALUE_OBJECT)
          .startObject(FIELD_PROPERTIES);
    }

    for (java.lang.reflect.Field field : fields) {

      if (field.isAnnotationPresent(Transient.class)) {
        continue;
      }

      if (isEntity(field) && !isInIgnoreFields(field)) {
        mapEntity(xContentBuilder, field.getType(), false, EMPTY, field.getName());
      }

      Field singleField = field.getAnnotation(Field.class);
      MultiField multiField = field.getAnnotation(MultiField.class);
      GeoPointField geoPoint = field.getAnnotation(GeoPointField.class);

      if (field.getType() == GeoPoint.class || geoPoint != null) {
        applyGeoPointFieldMapping(xContentBuilder, field);
      }

      if (isRootObject && singleField != null && isIdField(field, idFieldName)) {
        applyDefaultIdFieldMapping(xContentBuilder, field);
      } else if (multiField != null) {
        addMultiFieldMapping(xContentBuilder, field, multiField);
      } else if (singleField != null) {
        addSingleFieldMapping(xContentBuilder, field, singleField);
      }
    }

    if (!isRootObject && isAnyPropertyAnnotatedAsField(fields)) {
      xContentBuilder.endObject().endObject();
    }
  }
 /**
  * Apply mapping for a single nested @Field annotation
  *
  * @param builder
  * @param field
  * @param annotation
  * @throws IOException
  */
 private static void addNestedFieldMapping(
     XContentBuilder builder, java.lang.reflect.Field field, NestedField annotation)
     throws IOException {
   builder.startObject(field.getName() + "." + annotation.dotSuffix());
   builder.field(FIELD_STORE, annotation.store());
   if (FieldType.Auto != annotation.type()) {
     builder.field(FIELD_TYPE, annotation.type().name().toLowerCase());
   }
   if (FieldIndex.not_analyzed == annotation.index()) {
     builder.field(FIELD_INDEX, annotation.index().name().toLowerCase());
   }
   if (isNotBlank(annotation.searchAnalyzer())) {
     builder.field(FIELD_SEARCH_ANALYZER, annotation.searchAnalyzer());
   }
   if (isNotBlank(annotation.indexAnalyzer())) {
     builder.field(FIELD_INDEX_ANALYZER, annotation.indexAnalyzer());
   }
   builder.endObject();
 }
 private static boolean isIdField(java.lang.reflect.Field field, String idFieldName) {
   return idFieldName.equals(field.getName());
 }
 private static void applyGeoPointFieldMapping(
     XContentBuilder xContentBuilder, java.lang.reflect.Field field) throws IOException {
   xContentBuilder.startObject(field.getName());
   xContentBuilder.field(FIELD_TYPE, TYPE_VALUE_GEO_POINT).endObject();
 }