#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.Specialized;
using System.Configuration;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Xml;
using Common.Logging;
using Spring.Core;
using Spring.Core.IO;
using Spring.Core.TypeResolution;
using Spring.Util;
using ConfigurationExceptionCommon.Logging.ConfigurationException;
using ConfigXmlDocumentSpring.Util.ConfigXmlDocument;
#endregion
namespace Spring.Objects.Factory.Config{
/// <summary>
/// Various utility methods for .NET style .config files.
/// </summary>
/// <remarks>
/// <p>
/// Currently supports reading custom configuration sections and returning them as
/// <see cref="System.Collections.Specialized.NameValueCollection"/> objects.
/// </p>
/// </remarks>
/// <author>Simon White</author>
/// <author>Mark Pollack</author>
public sealed class ConfigurationReader
{
private const string ConfigSectionTypeAttribute = "type";
private const string ConfigurationElement = "configuration";
private const string ConfigSectionsElement = "configSections";
private const string ConfigSectionGroupElement = "sectionGroup";
private const string ConfigSectionElement = "section";
private const string ConfigSectionNameAttribute = "name";
private static readonly ILog _log = LogManager.GetLogger(typeof(ConfigurationReader));
/// <summary>
/// Initializes the type members
/// </summary>
static ConfigurationReader()
{}
/// <summary>
/// Reads the specified configuration section into a
/// <see cref="System.Collections.Specialized.NameValueCollection"/>.
/// </summary>
/// <param name="resource">The resource to read.</param>
/// <param name="configSection">The section name.</param>
/// <returns>
/// A newly populated
/// <see cref="System.Collections.Specialized.NameValueCollection"/>.
/// </returns>
/// <exception cref="System.IO.IOException">
/// If any errors are encountered while attempting to open a stream
/// from the supplied <paramref name="resource"/>.
/// </exception>
/// <exception cref="System.Xml.XmlException">
/// If any errors are encountered while loading or reading (this only applies to
/// v1.1 and greater of the .NET Framework) the actual XML.
/// </exception>
/// <exception cref="System.Exception">
/// If any errors are encountered while loading or reading (this only applies to
/// v1.0 of the .NET Framework).
/// </exception>
/// <exception cref="Spring.Objects.FatalObjectException">
/// If the configuration section was otherwise invalid.
/// </exception>
public static NameValueCollection Read(IResource resource, string configSection)
{
return ConfigurationReader.Read(resource, configSection, new NameValueCollection());
}
/// <summary>
/// Reads the specified configuration section into the supplied
/// <see cref="System.Collections.Specialized.NameValueCollection"/>.
/// </summary>
/// <param name="resource">The resource to read.</param>
/// <param name="configSection">The section name.</param>
/// <param name="properties">
/// The collection that is to be populated. May be
/// <see langword="null"/>.
/// </param>
/// <returns>
/// A newly populated
/// <see cref="System.Collections.Specialized.NameValueCollection"/>.
/// </returns>
/// <exception cref="System.IO.IOException">
/// If any errors are encountered while attempting to open a stream
/// from the supplied <paramref name="resource"/>.
/// </exception>
/// <exception cref="System.Xml.XmlException">
/// If any errors are encountered while loading or reading (this only applies to
/// v1.1 and greater of the .NET Framework) the actual XML.
/// </exception>
/// <exception cref="System.Exception">
/// If any errors are encountered while loading or reading (this only applies to
/// v1.0 of the .NET Framework).
/// </exception>
/// <exception cref="Spring.Objects.FatalObjectException">
/// If the configuration section was otherwise invalid.
/// </exception>
public static NameValueCollection Read(
IResource resource, string configSection, NameValueCollection properties)
{
return ConfigurationReader.Read(resource, configSection, properties, true);
}
/// <summary>
/// Reads the specified configuration section into the supplied
/// <see cref="System.Collections.Specialized.NameValueCollection"/>.
/// </summary>
/// <param name="resource">The resource to read.</param>
/// <param name="configSection">The section name.</param>
/// <param name="properties">
/// The collection that is to be populated. May be
/// <see langword="null"/>.
/// </param>
/// <param name="overrideValues">
/// If a key already exists, is its value to be appended to the current
/// value or replaced?
/// </param>
/// <returns>
/// The populated
/// <see cref="System.Collections.Specialized.NameValueCollection"/>.
/// </returns>
/// <exception cref="System.IO.IOException">
/// If any errors are encountered while attempting to open a stream
/// from the supplied <paramref name="resource"/>.
/// </exception>
/// <exception cref="System.Xml.XmlException">
/// If any errors are encountered while loading or reading (this only applies to
/// v1.1 and greater of the .NET Framework) the actual XML.
/// </exception>
/// <exception cref="System.Exception">
/// If any errors are encountered while loading or reading (this only applies to
/// v1.0 of the .NET Framework).
/// </exception>
/// <exception cref="Spring.Objects.FatalObjectException">
/// If the configuration section was otherwise invalid.
/// </exception>
public static NameValueCollection Read(
IResource resource, string configSection, NameValueCollection properties, bool overrideValues)
{
if (properties == null)
{
properties = new NameValueCollection();
}
Stream stream = null;
try
{
ConfigXmlDocument doc = new ConfigXmlDocument();
stream = resource.InputStream;
doc.Load(stream);
NameValueCollection newProperties = ReadFromXmlDocument(doc, configSection);
if (newProperties != null)
{
PopulateProperties(overrideValues, properties, newProperties);
}
}
finally
{
if (stream != null)
{
try
{
stream.Close();
}
catch (IOException ex)
{
#region Instrumentation
if (_log.IsWarnEnabled)
{
_log.Warn("Could not close stream from resource " + resource.Description, ex);
}
#endregion
}
}
}
return properties;
}
/// <summary>
/// Read from the specified configuration from the supplied XML
/// <paramref name="document"/> into a
/// <see cref="System.Collections.Specialized.NameValueCollection"/>.
/// </summary>
/// <remarks>
/// <note>
/// Does <b>not</b> support section grouping. The supplied XML
/// <paramref name="document"/> must already be loaded.
/// </note>
/// </remarks>
/// <param name="document">
/// The <see cref="System.Xml.XmlDocument"/> to read from.
/// </param>
/// <param name="configSectionName">
/// The configuration section name to read.
/// </param>
/// <returns>
/// A newly populated
/// <see cref="System.Collections.Specialized.NameValueCollection"/>.
/// </returns>
/// <exception cref="System.Xml.XmlException">
/// If any errors are encountered while reading (this only applies to
/// v1.1 and greater of the .NET Framework).
/// </exception>
/// <exception cref="System.Exception">
/// If any errors are encountered while reading (this only applies to
/// v1.0 of the .NET Framework).
/// </exception>
/// <exception cref="Spring.Objects.FatalObjectException">
/// If the configuration section was otherwise invalid.
/// </exception>
public static NameValueCollection ReadFromXmlDocument(XmlDocument document,
string configSectionName)
{
// find the config section declaration (if one exists)...
return (NameValueCollection)GetSectionFromXmlDocument(document, configSectionName);
}
/// <summary>
/// Returns the section from the specified resource with the given section name
/// </summary>
public static object GetSection(IResource resource, string configSectionName)
{
using (Stream istm = resource.InputStream)
{
ConfigXmlDocument doc = new ConfigXmlDocument();
doc.Load(istm);
return GetSectionFromXmlDocument(doc, configSectionName);
}
}
#if NET_2_0
/// <summary>
/// Returns the typed section from the specified resource with the given section name
/// </summary>
public static TResult GetSection<TResult>(IResource resource, string configSectionName)
{
using (Stream istm = resource.InputStream)
{
ConfigXmlDocument doc = new ConfigXmlDocument();
doc.Load(istm);
object result = GetSectionFromXmlDocument(doc, configSectionName);
if (result != null && !(result is TResult))
{
throw new ArgumentException(string.Format("evaluating configuration sectoin {0} does not result in an instance of type {1}", configSectionName, typeof(TResult)));
}
return (TResult)result;
}
}
/// <summary>
/// Returns the typed result of evaluating the specified <paramref name="configSectionName"/>.
/// </summary>
/// <exception cref="ArgumentException">if the result's type does not match the expected type</exception>
public static TResult GetSectionFromXmlDocument<TResult>(XmlDocument configDocument, string configSectionName)
{
object result = GetSectionFromXmlDocument(configDocument, configSectionName);
if (result != null && !(result is TResult))
{
throw new ArgumentException(string.Format("evaluating configuration sectoin {0} does not result in an instance of type {1}", configSectionName, typeof(TResult)));
}
return (TResult)result;
}
#endif
/// <summary>
/// Reads the specified configuration section from the given <see cref="XmlDocument"/>
/// </summary>
/// <param name="document"></param>
/// <param name="configSectionName"></param>
/// <returns></returns>
public static object GetSectionFromXmlDocument(XmlDocument document, string configSectionName)
{
string[] sectionNameParts = configSectionName.Split('/');
string sectionHandlerPath = string.Format("//{0}/{1}", ConfigurationElement, ConfigSectionsElement);
if (sectionNameParts.Length > 1)
{
// deal with sectionGroups
for (int i = 0; i < sectionNameParts.Length - 1; i++)
{
sectionHandlerPath = string.Format("{0}/{1}[@{2}='{3}']", sectionHandlerPath, ConfigSectionGroupElement, ConfigSectionNameAttribute, sectionNameParts[i]);
}
}
sectionHandlerPath = string.Format("{0}/{1}[@{2}='{3}']", sectionHandlerPath, ConfigSectionElement, ConfigSectionNameAttribute, sectionNameParts[sectionNameParts.Length - 1]);
XmlNode xmlConfig = document.SelectSingleNode(sectionHandlerPath);
// create appropriate configuration section handler...
Type handlerType = null;
if (xmlConfig == null)
{
// none specified, use machine inherited
XmlDocument machineConfig = new XmlDocument();
machineConfig.Load(RuntimeEnvironment.SystemConfigurationFile);
xmlConfig = machineConfig.SelectSingleNode(sectionHandlerPath);
if (xmlConfig == null)
{
// TOOD: better throw a sensible exception in case of a missing handler configuration?
handlerType = typeof(NameValueFileSectionHandler);
}
}
if (xmlConfig != null)
{
XmlAttribute xmlConfigType = xmlConfig.Attributes[ConfigSectionTypeAttribute];
handlerType = TypeResolutionUtils.ResolveType(xmlConfigType.Value);
}
if (handlerType == null)
{
throw new ConfigurationException(string.Format("missing 'type' attribute on section definition for '{0}'", configSectionName));
}
// obtain Xml node with section content
XmlNode sectionContent = document.SelectSingleNode(string.Format("//{0}/{1}", ConfigurationElement, configSectionName));
if (sectionContent == null)
{
throw ConfigurationUtils.CreateConfigurationException("Cannot read properties; config section '" + configSectionName + "' not found.");
}
// IConfigurationSectionHandler
if (typeof(IConfigurationSectionHandler).IsAssignableFrom(handlerType))
{
IConfigurationSectionHandler handler = (IConfigurationSectionHandler)ObjectUtils.InstantiateType(handlerType);
return ((IConfigurationSectionHandler)handler).Create(null, null, sectionContent);
}
#if !NET_1_0 && !NET_1_1
// NET 2.0 ConfigurationSection
if (typeof(ConfigurationSection).IsAssignableFrom(handlerType))
{
ConfigurationSection section = CreateConfigurationSection(handlerType, new XmlNodeReader(sectionContent));
return section;
}
#endif
// Not supported
throw ConfigurationUtils.CreateConfigurationException("Configuration section '" + configSectionName + "' is neither of type IConfigurationSectionHandler nor ConfigurationSection.");
}
#if !NET_1_0 && !NET_1_1
private delegate void DeserializeSectionMethod(ConfigurationSection section, XmlReader reader);
private static DeserializeSectionMethod deserialized = (DeserializeSectionMethod)Delegate.CreateDelegate(typeof(DeserializeSectionMethod),
typeof(ConfigurationSection).GetMethod("DeserializeSection",
BindingFlags.Instance |
BindingFlags.NonPublic));
private static ConfigurationSection CreateConfigurationSection(Type handlerType, XmlReader reader)
{
ConfigurationSection section = (ConfigurationSection)ObjectUtils.InstantiateType(handlerType);
deserialized(section, reader);
return section;
}
#endif
/// <summary>
/// Populates the supplied <paramref name="properties"/> with values from
/// a .NET application configuration file.
/// </summary>
/// <param name="properties">
/// The <see cref="System.Collections.Specialized.NameValueCollection"/>
/// to add any key-value pairs to.
/// </param>
/// <param name="configSectionName">
/// The configuration section name in the a .NET application configuration
/// file.
/// </param>
/// <param name="overrideValues">
/// If a key already exists, is its value to be appended to the current
/// value or replaced?
/// </param>
/// <returns>
/// <see langword="true"/> if the supplied
/// <paramref name="configSectionName"/> was found.
/// </returns>
public static bool PopulateFromAppConfig(
NameValueCollection properties, string configSectionName, bool overrideValues)
{
bool sectionFound = false;
NameValueCollection newProperties
= ConfigurationUtils.GetSection(configSectionName) as NameValueCollection;
if (newProperties != null)
{
sectionFound = true;
PopulateProperties(overrideValues, properties, newProperties);
}
return sectionFound;
}
private static void PopulateProperties(
bool overrideValues, NameValueCollection properties, NameValueCollection newProperties)
{
if (!overrideValues)
{
properties.Add(newProperties);
}
else
{
foreach (string key in newProperties.AllKeys)
{
properties.Set(key, newProperties.Get(key));
}
}
}
#region Constructor (s) / Destructor
// CLOVER:OFF
/// <summary>
/// Creates a new instance of the ConfigurationReader class.
/// </summary>
/// <remarks>
/// <p>
/// This is a utility class, and as such has no publicly visible
/// constructors.
/// </p>
/// </remarks>
private ConfigurationReader()
{
}
// CLOVER:ON
#endregion
}
}
|