#region Using Directives
using System;
using System.Reflection;
#endregion
#region MPFramework.AppCore.PlatformUtilities
///////////////////////////////////////////////////////////////////////////
//// Need Platform defs and the Attribute from PlatformUtilities.
///////////////////////////////////////////////////////////////////////////
namespace MPFramework.AppCore.PlatformUtilities
{
#region Enums
/// <summary>
/// This enumeration describes the platforms supported by a given method
/// or assembly, etc.. It is a flags enum since multiple platforms can be
/// supported by a given item.
/// </summary>
/// <remarks>
/// Note we don't use anything from PlatformID, since it is not platform-independent.
/// Silly, Huh? Also, we never want to support Win32s or anything lower
/// than NT.
/// </remarks>
// New suggestions have been Portable .Net and Intel. Well, OK, somebody has
// to sign up to do it!!! Don't everybody talk at once.... (KRM)
[System.Flags]
public enum CLRPlatformsSupported
{
/// <summary>
/// Good old Microsoft Windows NT. Server and XP are same for now.
/// Leave room for divergence later.....
/// </summary>
// (KRM) Prob'ly want to do Server-specific stuff the usual way in config.
// files.
MSWNT = 1,
/// <summary>
/// Microsoft CE.
/// </summary>
// We might do something with this if MS ever gets decent RT support
// of more than managed c++. Look at Java RTS, Microsoft!!
MSWCE = 4,
/// <summary>
/// For our courses.
/// </summary>
Rotor = 64,
/// <summary>
/// Microsoft running Mono.
/// </summary>
// We don't have any apps running the Mono CLI on Windows (we are not crazy),
// but it sure is nice to have it there for preliminary testing of Mono stuff!!
// We have finally settled the deep philosophical question: Is Mono's
// port to MSWindows a "Windows" category? We decided no....
MSMono = 128,
/// <summary>
/// Microsoft versions - 8 bits. Anything running on a MS OS.
/// </summary>
MicrosoftMask = 255,
/// <summary>
/// Microsoft Windows versions - 5 bits.
/// </summary>
MicrosoftWindowsMask = 31,
/// <summary>
/// Linux running Mono. Just dealing with WS for now.
/// </summary>
LinuxMono = 256,
/// <summary>
/// BSD's compilation/augmentation of Rotor.
/// </summary>
// (For Clem).
BSDRotor = 512,
/// <summary>
/// Unix versions (including Linux, et. al.) - 10 bits.
/// </summary>
UnixMask = 1023 << 8
}
#endregion
#region Attributes
/// <summary>
/// <para>
/// This attribute is supplied to indicate any assemblies, classes methods, etc.
/// that are platform-specific. It is useful in situations where it is difficult
/// to move platform-specific code into separate assemblies and provides a useful
/// marker for these items. The MPFramework can scan all assemblies in an AppDomain
/// when the AppDomain is loaded to check whether an item (class, method, etc.) is
/// not compatible with the current platform.
/// </para>
/// <para>
/// Note that many methods adorned with this attribute have the "__currentPlatform"
/// set as the property. Generally this means that the logic of the method is
/// platform-independent, but data referenced may be platform-dependent.
/// </para>
/// </summary>
[Serializable]
class PlatformSpecificAttribute : Attribute
{
#region Class Fields
static CLRPlatformsSupported _platformsSupported = 0;
#endregion // Class Fields
#region Constructors
/// <summary>
/// Default constructor just constructs it with a default value of 0.
/// Just use <c>[PlatformSpecific]</c> if the item is to simply be flagged
/// as being platform-dependent in some way.
/// </summary>
public PlatformSpecificAttribute() { }
#endregion // Constructors
#region Properties
/// <summary>
/// Sets/gets the enum. Examples:
/// <list type="bullet">
/// <item>
/// <description>
/// Use <c>[PlatformSpecific(PlatformsSupported = CLRPlatformsSupported.MSWNT)]</c>
/// to decorate an item that is supported under WindowsNT.
/// </description>
/// </item>
/// <item>
/// <description>
/// Use <c>[PlatformSpecific(PlatformsSupported
/// = CLRPlatformsSupported.MicrosoftWindowsMask & CLRPlatformsSupported.LinuxMono)]</c>
/// to indicate support for all Windows platforms and Mono on Linux.
/// </description>
/// </item>
/// </list>
/// </summary>
public CLRPlatformsSupported PlatformsSupported
{
get { return _platformsSupported; }
set { _platformsSupported = value; }
}
#endregion // Properties
}
#endregion // Attributes
}
#endregion // MPFramework.AppCore.PlatformUtilities
#region MPFramework.AppCore.Manufacturing
///////////////////////////////////////////////////////////////////////////
//// Few things from Manufacturing that Article needs.
///////////////////////////////////////////////////////////////////////////
namespace MPFramework.AppCore.Manufacturing
{
#region Class ManufacturingUtils
/// <summary>
/// Helper classes/methods for manufacturing Types in AppDomains.
/// </summary>
public class ManufacturingUtils
{
#region Class Fields
// ManufacturingErrorHandler m_manufacturintErrorHandler
#region Errors
/// <summary>
/// A constructor had bad construction parameters.
/// </summary>
// For copy/stub - need just a string
// static public readonly MPError BadConstructionParamsError;
static public readonly string BadConstructionParamsError
= "Bad Construction Parameters";
#endregion // Errors
#region Binding Flags
/// <summary>
/// This set of binding flags is used is for looking up items on a
/// type when we want to get all non-static public items.
/// </summary>
static public readonly BindingFlags PublicInstanceBindingFlags
= BindingFlags.Public | BindingFlags.Instance;
/// <summary>
/// This set of binding flags is used is for looking up items on a
/// type when we want to get all non-static items.
/// </summary>
static public readonly BindingFlags PublicOrPrivateInstanceBindingFlags
= BindingFlags.Instance | BindingFlags.Public |
BindingFlags.NonPublic;
/// <summary>
/// This set of binding flags is used is for looking up constituents on a
/// type when we want to get 'em all.
/// </summary>
static public readonly BindingFlags PublicPrivateStaticInstanceBindingFlags
= BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public |
BindingFlags.NonPublic;
/// <summary>
/// This set of binding flags is used for grabbing all fields, etc. in
/// an inheritance hierarchy.
/// </summary>
static public BindingFlags FlattenAndDisplayAllInstancesBindingFlags
= BindingFlags.Instance | BindingFlags.Public |
BindingFlags.NonPublic | BindingFlags.FlattenHierarchy;
#endregion // Binding Flags
#endregion // Class Fields
/// <summary>
/// Easiest is just to make this a nested class for the stub.
/// </summary>
public static class ManufacturingErrorHandler
{
/// <summary>
/// Stub for tests.
/// </summary>
public static void ProcessError(/* MPError */string instanceOfMPError, Exception thrownException,
Exception exceptionToThrow, bool showDetail, string userPrependString,
string userAppendString)
{
string printString;
if(userPrependString == null)
// No user string, so create it here.
userPrependString = String.Empty;
if(userAppendString == null)
// No user string, so create it here.
userAppendString = String.Empty;
printString = instanceOfMPError + userPrependString + userPrependString;
Console.WriteLine(printString);
throw new ApplicationException();
}
}
#endregion // Class ManufacturingUtils
}
}
#endregion // MPFramework.AppCore.Manufacturing
#region MPFramework.AppCore.Manufacturing.TypeHandling
///////////////////////////////////////////////////////////////////////////
//// Need DoesSecondTypeInheritFrom FirstType() from TypeHandling.
///////////////////////////////////////////////////////////////////////////
namespace MPFramework.AppCore.Manufacturing.TypeHandling
{
#region Delegates
/// <summary>
/// This is a delegate for filtering accessor objects (PropertyDescriptors, FieldInfos,
/// etc.) according to specific conditions on their <see cref="System.Attributes"/>.
/// The delegate is obviously very general, but is named according to it's original
/// purpose. Methods like <see cref="ICustomTypeDescriptor.GetProperties(Attributes[])"/>
/// offer filtering only based on the presence of certain attribute Types. This delegate
/// is designed to allow clients to perform more general filtering based on attributes.
/// </summary>
/// <param name="accessor">
/// This is a constituent accessor that may have attributes attached to it. It may be
/// <c>null</c>, in which case the method returns <c>true</c>.
/// </param>
/// <param name="attributes">
/// This is an array of <see cref="System.Attribute"/>s that are to be evaluated
/// as to whether a given accessor "passes" the criteria for inclusion. It may
/// be <c>null</c>, in which case the method returns <c>true</c>.
/// </param>
/// <returns>
/// <c>true</c> if the accessor passes the criteria.
/// </returns>
/// <remarks>
/// A simple example of a useful criterion that is not available in the
/// CLR is to check to see whether a given accessor does NOT have a certain
/// attribute. Much more complex scenarios are possible as in the
/// <see cref="LayeredSerializer"/>.
/// </remarks>
public delegate bool FilterAccessorByAttributes(object accessor, Attribute[] attributes);
#endregion // Delegates
#region Class TypeHandlingUtils
/// <summary>
/// Helper classes/methods for handling Types in AppDomains.
/// </summary>
public class TypeHandlingUtils
{
// Need a couple errors for tests.
#region Class Fields
/// <summary>
/// A client attempted to add a Type-specific Type handler factory to
/// a provider and neither the provider or the factory could produce
/// the factory's Type.
/// </summary>
// static public readonly MPError ProviderOrFactoryCantCreateTypeError;
static public readonly string ProviderOrFactoryCantCreateTypeError
= "ProviderOrFactoryCantCreateTypeError";
/// <summary>
/// A search for a named FieldInfo did not return a FieldInfo.
/// </summary>
//static public readonly MPError FailureToLocateFieldInfoError;
static public readonly string FailureToLocateFieldInfoError
= "FailureToLocateFieldInfoError";
/// <summary>
/// Failure casting one Type to another.
/// </summary>
//static public readonly MPError CastFailureError;
static public readonly string CastFailureError
= "CastFailureError";
// Couple copies for static constants.
static Type TypeOfMemberInfo = typeof(MemberInfo);
static Type TypeOfFieldInfo = typeof(FieldInfo);
static Type TypeOfAttribute = typeof(Attribute);
#endregion // Class Fields
/// <summary>
/// This method checks a given Type to see if it inherits from another
/// given Type.
/// </summary>
/// <param name="inheritedType">
/// This Type may be either an interface or a struct or class. If it is a
/// struct, the method returns <c>false</c>. If it is class, and the
/// <see paramref="typeToCheck"/> is an interface or struct, the method returns
/// <c>false</c>.
/// </param>
/// <param name="typeToCheck">
/// If this Type is a struct, the method returns <c>false</c> if the
/// <see paramref="inheritedType"/> is not an interface.
/// </param>
/// <returns>
/// Indicates whether <see paramref name="typeToCheck"> inherits from
/// <see paramref name="inheritedType">.
/// </returns>
/// <remarks>
/// Note, unfortunately, that this method is NOT lightweight. MS's GetInterfaces
/// method generates a new Type[] array every time.
/// </remarks>
public static bool DoesSecondTypeInheritFromFirstType(Type inheritedType, Type typeToCheck)
{
// Interface collection is always flat. Just loop over them.
foreach(Type iface in typeToCheck.GetInterfaces()) {
if(iface == inheritedType) {
return true;
}
}
// Gotta' climb the inheritance tree.
while(typeToCheck != typeof(System.Object)) {
if(inheritedType == typeToCheck) {
return true;
}
typeToCheck = typeToCheck.BaseType;
}
return false;
}
/// <summary>
/// Uses reflection to retrieve a specific <see cref="FieldInfo"/> for a field member
/// on a Type. This method has the ability to filter <see cref="FieldInfo"/> elements
/// according to general attribute settings.
/// </summary>
/// <param name="hostObjectType">
/// This is the Type of the object hosting the field. This cannot be <c>null</c>.
/// </param>
/// <param name="fieldName">
/// This name is the textual name of the field as known by the host,
/// not the fully-qualified name of the Type, such as <see cref="System.Drawing.Size"/>.
/// An example would be "autoScaleBaseSize" on a <see cref="System.Windows.Forms.Form"/>
/// host object. This cannot be <c>null</c>.
/// </param>
/// <param name="attributes">
/// This is an array of <see cref="System.Attribute"/>s that are returned
/// field is to be evaluated against within the <see cref="attributeFilter"/>.
/// If the field fails the evaluation, it is not returned.
/// </param>
/// <param name="attributeFilter">
/// This is an <see cref="FilterAccessorByAttributes"/> delegate that will be
/// used to evaluate the descriptor against the supplied set of attributes.
/// This filter must understand how to pull attributes from properties and
/// examine them. If this filter is <c>null</c> and attributes is
/// non-<c>null</c>, the standard CLR style attribute filtering is
/// applied. With this standard filtering, <see cref="FieldInfo"/>s are returned
/// only if they have attributes on the list of <see cref="attributes"/> supplied.
/// With this standard filtering, a single attribute match will qualify the field
/// for return.
/// </param>
/// <param name="bindingFlags">
/// These flags tell the CLR how to access fields on the Type. User can specify
/// public/private, etc.. See <see cref="System.Reflection.BindingFlags"/>.
/// </param>
/// <param name="throwException">
/// True to throw an exception when the named field cannot be located. This check
/// is made before filtering. Filtering may also cause the method to return
/// <c>null</c> if a located field does not meet the filtering criteria.
/// </param>
/// <returns>
/// A FieldInfo object, if one is found that passes the filtering criteria
/// or <c>null</c> if one is not found.
/// </returns>
/// <exception cref="ApplicationException">
/// When a <see cref="FieldInfo"/> with <see cref="fieldName"/> cannot be
/// found on the <see cref="hostType"/>, an exception: FailureToLocateFieldInfoError
/// is thrown if <see cref="throwException"/> is <c>true</c>.
/// </exception>
public static FieldInfo GetFieldInfo(Type hostObjectType, string fieldName,
Attribute[] attributes, FilterAccessorByAttributes attributeFilter,
BindingFlags bindingFlags, bool throwException)
{
// Grab it if it's here.
FieldInfo fieldInfo = hostObjectType.GetField(fieldName, bindingFlags);
// Got nothing is a problem if throwing exceptions.
if((fieldInfo == null) && (throwException)) {
TypeHandlingUtils.TypeHandlingErrorHandler.ProcessError(
TypeHandlingUtils.FailureToLocateFieldInfoError, null,
null, true, null,
"\nTYPE: " + hostObjectType.ToString() +
"\nFIELDNAME: " + fieldName + "\n");
}
// If we have no custom filter, just do the standard CLR attribute filter if we
// have attributes - otherwise use our custom filter.
if(attributes != null) {
// Plug in our standard filter if we need it.
if(attributeFilter == null)
attributeFilter = TypeHandlingUtils.FilterMembersByAnyAttributePresence;
// Apply the filter.
if(attributeFilter(fieldInfo, attributes)) return fieldInfo;
// Nope, didn't pass.
return null;
}
// No filtering at all - just return unconditionally.
return fieldInfo;
}
/// <summary>
/// This is a method for filtering <see cref="MemberInfo"/> objects according to
/// the presence or non presence of certain <see cref="System.Attribute"/>s. The
/// whole reason that we built this is that the CLR does not have a built-in
/// filtering option such as GetFields(BindingFlags, Attribute[]). This method
/// tests whether attributes match based on a<c>typeof(Attribute)</c> check.
/// </summary>
/// <param name="memberObject">
/// This is a <see cref="MemberInfo"/> that may have attributes attached to it.
/// It may be <c>null</c>, in which case the method returns <c>true</c>.
/// </param>
/// <param name="attributes">
/// This is an array of <see cref="System.Attribute"/>s that must be present on
/// the incoming field in order for it to "pass" the test criteria. If this argument
/// is <c>null</c>, the method always returns <c>true</c>. If
/// any of the attributes in this array is present on the field, it passes the test
/// and <c>true</c> is returned.
/// </param>
/// <returns>
/// <c>true</c> if the field passes the criteria.
/// </returns>
/// <exception> <see cref="ApplicationException"/> is thrown:
/// <c>"Problem casting between Types."</c> if the
/// <see paramref="memberObject"/> is not a <see cref="MemberInfo"/> Type.
/// </exception>
public static bool FilterMembersByAnyAttributePresence(System.Object memberObject,
Attribute[] attributes)
{
MemberInfo memberInfo;
// Get out if nothing to do.
if((attributes == null) || (memberObject == null)) return true;
// Can't survive a problem with the wrong Type.
if((memberInfo = (memberObject as MemberInfo)) == null) {
TypeHandlingUtils.TypeHandlingErrorHandler.ProcessError(
TypeHandlingUtils.CastFailureError, null,
null, true, null,
"\nFROMTYPE: " + memberObject.GetType().ToString() +
"\nTOTYPE: " + TypeOfMemberInfo.ToString() + "\n");
}
// Double loop - if we collide with anything, we pass.
foreach(Attribute memberAttribute in memberInfo.GetCustomAttributes(TypeOfAttribute, true))
foreach(Attribute incomingAttribute in attributes)
if(incomingAttribute.GetType() == memberAttribute.GetType()) return true;
// Doesn't have any of the attributes we require, so forget it.
return false;
}
/// <summary>
/// Easiest is just to make this a nested class for the stub. Same
/// deal as ManufacturingErrorHandler - but just call
/// ManufacturingErrorHandler.
/// </summary>
public static class TypeHandlingErrorHandler
{
/// <summary>
/// Stub for tests.
/// </summary>
public static void ProcessError(/* MPError */string instanceOfMPError, Exception thrownException,
Exception exceptionToThrow, bool showDetail, string userPrependString,
string userAppendString)
{
ManufacturingUtils.ManufacturingErrorHandler.ProcessError
(instanceOfMPError, thrownException,
exceptionToThrow, showDetail, userPrependString,
userAppendString);
}
}
}
#endregion // Class TypeHandlingUtils
}
#endregion // MPFramework.AppCore.Manufacturing.TypeHandling