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) {
if (LZF.isCompressed(source.source())) {
BytesStreamInput siBytes = new BytesStreamInput(source.source());
LZFStreamInput siLzf = CachedStreamInput.cachedLzf(siBytes);
XContentType contentType = XContentFactory.xContentType(siLzf);
siLzf.resetToBufferStart();
parser = XContentFactory.xContent(contentType).createParser(siLzf);
} else {
parser = XContentFactory.xContent(source.source()).createParser(source.source());
}
}
context.reset(parser, new Document(), type, source.source(), source.flyweight(), listener);
// 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");
// }
}
if (sizeFieldMapper.enabled()) {
context.externalValue(source.source().length);
sizeFieldMapper.parse(context);
}
if (sourceFieldMapper.enabled()) {
sourceFieldMapper.parse(context);
}
// set the id if we have it so we can validate it later on, also, add the uid if we can
if (source.id() != null) {
context.id(source.id());
uidFieldMapper.parse(context);
}
typeFieldMapper.parse(context);
if (source.routing() != null) {
context.externalValue(source.routing());
routingFieldMapper.parse(context);
}
indexFieldMapper.parse(context);
if (!emptyDoc) {
rootObjectMapper.parse(context);
}
for (int i = 0; i < countDownTokens; i++) {
parser.nextToken();
}
// if we did not get the id, we need to parse the uid into the document now, after it was added
if (source.id() == null) {
if (context.id() == null) {
if (!source.flyweight()) {
throw new MapperParsingException("No id found while parsing the content source");
}
} else {
uidFieldMapper.parse(context);
if (context.docs().size() > 1) {
UidField uidField = (UidField) context.doc().getFieldable(UidFieldMapper.NAME);
assert uidField != null;
// we need to go over the docs and add it...
for (int i = 1; i < context.docs().size(); i++) {
// we don't need to add it as a full uid field in nested docs, since we don't need versioning
context.docs().get(i).add(new Field(UidFieldMapper.NAME, uidField.uid(), Field.Store.NO, Field.Index.NOT_ANALYZED));
}
}
}
}
if (context.parsedIdState() != ParseContext.ParsedIdState.PARSED) {
if (context.id() == null) {
if (!source.flyweight()) {
throw new MapperParsingException("No id mapping with [_id] found in the content, and not explicitly set");
}
} else {
// mark it as external, so we can parse it
context.parsedId(ParseContext.ParsedIdState.EXTERNAL);
idFieldMapper.parse(context);
}
}
if (parentFieldMapper != null) {
context.externalValue(source.parent());
parentFieldMapper.parse(context);
}
analyzerMapper.parse(context);
allFieldMapper.parse(context);
// validate aggregated mappers (TODO: need to be added as a phase to any field mapper)
routingFieldMapper.validate(context, source.routing());
} catch (IOException 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());