(Java) Using primitive arrays instead of Object with map/builder

also simplify and consolidate the builder generic value write handling
fixes #3279
This commit is contained in:
Shay Banon 2013-07-01 21:46:52 +02:00
parent 2314b8665b
commit 3a0ce0bde8
3 changed files with 210 additions and 356 deletions

View File

@ -28,7 +28,6 @@ import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.io.BytesStream;
import org.elasticsearch.common.io.FastByteArrayOutputStream;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.xcontent.support.XContentMapConverter;
import org.joda.time.DateTimeZone;
import org.joda.time.ReadableInstant;
import org.joda.time.format.DateTimeFormatter;
@ -647,7 +646,7 @@ public final class XContentBuilder implements BytesStream {
endArray();
return this;
}
public XContentBuilder field(XContentBuilderString name, int offset, int length, int... value) throws IOException {
assert ((offset >= 0) && (value.length > length));
startArray(name);
@ -722,163 +721,22 @@ public final class XContentBuilder implements BytesStream {
}
public XContentBuilder field(String name, Object value) throws IOException {
if (value == null) {
nullField(name);
return this;
}
Class type = value.getClass();
if (type == String.class) {
field(name, (String) value);
} else if (type == Float.class) {
field(name, ((Float) value).floatValue());
} else if (type == Double.class) {
field(name, ((Double) value).doubleValue());
} else if (type == Integer.class) {
field(name, ((Integer) value).intValue());
} else if (type == Long.class) {
field(name, ((Long) value).longValue());
} else if (type == Short.class) {
field(name, ((Short) value).shortValue());
} else if (type == Byte.class) {
field(name, ((Byte) value).byteValue());
} else if (type == Boolean.class) {
field(name, ((Boolean) value).booleanValue());
} else if (value instanceof Date) {
field(name, (Date) value);
} else if (value instanceof Calendar) {
field(name, convertCalendar((Calendar) value));
} else if (type == byte[].class) {
field(name, (byte[]) value);
} else if (value instanceof ReadableInstant) {
field(name, (ReadableInstant) value);
} else if (value instanceof Map) {
//noinspection unchecked
field(name, (Map<String, Object>) value);
} else if (value instanceof Iterable) {
field(name, (Iterable) value);
} else if (value instanceof Object[]) {
field(name, (Object[]) value);
} else if (value instanceof int[]) {
field(name, (int[]) value);
} else if (value instanceof long[]) {
field(name, (long[]) value);
} else if (value instanceof float[]) {
field(name, (float[]) value);
} else if (value instanceof double[]) {
field(name, (double[]) value);
} else if (value instanceof BytesReference) {
field(name, (BytesReference) value);
} else if (value instanceof Text) {
field(name, (Text) value);
} else if (value instanceof ToXContent) {
field(name, (ToXContent) value);
} else {
field(name, value.toString());
}
field(name);
writeValue(value);
return this;
}
public XContentBuilder field(XContentBuilderString name, Object value) throws IOException {
if (value == null) {
nullField(name);
return this;
}
Class type = value.getClass();
if (type == String.class) {
field(name, (String) value);
} else if (type == Float.class) {
field(name, ((Float) value).floatValue());
} else if (type == Double.class) {
field(name, ((Double) value).doubleValue());
} else if (type == Integer.class) {
field(name, ((Integer) value).intValue());
} else if (type == Long.class) {
field(name, ((Long) value).longValue());
} else if (type == Short.class) {
field(name, ((Short) value).shortValue());
} else if (type == Byte.class) {
field(name, ((Byte) value).byteValue());
} else if (type == Boolean.class) {
field(name, ((Boolean) value).booleanValue());
} else if (value instanceof Date) {
field(name, (Date) value);
} else if (type == byte[].class) {
field(name, (byte[]) value);
} else if (value instanceof ReadableInstant) {
field(name, (ReadableInstant) value);
} else if (value instanceof Map) {
//noinspection unchecked
field(name, (Map<String, Object>) value);
} else if (value instanceof Iterable) {
field(name, (Iterable) value);
} else if (value instanceof Object[]) {
field(name, (Object[]) value);
} else if (value instanceof int[]) {
field(name, (int[]) value);
} else if (value instanceof long[]) {
field(name, (long[]) value);
} else if (value instanceof float[]) {
field(name, (float[]) value);
} else if (value instanceof double[]) {
field(name, (double[]) value);
} else if (value instanceof BytesReference) {
field(name, (BytesReference) value);
} else if (value instanceof Text) {
field(name, (Text) value);
} else {
field(name, value.toString());
}
field(name);
writeValue(value);
return this;
}
public XContentBuilder value(Object value) throws IOException {
if (value == null) {
return nullValue();
}
Class type = value.getClass();
if (type == String.class) {
value((String) value);
} else if (type == Float.class) {
value(((Float) value).floatValue());
} else if (type == Double.class) {
value(((Double) value).doubleValue());
} else if (type == Integer.class) {
value(((Integer) value).intValue());
} else if (type == Long.class) {
value(((Long) value).longValue());
} else if (type == Short.class) {
value(((Short) value).shortValue());
} else if (type == Byte.class) {
value(((Byte) value).byteValue());
} else if (type == Boolean.class) {
value((Boolean) value);
} else if (type == byte[].class) {
value((byte[]) value);
} else if (value instanceof Date) {
value((Date) value);
} else if (value instanceof Calendar) {
value(convertCalendar((Calendar) value));
} else if (value instanceof ReadableInstant) {
value((ReadableInstant) value);
} else if (value instanceof BytesReference) {
value((BytesReference) value);
} else if (value instanceof Text) {
value((Text) value);
} else if (value instanceof Map) {
//noinspection unchecked
value((Map<String, Object>) value);
} else if (value instanceof Iterable) {
value((Iterable) value);
} else {
throw new IOException("Type not allowed [" + type + "]");
}
writeValue(value);
return this;
}
private Date convertCalendar(Calendar value) {
return value.getTime();
}
public XContentBuilder field(String name, boolean value) throws IOException {
field(name);
generator.writeBoolean(value);
@ -1120,7 +978,7 @@ public final class XContentBuilder implements BytesStream {
if (map == null) {
return nullValue();
}
XContentMapConverter.writeMap(generator, map);
writeMap(map);
return this;
}
@ -1128,7 +986,7 @@ public final class XContentBuilder implements BytesStream {
if (map == null) {
return nullValue();
}
XContentMapConverter.writeMap(generator, map);
writeMap(map);
return this;
}
@ -1162,6 +1020,10 @@ public final class XContentBuilder implements BytesStream {
}
}
public XContentGenerator generator() {
return this.generator;
}
@Nullable
public Object payload() {
return this.payload;
@ -1195,4 +1057,116 @@ public final class XContentBuilder implements BytesStream {
BytesArray bytesArray = bytes().toBytesArray();
return new String(bytesArray.array(), bytesArray.arrayOffset(), bytesArray.length(), Charsets.UTF_8);
}
private void writeMap(Map<String, Object> map) throws IOException {
generator.writeStartObject();
for (Map.Entry<String, Object> entry : map.entrySet()) {
field(entry.getKey());
Object value = entry.getValue();
if (value == null) {
generator.writeNull();
} else {
writeValue(value);
}
}
generator.writeEndObject();
}
private void writeValue(Object value) throws IOException {
if (value == null) {
generator.writeNull();
return;
}
Class type = value.getClass();
if (type == String.class) {
generator.writeString((String) value);
} else if (type == Integer.class) {
generator.writeNumber(((Integer) value).intValue());
} else if (type == Long.class) {
generator.writeNumber(((Long) value).longValue());
} else if (type == Float.class) {
generator.writeNumber(((Float) value).floatValue());
} else if (type == Double.class) {
generator.writeNumber(((Double) value).doubleValue());
} else if (type == Short.class) {
generator.writeNumber(((Short) value).shortValue());
} else if (type == Boolean.class) {
generator.writeBoolean(((Boolean) value).booleanValue());
} else if (value instanceof Map) {
writeMap((Map) value);
} else if (value instanceof Iterable) {
generator.writeStartArray();
for (Object v : (Iterable) value) {
writeValue(v);
}
generator.writeEndArray();
} else if (value instanceof Object[]) {
generator.writeStartArray();
for (Object v : (Object[]) value) {
writeValue(v);
}
generator.writeEndArray();
} else if (type == byte[].class) {
generator.writeBinary((byte[]) 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 BytesReference) {
BytesReference bytes = (BytesReference) value;
if (!bytes.hasArray()) {
bytes = bytes.toBytesArray();
}
generator.writeBinary(bytes.array(), bytes.arrayOffset(), bytes.length());
} else if (value instanceof Text) {
Text text = (Text) value;
if (text.hasBytes() && text.bytes().hasArray()) {
generator.writeUTF8String(text.bytes().array(), text.bytes().arrayOffset(), text.bytes().length());
} else if (text.hasString()) {
generator.writeString(text.string());
} else {
BytesArray bytesArray = text.bytes().toBytesArray();
generator.writeUTF8String(bytesArray.array(), bytesArray.arrayOffset(), bytesArray.length());
}
} else if (value instanceof ToXContent) {
((ToXContent) value).toXContent(this, ToXContent.EMPTY_PARAMS);
} else if (value instanceof double[]) {
generator.writeStartArray();
for (double v : (double[]) value) {
generator.writeNumber(v);
}
generator.writeEndArray();
} else if (value instanceof long[]) {
generator.writeStartArray();
for (long v : (long[]) value) {
generator.writeNumber(v);
}
generator.writeEndArray();
} else if (value instanceof int[]) {
generator.writeStartArray();
for (int v : (int[]) value) {
generator.writeNumber(v);
}
generator.writeEndArray();
} else if (value instanceof float[]) {
generator.writeStartArray();
for (float v : (float[]) value) {
generator.writeNumber(v);
}
generator.writeEndArray();
} else if (value instanceof short[]) {
generator.writeStartArray();
for (float v : (short[]) value) {
generator.writeNumber(v);
}
generator.writeEndArray();
} 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);
}
}
}

View File

@ -24,7 +24,7 @@ import org.elasticsearch.common.Booleans;
import org.elasticsearch.common.xcontent.XContentParser;
import java.io.IOException;
import java.util.Map;
import java.util.*;
/**
*
@ -118,12 +118,12 @@ public abstract class AbstractXContentParser implements XContentParser {
@Override
public Map<String, Object> map() throws IOException {
return XContentMapConverter.readMap(this);
return readMap(this);
}
@Override
public Map<String, Object> mapOrdered() throws IOException {
return XContentMapConverter.readOrderedMap(this);
return readOrderedMap(this);
}
@Override
@ -143,4 +143,87 @@ public abstract class AbstractXContentParser implements XContentParser {
close();
}
}
static interface MapFactory {
Map<String, Object> newMap();
}
static final MapFactory SIMPLE_MAP_FACTORY = new MapFactory() {
@Override
public Map<String, Object> newMap() {
return new HashMap<String, Object>();
}
};
static final MapFactory ORDERED_MAP_FACTORY = new MapFactory() {
@Override
public Map<String, Object> newMap() {
return new LinkedHashMap<String, Object>();
}
};
static Map<String, Object> readMap(XContentParser parser) throws IOException {
return readMap(parser, SIMPLE_MAP_FACTORY);
}
static Map<String, Object> readOrderedMap(XContentParser parser) throws IOException {
return readMap(parser, ORDERED_MAP_FACTORY);
}
static Map<String, Object> readMap(XContentParser parser, MapFactory mapFactory) throws IOException {
Map<String, Object> map = mapFactory.newMap();
XContentParser.Token t = parser.currentToken();
if (t == null) {
t = parser.nextToken();
}
if (t == XContentParser.Token.START_OBJECT) {
t = parser.nextToken();
}
for (; t == XContentParser.Token.FIELD_NAME; t = parser.nextToken()) {
// Must point to field name
String fieldName = parser.currentName();
// And then the value...
t = parser.nextToken();
Object value = readValue(parser, mapFactory, t);
map.put(fieldName, value);
}
return map;
}
private static List<Object> readList(XContentParser parser, MapFactory mapFactory, XContentParser.Token t) throws IOException {
ArrayList<Object> list = new ArrayList<Object>();
while ((t = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
list.add(readValue(parser, mapFactory, t));
}
return list;
}
private static Object readValue(XContentParser parser, MapFactory mapFactory, XContentParser.Token t) throws IOException {
if (t == XContentParser.Token.VALUE_NULL) {
return null;
} else if (t == XContentParser.Token.VALUE_STRING) {
return parser.text();
} else if (t == XContentParser.Token.VALUE_NUMBER) {
XContentParser.NumberType numberType = parser.numberType();
if (numberType == XContentParser.NumberType.INT) {
return parser.intValue();
} else if (numberType == XContentParser.NumberType.LONG) {
return parser.longValue();
} else if (numberType == XContentParser.NumberType.FLOAT) {
return parser.floatValue();
} else if (numberType == XContentParser.NumberType.DOUBLE) {
return parser.doubleValue();
}
} else if (t == XContentParser.Token.VALUE_BOOLEAN) {
return parser.booleanValue();
} else if (t == XContentParser.Token.START_OBJECT) {
return readMap(parser, mapFactory);
} else if (t == XContentParser.Token.START_ARRAY) {
return readList(parser, mapFactory, t);
} else if (t == XContentParser.Token.VALUE_EMBEDDED_OBJECT) {
return parser.binaryValue();
}
return null;
}
}

View File

@ -1,203 +0,0 @@
/*
* Licensed to ElasticSearch and Shay Banon under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. ElasticSearch licenses this
* file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.common.xcontent.support;
import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentGenerator;
import org.elasticsearch.common.xcontent.XContentParser;
import java.io.IOException;
import java.util.*;
/**
*
*/
public class XContentMapConverter {
public static interface MapFactory {
Map<String, Object> newMap();
}
public static final MapFactory SIMPLE_MAP_FACTORY = new MapFactory() {
@Override
public Map<String, Object> newMap() {
return new HashMap<String, Object>();
}
};
public static final MapFactory ORDERED_MAP_FACTORY = new MapFactory() {
@Override
public Map<String, Object> newMap() {
return new LinkedHashMap<String, Object>();
}
};
public static Map<String, Object> readMap(XContentParser parser) throws IOException {
return readMap(parser, SIMPLE_MAP_FACTORY);
}
public static Map<String, Object> readOrderedMap(XContentParser parser) throws IOException {
return readMap(parser, ORDERED_MAP_FACTORY);
}
public static Map<String, Object> readMap(XContentParser parser, MapFactory mapFactory) throws IOException {
Map<String, Object> map = mapFactory.newMap();
XContentParser.Token t = parser.currentToken();
if (t == null) {
t = parser.nextToken();
}
if (t == XContentParser.Token.START_OBJECT) {
t = parser.nextToken();
}
for (; t == XContentParser.Token.FIELD_NAME; t = parser.nextToken()) {
// Must point to field name
String fieldName = parser.currentName();
// And then the value...
t = parser.nextToken();
Object value = readValue(parser, mapFactory, t);
map.put(fieldName, value);
}
return map;
}
private static List<Object> readList(XContentParser parser, MapFactory mapFactory, XContentParser.Token t) throws IOException {
ArrayList<Object> list = new ArrayList<Object>();
while ((t = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
list.add(readValue(parser, mapFactory, t));
}
return list;
}
private static Object readValue(XContentParser parser, MapFactory mapFactory, XContentParser.Token t) throws IOException {
if (t == XContentParser.Token.VALUE_NULL) {
return null;
} else if (t == XContentParser.Token.VALUE_STRING) {
return parser.text();
} else if (t == XContentParser.Token.VALUE_NUMBER) {
XContentParser.NumberType numberType = parser.numberType();
if (numberType == XContentParser.NumberType.INT) {
return parser.intValue();
} else if (numberType == XContentParser.NumberType.LONG) {
return parser.longValue();
} else if (numberType == XContentParser.NumberType.FLOAT) {
return parser.floatValue();
} else if (numberType == XContentParser.NumberType.DOUBLE) {
return parser.doubleValue();
}
} else if (t == XContentParser.Token.VALUE_BOOLEAN) {
return parser.booleanValue();
} else if (t == XContentParser.Token.START_OBJECT) {
return readMap(parser, mapFactory);
} else if (t == XContentParser.Token.START_ARRAY) {
return readList(parser, mapFactory, t);
} else if (t == XContentParser.Token.VALUE_EMBEDDED_OBJECT) {
return parser.binaryValue();
}
return null;
}
public static void writeMap(XContentGenerator gen, Map<String, Object> map) throws IOException {
gen.writeStartObject();
for (Map.Entry<String, Object> entry : map.entrySet()) {
gen.writeFieldName(entry.getKey());
Object value = entry.getValue();
if (value == null) {
gen.writeNull();
} else {
writeValue(gen, value);
}
}
gen.writeEndObject();
}
private static void writeIterable(XContentGenerator gen, Iterable iterable) throws IOException {
gen.writeStartArray();
for (Object value : iterable) {
writeValue(gen, value);
}
gen.writeEndArray();
}
private static void writeObjectArray(XContentGenerator gen, Object[] array) throws IOException {
gen.writeStartArray();
for (Object value : array) {
writeValue(gen, value);
}
gen.writeEndArray();
}
private static void writeValue(XContentGenerator gen, Object value) throws IOException {
if (value == null) {
gen.writeNull();
return;
}
Class type = value.getClass();
if (type == String.class) {
gen.writeString((String) value);
} else if (type == Integer.class) {
gen.writeNumber(((Integer) value).intValue());
} else if (type == Long.class) {
gen.writeNumber(((Long) value).longValue());
} else if (type == Float.class) {
gen.writeNumber(((Float) value).floatValue());
} else if (type == Double.class) {
gen.writeNumber(((Double) value).doubleValue());
} else if (type == Short.class) {
gen.writeNumber(((Short) value).shortValue());
} else if (type == Boolean.class) {
gen.writeBoolean(((Boolean) value).booleanValue());
} else if (value instanceof Map) {
writeMap(gen, (Map) value);
} else if (value instanceof Iterable) {
writeIterable(gen, (Iterable) value);
} else if (value instanceof Object[]) {
writeObjectArray(gen, (Object[]) value);
} else if (type == byte[].class) {
gen.writeBinary((byte[]) value);
} else if (value instanceof Date) {
gen.writeString(XContentBuilder.defaultDatePrinter.print(((Date) value).getTime()));
} else if (value instanceof Calendar) {
gen.writeString(XContentBuilder.defaultDatePrinter.print((((Calendar) value)).getTimeInMillis()));
} else if (value instanceof BytesReference) {
BytesReference bytes = (BytesReference) value;
if (!bytes.hasArray()) {
bytes = bytes.toBytesArray();
}
gen.writeBinary(bytes.array(), bytes.arrayOffset(), bytes.length());
} else if (value instanceof Text) {
Text text = (Text) value;
if (text.hasBytes() && text.bytes().hasArray()) {
gen.writeUTF8String(text.bytes().array(), text.bytes().arrayOffset(), text.bytes().length());
} else if (text.hasString()) {
gen.writeString(text.string());
} else {
BytesArray bytesArray = text.bytes().toBytesArray();
gen.writeUTF8String(bytesArray.array(), bytesArray.arrayOffset(), bytesArray.length());
}
} else {
gen.writeString(value.toString());
}
}
}