[NUI] Rebase develnui (DevelNUI only patches --> master) (#3910)
[platform/core/csapi/tizenfx.git] / test / Tizen.NUI.Devel.Tests.Ubuntu / nunit.framework / Interfaces / TNode.cs
1 // ***********************************************************************
2 // Copyright (c) 2012-2015 Charlie Poole
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining
5 // a copy of this software and associated documentation files (the
6 // "Software"), to deal in the Software without restriction, including
7 // without limitation the rights to use, copy, modify, merge, publish,
8 // distribute, sublicense, and/or sell copies of the Software, and to
9 // permit persons to whom the Software is furnished to do so, subject to
10 // the following conditions:
11 // 
12 // The above copyright notice and this permission notice shall be
13 // included in all copies or substantial portions of the Software.
14 // 
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 // ***********************************************************************
23 #define PORTABLE
24 #define TIZEN
25 #define NUNIT_FRAMEWORK
26 #define NUNITLITE
27 #define NET_4_5
28 #define PARALLEL
29 using System;
30 using System.Text.RegularExpressions;
31 using System.Xml;
32
33 #if PORTABLE || SILVERLIGHT
34 using System.Xml.Linq;
35 #endif
36
37 namespace NUnit.Framework.Interfaces
38 {
39     /// <summary>
40     /// TNode represents a single node in the XML representation
41     /// of a Test or TestResult. It replaces System.Xml.XmlNode and
42     /// System.Xml.Linq.XElement, providing a minimal set of methods 
43     /// for operating on the XML in a platform-independent manner.
44     /// </summary>
45     public class TNode
46     {
47         #region Constructors
48
49         /// <summary>
50         /// Constructs a new instance of TNode
51         /// </summary>
52         /// <param name="name">The name of the node</param>
53         public TNode(string name)
54         {
55             Name = name;
56             Attributes = new AttributeDictionary();
57             ChildNodes = new NodeList();
58         }
59
60         /// <summary>
61         /// Constructs a new instance of TNode with a value
62         /// </summary>
63         /// <param name="name">The name of the node</param>
64         /// <param name="value">The text content of the node</param>
65         public TNode(string name, string value) : this(name, value, false) { }
66
67         /// <summary>
68         /// Constructs a new instance of TNode with a value
69         /// </summary>
70         /// <param name="name">The name of the node</param>
71         /// <param name="value">The text content of the node</param>
72         /// <param name="valueIsCDATA">Flag indicating whether to use CDATA when writing the text</param>
73         public TNode(string name, string value, bool valueIsCDATA)
74             : this(name)
75         {
76             Value = value;
77             ValueIsCDATA = valueIsCDATA;
78         }
79
80         #endregion
81
82         #region Properties
83
84         /// <summary>
85         /// Gets the name of the node
86         /// </summary>
87         public string Name { get; private set; }
88
89         /// <summary>
90         /// Gets the value of the node
91         /// </summary>
92         public string Value { get; set; }
93
94         /// <summary>
95         /// Gets a flag indicating whether the value should be output using CDATA.
96         /// </summary>
97         public bool ValueIsCDATA { get; private set; }
98
99         /// <summary>
100         /// Gets the dictionary of attributes
101         /// </summary>
102         public AttributeDictionary Attributes { get; private set; }
103
104         /// <summary>
105         /// Gets a list of child nodes
106         /// </summary>
107         public NodeList ChildNodes { get; private set; }
108
109         /// <summary>
110         /// Gets the first ChildNode
111         /// </summary>
112         public TNode FirstChild
113         {
114             get { return ChildNodes.Count == 0 ? null : ChildNodes[0]; }
115         }
116
117         /// <summary>
118         /// Gets the XML representation of this node.
119         /// </summary>
120         public string OuterXml
121         {
122             get
123             {
124                 var stringWriter = new System.IO.StringWriter();
125                 var settings = new XmlWriterSettings();
126                 settings.ConformanceLevel = ConformanceLevel.Fragment;
127                 
128                 using (XmlWriter xmlWriter = XmlWriter.Create(stringWriter, settings))
129                 {
130                     WriteTo(xmlWriter);
131                 }
132
133                 return stringWriter.ToString();
134             }
135         }
136
137         #endregion
138
139         #region Static Methods
140
141         /// <summary>
142         /// Create a TNode from it's XML text representation
143         /// </summary>
144         /// <param name="xmlText">The XML text to be parsed</param>
145         /// <returns>A TNode</returns>
146         public static TNode FromXml(string xmlText)
147         {
148 #if PORTABLE || SILVERLIGHT
149             return FromXml(XElement.Parse(xmlText));
150 #else
151             var doc = new XmlDocument();
152             doc.LoadXml(xmlText);
153             return FromXml(doc.FirstChild);
154 #endif
155         }
156
157         #endregion
158
159         #region Instance Methods
160
161         /// <summary>
162         /// Adds a new element as a child of the current node and returns it.
163         /// </summary>
164         /// <param name="name">The element name.</param>
165         /// <returns>The newly created child element</returns>
166         public TNode AddElement(string name)
167         {
168             TNode childResult = new TNode(name);
169             ChildNodes.Add(childResult);
170             return childResult;
171         }
172
173         /// <summary>
174         /// Adds a new element with a value as a child of the current node and returns it.
175         /// </summary>
176         /// <param name="name">The element name</param>
177         /// <param name="value">The text content of the new element</param>
178         /// <returns>The newly created child element</returns>
179         public TNode AddElement(string name, string value)
180         {
181             TNode childResult = new TNode(name, EscapeInvalidXmlCharacters(value));
182             ChildNodes.Add(childResult);
183             return childResult;
184         }
185
186         /// <summary>
187         /// Adds a new element with a value as a child of the current node and returns it.
188         /// The value will be output using a CDATA section.
189         /// </summary>
190         /// <param name="name">The element name</param>
191         /// <param name="value">The text content of the new element</param>
192         /// <returns>The newly created child element</returns>
193         public TNode AddElementWithCDATA(string name, string value)
194         {
195             TNode childResult = new TNode(name, EscapeInvalidXmlCharacters(value), true);
196             ChildNodes.Add(childResult);
197             return childResult;
198         }
199
200         /// <summary>
201         /// Adds an attribute with a specified name and value to the XmlNode.
202         /// </summary>
203         /// <param name="name">The name of the attribute.</param>
204         /// <param name="value">The value of the attribute.</param>
205         public void AddAttribute(string name, string value)
206         {
207             Attributes.Add(name, EscapeInvalidXmlCharacters(value));
208         }
209
210         /// <summary>
211         /// Finds a single descendant of this node matching an xpath
212         /// specification. The format of the specification is
213         /// limited to what is needed by NUnit and its tests.
214         /// </summary>
215         /// <param name="xpath"></param>
216         /// <returns></returns>
217         public TNode SelectSingleNode(string xpath)
218         {
219             NodeList nodes = SelectNodes(xpath);
220
221             return nodes.Count > 0
222                 ? nodes[0] as TNode
223                 : null;
224         }
225
226         /// <summary>
227         /// Finds all descendants of this node matching an xpath
228         /// specification. The format of the specification is
229         /// limited to what is needed by NUnit and its tests.
230         /// </summary>
231         public NodeList SelectNodes(string xpath)
232         {
233             NodeList nodeList = new NodeList();
234             nodeList.Add(this);
235
236             return ApplySelection(nodeList, xpath);
237         }
238         
239         /// <summary>
240         /// Writes the XML representation of the node to an XmlWriter
241         /// </summary>
242         /// <param name="writer"></param>
243         public void WriteTo(XmlWriter writer)
244         {
245             writer.WriteStartElement(Name);
246
247             foreach (string name in Attributes.Keys)
248                 writer.WriteAttributeString(name, Attributes[name]);
249
250             if (Value != null)
251                 if (ValueIsCDATA)
252                     WriteCDataTo(writer);
253                 else
254                     writer.WriteString(Value);
255
256             foreach (TNode node in ChildNodes)
257                 node.WriteTo(writer);
258
259             writer.WriteEndElement();
260         }
261
262         #endregion
263
264         #region Helper Methods
265
266 #if PORTABLE || SILVERLIGHT
267         private static TNode FromXml(XElement xElement)
268         {
269             TNode tNode = new TNode(xElement.Name.ToString(), xElement.Value);
270
271             foreach (var attr in xElement.Attributes())
272                 tNode.AddAttribute(attr.Name.ToString(), attr.Value);
273
274             foreach (var child in xElement.Elements())
275                 tNode.ChildNodes.Add(FromXml(child));
276
277             return tNode;
278         }
279 #else
280         private static TNode FromXml(XmlNode xmlNode)
281         {
282             TNode tNode = new TNode(xmlNode.Name, xmlNode.InnerText);
283
284             foreach (XmlAttribute attr in xmlNode.Attributes)
285                 tNode.AddAttribute(attr.Name, attr.Value);
286
287             foreach (XmlNode child in xmlNode.ChildNodes)
288                 if (child.NodeType == XmlNodeType.Element)
289                     tNode.ChildNodes.Add(FromXml(child));
290
291             return tNode;
292         }
293 #endif
294
295         private static NodeList ApplySelection(NodeList nodeList, string xpath)
296         {
297             Guard.ArgumentNotNullOrEmpty(xpath, "xpath");
298             if (xpath[0] == '/')
299                 throw new ArgumentException("XPath expressions starting with '/' are not supported", "xpath");
300             if (xpath.IndexOf("//") >= 0)
301                 throw new ArgumentException("XPath expressions with '//' are not supported", "xpath");
302
303             string head = xpath;
304             string tail = null;
305
306             int slash = xpath.IndexOf('/');
307             if (slash >= 0)
308             {
309                 head = xpath.Substring(0, slash);
310                 tail = xpath.Substring(slash + 1);
311             }
312
313             NodeList resultNodes = new NodeList();
314             NodeFilter filter = new NodeFilter(head);
315
316             foreach(TNode node in nodeList)
317                 foreach (TNode childNode in node.ChildNodes)
318                     if (filter.Pass(childNode))
319                         resultNodes.Add(childNode);
320
321             return tail != null
322                 ? ApplySelection(resultNodes, tail)
323                 : resultNodes;
324         }
325
326         private static readonly Regex InvalidXmlCharactersRegex = new Regex("[^\u0009\u000a\u000d\u0020-\ufffd]|([\ud800-\udbff](?![\udc00-\udfff]))|((?<![\ud800-\udbff])[\udc00-\udfff])");
327         private static string EscapeInvalidXmlCharacters(string str)
328         {
329             if (str == null) return null;
330
331             // Based on the XML spec http://www.w3.org/TR/xml/#charsets
332             // For detailed explanation of the regex see http://mnaoumov.wordpress.com/2014/06/15/escaping-invalid-xml-unicode-characters/
333
334             return InvalidXmlCharactersRegex.Replace(str, match => CharToUnicodeSequence(match.Value[0]));
335         }
336
337         private static string CharToUnicodeSequence(char symbol)
338         {
339             return string.Format("\\u{0}", ((int)symbol).ToString("x4"));
340         }
341
342         private void WriteCDataTo(XmlWriter writer)
343         {
344             int start = 0;
345             string text = Value;
346
347             while (true)
348             {
349                 int illegal = text.IndexOf("]]>", start);
350                 if (illegal < 0)
351                     break;
352                 writer.WriteCData(text.Substring(start, illegal - start + 2));
353                 start = illegal + 2;
354                 if (start >= text.Length)
355                     return;
356             }
357
358             if (start > 0)
359                 writer.WriteCData(text.Substring(start));
360             else
361                 writer.WriteCData(text);
362         }
363
364         #endregion
365
366         #region Nested NodeFilter class
367
368         class NodeFilter
369         {
370             private string _nodeName;
371             private string _propName;
372             private string _propValue;
373
374             public NodeFilter(string xpath)
375             {
376                 _nodeName = xpath;
377                 
378                 int lbrack = xpath.IndexOf('[');
379                 if (lbrack >= 0)
380                 {
381                     if (!xpath.EndsWith("]"))
382                         throw new ArgumentException("Invalid property expression", "xpath");
383
384                     _nodeName = xpath.Substring(0, lbrack);
385                     string filter = xpath.Substring(lbrack+1, xpath.Length - lbrack - 2);
386
387                     int equals = filter.IndexOf('=');
388                     if (equals < 0 || filter[0] != '@')
389                         throw new ArgumentException("Invalid property expression", "xpath");
390
391                     _propName = filter.Substring(1, equals - 1).Trim();
392                     _propValue = filter.Substring(equals + 1).Trim(new char[] { ' ', '"', '\'' });
393                 }
394             }
395
396             public bool Pass(TNode node)
397             {
398                 if (node.Name != _nodeName)
399                     return false;
400                 
401                 if (_propName == null)
402                     return true;
403                 
404                 return node.Attributes[_propName] == _propValue;
405             }
406         }
407
408         #endregion
409     }
410
411     /// <summary>
412     /// Class used to represent a list of XmlResults
413     /// </summary>
414     public class NodeList : System.Collections.Generic.List<TNode>
415     {
416     }
417
418     /// <summary>
419     /// Class used to represent the attributes of a node
420     /// </summary>
421     public class AttributeDictionary : System.Collections.Generic.Dictionary<string, string>
422     {
423         /// <summary>
424         /// Gets or sets the value associated with the specified key.
425         /// Overridden to return null if attribute is not found.
426         /// </summary>
427         /// <param name="key">The key.</param>
428         /// <returns>Value of the attribute or null</returns>
429         public new string this[string key]
430         {
431             get
432             {
433                 string value;
434
435                 if (TryGetValue(key, out value))
436                     return value;
437
438                 return null;
439             }
440         }
441     }
442 }