5 // Copyright 2007 Novell (http://www.novell.com)
7 // Permission is hereby granted, free of charge, to any person obtaining
8 // a copy of this software and associated documentation files (the
9 // "Software"), to deal in the Software without restriction, including
10 // without limitation the rights to use, copy, modify, merge, publish,
11 // distribute, sublicense, and/or sell copies of the Software, and to
12 // permit persons to whom the Software is furnished to do so, subject to
13 // the following conditions:
15 // The above copyright notice and this permission notice shall be
16 // included in all copies or substantial portions of the Software.
18 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 using System.Collections;
29 using System.Collections.Generic;
33 using System.Xml.Schema;
34 using System.Xml.Serialization;
36 namespace System.Xml.Linq
38 [XmlSchemaProvider (null, IsAny = true)]
39 public class XElement : XContainer, IXmlSerializable
41 static IEnumerable <XElement> emptySequence =
42 new List <XElement> ();
44 public static IEnumerable <XElement> EmptySequence {
45 get { return emptySequence; }
49 XAttribute attr_first, attr_last;
50 bool explicit_is_empty = true;
52 public XElement (XName name, object value)
58 public XElement (XElement source)
61 Add (source.Attributes ());
62 Add (source.Nodes ());
65 public XElement (XName name)
70 public XElement (XName name, params object [] contents)
76 public XElement (XStreamingElement source)
78 this.name = source.Name;
79 Add (source.Contents);
82 public static explicit operator bool (XElement element)
85 throw new ArgumentNullException ("element");
86 return XmlConvert.ToBoolean (element.Value);
89 public static explicit operator bool? (XElement element)
94 return element.Value == null ? (bool?) null : XmlConvert.ToBoolean (element.Value);
97 public static explicit operator DateTime (XElement element)
100 throw new ArgumentNullException ("element");
101 return XmlConvert.ToDateTime (element.Value, XmlDateTimeSerializationMode.RoundtripKind);
104 public static explicit operator DateTime? (XElement element)
109 return element.Value == null ? (DateTime?) null : XmlConvert.ToDateTime (element.Value, XmlDateTimeSerializationMode.RoundtripKind);
112 public static explicit operator decimal (XElement element)
115 throw new ArgumentNullException ("element");
116 return XmlConvert.ToDecimal (element.Value);
119 public static explicit operator decimal? (XElement element)
124 return element.Value == null ? (decimal?) null : XmlConvert.ToDecimal (element.Value);
127 public static explicit operator double (XElement element)
130 throw new ArgumentNullException ("element");
131 return XmlConvert.ToDouble (element.Value);
134 public static explicit operator double? (XElement element)
139 return element.Value == null ? (double?) null : XmlConvert.ToDouble (element.Value);
142 public static explicit operator float (XElement element)
145 throw new ArgumentNullException ("element");
146 return XmlConvert.ToSingle (element.Value);
149 public static explicit operator float? (XElement element)
154 return element.Value == null ? (float?) null : XmlConvert.ToSingle (element.Value);
157 public static explicit operator Guid (XElement element)
160 throw new ArgumentNullException ("element");
161 return XmlConvert.ToGuid (element.Value);
164 public static explicit operator Guid? (XElement element)
169 return element.Value == null ? (Guid?) null : XmlConvert.ToGuid (element.Value);
172 public static explicit operator int (XElement element)
175 throw new ArgumentNullException ("element");
176 return XmlConvert.ToInt32 (element.Value);
179 public static explicit operator int? (XElement element)
184 return element.Value == null ? (int?) null : XmlConvert.ToInt32 (element.Value);
187 public static explicit operator long (XElement element)
190 throw new ArgumentNullException ("element");
191 return XmlConvert.ToInt64 (element.Value);
194 public static explicit operator long? (XElement element)
199 return element.Value == null ? (long?) null : XmlConvert.ToInt64 (element.Value);
202 [CLSCompliant (false)]
203 public static explicit operator uint (XElement element)
206 throw new ArgumentNullException ("element");
207 return XmlConvert.ToUInt32 (element.Value);
210 [CLSCompliant (false)]
211 public static explicit operator uint? (XElement element)
216 return element.Value == null ? (uint?) null : XmlConvert.ToUInt32 (element.Value);
219 [CLSCompliant (false)]
220 public static explicit operator ulong (XElement element)
223 throw new ArgumentNullException ("element");
224 return XmlConvert.ToUInt64 (element.Value);
227 [CLSCompliant (false)]
228 public static explicit operator ulong? (XElement element)
233 return element.Value == null ? (ulong?) null : XmlConvert.ToUInt64 (element.Value);
236 public static explicit operator TimeSpan (XElement element)
239 throw new ArgumentNullException ("element");
240 return XmlConvert.ToTimeSpan (element.Value);
243 public static explicit operator TimeSpan? (XElement element)
248 return element.Value == null ? (TimeSpan?) null : XmlConvert.ToTimeSpan (element.Value);
251 public static explicit operator string (XElement element)
256 return element.Value;
259 public XAttribute FirstAttribute {
260 get { return attr_first; }
261 internal set { attr_first = value; }
264 public XAttribute LastAttribute {
265 get { return attr_last; }
266 internal set { attr_last = value; }
269 public bool HasAttributes {
270 get { return attr_first != null; }
273 public bool HasElements {
275 foreach (object o in Nodes ())
282 public bool IsEmpty {
283 get { return !Nodes ().GetEnumerator ().MoveNext () && explicit_is_empty; }
284 internal set { explicit_is_empty = value; }
291 throw new ArgumentNullException ("value");
296 public override XmlNodeType NodeType {
297 get { return XmlNodeType.Element; }
300 public string Value {
302 StringBuilder sb = null;
303 foreach (XNode n in Nodes ()) {
305 sb = new StringBuilder ();
307 sb.Append (((XText) n).Value);
308 else if (n is XElement)
309 sb.Append (((XElement) n).Value);
311 return sb == null ? String.Empty : sb.ToString ();
319 IEnumerable <XElement> GetAncestorList (XName name, bool getMeIn)
321 List <XElement> list = new List <XElement> ();
324 for (XElement el = Parent as XElement; el != null; el = el.Parent as XElement)
325 if (name == null || el.Name == name)
330 public XAttribute Attribute (XName name)
332 foreach (XAttribute a in Attributes ())
338 public IEnumerable <XAttribute> Attributes ()
341 for (XAttribute a = attr_first; a != null; a = next) {
342 next = a.NextAttribute;
348 public IEnumerable <XAttribute> Attributes (XName name)
350 foreach (XAttribute a in Attributes ())
355 static void DefineDefaultSettings (XmlReaderSettings settings, LoadOptions options)
358 // 2.1 has a DtdProcessing property which defaults to DtdProcessing.Prohibit
359 settings.DtdProcessing = DtdProcessing.Parse;
361 settings.ProhibitDtd = false;
364 settings.IgnoreWhitespace = (options & LoadOptions.PreserveWhitespace) == 0;
367 static XmlReaderSettings CreateDefaultSettings (LoadOptions options)
369 var settings = new XmlReaderSettings ();
370 DefineDefaultSettings (settings, options);
374 public static XElement Load (string uri)
376 return Load (uri, LoadOptions.None);
379 public static XElement Load (string uri, LoadOptions options)
381 XmlReaderSettings s = CreateDefaultSettings (options);
383 using (XmlReader r = XmlReader.Create (uri, s)) {
384 return LoadCore (r, options);
388 public static XElement Load (TextReader tr)
390 return Load (tr, LoadOptions.None);
393 public static XElement Load (TextReader tr, LoadOptions options)
395 XmlReaderSettings s = CreateDefaultSettings (options);
397 using (XmlReader r = XmlReader.Create (tr, s)) {
398 return LoadCore (r, options);
402 public static XElement Load (XmlReader reader)
404 return Load (reader, LoadOptions.None);
407 public static XElement Load (XmlReader reader, LoadOptions options)
409 XmlReaderSettings s = reader.Settings != null ? reader.Settings.Clone () : new XmlReaderSettings ();
410 DefineDefaultSettings (s, options);
412 using (XmlReader r = XmlReader.Create (reader, s)) {
413 return LoadCore (r, options);
417 internal static XElement LoadCore (XmlReader r, LoadOptions options)
420 if (r.NodeType != XmlNodeType.Element)
421 throw new InvalidOperationException ("The XmlReader must be positioned at an element");
422 XName name = XName.Get (r.LocalName, r.NamespaceURI);
423 XElement e = new XElement (name);
424 e.FillLineInfoAndBaseUri (r, options);
426 if (r.MoveToFirstAttribute ()) {
428 // not sure how current Orcas behavior makes sense here though ...
429 if (r.LocalName == "xmlns" && r.NamespaceURI == XNamespace.Xmlns.NamespaceName)
430 e.SetAttributeValue (XNamespace.None.GetName ("xmlns"), r.Value);
432 e.SetAttributeValue (XName.Get (r.LocalName, r.NamespaceURI), r.Value);
433 e.LastAttribute.FillLineInfoAndBaseUri (r, options);
434 } while (r.MoveToNextAttribute ());
437 if (!r.IsEmptyElement) {
439 e.ReadContentFrom (r, options);
441 e.explicit_is_empty = false;
443 e.explicit_is_empty = true;
449 public static XElement Parse (string s)
451 return Parse (s, LoadOptions.None);
454 public static XElement Parse (string s, LoadOptions options)
456 return Load (new StringReader (s), options);
459 public void RemoveAll ()
465 public void RemoveAttributes ()
467 while (attr_first != null)
471 public void Save (string filename)
473 Save (filename, SaveOptions.None);
476 public void Save (string filename, SaveOptions options)
478 XmlWriterSettings s = new XmlWriterSettings ();
479 s.Indent = options != SaveOptions.DisableFormatting;
480 using (XmlWriter w = XmlWriter.Create (filename, s)) {
485 public void Save (TextWriter tw)
487 Save (tw, SaveOptions.None);
490 public void Save (TextWriter tw, SaveOptions options)
492 XmlWriterSettings s = new XmlWriterSettings ();
493 s.Indent = options != SaveOptions.DisableFormatting;
494 using (XmlWriter w = XmlWriter.Create (tw, s)) {
499 public void Save (XmlWriter w)
504 public IEnumerable <XElement> AncestorsAndSelf ()
506 return GetAncestorList (null, true);
509 public IEnumerable <XElement> AncestorsAndSelf (XName name)
511 return GetAncestorList (name, true);
514 public IEnumerable <XElement> DescendantsAndSelf ()
516 List <XElement> list = new List <XElement> ();
518 list.AddRange (Descendants ());
522 public IEnumerable <XElement> DescendantsAndSelf (XName name)
524 List <XElement> list = new List <XElement> ();
525 if (name == this.name)
527 list.AddRange (Descendants (name));
531 public IEnumerable <XNode> DescendantNodesAndSelf ()
534 foreach (XNode node in DescendantNodes ())
538 public void SetAttributeValue (XName name, object value)
540 XAttribute a = Attribute (name);
546 SetAttributeObject (new XAttribute (name, value));
549 a.Value = XUtil.ToString (value);
553 void SetAttributeObject (XAttribute a)
555 a = (XAttribute) XUtil.GetDetachedObject (a);
557 if (attr_first == null) {
561 attr_last.NextAttribute = a;
562 a.PreviousAttribute = attr_last;
567 public override void WriteTo (XmlWriter w)
569 // some people expect the same prefix output as in input,
570 // in the loss of performance... see bug #466423.
571 string prefix = name.NamespaceName.Length > 0 ? w.LookupPrefix (name.Namespace.NamespaceName) : String.Empty;
572 foreach (XAttribute a in Attributes ()) {
573 if (a.IsNamespaceDeclaration && a.Value == name.Namespace.NamespaceName) {
574 if (a.Name.Namespace == XNamespace.Xmlns)
575 prefix = a.Name.LocalName;
576 // otherwise xmlns="..."
581 w.WriteStartElement (prefix, name.LocalName, name.Namespace.NamespaceName);
583 foreach (XAttribute a in Attributes ()) {
584 if (a.IsNamespaceDeclaration) {
585 if (a.Name.Namespace == XNamespace.Xmlns)
586 w.WriteAttributeString ("xmlns", a.Name.LocalName, XNamespace.Xmlns.NamespaceName, a.Value);
588 w.WriteAttributeString ("xmlns", a.Value);
591 w.WriteAttributeString (a.Name.LocalName, a.Name.Namespace.NamespaceName, a.Value);
594 foreach (XNode node in Nodes ())
597 if (explicit_is_empty)
598 w.WriteEndElement ();
600 w.WriteFullEndElement ();
603 public XNamespace GetDefaultNamespace ()
605 for (XElement el = this; el != null; el = el.Parent)
606 foreach (XAttribute a in el.Attributes ())
607 if (a.IsNamespaceDeclaration && a.Name.Namespace == XNamespace.None)
608 return XNamespace.Get (a.Value);
609 return XNamespace.None; // nothing is declared.
612 public XNamespace GetNamespaceOfPrefix (string prefix)
614 for (XElement el = this; el != null; el = el.Parent)
615 foreach (XAttribute a in el.Attributes ())
616 if (a.IsNamespaceDeclaration && a.Name.LocalName == prefix)
617 return XNamespace.Get (a.Value);
618 return XNamespace.None; // nothing is declared.
621 public string GetPrefixOfNamespace (XNamespace ns)
623 foreach (string prefix in GetPrefixOfNamespaceCore (ns))
624 if (GetNamespaceOfPrefix (prefix) == ns)
626 return null; // nothing is declared
629 IEnumerable<string> GetPrefixOfNamespaceCore (XNamespace ns)
631 for (XElement el = this; el != null; el = el.Parent)
632 foreach (XAttribute a in el.Attributes ())
633 if (a.IsNamespaceDeclaration && a.Value == ns.NamespaceName)
634 yield return a.Name.Namespace == XNamespace.None ? String.Empty : a.Name.LocalName;
637 public void ReplaceAll (object item)
643 public void ReplaceAll (params object [] items)
649 public void ReplaceAttributes (object item)
655 public void ReplaceAttributes (params object [] items)
661 public void SetElementValue (XName name, object value)
663 XElement el = new XElement (name, value);
668 public void SetValue (object value)
671 throw new ArgumentNullException ("value");
672 if (value is XAttribute || value is XDocument || value is XDeclaration || value is XDocumentType)
673 throw new ArgumentException (String.Format ("Node type {0} is not allowed as element value", value.GetType ()));
675 foreach (object o in XUtil.ExpandArray (value))
679 internal override bool OnAddingObject (object o, bool rejectAttribute, XNode refNode, bool addFirst)
681 if (o is XDocument || o is XDocumentType || o is XDeclaration || (rejectAttribute && o is XAttribute))
682 throw new ArgumentException (String.Format ("A node of type {0} cannot be added as a content", o.GetType ()));
684 XAttribute a = o as XAttribute;
686 foreach (XAttribute ia in Attributes ())
687 if (a.Name == ia.Name)
688 throw new InvalidOperationException (String.Format ("Duplicate attribute: {0}", a.Name));
689 SetAttributeObject (a);
692 else if (o is string && refNode is XText) {
693 ((XText) refNode).Value += o as string;
700 void IXmlSerializable.WriteXml (XmlWriter writer)
705 void IXmlSerializable.ReadXml (XmlReader reader)
707 ReadContentFrom (reader, LoadOptions.None);
710 XmlSchema IXmlSerializable.GetSchema ()