/*
* 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.HashMap;
import java.util.Map;
import org.apache.flink.annotation.Internal;
import org.apache.flink.api.common.operators.util.FieldSet;
/**
* Container for the semantic properties associated to a dual input operator.
*/
@Internal
public class DualInputSemanticProperties implements SemanticProperties {
private static final long serialVersionUID = 1L;
/**
* Mapping from fields in the source record(s) in the first input to fields
* in the destination record(s).
*/
private Map<Integer,FieldSet> fieldMapping1;
/**
* Mapping from fields in the source record(s) in the second input to fields
* in the destination record(s).
*/
private Map<Integer,FieldSet> fieldMapping2;
/**
* Set of fields that are read in the source record(s) from the
* first input.
*/
private FieldSet readFields1;
/**
* Set of fields that are read in the source record(s) from the
* second input.
*/
private FieldSet readFields2;
public DualInputSemanticProperties() {
this.fieldMapping1 = new HashMap<Integer,FieldSet>();
this.fieldMapping2 = new HashMap<Integer,FieldSet>();
this.readFields1 = null;
this.readFields2 = null;
}
@Override
public FieldSet getForwardingTargetFields(int input, int sourceField) {
if (input != 0 && input != 1) {
throw new IndexOutOfBoundsException();
} else if (input == 0) {
return fieldMapping1.containsKey(sourceField) ? fieldMapping1.get(sourceField) : FieldSet.EMPTY_SET;
} else {
return fieldMapping2.containsKey(sourceField) ? fieldMapping2.get(sourceField) : FieldSet.EMPTY_SET;
}
}
@Override
public int getForwardingSourceField(int input, int targetField) {
Map<Integer, FieldSet> fieldMapping;
if (input != 0 && input != 1) {
throw new IndexOutOfBoundsException();
} else if (input == 0) {
fieldMapping = fieldMapping1;
} else {
fieldMapping = fieldMapping2;
}
for (Map.Entry<Integer, FieldSet> e : fieldMapping.entrySet()) {
if (e.getValue().contains(targetField)) {
return e.getKey();
}
}
return -1;
}
@Override
public FieldSet getReadFields(int input) {
if (input != 0 && input != 1) {
throw new IndexOutOfBoundsException();
}
if (input == 0) {
return readFields1;
} else {
return readFields2;
}
}
/**
* Adds, to the existing information, a field that is forwarded directly
* from the source record(s) in the first input to the destination
* record(s).
*
* @param input the input of the source field
* @param sourceField the position in the source record
* @param targetField the position in the destination record
*/
public void addForwardedField(int input, int sourceField, int targetField) {
Map<Integer, FieldSet> fieldMapping;
if (input != 0 && input != 1) {
throw new IndexOutOfBoundsException();
} else if (input == 0) {
fieldMapping = this.fieldMapping1;
} else {
fieldMapping = this.fieldMapping2;
}
if(isTargetFieldPresent(targetField, fieldMapping)) {
throw new InvalidSemanticAnnotationException("Target field "+targetField+" was added twice to input "+input);
}
FieldSet targetFields = fieldMapping.get(sourceField);
if (targetFields != null) {
fieldMapping.put(sourceField, targetFields.addField(targetField));
} else {
fieldMapping.put(sourceField, new FieldSet(targetField));
}
}
private boolean isTargetFieldPresent(int targetField, Map<Integer, FieldSet> fieldMapping) {
for(FieldSet targetFields : fieldMapping.values()) {
if(targetFields.contains(targetField)) {
return true;
}
}
return false;
}
/**
* Adds, to the existing information, field(s) that are read in
* the source record(s) from the first input.
*
* @param input the input of the read fields
* @param readFields the position(s) in the source record(s)
*/
public void addReadFields(int input, FieldSet readFields) {
if (input != 0 && input != 1) {
throw new IndexOutOfBoundsException();
} else if (input == 0) {
this.readFields1 = (this.readFields1 == null) ? readFields.clone() : this.readFields1.addFields(readFields);
} else {
this.readFields2 = (this.readFields2 == null) ? readFields.clone() : this.readFields2.addFields(readFields);
}
}
@Override
public String toString() {
return "DISP(" + this.fieldMapping1 + "; " + this.fieldMapping2 + ")";
}
}