mirror of https://github.com/buggins/dlangui.git
DOM
This commit is contained in:
parent
d090f8ad49
commit
e2a847f108
|
@ -4,7 +4,19 @@ import dlangui.core.collections;
|
|||
|
||||
import std.traits;
|
||||
import std.conv : to;
|
||||
import std.string;
|
||||
import std.string : startsWith, endsWith;
|
||||
import std.array : empty;
|
||||
import std.algorithm : equal;
|
||||
|
||||
// Namespace, element tag and attribute names are stored as numeric ids for better performance and lesser memory consumption.
|
||||
|
||||
/// id type for interning namespaces
|
||||
alias ns_id = ushort;
|
||||
/// id type for interning element names
|
||||
alias elem_id = uint;
|
||||
/// id type for interning attribute names
|
||||
alias attr_id = ushort;
|
||||
|
||||
|
||||
/// Base class for DOM nodes
|
||||
class Node {
|
||||
|
@ -17,6 +29,15 @@ public:
|
|||
/// returns document node
|
||||
@property Document document() { return _document; }
|
||||
|
||||
/// return element tag id
|
||||
@property elem_id id() { return 0; }
|
||||
/// return element namespace id
|
||||
@property ns_id nsid() { return 0; }
|
||||
/// return element tag name
|
||||
@property string name() { return document.tagName(id); }
|
||||
/// return element namespace name
|
||||
@property string nsname() { return document.nsName(nsid); }
|
||||
|
||||
// node properties
|
||||
|
||||
/// returns true if node is text
|
||||
|
@ -42,6 +63,18 @@ public:
|
|||
/// returns last child node
|
||||
@property Node lastChild() { return null; }
|
||||
|
||||
/// find child node, return its index if found, -1 if not found or not child of this node
|
||||
int childIndex(Node child) { return -1; }
|
||||
/// return node index in parent's child node collection, -1 if not found
|
||||
@property int index() { return _parent ? _parent.childIndex(this) : -1; }
|
||||
|
||||
/// append text child
|
||||
Node appendText(dstring s, int index = -1) { assert(false); }
|
||||
/// append element child - by namespace and tag names
|
||||
Node appendElement(string ns, string tag, int index = -1) { assert(false); }
|
||||
/// append element child - by namespace and tag ids
|
||||
Node appendElement(ns_id ns, elem_id tag, int index = -1) { assert(false); }
|
||||
|
||||
/// node text
|
||||
@property dstring text() { return null; }
|
||||
/// ditto
|
||||
|
@ -54,7 +87,10 @@ public:
|
|||
class Text : Node {
|
||||
private:
|
||||
dstring _text;
|
||||
|
||||
this(Document doc, dstring text = null) {
|
||||
_document = doc;
|
||||
_text = text;
|
||||
}
|
||||
public:
|
||||
/// node text
|
||||
override @property dstring text() { return _text; }
|
||||
|
@ -66,7 +102,21 @@ public:
|
|||
class Element : Node {
|
||||
private:
|
||||
Collection!Node _children;
|
||||
elem_id _id; // element tag id
|
||||
ns_id _ns; // element namespace id
|
||||
|
||||
this(Document doc, ns_id ns, elem_id id) {
|
||||
_document = doc;
|
||||
_ns = ns;
|
||||
_id = id;
|
||||
}
|
||||
public:
|
||||
|
||||
/// return element tag id
|
||||
override @property elem_id id() { return _id; }
|
||||
/// return element namespace id
|
||||
override @property ns_id nsid() { return _ns; }
|
||||
|
||||
// child nodes
|
||||
|
||||
/// returns child node count
|
||||
|
@ -77,15 +127,88 @@ public:
|
|||
override @property Node firstChild() { return _children.length > 0 ? _children[0] : null; }
|
||||
/// returns last child node
|
||||
override @property Node lastChild() { return _children.length > 0 ? _children[_children.length - 1] : null; }
|
||||
/// find child node, return its index if found, -1 if not found or not child of this node
|
||||
override int childIndex(Node child) {
|
||||
for (int i = 0; i < _children.length; i++)
|
||||
if (child is _children[i])
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// append text child
|
||||
override Node appendText(dstring s, int index = -1) {
|
||||
Node item = document.createText(s);
|
||||
_children.add(item, index >= 0 ? index : size_t.max);
|
||||
return item;
|
||||
}
|
||||
/// append element child - by namespace and tag names
|
||||
override Node appendElement(string ns, string tag, int index = -1) {
|
||||
Node item = document.createElement(ns, tag);
|
||||
_children.add(item, index >= 0 ? index : size_t.max);
|
||||
return item;
|
||||
}
|
||||
/// append element child - by namespace and tag ids
|
||||
override Node appendElement(ns_id ns, elem_id tag, int index = -1) {
|
||||
Node item = document.createElement(ns, tag);
|
||||
_children.add(item, index >= 0 ? index : size_t.max);
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
/// Document node
|
||||
class Document : Element {
|
||||
public:
|
||||
this() {
|
||||
super(null, 0, 0);
|
||||
_elemIds.init!Tag();
|
||||
_attrIds.init!Attr();
|
||||
_nsIds.init!Ns();
|
||||
_document = this;
|
||||
}
|
||||
/// create text node
|
||||
Text createText(dstring text) {
|
||||
return new Text(this, text);
|
||||
}
|
||||
/// create element node by namespace and tag ids
|
||||
Element createElement(ns_id ns, elem_id tag) {
|
||||
return new Element(this, ns, tag);
|
||||
}
|
||||
/// create element node by namespace and tag names
|
||||
Element createElement(string ns, string tag) {
|
||||
return new Element(this, internNs(ns), internTag(tag));
|
||||
}
|
||||
|
||||
// Ids
|
||||
|
||||
/// return name for element tag id
|
||||
string tagName(elem_id id) {
|
||||
return _elemIds[id];
|
||||
}
|
||||
/// return name for namespace id
|
||||
string nsName(ns_id id) {
|
||||
return _nsIds[id];
|
||||
}
|
||||
/// return name for attribute id
|
||||
string attrName(ns_id id) {
|
||||
return _attrIds[id];
|
||||
}
|
||||
/// get id for element tag name
|
||||
elem_id internTag(string s) {
|
||||
if (s.empty)
|
||||
return 0;
|
||||
return _elemIds.intern(s);
|
||||
}
|
||||
/// get id for namespace name
|
||||
ns_id internNs(string s) {
|
||||
if (s.empty)
|
||||
return 0;
|
||||
return _nsIds.intern(s);
|
||||
}
|
||||
/// get id for namespace name
|
||||
attr_id internAttr(string s) {
|
||||
if (s.empty)
|
||||
return 0;
|
||||
return _attrIds.intern(s);
|
||||
}
|
||||
private:
|
||||
IdentMap!(elem_id) _elemIds;
|
||||
|
@ -93,12 +216,7 @@ private:
|
|||
IdentMap!(ns_id) _nsIds;
|
||||
}
|
||||
|
||||
/// id type for interning namespaces
|
||||
alias ns_id = ushort;
|
||||
/// id type for interning element names
|
||||
alias elem_id = uint;
|
||||
/// id type for interning attribute names
|
||||
alias attr_id = ushort;
|
||||
|
||||
|
||||
/// remove trailing _ from string, e.g. "body_" -> "body"
|
||||
private string removeTrailingUnderscore(string s) {
|
||||
|
@ -112,11 +230,14 @@ struct IdentMap(ident_t) {
|
|||
/// initialize with elements of enum
|
||||
void init(E)() if (is(E == enum)) {
|
||||
foreach(member; EnumMembers!E) {
|
||||
internString(removeTrailingUnderscore(member.to!string), member);
|
||||
static if (member.to!int) {
|
||||
//pragma(msg, "interning string '" ~ removeTrailingUnderscore(member.to!string) ~ "' for " ~ E.stringof);
|
||||
intern(removeTrailingUnderscore(member.to!string), member);
|
||||
}
|
||||
}
|
||||
}
|
||||
/// intern string - return ID assigned for it
|
||||
ident_t internString(string s, ident_t id = 0) {
|
||||
ident_t intern(string s, ident_t id = 0) {
|
||||
if (auto p = s in _stringToId)
|
||||
return *p;
|
||||
ident_t res;
|
||||
|
@ -133,12 +254,16 @@ struct IdentMap(ident_t) {
|
|||
}
|
||||
/// lookup id for string, return 0 if string is not found
|
||||
ident_t opIndex(string s) {
|
||||
if (s.empty)
|
||||
return 0;
|
||||
if (auto p = s in _stringToId)
|
||||
return *p;
|
||||
return 0;
|
||||
}
|
||||
/// lookup name for id, return null if not found
|
||||
string opIndex(ident_t id) {
|
||||
if (!id)
|
||||
return null;
|
||||
if (auto p = id in _idToString)
|
||||
return *p;
|
||||
return null;
|
||||
|
@ -187,5 +312,16 @@ unittest {
|
|||
assert(map[Tag.div].equal("div"));
|
||||
|
||||
Document doc = new Document();
|
||||
auto body_ = doc.appendElement(null, "body");
|
||||
assert(body_.id == Tag.body_);
|
||||
assert(body_.name.equal("body"));
|
||||
auto div = body_.appendElement(null, "div");
|
||||
assert(body_.childCount == 1);
|
||||
assert(div.id == Tag.body_);
|
||||
assert(div.name.equal("div"));
|
||||
div.appendText("Some text"d);
|
||||
assert(div.childCount == 1);
|
||||
assert(div.child(0).text.equal("Some text"d));
|
||||
|
||||
destroy(doc);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue