@Override public void merge(final Mapper mergeWith, final MergeContext mergeContext) throws MergeMappingException { if (!(mergeWith instanceof ObjectMapper)) { mergeContext.addConflict( "Can't merge a non object mapping [" + mergeWith.name() + "] with an object mapping [" + name() + "]"); return; } ObjectMapper mergeWithObject = (ObjectMapper) mergeWith; doMerge(mergeWithObject, mergeContext); List<Mapper> mappersToTraverse = new ArrayList<Mapper>(); synchronized (mutex) { for (Mapper mergeWithMapper : mergeWithObject.mappers.values()) { Mapper mergeIntoMapper = mappers.get(mergeWithMapper.name()); if (mergeIntoMapper == null) { // no mapping, simply add it if not simulating if (!mergeContext.mergeFlags().simulate()) { putMapper(mergeWithMapper); mappersToTraverse.add(mergeWithMapper); } } else { if ((mergeWithMapper instanceof MultiFieldMapper) && !(mergeIntoMapper instanceof MultiFieldMapper)) { MultiFieldMapper mergeWithMultiField = (MultiFieldMapper) mergeWithMapper; mergeWithMultiField.merge(mergeIntoMapper, mergeContext); if (!mergeContext.mergeFlags().simulate()) { putMapper(mergeWithMultiField); // now, record mappers to traverse events for all mappers for (Mapper mapper : mergeWithMultiField.mappers().values()) { mappersToTraverse.add(mapper); } } } else { mergeIntoMapper.merge(mergeWithMapper, mergeContext); } } } } // call this outside of the mutex for (Mapper mapper : mappersToTraverse) { mapper.traverse( new FieldMapperListener() { @Override public void fieldMapper(FieldMapper fieldMapper) { mergeContext.docMapper().addFieldMapper(fieldMapper); } }); mapper.traverse( new ObjectMapperListener() { @Override public void objectMapper(ObjectMapper objectMapper) { mergeContext.docMapper().addObjectMapper(objectMapper); } }); } }