/***********************************************************************************************************************
*
* Copyright (C) 2010-2013 by the Stratosphere project (http://stratosphere.eu)
*
* 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 eu.stratosphere.api.java.operators;
import java.util.Arrays;
import eu.stratosphere.api.common.InvalidProgramException;
import eu.stratosphere.api.java.functions.KeySelector;
import eu.stratosphere.api.java.typeutils.TupleTypeInfo;
import eu.stratosphere.api.java.typeutils.TypeExtractor;
import eu.stratosphere.types.TypeInformation;
public abstract class Keys<T> {
public abstract int getNumberOfKeyFields();
public boolean isEmpty() {
return getNumberOfKeyFields() == 0;
}
public abstract boolean areCompatibale(Keys<?> other);
public abstract int[] computeLogicalKeyPositions();
// --------------------------------------------------------------------------------------------
// Specializations for field indexed / expression-based / extractor-based grouping
// --------------------------------------------------------------------------------------------
public static class FieldPositionKeys<T> extends Keys<T> {
private final int[] groupingFields;
private final TypeInformation<?>[] types;
public FieldPositionKeys(int[] groupingFields, TypeInformation<T> type) {
this(groupingFields, type, false);
}
public FieldPositionKeys(int[] groupingFields, TypeInformation<T> type, boolean allowEmpty) {
if (!type.isTupleType()) {
throw new InvalidProgramException("Specifying keys via field positions is only valid for tuple data types");
}
if (!allowEmpty && (groupingFields == null || groupingFields.length == 0)) {
throw new IllegalArgumentException("The grouping fields must not be empty.");
}
TupleTypeInfo<?> tupleType = (TupleTypeInfo<?>)type;
this.groupingFields = makeFields(groupingFields, (TupleTypeInfo<?>) type);
types = new TypeInformation[this.groupingFields.length];
for(int i = 0; i < this.groupingFields.length; i++) {
types[i] = tupleType.getTypeAt(this.groupingFields[i]);
}
}
@Override
public int getNumberOfKeyFields() {
return this.groupingFields.length;
}
@Override
public boolean areCompatibale(Keys<?> other) {
if (other instanceof FieldPositionKeys) {
FieldPositionKeys<?> oKey = (FieldPositionKeys<?>) other;
if(oKey.types.length != this.types.length) {
return false;
}
for(int i=0; i<this.types.length; i++) {
if(!this.types[i].equals(oKey.types[i])) {
return false;
}
}
return true;
} else if (other instanceof SelectorFunctionKeys) {
if(this.types.length != 1) {
return false;
}
SelectorFunctionKeys<?, ?> sfk = (SelectorFunctionKeys<?, ?>) other;
return sfk.keyType.equals(this.types[0]);
}
else {
return false;
}
}
@Override
public int[] computeLogicalKeyPositions() {
return this.groupingFields;
}
}
// --------------------------------------------------------------------------------------------
public static class SelectorFunctionKeys<T, K> extends Keys<T> {
private final KeySelector<T, K> keyExtractor;
private final TypeInformation<K> keyType;
public SelectorFunctionKeys(KeySelector<T, K> keyExtractor, TypeInformation<T> type) {
this.keyExtractor = keyExtractor;
this.keyType = TypeExtractor.getKeySelectorTypes(keyExtractor, type);
}
public TypeInformation<K> getKeyType() {
return keyType;
}
public KeySelector<T, K> getKeyExtractor() {
return keyExtractor;
}
@Override
public int getNumberOfKeyFields() {
return 1;
}
@Override
public boolean areCompatibale(Keys<?> other) {
if (other instanceof SelectorFunctionKeys) {
@SuppressWarnings("unchecked")
SelectorFunctionKeys<?, K> sfk = (SelectorFunctionKeys<?, K>) other;
return sfk.keyType.equals(this.keyType);
}
else if (other instanceof FieldPositionKeys) {
FieldPositionKeys<?> fpk = (FieldPositionKeys<?>) other;
if(fpk.types.length != 1) {
return false;
}
return fpk.types[0].equals(this.keyType);
}
else {
return false;
}
}
@Override
public int[] computeLogicalKeyPositions() {
return new int[] {0};
}
}
// --------------------------------------------------------------------------------------------
public static class ExpressionKeys<T> extends Keys<T> {
public ExpressionKeys(String expression, TypeInformation<T> type) {
}
@Override
public int getNumberOfKeyFields() {
throw new UnsupportedOperationException("Expression keys not yet implemented");
}
@Override
public boolean areCompatibale(Keys<?> other) {
throw new UnsupportedOperationException("Expression keys not yet implemented");
}
@Override
public int[] computeLogicalKeyPositions() {
throw new UnsupportedOperationException("Expression keys not yet implemented");
}
}
// --------------------------------------------------------------------------------------------
// Utilities
// --------------------------------------------------------------------------------------------
private static int[] makeFields(int[] fields, TupleTypeInfo<?> type) {
int inLength = type.getArity();
// null parameter means all fields are considered
if (fields == null || fields.length == 0) {
fields = new int[inLength];
for (int i = 0; i < inLength; i++) {
fields[i] = i;
}
return fields;
} else {
return rangeCheckAndOrderFields(fields, inLength-1);
}
}
private static final int[] rangeCheckAndOrderFields(int[] fields, int maxAllowedField) {
// order
Arrays.sort(fields);
// range check and duplicate eliminate
int i = 1, k = 0;
int last = fields[0];
if (last < 0 || last > maxAllowedField) {
throw new IllegalArgumentException("Tuple position is out of range.");
}
for (; i < fields.length; i++) {
if (fields[i] < 0 || i > maxAllowedField) {
throw new IllegalArgumentException("Tuple position is out of range.");
}
if (fields[i] != last) {
k++;
fields[k] = fields[i];
}
}
// check if we eliminated something
if (k == fields.length - 1) {
return fields;
} else {
return Arrays.copyOfRange(fields, 0, k);
}
}
}