blob: 3288aa33c017fbc73176af5947a67682c2ed8225 [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.Text;
using System.Reflection;
using System.Reflection.Emit;
namespace Avro.Specific
{
public sealed class ObjectCreator
{
private static readonly ObjectCreator instance = new ObjectCreator();
public static ObjectCreator Instance { get { return instance; } }
/// <summary>
/// Static generic dictionary type used for creating new dictionary instances
/// </summary>
private Type GenericMapType = typeof(Dictionary<,>);
/// <summary>
/// Static generic list type used for creating new array instances
/// </summary>
private Type GenericListType = typeof(List<>);
private readonly Assembly execAssembly;
private readonly Assembly entryAssembly;
private readonly bool diffAssembly;
private readonly Type[] margs;
private readonly Type[] largs;
public delegate object CtorDelegate();
private Type ctorType = typeof(CtorDelegate);
Dictionary<NameCtorKey, CtorDelegate> ctors;
private ObjectCreator()
{
execAssembly = System.Reflection.Assembly.GetExecutingAssembly();
entryAssembly = System.Reflection.Assembly.GetEntryAssembly();
if (entryAssembly != null && execAssembly != entryAssembly) // entryAssembly returns null when running from NUnit
diffAssembly = true;
GenericMapType = typeof(Dictionary<,>);
GenericListType = typeof(List<>);
margs = new Type[2] { typeof(string), null };
largs = new Type[1] { null };
ctors = new Dictionary<NameCtorKey, CtorDelegate>();
}
public struct NameCtorKey : IEquatable<NameCtorKey>
{
public string name { get; private set; }
public Schema.Type type { get; private set; }
public NameCtorKey(string value1, Schema.Type value2)
: this()
{
name = value1;
type = value2;
}
public bool Equals(NameCtorKey other)
{
return Equals(other.name, name) && other.type == type;
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
return false;
if (obj.GetType() != typeof(NameCtorKey))
return false;
return Equals((NameCtorKey)obj);
}
public override int GetHashCode()
{
unchecked
{
return ((name != null ? name.GetHashCode() : 0) * 397) ^ type.GetHashCode();
}
}
public static bool operator ==(NameCtorKey left, NameCtorKey right)
{
return left.Equals(right);
}
public static bool operator !=(NameCtorKey left, NameCtorKey right)
{
return !left.Equals(right);
}
}
/// <summary>
/// Gets the type of the specified type name
/// </summary>
/// <param name="name">name of the object to get type of</param>
/// <param name="schemaType">schema type for the object</param>
/// <returns>Type</returns>
public Type GetType(string name, Schema.Type schemaType)
{
Type type;
if (diffAssembly)
{
// entry assembly different from current assembly, try entry assembly first
type = entryAssembly.GetType(name);
if (type == null) // now try current assembly and mscorlib
type = Type.GetType(name);
}
else
type = Type.GetType(name);
if (type == null) // type is still not found, need to loop through all loaded assemblies
{
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (Assembly assembly in assemblies)
{
type = assembly.GetType(name);
if (type != null)
break;
}
}
if (type == null)
throw new AvroException("Unable to find type " + name + " in all loaded assemblies");
if (schemaType == Schema.Type.Map)
{
margs[1] = type;
type = GenericMapType.MakeGenericType(margs);
}
else if (schemaType == Schema.Type.Array)
{
largs[0] = type;
type = GenericListType.MakeGenericType(largs);
}
return type;
}
/// <summary>
/// Gets the default constructor for the specified type
/// </summary>
/// <param name="name">name of object for the type</param>
/// <param name="schemaType">schema type for the object</param>
/// <param name="type">type of the object</param>
/// <returns>Default constructor for the type</returns>
public CtorDelegate GetConstructor(string name, Schema.Type schemaType, Type type)
{
ConstructorInfo ctorInfo = type.GetConstructor(Type.EmptyTypes);
if (ctorInfo == null)
throw new AvroException("Class " + name + " has no default constructor");
DynamicMethod dynMethod = new DynamicMethod("DM$OBJ_FACTORY_" + name, typeof(object), null, type, true);
ILGenerator ilGen = dynMethod.GetILGenerator();
ilGen.Emit(OpCodes.Nop);
ilGen.Emit(OpCodes.Newobj, ctorInfo);
ilGen.Emit(OpCodes.Ret);
return (CtorDelegate)dynMethod.CreateDelegate(ctorType);
}
/// <summary>
/// Creates new instance of the given type
/// </summary>
/// <param name="name">fully qualified name of the type</param>
/// <param name="schemaType">type of schema</param>
/// <returns>new object of the given type</returns>
public object New(string name, Schema.Type schemaType)
{
NameCtorKey key = new NameCtorKey(name, schemaType);
CtorDelegate ctor;
if (!ctors.TryGetValue(key, out ctor))
{
Type type = GetType(name, schemaType);
ctor = GetConstructor(name, schemaType, type);
ctors.Add(key, ctor);
}
return ctor();
}
}
}