/* * 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.operators; import java.util.List; import org.apache.flink.annotation.Internal; import org.apache.flink.api.common.ExecutionConfig; import org.apache.flink.api.common.functions.Function; import org.apache.flink.api.common.functions.RuntimeContext; import org.apache.flink.api.common.operators.util.UserCodeWrapper; import org.apache.flink.util.Visitor; /** * Abstract operator superclass for for all operators that have two inputs, like "Join", "CoGroup", or "Cross". * * @param <IN1> First input type of the user function * @param <IN2> Second input type of the user function * @param <OUT> Output type of the user function * @param <FT> Type of the user function */ @Internal public abstract class DualInputOperator<IN1, IN2, OUT, FT extends Function> extends AbstractUdfOperator<OUT, FT> { /** * The operator producing the first input. */ protected Operator<IN1> input1; /** * The operator producing the second input. */ protected Operator<IN2> input2; /** * The positions of the keys in the tuples of the first input. */ private final int[] keyFields1; /** * The positions of the keys in the tuples of the second input. */ private final int[] keyFields2; /** * Semantic properties of the associated function. */ private DualInputSemanticProperties semanticProperties = new DualInputSemanticProperties(); // -------------------------------------------------------------------------------------------- /** * Creates a new abstract dual-input Pact with the given name wrapping the given user function. * * @param stub The class containing the user function. * @param name The given name for the operator, used in plans, logs and progress messages. */ protected DualInputOperator(UserCodeWrapper<FT> stub, BinaryOperatorInformation<IN1, IN2, OUT> operatorInfo, String name) { super(stub, operatorInfo, name); this.keyFields1 = this.keyFields2 = new int[0]; } /** * Creates a new abstract dual-input operator with the given name wrapping the given user function. * This constructor is specialized only for operator that require no keys for their processing. * * @param stub The object containing the user function. * @param keyPositions1 The positions of the fields in the first input that act as keys. * @param keyPositions2 The positions of the fields in the second input that act as keys. * @param name The given name for the operator, used in plans, logs and progress messages. */ protected DualInputOperator(UserCodeWrapper<FT> stub, BinaryOperatorInformation<IN1, IN2, OUT> operatorInfo, int[] keyPositions1, int[] keyPositions2, String name) { super(stub, operatorInfo, name); this.keyFields1 = keyPositions1; this.keyFields2 = keyPositions2; } // -------------------------------------------------------------------------------------------- /** * Gets the information about the operators input/output types. */ @Override @SuppressWarnings("unchecked") public BinaryOperatorInformation<IN1, IN2, OUT> getOperatorInfo() { return (BinaryOperatorInformation<IN1, IN2, OUT>) this.operatorInfo; } /** * Returns the first input, or null, if none is set. * * @return The contract's first input. */ public Operator<IN1> getFirstInput() { return this.input1; } /** * Returns the second input, or null, if none is set. * * @return The contract's second input. */ public Operator<IN2> getSecondInput() { return this.input2; } /** * Clears this operator's first input. */ public void clearFirstInput() { this.input1 = null; } /** * Clears this operator's second input. */ public void clearSecondInput() { this.input2 = null; } /** * Clears all previous connections and connects the first input to the task wrapped in this contract * * @param input The contract that is connected as the first input. */ public void setFirstInput(Operator<IN1> input) { this.input1 = input; } /** * Clears all previous connections and connects the second input to the task wrapped in this contract * * @param input The contract that is connected as the second input. */ public void setSecondInput(Operator<IN2> input) { this.input2 = input; } /** * Sets the first input to the union of the given operators. * * @param inputs The operator(s) that form the first input. * @deprecated This method will be removed in future versions. Use the {@link Union} operator instead. */ @Deprecated public void setFirstInput(Operator<IN1>... inputs) { this.input1 = Operator.createUnionCascade(inputs); } /** * Sets the second input to the union of the given operators. * * @param inputs The operator(s) that form the second input. * @deprecated This method will be removed in future versions. Use the {@link Union} operator instead. */ @Deprecated public void setSecondInput(Operator<IN2>... inputs) { this.input2 = Operator.createUnionCascade(inputs); } /** * Sets the first input to the union of the given operators. * * @param inputs The operator(s) that form the first inputs. * @deprecated This method will be removed in future versions. Use the {@link Union} operator instead. */ @Deprecated public void setFirstInputs(List<Operator<IN1>> inputs) { this.input1 = Operator.createUnionCascade(inputs); } /** * Sets the second input to the union of the given operators. * * @param inputs The operator(s) that form the second inputs. * @deprecated This method will be removed in future versions. Use the {@link Union} operator instead. */ @Deprecated public void setSecondInputs(List<Operator<IN2>> inputs) { this.input2 = Operator.createUnionCascade(inputs); } /** * Add to the first input the union of the given operators. * * @param input The operator(s) to be unioned with the first input. * @deprecated This method will be removed in future versions. Use the {@link Union} operator instead. */ @Deprecated public void addFirstInput(Operator<IN1>... input) { this.input1 = Operator.createUnionCascade(this.input1, input); } /** * Add to the second input the union of the given operators. * * @param input The operator(s) to be unioned with the second input. * @deprecated This method will be removed in future versions. Use the {@link Union} operator instead. */ @Deprecated public void addSecondInput(Operator<IN2>... input) { this.input2 = Operator.createUnionCascade(this.input2, input); } /** * Add to the first input the union of the given operators. * * @param inputs The operator(s) to be unioned with the first input. * @deprecated This method will be removed in future versions. Use the {@link Union} operator instead. */ @Deprecated @SuppressWarnings("unchecked") public void addFirstInputs(List<Operator<IN1>> inputs) { this.input1 = Operator.createUnionCascade(this.input1, inputs.toArray(new Operator[inputs.size()])); } /** * Add to the second input the union of the given operators. * * @param inputs The operator(s) to be unioned with the second input. * @deprecated This method will be removed in future versions. Use the {@link Union} operator instead. */ @Deprecated @SuppressWarnings("unchecked") public void addSecondInputs(List<Operator<IN2>> inputs) { this.input2 = Operator.createUnionCascade(this.input2, inputs.toArray(new Operator[inputs.size()])); } // -------------------------------------------------------------------------------------------- public DualInputSemanticProperties getSemanticProperties() { return this.semanticProperties; } public void setSemanticProperties(DualInputSemanticProperties semanticProperties) { this.semanticProperties = semanticProperties; } // -------------------------------------------------------------------------------------------- @Override public final int getNumberOfInputs() { return 2; } @Override public int[] getKeyColumns(int inputNum) { if (inputNum == 0) { return this.keyFields1; } else if (inputNum == 1) { return this.keyFields2; } else { throw new IndexOutOfBoundsException(); } } // -------------------------------------------------------------------------------------------- @Override public void accept(Visitor<Operator<?>> visitor) { boolean descend = visitor.preVisit(this); if (descend) { this.input1.accept(visitor); this.input2.accept(visitor); for (Operator<?> c : this.broadcastInputs.values()) { c.accept(visitor); } visitor.postVisit(this); } } // -------------------------------------------------------------------------------------------- protected abstract List<OUT> executeOnCollections(List<IN1> inputData1, List<IN2> inputData2, RuntimeContext runtimeContext, ExecutionConfig executionConfig) throws Exception; }