diff --git a/core/src/main/java/org/elasticsearch/action/admin/indices/analyze/DetailAnalyzeResponse.java b/core/src/main/java/org/elasticsearch/action/admin/indices/analyze/DetailAnalyzeResponse.java index c67c0360230..2d1ba22b989 100644 --- a/core/src/main/java/org/elasticsearch/action/admin/indices/analyze/DetailAnalyzeResponse.java +++ b/core/src/main/java/org/elasticsearch/action/admin/indices/analyze/DetailAnalyzeResponse.java @@ -292,7 +292,7 @@ public class DetailAnalyzeResponse implements Streamable, ToXContent { public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { builder.startObject(); builder.field(Fields.NAME, name); - builder.field(Fields.FILTERED_TEXT, texts); + builder.array(Fields.FILTERED_TEXT, texts); builder.endObject(); return builder; } diff --git a/core/src/main/java/org/elasticsearch/common/xcontent/XContent.java b/core/src/main/java/org/elasticsearch/common/xcontent/XContent.java index 83facb00f00..c73f5f19d25 100644 --- a/core/src/main/java/org/elasticsearch/common/xcontent/XContent.java +++ b/core/src/main/java/org/elasticsearch/common/xcontent/XContent.java @@ -19,7 +19,6 @@ package org.elasticsearch.common.xcontent; -import org.elasticsearch.common.Strings; import org.elasticsearch.common.bytes.BytesReference; import java.io.IOException; diff --git a/core/src/main/java/org/elasticsearch/common/xcontent/XContentBuilder.java b/core/src/main/java/org/elasticsearch/common/xcontent/XContentBuilder.java index 5274773b994..32317d8e9d5 100644 --- a/core/src/main/java/org/elasticsearch/common/xcontent/XContentBuilder.java +++ b/core/src/main/java/org/elasticsearch/common/xcontent/XContentBuilder.java @@ -20,7 +20,6 @@ package org.elasticsearch.common.xcontent; import org.apache.lucene.util.BytesRef; -import org.elasticsearch.common.Strings; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.geo.GeoPoint; import org.elasticsearch.common.io.BytesStream; @@ -34,11 +33,10 @@ import org.joda.time.ReadableInstant; import org.joda.time.format.DateTimeFormatter; import org.joda.time.format.ISODateTimeFormat; +import java.io.Flushable; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.math.BigDecimal; -import java.math.RoundingMode; import java.nio.file.Path; import java.util.Calendar; import java.util.Collections; @@ -46,32 +44,96 @@ import java.util.Date; import java.util.HashMap; import java.util.Locale; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.concurrent.TimeUnit; /** * A utility to build XContent (ie json). */ -public final class XContentBuilder implements BytesStream, Releasable { - - public static final DateTimeFormatter defaultDatePrinter = ISODateTimeFormat.dateTime().withZone(DateTimeZone.UTC); +public final class XContentBuilder implements BytesStream, Releasable, Flushable { + /** + * Create a new {@link XContentBuilder} using the given {@link XContent} content. + *

+ * The builder uses an internal {@link BytesStreamOutput} output stream to build the content. + *

+ * + * @param xContent the {@link XContent} + * @return a new {@link XContentBuilder} + * @throws IOException if an {@link IOException} occurs while building the content + */ public static XContentBuilder builder(XContent xContent) throws IOException { return new XContentBuilder(xContent, new BytesStreamOutput()); } + /** + * Create a new {@link XContentBuilder} using the given {@link XContent} content and some inclusive and/or exclusive filters. + *

+ * The builder uses an internal {@link BytesStreamOutput} output stream to build the content. When both exclusive and + * inclusive filters are provided, the underlying builder will first use exclusion filters to remove fields and then will check the + * remaining fields against the inclusive filters. + *

+ * + * @param xContent the {@link XContent} + * @param includes the inclusive filters: only fields and objects that match the inclusive filters will be written to the output. + * @param excludes the exclusive filters: only fields and objects that don't match the exclusive filters will be written to the output. + * @throws IOException if an {@link IOException} occurs while building the content + */ public static XContentBuilder builder(XContent xContent, Set includes, Set excludes) throws IOException { return new XContentBuilder(xContent, new BytesStreamOutput(), includes, excludes); } - private XContentGenerator generator; + public static final DateTimeFormatter DEFAULT_DATE_PRINTER = ISODateTimeFormat.dateTime().withZone(DateTimeZone.UTC); + private static final Map, Writer> WRITERS; + static { + Map, Writer> writers = new HashMap<>(); + writers.put(Boolean.class, (b, v) -> b.value((Boolean) v)); + writers.put(Byte.class, (b, v) -> b.value((Byte) v)); + writers.put(byte[].class, (b, v) -> b.value((byte[]) v)); + writers.put(BytesRef.class, (b, v) -> b.binaryValue((BytesRef) v)); + writers.put(Date.class, (b, v) -> b.value((Date) v)); + writers.put(Double.class, (b, v) -> b.value((Double) v)); + writers.put(double[].class, (b, v) -> b.values((double[]) v)); + writers.put(Float.class, (b, v) -> b.value((Float) v)); + writers.put(float[].class, (b, v) -> b.values((float[]) v)); + writers.put(GeoPoint.class, (b, v) -> b.value((GeoPoint) v)); + writers.put(Integer.class, (b, v) -> b.value((Integer) v)); + writers.put(int[].class, (b, v) -> b.values((int[]) v)); + writers.put(Long.class, (b, v) -> b.value((Long) v)); + writers.put(long[].class, (b, v) -> b.values((long[]) v)); + writers.put(Short.class, (b, v) -> b.value((Short) v)); + writers.put(short[].class, (b, v) -> b.values((short[]) v)); + writers.put(String.class, (b, v) -> b.value((String) v)); + writers.put(String[].class, (b, v) -> b.values((String[]) v)); + writers.put(Text.class, (b, v) -> b.value((Text) v)); + + WRITERS = Collections.unmodifiableMap(writers); + } + + @FunctionalInterface + private interface Writer { + void write(XContentBuilder builder, Object value) throws IOException; + } + + /** + * XContentGenerator used to build the XContent object + */ + private final XContentGenerator generator; + + /** + * Output stream to which the built object is written + */ private final OutputStream bos; + /** + * When this flag is set to true, some types of values are written in a format easier to read for a human. + */ private boolean humanReadable = false; /** - * Constructs a new builder using the provided xcontent and an OutputStream. Make sure + * Constructs a new builder using the provided XContent and an OutputStream. Make sure * to call {@link #close()} when the builder is done with. */ public XContentBuilder(XContent xContent, OutputStream bos) throws IOException { @@ -79,7 +141,7 @@ public final class XContentBuilder implements BytesStream, Releasable { } /** - * Constructs a new builder using the provided xcontent, an OutputStream and + * Constructs a new builder using the provided XContent, an OutputStream and * some filters. If filters are specified, only those values matching a * filter will be written to the output stream. Make sure to call * {@link #close()} when the builder is done with. @@ -117,377 +179,74 @@ public final class XContentBuilder implements BytesStream, Releasable { return generator.isPrettyPrint(); } + /** + * Indicate that the current {@link XContentBuilder} must write a line feed ("\n") + * at the end of the built object. + *

+ * This only applies for JSON XContent type. It has no effect for other types. + */ public XContentBuilder lfAtEnd() { generator.usePrintLineFeedAtEnd(); return this; } + /** + * Set the "human readable" flag. Once set, some types of values are written in a + * format easier to read for a human. + */ public XContentBuilder humanReadable(boolean humanReadable) { this.humanReadable = humanReadable; return this; } + /** + * @return the value of the "human readable" flag. When the value is equal to true, + * some types of values are written in a format easier to read for a human. + */ public boolean humanReadable() { return this.humanReadable; } - public XContentBuilder field(String name, ToXContent xContent) throws IOException { - field(name); - xContent.toXContent(this, ToXContent.EMPTY_PARAMS); - return this; - } - - public XContentBuilder field(String name, ToXContent xContent, ToXContent.Params params) throws IOException { - field(name); - xContent.toXContent(this, params); - return this; - } - - public XContentBuilder startObject(String name) throws IOException { - field(name); - startObject(); - return this; - } + //////////////////////////////////////////////////////////////////////////// + // Structure (object, array, field, null values...) + ////////////////////////////////// public XContentBuilder startObject() throws IOException { generator.writeStartObject(); return this; } + public XContentBuilder startObject(String name) throws IOException { + return field(name).startObject(); + } + public XContentBuilder endObject() throws IOException { generator.writeEndObject(); return this; } - public XContentBuilder array(String name, String... values) throws IOException { - startArray(name); - for (String value : values) { - value(value); - } - endArray(); - return this; - } - - public XContentBuilder array(String name, Object... values) throws IOException { - startArray(name); - for (Object value : values) { - value(value); - } - endArray(); - return this; - } - - public XContentBuilder startArray(String name) throws IOException { - field(name); - startArray(); - return this; - } - public XContentBuilder startArray() throws IOException { generator.writeStartArray(); return this; } + public XContentBuilder startArray(String name) throws IOException { + return field(name).startArray(); + } + public XContentBuilder endArray() throws IOException { generator.writeEndArray(); return this; } public XContentBuilder field(String name) throws IOException { - if (name == null) { - throw new IllegalArgumentException("field name cannot be null"); - } + ensureNameNotNull(name); generator.writeFieldName(name); return this; } - public XContentBuilder field(String name, char[] value, int offset, int length) throws IOException { - field(name); - if (value == null) { - generator.writeNull(); - } else { - generator.writeString(value, offset, length); - } - return this; - } - - public XContentBuilder field(String name, String value) throws IOException { - field(name); - if (value == null) { - generator.writeNull(); - } else { - generator.writeString(value); - } - return this; - } - - public XContentBuilder field(String name, Integer value) throws IOException { - field(name); - if (value == null) { - generator.writeNull(); - } else { - generator.writeNumber(value.intValue()); - } - return this; - } - - public XContentBuilder field(String name, int value) throws IOException { - field(name); - generator.writeNumber(value); - return this; - } - - public XContentBuilder field(String name, Long value) throws IOException { - field(name); - if (value == null) { - generator.writeNull(); - } else { - generator.writeNumber(value.longValue()); - } - return this; - } - - public XContentBuilder field(String name, long value) throws IOException { - field(name); - generator.writeNumber(value); - return this; - } - - public XContentBuilder field(String name, Float value) throws IOException { - field(name); - if (value == null) { - generator.writeNull(); - } else { - generator.writeNumber(value.floatValue()); - } - return this; - } - - public XContentBuilder field(String name, float value) throws IOException { - field(name); - generator.writeNumber(value); - return this; - } - - public XContentBuilder field(String name, Double value) throws IOException { - field(name); - if (value == null) { - generator.writeNull(); - } else { - generator.writeNumber(value); - } - return this; - } - - public XContentBuilder field(String name, double value) throws IOException { - field(name); - generator.writeNumber(value); - return this; - } - - public XContentBuilder field(String name, BigDecimal value) throws IOException { - return field(name, value, value.scale(), RoundingMode.HALF_UP, true); - } - - public XContentBuilder field(String name, BigDecimal value, int scale, RoundingMode rounding, boolean toDouble) throws IOException { - field(name); - if (value == null) { - generator.writeNull(); - } else { - if (toDouble) { - try { - generator.writeNumber(value.setScale(scale, rounding).doubleValue()); - } catch (ArithmeticException e) { - generator.writeString(value.toEngineeringString()); - } - } else { - generator.writeString(value.toEngineeringString()); - } - } - return this; - } - - /** - * Writes the binary content of the given BytesRef - * Use {@link org.elasticsearch.common.xcontent.XContentParser#binaryValue()} to read the value back - */ - public XContentBuilder field(String name, BytesRef value) throws IOException { - field(name); - generator.writeBinary(value.bytes, value.offset, value.length); - return this; - } - - /** - * Writes the binary content of the given BytesReference - * Use {@link org.elasticsearch.common.xcontent.XContentParser#binaryValue()} to read the value back - */ - public XContentBuilder field(String name, BytesReference value) throws IOException { - field(name); - final BytesRef ref = value.toBytesRef(); - generator.writeBinary(ref.bytes, ref.offset, ref.length); - return this; - } - - /** - * Writes the binary content of the given BytesRef as UTF-8 bytes - * Use {@link XContentParser#utf8Bytes()} to read the value back - */ - public XContentBuilder utf8Field(String name, BytesRef value) throws IOException { - field(name); - generator.writeUTF8String(value.bytes, value.offset, value.length); - return this; - } - - public XContentBuilder field(String name, Text value) throws IOException { - field(name); - if (value.hasString()) { - generator.writeString(value.string()); - } else { - // TODO: TextBytesOptimization we can use a buffer here to convert it? maybe add a request to jackson to support InputStream as well? - final BytesRef ref = value.bytes().toBytesRef(); - generator.writeUTF8String(ref.bytes, ref.offset, ref.length); - } - return this; - } - - public XContentBuilder field(String name, byte[] value, int offset, int length) throws IOException { - field(name); - generator.writeBinary(value, offset, length); - return this; - } - - public XContentBuilder field(String name, Map value) throws IOException { - field(name); - value(value); - return this; - } - - public XContentBuilder field(String name, Iterable value) throws IOException { - if (value instanceof Path) { - //treat Paths as single value - field(name); - value(value); - } else { - startArray(name); - for (Object o : value) { - value(o); - } - endArray(); - } - return this; - } - - public XContentBuilder field(String name, boolean... value) throws IOException { - startArray(name); - for (boolean o : value) { - value(o); - } - endArray(); - return this; - } - - public XContentBuilder field(String name, String... value) throws IOException { - startArray(name); - for (String o : value) { - value(o); - } - endArray(); - return this; - } - - public XContentBuilder field(String name, Object... value) throws IOException { - startArray(name); - for (Object o : value) { - value(o); - } - endArray(); - return this; - } - - public XContentBuilder field(String name, int... value) throws IOException { - startArray(name); - for (Object o : value) { - value(o); - } - endArray(); - return this; - } - - public XContentBuilder field(String name, long... value) throws IOException { - startArray(name); - for (Object o : value) { - value(o); - } - endArray(); - return this; - } - - public XContentBuilder field(String name, float... value) throws IOException { - startArray(name); - for (Object o : value) { - value(o); - } - endArray(); - return this; - } - - public XContentBuilder field(String name, double... value) throws IOException { - startArray(name); - for (Object o : value) { - value(o); - } - endArray(); - return this; - } - - public XContentBuilder field(String name, Object value) throws IOException { - field(name); - writeValue(value); - return this; - } - - public XContentBuilder value(Object value) throws IOException { - writeValue(value); - return this; - } - - public XContentBuilder field(String name, boolean value) throws IOException { - field(name); - generator.writeBoolean(value); - return this; - } - - public XContentBuilder field(String name, byte[] value) throws IOException { - field(name); - if (value == null) { - generator.writeNull(); - } else { - generator.writeBinary(value); - } - return this; - } - - public XContentBuilder field(String name, ReadableInstant date) throws IOException { - field(name); - return value(date); - } - - public XContentBuilder field(String name, ReadableInstant date, DateTimeFormatter formatter) throws IOException { - field(name); - return value(date, formatter); - } - - public XContentBuilder field(String name, Date date) throws IOException { - field(name); - return value(date); - } - - public XContentBuilder field(String name, Date date, DateTimeFormatter formatter) throws IOException { - field(name); - return value(date, formatter); - } - public XContentBuilder nullField(String name) throws IOException { + ensureNameNotNull(name); generator.writeNullField(name); return this; } @@ -497,21 +256,644 @@ public final class XContentBuilder implements BytesStream, Releasable { return this; } - public XContentBuilder rawField(String fieldName, InputStream content) throws IOException { - generator.writeRawField(fieldName, content); + //////////////////////////////////////////////////////////////////////////// + // Boolean + ////////////////////////////////// + + public XContentBuilder field(String name, Boolean value) throws IOException { + return (value == null) ? nullField(name) : field(name, value.booleanValue()); + } + + public XContentBuilder field(String name, boolean value) throws IOException { + ensureNameNotNull(name); + generator.writeBooleanField(name, value); return this; } - public XContentBuilder rawField(String fieldName, BytesReference content) throws IOException { - generator.writeRawField(fieldName, content); + public XContentBuilder array(String name, boolean[] values) throws IOException { + return field(name).values(values); + } + + private XContentBuilder values(boolean[] values) throws IOException { + if (values == null) { + return nullValue(); + } + startArray(); + for (boolean b : values) { + value(b); + } + endArray(); return this; } - public XContentBuilder rawValue(BytesReference content) throws IOException { - generator.writeRawValue(content); + public XContentBuilder value(Boolean value) throws IOException { + return (value == null) ? nullValue() : value(value.booleanValue()); + } + + public XContentBuilder value(boolean value) throws IOException { + generator.writeBoolean(value); return this; } + //////////////////////////////////////////////////////////////////////////// + // Byte + ////////////////////////////////// + + public XContentBuilder field(String name, Byte value) throws IOException { + return (value == null) ? nullField(name) : field(name, value.byteValue()); + } + + public XContentBuilder field(String name, byte value) throws IOException { + return field(name).value(value); + } + + public XContentBuilder value(Byte value) throws IOException { + return (value == null) ? nullValue() : value(value.byteValue()); + } + + public XContentBuilder value(byte value) throws IOException { + generator.writeNumber(value); + return this; + } + + //////////////////////////////////////////////////////////////////////////// + // Double + ////////////////////////////////// + + public XContentBuilder field(String name, Double value) throws IOException { + return (value == null) ? nullField(name) : field(name, value.doubleValue()); + } + + public XContentBuilder field(String name, double value) throws IOException { + ensureNameNotNull(name); + generator.writeNumberField(name, value); + return this; + } + + public XContentBuilder array(String name, double[] values) throws IOException { + return field(name).values(values); + } + + private XContentBuilder values(double[] values) throws IOException { + if (values == null) { + return nullValue(); + } + startArray(); + for (double b : values) { + value(b); + } + endArray(); + return this; + } + + public XContentBuilder value(Double value) throws IOException { + return (value == null) ? nullValue() : value(value.doubleValue()); + } + + public XContentBuilder value(double value) throws IOException { + generator.writeNumber(value); + return this; + } + + //////////////////////////////////////////////////////////////////////////// + // Float + ////////////////////////////////// + + public XContentBuilder field(String name, Float value) throws IOException { + return (value == null) ? nullField(name) : field(name, value.floatValue()); + } + + public XContentBuilder field(String name, float value) throws IOException { + ensureNameNotNull(name); + generator.writeNumberField(name, value); + return this; + } + + public XContentBuilder array(String name, float[] values) throws IOException { + return field(name).values(values); + } + + private XContentBuilder values(float[] values) throws IOException { + if (values == null) { + return nullValue(); + } + startArray(); + for (float f : values) { + value(f); + } + endArray(); + return this; + } + + public XContentBuilder value(Float value) throws IOException { + return (value == null) ? nullValue() : value(value.floatValue()); + } + + public XContentBuilder value(float value) throws IOException { + generator.writeNumber(value); + return this; + } + + //////////////////////////////////////////////////////////////////////////// + // Integer + ////////////////////////////////// + + public XContentBuilder field(String name, Integer value) throws IOException { + return (value == null) ? nullField(name) : field(name, value.intValue()); + } + + public XContentBuilder field(String name, int value) throws IOException { + ensureNameNotNull(name); + generator.writeNumberField(name, value); + return this; + } + + public XContentBuilder array(String name, int[] values) throws IOException { + return field(name).values(values); + } + + private XContentBuilder values(int[] values) throws IOException { + if (values == null) { + return nullValue(); + } + startArray(); + for (int i : values) { + value(i); + } + endArray(); + return this; + } + + public XContentBuilder value(Integer value) throws IOException { + return (value == null) ? nullValue() : value(value.intValue()); + } + + public XContentBuilder value(int value) throws IOException { + generator.writeNumber(value); + return this; + } + + //////////////////////////////////////////////////////////////////////////// + // Long + ////////////////////////////////// + + public XContentBuilder field(String name, Long value) throws IOException { + return (value == null) ? nullField(name) : field(name, value.longValue()); + } + + public XContentBuilder field(String name, long value) throws IOException { + ensureNameNotNull(name); + generator.writeNumberField(name, value); + return this; + } + + public XContentBuilder array(String name, long[] values) throws IOException { + return field(name).values(values); + } + + private XContentBuilder values(long[] values) throws IOException { + if (values == null) { + return nullValue(); + } + startArray(); + for (long l : values) { + value(l); + } + endArray(); + return this; + } + + public XContentBuilder value(Long value) throws IOException { + return (value == null) ? nullValue() : value(value.longValue()); + } + + public XContentBuilder value(long value) throws IOException { + generator.writeNumber(value); + return this; + } + + //////////////////////////////////////////////////////////////////////////// + // Short + ////////////////////////////////// + + public XContentBuilder field(String name, Short value) throws IOException { + return (value == null) ? nullField(name) : field(name, value.shortValue()); + } + + public XContentBuilder field(String name, short value) throws IOException { + return field(name).value(value); + } + + public XContentBuilder array(String name, short[] values) throws IOException { + return field(name).values(values); + } + + private XContentBuilder values(short[] values) throws IOException { + if (values == null) { + return nullValue(); + } + startArray(); + for (short s : values) { + value(s); + } + endArray(); + return this; + } + + public XContentBuilder value(Short value) throws IOException { + return (value == null) ? nullValue() : value(value.shortValue()); + } + + public XContentBuilder value(short value) throws IOException { + generator.writeNumber(value); + return this; + } + + //////////////////////////////////////////////////////////////////////////// + // String + ////////////////////////////////// + + public XContentBuilder field(String name, String value) throws IOException { + if (value == null) { + return nullField(name); + } + ensureNameNotNull(name); + generator.writeStringField(name, value); + return this; + } + + public XContentBuilder array(String name, String... values) throws IOException { + return field(name).values(values); + } + + private XContentBuilder values(String[] values) throws IOException { + if (values == null) { + return nullValue(); + } + startArray(); + for (String s : values) { + value(s); + } + endArray(); + return this; + } + + public XContentBuilder value(String value) throws IOException { + if (value == null) { + return nullValue(); + } + generator.writeString(value); + return this; + } + + //////////////////////////////////////////////////////////////////////////// + // Binary + ////////////////////////////////// + + public XContentBuilder field(String name, byte[] value) throws IOException { + if (value == null) { + return nullField(name); + } + ensureNameNotNull(name); + generator.writeBinaryField(name, value); + return this; + } + + public XContentBuilder value(byte[] value) throws IOException { + if (value == null) { + return nullValue(); + } + generator.writeBinary(value); + return this; + } + + public XContentBuilder field(String name, byte[] value, int offset, int length) throws IOException { + return field(name).value(value, offset, length); + } + + public XContentBuilder value(byte[] value, int offset, int length) throws IOException { + if (value == null) { + return nullValue(); + } + generator.writeBinary(value, offset, length); + return this; + } + + /** + * Writes the binary content of the given {@link BytesRef}. + * + * Use {@link org.elasticsearch.common.xcontent.XContentParser#binaryValue()} to read the value back + */ + public XContentBuilder field(String name, BytesRef value) throws IOException { + return field(name).binaryValue(value); + } + + /** + * Writes the binary content of the given {@link BytesRef} as UTF-8 bytes. + * + * Use {@link XContentParser#utf8Bytes()} to read the value back + */ + public XContentBuilder utf8Field(String name, BytesRef value) throws IOException { + return field(name).utf8Value(value); + } + + /** + * Writes the binary content of the given {@link BytesRef}. + * + * Use {@link org.elasticsearch.common.xcontent.XContentParser#binaryValue()} to read the value back + */ + public XContentBuilder binaryValue(BytesRef value) throws IOException { + if (value == null) { + return nullValue(); + } + value(value.bytes, value.offset, value.length); + return this; + } + + /** + * Writes the binary content of the given {@link BytesRef} as UTF-8 bytes. + * + * Use {@link XContentParser#utf8Bytes()} to read the value back + */ + public XContentBuilder utf8Value(BytesRef value) throws IOException { + if (value == null) { + return nullValue(); + } + generator.writeUTF8String(value.bytes, value.offset, value.length); + return this; + } + + /** + * Writes the binary content of the given {@link BytesReference}. + * + * Use {@link org.elasticsearch.common.xcontent.XContentParser#binaryValue()} to read the value back + */ + public XContentBuilder field(String name, BytesReference value) throws IOException { + return field(name).value(value); + } + + /** + * Writes the binary content of the given {@link BytesReference}. + * + * Use {@link org.elasticsearch.common.xcontent.XContentParser#binaryValue()} to read the value back + */ + public XContentBuilder value(BytesReference value) throws IOException { + return (value == null) ? nullValue() : binaryValue(value.toBytesRef()); + } + + //////////////////////////////////////////////////////////////////////////// + // Text + ////////////////////////////////// + + public XContentBuilder field(String name, Text value) throws IOException { + return field(name).value(value); + } + + public XContentBuilder value(Text value) throws IOException { + if (value == null) { + return nullValue(); + } else if (value.hasString()) { + return value(value.string()); + } else { + // TODO: TextBytesOptimization we can use a buffer here to convert it? maybe add a + // request to jackson to support InputStream as well? + return utf8Value(value.bytes().toBytesRef()); + } + } + + //////////////////////////////////////////////////////////////////////////// + // Date + ////////////////////////////////// + + public XContentBuilder field(String name, ReadableInstant value) throws IOException { + return field(name).value(value); + } + + public XContentBuilder field(String name, ReadableInstant value, DateTimeFormatter formatter) throws IOException { + return field(name).value(value, formatter); + } + + public XContentBuilder value(ReadableInstant value) throws IOException { + return value(value, DEFAULT_DATE_PRINTER); + } + + public XContentBuilder value(ReadableInstant value, DateTimeFormatter formatter) throws IOException { + if (value == null) { + return nullValue(); + } + ensureFormatterNotNull(formatter); + return value(formatter.print(value)); + } + + public XContentBuilder field(String name, Date value) throws IOException { + return field(name).value(value); + } + + public XContentBuilder field(String name, Date value, DateTimeFormatter formatter) throws IOException { + return field(name).value(value, formatter); + } + + public XContentBuilder value(Date value) throws IOException { + return value(value, DEFAULT_DATE_PRINTER); + } + + public XContentBuilder value(Date value, DateTimeFormatter formatter) throws IOException { + if (value == null) { + return nullValue(); + } + return value(formatter, value.getTime()); + } + + public XContentBuilder dateField(String name, String readableName, long value) throws IOException { + if (humanReadable) { + field(readableName).value(DEFAULT_DATE_PRINTER, value); + } + field(name, value); + return this; + } + + XContentBuilder value(Calendar value) throws IOException { + if (value == null) { + return nullValue(); + } + return value(DEFAULT_DATE_PRINTER, value.getTimeInMillis()); + } + + XContentBuilder value(DateTimeFormatter formatter, long value) throws IOException { + ensureFormatterNotNull(formatter); + return value(formatter.print(value)); + } + + //////////////////////////////////////////////////////////////////////////// + // GeoPoint & LatLon + ////////////////////////////////// + + public XContentBuilder field(String name, GeoPoint value) throws IOException { + return field(name).value(value); + } + + public XContentBuilder value(GeoPoint value) throws IOException { + if (value == null) { + return nullValue(); + } + return latlon(value.getLat(), value.getLon()); + } + + public XContentBuilder latlon(String name, double lat, double lon) throws IOException { + return field(name).latlon(lat, lon); + } + + public XContentBuilder latlon(double lat, double lon) throws IOException { + return startObject().field("lat", lat).field("lon", lon).endObject(); + } + + //////////////////////////////////////////////////////////////////////////// + // Path + ////////////////////////////////// + + public XContentBuilder value(Path value) throws IOException { + if (value == null) { + return nullValue(); + } + return value(value.toString()); + } + + //////////////////////////////////////////////////////////////////////////// + // Objects + // + // These methods are used when the type of value is unknown. It tries to fallback + // on typed methods and use Object.toString() as a last resort. Always prefer using + // typed methods over this. + ////////////////////////////////// + + public XContentBuilder field(String name, Object value) throws IOException { + return field(name).value(value); + } + + public XContentBuilder array(String name, Object... values) throws IOException { + return field(name).values(values); + } + + XContentBuilder values(Object[] values) throws IOException { + if (values == null) { + return nullValue(); + } + startArray(); + for (Object o : values) { + value(o); + } + endArray(); + return this; + } + + public XContentBuilder value(Object value) throws IOException { + unknownValue(value); + return this; + } + + private void unknownValue(Object value) throws IOException { + if (value == null) { + nullValue(); + return; + } + Writer writer = WRITERS.get(value.getClass()); + if (writer != null) { + writer.write(this, value); + } else if (value instanceof Path) { + //Path implements Iterable and causes endless recursion and a StackOverFlow if treated as an Iterable here + value((Path) value); + } else if (value instanceof Map) { + map((Map) value); + } else if (value instanceof Iterable) { + value((Iterable) value); + } else if (value instanceof Object[]) { + values((Object[]) value); + } else if (value instanceof Calendar) { + value((Calendar) value); + } else if (value instanceof ReadableInstant) { + value((ReadableInstant) value); + } else if (value instanceof BytesReference) { + value((BytesReference) value); + } else if (value instanceof ToXContent) { + value((ToXContent) value); + } else { + // This is a "value" object (like enum, DistanceUnit, etc) just toString() it + // (yes, it can be misleading when toString a Java class, but really, jackson should be used in that case) + value(Objects.toString(value)); + } + } + + //////////////////////////////////////////////////////////////////////////// + // ToXContent + ////////////////////////////////// + + public XContentBuilder field(String name, ToXContent value) throws IOException { + return field(name).value(value); + } + + public XContentBuilder field(String name, ToXContent value, ToXContent.Params params) throws IOException { + return field(name).value(value, params); + } + + private XContentBuilder value(ToXContent value) throws IOException { + return value(value, ToXContent.EMPTY_PARAMS); + } + + private XContentBuilder value(ToXContent value, ToXContent.Params params) throws IOException { + if (value == null) { + return nullValue(); + } + value.toXContent(this, params); + return this; + } + + //////////////////////////////////////////////////////////////////////////// + // Maps & Iterable + ////////////////////////////////// + + public XContentBuilder field(String name, Map values) throws IOException { + return field(name).map(values); + } + + public XContentBuilder map(Map values) throws IOException { + if (values == null) { + return nullValue(); + } + startObject(); + for (Map.Entry value : values.entrySet()) { + field(value.getKey()); + unknownValue(value.getValue()); + } + endObject(); + return this; + } + + public XContentBuilder field(String name, Iterable values) throws IOException { + return field(name).value(values); + } + + private XContentBuilder value(Iterable values) throws IOException { + if (values == null) { + return nullValue(); + } + + if (values instanceof Path) { + //treat as single value + value((Path) values); + } else { + startArray(); + for (Object value : values) { + unknownValue(value); + } + endArray(); + } + return this; + } + + //////////////////////////////////////////////////////////////////////////// + // Misc. + ////////////////////////////////// + public XContentBuilder timeValueField(String rawFieldName, String readableFieldName, TimeValue timeValue) throws IOException { if (humanReadable) { field(readableFieldName, timeValue.toString()); @@ -529,7 +911,7 @@ public final class XContentBuilder implements BytesStream, Releasable { } public XContentBuilder timeValueField(String rawFieldName, String readableFieldName, long rawTime, TimeUnit timeUnit) throws - IOException { + IOException { if (humanReadable) { field(readableFieldName, new TimeValue(rawTime, timeUnit).toString()); } @@ -537,11 +919,12 @@ public final class XContentBuilder implements BytesStream, Releasable { return this; } - public XContentBuilder dateValueField(String rawFieldName, String readableFieldName, long rawTimestamp) throws IOException { + + public XContentBuilder percentageField(String rawFieldName, String readableFieldName, double percentage) throws IOException { if (humanReadable) { - field(readableFieldName, defaultDatePrinter.print(rawTimestamp)); + field(readableFieldName, String.format(Locale.ROOT, "%1.1f%%", percentage)); } - field(rawFieldName, rawTimestamp); + field(rawFieldName, percentage); return this; } @@ -561,206 +944,33 @@ public final class XContentBuilder implements BytesStream, Releasable { return this; } - public XContentBuilder percentageField(String rawFieldName, String readableFieldName, double percentage) throws IOException { - if (humanReadable) { - field(readableFieldName, String.format(Locale.ROOT, "%1.1f%%", percentage)); - } - field(rawFieldName, percentage); + //////////////////////////////////////////////////////////////////////////// + // Raw fields + ////////////////////////////////// + + public XContentBuilder rawField(String name, InputStream value) throws IOException { + generator.writeRawField(name, value); return this; } - public XContentBuilder value(Boolean value) throws IOException { - if (value == null) { - return nullValue(); - } - return value(value.booleanValue()); - } - - public XContentBuilder value(boolean value) throws IOException { - generator.writeBoolean(value); + public XContentBuilder rawField(String name, BytesReference value) throws IOException { + generator.writeRawField(name, value); return this; } - public XContentBuilder value(ReadableInstant date) throws IOException { - return value(date, defaultDatePrinter); - } - - public XContentBuilder value(ReadableInstant date, DateTimeFormatter dateTimeFormatter) throws IOException { - if (date == null) { - return nullValue(); - } - return value(dateTimeFormatter.print(date)); - } - - public XContentBuilder value(Date date) throws IOException { - return value(date, defaultDatePrinter); - } - - public XContentBuilder value(Date date, DateTimeFormatter dateTimeFormatter) throws IOException { - if (date == null) { - return nullValue(); - } - return value(dateTimeFormatter.print(date.getTime())); - } - - public XContentBuilder value(Integer value) throws IOException { - if (value == null) { - return nullValue(); - } - return value(value.intValue()); - } - - public XContentBuilder value(int value) throws IOException { - generator.writeNumber(value); + public XContentBuilder rawValue(BytesReference value) throws IOException { + generator.writeRawValue(value); return this; } - public XContentBuilder value(Long value) throws IOException { - if (value == null) { - return nullValue(); - } - return value(value.longValue()); - } - - public XContentBuilder value(long value) throws IOException { - generator.writeNumber(value); - return this; - } - - public XContentBuilder value(Float value) throws IOException { - if (value == null) { - return nullValue(); - } - return value(value.floatValue()); - } - - public XContentBuilder value(float value) throws IOException { - generator.writeNumber(value); - return this; - } - - public XContentBuilder value(Double value) throws IOException { - if (value == null) { - return nullValue(); - } - return value(value.doubleValue()); - } - - public XContentBuilder value(double value) throws IOException { - generator.writeNumber(value); - return this; - } - - public XContentBuilder value(String value) throws IOException { - if (value == null) { - return nullValue(); - } - generator.writeString(value); - return this; - } - - public XContentBuilder value(byte[] value) throws IOException { - if (value == null) { - return nullValue(); - } - generator.writeBinary(value); - return this; - } - - public XContentBuilder value(byte[] value, int offset, int length) throws IOException { - if (value == null) { - return nullValue(); - } - generator.writeBinary(value, offset, length); - return this; - } - - /** - * Writes the binary content of the given BytesRef - * Use {@link org.elasticsearch.common.xcontent.XContentParser#binaryValue()} to read the value back - */ - public XContentBuilder value(BytesRef value) throws IOException { - if (value == null) { - return nullValue(); - } - generator.writeBinary(value.bytes, value.offset, value.length); - return this; - } - - /** - * Writes the binary content of the given BytesReference - * Use {@link org.elasticsearch.common.xcontent.XContentParser#binaryValue()} to read the value back - */ - public XContentBuilder value(BytesReference value) throws IOException { - if (value == null) { - return nullValue(); - } - BytesRef ref = value.toBytesRef(); - generator.writeBinary(ref.bytes, ref.offset, ref.length); - return this; - } - - public XContentBuilder value(Text value) throws IOException { - if (value == null) { - return nullValue(); - } else if (value.hasString()) { - generator.writeString(value.string()); - } else { - BytesRef bytesRef = value.bytes().toBytesRef(); - generator.writeUTF8String(bytesRef.bytes, bytesRef.offset, bytesRef.length); - } - return this; - } - - public XContentBuilder map(Map map) throws IOException { - if (map == null) { - return nullValue(); - } - writeMap(map); - return this; - } - - public XContentBuilder value(Map map) throws IOException { - if (map == null) { - return nullValue(); - } - writeMap(map); - return this; - } - - public XContentBuilder value(Iterable value) throws IOException { - if (value == null) { - return nullValue(); - } - if (value instanceof Path) { - //treat as single value - writeValue(value); - } else { - startArray(); - for (Object o : value) { - value(o); - } - endArray(); - } - return this; - } - - public XContentBuilder latlon(String name, double lat, double lon) throws IOException { - return startObject(name).field("lat", lat).field("lon", lon).endObject(); - } - - public XContentBuilder latlon(double lat, double lon) throws IOException { - return startObject().field("lat", lat).field("lon", lon).endObject(); - } - public XContentBuilder copyCurrentStructure(XContentParser parser) throws IOException { generator.copyCurrentStructure(parser); return this; } - public XContentBuilder flush() throws IOException { + @Override + public void flush() throws IOException { generator.flush(); - return this; } @Override @@ -768,7 +978,7 @@ public final class XContentBuilder implements BytesStream, Releasable { try { generator.close(); } catch (IOException e) { - throw new IllegalStateException("failed to close the XContentBuilder", e); + throw new IllegalStateException("Failed to close the XContentBuilder", e); } } @@ -786,156 +996,20 @@ public final class XContentBuilder implements BytesStream, Releasable { * Returns a string representation of the builder (only applicable for text based xcontent). */ public String string() throws IOException { - close(); return bytes().utf8ToString(); } - - private void writeMap(Map map) throws IOException { - generator.writeStartObject(); - - for (Map.Entry entry : map.entrySet()) { - field(entry.getKey()); - Object value = entry.getValue(); - if (value == null) { - generator.writeNull(); - } else { - writeValue(value); - } - } - generator.writeEndObject(); + static void ensureNameNotNull(String name) { + ensureNotNull(name, "Field name cannot be null"); } - @FunctionalInterface - interface Writer { - void write(XContentGenerator g, Object v) throws IOException; + static void ensureFormatterNotNull(DateTimeFormatter formatter) { + ensureNotNull(formatter, "DateTimeFormatter cannot be null"); } - private static final Map, Writer> MAP; - - static { - Map, Writer> map = new HashMap<>(); - map.put(String.class, (g, v) -> g.writeString((String) v)); - map.put(Integer.class, (g, v) -> g.writeNumber((Integer) v)); - map.put(Long.class, (g, v) -> g.writeNumber((Long) v)); - map.put(Float.class, (g, v) -> g.writeNumber((Float) v)); - map.put(Double.class, (g, v) -> g.writeNumber((Double) v)); - map.put(Byte.class, (g, v) -> g.writeNumber((Byte) v)); - map.put(Short.class, (g, v) -> g.writeNumber((Short) v)); - map.put(Boolean.class, (g, v) -> g.writeBoolean((Boolean) v)); - map.put(GeoPoint.class, (g, v) -> { - g.writeStartObject(); - g.writeNumberField("lat", ((GeoPoint) v).lat()); - g.writeNumberField("lon", ((GeoPoint) v).lon()); - g.writeEndObject(); - }); - map.put(int[].class, (g, v) -> { - g.writeStartArray(); - for (int item : (int[]) v) { - g.writeNumber(item); - } - g.writeEndArray(); - }); - map.put(long[].class, (g, v) -> { - g.writeStartArray(); - for (long item : (long[]) v) { - g.writeNumber(item); - } - g.writeEndArray(); - }); - map.put(float[].class, (g, v) -> { - g.writeStartArray(); - for (float item : (float[]) v) { - g.writeNumber(item); - } - g.writeEndArray(); - }); - map.put(double[].class, (g, v) -> { - g.writeStartArray(); - for (double item : (double[])v) { - g.writeNumber(item); - } - g.writeEndArray(); - }); - map.put(byte[].class, (g, v) -> g.writeBinary((byte[]) v)); - map.put(short[].class, (g, v) -> { - g.writeStartArray(); - for (short item : (short[])v) { - g.writeNumber(item); - } - g.writeEndArray(); - }); - map.put(BytesRef.class, (g, v) -> { - BytesRef bytes = (BytesRef) v; - g.writeBinary(bytes.bytes, bytes.offset, bytes.length); - }); - map.put(Text.class, (g, v) -> { - Text text = (Text) v; - if (text.hasString()) { - g.writeString(text.string()); - } else { - BytesRef ref = text.bytes().toBytesRef(); - g.writeUTF8String(ref.bytes, ref.offset, ref.length); - } - }); - MAP = Collections.unmodifiableMap(map); - } - - private void writeValue(Object value) throws IOException { + static void ensureNotNull(Object value, String message) { if (value == null) { - generator.writeNull(); - return; - } - Class type = value.getClass(); - Writer writer = MAP.get(type); - if (writer != null) { - writer.write(generator, value); - } else if (value instanceof Map) { - writeMap((Map) value); - } else if (value instanceof Path) { - //Path implements Iterable and causes endless recursion and a StackOverFlow if treated as an Iterable here - generator.writeString(value.toString()); - } else if (value instanceof Iterable) { - writeIterable((Iterable) value); - } else if (value instanceof Object[]) { - writeObjectArray((Object[]) value); - } else if (value instanceof Date) { - generator.writeString(XContentBuilder.defaultDatePrinter.print(((Date) value).getTime())); - } else if (value instanceof Calendar) { - generator.writeString(XContentBuilder.defaultDatePrinter.print((((Calendar) value)).getTimeInMillis())); - } else if (value instanceof ReadableInstant) { - generator.writeString(XContentBuilder.defaultDatePrinter.print((((ReadableInstant) value)).getMillis())); - } else if (value instanceof BytesReference) { - writeBytesReference((BytesReference) value); - } else if (value instanceof ToXContent) { - ((ToXContent) value).toXContent(this, ToXContent.EMPTY_PARAMS); - } else { - // if this is a "value" object, like enum, DistanceUnit, ..., just toString it - // yea, it can be misleading when toString a Java class, but really, jackson should be used in that case - generator.writeString(value.toString()); - //throw new ElasticsearchIllegalArgumentException("type not supported for generic value conversion: " + type); + throw new IllegalArgumentException(message); } } - - private void writeBytesReference(BytesReference value) throws IOException { - BytesRef ref = value.toBytesRef(); - generator.writeBinary(ref.bytes, ref.offset, ref.length); - } - - private void writeIterable(Iterable value) throws IOException { - generator.writeStartArray(); - for (Object v : value) { - writeValue(v); - } - generator.writeEndArray(); - } - - private void writeObjectArray(Object[] value) throws IOException { - generator.writeStartArray(); - for (Object v : value) { - writeValue(v); - } - generator.writeEndArray(); - } - } diff --git a/core/src/main/java/org/elasticsearch/common/xcontent/XContentGenerator.java b/core/src/main/java/org/elasticsearch/common/xcontent/XContentGenerator.java index a2cceae8367..8d1b8efef51 100644 --- a/core/src/main/java/org/elasticsearch/common/xcontent/XContentGenerator.java +++ b/core/src/main/java/org/elasticsearch/common/xcontent/XContentGenerator.java @@ -20,14 +20,13 @@ package org.elasticsearch.common.xcontent; import org.elasticsearch.common.bytes.BytesReference; + import java.io.Closeable; +import java.io.Flushable; import java.io.IOException; import java.io.InputStream; -/** - * - */ -public interface XContentGenerator extends Closeable { +public interface XContentGenerator extends Closeable, Flushable { XContentType contentType(); @@ -37,68 +36,62 @@ public interface XContentGenerator extends Closeable { void usePrintLineFeedAtEnd(); - void writeStartArray() throws IOException; - - void writeEndArray() throws IOException; - void writeStartObject() throws IOException; void writeEndObject() throws IOException; + void writeStartArray() throws IOException; + + void writeEndArray() throws IOException; + void writeFieldName(String name) throws IOException; - void writeString(String text) throws IOException; - - void writeString(char[] text, int offset, int len) throws IOException; - - void writeUTF8String(byte[] text, int offset, int length) throws IOException; - - void writeBinary(byte[] data, int offset, int len) throws IOException; - - void writeBinary(byte[] data) throws IOException; - - void writeNumber(int v) throws IOException; - - void writeNumber(long v) throws IOException; - - void writeNumber(double d) throws IOException; - - void writeNumber(float f) throws IOException; - - void writeBoolean(boolean state) throws IOException; - void writeNull() throws IOException; - void writeStringField(String fieldName, String value) throws IOException; + void writeNullField(String name) throws IOException; - void writeBooleanField(String fieldName, boolean value) throws IOException; + void writeBooleanField(String name, boolean value) throws IOException; - void writeNullField(String fieldName) throws IOException; + void writeBoolean(boolean value) throws IOException; - void writeNumberField(String fieldName, int value) throws IOException; + void writeNumberField(String name, double value) throws IOException; - void writeNumberField(String fieldName, long value) throws IOException; + void writeNumber(double value) throws IOException; - void writeNumberField(String fieldName, double value) throws IOException; + void writeNumberField(String name, float value) throws IOException; - void writeNumberField(String fieldName, float value) throws IOException; + void writeNumber(float value) throws IOException; - void writeBinaryField(String fieldName, byte[] data) throws IOException; + void writeNumberField(String name, int value) throws IOException; - void writeArrayFieldStart(String fieldName) throws IOException; + void writeNumber(int value) throws IOException; - void writeObjectFieldStart(String fieldName) throws IOException; + void writeNumberField(String name, long value) throws IOException; - void writeRawField(String fieldName, InputStream content) throws IOException; + void writeNumber(long value) throws IOException; - void writeRawField(String fieldName, BytesReference content) throws IOException; + void writeNumber(short value) throws IOException; - void writeRawValue(BytesReference content) throws IOException; + void writeStringField(String name, String value) throws IOException; + + void writeString(String value) throws IOException; + + void writeString(char[] text, int offset, int len) throws IOException; + + void writeUTF8String(byte[] value, int offset, int length) throws IOException; + + void writeBinaryField(String name, byte[] value) throws IOException; + + void writeBinary(byte[] value) throws IOException; + + void writeBinary(byte[] value, int offset, int length) throws IOException; + + void writeRawField(String name, InputStream value) throws IOException; + + void writeRawField(String name, BytesReference value) throws IOException; + + void writeRawValue(BytesReference value) throws IOException; void copyCurrentStructure(XContentParser parser) throws IOException; - void flush() throws IOException; - - @Override - void close() throws IOException; } diff --git a/core/src/main/java/org/elasticsearch/common/xcontent/json/JsonXContentGenerator.java b/core/src/main/java/org/elasticsearch/common/xcontent/json/JsonXContentGenerator.java index 4a393b9dd10..74e1cb5e58f 100644 --- a/core/src/main/java/org/elasticsearch/common/xcontent/json/JsonXContentGenerator.java +++ b/core/src/main/java/org/elasticsearch/common/xcontent/json/JsonXContentGenerator.java @@ -47,9 +47,6 @@ import java.util.Collections; import java.util.Objects; import java.util.Set; -/** - * - */ public class JsonXContentGenerator implements XContentGenerator { /** Generator used to write content **/ @@ -130,16 +127,6 @@ public class JsonXContentGenerator implements XContentGenerator { writeLineFeedAtEnd = true; } - @Override - public void writeStartArray() throws IOException { - generator.writeStartArray(); - } - - @Override - public void writeEndArray() throws IOException { - generator.writeEndArray(); - } - private boolean isFiltered() { return filter != null; } @@ -184,118 +171,124 @@ public class JsonXContentGenerator implements XContentGenerator { generator.writeEndObject(); } + + @Override + public void writeStartArray() throws IOException { + generator.writeStartArray(); + } + + @Override + public void writeEndArray() throws IOException { + generator.writeEndArray(); + } + @Override public void writeFieldName(String name) throws IOException { generator.writeFieldName(name); } - @Override - public void writeString(String text) throws IOException { - generator.writeString(text); - } - - @Override - public void writeString(char[] text, int offset, int len) throws IOException { - generator.writeString(text, offset, len); - } - - @Override - public void writeUTF8String(byte[] text, int offset, int length) throws IOException { - generator.writeUTF8String(text, offset, length); - } - - @Override - public void writeBinary(byte[] data, int offset, int len) throws IOException { - generator.writeBinary(data, offset, len); - } - - @Override - public void writeBinary(byte[] data) throws IOException { - generator.writeBinary(data); - } - - @Override - public void writeNumber(int v) throws IOException { - generator.writeNumber(v); - } - - @Override - public void writeNumber(long v) throws IOException { - generator.writeNumber(v); - } - - @Override - public void writeNumber(double d) throws IOException { - generator.writeNumber(d); - } - - @Override - public void writeNumber(float f) throws IOException { - generator.writeNumber(f); - } - - @Override - public void writeBoolean(boolean state) throws IOException { - generator.writeBoolean(state); - } - @Override public void writeNull() throws IOException { generator.writeNull(); } @Override - public void writeStringField(String fieldName, String value) throws IOException { - generator.writeStringField(fieldName, value); + public void writeNullField(String name) throws IOException { + generator.writeNullField(name); } @Override - public void writeBooleanField(String fieldName, boolean value) throws IOException { - generator.writeBooleanField(fieldName, value); + public void writeBooleanField(String name, boolean value) throws IOException { + generator.writeBooleanField(name, value); } @Override - public void writeNullField(String fieldName) throws IOException { - generator.writeNullField(fieldName); + public void writeBoolean(boolean value) throws IOException { + generator.writeBoolean(value); } @Override - public void writeNumberField(String fieldName, int value) throws IOException { - generator.writeNumberField(fieldName, value); + public void writeNumberField(String name, double value) throws IOException { + generator.writeNumberField(name, value); } @Override - public void writeNumberField(String fieldName, long value) throws IOException { - generator.writeNumberField(fieldName, value); + public void writeNumber(double value) throws IOException { + generator.writeNumber(value); } @Override - public void writeNumberField(String fieldName, double value) throws IOException { - generator.writeNumberField(fieldName, value); + public void writeNumberField(String name, float value) throws IOException { + generator.writeNumberField(name, value); } @Override - public void writeNumberField(String fieldName, float value) throws IOException { - generator.writeNumberField(fieldName, value); + public void writeNumber(float value) throws IOException { + generator.writeNumber(value); } @Override - public void writeBinaryField(String fieldName, byte[] data) throws IOException { - generator.writeBinaryField(fieldName, data); + public void writeNumberField(String name, int value) throws IOException { + generator.writeNumberField(name, value); } @Override - public void writeArrayFieldStart(String fieldName) throws IOException { - generator.writeArrayFieldStart(fieldName); + public void writeNumber(int value) throws IOException { + generator.writeNumber(value); } @Override - public void writeObjectFieldStart(String fieldName) throws IOException { - generator.writeObjectFieldStart(fieldName); + public void writeNumberField(String name, long value) throws IOException { + generator.writeNumberField(name, value); } - private void writeStartRaw(String fieldName) throws IOException { - writeFieldName(fieldName); + @Override + public void writeNumber(long value) throws IOException { + generator.writeNumber(value); + } + + @Override + public void writeNumber(short value) throws IOException { + generator.writeNumber(value); + } + + @Override + public void writeStringField(String name, String value) throws IOException { + generator.writeStringField(name, value); + } + + @Override + public void writeString(String value) throws IOException { + generator.writeString(value); + } + + @Override + public void writeString(char[] value, int offset, int len) throws IOException { + generator.writeString(value, offset, len); + } + + @Override + public void writeUTF8String(byte[] value, int offset, int length) throws IOException { + generator.writeUTF8String(value, offset, length); + } + + @Override + public void writeBinaryField(String name, byte[] value) throws IOException { + generator.writeBinaryField(name, value); + } + + @Override + public void writeBinary(byte[] value) throws IOException { + generator.writeBinary(value); + } + + @Override + public void writeBinary(byte[] value, int offset, int len) throws IOException { + generator.writeBinary(value, offset, len); + } + + private void writeStartRaw(String name) throws IOException { + writeFieldName(name); generator.writeRaw(':'); } @@ -309,7 +302,7 @@ public class JsonXContentGenerator implements XContentGenerator { } @Override - public void writeRawField(String fieldName, InputStream content) throws IOException { + public void writeRawField(String name, InputStream content) throws IOException { if (content.markSupported() == false) { // needed for the XContentFactory.xContentType call content = new BufferedInputStream(content); @@ -321,11 +314,11 @@ public class JsonXContentGenerator implements XContentGenerator { if (mayWriteRawData(contentType) == false) { try (XContentParser parser = XContentFactory.xContent(contentType).createParser(content)) { parser.nextToken(); - writeFieldName(fieldName); + writeFieldName(name); copyCurrentStructure(parser); } } else { - writeStartRaw(fieldName); + writeStartRaw(name); flush(); Streams.copy(content, os); writeEndRaw(); @@ -333,16 +326,16 @@ public class JsonXContentGenerator implements XContentGenerator { } @Override - public final void writeRawField(String fieldName, BytesReference content) throws IOException { + public final void writeRawField(String name, BytesReference content) throws IOException { XContentType contentType = XContentFactory.xContentType(content); if (contentType == null) { throw new IllegalArgumentException("Can't write raw bytes whose xcontent-type can't be guessed"); } if (mayWriteRawData(contentType) == false) { - writeFieldName(fieldName); + writeFieldName(name); copyRawValue(content, contentType.xContent()); } else { - writeStartRaw(fieldName); + writeStartRaw(name); flush(); content.writeTo(os); writeEndRaw(); @@ -416,7 +409,7 @@ public class JsonXContentGenerator implements XContentGenerator { } JsonStreamContext context = generator.getOutputContext(); if ((context != null) && (context.inRoot() == false)) { - throw new IOException("unclosed object or array found"); + throw new IOException("Unclosed object or array found"); } if (writeLineFeedAtEnd) { flush(); diff --git a/core/src/main/java/org/elasticsearch/index/mapper/SourceFieldMapper.java b/core/src/main/java/org/elasticsearch/index/mapper/SourceFieldMapper.java index 4854eb57752..63d4d958b36 100644 --- a/core/src/main/java/org/elasticsearch/index/mapper/SourceFieldMapper.java +++ b/core/src/main/java/org/elasticsearch/index/mapper/SourceFieldMapper.java @@ -275,15 +275,15 @@ public class SourceFieldMapper extends MetadataFieldMapper { } if (includes != null) { - builder.field("includes", includes); + builder.array("includes", includes); } else if (includeDefaults) { - builder.field("includes", Strings.EMPTY_ARRAY); + builder.array("includes", Strings.EMPTY_ARRAY); } if (excludes != null) { - builder.field("excludes", excludes); + builder.array("excludes", excludes); } else if (includeDefaults) { - builder.field("excludes", Strings.EMPTY_ARRAY); + builder.array("excludes", Strings.EMPTY_ARRAY); } builder.endObject(); diff --git a/core/src/main/java/org/elasticsearch/index/query/IndicesQueryBuilder.java b/core/src/main/java/org/elasticsearch/index/query/IndicesQueryBuilder.java index 736387a0d24..734d4cda922 100644 --- a/core/src/main/java/org/elasticsearch/index/query/IndicesQueryBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/query/IndicesQueryBuilder.java @@ -132,7 +132,7 @@ public class IndicesQueryBuilder extends AbstractQueryBuilder 0) { - builder.dateValueField(Fields.STOP_TIME_IN_MILLIS, Fields.STOP_TIME, timer.stopTime); + builder.dateField(Fields.STOP_TIME_IN_MILLIS, Fields.STOP_TIME, timer.stopTime); } builder.timeValueField(Fields.TOTAL_TIME_IN_MILLIS, Fields.TOTAL_TIME, timer.time()); diff --git a/core/src/main/java/org/elasticsearch/monitor/jvm/JvmInfo.java b/core/src/main/java/org/elasticsearch/monitor/jvm/JvmInfo.java index ca0bb4f3e80..36179fb06ea 100644 --- a/core/src/main/java/org/elasticsearch/monitor/jvm/JvmInfo.java +++ b/core/src/main/java/org/elasticsearch/monitor/jvm/JvmInfo.java @@ -431,7 +431,7 @@ public class JvmInfo implements Writeable, ToXContent { builder.field(Fields.VM_NAME, vmName); builder.field(Fields.VM_VERSION, vmVersion); builder.field(Fields.VM_VENDOR, vmVendor); - builder.dateValueField(Fields.START_TIME_IN_MILLIS, Fields.START_TIME, startTime); + builder.dateField(Fields.START_TIME_IN_MILLIS, Fields.START_TIME, startTime); builder.startObject(Fields.MEM); builder.byteSizeField(Fields.HEAP_INIT_IN_BYTES, Fields.HEAP_INIT, mem.heapInit); @@ -441,8 +441,8 @@ public class JvmInfo implements Writeable, ToXContent { builder.byteSizeField(Fields.DIRECT_MAX_IN_BYTES, Fields.DIRECT_MAX, mem.directMemoryMax); builder.endObject(); - builder.field(Fields.GC_COLLECTORS, gcCollectors); - builder.field(Fields.MEMORY_POOLS, memoryPools); + builder.array(Fields.GC_COLLECTORS, gcCollectors); + builder.array(Fields.MEMORY_POOLS, memoryPools); builder.field(Fields.USING_COMPRESSED_OOPS, useCompressedOops); diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/PercentileRanksAggregationBuilder.java b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/PercentileRanksAggregationBuilder.java index 8320774da0f..e4811902f89 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/PercentileRanksAggregationBuilder.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/metrics/percentiles/PercentileRanksAggregationBuilder.java @@ -180,7 +180,7 @@ public class PercentileRanksAggregationBuilder extends LeafOnly 1 || randomBoolean()) { - b.field("indices", indices); + b.array("indices", indices); } else { b.field("index", indices[0]); } if (aliases.length > 1 || randomBoolean()) { - b.field("aliases", aliases); + b.array("aliases", aliases); } else { b.field("alias", aliases[0]); } @@ -196,12 +196,12 @@ public class AliasActionsTests extends ESTestCase { b.startObject(); { b.startObject("remove"); { if (indices.length > 1 || randomBoolean()) { - b.field("indices", indices); + b.array("indices", indices); } else { b.field("index", indices[0]); } if (aliases.length > 1 || randomBoolean()) { - b.field("aliases", aliases); + b.array("aliases", aliases); } else { b.field("alias", aliases[0]); } @@ -224,7 +224,7 @@ public class AliasActionsTests extends ESTestCase { b.startObject(); { b.startObject("remove_index"); { if (indices.length > 1 || randomBoolean()) { - b.field("indices", indices); + b.array("indices", indices); } else { b.field("index", indices[0]); } @@ -246,7 +246,7 @@ public class AliasActionsTests extends ESTestCase { b.startObject(); { b.startObject(randomFrom("add", "remove")); { b.field("index", randomAsciiOfLength(5)); - b.field("indices", generateRandomStringArray(10, 5, false, false)); + b.array("indices", generateRandomStringArray(10, 5, false, false)); b.field("alias", randomAsciiOfLength(5)); } b.endObject(); @@ -265,7 +265,7 @@ public class AliasActionsTests extends ESTestCase { b.startObject(randomFrom("add", "remove")); { b.field("index", randomAsciiOfLength(5)); b.field("alias", randomAsciiOfLength(5)); - b.field("aliases", generateRandomStringArray(10, 5, false, false)); + b.array("aliases", generateRandomStringArray(10, 5, false, false)); } b.endObject(); } diff --git a/core/src/test/java/org/elasticsearch/common/xcontent/BaseXContentTestCase.java b/core/src/test/java/org/elasticsearch/common/xcontent/BaseXContentTestCase.java index a602e81b854..2223163c367 100644 --- a/core/src/test/java/org/elasticsearch/common/xcontent/BaseXContentTestCase.java +++ b/core/src/test/java/org/elasticsearch/common/xcontent/BaseXContentTestCase.java @@ -19,23 +19,657 @@ package org.elasticsearch.common.xcontent; +import com.fasterxml.jackson.core.JsonGenerationException; import com.fasterxml.jackson.core.JsonGenerator; - +import org.apache.lucene.util.BytesRef; +import org.elasticsearch.cluster.metadata.IndexMetaData; +import org.elasticsearch.common.Strings; import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.bytes.BytesReference; +import org.elasticsearch.common.geo.GeoPoint; +import org.elasticsearch.common.io.PathUtils; +import org.elasticsearch.common.text.Text; +import org.elasticsearch.common.unit.DistanceUnit; import org.elasticsearch.common.xcontent.XContentParser.Token; import org.elasticsearch.test.ESTestCase; +import org.hamcrest.Matcher; +import org.hamcrest.Matchers; +import org.joda.time.DateTime; +import org.joda.time.DateTimeZone; +import org.joda.time.Instant; +import org.joda.time.ReadableInstant; +import org.joda.time.format.DateTimeFormatter; +import org.joda.time.format.ISODateTimeFormat; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.math.BigDecimal; import java.math.BigInteger; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.Locale; import java.util.Map; +import java.util.concurrent.TimeUnit; + +import static java.util.Collections.singletonMap; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.notNullValue; public abstract class BaseXContentTestCase extends ESTestCase { - public abstract XContentType xcontentType(); + protected abstract XContentType xcontentType(); + + private XContentBuilder builder() throws IOException { + return XContentBuilder.builder(xcontentType().xContent()); + } + + public void testContentType() throws IOException { + assertThat(builder().contentType(), equalTo(xcontentType())); + } + + public void testStartEndObject() throws IOException { + expectUnclosedException(() -> builder().startObject().bytes()); + expectUnclosedException(() -> builder().startObject().close()); + expectUnclosedException(() -> builder().startObject().string()); + + expectObjectException(() -> builder().endObject().bytes()); + expectObjectException(() -> builder().endObject().close()); + expectObjectException(() -> builder().endObject().string()); + + expectValueException(() -> builder().startObject("foo").endObject()); + expectNonNullFieldException(() -> builder().startObject().startObject(null)); + + assertResult("{}", () -> builder().startObject().endObject()); + assertResult("{'foo':{}}", () -> builder().startObject().startObject("foo").endObject().endObject()); + + assertResult("{'foo':{'bar':{}}}", () -> builder() + .startObject() + .startObject("foo") + .startObject("bar") + .endObject() + .endObject() + .endObject()); + } + + public void testStartEndArray() throws IOException { + expectUnclosedException(() -> builder().startArray().bytes()); + expectUnclosedException(() -> builder().startArray().close()); + expectUnclosedException(() -> builder().startArray().string()); + + expectArrayException(() -> builder().endArray().bytes()); + expectArrayException(() -> builder().endArray().close()); + expectArrayException(() -> builder().endArray().string()); + + expectValueException(() -> builder().startArray("foo").endObject()); + expectFieldException(() -> builder().startObject().startArray().endArray().endObject()); + expectNonNullFieldException(() -> builder().startObject().startArray(null).endArray().endObject()); + + assertResult("{'foo':[]}", () -> builder().startObject().startArray("foo").endArray().endObject()); + assertResult("{'foo':[1,2,3]}", () -> builder() + .startObject() + .startArray("foo") + .value(1) + .value(2) + .value(3) + .endArray() + .endObject()); + } + + public void testField() throws IOException { + expectValueException(() -> builder().field("foo").bytes()); + expectNonNullFieldException(() -> builder().field(null).bytes()); + expectUnclosedException(() -> builder().startObject().field("foo").bytes()); + + assertResult("{'foo':'bar'}", () -> builder().startObject().field("foo").value("bar").endObject()); + } + + public void testNullField() throws IOException { + expectValueException(() -> builder().nullField("foo").bytes()); + expectNonNullFieldException(() -> builder().nullField(null).bytes()); + expectUnclosedException(() -> builder().startObject().nullField("foo").bytes()); + + assertResult("{'foo':null}", () -> builder().startObject().nullField("foo").endObject()); + } + + public void testNullValue() throws IOException { + assertResult("{'foo':null}", () -> builder().startObject().field("foo").nullValue().endObject()); + } + + public void testBooleans() throws IOException { + assertResult("{'boolean':null}", () -> builder().startObject().field("boolean", (Boolean) null).endObject()); + assertResult("{'boolean':true}", () -> builder().startObject().field("boolean", Boolean.TRUE).endObject()); + assertResult("{'boolean':false}", () -> builder().startObject().field("boolean", Boolean.FALSE).endObject()); + assertResult("{'boolean':[true,false,true]}", () -> builder().startObject().array("boolean", true, false, true).endObject()); + assertResult("{'boolean':[false,true]}", () -> builder().startObject().array("boolean", new boolean[]{false, true}).endObject()); + assertResult("{'boolean':null}", () -> builder().startObject().array("boolean", (boolean[]) null).endObject()); + assertResult("{'boolean':[]}", () -> builder().startObject().array("boolean", new boolean[]{}).endObject()); + assertResult("{'boolean':null}", () -> builder().startObject().field("boolean").value((Boolean) null).endObject()); + assertResult("{'boolean':true}", () -> builder().startObject().field("boolean").value(Boolean.TRUE).endObject()); + assertResult("{'boolean':false}", () -> builder().startObject().field("boolean").value(Boolean.FALSE).endObject()); + } + + public void testBytes() throws IOException { + assertResult("{'byte':null}", () -> builder().startObject().field("byte", (Byte) null).endObject()); + assertResult("{'byte':0}", () -> builder().startObject().field("byte", (byte) 0).endObject()); + assertResult("{'byte':1}", () -> builder().startObject().field("byte", (byte) 1).endObject()); + assertResult("{'byte':null}", () -> builder().startObject().field("byte").value((Byte) null).endObject()); + assertResult("{'byte':0}", () -> builder().startObject().field("byte").value((byte) 0).endObject()); + assertResult("{'byte':1}", () -> builder().startObject().field("byte").value((byte) 1).endObject()); + } + + public void testDoubles() throws IOException { + assertResult("{'double':null}", () -> builder().startObject().field("double", (Double) null).endObject()); + assertResult("{'double':42.5}", () -> builder().startObject().field("double", Double.valueOf(42.5)).endObject()); + assertResult("{'double':1.2}", () -> builder().startObject().field("double", 1.2).endObject()); + assertResult("{'double':[42.0,43.0,45]}", () -> builder().startObject().array("double", 42.0, 43.0, 45).endObject()); + assertResult("{'double':null}", () -> builder().startObject().array("double", (double[]) null).endObject()); + assertResult("{'double':[]}", () -> builder().startObject().array("double", new double[]{}).endObject()); + assertResult("{'double':null}", () -> builder().startObject().field("double").value((Double) null).endObject()); + assertResult("{'double':0.001}", () -> builder().startObject().field("double").value(0.001).endObject()); + assertResult("{'double':[1.7976931348623157E308,4.9E-324]}", () -> builder() + .startObject() + .array("double", new double[]{Double.MAX_VALUE, Double.MIN_VALUE}) + .endObject()); + } + + public void testFloats() throws IOException { + assertResult("{'float':null}", () -> builder().startObject().field("float", (Float) null).endObject()); + assertResult("{'float':42.5}", () -> builder().startObject().field("float", Float.valueOf(42.5f)).endObject()); + assertResult("{'float':1.2}", () -> builder().startObject().field("float", 1.2f).endObject()); + assertResult("{'float':null}", () -> builder().startObject().array("float", (float[]) null).endObject()); + assertResult("{'float':[]}", () -> builder().startObject().array("float", new float[]{}).endObject()); + assertResult("{'float':null}", () -> builder().startObject().field("float").value((Float) null).endObject()); + assertResult("{'float':9.9E-7}", () -> builder().startObject().field("float").value(0.00000099f).endObject()); + assertResult("{'float':[42.0,43.0,45.666668]}", () -> builder() + .startObject() + .array("float", 42.0f, 43.0f, 45.66666667f) + .endObject()); + assertResult("{'float':[3.4028235E38,1.4E-45]}", () -> builder() + .startObject() + .array("float", new float[]{Float.MAX_VALUE, Float.MIN_VALUE}) + .endObject()); + } + + public void testIntegers() throws IOException { + assertResult("{'integer':null}", () -> builder().startObject().field("integer", (Integer) null).endObject()); + assertResult("{'integer':42}", () -> builder().startObject().field("integer", Integer.valueOf(42)).endObject()); + assertResult("{'integer':3}", () -> builder().startObject().field("integer", 3).endObject()); + assertResult("{'integer':[1,3,5,7,11]}", () -> builder().startObject().array("integer", 1, 3, 5, 7, 11).endObject()); + assertResult("{'integer':null}", () -> builder().startObject().array("integer", (int[]) null).endObject()); + assertResult("{'integer':[]}", () -> builder().startObject().array("integer", new int[]{}).endObject()); + assertResult("{'integer':null}", () -> builder().startObject().field("integer").value((Integer) null).endObject()); + assertResult("{'integer':42}", () -> builder().startObject().field("integer").value(42).endObject()); + assertResult("{'integer':[2147483647,-2147483648]}", () -> builder() + .startObject() + .array("integer", new int[]{Integer.MAX_VALUE, Integer.MIN_VALUE}) + .endObject()); + } + + public void testLongs() throws IOException { + assertResult("{'long':null}", () -> builder().startObject().field("long", (Long) null).endObject()); + assertResult("{'long':42}", () -> builder().startObject().field("long", Long.valueOf(42L)).endObject()); + assertResult("{'long':9223372036854775807}", () -> builder().startObject().field("long", 9_223_372_036_854_775_807L).endObject()); + assertResult("{'long':[1,3,5,7,11]}", () -> builder().startObject().array("long", 1L, 3L, 5L, 7L, 11L).endObject()); + assertResult("{'long':null}", () -> builder().startObject().array("long", (long[]) null).endObject()); + assertResult("{'long':[]}", () -> builder().startObject().array("long", new long[]{}).endObject()); + assertResult("{'long':null}", () -> builder().startObject().field("long").value((Long) null).endObject()); + assertResult("{'long':42}", () -> builder().startObject().field("long").value(42).endObject()); + assertResult("{'long':[2147483647,-2147483648]}", () -> builder() + .startObject() + .array("long", new long[]{Integer.MAX_VALUE, Integer.MIN_VALUE}) + .endObject()); + } + + public void testShorts() throws IOException { + assertResult("{'short':null}", () -> builder().startObject().field("short", (Short) null).endObject()); + assertResult("{'short':5000}", () -> builder().startObject().field("short", Short.valueOf((short) 5000)).endObject()); + assertResult("{'short':null}", () -> builder().startObject().array("short", (short[]) null).endObject()); + assertResult("{'short':[]}", () -> builder().startObject().array("short", new short[]{}).endObject()); + assertResult("{'short':null}", () -> builder().startObject().field("short").value((Short) null).endObject()); + assertResult("{'short':42}", () -> builder().startObject().field("short").value((short) 42).endObject()); + assertResult("{'short':[1,3,5,7,11]}", () -> builder() + .startObject() + .array("short", (short) 1, (short) 3, (short) 5, (short) 7, (short) 11) + .endObject()); + assertResult("{'short':[32767,-32768]}", () -> builder() + .startObject() + .array("short", new short[]{Short.MAX_VALUE, Short.MIN_VALUE}) + .endObject()); + } + + public void testStrings() throws IOException { + assertResult("{'string':null}", () -> builder().startObject().field("string", (String) null).endObject()); + assertResult("{'string':'value'}", () -> builder().startObject().field("string", "value").endObject()); + assertResult("{'string':''}", () -> builder().startObject().field("string", "").endObject()); + assertResult("{'string':null}", () -> builder().startObject().array("string", (String[]) null).endObject()); + assertResult("{'string':[]}", () -> builder().startObject().array("string", Strings.EMPTY_ARRAY).endObject()); + assertResult("{'string':null}", () -> builder().startObject().field("string").value((String) null).endObject()); + assertResult("{'string':'42'}", () -> builder().startObject().field("string").value("42").endObject()); + assertResult("{'string':['a','b','c','d']}", () -> builder() + .startObject() + .array("string", "a", "b", "c", "d") + .endObject()); + } + + public void testBinaryField() throws Exception { + assertResult("{'binary':null}", () -> builder().startObject().field("binary", (byte[]) null).endObject()); + + final byte[] randomBytes = randomBytes(); + BytesReference bytes = builder().startObject().field("binary", randomBytes).endObject().bytes(); + + XContentParser parser = xcontentType().xContent().createParser(bytes); + assertSame(parser.nextToken(), Token.START_OBJECT); + assertSame(parser.nextToken(), Token.FIELD_NAME); + assertEquals(parser.currentName(), "binary"); + assertTrue(parser.nextToken().isValue()); + assertArrayEquals(randomBytes, parser.binaryValue()); + assertSame(parser.nextToken(), Token.END_OBJECT); + assertNull(parser.nextToken()); + } + + public void testBinaryValue() throws Exception { + assertResult("{'binary':null}", () -> builder().startObject().field("binary").value((byte[]) null).endObject()); + + final byte[] randomBytes = randomBytes(); + BytesReference bytes = builder().startObject().field("binary").value(randomBytes).endObject().bytes(); + + XContentParser parser = xcontentType().xContent().createParser(bytes); + assertSame(parser.nextToken(), Token.START_OBJECT); + assertSame(parser.nextToken(), Token.FIELD_NAME); + assertEquals(parser.currentName(), "binary"); + assertTrue(parser.nextToken().isValue()); + assertArrayEquals(randomBytes, parser.binaryValue()); + assertSame(parser.nextToken(), Token.END_OBJECT); + assertNull(parser.nextToken()); + } + + public void testBinaryValueWithOffsetLength() throws Exception { + assertResult("{'binary':null}", () -> builder().startObject().field("binary").value(null, 0, 0).endObject()); + + final byte[] randomBytes = randomBytes(); + final int offset = randomIntBetween(0, randomBytes.length); + final int length = randomIntBetween(1, Math.max(1, randomBytes.length - offset)); + + XContentBuilder builder = builder().startObject(); + if (randomBoolean()) { + builder.field("bin", randomBytes, offset, length); + } else { + builder.field("bin").value(randomBytes, offset, length); + } + builder.endObject(); + + XContentParser parser = xcontentType().xContent().createParser(builder.bytes()); + assertSame(parser.nextToken(), Token.START_OBJECT); + assertSame(parser.nextToken(), Token.FIELD_NAME); + assertEquals(parser.currentName(), "bin"); + assertTrue(parser.nextToken().isValue()); + assertArrayEquals(Arrays.copyOfRange(randomBytes, offset, offset + length), parser.binaryValue()); + assertSame(parser.nextToken(), Token.END_OBJECT); + assertNull(parser.nextToken()); + } + + public void testBinaryUTF8() throws Exception { + assertResult("{'utf8':null}", () -> builder().startObject().utf8Field("utf8", null).endObject()); + + final BytesRef randomBytesRef = new BytesRef(randomBytes()); + XContentBuilder builder = builder().startObject(); + if (randomBoolean()) { + builder.utf8Field("utf8", randomBytesRef); + } else { + builder.field("utf8").utf8Value(randomBytesRef); + } + builder.endObject(); + + XContentParser parser = xcontentType().xContent().createParser(builder.bytes()); + assertSame(parser.nextToken(), Token.START_OBJECT); + assertSame(parser.nextToken(), Token.FIELD_NAME); + assertEquals(parser.currentName(), "utf8"); + assertTrue(parser.nextToken().isValue()); + assertThat(parser.utf8Bytes().utf8ToString(), equalTo(randomBytesRef.utf8ToString())); + assertSame(parser.nextToken(), Token.END_OBJECT); + assertNull(parser.nextToken()); + } + + public void testText() throws Exception { + assertResult("{'text':null}", () -> builder().startObject().field("text", (Text) null).endObject()); + assertResult("{'text':''}", () -> builder().startObject().field("text", new Text("")).endObject()); + assertResult("{'text':'foo bar'}", () -> builder().startObject().field("text", new Text("foo bar")).endObject()); + + final BytesReference random = new BytesArray(randomBytes()); + XContentBuilder builder = builder().startObject().field("text", new Text(random)).endObject(); + + XContentParser parser = xcontentType().xContent().createParser(builder.bytes()); + assertSame(parser.nextToken(), Token.START_OBJECT); + assertSame(parser.nextToken(), Token.FIELD_NAME); + assertEquals(parser.currentName(), "text"); + assertTrue(parser.nextToken().isValue()); + assertThat(parser.utf8Bytes().utf8ToString(), equalTo(random.utf8ToString())); + assertSame(parser.nextToken(), Token.END_OBJECT); + assertNull(parser.nextToken()); + } + + public void testReadableInstant() throws Exception { + assertResult("{'instant':null}", () -> builder().startObject().field("instant", (ReadableInstant) null).endObject()); + assertResult("{'instant':null}", () -> builder().startObject().field("instant").value((ReadableInstant) null).endObject()); + + final DateTime t1 = new DateTime(2016, 1, 1, 0, 0, DateTimeZone.UTC); + + String expected = "{'t1':'2016-01-01T00:00:00.000Z'}"; + assertResult(expected, () -> builder().startObject().field("t1", t1).endObject()); + assertResult(expected, () -> builder().startObject().field("t1").value(t1).endObject()); + + final DateTime t2 = new DateTime(2016, 12, 25, 7, 59, 42, 213, DateTimeZone.UTC); + + expected = "{'t2':'2016-12-25T07:59:42.213Z'}"; + assertResult(expected, () -> builder().startObject().field("t2", t2).endObject()); + assertResult(expected, () -> builder().startObject().field("t2").value(t2).endObject()); + + final DateTimeFormatter formatter = randomFrom(ISODateTimeFormat.basicDate(), ISODateTimeFormat.dateTimeNoMillis()); + final DateTime t3 = DateTime.now(); + + expected = "{'t3':'" + formatter.print(t3) + "'}"; + assertResult(expected, () -> builder().startObject().field("t3", t3, formatter).endObject()); + assertResult(expected, () -> builder().startObject().field("t3").value(t3, formatter).endObject()); + + final DateTime t4 = new DateTime(randomDateTimeZone()); + + expected = "{'t4':'" + formatter.print(t4) + "'}"; + assertResult(expected, () -> builder().startObject().field("t4", t4, formatter).endObject()); + assertResult(expected, () -> builder().startObject().field("t4").value(t4, formatter).endObject()); + + long date = Math.abs(randomLong() % (2 * (long) 10e11)); // 1970-01-01T00:00:00Z - 2033-05-18T05:33:20.000+02:00 + final DateTime t5 = new DateTime(date, randomDateTimeZone()); + + expected = "{'t5':'" + XContentBuilder.DEFAULT_DATE_PRINTER.print(t5) + "'}"; + assertResult(expected, () -> builder().startObject().field("t5", t5).endObject()); + assertResult(expected, () -> builder().startObject().field("t5").value(t5).endObject()); + + expected = "{'t5':'" + formatter.print(t5) + "'}"; + assertResult(expected, () -> builder().startObject().field("t5", t5, formatter).endObject()); + assertResult(expected, () -> builder().startObject().field("t5").value(t5, formatter).endObject()); + + Instant i1 = new Instant(1451606400000L); // 2016-01-01T00:00:00.000Z + expected = "{'i1':'2016-01-01T00:00:00.000Z'}"; + assertResult(expected, () -> builder().startObject().field("i1", i1).endObject()); + assertResult(expected, () -> builder().startObject().field("i1").value(i1).endObject()); + + Instant i2 = new Instant(1482652782213L); // 2016-12-25T07:59:42.213Z + expected = "{'i2':'" + formatter.print(i2) + "'}"; + assertResult(expected, () -> builder().startObject().field("i2", i2, formatter).endObject()); + assertResult(expected, () -> builder().startObject().field("i2").value(i2, formatter).endObject()); + + expectNonNullFormatterException(() -> builder().startObject().field("t3", t3, null).endObject()); + expectNonNullFormatterException(() -> builder().startObject().field("t3").value(t3, null).endObject()); + } + + public void testDate() throws Exception { + assertResult("{'date':null}", () -> builder().startObject().field("date", (Date) null).endObject()); + assertResult("{'date':null}", () -> builder().startObject().field("date").value((Date) null).endObject()); + + final Date d1 = new DateTime(2016, 1, 1, 0, 0, DateTimeZone.UTC).toDate(); + assertResult("{'d1':'2016-01-01T00:00:00.000Z'}", () -> builder().startObject().field("d1", d1).endObject()); + assertResult("{'d1':'2016-01-01T00:00:00.000Z'}", () -> builder().startObject().field("d1").value(d1).endObject()); + + final Date d2 = new DateTime(2016, 12, 25, 7, 59, 42, 213, DateTimeZone.UTC).toDate(); + assertResult("{'d2':'2016-12-25T07:59:42.213Z'}", () -> builder().startObject().field("d2", d2).endObject()); + assertResult("{'d2':'2016-12-25T07:59:42.213Z'}", () -> builder().startObject().field("d2").value(d2).endObject()); + + final DateTimeFormatter formatter = randomFrom(ISODateTimeFormat.basicDate(), ISODateTimeFormat.dateTimeNoMillis()); + final Date d3 = DateTime.now().toDate(); + + String expected = "{'d3':'" + formatter.print(d3.getTime()) + "'}"; + assertResult(expected, () -> builder().startObject().field("d3", d3, formatter).endObject()); + assertResult(expected, () -> builder().startObject().field("d3").value(d3, formatter).endObject()); + + expectNonNullFormatterException(() -> builder().startObject().field("d3", d3, null).endObject()); + expectNonNullFormatterException(() -> builder().startObject().field("d3").value(d3, null).endObject()); + expectNonNullFormatterException(() -> builder().value(null, 1L)); + } + + public void testDateField() throws Exception { + final Date d = new DateTime(2016, 1, 1, 0, 0, DateTimeZone.UTC).toDate(); + + assertResult("{'date_in_millis':1451606400000}", () -> builder() + .startObject() + .dateField("date_in_millis", "date", d.getTime()) + .endObject()); + assertResult("{'date':'2016-01-01T00:00:00.000Z','date_in_millis':1451606400000}", () -> builder() + .humanReadable(true) + .startObject + ().dateField("date_in_millis", "date", d.getTime()) + .endObject()); + } + + public void testCalendar() throws Exception { + Calendar calendar = new DateTime(2016, 1, 1, 0, 0, DateTimeZone.UTC).toCalendar(Locale.ROOT); + assertResult("{'calendar':'2016-01-01T00:00:00.000Z'}", () -> builder() + .startObject() + .field("calendar") + .value(calendar) + .endObject()); + } + + public void testGeoPoint() throws Exception { + assertResult("{'geo':null}", () -> builder().startObject().field("geo", (GeoPoint) null).endObject()); + assertResult("{'geo':{'lat':52.4267578125,'lon':13.271484375}}", () -> builder() + .startObject() + . field("geo", GeoPoint.fromGeohash("u336q")) + .endObject()); + assertResult("{'geo':{'lat':52.5201416015625,'lon':13.4033203125}}", () -> builder() + .startObject() + .field("geo") + .value(GeoPoint.fromGeohash("u33dc1")) + .endObject()); + } + + public void testLatLon() throws Exception { + final String expected = "{'latlon':{'lat':13.271484375,'lon':52.4267578125}}"; + assertResult(expected, () -> builder().startObject().latlon("latlon", 13.271484375, 52.4267578125).endObject()); + assertResult(expected, () -> builder().startObject().field("latlon").latlon(13.271484375, 52.4267578125).endObject()); + } + + public void testPath() throws Exception { + assertResult("{'path':null}", () -> builder().startObject().field("path", (Path) null).endObject()); + + Path path = PathUtils.get("first", "second", "third"); + assertResult("{'path':'first/second/third'}", () -> builder().startObject().field("path", path).endObject()); + } + + public void testObjects() throws Exception { + Map objects = new HashMap<>(); + objects.put("{'objects':[false,true,false]}", new Object[]{false, true, false}); + objects.put("{'objects':[1,1,2,3,5,8,13]}", new Object[]{(byte) 1, (byte) 1, (byte) 2, (byte) 3, (byte) 5, (byte) 8, (byte) 13}); + objects.put("{'objects':[1.0,1.0,2.0,3.0,5.0,8.0,13.0]}", new Object[]{1.0d, 1.0d, 2.0d, 3.0d, 5.0d, 8.0d, 13.0d}); + objects.put("{'objects':[1.0,1.0,2.0,3.0,5.0,8.0,13.0]}", new Object[]{1.0f, 1.0f, 2.0f, 3.0f, 5.0f, 8.0f, 13.0f}); + objects.put("{'objects':[{'lat':45.759429931640625,'lon':4.8394775390625}]}", new Object[]{GeoPoint.fromGeohash("u05kq4k")}); + objects.put("{'objects':[1,1,2,3,5,8,13]}", new Object[]{1, 1, 2, 3, 5, 8, 13}); + objects.put("{'objects':[1,1,2,3,5,8,13]}", new Object[]{1L, 1L, 2L, 3L, 5L, 8L, 13L}); + objects.put("{'objects':[1,1,2,3,5,8]}", new Object[]{(short) 1, (short) 1, (short) 2, (short) 3, (short) 5, (short) 8}); + objects.put("{'objects':['a','b','c']}", new Object[]{"a", "b", "c"}); + objects.put("{'objects':['a','b','c']}", new Object[]{new Text("a"), new Text(new BytesArray("b")), new Text("c")}); + objects.put("{'objects':['a/b/c','d/e']}", new Object[]{PathUtils.get("a", "b", "c"), PathUtils.get("d", "e")}); + objects.put("{'objects':null}", null); + objects.put("{'objects':[null,null,null]}", new Object[]{null, null, null}); + objects.put("{'objects':['OPEN','CLOSE']}", IndexMetaData.State.values()); + objects.put("{'objects':[{'f1':'v1'},{'f2':'v2'}]}", new Object[]{singletonMap("f1", "v1"), singletonMap("f2", "v2")}); + objects.put("{'objects':[[1,2,3],[4,5]]}", new Object[]{Arrays.asList(1, 2, 3), Arrays.asList(4, 5)}); + + final DateTimeFormatter formatter = XContentBuilder.DEFAULT_DATE_PRINTER; + final Date d1 = new DateTime(2016, 1, 1, 0, 0, DateTimeZone.UTC).toDate(); + final Date d2 = new DateTime(2015, 1, 1, 0, 0, DateTimeZone.UTC).toDate(); + objects.put("{'objects':['" + formatter.print(d1.getTime()) + "','" + formatter.print(d2.getTime()) + "']}", new Object[]{d1, d2}); + + final DateTime dt1 = DateTime.now(); + final DateTime dt2 = new DateTime(2016, 12, 25, 7, 59, 42, 213, DateTimeZone.UTC); + objects.put("{'objects':['" + formatter.print(dt1) + "','2016-12-25T07:59:42.213Z']}", new Object[]{dt1, dt2}); + + final Calendar c1 = new DateTime(2012, 7, 7, 10, 23, DateTimeZone.UTC).toCalendar(Locale.ROOT); + final Calendar c2 = new DateTime(2014, 11, 16, 19, 36, DateTimeZone.UTC).toCalendar(Locale.ROOT); + objects.put("{'objects':['2012-07-07T10:23:00.000Z','2014-11-16T19:36:00.000Z']}", new Object[]{c1, c2}); + + final ToXContent x1 = (builder, params) -> builder.startObject().field("f1", "v1").field("f2", 2).array("f3", 3, 4, 5).endObject(); + final ToXContent x2 = (builder, params) -> builder.startObject().field("f1", "v1").field("f2", x1).endObject(); + objects.put("{'objects':[{'f1':'v1','f2':2,'f3':[3,4,5]},{'f1':'v1','f2':{'f1':'v1','f2':2,'f3':[3,4,5]}}]}", new Object[]{x1, x2}); + + for (Map.Entry o : objects.entrySet()) { + final String expected = o.getKey(); + assertResult(expected, () -> builder().startObject().field("objects", o.getValue()).endObject()); + assertResult(expected, () -> builder().startObject().field("objects").value(o.getValue()).endObject()); + assertResult(expected, () -> builder().startObject().field("objects").values(o.getValue()).endObject()); + assertResult(expected, () -> builder().startObject().array("objects", o.getValue()).endObject()); + } + } + + public void testObject() throws Exception { + Map object = new HashMap<>(); + object.put("{'object':false}", Boolean.FALSE); + object.put("{'object':13}", (byte) 13); + object.put("{'object':5.0}", 5.0d); + object.put("{'object':8.0}", 8.0f); + object.put("{'object':{'lat':45.759429931640625,'lon':4.8394775390625}}", GeoPoint.fromGeohash("u05kq4k")); + object.put("{'object':3}", 3); + object.put("{'object':2}", 2L); + object.put("{'object':1}", (short) 1); + object.put("{'object':'string'}", "string"); + object.put("{'object':'a'}", new Text("a")); + object.put("{'object':'b'}", new Text(new BytesArray("b"))); + object.put("{'object':'a/b/c'}", PathUtils.get("a", "b", "c")); + object.put("{'object':null}", null); + object.put("{'object':'OPEN'}", IndexMetaData.State.OPEN); + object.put("{'object':'NM'}", DistanceUnit.NAUTICALMILES); + object.put("{'object':{'f1':'v1'}}", singletonMap("f1", "v1")); + object.put("{'object':{'f1':{'f2':'v2'}}}", singletonMap("f1", singletonMap("f2", "v2"))); + object.put("{'object':[1,2,3]}", Arrays.asList(1, 2, 3)); + + final DateTimeFormatter formatter = XContentBuilder.DEFAULT_DATE_PRINTER; + final Date d1 = new DateTime(2016, 1, 1, 0, 0, DateTimeZone.UTC).toDate(); + object.put("{'object':'" + formatter.print(d1.getTime()) + "'}", d1); + + final DateTime d2 = DateTime.now(); + object.put("{'object':'" + formatter.print(d2) + "'}", d2); + + final Calendar c1 = new DateTime(2010, 1, 1, 0, 0, DateTimeZone.UTC).toCalendar(Locale.ROOT); + object.put("{'object':'2010-01-01T00:00:00.000Z'}", c1); + + final ToXContent x1 = (builder, params) -> builder.startObject().field("f1", "v1").field("f2", 2).array("f3", 3, 4, 5).endObject(); + final ToXContent x2 = (builder, params) -> builder.startObject().field("f1", "v1").field("f2", x1).endObject(); + object.put("{'object':{'f1':'v1','f2':{'f1':'v1','f2':2,'f3':[3,4,5]}}}", x2); + + for (Map.Entry o : object.entrySet()) { + final String expected = o.getKey(); + assertResult(expected, () -> builder().humanReadable(true).startObject().field("object", o.getValue()).endObject()); + assertResult(expected, () -> builder().humanReadable(true).startObject().field("object").value(o.getValue()).endObject()); + } + + assertResult("{'objects':[null,null,null]}", () -> builder().startObject().array("objects", null, null, null).endObject()); + } + + public void testToXContent() throws Exception { + assertResult("{'xcontent':null}", () -> builder().startObject().field("xcontent", (ToXContent) null).endObject()); + assertResult("{'xcontent':null}", () -> builder().startObject().field("xcontent").value((ToXContent) null).endObject()); + + ToXContent xcontent0 = (builder, params) -> { + builder.startObject(); + builder.field("field", "value"); + builder.array("array", "1", "2", "3"); + builder.startObject("foo"); + builder.field("bar", "baz"); + builder.endObject(); + builder.endObject(); + return builder; + }; + + assertResult("{'field':'value','array':['1','2','3'],'foo':{'bar':'baz'}}", () -> builder().value(xcontent0)); + assertResult("{'xcontent':{'field':'value','array':['1','2','3'],'foo':{'bar':'baz'}}}", () -> builder() + .startObject() + .field("xcontent", xcontent0) + .endObject()); + + ToXContent xcontent1 = (builder, params) -> { + builder.startObject(); + builder.field("field", "value"); + builder.startObject("foo"); + builder.field("bar", "baz"); + builder.endObject(); + builder.endObject(); + return builder; + }; + + ToXContent xcontent2 = (builder, params) -> { + builder.startObject(); + builder.field("root", xcontent0); + builder.array("childs", xcontent0, xcontent1); + builder.endObject(); + return builder; + }; + assertResult("{'root':{" + + "'field':'value'," + + "'array':['1','2','3']," + + "'foo':{'bar':'baz'}" + + "}," + + "'childs':[" + + "{'field':'value','array':['1','2','3'],'foo':{'bar':'baz'}}," + + "{'field':'value','foo':{'bar':'baz'}}" + + "]}", () -> builder().value(xcontent2)); + } + + public void testMap() throws Exception { + Map> maps = new HashMap<>(); + maps.put("{'map':null}", (Map) null); + maps.put("{'map':{}}", Collections.emptyMap()); + maps.put("{'map':{'key':'value'}}", singletonMap("key", "value")); + + Map innerMap = new HashMap<>(); + innerMap.put("string", "value"); + innerMap.put("int", 42); + innerMap.put("long", 42L); + innerMap.put("long[]", new long[]{1L, 3L}); + innerMap.put("path", PathUtils.get("path", "to", "file")); + innerMap.put("object", singletonMap("key", "value")); + maps.put("{'map':{'path':'path/to/file','string':'value','long[]':[1,3],'int':42,'long':42,'object':{'key':'value'}}}", innerMap); + + for (Map.Entry> m : maps.entrySet()) { + final String expected = m.getKey(); + assertResult(expected, () -> builder().startObject().field("map", m.getValue()).endObject()); + assertResult(expected, () -> builder().startObject().field("map").value(m.getValue()).endObject()); + assertResult(expected, () -> builder().startObject().field("map").map(m.getValue()).endObject()); + } + } + + public void testIterable() throws Exception { + Map> iterables = new HashMap<>(); + iterables.put("{'iter':null}", (Iterable) null); + iterables.put("{'iter':[]}", Collections.emptyList()); + iterables.put("{'iter':['a','b']}", Arrays.asList("a", "b")); + iterables.put("{'iter':'path/to/file'}", PathUtils.get("path", "to", "file")); + iterables.put("{'iter':['a/b/c','c/d']}", Arrays.asList(PathUtils.get("a", "b", "c"), PathUtils.get("c", "d"))); + + for (Map.Entry> i : iterables.entrySet()) { + final String expected = i.getKey(); + assertResult(expected, () -> builder().startObject().field("iter", i.getValue()).endObject()); + assertResult(expected, () -> builder().startObject().field("iter").value(i.getValue()).endObject()); + } + } + + public void testUnknownObject() throws Exception { + Map objects = new HashMap<>(); + objects.put("{'obj':50.63}", DistanceUnit.METERS.fromMeters(50.63)); + objects.put("{'obj':'MINUTES'}", TimeUnit.MINUTES); + objects.put("{'obj':'class org.elasticsearch.common.xcontent.BaseXContentTestCase'}", BaseXContentTestCase.class); + + for (Map.Entry o : objects.entrySet()) { + final String expected = o.getKey(); + assertResult(expected, () -> builder().startObject().field("obj", o.getValue()).endObject()); + assertResult(expected, () -> builder().startObject().field("obj").value(o.getValue()).endObject()); + } + } public void testBasics() throws IOException { ByteArrayOutputStream os = new ByteArrayOutputStream(); @@ -56,7 +690,7 @@ public abstract class BaseXContentTestCase extends ESTestCase { generator.writeNumber(2L); } }); - assertEquals(e.getMessage(), "unclosed object or array found"); + assertEquals(e.getMessage(), "Unclosed object or array found"); } public void testMissingEndArray() throws IOException { @@ -67,11 +701,11 @@ public abstract class BaseXContentTestCase extends ESTestCase { generator.writeNumber(2L); } }); - assertEquals(e.getMessage(), "unclosed object or array found"); + assertEquals(e.getMessage(), "Unclosed object or array found"); } public void testRawField() throws Exception { - for (boolean useStream : new boolean[] {false, true}) { + for (boolean useStream : new boolean[]{false, true}) { for (XContentType xcontentType : XContentType.values()) { doTestRawField(xcontentType.xContent(), useStream); } @@ -182,4 +816,76 @@ public abstract class BaseXContentTestCase extends ESTestCase { assertEquals("bar", map.get("foo")); assertEquals(bigInteger, map.get("bigint")); } + + public void testEnsureNameNotNull() { + IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> XContentBuilder.ensureNameNotNull(null)); + assertThat(e.getMessage(), containsString("Field name cannot be null")); + } + + public void testFormatterNameNotNull() { + IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> XContentBuilder.ensureFormatterNotNull(null)); + assertThat(e.getMessage(), containsString("DateTimeFormatter cannot be null")); + } + + public void testEnsureNotNull() { + IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> XContentBuilder.ensureNotNull(null, "message")); + assertThat(e.getMessage(), containsString("message")); + + XContentBuilder.ensureNotNull("foo", "No exception must be thrown"); + } + + private static void expectUnclosedException(ThrowingRunnable runnable) { + IllegalStateException e = expectThrows(IllegalStateException.class, runnable); + assertThat(e.getMessage(), containsString("Failed to close the XContentBuilder")); + assertThat(e.getCause(), allOf(notNullValue(), instanceOf(IOException.class))); + assertThat(e.getCause().getMessage(), containsString("Unclosed object or array found")); + } + + private static void expectValueException(ThrowingRunnable runnable) { + JsonGenerationException e = expectThrows(JsonGenerationException.class, runnable); + assertThat(e.getMessage(), containsString("expecting a value")); + } + + private static void expectFieldException(ThrowingRunnable runnable) { + JsonGenerationException e = expectThrows(JsonGenerationException.class, runnable); + assertThat(e.getMessage(), containsString("expecting field name")); + } + + private static void expectNonNullFieldException(ThrowingRunnable runnable) { + IllegalArgumentException e = expectThrows(IllegalArgumentException.class, runnable); + assertThat(e.getMessage(), containsString("Field name cannot be null")); + } + + private static void expectNonNullFormatterException(ThrowingRunnable runnable) { + IllegalArgumentException e = expectThrows(IllegalArgumentException.class, runnable); + assertThat(e.getMessage(), containsString("DateTimeFormatter cannot be null")); + } + + private static void expectObjectException(ThrowingRunnable runnable) { + JsonGenerationException e = expectThrows(JsonGenerationException.class, runnable); + assertThat(e.getMessage(), containsString("Current context not Object")); + } + + private static void expectArrayException(ThrowingRunnable runnable) { + JsonGenerationException e = expectThrows(JsonGenerationException.class, runnable); + assertThat(e.getMessage(), containsString("Current context not Array")); + } + + public static Matcher equalToJson(String json) { + return Matchers.equalTo(json.replace("'", "\"")); + } + + private static void assertResult(String expected, Builder builder) throws IOException { + // Build the XContentBuilder, convert its bytes to JSON and check it matches + assertThat(XContentHelper.convertToJson(builder.build().bytes(), randomBoolean()), equalToJson(expected)); + } + + private static byte[] randomBytes() throws Exception { + return randomUnicodeOfLength(scaledRandomIntBetween(10, 1000)).getBytes("UTF-8"); + } + + @FunctionalInterface + private interface Builder { + XContentBuilder build() throws IOException; + } } diff --git a/core/src/test/java/org/elasticsearch/common/xcontent/builder/XContentBuilderTests.java b/core/src/test/java/org/elasticsearch/common/xcontent/builder/XContentBuilderTests.java index d0e095e8c65..f0b1b7f5e1b 100644 --- a/core/src/test/java/org/elasticsearch/common/xcontent/builder/XContentBuilderTests.java +++ b/core/src/test/java/org/elasticsearch/common/xcontent/builder/XContentBuilderTests.java @@ -173,9 +173,9 @@ public class XContentBuilderTests extends ESTestCase { public void testDateTypesConversion() throws Exception { Date date = new Date(); - String expectedDate = XContentBuilder.defaultDatePrinter.print(date.getTime()); + String expectedDate = XContentBuilder.DEFAULT_DATE_PRINTER.print(date.getTime()); Calendar calendar = new GregorianCalendar(TimeZone.getTimeZone("UTC"), Locale.ROOT); - String expectedCalendar = XContentBuilder.defaultDatePrinter.print(calendar.getTimeInMillis()); + String expectedCalendar = XContentBuilder.DEFAULT_DATE_PRINTER.print(calendar.getTimeInMillis()); XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON); builder.startObject().field("date", date).endObject(); assertThat(builder.string(), equalTo("{\"date\":\"" + expectedDate + "\"}")); @@ -339,17 +339,17 @@ public class XContentBuilderTests extends ESTestCase { builder.map(Collections.singletonMap(null, "test")); fail("write map should have failed"); } catch(IllegalArgumentException e) { - assertThat(e.getMessage(), equalTo("field name cannot be null")); + assertThat(e.getMessage(), equalTo("Field name cannot be null")); } } public void testWriteMapValueWithNullKeys() throws IOException { XContentBuilder builder = XContentFactory.contentBuilder(randomFrom(XContentType.values())); try { - builder.value(Collections.singletonMap(null, "test")); + builder.map(Collections.singletonMap(null, "test")); fail("write map should have failed"); } catch(IllegalArgumentException e) { - assertThat(e.getMessage(), equalTo("field name cannot be null")); + assertThat(e.getMessage(), equalTo("Field name cannot be null")); } } @@ -360,7 +360,7 @@ public class XContentBuilderTests extends ESTestCase { builder.field("map", Collections.singletonMap(null, "test")); fail("write map should have failed"); } catch(IllegalArgumentException e) { - assertThat(e.getMessage(), equalTo("field name cannot be null")); + assertThat(e.getMessage(), equalTo("Field name cannot be null")); } } @@ -371,8 +371,8 @@ public class XContentBuilderTests extends ESTestCase { builder.field("foo", true); } }); - assertThat(e.getMessage(), equalTo("failed to close the XContentBuilder")); - assertThat(e.getCause().getMessage(), equalTo("unclosed object or array found")); + assertThat(e.getMessage(), equalTo("Failed to close the XContentBuilder")); + assertThat(e.getCause().getMessage(), equalTo("Unclosed object or array found")); } public void testMissingEndArray() throws IOException { @@ -384,7 +384,7 @@ public class XContentBuilderTests extends ESTestCase { builder.value(1); } }); - assertThat(e.getMessage(), equalTo("failed to close the XContentBuilder")); - assertThat(e.getCause().getMessage(), equalTo("unclosed object or array found")); + assertThat(e.getMessage(), equalTo("Failed to close the XContentBuilder")); + assertThat(e.getCause().getMessage(), equalTo("Unclosed object or array found")); } } diff --git a/core/src/test/java/org/elasticsearch/common/xcontent/cbor/JsonVsCborTests.java b/core/src/test/java/org/elasticsearch/common/xcontent/cbor/JsonVsCborTests.java index efbca114aac..fb726b97e3e 100644 --- a/core/src/test/java/org/elasticsearch/common/xcontent/cbor/JsonVsCborTests.java +++ b/core/src/test/java/org/elasticsearch/common/xcontent/cbor/JsonVsCborTests.java @@ -48,8 +48,10 @@ public class JsonVsCborTests extends ESTestCase { xsonGen.writeStringField("test", "value"); jsonGen.writeStringField("test", "value"); - xsonGen.writeArrayFieldStart("arr"); - jsonGen.writeArrayFieldStart("arr"); + xsonGen.writeFieldName("arr"); + xsonGen.writeStartArray(); + jsonGen.writeFieldName("arr"); + jsonGen.writeStartArray(); xsonGen.writeNumber(1); jsonGen.writeNumber(1); xsonGen.writeNull(); diff --git a/core/src/test/java/org/elasticsearch/common/xcontent/smile/JsonVsSmileTests.java b/core/src/test/java/org/elasticsearch/common/xcontent/smile/JsonVsSmileTests.java index 63b19a63822..ecf49be6629 100644 --- a/core/src/test/java/org/elasticsearch/common/xcontent/smile/JsonVsSmileTests.java +++ b/core/src/test/java/org/elasticsearch/common/xcontent/smile/JsonVsSmileTests.java @@ -48,8 +48,10 @@ public class JsonVsSmileTests extends ESTestCase { xsonGen.writeStringField("test", "value"); jsonGen.writeStringField("test", "value"); - xsonGen.writeArrayFieldStart("arr"); - jsonGen.writeArrayFieldStart("arr"); + xsonGen.writeFieldName("arr"); + xsonGen.writeStartArray(); + jsonGen.writeFieldName("arr"); + jsonGen.writeStartArray(); xsonGen.writeNumber(1); jsonGen.writeNumber(1); xsonGen.writeNull(); diff --git a/core/src/test/java/org/elasticsearch/common/xcontent/support/XContentMapValuesTests.java b/core/src/test/java/org/elasticsearch/common/xcontent/support/XContentMapValuesTests.java index 1c4ff9874a5..ba2043bbe20 100644 --- a/core/src/test/java/org/elasticsearch/common/xcontent/support/XContentMapValuesTests.java +++ b/core/src/test/java/org/elasticsearch/common/xcontent/support/XContentMapValuesTests.java @@ -139,7 +139,7 @@ public class XContentMapValuesTests extends ESTestCase { // lists builder = XContentFactory.jsonBuilder().startObject() - .startObject("path1").field("test", "value1", "value2").endObject() + .startObject("path1").array("test", "value1", "value2").endObject() .endObject(); try (XContentParser parser = XContentFactory.xContent(XContentType.JSON).createParser(builder.string())) { diff --git a/core/src/test/java/org/elasticsearch/get/GetActionIT.java b/core/src/test/java/org/elasticsearch/get/GetActionIT.java index 57de501d93d..25c0e616090 100644 --- a/core/src/test/java/org/elasticsearch/get/GetActionIT.java +++ b/core/src/test/java/org/elasticsearch/get/GetActionIT.java @@ -264,10 +264,10 @@ public class GetActionIT extends ESIntegTestCase { assertThat(response.isExists(), equalTo(false)); client().prepareIndex("test", "type1", "1") - .setSource(jsonBuilder().startObject().field("field", "1", "2").endObject()).get(); + .setSource(jsonBuilder().startObject().array("field", "1", "2").endObject()).get(); client().prepareIndex("test", "type2", "1") - .setSource(jsonBuilder().startObject().field("field", "1", "2").endObject()).get(); + .setSource(jsonBuilder().startObject().array("field", "1", "2").endObject()).get(); response = client().prepareGet("test", "type1", "1").setFields("field").get(); assertThat(response.isExists(), equalTo(true)); diff --git a/core/src/test/java/org/elasticsearch/index/mapper/CompletionFieldMapperTests.java b/core/src/test/java/org/elasticsearch/index/mapper/CompletionFieldMapperTests.java index 902b9f2f406..78f9f355b0c 100644 --- a/core/src/test/java/org/elasticsearch/index/mapper/CompletionFieldMapperTests.java +++ b/core/src/test/java/org/elasticsearch/index/mapper/CompletionFieldMapperTests.java @@ -37,13 +37,6 @@ import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.json.JsonXContent; import org.elasticsearch.index.IndexService; import org.elasticsearch.index.analysis.NamedAnalyzer; -import org.elasticsearch.index.mapper.CompletionFieldMapper; -import org.elasticsearch.index.mapper.DocumentMapper; -import org.elasticsearch.index.mapper.DocumentMapperParser; -import org.elasticsearch.index.mapper.FieldMapper; -import org.elasticsearch.index.mapper.MappedFieldType; -import org.elasticsearch.index.mapper.MapperParsingException; -import org.elasticsearch.index.mapper.ParsedDocument; import org.elasticsearch.test.ESSingleNodeTestCase; import java.io.IOException; @@ -285,7 +278,7 @@ public class CompletionFieldMapperTests extends ESSingleNodeTestCase { .field("weight", 4) .endObject() .startObject() - .field("input", "suggestion4", "suggestion5", "suggestion6") + .array("input", "suggestion4", "suggestion5", "suggestion6") .field("weight", 5) .endObject() .endArray() diff --git a/core/src/test/java/org/elasticsearch/index/mapper/CopyToMapperTests.java b/core/src/test/java/org/elasticsearch/index/mapper/CopyToMapperTests.java index e71ac5b492c..cd08ba98a88 100644 --- a/core/src/test/java/org/elasticsearch/index/mapper/CopyToMapperTests.java +++ b/core/src/test/java/org/elasticsearch/index/mapper/CopyToMapperTests.java @@ -28,16 +28,7 @@ import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.json.JsonXContent; import org.elasticsearch.index.IndexService; -import org.elasticsearch.index.mapper.DocumentMapper; -import org.elasticsearch.index.mapper.DocumentMapperParser; -import org.elasticsearch.index.mapper.FieldMapper; -import org.elasticsearch.index.mapper.LegacyLongFieldMapper; -import org.elasticsearch.index.mapper.MapperParsingException; -import org.elasticsearch.index.mapper.MapperService; -import org.elasticsearch.index.mapper.ParseContext; import org.elasticsearch.index.mapper.ParseContext.Document; -import org.elasticsearch.index.mapper.ParsedDocument; -import org.elasticsearch.index.mapper.TextFieldMapper; import org.elasticsearch.test.ESSingleNodeTestCase; import java.util.Arrays; @@ -46,7 +37,6 @@ import java.util.Map; import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.startsWith; diff --git a/core/src/test/java/org/elasticsearch/index/mapper/SourceFieldMapperTests.java b/core/src/test/java/org/elasticsearch/index/mapper/SourceFieldMapperTests.java index 169a7e1d90a..c6f9615623c 100644 --- a/core/src/test/java/org/elasticsearch/index/mapper/SourceFieldMapperTests.java +++ b/core/src/test/java/org/elasticsearch/index/mapper/SourceFieldMapperTests.java @@ -28,11 +28,6 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentType; -import org.elasticsearch.index.mapper.DocumentMapper; -import org.elasticsearch.index.mapper.DocumentMapperParser; -import org.elasticsearch.index.mapper.MapperParsingException; -import org.elasticsearch.index.mapper.MapperService; -import org.elasticsearch.index.mapper.ParsedDocument; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.test.ESSingleNodeTestCase; import org.elasticsearch.test.InternalSettingsPlugin; @@ -87,7 +82,7 @@ public class SourceFieldMapperTests extends ESSingleNodeTestCase { public void testIncludes() throws Exception { String mapping = XContentFactory.jsonBuilder().startObject().startObject("type") - .startObject("_source").field("includes", new String[]{"path1*"}).endObject() + .startObject("_source").array("includes", new String[]{"path1*"}).endObject() .endObject().endObject().string(); DocumentMapper documentMapper = createIndex("test").mapperService().documentMapperParser().parse("type", new CompressedXContent(mapping)); @@ -108,7 +103,7 @@ public class SourceFieldMapperTests extends ESSingleNodeTestCase { public void testExcludes() throws Exception { String mapping = XContentFactory.jsonBuilder().startObject().startObject("type") - .startObject("_source").field("excludes", new String[]{"path1*"}).endObject() + .startObject("_source").array("excludes", new String[]{"path1*"}).endObject() .endObject().endObject().string(); DocumentMapper documentMapper = createIndex("test").mapperService().documentMapperParser().parse("type", new CompressedXContent(mapping)); diff --git a/core/src/test/java/org/elasticsearch/index/mapper/TextFieldMapperTests.java b/core/src/test/java/org/elasticsearch/index/mapper/TextFieldMapperTests.java index af24e16b54b..846d2c56669 100644 --- a/core/src/test/java/org/elasticsearch/index/mapper/TextFieldMapperTests.java +++ b/core/src/test/java/org/elasticsearch/index/mapper/TextFieldMapperTests.java @@ -208,7 +208,7 @@ public class TextFieldMapperTests extends ESSingleNodeTestCase { ParsedDocument doc = mapper.parse("test", "type", "1", XContentFactory.jsonBuilder() .startObject() - .field("field", new String[] {"a", "b"}) + .array("field", new String[] {"a", "b"}) .endObject() .bytes()); @@ -247,7 +247,7 @@ public class TextFieldMapperTests extends ESSingleNodeTestCase { ParsedDocument doc = mapper.parse("test", "type", "1", XContentFactory.jsonBuilder() .startObject() - .field("field", new String[] {"a", "b"}) + .array("field", new String[] {"a", "b"}) .endObject() .bytes()); diff --git a/core/src/test/java/org/elasticsearch/mget/SimpleMgetIT.java b/core/src/test/java/org/elasticsearch/mget/SimpleMgetIT.java index 15fac5ab4c2..e022bd75213 100644 --- a/core/src/test/java/org/elasticsearch/mget/SimpleMgetIT.java +++ b/core/src/test/java/org/elasticsearch/mget/SimpleMgetIT.java @@ -108,7 +108,7 @@ public class SimpleMgetIT extends ESIntegTestCase { public void testThatSourceFilteringIsSupported() throws Exception { assertAcked(prepareCreate("test").addAlias(new Alias("alias"))); BytesReference sourceBytesRef = jsonBuilder().startObject() - .field("field", "1", "2") + .array("field", "1", "2") .startObject("included").field("field", "should be seen").field("hidden_field", "should not be seen").endObject() .field("excluded", "should not be seen") .endObject().bytes(); diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/BooleanTermsIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/BooleanTermsIT.java index e38f33cfa2b..cfb290284ae 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/BooleanTermsIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/BooleanTermsIT.java @@ -78,7 +78,7 @@ public class BooleanTermsIT extends ESIntegTestCase { builders[i] = client().prepareIndex("idx", "type").setSource(jsonBuilder() .startObject() .field(SINGLE_VALUED_FIELD_NAME, singleValue) - .field(MULTI_VALUED_FIELD_NAME, multiValue) + .array(MULTI_VALUED_FIELD_NAME, multiValue) .endObject()); } indexRandom(true, builders); diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/NestedIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/NestedIT.java index c8b7aa6ad55..51d702e3548 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/NestedIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/NestedIT.java @@ -468,7 +468,7 @@ public class NestedIT extends ESIntegTestCase { client().prepareIndex("idx4", "product", "1").setSource(jsonBuilder().startObject() .field("name", "product1") - .field("categories", "1", "2", "3", "4") + .array("categories", "1", "2", "3", "4") .startArray("property") .startObject().field("id", 1).endObject() .startObject().field("id", 2).endObject() @@ -477,7 +477,7 @@ public class NestedIT extends ESIntegTestCase { .endObject()).get(); client().prepareIndex("idx4", "product", "2").setSource(jsonBuilder().startObject() .field("name", "product2") - .field("categories", "1", "2") + .array("categories", "1", "2") .startArray("property") .startObject().field("id", 1).endObject() .startObject().field("id", 5).endObject() diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/CardinalityIT.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/CardinalityIT.java index 594eba7ddb5..cff1fa746dc 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/CardinalityIT.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/CardinalityIT.java @@ -134,11 +134,11 @@ public class CardinalityIT extends ESIntegTestCase { builders[i] = client().prepareIndex("idx", "type").setSource(jsonBuilder() .startObject() .field("str_value", "s" + i) - .field("str_values", new String[]{"s" + (i * 2), "s" + (i * 2 + 1)}) + .array("str_values", new String[]{"s" + (i * 2), "s" + (i * 2 + 1)}) .field("l_value", i) - .field("l_values", new int[] {i * 2, i * 2 + 1}) + .array("l_values", new int[] {i * 2, i * 2 + 1}) .field("d_value", i) - .field("d_values", new double[]{i * 2, i * 2 + 1}) + .array("d_values", new double[]{i * 2, i * 2 + 1}) .endObject()); } indexRandom(true, builders); diff --git a/core/src/test/java/org/elasticsearch/search/fetch/subphase/highlight/HighlighterSearchIT.java b/core/src/test/java/org/elasticsearch/search/fetch/subphase/highlight/HighlighterSearchIT.java index 33e8cb3784f..843ab09b2fe 100644 --- a/core/src/test/java/org/elasticsearch/search/fetch/subphase/highlight/HighlighterSearchIT.java +++ b/core/src/test/java/org/elasticsearch/search/fetch/subphase/highlight/HighlighterSearchIT.java @@ -1537,7 +1537,7 @@ public class HighlighterSearchIT extends ESIntegTestCase { .addMapping("type1", "tags", "type=text")); ensureGreen(); client().prepareIndex("test", "type1", "1") - .setSource(jsonBuilder().startObject().field("tags", + .setSource(jsonBuilder().startObject().array("tags", "this is a really long tag i would like to highlight", "here is another one that is very long tag and has the tag token near the end").endObject()).get(); refresh(); diff --git a/core/src/test/java/org/elasticsearch/search/sort/GeoDistanceIT.java b/core/src/test/java/org/elasticsearch/search/sort/GeoDistanceIT.java index 95c20381cfd..b78496ff18d 100644 --- a/core/src/test/java/org/elasticsearch/search/sort/GeoDistanceIT.java +++ b/core/src/test/java/org/elasticsearch/search/sort/GeoDistanceIT.java @@ -230,7 +230,7 @@ public class GeoDistanceIT extends ESIntegTestCase { .actionGet(); client().prepareIndex("test", "type1", "3") - .setSource(jsonBuilder().startObject().field("names", "Times Square", "Tribeca").startArray("locations") + .setSource(jsonBuilder().startObject().array("names", "Times Square", "Tribeca").startArray("locations") // to NY: 5.286 km .startObject().field("lat", 40.759011).field("lon", -73.9844722).endObject() // to NY: 0.4621 km @@ -238,7 +238,7 @@ public class GeoDistanceIT extends ESIntegTestCase { .execute().actionGet(); client().prepareIndex("test", "type1", "4") - .setSource(jsonBuilder().startObject().field("names", "Wall Street", "Soho").startArray("locations") + .setSource(jsonBuilder().startObject().array("names", "Wall Street", "Soho").startArray("locations") // to NY: 1.055 km .startObject().field("lat", 40.7051157).field("lon", -74.0088305).endObject() // to NY: 1.258 km @@ -246,7 +246,7 @@ public class GeoDistanceIT extends ESIntegTestCase { .execute().actionGet(); client().prepareIndex("test", "type1", "5") - .setSource(jsonBuilder().startObject().field("names", "Greenwich Village", "Brooklyn").startArray("locations") + .setSource(jsonBuilder().startObject().array("names", "Greenwich Village", "Brooklyn").startArray("locations") // to NY: 2.029 km .startObject().field("lat", 40.731033).field("lon", -73.9962255).endObject() // to NY: 8.572 km @@ -355,14 +355,14 @@ public class GeoDistanceIT extends ESIntegTestCase { ensureGreen(); client().prepareIndex("test", "type1", "1") - .setSource(jsonBuilder().startObject().field("names", "Times Square", "Tribeca").startArray("locations") + .setSource(jsonBuilder().startObject().array("names", "Times Square", "Tribeca").startArray("locations") // to NY: 5.286 km .startObject().field("lat", 40.759011).field("lon", -73.9844722).endObject() // to NY: 0.4621 km .startObject().field("lat", 40.718266).field("lon", -74.007819).endObject().endArray().endObject()) .execute().actionGet(); - client().prepareIndex("test", "type1", "2").setSource(jsonBuilder().startObject().field("names", "Wall Street", "Soho").endObject()) + client().prepareIndex("test", "type1", "2").setSource(jsonBuilder().startObject().array("names", "Wall Street", "Soho").endObject()) .execute().actionGet(); refresh(); diff --git a/core/src/test/java/org/elasticsearch/search/sort/SimpleSortIT.java b/core/src/test/java/org/elasticsearch/search/sort/SimpleSortIT.java index 80cda7d7b56..24a82526eda 100644 --- a/core/src/test/java/org/elasticsearch/search/sort/SimpleSortIT.java +++ b/core/src/test/java/org/elasticsearch/search/sort/SimpleSortIT.java @@ -25,7 +25,6 @@ import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.search.ShardSearchFailure; import org.elasticsearch.common.geo.GeoPoint; import org.elasticsearch.common.geo.GeoUtils; -import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.index.fielddata.ScriptDocValues; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.script.MockScriptPlugin; @@ -251,9 +250,9 @@ public class SimpleSortIT extends ESIntegTestCase { .setSource(jsonBuilder() .startObject() .field("ord", i) - .field("svalue", new String[]{"" + i, "" + (i + 1), "" + (i + 2)}) - .field("lvalue", new long[]{i, i + 1, i + 2}) - .field("dvalue", new double[]{i, i + 1, i + 2}) + .array("svalue", new String[]{"" + i, "" + (i + 1), "" + (i + 2)}) + .array("lvalue", new long[]{i, i + 1, i + 2}) + .array("dvalue", new double[]{i, i + 1, i + 2}) .startObject("gvalue") .field("lat", (double) i + 1) .field("lon", (double) i) diff --git a/core/src/test/java/org/elasticsearch/search/suggest/CompletionSuggestSearch2xIT.java b/core/src/test/java/org/elasticsearch/search/suggest/CompletionSuggestSearch2xIT.java index 49ad540d956..172183c57c8 100644 --- a/core/src/test/java/org/elasticsearch/search/suggest/CompletionSuggestSearch2xIT.java +++ b/core/src/test/java/org/elasticsearch/search/suggest/CompletionSuggestSearch2xIT.java @@ -99,7 +99,7 @@ public class CompletionSuggestSearch2xIT extends ESIntegTestCase { client().prepareIndex(INDEX, TYPE, "" + i) .setSource(jsonBuilder() .startObject().startObject(FIELD) - .field("input", input[i]) + .array("input", input[i]) .endObject() .endObject() ) @@ -941,7 +941,7 @@ public class CompletionSuggestSearch2xIT extends ESIntegTestCase { builders[i] = client().prepareIndex(INDEX, TYPE, "" + i) .setSource(jsonBuilder() .startObject().startObject(FIELD) - .field("input", input[i]) + .array("input", input[i]) .field("output", surface[i]) .startObject("payload").field("id", i).endObject() .field("weight", 1) // WE FORCEFULLY INDEX A BOGUS WEIGHT @@ -955,7 +955,7 @@ public class CompletionSuggestSearch2xIT extends ESIntegTestCase { builders[i] = client().prepareIndex(INDEX, TYPE, "n" + i) .setSource(jsonBuilder() .startObject().startObject(FIELD) - .field("input", input[i]) + .array("input", input[i]) .field("output", surface[i]) .startObject("payload").field("id", i).endObject() .field("weight", weight[i]) diff --git a/core/src/test/java/org/elasticsearch/search/suggest/ContextSuggestSearch2xIT.java b/core/src/test/java/org/elasticsearch/search/suggest/ContextSuggestSearch2xIT.java index a3d72f671f7..50733f10838 100644 --- a/core/src/test/java/org/elasticsearch/search/suggest/ContextSuggestSearch2xIT.java +++ b/core/src/test/java/org/elasticsearch/search/suggest/ContextSuggestSearch2xIT.java @@ -190,7 +190,7 @@ public class ContextSuggestSearch2xIT extends ESIntegTestCase { .startObject("context") .startObject("location") .field("type", "geo") - .field("precision", precision) + .array("precision", precision) .endObject() .endObject() .endObject().endObject() @@ -209,7 +209,7 @@ public class ContextSuggestSearch2xIT extends ESIntegTestCase { .startObject("context") .startObject("location") .field("type", "geo") - .field("precision", precision) + .array("precision", precision) .endObject() .endObject() .endObject().endObject() @@ -314,7 +314,7 @@ public class ContextSuggestSearch2xIT extends ESIntegTestCase { { "pizza - treptow", "pizza", "food" } }; for (int i = 0; i < locations.length; i++) { - XContentBuilder source = jsonBuilder().startObject().startObject(FIELD).field("input", input[i]) + XContentBuilder source = jsonBuilder().startObject().startObject(FIELD).array("input", input[i]) .startObject("context").field("st", locations[i]).endObject().field("payload", locations[i]) .endObject().endObject(); client().prepareIndex(INDEX, TYPE, "" + i).setSource(source).execute().actionGet(); @@ -343,7 +343,7 @@ public class ContextSuggestSearch2xIT extends ESIntegTestCase { .addMapping(TYPE, createMapping(TYPE, ContextBuilder.category("st")))); for (int i = 0; i < HEROS.length; i++) { - XContentBuilder source = jsonBuilder().startObject().startObject(FIELD).field("input", HEROS[i]) + XContentBuilder source = jsonBuilder().startObject().startObject(FIELD).array("input", HEROS[i]) .startObject("context").field("st", i%3).endObject() .startObject("payload").field("group", i % 3).field("id", i).endObject() .endObject().endObject(); @@ -376,12 +376,12 @@ public class ContextSuggestSearch2xIT extends ESIntegTestCase { XContentBuilder doc1 = jsonBuilder(); doc1.startObject().startObject("suggest_field") .field("input", "backpack_red") - .startObject("context").field("color", "red", "all_colors").endObject() + .startObject("context").array("color", "red", "all_colors").endObject() .endObject().endObject(); XContentBuilder doc2 = jsonBuilder(); doc2.startObject().startObject("suggest_field") .field("input", "backpack_green") - .startObject("context").field("color", "green", "all_colors").endObject() + .startObject("context").array("color", "green", "all_colors").endObject() .endObject().endObject(); client().prepareIndex(INDEX, TYPE, "1") @@ -451,7 +451,7 @@ public class ContextSuggestSearch2xIT extends ESIntegTestCase { client().prepareIndex(INDEX, TYPE, "" + i) .setSource( jsonBuilder().startObject().field("category", Integer.toString(i % 3)).startObject(FIELD) - .field("input", HEROS[i]) + .array("input", HEROS[i]) .startObject("context").endObject().field("payload", Integer.toString(i % 3)) .endObject().endObject()).execute().actionGet(); } @@ -508,7 +508,7 @@ public class ContextSuggestSearch2xIT extends ESIntegTestCase { client().prepareIndex(INDEX, TYPE, "" + i) .setSource( jsonBuilder().startObject().startArray("category").value(Integer.toString(i % 3)).value("other").endArray() - .startObject(FIELD).field("input", HEROS[i]).startObject("context").endObject() + .startObject(FIELD).array("input", HEROS[i]).startObject("context").endObject() .field("payload", Integer.toString(i % 3)).endObject().endObject()).execute().actionGet(); } @@ -535,7 +535,7 @@ public class ContextSuggestSearch2xIT extends ESIntegTestCase { client().prepareIndex(INDEX, TYPE, "" + i) .setSource( jsonBuilder().startObject().field("categoryA").value("" + (char) ('0' + (i % 3))).field("categoryB") - .value("" + (char) ('A' + (i % 3))).startObject(FIELD).field("input", HEROS[i]) + .value("" + (char) ('A' + (i % 3))).startObject(FIELD).array("input", HEROS[i]) .startObject("context").endObject().field("payload", ((char) ('0' + (i % 3))) + "" + (char) ('A' + (i % 3))) .endObject().endObject()).execute().actionGet(); } @@ -561,7 +561,7 @@ public class ContextSuggestSearch2xIT extends ESIntegTestCase { for (int i = 0; i < HEROS.length; i++) { String source = jsonBuilder().startObject().field("categoryA", "" + (char) ('0' + (i % 3))) - .field("categoryB", "" + (char) ('a' + (i % 3))).startObject(FIELD).field("input", HEROS[i]) + .field("categoryB", "" + (char) ('a' + (i % 3))).startObject(FIELD).array("input", HEROS[i]) .startObject("context").endObject().startObject("payload").field("categoryA", "" + (char) ('0' + (i % 3))) .field("categoryB", "" + (char) ('a' + (i % 3))).endObject().endObject().endObject().string(); client().prepareIndex(INDEX, TYPE, "" + i).setSource(source).execute().actionGet(); @@ -599,7 +599,7 @@ public class ContextSuggestSearch2xIT extends ESIntegTestCase { String type = types[i % types.length]; client().prepareIndex(INDEX, type, "" + i) .setSource( - jsonBuilder().startObject().startObject(FIELD).field("input", HEROS[i]) + jsonBuilder().startObject().startObject(FIELD).array("input", HEROS[i]) .startObject("context").endObject().field("payload", type).endObject().endObject()).execute().actionGet(); } diff --git a/core/src/test/java/org/elasticsearch/search/suggest/completion/CategoryContextMappingTests.java b/core/src/test/java/org/elasticsearch/search/suggest/completion/CategoryContextMappingTests.java index de912bb9636..fd7a33ee5ba 100644 --- a/core/src/test/java/org/elasticsearch/search/suggest/completion/CategoryContextMappingTests.java +++ b/core/src/test/java/org/elasticsearch/search/suggest/completion/CategoryContextMappingTests.java @@ -77,7 +77,7 @@ public class CategoryContextMappingTests extends ESSingleNodeTestCase { .field("weight", 4) .endObject() .startObject() - .field("input", "suggestion5", "suggestion6", "suggestion7") + .array("input", "suggestion5", "suggestion6", "suggestion7") .field("weight", 5) .endObject() .endArray() @@ -107,7 +107,7 @@ public class CategoryContextMappingTests extends ESSingleNodeTestCase { .startObject() .startArray("completion") .startObject() - .field("input", "suggestion5", "suggestion6", "suggestion7") + .array("input", "suggestion5", "suggestion6", "suggestion7") .startObject("contexts") .field("ctx", "ctx1") .endObject() @@ -139,7 +139,7 @@ public class CategoryContextMappingTests extends ESSingleNodeTestCase { ParsedDocument parsedDocument = defaultMapper.parse("test", "type1", "1", jsonBuilder() .startObject() .startObject("completion") - .field("input", "suggestion5", "suggestion6", "suggestion7") + .array("input", "suggestion5", "suggestion6", "suggestion7") .startObject("contexts") .array("ctx", "ctx1", "ctx2", "ctx3") .endObject() @@ -175,7 +175,7 @@ public class CategoryContextMappingTests extends ESSingleNodeTestCase { .startObject() .startArray("completion") .startObject() - .field("input", "suggestion5", "suggestion6", "suggestion7") + .array("input", "suggestion5", "suggestion6", "suggestion7") .field("weight", 5) .startObject("contexts") .array("ctx", "ctx1", "ctx2", "ctx3") diff --git a/core/src/test/java/org/elasticsearch/search/suggest/completion/GeoContextMappingTests.java b/core/src/test/java/org/elasticsearch/search/suggest/completion/GeoContextMappingTests.java index 33624ed8002..2237c1a41da 100644 --- a/core/src/test/java/org/elasticsearch/search/suggest/completion/GeoContextMappingTests.java +++ b/core/src/test/java/org/elasticsearch/search/suggest/completion/GeoContextMappingTests.java @@ -77,7 +77,7 @@ public class GeoContextMappingTests extends ESSingleNodeTestCase { .field("weight", 4) .endObject() .startObject() - .field("input", "suggestion5", "suggestion6", "suggestion7") + .array("input", "suggestion5", "suggestion6", "suggestion7") .field("weight", 5) .endObject() .endArray() @@ -108,7 +108,7 @@ public class GeoContextMappingTests extends ESSingleNodeTestCase { .startObject() .startArray("completion") .startObject() - .field("input", "suggestion5", "suggestion6", "suggestion7") + .array("input", "suggestion5", "suggestion6", "suggestion7") .startObject("contexts") .startObject("ctx") .field("lat", 43.6624803) @@ -143,7 +143,7 @@ public class GeoContextMappingTests extends ESSingleNodeTestCase { ParsedDocument parsedDocument = defaultMapper.parse("test", "type1", "1", jsonBuilder() .startObject() .startObject("completion") - .field("input", "suggestion5", "suggestion6", "suggestion7") + .array("input", "suggestion5", "suggestion6", "suggestion7") .startObject("contexts") .startArray("ctx") .startObject() @@ -188,7 +188,7 @@ public class GeoContextMappingTests extends ESSingleNodeTestCase { .startObject() .startArray("completion") .startObject() - .field("input", "suggestion5", "suggestion6", "suggestion7") + .array("input", "suggestion5", "suggestion6", "suggestion7") .field("weight", 5) .startObject("contexts") .array("loc1", "ezs42e44yx96") diff --git a/core/src/test/java/org/elasticsearch/threadpool/SimpleThreadPoolIT.java b/core/src/test/java/org/elasticsearch/threadpool/SimpleThreadPoolIT.java index 974929dddf2..3928c95bcf8 100644 --- a/core/src/test/java/org/elasticsearch/threadpool/SimpleThreadPoolIT.java +++ b/core/src/test/java/org/elasticsearch/threadpool/SimpleThreadPoolIT.java @@ -76,11 +76,11 @@ public class SimpleThreadPoolIT extends ESIntegTestCase { builders[i] = client().prepareIndex("idx", "type").setSource(jsonBuilder() .startObject() .field("str_value", "s" + i) - .field("str_values", new String[]{"s" + (i * 2), "s" + (i * 2 + 1)}) + .array("str_values", new String[]{"s" + (i * 2), "s" + (i * 2 + 1)}) .field("l_value", i) - .field("l_values", new int[]{i * 2, i * 2 + 1}) + .array("l_values", new int[]{i * 2, i * 2 + 1}) .field("d_value", i) - .field("d_values", new double[]{i * 2, i * 2 + 1}) + .array("d_values", new double[]{i * 2, i * 2 + 1}) .endObject()); } indexRandom(true, builders); diff --git a/test/framework/src/test/java/org/elasticsearch/test/rest/yaml/ObjectPathTests.java b/test/framework/src/test/java/org/elasticsearch/test/rest/yaml/ObjectPathTests.java index 5559fd3f385..1f97a6ed13b 100644 --- a/test/framework/src/test/java/org/elasticsearch/test/rest/yaml/ObjectPathTests.java +++ b/test/framework/src/test/java/org/elasticsearch/test/rest/yaml/ObjectPathTests.java @@ -22,8 +22,6 @@ import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.test.ESTestCase; -import org.elasticsearch.test.rest.yaml.ObjectPath; -import org.elasticsearch.test.rest.yaml.Stash; import java.io.IOException; import java.util.HashMap;