/src/Framework/OpenXmlCompositeElement.cs

https://github.com/Acidburn0zzz/Open-XML-SDK · C# · 1145 lines · 744 code · 166 blank · 235 comment · 175 complexity · f4f001a208e663b57efaa98d4c133576 MD5 · raw file

  1. // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
  2. using System;
  3. using System.Collections;
  4. using System.Collections.Generic;
  5. using System.Text;
  6. using System.Xml;
  7. using System.IO;
  8. using System.Diagnostics;
  9. using System.Globalization;
  10. using System.Linq;
  11. namespace DocumentFormat.OpenXml
  12. {
  13. /// <summary>
  14. /// Specifies the type of each child element's occurence.
  15. /// Used in GetElement() and SetElement() for generated code.
  16. /// </summary>
  17. internal enum OpenXmlCompositeType
  18. {
  19. Other,
  20. /// <summary>
  21. /// xsd:sequence, and maxOccurs=1.
  22. /// </summary>
  23. OneSequence,
  24. /// <summary>
  25. /// xsd:choice, and maxOccurs=1.
  26. /// </summary>
  27. OneChoice,
  28. /// <summary>
  29. /// xsd:all.
  30. /// </summary>
  31. OneAll
  32. }
  33. /// <summary>
  34. /// Represents the base class for composite elements.
  35. /// </summary>
  36. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")]
  37. public abstract class OpenXmlCompositeElement : OpenXmlElement
  38. {
  39. private OpenXmlElement _lastChild;
  40. /// <summary>
  41. /// Initializes a new instance of the OpenXmlCompositeElement class.
  42. /// </summary>
  43. protected OpenXmlCompositeElement()
  44. : base()
  45. {
  46. }
  47. /// <summary>
  48. /// Initializes a new instance of the OpenXmlCompositeElement class using the supplied outer XML.
  49. /// </summary>
  50. /// <param name="outerXml">The outer XML of the element.</param>
  51. protected OpenXmlCompositeElement(string outerXml)
  52. : base(outerXml)
  53. {
  54. }
  55. /// <summary>
  56. /// Initializes a new instance of the OpenXmlCompositeElement class using the supplied collection of elements.
  57. /// </summary>
  58. /// <param name="childrenElements">A collection of elements.</param>
  59. protected OpenXmlCompositeElement(IEnumerable childrenElements)
  60. : this( )
  61. {
  62. if (childrenElements == null)
  63. {
  64. throw new ArgumentNullException("childrenElements");
  65. }
  66. foreach (OpenXmlElement child in childrenElements)
  67. {
  68. this.AppendChild(child);
  69. }
  70. }
  71. /// <summary>
  72. /// Initializes a new instance of the OpenXmlCompositeElement class using the supplied collection of OpenXmlElement elements.
  73. /// </summary>
  74. /// <param name="childrenElements">A collection of OpenXmlElement elements.</param>
  75. protected OpenXmlCompositeElement(IEnumerable<OpenXmlElement> childrenElements)
  76. : this()
  77. {
  78. if (childrenElements == null)
  79. {
  80. throw new ArgumentNullException("childrenElements");
  81. }
  82. foreach (OpenXmlElement child in childrenElements)
  83. {
  84. this.AppendChild(child);
  85. }
  86. }
  87. /// <summary>
  88. /// Initializes a new instance of the OpenXmlCompositeElement using the supplied array of OpenXmlElement elements.
  89. /// </summary>
  90. /// <param name="childrenElements">An array of OpenXmlElement elements.</param>
  91. protected OpenXmlCompositeElement(params OpenXmlElement[] childrenElements)
  92. : this()
  93. {
  94. if (childrenElements == null)
  95. {
  96. throw new ArgumentNullException("childrenElements");
  97. }
  98. foreach (OpenXmlElement child in childrenElements)
  99. {
  100. this.AppendChild(child);
  101. }
  102. }
  103. #region public properties
  104. /// <summary>
  105. /// Gets the first child of the current OpenXmlElement element.
  106. /// </summary>
  107. /// <remarks>
  108. /// Returns null (Nothing in Visual Basic) if there is no such OpenXmlElement element.
  109. /// </remarks>
  110. public override OpenXmlElement FirstChild
  111. {
  112. get
  113. {
  114. this.MakeSureParsed();
  115. OpenXmlElement lastChild = this._lastChild;
  116. if (lastChild != null)
  117. {
  118. return lastChild.next;
  119. }
  120. return null;
  121. }
  122. }
  123. /// <summary>
  124. /// Gets the last child of the current OpenXmlElement element.
  125. /// Returns null (Nothing in Visual Basic) if there is no such OpenXmlElement element.
  126. /// </summary>
  127. public override OpenXmlElement LastChild
  128. {
  129. get
  130. {
  131. this.MakeSureParsed();
  132. return this._lastChild;
  133. }
  134. }
  135. /// <summary>
  136. /// Gets a value that indicates whether the current element has any child elements.
  137. /// </summary>
  138. public override bool HasChildren
  139. {
  140. get
  141. {
  142. return this.LastChild != null;
  143. }
  144. }
  145. /// <summary>
  146. /// Gets or sets the concatenated values of the current node and all of its children.
  147. /// </summary>
  148. public override string InnerText
  149. {
  150. get
  151. {
  152. StringBuilder innerText = new StringBuilder();
  153. foreach (OpenXmlElement child in this.ChildElements)
  154. {
  155. innerText.Append(child.InnerText);
  156. }
  157. return innerText.ToString();
  158. }
  159. //set
  160. //{
  161. // throw new InvalidOperationException();
  162. //}
  163. }
  164. /// <summary>
  165. /// Gets or sets the markup that represents only the child nodes of the current node.
  166. /// </summary>
  167. public override string InnerXml
  168. {
  169. set
  170. {
  171. // first, clear all children
  172. this.RemoveAllChildren();
  173. if ( ! String.IsNullOrEmpty(value))
  174. {
  175. // create an outer XML by wrapping the InnerXml with this element.
  176. // because XmlReader can not be created on InnerXml ( InnerXml may have several root elements ).
  177. StringWriter w = new StringWriter(CultureInfo.InvariantCulture);
  178. XmlTextWriter writer2 = new XmlDOMTextWriter(w);
  179. try
  180. {
  181. writer2.WriteStartElement(this.Prefix, this.LocalName, this.NamespaceUri);
  182. writer2.WriteRaw(value);
  183. writer2.WriteEndElement();
  184. }
  185. finally
  186. {
  187. writer2.Close();
  188. }
  189. // create a temp element to parse the xml.
  190. OpenXmlElement newElement = this.CloneNode(false);
  191. newElement.OuterXml = w.ToString();
  192. OpenXmlElement child = newElement.FirstChild;
  193. OpenXmlElement next = null;
  194. // then move all children to this element.
  195. while (child != null)
  196. {
  197. next = child.NextSibling();
  198. child = newElement.RemoveChild(child);
  199. this.AppendChild(child);
  200. child = next;
  201. }
  202. }
  203. }
  204. }
  205. #endregion
  206. #region change children
  207. /// <summary>
  208. /// Appends the specified element to the end of the current element's list of child nodes.
  209. /// </summary>
  210. /// <param name="newChild">The OpenXmlElement element to append.</param>
  211. /// <returns>The OpenXmlElement element that was appended. </returns>
  212. /// <remarks>Returns null if newChild equals null.</remarks>
  213. public override T AppendChild<T>(T newChild)
  214. {
  215. if (newChild == null)
  216. {
  217. // throw new ArgumentNullException("newChild");
  218. return null;
  219. }
  220. if (newChild.Parent != null)
  221. {
  222. throw new InvalidOperationException(ExceptionMessages.ElementIsPartOfTree);
  223. }
  224. this.ElementInsertingEvent(newChild);
  225. OpenXmlElement prevNode = this.LastChild;
  226. OpenXmlElement nextNode = newChild;
  227. if (prevNode == null)
  228. {
  229. nextNode.next = nextNode;
  230. this._lastChild = nextNode;
  231. }
  232. else
  233. {
  234. nextNode.next = prevNode.next;
  235. prevNode.next = nextNode;
  236. this._lastChild = nextNode;
  237. }
  238. newChild.Parent = this;
  239. // SetOwner(newChild);
  240. this.ElementInsertedEvent(newChild);
  241. return newChild;
  242. }
  243. /// <summary>
  244. /// Inserts the specified element immediately after the specified reference element.
  245. /// </summary>
  246. /// <param name="newChild">The OpenXmlElement element to insert.</param>
  247. /// <param name="refChild">The OpenXmlElement element that is in the reference node.</param>
  248. /// <returns>The OpenXmlElement element that was inserted.</returns>
  249. /// <remarks>Returns null if newChild is null. </remarks>
  250. public override T InsertAfter<T>(T newChild, OpenXmlElement refChild)
  251. {
  252. if (newChild == null)
  253. {
  254. // throw new ArgumentNullException("newChild");
  255. return null;
  256. }
  257. if (newChild.Parent != null)
  258. {
  259. throw new InvalidOperationException(ExceptionMessages.ElementIsPartOfTree);
  260. }
  261. if (refChild == null)
  262. {
  263. return this.PrependChild(newChild);
  264. }
  265. if (refChild.Parent != this)
  266. {
  267. throw new InvalidOperationException();
  268. }
  269. this.ElementInsertingEvent(newChild);
  270. OpenXmlElement nextNode = newChild;
  271. OpenXmlElement prevNode = refChild;
  272. Debug.Assert(nextNode != null);
  273. Debug.Assert(prevNode != null);
  274. if (prevNode == this._lastChild)
  275. {
  276. nextNode.next = prevNode.next;
  277. prevNode.next = nextNode;
  278. this._lastChild = nextNode;
  279. }
  280. else
  281. {
  282. OpenXmlElement next = prevNode.next;
  283. nextNode.next = next;
  284. prevNode.next = nextNode;
  285. }
  286. newChild.Parent = this;
  287. // SetOwner(newChild);
  288. this.ElementInsertedEvent(newChild);
  289. return newChild;
  290. }
  291. /// <summary>
  292. /// Inserts the specified element immediately before the specified reference element.
  293. /// </summary>
  294. /// <param name="newChild">The OpenXmlElement to insert.</param>
  295. /// <param name="refChild">The OpenXmlElement that is in the reference node.</param>
  296. /// <returns>The OpenXmlElement that was inserted.</returns>
  297. /// <remarks>Returns null if newChild equals null.</remarks>
  298. public override T InsertBefore<T>(T newChild, OpenXmlElement refChild)
  299. {
  300. if (newChild == null)
  301. {
  302. // throw new ArgumentNullException("newChild");
  303. return null;
  304. }
  305. if (newChild.Parent != null)
  306. {
  307. throw new InvalidOperationException(ExceptionMessages.ElementIsPartOfTree);
  308. }
  309. if (refChild == null)
  310. {
  311. return this.AppendChild(newChild);
  312. }
  313. if (refChild != null && refChild.Parent != this)
  314. {
  315. throw new InvalidOperationException();
  316. }
  317. this.ElementInsertingEvent(newChild);
  318. OpenXmlElement prevNode = newChild;
  319. OpenXmlElement nextNode = refChild;
  320. Debug.Assert(nextNode != null);
  321. Debug.Assert(prevNode != null);
  322. if (nextNode == this.FirstChild)
  323. {
  324. prevNode.next = nextNode;
  325. this._lastChild.next = prevNode;
  326. }
  327. else
  328. {
  329. OpenXmlElement previousSibling = nextNode.PreviousSibling();
  330. prevNode.next = nextNode;
  331. previousSibling.next = prevNode;
  332. }
  333. newChild.Parent = this;
  334. // SetOwner(newChild);
  335. this.ElementInsertedEvent(newChild);
  336. return newChild;
  337. }
  338. /// <summary>
  339. /// Inserts the specified element at the specified index of the current element's children.
  340. /// </summary>
  341. /// <param name="newChild">The OpenXmlElement element to insert.</param>
  342. /// <param name="index">The zero-based index to insert the element to.</param>
  343. /// <returns>The OpenXmlElement element that was inserted.</returns>
  344. /// <remarks>Returns null if newChild equals null.</remarks>
  345. /// <exception cref="ArgumentOutOfRangeException">Thrown when index is less than 0 or is greater than the count of children.</exception>
  346. public override T InsertAt<T>(T newChild, int index)
  347. {
  348. if (newChild == null)
  349. {
  350. // throw new ArgumentNullException("newChild");
  351. return null;
  352. }
  353. if (newChild.Parent != null)
  354. {
  355. throw new InvalidOperationException(ExceptionMessages.ElementIsPartOfTree);
  356. }
  357. if (index < 0 || index > this.ChildElements.Count)
  358. {
  359. throw new ArgumentOutOfRangeException("index");
  360. }
  361. else if ( index == 0 )
  362. {
  363. return this.PrependChild(newChild);
  364. }
  365. else if ( index == this.ChildElements.Count )
  366. {
  367. return this.AppendChild(newChild);
  368. }
  369. else
  370. {
  371. OpenXmlElement refChild = this.ChildElements[index];
  372. return this.InsertBefore(newChild, refChild);
  373. }
  374. }
  375. /// <summary>
  376. /// Inserts the specified element at the beginning of the current element's list of child nodes.
  377. /// </summary>
  378. /// <param name="newChild">The OpenXmlElement element to add.</param>
  379. /// <returns>The OpenXmlElement that was added.</returns>
  380. /// <remarks>Returns null if newChild equals null.</remarks>
  381. public override T PrependChild<T>(T newChild)
  382. {
  383. if (newChild == null)
  384. {
  385. //throw new ArgumentNullException("newChild");
  386. return null;
  387. }
  388. if (newChild.Parent != null)
  389. {
  390. throw new InvalidOperationException(ExceptionMessages.ElementIsPartOfTree);
  391. }
  392. return this.InsertBefore(newChild, this.FirstChild);
  393. }
  394. /// <summary>
  395. /// Removes the specified child element.
  396. /// </summary>
  397. /// <param name="oldChild">The element to remove. </param>
  398. /// <returns>The element that was removed. </returns>
  399. /// <remarks>Returns null if newChild equals null. </remarks>
  400. public override T RemoveChild<T>(T oldChild)
  401. {
  402. if (oldChild == null)
  403. {
  404. // throw new ArgumentNullException("oldChild");
  405. return null;
  406. }
  407. if (oldChild.Parent != this)
  408. {
  409. throw new InvalidOperationException(ExceptionMessages.ElementIsNotChild);
  410. }
  411. T removedElement = oldChild;
  412. OpenXmlElement last = this._lastChild;
  413. this.ElementRemovingEvent(removedElement);
  414. if (removedElement == this.FirstChild)
  415. {
  416. if (removedElement == this._lastChild)
  417. {
  418. this._lastChild = null;
  419. }
  420. else
  421. {
  422. OpenXmlElement nextNode = removedElement.next;
  423. last.next = nextNode;
  424. }
  425. }
  426. else if (removedElement == this._lastChild)
  427. {
  428. OpenXmlElement prevNode = removedElement.PreviousSibling();
  429. OpenXmlElement next = removedElement.next;
  430. prevNode.next = next;
  431. this._lastChild = prevNode;
  432. }
  433. else
  434. {
  435. OpenXmlElement prevNode = removedElement.PreviousSibling();
  436. OpenXmlElement next = removedElement.next;
  437. prevNode.next = next;
  438. }
  439. //foreach (OpenXmlElement descendant in removedElement.Descendants())
  440. //{
  441. // descendant.Owner = null;
  442. //}
  443. removedElement.next = null;
  444. removedElement.Parent = null;
  445. //removedElement.Owner = null;
  446. this.ElementRemovedEvent(removedElement);
  447. return removedElement;
  448. }
  449. /// <summary>
  450. /// Removes all of the current element's child elements.
  451. /// </summary>
  452. public override void RemoveAllChildren()
  453. {
  454. OpenXmlElement element = this.FirstChild;
  455. while (element != null)
  456. {
  457. OpenXmlElement next = element.NextSibling();
  458. this.RemoveChild(element);
  459. element = next;
  460. }
  461. Debug.Assert(this._lastChild == null);
  462. }
  463. /// <summary>
  464. /// Replaces one of the current element's child elements with another OpenXmlElement element.
  465. /// </summary>
  466. /// <param name="newChild">The new OpenXmlElement to put in the child list.</param>
  467. /// <param name="oldChild">The OpenXmlElement to be replaced in the child list.</param>
  468. /// <returns>The OpenXmlElement that was replaced.</returns>
  469. /// <remarks>Returns null if newChild equals null.</remarks>
  470. public override T ReplaceChild<T>(OpenXmlElement newChild, T oldChild)
  471. {
  472. if (oldChild == null)
  473. {
  474. //throw new ArgumentNullException("oldChild");
  475. return null;
  476. }
  477. if (newChild == null)
  478. {
  479. throw new ArgumentNullException("newChild");
  480. }
  481. if (oldChild.Parent != this)
  482. {
  483. throw new InvalidOperationException(ExceptionMessages.ElementIsNotChild);
  484. }
  485. if (newChild.Parent != null)
  486. {
  487. throw new InvalidOperationException(ExceptionMessages.ElementIsPartOfTree);
  488. }
  489. OpenXmlElement refChild = oldChild.NextSibling();
  490. this.RemoveChild(oldChild);
  491. this.InsertBefore(newChild, refChild);
  492. return oldChild;
  493. }
  494. #endregion
  495. /// <summary>
  496. /// Saves all of the current node's children to the specified XmlWriter.
  497. /// </summary>
  498. /// <param name="w">The XmlWriter at which to save the child nodes. </param>
  499. internal override void WriteContentTo(XmlWriter w)
  500. {
  501. if (this.HasChildren)
  502. {
  503. foreach (OpenXmlElement childElement in this.ChildElements)
  504. {
  505. childElement.WriteTo(w);
  506. }
  507. }
  508. }
  509. #region Event mechanism
  510. /// <summary>
  511. /// Fires the ElementInserting event.
  512. /// </summary>
  513. /// <param name="element">The OpenXmlElement element to insert.</param>
  514. internal void ElementInsertingEvent(OpenXmlElement element)
  515. {
  516. if (this.OpenXmlElementContext != null)
  517. {
  518. this.OpenXmlElementContext.ElementInsertingEvent(element, this);
  519. }
  520. }
  521. /// <summary>
  522. /// Fires the ElementInserted event.
  523. /// </summary>
  524. /// <param name="element">The OpenXmlElement element to insert.</param>
  525. internal void ElementInsertedEvent(OpenXmlElement element)
  526. {
  527. if (this.OpenXmlElementContext != null)
  528. {
  529. this.OpenXmlElementContext.ElementInsertedEvent(element, this);
  530. }
  531. }
  532. /// <summary>
  533. /// Fires the ElementRemoving event.
  534. /// </summary>
  535. /// <param name="element">The OpenXmlElement element to remove.</param>
  536. internal void ElementRemovingEvent(OpenXmlElement element)
  537. {
  538. if (this.OpenXmlElementContext != null)
  539. {
  540. this.OpenXmlElementContext.ElementRemovingEvent(element, this);
  541. }
  542. }
  543. /// <summary>
  544. /// Fires the ElementRemoved event.
  545. /// </summary>
  546. /// <param name="element">The OpenXmlElement element to be removed.</param>
  547. internal void ElementRemovedEvent(OpenXmlElement element)
  548. {
  549. if (this.OpenXmlElementContext != null)
  550. {
  551. this.OpenXmlElementContext.ElementRemovedEvent(element, this);
  552. }
  553. }
  554. #endregion
  555. #region internal methods
  556. /// <summary>
  557. /// Populates the XML into a strong typed DOM tree.
  558. /// </summary>
  559. /// <param name="xmlReader">The XmlReader to read the XML content.</param>
  560. /// <param name="loadMode">Specifies a load mode that is either lazy or full.</param>
  561. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")]
  562. internal override void Populate(XmlReader xmlReader, OpenXmlLoadMode loadMode)
  563. {
  564. LoadAttributes(xmlReader);
  565. if (!xmlReader.IsEmptyElement)
  566. {
  567. xmlReader.Read(); // read this element
  568. while (!xmlReader.EOF)
  569. {
  570. // O15:#3024890, OpenXmlCompositElement ignores the Whitespace NodeType.
  571. if (xmlReader.NodeType == XmlNodeType.Whitespace)
  572. {
  573. xmlReader.Skip();
  574. continue;
  575. }
  576. else if (xmlReader.NodeType == XmlNodeType.EndElement)
  577. {
  578. Debug.Assert(xmlReader.LocalName.Equals(this.LocalName));
  579. xmlReader.Skip(); // move to next node
  580. break;
  581. }
  582. OpenXmlElement element = this.ElementFactory(xmlReader);
  583. // set parent before Load( ) call. AlternateContentChoice need parent info on loading.
  584. element.Parent = this;
  585. bool isACB = element is AlternateContent;
  586. if (isACB && element.OpenXmlElementContext != null)
  587. {
  588. element.OpenXmlElementContext.ACBlockLevel++;
  589. }
  590. bool mcContextPushed = false;
  591. if (!(element is OpenXmlMiscNode))
  592. {
  593. // push MC context based on the context of the child element to be loaded
  594. mcContextPushed = this.PushMcContext(xmlReader);
  595. }
  596. //Process the element according to the MC behavior
  597. var action = ElementAction.Normal;
  598. if (OpenXmlElementContext != null && OpenXmlElementContext.MCSettings.ProcessMode != DocumentFormat.OpenXml.Packaging.MarkupCompatibilityProcessMode.NoProcess)
  599. {
  600. action = OpenXmlElementContext.MCContext.GetElementAction(element, OpenXmlElementContext.MCSettings.TargetFileFormatVersions);
  601. }
  602. element.Load(xmlReader, loadMode);
  603. if (mcContextPushed)
  604. {
  605. this.PopMcContext();
  606. }
  607. if (isACB && element.OpenXmlElementContext != null)
  608. {
  609. element.OpenXmlElementContext.ACBlockLevel--;
  610. }
  611. switch (action)
  612. {
  613. case ElementAction.Normal:
  614. {
  615. AddANode(element);
  616. break;
  617. }
  618. case ElementAction.Ignore:
  619. {
  620. element.Parent = null;
  621. continue;
  622. }
  623. case ElementAction.ProcessContent:
  624. {
  625. element.Parent = null;
  626. while(element.ChildElements.Count > 0)
  627. {
  628. var node = element.FirstChild;
  629. node.Remove();
  630. OpenXmlElement newnode = null;
  631. //if node is an UnknowElement, we should try to see whether the parent element can load the node as strong typed element
  632. if (node is OpenXmlUnknownElement)
  633. {
  634. newnode = this.ElementFactory(node.Prefix, node.LocalName, node.NamespaceUri);
  635. if (!(newnode is OpenXmlUnknownElement))
  636. {
  637. //the following method will load teh element in MCMode.Full
  638. //since the node is already MC-processed when loading as unknown type, full loading the outerXml is fine
  639. newnode.OuterXml = node.OuterXml;
  640. //unnecessary xmlns attr will be added, remove it.
  641. RemoveUnnecessaryExtAttr(node, newnode);
  642. }
  643. else
  644. {
  645. newnode = null;
  646. }
  647. }
  648. if (newnode != null)
  649. {
  650. AddANode(newnode);
  651. }
  652. else
  653. {
  654. //append the orignal node
  655. AddANode(node);
  656. }
  657. }
  658. break;
  659. }
  660. case ElementAction.ACBlock:
  661. {
  662. var effectiveNode = OpenXmlElementContext.MCContext.GetContentFromACBlock(element as AlternateContent, OpenXmlElementContext.MCSettings.TargetFileFormatVersions);
  663. if (effectiveNode == null)
  664. {
  665. break;
  666. }
  667. element.Parent = null;
  668. effectiveNode.Parent = null;
  669. while(effectiveNode.FirstChild != null)
  670. {
  671. var node = effectiveNode.FirstChild;
  672. node.Remove();
  673. AddANode(node);
  674. node.CheckMustUnderstandAttr();
  675. }
  676. break;
  677. }
  678. }
  679. }
  680. }
  681. else
  682. {
  683. xmlReader.Skip();
  684. }
  685. // set raw outer xml to empty to indicate that it is pased
  686. this.RawOuterXml = string.Empty;
  687. }
  688. private static void RemoveUnnecessaryExtAttr(OpenXmlElement node, OpenXmlElement newnode)
  689. {
  690. //re-construct the _nsMappings for newnode based on the orignal node
  691. node.MakeSureParsed();
  692. if (newnode.NamespaceDeclField != null && node.NamespaceDeclField != null)
  693. {
  694. newnode.NamespaceDeclField = new List<KeyValuePair<string, string>>(node.NamespaceDeclField);
  695. }
  696. }
  697. /// <summary>
  698. /// Gets the tag names of the child elements.
  699. /// </summary>
  700. /// <remarks>
  701. /// This property is overriden in generated classes.
  702. /// </remarks>
  703. internal virtual string[] ElementTagNames
  704. {
  705. get { return null; }
  706. }
  707. /// <summary>
  708. /// Gets the namespace IDs of the child elements.
  709. /// </summary>
  710. /// <remarks>
  711. /// This property is overriden in generated classes.
  712. /// </remarks>
  713. internal virtual byte[] ElementNamespaceIds
  714. {
  715. get { return null; }
  716. }
  717. private int GetSequenceNumber( OpenXmlElement child )
  718. {
  719. for (int i = 0; i < this.ElementNamespaceIds.Length; i++)
  720. {
  721. if (this.ElementNamespaceIds[i] == child.NamespaceId && Object.Equals(this.ElementTagNames[i], child.LocalName))
  722. {
  723. return i;
  724. }
  725. }
  726. return -1;
  727. }
  728. /// <summary>
  729. /// Gets the child element that has the specified sequence number.
  730. /// Sequence numbers are generated by a code generator.
  731. /// </summary>
  732. /// <param name="sequenceNumber">The sequence number of the element.</param>
  733. /// <returns>An element that has the specified sequence number. Returns null (Nothing in Visual Basic) if no element is found having the specified sequence number.</returns>
  734. /// <remarks>
  735. /// Returns null if there are invalid children.
  736. /// </remarks>
  737. internal T GetElement<T>(int sequenceNumber) where T : OpenXmlElement
  738. {
  739. T theChild;
  740. switch (this.OpenXmlCompositeType)
  741. {
  742. case OpenXmlCompositeType.Other:
  743. throw new InvalidOperationException();
  744. case OpenXmlCompositeType.OneAll:
  745. foreach (OpenXmlElement child in this.ChildElements)
  746. {
  747. // skip unknown element and MiscNode
  748. if (OpenXmlElement.IsKnownElement(child))
  749. {
  750. int childSequenceNumber = this.GetSequenceNumber(child);
  751. if (childSequenceNumber == sequenceNumber)
  752. {
  753. theChild = child as T;
  754. if (theChild != null)
  755. {
  756. return theChild;
  757. }
  758. }
  759. }
  760. }
  761. // TODO: should we handle error case?
  762. // 1: there are more than 1 elements for a type?
  763. // 2: there are more than 2 elements?
  764. // 3. there are other elements other than allowed children?
  765. break;
  766. case OpenXmlCompositeType.OneChoice:
  767. {
  768. OpenXmlElement child = this.FirstChild;
  769. // skip unknown element and MiscNode
  770. while (child != null && !OpenXmlElement.IsKnownElement(child))
  771. {
  772. child = child.NextSibling();
  773. }
  774. // There should be only one valide child.
  775. if (child != null)
  776. {
  777. Debug.Assert(OpenXmlElement.IsKnownElement(child));
  778. int childSequenceNumber = this.GetSequenceNumber(child);
  779. if (childSequenceNumber == sequenceNumber)
  780. {
  781. theChild = child as T;
  782. if (theChild != null)
  783. {
  784. return theChild;
  785. }
  786. }
  787. }
  788. }
  789. // TODO: should we handle error case?
  790. // 1: there are more than 1 elements for a type?
  791. // 2: there are more than 2 elements?
  792. // 3. there are other elements other than allowed children?
  793. break;
  794. case OpenXmlCompositeType.OneSequence:
  795. {
  796. OpenXmlElement child = this.FirstChild;
  797. while (child != null)
  798. {
  799. if (OpenXmlElement.IsKnownElement(child))
  800. {
  801. int childSequenceNumber = this.GetSequenceNumber(child);
  802. if (childSequenceNumber == sequenceNumber)
  803. {
  804. theChild = child as T;
  805. if (theChild != null)
  806. {
  807. return theChild;
  808. }
  809. else
  810. {
  811. // same tag name, but wrong type, see bug 448241
  812. child = child.NextSibling();
  813. }
  814. }
  815. else if (childSequenceNumber > sequenceNumber)
  816. {
  817. return null;
  818. }
  819. else
  820. {
  821. child = child.NextSibling();
  822. }
  823. }
  824. else
  825. {
  826. // skip unknown element and MiscNode
  827. child = child.NextSibling();
  828. }
  829. }
  830. }
  831. // TODO: should we handle error case?
  832. // 1: there are more than 1 elements for a type?
  833. // 2: there are more than 2 elements?
  834. // 3. there are other elements other than allowed children?
  835. break;
  836. }
  837. return null;
  838. }
  839. internal void SetElement<T>(int sequenceNumber, T newChild) where T : OpenXmlElement
  840. {
  841. switch (this.OpenXmlCompositeType)
  842. {
  843. case OpenXmlCompositeType.Other:
  844. throw new InvalidOperationException();
  845. case OpenXmlCompositeType.OneAll:
  846. {
  847. T child = this.GetElement<T>(sequenceNumber);
  848. if (child != null)
  849. {
  850. // remove the old one
  851. this.RemoveChild(child);
  852. }
  853. if (newChild != null)
  854. {
  855. this.AppendChild(newChild);
  856. }
  857. }
  858. // TODO: should we handle error case?
  859. // 1: there are more than 1 elements for a type?
  860. // 2: there are more than 2 elements?
  861. // 3. there are other elements other than allowed children?
  862. break;
  863. case OpenXmlCompositeType.OneChoice:
  864. {
  865. OpenXmlElement child = this.FirstChild;
  866. OpenXmlElement previousChild = null;
  867. // skip unknown element and MiscNode
  868. while (child != null && !OpenXmlElement.IsKnownElement(child))
  869. {
  870. previousChild = child;
  871. child = child.NextSibling();
  872. }
  873. OpenXmlElement next = null;
  874. while (child != null)
  875. {
  876. next = child.NextSibling();
  877. // remove all exist elements
  878. if (OpenXmlElement.IsKnownElement(child))
  879. {
  880. this.RemoveChild(child);
  881. }
  882. child = next;
  883. }
  884. if (newChild != null)
  885. {
  886. this.InsertAfter(newChild, previousChild);
  887. }
  888. }
  889. // TODO: should we handle error case?
  890. // 1: there are more than 1 elements for a type?
  891. // 2: there are more than 2 elements?
  892. // 3. there are other elements other than allowed children?
  893. break;
  894. case OpenXmlCompositeType.OneSequence:
  895. {
  896. OpenXmlElement child = this.FirstChild;
  897. OpenXmlElement prev = null;
  898. while (child != null)
  899. {
  900. if (OpenXmlElement.IsKnownElement(child))
  901. {
  902. int childSequenceNumber = this.GetSequenceNumber(child);
  903. if (childSequenceNumber == sequenceNumber)
  904. {
  905. // remove the old one
  906. if (child is T)
  907. {
  908. // insert the new element after the previous.
  909. prev = child.PreviousSibling();
  910. this.RemoveChild(child);
  911. break;
  912. }
  913. else
  914. {
  915. // same tag name, but wrong type, see bug 448241
  916. prev = child;
  917. }
  918. }
  919. else if (childSequenceNumber > sequenceNumber)
  920. {
  921. break;
  922. }
  923. else
  924. {
  925. // always insert after the first known element
  926. prev = child;
  927. // continue search
  928. }
  929. }
  930. // continue search
  931. child = child.NextSibling();
  932. }
  933. if (newChild != null)
  934. {
  935. this.InsertAfter(newChild, prev);
  936. }
  937. }
  938. // TODO: should we handle error case?
  939. // 1: there are more than 1 elements for a type?
  940. // 2: there are more than 2 elements?
  941. // 3. there are other elements other than allowed children?
  942. break;
  943. }
  944. }
  945. internal virtual OpenXmlCompositeType OpenXmlCompositeType
  946. {
  947. get { return OpenXmlCompositeType.Other; }
  948. }
  949. #endregion
  950. #region private methods
  951. private void AddANode(OpenXmlElement node)
  952. {
  953. node.Parent = this;
  954. if (this._lastChild == null)
  955. {
  956. node.next = node;
  957. this._lastChild = node;
  958. }
  959. else
  960. {
  961. node.next = this._lastChild.next;
  962. this._lastChild.next = node;
  963. this._lastChild = node;
  964. }
  965. }
  966. //private void SetOwner(OpenXmlElement element)
  967. //{
  968. // element.Owner = this.Owner;
  969. // if (element.XmlParsed)
  970. // {
  971. // foreach (OpenXmlElement child in element.ChildElements)
  972. // {
  973. // SetOwner(child);
  974. // }
  975. // }
  976. //}
  977. #endregion
  978. }
  979. }