diff options
author | Håkon Hallingstad <hakon@verizonmedia.com> | 2019-01-29 21:37:12 +0100 |
---|---|---|
committer | Håkon Hallingstad <hakon@verizonmedia.com> | 2019-01-29 21:37:12 +0100 |
commit | 25267c426ea5d10a41f7876535160156a073af2f (patch) | |
tree | c81dcc15dcd6d4c8b0d9acb14b95b49e30b5598a /vespajlib/src/main/java | |
parent | 5dbacf2ff71a530b62f862b43d9893b47d52e075 (diff) |
Port Slime injection to Java
Diffstat (limited to 'vespajlib/src/main/java')
4 files changed, 227 insertions, 9 deletions
diff --git a/vespajlib/src/main/java/com/yahoo/slime/Injector.java b/vespajlib/src/main/java/com/yahoo/slime/Injector.java new file mode 100644 index 00000000000..3e507cf29c2 --- /dev/null +++ b/vespajlib/src/main/java/com/yahoo/slime/Injector.java @@ -0,0 +1,84 @@ +// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.slime; + +/** + * @author hakonhall + */ +public class Injector { + /** + * Inject a slime sub-structure described by an Inspector into a slime + * structure where the insertion point is described by an + * Inserter. This will copy all the values represented by the + * Inspector into the position described by the Inserter. Note that + * this can be used to either copy data from one Slime structure to + * another, or to copy data internally within a single slime + * structure. If the Inspector contains the insertion point it will + * only be expanded once to avoid infinite recursion. + * + * @param inspector what to inject + * @param inserter where to inject + **/ + public void inject(Inspector inspector, Inserter inserter) { + if (inspector.valid()) { + injectValue(inserter, inspector, null); + } + } + + private void injectValue(Inserter inserter, Inspector inspector, Inspector guard) { + switch (inspector.type()) { + case NIX: inserter.insertNIX(); break; + case BOOL: inserter.insertBOOL(inspector.asBool()); break; + case LONG: inserter.insertLONG(inspector.asLong()); break; + case DOUBLE: inserter.insertDOUBLE(inspector.asDouble()); break; + case STRING: inserter.insertSTRING(inspector.asString()); break; + case DATA: inserter.insertDATA(inspector.asData()); break; + case ARRAY: injectArray(inserter, inspector, guard); break; + case OBJECT: injectObject(inserter, inspector, guard); break; + default: throw new IllegalArgumentException("Unknown type " + inspector.type()); + } + } + + private void injectArray(Inserter inserter, Inspector inspector, Inspector guard) { + Cursor cursor = inserter.insertARRAY(); + ArrayTraverser arrayTraverser = new NestedInjector(cursor, guard != null ? guard : cursor); + inspector.traverse(arrayTraverser); + } + + private void injectObject(Inserter inserter, Inspector inspector, Inspector guard) { + Cursor cursor = inserter.insertOBJECT(); + ObjectTraverser objectTraverser = new NestedInjector(cursor, guard != null ? guard : cursor); + inspector.traverse(objectTraverser); + } + + private class NestedInjector implements ArrayTraverser, ObjectTraverser { + private final Cursor cursor; + private final Inspector guard; + + public NestedInjector(Cursor cursor, Inspector guard) { + this.cursor = cursor; + this.guard = guard; + } + + @Override + public void entry(int idx, Inspector inspector) { + if (inspector == guard) { + return; + } + + var inserter = new ArrayInserter(); + inserter.adjust(cursor); + injectValue(inserter, inspector, guard); + } + + @Override + public void field(String name, Inspector inspector) { + if (inspector == guard) { + return; + } + + var inserter = new ObjectInserter(); + inserter.adjust(cursor, name); + injectValue(inserter, inspector, guard); + } + } +} diff --git a/vespajlib/src/main/java/com/yahoo/slime/Inspector.java b/vespajlib/src/main/java/com/yahoo/slime/Inspector.java index 99489faca47..ca2e678c152 100644 --- a/vespajlib/src/main/java/com/yahoo/slime/Inspector.java +++ b/vespajlib/src/main/java/com/yahoo/slime/Inspector.java @@ -131,4 +131,11 @@ public interface Inspector { */ Inspector field(String name); + /** + * Tests whether this is equal to Inspector. + * + * @param that inspector. + * @return true if they are equal. + */ + boolean equalTo(Inspector that); } diff --git a/vespajlib/src/main/java/com/yahoo/slime/ObjectInserter.java b/vespajlib/src/main/java/com/yahoo/slime/ObjectInserter.java index 3e0073beccd..450b5271db4 100644 --- a/vespajlib/src/main/java/com/yahoo/slime/ObjectInserter.java +++ b/vespajlib/src/main/java/com/yahoo/slime/ObjectInserter.java @@ -1,6 +1,8 @@ // Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.slime; +import java.util.Optional; + /** * Helper class for inserting values into an ObjectValue. * For justification read Inserter documentation. @@ -8,18 +10,64 @@ package com.yahoo.slime; final class ObjectInserter implements Inserter { private Cursor target; private int symbol; + private Optional<String> symbolName = Optional.empty(); + public final ObjectInserter adjust(Cursor c, int sym) { target = c; symbol = sym; + symbolName = Optional.empty(); + return this; + } + + public final ObjectInserter adjust(Cursor c, String name) { + target = c; + symbol = -1; + symbolName = Optional.of(name); return this; } - public final Cursor insertNIX() { return target.setNix(symbol); } - public final Cursor insertBOOL(boolean value) { return target.setBool(symbol, value); } - public final Cursor insertLONG(long value) { return target.setLong(symbol, value); } - public final Cursor insertDOUBLE(double value) { return target.setDouble(symbol, value); } - public final Cursor insertSTRING(String value) { return target.setString(symbol, value); } - public final Cursor insertSTRING(byte[] utf8) { return target.setString(symbol, utf8); } - public final Cursor insertDATA(byte[] value) { return target.setData(symbol, value); } - public final Cursor insertARRAY() { return target.setArray(symbol); } - public final Cursor insertOBJECT() { return target.setObject(symbol); } + + public final Cursor insertNIX() { + return symbolName.map(name -> target.setNix(name)) + .orElseGet(() -> target.setNix(symbol)); + } + + public final Cursor insertBOOL(boolean value) { + return symbolName.map(name -> target.setBool(name, value)) + .orElseGet(() -> target.setBool(symbol, value)); + } + + public final Cursor insertLONG(long value) { + return symbolName.map(name -> target.setLong(name, value)) + .orElseGet(() -> target.setLong(symbol, value)); + } + + public final Cursor insertDOUBLE(double value) { + return symbolName.map(name -> target.setDouble(name, value)) + .orElseGet(() -> target.setDouble(symbol, value)); + } + + public final Cursor insertSTRING(String value) { + return symbolName.map(name -> target.setString(name, value)) + .orElseGet(() -> target.setString(symbol, value)); + } + + public final Cursor insertSTRING(byte[] utf8) { + return symbolName.map(name -> target.setString(name, utf8)) + .orElseGet(() -> target.setString(symbol, utf8)); + } + + public final Cursor insertDATA(byte[] value) { + return symbolName.map(name -> target.setData(name, value)) + .orElseGet(() -> target.setData(symbol, value)); + } + + public final Cursor insertARRAY() { + return symbolName.map(name -> target.setArray(name)) + .orElseGet(() -> target.setArray(symbol)); + } + + public final Cursor insertOBJECT() { + return symbolName.map(name -> target.setObject(name)) + .orElseGet(() -> target.setObject(symbol)); + } } diff --git a/vespajlib/src/main/java/com/yahoo/slime/Value.java b/vespajlib/src/main/java/com/yahoo/slime/Value.java index 51a8262333b..183c659c35a 100644 --- a/vespajlib/src/main/java/com/yahoo/slime/Value.java +++ b/vespajlib/src/main/java/com/yahoo/slime/Value.java @@ -3,6 +3,7 @@ package com.yahoo.slime; import java.io.ByteArrayOutputStream; +import java.util.Arrays; /** * Common implementation for all value types. @@ -84,4 +85,82 @@ abstract class Value implements Cursor { return "null"; } } + + private static class Equal { + protected final Inspector rhsInspector; + + protected boolean equal = true; + + public Equal(Inspector rhsInspector) { this.rhsInspector = rhsInspector; } + + public boolean isEqual() { return equal; } + } + + private static class EqualArray extends Equal implements ArrayTraverser { + public EqualArray(Inspector rhsInspector) { super(rhsInspector); } + + @Override + public void entry(int idx, Inspector inspector) { + if (equal) { + equal = inspector.equalTo(rhsInspector.entry(idx)); + } + } + } + + private static class EqualObject extends Equal implements ObjectTraverser { + public EqualObject(Inspector rhsInspector) { super(rhsInspector); } + + @Override + public void field(String name, Inspector inspector) { + if (equal) { + equal = inspector.equalTo(rhsInspector.field(name)); + } + } + } + + @Override + public boolean equalTo(Inspector that) { + boolean equal = type() == that.type(); + + if (equal) { + switch (type()) { + case NIX: + break; + case BOOL: + equal = asBool() == that.asBool(); + break; + case LONG: + equal = asLong() == that.asLong(); + break; + case DOUBLE: + equal = Double.compare(asDouble(), that.asDouble()) == 0; + break; + case STRING: + equal = asString().equals(that.asString()); + break; + case DATA: + equal = Arrays.equals(asData(), that.asData()); + break; + case ARRAY: + { + var traverser = new EqualArray(that); + traverse(traverser); + equal = traverser.isEqual() && (entries() == that.entries()); + } + break; + case OBJECT: + { + var traverser = new EqualObject(that); + traverse(traverser); + equal = traverser.isEqual() && (fields() == that.fields()); + } + break; + default: + assert(false); + break; + } + } + + return equal; + } } |