using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Drawing;
using System.Reflection;
namespace Snippets_Wettbewerb.XMLConfigReader
{
/// <summary>
/// This class reads xml configuration file and will try to translate all xml nodes
/// into configuration strings. The configuration strings will be used to set public
/// properties from specified object via runtime and reflection. There are several
/// ways to define kind of property values (e.g. color-definition).
/// </summary>
/// <example>
/// Following xml configuration:
/// <?xml version="1.0" encoding="utf-8" ?>
/// <Config>
/// <TestString>This is a string.</TestString>
/// <TestColor1 type="rgb">125, 125, 125</TestColor1>
/// <TestColor2 type="colorname">White</TestColor2>
/// <TestFont type="font">Microsoft Sans Sarif, 10, bold</TestFont>
/// </Config>
/// Can be used with objects containing "TestString", "TestColor1", "TestColor2" and "TestFont"
/// properties with following instruction:
/// <code>XMLConfigReader.XmlConfigReader.ReadAllConfig("config.xml", this);</code>
/// </example>
public class XmlConfigReader
{
#region Constructor
/// <summary>
/// Initializes a new instance of the <see cref="T:BVXmlConfigReader"/> class.
/// </summary>
private XmlConfigReader()
{
}
#endregion
#region Private Methods
/// <summary>
/// Method which gets all property nodes in xml config file.
/// </summary>
/// <param name="p_sXmlFilenamePath">Name and path of xml config file.</param>
/// <param name="p_sXmlRoot">Xml path root, from where to search properties.</param>
/// <returns>XmlNodeList of all properties, or null if xml file/rootnode was not found.</returns>
private static XmlNodeList GetXmlNodes(string p_sXmlFilenamePath, string p_sXmlRoot)
{
// check whether xml file exists
if (System.IO.File.Exists(p_sXmlFilenamePath))
{
// local variable
string sXmlPathRoot = String.Empty;
// load xml file
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(p_sXmlFilenamePath);
// create XPath expression
sXmlPathRoot = CreateXPathExpression(p_sXmlRoot, "*");
// return selected nodes
return xmlDoc.SelectNodes(sXmlPathRoot);
}
return null;
}
/// <summary>
/// Method which get single xml node from xml config file.
/// </summary>
/// <param name="p_sXmlFilenamePath">Name and path of xml config file.</param>
/// <param name="p_sXmlRoot">Xml path root, from where to search properties.</param>
/// <param name="p_sNodename">Xml node name which should be received.</param>
/// <returns>XmlNode of requested property or null if node was not found.</returns>
private static XmlNode GetSingleXmlNode(string p_sXmlFilenamePath, string p_sXmlRoot, string p_sNodename)
{
// check whether file exists
if (System.IO.File.Exists(p_sXmlFilenamePath))
{
// load xml file
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(p_sXmlFilenamePath);
// create XPath expression
string sXmlPathRoot = CreateXPathExpression(p_sXmlRoot, p_sNodename);
// return selected node
return xmlDoc.DocumentElement.SelectSingleNode(sXmlPathRoot);
}
return null;
}
/// <summary>
/// Method which creates xml path expression to find all nodes defined with specified
/// root node (which don't have to be same like document element node).
/// </summary>
/// <param name="p_sXmlRoot">Name of inner root node. May be same like document element node.</param>
/// <param name="p_sNodename">Define one specific node name where to search.</param>
/// <returns>XPath Expression pointing to specific root/named node.</returns>
private static string CreateXPathExpression(string p_sXmlRoot, string p_sNodename)
{
// local variable
string sXmlPathRoot = String.Empty;
// concat XmlRoot (to search) with XmlPathRoot
if (p_sXmlRoot != String.Empty)
{
sXmlPathRoot = String.Concat(
p_sXmlRoot,
"/",
p_sNodename);
}
else
{
sXmlPathRoot = String.Concat(
"./*/",
p_sNodename);
}
return sXmlPathRoot;
}
/// <summary>
/// Method which tries to set property from xml node.
/// </summary>
/// <param name="node">The xml node.</param>
private static bool SetPropertyFromXmlNode(XmlNode node, object p_oConcreteInstance)
{
// local variables
string sAttributeType = "default";
// check if node or object is null
if ((node == null) || (p_oConcreteInstance == null))
{
return false;
}
// check if property with sub property is given
string[] sSubProperties = node.Name.Split('.');
// detect if given property exists
PropertyInfo propInfo = null;
Type currPropertyType = p_oConcreteInstance.GetType();
// go straight forward with nested types until last property should get
for (int i = 0; i < sSubProperties.Length; i++)
{
// name of current property
string sCurrentProperty = sSubProperties[i];
// if last property detected, get it PropertyInfo object, otherwise get nested type
if (i == sSubProperties.Length-1)
{
propInfo = currPropertyType.GetProperty(sSubProperties[i]);
}
else
{
PropertyInfo pi = currPropertyType.GetProperty(sSubProperties[i]);
currPropertyType = pi.GetValue(p_oConcreteInstance, null).GetType();
// create new instane of current property value
try
{
object obj = pi.GetValue(p_oConcreteInstance, null);
p_oConcreteInstance = obj;
}
catch
{
break;
}
// check if nested type was found, otherwise break
if (currPropertyType == null)
{
break;
}
}
}
// check if property info was found
if (propInfo == null)
{
return false;
}
if (!propInfo.CanWrite)
{
return false;
}
// check if node attribute "type" is specified
XmlAttribute xmlAttr = (XmlAttribute)node.Attributes.GetNamedItem("type");
if (xmlAttr != null)
{
sAttributeType = xmlAttr.Value.ToLower();
}
try
{
// switch for all different types
switch (sAttributeType)
{
case "rgb":
Color color = ConvertRGBString(node.InnerText);
if (color != Color.Empty)
{
propInfo.SetValue(p_oConcreteInstance, color, null);
}
break;
case "colorname":
color = Color.FromName(node.InnerText);
if (!color.Equals(Color.FromArgb(0)))
{
propInfo.SetValue(p_oConcreteInstance, color, null);
}
break;
case "bool":
string nodeVal = node.InnerText;
bool convertedVal = false;
if (nodeVal.ToLower() == "true")
{
convertedVal = true;
}
propInfo.SetValue(p_oConcreteInstance, convertedVal, null);
break;
case "font":
Font fontToSet = ConvertFontString(node.InnerText);
propInfo.SetValue(p_oConcreteInstance, fontToSet, null);
break;
case "enum":
object enumToSet = ConvertEnumString(node.InnerText,
propInfo.GetValue(p_oConcreteInstance, null));
propInfo.SetValue(p_oConcreteInstance, enumToSet, null);
break;
default:
propInfo.SetValue(p_oConcreteInstance, node.InnerText, null);
break;
}
}
catch (Exception ex)
{
// TODO: Decide what is necessary: Exception or log entry...
return false;
}
return true;
}
#endregion
#region Converting routines
/// <summary>
/// Converts the enum string.
/// </summary>
/// <param name="p_sEnumString">The EnumString.</param>
/// <param name="p_oCurrentEnumValue">The current enum value object to detect which enum is necessary.</param>
/// <returns>Parsed enum value.</returns>
private static object ConvertEnumString(string p_sEnumString, object p_oCurrentEnumValue)
{
object retValue = Enum.Parse(p_oCurrentEnumValue.GetType(), p_sEnumString);
return retValue;
}
/// <summary>
/// Converts the font string.
/// </summary>
/// <param name="p_sFontString">The Font string coming from xml file.</param>
/// <returns>Converted Font object, or null if conversion fails.</returns>
private static Font ConvertFontString(string p_sFontString)
{
// local variables
float fFontSize = 0;
FontStyle fontStyle;
string[] fontParams = p_sFontString.Split(',');
// check count of input parameters
if (fontParams.Length < 3)
{
return null;
}
// try to parse font size
if (!Single.TryParse(fontParams[1], out fFontSize))
{
fFontSize = 10f;
}
// try to parse font style
try
{
fontStyle = (FontStyle)Enum.Parse(typeof(FontStyle), fontParams[2], true);
}
catch
{
fontStyle = FontStyle.Regular;
}
return new Font(fontParams[0], fFontSize, fontStyle);
}
/// <summary>
/// Converts the RGB string to Color object.
/// </summary>
/// <param name="p">String contains comma seperated rgb values.</param>
/// <returns>Color from RGB values.</returns>
private static Color ConvertRGBString(string p_sRGBValues)
{
// local variables
int iColorR = 300; // initial illegal range
int iColorG = 300; // initial illegal range
int iColorB = 300; // initial illegal range
string[] rgbValues = p_sRGBValues.Split(',');
// check if 3 rgb values exists
if (rgbValues.Length != 3)
{
return Color.Empty;
}
// try to parse 3 rgb values to integer
Int32.TryParse(rgbValues[0], out iColorR);
Int32.TryParse(rgbValues[1], out iColorG);
Int32.TryParse(rgbValues[2], out iColorB);
// check rgb range
if ((iColorR < 0) || (iColorR > 255))
{
return Color.Empty;
}
if ((iColorG < 0) || (iColorG > 255))
{
return Color.Empty;
}
if ((iColorB < 0) || (iColorB > 255))
{
return Color.Empty;
}
// return RGB color
return Color.FromArgb(iColorR, iColorG, iColorB);
}
#endregion
#region Public / Protected Methods
/// <summary>
/// This method reads from xml configuration file one property and tries to use it in concrete
/// object instance.
/// </summary>
/// <param name="p_sXmlFilePath">Filepath to xml config file.</param>
/// <param name="p_sProperty">Name of property (XmlNode name), which to read.</param>
/// <param name="p_oConcreteInstance">Concrete instance for which property should be set.</param>
/// <returns>
/// True, if property setting was successfully, otherwise False.
/// </returns>
public static bool ReadConfig(string p_sXmlFilePath, string p_sProperty, object p_oConcreteInstance)
{
XmlNode node = GetSingleXmlNode(p_sXmlFilePath, String.Empty, p_sProperty);
return SetPropertyFromXmlNode(node, p_oConcreteInstance);
}
/// <summary>
/// This method reads from xml configuration file all properties and tries to use them in concrete
/// object instance.
/// </summary>
/// <param name="p_sXmlFilePath">Filepath to xml config file.</param>
/// <param name="p_oConcreteInstance">Concrete instance for which properties should be set.</param>
/// <returns>
/// True, if properties setting was successfully, otherwise False.
/// </returns>
public static bool ReadAllConfig(string p_sXmlFilePath, object p_oConcreteInstance)
{
// local variable
bool retValue = true;
XmlNodeList nodeList = GetXmlNodes(p_sXmlFilePath, String.Empty);
if (nodeList == null)
{
return false;
}
foreach (XmlNode node in nodeList)
{
if(!SetPropertyFromXmlNode(node, p_oConcreteInstance))
{
retValue = false;
}
}
return retValue;
}
#endregion
}
}