aboutsummaryrefslogtreecommitdiffstats
path: root/document/src/main/java/com/yahoo/document/json/JsonReader.java
diff options
context:
space:
mode:
Diffstat (limited to 'document/src/main/java/com/yahoo/document/json/JsonReader.java')
-rw-r--r--document/src/main/java/com/yahoo/document/json/JsonReader.java618
1 files changed, 47 insertions, 571 deletions
diff --git a/document/src/main/java/com/yahoo/document/json/JsonReader.java b/document/src/main/java/com/yahoo/document/json/JsonReader.java
index 42cc5c5b065..8c48be01799 100644
--- a/document/src/main/java/com/yahoo/document/json/JsonReader.java
+++ b/document/src/main/java/com/yahoo/document/json/JsonReader.java
@@ -6,10 +6,6 @@ import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.google.common.annotations.Beta;
import com.google.common.base.Preconditions;
-import com.yahoo.collections.Pair;
-import com.yahoo.document.ArrayDataType;
-import com.yahoo.document.CollectionDataType;
-import com.yahoo.document.DataType;
import com.yahoo.document.Document;
import com.yahoo.document.DocumentId;
import com.yahoo.document.DocumentOperation;
@@ -19,33 +15,26 @@ import com.yahoo.document.DocumentType;
import com.yahoo.document.DocumentTypeManager;
import com.yahoo.document.DocumentUpdate;
import com.yahoo.document.Field;
-import com.yahoo.document.MapDataType;
-import com.yahoo.document.PositionDataType;
-import com.yahoo.document.ReferenceDataType;
import com.yahoo.document.TestAndSetCondition;
-import com.yahoo.document.WeightedSetDataType;
-import com.yahoo.document.datatypes.CollectionFieldValue;
-import com.yahoo.document.datatypes.FieldValue;
-import com.yahoo.document.datatypes.IntegerFieldValue;
-import com.yahoo.document.datatypes.MapFieldValue;
-import com.yahoo.document.datatypes.StructuredFieldValue;
-import com.yahoo.document.datatypes.TensorFieldValue;
-import com.yahoo.document.datatypes.WeightedSet;
-import com.yahoo.document.json.TokenBuffer.Token;
+import com.yahoo.document.json.document.DocumentParser;
+import com.yahoo.document.json.readers.DocumentParseInfo;
import com.yahoo.document.update.FieldUpdate;
-import com.yahoo.document.update.MapValueUpdate;
-import com.yahoo.document.update.ValueUpdate;
-import com.yahoo.tensor.MappedTensor;
-import com.yahoo.tensor.Tensor;
-import com.yahoo.tensor.TensorType;
-import org.apache.commons.codec.binary.Base64;
import java.io.IOException;
import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.List;
import java.util.Optional;
+import static com.yahoo.document.json.document.DocumentParser.parseDocumentsFields;
+import static com.yahoo.document.json.readers.AddRemoveCreator.createAdds;
+import static com.yahoo.document.json.readers.AddRemoveCreator.createRemoves;
+import static com.yahoo.document.json.readers.CompositeReader.populateComposite;
+import static com.yahoo.document.json.readers.JsonParserHelpers.expectArrayStart;
+import static com.yahoo.document.json.readers.JsonParserHelpers.expectObjectEnd;
+import static com.yahoo.document.json.readers.JsonParserHelpers.expectObjectStart;
+import static com.yahoo.document.json.readers.MapReader.UPDATE_MATCH;
+import static com.yahoo.document.json.readers.MapReader.createMapUpdate;
+import static com.yahoo.document.json.readers.SingleValueReader.readSingleUpdate;
+
/**
* Initialize Vespa documents/updates/removes from an InputStream containing a
* valid JSON representation of a feed.
@@ -56,50 +45,18 @@ import java.util.Optional;
@Beta
public class JsonReader {
- private enum FieldOperation {
- ADD, REMOVE
+ // Only used for testing.
+ public Optional<DocumentParseInfo> parseDocument() {
+ return DocumentParser.parseDocument(parser);
}
- static final String MAP_KEY = "key";
- static final String MAP_VALUE = "value";
- static final String FIELDS = "fields";
- static final String REMOVE = "remove";
- static final String UPDATE_INCREMENT = "increment";
- static final String UPDATE_DECREMENT = "decrement";
- static final String UPDATE_MULTIPLY = "multiply";
- static final String UPDATE_DIVIDE = "divide";
- static final String TENSOR_DIMENSIONS = "dimensions";
- static final String TENSOR_CELLS = "cells";
- static final String TENSOR_ADDRESS = "address";
- static final String TENSOR_VALUE = "value";
-
- private static final String UPDATE = "update";
- private static final String PUT = "put";
- private static final String ID = "id";
- private static final String CONDITION = "condition";
- private static final String CREATE_IF_NON_EXISTENT = "create";
- private static final String UPDATE_ASSIGN = "assign";
private static final String UPDATE_REMOVE = "remove";
- private static final String UPDATE_MATCH = "match";
private static final String UPDATE_ADD = "add";
- private static final String UPDATE_ELEMENT = "element";
private final JsonParser parser;
- private TokenBuffer buffer = new TokenBuffer();
private final DocumentTypeManager typeManager;
private ReaderState state = ReaderState.AT_START;
- static class DocumentParseInfo {
- public DocumentId documentId;
- public Optional<Boolean> create = Optional.empty();
- Optional<String> condition = Optional.empty();
- SupportedOperation operationType = null;
- }
-
- enum SupportedOperation {
- PUT, UPDATE, REMOVE
- }
-
enum ReaderState {
AT_START, READING, END_OF_FEED
}
@@ -121,11 +78,11 @@ public class JsonReader {
* @param docIdString document ID.
* @return the document
*/
- public DocumentOperation readSingleDocument(SupportedOperation operationType, String docIdString) {
+ public DocumentOperation readSingleDocument(DocumentParser.SupportedOperation operationType, String docIdString) {
DocumentId docId = new DocumentId(docIdString);
- DocumentParseInfo documentParseInfo = parseToDocumentsFieldsAndInsertFieldsIntoBuffer(docId);
+ DocumentParseInfo documentParseInfo = parseDocumentsFields(parser, docId);
documentParseInfo.operationType = operationType;
- DocumentOperation operation = createDocumentOperation(documentParseInfo);
+ DocumentOperation operation = createDocumentOperation(documentParseInfo.fieldsBuffer, documentParseInfo);
operation.setCondition(TestAndSetCondition.fromConditionString(documentParseInfo.condition));
return operation;
}
@@ -133,7 +90,7 @@ public class JsonReader {
public DocumentOperation next() {
switch (state) {
case AT_START:
- JsonToken t = nextToken();
+ JsonToken t = nextToken(parser);
expectArrayStart(t);
state = ReaderState.READING;
break;
@@ -143,34 +100,34 @@ public class JsonReader {
break;
}
- Optional<DocumentParseInfo> documentParseInfo = parseDocument();
+ Optional<DocumentParseInfo> documentParseInfo = DocumentParser.parseDocument(parser);
if (! documentParseInfo.isPresent()) {
state = ReaderState.END_OF_FEED;
return null;
}
- DocumentOperation operation = createDocumentOperation(documentParseInfo.get());
+ DocumentOperation operation = createDocumentOperation(documentParseInfo.get().fieldsBuffer, documentParseInfo.get());
operation.setCondition(TestAndSetCondition.fromConditionString(documentParseInfo.get().condition));
return operation;
}
- private DocumentOperation createDocumentOperation(DocumentParseInfo documentParseInfo) {
+ private DocumentOperation createDocumentOperation(TokenBuffer buffer, DocumentParseInfo documentParseInfo) {
DocumentType documentType = getDocumentTypeFromString(documentParseInfo.documentId.getDocType(), typeManager);
final DocumentOperation documentOperation;
try {
switch (documentParseInfo.operationType) {
case PUT:
documentOperation = new DocumentPut(new Document(documentType, documentParseInfo.documentId));
- readPut((DocumentPut) documentOperation);
- verifyEndState();
+ readPut(buffer, (DocumentPut) documentOperation);
+ verifyEndState(buffer);
break;
case REMOVE:
documentOperation = new DocumentRemove(documentParseInfo.documentId);
break;
case UPDATE:
documentOperation = new DocumentUpdate(documentType, documentParseInfo.documentId);
- readUpdate((DocumentUpdate) documentOperation);
- verifyEndState();
+ readUpdate(buffer, (DocumentUpdate) documentOperation);
+ verifyEndState(buffer);
break;
default:
throw new IllegalStateException("Implementation out of sync with itself. This is a bug.");
@@ -188,83 +145,34 @@ public class JsonReader {
return documentOperation;
}
- void readUpdate(DocumentUpdate next) {
+ // Exposed for unit testing...
+ void readUpdate(TokenBuffer buffer, DocumentUpdate next) {
if (buffer.size() == 0) {
- bufferFields(nextToken());
+ buffer.bufferObject(nextToken(parser), parser);
}
- populateUpdateFromBuffer(next);
+ populateUpdateFromBuffer(buffer, next);
}
- void readPut(DocumentPut put) {
+ // Exposed for unit testing...
+ void readPut(TokenBuffer buffer, DocumentPut put) {
if (buffer.size() == 0) {
- bufferFields(nextToken());
+ buffer.bufferObject(nextToken(parser), parser);
}
- JsonToken t = buffer.currentToken();
try {
- populateComposite(put.getDocument(), t);
+ populateComposite(buffer, put.getDocument());
} catch (JsonReaderException e) {
throw JsonReaderException.addDocId(e, put.getId());
}
}
- private DocumentParseInfo parseToDocumentsFieldsAndInsertFieldsIntoBuffer(DocumentId documentId) {
- long indentLevel = 0;
- DocumentParseInfo documentParseInfo = new DocumentParseInfo();
- documentParseInfo.documentId = documentId;
- while (true) {
- // we should now be at the start of a feed operation or at the end of the feed
- JsonToken t = nextToken();
- if (t == null) {
- throw new IllegalArgumentException("Could not read document, no document?");
- }
- switch (t) {
- case START_OBJECT:
- indentLevel++;
- break;
- case END_OBJECT:
- indentLevel--;
- break;
- case START_ARRAY:
- indentLevel+=10000L;
- break;
- case END_ARRAY:
- indentLevel-=10000L;
- break;
- }
- if (indentLevel == 1 && (t == JsonToken.VALUE_TRUE || t == JsonToken.VALUE_FALSE)) {
- try {
- if (CREATE_IF_NON_EXISTENT.equals(parser.getCurrentName())) {
- documentParseInfo.create = Optional.ofNullable(parser.getBooleanValue());
- continue;
- }
- } catch (IOException e) {
- throw new RuntimeException("Got IO exception while parsing document", e);
- }
- }
- if (indentLevel == 2L && t == JsonToken.START_OBJECT) {
-
- try {
- if (!FIELDS.equals(parser.getCurrentName())) {
- continue;
- }
- } catch (IOException e) {
- throw new RuntimeException("Got IO exception while parsing document", e);
- }
- bufferFields(t);
- break;
- }
- }
- return documentParseInfo;
- }
-
- private void verifyEndState() {
+ private void verifyEndState(TokenBuffer buffer) {
Preconditions.checkState(buffer.nesting() == 0, "Nesting not zero at end of operation");
expectObjectEnd(buffer.currentToken());
Preconditions.checkState(buffer.next() == null, "Dangling data at end of operation");
Preconditions.checkState(buffer.size() == 0, "Dangling data at end of operation");
}
- private void populateUpdateFromBuffer(DocumentUpdate update) {
+ private static void populateUpdateFromBuffer(TokenBuffer buffer, DocumentUpdate update) {
expectObjectStart(buffer.currentToken());
int localNesting = buffer.nesting();
JsonToken t = buffer.next();
@@ -273,12 +181,12 @@ public class JsonReader {
expectObjectStart(t);
String fieldName = buffer.currentName();
Field field = update.getType().getField(fieldName);
- addFieldUpdates(update, field);
+ addFieldUpdates(buffer, update, field);
t = buffer.next();
}
}
- private void addFieldUpdates(DocumentUpdate update, Field field) {
+ private static void addFieldUpdates(TokenBuffer buffer, DocumentUpdate update, Field field) {
int localNesting = buffer.nesting();
FieldUpdate fieldUpdate = FieldUpdate.create(field);
@@ -286,457 +194,24 @@ public class JsonReader {
while (localNesting <= buffer.nesting()) {
switch (buffer.currentName()) {
case UPDATE_REMOVE:
- createAddsOrRemoves(field, fieldUpdate, FieldOperation.REMOVE);
+ createRemoves(buffer, field, fieldUpdate);
break;
case UPDATE_ADD:
- createAddsOrRemoves(field, fieldUpdate, FieldOperation.ADD);
+ createAdds(buffer, field, fieldUpdate);
break;
case UPDATE_MATCH:
- fieldUpdate.addValueUpdate(createMapUpdate(field));
+ fieldUpdate.addValueUpdate(createMapUpdate(buffer, field));
break;
default:
String action = buffer.currentName();
- fieldUpdate.addValueUpdate(readSingleUpdate(field.getDataType(), action));
+ fieldUpdate.addValueUpdate(readSingleUpdate(buffer, field.getDataType(), action));
}
buffer.next();
}
update.addFieldUpdate(fieldUpdate);
}
- @SuppressWarnings("rawtypes")
- private ValueUpdate createMapUpdate(Field field) {
- buffer.next();
- MapValueUpdate m = (MapValueUpdate) createMapUpdate(field.getDataType(), null, null);
- buffer.next();
- // must generate the field value in parallell with the actual
- return m;
-
- }
-
- @SuppressWarnings({ "rawtypes", "unchecked" })
- private ValueUpdate createMapUpdate(DataType currentLevel, FieldValue keyParent, FieldValue topLevelKey) {
- TokenBuffer.Token element = buffer.prefetchScalar(UPDATE_ELEMENT);
- if (UPDATE_ELEMENT.equals(buffer.currentName())) {
- buffer.next();
- }
-
- FieldValue key = keyTypeForMapUpdate(element, currentLevel);
- if (keyParent != null) {
- ((CollectionFieldValue) keyParent).add(key);
- }
- // structure is: [(match + element)*, (element + action)]
- // match will always have element, and either match or action
- if (!UPDATE_MATCH.equals(buffer.currentName())) {
- // we have reached an action...
- if (topLevelKey == null) {
- return ValueUpdate.createMap(key, readSingleUpdate(valueTypeForMapUpdate(currentLevel), buffer.currentName()));
- } else {
- return ValueUpdate.createMap(topLevelKey, readSingleUpdate(valueTypeForMapUpdate(currentLevel), buffer.currentName()));
- }
- } else {
- // next level of matching
- if (topLevelKey == null) {
- return createMapUpdate(valueTypeForMapUpdate(currentLevel), key, key);
- } else {
- return createMapUpdate(valueTypeForMapUpdate(currentLevel), key, topLevelKey);
- }
- }
- }
-
- private DataType valueTypeForMapUpdate(DataType parentType) {
- if (parentType instanceof WeightedSetDataType) {
- return DataType.INT;
- } else if (parentType instanceof CollectionDataType) {
- return ((CollectionDataType) parentType).getNestedType();
- } else if (parentType instanceof MapDataType) {
- return ((MapDataType) parentType).getValueType();
- } else {
- throw new UnsupportedOperationException("Unexpected parent type: " + parentType);
- }
- }
-
- private FieldValue keyTypeForMapUpdate(Token element, DataType expectedType) {
- FieldValue v;
- if (expectedType instanceof ArrayDataType) {
- v = new IntegerFieldValue(Integer.valueOf(element.text));
- } else if (expectedType instanceof WeightedSetDataType) {
- v = ((WeightedSetDataType) expectedType).getNestedType().createFieldValue(element.text);
- } else if (expectedType instanceof MapDataType) {
- v = ((MapDataType) expectedType).getKeyType().createFieldValue(element.text);
- } else {
- throw new IllegalArgumentException("Container type " + expectedType + " not supported for match update.");
- }
- return v;
- }
-
- @SuppressWarnings("rawtypes")
- private ValueUpdate readSingleUpdate(DataType expectedType, String action) {
- ValueUpdate update;
-
- switch (action) {
- case UPDATE_ASSIGN:
- update = (buffer.currentToken() == JsonToken.VALUE_NULL)
- ? ValueUpdate.createClear()
- : ValueUpdate.createAssign(readSingleValue(buffer.currentToken(), expectedType));
- break;
- // double is silly, but it's what is used internally anyway
- case UPDATE_INCREMENT:
- update = ValueUpdate.createIncrement(Double.valueOf(buffer.currentText()));
- break;
- case UPDATE_DECREMENT:
- update = ValueUpdate.createDecrement(Double.valueOf(buffer.currentText()));
- break;
- case UPDATE_MULTIPLY:
- update = ValueUpdate.createMultiply(Double.valueOf(buffer.currentText()));
- break;
- case UPDATE_DIVIDE:
- update = ValueUpdate.createDivide(Double.valueOf(buffer.currentText()));
- break;
- default:
- throw new IllegalArgumentException("Operation \"" + buffer.currentName() + "\" not implemented.");
- }
- return update;
- }
-
- // yes, this suppresswarnings ugliness is by intention, the code relies on
- // the contracts in the builders
- @SuppressWarnings({ "cast", "rawtypes", "unchecked" })
- private void createAddsOrRemoves(Field field, FieldUpdate update, FieldOperation op) {
- FieldValue container = field.getDataType().createFieldValue();
- FieldUpdate singleUpdate;
- int initNesting = buffer.nesting();
- JsonToken token;
-
- Preconditions.checkState(buffer.currentToken().isStructStart(), "Expected start of composite, got %s", buffer.currentToken());
- if (container instanceof CollectionFieldValue) {
- token = buffer.next();
- DataType valueType = ((CollectionFieldValue) container).getDataType().getNestedType();
- if (container instanceof WeightedSet) {
- // these are objects with string keys (which are the nested
- // types) and values which are the weight
- WeightedSet weightedSet = (WeightedSet) container;
- fillWeightedSetUpdate(initNesting, valueType, weightedSet);
- if (op == FieldOperation.REMOVE) {
- singleUpdate = FieldUpdate.createRemoveAll(field, weightedSet);
- } else {
- singleUpdate = FieldUpdate.createAddAll(field, weightedSet);
-
- }
- } else {
- List<FieldValue> arrayContents = new ArrayList<>();
- token = fillArrayUpdate(initNesting, token, valueType, arrayContents);
- if (token != JsonToken.END_ARRAY) {
- throw new IllegalStateException("Expected END_ARRAY. Got '" + token + "'.");
- }
- if (op == FieldOperation.REMOVE) {
- singleUpdate = FieldUpdate.createRemoveAll(field, arrayContents);
- } else {
- singleUpdate = FieldUpdate.createAddAll(field, arrayContents);
- }
- }
- } else {
- throw new UnsupportedOperationException(
- "Trying to add or remove from a field of a type the reader does not know how to handle: "
- + container.getClass().getName());
- }
- expectCompositeEnd(buffer.currentToken());
- update.addAll(singleUpdate);
- }
-
- private JsonToken fillArrayUpdate(int initNesting, JsonToken initToken, DataType valueType, List<FieldValue> arrayContents) {
- JsonToken token = initToken;
- while (buffer.nesting() >= initNesting) {
- arrayContents.add(readSingleValue(token, valueType));
- token = buffer.next();
- }
- return token;
- }
-
- private void fillWeightedSetUpdate(int initNesting, DataType valueType, @SuppressWarnings("rawtypes") WeightedSet weightedSet) {
- iterateThroughWeightedSet(initNesting, valueType, weightedSet);
- }
-
- @SuppressWarnings({ "rawtypes", "unchecked" })
- private void iterateThroughWeightedSet(int initNesting, DataType valueType, WeightedSet weightedSet) {
- while (buffer.nesting() >= initNesting) {
- // XXX the keys are defined in the spec to always be represented as strings
- FieldValue v = valueType.createFieldValue(buffer.currentName());
- weightedSet.put(v, Integer.valueOf(buffer.currentText()));
- buffer.next();
- }
- }
-
- // TODO populateComposite is extremely similar to add/remove, refactor
- // yes, this suppresswarnings ugliness is by intention, the code relies on the contracts in the builders
- @SuppressWarnings({ "cast", "rawtypes" })
- private void populateComposite(FieldValue parent, JsonToken token) {
- if ((token != JsonToken.START_OBJECT) && (token != JsonToken.START_ARRAY)) {
- throw new IllegalArgumentException("Expected '[' or '{'. Got '" + token + "'.");
- }
- if (parent instanceof CollectionFieldValue) {
- DataType valueType = ((CollectionFieldValue) parent).getDataType().getNestedType();
- if (parent instanceof WeightedSet) {
- fillWeightedSet(valueType, (WeightedSet) parent);
- } else {
- fillArray((CollectionFieldValue) parent, valueType);
- }
- } else if (parent instanceof MapFieldValue) {
- fillMap((MapFieldValue) parent);
- } else if (parent instanceof StructuredFieldValue) {
- fillStruct((StructuredFieldValue) parent);
- } else if (parent instanceof TensorFieldValue) {
- fillTensor((TensorFieldValue) parent);
- } else {
- throw new IllegalStateException("Has created a composite field"
- + " value the reader does not know how to handle: "
- + parent.getClass().getName() + " This is a bug. token = " + token);
- }
- expectCompositeEnd(buffer.currentToken());
- }
-
- private void expectCompositeEnd(JsonToken token) {
- Preconditions.checkState(token.isStructEnd(), "Expected end of composite, got %s", token);
- }
-
- private void fillStruct(StructuredFieldValue parent) {
- // do note the order of initializing initNesting and token is relevant for empty docs
- int initNesting = buffer.nesting();
- JsonToken token = buffer.next();
-
- while (buffer.nesting() >= initNesting) {
- Field f = getField(parent);
- try {
- FieldValue v = readSingleValue(token, f.getDataType());
- parent.setFieldValue(f, v);
- token = buffer.next();
- } catch (IllegalArgumentException e) {
- throw new JsonReaderException(f, e);
- }
- }
- }
-
- private Field getField(StructuredFieldValue parent) {
- Field f = parent.getField(buffer.currentName());
- if (f == null) {
- throw new NullPointerException("Could not get field \"" + buffer.currentName() +
- "\" in the structure of type \"" + parent.getDataType().getDataTypeName() + "\".");
- }
- return f;
- }
-
- @SuppressWarnings({ "rawtypes", "cast", "unchecked" })
- private void fillMap(MapFieldValue parent) {
- JsonToken token = buffer.currentToken();
- int initNesting = buffer.nesting();
- expectArrayStart(token);
- token = buffer.next();
- DataType keyType = parent.getDataType().getKeyType();
- DataType valueType = parent.getDataType().getValueType();
- while (buffer.nesting() >= initNesting) {
- FieldValue key = null;
- FieldValue value = null;
- expectObjectStart(token);
- token = buffer.next();
- for (int i = 0; i < 2; ++i) {
- if (MAP_KEY.equals(buffer.currentName())) {
- key = readSingleValue(token, keyType);
- } else if (MAP_VALUE.equals(buffer.currentName())) {
- value = readSingleValue(token, valueType);
- }
- token = buffer.next();
- }
- Preconditions.checkState(key != null && value != null, "Missing key or value for map entry.");
- parent.put(key, value);
-
- expectObjectEnd(token);
- token = buffer.next(); // array end or next entry
- }
- }
-
- private void expectArrayStart(JsonToken token) {
- Preconditions.checkState(token == JsonToken.START_ARRAY, "Expected start of array, got %s", token);
- }
-
- private void expectObjectStart(JsonToken token) {
- Preconditions.checkState(token == JsonToken.START_OBJECT, "Expected start of JSON object, got %s", token);
- }
-
- private void expectObjectEnd(JsonToken token) {
- Preconditions.checkState(token == JsonToken.END_OBJECT, "Expected end of JSON object, got %s", token);
- }
-
- @SuppressWarnings({ "unchecked", "rawtypes" })
- private void fillArray(CollectionFieldValue parent, DataType valueType) {
- int initNesting = buffer.nesting();
- expectArrayStart(buffer.currentToken());
- JsonToken token = buffer.next();
- while (buffer.nesting() >= initNesting) {
- parent.add(readSingleValue(token, valueType));
- token = buffer.next();
- }
- }
-
- private void fillWeightedSet(DataType valueType, @SuppressWarnings("rawtypes") WeightedSet weightedSet) {
- int initNesting = buffer.nesting();
- expectObjectStart(buffer.currentToken());
- buffer.next();
- iterateThroughWeightedSet(initNesting, valueType, weightedSet);
- }
-
- private void fillTensor(TensorFieldValue tensorFieldValue) {
- Tensor.Builder tensorBuilder = Tensor.Builder.of(tensorFieldValue.getDataType().getTensorType());
- expectObjectStart(buffer.currentToken());
- int initNesting = buffer.nesting();
- // read tensor cell fields and ignore everything else
- for (buffer.next(); buffer.nesting() >= initNesting; buffer.next()) {
- if (TENSOR_CELLS.equals(buffer.currentName()))
- readTensorCells(tensorBuilder);
- }
- expectObjectEnd(buffer.currentToken());
- tensorFieldValue.assign(tensorBuilder.build());
- }
-
- private void readTensorCells(Tensor.Builder tensorBuilder) {
- expectArrayStart(buffer.currentToken());
- int initNesting = buffer.nesting();
- for (buffer.next(); buffer.nesting() >= initNesting; buffer.next())
- readTensorCell(tensorBuilder);
- expectCompositeEnd(buffer.currentToken());
- }
-
- private void readTensorCell(Tensor.Builder tensorBuilder) {
- expectObjectStart(buffer.currentToken());
- int initNesting = buffer.nesting();
- double cellValue = 0.0;
- Tensor.Builder.CellBuilder cellBuilder = tensorBuilder.cell();
- for (buffer.next(); buffer.nesting() >= initNesting; buffer.next()) {
- String currentName = buffer.currentName();
- if (TENSOR_ADDRESS.equals(currentName)) {
- readTensorAddress(cellBuilder);
- } else if (TENSOR_VALUE.equals(currentName)) {
- cellValue = Double.valueOf(buffer.currentText());
- }
- }
- expectObjectEnd(buffer.currentToken());
- cellBuilder.value(cellValue);
- }
-
- private void readTensorAddress(MappedTensor.Builder.CellBuilder cellBuilder) {
- expectObjectStart(buffer.currentToken());
- int initNesting = buffer.nesting();
- for (buffer.next(); buffer.nesting() >= initNesting; buffer.next()) {
- String dimension = buffer.currentName();
- String label = buffer.currentText();
- cellBuilder.label(dimension, label);
- }
- expectObjectEnd(buffer.currentToken());
- }
-
- private FieldValue readSingleValue(JsonToken t, DataType expectedType) {
- if (t.isScalarValue()) {
- return readAtomic(expectedType);
- } else {
- FieldValue v = expectedType.createFieldValue();
- populateComposite(v, t);
- return v;
- }
- }
-
- private FieldValue readAtomic(DataType expectedType) {
- if (expectedType.equals(DataType.RAW)) {
- return expectedType.createFieldValue(new Base64().decode(buffer.currentText()));
- } else if (expectedType.equals(PositionDataType.INSTANCE)) {
- return PositionDataType.fromString(buffer.currentText());
- } else if (expectedType instanceof ReferenceDataType) {
- return readReferenceFieldValue(expectedType);
- } else {
- return expectedType.createFieldValue(buffer.currentText());
- }
- }
-
- private FieldValue readReferenceFieldValue(DataType expectedType) {
- final FieldValue value = expectedType.createFieldValue();
- final String refText = buffer.currentText();
- if (!refText.isEmpty()) {
- value.assign(new DocumentId(buffer.currentText()));
- }
- return value;
- }
-
- private void bufferFields(JsonToken current) {
- buffer.bufferObject(current, parser);
- }
-
- Optional<DocumentParseInfo> parseDocument() {
- // we should now be at the start of a feed operation or at the end of the feed
- JsonToken token = nextToken();
- if (token == JsonToken.END_ARRAY) {
- return Optional.empty(); // end of feed
- }
- expectObjectStart(token);
-
- DocumentParseInfo documentParseInfo = new DocumentParseInfo();
-
- while (true) {
- try {
- token = nextToken();
- if ((token == JsonToken.VALUE_TRUE || token == JsonToken.VALUE_FALSE) &&
- CREATE_IF_NON_EXISTENT.equals(parser.getCurrentName())) {
- documentParseInfo.create = Optional.of(token == JsonToken.VALUE_TRUE);
- continue;
- }
- if (token == JsonToken.VALUE_STRING && CONDITION.equals(parser.getCurrentName())) {
- documentParseInfo.condition = Optional.of(parser.getText());
- continue;
- }
- if (token == JsonToken.START_OBJECT) {
- try {
- if (!FIELDS.equals(parser.getCurrentName())) {
- throw new IllegalArgumentException("Unexpected object key: " + parser.getCurrentName());
- }
- } catch (IOException e) {
- // TODO more specific wrapping
- throw new RuntimeException(e);
- }
- bufferFields(token);
- continue;
- }
- if (token == JsonToken.END_OBJECT) {
- if (documentParseInfo.documentId == null) {
- throw new RuntimeException("Did not find document operation");
- }
- return Optional.of(documentParseInfo);
- }
- if (token == JsonToken.VALUE_STRING) {
- documentParseInfo.operationType = operationNameToOperationType(parser.getCurrentName());
- documentParseInfo.documentId = new DocumentId(parser.getText());
- continue;
- }
- throw new RuntimeException("Expected document start or document operation.");
- } catch (IOException e) {
- throw new IllegalStateException(e);
- }
-
- }
- }
-
- private static SupportedOperation operationNameToOperationType(String operationName) {
- switch (operationName) {
- case PUT:
- case ID:
- return SupportedOperation.PUT;
- case REMOVE:
- return SupportedOperation.REMOVE;
- case UPDATE:
- return SupportedOperation.UPDATE;
- default:
- throw new IllegalArgumentException(
- "Got " + operationName + " as document operation, only \"put\", " +
- "\"remove\" and \"update\" are supported.");
- }
- }
-
- DocumentType readDocumentType(DocumentId docId) {
+ public DocumentType readDocumentType(DocumentId docId) {
return getDocumentTypeFromString(docId.getDocType(), typeManager);
}
@@ -748,12 +223,13 @@ public class JsonReader {
return docType;
}
- private JsonToken nextToken() {
+ public static JsonToken nextToken(JsonParser parser) {
try {
return parser.nextValue();
} catch (IOException e) {
// Jackson is not able to recover from structural parse errors
- state = ReaderState.END_OF_FEED;
+ // TODO Do we really need to set state on exception?
+ // state = ReaderState.END_OF_FEED;
throw new RuntimeException(e);
}
}