blob: 9c44583e15da33a6fbb937177faef248ec63e1c8 [file] [log] [blame]
// Copyright (C) 2018 The Android Open Source Project, 2009-2015 Elasticsearch
//
// Licensed 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 com.google.gerrit.elasticsearch.builders;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.time.format.DateTimeFormatter.ISO_INSTANT;
import com.fasterxml.jackson.core.JsonEncoding;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.json.JsonReadFeature;
import com.fasterxml.jackson.core.json.JsonWriteFeature;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.IOException;
import java.util.Date;
/** A trimmed down and modified version of org.elasticsearch.common.xcontent.XContentBuilder. */
public final class XContentBuilder implements Closeable {
private final JsonGenerator generator;
private final ByteArrayOutputStream bos = new ByteArrayOutputStream();
/**
* Constructs a new builder. Make sure to call {@link #close()} when the builder is done with.
* Inspired from org.elasticsearch.common.xcontent.json.JsonXContent static block.
*/
public XContentBuilder() throws IOException {
this.generator =
JsonFactory.builder()
.configure(JsonReadFeature.ALLOW_UNQUOTED_FIELD_NAMES, true)
.configure(JsonWriteFeature.QUOTE_FIELD_NAMES, true)
.configure(JsonReadFeature.ALLOW_JAVA_COMMENTS, true)
.configure(JsonFactory.Feature.FAIL_ON_SYMBOL_HASH_OVERFLOW, false)
.build()
.createGenerator(bos, JsonEncoding.UTF8);
}
public XContentBuilder startObject(String name) throws IOException {
field(name);
startObject();
return this;
}
public XContentBuilder startObject() throws IOException {
generator.writeStartObject();
return this;
}
public XContentBuilder endObject() throws IOException {
generator.writeEndObject();
return this;
}
public void startArray(String name) throws IOException {
field(name);
startArray();
}
private void startArray() throws IOException {
generator.writeStartArray();
}
public void endArray() throws IOException {
generator.writeEndArray();
}
public XContentBuilder field(String name) throws IOException {
generator.writeFieldName(name);
return this;
}
public XContentBuilder field(String name, String value) throws IOException {
field(name);
generator.writeString(value);
return this;
}
public XContentBuilder field(String name, int value) throws IOException {
field(name);
generator.writeNumber(value);
return this;
}
public XContentBuilder field(String name, Iterable<?> 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 value(String value) throws IOException {
generator.writeString(value);
return this;
}
@Override
public void close() {
try {
generator.close();
} catch (IOException e) {
// ignore
}
}
/** Returns a string representation of the builder (only applicable for text based xcontent). */
public String string() {
close();
byte[] bytesArray = bos.toByteArray();
return new String(bytesArray, UTF_8);
}
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));
} else if (type == byte[].class) {
generator.writeBinary((byte[]) value);
} else if (value instanceof Date) {
generator.writeString(ISO_INSTANT.format(((Date) value).toInstant()));
} 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);
}
}
}