@Override
    public Mapper.Builder parse(String name, Map<String, Object> node, ParserContext parserContext)
        throws MapperParsingException {
      Map<String, Object> objectNode = node;
      ObjectMapper.Builder builder = createBuilder(name);

      boolean nested = false;
      boolean nestedIncludeInParent = false;
      boolean nestedIncludeInRoot = false;
      for (Map.Entry<String, Object> entry : objectNode.entrySet()) {
        String fieldName = Strings.toUnderscoreCase(entry.getKey());
        Object fieldNode = entry.getValue();

        if (fieldName.equals("dynamic")) {
          String value = fieldNode.toString();
          if (value.equalsIgnoreCase("strict")) {
            builder.dynamic(Dynamic.STRICT);
          } else {
            builder.dynamic(nodeBooleanValue(fieldNode) ? Dynamic.TRUE : Dynamic.FALSE);
          }
        } else if (fieldName.equals("type")) {
          String type = fieldNode.toString();
          if (type.equals(CONTENT_TYPE)) {
            builder.nested = Nested.NO;
          } else if (type.equals(NESTED_CONTENT_TYPE)) {
            nested = true;
          } else {
            throw new MapperParsingException(
                "Trying to parse an object but has a different type ["
                    + type
                    + "] for ["
                    + name
                    + "]");
          }
        } else if (fieldName.equals("include_in_parent")) {
          nestedIncludeInParent = nodeBooleanValue(fieldNode);
        } else if (fieldName.equals("include_in_root")) {
          nestedIncludeInRoot = nodeBooleanValue(fieldNode);
        } else if (fieldName.equals("enabled")) {
          builder.enabled(nodeBooleanValue(fieldNode));
        } else if (fieldName.equals("path")) {
          builder.pathType(parsePathType(name, fieldNode.toString()));
        } else if (fieldName.equals("properties")) {
          parseProperties(builder, (Map<String, Object>) fieldNode, parserContext);
        } else if (fieldName.equals("include_in_all")) {
          builder.includeInAll(nodeBooleanValue(fieldNode));
        } else {
          processField(builder, fieldName, fieldNode);
        }
      }

      if (nested) {
        builder.nested = Nested.newNested(nestedIncludeInParent, nestedIncludeInRoot);
      }

      return builder;
    }
 @Override
 public Mapper.Builder parse(String name, Map<String, Object> node, ParserContext parserContext)
     throws MapperParsingException {
   ObjectMapper.Builder builder = createBuilder(name);
   for (Map.Entry<String, Object> entry : node.entrySet()) {
     String fieldName = Strings.toUnderscoreCase(entry.getKey());
     Object fieldNode = entry.getValue();
     parseObjectOrDocumentTypeProperties(fieldName, fieldNode, parserContext, builder);
     parseObjectProperties(name, fieldName, fieldNode, builder);
   }
   parseNested(name, node, builder);
   return builder;
 }
    protected static void parseProperties(
        ObjectMapper.Builder objBuilder,
        Map<String, Object> propsNode,
        ParserContext parserContext) {
      for (Map.Entry<String, Object> entry : propsNode.entrySet()) {
        String propName = entry.getKey();
        // Should accept empty arrays, as a work around for when the user can't provide an empty
        // Map. (PHP for example)
        boolean isEmptyList =
            entry.getValue() instanceof List && ((List<?>) entry.getValue()).isEmpty();

        if (entry.getValue() instanceof Map) {
          @SuppressWarnings("unchecked")
          Map<String, Object> propNode = (Map<String, Object>) entry.getValue();
          String type;
          Object typeNode = propNode.get("type");
          if (typeNode != null) {
            type = typeNode.toString();
          } else {
            // lets see if we can derive this...
            if (propNode.get("properties") != null) {
              type = ObjectMapper.CONTENT_TYPE;
            } else if (propNode.size() == 1 && propNode.get("enabled") != null) {
              // if there is a single property with the enabled flag on it, make it an object
              // (usually, setting enabled to false to not index any type, including core values,
              // which
              // non enabled object type supports).
              type = ObjectMapper.CONTENT_TYPE;
            } else {
              throw new MapperParsingException("No type specified for property [" + propName + "]");
            }
          }

          Mapper.TypeParser typeParser = parserContext.typeParser(type);
          if (typeParser == null) {
            throw new MapperParsingException(
                "No handler for type [" + type + "] declared on field [" + propName + "]");
          }
          objBuilder.add(typeParser.parse(propName, propNode, parserContext));
        } else if (!isEmptyList) {
          throw new MapperParsingException(
              "Expected map for property [fields] on field ["
                  + propName
                  + "] but got a "
                  + propName.getClass());
        }
      }
    }
    private void parseProperties(
        ObjectMapper.Builder objBuilder,
        Map<String, Object> propsNode,
        ParserContext parserContext) {
      for (Map.Entry<String, Object> entry : propsNode.entrySet()) {
        String propName = entry.getKey();
        Map<String, Object> propNode = (Map<String, Object>) entry.getValue();

        String type;
        Object typeNode = propNode.get("type");
        if (typeNode != null) {
          type = typeNode.toString();
        } else {
          // lets see if we can derive this...
          if (propNode.get("properties") != null) {
            type = ObjectMapper.CONTENT_TYPE;
          } else if (propNode.get("fields") != null) {
            type = MultiFieldMapper.CONTENT_TYPE;
          } else if (propNode.size() == 1 && propNode.get("enabled") != null) {
            // if there is a single property with the enabled flag on it, make it an object
            // (usually, setting enabled to false to not index any type, including core values,
            // which
            // non enabled object type supports).
            type = ObjectMapper.CONTENT_TYPE;
          } else {
            throw new MapperParsingException("No type specified for property [" + propName + "]");
          }
        }

        Mapper.TypeParser typeParser = parserContext.typeParser(type);
        if (typeParser == null) {
          throw new MapperParsingException(
              "No handler for type [" + type + "] declared on field [" + propName + "]");
        }
        objBuilder.add(typeParser.parse(propName, propNode, parserContext));
      }
    }
    @SuppressWarnings({"unchecked"})
    @Override
    public Mapper.Builder parse(String name, Map<String, Object> node, ParserContext parserContext)
        throws MapperParsingException {
      AttachmentMapper.Builder builder = new AttachmentMapper.Builder(name);

      for (Iterator<Map.Entry<String, Object>> iterator = node.entrySet().iterator();
          iterator.hasNext(); ) {
        Map.Entry<String, Object> entry = iterator.next();
        String fieldName = entry.getKey();
        Object fieldNode = entry.getValue();
        if (fieldName.equals("path")
            && parserContext.indexVersionCreated().before(Version.V_2_0_0_beta1)) {
          builder.pathType(parsePathType(name, fieldNode.toString()));
          iterator.remove();
        } else if (fieldName.equals("fields")) {
          Map<String, Object> fieldsNode = (Map<String, Object>) fieldNode;
          for (Iterator<Map.Entry<String, Object>> fieldsIterator =
                  fieldsNode.entrySet().iterator();
              fieldsIterator.hasNext(); ) {
            Map.Entry<String, Object> entry1 = fieldsIterator.next();
            String propName = entry1.getKey();
            Map<String, Object> propNode = (Map<String, Object>) entry1.getValue();

            Mapper.Builder<?, ?> mapperBuilder =
                findMapperBuilder(propNode, propName, parserContext);
            if (parseMultiField(
                (FieldMapper.Builder) mapperBuilder,
                fieldName,
                parserContext,
                propName,
                propNode)) {
              fieldsIterator.remove();
            } else if (propName.equals(name)
                && parserContext.indexVersionCreated().before(Version.V_2_0_0_beta1)) {
              builder.content(mapperBuilder);
              fieldsIterator.remove();
            } else {
              switch (propName) {
                case FieldNames.CONTENT:
                  builder.content(mapperBuilder);
                  fieldsIterator.remove();
                  break;
                case FieldNames.DATE:
                  builder.date(mapperBuilder);
                  fieldsIterator.remove();
                  break;
                case FieldNames.AUTHOR:
                  builder.author(mapperBuilder);
                  fieldsIterator.remove();
                  break;
                case FieldNames.CONTENT_LENGTH:
                  builder.contentLength(mapperBuilder);
                  fieldsIterator.remove();
                  break;
                case FieldNames.CONTENT_TYPE:
                  builder.contentType(mapperBuilder);
                  fieldsIterator.remove();
                  break;
                case FieldNames.KEYWORDS:
                  builder.keywords(mapperBuilder);
                  fieldsIterator.remove();
                  break;
                case FieldNames.LANGUAGE:
                  builder.language(mapperBuilder);
                  fieldsIterator.remove();
                  break;
                case FieldNames.TITLE:
                  builder.title(mapperBuilder);
                  fieldsIterator.remove();
                  break;
                case FieldNames.NAME:
                  builder.name(mapperBuilder);
                  fieldsIterator.remove();
                  break;
              }
            }
          }
          DocumentMapperParser.checkNoRemainingFields(
              fieldName, fieldsNode, parserContext.indexVersionCreated());
          iterator.remove();
        }
      }

      return builder;
    }