#region License
/*
* Copyright 2002-2005 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion
#region Imports
using System;
using System.Collections;
using System.Globalization;
using System.Reflection;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Proxies;
using Common.Logging;
using Spring.Objects;
using Spring.Reflection.Dynamic;
#endregion
namespace Spring.Util{
/// <summary>
/// Helper methods with regard to objects, types, properties, etc.
/// </summary>
/// <remarks>
/// <p>
/// Not intended to be used directly by applications.
/// </p>
/// </remarks>
/// <author>Rod Johnson</author>
/// <author>Juergen Hoeller</author>
/// <author>Rick Evans (.NET)</author>
public sealed class ObjectUtils
{
/// <summary>
/// The <see cref="Common.Logging.ILog"/> instance for this class.
/// </summary>
private static readonly ILog log = LogManager.GetLogger(typeof(ObjectUtils));
#region Constants
/// <summary>
/// An empty object array.
/// </summary>
public static readonly object[] EmptyObjects = new object[] { };
private static MethodInfo GetHashCodeMethodInfo = null;
#endregion
static ObjectUtils()
{
Type type = typeof(object);
GetHashCodeMethodInfo = type.GetMethod("GetHashCode");
}
#region Constructor (s) / Destructor
// CLOVER:OFF
/// <summary>
/// Creates a new instance of the <see cref="Spring.Util.ObjectUtils"/> class.
/// </summary>
/// <remarks>
/// <p>
/// This is a utility class, and as such exposes no public constructors.
/// </p>
/// </remarks>
private ObjectUtils()
{
}
// CLOVER:ON
#endregion
#region Methods
/// <summary>
/// Instantiates the type using the assembly specified to load the type.
/// </summary>
/// <remarks>This is a convenience in the case of needing to instantiate a type but not
/// wanting to specify in the string the version, culture and public key token.</remarks>
/// <param name="assembly">The assembly.</param>
/// <param name="typeName">Name of the type.</param>
/// <returns></returns>
/// <exception cref="System.ArgumentNullException">
/// If the <paramref name="assembly"/> or <paramref name="typeName"/> is <see langword="null"/>
/// </exception>
/// <exception cref="Spring.Util.FatalReflectionException">
/// If cannot load the type from the assembly or the call to <code>InstantiateType(Type)</code> fails.
/// </exception>
public static object InstantiateType(Assembly assembly, string typeName)
{
AssertUtils.ArgumentNotNull(assembly, "assembly");
AssertUtils.ArgumentNotNull(typeName, "typeName");
Type resolvedType = assembly.GetType(typeName, false, false);
if (resolvedType == null)
{
throw new FatalReflectionException(
string.Format(
CultureInfo.InvariantCulture, "Cannot load type named [{0}] from assembly [{1}].", typeName, assembly));
}
return InstantiateType(resolvedType);
}
/// <summary>
/// Convenience method to instantiate a <see cref="System.Type"/> using
/// its no-arg constructor.
/// </summary>
/// <remarks>
/// <p>
/// As this method doesn't try to instantiate <see cref="System.Type"/>s
/// by name, it should avoid <see cref="System.Type"/> loading issues.
/// </p>
/// </remarks>
/// <param name="type">
/// The <see cref="System.Type"/> to instantiate*
/// </param>
/// <returns>A new instance of the <see cref="System.Type"/>.</returns>
/// <exception cref="System.ArgumentNullException">
/// If the <paramref name="type"/> is <see langword="null"/>
/// </exception>
/// <exception cref="Spring.Util.FatalReflectionException">
/// If the <paramref name="type"/> is an abstract class, an interface,
/// an open generic type or does not have a public no-argument constructor.
/// </exception>
public static object InstantiateType(Type type)
{
AssertUtils.ArgumentNotNull(type, "type");
ConstructorInfo constructor = GetZeroArgConstructorInfo(type);
return ObjectUtils.InstantiateType(constructor, ObjectUtils.EmptyObjects);
}
/// <summary>
/// Gets the zero arg ConstructorInfo object, if the type offers such functionality.
/// </summary>
/// <param name="type">The type.</param>
/// <returns>Zero argument ConstructorInfo</returns>
/// <exception cref="FatalReflectionException">
/// If the type is an interface, abstract, open generic type, or does not have a zero-arg constructor.
/// </exception>
public static ConstructorInfo GetZeroArgConstructorInfo(Type type)
{
IsInstantiable(type);
ConstructorInfo constructor = type.GetConstructor(Type.EmptyTypes);
if (constructor == null)
{
throw new FatalReflectionException(
string.Format(
CultureInfo.InvariantCulture, "Cannot instantiate a class that does not have a public no-argument constructor [{0}].", type));
}
return constructor;
}
/// <summary>
/// Determines whether the specified type is instantiable, i.e. not an interface, abstract class or contains
/// open generic type parameters.
/// </summary>
/// <param name="type">The type.</param>
public static void IsInstantiable(Type type)
{
if (type.IsInterface)
{
throw new FatalReflectionException(
string.Format(
CultureInfo.InvariantCulture, "Cannot instantiate an interface [{0}].", type));
}
if (type.IsAbstract)
{
throw new FatalReflectionException(
string.Format(
CultureInfo.InvariantCulture, "Cannot instantiate an abstract class [{0}].", type));
}
#if NET_2_0
if (type.ContainsGenericParameters)
{
throw new FatalReflectionException(
string.Format(
CultureInfo.InvariantCulture, "Cannot instantiate an open generic type [{0}].", type));
}
#endif
}
/// <summary>
/// Convenience method to instantiate a <see cref="System.Type"/> using
/// the given constructor.
/// </summary>
/// <remarks>
/// <p>
/// As this method doesn't try to instantiate <see cref="System.Type"/>s
/// by name, it should avoid <see cref="System.Type"/> loading issues.
/// </p>
/// </remarks>
/// <param name="constructor">
/// The constructor to use for the instantiation.
/// </param>
/// <param name="arguments">
/// The arguments to be passed to the constructor.
/// </param>
/// <returns>A new instance.</returns>
/// <exception cref="System.ArgumentNullException">
/// If the <paramref name="constructor"/> is <see langword="null"/>
/// </exception>
/// <exception cref="Spring.Util.FatalReflectionException">
/// If the <paramref name="constructor"/>'s declaring type is an abstract class,
/// an interface, an open generic type or does not have a public no-argument constructor.
/// </exception>
public static object InstantiateType(ConstructorInfo constructor, object[] arguments)
{
AssertUtils.ArgumentNotNull(constructor, "constructor");
if (log.IsTraceEnabled) log.Trace(string.Format("instantiating type [{0}] using constructor [{1}]", constructor.DeclaringType, constructor));
if (constructor.DeclaringType.IsInterface)
{
throw new FatalReflectionException(
string.Format(
CultureInfo.InvariantCulture, "Cannot instantiate an interface [{0}].", constructor.DeclaringType));
}
if (constructor.DeclaringType.IsAbstract)
{
throw new FatalReflectionException(
string.Format(
CultureInfo.InvariantCulture, "Cannot instantiate an abstract class [{0}].", constructor.DeclaringType));
}
#if NET_2_0
if (constructor.DeclaringType.ContainsGenericParameters)
{
throw new FatalReflectionException(
string.Format(
CultureInfo.InvariantCulture, "Cannot instantiate an open generic type [{0}].", constructor.DeclaringType));
}
#endif
try
{
// replaced with SafeConstructor() to avoid nasty "TargetInvocationException"s in NET >= 2.0
return (new SafeConstructor(constructor)).Invoke(arguments);
}
catch (Exception ex)
{
Type ctorType = constructor.DeclaringType;
throw new FatalReflectionException(
string.Format(
CultureInfo.InvariantCulture,
"Cannot instantiate Type [{0}] using ctor [{1}] : '{2}'",
constructor.DeclaringType, constructor, ex.Message),
ex);
}
}
/// <summary>
/// Checks whether the supplied <paramref name="instance"/> is not a transparent proxy and is
/// assignable to the supplied <paramref name="type"/>.
/// </summary>
/// <remarks>
/// <p>
/// Neccessary when dealing with server-activated remote objects, because the
/// object is of the type TransparentProxy and regular <c>is</c> testing for assignable
/// types does not work.
/// </p>
/// <p>
/// Transparent proxy instances always return <see langword="true"/> when tested
/// with the <c>'is'</c> operator (C#). This method only checks if the object
/// is assignable to the type if it is not a transparent proxy.
/// </p>
/// </remarks>
/// <param name="type">The target <see cref="System.Type"/> to be checked.</param>
/// <param name="instance">The value that should be assigned to the type.</param>
/// <returns>
/// <see langword="true"/> if the supplied <paramref name="instance"/> is not a
/// transparent proxy and is assignable to the supplied <paramref name="type"/>.
/// </returns>
public static bool IsAssignableAndNotTransparentProxy(Type type, object instance)
{
if (!RemotingServices.IsTransparentProxy(instance))
{
return IsAssignable(type, instance);
}
return false;
}
/// <summary>
/// Determine if the given <see cref="System.Type"/> is assignable from the
/// given value, assuming setting by reflection and taking care of transparent proxies.
/// </summary>
/// <remarks>
/// <p>
/// Considers primitive wrapper classes as assignable to the
/// corresponding primitive types.
/// </p>
/// <p>
/// For example used in an object factory's constructor resolution.
/// </p>
/// </remarks>
/// <param name="type">The target <see cref="System.Type"/>.</param>
/// <param name="obj">The value that should be assigned to the type.</param>
/// <returns>True if the type is assignable from the value.</returns>
public static bool IsAssignable(Type type, object obj)
{
AssertUtils.ArgumentNotNull(type, "type");
if (!type.IsPrimitive && obj == null)
{
return true;
}
if (RemotingServices.IsTransparentProxy(obj))
{
RealProxy rp = RemotingServices.GetRealProxy(obj);
if (rp is IRemotingTypeInfo)
{
return ((IRemotingTypeInfo) rp).CanCastTo(type, obj);
}
else if (rp != null)
{
type = rp.GetProxiedType();
}
if (type == null)
{
// cannot decide
return false;
}
}
return (type.IsInstanceOfType(obj) ||
(type.Equals(typeof(bool)) && obj is Boolean) ||
(type.Equals(typeof(byte)) && obj is Byte) ||
(type.Equals(typeof(char)) && obj is Char) ||
(type.Equals(typeof(sbyte)) && obj is SByte) ||
(type.Equals(typeof(int)) && obj is Int32) ||
(type.Equals(typeof(short)) && obj is Int16) ||
(type.Equals(typeof(long)) && obj is Int64) ||
(type.Equals(typeof(float)) && obj is Single) ||
(type.Equals(typeof(double)) && obj is Double));
}
/// <summary>
/// Check if the given <see cref="System.Type"/> represents a
/// "simple" property,
/// i.e. a primitive, a <see cref="System.String"/>, a
/// <see cref="System.Type"/>, or a corresponding array.
/// </summary>
/// <remarks>
/// <p>
/// Used to determine properties to check for a "simple" dependency-check.
/// </p>
/// </remarks>
/// <param name="type">
/// The <see cref="System.Type"/> to check.
/// </param>
public static bool IsSimpleProperty(Type type)
{
return type.IsPrimitive
|| type.Equals(typeof(string))
|| type.Equals(typeof(string[]))
|| IsPrimitiveArray(type)
|| type.Equals(typeof(Type))
|| type.Equals(typeof(Type[]));
}
/// <summary>
/// Check if the given class represents a primitive array,
/// i.e. boolean, byte, char, short, int, long, float, or double.
/// </summary>
public static bool IsPrimitiveArray(Type type)
{
return typeof(bool[]).Equals(type)
|| typeof(sbyte[]).Equals(type)
|| typeof(char[]).Equals(type)
|| typeof(short[]).Equals(type)
|| typeof(int[]).Equals(type)
|| typeof(long[]).Equals(type)
|| typeof(float[]).Equals(type)
|| typeof(double[]).Equals(type);
}
/// <summary>
/// Determines whether the specified array is null or empty.
/// </summary>
/// <param name="array">The array to check.</param>
/// <returns>
/// <c>true</c> if the specified array is null empty; otherwise, <c>false</c>.
/// </returns>
public static bool IsEmpty(object[] array)
{
return (array == null || array.Length == 0);
}
/// <summary>
/// Determine if the given objects are equal, returning <see langword="true"/>
/// if both are <see langword="null"/> respectively <see langword="false"/>
/// if only one is <see langword="null"/>.
/// </summary>
/// <param name="o1">The first object to compare.</param>
/// <param name="o2">The second object to compare.</param>
/// <returns>
/// <see langword="true"/> if the given objects are equal.
/// </returns>
public static bool NullSafeEquals(object o1, object o2)
{
return (o1 == o2 || (o1 != null && o1.Equals(o2)));
}
/// <summary>
/// Returns the first element in the supplied <paramref name="enumerator"/>.
/// </summary>
/// <param name="enumerator">
/// The <see cref="System.Collections.IEnumerator"/> to use to enumerate
/// elements.
/// </param>
/// <returns>
/// The first element in the supplied <paramref name="enumerator"/>.
/// </returns>
/// <exception cref="System.IndexOutOfRangeException">
/// If the supplied <paramref name="enumerator"/> did not have any elements.
/// </exception>
public static object EnumerateFirstElement(IEnumerator enumerator)
{
return ObjectUtils.EnumerateElementAtIndex(enumerator, 0);
}
/// <summary>
/// Returns the first element in the supplied <paramref name="enumerable"/>.
/// </summary>
/// <param name="enumerable">
/// The <see cref="System.Collections.IEnumerable"/> to use to enumerate
/// elements.
/// </param>
/// <returns>
/// The first element in the supplied <paramref name="enumerable"/>.
/// </returns>
/// <exception cref="System.IndexOutOfRangeException">
/// If the supplied <paramref name="enumerable"/> did not have any elements.
/// </exception>
/// <exception cref="System.ArgumentNullException">
/// If the supplied <paramref name="enumerable"/> is <see langword="null"/>.
/// </exception>
public static object EnumerateFirstElement(IEnumerable enumerable)
{
AssertUtils.ArgumentNotNull(enumerable, "enumerable");
return ObjectUtils.EnumerateElementAtIndex(enumerable.GetEnumerator(), 0);
}
/// <summary>
/// Returns the element at the specified index using the supplied
/// <paramref name="enumerator"/>.
/// </summary>
/// <param name="enumerator">
/// The <see cref="System.Collections.IEnumerator"/> to use to enumerate
/// elements until the supplied <paramref name="index"/> is reached.
/// </param>
/// <param name="index">
/// The index of the element in the enumeration to return.
/// </param>
/// <returns>
/// The element at the specified index using the supplied
/// <paramref name="enumerator"/>.
/// </returns>
/// <exception cref="System.ArgumentOutOfRangeException">
/// If the supplied <paramref name="index"/> was less than zero, or the
/// supplied <paramref name="enumerator"/> did not contain enough elements
/// to be able to reach the supplied <paramref name="index"/>.
/// </exception>
public static object EnumerateElementAtIndex(IEnumerator enumerator, int index)
{
if (index < 0)
{
throw new ArgumentOutOfRangeException();
}
object element = null;
int i = 0;
while (enumerator.MoveNext())
{
element = enumerator.Current;
if (++i > index)
{
break;
}
}
if (i < index)
{
throw new ArgumentOutOfRangeException();
}
return element;
}
/// <summary>
/// Returns the element at the specified index using the supplied
/// <paramref name="enumerable"/>.
/// </summary>
/// <param name="enumerable">
/// The <see cref="System.Collections.IEnumerable"/> to use to enumerate
/// elements until the supplied <paramref name="index"/> is reached.
/// </param>
/// <param name="index">
/// The index of the element in the enumeration to return.
/// </param>
/// <returns>
/// The element at the specified index using the supplied
/// <paramref name="enumerable"/>.
/// </returns>
/// <exception cref="System.ArgumentOutOfRangeException">
/// If the supplied <paramref name="index"/> was less than zero, or the
/// supplied <paramref name="enumerable"/> did not contain enough elements
/// to be able to reach the supplied <paramref name="index"/>.
/// </exception>
/// <exception cref="System.ArgumentNullException">
/// If the supplied <paramref name="enumerable"/> is <see langword="null"/>.
/// </exception>
public static object EnumerateElementAtIndex(IEnumerable enumerable, int index)
{
AssertUtils.ArgumentNotNull(enumerable, "enumerable");
return ObjectUtils.EnumerateElementAtIndex(enumerable.GetEnumerator(), index);
}
#endregion
/// <summary>
/// Gets the qualified name of the given method, consisting of
/// fully qualified interface/class name + "." method name.
/// </summary>
/// <param name="method">The method.</param>
/// <returns>qualified name of the method.</returns>
public static string GetQualifiedMethodName(MethodInfo method)
{
AssertUtils.ArgumentNotNull(method, "method", "MethodInfo must not be null");
return method.DeclaringType.FullName + "." + method.Name;
}
/// <summary>
/// Return a String representation of an object's overall identity.
/// </summary>
/// <param name="obj">The object (may be <code>null</code>).</param>
/// <returns>The object's identity as String representation,
/// or an empty String if the object was <code>null</code>
/// </returns>
public static object IdentityToString(object obj)
{
if (obj == null)
{
return string.Empty;
}
return obj.GetType().FullName + "@" + GetIdentityHexString(obj);
}
/// <summary>
/// Gets a hex String form of an object's identity hash code.
/// </summary>
/// <param name="obj">The obj.</param>
/// <returns>The object's identity code in hex notation</returns>
public static string GetIdentityHexString(object obj)
{
int hashcode = (int)GetHashCodeMethodInfo.Invoke(obj, null);
return hashcode.ToString("X6");
}
}
}
|