220 lines
7.2 KiB
C#
220 lines
7.2 KiB
C#
/***************************************************************************
|
|
|
|
Copyright (c) Microsoft Corporation 2011.
|
|
|
|
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
|
|
|
|
***************************************************************************/
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Xml;
|
|
using System.Xml.Linq;
|
|
|
|
namespace OpenXmlPowerTools
|
|
{
|
|
public static class PtExtensions
|
|
{
|
|
public static string StringConcatenate(this IEnumerable<string> source)
|
|
{
|
|
StringBuilder sb = new StringBuilder();
|
|
foreach (string s in source)
|
|
sb.Append(s);
|
|
return sb.ToString();
|
|
}
|
|
|
|
public static string StringConcatenate<T>(
|
|
this IEnumerable<T> source,
|
|
Func<T, string> projectionFunc)
|
|
{
|
|
return source.Aggregate(
|
|
new StringBuilder(),
|
|
(s, i) => s.Append(projectionFunc(i)),
|
|
s => s.ToString());
|
|
}
|
|
|
|
public static IEnumerable<TResult> Zip<TFirst, TSecond, TResult>(
|
|
this IEnumerable<TFirst> first,
|
|
IEnumerable<TSecond> second,
|
|
Func<TFirst, TSecond, TResult> func)
|
|
{
|
|
var ie1 = first.GetEnumerator();
|
|
var 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);
|
|
bool haveLast = false;
|
|
List<TSource> 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>();
|
|
list.Add(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 InitializeReverseDocumentOrder(XElement element)
|
|
{
|
|
XElement prev = null;
|
|
foreach (XElement e in element.Elements())
|
|
{
|
|
e.AddAnnotation(new ReverseDocumentOrderInfo { PreviousSibling = prev });
|
|
prev = e;
|
|
}
|
|
}
|
|
|
|
public static IEnumerable<XElement> ElementsBeforeSelfReverseDocumentOrder(
|
|
this XElement element)
|
|
{
|
|
if (element.Annotation<ReverseDocumentOrderInfo>() == null)
|
|
InitializeReverseDocumentOrder(element.Parent);
|
|
XElement current = element;
|
|
while (true)
|
|
{
|
|
XElement previousElement = current
|
|
.Annotation<ReverseDocumentOrderInfo>()
|
|
.PreviousSibling;
|
|
if (previousElement == null)
|
|
yield break;
|
|
yield return previousElement;
|
|
current = previousElement;
|
|
}
|
|
}
|
|
|
|
public static string ToStringNewLineOnAttributes(this XElement element)
|
|
{
|
|
XmlWriterSettings settings = new XmlWriterSettings();
|
|
settings.Indent = true;
|
|
settings.OmitXmlDeclaration = true;
|
|
settings.NewLineOnAttributes = true;
|
|
StringBuilder stringBuilder = new StringBuilder();
|
|
using (StringWriter 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<TSource> SequenceAt<TSource>(this TSource[] source, int index)
|
|
{
|
|
int i = index;
|
|
while (i < source.Length)
|
|
yield return source[i++];
|
|
}
|
|
}
|
|
|
|
public class ReverseDocumentOrderInfo
|
|
{
|
|
public XElement PreviousSibling;
|
|
}
|
|
|
|
public class GroupOfAdjacent<TSource, TKey> : IEnumerable<TSource>, IGrouping<TKey, TSource>
|
|
{
|
|
public TKey Key { get; set; }
|
|
private List<TSource> GroupList { get; set; }
|
|
|
|
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
|
|
{
|
|
return ((System.Collections.Generic.IEnumerable<TSource>)this).GetEnumerator();
|
|
}
|
|
|
|
System.Collections.Generic.IEnumerator<TSource>
|
|
System.Collections.Generic.IEnumerable<TSource>.GetEnumerator()
|
|
{
|
|
foreach (var s in GroupList)
|
|
yield return s;
|
|
}
|
|
|
|
public GroupOfAdjacent(List<TSource> source, TKey key)
|
|
{
|
|
GroupList = source;
|
|
Key = key;
|
|
}
|
|
}
|
|
|
|
|
|
public class XEntity : XText
|
|
{
|
|
public override void WriteTo(XmlWriter writer)
|
|
{
|
|
writer.WriteEntityRef(this.Value);
|
|
}
|
|
public XEntity(string value) : base(value) { }
|
|
}
|
|
}
|