// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.search.yql;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/**
* Represents a use of an operator against concrete arguments. The types of arguments depend on the operator.
*
* The extension point of this scheme is the Operator rather than new types of Nodes.
*
* Operators SHOULD take a fixed number of arguments -- wrap variable argument counts in Lists.
*/
final class OperatorNode {
public static OperatorNode create(T operator, Object... args) {
operator.checkArguments(args == null ? EMPTY_ARGS : args);
return new OperatorNode(operator, args);
}
public static OperatorNode create(Location loc, T operator, Object... args) {
operator.checkArguments(args == null ? EMPTY_ARGS : args);
return new OperatorNode(loc, operator, args);
}
public static OperatorNode create(Location loc, Map annotations, T operator, Object... args) {
operator.checkArguments(args == null ? EMPTY_ARGS : args);
return new OperatorNode(loc, annotations, operator, args);
}
private static final Object[] EMPTY_ARGS = new Object[0];
private final Location location;
private final T operator;
private Map annotations = ImmutableMap.of();
private final Object[] args;
private OperatorNode(T operator, Object... args) {
this.location = null;
this.operator = operator;
if (args == null) {
this.args = EMPTY_ARGS;
} else {
this.args = args;
}
}
private OperatorNode(Location loc, T operator, Object... args) {
this.location = loc;
this.operator = operator;
if (args == null) {
this.args = EMPTY_ARGS;
} else {
this.args = args;
}
}
private OperatorNode(Location loc, Map annotations, T operator, Object... args) {
this.location = loc;
this.operator = operator;
this.annotations = ImmutableMap.copyOf(annotations);
if (args == null) {
this.args = EMPTY_ARGS;
} else {
this.args = args;
}
}
public T getOperator() {
return operator;
}
public Object[] getArguments() {
// this is only called by a test right now, but ImmutableList.copyOf won't tolerate null elements
if (args.length == 0) {
return args;
}
Object[] copy = new Object[args.length];
System.arraycopy(args, 0, copy, 0, args.length);
return copy;
}
public T getArgument(int i) {
return (T) args[i];
}
public T getArgument(int i, Class clazz) {
return clazz.cast(getArgument(i));
}
public Location getLocation() {
return location;
}
public Object getAnnotation(String name) {
return annotations.get(name);
}
public OperatorNode putAnnotation(String name, Object value) {
if (annotations.isEmpty()) {
annotations = Maps.newLinkedHashMap();
} else if (annotations instanceof ImmutableMap) {
annotations = Maps.newLinkedHashMap(annotations);
}
annotations.put(name, value);
return this;
}
public Map getAnnotations() {
// TODO: this should be a read-only view?
return ImmutableMap.copyOf(annotations);
}
public OperatorNode transform(Function