public synchronized MergeResult merge(DocumentMapper mergeWith, MergeFlags mergeFlags) {
    MergeContext mergeContext = new MergeContext(this, mergeFlags);
    rootObjectMapper.merge(mergeWith.rootObjectMapper, mergeContext);

    for (Map.Entry<Class<? extends RootMapper>, RootMapper> entry : rootMappers.entrySet()) {
      // root mappers included in root object will get merge in the rootObjectMapper
      if (entry.getValue().includeInObject()) {
        continue;
      }
      RootMapper mergeWithRootMapper = mergeWith.rootMappers.get(entry.getKey());
      if (mergeWithRootMapper != null) {
        entry.getValue().merge(mergeWithRootMapper, mergeContext);
      }
    }

    if (!mergeFlags.simulate()) {
      if (!mergeContext.newFieldMappers().mappers.isEmpty()) {
        addFieldMappers(mergeContext.newFieldMappers().mappers);
      }
      if (!mergeContext.newObjectMappers().mappers.isEmpty()) {
        addObjectMappers(mergeContext.newObjectMappers().mappers);
      }
      // let the merge with attributes to override the attributes
      meta = mergeWith.meta();
      // update the source of the merged one
      refreshSource();
    }
    return new MergeResult(mergeContext.buildConflicts());
  }
 public void close() {
   cache.remove();
   rootObjectMapper.close();
   for (RootMapper rootMapper : rootMappersOrdered) {
     rootMapper.close();
   }
 }
 public void traverse(FieldMapperListener listener) {
   for (RootMapper rootMapper : rootMappersOrdered) {
     if (!rootMapper.includeInObject() && rootMapper instanceof FieldMapper) {
       listener.fieldMapper((FieldMapper) rootMapper);
     }
   }
   rootObjectMapper.traverse(listener);
 }
  @Override
  public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
    rootObjectMapper.toXContent(
        builder,
        params,
        new ToXContent() {
          @Override
          public XContentBuilder toXContent(XContentBuilder builder, Params params)
              throws IOException {
            if (indexAnalyzer != null
                && searchAnalyzer != null
                && indexAnalyzer.name().equals(searchAnalyzer.name())
                && !indexAnalyzer.name().startsWith("_")) {
              if (!indexAnalyzer.name().equals("default")) {
                // same analyzers, output it once
                builder.field("analyzer", indexAnalyzer.name());
              }
            } else {
              if (indexAnalyzer != null && !indexAnalyzer.name().startsWith("_")) {
                if (!indexAnalyzer.name().equals("default")) {
                  builder.field("index_analyzer", indexAnalyzer.name());
                }
              }
              if (searchAnalyzer != null && !searchAnalyzer.name().startsWith("_")) {
                if (!searchAnalyzer.name().equals("default")) {
                  builder.field("search_analyzer", searchAnalyzer.name());
                }
              }
            }

            if (meta != null && !meta.isEmpty()) {
              builder.field("_meta", meta());
            }
            return builder;
          }
          // no need to pass here id and boost, since they are added to the root object mapper
          // in the constructor
        },
        rootMappersNotIncludedInObject);
    return builder;
  }
 public void traverse(ObjectMapperListener listener) {
   rootObjectMapper.traverse(listener);
 }
  public ParsedDocument parse(SourceToParse source, @Nullable ParseListener listener)
      throws MapperParsingException {
    ParseContext context = cache.get();

    if (source.type() != null && !source.type().equals(this.type)) {
      throw new MapperParsingException(
          "Type mismatch, provide type ["
              + source.type()
              + "] but mapper is of type ["
              + this.type
              + "]");
    }
    source.type(this.type);

    XContentParser parser = source.parser();
    try {
      if (parser == null) {
        parser = XContentHelper.createParser(source.source());
      }
      context.reset(parser, new Document(), source, listener);
      // on a newly created instance of document mapper, we always consider it as new mappers that
      // have been added
      if (initMappersAdded) {
        context.setMappingsModified();
        initMappersAdded = false;
      }

      // will result in START_OBJECT
      int countDownTokens = 0;
      XContentParser.Token token = parser.nextToken();
      if (token != XContentParser.Token.START_OBJECT) {
        throw new MapperParsingException("Malformed content, must start with an object");
      }
      boolean emptyDoc = false;
      token = parser.nextToken();
      if (token == XContentParser.Token.END_OBJECT) {
        // empty doc, we can handle it...
        emptyDoc = true;
      } else if (token != XContentParser.Token.FIELD_NAME) {
        throw new MapperParsingException(
            "Malformed content, after first object, either the type field or the actual properties should exist");
      }
      if (type.equals(parser.currentName())) {
        // first field is the same as the type, this might be because the type is provided, and the
        // object exists within it
        // or because there is a valid field that by chance is named as the type

        // Note, in this case, we only handle plain value types, an object type will be analyzed as
        // if it was the type itself
        // and other same level fields will be ignored
        token = parser.nextToken();
        countDownTokens++;
        // commented out, allow for same type with START_OBJECT, we do our best to handle it except
        // for the above corner case
        //                if (token != XContentParser.Token.START_OBJECT) {
        //                    throw new MapperException("Malformed content, a field with the same
        // name as the type must be an object with the properties/fields within it");
        //                }
      }

      for (RootMapper rootMapper : rootMappersOrdered) {
        rootMapper.preParse(context);
      }

      if (!emptyDoc) {
        rootObjectMapper.parse(context);
      }

      for (int i = 0; i < countDownTokens; i++) {
        parser.nextToken();
      }

      // fire up any new mappers if exists
      if (!context.newFieldMappers().mappers.isEmpty()) {
        addFieldMappers(context.newFieldMappers().mappers);
        context.newFieldMappers().mappers.clear();
      }
      if (!context.newObjectMappers().mappers.isEmpty()) {
        addObjectMappers(context.newObjectMappers().mappers);
        context.newObjectMappers().mappers.clear();
      }

      for (RootMapper rootMapper : rootMappersOrdered) {
        rootMapper.postParse(context);
      }

      for (RootMapper rootMapper : rootMappersOrdered) {
        rootMapper.validate(context);
      }
    } catch (Throwable e) {
      // we have to fire up any new mappers even on a failure, because they
      // have been added internally to each compound mapper...
      // ... we have no option to "rollback" a change, which is very tricky in our copy on change
      // system...
      if (!context.newFieldMappers().mappers.isEmpty()) {
        addFieldMappers(context.newFieldMappers().mappers);
        context.newFieldMappers().mappers.clear();
      }
      if (!context.newObjectMappers().mappers.isEmpty()) {
        addObjectMappers(context.newObjectMappers().mappers);
        context.newObjectMappers().mappers.clear();
      }

      // if its already a mapper parsing exception, no need to wrap it...
      if (e instanceof MapperParsingException) {
        throw (MapperParsingException) e;
      }

      throw new MapperParsingException("failed to parse", e);
    } finally {
      // only close the parser when its not provided externally
      if (source.parser() == null && parser != null) {
        parser.close();
      }
    }
    // reverse the order of docs for nested docs support, parent should be last
    if (context.docs().size() > 1) {
      Collections.reverse(context.docs());
    }
    // apply doc boost
    if (context.docBoost() != 1.0f) {
      Set<String> encounteredFields = Sets.newHashSet();
      for (Document doc : context.docs()) {
        encounteredFields.clear();
        for (IndexableField field : doc) {
          if (field.fieldType().indexed() && !field.fieldType().omitNorms()) {
            if (!encounteredFields.contains(field.name())) {
              ((Field) field).setBoost(context.docBoost() * field.boost());
              encounteredFields.add(field.name());
            }
          }
        }
      }
    }

    ParsedDocument doc =
        new ParsedDocument(
                context.uid(),
                context.id(),
                context.type(),
                source.routing(),
                source.timestamp(),
                source.ttl(),
                context.docs(),
                context.analyzer(),
                context.source(),
                context.mappingsModified())
            .parent(source.parent());
    // reset the context to free up memory
    context.reset(null, null, null, null);
    return doc;
  }
  public DocumentMapper(
      String index,
      @Nullable Settings indexSettings,
      DocumentMapperParser docMapperParser,
      RootObjectMapper rootObjectMapper,
      ImmutableMap<String, Object> meta,
      NamedAnalyzer indexAnalyzer,
      NamedAnalyzer searchAnalyzer,
      NamedAnalyzer searchQuoteAnalyzer,
      Map<Class<? extends RootMapper>, RootMapper> rootMappers) {
    this.index = index;
    this.indexSettings = indexSettings;
    this.type = rootObjectMapper.name();
    this.docMapperParser = docMapperParser;
    this.meta = meta;
    this.rootObjectMapper = rootObjectMapper;

    this.rootMappers = ImmutableMap.copyOf(rootMappers);
    this.rootMappersOrdered =
        rootMappers.values().toArray(new RootMapper[rootMappers.values().size()]);
    List<RootMapper> rootMappersNotIncludedInObjectLst = newArrayList();
    for (RootMapper rootMapper : rootMappersOrdered) {
      if (!rootMapper.includeInObject()) {
        rootMappersNotIncludedInObjectLst.add(rootMapper);
      }
    }
    this.rootMappersNotIncludedInObject =
        rootMappersNotIncludedInObjectLst.toArray(
            new RootMapper[rootMappersNotIncludedInObjectLst.size()]);

    this.indexAnalyzer = indexAnalyzer;
    this.searchAnalyzer = searchAnalyzer;
    this.searchQuoteAnalyzer = searchQuoteAnalyzer != null ? searchQuoteAnalyzer : searchAnalyzer;

    this.typeFilter = typeMapper().termFilter(type, null);

    if (rootMapper(ParentFieldMapper.class) != null) {
      // mark the routing field mapper as required
      rootMapper(RoutingFieldMapper.class).markAsRequired();
    }

    FieldMapperListener.Aggregator fieldMappersAgg = new FieldMapperListener.Aggregator();
    for (RootMapper rootMapper : rootMappersOrdered) {
      if (rootMapper.includeInObject()) {
        rootObjectMapper.putMapper(rootMapper);
      } else {
        if (rootMapper instanceof FieldMapper) {
          fieldMappersAgg.mappers.add((FieldMapper) rootMapper);
        }
      }
    }

    // now traverse and get all the statically defined ones
    rootObjectMapper.traverse(fieldMappersAgg);

    this.fieldMappers = new DocumentFieldMappers(this, fieldMappersAgg.mappers);

    final Map<String, ObjectMapper> objectMappers = Maps.newHashMap();
    rootObjectMapper.traverse(
        new ObjectMapperListener() {
          @Override
          public void objectMapper(ObjectMapper objectMapper) {
            objectMappers.put(objectMapper.fullPath(), objectMapper);
          }
        });
    this.objectMappers = ImmutableMap.copyOf(objectMappers);
    for (ObjectMapper objectMapper : objectMappers.values()) {
      if (objectMapper.nested().isNested()) {
        hasNestedObjects = true;
      }
    }

    refreshSource();
  }
  public DocumentMapper(
      MapperService mapperService,
      String index,
      @Nullable Settings indexSettings,
      DocumentMapperParser docMapperParser,
      RootObjectMapper rootObjectMapper,
      ImmutableMap<String, Object> meta,
      Map<Class<? extends MetadataFieldMapper>, MetadataFieldMapper> rootMappers,
      List<SourceTransform> sourceTransforms,
      ReentrantReadWriteLock mappingLock) {
    this.mapperService = mapperService;
    this.type = rootObjectMapper.name();
    this.typeText = new StringAndBytesText(this.type);
    this.mapping =
        new Mapping(
            Version.indexCreated(indexSettings),
            rootObjectMapper,
            rootMappers.values().toArray(new MetadataFieldMapper[rootMappers.values().size()]),
            sourceTransforms.toArray(new SourceTransform[sourceTransforms.size()]),
            meta);
    this.documentParser =
        new DocumentParser(
            index,
            indexSettings,
            docMapperParser,
            this,
            new ReleasableLock(mappingLock.readLock()));

    this.typeFilter = typeMapper().fieldType().termQuery(type, null);
    this.mappingWriteLock = new ReleasableLock(mappingLock.writeLock());
    this.mappingLock = mappingLock;

    if (rootMapper(ParentFieldMapper.class).active()) {
      // mark the routing field mapper as required
      rootMapper(RoutingFieldMapper.class).markAsRequired();
    }

    // collect all the mappers for this type
    List<ObjectMapper> newObjectMappers = new ArrayList<>();
    List<FieldMapper> newFieldMappers = new ArrayList<>();
    for (MetadataFieldMapper metadataMapper : this.mapping.metadataMappers) {
      if (metadataMapper instanceof FieldMapper) {
        newFieldMappers.add((FieldMapper) metadataMapper);
      }
    }
    MapperUtils.collect(this.mapping.root, newObjectMappers, newFieldMappers);

    this.fieldMappers =
        new DocumentFieldMappers(docMapperParser.analysisService).copyAndAllAll(newFieldMappers);
    this.objectMappers =
        Maps.uniqueIndex(
            newObjectMappers,
            new Function<ObjectMapper, String>() {
              @Override
              public String apply(ObjectMapper mapper) {
                return mapper.fullPath();
              }
            });
    for (ObjectMapper objectMapper : newObjectMappers) {
      if (objectMapper.nested().isNested()) {
        hasNestedObjects = true;
      }
    }

    refreshSource();
  }