/* * 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.common.typeutils; import java.util.ArrayList; import java.util.List; import java.util.Objects; 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.typeinfo.AtomicType; import org.apache.flink.api.common.typeinfo.TypeInformation; import static org.apache.flink.util.Preconditions.checkNotNull; /** * Base type information class for Tuple and Pojo types * * The class is taking care of serialization and comparators for Tuples as well. */ @Public public abstract class CompositeType<T> extends TypeInformation<T> { private static final long serialVersionUID = 1L; private final Class<T> typeClass; @PublicEvolving public CompositeType(Class<T> typeClass) { this.typeClass = checkNotNull(typeClass); } /** * Returns the type class of the composite type * * @return Type class of the composite type */ @PublicEvolving public Class<T> getTypeClass() { return typeClass; } /** * Returns the flat field descriptors for the given field expression. * * @param fieldExpression The field expression for which the flat field descriptors are computed. * @return The list of descriptors for the flat fields which are specified by the field expression. */ @PublicEvolving public List<FlatFieldDescriptor> getFlatFields(String fieldExpression) { List<FlatFieldDescriptor> result = new ArrayList<FlatFieldDescriptor>(); this.getFlatFields(fieldExpression, 0, result); return result; } /** * Computes the flat field descriptors for the given field expression with the given offset. * * @param fieldExpression The field expression for which the FlatFieldDescriptors are computed. * @param offset The offset to use when computing the positions of the flat fields. * @param result The list into which all flat field descriptors are inserted. */ @PublicEvolving public abstract void getFlatFields(String fieldExpression, int offset, List<FlatFieldDescriptor> result); /** * Returns the type of the (nested) field at the given field expression position. * Wildcards are not allowed. * * @param fieldExpression The field expression for which the field of which the type is returned. * @return The type of the field at the given field expression. */ @PublicEvolving public abstract <X> TypeInformation<X> getTypeAt(String fieldExpression); /** * Returns the type of the (unnested) field at the given field position. * * @param pos The position of the (unnested) field in this composite type. * @return The type of the field at the given position. */ @PublicEvolving public abstract <X> TypeInformation<X> getTypeAt(int pos); @PublicEvolving protected abstract TypeComparatorBuilder<T> createTypeComparatorBuilder(); /** * Generic implementation of the comparator creation. Composite types are supplying the infrastructure * to create the actual comparators * @return The comparator */ @PublicEvolving public TypeComparator<T> createComparator(int[] logicalKeyFields, boolean[] orders, int logicalFieldOffset, ExecutionConfig config) { TypeComparatorBuilder<T> builder = createTypeComparatorBuilder(); builder.initializeTypeComparatorBuilder(logicalKeyFields.length); for (int logicalKeyFieldIndex = 0; logicalKeyFieldIndex < logicalKeyFields.length; logicalKeyFieldIndex++) { int logicalKeyField = logicalKeyFields[logicalKeyFieldIndex]; int logicalField = logicalFieldOffset; // this is the global/logical field number boolean comparatorAdded = false; for (int localFieldId = 0; localFieldId < this.getArity() && logicalField <= logicalKeyField && !comparatorAdded; localFieldId++) { TypeInformation<?> localFieldType = this.getTypeAt(localFieldId); if (localFieldType instanceof AtomicType && logicalField == logicalKeyField) { // we found an atomic key --> create comparator builder.addComparatorField( localFieldId, ((AtomicType<?>) localFieldType).createComparator( orders[logicalKeyFieldIndex], config)); comparatorAdded = true; } // must be composite type and check that the logicalKeyField is within the bounds // of the composite type's logical fields else if (localFieldType instanceof CompositeType && logicalField <= logicalKeyField && logicalKeyField <= logicalField + (localFieldType.getTotalFields() - 1)) { // we found a compositeType that is containing the logicalKeyField we are looking for --> create comparator builder.addComparatorField( localFieldId, ((CompositeType<?>) localFieldType).createComparator( new int[]{logicalKeyField}, new boolean[]{orders[logicalKeyFieldIndex]}, logicalField, config) ); comparatorAdded = true; } if (localFieldType instanceof CompositeType) { // we need to subtract 1 because we are not accounting for the local field (not accessible for the user) logicalField += localFieldType.getTotalFields() - 1; } logicalField++; } if (!comparatorAdded) { throw new IllegalArgumentException("Could not add a comparator for the logical" + "key field index " + logicalKeyFieldIndex + "."); } } return builder.createTypeComparator(config); } // -------------------------------------------------------------------------------------------- @PublicEvolving protected interface TypeComparatorBuilder<T> { void initializeTypeComparatorBuilder(int size); void addComparatorField(int fieldId, TypeComparator<?> comparator); TypeComparator<T> createTypeComparator(ExecutionConfig config); } @PublicEvolving public static class FlatFieldDescriptor { private int keyPosition; private TypeInformation<?> type; public FlatFieldDescriptor(int keyPosition, TypeInformation<?> type) { if(type instanceof CompositeType) { throw new IllegalArgumentException("A flattened field can not be a composite type"); } this.keyPosition = keyPosition; this.type = type; } public int getPosition() { return keyPosition; } public TypeInformation<?> getType() { return type; } @Override public String toString() { return "FlatFieldDescriptor [position="+keyPosition+" typeInfo="+type+"]"; } } /** * Returns true when this type has a composite field with the given name. */ @PublicEvolving public boolean hasField(String fieldName) { return getFieldIndex(fieldName) >= 0; } @Override @PublicEvolving public boolean isKeyType() { for(int i=0;i<this.getArity();i++) { if (!this.getTypeAt(i).isKeyType()) { return false; } } return true; } @Override @PublicEvolving public boolean isSortKeyType() { for(int i=0;i<this.getArity();i++) { if (!this.getTypeAt(i).isSortKeyType()) { return false; } } return true; } /** * Returns the names of the composite fields of this type. The order of the returned array must * be consistent with the internal field index ordering. */ @PublicEvolving public abstract String[] getFieldNames(); /** * True if this type has an inherent ordering of the fields, such that a user can * always be sure in which order the fields will be in. This is true for Tuples and * Case Classes. It is not true for Regular Java Objects, since there, the ordering of * the fields can be arbitrary. * * This is used when translating a DataSet or DataStream to an Expression Table, when * initially renaming the fields of the underlying type. */ @PublicEvolving public boolean hasDeterministicFieldOrder() { return false; } /** * Returns the field index of the composite field of the given name. * * @return The field index or -1 if this type does not have a field of the given name. */ @PublicEvolving public abstract int getFieldIndex(String fieldName); @PublicEvolving public static class InvalidFieldReferenceException extends IllegalArgumentException { private static final long serialVersionUID = 1L; public InvalidFieldReferenceException(String s) { super(s); } } @Override public boolean equals(Object obj) { if (obj instanceof CompositeType) { @SuppressWarnings("unchecked") CompositeType<T> compositeType = (CompositeType<T>)obj; return compositeType.canEqual(this) && typeClass == compositeType.typeClass; } else { return false; } } @Override public int hashCode() { return Objects.hash(typeClass); } @Override public boolean canEqual(Object obj) { return obj instanceof CompositeType; } @Override public String toString() { return getClass().getSimpleName() + "<" + typeClass.getSimpleName() + ">"; } }