/** * 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.service; import com.foundationdb.server.types.InputSetFlags; import com.foundationdb.server.types.TClass; import com.foundationdb.server.types.TInputSet; import com.foundationdb.server.types.texpressions.TValidatedOverload; import com.foundationdb.util.Strings; import com.google.common.base.Function; import com.google.common.base.Predicate; import com.google.common.collect.Lists; import java.util.ArrayList; import java.util.Collection; import java.util.List; abstract class OverloadsFolder { protected abstract TClass foldOne(TClass accumulated, TClass input); public Result<TClass> fold(Collection<? extends TValidatedOverload> overloads) { int nFinites = 0; boolean anyVarargs = false; for (TValidatedOverload overload : overloads) { int nArgs = overload.positionalInputs(); nFinites = Math.max(nFinites, nArgs); if (overload.isVararg()) anyVarargs = true; } List<TClass> finitesList = new ArrayList<>(nFinites); for (int pos = 0; pos < nFinites; ++pos) { TClass result = foldBy(overloads, new FoldByPositionalArity(pos)); finitesList.add(result); } TClass infiniteArityElement; boolean hasInfiniteArityElement; if (anyVarargs) { infiniteArityElement = foldBy(overloads, foldByVarags); hasInfiniteArityElement = true; } else { infiniteArityElement = null; hasInfiniteArityElement = false; } return new Result<>(finitesList, infiniteArityElement, hasInfiniteArityElement); } private TClass foldBy(Collection<? extends TValidatedOverload> overloads, Function<TValidatedOverload, TInputSet> f) { TClass result = null; boolean seenOne = false; for (TValidatedOverload overload : overloads) { TInputSet inputSet = f.apply(overload); if (inputSet != null) { TClass attribute = inputSet.targetType(); if (seenOne) { if (attribute != null) { result = (result == null) ? attribute : foldOne(result, attribute); } } else { result = attribute; seenOne = true; } } } assert seenOne; return result; } private static class FoldByPositionalArity implements Function<TValidatedOverload, TInputSet> { @Override public TInputSet apply(TValidatedOverload input) { return pos < input.positionalInputs() ? input.inputSetAt(pos) : input.varargInputSet(); } public FoldByPositionalArity(int pos) { this.pos = pos; } private int pos; } private static final Function<TValidatedOverload, TInputSet> foldByVarags = new Function<TValidatedOverload, TInputSet>() { @Override public TInputSet apply(TValidatedOverload input) { return input.varargInputSet(); } }; static class Result<T> { public List<T> finiteArityList() { return finiteArityList; } public T infiniteArityElement(T ifNone) { return hasInfiniteArityElement ? infiniteArityElement : ifNone; } public T at(int i, T ifUndefined) { if (i < finiteArityList.size()) { return finiteArityList.get(i); } else { return hasInfiniteArityElement ? infiniteArityElement : ifUndefined; } } public <M> Result<M> transform(Function<? super T, ? extends M> mapFunction) { List<M> mappedFinites = Lists.transform(finiteArityList, mapFunction); M mappedInfinite = hasInfiniteArityElement ? mapFunction.apply(infiniteArityElement) : null; return new Result<>(mappedFinites, mappedInfinite, hasInfiniteArityElement); } public InputSetFlags toInputSetFlags(Predicate<? super T> predicate) { boolean[] finites = new boolean[finiteArityList.size()]; for (int i = 0; i < finites.length; ++i) { finites[i] = predicate.apply(finiteArityList.get(i)); } boolean infinite = predicate.apply(infiniteArityElement); return new InputSetFlags(finites, infinite); } // Object interface @Override public String toString() { String finites = Strings.join(finiteArityList, ", "); return hasInfiniteArityElement ? (finites + ", " + infiniteArityElement + "...") : finites; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Result result = (Result) o; return finiteArityList.equals(result.finiteArityList) && infiniteArityElement.equals(result.infiniteArityElement); } @Override public int hashCode() { int result = finiteArityList.hashCode(); result = 31 * result + infiniteArityElement.hashCode(); return result; } public Result(List<T> finiteArityList, T infiniteArityElement, boolean hasInfiniteArityElement) { assert finiteArityList != null; this.finiteArityList = finiteArityList; this.infiniteArityElement = infiniteArityElement; this.hasInfiniteArityElement = hasInfiniteArityElement; } private final List<T> finiteArityList; private final T infiniteArityElement; private final boolean hasInfiniteArityElement; } }