diff --git a/versioned/tiered/tiered-impl/src/main/java/org/projectnessie/versioned/impl/condition/AddClause.java b/versioned/tiered/tiered-impl/src/main/java/org/projectnessie/versioned/impl/condition/AddClause.java index db500cac159..eee46647f87 100644 --- a/versioned/tiered/tiered-impl/src/main/java/org/projectnessie/versioned/impl/condition/AddClause.java +++ b/versioned/tiered/tiered-impl/src/main/java/org/projectnessie/versioned/impl/condition/AddClause.java @@ -32,6 +32,11 @@ public static AddClause addToSetOrNumber(ExpressionPath path, Entity value) { return ImmutableAddClause.builder().path(path).value(Value.of(value)).build(); } + @Override + public T accept(UpdateClauseVisitor visitor) { + return visitor.visit(this); + } + @Override public UpdateClause alias(AliasCollector c) { return ImmutableAddClause.builder().path(getPath().alias(c)).value(getValue().alias(c)).build(); diff --git a/versioned/tiered/tiered-impl/src/main/java/org/projectnessie/versioned/impl/condition/RemoveClause.java b/versioned/tiered/tiered-impl/src/main/java/org/projectnessie/versioned/impl/condition/RemoveClause.java index b789602c079..6459c87578d 100644 --- a/versioned/tiered/tiered-impl/src/main/java/org/projectnessie/versioned/impl/condition/RemoveClause.java +++ b/versioned/tiered/tiered-impl/src/main/java/org/projectnessie/versioned/impl/condition/RemoveClause.java @@ -21,6 +21,11 @@ public abstract class RemoveClause implements UpdateClause { public abstract ExpressionPath getPath(); + @Override + public T accept(UpdateClauseVisitor visitor) { + return visitor.visit(this); + } + @Override public RemoveClause alias(AliasCollector c) { return ImmutableRemoveClause.builder().path(getPath().alias(c)).build(); diff --git a/versioned/tiered/tiered-impl/src/main/java/org/projectnessie/versioned/impl/condition/SetClause.java b/versioned/tiered/tiered-impl/src/main/java/org/projectnessie/versioned/impl/condition/SetClause.java index 05f840477ff..43e9cbf6a74 100644 --- a/versioned/tiered/tiered-impl/src/main/java/org/projectnessie/versioned/impl/condition/SetClause.java +++ b/versioned/tiered/tiered-impl/src/main/java/org/projectnessie/versioned/impl/condition/SetClause.java @@ -24,6 +24,11 @@ public abstract class SetClause implements UpdateClause { public abstract Value getValue(); + @Override + public T accept(UpdateClauseVisitor visitor) { + return visitor.visit(this); + } + public static SetClause equals(ExpressionPath path, Entity value) { return ImmutableSetClause.builder().path(path).value(Value.of(value)).build(); } diff --git a/versioned/tiered/tiered-impl/src/main/java/org/projectnessie/versioned/impl/condition/UpdateClause.java b/versioned/tiered/tiered-impl/src/main/java/org/projectnessie/versioned/impl/condition/UpdateClause.java index 9e5dedf5520..bedec125e54 100644 --- a/versioned/tiered/tiered-impl/src/main/java/org/projectnessie/versioned/impl/condition/UpdateClause.java +++ b/versioned/tiered/tiered-impl/src/main/java/org/projectnessie/versioned/impl/condition/UpdateClause.java @@ -27,4 +27,11 @@ enum Type { String toClauseString(); + /** + * Entry point for visitation. + * @param visitor the visitor that will be invoked. + * @param the type of the returned value. + * @return the possibly transformed value resulting from the visitation. + */ + abstract T accept(UpdateClauseVisitor visitor); } diff --git a/versioned/tiered/tiered-impl/src/main/java/org/projectnessie/versioned/impl/condition/UpdateClauseVisitor.java b/versioned/tiered/tiered-impl/src/main/java/org/projectnessie/versioned/impl/condition/UpdateClauseVisitor.java new file mode 100644 index 00000000000..d71362c2835 --- /dev/null +++ b/versioned/tiered/tiered-impl/src/main/java/org/projectnessie/versioned/impl/condition/UpdateClauseVisitor.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2020 Dremio + * + * 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 org.projectnessie.versioned.impl.condition; + +/** + * Visitor for all classes in the UpdateClause hierarchy. + * @param The type to which the UpdateClause will be converted. + */ +public interface UpdateClauseVisitor { + /** + * Visit the passed in AddClause. + * @param clause the clause to visit. + * @return the possibly transformed value resulting from the visitation. + */ + T visit(AddClause clause); + + /** + * Visit the passed in RemoveClause. + * @param clause the clause to visit. + * @return the possibly transformed value resulting from the visitation. + */ + T visit(RemoveClause clause); + + /** + * Visit the passed in SetClause. + * @param clause the clause to visit. + * @return the possibly transformed value resulting from the visitation. + */ + T visit(SetClause clause); +} diff --git a/versioned/tiered/tiered-impl/src/test/java/org/projectnessie/versioned/impl/condition/TestUpdateClauseVisitor.java b/versioned/tiered/tiered-impl/src/test/java/org/projectnessie/versioned/impl/condition/TestUpdateClauseVisitor.java new file mode 100644 index 00000000000..d55474fa2f8 --- /dev/null +++ b/versioned/tiered/tiered-impl/src/test/java/org/projectnessie/versioned/impl/condition/TestUpdateClauseVisitor.java @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2020 Dremio + * + * 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 org.projectnessie.versioned.impl.condition; + +import org.immutables.value.Value.Immutable; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.projectnessie.versioned.store.Entity; + +/** + * Test basic functionality and show usage of the UpdateClauseVisitor as pertains to UpdateExpression and the UpdateClause + * hierarchy (ValueOfEntity, ExpressionFunction, ExpressionPath). + */ +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +public class TestUpdateClauseVisitor { + + private static final Visitor VISITOR = new Visitor(); + private static final String ID_STR = "id"; + private static final Entity ENTITY_STR = Entity.ofString(ID_STR); + private static final String PATH_STR = "commits"; + private static final ExpressionPath PATH = ExpressionPath.builder(PATH_STR).build(); + + /** + * A test visitor which builds up an UpdateCommand representation of the passed in UpdateClause. + */ + static class Visitor implements UpdateClauseVisitor { + + @Override + public UpdateCommand visit(final AddClause clause) { + throw new UnsupportedOperationException(); + } + + @Override + public UpdateCommand visit(final RemoveClause clause) { + return ImmutableRemoveCommand.builder() + .operator(UpdateCommand.Operator.REMOVE) + .path(clause.getPath()) + .build(); + } + + @Override + public UpdateCommand visit(final SetClause clause) { + switch (clause.getValue().getType()) { + case VALUE: + return ImmutableSetCommand.builder() + .operator(UpdateCommand.Operator.SET) + .path(clause.getPath()) + .entity(clause.getValue().getValue()) + .build(); + case FUNCTION: + return ImmutableSetCommand.builder() + .operator(UpdateCommand.Operator.SET) + .path(clause.getPath()) + .entity(handleFunction(clause.getValue().getFunction())) + .build(); + default: + throw new UnsupportedOperationException(String.format("Unsupported SetClause type: %s", clause.getValue().getType().name())); + } + } + + private Entity handleFunction(ExpressionFunction expressionFunction) { + if (ExpressionFunction.FunctionName.LIST_APPEND == expressionFunction.getName()) { + return expressionFunction.getArguments().get(1).getValue(); + } + throw new UnsupportedOperationException(String.format("Unsupported Set function: %s", expressionFunction.getName())); + } + } + + @Test + void testRemoveClause() { + final UpdateClause clause = RemoveClause.of(PATH); + testClause(RemoveClause.of(PATH), UpdateCommand.Operator.REMOVE, PATH); + } + + @Test + void testSetClauseEquals() { + final UpdateCommand command = testClause(SetClause.equals(PATH, ENTITY_STR), UpdateCommand.Operator.SET, PATH); + Assertions.assertEquals(ENTITY_STR, ((UpdateCommand.SetCommand)command).getEntity()); + } + + @Test + void testSetClauseAppendToList() { + final UpdateCommand command = testClause(SetClause.appendToList(PATH, ENTITY_STR), UpdateCommand.Operator.SET, PATH); + Assertions.assertEquals(ENTITY_STR, ((UpdateCommand.SetCommand)command).getEntity()); + } + + UpdateCommand testClause(UpdateClause clause, UpdateCommand.Operator operator, ExpressionPath path) { + final UpdateCommand command = clause.accept(VISITOR); + Assertions.assertEquals(operator, command.getOperator()); + Assertions.assertEquals(path, command.getPath()); + return command; + } + + /** + * Sample interface or class into which UpdateClauses are converted. + */ + static interface UpdateCommand { + /** + * An enum encapsulating the type of update. + */ + enum Operator { + // An operator to remove some part or all of an entity. + REMOVE, + + // An operator to set some part or all of an entity. + SET + } + + Operator getOperator(); + + ExpressionPath getPath(); + + /** + * Sample of a specific type of update command into which UpdateClauses are converted. + */ + @Immutable + abstract class RemoveCommand implements UpdateCommand { + } + + /** + * Sample of a specific type of update command into which UpdateClauses are converted. + */ + @Immutable + abstract class SetCommand implements UpdateCommand { + + abstract Entity getEntity(); + } + } +}