#region License
/*
* Copyright 2002-2006 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.Collections.Specialized;
using System.IO;
using System.Resources;
using Spring.Core.IO;
using Spring.Objects.Factory.Config;
using Spring.Util;
#endregion
namespace Spring.Objects.Factory.Support{
/// <summary>
/// Object definition reader for a simple properties format.
/// </summary>
/// <remarks>
/// Provides object definition registration methods for
/// <see cref="System.Collections.IDictionary"/> and
/// <see cref="System.Resources.ResourceSet"/> instances. Typically applied to a
/// <see cref="Spring.Objects.Factory.Support.DefaultListableObjectFactory"/>.
/// </remarks>
/// <author>Rod Johnson</author>
/// <author>Juergen Hoeller</author>
/// <author>Simon White (.NET)</author>
public class PropertiesObjectDefinitionReader : AbstractObjectDefinitionReader
{
/// <summary>
/// Value of a T/F attribute that represents true.
/// Anything else represents false. Case seNsItive.
/// </summary>
public const string TrueValue = "true";
/// <summary>
/// Separator between object name and property name.
/// </summary>
public const string Separator = ".";
/// <summary>
/// Prefix for the class property of a root object definition.
/// </summary>
public const string ClassKey = "class";
/// <summary>
/// Special string added to distinguish if the object will be
/// a singleton.
/// </summary>
/// <remarks>
/// <p>
/// Default is true.
/// </p>
/// </remarks>
/// <example>
/// <p>
/// owner.(singleton)=true
/// </p>
/// </example>
public const string SingletonKey = "(singleton)";
/// <summary>
/// Special string added to distinguish if the object will be
/// lazily initialised.
/// </summary>
/// <remarks>
/// <p>
/// Default is false.
/// </p>
/// </remarks>
/// <example>
/// <p>
/// owner.(lazy-init)=true
/// </p>
/// </example>
public const string LazyInitKey = "(lazy-init)";
/// <summary>
/// Reserved "property" to indicate the parent of a child object definition.
/// </summary>
public const string ParentKey = "parent";
/// <summary>
/// Property suffix for references to other objects in the current
/// <see cref="Spring.Objects.Factory.IObjectFactory"/>: e.g.
/// owner.dog(ref)=fido.
/// </summary>
/// <remarks>
/// <p>
/// Whether this is a reference to a singleton or a prototype
/// will depend on the definition of the target object.
/// </p>
/// </remarks>
public const string RefSuffix = "(ref)";
/// <summary>
/// Prefix before values referencing other objects.
/// </summary>
public const string RefPrefix = "*";
private string _defaultParentObject = string.Empty;
private IObjectDefinitionFactory _objectDefinitionFactory = new DefaultObjectDefinitionFactory();
/// <summary>
/// Name of default parent object
/// </summary>
public string DefaultParentObject
{
get { return _defaultParentObject; }
set { this._defaultParentObject = value; }
}
/// <summary>
/// Gets or sets object definition factory to use.
/// </summary>
public IObjectDefinitionFactory ObjectDefinitionFactory
{
get { return _objectDefinitionFactory; }
set { _objectDefinitionFactory = value; }
}
/// <summary>
/// Creates a new instance of the
/// <see cref="Spring.Objects.Factory.Support.PropertiesObjectDefinitionReader"/>
/// class.
/// </summary>
/// <param name="registry">
/// The <see cref="Spring.Objects.Factory.Support.IObjectDefinitionRegistry"/>
/// instance that this reader works on.
/// </param>
public PropertiesObjectDefinitionReader(IObjectDefinitionRegistry registry)
: base(registry)
{}
/// <summary>
/// Load object definitions from the supplied <paramref name="resource"/>.
/// </summary>
/// <param name="resource">
/// The resource for the object definitions that are to be loaded.
/// </param>
/// <returns>
/// The number of object definitions that were loaded.
/// </returns>
/// <exception cref="Spring.Objects.ObjectsException">
/// In the case of loading or parsing errors.
/// </exception>
public override int LoadObjectDefinitions(IResource resource)
{
return LoadObjectDefinitions(resource, string.Empty);
}
/// <summary>
/// Load object definitions from the specified properties file.
/// </summary>
/// <param name="resource">
/// The resource descriptor for the properties file.
/// </param>
/// <param name="prefix">
/// The match or filter for object definition names, e.g. 'objects.'
/// </param>
/// <exception cref="Spring.Objects.ObjectsException">in case of loading or parsing errors</exception>
/// <returns>the number of object definitions found</returns>
public int LoadObjectDefinitions(IResource resource, string prefix)
{
Properties props = new Properties();
try
{
Stream str = resource.InputStream;
try
{
props.Load(str);
}
finally
{
str.Close();
}
return RegisterObjectDefinitions(props, prefix, resource.Description);
}
catch (IOException ex)
{
throw new ObjectDefinitionStoreException("IOException parsing properties from " + resource, ex);
}
}
/// <summary>
/// Register object definitions contained in a
/// <see cref="System.Resources.ResourceSet"/>, using all property keys (i.e.
/// not filtering by prefix).
/// </summary>
/// <param name="rs">
/// The <see cref="System.Resources.ResourceSet"/> containing object definitions.
/// </param>
/// <exception cref="Spring.Objects.ObjectsException">
/// In case of loading or parsing errors.
/// </exception>
/// <returns>The number of object definitions registered.</returns>
public int RegisterObjectDefinitions(ResourceSet rs)
{
return RegisterObjectDefinitions(rs, string.Empty);
}
/// <summary>
/// Register object definitions contained in a
/// <see cref="System.Resources.ResourceSet"/>.
/// </summary>
/// <remarks>
/// <p>
/// Similar syntax as for an <see cref="System.Collections.IDictionary"/>.
/// This method is useful to enable standard .NET internationalization support.
/// </p>
/// </remarks>
/// <param name="rs">
/// The <see cref="System.Resources.ResourceSet"/> containing object definitions.
/// </param>
/// <param name="prefix">
/// The match or filter for object definition names, e.g. 'objects.'
/// </param>
/// <exception cref="Spring.Objects.ObjectsException">
/// In case of loading or parsing errors.
/// </exception>
/// <returns>The number of object definitions registered.</returns>
public int RegisterObjectDefinitions(ResourceSet rs, string prefix)
{
#if ! NET_1_0
// Simply create a map and call overloaded method
IDictionary id = new Hashtable();
foreach (DictionaryEntry de in rs)
{
id.Add(de.Key, de.Value);
}
return RegisterObjectDefinitions(id, prefix);
#else
throw new NotSupportedException("Operation not supported on NET 1.0");
#endif
}
/// <summary>
/// Register object definitions contained in an
/// <see cref="System.Collections.IDictionary"/>, using all property keys
/// (i.e. not filtering by prefix).
/// </summary>
/// <param name="id">
/// The <see cref="System.Collections.IDictionary"/> containing object definitions.
/// </param>
/// <exception cref="Spring.Objects.ObjectsException">
/// In case of loading or parsing errors.
/// </exception>
/// <returns>The number of object definitions registered.</returns>
public int RegisterObjectDefinitions(IDictionary id)
{
return RegisterObjectDefinitions(id, string.Empty);
}
/// <summary>
/// Registers object definitions contained in an <see cref="System.Collections.Specialized.NameValueCollection"/>
/// using all property keys ( i.e. not filtering by prefix )
/// </summary>
/// <param name="nameValueCollection">The <see cref="System.Collections.Specialized.NameValueCollection"/> containing
/// object definitions.
/// </param>
/// <exception cref="Spring.Objects.ObjectsException">
/// In case of loading or parsing errors.
/// </exception>
/// <returns>The number of object definitions registered.</returns>
public int RegisterObjectDefinitions(NameValueCollection nameValueCollection)
{
IDictionary id = new Hashtable();
foreach (DictionaryEntry de in nameValueCollection)
{
id.Add(de.Key, de.Value);
}
return RegisterObjectDefinitions(id);
}
/// <summary>
/// Register object definitions contained in a
/// <see cref="System.Collections.IDictionary"/>.
/// </summary>
/// <remarks>
/// <p>
/// Ignores ineligible properties.
/// </p>
/// </remarks>
/// <param name="id">IDictionary name -> property (String or Object). Property values
/// will be strings if coming from a Properties file etc. Property names
/// (keys) must be strings. Type keys must be strings.
/// </param>
/// <param name="prefix">
/// The match or filter within the keys in the map: e.g. 'objects.'
/// </param>
/// <exception cref="Spring.Objects.ObjectsException">
/// In case of loading or parsing errors.
/// </exception>
/// <returns>The number of object definitions found.</returns>
public int RegisterObjectDefinitions(IDictionary id, string prefix)
{
return RegisterObjectDefinitions(id, prefix, "(no description)");
}
/// <summary>
/// Register object definitions contained in a
/// <see cref="System.Collections.IDictionary"/>.
/// </summary>
/// <remarks>
/// <p>
/// Ignores ineligible properties.
/// </p>
/// </remarks>
/// <param name="id">IDictionary name -> property (String or Object). Property values
/// will be strings if coming from a Properties file etc. Property names
/// (keys) must be strings. Type keys must be strings.
/// </param>
/// <param name="prefix">
/// The match or filter within the keys in the map: e.g. 'objects.'
/// </param>
/// <param name="resourceDescription">
/// The description of the resource that the
/// <see cref="System.Collections.IDictionary"/> came from (for logging purposes).
/// </param>
/// <exception cref="Spring.Objects.ObjectsException">
/// In case of loading or parsing errors.
/// </exception>
/// <returns>The number of object definitions found.</returns>
public int RegisterObjectDefinitions(
IDictionary id, string prefix, string resourceDescription)
{
if (prefix == null)
{
prefix = string.Empty;
}
int objectCount = 0;
foreach (string key in id.Keys)
{
if (key.StartsWith(prefix))
{
// Key is of form prefix<name>.property
string nameAndProperty = key.Substring(prefix.Length);
int sepIndx = nameAndProperty.IndexOf(Separator);
if (sepIndx != -1)
{
string name = nameAndProperty.Substring(0, sepIndx);
#region Instrumentation
if (log.IsDebugEnabled)
{
log.Debug("Found object name '" + name + "'");
}
#endregion
if (!Registry.ContainsObjectDefinition(name))
{
++objectCount;
}
RegisterObjectDefinition(name, id, prefix + name, resourceDescription);
}
else
{
// Ignore it: it wasn't a valid object name and property,
// although it did start with the required prefix
#region Instrumentation
if (log.IsDebugEnabled)
{
log.Debug("Invalid object name and property [" + nameAndProperty + "]");
}
#endregion
}
} // if the key started with the prefix we're looking for
} // while there are more keys
return objectCount;
}
/// <summary>
/// Get all property values, given a prefix (which will be stripped)
/// and add the object they define to the factory with the given name
/// </summary>
/// <param name="name">The name of the object to define.</param>
/// <param name="id">
/// The <see cref="System.Collections.IDictionary"/> containing string pairs.
/// </param>
/// <param name="prefix">The prefix of each entry, which will be stripped.</param>
/// <param name="resourceDescription">
/// The description of the resource that the
/// <see cref="System.Collections.IDictionary"/> came from (for logging purposes).
/// </param>
/// <exception cref="Spring.Objects.ObjectsException">
/// In case of loading or parsing errors.
/// </exception>
protected void RegisterObjectDefinition(
string name, IDictionary id, string prefix, string resourceDescription)
{
string typeName = null;
string parent = null;
bool singleton = true;
bool lazyInit = false;
MutablePropertyValues pvs = new MutablePropertyValues();
foreach (string key in id.Keys)
{
if (key.StartsWith(prefix + Separator))
{
string property = key.Substring(prefix.Length + Separator.Length);
if (property.Equals(ClassKey))
{
typeName = (string) id[key];
}
else if (property.Equals(SingletonKey))
{
string val = (string) id[key];
singleton = (val == null) || val.Equals(TrueValue);
}
else if (property.Equals(LazyInitKey))
{
string val = (string) id[key];
lazyInit = val.Equals(TrueValue);
}
else if (property.Equals(ParentKey))
{
parent = (string) id[key];
}
else if (property.EndsWith(RefSuffix))
{
// This isn't a real property, but a reference to another prototype
// Extract property name: property is of form dog(ref)
property = property.Substring(0, property.Length - RefSuffix.Length);
string reference = (String) id[key];
// It doesn't matter if the referenced object hasn't yet been registered:
// this will ensure that the reference is resolved at runtime
// Default is not to use singleton
object val = new RuntimeObjectReference(reference);
pvs.Add(new PropertyValue(property, val));
}
else
{
// normal object property
object val = id[key];
if (val is String)
{
string strVal = (string) val;
// if it starts with a reference prefix...
if (strVal.StartsWith(RefPrefix))
{
// expand reference
string targetName = strVal.Substring(1);
if (targetName.StartsWith(RefPrefix))
{
// escaped prefix -> use plain value
val = targetName;
}
else
{
val = new RuntimeObjectReference(targetName);
}
}
}
pvs.Add(new PropertyValue(property, val));
}
}
}
if (log.IsDebugEnabled)
{
log.Debug(pvs.ToString());
}
if (parent == null)
{
log.Debug(this.DefaultParentObject);
parent = this.DefaultParentObject;
}
if (typeName == null && parent == null)
{
throw new ObjectDefinitionStoreException(resourceDescription, name,
"Either 'type' or 'parent' is required");
}
try
{
IConfigurableObjectDefinition objectDefinition = ObjectDefinitionFactory.CreateObjectDefinition(typeName, parent, Domain);
objectDefinition.PropertyValues = pvs;
objectDefinition.IsSingleton = singleton;
objectDefinition.IsLazyInit = lazyInit;
Registry.RegisterObjectDefinition(name, objectDefinition);
}
catch (Exception ex)
{
throw new ObjectDefinitionStoreException(
resourceDescription, name, "Unable to load type [" + typeName + "]", ex);
}
}
}
}
|