http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/c1b5200f/lang/cs/Org.Apache.REEF.Tang/Implementations/ClassHierarchy/AbstractNode.cs ---------------------------------------------------------------------- diff --git a/lang/cs/Org.Apache.REEF.Tang/Implementations/ClassHierarchy/AbstractNode.cs b/lang/cs/Org.Apache.REEF.Tang/Implementations/ClassHierarchy/AbstractNode.cs new file mode 100644 index 0000000..626a4a0 --- /dev/null +++ b/lang/cs/Org.Apache.REEF.Tang/Implementations/ClassHierarchy/AbstractNode.cs @@ -0,0 +1,131 @@ +/** + * 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. + */ +using System; +using System.Collections.Generic; +using Org.Apache.REEF.Tang.Types; +using Org.Apache.REEF.Tang.Util; + +namespace Org.Apache.REEF.Tang.Implementations.ClassHierarchy +{ + public class AbstractNode : INode + { + /// It is from Type.FullName. This name is used as Name in a Node. + /// It is not unique for a generic type with different type of arguments. + /// It is used for toString or debug info as AssemblyQualifiedName is really long + private String name; + + /// It is from Type.AssemblyQualifiedName. THis name is used as full name in a Node + /// It is unique for a generic type with different type of arguments. + private String fullName; //it comes from + + //parent node in the class hierarchy + private INode parent; + + //children in the class hierarchy + protected IDictionary<String, INode> children = new MonotonicTreeMap<string, INode>(); + + public AbstractNode(INode parent, String name, String fullName) + { + this.parent = parent; + this.name = name; + this.fullName = fullName; + if (parent != null) + { + parent.Add(this); + } + } + + public ICollection<INode> GetChildren() + { + return children.Values; + } + + public bool Contains(String key) + { + return children.ContainsKey(key); + } + + public INode Get(String key) + { + INode val; + if (children.TryGetValue(key, out val)) + { + return val; + } + return null; + } + + public virtual void Add(INode n) + { + children.Add(n.GetFullName(), n); + } + + public string GetFullName() + { + return fullName; + } + + public string GetName() + { + return name; + } + + public INode GetParent() + { + return parent; + } + + public override bool Equals(Object o) + { + if(o == null) return false; + if(o == this) return true; + + AbstractNode n = (AbstractNode) o; + bool parentsEqual; + if (n.parent == this.parent) { + parentsEqual = true; + } else if (n.parent == null) { + parentsEqual = false; + } else if (this.parent == null) { + parentsEqual = false; + } else { + parentsEqual = n.parent.Equals(this.parent); + } + if (!parentsEqual) { + return false; + } + return fullName.Equals(n.fullName); + } + + public override int GetHashCode() + { + return fullName.GetHashCode(); + } + + public override String ToString() + { + return "[" + this.GetType().FullName + " '" + fullName + "']"; + } + + public int CompareTo(INode n) + { + return fullName.CompareTo(n.GetFullName()); + } + } +} \ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/c1b5200f/lang/cs/Org.Apache.REEF.Tang/Implementations/ClassHierarchy/ClassHierarchyImpl.cs ---------------------------------------------------------------------- diff --git a/lang/cs/Org.Apache.REEF.Tang/Implementations/ClassHierarchy/ClassHierarchyImpl.cs b/lang/cs/Org.Apache.REEF.Tang/Implementations/ClassHierarchy/ClassHierarchyImpl.cs new file mode 100644 index 0000000..1f2fe82 --- /dev/null +++ b/lang/cs/Org.Apache.REEF.Tang/Implementations/ClassHierarchy/ClassHierarchyImpl.cs @@ -0,0 +1,526 @@ +/** + * 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. + */ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using Org.Apache.REEF.Utilities.Logging; +using Org.Apache.REEF.Tang.Annotations; +using Org.Apache.REEF.Tang.Exceptions; +using Org.Apache.REEF.Tang.Interface; +using Org.Apache.REEF.Tang.Types; +using Org.Apache.REEF.Tang.Util; + +namespace Org.Apache.REEF.Tang.Implementations.ClassHierarchy +{ + public class ClassHierarchyImpl : ICsClassHierarchy + { + private static readonly Logger LOGGER = Logger.GetLogger(typeof (ClassHierarchyImpl)); + private INode rootNode; + private MonotonicTreeMap<String, INamedParameterNode> shortNames = new MonotonicTreeMap<String, INamedParameterNode>(); + private IList<string> assemblies; + private AssemblyLoader loader = null; + + public ParameterParser Parameterparser = new ParameterParser(); + + public ClassHierarchyImpl(String file) : this(new string[] { file }, new Type[0]) + { + } + + public ClassHierarchyImpl(string[] assemblies) : this(assemblies, new Type[0]) + { + } + + //parameterParsers are classes that extends from IExternalConstructor + public ClassHierarchyImpl(string[] assemblies, Type[] parameterParsers) + { + this.assemblies = assemblies; + rootNode = NodeFactory.CreateRootPackageNode(); + loader = new AssemblyLoader(assemblies); + + foreach (Type p in parameterParsers) //p must be extend from IExternalConstructor + { + try + { + Parameterparser.AddParser(p); + } + catch (BindException e) + { + Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Caught(e, Level.Error, LOGGER); + Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(new ArgumentException("Could not register parameter parsers", e), LOGGER); + } + } + + foreach (var a in loader.Assemblies) + { + foreach (var t in a.GetTypes()) + { + RegisterType(t); + } + } + } + + public INode RegisterType(string assemblyQualifiedName) + { + Type type = this.loader.GetType(assemblyQualifiedName); + if (type != null) + { + return RegisterType(type); + } + return null; + } + + public INode RegisterType(Type type) + { + if (ReflectionUtilities.IsAnnonymousType(type)) + { + // DevNote: Kinda hacky way to indicate the no-op case. + return rootNode; + } + + INode n = GetAlreadyBoundNode(type); + if (n != null) + { + return n; + } + + if (type.BaseType != null) + { + RegisterType(type.BaseType); + } + + foreach (Type interf in type.GetInterfaces()) + { + RegisterType(ReflectionUtilities.EnsureInterfaceType(interf)); + } + + Type enclosingClass = type.DeclaringType; // this.GetEnclosingClass(type); + if (enclosingClass != null) + { + RegisterType(enclosingClass); + } + + INode node = RegisterClass(type); + + foreach (Type inner in type.GetNestedTypes()) + { + RegisterType(inner); + } + + IClassNode classNode = node as ClassNodeImpl; + if (classNode != null) + { + foreach (IConstructorDef constructorDef in classNode.GetInjectableConstructors()) + { + foreach (IConstructorArg constructorArg in constructorDef.GetArgs()) + { + if (constructorArg.Gettype() == null) + { + Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(new ArgumentException("not type in arg"), LOGGER); + } + RegisterType(constructorArg.Gettype()); //Gettype returns param's Type.fullname + if (constructorArg.GetNamedParameterName() != null) + { + INamedParameterNode np = (INamedParameterNode)RegisterType(constructorArg.GetNamedParameterName()); + try + { + if (np.IsSet() || np.IsList()) + { + //throw new NotImplementedException(); + } + else + { + if (!ReflectionUtilities.IsCoercable(ClassForName(constructorArg.Gettype()), ClassForName(np.GetFullArgName()))) + { + var e = new ClassHierarchyException( + "Named parameter type mismatch in " + classNode.GetFullName() + ". Constructor expects a " + + constructorArg.Gettype() + " but " + np.GetName() + " is a " + + np.GetFullArgName()); + Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(e, LOGGER); + } + } + } + catch (TypeLoadException e) + { + Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Caught(e, Level.Error, LOGGER); + var ex = new ClassHierarchyException("Constructor refers to unknown class " + + constructorArg.GetType(), e); + Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(ex, LOGGER); + } + } + } + } + } + else + { + INamedParameterNode npNode = node as INamedParameterNode; + if (npNode != null) + { + RegisterType(npNode.GetFullArgName()); + } + } + + return node; + } + + private INode RegisterClass(Type type) + { + INode node = GetAlreadyBoundNode(type); + if (node != null) + { + return node; + } + + node = BuildPathToNode(type); + IClassNode classNode = node as IClassNode; + if (classNode != null) + { + Type baseType = type.BaseType; + if (baseType != null) + { + IClassNode n = (IClassNode) GetAlreadyBoundNode(baseType); + if (n != null) + { + n.PutImpl(classNode); + } + else + { + Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(new IllegalStateException("Error in finding Node for BaseType"), LOGGER); + } + } + + foreach (Type interf in ReflectionUtilities.GetInterfaces(type, false)) + { + IClassNode n = (IClassNode)GetAlreadyBoundNode(ReflectionUtilities.EnsureInterfaceType(interf)); + if (n != null) + { + n.PutImpl(classNode); + } + else + { + Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(new IllegalStateException("Error in finding Node for Interface"), LOGGER); + } + } + } + return node; + } + + public INode BuildPathToNode(Type type) + { + INode parent = GetParentNode(type); + + Type argType = ReflectionUtilities.GetNamedParameterTargetOrNull(type); + + if (argType == null) + { + return NodeFactory.CreateClassNode(parent, type); + } + INamedParameterNode np = NodeFactory.CreateNamedParameterNode(parent, type, argType); + + if(Parameterparser.CanParse(ReflectionUtilities.GetAssemblyQualifiedName(argType))) { + if(type.GetCustomAttribute<NamedParameterAttribute>().DefaultClass != null) + { + var e = new ClassHierarchyException("Named parameter " + ReflectionUtilities.GetAssemblyQualifiedName(type) + " defines default implementation for parsable type " + ReflectionUtilities.GetAssemblyQualifiedName(argType)); + Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(e, LOGGER); + } + } + + string shortName = np.GetShortName(); + if (shortName != null && !shortName.Equals("")) + { + INamedParameterNode oldNode = null; + shortNames.TryGetValue(shortName, out oldNode); + if (oldNode != null) + { + if (oldNode.GetFullName().Equals(np.GetFullName())) + { + var ex = new IllegalStateException("Tried to double bind " + + oldNode.GetFullName() + " to short name " + shortName); + Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(ex, LOGGER); + } + var e = new ClassHierarchyException("Named parameters " + oldNode.GetFullName() + + " and " + np.GetFullName() + " have the same short name: " + + shortName); + Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(e, LOGGER); + } + shortNames.Add(shortName, np); + + } + return np; + } + + //return Type T if type implements Name<T>, null otherwise + //e.g. [NamedParameter(typeof(System.String), "Number of seconds to sleep", "10", "sec")] + //class Seconds : Name<Int32> { } + //return Int32 + + //TODO add error handlings + public Type GetNamedParameterTargetOrNull(Type type) + { + var npAnnotation = type.GetCustomAttribute<NamedParameterAttribute>(); + if (npAnnotation != null) + { + Type[] intfs = type.GetInterfaces(); + if (intfs.Length == 1) + { + if (intfs[0].Name.Equals(GetNameOfNameInterface())) + { + Type[] args = intfs[0].GetGenericArguments(); + if (args.Length == 1) + { + return args[0]; + } + } + } + + } + return null; + } + + private INode GetAlreadyBoundNode(Type t) + { + //get outclass names including itsself + string[] outerClassNames = ReflectionUtilities.GetEnclosingClassNames(t); + + INode current = rootNode; + for (int i = 0; i < outerClassNames.Length; i++) + { + current = current.Get(outerClassNames[i]); + if (current == null) + { + StringBuilder sb = new StringBuilder(); + for (int j = 0; j <= i; j++) + { + sb.Append(outerClassNames[j]); + if (j != i) + { + sb.Append("."); + } + } + return null; + //throw new NameResolutionException(t.FullName, sb.ToString()); + } + + } + return current; + } + + //starting from the root, get child for each eclosing class excluding the type itsself + //all enclosing classes should be already in the hierarchy + //Type B2 = asm.GetType(@"Org.Apache.REEF.Tang.Examples.B+B1+B2"); + //string[] pathB2 = ClassNameParser.GetEnclosingClassShortNames(B2); + //Assert.AreEqual(pathB2[0], "B"); + //Assert.AreEqual(pathB2[1], "B1"); + //Assert.AreEqual(pathB2[2], "B2"); + //return INode for B1 + private INode GetParentNode(Type type) + { + INode current = rootNode; + string[] enclosingPath = ReflectionUtilities.GetEnclosingClassNames(type); + for (int i = 0; i < enclosingPath.Length - 1; i++) + { + current = current.Get(enclosingPath[i]); + } + return current; + } + + private string GetNameOfNameInterface() + { + var tn = typeof(Name<int>); + return tn.Name; + } + + public INode GetNode(string fullName) + { + Type t = loader.GetType(fullName); + + if (t == null) + { + Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(new NameResolutionException(fullName, fullName), LOGGER); + } + + return this.GetNode(t); + } + + public INode GetNode(Type type) + { + this.RegisterType(type); + return GetAlreadyBoundNode(type); + } + + public INode GetNamespace() + { + return rootNode; + } + + public bool IsImplementation(IClassNode inter, IClassNode impl) + { + return impl.IsImplementationOf(inter); + } + + public IClassHierarchy Merge(IClassHierarchy ch) + { + if (this == ch) { return this; } + + if (!(ch is ClassHierarchyImpl)) + { + Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(new NotSupportedException("Can't merge java and non-java class hierarchies yet!"), LOGGER); + } + + if(this.assemblies.Count == 0) + { + return ch; + } + + ClassHierarchyImpl chi = (ClassHierarchyImpl)ch; + MonotonicHashSet<string> otherJars = new MonotonicHashSet<string>(); + otherJars.AddAll(chi.assemblies); + MonotonicHashSet<string> myJars = new MonotonicHashSet<string>(); + myJars.AddAll(this.assemblies); + if(myJars.ContainsAll(otherJars)) + { + return this; + } + if (otherJars.ContainsAll(myJars)) + { + return ch; + } + myJars.AddAll(otherJars); + return new ClassHierarchyImpl(myJars.ToArray()); + } + + public object Parse(INamedParameterNode np, string value) + { + IClassNode iface = null; + try + { + iface = (IClassNode)GetNode(np.GetFullArgName()); + } + catch(NameResolutionException e) + { + Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Caught(e, Level.Error, LOGGER); + var ex = new IllegalStateException("Could not parse validated named parameter argument type. NamedParameter is " + np.GetFullName() + " argument type is " + np.GetFullArgName(), e); + Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(ex, LOGGER); + } + Type clazz; + String fullName; + try + { + clazz = (Type)ClassForName(iface.GetFullName()); + fullName = null; + } + catch(TypeLoadException e) + { + Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Caught(e, Level.Warning, LOGGER); + clazz = null; + fullName = iface.GetFullName(); + } + + object result = null; + if (clazz != null) + { + result = Parameterparser.Parse(clazz, value); + } + else + { + result = Parameterparser.Parse(fullName, value); + } + + if (result == null) + { + try + { + INode impl = GetNode(value); + if (impl is IClassNode) + { + if (IsImplementation(iface, (IClassNode) impl)) + { + return impl; + } + } + var ex = + new ParseException( + "Name<" + iface.GetFullName() + "> " + np.GetFullName() + " cannot take non-subclass " + + impl.GetFullName()); + Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(ex, LOGGER); + } + catch (NameResolutionException ec) + { + Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Caught(ec, Level.Error, LOGGER); + var ex = + new ParseException( + "Name<" + iface.GetFullName() + "> " + np.GetFullName() + " cannot take non-class " + value, + ec); + Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(ex, LOGGER); + } + } + return result; + } + + public object ParseDefaultValue(INamedParameterNode name) + { + string[] vals = name.GetDefaultInstanceAsStrings(); + object[] ret = new Object[vals.Length]; + for (int i = 0; i < vals.Length; i++) + { + string val = vals[i]; + try + { + ret[i] = Parse(name, val); + } + catch (ParseException e) + { + Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Caught(e, Level.Error, LOGGER); + var ex = new ClassHierarchyException("Could not parse default value " + val, e); + Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(ex, LOGGER); + } + } + if (name.IsSet()) + { + return new HashSet<object>(ret.ToList<object>()); + } + if (name.IsList()) + { + return new List<object>(ret.ToList<object>()); + } + if (ret.Length == 0) + { + return null; + } + if (ret.Length == 1) + { + return ret[0]; + } + var ec = new IllegalStateException("Multiple defaults for non-set named parameter! " + name.GetFullName()); + Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(ec, LOGGER); + return null; //this line would be never reached as Throw will throw an exception + } + + public Type ClassForName(string name) + { + return this.GetType(name); + } + + public Type GetType(string name) + { + return this.loader.GetType(name); + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/c1b5200f/lang/cs/Org.Apache.REEF.Tang/Implementations/ClassHierarchy/ClassNodeImpl.cs ---------------------------------------------------------------------- diff --git a/lang/cs/Org.Apache.REEF.Tang/Implementations/ClassHierarchy/ClassNodeImpl.cs b/lang/cs/Org.Apache.REEF.Tang/Implementations/ClassHierarchy/ClassNodeImpl.cs new file mode 100644 index 0000000..5d529b2 --- /dev/null +++ b/lang/cs/Org.Apache.REEF.Tang/Implementations/ClassHierarchy/ClassNodeImpl.cs @@ -0,0 +1,157 @@ +/** + * 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. + */ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Org.Apache.REEF.Utilities.Logging; +using Org.Apache.REEF.Tang.Exceptions; +using Org.Apache.REEF.Tang.Types; +using Org.Apache.REEF.Tang.Util; + +namespace Org.Apache.REEF.Tang.Implementations.ClassHierarchy +{ + public class ClassNodeImpl : AbstractNode, IClassNode + { + private static readonly Logger LOGGER = Logger.GetLogger(typeof(ClassNodeImpl)); + + private readonly bool injectable; + private readonly bool unit; + private readonly bool externalConstructor; + private readonly IList<IConstructorDef> injectableConstructors; + private readonly IList<IConstructorDef> allConstructors; + private readonly MonotonicSet<IClassNode> knownImpls; + private readonly String defaultImpl; + + public ClassNodeImpl(INode parent, String simpleName, String fullName, + bool unit, bool injectable, bool externalConstructor, + IList<IConstructorDef> injectableConstructors, + IList<IConstructorDef> allConstructors, + String defaultImplementation) + : base(parent, simpleName, fullName) + { + + this.unit = unit; + this.injectable = injectable; + this.externalConstructor = externalConstructor; + this.injectableConstructors = injectableConstructors; + this.allConstructors = allConstructors; + this.knownImpls = new MonotonicSet<IClassNode>(); + this.defaultImpl = defaultImplementation; + } + + public IList<IConstructorDef> GetInjectableConstructors() + { + return injectableConstructors; + } + + public IList<IConstructorDef> GetAllConstructors() + { + return allConstructors; + } + + public bool IsInjectionCandidate() + { + return injectable; + } + + public bool IsExternalConstructor() + { + return externalConstructor; + } + + public override String ToString() + { + StringBuilder sb = new StringBuilder(base.ToString() + ": "); + if (GetInjectableConstructors() != null) + { + foreach (IConstructorDef c in GetInjectableConstructors()) + { + sb.Append(c.ToString() + ", "); + } + } + else + { + sb.Append("OBJECT BUILD IN PROGRESS! BAD NEWS!"); + } + return sb.ToString(); + } + + public IConstructorDef GetConstructorDef(IList<IClassNode> paramTypes) + { + if (!IsInjectionCandidate()) + { + var e = new BindException("Cannot @Inject non-static member/local class: " + + GetFullName()); + Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(e, LOGGER); + } + foreach (IConstructorDef c in GetAllConstructors()) + { + if (c.TakesParameters(paramTypes)) + { + return c; + } + } + Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(new BindException("Could not find requested constructor for class " + GetFullName()), LOGGER); + return null; // this line would not be reached as Thrwo throws an exception + } + + public void PutImpl(IClassNode impl) + { + knownImpls.Add(impl); + } + + public ISet<IClassNode> GetKnownImplementations() + { + return new MonotonicSet<IClassNode>(knownImpls); + } + + public bool IsUnit() + { + return unit; + } + + public bool IsImplementationOf(IClassNode inter) + { + List<IClassNode> worklist = new List<IClassNode>(); + if (this.Equals(inter)) + { + return true; + } + worklist.Add(inter); + while (worklist.Count != 0) + { + IClassNode cn = worklist[worklist.Count - 1]; + worklist.RemoveAt(worklist.Count - 1); + ISet<IClassNode> impls = cn.GetKnownImplementations(); + if (impls.Contains(this)) + { + return true; + } + worklist.AddRange(impls); + } + return false; + } + + public String GetDefaultImplementation() + { + return defaultImpl; + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/c1b5200f/lang/cs/Org.Apache.REEF.Tang/Implementations/ClassHierarchy/ConstructorArgImpl.cs ---------------------------------------------------------------------- diff --git a/lang/cs/Org.Apache.REEF.Tang/Implementations/ClassHierarchy/ConstructorArgImpl.cs b/lang/cs/Org.Apache.REEF.Tang/Implementations/ClassHierarchy/ConstructorArgImpl.cs new file mode 100644 index 0000000..f1083f6 --- /dev/null +++ b/lang/cs/Org.Apache.REEF.Tang/Implementations/ClassHierarchy/ConstructorArgImpl.cs @@ -0,0 +1,97 @@ +/** + * 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. + */ +using System; +using Org.Apache.REEF.Tang.Types; + +namespace Org.Apache.REEF.Tang.Implementations.ClassHierarchy +{ + public class ConstructorArgImpl : IConstructorArg + { + private readonly String type; + private readonly String name; + private readonly bool isInjectionFuture; + + public ConstructorArgImpl(String type, String namedParameterName, bool isInjectionFuture) + { + if (type == null) + { + ; + } + this.type = type; + this.name = namedParameterName; + + //if (name != null && name.Contains(',')) + // throw new ApplicationException("Name contains comma : " + name); + this.isInjectionFuture = isInjectionFuture; + } + + public string GetName() + { + return name == null ? type : name; + } + + public string GetNamedParameterName() + { + return name; + } + + public string Gettype() + { + return type; + } + + public bool IsInjectionFuture() + { + return isInjectionFuture; + } + + public override String ToString() + { + return name == null ? type : type + " " + name; + } + + public override int GetHashCode() + { + return 0; + } + + public override bool Equals(Object o) + { + ConstructorArgImpl arg = (ConstructorArgImpl)o; + if (!type.Equals(arg.type)) + { + return false; + } + if (name == null && arg.name == null) + { + return true; + } + if (name == null && arg.name != null) + { + return false; + } + if (name != null && arg.name == null) + { + return false; + } + return name.Equals(arg.name); + + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/c1b5200f/lang/cs/Org.Apache.REEF.Tang/Implementations/ClassHierarchy/ConstructorDefImpl.cs ---------------------------------------------------------------------- diff --git a/lang/cs/Org.Apache.REEF.Tang/Implementations/ClassHierarchy/ConstructorDefImpl.cs b/lang/cs/Org.Apache.REEF.Tang/Implementations/ClassHierarchy/ConstructorDefImpl.cs new file mode 100644 index 0000000..624189d --- /dev/null +++ b/lang/cs/Org.Apache.REEF.Tang/Implementations/ClassHierarchy/ConstructorDefImpl.cs @@ -0,0 +1,203 @@ +/** + * 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. + */ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Org.Apache.REEF.Utilities.Logging; +using Org.Apache.REEF.Tang.Exceptions; +using Org.Apache.REEF.Tang.Types; + +namespace Org.Apache.REEF.Tang.Implementations.ClassHierarchy +{ + public class ConstructorDefImpl : IConstructorDef + { + private static readonly Logger LOGGER = Logger.GetLogger(typeof(ConstructorDefImpl)); + + private readonly IList<IConstructorArg> args = new List<IConstructorArg>(); + private readonly String className; + + public ConstructorDefImpl(String className, IConstructorArg[] args, bool injectable) + { + this.args = args; + this.className = className; + if (injectable) + { + var duplicateItems = from x in args + group x by x into grouped + where grouped.Count() > 1 + select grouped.Key; + + if (duplicateItems.Any()) + { + var e = new ClassHierarchyException( + "Repeated constructor parameter detected. " + + "Cannot inject constructor " + ToString()); + Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(e, LOGGER); + } + } + } + + public IList<IConstructorArg> GetArgs() + { + return args; + } + + public String GetClassName() + { + return className; + } + + private String Join(String sep, Object[] vals) + { + if (vals.Length != 0) + { + StringBuilder sb = new StringBuilder(vals[0].ToString()); + for (int i = 1; i < vals.Length; i++) + { + sb.Append(sep + vals[i]); + } + return sb.ToString(); + } + else + { + return ""; + } + } + + public override String ToString() + { + StringBuilder sb = new StringBuilder(className); + sb.Append("("); + sb.Append(Join(",", args.ToArray())); + sb.Append(")"); + return sb.ToString(); + } + + // Return true if our list of args is a superset of those in def. + public bool IsMoreSpecificThan(IConstructorDef def) + { + // Is everything in def also in this? + for (int i = 0; i < def.GetArgs().Count; i++) + { + bool found = false; + for (int j = 0; j < this.GetArgs().Count; j++) + { + if (GetArgs()[j].Equals(def.GetArgs()[i])) + { + found = true; + break; + } + } + // If not, then argument j from def is not in our list. Return false. + if (found == false) + return false; + } + // Everything in def's arg list is in ours. Do we have at least one extra + // argument? + return GetArgs().Count > def.GetArgs().Count; + } + + public bool TakesParameters(IList<IClassNode> paramTypes) + { + if (paramTypes.Count != args.Count) + { + return false; + } + + int i = 0; + foreach (INode t in paramTypes) + { + string s; + if (t is INamedParameterNode) + { + s = ((INamedParameterNode)paramTypes[i]).GetFullArgName(); + } + else + { + s = paramTypes[i].GetFullName(); + } + if (!args[i].Gettype().Equals(s)) + { + return false; + } + else + { + i++; + } + + } + return true; + } + + public override bool Equals(Object o) + { + return EqualsIgnoreOrder((IConstructorDef)o); + } + + public override int GetHashCode() + { + return 0; + } + + //A(int i, string j) vs. A(string i, int j) is Ambiguous in injection + private bool EqualsIgnoreOrder(IConstructorDef def) + { + if (GetArgs().Count != def.GetArgs().Count) + { + return false; + } + for (int i = 0; i < GetArgs().Count; i++) + { + bool found = false; + for (int j = 0; j < def.GetArgs().Count; j++) + { + if (GetArgs()[i].GetName().Equals(def.GetArgs()[j].GetName())) + { + found = true; + } + } + if (!found) + { + return false; + } + } + return true; + } + + public int CompareTo(object obj) + { + IConstructorDef o = (IConstructorDef)obj; + return ToString().CompareTo(o.ToString()); + } + + public bool IsInList(IList<IConstructorDef> list ) + { + foreach (IConstructorDef def in list) + { + if (CompareTo(def) == 0) + { + return true; + } + + } + return false; + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/c1b5200f/lang/cs/Org.Apache.REEF.Tang/Implementations/ClassHierarchy/NamedParameterNodeImpl.cs ---------------------------------------------------------------------- diff --git a/lang/cs/Org.Apache.REEF.Tang/Implementations/ClassHierarchy/NamedParameterNodeImpl.cs b/lang/cs/Org.Apache.REEF.Tang/Implementations/ClassHierarchy/NamedParameterNodeImpl.cs new file mode 100644 index 0000000..fcfff7f --- /dev/null +++ b/lang/cs/Org.Apache.REEF.Tang/Implementations/ClassHierarchy/NamedParameterNodeImpl.cs @@ -0,0 +1,88 @@ +/** + * 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. + */ +using System; +using Org.Apache.REEF.Tang.Types; + +namespace Org.Apache.REEF.Tang.Implementations.ClassHierarchy +{ + public class NamedParameterNodeImpl : AbstractNode, INamedParameterNode + { + private readonly String fullArgName; + private readonly String simpleArgName; + private readonly String documentation; + private readonly String shortName; + private readonly String[] defaultInstanceAsStrings; + private readonly bool isSet; + private readonly bool isList; + + public NamedParameterNodeImpl(INode parent, String simpleName, + String fullName, String fullArgName, String simpleArgName, bool isSet, bool isList, + String documentation, String shortName, String[] defaultInstanceAsStrings) + : base(parent, simpleName, fullName) + { + this.fullArgName = fullArgName; + this.simpleArgName = simpleArgName; + this.isSet = isSet; + this.isList = isList; + this.documentation = documentation; + this.shortName = shortName; + this.defaultInstanceAsStrings = defaultInstanceAsStrings; + } + + public override String ToString() + { + return GetSimpleArgName() + " " + GetName(); + } + + public String GetSimpleArgName() + { + return simpleArgName; + } + + public String GetFullArgName() + { + return fullArgName; + } + + public String GetDocumentation() + { + return documentation; + } + + public String GetShortName() + { + return shortName; + } + + public String[] GetDefaultInstanceAsStrings() + { + return defaultInstanceAsStrings; + } + + public bool IsSet() + { + return isSet; + } + + public bool IsList() + { + return isList; + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/c1b5200f/lang/cs/Org.Apache.REEF.Tang/Implementations/ClassHierarchy/NodeFactory.cs ---------------------------------------------------------------------- diff --git a/lang/cs/Org.Apache.REEF.Tang/Implementations/ClassHierarchy/NodeFactory.cs b/lang/cs/Org.Apache.REEF.Tang/Implementations/ClassHierarchy/NodeFactory.cs new file mode 100644 index 0000000..2f73509 --- /dev/null +++ b/lang/cs/Org.Apache.REEF.Tang/Implementations/ClassHierarchy/NodeFactory.cs @@ -0,0 +1,316 @@ +/** + * 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. + */ +using System; +using System.Collections.Generic; +using System.Reflection; +using Org.Apache.REEF.Utilities.Logging; +using Org.Apache.REEF.Tang.Annotations; +using Org.Apache.REEF.Tang.Types; +using Org.Apache.REEF.Tang.Util; +using Org.Apache.REEF.Tang.Exceptions; +using Org.Apache.REEF.Tang.Interface; +using Org.Apache.REEF.Tang.Implementations.InjectionPlan; + +namespace Org.Apache.REEF.Tang.Implementations.ClassHierarchy +{ + public class NodeFactory + { + private static readonly Logger LOGGER = Logger.GetLogger(typeof(NodeFactory)); + + public static IPackageNode CreateRootPackageNode() + { + return new PackageNodeImpl(); + } + + public static INode CreateClassNode(INode parent, Type clazz) + { + //var namedParameter = clazz.GetCustomAttribute<NamedParameterAttribute>(); + var unit = null != clazz.GetCustomAttribute<UnitAttribute>(); + string simpleName = ReflectionUtilities.GetName(clazz); + string fullName = ReflectionUtilities.GetAssemblyQualifiedName(clazz); + //bool isStatic = true; // clazz.IsSealed && clazz.IsAbstract; always true in C# for Java static class + //bool injectable = true; // always true in C# + + bool isAssignableFromExternalConstructor = ReflectionUtilities.IsAssignableFromIgnoreGeneric(typeof(IExternalConstructor<>), clazz); + + //bool parentIsUnit = false; + + //No such thing in C#, should be false + //bool foundNonStaticInnerClass = false; + //foreach (Type c in clazz.getNestedTypes()) { + // if (!Modifier.isStatic(c.getModifiers())) { + // foundNonStaticInnerClass = true; + // } + //} + + var injectableConstructors = new List<IConstructorDef>(); + var allConstructors = new List<IConstructorDef>(); + + foreach (ConstructorInfo c in clazz.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)) + { + var constructorAnnotatedInjectable = null != c.GetCustomAttribute<InjectAttribute>(); + + bool constructorInjectable = constructorAnnotatedInjectable; + + ConstructorDefImpl constructorDef = CreateConstructorDef(c, constructorAnnotatedInjectable); + + if (constructorInjectable) + { +// if (injectableConstructors.Contains(constructorDef)) + if (constructorDef.IsInList(injectableConstructors)) + { + var e = new ClassHierarchyException( + "Ambiguous boundConstructors detected in class " + clazz + ": " + + constructorDef + " differs from some other" + " constructor only " + + "by parameter order."); + Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(e, LOGGER); + } + else + { + injectableConstructors.Add(constructorDef); + } + } + allConstructors.Add(constructorDef); + + } + + string defaultImplementation = null; + DefaultImplementationAttribute defaultImpl = clazz.GetCustomAttribute<DefaultImplementationAttribute>(); + if (null != defaultImpl) + { + Type defaultImplementationClazz = defaultImpl.Value; + + if (defaultImplementationClazz == null) + { + defaultImplementation = defaultImpl.Name; + } + else + { + if (!ReflectionUtilities.IsAssignableFromIgnoreGeneric(clazz, defaultImplementationClazz)) + { + var e = new ClassHierarchyException(clazz + + " declares its default implementation to be non-subclass " + + defaultImplementationClazz); + Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(e, LOGGER); + } + defaultImplementation = ReflectionUtilities.GetAssemblyQualifiedName(defaultImplementationClazz); + } + } + else + { + defaultImplementation = null; + } + + return new ClassNodeImpl(parent, simpleName, fullName, unit, true, isAssignableFromExternalConstructor, + injectableConstructors, allConstructors, defaultImplementation); + } + + //TODO + private static ConstructorDefImpl CreateConstructorDef(ConstructorInfo constructor, bool injectable) + { + var parameters = constructor.GetParameters(); + + IConstructorArg[] args = new ConstructorArgImpl[parameters.Length]; + + for (int i = 0; i < parameters.Length; i++) + { + //TODO for getInterfaceTarget() call + Type type = parameters[i].ParameterType; + type = ReflectionUtilities.EnsureInterfaceType(type); + //if (type.IsGenericType && type.FullName == null) + //{ + // type = type.GetGenericTypeDefinition(); + //} + bool isFuture; + + if(ReflectionUtilities.IsAssignableFromIgnoreGeneric(typeof(IInjectionFuture<>), type)) + { + type = ReflectionUtilities.GetInterfaceTarget(typeof(IInjectionFuture<>), type); + isFuture = true; + } + else + { + isFuture = false; + } + + ParameterAttribute named = parameters[i].GetCustomAttribute<ParameterAttribute>(); + if (named != null && !injectable) + { + Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(new ClassHierarchyException(constructor + " is not injectable, but it has an @Parameter annotation."), LOGGER); + } + + if (type == null) + { + Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(new ApplicationException("Exception"), LOGGER); + } + + string typename = ReflectionUtilities.GetAssemblyQualifiedName(type); + if (typename == null) + { + typename = type.Name; + } + args[i] = new ConstructorArgImpl(typename, named == null ? null : ReflectionUtilities.GetAssemblyQualifiedName(named.Value), isFuture); + } + return new ConstructorDefImpl(ReflectionUtilities.GetAssemblyQualifiedName(constructor.DeclaringType), args, injectable); + } + + public static INamedParameterNode CreateNamedParameterNode(INode parent, Type clazz, Type argClass) + { + Type setRawArgType = ReflectionUtilities.GetInterfaceTarget(typeof(ISet<>), argClass); + bool isSet = setRawArgType != null; + if(isSet) { + argClass = setRawArgType; + } + + Type listRawArgType = ReflectionUtilities.GetInterfaceTargetForType(typeof (IList<>), argClass); + bool isList = listRawArgType != null; + if (isList) + { + argClass = listRawArgType; + } + + string simpleName = ReflectionUtilities.GetName(clazz); + string fullName = ReflectionUtilities.GetAssemblyQualifiedName(clazz); + string fullArgName = ReflectionUtilities.GetAssemblyQualifiedName(argClass); + string simpleArgName = ReflectionUtilities.GetName(argClass); + + NamedParameterAttribute namedParameter = clazz.GetCustomAttribute<NamedParameterAttribute>(); + + if (namedParameter == null) + { + Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(new IllegalStateException("Got name without named parameter post-validation!"), LOGGER); + } + + bool hasStringDefault, hasClassDefault, hasStringSetDefault, hasClassSetDefault; + int default_count = 0; + //if(!namedParameter.default_value().isEmpty()) { //QUESTION: difference from below? + if (namedParameter.DefaultValue != null && namedParameter.DefaultValue.Length > 0) + { + hasStringDefault = true; + default_count++; + } + else + { + hasStringDefault = false; + } + + if (namedParameter.DefaultClass != null /*Void.class*/) + { + hasClassDefault = true; + default_count++; + } + else + { + hasClassDefault = false; + } + + if (namedParameter.DefaultValues != null && namedParameter.DefaultValues.Length > 0) + { + hasStringSetDefault = true; + default_count++; + } + else + { + hasStringSetDefault = false; + } + + if (namedParameter.DefaultClasses != null && namedParameter.DefaultClasses.Length > 0) + { + hasClassSetDefault = true; + default_count++; + } + else + { + hasClassSetDefault = false; + } + + if (default_count > 1) + { + var e = new ClassHierarchyException("Named parameter " + fullName + " defines more than one of default_value, default_class, default_values and default_classes"); + Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(e, LOGGER); + } + + string[] defaultInstanceAsStrings = new string[]{}; + + if (default_count == 0) + { + defaultInstanceAsStrings = new String[] { }; + } + else if (hasClassDefault) + { + Type default_class = namedParameter.DefaultClass; + AssertIsSubclassOf(clazz, default_class, argClass); + defaultInstanceAsStrings = new String[] { ReflectionUtilities.GetAssemblyQualifiedName(default_class) }; + } + else if (hasStringDefault) + { + // Don't know if the string is a class or literal here, so don't bother validating. + defaultInstanceAsStrings = new String[] { namedParameter.DefaultValue }; + } + else if (hasClassSetDefault) + { + Type[] clzs = namedParameter.DefaultClasses; + defaultInstanceAsStrings = new String[clzs.Length]; + for (int i = 0; i < clzs.Length; i++) + { + AssertIsSubclassOf(clazz, clzs[i], argClass); + defaultInstanceAsStrings[i] = ReflectionUtilities.GetAssemblyQualifiedName(clzs[i]); + } + } + else if (hasStringSetDefault) + { + defaultInstanceAsStrings = namedParameter.DefaultValues; + } + else + { + Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(new IllegalStateException(), LOGGER); + } + + string documentation = namedParameter.Documentation; + string shortName = namedParameter.ShortName; + if (namedParameter.ShortName != null && namedParameter.ShortName.Length == 0) + { + shortName = null; + } + + return new NamedParameterNodeImpl(parent, simpleName, fullName, + fullArgName, simpleArgName, isSet, isList, documentation, shortName, defaultInstanceAsStrings); + } + + // private static void assertIsSubclassOf(Class<?> named_parameter, Class<?> default_class, Type argClass) { + private static void AssertIsSubclassOf(Type namedparameter, Type defaultclass, Type argClass) + { + bool isSubclass = false; + string argClassName = ReflectionUtilities.GetAssemblyQualifiedName(argClass); + foreach (Type t in ReflectionUtilities.ClassAndAncestors(defaultclass)) + { + if (argClassName.Equals(ReflectionUtilities.GetAssemblyQualifiedName(t))) + { + isSubclass = true; + } + } + if (!(isSubclass)) + { + var e = new ClassHierarchyException(namedparameter + " defines a default class " + + ReflectionUtilities.GetName(defaultclass) + " with a type that does not extend of its target's type " + argClass); + Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(e, LOGGER); + } + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/c1b5200f/lang/cs/Org.Apache.REEF.Tang/Implementations/ClassHierarchy/PackageNodeImpl.cs ---------------------------------------------------------------------- diff --git a/lang/cs/Org.Apache.REEF.Tang/Implementations/ClassHierarchy/PackageNodeImpl.cs b/lang/cs/Org.Apache.REEF.Tang/Implementations/ClassHierarchy/PackageNodeImpl.cs new file mode 100644 index 0000000..4ff51f2 --- /dev/null +++ b/lang/cs/Org.Apache.REEF.Tang/Implementations/ClassHierarchy/PackageNodeImpl.cs @@ -0,0 +1,49 @@ +/** + * 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. + */ +using System; +using Org.Apache.REEF.Tang.Types; + +namespace Org.Apache.REEF.Tang.Implementations.ClassHierarchy +{ + public class PackageNodeImpl : AbstractNode, IPackageNode + { + public PackageNodeImpl(INode parent, String name, String fullName) : + base(parent, name, fullName) + { + } + + public PackageNodeImpl() + : base(null, "", "[root node]") + { + } + + /** + * Unlike normal nodes, the root node needs to take the package name of its + * children into account. Therefore, we use the full name as the key when + * we insert nodes into the root. + */ + public override void Add(INode n) { + if (children.Count == 289) + { + Console.WriteLine("Test"); + } + children.Add(n.GetFullName(), n); + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/c1b5200f/lang/cs/Org.Apache.REEF.Tang/Implementations/ClassHierarchy/ParameterParser.cs ---------------------------------------------------------------------- diff --git a/lang/cs/Org.Apache.REEF.Tang/Implementations/ClassHierarchy/ParameterParser.cs b/lang/cs/Org.Apache.REEF.Tang/Implementations/ClassHierarchy/ParameterParser.cs new file mode 100644 index 0000000..2396e4a --- /dev/null +++ b/lang/cs/Org.Apache.REEF.Tang/Implementations/ClassHierarchy/ParameterParser.cs @@ -0,0 +1,199 @@ +/** + * 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. + */ +using System; +using System.Collections.Generic; +using System.Reflection; +using Org.Apache.REEF.Utilities.Logging; +using Org.Apache.REEF.Tang.Exceptions; +using Org.Apache.REEF.Tang.Interface; +using Org.Apache.REEF.Tang.Util; + +namespace Org.Apache.REEF.Tang.Implementations.ClassHierarchy +{ + public class ParameterParser + { + private static readonly Logger LOGGER = Logger.GetLogger(typeof(ParameterParser)); + + MonotonicTreeMap<String, ConstructorInfo> parsers = new MonotonicTreeMap<String, ConstructorInfo>(); + + //ec: ACons, tc: A + public void AddParser(Type ec) + { + Type tc = (Type)ReflectionUtilities.GetInterfaceTarget(typeof(IExternalConstructor<>), ec); + AddParser(tc, ec); + } + + //TODO + //public <T, U extends T> void AddParser(Class<U> clazz, Class<? extends ExternalConstructor<T>> ec) throws BindException { + //public void AddParser<T, U, V>(GenericType<U> clazz, GenericType<V> ec) + // where U : T + // where V: IExternalConstructor<T> + //{ + + //clazz: A, ec: ACons + private void AddParser(Type clazz, Type ec) + { + ConstructorInfo c = ec.GetConstructor(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance, null, new Type[] { typeof(string) }, null); + + if (c == null) + { + var e = new BindException("Constructor " + ReflectionUtilities.GetAssemblyQualifiedName(ec) + "(String) does not exist!"); + Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(e, LOGGER); + } + + //c.setAccessible(true); //set as public + parsers.Add(ReflectionUtilities.GetAssemblyQualifiedName(clazz), c); + } + + + public void MergeIn(ParameterParser p) + { + foreach (string s in p.parsers.Keys) + { + if (!parsers.ContainsKey(s)) + { + ConstructorInfo ci; + p.parsers.TryGetValue(s, out ci); + parsers.Add(s, ci); + } + else + { + ConstructorInfo oldC; + ConstructorInfo newC; + parsers.TryGetValue(s, out oldC); + p.parsers.TryGetValue(s, out newC); + if (!oldC.Equals(newC)) + { + var e = new ArgumentException( + "Conflict detected when merging parameter parsers! To parse " + s + + " I have a: " + ReflectionUtilities.GetAssemblyQualifiedName(oldC.DeclaringType) + + " the other instance has a: " + ReflectionUtilities.GetAssemblyQualifiedName(newC.DeclaringType)); + Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(e, LOGGER); + } + } + } + } + + //(Integer, "3") return object of new Integer(3) + public object Parse(Type c, String s) + { + Type d = ReflectionUtilities.BoxClass(c); + foreach (Type e in ReflectionUtilities.ClassAndAncestors(d)) // get all the super classes of Integer for example + { + string name = ReflectionUtilities.GetAssemblyQualifiedName(e); + if (parsers.ContainsKey(name)) + { + object ret = Parse(name, s); + if (c.IsAssignableFrom(ret.GetType())) //check if ret can be cast as c + { + return ret; + } + else + { + var ex = new InvalidCastException("Cannot cast from " + ret.GetType() + " to " + c); + Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(ex, LOGGER); + } + } + } + return Parse(d.Name, s); + } + + //name: "Integer", value: "12" + public object Parse(string name, string value) + { + if (parsers.ContainsKey(name)) + { + try + { + ConstructorInfo c = null; + parsers.TryGetValue(name, out c); + var o = c.Invoke(new object[] { value }); + var m = o.GetType().GetMethod("NewInstance", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + return m.Invoke(o, new object[] {}); + } + catch (TargetInvocationException e) + { + Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Caught(e, Level.Error, LOGGER); + Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(new ArgumentException("Error invoking constructor for " + name, e), LOGGER); + } + } + else if (name.Equals(typeof(string).Name)) + { + return (object)value; + } + if (name.Equals(typeof(Byte).Name)) + { + return (object)(Byte)Byte.Parse(value); + } + if (name.Equals(typeof(Char).Name)) + { + return (object)(Char)value[0]; + } + if (name.Equals(typeof(short).Name)) + { + return (System.Int16)System.Int16.Parse(value); + } + if (name.Equals(typeof(int).Name)) + { + return (object)(Int32)Int32.Parse(value); + } + if (name.Equals(typeof(long).Name)) + { + return (object)(Int64)Int64.Parse(value); + } + if (name.Equals(typeof(float).Name)) + { + return (object)(Single)Single.Parse(value); + } + if (name.Equals(typeof(Double).Name)) + { + return (object)(Double)Double.Parse(value); + } + if (name.Equals(typeof(Boolean).Name)) + { + return (object)(Boolean)Boolean.Parse(value); + } + if (name.Equals(typeof(byte[]).Name)) + { + byte[] bytes = new byte[value.Length * sizeof(char)]; + System.Buffer.BlockCopy(value.ToCharArray(), 0, bytes, 0, bytes.Length); + return bytes; + } + return null; + } + + private static readonly ISet<string> BUILTIN_NAMES = new HashSet<string>(new string[] + { + typeof(string).AssemblyQualifiedName, + typeof(Byte).AssemblyQualifiedName, + typeof(char).AssemblyQualifiedName, + typeof(short).AssemblyQualifiedName, + typeof(Int32).AssemblyQualifiedName, + typeof(long).AssemblyQualifiedName, + typeof(float).AssemblyQualifiedName, + typeof(double).AssemblyQualifiedName, + typeof(bool).AssemblyQualifiedName + } ); + + public bool CanParse(string name) + { + return parsers.ContainsKey(name) || BUILTIN_NAMES.Contains(name); + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/c1b5200f/lang/cs/Org.Apache.REEF.Tang/Implementations/Configuration/ConfigurationBuilderImpl.cs ---------------------------------------------------------------------- diff --git a/lang/cs/Org.Apache.REEF.Tang/Implementations/Configuration/ConfigurationBuilderImpl.cs b/lang/cs/Org.Apache.REEF.Tang/Implementations/Configuration/ConfigurationBuilderImpl.cs new file mode 100644 index 0000000..4098195 --- /dev/null +++ b/lang/cs/Org.Apache.REEF.Tang/Implementations/Configuration/ConfigurationBuilderImpl.cs @@ -0,0 +1,366 @@ +/** + * 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. + */ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Org.Apache.REEF.Utilities.Logging; +using Org.Apache.REEF.Tang.Exceptions; +using Org.Apache.REEF.Tang.Interface; +using Org.Apache.REEF.Tang.Types; +using Org.Apache.REEF.Tang.Util; +using Org.Apache.REEF.Tang.Implementations.ClassHierarchy; +using Org.Apache.REEF.Tang.Implementations.Tang; + +namespace Org.Apache.REEF.Tang.Implementations.Configuration +{ + public class ConfigurationBuilderImpl : IConfigurationBuilder + { + public IClassHierarchy ClassHierarchy; + + public readonly IDictionary<IClassNode, IClassNode> BoundImpls = new MonotonicTreeMap<IClassNode, IClassNode>(); + public readonly IDictionary<IClassNode, IClassNode> BoundConstructors = new MonotonicTreeMap<IClassNode, IClassNode>(); + public readonly IDictionary<INamedParameterNode, String> NamedParameters = new MonotonicTreeMap<INamedParameterNode, String>(); + public readonly IDictionary<IClassNode, IConstructorDef> LegacyConstructors = new MonotonicTreeMap<IClassNode, IConstructorDef>(); + public readonly MonotonicMultiMap<INamedParameterNode, object> BoundSetEntries = new MonotonicMultiMap<INamedParameterNode, object>(); + public readonly IDictionary<INamedParameterNode, IList<object>> BoundLists = new MonotonicTreeMap<INamedParameterNode, IList<object>>(); + + public readonly static string INIT = "<init>"; + + private static readonly Logger LOGGER = Logger.GetLogger(typeof(ConfigurationBuilderImpl)); + + protected ConfigurationBuilderImpl() + { + this.ClassHierarchy = TangFactory.GetTang().GetDefaultClassHierarchy(); + } + + public ConfigurationBuilderImpl(IClassHierarchy classHierarchy) + { + this.ClassHierarchy = classHierarchy; + } + + protected ConfigurationBuilderImpl(string[] assemblies, IConfiguration[] confs, Type[] parsers) + { + this.ClassHierarchy = TangFactory.GetTang().GetDefaultClassHierarchy(assemblies, parsers); + foreach (IConfiguration tc in confs) + { + AddConfiguration(((ConfigurationImpl) tc)); + } + } + + public ConfigurationBuilderImpl(ConfigurationBuilderImpl t) + { + this.ClassHierarchy = t.GetClassHierarchy(); + try { + AddConfiguration(t.GetClassHierarchy(), t); + } + catch (BindException e) + { + Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Caught(e, Level.Error, LOGGER); + Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(new IllegalStateException("Could not copy builder", e), LOGGER); + } + } + + protected ConfigurationBuilderImpl(string[] assemblies) : this(assemblies, new IConfiguration[0], new Type[0]) + { + } + + protected ConfigurationBuilderImpl(IConfiguration[] confs) : this(new string[0], confs, new Type[0]) + { + } + + public void AddConfiguration(IConfiguration conf) + { + AddConfiguration(conf.GetClassHierarchy(), ((ConfigurationImpl)conf).Builder); + } + + private void AddConfiguration(IClassHierarchy ns, ConfigurationBuilderImpl builder) + { + this.ClassHierarchy = this.ClassHierarchy.Merge(ns); + + if((ClassHierarchy is ClassHierarchyImpl || builder.ClassHierarchy is ClassHierarchyImpl)) + { + if((ClassHierarchy is ClassHierarchyImpl && builder.ClassHierarchy is ClassHierarchyImpl)) + { + ((ClassHierarchyImpl) ClassHierarchy).Parameterparser.MergeIn(((ClassHierarchyImpl) builder.ClassHierarchy).Parameterparser); + } + else + { + Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(new ArgumentException("Attempt to merge Java and non-Java class hierarchy! Not supported."), LOGGER); + } + } + + foreach (IClassNode cn in builder.BoundImpls.Keys) + { + IClassNode n = null; + builder.BoundImpls.TryGetValue(cn, out n); + if (n != null) + { + Bind(cn.GetFullName(), n.GetFullName()); + } + } + + foreach (IClassNode cn in builder.BoundConstructors.Keys) + { + IClassNode n = null; + builder.BoundConstructors.TryGetValue(cn, out n); + if (n != null) + { + Bind(cn.GetFullName(), n.GetFullName()); + } + } + + // The namedParameters set contains the strings that can be used to + // instantiate new + // named parameter instances. Create new ones where we can. + foreach (INamedParameterNode np in builder.NamedParameters.Keys) + { + string v = null; + builder.NamedParameters.TryGetValue(np, out v); + Bind(np.GetFullName(), v); + } + + foreach (IClassNode cn in builder.LegacyConstructors.Keys) + { + IConstructorDef cd = null; + builder.LegacyConstructors.TryGetValue(cn, out cd); + RegisterLegacyConstructor(cn, cd.GetArgs()); + } + + foreach (KeyValuePair<INamedParameterNode, object> e in builder.BoundSetEntries) + { + String name = ((INamedParameterNode)e.Key).GetFullName(); + if(e.Value is INode) + { + BindSetEntry(name, (INode)e.Value); + } + else if (e.Value is string) + { + BindSetEntry(name, (string)e.Value); + } else { + var ex = new IllegalStateException(string.Format(CultureInfo.CurrentCulture, "The value {0} set to the named parameter {1} is illegel.", e.Value, name)); + Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(ex, LOGGER); + } + } + + foreach (var p in builder.BoundLists) + { + BoundLists.Add(p.Key, p.Value); + } + } + + public IClassHierarchy GetClassHierarchy() + { + return this.ClassHierarchy; + } + + public void RegisterLegacyConstructor(IClassNode cn, IList<IClassNode> args) + { + LegacyConstructors.Add(cn, cn.GetConstructorDef(args)); + } + + public void RegisterLegacyConstructor(string s, IList<string> args) + { + IClassNode cn = (IClassNode) this.ClassHierarchy.GetNode(s); + IList<IClassNode> cnArgs = new List<IClassNode>(); + for (int i = 0; i < args.Count; i++) + { + cnArgs.Add((IClassNode)ClassHierarchy.GetNode(args[i])); + } + RegisterLegacyConstructor(cn, cnArgs); + } + + public void RegisterLegacyConstructor(IClassNode c, IList<IConstructorArg> args) + { + string[] cn = new string[args.Count]; + + for (int i = 0; i < args.Count; i++) + { + cn[i] = args[i].Gettype(); + } + RegisterLegacyConstructor(c.GetFullName(), cn); + } + + public IConfiguration Build() + { + return new ConfigurationImpl(new ConfigurationBuilderImpl(this)); + } + + public void Bind(string key, string value) + { + INode n = this.ClassHierarchy.GetNode(key); + if (n is INamedParameterNode) + { + BindParameter((INamedParameterNode)n, value); + } + else if (n is IClassNode) + { + INode m = this.ClassHierarchy.GetNode(value); + Bind((IClassNode)n, (IClassNode)m); + } + else + { + var ex = new IllegalStateException(string.Format("getNode() returned {0} which is neither a ClassNode nor a NamedParameterNode", n)); + Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(ex, LOGGER); + } + } + + public void Bind(Types.INode key, Types.INode value) + { + if (key is INamedParameterNode) + { + BindParameter((INamedParameterNode)key, value.GetFullName()); + } + else if (key is IClassNode) + { + IClassNode k = (IClassNode)key; + if (value is IClassNode) + { + IClassNode val = (IClassNode)value; + if (val.IsExternalConstructor() && !k.IsExternalConstructor()) + { + BindConstructor(k, (IClassNode) val); + } + else + { + BindImplementation(k, (IClassNode)val); + } + } + } + } + + public void BindParameter(INamedParameterNode name, String value) + { + /* Parse and discard value; this is just for type checking, skip for now*/ + if (this.ClassHierarchy is ICsClassHierarchy) + { + ((ICsClassHierarchy)ClassHierarchy).Parse(name, value); + } + + if(name.IsSet()) + { + BindSetEntry((INamedParameterNode)name, value); + } + else + { + NamedParameters.Add(name, value); + } + } + + public void BindImplementation(IClassNode n, IClassNode m) + { + if (this.ClassHierarchy.IsImplementation(n, m)) + { + BoundImpls.Add(n, m); + } + else + { + var ex = new ArgumentException(string.Format("Class {0} does not extend {1}.", m, n)); + Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Throw(ex, LOGGER); + } + } + + public void BindSetEntry(String iface, String impl) + { + BoundSetEntries.Add((INamedParameterNode)this.ClassHierarchy.GetNode(iface), impl); + } + + public void BindSetEntry(String iface, INode impl) + { + BoundSetEntries.Add((INamedParameterNode)ClassHierarchy.GetNode(iface), impl); + } + + public void BindSetEntry(INamedParameterNode iface, String impl) + { + BoundSetEntries.Add(iface, impl); + } + + public void BindSetEntry(INamedParameterNode iface, INode impl) + { + BoundSetEntries.Add(iface, impl); + } + + public void BindList(INamedParameterNode iface, IList<INode> impl) + { + IList<object> l = new List<object>(); + foreach (var n in impl) + { + l.Add((object)n); + } + BoundLists.Add(iface, l); + } + + public void BindList(INamedParameterNode iface, IList<string> impl) + { + IList<object> l = new List<object>(); + foreach (var n in impl) + { + l.Add((object)n); + } + BoundLists.Add(iface, l); + } + + public void BindList(string iface, IList<INode> impl) + { + BindList((INamedParameterNode)ClassHierarchy.GetNode(iface), impl); + } + + public void BindList(string iface, IList<string> impl) + { + BindList((INamedParameterNode)ClassHierarchy.GetNode(iface), impl); + } + + public void BindConstructor(Types.IClassNode k, Types.IClassNode v) + { + BoundConstructors.Add(k, v); + } + + public string ClassPrettyDefaultString(string longName) + { + INamedParameterNode param = (INamedParameterNode) this.ClassHierarchy.GetNode(longName); + return param.GetSimpleArgName() + "=" + Join(",", param.GetDefaultInstanceAsStrings()); + } + + private String Join(string sep, string[] s) + { + if (s.Length == 0) + { + return null; + } + else + { + StringBuilder sb = new StringBuilder(s[0]); + for (int i = 1; i < s.Length; i++) + { + sb.Append(sep); + sb.Append(s[i]); + } + return sb.ToString(); + } + } + + public string ClassPrettyDescriptionString(string fullName) + { + INamedParameterNode param = (INamedParameterNode) this.ClassHierarchy.GetNode(fullName); + return param.GetDocumentation() + "\n" + param.GetFullName(); + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-reef/blob/c1b5200f/lang/cs/Org.Apache.REEF.Tang/Implementations/Configuration/ConfigurationImpl.cs ---------------------------------------------------------------------- diff --git a/lang/cs/Org.Apache.REEF.Tang/Implementations/Configuration/ConfigurationImpl.cs b/lang/cs/Org.Apache.REEF.Tang/Implementations/Configuration/ConfigurationImpl.cs new file mode 100644 index 0000000..024ab87 --- /dev/null +++ b/lang/cs/Org.Apache.REEF.Tang/Implementations/Configuration/ConfigurationImpl.cs @@ -0,0 +1,122 @@ +/** + * 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. + */ +using System; +using System.Collections.Generic; +using Org.Apache.REEF.Tang.Interface; +using Org.Apache.REEF.Tang.Types; + +namespace Org.Apache.REEF.Tang.Implementations.Configuration +{ + public class ConfigurationImpl : IConfiguration + { + public readonly ConfigurationBuilderImpl Builder; + + public ConfigurationImpl(ConfigurationBuilderImpl builder) + { + Builder = builder; + } + + public IClassHierarchy GetClassHierarchy() + { + return Builder.ClassHierarchy; + } + + public IConfigurationBuilder newBuilder() + { + return ((ConfigurationImpl)Builder.Build()).Builder; + } + + public ICollection<IClassNode> GetBoundImplementations() + { + return Builder.BoundImpls.Keys; + } + + public IClassNode GetBoundImplementation(IClassNode cn) + { + IClassNode v; + + Builder.BoundImpls.TryGetValue(cn, out v); + + return v; + } + + public ICollection<IClassNode> GetBoundConstructors() + { + return Builder.BoundConstructors.Keys; + } + + public IClassNode GetBoundConstructor(IClassNode cn) + { + IClassNode v; + + Builder.BoundConstructors.TryGetValue(cn, out v); + + return v; + } + + public ICollection<INamedParameterNode> GetNamedParameters() + { + return Builder.NamedParameters.Keys; + } + + public string GetNamedParameter(INamedParameterNode np) + { + string v = null; + Builder.NamedParameters.TryGetValue(np, out v); + + return v; + } + + public IConstructorDef GetLegacyConstructor(IClassNode cn) + { + IConstructorDef v; + + Builder.LegacyConstructors.TryGetValue(cn, out v); + + return v; + } + + public ICollection<IClassNode> GetLegacyConstructors() + { + return Builder.LegacyConstructors.Keys; + } + + public ISet<Object> GetBoundSet(INamedParameterNode np) + { + return Builder.BoundSetEntries.GetValuesForKey(np); + } + + public IEnumerator<KeyValuePair<INamedParameterNode, object>> GetBoundSets() + { + return Builder.BoundSetEntries.GetEnumerator(); + } + + public IDictionary<INamedParameterNode, IList<object>> GetBoundList() + { + return Builder.BoundLists; + } + + public IList<object> GetBoundList(INamedParameterNode np) + { + IList<object> list; + Builder.BoundLists.TryGetValue(np, out list); + return list; + } + } +} \ No newline at end of file
