/***********************************************************************************************************************
* 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.record.functions;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import eu.stratosphere.api.common.operators.DualInputSemanticProperties;
import eu.stratosphere.api.common.operators.SingleInputSemanticProperties;
import eu.stratosphere.api.common.operators.util.FieldSet;
import eu.stratosphere.api.common.operators.util.UserCodeWrapper;
/**
* This class defines the semantic assertions that can be added to functions.
* The assertions are realized as java annotations, to be added to the class declaration of
* the class that realized the user function. For example, to declare the <i>ConstantFieldsExcept</i>
* annotation for a map-type function that realizes a simple absolute function,
* use it the following way:
*
* <pre><blockquote>
* \@ConstantFieldsExcept(fields={2})
* public class MyMapper extends MapFunction
* {
* public void map(Record record, Collector out)
* {
* int value = record.getField(2, IntValue.class).getValue();
record.setField(2, new IntValue(Math.abs(value)));
out.collect(record);
* }
* }
* </blockquote></pre>
*
* Be aware that some annotations should only be used for functions with as single input
* ({@link MapFunction}, {@link ReduceFunction}) and some only for stubs with two inputs
* ({@link CrossFunction}, {@link JoinFunction}, {@link CoGroupFunction}).
*/
public class FunctionAnnotation {
/**
* Specifies the fields of an input record that are unchanged in the output of
* a stub with a single input ( {@link MapFunction}, {@link ReduceFunction}).
*
* A field is considered to be constant if its value is not changed and copied to the same position of
* output record.
*
* <b>
* It is very important to follow a conservative strategy when specifying constant fields.
* Only fields that are always constant (regardless of value, stub call, etc.) to the output may be
* inserted! Otherwise, the correct execution of a program can not be guaranteed.
* So if in doubt, do not add a field to this set.
* </b>
*
* This annotation is mutually exclusive with the {@link ConstantFieldsExcept} annotation.
*
* If this annotation and the {@link ConstantFieldsExcept} annotation is not set, it is
* assumed that <i>no</i> field is constant.
*
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ConstantFields {
int[] value();
}
/**
* Specifies that all fields of an input record that are unchanged in the output of
* a {@link MapFunction}, or {@link ReduceFunction}).
*
* A field is considered to be constant if its value is not changed and copied to the same position of
* output record.
*
* <b>
* It is very important to follow a conservative strategy when specifying constant fields.
* Only fields that are always constant (regardless of value, stub call, etc.) to the output may be
* inserted! Otherwise, the correct execution of a program can not be guaranteed.
* So if in doubt, do not add a field to this set.
* </b>
*
* This annotation is mutually exclusive with the {@link ConstantFieldsExcept} annotation.
*
* If this annotation and the {@link ConstantFieldsExcept} annotation is not set, it is
* assumed that <i>no</i> field is constant.
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface AllFieldsConstants {}
/**
* Specifies the fields of an input record of the first input that are unchanged in
* the output of a stub with two inputs ( {@link CrossFunction}, {@link JoinFunction}, {@link CoGroupFunction})
*
* A field is considered to be constant if its value is not changed and copied to the same position of
* output record.
*
* <b>
* It is very important to follow a conservative strategy when specifying constant fields.
* Only fields that are always constant (regardless of value, stub call, etc.) to the output may be
* inserted! Otherwise, the correct execution of a program can not be guaranteed.
* So if in doubt, do not add a field to this set.
* </b>
*
* This annotation is mutually exclusive with the {@link ConstantFieldsFirstExcept} annotation.
*
* If this annotation and the {@link ConstantFieldsFirstExcept} annotation is not set, it is
* assumed that <i>no</i> field is constant.
*
*
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ConstantFieldsFirst {
int[] value();
}
/**
* Specifies the fields of an input record of the second input that are unchanged in
* the output of a stub with two inputs ( {@link CrossFunction}, {@link JoinFunction}, {@link CoGroupFunction})
*
* A field is considered to be constant if its value is not changed and copied to the same position of
* output record.
*
* <b>
* It is very important to follow a conservative strategy when specifying constant fields.
* Only fields that are always constant (regardless of value, stub call, etc.) to the output may be
* inserted! Otherwise, the correct execution of a program can not be guaranteed.
* So if in doubt, do not add a field to this set.
* </b>
*
* This annotation is mutually exclusive with the {@link ConstantFieldsSecondExcept} annotation.
*
* If this annotation and the {@link ConstantFieldsSecondExcept} annotation is not set, it is
* assumed that <i>no</i> field is constant.
*
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ConstantFieldsSecond {
int[] value();
}
/**
* Specifies the fields of an input record that are changed in the output of
* a stub with a single input ( {@link MapFunction}, {@link ReduceFunction}). All other
* fields are assumed to be constant.
*
* A field is considered to be constant if its value is not changed and copied to the same position of
* output record.
*
* <b>
* It is very important to follow a conservative strategy when specifying constant fields.
* Only fields that are always constant (regardless of value, stub call, etc.) to the output may be
* inserted! Otherwise, the correct execution of a program can not be guaranteed.
* So if in doubt, do not add a field to this set.
* </b>
*
* This annotation is mutually exclusive with the {@link ConstantFields} annotation.
*
* If this annotation and the {@link ConstantFields} annotation is not set, it is
* assumed that <i>no</i> field is constant.
*
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ConstantFieldsExcept {
int[] value();
}
/**
* Specifies the fields of an input record of the first input that are changed in
* the output of a stub with two inputs ( {@link CrossFunction}, {@link JoinFunction}, {@link CoGroupFunction})
* All other fields are assumed to be constant.
*
* A field is considered to be constant if its value is not changed and copied to the same position of
* output record.
*
* <b>
* It is very important to follow a conservative strategy when specifying constant fields.
* Only fields that are always constant (regardless of value, stub call, etc.) to the output may be
* inserted! Otherwise, the correct execution of a program can not be guaranteed.
* So if in doubt, do not add a field to this set.
* </b>
*
* This annotation is mutually exclusive with the {@link ConstantFieldsFirst} annotation.
*
* If this annotation and the {@link ConstantFieldsFirst} annotation is not set, it is
* assumed that <i>no</i> field is constant.
*
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ConstantFieldsFirstExcept {
int[] value();
}
/**
* Specifies the fields of an input record of the second input that are changed in
* the output of a stub with two inputs ( {@link CrossFunction}, {@link JoinFunction}, {@link CoGroupFunction})
* All other fields are assumed to be constant.
*
* A field is considered to be constant if its value is not changed and copied to the same position of
* output record.
*
* <b>
* It is very important to follow a conservative strategy when specifying constant fields.
* Only fields that are always constant (regardless of value, stub call, etc.) to the output may be
* inserted! Otherwise, the correct execution of a program can not be guaranteed.
* So if in doubt, do not add a field to this set.
* </b>
*
* This annotation is mutually exclusive with the {@link ConstantFieldsSecond} annotation.
*
* If this annotation and the {@link ConstantFieldsSecond} annotation is not set, it is
* assumed that <i>no</i> field is constant.
*
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ConstantFieldsSecondExcept {
int[] value();
}
/**
* Private constructor to prevent instantiation. This class is intended only as a container.
*/
private FunctionAnnotation() {}
// --------------------------------------------------------------------------------------------
// Function Annotation Handling
// --------------------------------------------------------------------------------------------
public static SingleInputSemanticProperties readSingleConstantAnnotations(UserCodeWrapper<?> udf) {
// get constantSet annotation from stub
AllFieldsConstants allConstants = udf.getUserCodeAnnotation(AllFieldsConstants.class);
ConstantFields constantSet = udf.getUserCodeAnnotation(ConstantFields.class);
ConstantFieldsExcept notConstantSet = udf.getUserCodeAnnotation(ConstantFieldsExcept.class);
if (notConstantSet != null && (constantSet != null || allConstants != null)) {
throw new RuntimeException("Either ConstantFields or ConstantFieldsExcept can be specified, not both.");
}
// extract notConstantSet from annotation
if (notConstantSet != null) {
FieldSet nonConstant = new FieldSet(notConstantSet.value());
return new ImplicitlyForwardingSingleInputSemanticProperties(nonConstant);
}
// extract notConstantSet from annotation
if (allConstants != null) {
FieldSet nonConstant = new FieldSet();
return new ImplicitlyForwardingSingleInputSemanticProperties(nonConstant);
}
SingleInputSemanticProperties semanticProperties = new SingleInputSemanticProperties();
// extract constantSet from annotation
if (constantSet != null) {
for (int value: constantSet.value()) {
semanticProperties.addForwardedField(value,value);
}
}
return semanticProperties;
}
// --------------------------------------------------------------------------------------------
public static DualInputSemanticProperties readDualConstantAnnotations(UserCodeWrapper<?> udf) {
ImplicitlyForwardingTwoInputSemanticProperties semanticProperties = new ImplicitlyForwardingTwoInputSemanticProperties();
// get readSet annotation from stub
ConstantFieldsFirst constantSet1Annotation = udf.getUserCodeAnnotation(ConstantFieldsFirst.class);
ConstantFieldsSecond constantSet2Annotation = udf.getUserCodeAnnotation(ConstantFieldsSecond.class);
// get readSet annotation from stub
ConstantFieldsFirstExcept notConstantSet1Annotation = udf.getUserCodeAnnotation(ConstantFieldsFirstExcept.class);
ConstantFieldsSecondExcept notConstantSet2Annotation = udf.getUserCodeAnnotation(ConstantFieldsSecondExcept.class);
if (notConstantSet1Annotation != null && constantSet1Annotation != null) {
throw new RuntimeException("Either ConstantFieldsFirst or ConstantFieldsFirstExcept can be specified, not both.");
}
if (constantSet2Annotation != null && notConstantSet2Annotation != null) {
throw new RuntimeException("Either ConstantFieldsSecond or ConstantFieldsSecondExcept can be specified, not both.");
}
// extract readSets from annotations
if(notConstantSet1Annotation != null) {
semanticProperties.setImplicitlyForwardingFirstExcept(new FieldSet(notConstantSet1Annotation.value()));
}
if(notConstantSet2Annotation != null) {
semanticProperties.setImplicitlyForwardingSecondExcept(new FieldSet(notConstantSet2Annotation.value()));
}
// extract readSets from annotations
if (constantSet1Annotation != null) {
for(int value: constantSet1Annotation.value()) {
semanticProperties.addForwardedField1(value, value);
}
}
if (constantSet2Annotation != null) {
for(int value: constantSet2Annotation.value()) {
semanticProperties.addForwardedField2(value, value);
}
}
return semanticProperties;
}
private static final class ImplicitlyForwardingSingleInputSemanticProperties extends SingleInputSemanticProperties {
private static final long serialVersionUID = 1L;
private FieldSet nonForwardedFields;
private ImplicitlyForwardingSingleInputSemanticProperties(FieldSet nonForwardedFields) {
this.nonForwardedFields = nonForwardedFields;
addWrittenFields(nonForwardedFields);
}
/**
* Returns the logical position where the given field is written to.
* In this variant of the semantic properties, all fields are assumed implicitly forwarded,
* unless stated otherwise. We return the same field position, unless the field is explicitly
* marked as modified.
*/
@Override
public FieldSet getForwardedField(int sourceField) {
if (this.nonForwardedFields.contains(sourceField)) {
return null;
} else {
return new FieldSet(sourceField);
}
}
@Override
public void addForwardedField(int sourceField, int destinationField) {
throw new UnsupportedOperationException("When defining fields as implicitly constant " +
"(such as through the ConstantFieldsExcept annotation), you cannot manually add forwarded fields.");
}
@Override
public void addForwardedField(int sourceField, FieldSet destinationFields) {
throw new UnsupportedOperationException("When defining fields as implicitly constant " +
"(such as through the ConstantFieldsExcept annotation), you cannot manually add forwarded fields.");
}
@Override
public void setForwardedField(int sourceField, FieldSet destinationFields) {
throw new UnsupportedOperationException("When defining fields as implicitly constant " +
"(such as through the ConstantFieldsExcept annotation), you cannot manually add forwarded fields.");
}
}
private static final class ImplicitlyForwardingTwoInputSemanticProperties extends DualInputSemanticProperties {
private static final long serialVersionUID = 1L;
private FieldSet nonForwardedFields1;
private FieldSet nonForwardedFields2;
private ImplicitlyForwardingTwoInputSemanticProperties() {}
public void setImplicitlyForwardingFirstExcept(FieldSet nonForwardedFields) {
this.nonForwardedFields1 = nonForwardedFields;
}
public void setImplicitlyForwardingSecondExcept(FieldSet nonForwardedFields) {
this.nonForwardedFields2 = nonForwardedFields;
}
@Override
public FieldSet getForwardedField1(int sourceField) {
if (this.nonForwardedFields1 == null) {
return super.getForwardedField1(sourceField);
}
else {
if (this.nonForwardedFields1.contains(sourceField)) {
return null;
} else {
return new FieldSet(sourceField);
}
}
}
@Override
public FieldSet getForwardedField2(int sourceField) {
if (this.nonForwardedFields2 == null) {
return super.getForwardedField2(sourceField);
}
else {
if (this.nonForwardedFields2.contains(sourceField)) {
return null;
} else {
return new FieldSet(sourceField);
}
}
}
@Override
public void addForwardedField1(int sourceField, int destinationField) {
if (this.nonForwardedFields1 == null) {
super.addForwardedField1(sourceField, destinationField);
}
else {
throw new UnsupportedOperationException("When defining fields as implicitly constant for an input" +
"(such as through the ConstantFieldsFirstExcept annotation), you cannot manually add forwarded fields.");
}
}
@Override
public void addForwardedField1(int sourceField, FieldSet destinationFields) {
if (this.nonForwardedFields1 == null) {
super.addForwardedField1(sourceField, destinationFields);
}
else {
throw new UnsupportedOperationException("When defining fields as implicitly constant for an input" +
"(such as through the ConstantFieldsFirstExcept annotation), you cannot manually add forwarded fields.");
}
}
@Override
public void setForwardedField1(int sourceField, FieldSet destinationFields) {
if (this.nonForwardedFields1 == null) {
super.addForwardedField1(sourceField, destinationFields);
}
else {
throw new UnsupportedOperationException("When defining fields as implicitly constant for an input" +
"(such as through the ConstantFieldsFirstExcept annotation), you cannot manually add forwarded fields.");
}
}
@Override
public void addForwardedField2(int sourceField, int destinationField) {
if (this.nonForwardedFields2 == null) {
super.addForwardedField2(sourceField, destinationField);
}
else {
throw new UnsupportedOperationException("When defining fields as implicitly constant for an input" +
"(such as through the ConstantFieldsSecondExcept annotation), you cannot manually add forwarded fields.");
}
}
@Override
public void addForwardedField2(int sourceField, FieldSet destinationFields) {
if (this.nonForwardedFields2 == null) {
super.addForwardedField2(sourceField, destinationFields);
}
else {
throw new UnsupportedOperationException("When defining fields as implicitly constant for an input" +
"(such as through the ConstantFieldsSecondExcept annotation), you cannot manually add forwarded fields.");
}
}
@Override
public void setForwardedField2(int sourceField, FieldSet destinationFields) {
if (this.nonForwardedFields2 == null) {
super.addForwardedField2(sourceField, destinationFields);
}
else {
throw new UnsupportedOperationException("When defining fields as implicitly constant for an input" +
"(such as through the ConstantFieldsSecondExcept annotation), you cannot manually add forwarded fields.");
}
}
}
}