#region Disclaimer / License
// Copyright (C) 2009, Kenneth Skovhede
// http://www.hexad.dk, opensource@hexad.dk
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
#endregion Disclaimer / License
using GeoAPI.Geometries;
using OSGeo.MapGuide.MaestroAPI.CoordinateSystem;
using OSGeo.MapGuide.MaestroAPI.Schema;
using OSGeo.MapGuide.MaestroAPI.Services;
using OSGeo.MapGuide.ObjectModels;
using OSGeo.MapGuide.ObjectModels.ApplicationDefinition;
using OSGeo.MapGuide.ObjectModels.Capabilities;
using OSGeo.MapGuide.ObjectModels.Capabilities.v1_0_0;
using OSGeo.MapGuide.ObjectModels.IO;
using OSGeo.MapGuide.ObjectModels.LayerDefinition;
using OSGeo.MapGuide.ObjectModels.MapDefinition;
using OSGeo.MapGuide.ObjectModels.SymbolDefinition;
using OSGeo.MapGuide.ObjectModels.WatermarkDefinition;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
using System.Xml.Serialization;
namespace OSGeo.MapGuide.MaestroAPI
{
///
/// Various helper functions
///
public static class Utility
{
///
/// Returns true if this value is zero for all intents and purposes
///
///
///
public static bool IsZero(this float val) => Math.Abs(val) < float.Epsilon;
///
/// Returns true if this value is zero for all intents and purposes
///
///
///
public static bool IsZero(this double val) => Math.Abs(val) < double.Epsilon;
///
/// Creates the polygon WKT for the given bounding box
///
///
///
///
///
///
public static string MakeWktPolygon(double x1, double y1, double x2, double y2)
=> $"POLYGON(({x1} {y1}, {x2} {y1}, {x2} {y2}, {x1} {y2}, {x1} {y1}))"; //NOXLATE
///
/// Creates the WKT for the given circle
///
///
///
///
///
public static string MakeWktCircle(double x, double y, double r)
=> $"CURVEPOLYGON (({(x - r)} {y} (CIRCULARARCSEGMENT ({x} {(y - r)}, {(x + r)} {y}), CIRCULARARCSEGMENT ({x} {(y + r)}, {(x - r)} {y}))))"; //NOXLATE
///
/// The exception data key that hints that an thrown is related to XML content errors
///
public const string XML_EXCEPTION_KEY = "XmlError"; //NOXLATE
///
/// Gets whether the thrown exception is related to DBXML
///
///
///
public static bool IsDbXmlError(Exception ex) => ex.Message.Contains("MgDbXmlException") || ex.Message.Contains("MgXmlParserException"); //NOXLATE
///
/// Gets whether the given exception has original xml content attached
///
///
///
public static bool HasOriginalXml(Exception ex) => ex.Data[XML_EXCEPTION_KEY] != null;
//Americans NEVER obey nationalization when outputting decimal values, so the rest of the world always have to work around their bugs :(
private static CultureInfo m_enCI = new CultureInfo("en-US"); //NOXLATE
///
/// Converts the specified name value collection into a connection string
///
///
///
public static string ToConnectionString(NameValueCollection values)
{
List tokens = new List();
foreach (string name in values.Keys)
{
string value = values[name];
if (value.Contains(";")) //NOXLATE
value = "\"" + value + "\""; //NOXLATE
tokens.Add(name + "=" + value); //NOXLATE
}
return string.Join(";", tokens.ToArray()); //NOXLATE
}
///
/// Strips the version component from provider name
///
/// The provider name.
///
public static string StripVersionFromProviderName(string providername)
{
double x;
string[] parts = providername.Split('.'); //NOXLATE
for (int i = parts.Length - 1; i >= 0; i--)
{
if (!double.TryParse(parts[i], System.Globalization.NumberStyles.Integer, null, out x))
{
if (i != 0)
return string.Join(".", parts, 0, i + 1); //NOXLATE
break;
}
}
return providername;
}
///
/// Checks if the given result is a successful test connection result
///
///
///
public static bool IsSuccessfulConnectionTestResult(string sResult) => (sResult == "No errors" || sResult.ToLower() == "true"); //NOXLATE
///
/// Parses a color in HTML notation (ea. #ffaabbff)
///
/// The HTML representation of the color
/// The .Net color structure that matches the color
public static Color ParseHTMLColor(string color)
{
if (color.Length == 8)
{
int a = int.Parse(color.Substring(0, 2), NumberStyles.HexNumber);
int r = int.Parse(color.Substring(2, 2), NumberStyles.HexNumber);
int g = int.Parse(color.Substring(4, 2), NumberStyles.HexNumber);
int b = int.Parse(color.Substring(6, 2), NumberStyles.HexNumber);
return Color.FromArgb(a, r, g, b);
}
else if (color.Length == 6)
{
int r = int.Parse(color.Substring(0, 2), NumberStyles.HexNumber);
int g = int.Parse(color.Substring(2, 2), NumberStyles.HexNumber);
int b = int.Parse(color.Substring(4, 2), NumberStyles.HexNumber);
return Color.FromArgb(r, g, b);
}
else
throw new Exception(string.Format(Strings.ErrorBadHtmlColor, color));
}
///
/// Parses a color in HTML notation (ea. #ffaabbff)
///
/// The HTML representation of the color
/// The .Net color structure that matches the color
public static Color ParseHTMLColorRGBA(string color)
{
if (color.Length == 8)
{
int r = int.Parse(color.Substring(0, 2), NumberStyles.HexNumber);
int g = int.Parse(color.Substring(2, 2), NumberStyles.HexNumber);
int b = int.Parse(color.Substring(4, 2), NumberStyles.HexNumber);
int a = int.Parse(color.Substring(6, 2), NumberStyles.HexNumber);
return Color.FromArgb(a, r, g, b);
}
else if (color.Length == 6)
{
int r = int.Parse(color.Substring(0, 2), NumberStyles.HexNumber);
int g = int.Parse(color.Substring(2, 2), NumberStyles.HexNumber);
int b = int.Parse(color.Substring(4, 2), NumberStyles.HexNumber);
return Color.FromArgb(r, g, b);
}
else
throw new Exception(string.Format(Strings.ErrorBadHtmlColor, color));
}
///
/// Parses a color in HTML notation (ea. #ffaabbff)
///
/// The HTML representation of the color
/// The .Net color structure that matches the color
public static Color ParseHTMLColorARGB(string color)
{
if (color.Length == 8)
{
int a = int.Parse(color.Substring(0, 2), NumberStyles.HexNumber);
int r = int.Parse(color.Substring(2, 2), NumberStyles.HexNumber);
int g = int.Parse(color.Substring(4, 2), NumberStyles.HexNumber);
int b = int.Parse(color.Substring(6, 2), NumberStyles.HexNumber);
return Color.FromArgb(a, r, g, b);
}
else if (color.Length == 6)
{
int r = int.Parse(color.Substring(0, 2), NumberStyles.HexNumber);
int g = int.Parse(color.Substring(2, 2), NumberStyles.HexNumber);
int b = int.Parse(color.Substring(4, 2), NumberStyles.HexNumber);
return Color.FromArgb(r, g, b);
}
else
throw new Exception(string.Format(Strings.ErrorBadHtmlColor, color));
}
///
/// Returns the HTML ARGB representation of an .Net color structure
///
/// The color to encode
/// A flag indicating if the color structures alpha value should be included
/// The HTML representation of the color structure
public static string SerializeHTMLColor(Color color, bool includeAlpha)
{
string res = string.Empty;
if (includeAlpha)
res += color.A.ToString("x02"); //NOXLATE
res += color.R.ToString("x02"); //NOXLATE
res += color.G.ToString("x02"); //NOXLATE
res += color.B.ToString("x02"); //NOXLATE
return res;
}
///
/// Returns the HTML RGBA representation of an .Net color structure
///
/// The color to encode
/// A flag indicating if the color structures alpha value should be included
/// The HTML representation of the color structure
public static string SerializeHTMLColorRGBA(Color color, bool includeAlpha)
{
string res = string.Empty;
res += color.R.ToString("x02"); //NOXLATE
res += color.G.ToString("x02"); //NOXLATE
res += color.B.ToString("x02"); //NOXLATE
if (includeAlpha)
res += color.A.ToString("x02"); //NOXLATE
return res;
}
///
/// Returns the HTML ARGB representation of an .Net color structure
///
/// The color to encode
/// A flag indicating if the color structures alpha value should be included
/// The HTML representation of the color structure
public static string SerializeHTMLColorARGB(Color color, bool includeAlpha)
{
string res = string.Empty;
if (includeAlpha)
res += color.A.ToString("x02"); //NOXLATE
res += color.R.ToString("x02"); //NOXLATE
res += color.G.ToString("x02"); //NOXLATE
res += color.B.ToString("x02"); //NOXLATE
return res;
}
///
/// Parses a string with a decimal value in EN-US format
///
/// The string value
/// The parsed value
public static float ParseDigit(string digit) => (float)double.Parse(digit, m_enCI);
///
/// Turns a decimal value into a string representation in EN-US format
///
/// The value to encode
/// The encoded value
public static string SerializeDigit(float digit) => digit.ToString(m_enCI);
///
/// Turns a decimal value into a string representation in EN-US format
///
/// The value to encode
/// The encoded value
public static string SerializeDigit(double digit) => digit.ToString(m_enCI);
///
/// Copies the content of a stream into another stream.
/// Automatically attempts to rewind the source stream.
///
/// The source stream
/// The target stream
public static void CopyStream(Stream source, Stream target) => CopyStream(source, target, true);
///
/// Copies the content of a stream into another stream.
///
/// The source stream
/// The target stream
/// True if the source stream should be rewound before being copied
public static void CopyStream(Stream source, Stream target, bool rewind)
{
//int r;
byte[] buf = new byte[1024];
//bool rewound = false;
if (rewind)
{
if (source.CanSeek)
{
try
{
source.Position = 0;
//rewound = true;
}
catch { }
}
else
{
ReadOnlyRewindableStream roSource = source as ReadOnlyRewindableStream;
if (roSource != null && roSource.CanRewind)
{
roSource.Rewind();
//rewound = true;
}
}
//if (!rewound)
// throw new InvalidOperationException("Could not rewind the source stream. Most likely the source stream does not support seeking or rewinding"); //LOCALIZEME
}
source.CopyTo(target);
/*
do
{
r = source.Read(buf, 0, buf.Length);
target.Write(buf, 0, r);
} while (r > 0);
*/
}
///
/// A delegate used to update a progress bar while copying a stream.
///
/// The number of bytes copied so far
/// The number of bytes left in the stream. -1 if the stream length is not known
/// The total number of bytes in the source stream. -1 if the stream length is unknown
public delegate void StreamCopyProgressDelegate(long copied, long remaining, long total);
///
/// Copies the content of a stream into another stream, with callbacks.
///
/// The source stream
/// The target stream
/// An optional callback delegate, may be null.
/// The number of bytes to copy before calling the callback delegate, set to 0 to get every update
public static void CopyStream(Stream source, Stream target, StreamCopyProgressDelegate callback, long updateFrequence)
{
long length = -1;
if (source.CanSeek)
length = source.Length;
long copied = 0;
long freqCount = 0;
if (callback != null)
callback(copied, length > 0 ? (length - copied) : -1, length);
int r;
byte[] buf = new byte[1024];
do
{
r = source.Read(buf, 0, buf.Length);
target.Write(buf, 0, r);
copied += r;
freqCount += r;
if (freqCount > updateFrequence)
{
freqCount = 0;
if (callback != null)
callback(copied, length > 0 ? (length - copied) : -1, length);
}
} while (r > 0);
if (callback != null)
callback(copied, 0, copied);
}
///
/// Builds a copy of the object by serializing it to xml, and then deserializing it.
/// Please note that this function is has a large overhead.
///
/// The object to copy
/// A copy of the object
public static object XmlDeepCopy(object source)
{
if (source == null)
return null;
var ser = new XmlSerializer(source.GetType());
var ms = new MemoryStream();
ser.Serialize(ms, source);
ms.Position = 0;
return ser.Deserialize(ms);
}
///
/// Makes a deep copy of an object, by copying all the public properties.
/// This overload tries to maintain object references by assigning properties
///
/// The object to copy
/// The object to assign to
/// A copied object
public static object DeepCopy(object source, object target)
{
foreach (System.Reflection.PropertyInfo pi in source.GetType().GetProperties())
{
if (!pi.CanRead || !pi.CanWrite)
continue;
if (!pi.PropertyType.IsClass || pi.PropertyType == typeof(string))
pi.SetValue(target, pi.GetValue(source, null), null);
else if (pi.GetValue(source, null) == null)
pi.SetValue(target, null, null);
else if (pi.GetValue(source, null).GetType().GetInterface(typeof(System.Collections.ICollection).FullName) != null)
{
System.Collections.ICollection srcList = (System.Collections.ICollection)pi.GetValue(source, null);
System.Collections.ICollection trgList = (System.Collections.ICollection)Activator.CreateInstance(srcList.GetType());
foreach (object o in srcList)
trgList.GetType().GetMethod("Add").Invoke(trgList, new object[] { DeepCopy(o) }); //NOXLATE
pi.SetValue(target, trgList, null);
}
else if (pi.GetValue(source, null).GetType().IsArray)
{
Array sourceArr = (Array)pi.GetValue(source, null);
Array targetArr = (Array)Activator.CreateInstance(sourceArr.GetType(), new object[] { sourceArr.Length });
for (int i = 0; i < targetArr.Length; i++)
targetArr.SetValue(DeepCopy(sourceArr.GetValue(i)), i);
pi.SetValue(target, targetArr, null);
}
else
{
if (pi.GetValue(target, null) == null)
pi.SetValue(target, Activator.CreateInstance(pi.GetValue(source, null).GetType()), null);
DeepCopy(pi.GetValue(source, null), pi.GetValue(target, null));
}
}
return target;
}
///
/// Makes a deep copy of an object, by copying all the public properties
///
/// The object to copy
/// A copied object
public static object DeepCopy(object source)
{
if (source == null)
return null;
object target = Activator.CreateInstance(source.GetType());
foreach (System.Reflection.PropertyInfo pi in source.GetType().GetProperties())
{
if (!pi.CanRead || !pi.CanWrite)
continue;
if (!pi.PropertyType.IsClass || pi.PropertyType == typeof(string))
pi.SetValue(target, pi.GetValue(source, null), null);
else if (pi.GetValue(source, null) == null)
pi.SetValue(target, null, null);
else if (pi.GetValue(source, null).GetType().GetInterface(typeof(System.Collections.ICollection).FullName) != null)
{
System.Collections.ICollection srcList = (System.Collections.ICollection)pi.GetValue(source, null);
System.Collections.ICollection trgList = (System.Collections.ICollection)Activator.CreateInstance(srcList.GetType());
foreach (object o in srcList)
trgList.GetType().GetMethod("Add").Invoke(trgList, new object[] { DeepCopy(o) }); //NOXLATE
pi.SetValue(target, trgList, null);
}
else if (pi.GetValue(source, null).GetType().IsArray)
{
Array sourceArr = (Array)pi.GetValue(source, null);
Array targetArr = (Array)Activator.CreateInstance(sourceArr.GetType(), new object[] { sourceArr.Length });
for (int i = 0; i < targetArr.Length; i++)
targetArr.SetValue(DeepCopy(sourceArr.GetValue(i)), i);
pi.SetValue(target, targetArr, null);
}
else
pi.SetValue(target, DeepCopy(pi.GetValue(source, null)), null);
}
return target;
}
///
/// Reads all data from a stream, and returns it as a single array.
/// Note that this is very inefficient if the stream is several megabytes long.
///
/// The stream to exhaust
/// The streams content as an array
public static byte[] StreamAsArray(Stream s)
{
var mes = s as MemoryStream;
if (mes != null)
return mes.ToArray();
if (!s.CanSeek)
{
var ms = new MemoryStream();
byte[] buf = new byte[1024];
int c;
while ((c = s.Read(buf, 0, buf.Length)) > 0)
ms.Write(buf, 0, c);
return ms.ToArray();
}
else
{
byte[] buf = new byte[s.Length];
s.Position = 0;
s.Read(buf, 0, buf.Length);
return buf;
}
}
///
/// Serializes the given object as a UTF-8 encoded XML string. Any BOM is stripped from the XML string
///
///
///
///
public static string NormalizedSerialize(XmlSerializer serializer, object o)
{
using (var ms = new MemoryStream())
{
using (var xw = new Utf8XmlWriter(ms))
{
serializer.Serialize(xw, o);
using (var ms2 = RemoveUTF8BOM(ms))
{
using (var sr = new StreamReader(ms2))
{
return sr.ReadToEnd();
}
}
}
}
}
///
/// Creates a copy of the stream, with removed Utf8 BOM, if any
///
/// The stream to fix
/// A stream with no Utf8 BOM
public static MemoryStream RemoveUTF8BOM(MemoryStream ms)
{
//Skip UTF file header, since the MapGuide XmlParser is broken
ms.Position = 0;
byte[] utfheader = new byte[3];
if (ms.Read(utfheader, 0, utfheader.Length) == utfheader.Length)
if (utfheader[0] == 0xEF && utfheader[1] == 0xBB && utfheader[2] == 0xBF)
{
ms.Position = 3;
var mxs = new MemoryStream();
CopyStream(ms, mxs, false);
mxs.Position = 0;
return mxs;
}
ms.Position = 0;
return ms;
}
///
/// Returns a type used to define an unknown column type in a feature reader
///
public static Type UnmappedType => typeof(UnmappedDataType);
///
/// Returns a type used to define a raster column in a feature reader
///
public static Type RasterType => typeof(Bitmap);
///
/// Returns the type used to define a geometry column in a feature reader
///
public static Type GeometryType => typeof(IGeometry);
///
/// This method tries to extract the html content of a WebException.
/// If succesfull, it will return an exception with an error message corresponding to the html text.
/// If not, it will return the original exception object.
///
/// The exception object to extract from
/// A potentially better exeception
public static Exception ThrowAsWebException(Exception ex)
{
if (ex as System.Net.WebException != null)
{
try
{
System.Net.WebException wex = ex as System.Net.WebException;
using (System.IO.StreamReader sr = new System.IO.StreamReader(wex.Response.GetResponseStream()))
{
string html = sr.ReadToEnd();
System.Text.RegularExpressions.Regex r = new System.Text.RegularExpressions.Regex("(\\)(.+)\\<\\/body\\>", System.Text.RegularExpressions.RegexOptions.Singleline);
System.Text.RegularExpressions.Match m = r.Match(html);
if (m.Success && m.Groups.Count == 3)
{
html = m.Groups[2].Value;
int n = html.IndexOf(""); //NOXLATE
if (n > 0)
html = html.Substring(n + "".Length); //NOXLATE
}
return new Exception(wex.Message + ": " + html, wex); //NOXLATE
}
}
catch
{
}
}
return ex;
}
///
/// Removes the outer <FeatureInformation> tag, and returns a blank string for empty sets.
/// This eases the use of SelectionXml, because different parts of MapGuide represents it differently.
///
/// The string to clean
/// The cleaned string
public static string CleanUpFeatureSet(string input)
{
if (string.IsNullOrEmpty(input))
return input;
var doc1 = new XmlDocument();
doc1.LoadXml(input);
var doc2 = new XmlDocument();
XmlNode root1 = doc1["FeatureInformation"]; //NOXLATE
if (root1 == null)
root1 = doc1;
if (root1["FeatureSet"] != null) //NOXLATE
{
XmlNode root2 = doc2.AppendChild(doc2.CreateElement("FeatureSet")); //NOXLATE
root2.InnerXml = root1["FeatureSet"].InnerXml; //NOXLATE
}
return doc2.OuterXml == "" ? "" : doc2.OuterXml; //NOXLATE
}
///
/// Formats a number of bytes to a human readable format, ea.: 2.56 Kb
///
/// The size in bytes
/// The human readable string
public static string FormatSizeString(long size)
{
if (size > 1024 * 1024 * 1024)
return string.Format("{0:N} GB", (double)size / (1024 * 1024 * 1024)); //NOXLATE
else if (size > 1024 * 1024)
return string.Format("{0:N} MB", (double)size / (1024 * 1024)); //NOXLATE
else if (size > 1024)
return string.Format("{0:N} KB", (double)size / 1024); //NOXLATE
else
return string.Format("{0} bytes", size); //NOXLATE
}
private static Regex EncRegExp = new Regex(@"(\-x[0-2a-fA-F][0-9a-fA-F]\-)|(\-dot\-)|(\-colon\-)", System.Text.RegularExpressions.RegexOptions.Compiled);
private static Regex TokenRegex = new Regex("^x[0-9a-fA-F][0-9a-fA-F]", RegexOptions.Compiled);
private static Regex TokenRegex2 = new Regex("^_x[0-9a-fA-F][0-9a-fA-F]", RegexOptions.Compiled);
///
/// FDO encodes a string
///
///
///
///
/// FDO names must always be encoded when writing back to attributes in XML configuration documents as it may contain reserved characters that would render the final XML attribute content invalid.
///
///
/// Consequently, such names must always be decoded when reading from XML configuration documents otherwise these escape characters may still be present after reading
///
///
///
public static string EncodeFDOName(string name)
{
//Decode characters not allowed by FDO
string lName = name.Replace("-dot-", ".")
.Replace("-colon-", ":");
//Break the name up by '-' delimiters
string[] tokens = lName.Split(new char[] { '-' }, StringSplitOptions.None);
StringBuilder outName = new StringBuilder();
// Encode any characters that are not allowed in XML names.
// The encoding pattern is "-x%x-" where %x is the character value in hexidecimal.
// The dash delimeters were an unfortunate choice since dash cannot be the 1st character
// in an XML name. When the 1st character needs to be encoded, it is encoded as "_x%x-" to
// resolve this issue.
for (int i = 0; i < tokens.Length; i++)
{
string token = tokens[i];
bool bMatchedToken = false;
if (TokenRegex.Match(token, 0).Success)
{
bMatchedToken = true;
// the token happens to match the encoding pattern. We want to avoid
// decoding this sub-string on decode. This is done by encoding the leading
// dash.
if (outName.Length == 0)
outName.Append(string.Format("_x{0:X}-", Convert.ToInt32('-')).ToLower());
else
outName.Append(string.Format("-x{0:X}-", Convert.ToInt32('-')).ToLower());
}
else if (TokenRegex2.Match(token, 0).Success && i == 0)
{
bMatchedToken = true;
// the token happens to match the encoding pattern for the 1st character.
// We want to avoid decoding this sub-string on decode.
// This is done by prepending a dummy encoding for character 0. This character is
// discarded on decode.
outName.Append("_x00-");
}
else
{
// The token doesn't match the encoding pattern, just write the dash
// that was discarded by the tokenizer.
if (i > 0)
{
if (outName.Length == 0)
outName.Append("_x2d-"); // 1st character so lead with '_'
else
outName.Append("-");
}
}
outName.Append(bMatchedToken ? token : ReplaceBadChars(token, outName.Length == 0));
}
char c = outName[0];
//Perform actual substitutions of bad characters
outName = outName.Remove(0, 1);
//Check if the first character requires a meta-escape character replacement
string prefix = c + string.Empty;
switch (c)
{
case ' ':
prefix = "_x20-";
break;
case '-':
prefix = "_x2d-";
break;
case '&':
prefix = "_x26-";
break;
default:
if (Char.IsDigit(c))
{
prefix = "_x3" + c + "-";
}
break;
}
string result = prefix + outName.ToString();
return result;
}
private static string ReplaceBadChars(string token, bool bFirstInString)
{
StringBuilder sb = new StringBuilder();
bool bFirstChar = bFirstInString;
foreach (char c in token)
{
if (Char.IsDigit(c) || IsValidXmlChar(c))
sb.Append(c);
else
sb.Append(string.Format("{0}x{1:X}-", bFirstChar ? "_" : "-", Convert.ToInt32(c)).ToLower());
bFirstChar = false;
}
return sb.ToString();
}
private static bool IsValidXmlChar(char c)
{
try
{
XmlConvert.VerifyNCName(c + "");
return true;
}
catch
{
return false;
}
}
///
/// Converts FDO encoded characters into their original character.
///
/// The FDO encoded string
///
///
/// FDO names must always be encoded when writing back to attributes in XML configuration documents as it may contain reserved characters that would render the final XML attribute content invalid.
///
///
/// Consequently, such names must always be decoded when reading from XML configuration documents otherwise these escape characters may still be present after reading
///
///
/// The unencoded version of the string
public static string DecodeFDOName(string name)
{
// The encoding pattern is delimited by '-' so break string up by '-'.
string[] tokens = name.Split(new char[] { '-' }, StringSplitOptions.None);
bool prevDecode = true;
StringBuilder decoded = new StringBuilder();
for (int i = 0; i < tokens.Length; i++)
{
string token = tokens[i];
//This is a special character inserted during the encoding process.
//If we find this at the beginning, discard it
if (i == 0 && token == "_x00")
continue;
var m = TokenRegex.Match(token, 0);
var m2 = TokenRegex2.Match(token, 0);
if (token.Length == 4 && token.StartsWith("_x3") && Char.IsDigit(token[3]))
{
decoded.Append(token[3]);
prevDecode = true;
}
else
{
if ((!prevDecode) && m.Success)
{
string replace = ((char)int.Parse(m.Value.Substring(1, 2), System.Globalization.NumberStyles.HexNumber)).ToString();
decoded.Append(replace);
prevDecode = true;
}
else if ((i == 0) && m2.Success)
{
string replace = ((char)int.Parse(m2.Value.Substring(2, 2), System.Globalization.NumberStyles.HexNumber)).ToString();
decoded.Append(replace);
prevDecode = true;
}
else
{
if (i > 0 && !prevDecode)
decoded.Append("-");
decoded.Append(token);
prevDecode = false;
}
}
}
return decoded.ToString();
}
///
/// Enumerates all xml nodes in the document, and looks for tags or attributes named ResourceId
///
/// The xml item to examine
/// A list with all found elements
public static List> GetResourceIdPointers(System.Xml.XmlNode item)
{
var lst = new Queue();
var res = new List>();
lst.Enqueue(item);
while (lst.Count > 0)
{
XmlNode n = lst.Dequeue();
foreach (XmlNode nx in n.ChildNodes)
{
if (nx.NodeType == XmlNodeType.Element)
lst.Enqueue(nx);
}
if (n.Name == "ResourceId") //NOXLATE
{
res.Add(new KeyValuePair(n, n.InnerXml));
}
if (n.Attributes != null)
{
foreach (XmlAttribute a in n.Attributes)
{
if (a.Name == "ResourceId") //NOXLATE
res.Add(new KeyValuePair(a, a.Value));
}
}
}
return res;
}
///
/// Enumerates all objects by reflection, returns the list of referenced objects
///
/// The object to examine
/// Te combined list of references
public static List