blob: 22143b5734f7276ec711403a0e2c650901459cff [file] [log] [blame]
/***************************************************************************
Copyright (c) Microsoft Corporation 2012-2015.
This code is licensed using the Microsoft Public License (Ms-PL). The text of the license can be found here:
http://www.microsoft.com/resources/sharedsource/licensingbasics/publiclicense.mspx
Published at http://OpenXmlDeveloper.org
Resource Center and Documentation: http://openxmldeveloper.org/wiki/w/wiki/powertools-for-open-xml.aspx
Developer: Eric White
Blog: http://www.ericwhite.com
Twitter: @EricWhiteDev
Email: eric@ericwhite.com
Version: 2.6.00
* Enhancements to support HtmlConverter.cs
***************************************************************************/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Schema;
namespace OpenXmlPowerTools
{
public static class PtUtils
{
public static string NormalizeDirName(string dirName)
{
string d = dirName.Replace('\\', '/');
if (d[dirName.Length - 1] != '/' && d[dirName.Length - 1] != '\\')
return d + "/";
return d;
}
public static string MakeValidXml(string p)
{
return p.Any(c => c < 0x20)
? p.Select(c => c < 0x20 ? string.Format("_{0:X}_", (int) c) : c.ToString()).StringConcatenate()
: p;
}
public static void AddElementIfMissing(XDocument partXDoc, XElement existing, string newElement)
{
if (existing != null)
return;
XElement newXElement = XElement.Parse(newElement);
newXElement.Attributes().Where(a => a.IsNamespaceDeclaration).Remove();
if (partXDoc.Root != null) partXDoc.Root.Add(newXElement);
}
}
public class MhtParser
{
public string MimeVersion;
public string ContentType;
public MhtParserPart[] Parts;
public class MhtParserPart
{
public string ContentLocation;
public string ContentTransferEncoding;
public string ContentType;
public string CharSet;
public string Text;
public byte[] Binary;
}
public static MhtParser Parse(string src)
{
string mimeVersion = null;
string contentType = null;
string boundary = null;
string[] lines = src.Split(new[] { Environment.NewLine }, StringSplitOptions.None);
var priambleKeyWords = new[]
{
"MIME-VERSION:",
"CONTENT-TYPE:",
};
var priamble = lines.TakeWhile(l =>
{
var s = l.ToUpper();
return priambleKeyWords.Any(pk => s.StartsWith(pk));
}).ToArray();
foreach (var item in priamble)
{
if (item.ToUpper().StartsWith("MIME-VERSION:"))
mimeVersion = item.Substring("MIME-VERSION:".Length).Trim();
else if (item.ToUpper().StartsWith("CONTENT-TYPE:"))
{
var contentTypeLine = item.Substring("CONTENT-TYPE:".Length).Trim();
var spl = contentTypeLine.Split(';').Select(z => z.Trim()).ToArray();
foreach (var s in spl)
{
if (s.StartsWith("boundary"))
{
var begText = "boundary=\"";
var begLen = begText.Length;
boundary = s.Substring(begLen, s.Length - begLen - 1).TrimStart('-');
continue;
}
if (contentType == null)
{
contentType = s;
continue;
}
throw new OpenXmlPowerToolsException("Unexpected content in MHTML");
}
}
}
var grouped = lines
.Skip(priamble.Length)
.GroupAdjacent(l =>
{
var b = l.TrimStart('-') == boundary;
return b;
})
.Where(g => g.Key == false)
.ToArray();
var parts = grouped.Select(rp =>
{
var partPriambleKeyWords = new[]
{
"CONTENT-LOCATION:",
"CONTENT-TRANSFER-ENCODING:",
"CONTENT-TYPE:",
};
var partPriamble = rp.TakeWhile(l =>
{
var s = l.ToUpper();
return partPriambleKeyWords.Any(pk => s.StartsWith(pk));
}).ToArray();
string contentLocation = null;
string contentTransferEncoding = null;
string partContentType = null;
string partCharSet = null;
byte[] partBinary = null;
foreach (var item in partPriamble)
{
if (item.ToUpper().StartsWith("CONTENT-LOCATION:"))
contentLocation = item.Substring("CONTENT-LOCATION:".Length).Trim();
else if (item.ToUpper().StartsWith("CONTENT-TRANSFER-ENCODING:"))
contentTransferEncoding = item.Substring("CONTENT-TRANSFER-ENCODING:".Length).Trim();
else if (item.ToUpper().StartsWith("CONTENT-TYPE:"))
partContentType = item.Substring("CONTENT-TYPE:".Length).Trim();
}
var blankLinesAtBeginning = rp
.Skip(partPriamble.Length)
.TakeWhile(l => l == "")
.Count();
var partText = rp
.Skip(partPriamble.Length)
.Skip(blankLinesAtBeginning)
.Select(l => l + Environment.NewLine)
.StringConcatenate();
if (partContentType != null && partContentType.Contains(";"))
{
string thisPartContentType = null;
var spl = partContentType.Split(';').Select(s => s.Trim()).ToArray();
foreach (var s in spl)
{
if (s.StartsWith("charset"))
{
var begText = "charset=\"";
var begLen = begText.Length;
partCharSet = s.Substring(begLen, s.Length - begLen - 1);
continue;
}
if (thisPartContentType == null)
{
thisPartContentType = s;
continue;
}
throw new OpenXmlPowerToolsException("Unexpected content in MHTML");
}
partContentType = thisPartContentType;
}
if (contentTransferEncoding != null && contentTransferEncoding.ToUpper() == "BASE64")
{
partBinary = Convert.FromBase64String(partText);
}
return new MhtParserPart()
{
ContentLocation = contentLocation,
ContentTransferEncoding = contentTransferEncoding,
ContentType = partContentType,
CharSet = partCharSet,
Text = partText,
Binary = partBinary,
};
})
.Where(p => p.ContentType != null)
.ToArray();
return new MhtParser()
{
ContentType = contentType,
MimeVersion = mimeVersion,
Parts = parts,
};
}
}
public class Normalizer
{
public static XDocument Normalize(XDocument source, XmlSchemaSet schema)
{
bool havePSVI = false;
// validate, throw errors, add PSVI information
if (schema != null)
{
source.Validate(schema, null, true);
havePSVI = true;
}
return new XDocument(
source.Declaration,
source.Nodes().Select(n =>
{
// Remove comments, processing instructions, and text nodes that are
// children of XDocument. Only white space text nodes are allowed as
// children of a document, so we can remove all text nodes.
if (n is XComment || n is XProcessingInstruction || n is XText)
return null;
XElement e = n as XElement;
if (e != null)
return NormalizeElement(e, havePSVI);
return n;
}
)
);
}
public static bool DeepEqualsWithNormalization(XDocument doc1, XDocument doc2,
XmlSchemaSet schemaSet)
{
XDocument d1 = Normalize(doc1, schemaSet);
XDocument d2 = Normalize(doc2, schemaSet);
return XNode.DeepEquals(d1, d2);
}
private static IEnumerable<XAttribute> NormalizeAttributes(XElement element,
bool havePSVI)
{
return element.Attributes()
.Where(a => !a.IsNamespaceDeclaration &&
a.Name != Xsi.schemaLocation &&
a.Name != Xsi.noNamespaceSchemaLocation)
.OrderBy(a => a.Name.NamespaceName)
.ThenBy(a => a.Name.LocalName)
.Select(
a =>
{
if (havePSVI)
{
var dt = a.GetSchemaInfo().SchemaType.TypeCode;
switch (dt)
{
case XmlTypeCode.Boolean:
return new XAttribute(a.Name, (bool)a);
case XmlTypeCode.DateTime:
return new XAttribute(a.Name, (DateTime)a);
case XmlTypeCode.Decimal:
return new XAttribute(a.Name, (decimal)a);
case XmlTypeCode.Double:
return new XAttribute(a.Name, (double)a);
case XmlTypeCode.Float:
return new XAttribute(a.Name, (float)a);
case XmlTypeCode.HexBinary:
case XmlTypeCode.Language:
return new XAttribute(a.Name,
((string)a).ToLower());
}
}
return a;
}
);
}
private static XNode NormalizeNode(XNode node, bool havePSVI)
{
// trim comments and processing instructions from normalized tree
if (node is XComment || node is XProcessingInstruction)
return null;
XElement e = node as XElement;
if (e != null)
return NormalizeElement(e, havePSVI);
// Only thing left is XCData and XText, so clone them
return node;
}
private static XElement NormalizeElement(XElement element, bool havePSVI)
{
if (havePSVI)
{
var dt = element.GetSchemaInfo();
switch (dt.SchemaType.TypeCode)
{
case XmlTypeCode.Boolean:
return new XElement(element.Name,
NormalizeAttributes(element, havePSVI),
(bool)element);
case XmlTypeCode.DateTime:
return new XElement(element.Name,
NormalizeAttributes(element, havePSVI),
(DateTime)element);
case XmlTypeCode.Decimal:
return new XElement(element.Name,
NormalizeAttributes(element, havePSVI),
(decimal)element);
case XmlTypeCode.Double:
return new XElement(element.Name,
NormalizeAttributes(element, havePSVI),
(double)element);
case XmlTypeCode.Float:
return new XElement(element.Name,
NormalizeAttributes(element, havePSVI),
(float)element);
case XmlTypeCode.HexBinary:
case XmlTypeCode.Language:
return new XElement(element.Name,
NormalizeAttributes(element, havePSVI),
((string)element).ToLower());
default:
return new XElement(element.Name,
NormalizeAttributes(element, havePSVI),
element.Nodes().Select(n => NormalizeNode(n, havePSVI))
);
}
}
else
{
return new XElement(element.Name,
NormalizeAttributes(element, havePSVI),
element.Nodes().Select(n => NormalizeNode(n, havePSVI))
);
}
}
}
public class FileUtils
{
public static DirectoryInfo GetDateTimeStampedDirectoryInfo(string prefix)
{
DateTime now = DateTime.Now;
string dirName =
prefix +
string.Format("-{0:00}-{1:00}-{2:00}-{3:00}{4:00}{5:00}", now.Year - 2000, now.Month, now.Day, now.Hour,
now.Minute, now.Second);
return new DirectoryInfo(dirName);
}
public static FileInfo GetDateTimeStampedFileInfo(string prefix, string suffix)
{
DateTime now = DateTime.Now;
string fileName =
prefix +
string.Format("-{0:00}-{1:00}-{2:00}-{3:00}{4:00}{5:00}", now.Year - 2000, now.Month, now.Day, now.Hour,
now.Minute, now.Second) +
suffix;
return new FileInfo(fileName);
}
public static void ThreadSafeCreateDirectory(DirectoryInfo dir)
{
while (true)
{
if (dir.Exists)
break;
try
{
dir.Create();
break;
}
catch (IOException)
{
System.Threading.Thread.Sleep(50);
}
}
}
public static void ThreadSafeCopy(FileInfo sourceFile, FileInfo destFile)
{
while (true)
{
if (destFile.Exists)
break;
try
{
File.Copy(sourceFile.FullName, destFile.FullName);
break;
}
catch (IOException)
{
System.Threading.Thread.Sleep(50);
}
}
}
public static void ThreadSafeCreateEmptyTextFileIfNotExist(FileInfo file)
{
while (true)
{
if (file.Exists)
break;
try
{
File.WriteAllText(file.FullName, "");
break;
}
catch (IOException)
{
System.Threading.Thread.Sleep(50);
}
}
}
#if !NET35
internal static void ThreadSafeAppendAllLines(FileInfo file, string[] strings)
{
while (true)
{
try
{
File.AppendAllLines(file.FullName, strings);
break;
}
catch (IOException)
{
System.Threading.Thread.Sleep(50);
}
}
}
#endif
public static List<string> GetFilesRecursive(DirectoryInfo dir, string searchPattern)
{
var fileList = new List<string>();
GetFilesRecursiveInternal(dir, searchPattern, fileList);
return fileList;
}
private static void GetFilesRecursiveInternal(DirectoryInfo dir, string searchPattern, List<string> fileList)
{
fileList.AddRange(dir.GetFiles(searchPattern).Select(file => file.FullName));
foreach (DirectoryInfo subdir in dir.GetDirectories())
GetFilesRecursiveInternal(subdir, searchPattern, fileList);
}
public static List<string> GetFilesRecursive(DirectoryInfo dir)
{
var fileList = new List<string>();
GetFilesRecursiveInternal(dir, fileList);
return fileList;
}
private static void GetFilesRecursiveInternal(DirectoryInfo dir, List<string> fileList)
{
fileList.AddRange(dir.GetFiles().Select(file => file.FullName));
foreach (DirectoryInfo subdir in dir.GetDirectories())
GetFilesRecursiveInternal(subdir, fileList);
}
public static void CopyStream(Stream source, Stream target)
{
const int bufSize = 0x4096;
var buf = new byte[bufSize];
int bytesRead;
while ((bytesRead = source.Read(buf, 0, bufSize)) > 0)
target.Write(buf, 0, bytesRead);
}
}
public static class PtExtensions
{
public static XElement GetXElement(this XmlNode node)
{
var xDoc = new XDocument();
using (XmlWriter xmlWriter = xDoc.CreateWriter())
node.WriteTo(xmlWriter);
return xDoc.Root;
}
public static XmlNode GetXmlNode(this XElement element)
{
var xmlDoc = new XmlDocument();
using (XmlReader xmlReader = element.CreateReader())
xmlDoc.Load(xmlReader);
return xmlDoc;
}
public static XDocument GetXDocument(this XmlDocument document)
{
var xDoc = new XDocument();
using (XmlWriter xmlWriter = xDoc.CreateWriter())
document.WriteTo(xmlWriter);
XmlDeclaration decl = document.ChildNodes.OfType<XmlDeclaration>().FirstOrDefault();
if (decl != null)
xDoc.Declaration = new XDeclaration(decl.Version, decl.Encoding, decl.Standalone);
return xDoc;
}
public static XmlDocument GetXmlDocument(this XDocument document)
{
var xmlDoc = new XmlDocument();
using (XmlReader xmlReader = document.CreateReader())
{
xmlDoc.Load(xmlReader);
if (document.Declaration != null)
{
XmlDeclaration dec = xmlDoc.CreateXmlDeclaration(document.Declaration.Version,
document.Declaration.Encoding, document.Declaration.Standalone);
xmlDoc.InsertBefore(dec, xmlDoc.FirstChild);
}
}
return xmlDoc;
}
public static string StringConcatenate(this IEnumerable<string> source)
{
return source.Aggregate(
new StringBuilder(),
(sb, s) => sb.Append(s),
sb => sb.ToString());
}
public static string StringConcatenate<T>(this IEnumerable<T> source, Func<T, string> projectionFunc)
{
return source.Aggregate(
new StringBuilder(),
(sb, i) => sb.Append(projectionFunc(i)),
sb => sb.ToString());
}
public static IEnumerable<TResult> PtZip<TFirst, TSecond, TResult>(
this IEnumerable<TFirst> first,
IEnumerable<TSecond> second,
Func<TFirst, TSecond, TResult> func)
{
using (IEnumerator<TFirst> ie1 = first.GetEnumerator())
using (IEnumerator<TSecond> ie2 = second.GetEnumerator())
while (ie1.MoveNext() && ie2.MoveNext())
yield return func(ie1.Current, ie2.Current);
}
public static IEnumerable<IGrouping<TKey, TSource>> GroupAdjacent<TSource, TKey>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector)
{
TKey last = default(TKey);
var haveLast = false;
var list = new List<TSource>();
foreach (TSource s in source)
{
TKey k = keySelector(s);
if (haveLast)
{
if (!k.Equals(last))
{
yield return new GroupOfAdjacent<TSource, TKey>(list, last);
list = new List<TSource> { s };
last = k;
}
else
{
list.Add(s);
last = k;
}
}
else
{
list.Add(s);
last = k;
haveLast = true;
}
}
if (haveLast)
yield return new GroupOfAdjacent<TSource, TKey>(list, last);
}
private static void InitializeSiblingsReverseDocumentOrder(XElement element)
{
XElement prev = null;
foreach (XElement e in element.Elements())
{
e.AddAnnotation(new SiblingsReverseDocumentOrderInfo { PreviousSibling = prev });
prev = e;
}
}
[SuppressMessage("ReSharper", "PossibleNullReferenceException")]
public static IEnumerable<XElement> SiblingsBeforeSelfReverseDocumentOrder(
this XElement element)
{
if (element.Annotation<SiblingsReverseDocumentOrderInfo>() == null)
InitializeSiblingsReverseDocumentOrder(element.Parent);
XElement current = element;
while (true)
{
XElement previousElement = current
.Annotation<SiblingsReverseDocumentOrderInfo>()
.PreviousSibling;
if (previousElement == null)
yield break;
yield return previousElement;
current = previousElement;
}
}
private static void InitializeDescendantsReverseDocumentOrder(XElement element)
{
XElement prev = null;
foreach (XElement e in element.Descendants())
{
e.AddAnnotation(new DescendantsReverseDocumentOrderInfo { PreviousElement = prev });
prev = e;
}
}
[SuppressMessage("ReSharper", "PossibleNullReferenceException")]
public static IEnumerable<XElement> DescendantsBeforeSelfReverseDocumentOrder(
this XElement element)
{
if (element.Annotation<DescendantsReverseDocumentOrderInfo>() == null)
InitializeDescendantsReverseDocumentOrder(element.AncestorsAndSelf().Last());
XElement current = element;
while (true)
{
XElement previousElement = current
.Annotation<DescendantsReverseDocumentOrderInfo>()
.PreviousElement;
if (previousElement == null)
yield break;
yield return previousElement;
current = previousElement;
}
}
private static void InitializeDescendantsTrimmedReverseDocumentOrder(XElement element, XName trimName)
{
XElement prev = null;
foreach (XElement e in element.DescendantsTrimmed(trimName))
{
e.AddAnnotation(new DescendantsTrimmedReverseDocumentOrderInfo { PreviousElement = prev });
prev = e;
}
}
[SuppressMessage("ReSharper", "PossibleNullReferenceException")]
public static IEnumerable<XElement> DescendantsTrimmedBeforeSelfReverseDocumentOrder(
this XElement element, XName trimName)
{
if (element.Annotation<DescendantsTrimmedReverseDocumentOrderInfo>() == null)
{
XElement ances = element.AncestorsAndSelf(W.txbxContent).FirstOrDefault() ??
element.AncestorsAndSelf().Last();
InitializeDescendantsTrimmedReverseDocumentOrder(ances, trimName);
}
XElement current = element;
while (true)
{
XElement previousElement = current
.Annotation<DescendantsTrimmedReverseDocumentOrderInfo>()
.PreviousElement;
if (previousElement == null)
yield break;
yield return previousElement;
current = previousElement;
}
}
public static string ToStringNewLineOnAttributes(this XElement element)
{
var settings = new XmlWriterSettings
{
Indent = true,
OmitXmlDeclaration = true,
NewLineOnAttributes = true
};
var stringBuilder = new StringBuilder();
using (var stringWriter = new StringWriter(stringBuilder))
using (XmlWriter xmlWriter = XmlWriter.Create(stringWriter, settings))
element.WriteTo(xmlWriter);
return stringBuilder.ToString();
}
public static IEnumerable<XElement> DescendantsTrimmed(this XElement element,
XName trimName)
{
return DescendantsTrimmed(element, e => e.Name == trimName);
}
public static IEnumerable<XElement> DescendantsTrimmed(this XElement element,
Func<XElement, bool> predicate)
{
Stack<IEnumerator<XElement>> iteratorStack = new Stack<IEnumerator<XElement>>();
iteratorStack.Push(element.Elements().GetEnumerator());
while (iteratorStack.Count > 0)
{
while (iteratorStack.Peek().MoveNext())
{
XElement currentXElement = iteratorStack.Peek().Current;
if (predicate(currentXElement))
{
yield return currentXElement;
continue;
}
yield return currentXElement;
iteratorStack.Push(currentXElement.Elements().GetEnumerator());
}
iteratorStack.Pop();
}
}
public static IEnumerable<TResult> Rollup<TSource, TResult>(
this IEnumerable<TSource> source,
TResult seed,
Func<TSource, TResult, TResult> projection)
{
TResult nextSeed = seed;
foreach (TSource src in source)
{
TResult projectedValue = projection(src, nextSeed);
nextSeed = projectedValue;
yield return projectedValue;
}
}
public static IEnumerable<TResult> Rollup<TSource, TResult>(
this IEnumerable<TSource> source,
TResult seed,
Func<TSource, TResult, int, TResult> projection)
{
TResult nextSeed = seed;
int index = 0;
foreach (TSource src in source)
{
TResult projectedValue = projection(src, nextSeed, index++);
nextSeed = projectedValue;
yield return projectedValue;
}
}
public static IEnumerable<TSource> SequenceAt<TSource>(this TSource[] source, int index)
{
int i = index;
while (i < source.Length)
yield return source[i++];
}
public static IEnumerable<T> SkipLast<T>(this IEnumerable<T> source, int count)
{
var saveList = new Queue<T>();
var saved = 0;
foreach (T item in source)
{
if (saved < count)
{
saveList.Enqueue(item);
++saved;
continue;
}
saveList.Enqueue(item);
yield return saveList.Dequeue();
}
}
public static bool? ToBoolean(this XAttribute a)
{
if (a == null)
return null;
string s = ((string) a).ToLower();
switch (s)
{
case "1":
return true;
case "0":
return false;
case "true":
return true;
case "false":
return false;
case "on":
return true;
case "off":
return false;
default:
return (bool) a;
}
}
private static string GetQName(XElement xe)
{
string prefix = xe.GetPrefixOfNamespace(xe.Name.Namespace);
if (xe.Name.Namespace == XNamespace.None || prefix == null)
return xe.Name.LocalName;
return prefix + ":" + xe.Name.LocalName;
}
private static string GetQName(XAttribute xa)
{
string prefix = xa.Parent != null ? xa.Parent.GetPrefixOfNamespace(xa.Name.Namespace) : null;
if (xa.Name.Namespace == XNamespace.None || prefix == null)
return xa.Name.ToString();
return prefix + ":" + xa.Name.LocalName;
}
private static string NameWithPredicate(XElement el)
{
if (el.Parent != null && el.Parent.Elements(el.Name).Count() != 1)
return GetQName(el) + "[" +
(el.ElementsBeforeSelf(el.Name).Count() + 1) + "]";
else
return GetQName(el);
}
public static string StrCat<T>(this IEnumerable<T> source,
string separator)
{
return source.Aggregate(new StringBuilder(),
(sb, i) => sb
.Append(i.ToString())
.Append(separator),
s => s.ToString());
}
public static string GetXPath(this XObject xobj)
{
if (xobj.Parent == null)
{
var doc = xobj as XDocument;
if (doc != null)
return ".";
var el = xobj as XElement;
if (el != null)
return "/" + NameWithPredicate(el);
var xt = xobj as XText;
if (xt != null)
return null;
//
//the following doesn't work because the XPath data
//model doesn't include white space text nodes that
//are children of the document.
//
//return
// "/" +
// (
// xt
// .Document
// .Nodes()
// .OfType<XText>()
// .Count() != 1 ?
// "text()[" +
// (xt
// .NodesBeforeSelf()
// .OfType<XText>()
// .Count() + 1) + "]" :
// "text()"
// );
//
var com = xobj as XComment;
if (com != null && com.Document != null)
return
"/" +
(
com
.Document
.Nodes()
.OfType<XComment>()
.Count() != 1
? "comment()[" +
(com
.NodesBeforeSelf()
.OfType<XComment>()
.Count() + 1) +
"]"
: "comment()"
);
var pi = xobj as XProcessingInstruction;
if (pi != null)
return
"/" +
(
pi.Document != null && pi.Document.Nodes().OfType<XProcessingInstruction>().Count() != 1
? "processing-instruction()[" +
(pi
.NodesBeforeSelf()
.OfType<XProcessingInstruction>()
.Count() + 1) +
"]"
: "processing-instruction()"
);
return null;
}
else
{
var el = xobj as XElement;
if (el != null)
{
return
"/" +
el
.Ancestors()
.InDocumentOrder()
.Select(e => NameWithPredicate(e))
.StrCat("/") +
NameWithPredicate(el);
}
var at = xobj as XAttribute;
if (at != null && at.Parent != null)
return
"/" +
at
.Parent
.AncestorsAndSelf()
.InDocumentOrder()
.Select(e => NameWithPredicate(e))
.StrCat("/") +
"@" + GetQName(at);
var com = xobj as XComment;
if (com != null && com.Parent != null)
return
"/" +
com
.Parent
.AncestorsAndSelf()
.InDocumentOrder()
.Select(e => NameWithPredicate(e))
.StrCat("/") +
(
com
.Parent
.Nodes()
.OfType<XComment>()
.Count() != 1
? "comment()[" +
(com
.NodesBeforeSelf()
.OfType<XComment>()
.Count() + 1) + "]"
: "comment()"
);
var cd = xobj as XCData;
if (cd != null && cd.Parent != null)
return
"/" +
cd
.Parent
.AncestorsAndSelf()
.InDocumentOrder()
.Select(e => NameWithPredicate(e))
.StrCat("/") +
(
cd
.Parent
.Nodes()
.OfType<XText>()
.Count() != 1
? "text()[" +
(cd
.NodesBeforeSelf()
.OfType<XText>()
.Count() + 1) + "]"
: "text()"
);
var tx = xobj as XText;
if (tx != null && tx.Parent != null)
return
"/" +
tx
.Parent
.AncestorsAndSelf()
.InDocumentOrder()
.Select(e => NameWithPredicate(e))
.StrCat("/") +
(
tx
.Parent
.Nodes()
.OfType<XText>()
.Count() != 1
? "text()[" +
(tx
.NodesBeforeSelf()
.OfType<XText>()
.Count() + 1) + "]"
: "text()"
);
var pi = xobj as XProcessingInstruction;
if (pi != null && pi.Parent != null)
return
"/" +
pi
.Parent
.AncestorsAndSelf()
.InDocumentOrder()
.Select(e => NameWithPredicate(e))
.StrCat("/") +
(
pi
.Parent
.Nodes()
.OfType<XProcessingInstruction>()
.Count() != 1
? "processing-instruction()[" +
(pi
.NodesBeforeSelf()
.OfType<XProcessingInstruction>()
.Count() + 1) + "]"
: "processing-instruction()"
);
return null;
}
}
}
public class ExecutableRunner
{
public class RunResults
{
public int ExitCode;
public Exception RunException;
public StringBuilder Output;
public StringBuilder Error;
}
public static RunResults RunExecutable(string executablePath, string arguments, string workingDirectory)
{
RunResults runResults = new RunResults
{
Output = new StringBuilder(),
Error = new StringBuilder(),
RunException = null
};
try
{
if (File.Exists(executablePath))
{
using (Process proc = new Process())
{
proc.StartInfo.FileName = executablePath;
proc.StartInfo.Arguments = arguments;
proc.StartInfo.WorkingDirectory = workingDirectory;
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.RedirectStandardOutput = true;
proc.StartInfo.RedirectStandardError = true;
proc.OutputDataReceived +=
(o, e) => runResults.Output.Append(e.Data).Append(Environment.NewLine);
proc.ErrorDataReceived +=
(o, e) => runResults.Error.Append(e.Data).Append(Environment.NewLine);
proc.Start();
proc.BeginOutputReadLine();
proc.BeginErrorReadLine();
proc.WaitForExit();
runResults.ExitCode = proc.ExitCode;
}
}
else
{
throw new ArgumentException("Invalid executable path.", "executablePath");
}
}
catch (Exception e)
{
runResults.RunException = e;
}
return runResults;
}
}
public class SiblingsReverseDocumentOrderInfo
{
public XElement PreviousSibling;
}
public class DescendantsReverseDocumentOrderInfo
{
public XElement PreviousElement;
}
public class DescendantsTrimmedReverseDocumentOrderInfo
{
public XElement PreviousElement;
}
public class GroupOfAdjacent<TSource, TKey> : IGrouping<TKey, TSource>
{
public GroupOfAdjacent(List<TSource> source, TKey key)
{
GroupList = source;
Key = key;
}
public TKey Key { get; set; }
private List<TSource> GroupList { get; set; }
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable<TSource>) this).GetEnumerator();
}
IEnumerator<TSource> IEnumerable<TSource>.GetEnumerator()
{
return ((IEnumerable<TSource>) GroupList).GetEnumerator();
}
}
public static class PtBucketTimer
{
private class BucketInfo
{
public int Count;
public TimeSpan Time;
}
private static string LastBucket = null;
private static DateTime LastTime;
private static Dictionary<string, BucketInfo> Buckets;
public static void Bucket(string bucket)
{
DateTime now = DateTime.Now;
if (LastBucket != null)
{
TimeSpan d = now - LastTime;
if (Buckets.ContainsKey(LastBucket))
{
Buckets[LastBucket].Count = Buckets[LastBucket].Count + 1;
Buckets[LastBucket].Time += d;
}
else
{
Buckets.Add(LastBucket, new BucketInfo()
{
Count = 1,
Time = d,
});
}
}
LastBucket = bucket;
LastTime = now;
}
public static string DumpBucketsByKey()
{
StringBuilder sb = new StringBuilder();
foreach (var bucket in Buckets.OrderBy(b => b.Key))
{
string ts = bucket.Value.Time.ToString();
if (ts.Contains('.'))
ts = ts.Substring(0, ts.Length - 5);
string s = bucket.Key.PadRight(60, '-') + " " + string.Format("{0:00000000}", bucket.Value.Count) + " " + ts;
sb.Append(s + Environment.NewLine);
}
TimeSpan total = Buckets
.Aggregate(TimeSpan.Zero, (t, b) => t + b.Value.Time);
var tz = total.ToString();
sb.Append(string.Format("Total: {0}", tz.Substring(0, tz.Length - 5)));
return sb.ToString();
}
public static string DumpBucketsByTime()
{
StringBuilder sb = new StringBuilder();
foreach (var bucket in Buckets.OrderBy(b => b.Value.Time))
{
string ts = bucket.Value.Time.ToString();
if (ts.Contains('.'))
ts = ts.Substring(0, ts.Length - 5);
string s = bucket.Key.PadRight(60, '-') + " " + string.Format("{0:00000000}", bucket.Value.Count) + " " + ts;
sb.Append(s + Environment.NewLine);
}
TimeSpan total = Buckets
.Aggregate(TimeSpan.Zero, (t, b) => t + b.Value.Time);
var tz = total.ToString();
sb.Append(string.Format("Total: {0}", tz.Substring(0, tz.Length - 5)));
return sb.ToString();
}
public static void Init()
{
Buckets = new Dictionary<string, BucketInfo>();
}
}
public class XEntity : XText
{
public override void WriteTo(XmlWriter writer)
{
if (Value.Substring(0, 1) == "#")
{
string e = string.Format("&{0};", Value);
writer.WriteRaw(e);
}
else
writer.WriteEntityRef(Value);
}
public XEntity(string value) : base(value)
{
}
}
public static class Xsi
{
public static XNamespace xsi = "http://www.w3.org/2001/XMLSchema-instance";
public static XName schemaLocation = xsi + "schemaLocation";
public static XName noNamespaceSchemaLocation = xsi + "noNamespaceSchemaLocation";
}
}