using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Dynamic; using System.Xml.Linq; using System.Xml.XPath; using System.Collections; namespace umbraco.MacroEngines { public class DynamicXml : DynamicObject, IEnumerable { public XElement BaseElement { get; set; } public DynamicXml(XElement baseElement) { this.BaseElement = baseElement; } public string InnerText { get { return BaseElement.Value; } } public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result) { int index = 0; if (indexes.Length > 0) { index = (int)indexes[0]; result = new DynamicXml(this.BaseElement.Elements().ToList()[index]); return true; } return base.TryGetIndex(binder, indexes, out result); } public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) { if (args.Length == 0 && binder.Name == "ToXml") { result = this.BaseElement.ToString(); return true; } if (args.Length == 1 && binder.Name == "XPath") { var elements = this.BaseElement.XPathSelectElements(args[0].ToString()); HandleIEnumerableXElement(elements, out result); return true; //anyway } return base.TryInvokeMember(binder, args, out result); } public override bool TryGetMember(GetMemberBinder binder, out object result) { //Go ahead and try to fetch all of the elements matching the member name, and wrap them var elements = BaseElement.Elements(binder.Name); if (HandleIEnumerableXElement(elements, out result)) { return true; } else { //Ok, so no elements matched, so lets try attributes var attributes = BaseElement.Attributes(binder.Name).Select(attr => attr.Value); int count = attributes.Count(); if (count > 0) { if (count > 1) result = attributes; //more than one attribute matched, lets return the collection else result = attributes.FirstOrDefault(); //only one attribute matched, lets just return it return true; // return true because we matched } } return base.TryGetMember(binder, out result); } private bool HandleIEnumerableXElement(IEnumerable elements, out object result) { //Get the count now, so we don't have to call it twice int count = elements.Count(); if (count > 0) { var firstElement = elements.FirstOrDefault(); //we have a single element, does it have any children? if (firstElement != null && firstElement.Elements().Count() == 0 && !firstElement.HasAttributes) { //no, return the text result = firstElement.Value; return true; } else { //We have more than one matching element, so let's return the collection //elements is IEnumerable //but we want to be able to re-enter this code XElement root = new XElement(XName.Get("root")); root.Add(elements); result = new DynamicXml(root); //From here, you'll either end up back here (because you have ) //or you use [] indexing and you end up with a single element return true; } } result = null; return false; } public IEnumerator GetEnumerator() { return this.BaseElement.Elements().Select(e => new DynamicXml(e)).GetEnumerator(); } public int Count() { return this.BaseElement.Elements().Count(); } public bool IsNull() { return false; } public bool HasValue() { return true; } public bool IsFirst() { return IsHelper(n => n.Index() == 0); } public string IsFirst(string valueIfTrue) { return IsHelper(n => n.Index() == 0, valueIfTrue); } public string IsFirst(string valueIfTrue, string valueIfFalse) { return IsHelper(n => n.Index() == 0, valueIfTrue, valueIfFalse); } public bool IsNotFirst() { return !IsHelper(n => n.Index() == 0); } public string IsNotFirst(string valueIfTrue) { return IsHelper(n => n.Index() != 0, valueIfTrue); } public string IsNotFirst(string valueIfTrue, string valueIfFalse) { return IsHelper(n => n.Index() != 0, valueIfTrue, valueIfFalse); } public bool IsPosition(int index) { if (this.BaseElement == null || this.BaseElement.Parent == null) { return false; } return IsHelper(n => n.Index() == index); } public string IsPosition(int index, string valueIfTrue) { if (this.BaseElement == null || this.BaseElement.Parent == null) { return string.Empty; } return IsHelper(n => n.Index() == index, valueIfTrue); } public string IsPosition(int index, string valueIfTrue, string valueIfFalse) { if (this.BaseElement == null || this.BaseElement.Parent == null) { return valueIfFalse; } return IsHelper(n => n.Index() == index, valueIfTrue, valueIfFalse); } public bool IsModZero(int modulus) { if (this.BaseElement == null || this.BaseElement.Parent == null) { return false; } return IsHelper(n => n.Index() % modulus == 0); } public string IsModZero(int modulus, string valueIfTrue) { if (this.BaseElement == null || this.BaseElement.Parent == null) { return string.Empty; } return IsHelper(n => n.Index() % modulus == 0, valueIfTrue); } public string IsModZero(int modulus, string valueIfTrue, string valueIfFalse) { if (this.BaseElement == null || this.BaseElement.Parent == null) { return valueIfFalse; } return IsHelper(n => n.Index() % modulus == 0, valueIfTrue, valueIfFalse); } public bool IsNotModZero(int modulus) { if (this.BaseElement == null || this.BaseElement.Parent == null) { return false; } return IsHelper(n => n.Index() % modulus != 0); } public string IsNotModZero(int modulus, string valueIfTrue) { if (this.BaseElement == null || this.BaseElement.Parent == null) { return string.Empty; } return IsHelper(n => n.Index() % modulus != 0, valueIfTrue); } public string IsNotModZero(int modulus, string valueIfTrue, string valueIfFalse) { if (this.BaseElement == null || this.BaseElement.Parent == null) { return valueIfFalse; } return IsHelper(n => n.Index() % modulus != 0, valueIfTrue, valueIfFalse); } public bool IsNotPosition(int index) { if (this.BaseElement == null || this.BaseElement.Parent == null) { return false; } return !IsHelper(n => n.Index() == index); } public string IsNotPosition(int index, string valueIfTrue) { if (this.BaseElement == null || this.BaseElement.Parent == null) { return string.Empty; } return IsHelper(n => n.Index() != index, valueIfTrue); } public string IsNotPosition(int index, string valueIfTrue, string valueIfFalse) { if (this.BaseElement == null || this.BaseElement.Parent == null) { return valueIfFalse; } return IsHelper(n => n.Index() != index, valueIfTrue, valueIfFalse); } public bool IsLast() { if (this.BaseElement == null || this.BaseElement.Parent == null) { return false; } int count = this.BaseElement.Parent.Elements().Count(); return IsHelper(n => n.Index() == count - 1); } public string IsLast(string valueIfTrue) { if (this.BaseElement == null || this.BaseElement.Parent == null) { return string.Empty; } int count = this.BaseElement.Parent.Elements().Count(); return IsHelper(n => n.Index() == count - 1, valueIfTrue); } public string IsLast(string valueIfTrue, string valueIfFalse) { if (this.BaseElement == null || this.BaseElement.Parent == null) { return valueIfFalse; } int count = this.BaseElement.Parent.Elements().Count(); return IsHelper(n => n.Index() == count - 1, valueIfTrue, valueIfFalse); } public bool IsNotLast() { if (this.BaseElement == null || this.BaseElement.Parent == null) { return false; } int count = this.BaseElement.Parent.Elements().Count(); return !IsHelper(n => n.Index() == count - 1); } public string IsNotLast(string valueIfTrue) { if (this.BaseElement == null || this.BaseElement.Parent == null) { return string.Empty; } int count = this.BaseElement.Parent.Elements().Count(); return IsHelper(n => n.Index() != count - 1, valueIfTrue); } public string IsNotLast(string valueIfTrue, string valueIfFalse) { if (this.BaseElement == null || this.BaseElement.Parent == null) { return valueIfFalse; } int count = this.BaseElement.Parent.Elements().Count(); return IsHelper(n => n.Index() != count - 1, valueIfTrue, valueIfFalse); } public bool IsEven() { return IsHelper(n => n.Index() % 2 == 0); } public string IsEven(string valueIfTrue) { return IsHelper(n => n.Index() % 2 == 0, valueIfTrue); } public string IsEven(string valueIfTrue, string valueIfFalse) { return IsHelper(n => n.Index() % 2 == 0, valueIfTrue, valueIfFalse); } public bool IsOdd() { return IsHelper(n => n.Index() % 2 == 1); } public string IsOdd(string valueIfTrue) { return IsHelper(n => n.Index() % 2 == 1, valueIfTrue); } public string IsOdd(string valueIfTrue, string valueIfFalse) { return IsHelper(n => n.Index() % 2 == 1, valueIfTrue, valueIfFalse); } public bool IsEqual(DynamicXml other) { return IsHelper(n => n.BaseElement == other.BaseElement); } public string IsEqual(DynamicXml other, string valueIfTrue) { return IsHelper(n => n.BaseElement == other.BaseElement, valueIfTrue); } public string IsEqual(DynamicXml other, string valueIfTrue, string valueIfFalse) { return IsHelper(n => n.BaseElement == other.BaseElement, valueIfTrue, valueIfFalse); } public bool IsDescendant(DynamicXml other) { var ancestors = this.Ancestors(); return IsHelper(n => ancestors.Find(ancestor => ancestor.BaseElement == other.BaseElement) != null); } public string IsDescendant(DynamicXml other, string valueIfTrue) { var ancestors = this.Ancestors(); return IsHelper(n => ancestors.Find(ancestor => ancestor.BaseElement == other.BaseElement) != null, valueIfTrue); } public string IsDescendant(DynamicXml other, string valueIfTrue, string valueIfFalse) { var ancestors = this.Ancestors(); return IsHelper(n => ancestors.Find(ancestor => ancestor.BaseElement == other.BaseElement) != null, valueIfTrue, valueIfFalse); } public bool IsDescendantOrSelf(DynamicXml other) { var ancestors = this.AncestorsOrSelf(); return IsHelper(n => ancestors.Find(ancestor => ancestor.BaseElement == other.BaseElement) != null); } public string IsDescendantOrSelf(DynamicXml other, string valueIfTrue) { var ancestors = this.AncestorsOrSelf(); return IsHelper(n => ancestors.Find(ancestor => ancestor.BaseElement == other.BaseElement) != null, valueIfTrue); } public string IsDescendantOrSelf(DynamicXml other, string valueIfTrue, string valueIfFalse) { var ancestors = this.AncestorsOrSelf(); return IsHelper(n => ancestors.Find(ancestor => ancestor.BaseElement == other.BaseElement) != null, valueIfTrue, valueIfFalse); } public bool IsAncestor(DynamicXml other) { var descendants = this.Descendants(); return IsHelper(n => descendants.Find(descendant => descendant.BaseElement == other.BaseElement) != null); } public string IsAncestor(DynamicXml other, string valueIfTrue) { var descendants = this.Descendants(); return IsHelper(n => descendants.Find(descendant => descendant.BaseElement == other.BaseElement) != null, valueIfTrue); } public string IsAncestor(DynamicXml other, string valueIfTrue, string valueIfFalse) { var descendants = this.Descendants(); return IsHelper(n => descendants.Find(descendant => descendant.BaseElement == other.BaseElement) != null, valueIfTrue, valueIfFalse); } public bool IsAncestorOrSelf(DynamicXml other) { var descendants = this.DescendantsOrSelf(); return IsHelper(n => descendants.Find(descendant => descendant.BaseElement == other.BaseElement) != null); } public string IsAncestorOrSelf(DynamicXml other, string valueIfTrue) { var descendants = this.DescendantsOrSelf(); return IsHelper(n => descendants.Find(descendant => descendant.BaseElement == other.BaseElement) != null, valueIfTrue); } public string IsAncestorOrSelf(DynamicXml other, string valueIfTrue, string valueIfFalse) { var descendants = this.DescendantsOrSelf(); return IsHelper(n => descendants.Find(descendant => descendant.BaseElement == other.BaseElement) != null, valueIfTrue, valueIfFalse); } public List Descendants() { return Descendants(n => true); } public List Descendants(Func func) { var flattenedNodes = this.BaseElement.Elements().Map(func, (XElement n) => { return n.Elements(); }); return flattenedNodes.ToList().ConvertAll(n => new DynamicXml(n)); } public List DescendantsOrSelf() { return DescendantsOrSelf(n => true); } public List DescendantsOrSelf(Func func) { var flattenedNodes = this.BaseElement.Elements().Map(func, (XElement n) => { return n.Elements(); }); var list = new List(); list.Add(this); list.AddRange(flattenedNodes.ToList().ConvertAll(n => new DynamicXml(n))); return list; } public List Ancestors() { return Ancestors(item => true); } public List Ancestors(Func func) { List ancestorList = new List(); var node = this.BaseElement; while (node != null) { if (node.Parent == null) break; XElement parent = node.Parent; if (parent != null) { if (this.BaseElement != parent) { node = parent; if (func(node)) { ancestorList.Add(node); } } else { break; } } else { break; } } ancestorList.Reverse(); return ancestorList.ConvertAll(item => new DynamicXml(item)); } public List AncestorsOrSelf() { return AncestorsOrSelf(item => true); } public List AncestorsOrSelf(Func func) { List ancestorList = new List(); var node = this.BaseElement; ancestorList.Add(node); while (node != null) { if (node.Parent == null) break; XElement parent = node.Parent; if (parent != null) { if (this.BaseElement != parent) { node = parent; if (func(node)) { ancestorList.Add(node); } } else { break; } } else { break; } } ancestorList.Reverse(); return ancestorList.ConvertAll(item => new DynamicXml(item)); } public int Index() { if (this.BaseElement != null && this.BaseElement.Parent != null) { var elements = this.BaseElement.Parent.Elements(); int index = 0; foreach (var element in elements) { if (element == this.BaseElement) break; index++; } return index; } return 0; } public bool IsHelper(Func test) { return test(this); } public string IsHelper(Func test, string valueIfTrue) { return IsHelper(test, valueIfTrue, string.Empty); } public string IsHelper(Func test, string valueIfTrue, string valueIfFalse) { return test(this) ? valueIfTrue : valueIfFalse; } } }