1 module hunt.xml.Attribute;
2 
3 import hunt.xml.Common;
4 import hunt.xml.Document;
5 import hunt.xml.Element;
6 import hunt.xml.Node;
7 
8 import std.format;
9 import std.string;
10 
11 /** 
12  * 
13  */
14 class Attribute : Node
15 {
16     protected Attribute m_prev_attribute;
17     protected Attribute m_next_attribute;
18     protected string    m_xmlns;
19     protected string    m_local_name;
20     protected string m_value;
21 
22     
23     this() {
24         super(NodeType.Attribute);
25     }
26 
27     this(string name, string value) {
28         super(name, NodeType.Attribute);
29         m_value = value;
30     }
31 
32     Document document() 
33     {
34         if (Element node = m_parent)
35         {
36             while (node.m_parent)
37                 node = node.m_parent;
38             return node.m_type == NodeType.Document ? cast(Document)(node) : null;
39         }
40         else
41             return null;
42     }
43 
44     string xmlns() 
45     {
46         if (m_xmlns) return m_xmlns;
47         char[] p;
48         char[] name = cast(char[])m_name.dup;
49         for (p = name; p.length > 0 && p[0] != ':'; p=p[1..$])
50         {    
51             if ((m_name.length - p.length) >= m_name.length) 
52                 break;
53         }
54         if (p.length == 0 || ((m_name.length - p.length) >= m_name.length)) {
55             m_xmlns = "nullstring";
56             return m_xmlns;
57         }
58         Element  element = m_parent;
59         if (element) 
60         {
61             char []xmlns = cast(char[])m_xmlns;
62             element.lookupXmlns(xmlns, name[0 .. m_name.length - p.length]);
63             m_xmlns = cast(string)xmlns.dup;
64         }
65         return m_xmlns;
66     }
67 
68     /// Gets previous attribute, optionally matching attribute name.
69     /// \param name Name of attribute to find, or 0 to return previous attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero
70     /// \param name_size Size of name, in characters, or 0 to have size calculated automatically from string
71     /// \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters
72     /// \return Pointer to found attribute, or 0 if not found.
73 
74     Attribute previousAttribute(string name = null, bool caseSensitive = true)
75     {
76         if (name.length > 0) {
77             if(caseSensitive) {
78                 for (Attribute attribute = m_prev_attribute; attribute; attribute = attribute.m_prev_attribute) {
79                     if (attribute.getName() == name) return attribute;
80                 }
81             } else {
82                 for (Attribute attribute = m_prev_attribute; attribute; attribute = attribute.m_prev_attribute) {
83                     if (icmp(attribute.getName(), name) == 0) return attribute;
84                 }
85             }
86             return null;
87         } else {
88             return m_parent is null ? null : m_prev_attribute;
89         }
90     }
91 
92     void previousAttribute(Attribute attribute) {
93         m_prev_attribute = attribute;
94     }
95 
96     /** 
97      * Gets next attribute, optionally matching attribute name.
98      * 
99      * Params:
100      *   name = Name of attribute to find, or 0 to return next attribute regardless of its name; 
101      * this string doesn't have to be zero-terminated if name_size is non-zero
102      *   caseSensitive = Should name comparison be case-sensitive; non case-sensitive comparison 
103      * works properly only for ASCII characters
104      * 
105      * Returns: 
106      *   Pointer to found attribute, or 0 if not found.
107      */
108     Attribute nextAttribute(string name = null , bool caseSensitive = true) {
109         if (name.length > 0)
110         {
111             if(caseSensitive) {
112                 for (Attribute attribute = m_next_attribute; attribute !is null; attribute = attribute.m_next_attribute) {
113                     if (attribute.getName() == name)
114                         return attribute;
115                 }
116             } else {
117                 for (Attribute attribute = m_next_attribute; attribute !is null; attribute = attribute.m_next_attribute) {
118                     if (icmp(attribute.getName(), name) == 0)
119                         return attribute;
120                 }
121             }
122             return null;
123         }
124         else {
125             return m_parent is null ? null : m_next_attribute;
126         }
127     }
128 
129     void nextAttribute(Attribute attribute) {
130         m_next_attribute = attribute;
131     }
132 
133 	/**
134 	 * Returns the value of the attribute. This method returns the same value as
135 	 * the {@link Node#getText()}method.
136 	 *
137 	 * @return the value of the attribute
138 	 */
139 	string getValue() {
140         return m_value;
141     }
142 
143 	/**
144 	 * Sets the value of this attribute or this method will throw an
145 	 * <code>UnsupportedOperationException</code> if it is read-only.
146 	 *
147 	 * @param value is the new value of this attribute
148 	 */
149 	void setValue(string value) {
150         m_value = value;
151     }
152 
153     string localName() {
154         if (m_local_name) return m_local_name;
155         string p = this.getName();
156         m_local_name = p;
157         
158         for (int i=0; i<p.length; ++i) {
159             if( p[i] == ':') {
160                 m_local_name = p[i+1 .. $];
161                 break;
162             }
163         }
164 
165         return m_local_name;
166     }
167     
168     void localName(string name) {
169         m_local_name = name;
170     }
171 
172     override string toString() {
173         return format("name: %s, value: %s", m_name, m_value);
174     }
175 }