blob: 6cb06d2291f5da6b0fef84d684d4152e5882ef32 [file] [log] [blame]
// 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.IO;
using System.Linq;
using Org.Apache.REEF.Tang.Exceptions;
using Org.Apache.REEF.Tang.Implementations.ClassHierarchy;
using Org.Apache.REEF.Tang.Interface;
using Org.Apache.REEF.Tang.Types;
using Org.Apache.REEF.Tang.Util;
using Org.Apache.REEF.Utilities.Logging;
using ProtoBuf;
namespace Org.Apache.REEF.Tang.Protobuf
{
public sealed class ProtocolBufferClassHierarchy : IClassHierarchy
{
private static readonly Logger LOGGER = Logger.GetLogger(typeof(ProtocolBufferClassHierarchy));
private readonly IPackageNode rootNode;
private readonly IDictionary<string, INode> lookupTable = new Dictionary<string, INode>();
private readonly IDictionary<string, IDictionary<string, string>> _aliasLookupTable = new Dictionary<string, IDictionary<string, string>>();
public static void Serialize(string fileName, IClassHierarchy classHierarchy)
{
Node node = Serialize(classHierarchy);
using (var file = File.Create(fileName))
{
Serializer.Serialize<Node>(file, node);
}
}
public static Node Serialize(IClassHierarchy classHierarchy)
{
return SerializeNode(classHierarchy.GetNamespace());
}
private static Node SerializeNode(INode n)
{
IList<Node> children = new List<Node>();
foreach (INode child in n.GetChildren())
{
children.Add(SerializeNode(child));
}
if (n is IClassNode)
{
IClassNode cn = (IClassNode)n;
IList<IConstructorDef> injectable = cn.GetInjectableConstructors();
IList<IConstructorDef> all = cn.GetAllConstructors();
IList<IConstructorDef> others = new List<IConstructorDef>(all);
foreach (var c in injectable)
{
others.Remove(c);
}
IList<ConstructorDef> injectableConstructors = new List<ConstructorDef>();
foreach (IConstructorDef inj in injectable)
{
injectableConstructors.Add(SerializeConstructorDef(inj));
}
IList<ConstructorDef> otherConstructors = new List<ConstructorDef>();
foreach (IConstructorDef other in others)
{
otherConstructors.Add(SerializeConstructorDef(other));
}
List<string> implFullNames = new List<string>();
foreach (IClassNode impl in cn.GetKnownImplementations())
{
implFullNames.Add(impl.GetFullName()); // we use class fully qualifed name
}
return NewClassNode(cn.GetName(), cn.GetFullName(),
cn.IsInjectionCandidate(), cn.IsExternalConstructor(), cn.IsUnit(),
injectableConstructors, otherConstructors, implFullNames, children);
}
if (n is INamedParameterNode)
{
INamedParameterNode np = (INamedParameterNode)n;
return NewNamedParameterNode(np.GetName(), np.GetFullName(),
np.GetSimpleArgName(), np.GetFullArgName(), np.IsSet(), np.IsList(), np.GetDocumentation(),
np.GetShortName(), np.GetDefaultInstanceAsStrings(), children, np.GetAlias(), np.GetAliasLanguage());
}
if (n is IPackageNode)
{
return NewPackageNode(n.GetName(), n.GetFullName(), children);
}
Utilities.Diagnostics.Exceptions.Throw(new IllegalStateException("Encountered unknown type of Node: " + n), LOGGER);
return null;
}
private static ConstructorDef SerializeConstructorDef(IConstructorDef def)
{
IList<ConstructorArg> args = new List<ConstructorArg>();
foreach (IConstructorArg arg in def.GetArgs())
{
args.Add(NewConstructorArg(arg.Gettype(), arg.GetNamedParameterName(), arg.IsInjectionFuture()));
}
return newConstructorDef(def.GetClassName(), args);
}
private static ConstructorArg NewConstructorArg(
string fullArgClassName, string namedParameterName, bool isFuture)
{
ConstructorArg constArg = new ConstructorArg();
constArg.full_arg_class_name = fullArgClassName;
constArg.named_parameter_name = namedParameterName;
constArg.is_injection_future = isFuture;
return constArg;
}
private static ConstructorDef newConstructorDef(
string fullClassName, IList<ConstructorArg> args)
{
ConstructorDef constDef = new ConstructorDef();
constDef.full_class_name = fullClassName;
foreach (ConstructorArg arg in args)
{
constDef.args.Add(arg);
}
return constDef;
}
private static Node NewClassNode(string name,
string fullName, bool isInjectionCandidate,
bool isExternalConstructor, bool isUnit,
IList<ConstructorDef> injectableConstructors,
IList<ConstructorDef> otherConstructors,
IList<string> implFullNames, IList<Node> children)
{
ClassNode classNode = new ClassNode();
classNode.is_injection_candidate = isInjectionCandidate;
foreach (var ic in injectableConstructors)
{
classNode.InjectableConstructors.Add(ic);
}
foreach (var oc in otherConstructors)
{
classNode.OtherConstructors.Add(oc);
}
foreach (var implFullName in implFullNames)
{
classNode.impl_full_names.Add(implFullName);
}
Node n = new Node();
n.name = name;
n.full_name = fullName;
n.class_node = classNode;
foreach (var c in children)
{
n.children.Add(c);
}
return n;
}
private static Node NewNamedParameterNode(string name,
string fullName, string simpleArgClassName, string fullArgClassName,
bool isSet, bool isList, string documentation, // can be null
string shortName, // can be null
string[] instanceDefault, // can be null
IList<Node> children,
string alias, Language aliasLanguage)
{
NamedParameterNode namedParameterNode = new NamedParameterNode();
namedParameterNode.simple_arg_class_name = simpleArgClassName;
namedParameterNode.full_arg_class_name = fullArgClassName;
namedParameterNode.is_set = isSet;
namedParameterNode.is_list = isList;
if (documentation != null)
{
namedParameterNode.documentation = documentation;
}
if (shortName != null)
{
namedParameterNode.short_name = shortName;
}
if (alias != null)
{
namedParameterNode.alias_name = alias;
}
namedParameterNode.alias_language = aliasLanguage.ToString();
foreach (var id in instanceDefault)
{
namedParameterNode.instance_default.Add(id);
}
Node n = new Node();
n.name = name;
n.full_name = fullName;
n.named_parameter_node = namedParameterNode;
foreach (var c in children)
{
n.children.Add(c);
}
return n;
}
private static Node NewPackageNode(string name,
string fullName, IList<Node> children)
{
PackageNode packageNode = new PackageNode();
Node n = new Node();
n.name = name;
n.full_name = fullName;
n.package_node = packageNode;
foreach (var c in children)
{
n.children.Add(c);
}
return n;
}
public static IClassHierarchy DeSerialize(string fileName)
{
Node root;
using (var file = File.OpenRead(fileName))
{
root = Serializer.Deserialize<Node>(file);
}
return new ProtocolBufferClassHierarchy(root);
}
// create a ProtocolBufferClassHierarchy with empty nodes and lookup table. It can be used to merge other class hierarchy to it
public ProtocolBufferClassHierarchy()
{
this.rootNode = new PackageNodeImpl();
}
public ProtocolBufferClassHierarchy(Node root)
{
this.rootNode = new PackageNodeImpl();
if (root.package_node == null)
{
Utilities.Diagnostics.Exceptions.Throw(new ArgumentException("Expected a package node. Got: " + root), LOGGER);
}
// Register all the classes.
foreach (Node child in root.children)
{
ParseSubHierarchy(rootNode, child);
}
BuildHashTable(rootNode);
foreach (Node child in root.children)
{
WireUpInheritanceRelationships(child);
}
}
public void BuildHashTable(INode n)
{
foreach (INode child in n.GetChildren())
{
lookupTable.Add(child.GetFullName(), child);
if (child is INamedParameterNode)
{
AddAlias((INamedParameterNode)child);
}
BuildHashTable(child);
}
}
private void AddAlias(INamedParameterNode np)
{
if (!string.IsNullOrEmpty(np.GetAlias()))
{
IDictionary<string, string> mapping = null;
_aliasLookupTable.TryGetValue(np.GetAliasLanguage().ToString(), out mapping);
if (mapping == null)
{
mapping = new Dictionary<string, string>();
_aliasLookupTable.Add(np.GetAliasLanguage().ToString(), mapping);
}
try
{
mapping.Add(np.GetAlias(), np.GetFullName());
}
catch (Exception)
{
var e = new ApplicationException(string.Format(CultureInfo.CurrentCulture, "Duplicated alias {0} on named parameter {1}.", np.GetAlias(), np.GetFullName()));
Utilities.Diagnostics.Exceptions.Throw(e, LOGGER);
}
}
}
private static void ParseSubHierarchy(INode parent, Node n)
{
INode parsed = null;
if (n.package_node != null)
{
parsed = new PackageNodeImpl(parent, n.name, n.full_name);
}
else if (n.named_parameter_node != null)
{
NamedParameterNode np = n.named_parameter_node;
if (!string.IsNullOrWhiteSpace(np.alias_name) && !string.IsNullOrWhiteSpace(np.alias_language))
{
Language language;
try
{
Enum.TryParse(np.alias_language, true, out language);
}
catch (Exception)
{
string msg = string.Format(CultureInfo.CurrentCulture, "Language {0} passed in is not supported", np.alias_language);
throw new ArgumentException(msg);
}
parsed = new NamedParameterNodeImpl(parent, n.name,
n.full_name, np.full_arg_class_name, np.simple_arg_class_name,
np.is_set, np.is_list, np.documentation, np.short_name,
np.instance_default.ToArray(), np.alias_name, language);
}
else
{
parsed = new NamedParameterNodeImpl(parent, n.name,
n.full_name, np.full_arg_class_name, np.simple_arg_class_name,
np.is_set, np.is_list, np.documentation, np.short_name,
np.instance_default.ToArray());
}
}
else if (n.class_node != null)
{
ClassNode cn = n.class_node;
IList<IConstructorDef> injectableConstructors = new List<IConstructorDef>();
IList<IConstructorDef> allConstructors = new List<IConstructorDef>();
foreach (ConstructorDef injectable in cn.InjectableConstructors)
{
IConstructorDef def = ParseConstructorDef(injectable, true);
injectableConstructors.Add(def);
allConstructors.Add(def);
}
foreach (ConstructorDef other in cn.OtherConstructors)
{
IConstructorDef def = ParseConstructorDef(other, false);
allConstructors.Add(def);
}
IConstructorDef[] dummy = new ConstructorDefImpl[0];
parsed = new ClassNodeImpl(parent, n.name, n.full_name,
cn.is_unit, cn.is_injection_candidate,
cn.is_external_constructor, injectableConstructors,
allConstructors, cn.default_implementation);
}
else
{
Utilities.Diagnostics.Exceptions.Throw(new IllegalStateException("Bad protocol buffer: got abstract node" + n), LOGGER);
}
foreach (Node child in n.children)
{
ParseSubHierarchy(parsed, child);
}
}
private static IConstructorDef ParseConstructorDef(ConstructorDef def, bool isInjectable)
{
IList<IConstructorArg> args = new List<IConstructorArg>();
foreach (ConstructorArg arg in def.args)
{
args.Add(new ConstructorArgImpl(arg.full_arg_class_name, arg.named_parameter_name, arg.is_injection_future));
}
return new ConstructorDefImpl(def.full_class_name, args.ToArray(), isInjectable);
}
private void WireUpInheritanceRelationships(Node n)
{
if (n.class_node != null)
{
ClassNode cn = n.class_node;
IClassNode iface = null;
try
{
iface = (IClassNode)GetNode(n.full_name);
}
catch (NameResolutionException e)
{
Org.Apache.REEF.Utilities.Diagnostics.Exceptions.Caught(e, Level.Error, LOGGER);
var ex = new IllegalStateException("When reading protocol buffer node "
+ n.full_name + " does not exist. Full record is " + n, e);
Utilities.Diagnostics.Exceptions.Throw(ex, LOGGER);
}
foreach (string impl in cn.impl_full_names)
{
try
{
iface.PutImpl((IClassNode)GetNode(impl));
}
catch (NameResolutionException e)
{
Utilities.Diagnostics.Exceptions.Caught(e, Level.Error, LOGGER);
var ex = new IllegalStateException("When reading protocol buffer node "
+ n + " refers to non-existent implementation:" + impl);
Utilities.Diagnostics.Exceptions.Throw(ex, LOGGER);
}
catch (InvalidCastException e)
{
Utilities.Diagnostics.Exceptions.Caught(e, Level.Error, LOGGER);
try
{
var ex = new IllegalStateException(
"When reading protocol buffer node " + n
+ " found implementation" + GetNode(impl)
+ " which is not a ClassNode!");
Utilities.Diagnostics.Exceptions.Throw(ex, LOGGER);
}
catch (NameResolutionException ne)
{
Utilities.Diagnostics.Exceptions.Caught(ne, Level.Error, LOGGER);
var ex = new IllegalStateException(
"Got 'cant happen' exception when producing error message for " + e);
Utilities.Diagnostics.Exceptions.Throw(ex, LOGGER);
}
}
}
}
}
public INode GetNode(string fullName)
{
INode ret;
lookupTable.TryGetValue(fullName, out ret);
if (ret == null)
{
var ex = new NameResolutionException(fullName, "Cannot resolve the name from the class hierarchy during deserialization: " + fullName);
Utilities.Diagnostics.Exceptions.Throw(ex, LOGGER);
}
return ret;
}
/// <summary>
/// This method get INode from deSerialized class hierarchy by fullName.
/// If the name is not found, it will found alias for aliasLanguage. If alias is found,
/// it will use the alias to do look up again.
/// </summary>
/// <param name="fullName"></param>
/// <param name="aliasLanguage"></param>
/// <returns></returns>
public INode GetNode(string fullName, string aliasLanguage)
{
INode ret = null;
lookupTable.TryGetValue(fullName, out ret);
if (ret == null)
{
IDictionary<string, string> mapping = null;
string assemblyName = null;
_aliasLookupTable.TryGetValue(aliasLanguage, out mapping);
if (mapping != null)
{
mapping.TryGetValue(fullName, out assemblyName);
if (assemblyName != null)
{
lookupTable.TryGetValue(assemblyName, out ret);
}
}
if (mapping == null || assemblyName == null || ret == null)
{
var ex = new NameResolutionException(fullName, "Cannot resolve the name from the class hierarchy during de-serialization: " + fullName);
Utilities.Diagnostics.Exceptions.Throw(ex, LOGGER);
}
}
return ret;
}
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 ProtocolBufferClassHierarchy))
{
Utilities.Diagnostics.Exceptions.Throw(new NotSupportedException(
"Cannot merge ExternalClassHierarchies yet!"), LOGGER);
}
ProtocolBufferClassHierarchy pch = (ProtocolBufferClassHierarchy)ch;
foreach (var pair in pch.lookupTable)
{
if (!this.lookupTable.ContainsKey(pair.Key))
{
this.lookupTable.Add(pair);
}
}
foreach (INode n in ch.GetNamespace().GetChildren())
{
if (!rootNode.Contains(n.GetFullName()))
{
if (n is INamedParameterNode)
{
INamedParameterNode np = (INamedParameterNode)n;
new NamedParameterNodeImpl(this.rootNode, np.GetName(),
np.GetFullName(), np.GetFullArgName(), np.GetSimpleArgName(),
np.IsSet(), np.IsList(), np.GetDocumentation(), np.GetShortName(),
np.GetDefaultInstanceAsStrings().ToArray());
}
else if (n is IClassNode)
{
IClassNode cn = (IClassNode)n;
new ClassNodeImpl(rootNode, cn.GetName(), cn.GetFullName(),
cn.IsUnit(), cn.IsInjectionCandidate(),
cn.IsExternalConstructor(), cn.GetInjectableConstructors(),
cn.GetAllConstructors(), cn.GetDefaultImplementation());
}
}
}
return this;
}
}
}