/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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.apache.flink.api.java.typeutils; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.apache.flink.annotation.PublicEvolving; import org.apache.flink.annotation.Public; import org.apache.flink.api.common.ExecutionConfig; import org.apache.flink.api.common.functions.InvalidTypesException; import org.apache.flink.api.common.typeinfo.BasicTypeInfo; import org.apache.flink.api.common.typeinfo.TypeInformation; import org.apache.flink.api.common.typeutils.TypeComparator; import org.apache.flink.api.common.typeutils.TypeSerializer; import org.apache.flink.api.java.typeutils.runtime.Tuple0Serializer; import org.apache.flink.api.java.typeutils.runtime.TupleComparator; import org.apache.flink.api.java.typeutils.runtime.TupleSerializer; import org.apache.flink.types.Value; //CHECKSTYLE.OFF: AvoidStarImport - Needed for TupleGenerator import org.apache.flink.api.java.tuple.*; //CHECKSTYLE.ON: AvoidStarImport import static org.apache.flink.util.Preconditions.checkArgument; import static org.apache.flink.util.Preconditions.checkState; /** * A {@link TypeInformation} for the tuple types of the Java API. * * @param <T> The type of the tuple. */ @Public public final class TupleTypeInfo<T extends Tuple> extends TupleTypeInfoBase<T> { private static final long serialVersionUID = 1L; protected final String[] fieldNames; @SuppressWarnings("unchecked") @PublicEvolving public TupleTypeInfo(TypeInformation<?>... types) { this((Class<T>) Tuple.getTupleClass(types.length), types); } @PublicEvolving public TupleTypeInfo(Class<T> tupleType, TypeInformation<?>... types) { super(tupleType, types); checkArgument( types.length <= Tuple.MAX_ARITY, "The tuple type exceeds the maximum supported arity."); this.fieldNames = new String[types.length]; for (int i = 0; i < types.length; i++) { fieldNames[i] = "f" + i; } } @Override @PublicEvolving public String[] getFieldNames() { return fieldNames; } @Override @PublicEvolving public int getFieldIndex(String fieldName) { for (int i = 0; i < fieldNames.length; i++) { if (fieldNames[i].equals(fieldName)) { return i; } } return -1; } @SuppressWarnings("unchecked") @Override @PublicEvolving public TupleSerializer<T> createSerializer(ExecutionConfig executionConfig) { if (getTypeClass() == Tuple0.class) { return (TupleSerializer<T>) Tuple0Serializer.INSTANCE; } TypeSerializer<?>[] fieldSerializers = new TypeSerializer<?>[getArity()]; for (int i = 0; i < types.length; i++) { fieldSerializers[i] = types[i].createSerializer(executionConfig); } Class<T> tupleClass = getTypeClass(); return new TupleSerializer<T>(tupleClass, fieldSerializers); } @Override protected TypeComparatorBuilder<T> createTypeComparatorBuilder() { return new TupleTypeComparatorBuilder(); } private class TupleTypeComparatorBuilder implements TypeComparatorBuilder<T> { private final ArrayList<TypeComparator> fieldComparators = new ArrayList<TypeComparator>(); private final ArrayList<Integer> logicalKeyFields = new ArrayList<Integer>(); @Override public void initializeTypeComparatorBuilder(int size) { fieldComparators.ensureCapacity(size); logicalKeyFields.ensureCapacity(size); } @Override public void addComparatorField(int fieldId, TypeComparator<?> comparator) { fieldComparators.add(comparator); logicalKeyFields.add(fieldId); } @Override public TypeComparator<T> createTypeComparator(ExecutionConfig config) { checkState( fieldComparators.size() > 0, "No field comparators were defined for the TupleTypeComparatorBuilder." ); checkState( logicalKeyFields.size() > 0, "No key fields were defined for the TupleTypeComparatorBuilder." ); checkState( fieldComparators.size() == logicalKeyFields.size(), "The number of field comparators and key fields is not equal." ); final int maxKey = Collections.max(logicalKeyFields); checkState( maxKey >= 0, "The maximum key field must be greater or equal than 0." ); TypeSerializer<?>[] fieldSerializers = new TypeSerializer<?>[maxKey + 1]; for (int i = 0; i <= maxKey; i++) { fieldSerializers[i] = types[i].createSerializer(config); } return new TupleComparator<T>( listToPrimitives(logicalKeyFields), fieldComparators.toArray(new TypeComparator[fieldComparators.size()]), fieldSerializers ); } } @Override public Map<String, TypeInformation<?>> getGenericParameters() { Map<String, TypeInformation<?>> m = new HashMap<>(types.length); for (int i = 0; i < types.length; i++) { m.put("T" + i, types[i]); } return m; } // -------------------------------------------------------------------------------------------- @Override public boolean equals(Object obj) { if (obj instanceof TupleTypeInfo) { @SuppressWarnings("unchecked") TupleTypeInfo<T> other = (TupleTypeInfo<T>) obj; return other.canEqual(this) && super.equals(other) && Arrays.equals(fieldNames, other.fieldNames); } else { return false; } } @Override public boolean canEqual(Object obj) { return obj instanceof TupleTypeInfo; } @Override public int hashCode() { return 31 * super.hashCode() + Arrays.hashCode(fieldNames); } @Override public String toString() { return "Java " + super.toString(); } // -------------------------------------------------------------------------------------------- @PublicEvolving public static <X extends Tuple> TupleTypeInfo<X> getBasicTupleTypeInfo(Class<?>... basicTypes) { if (basicTypes == null || basicTypes.length == 0) { throw new IllegalArgumentException(); } TypeInformation<?>[] infos = new TypeInformation<?>[basicTypes.length]; for (int i = 0; i < infos.length; i++) { Class<?> type = basicTypes[i]; if (type == null) { throw new IllegalArgumentException("Type at position " + i + " is null."); } TypeInformation<?> info = BasicTypeInfo.getInfoFor(type); if (info == null) { throw new IllegalArgumentException("Type at position " + i + " is not a basic type."); } infos[i] = info; } @SuppressWarnings("unchecked") TupleTypeInfo<X> tupleInfo = (TupleTypeInfo<X>) new TupleTypeInfo<Tuple>(infos); return tupleInfo; } @SuppressWarnings("unchecked") @PublicEvolving public static <X extends Tuple> TupleTypeInfo<X> getBasicAndBasicValueTupleTypeInfo(Class<?>... basicTypes) { if (basicTypes == null || basicTypes.length == 0) { throw new IllegalArgumentException(); } TypeInformation<?>[] infos = new TypeInformation<?>[basicTypes.length]; for (int i = 0; i < infos.length; i++) { Class<?> type = basicTypes[i]; if (type == null) { throw new IllegalArgumentException("Type at position " + i + " is null."); } TypeInformation<?> info = BasicTypeInfo.getInfoFor(type); if (info == null) { try { info = ValueTypeInfo.getValueTypeInfo((Class<Value>) type); if (!((ValueTypeInfo<?>) info).isBasicValueType()) { throw new IllegalArgumentException("Type at position " + i + " is not a basic or value type."); } } catch (ClassCastException | InvalidTypesException e) { throw new IllegalArgumentException("Type at position " + i + " is not a basic or value type.", e); } } infos[i] = info; } return (TupleTypeInfo<X>) new TupleTypeInfo<>(infos); } private static int[] listToPrimitives(ArrayList<Integer> ints) { int[] result = new int[ints.size()]; for (int i = 0; i < result.length; i++) { result[i] = ints.get(i); } return result; } }