|
@@ -43,8 +43,8 @@ import java.util.HashMap;
|
|
|
import java.util.Locale;
|
|
|
import java.util.Map;
|
|
|
import java.util.Objects;
|
|
|
+import java.util.ServiceLoader;
|
|
|
import java.util.Set;
|
|
|
-import java.util.concurrent.TimeUnit;
|
|
|
|
|
|
/**
|
|
|
* A utility to build XContent (ie json).
|
|
@@ -85,6 +85,7 @@ public final class XContentBuilder implements Releasable, Flushable {
|
|
|
public static final DateTimeFormatter DEFAULT_DATE_PRINTER = ISODateTimeFormat.dateTime().withZone(DateTimeZone.UTC);
|
|
|
|
|
|
private static final Map<Class<?>, Writer> WRITERS;
|
|
|
+ private static final Map<Class<?>, HumanReadableTransformer> HUMAN_READABLE_TRANSFORMERS;
|
|
|
static {
|
|
|
Map<Class<?>, Writer> writers = new HashMap<>();
|
|
|
writers.put(Boolean.class, (b, v) -> b.value((Boolean) v));
|
|
@@ -105,14 +106,43 @@ public final class XContentBuilder implements Releasable, Flushable {
|
|
|
writers.put(String.class, (b, v) -> b.value((String) v));
|
|
|
writers.put(String[].class, (b, v) -> b.values((String[]) v));
|
|
|
|
|
|
+
|
|
|
+ Map<Class<?>, HumanReadableTransformer> humanReadableTransformer = new HashMap<>();
|
|
|
+ // These will be moved to a different class at a later time to decouple them from XContentBuilder
|
|
|
+ humanReadableTransformer.put(TimeValue.class, v -> ((TimeValue) v).millis());
|
|
|
+ humanReadableTransformer.put(ByteSizeValue.class, v -> ((ByteSizeValue) v).getBytes());
|
|
|
+
|
|
|
+ // Load pluggable extensions
|
|
|
+ for (XContentBuilderExtension service : ServiceLoader.load(XContentBuilderExtension.class)) {
|
|
|
+ Map<Class<?>, Writer> addlWriters = service.getXContentWriters();
|
|
|
+ Map<Class<?>, HumanReadableTransformer> addlTransformers = service.getXContentHumanReadableTransformers();
|
|
|
+
|
|
|
+ addlWriters.forEach((key, value) -> Objects.requireNonNull(value,
|
|
|
+ "invalid null xcontent writer for class " + key));
|
|
|
+ addlTransformers.forEach((key, value) -> Objects.requireNonNull(value,
|
|
|
+ "invalid null xcontent transformer for human readable class " + key));
|
|
|
+
|
|
|
+ writers.putAll(addlWriters);
|
|
|
+ humanReadableTransformer.putAll(addlTransformers);
|
|
|
+ }
|
|
|
+
|
|
|
WRITERS = Collections.unmodifiableMap(writers);
|
|
|
+ HUMAN_READABLE_TRANSFORMERS = Collections.unmodifiableMap(humanReadableTransformer);
|
|
|
}
|
|
|
|
|
|
@FunctionalInterface
|
|
|
- private interface Writer {
|
|
|
+ public interface Writer {
|
|
|
void write(XContentBuilder builder, Object value) throws IOException;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Interface for transforming complex objects into their "raw" equivalents for human-readable fields
|
|
|
+ */
|
|
|
+ @FunctionalInterface
|
|
|
+ public interface HumanReadableTransformer {
|
|
|
+ Object rawValue(Object value) throws IOException;
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* XContentGenerator used to build the XContent object
|
|
|
*/
|
|
@@ -856,33 +886,30 @@ public final class XContentBuilder implements Releasable, Flushable {
|
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
- // Misc.
|
|
|
+ // Human readable fields
|
|
|
+ //
|
|
|
+ // These are fields that have a "raw" value and a "human readable" value,
|
|
|
+ // such as time values or byte sizes. The human readable variant is only
|
|
|
+ // used if the humanReadable flag has been set
|
|
|
//////////////////////////////////
|
|
|
|
|
|
- public XContentBuilder timeValueField(String rawFieldName, String readableFieldName, TimeValue timeValue) throws IOException {
|
|
|
+ public XContentBuilder humanReadableField(String rawFieldName, String readableFieldName, Object value) throws IOException {
|
|
|
if (humanReadable) {
|
|
|
- field(readableFieldName, timeValue.toString());
|
|
|
+ field(readableFieldName, Objects.toString(value));
|
|
|
}
|
|
|
- field(rawFieldName, timeValue.millis());
|
|
|
- return this;
|
|
|
- }
|
|
|
-
|
|
|
- public XContentBuilder timeValueField(String rawFieldName, String readableFieldName, long rawTime) throws IOException {
|
|
|
- if (humanReadable) {
|
|
|
- field(readableFieldName, new TimeValue(rawTime).toString());
|
|
|
+ HumanReadableTransformer transformer = HUMAN_READABLE_TRANSFORMERS.get(value.getClass());
|
|
|
+ if (transformer != null) {
|
|
|
+ Object rawValue = transformer.rawValue(value);
|
|
|
+ field(rawFieldName, rawValue);
|
|
|
+ } else {
|
|
|
+ throw new IllegalArgumentException("no raw transformer found for class " + value.getClass());
|
|
|
}
|
|
|
- field(rawFieldName, rawTime);
|
|
|
return this;
|
|
|
}
|
|
|
|
|
|
- public XContentBuilder timeValueField(String rawFieldName, String readableFieldName, long rawTime, TimeUnit timeUnit) throws
|
|
|
- IOException {
|
|
|
- if (humanReadable) {
|
|
|
- field(readableFieldName, new TimeValue(rawTime, timeUnit).toString());
|
|
|
- }
|
|
|
- field(rawFieldName, rawTime);
|
|
|
- return this;
|
|
|
- }
|
|
|
+ ////////////////////////////////////////////////////////////////////////////
|
|
|
+ // Misc.
|
|
|
+ //////////////////////////////////
|
|
|
|
|
|
|
|
|
public XContentBuilder percentageField(String rawFieldName, String readableFieldName, double percentage) throws IOException {
|
|
@@ -893,14 +920,6 @@ public final class XContentBuilder implements Releasable, Flushable {
|
|
|
return this;
|
|
|
}
|
|
|
|
|
|
- public XContentBuilder byteSizeField(String rawFieldName, String readableFieldName, ByteSizeValue byteSizeValue) throws IOException {
|
|
|
- if (humanReadable) {
|
|
|
- field(readableFieldName, byteSizeValue.toString());
|
|
|
- }
|
|
|
- field(rawFieldName, byteSizeValue.getBytes());
|
|
|
- return this;
|
|
|
- }
|
|
|
-
|
|
|
public XContentBuilder byteSizeField(String rawFieldName, String readableFieldName, long rawSize) throws IOException {
|
|
|
if (humanReadable) {
|
|
|
field(readableFieldName, new ByteSizeValue(rawSize).toString());
|