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() + ":" + 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() + ":" + 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 ~ "":"" ~ 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 }