Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Visitor for UpdateClause class hierarchy #21

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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> T accept(UpdateClauseVisitor<T> visitor) {
return visitor.visit(this);
}

@Override
public UpdateClause alias(AliasCollector c) {
return ImmutableAddClause.builder().path(getPath().alias(c)).value(getValue().alias(c)).build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@
public abstract class RemoveClause implements UpdateClause {
public abstract ExpressionPath getPath();

@Override
public <T> T accept(UpdateClauseVisitor<T> visitor) {
return visitor.visit(this);
}

@Override
public RemoveClause alias(AliasCollector c) {
return ImmutableRemoveClause.builder().path(getPath().alias(c)).build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ public abstract class SetClause implements UpdateClause {

public abstract Value getValue();

@Override
public <T> T accept(UpdateClauseVisitor<T> visitor) {
return visitor.visit(this);
}

public static SetClause equals(ExpressionPath path, Entity value) {
return ImmutableSetClause.builder().path(path).value(Value.of(value)).build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,11 @@ enum Type {

String toClauseString();

/**
* Entry point for visitation.
* @param visitor the visitor that will be invoked.
* @param <T> the type of the returned value.
* @return the possibly transformed value resulting from the visitation.
*/
abstract <T> T accept(UpdateClauseVisitor<T> visitor);
}
Original file line number Diff line number Diff line change
@@ -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 <T> The type to which the UpdateClause will be converted.
*/
public interface UpdateClauseVisitor<T> {
/**
* 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);
}
Original file line number Diff line number Diff line change
@@ -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<UpdateCommand> {

@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);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How come this isn't used for the RemoveClause as well?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you mean why isn't testSetClause() used for remove? Remove doesn't use an entity. Unless you are suggesting I use Optional instead?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a minor nit, but it seems that you could remove the entity assertion and add it to the specific tests, or have another method call this one. This comment is minor, do what you think is best.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I decided moving the assertion to the specific test was the cleanest.

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();
}
}
}