/**
* Copyright (C) 2009-2013 FoundationDB, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.foundationdb.server.types.texpressions;
import com.foundationdb.server.types.InputSetFlags;
import com.foundationdb.server.types.TClass;
import com.foundationdb.server.types.TInputSet;
import com.foundationdb.server.types.TOverloadResult;
import com.foundationdb.server.types.TOverload;
import com.foundationdb.server.types.TPreptimeValue;
import com.foundationdb.util.SparseArray;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class TValidatedOverload implements TOverload {
// TResolvable methods (straight delegation)
@Override
public String displayName() {
return overload.displayName();
}
@Override
public String[] registeredNames()
{
return overload.registeredNames();
}
@Override
public String id() {
return overload.id();
}
@Override
public int[] getPriorities() {
return overload.getPriorities();
}
@Override
public boolean equals(Object obj) {
return (obj instanceof TOverload) && overload.equals(obj);
}
@Override
public int hashCode() {
return overload.hashCode();
}
// TResolvable methods (cached)
@Override
public List<TInputSet> inputSets() {
return inputSetsCached;
}
@Override
public TOverloadResult resultType() {
return resultStrategy;
}
@Override
public InputSetFlags exactInputs() {
return exactInputs;
}
// TValidatedResolvable methods
public TOverload getUnderlying() {
return overload;
}
// TValidatedOverload methods
public int firstVarargInput() {
if (varargs == null)
return -1;
return inputSetsByPos.size();
}
public TInputSet pickingInputSet() {
return pickingSet;
}
public TInputSet varargInputSet() {
return varargs;
}
public boolean isVararg() {
return varargs != null;
}
public boolean coversNInputs(int nInputs) {
/* no pos : nInputs = 0
* POS(N) : nInputs = N+1
* REMAINING : nInputs >= 0
* POS(N),REMAINING : nInputs >= N+1
*/
int minSize = inputSetsByPos.size();
return (varargs == null) ? (nInputs == minSize) : (nInputs >= minSize);
}
public int positionalInputs() {
return inputSetsByPos.size();
}
public int inputSetIndexAtPos(int atPosition) {
if (atPosition < 0)
throw new IllegalArgumentException("atPosition must be non-negative: " + atPosition);
if (atPosition >= inputsToInputSetIndex.length) {
if (!isVararg())
throw new IllegalArgumentException("out of range for non-vararg: " + atPosition);
atPosition = inputsToInputSetIndex.length - 1;
}
return inputsToInputSetIndex[atPosition];
}
public int inputSetIndexCount() {
return inputsToInputSetIndex.length;
}
public TInputSet inputSetAt(int index) {
if(index >= inputSetsByPos.size()) {
if(varargs == null) {
throw new IllegalArgumentException("No such input set: " + index);
}
return varargs;
}
return inputSetsByPos.get(index);
}
public TOverloadResult resultStrategy() {
return resultStrategy;
}
public int firstInput(TInputSet inputSet) {
int result = inputSet.firstPosition();
if (result < 0 && inputSet.isPicking())
result = firstVarargInput();
assert result >= 0 : result + " in " + inputSet + " within " + this;
return result;
}
public int nextInput(TInputSet inputSet, int i, int max) {
if (i >= max)
return -1;
int result = inputSet.nextPosition(i);
if (result < 0 && inputSet.coversRemaining())
result = Math.max(i, inputSetsByPos.size());
return result;
}
public String describeInputs() {
StringBuilder sb = new StringBuilder();
buildInputsDescription(sb);
return sb.toString();
}
// Redefine toString
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(overload.displayName()).append('(');
buildInputsDescription(sb);
sb.append(") -> ").append(resultStrategy);
return sb.toString();
}
// package-private
boolean coversExactlyNArgs(int nargs) {
return (!isVararg()) && inputSetsByPos.size() == nargs;
}
TValidatedOverload(TOverload overload) {
this(overload, overload.inputSets());
}
TValidatedOverload(TOverload overload, List<TInputSet> inputSets) {
TInputSet localVarargInputs = null;
TInputSet localPickingInputs = null;
SparseArray<TInputSet> inputSetsArray = new SparseArray<>();
this.inputSetsCached = new ArrayList<>(inputSets);
for (TInputSet inputSet : inputSetsCached) {
if (inputSet.coversRemaining()) {
if (localVarargInputs != null)
throw new InvalidOverloadException("multiple input sets are vararg");
localVarargInputs = inputSet;
}
for (int i = 0, max = inputSet.positionsLength(); i < max; ++i) {
if (inputSet.covers(i)) {
if (inputSetsArray.isDefined(i))
throw new InvalidOverloadException("multiple input sets cover input " + i);
inputSetsArray.set(i, inputSet);
}
}
if (inputSet.isPicking()) {
if (localPickingInputs != null)
throw new InvalidOverloadException("overloads can't define multiple picking input sets");
localPickingInputs = inputSet;
}
}
if (!inputSetsArray.isCompactable())
throw new InvalidOverloadException("not all inputs covered");
this.overload = overload;
this.inputSetsByPos = inputSetsArray.toList();
this.varargs = localVarargInputs;
this.resultStrategy = overload.resultType();
this.pickingSet = localPickingInputs;
this.inputSetDescriptions = createInputSetDescriptions(inputSetsByPos, pickingSet, varargs);
this.exactInputs = overload.exactInputs();
this.inputsToInputSetIndex = mapInputsToInputSetIndex(inputSetsByPos, inputSetsCached, varargs);
}
private void buildInputsDescription(StringBuilder sb) {
for (int i = 0, nPos = positionalInputs(), nDesc = inputSetDescriptions.length; i < nDesc; ++i) {
sb.append(inputSetDescriptions[i]);
if (i == nPos)
sb.append("...");
if (i+1 < nDesc)
sb.append(", ");
}
}
private static int[] mapInputsToInputSetIndex(List<TInputSet> inputSetsByPos,
List<TInputSet> inputSetsCached,
TInputSet varargs)
{
int naturalPositions = inputSetsByPos.size();
int positions = naturalPositions;
if (varargs != null && varargs.positionsLength() == 0)
++positions;
int[] results = new int[positions];
Map<TInputSet, Integer> inputSetsToIndex = new HashMap<>(positions);
int indexCounter = 0;
for (int i = 0; i < positions; ++i) {
TInputSet inputSet = (i < naturalPositions) ? inputSetsByPos.get(i) : varargs;
Integer inputSetIndex = inputSetsToIndex.get(inputSet);
if (inputSetIndex == null) {
inputSetIndex = indexCounter++;
inputSetsToIndex.put(inputSet, inputSetIndex);
}
results[i] = inputSetIndex;
}
return results;
}
private static String[] createInputSetDescriptions(List<TInputSet> inputSetsByPos,
TInputSet pickingSet, TInputSet varargInputSet)
{
int nInputsRaw = inputSetsByPos.size();
int nInputsExtended = (varargInputSet == null) ? nInputsRaw : (nInputsRaw + 1);
String[] result = new String[nInputsExtended];
Map<TInputSet,String> map = new HashMap<>(nInputsRaw);
int anyCount = 0;
// if the picking input set is T, it's always T (not T#1 etc)
if (pickingSet != null && pickingSet.targetType() == null) {
map.put(pickingSet, "T");
++anyCount;
}
for (int i = 0; i < nInputsExtended; i++) {
TInputSet inputSet = (i == nInputsRaw) ? varargInputSet : inputSetsByPos.get(i);
String description = map.get(inputSet);
if (description == null) {
TClass inputTClass = inputSet == null ? null : inputSet.targetType();
if (inputTClass == null) {
description = "T";
if (anyCount > 0)
description += ('#' + anyCount);
++anyCount;
} else {
description = inputTClass.name().unqualifiedName();
}
map.put(inputSet, description);
}
result[i] = description;
}
return result;
}
private final TOverload overload;
private final List<TInputSet> inputSetsCached;
private final List<TInputSet> inputSetsByPos;
private final TOverloadResult resultStrategy;
private final TInputSet varargs;
private final TInputSet pickingSet;
private final InputSetFlags exactInputs;
private final int[] inputsToInputSetIndex;
/**
* A description of each input, indexed by its position. If there is a vararg input, its index is
* one greater than the 0-indexing of positions.
*/
private final String[] inputSetDescriptions;
private static class InvalidOverloadException extends RuntimeException {
private InvalidOverloadException(String message) {
super(message);
}
}
}