diff options
author | freva <valerijf@yahoo-inc.com> | 2017-02-10 12:01:22 +0100 |
---|---|---|
committer | freva <valerijf@yahoo-inc.com> | 2017-02-10 12:01:22 +0100 |
commit | 47b2a2968895b3cfd32d6c709503fd8b705da017 (patch) | |
tree | bfa88cea94e1ff85f0d1a97b9c3dc1457e3322cb /document | |
parent | 3fbb0334b81893ef4495d80ba79a492f96d7d60c (diff) | |
parent | 0a1048e4bccd90fe0a2ba50e1e74aed76606838b (diff) |
Merge branch 'master' into freva/fieldpath-parsing
# Conflicts:
# document/src/main/java/com/yahoo/document/json/document/DocumentParser.java
Diffstat (limited to 'document')
5 files changed, 144 insertions, 100 deletions
diff --git a/document/src/main/java/com/yahoo/document/PositionDataType.java b/document/src/main/java/com/yahoo/document/PositionDataType.java index a2a1c6012a1..816fcd7bdf7 100644 --- a/document/src/main/java/com/yahoo/document/PositionDataType.java +++ b/document/src/main/java/com/yahoo/document/PositionDataType.java @@ -7,6 +7,10 @@ import com.yahoo.document.datatypes.Struct; import com.yahoo.geo.DegreesParser; import com.yahoo.document.serialization.XmlStream; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.util.Locale; + /** * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> */ @@ -20,6 +24,19 @@ public final class PositionDataType { private static final Field FFIELD_X = INSTANCE.getField(FIELD_X); private static final Field FFIELD_Y = INSTANCE.getField(FIELD_Y); + private static final DecimalFormat degreeFmt; + + static { + degreeFmt = new DecimalFormat("0.0#####", DecimalFormatSymbols.getInstance(Locale.ENGLISH)); + degreeFmt.setMinimumIntegerDigits(1); + degreeFmt.setMinimumFractionDigits(1); + degreeFmt.setMaximumFractionDigits(6); + } + + static String fmtD(double degrees) { + return degreeFmt.format(degrees); + } + private PositionDataType() { // unreachable } @@ -29,10 +46,10 @@ public final class PositionDataType { double ns = getYValue(pos).getInteger() / 1.0e6; double ew = getXValue(pos).getInteger() / 1.0e6; buf.append(ns < 0 ? "S" : "N"); - buf.append(ns < 0 ? (-ns) : ns); + buf.append(fmtD(ns < 0 ? (-ns) : ns)); buf.append(";"); buf.append(ew < 0 ? "W" : "E"); - buf.append(ew < 0 ? (-ew) : ew); + buf.append(fmtD(ew < 0 ? (-ew) : ew)); return buf.toString(); } 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 51c623cdbda..e8cade15254 100644 --- a/document/src/main/java/com/yahoo/document/json/JsonReader.java +++ b/document/src/main/java/com/yahoo/document/json/JsonReader.java @@ -25,7 +25,6 @@ import java.io.InputStream; import java.util.Optional; import static com.yahoo.document.json.JsonReader.ReaderState.END_OF_FEED; -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; @@ -41,14 +40,14 @@ import static com.yahoo.document.json.readers.SingleValueReader.readSingleUpdate * valid JSON representation of a feed. * * @author Steinar Knutsen - * @since 5.1.25 + * @author dybis */ @Beta public class JsonReader { - // Only used for testing. public Optional<DocumentParseInfo> parseDocument() throws IOException { - return DocumentParser.parseDocument(parser); + DocumentParser documentParser = new DocumentParser(parser); + return documentParser.parse(Optional.empty()); } private static final String UPDATE_REMOVE = "remove"; @@ -81,9 +80,10 @@ public class JsonReader { */ public DocumentOperation readSingleDocument(DocumentParser.SupportedOperation operationType, String docIdString) { DocumentId docId = new DocumentId(docIdString); - DocumentParseInfo documentParseInfo = null; + final DocumentParseInfo documentParseInfo; try { - documentParseInfo = parseDocumentsFields(parser, docId); + DocumentParser documentParser = new DocumentParser(parser); + documentParseInfo = documentParser.parse(Optional.of(docId)).get(); } catch (IOException e) { state = END_OF_FEED; throw new RuntimeException(e); @@ -108,7 +108,7 @@ public class JsonReader { } Optional<DocumentParseInfo> documentParseInfo; try { - documentParseInfo = DocumentParser.parseDocument(parser); + documentParseInfo = parseDocument(); } catch (IOException r) { // Jackson is not able to recover from structural parse errors state = END_OF_FEED; diff --git a/document/src/main/java/com/yahoo/document/json/TokenBuffer.java b/document/src/main/java/com/yahoo/document/json/TokenBuffer.java index edb2ba0be00..4902dc65f3d 100644 --- a/document/src/main/java/com/yahoo/document/json/TokenBuffer.java +++ b/document/src/main/java/com/yahoo/document/json/TokenBuffer.java @@ -64,7 +64,7 @@ public class TokenBuffer { return buffer.peekFirst().text; } - int size() { + public int size() { return buffer.size(); } diff --git a/document/src/main/java/com/yahoo/document/json/document/DocumentParser.java b/document/src/main/java/com/yahoo/document/json/document/DocumentParser.java index 1c3f3af58b0..a9fad40912b 100644 --- a/document/src/main/java/com/yahoo/document/json/document/DocumentParser.java +++ b/document/src/main/java/com/yahoo/document/json/document/DocumentParser.java @@ -9,8 +9,11 @@ import com.yahoo.document.json.readers.DocumentParseInfo; import java.io.IOException; import java.util.Optional; -import static com.yahoo.document.json.readers.JsonParserHelpers.expectObjectStart; - +/** + * Parses a document operation. + * + * @author dybis + */ public class DocumentParser { public enum SupportedOperation { PUT, UPDATE, REMOVE @@ -23,56 +26,101 @@ public class DocumentParser { public static final String FIELDS = "fields"; public static final String FIELDPATHS = "fieldpaths"; public static final String REMOVE = "remove"; + private final JsonParser parser; + private long indentLevel; - public static Optional<DocumentParseInfo> parseDocument(JsonParser parser) throws IOException { - // we should now be at the start of a feed operation or at the end of the feed - JsonToken token = parser.nextValue(); - if (token == JsonToken.END_ARRAY) { - return Optional.empty(); // end of feed - } - expectObjectStart(token); + public DocumentParser(JsonParser parser) { + this.parser = parser; + } + public Optional<DocumentParseInfo> parse(Optional<DocumentId> documentIdArg) throws IOException { + indentLevel = 0; DocumentParseInfo documentParseInfo = new DocumentParseInfo(); + if (documentIdArg.isPresent()) { + documentParseInfo.documentId = documentIdArg.get(); + } + do { + parseOneItem(documentParseInfo, documentIdArg.isPresent() /* doc id set externally */); + } while (indentLevel > 0L); + + if (documentParseInfo.documentId != null) { + return Optional.of(documentParseInfo); + } + return Optional.empty(); + } - while (true) { + private void parseOneItem(DocumentParseInfo documentParseInfo, boolean docIdAndOperationIsSetExternally) throws IOException { + parser.nextValue(); + processIndent(); + if (parser.getCurrentName() == null) { + return; + } + if (indentLevel == 1L) { + handleIdentLevelOne(documentParseInfo, docIdAndOperationIsSetExternally); + } else if (indentLevel == 2L) { + handleIdentLevelTwo(documentParseInfo); + } + } + + private void processIndent() throws IOException { + JsonToken currentToken = parser.currentToken(); + if (currentToken == null) { + throw new IllegalArgumentException("Could not read document, no document?"); + } + switch (currentToken) { + case START_OBJECT: + indentLevel++; + break; + case END_OBJECT: + indentLevel--; + return; + case START_ARRAY: + indentLevel+=10000L; + break; + case END_ARRAY: + indentLevel-=10000L; + break; + } + } + + private void handleIdentLevelOne(DocumentParseInfo documentParseInfo, boolean docIdAndOperationIsSetExternally) + throws IOException { + JsonToken currentToken = parser.getCurrentToken(); + if (currentToken == JsonToken.VALUE_TRUE || currentToken == JsonToken.VALUE_FALSE) { try { - token = parser.nextValue(); - 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); - } - documentParseInfo.fieldsBuffer.bufferObject(token, parser); - 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; + if (CREATE_IF_NON_EXISTENT.equals(parser.getCurrentName())) { + documentParseInfo.create = Optional.ofNullable(parser.getBooleanValue()); + return; } - throw new RuntimeException("Expected document start or document operation."); } catch (IOException e) { - throw new IllegalStateException(e); + throw new RuntimeException("Got IO exception while parsing document", e); + } + } + if ((currentToken == JsonToken.VALUE_TRUE || currentToken == JsonToken.VALUE_FALSE) && + CREATE_IF_NON_EXISTENT.equals(parser.getCurrentName())) { + documentParseInfo.create = Optional.of(currentToken == JsonToken.VALUE_TRUE); + } else if (currentToken == JsonToken.VALUE_STRING && CONDITION.equals(parser.getCurrentName())) { + documentParseInfo.condition = Optional.of(parser.getText()); + } else if (currentToken == JsonToken.VALUE_STRING) { + // Value is expected to be set in the header not in the document. Ignore any unknown field + // as well. + if (! docIdAndOperationIsSetExternally) { + documentParseInfo.operationType = operationNameToOperationType(parser.getCurrentName()); + documentParseInfo.documentId = new DocumentId(parser.getText()); + } + } + } + + private void handleIdentLevelTwo(DocumentParseInfo documentParseInfo) { + try { + JsonToken currentToken = parser.getCurrentToken(); + // "Fields" opens a dictionary and is therefore on level two which might be surprising. + if (currentToken == JsonToken.START_OBJECT && FIELDS.equals(parser.getCurrentName())) { + documentParseInfo.fieldsBuffer.bufferObject(currentToken, parser); + processIndent(); } + } catch (IOException e) { + throw new RuntimeException("Got IO exception while parsing document", e); } } @@ -91,50 +139,4 @@ public class DocumentParser { "\"remove\" and \"update\" are supported."); } } - - public static DocumentParseInfo parseDocumentsFields(JsonParser parser, DocumentId documentId) throws IOException { - 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 = parser.nextValue(); - 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; - } - try { - if (indentLevel == 0L && t == JsonToken.END_OBJECT) { - break; - } else if (indentLevel == 1L && (t == JsonToken.VALUE_TRUE || t == JsonToken.VALUE_FALSE)) { - if (CREATE_IF_NON_EXISTENT.equals(parser.getCurrentName())) { - documentParseInfo.create = Optional.ofNullable(parser.getBooleanValue()); - } - } else if (indentLevel == 2L && t == JsonToken.START_OBJECT && FIELDS.equals(parser.getCurrentName())) { - documentParseInfo.fieldsBuffer.bufferObject(t, parser); - indentLevel--; - } else if (indentLevel == 10001L && t == JsonToken.START_ARRAY && FIELDPATHS.equals(parser.getCurrentName())) { - documentParseInfo.fieldpathsBuffer.bufferArray(t, parser); - indentLevel -= 10000L; - } - } catch (IOException e) { - throw new RuntimeException("Got IO exception while parsing document", e); - } - } - - return documentParseInfo; - } } diff --git a/document/src/test/java/com/yahoo/document/PositionTypeTestCase.java b/document/src/test/java/com/yahoo/document/PositionTypeTestCase.java index 3fb9c8d3f9e..1e05c46f898 100644 --- a/document/src/test/java/com/yahoo/document/PositionTypeTestCase.java +++ b/document/src/test/java/com/yahoo/document/PositionTypeTestCase.java @@ -41,4 +41,29 @@ public class PositionTypeTestCase { assertEquals("foo.position", PositionDataType.getPositionSummaryFieldName("foo")); assertEquals("foo.distance", PositionDataType.getDistanceSummaryFieldName("foo")); } + + @Test + public void requireFixedPointFormat() { + assertEquals("0.0", PositionDataType.fmtD(0)); + assertEquals("123.0", PositionDataType.fmtD(123)); + assertEquals("0.000123", PositionDataType.fmtD(0.000123)); + assertEquals("0.000001", PositionDataType.fmtD(0.000001)); + assertEquals("0.0", PositionDataType.fmtD(0.0000004)); + assertEquals("999.123456", PositionDataType.fmtD(999.1234564)); + + Struct val1 = PositionDataType.valueOf(0, 0); + assertEquals("N0.0;E0.0", PositionDataType.renderAsString(val1)); + + Struct val2 = PositionDataType.valueOf(-1, -1); + assertEquals("S0.000001;W0.000001", PositionDataType.renderAsString(val2)); + + Struct val3 = PositionDataType.valueOf(123456789, 87654321); + assertEquals("N87.654321;E123.456789", PositionDataType.renderAsString(val3)); + + Struct val4 = PositionDataType.valueOf(Integer.MIN_VALUE, Integer.MIN_VALUE); + assertEquals("S2147.483648;W2147.483648", PositionDataType.renderAsString(val4)); + + Struct val5 = PositionDataType.valueOf(Integer.MAX_VALUE, Integer.MAX_VALUE); + assertEquals("N2147.483647;E2147.483647", PositionDataType.renderAsString(val5)); + } } |