#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.Globalization;
using System.Reflection;
using System.Text;
using Spring.Core;
using Spring.Core.TypeResolution;
using Spring.Util;
#endregion
namespace Spring.Objects.Factory.Config{
/// <summary>
/// <see cref="Spring.Objects.Factory.IFactoryObject"/> implementation that
/// retrieves a static or non-static <b>public</b> field value.
/// </summary>
/// <remarks>
/// <p>
/// Typically used for retrieving <b>public</b> constants.
/// </p>
/// </remarks>
/// <example>
/// <p>
/// The following example retrieves the <see cref="System.DBNull.Value"/> field value...
/// </p>
/// <code escaped="true">
/// <object id="dbnull" type="Spring.Objects.Factory.Config.FieldRetrievingFactoryObject, Spring.Core">
/// <property name="TargetType" value="System.DBNull"/>
/// <property name="TargetField" value="Value"/>
/// </object>
/// </code>
/// <p>
/// The previous example could also have been written using the convenience
/// <see cref="Spring.Objects.Factory.Config.FieldRetrievingFactoryObject.StaticField"/>
/// property, like so...
/// </p>
/// <code escaped="true">
/// <object id="dbnull" type="Spring.Objects.Factory.Config.FieldRetrievingFactoryObject, Spring.Core">
/// <property name="StaticField" value="System.DBNull.Value"/>
/// </object>
/// </code>
/// <p>
/// This class also implements the <see cref="Spring.Objects.Factory.IObjectNameAware"/>
/// interface
/// (<see cref="Spring.Objects.Factory.Config.FieldRetrievingFactoryObject.ObjectName"/>).
/// If the id (or name) of one's
/// <see cref="Spring.Objects.Factory.Config.FieldRetrievingFactoryObject"/>
/// object definition is set to the <see cref="System.Type.AssemblyQualifiedName"/>
/// of the <see langword="static"/> field to be retrieved, then the id (or
/// name) of one's object definition will be used for the name of the
/// <see langword="static"/> field lookup. See below for an example of this
/// concise style of definition.
/// </p>
/// <code escaped="true">
/// <!-- returns the value of the DBNull.Value field -->
/// <object id="System.DBNull.Value"
/// type="Spring.Objects.Factory.Config.FieldRetrievingFactoryObject, Spring.Core"/>
///
/// <!-- returns the value of the Type.Delimiter field -->
/// <object id="System.Type.Delimiter"
/// type="Spring.Objects.Factory.Config.FieldRetrievingFactoryObject, Spring.Core"/>
/// </code>
/// <p>
/// The usage for retrieving instance fields is similar. No example is shown
/// because public instance fields are <i>generally</i> bad practice; but if
/// you have some legacy code that exposes public instance fields, or if you
/// just really like coding public instance fields, then you can use this
/// <see cref="Spring.Objects.Factory.IFactoryObject"/> implementation to
/// retrieve such field values.
/// </p>
/// </example>
/// <author>Juergen Hoeller</author>
/// <author>Rick Evans (.NET)</author>
[Serializable]
public class FieldRetrievingFactoryObject : IFactoryObject, IInitializingObject, IObjectNameAware
{
private string targetField;
private object targetObject;
private Type targetType;
private FieldInfo field;
private string objectName;
private string staticField;
/// <summary>
/// The <see cref="System.Type.AssemblyQualifiedName"/> of the
/// <see langword="static"/> field to be retrieved.
/// </summary>
public string StaticField
{
set { this.staticField = value; }
}
/// <summary>
/// Set the name of the object in the object factory that created this object.
/// </summary>
/// <value>
/// The name of the object in the factory.
/// </value>
/// <remarks>
/// <p>
/// In the context of the
/// <see cref="Spring.Objects.Factory.Config.FieldRetrievingFactoryObject"/>
/// class, the
/// <see cref="Spring.Objects.Factory.Config.FieldRetrievingFactoryObject.ObjectName"/>
/// value will be interepreted as the value of the
/// <see cref="Spring.Objects.Factory.Config.FieldRetrievingFactoryObject.StaticField"/>
/// property if no value has been explicitly assigned to the
/// <see cref="Spring.Objects.Factory.Config.FieldRetrievingFactoryObject.StaticField"/>
/// property. This allows for concise object definitions with just an id or name;
/// see the class documentation for
/// <see cref="Spring.Objects.Factory.Config.FieldRetrievingFactoryObject"/>
/// for an example of this style of usage.
/// </p>
/// </remarks>
public string ObjectName
{
set { this.objectName = value; }
}
/// <summary>
/// The name of the field the value of which is to be retrieved.
/// </summary>
/// <remarks>
/// <p>
/// If the
/// <see cref="Spring.Objects.Factory.Config.FieldRetrievingFactoryObject.TargetObject"/>
/// has been set (and is not <cref lang="null"/>), then the value of this property
/// refers to an instance field name; it otherwise refers to a <see langword="static"/>
/// field name.
/// </p>
/// </remarks>
public string TargetField
{
get { return targetField; }
set { targetField = value; }
}
/// <summary>
/// The object instance on which the field is defined.
/// </summary>
public object TargetObject
{
get { return targetObject; }
set { targetObject = value; }
}
/// <summary>
/// The <see cref="System.Type"/> on which the field is defined.
/// </summary>
public Type TargetType
{
get { return targetType; }
set { targetType = value; }
}
/// <summary>
/// The <see cref="System.Type"/> of object that this
/// <see cref="Spring.Objects.Factory.IFactoryObject"/> creates, or
/// <cref lang="null"/> if not known in advance.
/// </summary>
public Type ObjectType
{
get { return (this.field == null) ? null : this.field.FieldType; }
}
/// <summary>
/// Is the object managed by this factory a singleton or a prototype?
/// </summary>
public bool IsSingleton
{
get { return true; }
}
/// <summary>
/// Invoked by an <see cref="Spring.Objects.Factory.IObjectFactory"/>
/// after it has set all object properties supplied
/// (and satisfied <see cref="Spring.Objects.Factory.IObjectFactoryAware"/>
/// and ApplicationContextAware).
/// </summary>
/// <remarks>
/// <p>
/// This method allows the object instance to perform initialization only
/// possible when all object properties have been set and to throw an
/// exception in the event of misconfiguration.
/// </p>
/// </remarks>
/// <exception cref="System.Exception">
/// In the event of misconfiguration (such as failure to set an essential
/// property) or if initialization fails.
/// </exception>
public void AfterPropertiesSet()
{
if (TargetType != null && TargetObject != null)
{
throw new ArgumentException(
"Only one of the TargetType or TargetObject properties can be set, not both.");
}
if (TargetType == null && TargetObject == null)
{
if (TargetField != null)
{
throw new ArgumentException(
"Specify the TargetType or TargetObject property in combination with the TargetField property.");
}
// if no other property specified, use the object name for the static field expression...
if (StringUtils.IsNullOrEmpty(this.staticField))
{
this.staticField = this.objectName;
}
ParseAndSetFromStaticFieldValue();
}
if (TargetField == null)
{
throw new ArgumentException("The TargetField property is required.");
}
BindingFlags fieldFlags = BindingFlags.Public | BindingFlags.IgnoreCase;
if (TargetObject == null)
{
// a static field...
fieldFlags |= BindingFlags.Static;
}
else
{
// an instance field...
fieldFlags |= BindingFlags.Instance;
TargetType = TargetObject.GetType();
}
this.field = targetType.GetField(TargetField, fieldFlags);
if (this.field == null)
{
throw new ObjectDefinitionStoreException(
string.Format(
CultureInfo.InvariantCulture,
"No such field '{0}' on [{1}].", TargetField,
TargetObject == null ? TargetType : TargetObject));
}
}
/// <summary>
/// Return an instance (possibly shared or independent) of the object
/// managed by this factory.
/// </summary>
/// <returns>
/// An instance (possibly shared or independent) of the object managed by
/// this factory.
/// </returns>
/// <see cref="Spring.Objects.Factory.IFactoryObject.GetObject"/>
public object GetObject()
{
if (TargetObject != null)
{
return this.field.GetValue(TargetObject);
}
else
{
return this.field.GetValue(null);
}
}
private void ParseAndSetFromStaticFieldValue()
{
TypeAssemblyHolder info = new TypeAssemblyHolder(this.staticField);
// the info.TypeName should now contains the Type name, followed by a
// period, followed by the name of the field
int lastDotIndex = info.TypeName.LastIndexOf('.');
if (lastDotIndex == -1
|| lastDotIndex == info.TypeName.Length)
{
throw new ArgumentException(
"The value passed to the StaticField property must be a fully " +
"qualified Type plus field name: " +
"e.g. 'Example.MyExampleClass.MyField, MyAssembly'");
}
string typeName = info.TypeName.Substring(0, lastDotIndex);
string fieldName = info.TypeName.Substring(lastDotIndex + 1);
StringBuilder buffer = new StringBuilder(typeName);
if (info.IsAssemblyQualified)
{
buffer.Append(TypeAssemblyHolder.TypeAssemblySeparator);
buffer.Append(info.AssemblyName);
}
TargetType = TypeResolutionUtils.ResolveType(buffer.ToString());
TargetField = fieldName;
}
}
}
|