1 module hunt.xml.Element;
2 
3 import hunt.logging;
4 
5 import hunt.xml.Attribute;
6 import hunt.xml.Common;
7 import hunt.xml.Document;
8 import hunt.xml.Node;
9 
10 import std.format;
11 import std.string;
12 
13 /** 
14  * 
15  */
16 class Element : Node {
17 
18     protected string m_prefix;
19     protected string m_value;
20     protected string m_xmlns;
21     protected Element m_first_node;
22     protected Element m_last_node;
23     protected Attribute m_first_attribute;
24     protected Attribute m_last_attribute;
25     protected Element m_prev_sibling;
26     protected Element m_next_sibling;
27     protected string m_contents;
28 
29     this(NodeType type = NodeType.Element) {
30         super(type);
31     }
32 
33     this(string name, NodeType type = NodeType.Element) {
34         super(name, type);
35     }
36 
37     string xmlns()
38     {
39         if(m_xmlns.length > 0)
40             return m_xmlns;
41         char[] xmlns;
42         lookupXmlns(xmlns , cast(char[])m_prefix);
43         m_xmlns = cast(string)xmlns.dup;
44         return m_xmlns;
45     }
46 
47     Document document() 
48     {
49         Element node = cast(Element)(this);
50         while (node.m_parent)
51             node = node.m_parent;
52         return node.m_type == NodeType.Document ? cast(Document)(node) : null;
53 
54     }
55 
56     package void lookupXmlns(ref char []xmlns,  char[]  prefix) 
57     {
58         char[] freeme;
59         char[] attrname;
60         int prefix_size = cast(int)prefix.length;
61         if (prefix) {
62             // Check if the prefix begins "xml".
63             if (prefix_size >= 3
64                 && prefix[0] == ('x')
65                 && prefix[1] == ('m')
66                 && prefix[2] == ('l')) {
67                 if (prefix_size == 3) {
68                     xmlns = cast(char[]) "http://www.w3.org/XML/1998/namespace";
69                     return;
70                 } else if (prefix_size == 5
71                             && prefix[3] == ('n')
72                             && prefix[4] == ('s')) {
73                     xmlns = cast(char[]) "http://www.w3.org/2000/xmlns/";
74                     return;
75                 }
76             }
77 
78             attrname.length = prefix_size + 6;
79             freeme = attrname;
80             string p1= "xmlns";
81             for(int i = 0 ;i < p1.length ; i++)
82                 attrname[i] = p1[i];
83 
84             char [] p = prefix;
85             attrname[p1.length] = ':';
86             int index = cast(int)p1.length + 1;
87             while (p.length > 0) {
88                 attrname[index++] = p[0];
89                 p = p[1 .. $];
90                 if ((freeme.length - attrname[index .. $].length ) >= (prefix_size + 6)) break;
91             }
92             attrname = freeme;
93         } else {
94             attrname.length = 5;
95             freeme = attrname ;
96             string p1="xmlns";
97             for(int i = 0 ;i < p1.length ; i++)
98                 attrname[i] = p1[i];
99             attrname = freeme;
100         }
101 
102         for ( Element node = this;
103                 node;
104                 node = node.m_parent) {
105             Attribute attr = node.firstAttribute(cast(string)attrname);
106             if (attr !is null ) {
107                 xmlns = cast(char[])attr.getValue().dup;
108                 break;
109             }
110         }
111         if (xmlns.length == 0) {
112             if (prefix.length == 0) {
113                 xmlns = cast(char[])"nullstring".dup;
114             }
115         }
116 
117     }
118 
119 	/**
120 	 * Returns the fully qualified name of this element. This will be the same
121 	 * as the value returned from {@link #getName}if this element has no
122 	 * namespace attached to this element or an expression of the form
123 	 * <pre>
124 	 * getNamespacePrefix() + &quot;:&quot; + getName()
125 	 * </pre>
126 	 * will be returned.
127 	 *
128 	 * @return the fully qualified name of the element.
129 	 */
130 	string namespacePrefix() {
131         return m_prefix;
132     }
133 
134     void namespacePrefix(string name) {
135         m_prefix = name;
136     }
137 
138     /**
139 	 * Returns the fully qualified name of this element. This will be the same
140 	 * as the value returned from {@link #getName}if this element has no
141 	 * namespace attached to this element or an expression of the form
142 	 * <pre>
143 	 * getNamespacePrefix() + &quot;:&quot; + getName()
144 	 * </pre>
145 	 * will be returned.
146 	 *
147 	 * @return the fully qualified name of the element.
148 	 */
149 	string getQualifiedName() {
150         // TODO: Tasks pending completion -@zhangxueping at 2019-11-26T17:44:39+08:00
151         // 
152         if(m_prefix.empty())
153             return getName();
154         return m_prefix ~ "&quot;:&quot;" ~ getName();
155     }
156 
157 
158     /**
159      * <p>
160      * Returns the text of this node.
161      * </p>
162      * 
163      * @return the text for this node.
164      */
165     string getText() {
166         return m_value;
167     }
168 
169     /**
170      * <p>
171      * Sets the text data of this node or this method will throw an
172      * <code>UnsupportedOperationException</code> if it is read-only.
173      * </p>
174      * 
175      * @param text
176      *            is the new textual value of this node
177      */
178     void setText(string text) {
179         m_value = text;
180     }
181 
182     string contents() {
183         return m_contents;
184     }
185 
186     void contents(string text) {
187         m_contents = text;
188     }
189 
190     Element firstNode(string name = null , string xmlns = null , bool caseSensitive = true)
191     {
192         if(xmlns.length == 0 && name.length > 0)
193         {
194             xmlns = this.xmlns();
195         }
196 
197         for(Element child = m_first_node ; child ; child = child.m_next_sibling)
198         {
199             if((!name || child.m_name == name) && (!xmlns || child.xmlns() == xmlns))
200             {                
201                 return child;
202             }
203         }
204 
205         return null;
206     }
207 
208     Element lastNode(string name = null , string xmlns = null , bool caseSensitive = true)
209     {
210         for(Element child = m_last_node ; child ; child = child.m_prev_sibling)
211         {
212             if((!name || child.m_name == name) && (!xmlns || child.xmlns() == xmlns))
213                 return child;
214         }
215 
216         return null;
217     }
218 
219     Element previousSibling(string name = null , string xmlns = null , bool caseSensitive = true)
220     {
221         assert(this.m_parent);     // Cannot query for siblings if node has no parent
222         if (name.length == 0)
223             return m_prev_sibling;
224 
225         if (xmlns.length == 0) {
226             // No XMLNS asked for, but a name is present.
227             // Assume "same XMLNS".
228             xmlns = this.xmlns();
229         }
230 
231         if(caseSensitive) {
232             for (Element sibling = m_prev_sibling; sibling !is null; sibling = sibling.m_prev_sibling) {
233                 if ((sibling.getName() == name)
234                     && (xmlns.length == 0 || (sibling.xmlns() == xmlns)))
235                     return sibling;
236             }
237         } else {
238             for (Element sibling = m_prev_sibling; sibling !is null; sibling = sibling.m_prev_sibling) {
239                 if ((icmp(sibling.getName(), name) == 0)
240                     && (xmlns.length == 0 || icmp(sibling.xmlns(), xmlns) == 0))
241                     return sibling;
242             }
243         }
244 
245         return null;
246     }
247     
248     Element nextSibling(string name = null , string xmlns = null , bool caseSensitive = true) {
249         if (name.length == 0)
250             return m_next_sibling;
251 
252         if (xmlns.length == 0) {
253             // No XMLNS asked for, but a name is present.
254             // Assume "same XMLNS".
255             xmlns = this.xmlns();
256         }
257 
258         if(caseSensitive) {
259             for (Element sibling = m_next_sibling; sibling !is null; sibling = sibling.m_next_sibling) {
260                 if ((sibling.getName() == name)
261                     && (xmlns.length == 0 || (sibling.xmlns() == xmlns)))
262                     return sibling;
263             }
264         } else {
265             for (Element sibling = m_next_sibling; sibling !is null; sibling = sibling.m_next_sibling) {
266                 if ((icmp(sibling.getName(), name) == 0)
267                     && (xmlns.length == 0 || icmp(sibling.xmlns(), xmlns) == 0))
268                     return sibling;
269             }
270         }
271         return null;
272     }
273 
274 
275     void prependNode(Element child)
276     {
277         if(firstNode())
278         {
279             child.m_next_sibling = m_first_node;
280             m_first_node.m_prev_sibling = child;
281         }
282         else
283         {
284             child.m_next_sibling = null;
285             m_last_node = child;
286         }
287 
288         m_first_node = child;
289         child.m_parent = this;
290         child.m_prev_sibling = null;
291     }
292 
293     void appendNode(Element child)
294     {
295         if(firstNode())
296         {
297             child.m_prev_sibling = m_last_node;
298             m_last_node.m_next_sibling = child;
299         }
300         else
301         {
302             child.m_prev_sibling = null;
303             m_first_node = child;
304         }
305 
306         m_last_node = child;
307         child.m_parent = this;
308         child.m_next_sibling = null;
309     }
310 
311     void insertNode(Element where , Element child)
312     {
313         if(where == m_first_node)
314             prependNode(child);
315         else if(where is null)
316             appendNode(child);
317         else
318         {
319             child.m_prev_sibling = where.m_prev_sibling;
320             child.m_next_sibling = where;
321             where.m_prev_sibling.m_next_sibling = child;
322             where.m_prev_sibling = child;
323             child.m_parent = this;
324         }
325     }
326 
327     void removeFirstNode()
328     {
329         Element child = m_first_node;
330         m_first_node = child.m_next_sibling;
331         if(child.m_next_sibling)
332             child.m_next_sibling.m_prev_sibling = null;
333         else
334             m_last_node = null;
335         child.m_parent = null;
336     }
337 
338     void removeLastNode()
339     {
340         Element child = m_last_node;
341         if(child.m_prev_sibling)
342         {
343             m_last_node = child.m_prev_sibling;
344             child.m_prev_sibling.m_next_sibling = null;
345         }
346         else
347         {
348             m_first_node = null;
349         }
350 
351         child.m_parent = null;
352     }
353 
354     void removeNode(Element where)
355     {
356         if(where == m_first_node)
357             removeFirstNode();
358         else if(where == m_last_node)
359             removeLastNode();
360         else
361         {
362             where.m_prev_sibling.m_next_sibling = where.m_next_sibling;
363             where.m_next_sibling.m_prev_sibling = where.m_prev_sibling;
364             where.m_parent = null;
365         }
366     }
367 
368     void removeAllNodes()
369     {
370         for( Element node = firstNode(); node; node = node.m_next_sibling)
371             node.m_parent = null;
372 
373         m_first_node = null;
374     }
375 
376     Attribute firstAttribute(string name = null , bool caseSensitive = true)
377     {
378         if(name)
379         {
380             for(Attribute attribute = m_first_attribute ; attribute ; attribute = attribute.nextAttribute())
381             {
382 
383                 if(attribute.m_name == name)
384                 {    
385                     return attribute;
386                 }
387             }
388 
389             return null;
390         }
391         else
392         {
393             return m_first_attribute;
394         }
395     }
396 
397     Attribute lastAttribute(string name = null , bool caseSensitive = true)
398     {
399         if(name)
400         {
401             for(Attribute attribute = m_last_attribute ; attribute ; attribute = attribute.previousAttribute())
402             {
403                 if(attribute.m_name == name)
404                     return attribute;
405             }
406 
407             return null;
408         }
409         else
410         {
411             return m_last_attribute;
412         }
413     }
414 
415     void prependAttribute(Attribute attribute)
416     {
417         if(firstAttribute())
418         {
419             attribute.nextAttribute = m_first_attribute;
420             m_first_attribute.previousAttribute = attribute;
421         }
422         else
423         {
424             attribute.nextAttribute = cast(Attribute)null;
425             m_last_attribute = attribute;
426         }
427         m_first_attribute = attribute;
428         attribute.m_parent = this;
429         attribute.previousAttribute = cast(Attribute)null;
430     }
431 
432     void appendAttribute(Attribute attribute)
433     {
434         if(firstAttribute())
435         {
436             attribute.previousAttribute = m_last_attribute;
437             m_last_attribute.nextAttribute = attribute;
438         }
439         else
440         {
441             attribute.previousAttribute = cast(Attribute)null;
442             m_first_attribute = attribute;
443         }
444 
445         m_last_attribute = attribute;
446         attribute.m_parent = this;
447         attribute.nextAttribute = cast(Attribute)null;
448     }
449 
450     void insertAttribute(Attribute where , Attribute attribute)
451     {
452         if(where == m_first_attribute)
453             prependAttribute(attribute);
454         else if(where is null)
455             appendAttribute(attribute);
456         else
457         {
458             attribute.previousAttribute = where.previousAttribute();
459             attribute.nextAttribute = where;
460             where.previousAttribute().nextAttribute = attribute;
461             where.previousAttribute = attribute;
462             attribute.m_parent = this;
463         }
464     }
465 
466     void removeFirstAttribute()
467     {
468         Attribute attribute = m_first_attribute;
469         if(attribute.nextAttribute())
470         {
471             attribute.nextAttribute().previousAttribute = cast(Attribute)null;
472         }
473         else
474         {
475             m_last_attribute = null;
476         }
477 
478         attribute.m_parent = null;
479         m_first_attribute = attribute.nextAttribute();
480     }
481 
482     void removeLastAttribute()
483     {
484         Attribute attribute = m_last_attribute;
485         if(attribute.previousAttribute())
486         {
487             attribute.previousAttribute().nextAttribute = cast(Attribute)null;
488             m_last_attribute = attribute.previousAttribute();
489         }
490         else
491             m_first_attribute = null;
492 
493         attribute.m_parent = null;
494     }
495 
496     void removeAttribute(Attribute where)
497     {
498         if(where == m_first_attribute)
499             removeFirstAttribute();
500         else if(where == m_last_attribute)
501             removeLastAttribute();
502         else
503         {
504             where.previousAttribute().nextAttribute = where.nextAttribute();
505             where.nextAttribute().previousAttribute = where.previousAttribute();
506             where.m_parent = null;
507         }
508     }
509 
510     void removeAllAttributes()
511     {
512         for(Attribute attribute = firstAttribute() ; attribute ; attribute = attribute.nextAttribute())
513         {
514             attribute.m_parent = null;
515         }
516         m_first_attribute = null;
517     }
518 
519     bool validate()
520     {
521         if(this.xmlns() == null)
522         {    
523             debug(HUNT_DEBUG) trace("Element XMLNS unbound");
524             return false;
525         }
526         for(Element child = firstNode(); child ; child = child.m_next_sibling)
527         {
528             if(!child.validate())
529                 return false;
530         }
531         for(Attribute attribute = firstAttribute() ; attribute ; attribute = attribute.nextAttribute())
532         {
533             if(attribute.xmlns() == null)
534             {    
535                 debug(HUNT_DEBUG) trace("Attribute XMLNS unbound");
536                 return false;
537             }
538             for(Attribute otherattr = firstAttribute() ; otherattr != attribute; otherattr = otherattr.nextAttribute())
539             {    
540                 if(attribute.m_name == otherattr.m_name)
541                 {    
542                     debug(HUNT_DEBUG) trace("Attribute doubled");
543                     return false;
544                 }
545                 if(attribute.xmlns() == otherattr.xmlns() && attribute.localName() == otherattr.localName())
546                 {
547                     debug(HUNT_DEBUG) trace("Attribute XMLNS doubled");
548                     return false;
549                 }
550             }
551 
552         }
553         return true;
554     }
555 
556     override string toString() {
557         return format("type: %s, name: %s, text: %s", m_type, m_name, m_value);
558     }    
559 }