/*
* Copyright 2015 S. Webber
*
* 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.oakgp.function.choice;
import static org.oakgp.Type.isNullable;
import static org.oakgp.node.NodeType.isFunction;
import org.oakgp.Arguments;
import org.oakgp.Assignments;
import org.oakgp.Type;
import org.oakgp.function.Function;
import org.oakgp.function.Signature;
import org.oakgp.node.FunctionNode;
import org.oakgp.node.Node;
import org.oakgp.node.walk.NodeWalk;
/**
* A selection operator that uses the value of an enum to determine which code to evaluate.
* <p>
* This behaviour is similar to a Java {@code switch} statement that uses an enum.
*/
public final class SwitchEnum implements Function {
private final Signature signature;
private final Enum<?>[] enumConstants;
/**
* Constructs a selection operator that returns values of the specified type.
*
* @param enumClass
* the enum to compare the first argument against in order to determine which branch to evaluate
* @param enumType
* the type associated with {@code enumClass}
* @param returnType
* the type associated with values returned from the evaluation of this function
*/
public SwitchEnum(Class<? extends Enum<?>> enumClass, Type enumType, Type returnType) {
this.enumConstants = enumClass.getEnumConstants();
Type[] types = new Type[enumConstants.length + (isNullable(enumType) ? 2 : 1)];
types[0] = enumType;
for (int i = 1; i < types.length; i++) {
types[i] = returnType;
}
this.signature = Signature.createSignature(returnType, types);
}
@Override
public Object evaluate(Arguments arguments, Assignments assignments) {
Enum<?> input = arguments.firstArg().evaluate(assignments);
int index = (input == null ? enumConstants.length : input.ordinal()) + 1;
return arguments.getArg(index).evaluate(assignments);
}
@Override
public Node simplify(Arguments arguments) {
// TODO this is similar to the logic in NodeWalk.replaceAll - is it possible to reuse?
boolean updated = false;
Node[] replacementArgs = new Node[arguments.getArgCount()];
Node input = arguments.firstArg();
replacementArgs[0] = input;
for (int i = 1; i < arguments.getArgCount(); i++) {
Node arg = arguments.getArg(i);
final int idx = i;
Node replacedArg = NodeWalk.replaceAll(arg, n -> isFunction(n) && ((FunctionNode) n).getFunction() == this, n -> ((FunctionNode) n).getArguments()
.getArg(idx));
if (arg != replacedArg) {
updated = true;
}
replacementArgs[i] = replacedArg;
}
if (updated) {
return new FunctionNode(this, Arguments.createArguments(replacementArgs));
} else {
return null;
}
}
@Override
public Signature getSignature() {
return signature;
}
@Override
public String getDisplayName() {
return "switch";
}
}