using System;
using System.Configuration;
using System.Xml;
using System.Xml.Serialization;

/// <summary>
/// Base class for custom configuration handlers
/// </summary>
public class BaseConfigHandler : IConfigurationSectionHandler {
    private static object _lockObject = new object();
    private static string _configSection = null;

    /// <summary>
    /// Get the configuration object parsed from the .config file.
    /// </summary>
    /// <param name="type">Pass the type of the object we are deserialising.</param>
    /// <returns>The instance of the object from the .config file.</returns>
    protected static object GetCurrent(Type type) {
        // We cache the config section name to save finding attributes
        // with reflection every time, so we only need to go find it
        // if we didn't get it before.
        if(_configSection==null) {
            lock(_lockObject) {
                // Double check now we're locked that we still don't have the name
                // in case someone else sneaked in while we were locking.
                if(_configSection==null) {
                    // Find the CustomConfigHandlerAttribute attribute.
                    object[] attList = type.GetCustomAttributes(typeof(CustomConfigHandlerAttribute),true);

                    // If the attribute is missing or there is more than one then throw an exception.
                    if(attList.Length==0) throw new ConfigurationException("Missing CustomConfigHandler attribute");
                    if(attList.Length>1) throw new ConfigurationException("More than one CustomConfigHandler attribute is not supported on a class.");

                    // Store the section name from the attribute.
                    _configSection = ((CustomConfigHandlerAttribute)attList[0]).SectionName;
                }
            }
        }

        // Do the lookup - this will call our IConfigurationSectionHandler.Create
        // method the first time we call to parse the XML. Thereafter, the object
        // will be cached by the framework.
        return ConfigurationSettings.GetConfig(_configSection);
    }

    // This method is called by the framework to parse the configuration section XML
    // into whatever config object we want to return. In this case, we simply use
    // the XML Serializer to deserialize ourselves from the XML node.
    object IConfigurationSectionHandler.Create(object parent, object configContext, System.Xml.XmlNode section) {
        XmlSerializer xml = new XmlSerializer(this.GetType());
        return xml.Deserialize(new XmlNodeReader(section));
    }
}

/// <summary>
/// Custom config handler attribute to indicate the section and group name.
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public class CustomConfigHandlerAttribute : XmlRootAttribute {
    public CustomConfigHandlerAttribute(string elementName) : base(elementName) {
    }

    /// <summary>
    /// Returns section name for config section including any group path.
    /// </summary>
    internal string SectionName {
        get {
            if(Group!=null) {
                return Group + "/" + base.ElementName;
            } else {
                return base.ElementName;
            }
        }
    }

    /// <summary>
    /// Configuration section group name is section is in a group.
    /// </summary>
    public string Group;
}