/// <summary> /// Parses a single CLR value out of an XML element. /// </summary> /// <param name="type">The destination type.</param> /// <param name="xObject">The XObject containing the value.</param> /// <param name="directoryName">The name of the directory where external files can be found.</param> /// <returns>The CLR value of the given XML node.</returns> static Object ParseValue(Type type, XObject xObject, String directoryName) { // This will evaluate a literal text object as the value. if (xObject is XText) { XText xText = xObject as XText; return(ScriptLoader.ConvertValue(typeof(String), xText.Value)); } // This will interpret an element as a value. if (xObject is XElement) { // This element holds special instructions for the parameter. XElement xElement = xObject as XElement; // Translate the element type into an Enum. parameterElementType parameterElementType = parameterElementType.None; if (!ScriptLoader.parameterElementDictionary.TryGetValue(xElement.Name, out parameterElementType)) { parameterElementType = parameterElementType.None; } // Common variables used below. XAttribute pathAttribute; String path; // This will convert the element according to the kind of element it is. switch (parameterElementType) { case parameterElementType.Value: // This will convert a simple text value into the CLR equivalent. return(ConvertValue(ScriptLoader.GetElementType(typeof(String), xElement), xElement)); case parameterElementType.Load: // This will load a resource file as a Base64 string. pathAttribute = xElement.Attribute("path"); path = Path.IsPathRooted(pathAttribute.Value) ? pathAttribute.Value : Path.Combine(directoryName, pathAttribute.Value); using (FileStream fileStream = new FileStream(path, FileMode.Open, FileAccess.Read)) { Byte[] binaryData = new Byte[fileStream.Length]; fileStream.Read(binaryData, 0, Convert.ToInt32(fileStream.Length)); return(ScriptLoader.ConvertValue(type, Convert.ToBase64String(binaryData))); } case parameterElementType.Import: // This will load an XML file as a text string. pathAttribute = xElement.Attribute("path"); path = Path.IsPathRooted(pathAttribute.Value) ? pathAttribute.Value : Path.Combine(directoryName, pathAttribute.Value); XDocument xDocument = XDocument.Load(path); return(ScriptLoader.ConvertValue(type, xDocument.ToString())); } } // Any node that can't be parsed is assumed to be a DBNull value. return(DBNull.Value); }
/// <summary> /// Convert text to a CLR object based on the target datatype. /// </summary> /// <param name="type">The target datatype.</param> /// <param name="element">The xml element.</param> /// <returns>The CLR equivalent of the given value.</returns> static Object ConvertValue(Type type, XElement element) { if (element.IsEmpty) { return(DBNull.Value); } else { return(ScriptLoader.ConvertValue(type, element.Value)); } }
/// <summary> /// Converts an XElement describing a value to a native CLR value. /// </summary> /// <param name="type">The target datatype.</param> /// <param name="parameterElement">An XElement containing a String representation of a value.</param> /// <param name="directoryName">The directory where external files can be found.</param> /// <returns>The native CLR value of the described value.</returns> static Object ConvertElement(Type type, XElement parameterElement, String directoryName) { // If the target parameter is an array, then construct a vector parameter. if (type == typeof(Object[])) { // The values can be found in a single attribute of the 'parameter' element or be listed as children. This list collects both methods of describing // values and constructs a single array when all elements and attributes are parsed. List <Object> valueList = new List <Object>(); // An attribute can be used to desribe a value. An optional 'Type' attribute can specify what type of conversion is used to evaluate the CLR value. XAttribute valueAttribute = parameterElement.Attribute("value"); if (valueAttribute != null) { valueList.Add(ScriptLoader.ConvertValue(ScriptLoader.GetElementType(typeof(String), parameterElement), valueAttribute.Value)); } // It is possible to specify the value using the content of an XML element or through an "import" statement. This will cycle through any nodes of // the parameter looking for additional nodes containing the data for the parameter. foreach (XObject xObject in parameterElement.Nodes()) { // This uses the element content as the value for the parameter. if (xObject is XText) { XText xText = xObject as XText; valueList.Add(ConvertValue(typeof(String), xText.Value)); } // Elements can be nested inside the parameter element to greater detail to the parameter. if (xObject is XElement) { // This element holds special instructions for the parameter. XElement xElement = xObject as XElement; // Values for a key can be specified as child elements of the parameter. if (xElement.Name == "value") { valueList.Add(ConvertValue(GetElementType(typeof(String), xElement), xElement.Value)); } // This special instruction allows the value of a parameter to come from an external file. This is used primary to load XML content into a // record. if (xElement.Name == "import") { XAttribute xAttribute = xElement.Attribute("path"); String path = Path.IsPathRooted(xAttribute.Value) ? xAttribute.Value : Path.Combine(directoryName, xAttribute.Value); XDocument xDocument = XDocument.Load(path); valueList.Add(ConvertValue(type, xDocument.ToString())); } // A 'load' element will read a binary resource into a byte array. if (xElement.Name == "load") { XAttribute xAttribute = xElement.Attribute("path"); String path = Path.IsPathRooted(xAttribute.Value) ? xAttribute.Value : Path.Combine(directoryName, xAttribute.Value); Byte[] binaryData; using (FileStream fileStream = new FileStream(path, FileMode.Open, FileAccess.Read)) { binaryData = new Byte[fileStream.Length]; fileStream.Read(binaryData, 0, Convert.ToInt32(fileStream.Length)); } return(ConvertValue(type, Convert.ToBase64String(binaryData))); } } } // This array is most often used as a key to find a record in a table. return(valueList.ToArray()); } else { // If the XML specifies an override for the native data type of the parameter, make sure that the new value's type is compatible with the parameter. Type originalType = type; type = ScriptLoader.GetElementType(type, parameterElement); if (type != originalType && !type.IsSubclassOf(originalType)) { throw new Exception(String.Format("Can't cast a parameter of type {0} to {1}.", type, originalType)); } // It is possible to specify the value using the content of an XML element or through an "import" statement. This will cycle through any nodes of // the parameter looking for additional nodes containing the data for the parameter. Of course, for a scalar, there is only a single value that // can be returned, so we'll take the first element we find that converts into a value. foreach (XObject xObject in parameterElement.DescendantNodes()) { return(ScriptLoader.ParseValue(type, xObject, directoryName)); } // If no elements are specified, then we'll simply convert the attribute into a value for this parameter. This is by far the most common method of // providing values for parameter. XAttribute valueAttribute = parameterElement.Attribute("value"); return(valueAttribute == null ? DBNull.Value : ScriptLoader.ConvertValue(type, valueAttribute.Value)); } }