/*
Copyright (c) Microsoft Corporation
All rights reserved.
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
THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER
EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF
TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions and
limitations under the License.
*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Diagnostics;
namespace Microsoft.Research.DryadLinq
{
[AttributeUsage(AttributeTargets.Field|AttributeTargets.Property|AttributeTargets.Class|AttributeTargets.Method, AllowMultiple = false)]
public sealed class NullableAttribute : Attribute
{
private bool m_canBeNull;
public NullableAttribute(bool canBeNull)
{
this.m_canBeNull = canBeNull;
}
public bool CanBeNull
{
get { return this.m_canBeNull; }
}
}
[AttributeUsage(AttributeTargets.Method|AttributeTargets.Constructor, AllowMultiple = true)]
public sealed class FieldMappingAttribute : Attribute
{
private string m_source;
private string m_destination;
public FieldMappingAttribute(string src, string dest)
{
this.m_source = src;
this.m_destination = dest;
}
public string Source
{
get { return this.m_source; }
}
public string Destination
{
get { return this.m_destination; }
}
}
[AttributeUsage(AttributeTargets.Class|AttributeTargets.Interface, AllowMultiple = false)]
internal sealed class AutoTypeInferenceAttribute : Attribute
{
public AutoTypeInferenceAttribute()
{
}
}
[AttributeUsage(AttributeTargets.Method|AttributeTargets.Constructor, AllowMultiple = false)]
internal sealed class DistinctAttribute : Attribute
{
private bool m_mustBeDistinct;
private string m_comparer;
public DistinctAttribute()
{
this.m_mustBeDistinct = false;
this.m_comparer = null;
}
public DistinctAttribute(string comparer)
{
this.m_mustBeDistinct = false;
this.m_comparer = comparer;
}
public bool MustBeDistinct
{
get { return this.m_mustBeDistinct; }
set { this.m_mustBeDistinct = value; }
}
public object Comparer
{
get {
if (this.m_comparer == null) return null;
object val = TypeSystem.GetFieldValue(this.m_comparer);
if (val == null)
{
throw new DryadLinqException(DryadLinqErrorCode.DistinctAttributeComparerNotDefined,
String.Format(SR.DistinctAttributeComparerNotDefined, this.m_comparer));
}
return val;
}
}
}
///
/// The Resource attribute is used to specify the computation cost of a function.
/// IsStateful asserts that the function is stateful; IsExpensive asserts that
/// the function is expensive to compute. The information is useful in generating
/// better execution plan. For example, expensive associative aggregation
/// functions can use multiple aggregation layers.
///
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public sealed class ResourceAttribute : Attribute
{
private bool m_isStateful;
private bool m_isExpensive;
public ResourceAttribute()
{
this.m_isStateful = true;
this.m_isExpensive = false;
}
public bool IsStateful
{
get { return this.m_isStateful; }
set { this.m_isStateful = value; }
}
public bool IsExpensive
{
get { return this.m_isExpensive; }
set { this.m_isExpensive = value; }
}
}
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public sealed class DecomposableAttribute : Attribute
{
Type m_decompositionType;
public DecomposableAttribute(Type decompositionType)
{
m_decompositionType = decompositionType;
}
public Type DecompositionType
{
get {
return m_decompositionType;
}
}
}
///
/// Indicates that a method can be used as an associative aggregation method.
/// The aggregation can either be via recursive calls to the tagged method, or
/// via top-level calls to the tagged method, followed by recursive calls to
/// a RecursiveAccumulate method.
///
///
/// If a recursive accumulator method is necessary, create type that implements
/// IAssociative and provide that to the ctor of this type.
///
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public sealed class AssociativeAttribute : Attribute
{
private Type m_associativeType;
///
/// Creates an instance of AssociativeAttribute
///
public AssociativeAttribute()
{
}
///
/// Creates an instance of AssociativeAttribute, with an associated type that provides
/// a recursive-accumulator method.
///
///
/// During aggregation, the recursiveAccumulator will be used to aggregate items arising
/// from the main aggregation.
///
/// A type that implements IAssociative{T,T} where T
/// is the output type of methods that are decorated with this attribute.
public AssociativeAttribute(Type associativeType)
{
this.m_associativeType = associativeType;
}
///
/// Type that implements IAssociative{T,T} where T is the output type of methods
/// that are decorated with this attribute.
///
public Type AssociativeType
{
get { return this.m_associativeType; }
}
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false, Inherited=false)]
public sealed class CustomDryadLinqSerializerAttribute : Attribute
{
public CustomDryadLinqSerializerAttribute(Type serializerType)
{
SerializerType = serializerType;
// We need to make sure serializerType implements IDryadLinqSerializer
// However we will defer that check until DryadCodeGen.FindCustomSerializerType(), because
// 1) we don't have access to here but it's available at code gen time, and
// 2) because an exception coming from the attribute ctor leads to an obscure failure.
}
public Type SerializerType { get; private set; }
}
internal static class AttributeSystem
{
private static Dictionary attribMap = new Dictionary();
internal static void Add(LambdaExpression func, Attribute attrib)
{
Attribute[] attribs;
if (attribMap.TryGetValue(func, out attribs))
{
Attribute[] oldAttribs = attribs;
attribs = new Attribute[oldAttribs.Length+1];
Array.Copy(oldAttribs, attribs, oldAttribs.Length);
attribs[oldAttribs.Length] = attrib;
attribMap.Remove(func);
}
else
{
attribs = new Attribute[] { attrib };
}
attribMap[func] = attribs;
}
private static Attribute[] Get(LambdaExpression func, Type attribType)
{
Attribute[] attribs;
attribMap.TryGetValue(func, out attribs);
if (attribs != null)
{
ArrayList alist = new ArrayList();
foreach (var x in attribs)
{
if (x.GetType() == attribType)
{
alist.Add(x);
}
}
attribs = (Attribute[])alist.ToArray(attribType);
}
return attribs;
}
internal static Attribute[] GetAttribs(LambdaExpression func, Type attribType)
{
Attribute[] attribs1 = AttributeSystem.Get(func, attribType);
Attribute[] attribs2 = null;
if (func.Body is MethodCallExpression)
{
MethodCallExpression expr = (MethodCallExpression)func.Body;
attribs2 = Attribute.GetCustomAttributes(expr.Method, attribType);
}
else if (func.Body is NewExpression && ((NewExpression)func.Body).Constructor != null)
{
NewExpression expr = (NewExpression)func.Body;
attribs2 = Attribute.GetCustomAttributes(expr.Constructor, attribType);
}
else if (func.Body is BinaryExpression)
{
BinaryExpression expr = (BinaryExpression)func.Body;
if (expr.Method != null)
{
attribs2 = Attribute.GetCustomAttributes(expr.Method, attribType);
}
}
else if (func.Body is InvocationExpression)
{
InvocationExpression expr = (InvocationExpression)func.Body;
if (expr.Expression is LambdaExpression)
{
attribs2 = GetAttribs((LambdaExpression)expr.Expression, attribType);
}
}
if (attribs1 == null)
{
return attribs2;
}
if (attribs2 == null)
{
return attribs1;
}
ArrayList alist = new ArrayList();
foreach (var x in attribs1)
{
alist.Add(x);
}
foreach (var x in attribs2)
{
alist.Add(x);
}
Attribute[] attribs = (Attribute[])alist.ToArray(attribType);
return attribs;
}
internal static Attribute GetAttrib(Expression expr, Type attribType)
{
Attribute[] attribs = null;
if (expr is MethodCallExpression)
{
attribs = Attribute.GetCustomAttributes(((MethodCallExpression)expr).Method, attribType);
}
else if (expr is NewExpression && ((NewExpression) expr).Constructor != null)
{
attribs = Attribute.GetCustomAttributes(((NewExpression)expr).Constructor, attribType);
}
else if (expr is LambdaExpression)
{
attribs = GetAttribs((LambdaExpression)expr, attribType);
}
if (attribs == null || attribs.Length == 0) return null;
return attribs[0];
}
internal static DecomposableAttribute GetDecomposableAttrib(Expression expr)
{
return (DecomposableAttribute)GetAttrib(expr, typeof(DecomposableAttribute));
}
internal static AssociativeAttribute GetAssociativeAttrib(Expression expr)
{
return (AssociativeAttribute)GetAttrib(expr, typeof(AssociativeAttribute));
}
internal static ResourceAttribute GetResourceAttrib(LambdaExpression func)
{
return (ResourceAttribute)GetAttrib(func, typeof(ResourceAttribute));
}
internal static FieldMappingAttribute[] GetFieldMappingAttribs(LambdaExpression func)
{
Attribute[] a = GetAttribs(func, typeof(FieldMappingAttribute));
if (a == null || a.Length == 0) return null;
return (FieldMappingAttribute[])a;
}
internal static bool DoAutoTypeInference(DryadLinqContext context, Type type)
{
if (!StaticConfig.AllowAutoTypeInference) return false;
object[] a = type.GetCustomAttributes(typeof(AutoTypeInferenceAttribute), true);
return (a.Length != 0);
}
internal static DistinctAttribute GetDistinctAttrib(LambdaExpression func)
{
return (DistinctAttribute)GetAttrib(func, typeof(DistinctAttribute));
}
internal static bool FieldCanBeNull(FieldInfo finfo)
{
if (finfo == null || finfo.FieldType.IsValueType) return false;
object[] attribs = finfo.GetCustomAttributes(typeof(NullableAttribute), true);
if (attribs.Length == 0)
{
return StaticConfig.AllowNullFields;
}
return ((NullableAttribute)attribs[0]).CanBeNull;
}
internal static bool RecordCanBeNull(DryadLinqContext context, Type type)
{
if (type == null || type.IsValueType) return false;
object[] attribs = type.GetCustomAttributes(typeof(NullableAttribute), true);
if (attribs.Length == 0)
{
return StaticConfig.AllowNullRecords;
}
return ((NullableAttribute)attribs[0]).CanBeNull;
}
}
}