diff --git a/source/newxml/cursor.d b/source/newxml/cursor.d index 7612d66..7aa9ff5 100644 --- a/source/newxml/cursor.d +++ b/source/newxml/cursor.d @@ -9,6 +9,7 @@ + Authors: + Lodovico Giaretta + László Szerémi ++ Robert Schadek + + License: + Boost License 1.0. @@ -31,18 +32,23 @@ import std.range.primitives; import std.typecons; import std.string; -public class CursorException : XMLException { - @nogc @safe pure nothrow this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable nextInChain = null) +@safe: + +public class CursorException : XMLException +{ + @nogc @safe pure nothrow this(string msg, string file = __FILE__, + size_t line = __LINE__, Throwable nextInChain = null) { super(msg, file, line, nextInChain); } - @nogc @safe pure nothrow this(string msg, Throwable nextInChain, string file = __FILE__, size_t line = __LINE__) + @nogc @safe pure nothrow this(string msg, Throwable nextInChain, + string file = __FILE__, size_t line = __LINE__) { super(msg, file, line, nextInChain); } } -@safe: + package struct Attribute(StringType) { StringType value; @@ -52,33 +58,34 @@ package struct Attribute(StringType) this(StringType qualifiedName, StringType value) { this.value = value; - name = qualifiedName; + this.name = qualifiedName; } @property auto name() inout { - return _name; + return this._name; } + @property void name(StringType _name) { this._name = _name; - auto i = _name.indexOf(':'); - colon = i > 0 - ? i - : 0; + auto i = this._name.indexOf(':'); + this.colon = i > 0 ? i : 0; } + @property auto prefix() inout { - return name[0..colon]; + return this.name[0 .. this.colon]; } + @property StringType localName() { - return colon - ? name[colon+1..$] - : name; + return this.colon != 0 ? this.name[this.colon + 1 .. $] : this.name; } - StringType toString() { - return name ~ " = \"" ~ value ~ "\""; + + StringType toString() + { + return this.name ~ " = \"" ~ this.value ~ "\""; } } @@ -95,12 +102,13 @@ package struct Attribute(StringType) + process the document. Otherwise it'll throw an appropriate exception if an error is encountered. +/ struct Cursor(P, Flag!"conflateCDATA" conflateCDATA = Yes.conflateCDATA, - Flag!"processBadDocument" processBadDocument = No.processBadDocument) - if (isLowLevelParser!P) + Flag!"processBadDocument" processBadDocument = No.processBadDocument) + if (isLowLevelParser!P) { - class AttributeException : XMLException { - @nogc @safe pure nothrow this(string msg, string file = __FILE__, size_t line = __LINE__, - Throwable nextInChain = null) + class AttributeException : XMLException + { + @nogc @safe pure nothrow this(string msg, string file = __FILE__, + size_t line = __LINE__, Throwable nextInChain = null) { super(msg, file, line, nextInChain); } @@ -115,21 +123,21 @@ struct Cursor(P, Flag!"conflateCDATA" conflateCDATA = Yes.conflateCDATA, private this(StringType str, ref Cursor cur) @system nothrow { - content = str; - cursor = &cur; + this.content = str; + this.cursor = &cur; } bool empty() @safe { - if (error) + if (this.error) { return true; } - auto i = content.indexOfNeither(" \r\n\t"); + auto i = this.content.indexOfNeither(" \r\n\t"); if (i >= 0) { - content = content[i..$]; + this.content = this.content[i .. $]; return false; } return true; @@ -137,13 +145,13 @@ struct Cursor(P, Flag!"conflateCDATA" conflateCDATA = Yes.conflateCDATA, auto front() @safe { - if (attr == attr.init) + if (this.attr == attr.init) { auto i = content.indexOfNeither(" \r\n\t"); enforce!AttributeException(i >= 0, "No more attributes..."); - content = content[i..$]; + this.content = this.content[i .. $]; - auto sep = indexOf(content[0..$], '='); + auto sep = indexOf(this.content[0 .. $], '='); if (sep == -1) { // attribute without value??? @@ -153,18 +161,17 @@ struct Cursor(P, Flag!"conflateCDATA" conflateCDATA = Yes.conflateCDATA, } else { - error = true; + this.error = true; return attr.init; } } - auto name = content[0..sep]; - + auto name = this.content[0 .. sep]; auto delta = indexOfAny(name, " \r\n\t"); if (delta >= 0) { - auto j = name[delta..$].indexOfNeither(" \r\n\t"); + auto j = name[delta .. $].indexOfNeither(" \r\n\t"); if (j != -1) { // attribute name contains spaces??? @@ -174,11 +181,11 @@ struct Cursor(P, Flag!"conflateCDATA" conflateCDATA = Yes.conflateCDATA, } else { - error = true; + this.error = true; return attr.init; } } - name = name[0..delta]; + name = name[0 .. delta]; } if (!isValidXMLName(name)) { @@ -188,31 +195,35 @@ struct Cursor(P, Flag!"conflateCDATA" conflateCDATA = Yes.conflateCDATA, } else { - error = true; + this.error = true; } } attr.name = name; size_t attEnd; size_t quote; - delta = (sep + 1 < content.length) ? indexOfNeither(content[sep + 1..$], " \r\n\t") : -1; + + delta = (sep + 1 < content.length) ? indexOfNeither( + this.content[sep + 1 .. $], " \r\n\t") : -1; + if (delta >= 0) { quote = sep + 1 + delta; if (content[quote] == '"' || content[quote] == '\'') { - delta = indexOf(content[(quote + 1)..$], content[quote]); + delta = indexOf(content[(quote + 1) .. $], content[quote]); if (delta == -1) { // attribute quotes never closed??? - static if (processBadDocument == No.processBadDocument) + static if (processBadDocument == No + .processBadDocument) { throw new CursorException("Invalid attribute syntax!"); } else { - error = true; - return attr.init; + this.error = true; + return this.attr.init; } } attEnd = quote + 1 + delta; @@ -225,8 +236,8 @@ struct Cursor(P, Flag!"conflateCDATA" conflateCDATA = Yes.conflateCDATA, } else { - error = true; - return attr.init; + this.error = true; + return this.attr.init; } } } @@ -239,28 +250,31 @@ struct Cursor(P, Flag!"conflateCDATA" conflateCDATA = Yes.conflateCDATA, } else { - error = true; - return attr.init; + this.error = true; + return this.attr.init; } } //attr.value = content[(quote + 1)..attEnd]; static if (processBadDocument == No.processBadDocument) { - attr.value = xmlUnescape(content[(quote + 1)..attEnd], cursor.chrEntities); + this.attr.value = xmlUnescape(content[(quote + 1) .. attEnd], cursor + .chrEntities); } else { - attr.value = xmlUnescape!No.strict(content[(quote + 1)..attEnd], cursor.chrEntities); + this.attr.value = xmlUnescape!No.strict( + content[(quote + 1) .. attEnd], cursor.chrEntities); } - content = content[attEnd+1..$]; + + this.content = this.content[attEnd + 1 .. $]; } - return attr; + return this.attr; } - auto popFront() @safe + void popFront() @safe { - front(); - attr = attr.init; + this.front(); + this.attr = this.attr.init; } } /++ The type of characters in the input, as returned by the underlying low level parser. +/ @@ -288,8 +302,8 @@ struct Cursor(P, Flag!"conflateCDATA" conflateCDATA = Yes.conflateCDATA, /++ Generic constructor; forwards its arguments to the parser constructor +/ this(Args...)(Args args) { - parser = P(args); - chrEntities = xmlPredefinedEntities!CharacterType(); + this.parser = P(args); + this.chrEntities = xmlPredefinedEntities!CharacterType(); } static if (isSaveableLowLevelParser!P) @@ -297,14 +311,14 @@ struct Cursor(P, Flag!"conflateCDATA" conflateCDATA = Yes.conflateCDATA, public auto save() { auto result = this; - result.parser = parser.save; + result.parser = this.parser.save; return result; } } ///Returns true if XML declaration was not found. public @property bool xmlDeclNotFound() @nogc @safe pure nothrow { - return _xmlDeclNotFound; + return this._xmlDeclNotFound; } /+ /** @@ -375,19 +389,18 @@ struct Cursor(P, Flag!"conflateCDATA" conflateCDATA = Yes.conflateCDATA, private bool advanceInput() { - colon = colon.max; - nameEnd = 0; - parser.popFront(); - if (!parser.empty) + this.colon = colon.max; + this.nameEnd = 0; + this.parser.popFront(); + if (!this.parser.empty) { - currentNode = parser.front; + this.currentNode = this.parser.front; return true; } - _documentEnd = true; + this._documentEnd = true; return false; } - static if (needSource!P) { /++ @@ -401,48 +414,48 @@ struct Cursor(P, Flag!"conflateCDATA" conflateCDATA = Yes.conflateCDATA, +/ void setSource(InputType input) { - parser.setSource(input); - initialize(); + this.parser.setSource(input); + this.initialize(); } } private void initialize() { // reset private fields - nextFailed = false; - _xmlDeclNotFound = false; - colon = colon.max; - nameEnd = 0; + this.nextFailed = false; + this._xmlDeclNotFound = false; + this.colon = colon.max; + this.nameEnd = 0; if (!parser.empty) { - if (parser.front.kind == XMLKind.processingInstruction && - parser.front.content.length >= 3 && - equal(parser.front.content[0..3], "xml")) + if (this.parser.front.kind == XMLKind.processingInstruction + && this.parser.front.content.length >= 3 + && equal(this.parser.front.content[0 .. 3], "xml")) { - currentNode = parser.front; + this.currentNode = this.parser.front; } else { // document without xml declaration??? // It turns out XML declaration is not mandatory, just assume UTF-8 and XML version 1.0 if it's missing! - currentNode.kind = XMLKind.processingInstruction; - currentNode.content = "xml version = \"1.0\" encoding = \"UTF-8\""; - _xmlDeclNotFound = true; + this.currentNode.kind = XMLKind.processingInstruction; + this.currentNode.content = "xml version = \"1.0\" encoding = \"UTF-8\""; + this._xmlDeclNotFound = true; } - starting = true; - _documentEnd = false; + this.starting = true; + this._documentEnd = false; } else { - _documentEnd = true; + this._documentEnd = true; } } /++ Returns whether the cursor is at the end of the document. +/ bool documentEnd() { - return _documentEnd; + return this._documentEnd; } /++ @@ -452,7 +465,7 @@ struct Cursor(P, Flag!"conflateCDATA" conflateCDATA = Yes.conflateCDATA, +/ bool atBeginning() { - return starting; + return this.starting; } /++ @@ -463,29 +476,30 @@ struct Cursor(P, Flag!"conflateCDATA" conflateCDATA = Yes.conflateCDATA, +/ bool enter() { - if (starting) + if (this.starting) { - starting = false; - if (currentNode.content is parser.front.content) + this.starting = false; + if (this.currentNode.content is this.parser.front.content) { - return advanceInput(); + return this.advanceInput(); } else { - nameEnd = 0; - nameBegin = 0; + this.nameEnd = 0; + this.nameBegin = 0; } - currentNode = parser.front; + this.currentNode = this.parser.front; return true; } - else if (currentNode.kind == XMLKind.elementStart) + else if (this.currentNode.kind == XMLKind.elementStart) { - return advanceInput() && currentNode.kind != XMLKind.elementEnd; + return this.advanceInput() && this.currentNode.kind != XMLKind + .elementEnd; } - else if (currentNode.kind == XMLKind.dtdStart) + else if (this.currentNode.kind == XMLKind.dtdStart) { - return advanceInput() && currentNode.kind != XMLKind.dtdEnd; + return this.advanceInput() && this.currentNode.kind != XMLKind.dtdEnd; } else { @@ -496,14 +510,14 @@ struct Cursor(P, Flag!"conflateCDATA" conflateCDATA = Yes.conflateCDATA, /++ Advances to the end of the parent of the current node. +/ void exit() { - if (!nextFailed) + if (!this.nextFailed) { - while (next()) + while (this.next()) { } } - nextFailed = false; + this.nextFailed = false; } /++ @@ -513,50 +527,51 @@ struct Cursor(P, Flag!"conflateCDATA" conflateCDATA = Yes.conflateCDATA, +/ bool next() { - if (parser.empty || starting || nextFailed) + if (this.parser.empty || this.starting || this.nextFailed) { return false; } - else if (currentNode.kind == XMLKind.dtdStart) + else if (this.currentNode.kind == XMLKind.dtdStart) { /+while (advanceInput && currentNode.kind != XMLKind.dtdEnd) { }+/ } - else if (currentNode.kind == XMLKind.elementStart) + else if (this.currentNode.kind == XMLKind.elementStart) { int count = 1; static if (processBadDocument == No.processBadDocument) { - StringType currName = name; + StringType currName = this.name; } - while (count > 0 && !parser.empty) + while (count > 0 && !this.parser.empty) { - if (!advanceInput) + if (!this.advanceInput) { return false; } - if (currentNode.kind == XMLKind.elementStart) + if (this.currentNode.kind == XMLKind.elementStart) { count++; } - else if (currentNode.kind == XMLKind.elementEnd) + else if (this.currentNode.kind == XMLKind.elementEnd) { count--; } } static if (processBadDocument == No.processBadDocument) { - enforce!CursorException(count == 0 && currName == name, + enforce!CursorException(count == 0 && currName == this.name, "Document is malformed!"); } } - if (!advanceInput || currentNode.kind == XMLKind.elementEnd || currentNode.kind == XMLKind.dtdEnd) + if (!this.advanceInput || this.currentNode.kind == XMLKind.elementEnd + || this.currentNode.kind == XMLKind.dtdEnd) { - nextFailed = true; + this.nextFailed = true; return false; } return true; @@ -565,20 +580,20 @@ struct Cursor(P, Flag!"conflateCDATA" conflateCDATA = Yes.conflateCDATA, /++ Returns the _kind of the current node. +/ XMLKind kind() const { - if (starting) + if (this.starting) { return XMLKind.document; } static if (conflateCDATA == Yes.conflateCDATA) { - if (currentNode.kind == XMLKind.cdata) + if (this.currentNode.kind == XMLKind.cdata) { return XMLKind.text; } } - return currentNode.kind; + return this.currentNode.kind; } /++ @@ -588,32 +603,33 @@ struct Cursor(P, Flag!"conflateCDATA" conflateCDATA = Yes.conflateCDATA, +/ StringType name() { - switch (currentNode.kind) + switch (this.currentNode.kind) { - case XMLKind.document: - case XMLKind.text: - case XMLKind.cdata: - case XMLKind.comment: - case XMLKind.declaration: - case XMLKind.conditional: - case XMLKind.dtdStart: - case XMLKind.dtdEmpty: - case XMLKind.dtdEnd: - return []; - default: - if (!nameEnd) + case XMLKind.document: + case XMLKind.text: + case XMLKind.cdata: + case XMLKind.comment: + case XMLKind.declaration: + case XMLKind.conditional: + case XMLKind.dtdStart: + case XMLKind.dtdEmpty: + case XMLKind.dtdEnd: + return []; + default: + if (!this.nameEnd) + { + ptrdiff_t i, j; + if ((j = indexOfNeither(this.currentNode.content, " \r\n\t")) >= 0) { - ptrdiff_t i, j; - if ((j = indexOfNeither(currentNode.content, " \r\n\t")) >= 0) - { - nameBegin = j; - } - - nameEnd = ((i = indexOfAny(currentNode.content[nameBegin..$], " \r\n\t")) >= 0) - ? i + nameBegin - : currentNode.content.length; + this.nameBegin = j; } - return currentNode.content[nameBegin..nameEnd]; + + this.nameEnd = (( + i = indexOfAny(this.currentNode.content[this.nameBegin .. $], + " \r\n\t")) >= 0) ? i + this.nameBegin : this.currentNode + .content.length; + } + return this.currentNode.content[this.nameBegin .. this.nameEnd]; } } @@ -623,14 +639,15 @@ struct Cursor(P, Flag!"conflateCDATA" conflateCDATA = Yes.conflateCDATA, +/ StringType localName() { - auto name = name(); - if (currentNode.kind == XMLKind.elementStart || currentNode.kind == XMLKind.elementEnd) + auto name = this.name(); + if (this.currentNode.kind == XMLKind.elementStart + || this.currentNode.kind == XMLKind.elementEnd) { - if (colon == colon.max) + if (this.colon == colon.max) { - colon = indexOf(name, ':'); + this.colon = indexOf(name, ':'); } - return name[(colon+1)..$]; + return name[this.colon + 1 .. $]; } return name; } @@ -641,17 +658,16 @@ struct Cursor(P, Flag!"conflateCDATA" conflateCDATA = Yes.conflateCDATA, +/ StringType prefix() { - if (currentNode.kind == XMLKind.elementStart || currentNode.kind == XMLKind.elementEnd) + if (this.currentNode.kind == XMLKind.elementStart + || this.currentNode.kind == XMLKind.elementEnd) { - auto name = name; - if (colon == colon.max) + auto name = this.name(); + if (this.colon == colon.max) { - colon = indexOf(name, ':'); + this.colon = indexOf(name, ':'); } - return colon >= 0 - ? name[0..colon] - : []; + return this.colon >= 0 ? name[0 .. this.colon] : []; } return []; } @@ -663,13 +679,12 @@ struct Cursor(P, Flag!"conflateCDATA" conflateCDATA = Yes.conflateCDATA, +/ auto attributes() @trusted { - - - auto kind = currentNode.kind; - if (kind == XMLKind.elementStart || kind == XMLKind.elementEmpty || kind == XMLKind.processingInstruction) + auto kind = this.currentNode.kind; + if (kind == XMLKind.elementStart || kind == XMLKind.elementEmpty + || kind == XMLKind.processingInstruction) { - name(); - return AttributesRange(currentNode.content[nameEnd..$], this); + this.name(); + return AttributesRange(this.currentNode.content[this.nameEnd .. $], this); } else { @@ -685,15 +700,18 @@ struct Cursor(P, Flag!"conflateCDATA" conflateCDATA = Yes.conflateCDATA, { switch (currentNode.kind) { - case XMLKind.entityDecl: + case XMLKind.entityDecl: { - sizediff_t b = indexOfAny(currentNode.content[nameEnd..$], "\"\'"); - sizediff_t e = lastIndexOf(currentNode.content[nameEnd..$], currentNode.content[b + nameEnd]); + sizediff_t b = indexOfAny(this.currentNode.content[this.nameEnd .. $], + "\"\'"); + sizediff_t e = lastIndexOf( + this.currentNode.content[this.nameEnd .. $], + this.currentNode.content[b + this.nameEnd]); + if (b > 0 && e > 0) { - return b + 1 <= e - ? currentNode.content[nameEnd + b + 1..nameEnd + e] - : null; + return b + 1 <= e ? this.currentNode.content[this.nameEnd + b + + 1 .. this.nameEnd + e] : null; } else { @@ -707,9 +725,10 @@ struct Cursor(P, Flag!"conflateCDATA" conflateCDATA = Yes.conflateCDATA, } } } - case XMLKind.dtdStart, XMLKind.dtdEmpty: + case XMLKind.dtdStart, XMLKind.dtdEmpty: { - sizediff_t b = indexOfNeither(currentNode.content[nameEnd..$], " \r\n\t"); + sizediff_t b = indexOfNeither(this.currentNode.content[this + .nameEnd .. $], " \r\n\t"); static if (processBadDocument == No.processBadDocument) { enforce!CursorException(b >= 0, "Document Type Declaration lacks name."); @@ -721,42 +740,43 @@ struct Cursor(P, Flag!"conflateCDATA" conflateCDATA = Yes.conflateCDATA, return null; } } - sizediff_t e = indexOfAny(currentNode.content[nameEnd + b..$], " \r\n\t"); - if (e <= 0) - { - e = currentNode.content.length; - } - else - { - e++; - } - return currentNode.content[nameEnd + b..e]; + sizediff_t e = indexOfAny(this.currentNode.content[this.nameEnd + b .. $], + " \r\n\t"); + + e = e <= 0 ? this.currentNode.content.length : e + 1; + + return this.currentNode.content[nameEnd + b .. e]; } - case XMLKind.text: - { //TO DO: This might have a performance impact if called multiple times. + case XMLKind.text: + { //TO DO: This might have a performance impact if called multiple times. static if (processBadDocument == No.processBadDocument) { - StringType result = xmlUnescape(currentNode.content[nameEnd..$], chrEntities); - if (xmlVersion == XMLVersion.XML1_0) + StringType result = xmlUnescape( + this.currentNode.content[this.nameEnd .. $], + this.chrEntities); + if (this.xmlVersion == XMLVersion.XML1_0) { - enforce!CursorException(isValidXMLText10(currentNode.content), + enforce!CursorException( + isValidXMLText10(this.currentNode.content), "Text contains invalid characters!"); } else { - enforce!CursorException(isValidXMLText11(currentNode.content), + enforce!CursorException( + isValidXMLText11(this.currentNode.content), "Text contains invalid characters!"); } return result; } else { - return xmlUnescape!(No.strict)(currentNode.content[nameEnd..$], chrEntities); + return xmlUnescape!(No.strict)( + this.currentNode.content[this.nameEnd .. $], chrEntities); } } - default: + default: { - return currentNode.content[nameEnd..$]; + return this.currentNode.content[this.nameEnd .. $]; } } } @@ -764,7 +784,7 @@ struct Cursor(P, Flag!"conflateCDATA" conflateCDATA = Yes.conflateCDATA, /++ Returns the entire text of the current node. +/ StringType wholeContent() const { - return currentNode.content; + return this.currentNode.content; } } @@ -779,8 +799,7 @@ template cursor(Flag!"conflateCDATA" conflateCDATA = Yes.conflateCDATA) { return cursor(parser); } */ - auto cursor(T)(auto ref T parser) - if(isLowLevelParser!T) + auto cursor(T)(auto ref T parser) if (isLowLevelParser!T) { auto cursor = Cursor!(T, conflateCDATA)(); cursor.parser = parser; @@ -792,7 +811,7 @@ template cursor(Flag!"conflateCDATA" conflateCDATA = Yes.conflateCDATA) } } -unittest +pure unittest { import newxml.lexers; import newxml.parser; @@ -830,111 +849,115 @@ unittest assert(cursor.name() == "xml"); assert(cursor.prefix() == ""); assert(cursor.localName() == "xml"); - assert(cursor.attributes().array == [Attribute!wstring("encoding", "utf-8")]); + assert(cursor.attributes().array == [ + Attribute!wstring("encoding", "utf-8") + ]); assert(cursor.content() == " encoding = \"utf-8\" "); assert(cursor.enter()); - assert(!cursor.atBeginning); + assert(!cursor.atBeginning); - // - assert(cursor.kind == XMLKind.elementDecl); - assert(cursor.wholeContent == " myelem ANY"); + assert(cursor.enter); + // + assert(cursor.kind == XMLKind.elementDecl); + assert(cursor.wholeContent == " myelem ANY"); + + assert(cursor.next); + // + assert(cursor.kind == XMLKind.entityDecl); + assert(cursor.wholeContent == " myent \"replacement text\""); + assert(cursor.name == "myent"); + assert(cursor.content == "replacement text", to!string(cursor.content)); + + assert(cursor.next); + // + assert(cursor.kind == XMLKind.attlistDecl); + assert(cursor.wholeContent == " myelem foo cdata #REQUIRED "); + + assert(cursor.next); + // + assert(cursor.kind == XMLKind.notationDecl); + assert(cursor.wholeContent == " PUBLIC 'h'"); + + assert(cursor.next); + // + assert(cursor.kind == XMLKind.declaration); + assert(cursor.wholeContent == "FOODECL asdffdsa "); + + assert(!cursor.next); + + //assert(cursor.parser._chrEntities["myent"] == "replacement text"); + cursor.exit; - assert(cursor.next); - // - assert(cursor.kind == XMLKind.entityDecl); - assert(cursor.wholeContent == " myent \"replacement text\""); - assert(cursor.name == "myent"); - assert(cursor.content == "replacement text", to!string(cursor.content)); + // ]> + assert(cursor.kind == XMLKind.dtdEnd); + assert(!cursor.wholeContent); + assert(cursor.next); - assert(cursor.next); - // - assert(cursor.kind == XMLKind.attlistDecl); - assert(cursor.wholeContent == " myelem foo cdata #REQUIRED "); + // + assert(cursor.kind() == XMLKind.elementStart); + assert(cursor.name() == "aaa"); + assert(cursor.prefix() == ""); + assert(cursor.localName() == "aaa"); + assert(cursor.attributes().array == [ + Attribute!wstring("xmlns:myns", "something") + ]); + assert(cursor.content() == " xmlns:myns=\"something\""); - assert(cursor.next); - // - assert(cursor.kind == XMLKind.notationDecl); - assert(cursor.wholeContent == " PUBLIC 'h'"); + assert(cursor.enter()); + // + assert(cursor.kind() == XMLKind.elementStart); + assert(cursor.name() == "myns:bbb"); + assert(cursor.prefix() == "myns"); + assert(cursor.localName() == "bbb"); + assert(cursor.attributes().array == [Attribute!wstring("myns:att", ">")]); + assert(cursor.content() == " myns:att='>'"); - assert(cursor.next); - // - assert(cursor.kind == XMLKind.declaration); - assert(cursor.wholeContent == "FOODECL asdffdsa "); + assert(cursor.enter()); + cursor.exit(); - assert(!cursor.next); + // + assert(cursor.kind() == XMLKind.elementEnd); + assert(cursor.name() == "myns:bbb"); + assert(cursor.prefix() == "myns"); + assert(cursor.localName() == "bbb"); + assert(cursor.attributes().empty); + assert(cursor.content() == []); + + assert(cursor.next()); + // + assert(cursor.kind() == XMLKind.text); + assert(cursor.name() == ""); + assert(cursor.prefix() == ""); + assert(cursor.localName() == ""); + assert(cursor.attributes().empty); + assert(cursor.content() == " Ciaone! "); + + assert(cursor.next()); + // + assert(cursor.kind() == XMLKind.elementEmpty); + assert(cursor.name() == "ccc"); + assert(cursor.prefix() == ""); + assert(cursor.localName() == "ccc"); + assert(cursor.attributes().empty); + assert(cursor.content() == []); - //assert(cursor.parser._chrEntities["myent"] == "replacement text"); - cursor.exit; + assert(!cursor.next()); + cursor.exit(); - // ]> - assert(cursor.kind == XMLKind.dtdEnd); - assert(!cursor.wholeContent); - assert(cursor.next); + // + assert(cursor.kind() == XMLKind.elementEnd); + assert(cursor.name() == "aaa"); + assert(cursor.prefix() == ""); + assert(cursor.localName() == "aaa"); + assert(cursor.attributes().empty); + assert(cursor.content() == []); - // - assert(cursor.kind() == XMLKind.elementStart); - assert(cursor.name() == "aaa"); - assert(cursor.prefix() == ""); - assert(cursor.localName() == "aaa"); - assert(cursor.attributes().array == [Attribute!wstring("xmlns:myns", "something")]); - assert(cursor.content() == " xmlns:myns=\"something\""); - - assert(cursor.enter()); - // - assert(cursor.kind() == XMLKind.elementStart); - assert(cursor.name() == "myns:bbb"); - assert(cursor.prefix() == "myns"); - assert(cursor.localName() == "bbb"); - assert(cursor.attributes().array == [Attribute!wstring("myns:att", ">")]); - assert(cursor.content() == " myns:att='>'"); - - assert(cursor.enter()); - cursor.exit(); - - // - assert(cursor.kind() == XMLKind.elementEnd); - assert(cursor.name() == "myns:bbb"); - assert(cursor.prefix() == "myns"); - assert(cursor.localName() == "bbb"); - assert(cursor.attributes().empty); - assert(cursor.content() == []); - - assert(cursor.next()); - // - assert(cursor.kind() == XMLKind.text); - assert(cursor.name() == ""); - assert(cursor.prefix() == ""); - assert(cursor.localName() == ""); - assert(cursor.attributes().empty); - assert(cursor.content() == " Ciaone! "); - - assert(cursor.next()); - // - assert(cursor.kind() == XMLKind.elementEmpty); - assert(cursor.name() == "ccc"); - assert(cursor.prefix() == ""); - assert(cursor.localName() == "ccc"); - assert(cursor.attributes().empty); - assert(cursor.content() == []); - - assert(!cursor.next()); - cursor.exit(); - - // - assert(cursor.kind() == XMLKind.elementEnd); - assert(cursor.name() == "aaa"); - assert(cursor.prefix() == ""); - assert(cursor.localName() == "aaa"); - assert(cursor.attributes().empty); - assert(cursor.content() == []); - - assert(!cursor.next()); + assert(!cursor.next()); cursor.exit(); assert(cursor.documentEnd); @@ -947,27 +970,38 @@ unittest + Advancing the range returned by this function also advances `cursor`. It is thus + not recommended to interleave usage of this function with raw usage of `cursor`. +/ -auto children(T)(ref T cursor) @trusted - if (isCursor!T) +auto children(T)(ref T cursor) @trusted if (isCursor!T) { struct XMLRange { T* cursor; bool endReached; - bool empty() const { return endReached; } - void popFront() { endReached = !cursor.next(); } - ref T front() { return *cursor; } + bool empty() const + { + return this.endReached; + } - ~this() { cursor.exit; } - } - auto workaround() @system { - return XMLRange(&cursor, cursor.enter); + void popFront() + { + this.endReached = !this.cursor.next(); + } + + ref T front() + { + return *this.cursor; + } + + ~this() + { + this.cursor.exit; + } } - return workaround(); + + return XMLRange(&cursor, cursor.enter); } -unittest +pure unittest { import newxml.lexers; import newxml.parser; @@ -1062,7 +1096,9 @@ unittest assert(range3.front.localName() == ""); assert(range3.front.attributes().empty); // split and strip so the unittest does not depend on the newline policy or indentation of this file - static immutable linesArr = ["Lots of Text!", " On multiple lines!", " "]; + static immutable linesArr = [ + "Lots of Text!", " On multiple lines!", " " + ]; assert(range3.front.content().lineSplitter.equal(linesArr)); range3.popFront; @@ -1125,18 +1161,16 @@ import std.traits : isArray; + `copyingCursor`. +/ struct CopyingCursor(CursorType, Flag!"intern" intern = No.intern) - if (isCursor!CursorType && isArray!(CursorType.StringType)) + if (isCursor!CursorType && isArray!(CursorType.StringType)) { alias StringType = CursorType.StringType; - //mixin UsesAllocator!Alloc; - CursorType cursor; alias cursor this; static if (intern == Yes.intern) { - import std.typecons: Rebindable; + import std.typecons : Rebindable; Rebindable!(immutable StringType)[const StringType] interned; } @@ -1145,7 +1179,7 @@ struct CopyingCursor(CursorType, Flag!"intern" intern = No.intern) { static if (intern == Yes.intern) { - auto match = str in interned; + auto match = str in this.interned; if (match) { return *match; @@ -1153,14 +1187,12 @@ struct CopyingCursor(CursorType, Flag!"intern" intern = No.intern) } import std.traits : Unqual; - import std.experimental.allocator;//import stdx.allocator; + import std.experimental.allocator; //import stdx.allocator; import std.range.primitives : ElementEncodingType; import core.stdc.string : memcpy; alias ElemType = ElementEncodingType!StringType; - ElemType[] cp;//auto cp = cast(ElemType[]) allocator.makeArray!(Unqual!ElemType)(str.length); - cp.length = str.length; - memcpy(cast(void*)cp.ptr, cast(void*)str.ptr, str.length * ElemType.sizeof); + ElemType[] cp = str.dup; static if (intern == Yes.intern) { @@ -1172,23 +1204,27 @@ struct CopyingCursor(CursorType, Flag!"intern" intern = No.intern) auto name() @trusted { - return copy(cursor.name); + return copy(this.cursor.name); } + auto localName() @trusted { - return copy(cursor.localName); + return copy(this.cursor.localName); } + auto prefix() @trusted { - return copy(cursor.prefix); + return copy(this.cursor.prefix); } + auto content() @trusted { - return copy(cursor.content); + return copy(this.cursor.content); } + auto wholeContent() @trusted { - return copy(cursor.wholeContent); + return copy(this.cursor.wholeContent); } auto attributes() @trusted @@ -1202,14 +1238,13 @@ struct CopyingCursor(CursorType, Flag!"intern" intern = No.intern) auto front() { - auto attr = attrs.front; - return Attribute!StringType( - parent.copy(attr.name), - parent.copy(attr.value), - ); + auto attr = this.attrs.front; + return Attribute!StringType(parent.copy(attr.name), parent.copy(attr + .value),); } } - return CopyRange(cursor.attributes, &this); + + return CopyRange(this.cursor.attributes, &this); } } @@ -1223,12 +1258,11 @@ auto copyingCursor(Flag!"intern" intern = No.intern, CursorType)(auto ref Cursor return res; } -unittest +pure unittest { import newxml.lexers; import newxml.parser; - wstring xml = q{ @@ -1240,9 +1274,7 @@ unittest }; - auto cursor = - xml - .lexer + auto cursor = xml.lexer .parser .cursor!(Yes.conflateCDATA) .copyingCursor!(Yes.intern)(); diff --git a/source/newxml/dom.d b/source/newxml/dom.d index 5e74b5b..7f9791b 100644 --- a/source/newxml/dom.d +++ b/source/newxml/dom.d @@ -27,7 +27,6 @@ module newxml.dom; import newxml.interfaces; -public import newxml.domstring; import std.typecons : BitFlags; import std.variant : Variant; @@ -43,7 +42,7 @@ alias UserData = Variant; + the application to implement various behaviors regarding the data it associates + to the DOM nodes. +/ -alias UserDataHandler = void delegate(UserDataOperation, DOMString, UserData, Node, Node) @safe; +alias UserDataHandler = void delegate(UserDataOperation, string, UserData, Node, Node) @safe; /++ + An integer indicating which type of node this is. @@ -51,7 +50,7 @@ alias UserDataHandler = void delegate(UserDataOperation, DOMString, UserData, No + Note: + Numeric codes up to 200 are reserved to W3C for possible future use. +/ -enum NodeType: ushort +enum NodeType : ushort { element = 1, attribute, @@ -71,18 +70,18 @@ enum NodeType: ushort + A bitmask indicating the relative document position of a node with respect to another node. + Returned by `Node.compareDocumentPosition`. +/ -enum DocumentPosition: ushort +enum DocumentPosition : ushort { /// Set when the two nodes are in fact the same - none = 0, + none = 0, /// Set when the two nodes are not in the same tree disconnected = 1, /// Set when the second node precedes the first - preceding = 2, + preceding = 2, /// Set when the second node follows the first - following = 4, + following = 4, /// Set when the second node _contains the first - contains = 8, + contains = 8, /// Set when the second node is contained by the first containedBy = 16, /++ @@ -98,7 +97,7 @@ enum DocumentPosition: ushort /++ + An integer indicating the type of operation being performed on a node. +/ -enum UserDataOperation: ushort +enum UserDataOperation : ushort { /// The node is cloned, using `Node.cloneNode()`. nodeCloned = 1, @@ -124,11 +123,11 @@ enum UserDataOperation: ushort + Note: + Other numeric codes are reserved for W3C for possible future use. +/ -enum ExceptionCode: ushort +enum ExceptionCode : ushort { /// If index or size is negative, or greater than the allowed value. indexSize, - /// If the specified range of text does not fit into a `DOMString`. + /// If the specified range of text does not fit into a `string`. domStringSize, /// If any `Node` is inserted somewhere it doesn't belong. hierarchyRequest, @@ -163,7 +162,7 @@ enum ExceptionCode: ushort } /// An integer indicating the severity of a `DOMError`. -enum ErrorSeverity: ushort +enum ErrorSeverity : ushort { /++ + The severity of the error described by the `DOMError` is warning. A `WARNING` @@ -186,13 +185,14 @@ enum ErrorSeverity: ushort fatalError, } -enum DerivationMethod: ulong +enum DerivationMethod : ulong { restriction = 0x00000001, - extension = 0x00000002, - union_ = 0x00000004, - list = 0x00000008, + extension = 0x00000002, + union_ = 0x00000004, + list = 0x00000008, } + @safe: /++ + DOM operations only raise exceptions in "exceptional" circumstances, i.e., @@ -205,35 +205,37 @@ enum DerivationMethod: ulong + example, implementations should raise an implementation-dependent exception + if a `null` argument is passed when `null` was not expected. +/ -abstract class DOMException: XMLException +abstract class DOMException : XMLException { /// @property ExceptionCode code(); /// - @nogc @safe pure nothrow this(string msg, string file = __FILE__, size_t line = __LINE__, - Throwable nextInChain = null) + @nogc @safe pure nothrow this(string msg, string file = __FILE__, + size_t line = __LINE__, Throwable nextInChain = null) { super(msg, file, line, nextInChain); } - @nogc @safe pure nothrow this(string msg, Throwable nextInChain, string file = __FILE__, size_t line = __LINE__) + @nogc @safe pure nothrow this(string msg, Throwable nextInChain, + string file = __FILE__, size_t line = __LINE__) { super(msg, file, line, nextInChain); } } /++ -+ The `DOMStringList` interface provides the abstraction of an ordered collection -+ of `DOMString` values, without defining or constraining how this collection is -+ implemented. The items in the DOMStringList are accessible via an integral index, ++ The `stringList` interface provides the abstraction of an ordered collection ++ of `string` values, without defining or constraining how this collection is ++ implemented. The items in the stringList are accessible via an integral index, + starting from `0`. +/ -interface DOMStringList { - DOMString item(size_t index); +interface stringList +{ + string item(size_t index); @property size_t length(); - bool contains(DOMString str); -}; + bool contains(string str); +} /++ + The `DOMImplementationList` interface provides the abstraction of an ordered @@ -241,44 +243,53 @@ interface DOMStringList { + collection is implemented. The items in the `DOMImplementationList` are accessible + via an integral index, starting from `0`. +/ -interface DOMImplementationList { +interface DOMImplementationList +{ DOMImplementation item(size_t index); @property size_t length(); } /++ -+ This interface permits a DOM implementer to supply one or more implementations, -+ based upon requested features and versions, as specified in DOM Features. -+ Each implemented DOMImplementationSource object is listed in the binding-specific -+ list of available sources so that its `DOMImplementation` objects are made available. ++ This interface permits a DOM implementer to supply one or more ++ implementations, based upon requested features and versions, as specified in ++ DOM Features. Each implemented DOMImplementationSource object is listed in ++ the binding-specific list of available sources so that its ++ `DOMImplementation` objects are made available. +/ -interface DOMImplementationSource { - /// A method to request the first DOM implementation that supports the specified features. - DOMImplementation getDOMImplementation(DOMString features); - /// A method to request a list of DOM implementations that support the specified features and versions, as specified in DOM Features. - DOMImplementationList getDOMImplementationList(DOMString features); +interface DOMImplementationSource +{ + /// A method to request the first DOM implementation that supports the + /// specified features. + DOMImplementation getDOMImplementation(string features); + /// A method to request a list of DOM implementations that support the + /// specified features and versions, as specified in DOM Features. + DOMImplementationList getDOMImplementationList(string features); } /++ + The DOMImplementation interface provides a number of methods for performing -+ operations that are independent of any particular instance of the document object model. ++ operations that are independent of any particular instance of the document ++ object model. +/ -interface DOMImplementation { +interface DOMImplementation +{ /++ - + Creates an empty DocumentType node. Entity declarations and notations are not - + made available. Entity reference expansions and default attribute additions do not occur. + + Creates an empty DocumentType node. Entity declarations and notations + + are not made available. Entity reference expansions and default + + attribute additions do not occur. +/ - DocumentType createDocumentType(DOMString qualifiedName, DOMString publicId, DOMString systemId); + DocumentType createDocumentType(string qualifiedName, string publicId, string systemId); /++ + Creates a DOM Document object of the specified type with its document element. + - + Note that based on the DocumentType given to create the document, the implementation - + may instantiate specialized Document objects that support additional features than the "Core", - + such as "HTML". On the other hand, setting the DocumentType after the document - + was created makes this very unlikely to happen. + + Note that based on the DocumentType given to create the document, the + + implementation may instantiate specialized Document objects that support + + additional features than the "Core", such as "HTML". On the other hand, + + setting the DocumentType after the document was created makes this very + + unlikely to happen. +/ - Document createDocument(DOMString namespaceURI, DOMString qualifiedName, DocumentType doctype); + Document createDocument(string namespaceURI, string qualifiedName, DocumentType doctype); bool hasFeature(string feature, string version_); Object getFeature(string feature, string version_); @@ -314,7 +325,8 @@ interface DOMImplementation { + acts as the parent of these nodes so that the user can use the standard methods + from the `Node` interface, such as `Node.insertBefore` and `Node.appendChild`. +/ -interface DocumentFragment : Node { +interface DocumentFragment : Node +{ } /++ @@ -327,7 +339,8 @@ interface DocumentFragment : Node { + a `ownerDocument` attribute which associates them with the `Document` within + whose context they were created. +/ -interface Document : Node { +interface Document : Node +{ /++ + The `DocumentType` associated with this document. For XML documents without a + document type declaration this returns `null`. @@ -356,53 +369,53 @@ interface Document : Node { + To create an `Element` with a qualified name and namespace URI, use the + `createElementNS` method. +/ - Element createElement(DOMString tagName); + Element createElement(string tagName); /++ + Creates an `Element` of the given qualified name and namespace URI. + Per the XML Namespaces specification, applications must use the value `null` + as the `namespaceURI` parameter for methods if they wish to have no namespace. +/ - Element createElementNS(DOMString namespaceURI, DOMString qualifiedName); + Element createElementNS(string namespaceURI, string qualifiedName); /// Creates an empty `DocumentFragment` object. DocumentFragment createDocumentFragment(); /// Creates a `Text` node given the specified string. - Text createTextNode(DOMString data); + Text createTextNode(string data); /// Creates a `Comment` node given the specified string. - Comment createComment(DOMString data); + Comment createComment(string data); /// Creates a `CDATASection` node whose value is the specified string. - CDATASection createCDATASection(DOMString data); + CDATASection createCDATASection(string data); /// Creates a `ProcessingInstruction` node given the specified name and data strings. - ProcessingInstruction createProcessingInstruction(DOMString target, DOMString data); + ProcessingInstruction createProcessingInstruction(string target, string data); /++ + Creates an `Attr` of the given name. Note that the `Attr` instance can + then be set on an `Element` using the `setAttributeNode` method. + To create an attribute with a qualified name and namespace URI, use the + `createAttributeNS` method. +/ - Attr createAttribute(DOMString name); + Attr createAttribute(string name); /++ + Creates an attribute of the given qualified name and namespace URI. + Per the XML Namespaces specification, applications must use the value `null` + as the `namespaceURI` parameter for methods if they wish to have no namespace. +/ - Attr createAttributeNS(DOMString namespaceURI, DOMString qualifiedName); + Attr createAttributeNS(string namespaceURI, string qualifiedName); /++ + Creates an `EntityReference` object. In addition, if the referenced entity + is known, the child list of the `EntityReference` node is made the same as + that of the corresponding `Entity` node. +/ - EntityReference createEntityReference(DOMString name); + EntityReference createEntityReference(string name); /++ + Returns a `NodeList` of all the `Element`s in document order with a given + tag name and are contained in the document. +/ - NodeList getElementsByTagName(DOMString tagname); + NodeList getElementsByTagName(string tagname); /++ + Returns a `NodeList` of all the `Element`s with a given local name and + namespace URI in document order. +/ - NodeList getElementsByTagNameNS(DOMString namespaceURI, DOMString localName); + NodeList getElementsByTagNameNS(string namespaceURI, string localName); /++ + Returns the `Element` that has an ID attribute with the given value. If no + such element exists, this returns `null`. If more than one element has an @@ -412,7 +425,7 @@ interface Document : Node { + + Note: Attributes with the name "ID" or "id" are not of type ID unless so defined. +/ - Element getElementById(DOMString elementId); + Element getElementById(string elementId); /++ + Imports a node from another document to this document, without altering or @@ -440,13 +453,13 @@ interface Document : Node { + the parsing. This is `null` when it is not known, such as when the `Document` + was created in memory. +/ - @property DOMString inputEncoding(); + @property string inputEncoding(); /++ + An attribute specifying, as part of the XML declaration, the encoding of + this document. This is `null` when unspecified or when it is not known, + such as when the Document was created in memory. +/ - @property DOMString xmlEncoding(); + @property string xmlEncoding(); /++ + An attribute specifying, as part of the XML declaration, whether this document @@ -462,9 +475,9 @@ interface Document : Node { + the "XML" feature, the value is "1.0". If this document does not support + the "XML" feature, the value is always `null`. +/ - @property DOMString xmlVersion(); + @property string xmlVersion(); /// ditto - @property void xmlVersion(DOMString); + @property void xmlVersion(string); /++ + An attribute specifying whether error checking is enforced or not. @@ -483,9 +496,9 @@ interface Document : Node { + is performed when setting this attribute; this could result in a `null` + value returned when using `Node.baseURI`. +/ - @property DOMString documentURI(); + @property string documentURI(); /// ditto - @property void documentURI(DOMString); + @property void documentURI(string); /// The configuration used when `Document.normalizeDocument()` is invoked. @property DOMConfiguration domConfig(); @@ -512,7 +525,7 @@ interface Document : Node { + nodes list if it has one, the user data that was attached to the old node + is attached to the new node. +/ - Node renameNode(Node n, DOMString namespaceURI, DOMString qualifiedName); + Node renameNode(Node n, string namespaceURI, string qualifiedName); } /++ @@ -530,18 +543,19 @@ interface Document : Node { + Note that the specialized interfaces may contain additional and more convenient + mechanisms to get and set the relevant information. +/ -interface Node { +interface Node +{ /// A code representing the type of the underlying object. @property NodeType nodeType(); /// The name of this node, depending on its type. - @property DOMString nodeName(); + @property string nodeName(); /++ + Returns the local part of the qualified name of this node. + + For nodes of any type other than `ELEMENT` and `ATTRIBUTE` and nodes created + with a DOM Level 1 method, such as `Document.createElement`, this is always `null`. +/ - @property DOMString localName(); + @property string localName(); /++ + The namespace prefix of this node, or `null` if it is unspecified. + When it is defined to be `null`, setting it has no effect, including if @@ -558,9 +572,9 @@ interface Node { + with a DOM Level 1 method, such as `createElement` from the `Document` + interface, this is always `null`. +/ - @property DOMString prefix(); + @property string prefix(); /// ditto - @property void prefix(DOMString); + @property void prefix(string); /++ + The namespace URI of this node, or `null` if it is unspecified. + This is not a computed value that is the result of a namespace lookup based @@ -569,18 +583,18 @@ interface Node { + For nodes of any type other than `ELEMENT` and `ATTRIBUTE` and nodes created + with a DOM Level 1 method, such as `Document.createElement`, this is always `null`. +/ - @property DOMString namespaceURI(); + @property string namespaceURI(); /// The absolute base URI of this node or null if the implementation wasn't able to obtain an absolute URI - @property DOMString baseURI(); + @property string baseURI(); /// The value of this node, depending on its type. - @property DOMString nodeValue(); + @property string nodeValue(); /// ditto - @property void nodeValue(DOMString); + @property void nodeValue(string); /// Returns the text content, if there's any. - @property DOMString textContent(); + @property string textContent(); /// Sets the text content, it there's any. - @property void textContent(DOMString); + @property void textContent(string); /++ + The parent of this node. All nodes, except `Attr`, `Document`, `DocumentFragment`, @@ -663,12 +677,12 @@ interface Node { + Retrieves the object associated to a key on a this node. The object must + first have been set to this node by calling `setUserData` with the same key. +/ - UserData getUserData(string key) @trusted ; + UserData getUserData(string key) @trusted; /++ + Associate an object to a key on this node. + The object can later be retrieved from this node by calling `getUserData` with the same key. +/ - UserData setUserData(string key, UserData data, UserDataHandler handler) @trusted ; + UserData setUserData(string key, UserData data, UserDataHandler handler) @trusted; /++ + Compares the reference node, i.e. the node on which this method is being @@ -681,11 +695,11 @@ interface Node { + Look up the prefix associated to the given namespace URI, starting from this node. + The default namespace declarations are ignored by this method. +/ - DOMString lookupPrefix(DOMString namespaceURI); + string lookupPrefix(string namespaceURI); /// Look up the namespace URI associated to the given `prefix`, starting from this node. - DOMString lookupNamespaceURI(DOMString prefix); + string lookupNamespaceURI(string prefix); /// This method checks if the specified `namespaceURI` is the default namespace or not. - bool isDefaultNamespace(DOMString namespaceURI); + bool isDefaultNamespace(string namespaceURI); } /++ @@ -695,7 +709,8 @@ interface Node { + + The items in the `NodeList` are accessible via an integral index, starting from `0`. +/ -interface NodeList { +interface NodeList +{ /++ + Returns the `index`th item in the collection. If `index` is greater than + or equal to the number of nodes in the list, this returns `null`. @@ -732,7 +747,8 @@ interface NodeList { + + `NamedNodeMap` objects in the DOM are live. +/ -interface NamedNodeMap { +interface NamedNodeMap +{ /++ + Returns the `index`th item in the collection. If `index` is greater than + or equal to the number of nodes in the list, this returns `null`. @@ -757,12 +773,13 @@ interface NamedNodeMap { return 0; } - final Node opIndex(size_t index) { + final Node opIndex(size_t index) + { return item(index); } /// Retrieves a node specified by name. - Node getNamedItem(DOMString name); + Node getNamedItem(string name); /++ + Adds a node using its `nodeName` attribute. If a node with that name is + already present in this map, it is replaced by the new one. Replacing a @@ -779,14 +796,14 @@ interface NamedNodeMap { + value, an attribute immediately appears containing the default value as + well as the corresponding namespace URI, local name, and prefix when applicable. +/ - Node removeNamedItem(DOMString name); + Node removeNamedItem(string name); /++ + Retrieves a node specified by local name and namespace URI. + Per the XML Namespaces specification, applications must use the value `null` + as the `namespaceURI` parameter for methods if they wish to have no namespace. +/ - Node getNamedItemNS(DOMString namespaceURI, DOMString localName); + Node getNamedItemNS(string namespaceURI, string localName); /++ + Adds a node using its `namespaceURI` and `localName`. If a node with that + namespace URI and that local name is already present in this map, it is @@ -804,7 +821,7 @@ interface NamedNodeMap { + Per the XML Namespaces specification, applications must use the value `null` + as the `namespaceURI` parameter for methods if they wish to have no namespace. +/ - Node removeNamedItemNS(DOMString namespaceURI, DOMString localName); + Node removeNamedItemNS(string namespaceURI, string localName); } /++ @@ -814,25 +831,26 @@ interface NamedNodeMap { + correspond directly to `CharacterData`, though `Text` and others do inherit + the interface from it. All offsets in this interface start from `0`. +/ -interface CharacterData : Node { - @property DOMString data(); - @property void data(DOMString); +interface CharacterData : Node +{ + @property string data(); + @property void data(string); @property size_t length(); /// Extracts a substring of `data` starting at `offset`, with length `count`. - DOMString substringData(size_t offset, size_t count); + string substringData(size_t offset, size_t count); /++ + Append the string to the end of the character data of the node. Upon success, - + data provides access to the concatenation of data and the DOMString specified. + + data provides access to the concatenation of data and the string specified. +/ - void appendData(DOMString arg); + void appendData(string arg); /// Insert a string at the specified offset. - void insertData(size_t offset, DOMString arg); + void insertData(size_t offset, string arg); /// Remove a range of characters from the node. Upon success, `data` and `length` reflect the change. void deleteData(size_t offset, size_t count); /// Replace `count` characters starting at the specified offset with the specified string. - void replaceData(size_t offset, size_t count, DOMString arg); + void replaceData(size_t offset, size_t count, string arg); } /++ @@ -852,12 +870,13 @@ interface CharacterData : Node { + DOM need to be aware that `Attr` nodes have some things in common with other + objects inheriting the `Node` interface, but they also are quite distinct. +/ -interface Attr : Node { +interface Attr : Node +{ /++ + Returns the _name of this attribute. If `Node.localName` is different from + `null`, this attribute is a qualified name. +/ - @property DOMString name(); + @property string name(); /++ + `true` if this attribute was explicitly given a value in the instance document, + `false` otherwise. If the application changed the value of this attribute @@ -868,7 +887,7 @@ interface Attr : Node { +/ @property bool specified(); /++ - + On retrieval, the value of the attribute is returned as a `DOMString`. + + On retrieval, the value of the attribute is returned as a `string`. + Character and general entity references are replaced with their values. + See also the method `getAttribute` on the `Element` interface. + On setting, this creates a `Text` node with the unparsed contents of the @@ -876,9 +895,9 @@ interface Attr : Node { + are instead treated as literal text. + See also the method `Element.setAttribute`. +/ - @property DOMString value(); + @property string value(); /// ditto - @property void value(DOMString); + @property void value(string); /// The `Element` node this attribute is attached to or `null` if this attribute is not in use. @property Element ownerElement(); @@ -897,16 +916,18 @@ interface Attr : Node { +/ @property bool isId(); } + /** * Elements are the main building block of an XML document. They have a name, an associated namespace, attributes, * text, and child elements. */ -interface Element : Node { +interface Element : Node +{ /// The name of the element. If `Node.localName` is different from `null`, this attribute is a qualified name. - @property DOMString tagName(); + @property string tagName(); /// Retrieves an attribute value by name. - DOMString getAttribute(DOMString name); + string getAttribute(string name); /++ + Adds a new attribute. If an attribute with that name is already present in + the element, its value is changed to be that of the value parameter. This @@ -919,7 +940,8 @@ interface Element : Node { + to assign it as the value of an attribute. + To set an attribute with a qualified name and namespace URI, use the `setAttributeNS` method. +/ - void setAttribute(DOMString name, DOMString value); + void setAttribute(string name, string value); + /++ + Removes an attribute by name. If a default value for the removed attribute + is defined in the DTD, a new attribute immediately appears with the default @@ -927,10 +949,11 @@ interface Element : Node { + when applicable. + To remove an attribute by local name and namespace URI, use the `removeAttributeNS` method. +/ - void removeAttribute(DOMString name); + void removeAttribute(string name); /// Retrieves an attribute node by name. - Attr getAttributeNode(DOMString name); + Attr getAttributeNode(string name); + /++ + Adds a new attribute node. If an attribute with that name (`nodeName`) is + already present in the element, it is replaced by the new one. Replacing an @@ -939,6 +962,7 @@ interface Element : Node { + the `setAttributeNodeNS` method. +/ Attr setAttributeNode(Attr newAttr); + /++ + Removes the specified attribute node. If a default value for the removed attribute + is defined in the DTD, a new attribute immediately appears with the default @@ -952,7 +976,8 @@ interface Element : Node { + Per the XML Namespaces specification, applications must use the value `null` + as the `namespaceURI` parameter for methods if they wish to have no namespace. +/ - DOMString getAttributeNS(DOMString namespaceURI, DOMString localName); + string getAttributeNS(string namespaceURI, string localName); + /++ + Adds a new attribute. If an attribute with the same local name and namespace + URI is already present on the element, its prefix is changed to be the prefix @@ -967,7 +992,8 @@ interface Element : Node { + Per the XML Namespaces specification, applications must use the value `null` + as the `namespaceURI` parameter for methods if they wish to have no namespace. +/ - void setAttributeNS(DOMString namespaceURI, DOMString qualifiedName, DOMString value); + void setAttributeNS(string namespaceURI, string qualifiedName, string value); + /++ + Removes an attribute by local name and namespace URI. If a default value + for the removed attribute is defined in the DTD, a new attribute immediately @@ -976,14 +1002,15 @@ interface Element : Node { + Per the XML Namespaces specification, applications must use the value `null` + as the `namespaceURI` parameter for methods if they wish to have no namespace. +/ - void removeAttributeNS(DOMString namespaceURI, DOMString localName); + void removeAttributeNS(string namespaceURI, string localName); /++ + Retrieves an `Attr` node by local name and namespace URI. + Per the XML Namespaces specification, applications must use the value `null` + as the `namespaceURI` parameter for methods if they wish to have no namespace. +/ - Attr getAttributeNodeNS(DOMString namespaceURI, DOMString localName); + Attr getAttributeNodeNS(string namespaceURI, string localName); + /++ + Adds a new attribute. If an attribute with that local name and that namespace + URI is already present in the element, it is replaced by the new one. Replacing @@ -994,14 +1021,15 @@ interface Element : Node { Attr setAttributeNodeNS(Attr newAttr); /// Returns `true` when an attribute with a given `name` is specified on this element or has a default value, `false` otherwise. - bool hasAttribute(DOMString name); + bool hasAttribute(string name); + /++ + Returns `true` when an attribute with a given `localName` and `namespaceURI` + is specified on this element or has a default value, `false` otherwise. + Per the XML Namespaces specification, applications must use the value `null` + as the `namespaceURI` parameter for methods if they wish to have no namespace. +/ - bool hasAttributeNS(DOMString namespaceURI, DOMString localName); + bool hasAttributeNS(string namespaceURI, string localName); /++ + If the parameter `isId` is `true`, this method declares the specified @@ -1011,16 +1039,19 @@ interface Element : Node { + the `Attr.schemaTypeInfo` of the specified `Attr` node. Use the value `false` + for the parameter `isId` to undeclare an attribute for being a user-determined ID attribute. +/ - void setIdAttribute(DOMString name, bool isId); + void setIdAttribute(string name, bool isId); + /// ditto - void setIdAttributeNS(DOMString namespaceURI, DOMString localName, bool isId); + void setIdAttributeNS(string namespaceURI, string localName, bool isId); + /// ditto void setIdAttributeNode(Attr idAttr, bool isId); /// Returns a `NodeList` of all descendant `Element`s with a given tag name, in document order. - NodeList getElementsByTagName(DOMString name); + NodeList getElementsByTagName(string name); + /// Returns a `NodeList` of all the descendant `Element`s with a given local name and namespace URI in document order. - NodeList getElementsByTagNameNS(DOMString namespaceURI, DOMString localName); + NodeList getElementsByTagNameNS(string namespaceURI, string localName); /// The type information associated with this element. @property XMLTypeInfo schemaTypeInfo(); @@ -1034,7 +1065,8 @@ interface Element : Node { + there is markup, it is parsed into the information items (elements, comments, + etc.) and `Text` nodes that form the list of children of the element. +/ -interface Text : CharacterData { +interface Text : CharacterData +{ /++ + Breaks this node into two nodes at the specified `offset`, keeping both + in the tree as siblings. After being split, this node will contain all @@ -1048,17 +1080,20 @@ interface Text : CharacterData { /// Returns whether this text node contains element content whitespace, often abusively called "ignorable whitespace". @property bool isElementContentWhitespace(); + /// Returns the combined data of all direct text node siblings. - @property DOMString wholeText(); + @property string wholeText(); + /// Couldn't find in DOM documentation, but leavint it there. - Text replaceWholeText(DOMString content); + Text replaceWholeText(string content); } /++ + This interface inherits from `CharacterData` and represents the content of a + comment, i.e., all the characters between the starting ''. +/ -interface Comment : CharacterData { +interface Comment : CharacterData +{ } /++ @@ -1066,30 +1101,34 @@ interface Comment : CharacterData { + nodes, specified in the schemas associated with the document. The type is a + pair of a namespace URI and name properties, and depends on the document's schema. +/ -interface XMLTypeInfo { - @property DOMString typeName(); - @property DOMString typeNamespace(); +interface XMLTypeInfo +{ + @property string typeName(); + @property string typeNamespace(); - bool isDerivedFrom(DOMString typeNamespaceArg, DOMString typeNameArg, DerivationMethod derivationMethod); + bool isDerivedFrom(string typeNamespaceArg, string typeNameArg, + DerivationMethod derivationMethod); } /// DOMError is an interface that describes an error. -interface DOMError { +interface DOMError +{ @property ErrorSeverity severity(); - @property DOMString message(); - @property DOMString type(); + @property string message(); + @property string type(); @property Object relatedException(); @property Object relatedData(); @property DOMLocator location(); } /// `DOMLocator` is an interface that describes a location (e.g. where an error occurred). -interface DOMLocator { +interface DOMLocator +{ @property long lineNumber(); @property long columnNumber(); @property long byteOffset(); @property Node relatedNode(); - @property DOMString uri(); + @property string uri(); } /++ @@ -1099,11 +1138,12 @@ interface DOMLocator { + the `CDATASection` nodes with `Text` nodes or specifying the type of the schema + that must be used when the validation of the `Document` is requested. +/ -interface DOMConfiguration { +interface DOMConfiguration +{ void setParameter(string name, UserData value) @trusted; UserData getParameter(string name) @trusted; bool canSetParameter(string name, UserData value) @trusted; - @property DOMStringList parameterNames(); + @property stringList parameterNames(); } /++ @@ -1117,7 +1157,8 @@ interface DOMConfiguration { + the `Text` interface. Adjacent `CDATASection` nodes are not merged by use of the + normalize method of the `Node` interface. +/ -interface CDATASection : Text { +interface CDATASection : Text +{ } /++ @@ -1129,23 +1170,29 @@ interface CDATASection : Text { + + DOM Level 3 doesn't support editing `DocumentType` nodes. `DocumentType` nodes are read-only. +/ -interface DocumentType : Node { +interface DocumentType : Node +{ /// The name of DTD; i.e., the name immediately following the `DOCTYPE` keyword. - @property DOMString name(); + @property string name(); + /++ + A `NamedNodeMap` containing the general entities, both external and internal, + declared in the DTD. Parameter entities are not contained. Duplicates are discarded. +/ @property NamedNodeMap entities(); + /++ + A `NamedNodeMap` containing the notations declared in the DTD. Duplicates are discarded. + Every node in this map also implements the `Notation` interface. +/ @property NamedNodeMap notations(); + /// The public identifier of the external subset. - @property DOMString publicId(); + @property string publicId(); + /// The system identifier of the external subset. This may be an absolute URI or not. - @property DOMString systemId(); + @property string systemId(); + /++ + The internal subset as a string, or `null` if there is none. + This is does not contain the delimiting square brackets. @@ -1155,7 +1202,7 @@ interface DocumentType : Node { + to the implementation. This may vary depending on various parameters, + including the XML processor used to build the document. +/ - @property DOMString internalSubset(); + @property string internalSubset(); } /++ @@ -1168,14 +1215,16 @@ interface DocumentType : Node { + + A `Notation` node does not have any parent. +/ -interface Notation : Node { +interface Notation : Node +{ /// The public identifier of this notation. If the public identifier was not specified, this is `null`. - @property DOMString publicId(); + @property string publicId(); + /++ + The system identifier of this notation. If the system identifier was not + specified, this is `null`. This may be an absolute URI or not. +/ - @property DOMString systemId(); + @property string systemId(); } /++ @@ -1198,33 +1247,39 @@ interface Notation : Node { + + An `Entity` node does not have any parent. +/ -interface Entity : Node { +interface Entity : Node +{ /// The public identifier associated with the entity if specified, and `null` otherwise. - @property DOMString publicId(); + @property string publicId(); + /++ + The system identifier associated with the entity if specified, and `null` otherwise. + This may be an absolute URI or not. +/ - @property DOMString systemId(); + @property string systemId(); + /// For unparsed entities, the name of the `Notation` for the entity. For parsed entities, this is `null`. - @property DOMString notationName(); + @property string notationName(); + /++ + An attribute specifying the encoding used for this entity at the time of + parsing, when it is an external parsed entity. This is `null` if it an + entity from the internal subset or if it is not known. +/ - @property DOMString inputEncoding(); + @property string inputEncoding(); + /++ + An attribute specifying, as part of the text declaration, the encoding of + this entity, when it is an external parsed entity. This is `null` otherwise. +/ - @property DOMString xmlEncoding(); + @property string xmlEncoding(); + /++ + An attribute specifying, as part of the text declaration, the version + number of this entity, when it is an external parsed entity. This is + `null` otherwise. +/ - @property DOMString xmlVersion(); + @property string xmlVersion(); } /++ @@ -1234,24 +1289,28 @@ interface Entity : Node { + + As for `Entity` nodes, `EntityReference` nodes and all their descendants are readonly. +/ -interface EntityReference : Node { +interface EntityReference : Node +{ } /++ + The `ProcessingInstruction` interface represents a "processing instruction", + used in XML as a way to keep processor-specific information in the text of the document. +/ -interface ProcessingInstruction : Node { +interface ProcessingInstruction : Node +{ /++ + The target of this processing instruction. XML defines this as being the + first token following the markup that begins the processing instruction. +/ - @property DOMString target(); + @property string target(); + /++ + The content of this processing instruction. This is from the first non white + space character after the target to the character immediately preceding the `?>`. +/ - @property DOMString data(); + @property string data(); + /// ditto - @property void data(DOMString); + @property void data(string); } diff --git a/source/newxml/domimpl.d b/source/newxml/domimpl.d index e9c40ef..1c30627 100644 --- a/source/newxml/domimpl.d +++ b/source/newxml/domimpl.d @@ -11,6 +11,7 @@ + Authors: + Lodovico Giaretta + László Szerémi ++ Robert Schadek + + License: + Boost License 1.0. @@ -22,7 +23,6 @@ module newxml.domimpl; import newxml.interfaces; -import newxml.domstring; import dom = newxml.dom; @@ -30,7 +30,6 @@ import std.range.primitives; import std.string; import std.typecons : rebindable, Flag, BitFlags; - /++ + An implementation of $(LINK2 ../dom/DOMImplementation, `newxml.dom.DOMImplementation`). + @@ -42,13 +41,14 @@ import std.typecons : rebindable, Flag, BitFlags; class DOMImplementation : dom.DOMImplementation { @safe: - //mixin UsesAllocator!(Alloc, true); - this() @nogc @safe pure nothrow { + this() @nogc @safe pure nothrow + { } - void enforce(Ex,T)(T cond, dom.ExceptionCode ec) { - if(!cond) + void enforce(Ex, T)(T cond, dom.ExceptionCode ec) + { + if (!cond) { throw new Ex(ec); } @@ -60,7 +60,7 @@ class DOMImplementation : dom.DOMImplementation + Implementation of $(LINK2 ../dom/DOMImplementation.createDocumentType, + `newxml.dom.DOMImplementation.createDocumentType`). +/ - DocumentType createDocumentType(DOMString qualifiedName, DOMString publicId, DOMString systemId) + DocumentType createDocumentType(string qualifiedName, string publicId, string systemId) { DocumentType res = new DocumentType(); res._name = qualifiedName; @@ -72,11 +72,12 @@ class DOMImplementation : dom.DOMImplementation + Implementation of $(LINK2 ../dom/DOMImplementation.createDocument, + `newxml.dom.DOMImplementation.createDocument`). +/ - Document createDocument(DOMString namespaceURI, DOMString qualifiedName, dom.DocumentType _doctype) + Document createDocument(string namespaceURI, string qualifiedName, + dom.DocumentType _doctype) { - DocumentType doctype = cast(DocumentType)_doctype; - enforce!DOMException(!(_doctype && !doctype), - dom.ExceptionCode.wrongDocument); + DocumentType doctype = cast(DocumentType) _doctype; + enforce!DOMException(!(_doctype && !doctype), dom.ExceptionCode + .wrongDocument); Document doc = new Document(); doc._ownerDocument = doc; @@ -104,7 +105,8 @@ class DOMImplementation : dom.DOMImplementation +/ bool hasFeature(string feature, string version_) { - import std.uni: sicmp; + import std.uni : sicmp; + return (!sicmp(feature, "Core") || !sicmp(feature, "XML")) && (version_ == "1.0" || version_ == "2.0" || version_ == "3.0"); } @@ -117,9 +119,7 @@ class DOMImplementation : dom.DOMImplementation +/ DOMImplementation getFeature(string feature, string version_) { - return hasFeature(feature, version_) - ? this - : null; + return hasFeature(feature, version_) ? this : null; } } @@ -127,46 +127,64 @@ class DOMImplementation : dom.DOMImplementation + The implementation of $(LINK2 ../dom/DOMException, `newxml.dom.DOMException`) + thrown by this DOM implementation. +/ - class DOMException: dom.DOMException + class DOMException : dom.DOMException { /// Constructs a `DOMException` with a specific `dom.ExceptionCode`. - pure nothrow @nogc @safe this(dom.ExceptionCode code, string file = __FILE__, size_t line = __LINE__, - Throwable nextInChain = null) + pure nothrow @nogc @safe this(dom.ExceptionCode code, + string file = __FILE__, + size_t line = __LINE__, Throwable nextInChain = null) { - _code = code; + this._code = code; super("", file, line); } - @nogc @safe pure nothrow this(string msg, Throwable nextInChain, string file = __FILE__, size_t line = __LINE__) + + @nogc @safe pure nothrow this(string msg, Throwable nextInChain, + string file = __FILE__, size_t line = __LINE__) { super(msg, file, line, nextInChain); } /// Implementation of $(LINK2 ../dom/DOMException.code, `newxml.dom.DOMException.code`). override @property dom.ExceptionCode code() { - return _code; + return this._code; } + private dom.ExceptionCode _code; } /// Implementation of $(LINK2 ../dom/Node, `newxml.dom.Node`) abstract class Node : dom.Node { - package this() { + package this() + { } + override { /// Implementation of $(LINK2 ../dom/Node.ownerDocument, `newxml.dom.Node.ownerDocument`). - @property Document ownerDocument() { return _ownerDocument; } + @property Document ownerDocument() + { + return this._ownerDocument; + } /// Implementation of $(LINK2 ../dom/Node.parentNode, `newxml.dom.Node.parentNode`). - @property Node parentNode() { return _parentNode; } + @property Node parentNode() + { + return this._parentNode; + } /++ + Implementation of $(LINK2 ../dom/Node.previousSibling, + `newxml.dom.Node.previousSibling`). +/ - @property Node previousSibling() { return _previousSibling; } + @property Node previousSibling() + { + return this._previousSibling; + } /// Implementation of $(LINK2 ../dom/Node.nextSibling, `newxml.dom.Node.nextSibling`). - @property Node nextSibling() { return _nextSibling; } + @property Node nextSibling() + { + return this._nextSibling; + } /++ + Implementation of $(LINK2 ../dom/Node.isSameNode, `newxml.dom.Node.isSameNode`). @@ -180,15 +198,15 @@ class DOMImplementation : dom.DOMImplementation /// Implementation of $(LINK2 ../dom/Node.isEqualNode, `newxml.dom.Node.isEqualNode`). bool isEqualNode(dom.Node other) { - import std.meta: AliasSeq; + import std.meta : AliasSeq; - if (!other || nodeType != other.nodeType) + if (!other || this.nodeType != other.nodeType) { return false; } - foreach (field; AliasSeq!("nodeName", "localName" - , "namespaceURI", "prefix", "nodeValue")) + foreach (field; AliasSeq!("nodeName", "localName", + "namespaceURI", "prefix", "nodeValue")) { mixin("auto a = " ~ field ~ ";\n"); mixin("auto b = other." ~ field ~ ";\n"); @@ -199,7 +217,7 @@ class DOMImplementation : dom.DOMImplementation } } - auto thisWithChildren = cast(NodeWithChildren)this; + auto thisWithChildren = cast(NodeWithChildren) this; if (thisWithChildren) { auto otherChild = other.firstChild; @@ -221,21 +239,22 @@ class DOMImplementation : dom.DOMImplementation } /// Implementation of $(LINK2 ../dom/Node.setUserData, `newxml.dom.Node.setUserData`). - dom.UserData setUserData(string key, dom.UserData data, dom.UserDataHandler handler) @trusted + dom.UserData setUserData(string key, dom.UserData data, + dom.UserDataHandler handler) @trusted { - userData[key] = data; + this.userData[key] = data; if (handler) { - userDataHandlers[key] = handler; + this.userDataHandlers[key] = handler; } return data; } /// Implementation of $(LINK2 ../dom/Node.getUserData, `newxml.dom.Node.getUserData`). dom.UserData getUserData(string key) const @trusted { - if (key in userData) + if (key in this.userData) { - return userData[key]; + return this.userData[key]; } return dom.UserData(null); } @@ -249,8 +268,7 @@ class DOMImplementation : dom.DOMImplementation bool isSupported(string feature, string version_) { return (feature == "Core" || feature == "XML") - && (version_ == "1.0" || version_ == "2.0" - || version_ == "3.0"); + && (version_ == "1.0" || version_ == "2.0" || version_ == "3.0"); } /++ + Implementation of $(LINK2 ../dom/Node.getFeature, `newxml.dom.Node.getFeature`). @@ -260,9 +278,7 @@ class DOMImplementation : dom.DOMImplementation +/ Node getFeature(string feature, string version_) { - return isSupported(feature, version_) - ? this - : null; + return isSupported(feature, version_) ? this : null; } /++ @@ -271,9 +287,10 @@ class DOMImplementation : dom.DOMImplementation +/ BitFlags!(dom.DocumentPosition) compareDocumentPosition(dom.Node _other) @trusted { - enum Ret(dom.DocumentPosition flag) = cast(BitFlags!(dom.DocumentPosition)) flag; + enum Ret(dom.DocumentPosition flag) = cast(BitFlags!(dom + .DocumentPosition)) flag; - Node other = cast(Node)_other; + Node other = cast(Node) _other; if (!other) { return Ret!(dom.DocumentPosition.disconnected); @@ -286,8 +303,8 @@ class DOMImplementation : dom.DOMImplementation Node node1 = other; Node node2 = this; - Attr attr1 = cast(Attr)node1; - Attr attr2 = cast(Attr)node2; + Attr attr1 = cast(Attr) node1; + Attr attr2 = cast(Attr) node2; if (attr1 && attr1.ownerElement) { @@ -299,17 +316,18 @@ class DOMImplementation : dom.DOMImplementation node2 = attr2.ownerElement; if (attr1 && node2 is node1) { - foreach (attr; (cast(Element)node2).attributes) with (dom.DocumentPosition) - { - if (attr is attr1) + foreach (attr; (cast(Element) node2).attributes) + with (dom.DocumentPosition) { - return Ret!implementationSpecific | Ret!preceding; + if (attr is attr1) + { + return Ret!implementationSpecific | Ret!preceding; + } + else if (attr is attr2) + { + return Ret!implementationSpecific | Ret!following; + } } - else if (attr is attr2) - { - return Ret!implementationSpecific | Ret!following; - } - } } } void rootAndDepth(ref Node node, out int depth) @@ -320,22 +338,23 @@ class DOMImplementation : dom.DOMImplementation depth++; } } + Node root1 = node1, root2 = node2; int depth1, depth2; rootAndDepth(root1, depth1); rootAndDepth(root2, depth2); - if (root1 !is root2) with (dom.DocumentPosition) - { - return (cast(void*)root1 < cast(void*)root2) - ? Ret!disconnected | Ret!implementationSpecific | Ret!preceding - : Ret!disconnected | Ret!implementationSpecific | Ret!following; - } + if (root1 !is root2) + with (dom.DocumentPosition) + { + return (cast(void*) root1 < cast(void*) root2) ? Ret!disconnected | Ret!implementationSpecific | Ret!preceding : Ret!disconnected | Ret!implementationSpecific | Ret!following; + } bool swapped = depth1 < depth2; if (swapped) { - import std.algorithm: swap; + import std.algorithm : swap; + swap(depth1, depth2); swap(node1, node2); swapped = true; @@ -346,14 +365,14 @@ class DOMImplementation : dom.DOMImplementation node1 = node1.parentNode; } - if (node1 is node2) with (dom.DocumentPosition) - { - return swapped - ? Ret!contains | Ret!preceding - : Ret!containedBy | Ret!following; - } + if (node1 is node2) + with (dom.DocumentPosition) + { + return swapped ? Ret!contains | Ret!preceding + : Ret!containedBy | Ret!following; + } - while(true) + while (true) { if (node1.parentNode is node2.parentNode) { @@ -384,28 +403,28 @@ class DOMImplementation : dom.DOMImplementation // internal methods Element parentElement() { - auto parent = parentNode; + auto parent = this.parentNode; while (parent && parent.nodeType != dom.NodeType.element) { parent = parent.parentNode; } - return cast(Element)parent; + return cast(Element) parent; } + void performClone(Node dest, bool deep) @trusted { - foreach (data; userDataHandlers.byKeyValue) + foreach (data; this.userDataHandlers.byKeyValue) { auto value = data.value; // putting data.value directly in the following line causes an error; should investigate further - value(dom.UserDataOperation.nodeCloned - , new DOMString(data.key), userData[data.key], this - , dest); + value(dom.UserDataOperation.nodeCloned, data.key, + userData[data.key], this, dest); } } } // method that must be overridden // just because otherwise it doesn't work [bugzilla 16318] - abstract override DOMString nodeName(); + abstract override string nodeName(); // methods specialized in NodeWithChildren override { @@ -419,8 +438,16 @@ class DOMImplementation : dom.DOMImplementation } return emptyList; } - @property Node firstChild() { return null; } - @property Node lastChild() { return null; } + + @property Node firstChild() + { + return null; + } + + @property Node lastChild() + { + return null; + } Node insertBefore(dom.Node _newChild, dom.Node _refChild) { @@ -442,42 +469,81 @@ class DOMImplementation : dom.DOMImplementation throw new DOMException(dom.ExceptionCode.hierarchyRequest); } - bool hasChildNodes() const { return false; } + bool hasChildNodes() const + { + return false; + } } // methods specialized in Element override { - @property Element.Map attributes() { return null; } - bool hasAttributes() { return false; } + @property Element.Map attributes() + { + return null; + } + + bool hasAttributes() + { + return false; + } } // methods specialized in various subclasses override { - @property DOMString nodeValue() { return null; } - @property void nodeValue(DOMString) {} - @property DOMString textContent() { return null; } - @property void textContent(DOMString) {} - @property DOMString baseURI() + @property string nodeValue() + { + return null; + } + + @property void nodeValue(string) { - return parentNode - ? parentNode.baseURI - : null; } - Node cloneNode(bool deep) { return null; } + @property string textContent() + { + return null; + } + + @property void textContent(string) + { + } + + @property string baseURI() + { + return parentNode ? parentNode.baseURI : null; + } + + Node cloneNode(bool deep) + { + return null; + } } // methods specialized in Element and Attribute override { - @property DOMString localName() { return null; } - @property DOMString prefix() { return null; } - @property void prefix(DOMString) { } - @property DOMString namespaceURI() { return null; } + @property string localName() + { + return null; + } + + @property string prefix() + { + return null; + } + + @property void prefix(string) + { + } + + @property string namespaceURI() + { + return null; + } } // methods specialized in Document, Element and Attribute override { - DOMString lookupPrefix(DOMString namespaceURI) + string lookupPrefix(string namespaceURI) { if (!namespaceURI) { @@ -486,76 +552,75 @@ class DOMImplementation : dom.DOMImplementation switch (nodeType) with (dom.NodeType) { - case entity: - case notation: - case documentFragment: - case documentType: - return null; - case attribute: - Attr attr = cast(Attr)this; - return attr.ownerElement - ? attr.ownerElement.lookupNamespacePrefix(namespaceURI, attr.ownerElement) - : null; - default: - auto parentElement = parentElement(); - return parentElement - ? parentElement.lookupNamespacePrefix(namespaceURI, parentElement) - : null; + case entity: + case notation: + case documentFragment: + case documentType: + return null; + case attribute: + Attr attr = cast(Attr) this; + return attr.ownerElement + ? attr.ownerElement.lookupNamespacePrefix(namespaceURI, + attr.ownerElement) : null; + default: + auto parentElement = this.parentElement(); + return parentElement ? parentElement.lookupNamespacePrefix( + namespaceURI, parentElement) : null; } } - DOMString lookupNamespaceURI(DOMString prefix) + + string lookupNamespaceURI(string prefix) { switch (nodeType) with (dom.NodeType) { - case entity: - case notation: - case documentType: - case documentFragment: - return null; - case attribute: - auto attr = cast(Attr)this; - return attr.ownerElement - ? attr.ownerElement.lookupNamespaceURI(prefix) - : null; - default: - auto parentElement = parentElement(); - return parentElement - ? parentElement.lookupNamespaceURI(prefix) - : null; + case entity: + case notation: + case documentType: + case documentFragment: + return null; + case attribute: + auto attr = cast(Attr) this; + return attr.ownerElement ? attr.ownerElement + .lookupNamespaceURI(prefix) : null; + default: + auto parentElement = this.parentElement(); + return parentElement ? parentElement.lookupNamespaceURI(prefix) : null; } } - bool isDefaultNamespace(DOMString namespaceURI) + + bool isDefaultNamespace(string namespaceURI) { switch (nodeType) with (dom.NodeType) { - case entity: - case notation: - case documentType: - case documentFragment: - return false; - case attribute: - auto attr = cast(Attr)this; - return attr.ownerElement - ? attr.ownerElement.isDefaultNamespace(namespaceURI) - : false; - default: - auto parentElement = parentElement(); - return parentElement - ? parentElement.isDefaultNamespace(namespaceURI) - : false; + case entity: + case notation: + case documentType: + case documentFragment: + return false; + case attribute: + auto attr = cast(Attr) this; + return attr.ownerElement + ? attr.ownerElement.isDefaultNamespace( + namespaceURI) : false; + default: + auto parentElement = parentElement(); + return parentElement ? parentElement.isDefaultNamespace(namespaceURI) : false; } } } // TODO methods override { - void normalize() {} + void normalize() + { + } } // inner class for use in NodeWithChildren class ChildList : dom.NodeList { private Node currentChild; - package this() { + package this() + { } // methods specific to NodeList @@ -570,6 +635,7 @@ class DOMImplementation : dom.DOMImplementation } return result; } + @property size_t length() { auto child = rebindable(this.outer.firstChild); @@ -582,18 +648,35 @@ class DOMImplementation : dom.DOMImplementation return result; } } + // more idiomatic methods auto opIndex(size_t i) { - return item(i); + return this.item(i); } // range interface - auto front() { return currentChild; } - void popFront() { currentChild = currentChild.nextSibling; } - bool empty() { return currentChild is null; } + auto front() + { + return this.currentChild; + } + + void popFront() + { + this.currentChild = this.currentChild.nextSibling; + } + + bool empty() + { + return this.currentChild is null; + } } + // method not required by the spec, specialized in NodeWithChildren - bool isAncestor(Node other) { return false; } + bool isAncestor(Node other) + { + return false; + } + /++ + `true` if and only if this node is _readonly. + @@ -606,13 +689,19 @@ class DOMImplementation : dom.DOMImplementation + are always readonly. +/ // method not required by the spec, specialized in varous subclasses - @property bool readonly() { return _readonly; } + @property bool readonly() + { + return this._readonly; + } } + private abstract class NodeWithChildren : Node { - package this() { + package this() + { } + override { @property ChildList childNodes() @@ -621,43 +710,49 @@ class DOMImplementation : dom.DOMImplementation res.currentChild = firstChild; return res; } + @property Node firstChild() { - return _firstChild; + return this._firstChild; } + @property Node lastChild() { - return _lastChild; + return this._lastChild; } Node insertBefore(dom.Node _newChild, dom.Node _refChild) { - enforce!DOMException(!readonly, dom.ExceptionCode.noModificationAllowed); + enforce!DOMException(!readonly, dom.ExceptionCode + .noModificationAllowed); if (!_refChild) { return appendChild(_newChild); } - Node newChild = cast(Node)_newChild; - Node refChild = cast(Node)_refChild; + Node newChild = cast(Node) _newChild; + Node refChild = cast(Node) _refChild; enforce!DOMException(!(!newChild || !refChild - || newChild.ownerDocument !is ownerDocument) - , dom.ExceptionCode.wrongDocument); - enforce!DOMException(!(this is newChild || newChild.isAncestor(this) - || newChild is refChild) - , dom.ExceptionCode.hierarchyRequest); - enforce!DOMException(!(refChild.parentNode !is this) - , dom.ExceptionCode.notFound); - enforce!DOMException(!(this is newChild || newChild.isAncestor(this) || newChild is refChild) - , dom.ExceptionCode.hierarchyRequest); - enforce!DOMException(!(refChild.parentNode !is this) - , dom.ExceptionCode.notFound); + || newChild.ownerDocument !is ownerDocument), + dom.ExceptionCode.wrongDocument); + enforce!DOMException(!(this is newChild + || newChild.isAncestor(this) + || newChild is refChild), dom.ExceptionCode + .hierarchyRequest); + enforce!DOMException(!(refChild.parentNode !is this), dom + .ExceptionCode.notFound); + enforce!DOMException(!(this is newChild + || newChild.isAncestor(this) + || newChild is refChild), dom.ExceptionCode + .hierarchyRequest); + enforce!DOMException(!(refChild.parentNode !is this), dom + .ExceptionCode.notFound); if (newChild.nodeType == dom.NodeType.documentFragment) { - for (auto child = rebindable(newChild); child !is null - ; child = child.nextSibling) + for (auto child = rebindable(newChild); child !is null; child = child + .nextSibling) { insertBefore(child, refChild); } @@ -683,40 +778,44 @@ class DOMImplementation : dom.DOMImplementation if (firstChild is refChild) { - _firstChild = newChild; + this._firstChild = newChild; } return newChild; } + Node replaceChild(dom.Node newChild, dom.Node oldChild) { - insertBefore(newChild, oldChild); - return removeChild(oldChild); + this.insertBefore(newChild, oldChild); + return this.removeChild(oldChild); } + Node removeChild(dom.Node _oldChild) { - enforce!DOMException(!readonly, dom.ExceptionCode.noModificationAllowed); - Node oldChild = cast(Node)_oldChild; + enforce!DOMException(!this.readonly, dom.ExceptionCode + .noModificationAllowed); + Node oldChild = cast(Node) _oldChild; enforce!DOMException(!(!oldChild - || oldChild.parentNode !is this) - , dom.ExceptionCode.noModificationAllowed); + || oldChild.parentNode !is this), + dom.ExceptionCode.noModificationAllowed); if (oldChild is firstChild) { - _firstChild = oldChild.nextSibling; + this._firstChild = oldChild.nextSibling; } else { oldChild.previousSibling._nextSibling = oldChild.nextSibling; } - if (oldChild is lastChild) + if (oldChild is this.lastChild) { _lastChild = oldChild.previousSibling; } else { - oldChild.nextSibling._previousSibling = oldChild.previousSibling; + oldChild.nextSibling._previousSibling = oldChild + .previousSibling; } oldChild._parentNode = null; @@ -724,14 +823,18 @@ class DOMImplementation : dom.DOMImplementation oldChild._nextSibling = null; return oldChild; } + Node appendChild(dom.Node _newChild) { - enforce!DOMException(!readonly, dom.ExceptionCode.noModificationAllowed); - Node newChild = cast(Node)_newChild; - enforce!DOMException(!(!newChild || newChild.ownerDocument !is ownerDocument) - , dom.ExceptionCode.wrongDocument); - enforce!DOMException(!(this is newChild || newChild.isAncestor(this)) - , dom.ExceptionCode.hierarchyRequest); + enforce!DOMException(!readonly, dom.ExceptionCode + .noModificationAllowed); + Node newChild = cast(Node) _newChild; + enforce!DOMException(!(!newChild + || newChild.ownerDocument !is ownerDocument), + dom.ExceptionCode.wrongDocument); + enforce!DOMException(!(this is newChild + || newChild.isAncestor(this)), + dom.ExceptionCode.hierarchyRequest); if (newChild.parentNode !is null) { newChild.parentNode.removeChild(newChild); @@ -739,8 +842,8 @@ class DOMImplementation : dom.DOMImplementation if (newChild.nodeType == dom.NodeType.documentFragment) { - for (auto node = rebindable(newChild.firstChild) - ; node !is null; node = node.nextSibling) + for (auto node = rebindable(newChild.firstChild); node !is null; + node = node.nextSibling) { appendChild(node); } @@ -748,25 +851,28 @@ class DOMImplementation : dom.DOMImplementation } newChild._parentNode = this; - if (lastChild) + if (this.lastChild) { newChild._previousSibling = lastChild; - lastChild._nextSibling = newChild; + this.lastChild._nextSibling = newChild; } else { - _firstChild = newChild; + this._firstChild = newChild; } - _lastChild = newChild; + this._lastChild = newChild; return newChild; } + bool hasChildNodes() const { - return _firstChild !is null; + return this._firstChild !is null; } + bool isAncestor(Node other) { - for (auto child = rebindable(firstChild); child !is null; child = child.nextSibling) + for (auto child = rebindable(this.firstChild); child !is null; child = child + .nextSibling) { if (child is other) { @@ -781,44 +887,48 @@ class DOMImplementation : dom.DOMImplementation return false; } - @property DOMString textContent() + @property string textContent() { - //import newxml.appender; - DOMString result; - //auto result = Appender!(typeof(this.textContent()[0]), typeof(*allocator))(allocator); - for (auto child = rebindable(firstChild); child !is null; child = child.nextSibling) + string result; + for (auto child = rebindable(this.firstChild); child !is null; child = child + .nextSibling) { - if (child.nodeType != dom.NodeType.comment && - child.nodeType != dom.NodeType.processingInstruction) + if (child.nodeType != dom.NodeType.comment + && child.nodeType != dom.NodeType + .processingInstruction) { - result ~= child.textContent();//result.put(child.textContent); + result ~= child.textContent(); //result.put(child.textContent); } } return result; } - @property void textContent(DOMString newVal) + + @property void textContent(string newVal) { - enforce!DOMException(readonly - , dom.ExceptionCode.noModificationAllowed); + enforce!DOMException(this.readonly, dom.ExceptionCode + .noModificationAllowed); - while (firstChild) + while (this.firstChild) { - removeChild(firstChild); + this.removeChild(this.firstChild); } - _firstChild = _lastChild = ownerDocument.createTextNode(newVal); + this._lastChild = ownerDocument.createTextNode(newVal); + this._firstChild = this._lastChild; } } private { - Node _firstChild, _lastChild; + Node _firstChild; + Node _lastChild; void performClone(NodeWithChildren dest, bool deep) { super.performClone(dest, deep); + if (deep) { - foreach (child; childNodes) + foreach (child; this.childNodes) { auto childClone = child.cloneNode(true); dest.appendChild(childClone); @@ -830,78 +940,106 @@ class DOMImplementation : dom.DOMImplementation /// Implementation of $(LINK2 ../dom/DocumentFragment, `newxml.dom.DocumentFragment`) class DocumentFragment : NodeWithChildren, dom.DocumentFragment { - package this() { + package this() + { } // inherited from Node override { - @property dom.NodeType nodeType() { return dom.NodeType.documentFragment; } - @property DOMString nodeName() { return new DOMString("#document-fragment"w); } + @property dom.NodeType nodeType() + { + return dom.NodeType.documentFragment; + } + + @property string nodeName() + { + return "#document-fragment"; + } } } /// Implementation of $(LINK2 ../dom/Document, `newxml.dom.Document`) class Document : NodeWithChildren, dom.Document { - package this() { + package this() + { } + package @property void doctype(DocumentType _doctype) { this._doctype = _doctype; } + // specific to Document override { - @property DocumentType doctype() { return _doctype; } - @property DOMImplementation implementation() { return this.outer; } - @property Element documentElement() { return _root; } + @property DocumentType doctype() + { + return this._doctype; + } - Element createElement(DOMString tagName) + @property DOMImplementation implementation() + { + return this.outer; + } + + @property Element documentElement() + { + return this._root; + } + + Element createElement(string tagName) { Element res = new Element(); res._name = tagName; res._ownerDocument = this; - res._attrs = res.createMap();//new Element.Map(); + res._attrs = res.createMap(); return res; } - Element createElementNS(DOMString namespaceURI, DOMString qualifiedName) + + Element createElementNS(string namespaceURI, string qualifiedName) { Element res = new Element(); res.setQualifiedName(qualifiedName); res._namespaceURI = namespaceURI; res._ownerDocument = this; - res._attrs = res.createMap();//res._attrs = new Element.Map(); + res._attrs = res.createMap(); return res; } + DocumentFragment createDocumentFragment() { DocumentFragment res = new DocumentFragment(); res._ownerDocument = this; return res; } - Text createTextNode(DOMString data) + + Text createTextNode(string data) { Text res = new Text(); res._data = data; res._ownerDocument = this; return res; } - Comment createComment(DOMString data) + + Comment createComment(string data) { Comment res = new Comment(); res._data = data; res._ownerDocument = this; return res; } - CDATASection createCDATASection(DOMString data) + + CDATASection createCDATASection(string data) { CDATASection res = new CDATASection(); res._data = data; res._ownerDocument = this; return res; } - ProcessingInstruction createProcessingInstruction(DOMString target, DOMString data) + + ProcessingInstruction createProcessingInstruction(string target, string data) { ProcessingInstruction res = new ProcessingInstruction(); res._target = target; @@ -909,14 +1047,16 @@ class DOMImplementation : dom.DOMImplementation res._ownerDocument = this; return res; } - Attr createAttribute(DOMString name) + + Attr createAttribute(string name) { Attr res = new Attr(); res._name = name; res._ownerDocument = this; return res; } - Attr createAttributeNS(DOMString namespaceURI, DOMString qualifiedName) + + Attr createAttributeNS(string namespaceURI, string qualifiedName) { Attr res = new Attr(); res.setQualifiedName(qualifiedName); @@ -924,9 +1064,13 @@ class DOMImplementation : dom.DOMImplementation res._ownerDocument = this; return res; } - EntityReference createEntityReference(DOMString name) { return null; } - ElementsByTagName getElementsByTagName(DOMString tagname) + EntityReference createEntityReference(string name) + { + return null; + } + + ElementsByTagName getElementsByTagName(string tagname) { ElementsByTagName res = new ElementsByTagName(); res.root = this; @@ -934,7 +1078,8 @@ class DOMImplementation : dom.DOMImplementation res.current = res.item(0); return res; } - ElementsByTagNameNS getElementsByTagNameNS(DOMString namespaceURI, DOMString localName) + + ElementsByTagNameNS getElementsByTagNameNS(string namespaceURI, string localName) { ElementsByTagNameNS res = new ElementsByTagNameNS(); res.root = this; @@ -943,16 +1088,18 @@ class DOMImplementation : dom.DOMImplementation res.current = res.item(0); return res; } - Element getElementById(DOMString elementId) + + Element getElementById(string elementId) { Element find(dom.Node node) @safe { - if (node.nodeType == dom.NodeType.element && node.hasAttributes) + if (node.nodeType == dom.NodeType.element && node + .hasAttributes) { foreach (attr; node.attributes) { - if ((cast(Attr)attr).isId && attr.nodeValue == elementId) - return cast(Element)node; + if ((cast(Attr) attr).isId && attr.nodeValue == elementId) + return cast(Element) node; } } foreach (child; node.childNodes) @@ -965,6 +1112,7 @@ class DOMImplementation : dom.DOMImplementation } return null; } + return find(_root); } @@ -972,70 +1120,91 @@ class DOMImplementation : dom.DOMImplementation { switch (node.nodeType) with (dom.NodeType) { - case attribute: - Attr result; - result = node.prefix - ? createAttributeNS(node.namespaceURI, node.nodeName) - : createAttribute(node.nodeName); + case attribute: + Attr result; + result = node.prefix ? createAttributeNS(node.namespaceURI, + node.nodeName) : createAttribute(node.nodeName); + auto children = node.childNodes; + foreach (i; 0 .. children.length) + { + result.appendChild(importNode(children.item(i), true)); + } + return result; + case documentFragment: + auto result = createDocumentFragment(); + if (deep) + { auto children = node.childNodes; - foreach (i; 0..children.length) + foreach (i; 0 .. children.length) { - result.appendChild(importNode(children.item(i), true)); - } - return result; - case documentFragment: - auto result = createDocumentFragment(); - if (deep) - { - auto children = node.childNodes; - foreach (i; 0..children.length) - { - result.appendChild(importNode(children.item(i), deep)); - } + result.appendChild(importNode(children.item(i), deep)); } - return result; - case element: - Element result; - result = node.prefix - ? createElementNS(node.namespaceURI, node.nodeName) - : createElement(node.nodeName); + } + return result; + case element: + Element result; + result = node.prefix ? createElementNS(node.namespaceURI, + node.nodeName) : createElement(node.nodeName); - if (node.hasAttributes) + if (node.hasAttributes) + { + auto attributes = node.attributes; + foreach (i; 0 .. attributes.length) { - auto attributes = node.attributes; - foreach (i; 0..attributes.length) - { - auto attr = cast(Attr)(importNode(attributes.item(i), deep)); - assert(attr); - result.setAttributeNode(attr); - } + auto attr = cast(Attr)(importNode(attributes.item(i), deep)); + assert(attr); + result.setAttributeNode(attr); } - if (deep) + } + if (deep) + { + auto children = node.childNodes; + foreach (i; 0 .. children.length) { - auto children = node.childNodes; - foreach (i; 0..children.length) - { - result.appendChild(importNode(children.item(i), true)); - } + result.appendChild(importNode(children.item(i), true)); } - return result; - case processingInstruction: - return createProcessingInstruction(node.nodeName, node.nodeValue); - default: - throw new DOMException(dom.ExceptionCode.notSupported); + } + return result; + case processingInstruction: + return createProcessingInstruction(node.nodeName, node + .nodeValue); + default: + throw new DOMException(dom.ExceptionCode.notSupported); } } - Node adoptNode(dom.Node source) { return null; } - @property DOMString inputEncoding() { return null; } - @property DOMString xmlEncoding() { return null; } + Node adoptNode(dom.Node source) + { + return null; + } + + @property string inputEncoding() + { + return null; + } - @property bool xmlStandalone() { return _standalone; } - @property void xmlStandalone(bool b) { _standalone = b; } + @property string xmlEncoding() + { + return null; + } - @property DOMString xmlVersion() { return _xmlVersion; } - @property void xmlVersion(DOMString ver) + @property bool xmlStandalone() + { + return _standalone; + } + + @property void xmlStandalone(bool b) + { + _standalone = b; + } + + @property string xmlVersion() + { + return _xmlVersion; + } + + @property void xmlVersion(string ver) { if (ver == "1.0" || ver == "1.1") { @@ -1047,58 +1216,91 @@ class DOMImplementation : dom.DOMImplementation } } - @property bool strictErrorChecking() { return _strictErrorChecking; } - @property void strictErrorChecking(bool b) { _strictErrorChecking = b; } + @property bool strictErrorChecking() + { + return _strictErrorChecking; + } + + @property void strictErrorChecking(bool b) + { + _strictErrorChecking = b; + } + + @property string documentURI() + { + return _documentURI; + } + + @property void documentURI(string uri) + { + _documentURI = uri; + } - @property DOMString documentURI() { return _documentURI; } - @property void documentURI(DOMString uri) { _documentURI = uri; } + @property DOMConfiguration domConfig() + { + return _config; + } - @property DOMConfiguration domConfig() { return _config; } - void normalizeDocument() { } - Node renameNode(dom.Node n, DOMString namespaceURI, DOMString qualifiedName) + void normalizeDocument() { - auto node = cast(Node)n; - enforce!DOMException(!(!node || node.ownerDocument !is this) - , dom.ExceptionCode.wrongDocument); + } + + Node renameNode(dom.Node n, string namespaceURI, string qualifiedName) + { + auto node = cast(Node) n; + enforce!DOMException(!(!node || node.ownerDocument !is this), + dom.ExceptionCode.wrongDocument); auto type = node.nodeType; enforce!DOMException(!(type != dom.NodeType.element - && type != dom.NodeType.attribute) - , dom.ExceptionCode.notSupported); + && type != dom.NodeType.attribute), + dom.ExceptionCode.notSupported); - auto withNs = (cast(NodeWithNamespace)node); + auto withNs = (cast(NodeWithNamespace) node); withNs.setQualifiedName(qualifiedName); withNs._namespaceURI = namespaceURI; return node; } } + private { - DOMString _documentURI, _xmlVersion = new DOMString("1.0"w); + string _documentURI, _xmlVersion = "1.0"; DocumentType _doctype; Element _root; DOMConfiguration _config; bool _strictErrorChecking = true, _standalone = false; } + // inherited from Node override { - @property dom.NodeType nodeType() { return dom.NodeType.document; } - @property DOMString nodeName() { return new DOMString("#document"w); } + @property dom.NodeType nodeType() + { + return dom.NodeType.document; + } - DOMString lookupPrefix(DOMString namespaceURI) + @property string nodeName() + { + return "#document"; + } + + string lookupPrefix(string namespaceURI) { return documentElement.lookupPrefix(namespaceURI); } - DOMString lookupNamespaceURI(DOMString prefix) + + string lookupNamespaceURI(string prefix) { return documentElement.lookupNamespaceURI(prefix); } - bool isDefaultNamespace(DOMString namespaceURI) + + bool isDefaultNamespace(string namespaceURI) { return documentElement.isDefaultNamespace(namespaceURI); } } + // inherited from NodeWithChildren override { @@ -1106,24 +1308,25 @@ class DOMImplementation : dom.DOMImplementation { if (newChild.nodeType == dom.NodeType.element) { - enforce!DOMException(!_root - , dom.ExceptionCode.hierarchyRequest); + enforce!DOMException(!_root, dom.ExceptionCode + .hierarchyRequest); auto res = super.insertBefore(newChild, refChild); - _root = cast(Element)newChild; + _root = cast(Element) newChild; return res; } else if (newChild.nodeType == dom.NodeType.documentType) { - enforce!DOMException(!_doctype - , dom.ExceptionCode.hierarchyRequest); + enforce!DOMException(!_doctype, dom.ExceptionCode + .hierarchyRequest); auto res = super.insertBefore(newChild, refChild); - _doctype = cast(DocumentType)newChild; + _doctype = cast(DocumentType) newChild; return res; } - else if (newChild.nodeType != dom.NodeType.comment && - newChild.nodeType != dom.NodeType.processingInstruction) + else if (newChild.nodeType != dom.NodeType.comment + && newChild.nodeType != dom.NodeType + .processingInstruction) { throw new DOMException(dom.ExceptionCode.hierarchyRequest); } @@ -1132,28 +1335,30 @@ class DOMImplementation : dom.DOMImplementation return super.insertBefore(newChild, refChild); } } + Node replaceChild(dom.Node newChild, dom.Node oldChild) { if (newChild.nodeType == dom.NodeType.element) { - enforce!DOMException(!(oldChild !is _root) - , dom.ExceptionCode.hierarchyRequest); + enforce!DOMException(!(oldChild !is _root), dom.ExceptionCode + .hierarchyRequest); auto res = super.replaceChild(newChild, oldChild); - _root = cast(Element)newChild; + _root = cast(Element) newChild; return res; } else if (newChild.nodeType == dom.NodeType.documentType) { - enforce!DOMException(!(oldChild !is _doctype) - , dom.ExceptionCode.hierarchyRequest); + enforce!DOMException(!(oldChild !is _doctype), + dom.ExceptionCode.hierarchyRequest); auto res = super.replaceChild(newChild, oldChild); - _doctype = cast(DocumentType)newChild; + _doctype = cast(DocumentType) newChild; return res; } - else if (newChild.nodeType != dom.NodeType.comment && - newChild.nodeType != dom.NodeType.processingInstruction) + else if (newChild.nodeType != dom.NodeType.comment + && newChild.nodeType != dom.NodeType + .processingInstruction) { throw new DOMException(dom.ExceptionCode.hierarchyRequest); } @@ -1162,6 +1367,7 @@ class DOMImplementation : dom.DOMImplementation return super.replaceChild(newChild, oldChild); } } + Node removeChild(dom.Node oldChild) { if (oldChild.nodeType == dom.NodeType.element) @@ -1181,24 +1387,25 @@ class DOMImplementation : dom.DOMImplementation return super.removeChild(oldChild); } } + Node appendChild(dom.Node newChild) { if (newChild.nodeType == dom.NodeType.element) { - enforce!DOMException(!(_root) - , dom.ExceptionCode.hierarchyRequest); + enforce!DOMException(!(_root), dom.ExceptionCode + .hierarchyRequest); auto res = super.appendChild(newChild); - _root = cast(Element)newChild; + this._root = cast(Element) newChild; return res; } else if (newChild.nodeType == dom.NodeType.documentType) { - enforce!DOMException(!(_doctype) - , dom.ExceptionCode.hierarchyRequest); + enforce!DOMException(!(_doctype), dom.ExceptionCode + .hierarchyRequest); auto res = super.appendChild(newChild); - _doctype = cast(DocumentType)newChild; + this._doctype = cast(DocumentType) newChild; return res; } else @@ -1208,22 +1415,27 @@ class DOMImplementation : dom.DOMImplementation } } } + alias ElementsByTagName = ElementsByTagNameImpl!false; alias ElementsByTagNameNS = ElementsByTagNameImpl!true; + static class ElementsByTagNameImpl(bool ns) : dom.NodeList { - package this() { + package this() + { } + private Node root; private Element current; static if (ns) { - private DOMString namespaceURI, localName; + private string namespaceURI; + private string localName; } else { - private DOMString tagname; + private string tagname; } private bool check(Node node) @@ -1232,7 +1444,7 @@ class DOMImplementation : dom.DOMImplementation { if (node.nodeType == dom.NodeType.element) { - Element elem = cast(Element)node; + Element elem = cast(Element) node; return elem.namespaceURI == namespaceURI && elem.localName == localName; } else @@ -1248,32 +1460,17 @@ class DOMImplementation : dom.DOMImplementation private Element findNext(Node node) { - if (node.firstChild) - { - auto item = node.firstChild; - if (check(item)) - { - return cast(Element)item; - } - else - { - return findNext(item); - } - } - else - { - return findNextBack(node); - } + return node.firstChild ? check(node.firstChild) + ? cast(Element) node.firstChild + : findNext(node.firstChild) : findNextBack(node); } + private Element findNextBack(Node node) { if (node.nextSibling) { - auto item = node.nextSibling; - - return check(item) - ? cast(Element)item - : findNext(item); + return check(node.nextSibling) ? cast(Element) node + .nextSibling : findNext(node.nextSibling); } else if (node.parentNode && node.parentNode !is node.ownerDocument) { @@ -1300,6 +1497,7 @@ class DOMImplementation : dom.DOMImplementation } return res; } + Element item(size_t i) { auto res = findNext(root); @@ -1312,110 +1510,145 @@ class DOMImplementation : dom.DOMImplementation } } // more idiomatic methods - auto opIndex(size_t i) { return item(i); } + auto opIndex(size_t i) + { + return item(i); + } // range interface - bool empty() { return current is null; } - void popFront() { current = findNext(current); } - auto front() { return current; } + bool empty() + { + return current is null; + } + + void popFront() + { + current = findNext(current); + } + + auto front() + { + return current; + } } + /// Implementation of $(LINK2 ../dom/CharacterData, `newxml.dom.CharacterData`) abstract class CharacterData : Node, dom.CharacterData { // specific to CharacterData override { - @property DOMString data() { return _data; } - @property void data(DOMString newVal) { _data = newVal; } - @property size_t length() { return _data.length; } + @property string data() + { + return this._data; + } - DOMString substringData(size_t offset, size_t count) + @property void data(string newVal) { - enforce!DOMException(!(offset > length) - , dom.ExceptionCode.indexSize); + this._data = newVal; + } - import std.algorithm : min; - return _data[offset..min(offset + count, length)]; + @property size_t length() + { + return this._data.length; } - void appendData(DOMString arg) + + string substringData(size_t offset, size_t count) { - import std.traits : Unqual; + enforce!DOMException(!(offset > length), dom.ExceptionCode + .indexSize); - //auto newData = allocator.makeArray!(Unqual!(typeof(_data[0])))(_data.length + arg.length); + import std.algorithm.comparison : min; - _data ~= arg; + return this._data[offset .. min(offset + count, length)]; } - void insertData(size_t offset, DOMString arg) - { - import std.traits : Unqual; - enforce!DOMException(!(offset > length) - , dom.ExceptionCode.indexSize); + void appendData(string arg) + { + this._data ~= arg; + } - //auto newData = allocator.makeArray!(Unqual!(typeof(_data[0])))(_data.length + arg.length); - /+CharType[] newData; - newData.reserve = _data.length + arg.length; - newData = _data[0 .. offset]; - newData[offset .. (offset + arg.length)] = arg; - newData[(offset + arg.length) .. $] = _data[offset .. $]; + void insertData(size_t offset, string arg) + { + enforce!DOMException(!(offset > length), dom.ExceptionCode + .indexSize); - _data = cast(typeof(_data))newData;+/ - _data.insertData(offset, arg); + this._data = this._data[0 .. offset] ~ arg ~ this._data[offset .. $]; } + void deleteData(size_t offset, size_t count) { - _data.deleteData(offset, count); + this._data = this._data[0 .. offset] ~ this._data[offset + count .. $]; } - void replaceData(size_t offset, size_t count, DOMString arg) + + void replaceData(size_t offset, size_t count, string arg) { - _data.deleteData(offset, count); - _data.insertData(offset, arg); + this._data = this._data[0 .. offset] ~ this._data[offset + count .. $]; + //this._data.deleteData(offset, count); + this._data = this._data[0 .. offset] ~ arg ~ this._data[offset .. $]; + //this._data.insertData(offset, arg); } } + // inherited from Node override { - @property DOMString nodeValue() { return data; } - @property void nodeValue(DOMString newVal) + @property string nodeValue() + { + return this.data; + } + + @property void nodeValue(string newVal) + { + enforce!DOMException(!this.readonly, dom.ExceptionCode + .noModificationAllowed); + this.data = newVal; + } + + @property string textContent() { - enforce!DOMException(!(readonly) - , dom.ExceptionCode.noModificationAllowed); - data = newVal; + return this.data; } - @property DOMString textContent() { return data; } - @property void textContent(DOMString newVal) + + @property void textContent(string newVal) { - enforce!DOMException(!(readonly) - , dom.ExceptionCode.noModificationAllowed); - data = newVal; + enforce!DOMException(!this.readonly, dom.ExceptionCode + .noModificationAllowed); + this.data = newVal; } } private { - DOMString _data; + string _data; // internal method void performClone(CharacterData dest, bool deep) { super.performClone(dest, deep); - dest._data = _data; + dest._data = this._data; } } } + private abstract class NodeWithNamespace : NodeWithChildren { private { - DOMString _name, _namespaceURI; + string _name; + string _namespaceURI; size_t _colon; - void setQualifiedName(DOMString name) + void setQualifiedName(string name) { - _name = name; - ptrdiff_t i = name.getDString.indexOf(':'); + this._name = name; + ptrdiff_t i = name.indexOf(':'); + if (i > 0) + { _colon = i; + } } + void performClone(NodeWithNamespace dest, bool deep) { super.performClone(dest, deep); @@ -1427,212 +1660,245 @@ class DOMImplementation : dom.DOMImplementation // inherited from Node override { - @property DOMString nodeName() { return _name; } - - @property DOMString localName() + @property string nodeName() { - return !_colon - ? null - : _name[(_colon+1)..$]; + return this._name; } - @property DOMString prefix() + + @property string localName() { - return _name[0.._colon]; + return !this._colon ? null : _name[this._colon + 1 .. $]; } - @property void prefix(DOMString pre) + + @property string prefix() { - enforce!DOMException(!(readonly) - , dom.ExceptionCode.noModificationAllowed); + return this._name[0 .. this._colon]; + } - import std.traits : Unqual; + @property void prefix(string pre) + { + enforce!DOMException(!readonly, dom.ExceptionCode + .noModificationAllowed); _name ~= pre; _name ~= "w"; _name ~= localName; _colon = pre.length; } - @property DOMString namespaceURI() { return _namespaceURI; } + + @property string namespaceURI() + { + return this._namespaceURI; + } } } + /// Implementation of $(LINK2 ../dom/Attr, `newxml.dom.Attr`) class Attr : NodeWithNamespace, dom.Attr { - package this() { - //_namespaceURI = new DOMString(); - } // specific to Attr override { /// Implementation of $(LINK2 ../dom/Attr.name, `newxml.dom.Attr.name`). - @property DOMString name() { return _name; } + @property string name() + { + return _name; + } /// Implementation of $(LINK2 ../dom/Attr.specified, `newxml.dom.Attr.specified`). - @property bool specified() { return _specified; } + @property bool specified() + { + return _specified; + } /// Implementation of $(LINK2 ../dom/Attr.value, `newxml.dom.Attr.value`). - @property DOMString value() + + @property string value() { - //auto result = Appender!(typeof(_name[0]), typeof(*allocator))(allocator); - /* DOMString result = new DOMString(); - auto child = rebindable(firstChild); - while (child) - { - result ~= child.textContent;//result.put(child.textContent); - child = child.nextSibling; - } - return result; */ - Text result = cast(Text)firstChild; + Text result = cast(Text) firstChild; return result.textContent(); } /// ditto - @property void value(DOMString newVal) + @property void value(string newVal) { - while (firstChild) + while (this.firstChild) { - removeChild(firstChild); + this.removeChild(this.firstChild); } - appendChild(ownerDocument.createTextNode(newVal)); + this.appendChild(this.ownerDocument.createTextNode(newVal)); } /// Implementation of $(LINK2 ../dom/Attr.ownerElement, `newxml.dom.Attr.ownerElement`). - @property Element ownerElement() { return _ownerElement; } + @property Element ownerElement() + { + return this._ownerElement; + } /// Implementation of $(LINK2 ../dom/Attr.schemaTypeInfo, `newxml.dom.Attr.schemaTypeInfo`). - @property dom.XMLTypeInfo schemaTypeInfo() { return null; } + @property dom.XMLTypeInfo schemaTypeInfo() + { + return null; + } /// Implementation of $(LINK2 ../dom/Attr.isId, `newxml.dom.Attr.isId`). - @property bool isId() { return _isId; } + @property bool isId() + { + return this._isId; + } } private { Element _ownerElement; - bool _specified = true, _isId = false; - @property Attr _nextAttr() { return cast(Attr)_nextSibling; } - @property Attr _previousAttr() { return cast(Attr)_previousSibling; } + bool _specified = true; + bool _isId = false; + + @property Attr _nextAttr() + { + return cast(Attr) this._nextSibling; + } + + @property Attr _previousAttr() + { + return cast(Attr) this._previousSibling; + } } + // inherited from Node override { - @property dom.NodeType nodeType() { return dom.NodeType.attribute; } + @property dom.NodeType nodeType() + { + return dom.NodeType.attribute; + } - @property DOMString nodeValue() { return value; } - @property void nodeValue(DOMString newVal) + @property string nodeValue() { - enforce!DOMException(!(readonly) - , dom.ExceptionCode.noModificationAllowed); + return value; + } + + @property void nodeValue(string newVal) + { + enforce!DOMException(!(readonly), dom.ExceptionCode + .noModificationAllowed); value = newVal; } // overridden because we reuse _nextSibling and _previousSibling with another meaning - @property Attr nextSibling() { return null; } - @property Attr previousSibling() { return null; } + @property Attr nextSibling() + { + return null; + } + + @property Attr previousSibling() + { + return null; + } Attr cloneNode(bool deep) { Attr cloned = new Attr(); - cloned._ownerDocument = _ownerDocument; + cloned._ownerDocument = this._ownerDocument; super.performClone(cloned, true); cloned._specified = true; return cloned; } - DOMString lookupPrefix(DOMString namespaceURI) + string lookupPrefix(string namespaceURI) { - return ownerElement - ? ownerElement.lookupPrefix(namespaceURI) - : null; + return ownerElement ? ownerElement.lookupPrefix(namespaceURI) : null; } - DOMString lookupNamespaceURI(DOMString prefix) + + string lookupNamespaceURI(string prefix) { - return ownerElement - ? ownerElement.lookupNamespaceURI(prefix) - : null; + return ownerElement ? ownerElement.lookupNamespaceURI(prefix) : null; } - bool isDefaultNamespace(DOMString namespaceURI) + + bool isDefaultNamespace(string namespaceURI) { - return ownerElement - ? ownerElement.isDefaultNamespace(namespaceURI) - : false; + return ownerElement ? ownerElement.isDefaultNamespace(namespaceURI) : false; } } } /// Implementation of $(LINK2 ../dom/Element, `newxml.dom.Element`) class Element : NodeWithNamespace, dom.Element { - package this() { + package this() + { } ///Created as a workaround to a common D compiler bug/artifact. - package Map createMap() { + package Map createMap() + { return new Map(); } // specific to Element override { /// Implementation of $(LINK2 ../dom/Element.tagName, `newxml.dom.Element.tagName`). - @property DOMString tagName() { return _name; } + @property string tagName() + { + return _name; + } /++ + Implementation of $(LINK2 ../dom/Element.getAttribute, + `newxml.dom.Element.getAttribute`). +/ - DOMString getAttribute(DOMString name) + string getAttribute(string name) { auto result = _attrs.getNamedItem(name); - if (result) - { - return result.value; - } - else - { - return null; - } + return result ? result.value : null; } + /++ + Implementation of $(LINK2 ../dom/Element.setAttribute, + `newxml.dom.Element.setAttribute`). +/ - void setAttribute(DOMString name, DOMString value) + void setAttribute(string name, string value) { auto attr = ownerDocument.createAttribute(name); attr.nodeValue = value; attr._ownerElement = this; - _attrs.setNamedItem(attr); + this._attrs.setNamedItem(attr); } + /++ + Implementation of $(LINK2 ../dom/Element.removeAttribute, + `newxml.dom.Element.removeAttribute`). +/ - void removeAttribute(DOMString name) + void removeAttribute(string name) { - _attrs.removeNamedItem(name); + this._attrs.removeNamedItem(name); } /++ + Implementation of $(LINK2 ../dom/Element.getAttributeNode, + `newxml.dom.Element.getAttributeNode`). +/ - Attr getAttributeNode(DOMString name) + Attr getAttributeNode(string name) { - return _attrs.getNamedItem(name); + return this._attrs.getNamedItem(name); } + /++ + Implementation of $(LINK2 ../dom/Element.setAttributeNode, + `newxml.dom.Element.setAttributeNode`). +/ Attr setAttributeNode(dom.Attr newAttr) { - return _attrs.setNamedItem(newAttr); + return this._attrs.setNamedItem(newAttr); } + /++ + Implementation of $(LINK2 ../dom/Element.removeAttributeNode, + `newxml.dom.Element.removeAttributeNode`). +/ Attr removeAttributeNode(dom.Attr oldAttr) { - if (_attrs.getNamedItemNS(oldAttr.namespaceURI, oldAttr.name) is oldAttr) + if (this._attrs.getNamedItemNS(oldAttr.namespaceURI, oldAttr.name) is oldAttr) { - return _attrs.removeNamedItemNS(oldAttr.namespaceURI, oldAttr.name); + return this._attrs.removeNamedItemNS(oldAttr.namespaceURI, oldAttr + .name); } - else if (_attrs.getNamedItem(oldAttr.name) is oldAttr) + else if (this._attrs.getNamedItem(oldAttr.name) is oldAttr) { - return _attrs.removeNamedItem(oldAttr.name); + return this._attrs.removeNamedItem(oldAttr.name); } throw new DOMException(dom.ExceptionCode.notFound); @@ -1642,48 +1908,44 @@ class DOMImplementation : dom.DOMImplementation + Implementation of $(LINK2 ../dom/Element.getAttributeNS, + `newxml.dom.Element.getAttributeNS`). +/ - DOMString getAttributeNS(DOMString namespaceURI, DOMString localName) + string getAttributeNS(string namespaceURI, string localName) { - auto result = _attrs.getNamedItemNS(namespaceURI, localName); - if (result) - { - return result.value; - } - else - { - return null; - } + auto result = this._attrs.getNamedItemNS(namespaceURI, localName); + return result ? result.value : null; } /++ + Implementation of $(LINK2 ../dom/Element.setAttributeNS, + `newxml.dom.Element.setAttributeNS`). +/ - void setAttributeNS(DOMString namespaceURI, DOMString qualifiedName, DOMString value) + void setAttributeNS(string namespaceURI, string qualifiedName, string value) { + import std.exception : enforce; + auto attr = ownerDocument.createAttributeNS(namespaceURI, qualifiedName); attr.nodeValue = value; attr._ownerElement = this; - assert(attr.nodeValue == value); - _attrs.setNamedItemNS(attr); - assert(_attrs.getNamedItemNS(namespaceURI, qualifiedName)); - assert(_attrs.getNamedItemNS(namespaceURI, qualifiedName).nodeValue); + enforce(attr.nodeValue == value); + this._attrs.setNamedItemNS(attr); + enforce(this._attrs.getNamedItemNS(namespaceURI, qualifiedName)); + enforce(this._attrs.getNamedItemNS(namespaceURI, qualifiedName) + .nodeValue); } /++ + Implementation of $(LINK2 ../dom/Element.removeAttributeNS, + `newxml.dom.Element.removeAttributeNS`). +/ - void removeAttributeNS(DOMString namespaceURI, DOMString localName) + void removeAttributeNS(string namespaceURI, string localName) { - _attrs.removeNamedItemNS(namespaceURI, localName); + this._attrs.removeNamedItemNS(namespaceURI, localName); } /++ + Implementation of $(LINK2 ../dom/Element.getAttributeNodeNS, + `newxml.dom.Element.getAttributeNodeNS`). +/ - Attr getAttributeNodeNS(DOMString namespaceURI, DOMString localName) + Attr getAttributeNodeNS(string namespaceURI, string localName) { - return _attrs.getNamedItemNS(namespaceURI, localName); + return this._attrs.getNamedItemNS(namespaceURI, localName); } /++ + Implementation of $(LINK2 ../dom/Element.setAttributeNodeNS, @@ -1691,58 +1953,48 @@ class DOMImplementation : dom.DOMImplementation +/ Attr setAttributeNodeNS(dom.Attr newAttr) { - return _attrs.setNamedItemNS(newAttr); + return this._attrs.setNamedItemNS(newAttr); } /++ + Implementation of $(LINK2 ../dom/Element.hasAttribute, + `newxml.dom.Element.hasAttribute`). +/ - bool hasAttribute(DOMString name) + bool hasAttribute(string name) { - return _attrs.getNamedItem(name) !is null; + return this._attrs.getNamedItem(name) !is null; } /++ + Implementation of $(LINK2 ../dom/Element.hasAttributeNS, + `newxml.dom.Element.hasAttributeNS`). +/ - bool hasAttributeNS(DOMString namespaceURI, DOMString localName) + bool hasAttributeNS(string namespaceURI, string localName) { - return _attrs.getNamedItemNS(namespaceURI, localName) !is null; + return this._attrs.getNamedItemNS(namespaceURI, localName) !is null; } /++ + Implementation of $(LINK2 ../dom/Element.setIdAttribute, + `newxml.dom.Element.setIdAttribute`). +/ - void setIdAttribute(DOMString name, bool isId) + void setIdAttribute(string name, bool isId) { auto attr = _attrs.getNamedItem(name); - if (attr) - { - attr._isId = isId; - } - else - { - throw new DOMException(dom.ExceptionCode.notFound); - } + enforce!(DOMException)(attr !is null, dom.ExceptionCode.notFound); + attr._isId = isId; } + /++ + Implementation of $(LINK2 ../dom/Element.setIdAttributeNS, + `newxml.dom.Element.setIdAttributeNS`). +/ - void setIdAttributeNS(DOMString namespaceURI, DOMString localName, bool isId) + void setIdAttributeNS(string namespaceURI, string localName, bool isId) { auto attr = _attrs.getNamedItemNS(namespaceURI, localName); - if (attr) - { - attr._isId = isId; - } - else - { - throw new DOMException(dom.ExceptionCode.notFound); - } + enforce!(DOMException)(attr !is null, dom.ExceptionCode.notFound); + attr._isId = isId; } + /++ + Implementation of $(LINK2 ../dom/Element.getAttribute, + `newxml.dom.Element.getAttribute`). @@ -1751,11 +2003,11 @@ class DOMImplementation : dom.DOMImplementation { if (_attrs.getNamedItemNS(idAttr.namespaceURI, idAttr.name) is idAttr) { - (cast(Attr)idAttr)._isId = isId; + (cast(Attr) idAttr)._isId = isId; } else if (_attrs.getNamedItem(idAttr.name) is idAttr) { - (cast(Attr)idAttr)._isId = isId; + (cast(Attr) idAttr)._isId = isId; } else { @@ -1767,7 +2019,7 @@ class DOMImplementation : dom.DOMImplementation + Implementation of $(LINK2 ../dom/Element.getElementsByTagName, + `newxml.dom.Element.getElementsByTagName`). +/ - ElementsByTagName getElementsByTagName(DOMString tagname) + ElementsByTagName getElementsByTagName(string tagname) { ElementsByTagName res = new ElementsByTagName(); res.root = this; @@ -1775,11 +2027,12 @@ class DOMImplementation : dom.DOMImplementation res.current = res.item(0); return res; } + /++ + Implementation of $(LINK2 ../dom/Element.getElementsByTagNameNS, + `newxml.dom.Element.getElementsByTagNameNS`). +/ - ElementsByTagNameNS getElementsByTagNameNS(DOMString namespaceURI, DOMString localName) + ElementsByTagNameNS getElementsByTagNameNS(string namespaceURI, string localName) { ElementsByTagNameNS res = new ElementsByTagNameNS(); res.root = this; @@ -1793,45 +2046,60 @@ class DOMImplementation : dom.DOMImplementation + Implementation of $(LINK2 ../dom/Element.schemaTypeInfo, + `newxml.dom.Element.schemaTypeInfo`). +/ - @property dom.XMLTypeInfo schemaTypeInfo() { return null; } + @property dom.XMLTypeInfo schemaTypeInfo() + { + return null; + } } private { Map _attrs; // internal methods - DOMString lookupNamespacePrefix(DOMString namespaceURI, Element originalElement) + string lookupNamespacePrefix(string namespaceURI, Element originalElement) { if (this.namespaceURI && this.namespaceURI == namespaceURI - && this.prefix && originalElement.lookupNamespaceURI(this.prefix) == namespaceURI) + && this.prefix && originalElement.lookupNamespaceURI( + this.prefix) == namespaceURI) { return this.prefix; } - if (hasAttributes) + if (this.hasAttributes) { foreach (attr; attributes) { - if (attr.prefix == "xmlns" && attr.nodeValue == namespaceURI && - originalElement.lookupNamespaceURI(attr.localName) == namespaceURI) + if (attr.prefix == "xmlns" + && attr.nodeValue == namespaceURI + && originalElement.lookupNamespaceURI( + attr.localName) == namespaceURI) { return attr.localName; } } } auto parentElement = parentElement(); - return parentElement - ? parentElement.lookupNamespacePrefix(namespaceURI, originalElement) - : null; + return parentElement ? parentElement.lookupNamespacePrefix( + namespaceURI, originalElement) : null; } } // inherited from Node override { - @property dom.NodeType nodeType() { return dom.NodeType.element; } + @property dom.NodeType nodeType() + { + return dom.NodeType.element; + } + + @property Map attributes() + { + return this._attrs.length > 0 ? _attrs : null; + } - @property Map attributes() { return _attrs.length > 0 ? _attrs : null; } - bool hasAttributes() { return _attrs.length > 0; } + bool hasAttributes() + { + return this._attrs.length > 0; + } Element cloneNode(bool deep) { @@ -1842,20 +2110,21 @@ class DOMImplementation : dom.DOMImplementation return cloned; } - DOMString lookupPrefix(DOMString namespaceURI) + string lookupPrefix(string namespaceURI) { return lookupNamespacePrefix(namespaceURI, this); } - DOMString lookupNamespaceURI(DOMString prefix) + + string lookupNamespaceURI(string prefix) { if (namespaceURI && prefix == prefix) { return namespaceURI; } - if (hasAttributes) + if (this.hasAttributes) { - foreach (attr; attributes) + foreach (attr; this.attributes) { if (attr.prefix == "xmlns" && attr.localName == prefix) { @@ -1867,14 +2136,12 @@ class DOMImplementation : dom.DOMImplementation } } } - auto parentElement = parentElement(); - if (parentElement) - { - return parentElement.lookupNamespaceURI(prefix); - } - return null; + + auto parentElement = this.parentElement(); + return parentElement ? parentElement.lookupNamespaceURI(prefix) : null; } - bool isDefaultNamespace(DOMString namespaceURI) + + bool isDefaultNamespace(string namespaceURI) { if (!prefix) { @@ -1891,16 +2158,16 @@ class DOMImplementation : dom.DOMImplementation } } } + auto parentElement = parentElement(); - return parentElement - ? parentElement.isDefaultNamespace(namespaceURI) - : false; + return parentElement ? parentElement.isDefaultNamespace(namespaceURI) : false; } } class Map : dom.NamedNodeMap { - package this() { + package this() + { } // specific to NamedNodeMap @@ -1909,7 +2176,7 @@ class DOMImplementation : dom.DOMImplementation size_t length() { size_t res = 0; - auto attr = firstAttr; + auto attr = this.firstAttr; while (attr) { res++; @@ -1917,10 +2184,11 @@ class DOMImplementation : dom.DOMImplementation } return res; } + Attr item(size_t index) { size_t count = 0; - auto res = firstAttr; + auto res = this.firstAttr; while (res && count < index) { count++; @@ -1929,23 +2197,25 @@ class DOMImplementation : dom.DOMImplementation return res; } - Attr getNamedItem(DOMString name) + Attr getNamedItem(string name) { - Attr res = firstAttr; + Attr res = this.firstAttr; while (res && res.nodeName != name) { res = res._nextAttr; } return res; } + Attr setNamedItem(dom.Node arg) { - enforce!DOMException(!(arg.ownerDocument !is this.outer.ownerDocument) - , dom.ExceptionCode.wrongDocument); + enforce!DOMException( + arg.ownerDocument is this.outer.ownerDocument, + dom.ExceptionCode.wrongDocument); - Attr attr = cast(Attr)arg; - enforce!DOMException(!(!attr) - , dom.ExceptionCode.hierarchyRequest); + Attr attr = cast(Attr) arg; + enforce!DOMException(attr !is null, dom.ExceptionCode + .hierarchyRequest); if (attr._previousAttr) { @@ -1957,7 +2227,7 @@ class DOMImplementation : dom.DOMImplementation attr._nextAttr._previousSibling = attr._previousAttr; } - auto res = firstAttr; + auto res = this.firstAttr; while (res && res.nodeName != attr.nodeName) { res = res._nextAttr; @@ -1967,21 +2237,25 @@ class DOMImplementation : dom.DOMImplementation { attr._previousSibling = res._previousAttr; attr._nextSibling = res._nextAttr; - if (res is firstAttr) firstAttr = attr; + if (res is firstAttr) + { + firstAttr = attr; + } } else { - attr._nextSibling = firstAttr; - firstAttr = attr; + attr._nextSibling = this.firstAttr; + this.firstAttr = attr; attr._previousSibling = null; - currentAttr = firstAttr; + currentAttr = this.firstAttr; } return res; } - Attr removeNamedItem(DOMString name) + + Attr removeNamedItem(string name) { - auto res = firstAttr; + auto res = this.firstAttr; while (res && res.nodeName != name) { res = res._nextAttr; @@ -2005,25 +2279,26 @@ class DOMImplementation : dom.DOMImplementation } } - Attr getNamedItemNS(DOMString namespaceURI, DOMString localName) + Attr getNamedItemNS(string namespaceURI, string localName) { - Attr res = firstAttr; + Attr res = this.firstAttr; while (res && (res.localName != localName || res.namespaceURI != namespaceURI)) { assert(res.localName != localName || res.namespaceURI != namespaceURI); res = res._nextAttr; } return res; - } + Attr setNamedItemNS(dom.Node arg) { - enforce!DOMException(!(arg.ownerDocument !is this.outer.ownerDocument) - , dom.ExceptionCode.wrongDocument); + enforce!DOMException( + !(arg.ownerDocument !is this.outer.ownerDocument), + dom.ExceptionCode.wrongDocument); - Attr attr = cast(Attr)arg; - enforce!DOMException(!(!attr) - , dom.ExceptionCode.hierarchyRequest); + Attr attr = cast(Attr) arg; + enforce!DOMException(!attr, dom.ExceptionCode + .hierarchyRequest); if (attr._previousAttr) { @@ -2034,9 +2309,9 @@ class DOMImplementation : dom.DOMImplementation attr._nextAttr._previousSibling = attr._previousAttr; } - auto res = firstAttr; + auto res = this.firstAttr; while (res && (res.localName != attr.localName - || res.namespaceURI != attr.namespaceURI)) + || res.namespaceURI != attr.namespaceURI)) { res = res._nextAttr; } @@ -2045,7 +2320,10 @@ class DOMImplementation : dom.DOMImplementation { attr._previousSibling = res._previousAttr; attr._nextSibling = res._nextAttr; - if (res is firstAttr) firstAttr = attr; + if (res is firstAttr) + { + firstAttr = attr; + } } else { @@ -2057,41 +2335,41 @@ class DOMImplementation : dom.DOMImplementation return res; } - Attr removeNamedItemNS(DOMString namespaceURI, DOMString localName) + + Attr removeNamedItemNS(string namespaceURI, string localName) { auto res = firstAttr; - while (res && (res.localName != localName - || res.namespaceURI != namespaceURI)) + while (res && (res.localName != localName || res.namespaceURI != namespaceURI)) { res = res._nextAttr; } - if (res) + enforce!(DOMException)(!res, dom.ExceptionCode.notFound); + + if (res._previousAttr) { - if (res._previousAttr) - { - res._previousAttr._nextSibling = res._nextAttr; - } - if (res._nextAttr) - { - res._nextAttr._previousSibling = res._previousAttr; - } - return res; + res._previousAttr._nextSibling = res._nextAttr; } - else + if (res._nextAttr) { - throw new DOMException(dom.ExceptionCode.notFound); + res._nextAttr._previousSibling = res._previousAttr; } + return res; } } + private { Attr firstAttr; Attr currentAttr; } + // better methods - auto opIndex(size_t i) { return item(i); } - + auto opIndex(size_t i) + { + return item(i); + } + // range interface auto opSlice() { @@ -2099,77 +2377,89 @@ class DOMImplementation : dom.DOMImplementation { Attr currentAttr; - auto front() { return currentAttr; } - void popFront() { currentAttr = currentAttr._nextAttr; } - bool empty() { return currentAttr is null; } + auto front() + { + return this.currentAttr; + } + + void popFront() + { + this.currentAttr = this.currentAttr._nextAttr; + } + + bool empty() + { + return this.currentAttr is null; + } } - return Range(firstAttr); + + return Range(this.firstAttr); } } } /// Implementation of $(LINK2 ../dom/Text, `newxml.dom.Text`) - class Text: CharacterData, dom.Text + class Text : CharacterData, dom.Text { - package this() { - - } // specific to Text override { /// Implementation of $(LINK2 ../dom/Text.splitText, `newxml.dom.Text.splitText`). Text splitText(size_t offset) { - enforce!DOMException(!(offset > data.length) - , dom.ExceptionCode.indexSize); - auto second = ownerDocument.createTextNode(data[offset..$]); - data = data[0..offset]; + enforce!DOMException(!(offset > data.length), dom.ExceptionCode + .indexSize); + auto second = ownerDocument.createTextNode(this.data[offset .. $]); + data = this.data[0 .. offset]; + if (parentNode) { if (nextSibling) { - parentNode.insertBefore(second, nextSibling); + this.parentNode.insertBefore(second, nextSibling); } else { - parentNode.appendChild(second); + this.parentNode.appendChild(second); } } return second; } + /++ + Implementation of $(LINK2 ../dom/Text.isElementContentWhitespace, + `newxml.dom.Text.isElementContentWhitespace`). +/ @property bool isElementContentWhitespace() { - return _data.getDString.indexOfNeither(" \r\n\t") == -1; + return this._data.indexOfNeither(" \r\n\t") == -1; } + /// Implementation of $(LINK2 ../dom/Text.wholeText, `newxml.dom.Text.wholeText`). - @property DOMString wholeText() + @property string wholeText() { Text findPreviousText(Text text) { Node node = text; do { - if (node.previousSibling) - switch (node.previousSibling.nodeType) with (dom.NodeType) - { - case text: - case cdataSection: - return cast(Text) node.previousSibling; - case entityReference: - return cast(Text)(node.previousSibling.lastChild) - ? cast(Text) node.previousSibling.lastChild - : null; - default: - return null; - } + if (node.previousSibling) switch (node.previousSibling + .nodeType) with (dom.NodeType) + { + case text: + case cdataSection: + return cast(Text) node.previousSibling; + case entityReference: + return cast(Text)(node.previousSibling.lastChild) ? cast( + Text) node.previousSibling.lastChild : null; + default: + return null; + } node = node.parentNode; } while (node && node.nodeType == dom.NodeType.entityReference); return null; } + Text findNextText(Text text) { Node node = text; @@ -2179,15 +2469,14 @@ class DOMImplementation : dom.DOMImplementation { switch (node.nextSibling.nodeType) with (dom.NodeType) { - case text: - case cdataSection: - return cast(Text) node.nextSibling; - case entityReference: - return cast(Text)(node.nextSibling.firstChild) - ? cast(Text) node.nextSibling.firstChild - : null; - default: - return null; + case text: + case cdataSection: + return cast(Text) node.nextSibling; + case entityReference: + return cast(Text)(node.nextSibling.firstChild) ? cast( + Text) node.nextSibling.firstChild : null; + default: + return null; } } node = node.parentNode; @@ -2197,10 +2486,10 @@ class DOMImplementation : dom.DOMImplementation return null; } - //import newxml.appender; - DOMString result;//auto result = Appender!(typeof(_data[0]), typeof(*allocator))(allocator); + string result; - Text node, prev = this; + Text node; + Text prev = this; do { node = prev; @@ -2215,12 +2504,13 @@ class DOMImplementation : dom.DOMImplementation } return result; } + /++ + Implementation of $(LINK2 ../dom/Text.replaceWholeText, + `newxml.dom.Text.replaceWholeText`). +/ // the W3C DOM spec explains the details of this - @property Text replaceWholeText(DOMString newText) + @property Text replaceWholeText(string newText) { bool hasOnlyText(Node reference) @safe { @@ -2228,19 +2518,20 @@ class DOMImplementation : dom.DOMImplementation { switch (child.nodeType) with (dom.NodeType) { - case text: - case cdataSection: - break; - case entityReference: - if (!hasOnlyText(reference)) - return false; - break; - default: + case text: + case cdataSection: + break; + case entityReference: + if (!hasOnlyText(reference)) return false; + break; + default: + return false; } } return false; } + bool startsWithText(Node reference) { if (!reference.firstChild) @@ -2250,15 +2541,16 @@ class DOMImplementation : dom.DOMImplementation switch (reference.firstChild.nodeType) with (dom.NodeType) { - case text: - case cdataSection: - return true; - case entityReference: - return startsWithText(reference.firstChild); - default: - return false; + case text: + case cdataSection: + return true; + case entityReference: + return startsWithText(reference.firstChild); + default: + return false; } } + bool endsWithText(Node reference) { if (!reference.lastChild) @@ -2267,39 +2559,42 @@ class DOMImplementation : dom.DOMImplementation } switch (reference.lastChild.nodeType) with (dom.NodeType) { - case text: - case cdataSection: - return true; - case entityReference: - return endsWithText(reference.lastChild); - default: - return false; + case text: + case cdataSection: + return true; + case entityReference: + return endsWithText(reference.lastChild); + default: + return false; } } Node current; - if (parentNode && parentNode.nodeType == dom.NodeType.entityReference) + if (parentNode && parentNode.nodeType == dom.NodeType + .entityReference) { current = parentNode; - while (current.parentNode - && current.parentNode.nodeType == dom.NodeType.entityReference) + while (current.parentNode && current.parentNode.nodeType + == dom.NodeType.entityReference) { current = current.parentNode; } - enforce!DOMException(!(!hasOnlyText(current)) - , dom.ExceptionCode.noModificationAllowed); + enforce!DOMException(!hasOnlyText(current), + dom.ExceptionCode.noModificationAllowed); } - else if (readonly) + else if (this.readonly) { - throw new DOMException(dom.ExceptionCode.noModificationAllowed); + throw new DOMException(dom.ExceptionCode + .noModificationAllowed); } else { current = this; } - size_t previousToKill, nextToKill; + size_t previousToKill; + size_t nextToKill; auto node = current.previousSibling; while (node) { @@ -2307,15 +2602,15 @@ class DOMImplementation : dom.DOMImplementation { if (endsWithText(node)) { - enforce!DOMException(!(!hasOnlyText(node)) - , dom.ExceptionCode.noModificationAllowed); + enforce!DOMException(!(!hasOnlyText(node)), + dom.ExceptionCode.noModificationAllowed); } else { break; } } - else if (!cast(Text)node) + else if (!cast(Text) node) { break; } @@ -2323,6 +2618,7 @@ class DOMImplementation : dom.DOMImplementation previousToKill++; node = node.previousSibling; } + node = current.nextSibling; while (node) { @@ -2330,15 +2626,15 @@ class DOMImplementation : dom.DOMImplementation { if (startsWithText(node)) { - enforce!DOMException(!(!hasOnlyText(node)) - , dom.ExceptionCode.noModificationAllowed); + enforce!DOMException(!(!hasOnlyText(node)), + dom.ExceptionCode.noModificationAllowed); } else { break; } } - else if (!cast(Text)node) + else if (!cast(Text) node) { break; } @@ -2347,12 +2643,12 @@ class DOMImplementation : dom.DOMImplementation node = node.nextSibling; } - foreach (i; 0..previousToKill) + foreach (i; 0 .. previousToKill) { current.parentNode.removeChild(current.previousSibling); } - foreach (i; 0..nextToKill) + foreach (i; 0 .. nextToKill) { current.parentNode.removeChild(current.nextSibling); } @@ -2371,19 +2667,25 @@ class DOMImplementation : dom.DOMImplementation current.removeChild(current); } - return !newText - ? null - : ownerDocument.createTextNode(newText); + return !newText ? null : ownerDocument.createTextNode(newText); } - _data = newText; + + this._data = newText; return this; } } // inherited from Node override { - @property dom.NodeType nodeType() { return dom.NodeType.text; } - @property DOMString nodeName() { return new DOMString("#text"); } + @property dom.NodeType nodeType() + { + return dom.NodeType.text; + } + + @property string nodeName() + { + return "#text"; + } Text cloneNode(bool deep) { @@ -2397,14 +2699,22 @@ class DOMImplementation : dom.DOMImplementation /// Implementation of $(LINK2 ../dom/Comment, `newxml.dom.Comment`) class Comment : CharacterData, dom.Comment { - package this() { + package this() + { } // inherited from Node override { - @property dom.NodeType nodeType() { return dom.NodeType.comment; } - @property DOMString nodeName() { return new DOMString("#comment"); } + @property dom.NodeType nodeType() + { + return dom.NodeType.comment; + } + + @property string nodeName() + { + return "#comment"; + } Comment cloneNode(bool deep) { @@ -2419,84 +2729,122 @@ class DOMImplementation : dom.DOMImplementation class DocumentType : Node, dom.DocumentType { package dom.NamedNodeMap _entities; - package this() { + package this() + { _entities = new NamedNodeMap(); } - package void createEntity(DOMString _name, DOMString content) { + + package void createEntity(string _name, string content) + { _entities.setNamedItem(new Entity(_name, content, null)); } // specific to DocumentType override { /// Implementation of $(LINK2 ../dom/DocumentType.name, `newxml.dom.DocumentType.name`). - @property DOMString name() { return _name; } + @property string name() + { + return this._name; + } /++ + Implementation of $(LINK2 ../dom/DocumentType.entities, + `newxml.dom.DocumentType.entities`). +/ - @property dom.NamedNodeMap entities() { return _entities; } + @property dom.NamedNodeMap entities() + { + return this._entities; + } /++ + Implementation of $(LINK2 ../dom/DocumentType.notations, + `newxml.dom.DocumentType.notations`). +/ - @property dom.NamedNodeMap notations() { return null; } + @property dom.NamedNodeMap notations() + { + return null; + } /++ + Implementation of $(LINK2 ../dom/DocumentType.publicId, + `newxml.dom.DocumentType.publicId`). +/ - @property DOMString publicId() { return _publicId; } + @property string publicId() + { + return this._publicId; + } /++ + Implementation of $(LINK2 ../dom/DocumentType.systemId, + `newxml.dom.DocumentType.systemId`). +/ - @property DOMString systemId() { return _systemId; } + @property string systemId() + { + return this._systemId; + } /++ + Implementation of $(LINK2 ../dom/DocumentType.internalSubset, + `newxml.dom.DocumentType.internalSubset`). +/ - @property DOMString internalSubset() { return _internalSubset; } + @property string internalSubset() + { + return this._internalSubset; + } + } + + private + { + string _name; + string _publicId; + string _systemId; + string _internalSubset; } - private DOMString _name, _publicId, _systemId, _internalSubset; + // inherited from Node override { - @property dom.NodeType nodeType() { return dom.NodeType.documentType; } - @property DOMString nodeName() { return _name; } + @property dom.NodeType nodeType() + { + return dom.NodeType.documentType; + } + + @property string nodeName() + { + return this._name; + } } - class NamedNodeMap : dom.NamedNodeMap + + class NamedNodeMap : dom.NamedNodeMap { package dom.Node[] nodes; - package this() - { - } /++ + Returns the `index`th item in the collection. If `index` is greater than + or equal to the number of nodes in the list, this returns `null`. +/ dom.Node item(size_t index) { - return nodes[index]; + return this.nodes[index]; } + /++ + The number of nodes in the list. The range of valid child node indices is + `0` to `length-1` inclusive. +/ @property size_t length() { - return nodes.length; + return this.nodes.length; } + /// Retrieves a node specified by name. - dom.Node getNamedItem(DOMString name) + dom.Node getNamedItem(string name) { - foreach (dom.Node key; nodes) { - if (key.nodeName == name) + foreach (dom.Node key; this.nodes) + { + if (key.nodeName == name) { return key; } } return null; } + /++ + Adds a node using its `nodeName` attribute. If a node with that name is + already present in this map, it is replaced by the new one. Replacing a @@ -2508,30 +2856,31 @@ class DOMImplementation : dom.DOMImplementation +/ dom.Node setNamedItem(dom.Node arg) { - foreach (size_t i, dom.Node key; nodes) { - if (key.nodeName == name) + foreach (size_t i, dom.Node key; this.nodes) + { + if (key.nodeName == name) { - nodes[i] = arg; - //cast(Node)(key)._parentNode = null; + this.nodes[i] = arg; return arg; } } nodes ~= arg; return arg; } + /++ + Removes a node specified by name. When this map contains the attributes + attached to an element, if the removed attribute is known to have a default + value, an attribute immediately appears containing the default value as + well as the corresponding namespace URI, local name, and prefix when applicable. +/ - dom.Node removeNamedItem(DOMString name) + dom.Node removeNamedItem(string name) { - foreach (size_t i, dom.Node key; nodes) { - if (key.nodeName == name) + foreach (size_t i, dom.Node key; this.nodes) + { + if (key.nodeName == name) { - nodes = nodes[0..i] ~ nodes[i+1..$]; - //cast(Node)(key)._parentNode = null; + this.nodes = this.nodes[0 .. i] ~ this.nodes[i + 1 .. $]; return key; } } @@ -2543,10 +2892,11 @@ class DOMImplementation : dom.DOMImplementation + Per the XML Namespaces specification, applications must use the value `null` + as the `namespaceURI` parameter for methods if they wish to have no namespace. +/ - Node getNamedItemNS(DOMString namespaceURI, DOMString localName) + Node getNamedItemNS(string namespaceURI, string localName) { return null; } + /++ + Adds a node using its `namespaceURI` and `localName`. If a node with that + namespace URI and that local name is already present in this map, it is @@ -2558,6 +2908,7 @@ class DOMImplementation : dom.DOMImplementation { return null; } + /++ + Removes a node specified by local name and namespace URI. A removed attribute + may be known to have a default value when this map contains the attributes attached @@ -2567,7 +2918,7 @@ class DOMImplementation : dom.DOMImplementation + Per the XML Namespaces specification, applications must use the value `null` + as the `namespaceURI` parameter for methods if they wish to have no namespace. +/ - Node removeNamedItemNS(DOMString namespaceURI, DOMString localName) + Node removeNamedItemNS(string namespaceURI, string localName) { return null; } @@ -2576,19 +2927,23 @@ class DOMImplementation : dom.DOMImplementation /// Implementation of $(LINK2 ../dom/CDATASection, `newxml.dom.CDATASection`) class CDATASection : Text, dom.CDATASection { - package this() { - - } // inherited from Node override { - @property dom.NodeType nodeType() { return dom.NodeType.cdataSection; } - @property DOMString nodeName() { return new DOMString("#cdata-section"); } + @property dom.NodeType nodeType() + { + return dom.NodeType.cdataSection; + } + + @property string nodeName() + { + return "#cdata-section"; + } CDATASection cloneNode(bool deep) { CDATASection cloned = new CDATASection(); - cloned._ownerDocument = _ownerDocument; + cloned._ownerDocument = this._ownerDocument; super.performClone(cloned, deep); return cloned; } @@ -2597,9 +2952,6 @@ class DOMImplementation : dom.DOMImplementation /// Implementation of $(LINK2 ../dom/ProcessingInstruction, `newxml.dom.ProcessingInstruction`) class ProcessingInstruction : Node, dom.ProcessingInstruction { - package this() { - - } // specific to ProcessingInstruction override { @@ -2607,44 +2959,71 @@ class DOMImplementation : dom.DOMImplementation + Implementation of $(LINK2 ../dom/ProcessingInstruction.target, + `newxml.dom.ProcessingInstruction.target`). +/ - @property DOMString target() { return _target; } + @property string target() + { + return this._target; + } /++ + Implementation of $(LINK2 ../dom/ProcessingInstruction.data, + `newxml.dom.ProcessingInstruction.data`). +/ - @property DOMString data() { return _data; } + @property string data() + { + return this._data; + } /// ditto - @property void data(DOMString newVal) { _data = newVal; } + @property void data(string newVal) + { + this._data = newVal; + } } - private DOMString _target, _data; + private string _target; + private string _data; + // inherited from Node override { - @property dom.NodeType nodeType() { return dom.NodeType.processingInstruction; } - @property DOMString nodeName() { return target; } - @property DOMString nodeValue() { return _data; } - @property void nodeValue(DOMString newVal) + @property dom.NodeType nodeType() + { + return dom.NodeType.processingInstruction; + } + + @property string nodeName() + { + return target; + } + + @property string nodeValue() + { + return _data; + } + + @property void nodeValue(string newVal) { - enforce!DOMException(!(readonly) - , dom.ExceptionCode.noModificationAllowed); - _data = newVal; + enforce!DOMException(!(readonly), dom.ExceptionCode + .noModificationAllowed); + this._data = newVal; } - @property DOMString textContent() { return _data; } - @property void textContent(DOMString newVal) + @property string textContent() { - enforce!DOMException(!(readonly) - , dom.ExceptionCode.noModificationAllowed); - _data = newVal; + return _data; + } + + @property void textContent(string newVal) + { + enforce!DOMException(!(readonly), dom.ExceptionCode + .noModificationAllowed); + this._data = newVal; } ProcessingInstruction cloneNode(bool deep) { auto cloned = new ProcessingInstruction(); - cloned._ownerDocument = _ownerDocument; + cloned._ownerDocument = this._ownerDocument; super.performClone(cloned, deep); - cloned._target = _target; - cloned._data = _data; + cloned._target = this._target; + cloned._data = this._data; return cloned; } } @@ -2653,108 +3032,129 @@ class DOMImplementation : dom.DOMImplementation ///Currently external (system) entities are not supported. class Entity : Node, dom.Entity { - private DOMString _publicId; - private DOMString _systemId; - private DOMString content; - package this(DOMString _publicId, DOMString content, Document _ownerDocument) + private string _publicId; + private string _systemId; + private string content; + + package this(string publicId, string content, Document ownerDocument) { - this._publicId = _publicId; + this._publicId = publicId; this.content = content; - this._ownerDocument = _ownerDocument; + this._ownerDocument = ownerDocument; } - package this(DOMString _publicId, DOMString _systemId, DOMString content, Document _ownerDocument) + + package this(string publicId, string systemId, string content, Document ownerDocument) { - this._publicId = _publicId; - this._systemId = _systemId; + this._publicId = publicId; + this._systemId = systemId; this.content = content; - this._ownerDocument = _ownerDocument; + this._ownerDocument = ownerDocument; } + /// The text substituted by this entity. - override @property DOMString nodeValue() + override @property string nodeValue() { return content; } + /// The text substituted by this entity. - override @property DOMString textContent() + override @property string textContent() { return content; } - override @property DOMString nodeName() + + override @property string nodeName() { return _publicId; } + override @property dom.NodeType nodeType() { return dom.NodeType.entity; } + /// The public identifier associated with the entity if specified, and `null` otherwise. - @property DOMString publicId() + @property string publicId() { - return _publicId; + return this._publicId; } + /++ + The system identifier associated with the entity if specified, and `null` otherwise. + This may be an absolute URI or not. +/ - @property DOMString systemId() + @property string systemId() { - return _systemId; + return this._systemId; } + /// For unparsed entities, the name of the `Notation` for the entity. For parsed entities, this is `null`. - @property DOMString notationName() + @property string notationName() { return null; } + /++ + An attribute specifying the encoding used for this entity at the time of + parsing, when it is an external parsed entity. This is `null` if it an + entity from the internal subset or if it is not known. +/ - @property DOMString inputEncoding() + @property string inputEncoding() { return null; } + /++ + An attribute specifying, as part of the text declaration, the encoding of + this entity, when it is an external parsed entity. This is `null` otherwise. +/ - @property DOMString xmlEncoding() + @property string xmlEncoding() { return null; } + /++ + An attribute specifying, as part of the text declaration, the version + number of this entity, when it is an external parsed entity. This is + `null` otherwise. +/ - @property DOMString xmlVersion() + @property string xmlVersion() { return null; } } + /// Implementation of $(LINK2 ../dom/EntityReference, `newxml.dom.EntityReference`) class EntityReference : NodeWithChildren, dom.EntityReference { - package this() { - - } // inherited from Node override { - @property dom.NodeType nodeType() { return dom.NodeType.entityReference; } - @property DOMString nodeName() { return _ent_name; } - @property bool readonly() { return true; } + @property dom.NodeType nodeType() + { + return dom.NodeType.entityReference; + } + + @property string nodeName() + { + return this._ent_name; + } + + @property bool readonly() + { + return true; + } } - private DOMString _ent_name; + + private string _ent_name; } + /// Implementation of $(LINK2 ../dom/DOMConfiguration, `newxml.dom.DOMConfiguration`) class DOMConfiguration : dom.DOMConfiguration { import std.meta; import std.traits; - package this() { - } private { enum string always = "((x) => true)"; @@ -2775,18 +3175,23 @@ class DOMImplementation : dom.DOMImplementation @Config("namespace-declarations", "bool", always) bool namespace_declarations; @Config("split-cdata-sections", "bool", always) bool split_cdata_sections; } + Params params; void assign(string field, string type)(dom.UserData val) @trusted { - mixin("if (val.convertsTo!(" ~ type ~ ")) params." ~ field ~ " = val.get!(" ~ type ~ "); \n"); + mixin("if (val.convertsTo!(" ~ type ~ ")) params." ~ field + ~ " = val.get!(" ~ type ~ "); \n"); } + bool canSet(string type, string settable)(dom.UserData val) @trusted { - mixin("if (val.convertsTo!(" ~ type ~ ")) return " ~ settable ~ "(val.get!(" ~ type ~ ")); \n"); + mixin("if (val.convertsTo!(" ~ type ~ ")) return " ~ settable + ~ "(val.get!(" ~ type ~ ")); \n"); return false; } } + // specific to DOMConfiguration override { @@ -2801,12 +3206,14 @@ class DOMImplementation : dom.DOMImplementation foreach (field; AliasSeq!(__traits(allMembers, Params))) { mixin("enum type = getUDAs!(Params." ~ field ~ ", Config)[0].type; \n"); - mixin("case getUDAs!(Params." ~ field ~ ", Config)[0].name: assign!(field, type)(value); \n"); + mixin("case getUDAs!(Params." ~ field + ~ ", Config)[0].name: assign!(field, type)(value); \n"); } - default: - throw new DOMException(dom.ExceptionCode.notFound); + default: + throw new DOMException(dom.ExceptionCode.notFound); } } + /++ + Implementation of $(LINK2 ../dom/DOMConfiguration.getParameter, + `newxml.dom.DOMConfiguration.getParameter`). @@ -2817,13 +3224,15 @@ class DOMImplementation : dom.DOMImplementation { foreach (field; AliasSeq!(__traits(allMembers, Params))) { - mixin("case getUDAs!(Params." ~ field ~ ", Config)[0].name: \n" ~ - "return dom.UserData(params." ~ field ~ "); \n"); + mixin("case getUDAs!(Params." ~ field + ~ ", Config)[0].name: \n" + ~ "return dom.UserData(params." ~ field ~ "); \n"); } - default: - throw new DOMException(dom.ExceptionCode.notFound); + default: + throw new DOMException(dom.ExceptionCode.notFound); } } + /++ + Implementation of $(LINK2 ../dom/DOMConfiguration.canSetParameter, + `newxml.dom.DOMConfiguration.canSetParameter`). @@ -2835,56 +3244,65 @@ class DOMImplementation : dom.DOMImplementation foreach (field; AliasSeq!(__traits(allMembers, Params))) { mixin("enum type = getUDAs!(Params." ~ field ~ ", Config)[0].type; \n"); - mixin("enum settable = getUDAs!(Params." ~ field ~ ", Config)[0].settable; \n"); - mixin("case getUDAs!(Params." ~ field ~ ", Config)[0].name: \n" ~ - "return canSet!(type, settable)(value); \n"); + mixin("enum settable = getUDAs!(Params." ~ field + ~ ", Config)[0].settable; \n"); + mixin("case getUDAs!(Params." ~ field + ~ ", Config)[0].name: \n" + ~ "return canSet!(type, settable)(value); \n"); } - default: - return false; + default: + return false; } } + /++ + Implementation of $(LINK2 ../dom/DOMConfiguration.parameterNames, + `newxml.dom.DOMConfiguration.parameterNames`). +/ - @property dom.DOMStringList parameterNames() + @property dom.stringList parameterNames() { return new StringList(); } } - class StringList : dom.DOMStringList + class StringList : dom.stringList { - package this() { - - } private template MapToConfigName(Members...) { static if (Members.length > 0) { - mixin("alias MapToConfigName = AliasSeq!(getUDAs!(Params." ~ Members[0] ~ - ", Config)[0].name, MapToConfigName!(Members[1..$])); \n"); + mixin("alias MapToConfigName = AliasSeq!(getUDAs!(Params." ~ Members[0] ~ ", Config)[0].name, MapToConfigName!(Members[1..$])); \n"); } else { alias MapToConfigName = AliasSeq!(); } } - static immutable string[] arr = [MapToConfigName!(__traits(allMembers, Params))]; - // specific to DOMStringList + static immutable string[] arr = [ + MapToConfigName!(__traits(allMembers, Params)) + ]; + + // specific to stringList override { - DOMString item(size_t index) { return new DOMString(arr[index]); } - size_t length() { return arr.length; } + string item(size_t index) + { + return arr[index]; + } - bool contains(DOMString str) + size_t length() + { + return arr.length; + } + + bool contains(string str) { - import std.algorithm: canFind; + import std.algorithm : canFind; + return arr.canFind(str); } } - //alias arr this; } } } @@ -2895,9 +3313,9 @@ class DOMImplementation : dom.DOMImplementation +/ auto domBuilder(CursorType)(auto ref CursorType cursor) { - //import std.experimental.allocator.gc_allocator;//import stdx.allocator.gc_allocator; import dompar = newxml.domparser; - return dompar.domBuilder(cursor, new DOMImplementation());//return dompar.domBuilder(cursor, new DOMImplementation!(CursorType.StringType, shared(GCAllocator))()); + + return dompar.domBuilder(cursor, new DOMImplementation()); } unittest @@ -2905,64 +3323,65 @@ unittest DOMImplementation impl = new DOMImplementation(); - auto doc = impl.createDocument(new DOMString("myNamespaceURI"), new DOMString("myPrefix:myRootElement"), null); + auto doc = impl.createDocument("myNamespaceURI", "myPrefix:myRootElement", null); auto root = doc.documentElement; assert(root.prefix == "myPrefix"); - auto attr = doc.createAttributeNS(new DOMString("myAttrNamespace"), new DOMString("myAttrPrefix:myAttrName")); - attr.value = new DOMString("something"); + auto attr = doc.createAttributeNS("myAttrNamespace", "myAttrPrefix:myAttrName"); + attr.value = "something"; root.setAttributeNode(attr); assert(attr.value); assert(attr.value == "something"); assert(root.attributes.length == 1); - assert(root.getAttributeNodeNS(new DOMString("myAttrNamespace"), new DOMString("myAttrName")) is attr); + assert(root.getAttributeNodeNS("myAttrNamespace", "myAttrName") is attr); - attr.nodeValue = new DOMString("myAttrValue"); + attr.nodeValue = "myAttrValue"; assert(attr.childNodes.length == 1); assert(attr.firstChild.nodeType == dom.NodeType.text); assert(attr.firstChild.nodeValue == attr.nodeValue); - auto elem = doc.createElementNS(new DOMString("myOtherNamespace"), new DOMString("myOtherPrefix:myOtherElement")); + auto elem = doc.createElementNS("myOtherNamespace", "myOtherPrefix:myOtherElement"); assert(root.ownerDocument is doc); assert(elem.ownerDocument is doc); root.appendChild(elem); assert(root.firstChild is elem); assert(root.firstChild.namespaceURI == "myOtherNamespace"); - /* elem.setAttributeNS(new DOMString("xxx"), new DOMString("yyy"), new DOMString("zzz")); - assert(elem.getAttributeNS(new DOMString("xxx"), new DOMString("yyy"))); - assert(elem.getAttributeNS(new DOMString("xxx"), new DOMString("yyy")) == "zzz"); */ + /* elem.setAttributeNS("xxx", "yyy", "zzz"); + assert(elem.getAttributeNS("xxx", "yyy")); + assert(elem.getAttributeNS("xxx", "yyy") == "zzz"); */ - auto comm = doc.createComment(new DOMString("myWonderfulComment")); + auto comm = doc.createComment("myWonderfulComment"); doc.insertBefore(comm, root); assert(doc.childNodes.length == 2); assert(doc.firstChild is comm); assert(comm.substringData(1, 4) == "yWon"); - comm.replaceData(0, 2, new DOMString("your")); + comm.replaceData(0, 2, "your"); comm.deleteData(4, 9); - comm.insertData(4, new DOMString("Questionable")); + comm.insertData(4, "Questionable"); assert(comm.data == "yourQuestionableComment"); - auto pi = doc.createProcessingInstruction(new DOMString("myPITarget"), new DOMString("myPIData")); + auto pi = doc.createProcessingInstruction("myPITarget", "myPIData"); elem.appendChild(pi); assert(elem.lastChild is pi); - auto cdata = doc.createCDATASection(new DOMString("mycdataContent")); + auto cdata = doc.createCDATASection("mycdataContent"); elem.replaceChild(cdata, pi); assert(elem.lastChild is cdata); elem.removeChild(cdata); assert(elem.childNodes.length == 0); - assert(doc.getElementsByTagNameNS(new DOMString("myOtherNamespace"), new DOMString("myOtherElement")).item(0) is elem); + assert(doc.getElementsByTagNameNS("myOtherNamespace", "myOtherElement").item( + 0) is elem); doc.setUserData("userDataKey1", dom.UserData(3.14), null); doc.setUserData("userDataKey2", dom.UserData(new Object()), null); doc.setUserData("userDataKey3", dom.UserData(null), null); assert(doc.getUserData("userDataKey1") == 3.14); assert(doc.getUserData("userDataKey2").type == typeid(Object)); - assert(doc.getUserData("userDataKey3").peek!long is null); + assert(doc.getUserData("userDataKey3").peek!long is null); - assert(elem.lookupNamespaceURI(new DOMString("myOtherPrefix")) == "myOtherNamespace"); - assert(doc.lookupPrefix(new DOMString("myNamespaceURI")) == "myPrefix"); + assert(elem.lookupNamespaceURI("myOtherPrefix") == "myOtherNamespace"); + assert(doc.lookupPrefix("myNamespaceURI") == "myPrefix"); assert(elem.isEqualNode(elem.cloneNode(false))); assert(root.isEqualNode(root.cloneNode(true))); @@ -2970,7 +3389,7 @@ unittest assert(pi.isEqualNode(pi.cloneNode(false))); } -unittest +@safe unittest { import newxml.lexers; import newxml.parser; @@ -2995,25 +3414,20 @@ unittest }"; - auto builder = - xml - .lexer - .parser - .cursor - .domBuilder; + auto builder = xml.lexer.parser.cursor.domBuilder; builder.setSource(xml); builder.buildRecursive; auto doc = builder.getDocument; - auto books = doc.getElementsByTagName(new DOMString("book")); - auto authors = doc.getElementsByTagName(new DOMString("author")); - auto titles = doc.getElementsByTagName(new DOMString("title")); + auto books = doc.getElementsByTagName("book"); + auto authors = doc.getElementsByTagName("author"); + auto titles = doc.getElementsByTagName("title"); assert(doc.xmlVersion == "1.0"); assert(doc.xmlStandalone); - enum Pos(dom.DocumentPosition pos) = cast(BitFlags!(dom.DocumentPosition))pos; + enum Pos(dom.DocumentPosition pos) = cast(BitFlags!(dom.DocumentPosition)) pos; with (dom.DocumentPosition) { assert(books[1].compareDocumentPosition(authors[2]) == Pos!following); @@ -3021,28 +3435,29 @@ unittest assert(books[1].compareDocumentPosition(titles[1]) == (Pos!containedBy | Pos!following)); assert(authors[0].compareDocumentPosition(books[0]) == (Pos!contains | Pos!preceding)); assert(titles[2].compareDocumentPosition(titles[2]) == Pos!none); - assert(books[2].attributes[0].compareDocumentPosition(books[2].attributes[1]) - == (Pos!implementationSpecific | Pos!following)); - assert(books[2].attributes[1].compareDocumentPosition(books[2].attributes[0]) - == (Pos!implementationSpecific | Pos!preceding)); + assert(books[2].attributes[0].compareDocumentPosition( + books[2].attributes[1]) == ( + Pos!implementationSpecific | Pos!following)); + assert(books[2].attributes[1].compareDocumentPosition( + books[2].attributes[0]) == ( + Pos!implementationSpecific | Pos!preceding)); } assert(books[1].cloneNode(true).childNodes[1].isEqualNode(authors[1])); books[2].setIdAttributeNode(books[2].attributes[1], true); assert(books[2].attributes[1].isId); - assert(doc.getElementById(new DOMString("978-0201704310")) is books[2]); + assert(doc.getElementById("978-0201704310") is books[2]); alias Text = typeof(doc.implementation).Text; - titles[1].appendChild(doc.createTextNode(new DOMString(" for Dummies"))); + titles[1].appendChild(doc.createTextNode(" for Dummies")); /+ TODO this test starts segfaulting with the next line - TODO creating a DOMString to call these functions seems wrong + TODO creating a string to call these functions seems wrong assert((cast(Text)(titles[1].firstChild)).wholeText == "Programming in D for Dummies"); (cast(Text)(titles[1].lastChild)).replaceWholeText(titles[1].firstChild.textContent); assert(titles[1].textContent == "Programming in D"); assert(titles[1].childNodes.length == 1); +/ } - diff --git a/source/newxml/domparser.d b/source/newxml/domparser.d index 041cf8b..d9e37da 100644 --- a/source/newxml/domparser.d +++ b/source/newxml/domparser.d @@ -25,7 +25,6 @@ import newxml.cursor; import dom = newxml.dom; import newxml.domimpl; -import newxml.domstring; /++ + Built on top of Cursor, the DOM builder adds to it the ability to @@ -36,8 +35,7 @@ import newxml.domstring; + This type should not be instantiated directly. Instead, the helper function + `domBuilder` should be used. +/ -struct DOMBuilder(T) - if (isCursor!T) +struct DOMBuilder(T) if (isCursor!T) { import std.traits : ReturnType; @@ -75,22 +73,23 @@ struct DOMBuilder(T) { switch (attr.name) { - case "version": - document.xmlVersion = new DOMString(attr.value); - switch (attr.value) { - case "1.1": - cursor.xmlVersion = XMLVersion.XML1_1; - break; - default: - cursor.xmlVersion = XMLVersion.XML1_0; - break; - } - break; - case "standalone": - document.xmlStandalone = attr.value == "yes"; + case "version": + document.xmlVersion = attr.value; + switch (attr.value) + { + case "1.1": + cursor.xmlVersion = XMLVersion.XML1_1; break; default: + cursor.xmlVersion = XMLVersion.XML1_0; break; + } + break; + case "standalone": + document.xmlStandalone = attr.value == "yes"; + break; + default: + break; } } } @@ -180,7 +179,9 @@ struct DOMBuilder(T) auto cur = createCurrent; if (cur) + { currentNode.appendChild(createCurrent); + } already_built = true; } @@ -207,65 +208,68 @@ struct DOMBuilder(T) return next(); } - private NodeType createCurrent() - // TODO: handling of system (external) entities + private NodeType createCurrent() // TODO: handling of system (external) entities { switch (cursor.kind) { // XMLKind.elementEnd is needed for empty tags: - case XMLKind.elementEnd: - case XMLKind.elementStart: - case XMLKind.elementEmpty: - /* DOMImplementation.Element elem = cursor.prefix.length ? - document.createElementNS(new DOMString(cursor.prefix), new DOMString(cursor.localName)) : - document.createElement(new DOMString(cursor.name)); */ - DOMImplementation.Element elem = document.createElement(new DOMString(cursor.name)); - foreach (attr; cursor.attributes) - { - /*if (attr.prefix.length) + case XMLKind.elementEnd: + case XMLKind.elementStart: + case XMLKind.elementEmpty: + /* DOMImplementation.Element elem = cursor.prefix.length ? + document.createElementNS(cursor.prefix, cursor.localName) : + document.createElement(cursor.name); */ + DOMImplementation.Element elem = document.createElement(cursor.name); + foreach (attr; cursor.attributes) + { + /*if (attr.prefix.length) { - elem.setAttributeNS(new DOMString(attr.prefix), new DOMString(attr.localName), - new DOMString(attr.value)); + elem.setAttributeNS(attr.prefix, attr.localName, + attr.value); } else {*/ - elem.setAttribute(new DOMString(attr.name), new DOMString(attr.value)); - //} - } - return elem; - case XMLKind.text: - return document.createTextNode(new DOMString(cursor.content)); - case XMLKind.cdata: - return document.createCDATASection(new DOMString(cursor.content)); - case XMLKind.processingInstruction: - return document.createProcessingInstruction(new DOMString(cursor.name), new DOMString(cursor.content)); - case XMLKind.comment: - return document.createComment(new DOMString(cursor.content)); - case XMLKind.dtdStart, XMLKind.dtdEmpty: - docType = domImpl.createDocumentType(new DOMString(cursor.name), new DOMString(), new DOMString()); - document.doctype = docType; - return null; - case XMLKind.entityDecl: - docType.createEntity(new DOMString(cursor.name), new DOMString(cursor.content)); - cursor.chrEntities[cursor.name] = cursor.content; - return null; - default: - return null; + elem.setAttribute(attr.name, attr.value); + //} + } + return elem; + case XMLKind.text: + return document.createTextNode(cursor.content); + case XMLKind.cdata: + return document.createCDATASection(cursor.content); + case XMLKind.processingInstruction: + return document.createProcessingInstruction(cursor.name, + cursor.content); + case XMLKind.comment: + return document.createComment(cursor.content); + case XMLKind.dtdStart, XMLKind.dtdEmpty: + docType = domImpl.createDocumentType(cursor.name, "", ""); + document.doctype = docType; + return null; + case XMLKind.entityDecl: + docType.createEntity(cursor.name, cursor.content); + cursor.chrEntities[cursor.name] = cursor.content; + return null; + default: + return null; } } /++ + Returns the Document being built by this builder. +/ - auto getDocument() { return document; } + auto getDocument() + { + return document; + } } /++ + Instantiates a suitable `DOMBuilder` on top of the given `cursor` and `DOMImplementation`. +/ auto domBuilder(CursorType)(auto ref CursorType cursor, DOMImplementation domimpl) - if (isCursor!CursorType) + if (isCursor!CursorType) { auto res = DOMBuilder!(CursorType)(domimpl); res.cursor = cursor; @@ -282,7 +286,6 @@ unittest import newxml.parser; import newxml.cursor; import domimpl = newxml.domimpl; - alias DOMImpl = domimpl.DOMImplementation; @@ -299,30 +302,25 @@ unittest }; - auto builder = - xml - .lexer - .parser - .cursor - .domBuilder(new DOMImpl()); + auto builder = xml.lexer.parser.cursor.domBuilder(new DOMImpl()); builder.setSource(xml); builder.buildRecursive; dom.Document doc = builder.getDocument; - assert(doc.getElementsByTagName(new DOMString("ccc")).length == 1); - assert(doc.documentElement.getAttribute(new DOMString("myattr"))); - assert(doc.documentElement.getAttribute(new DOMString("myattr")) == "something"); - assert(doc.documentElement.getAttribute(new DOMString("xmlns:myns"))); - assert(doc.documentElement.getAttribute(new DOMString("xmlns:myns")) == "something"); - dom.Element e1 = cast(dom.Element)doc.firstChild; + assert(doc.getElementsByTagName("ccc").length == 1); + assert(doc.documentElement.getAttribute("myattr")); + assert(doc.documentElement.getAttribute("myattr") == "something"); + assert(doc.documentElement.getAttribute("xmlns:myns")); + assert(doc.documentElement.getAttribute("xmlns:myns") == "something"); + dom.Element e1 = cast(dom.Element) doc.firstChild; assert(e1.nodeName == "aaa"); - dom.Element e2 = cast(dom.Element)e1.firstChild(); + dom.Element e2 = cast(dom.Element) e1.firstChild(); assert(e2.nodeName == "myns:bbb"); - dom.Comment c1 = cast(dom.Comment)e2.firstChild; + dom.Comment c1 = cast(dom.Comment) e2.firstChild; assert(c1.data == " lol "); - dom.Text t1 = cast(dom.Text)e2.lastChild; - //Issue: Extra whitespace isn't dropped between and after words when dropWhiteSpace is enabled in + dom.Text t1 = cast(dom.Text) e2.lastChild; + //Issue: Extra whitespace isn't dropped between and after words when dropWhiteSpace is enabled in //assert(t1.data == "Lots of Text! On multiple lines!", t1.data.transcodeToUTF8); } @@ -337,13 +335,7 @@ unittest alias DOMImplType = domimpl.DOMImplementation; auto xml = ``; - auto builder = - xml - .lexer - .parser - .cursor - .copyingCursor - .domBuilder(new DOMImplType()); + auto builder = xml.lexer.parser.cursor.copyingCursor.domBuilder(new DOMImplType()); builder.setSource(xml); builder.buildRecursive; diff --git a/source/newxml/domstring.d b/source/newxml/domstring.d deleted file mode 100644 index fd5b26d..0000000 --- a/source/newxml/domstring.d +++ /dev/null @@ -1,471 +0,0 @@ -module newxml.domstring; - -import std.algorithm.comparison : equal; -import std.exception : enforce; -import std.range; -import std.string; -import std.utf; - -import newxml.faststrings; -import newxml.interfaces; - -version (newxml_force_utf8) { - alias XMLCh = immutable(char); -} else version (newxml_force_utf32) { - alias XMLCh = immutable(dchar); -} else { - alias XMLCh = immutable(wchar); -} -/** - * Proper DOMString implementation, with some added range capabilities. - * Authors: - * László Szerémi - * Contains UTF-16 strings by default, but can be configured to either UTF-8 or UTF-32 with version labels. - */ -public class DOMString : RandomAccessFinite!XMLCh { - ///Stores the character data. - private XMLCh[] buffer; - ///Front and rear positions. - private size_t frontPos, backPos; - /**`foreach` iteration uses opApply, since one delegate call per loop - * iteration is faster than three virtual function calls. - * TO DO: Use metaprogramming to make it able to be used in all sorts of context. - */ - int opApply(scope int delegate(XMLCh) deleg) { - for (size_t i ; i < buffer.length ; i++) { - int result = deleg(buffer[i]); - if (result) - { - return result; - } - } - return 0; - } - - /// Ditto - int opApply(scope int delegate(size_t, XMLCh) deleg) { - for (size_t i ; i < buffer.length ; i++) { - int result = deleg(i, buffer[i]); - if (result) - { - return result; - } - } - return 0; - } -@safe: - /** - * Default constructor for DOMString. The resulting DOMString object refers to no string at all - * Difference from C++ implementation: Does not compare with 0 - */ - this() @nogc nothrow pure { - - } - ///Copy constructor. - this(const(DOMString) other) nothrow pure { - buffer = other.buffer.dup; - backPos = buffer.length; - } - ///Constructor to build a DOMString from an XML character array. (XMLCh is a UTF-16 character by default, can be configured with version labels) - this(XMLCh* other) @nogc @trusted nothrow pure { - buffer = fromStringz(other); - backPos = buffer.length; - } - /** - * Constructor to build a DOMString from a character array of given length. - * Params: - * other = The character array to be imported into the DOMString - * length = The length of the character array to be imported - */ - this(XMLCh* other, size_t length) @nogc @system nothrow pure { - buffer = other[0..length]; - backPos = buffer.length; - } - version (newxml_force_utf8) { - - } else { - /** - * Constructor to build a DOMString from an 8 bit character array. - * Params: - * other = The character array to be imported into the DOMString - */ - this(const(char)* other) @trusted nothrow pure { - version (newxml_force_utf32) { - buffer = toUTF32(fromStringz(other)); - } else { - buffer = toUTF16(fromStringz(other)); - } - backPos = buffer.length; - } - } - ///Creates DOMString objects from standard D strings. - this(T)(T[] other) nothrow pure { - version (newxml_force_utf8) { - buffer = toUTF8(other); - } else version (newxml_force_utf32) { - buffer = toUTF32(other); - } else { - buffer = toUTF16(other); - } - backPos = buffer.length; - } - /** - * Append a null-terminated XMLCh * (Unicode) string to this string. - * Params: - * other = The object to be appended - */ - void appendData(XMLCh* other) @trusted nothrow pure { - buffer ~= fromStringz(other); - backPos = buffer.length; - } - /** - * Append a single Unicode character to this string. - * Params: - * ch = The single character to be appended - */ - void appendData(XMLCh ch) nothrow pure { - buffer ~= ch; - backPos = buffer.length; - } - /** - * Appends the content of another DOMString to this string. - * Params: - * other = The object to be appended - */ - void appendData(DOMString other) nothrow pure { - buffer ~= other.buffer; - backPos = buffer.length; - } - /** - * Appends a D string to this string. - * Params: - * other = The D string (string/wstring/dstring) as an array - */ - void appendData(T)(T[] other) nothrow pure { - version (newxml_force_utf8) { - buffer ~= toUTF8(other); - } else version (newxml_force_utf32) { - buffer ~= toUTF32(other); - } else { - buffer ~= toUTF16(other); - } - backPos = buffer.length; - } - /** - * Returns the character at the specified position. - * Params: - * index = The position at which the character is being requested - * Returns: Returns the character at the specified position. - */ - XMLCh charAt(size_t index) @nogc nothrow pure { - return buffer[index]; - } - /** - * Makes a clone of a the DOMString. - * Returns: The object to be cloned. - */ - DOMString clone() nothrow pure const { - return new DOMString(this); - } - //TO DO:read up on how this works - int compareString(DOMString other) { - return int.init; - } - /** - * Clears the data of this DOMString. - * Params: - * offset = The position from the beginning from which the data must be deleted - * count = The count of characters from the offset that must be deleted - */ - void deleteData(size_t offset, size_t count) pure { - enforce!XMLException(offset + count <= buffer.length - , "offset + count larger than buffer length!"); - buffer = buffer[0..offset] ~ buffer[offset+count..$]; - backPos = buffer.length; - } - /** - * Compare a DOMString with a null-terminated raw 16-bit character string. - * Params: - * other = The character string to be compared with. - * Returns: True if the strings are the same, false otherwise. - */ - bool equals(XMLCh* other) @trusted pure const { - auto str = fromStringz(other); - return equal(buffer, str); - } - /** - * Tells if a DOMString contains the same character data as another. - * Params: - * other = The DOMString to be compared with. - * Returns: True if the two DOMStrings are same, false otherwise. - */ - bool equals(DOMString other) pure const { - if (!other) return false; - return equal(buffer, other.buffer); - } - /** - * Compares the content of a D string against a DOMString. - * Params: - * other = The D string to be compared with. - * Returns: True if their textual data are the same, false otherwise. - */ - bool equals(T)(T other) pure const { - XMLCh[] o; - version (newxml_force_utf8) - { - o = toUTF8(other); - } - else version (newxml_force_utf32) - { - o = toUTF32(other); - } - else - { - o = toUTF16(other); - } - - return equal(buffer, o); - } - /** - * Inserts a string within the existing DOMString at an arbitrary position. - * Params: - * offset = The offset from the beginning at which the insertion needs to be done in this object - * data = The DOMString containing the data that needs to be inserted - */ - void insertData(size_t offset, DOMString data) pure nothrow { - buffer = buffer[0..offset] ~ data.buffer ~ buffer[offset..$]; - } - /** - * Inserts a string of type XMLCh within the existing DOMString at an arbitrary position - * Params: - * offset = The offset from the beginning at which the insertion needs to be done in this object - * other = The DOMString containing the data that needs to be inserted - */ - void insertData(size_t offset, XMLCh[] other) pure nothrow { - buffer = buffer[0..offset] ~ other ~ buffer[offset..$]; - } - /** - * Compares the string against various other types or itself using the `==` and `!=` operators. - * Params: - * other = The instance of the type to be tested against. - * Returns: True if they have the same textual data, false otherwise. - */ - bool opEquals(R)(R other) pure const { - return equals(other); - } - T opCast(T)() const { - static if (is(T == string)) { - return transcodeToUTF8; - } else static if (is(T == wstring)) { - return transcodeToUTF16; - } else static if (is(T == dstring)) { - return transcodeToUTF32; - } - } - /** - * Implements easy array appending with operator overloading. - * Params: - * rhs = The data to be appended to the string. - */ - auto opOpAssign(string op, R)(R rhs) { - static if(op == "+" || op == "~"){ - appendData(rhs); - } - } - ///Dumps the DOMString on the console. - void print() const { - import std.stdio; - write(buffer); - } - ///Dumps the DOMString on the console with a line feed at the end. - void println() const { - import std.stdio; - writeln(buffer); - } - ///Returns a handle to the raw buffer in the DOMString. - XMLCh* rawBuffer() @system @nogc nothrow pure const { - return buffer.ptr; - } - alias ptr = rawBuffer; - ///Returns the underlying array (string). - XMLCh[] getDString() @nogc nothrow pure const { - return buffer; - } - /** - * Preallocate storage in the string to hold a given number of characters. A DOMString will grow its buffer on - * demand, as characters are added, but it can be more efficient to allocate once in advance, if the size is known. - * Params: - * size = The number of characters to reserve. - */ - void reserve(size_t size) nothrow pure { - buffer.reserve(size); - } - /** - * Returns a sub-string of the DOMString starting at a specified position. - * Params: - * offset = The offset from the beginning from which the sub-string is being requested. - * count = The count of characters in the requested sub-string - * Returns: The sub-string of the DOMString being requested - */ - DOMString substringData(size_t offset, size_t count) nothrow pure const { - return new DOMString(buffer[offset..offset + count]); - } - /** - * Returns a copy of the string, transcoded to the local code page. The caller owns the (char *) string that is - * returned, and is responsible for deleting it. - * Returns: A pointer to a newly allocated buffer of char elements, which represents the original string, but in - * the local encoding. - * Note: This function is using the `toStringz` function, and rules of that apply here too. - */ - immutable(char)* transcode() @trusted pure nothrow const { - return toStringz(toUTF8(buffer)); - } - /** - * Transcodes the string as a UTF-8 string - * Returns: The content of this string as UTF-8 data. - */ - string transcodeToUTF8() pure nothrow const { - return toUTF8(buffer); - } - /** - * Transcodes the string as a UTF-16 string - * Returns: The content of this string as UTF-16 data. - */ - wstring transcodeToUTF16() pure nothrow const { - return toUTF16(buffer); - } - /** - * Transcodes the string as a UTF-32 string - * Returns: The content of this string as UTF-32 data. - */ - dstring transcodeToUTF32() pure nothrow const { - return toUTF32(buffer); - } - ///Templated transcoder. - T transcodeTo(T)() pure nothrow const { - static if (is(T == string)) - { - return transcodeToUTF8; - } - else static if (is(T == wstring)) - { - return transcodeToUTF16; - } - else static if (is(T == dstring)) - { - return transcodeToUTF32; - } - else - { - static assert(false, "Template parameter `" ~ T.stringof ~ "` not " - ~ "supported for function `DOMString.transcodeTo(T)()`"); - } - } - //range stuff begins here - ///Returns the front element of the range. - @property XMLCh front() @nogc nothrow pure { - return buffer[frontPos]; - - } - /**Calls $(REF moveFront, std, range, primitives) on the wrapped range, if - * possible. Otherwise, throws an $(LREF UnsupportedRangeMethod) exception. - */ - XMLCh moveFront() { - if (frontPos + 1 < backPos) - { - frontPos++; - } - - return buffer[frontPos]; - } - - ///Moves the front pointer up by one. - void popFront() { - if (frontPos + 1 < backPos) - { - frontPos++; - } - } - - ///Returns true if all content of the string have been consumed. - @property bool empty() { - return !(frontPos + 1 < backPos); - } - ///Returns the back element of the range. - @property XMLCh back() { - return buffer[backPos - 1]; - } - - /**Calls $(REF moveBack, std, range, primitives) on the wrapped range, if - * possible. Otherwise, throws an $(LREF UnsupportedRangeMethod) exception - */ - XMLCh moveBack() { - if (backPos > 1) - { - backPos--; - } - return buffer[backPos]; - } - ///Moves the back pointer down by one. - void popBack() { - if (backPos > 1) - { - backPos--; - } - } - ///Returns a copy of the DOMString. - @property RandomAccessFinite!XMLCh save() { - return new DOMString(this); - } - ///Allows the characters to be accessed in an array-like fashion. - XMLCh opIndex(size_t index) @nogc nothrow pure const { - return buffer[index]; - } - /** - * Returns a slice of the string. - * Params: - * from = The beginning point. - * to = The ending point + 1. - * Returns: The content of the slice as a DOMString. - */ - DOMString opSlice(size_t from, size_t to) nothrow pure const { - return new DOMString(buffer[from..to]); - } - ///Moves the front pointer to the given position. - XMLCh moveAt(size_t pos) @nogc nothrow pure { - frontPos = pos; - return buffer[frontPos]; - } - ///Returns the length of the string. - @property size_t length() @nogc nothrow pure const { - return buffer.length; - } - - /// - alias opDollar = length; -} -unittest { - DOMString test0 = new DOMString("Hello World!"), test1 = new DOMString("Hello World!"w), - test2 = new DOMString("Hello World!"d); - assert(test0 == "Hello World!"w); - assert(test1 == "Hello World!"w); - assert(test2 == "Hello World!"w); - assert(test1 == test2); - assert(test0.length == 12); - assert(test1.length == 12); - assert(test2.length == 12); - assert(test0[3..5].getDString == "lo"); - - DOMString test3 = new DOMString("test"); - assert(test0 != test3); - test3.insertData(2, "te"); - assert(test3 == "tetest"); - test3.deleteData(2, 2); - assert(test3 == "test"); - foreach (size_t i, XMLCh c; test3) { - assert(c == "test"[i]); - } - - DOMString test4 = new DOMString("Hello World!"w); - assert(test1 == test4); -} diff --git a/source/newxml/faststrings.d b/source/newxml/faststrings.d index ebb9674..c256351 100644 --- a/source/newxml/faststrings.d +++ b/source/newxml/faststrings.d @@ -22,13 +22,12 @@ import newxml.interfaces : XMLException; package bool checkStringBeforeChr(T, S)(T[] haysack, S[] needle, S before) @nogc @safe pure nothrow { - for (sizediff_t i ; i < haysack.length ; i++) + for (sizediff_t i; i < haysack.length; i++) { if (haysack[i] == needle[0]) { - return (cast(sizediff_t)(haysack.length) - i > needle.length) - ? equal(haysack[i..i + needle.length], needle) - : false; + return (cast(sizediff_t)(haysack.length) - i > needle.length) ? equal( + haysack[i .. i + needle.length], needle) : false; } else if (haysack[i] == before) { @@ -37,9 +36,11 @@ package bool checkStringBeforeChr(T, S)(T[] haysack, S[] needle, S before) @nogc } return false; } -unittest + +@safe pure unittest { - assert(checkStringBeforeChr("extentity SYSTEM \"someexternalentity.file\"", "SYSTEM", '"')); + assert(checkStringBeforeChr("extentity SYSTEM \"someexternalentity.file\"", "SYSTEM", + '"')); assert(!checkStringBeforeChr("extentity SYST", "SYSTEM", '"')); assert(!checkStringBeforeChr("intentity \"Some internal entity\"", "SYSTEM", '"')); } @@ -71,6 +72,7 @@ T[] xmlEscape(T)(T[] str) void xmlEscapedWrite(Out, T)(ref Out output, T[] str) { import std.conv : to; + static immutable amp = to!(T[])("&"); static immutable lt = to!(T[])("<"); static immutable gt = to!(T[])(">"); @@ -80,7 +82,7 @@ void xmlEscapedWrite(Out, T)(ref Out output, T[] str) ptrdiff_t i; while ((i = str.indexOfAny("&<>'\"")) >= 0) { - output ~= str[0..i]; + output ~= str[0 .. i]; if (str[i] == '&') { @@ -103,12 +105,13 @@ void xmlEscapedWrite(Out, T)(ref Out output, T[] str) output ~= quot; } - str = str[i+1..$]; + str = str[i + 1 .. $]; } output ~= str; } -auto xmlPredefinedEntities(T)() { +auto xmlPredefinedEntities(T)() +{ alias STR = T[]; STR[STR] result; result["amp"] = "&"; @@ -120,7 +123,7 @@ auto xmlPredefinedEntities(T)() { return result; } -import std.typecons: Flag, Yes; +import std.typecons : Flag, Yes; /++ + Returns a copy of the input string, after unescaping all known entity references. @@ -137,7 +140,7 @@ T[] xmlUnescape(Flag!"strict" strict = Yes.strict, T, U)(T[] str, U replacements { //import newxml.appender; - T[] app;//auto app = Appender!(T, Alloc)(alloc); + T[] app; //auto app = Appender!(T, Alloc)(alloc); app.reserve(str.length); app.xmlUnescapedWrite!strict(str, replacements); @@ -145,13 +148,14 @@ T[] xmlUnescape(Flag!"strict" strict = Yes.strict, T, U)(T[] str, U replacements } return str; } + T[] xmlUnescape(Flag!"strict" strict = Yes.strict, T)(T[] str) { if (str.indexOf('&') >= 0) { //import newxml.appender; - T[] app;//auto app = Appender!(T, Alloc)(alloc); + T[] app; //auto app = Appender!(T, Alloc)(alloc); app.reserve(str.length); app.xmlUnescapedWrite!strict(str, xmlPredefinedEntities!T()); @@ -166,15 +170,15 @@ T[] xmlUnescape(Flag!"strict" strict = Yes.strict, T)(T[] str) + The set of known entities can be specified with the last parameter, which must support + the `in` operator (it is treated as an associative array). +/ -void xmlUnescapedWrite(Flag!"strict" strict = Yes.strict, Out, T, U) - (ref Out output, T[] str, U replacements) +void xmlUnescapedWrite(Flag!"strict" strict = Yes.strict, Out, T, U)( + ref Out output, T[] str, U replacements) { ptrdiff_t i; while ((i = str.indexOf('&')) >= 0) { - output ~= str[0..i]; + output ~= str[0 .. i]; - ptrdiff_t j = str[(i+1)..$].indexOf(';'); + ptrdiff_t j = str[(i + 1) .. $].indexOf(';'); static if (strict == Yes.strict) { enforce!XMLException(j >= 0, "Missing ';' ending XML entity!"); @@ -186,7 +190,7 @@ void xmlUnescapedWrite(Flag!"strict" strict = Yes.strict, Out, T, U) continue; } } - auto ent = str[(i+1)..(i+j+1)]; + auto ent = str[(i + 1) .. (i + j + 1)]; static if (strict == Yes.strict) { enforce!XMLException(ent.length, "Character replacement entity not found!"); @@ -209,11 +213,10 @@ void xmlUnescapedWrite(Flag!"strict" strict = Yes.strict, Out, T, U) { static if (strict == Yes.strict) { - enforce!XMLException(ent.length <= 10 - , "Number escape value is too large!"); + enforce!XMLException(ent.length <= 10, "Number escape value is too large!"); } // TODO this should likely be a call to some phobos function - foreach(digit; ent[2..$]) + foreach (digit; ent[2 .. $]) { if ('0' <= digit && digit <= '9') { @@ -241,14 +244,14 @@ void xmlUnescapedWrite(Flag!"strict" strict = Yes.strict, Out, T, U) } } // decimal number - else + else { static if (strict == Yes.strict) { enforce!XMLException(ent.length <= 12, "Number escape value is too large!"); } // TODO this should likely be a call to some phobos function - foreach(digit; ent[1..$]) + foreach (digit; ent[1 .. $]) { if ('0' <= digit && digit <= '9') { @@ -269,11 +272,10 @@ void xmlUnescapedWrite(Flag!"strict" strict = Yes.strict, Out, T, U) } static if (strict == Yes.strict) { - enforce!XMLException(num <= 0x10FFFF - , "Number escape value is too large!"); + enforce!XMLException(num <= 0x10FFFF, "Number escape value is too large!"); } - output ~= cast(dchar)num; + output ~= cast(dchar) num; } // named entities else @@ -281,22 +283,21 @@ void xmlUnescapedWrite(Flag!"strict" strict = Yes.strict, Out, T, U) auto repl = replacements.get(ent, null); static if (strict == Yes.strict) { - enforce!XMLException(repl - , "Character replacement entity not found!"); + enforce!XMLException(repl, "Character replacement entity not found!"); } else { if (!repl) { output ~= str[i]; - str = str[(i+1)..$]; + str = str[(i + 1) .. $]; continue; } } output ~= repl; } - str = str[(i+j+2)..$]; + str = str[(i + j + 2) .. $]; } output ~= str; } @@ -306,21 +307,24 @@ unittest //import std.experimental.allocator.mallocator;//import stdx.allocator.mallocator; //auto alloc = Mallocator.instance; assert(xmlEscape("some standard string"d) == "some standard string"d); - assert(xmlEscape("& \"some\" 'string'") == - "& "some" <standard> 'string'"); - assert(xmlEscape("<&'>>>\"'\"<&&"w) == - "<&'>>>"'"<&&"w); + assert(xmlEscape("& \"some\" 'string'") + == "& "some" <standard> 'string'"); + assert(xmlEscape("<&'>>>\"'\"<&&"w) + == "<&'>>>"'"<&&"w); } unittest { import std.exception : assertThrown; + assert(xmlUnescape("some standard string"d) == "some standard string"d); assert(xmlUnescape("some strange string") == "some strange string"); - assert(xmlUnescape("& "some" <standard> 'string'") - == "& \"some\" 'string'"); - assert(xmlUnescape("<&'>>>"'"<&&"w) - == "<&'>>>\"'\"<&&"w); + assert(xmlUnescape( + "& "some" <standard> 'string'") + == "& \"some\" 'string'"); + assert(xmlUnescape( + "<&'>>>"'"<&&"w) + == "<&'>>>\"'\"<&&"w); assert(xmlUnescape("Illegal markup (<% ... %>)") == "Illegal markup (<% ... %>)"); assertThrown!XMLException(xmlUnescape("Fa�il")); assertThrown!XMLException(xmlUnescape("Fa�il")); diff --git a/source/newxml/interfaces.d b/source/newxml/interfaces.d index e2565a0..6db09c7 100644 --- a/source/newxml/interfaces.d +++ b/source/newxml/interfaces.d @@ -131,25 +131,23 @@ import std.traits; +/ template isLexer(L) { - enum bool isLexer = is(typeof( - (inout int = 0) - { - alias C = L.CharacterType; - - L lexer; - char c; - bool b; - string s; - C[] cs; - - b = lexer.empty; - lexer.start(); - cs = lexer.get(); - b = lexer.testAndAdvance(c); - lexer.advanceUntil(c, b); - lexer.advanceUntilAny(s, b); - lexer.dropWhile(s); - })); + enum bool isLexer = is(typeof((inout int = 0) { + alias C = L.CharacterType; + + L lexer; + char c; + bool b; + string s; + C[] cs; + + b = lexer.empty; + lexer.start(); + cs = lexer.get(); + b = lexer.testAndAdvance(c); + lexer.advanceUntil(c, b); + lexer.advanceUntilAny(s, b); + lexer.dropWhile(s); + })); } /++ @@ -177,12 +175,10 @@ template isLexer(L) +/ template isSaveableLexer(L) { - enum bool isSaveableLexer = isLexer!L && is(typeof( - (inout int = 0) - { - L lexer1; - L lexer2 = lexer1.save(); - })); + enum bool isSaveableLexer = isLexer!L && is(typeof((inout int = 0) { + L lexer1; + L lexer2 = lexer1.save(); + })); } // LEVEL 2: PARSERS @@ -274,8 +270,9 @@ enum XMLKind +/ template isLowLevelParser(P) { - enum bool isLowLevelParser = isInputRange!P && is(typeof(ElementType!P.kind) == XMLKind) - && is(typeof(ElementType!P.content) == P.CharacterType[]); + enum bool isLowLevelParser = isInputRange!P + && is(typeof(ElementType!P.kind) == XMLKind) + && is(typeof(ElementType!P.content) == P.CharacterType[]); } /++ @@ -417,32 +414,29 @@ template isSaveableLowLevelParser(P) +/ template isCursor(CursorType) { - enum bool isCursor = is(typeof( - (inout int = 0) - { - alias S = CursorType.StringType; - - CursorType cursor; - bool b; - - b = cursor.atBeginning; - b = cursor.documentEnd; - b = cursor.next; - b = cursor.enter; - cursor.exit; - XMLKind kind = cursor.kind; - auto s = cursor.name; - s = cursor.localName; - s = cursor.prefix; - s = cursor.content; - s = cursor.wholeContent; - auto attrs = cursor.attributes; - s = attrs.front.prefix; - s = attrs.front.localName; - s = attrs.front.name; - s = attrs.front.value; - } - )); + enum bool isCursor = is(typeof((inout int = 0) { + alias S = CursorType.StringType; + + CursorType cursor; + bool b; + + b = cursor.atBeginning; + b = cursor.documentEnd; + b = cursor.next; + b = cursor.enter; + cursor.exit; + XMLKind kind = cursor.kind; + auto s = cursor.name; + s = cursor.localName; + s = cursor.prefix; + s = cursor.content; + s = cursor.wholeContent; + auto attrs = cursor.attributes; + s = attrs.front.prefix; + s = attrs.front.localName; + s = attrs.front.name; + s = attrs.front.value; + })); } /++ @@ -470,12 +464,10 @@ template isCursor(CursorType) +/ template isSaveableCursor(CursorType) { - enum bool isSaveableCursor = isCursor!CursorType && is(typeof( - (inout int = 0) - { - CursorType cursor1; - CursorType cursor2 = cursor1.save(); - })); + enum bool isSaveableCursor = isCursor!CursorType && is(typeof((inout int = 0) { + CursorType cursor1; + CursorType cursor2 = cursor1.save(); + })); } // WRITERS @@ -484,39 +476,35 @@ template isSaveableCursor(CursorType) +/ template isWriter(WriterType) { - enum bool isWriter = is(typeof( - (inout int = 0) - { - alias StringType = WriterType.StringType; - - WriterType writer; - StringType s; - - writer.writeXMLDeclaration(10, s, true); - writer.writeComment(s); - writer.writeText(s); - writer.writeCDATA(s); - writer.writeProcessingInstruction(s, s); - writer.startElement(s); - writer.closeElement(s); - writer.writeAttribute(s, s); - })); + enum bool isWriter = is(typeof((inout int = 0) { + alias StringType = WriterType.StringType; + + WriterType writer; + StringType s; + + writer.writeXMLDeclaration(10, s, true); + writer.writeComment(s); + writer.writeText(s); + writer.writeCDATA(s); + writer.writeProcessingInstruction(s, s); + writer.startElement(s); + writer.closeElement(s); + writer.writeAttribute(s, s); + })); } // COMMON template needSource(T) { - enum bool needSource = is(typeof( - (inout int = 0) - { - alias InputType = T.InputType; + enum bool needSource = is(typeof((inout int = 0) { + alias InputType = T.InputType; - T component; - InputType input; + T component; + InputType input; - component.setSource(input); - })); + component.setSource(input); + })); } /++ @@ -525,13 +513,14 @@ template needSource(T) +/ class XMLException : Exception { - @nogc @safe pure nothrow this(string msg, string file = __FILE__, size_t line = __LINE__, - Throwable nextInChain = null) + @nogc @safe pure nothrow this(string msg, string file = __FILE__, + size_t line = __LINE__, Throwable nextInChain = null) { super(msg, file, line, nextInChain); } - @nogc @safe pure nothrow this(string msg, Throwable nextInChain, string file = __FILE__, size_t line = __LINE__) + @nogc @safe pure nothrow this(string msg, Throwable nextInChain, + string file = __FILE__, size_t line = __LINE__) { super(msg, file, line, nextInChain); } @@ -540,7 +529,8 @@ class XMLException : Exception /** * Defines the document's XML version. */ -enum XMLVersion { +enum XMLVersion +{ XML1_0, XML1_1, } diff --git a/source/newxml/lexers.d b/source/newxml/lexers.d index 01e7688..394bbe0 100644 --- a/source/newxml/lexers.d +++ b/source/newxml/lexers.d @@ -42,17 +42,21 @@ import std.typecons : Flag, Yes; /** * Thrown on lexing errors. */ -public class LexerException : Exception { - @nogc @safe pure nothrow this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable nextInChain = null) +public class LexerException : Exception +{ + @nogc @safe pure nothrow this(string msg, string file = __FILE__, + size_t line = __LINE__, Throwable nextInChain = null) { super(msg, file, line, nextInChain); } - @nogc @safe pure nothrow this(string msg, Throwable nextInChain, string file = __FILE__, size_t line = __LINE__) + @nogc @safe pure nothrow this(string msg, Throwable nextInChain, + string file = __FILE__, size_t line = __LINE__) { super(msg, file, line, nextInChain); } } + @safe: /++ + A lexer that takes a sliceable input. @@ -93,7 +97,7 @@ struct SliceLexer(T) pos = 0; } - static if(isForwardRange!T) + static if (isForwardRange!T) { auto save() { @@ -118,7 +122,7 @@ struct SliceLexer(T) /// ditto CharacterType[] get() const { - return input[begin..pos]; + return input[begin .. pos]; } /// ditto @@ -134,7 +138,7 @@ struct SliceLexer(T) bool testAndAdvance(char c) { enforce!LexerException(!empty, "No more characters are found!"); - //handler(); + //handler(); if (input[pos] == c) { pos++; @@ -147,13 +151,13 @@ struct SliceLexer(T) void advanceUntil(char c, bool included) { enforce!LexerException(!empty, "No more characters are found!"); - //handler(); - auto adv = indexOf(input[pos..$], c); + //handler(); + auto adv = indexOf(input[pos .. $], c); if (adv != -1) { pos += adv; enforce!LexerException(!empty, "No more characters are found!"); - //handler(); + //handler(); } else { @@ -163,7 +167,7 @@ struct SliceLexer(T) if (included) { enforce!LexerException(!empty, "No more characters are found!"); - //handler(); + //handler(); pos++; } } @@ -176,8 +180,7 @@ struct SliceLexer(T) ptrdiff_t res; while ((res = indexOf(s, input[pos])) == -1) { - enforce!LexerException(++pos < input.length - , "No more characters are found!"); + enforce!LexerException(++pos < input.length, "No more characters are found!"); } if (included) @@ -203,8 +206,7 @@ struct SliceLexer(T) + Params: + T = the InputRange to be used as input for this lexer +/ -struct RangeLexer(T) - if (isInputRange!T) +struct RangeLexer(T) if (isInputRange!T) { //import newxml.appender; @@ -222,7 +224,8 @@ struct RangeLexer(T) //private Appender!(CharacterType, Alloc) app; private CharacterType[] buffer; - import std.string: representation; + import std.string : representation; + static if (is(typeof(representation!CharacterType("")))) { private typeof(representation!CharacterType("")) input; @@ -278,7 +281,7 @@ struct RangeLexer(T) /// ditto CharacterType[] get() const { - return buffer;//app.data; + return buffer; //app.data; } /// ditto @@ -293,11 +296,10 @@ struct RangeLexer(T) /// ditto bool testAndAdvance(char c) { - enforce!LexerException(!input.empty - , "No more characters are found!");//handler(); + enforce!LexerException(!input.empty, "No more characters are found!"); //handler(); if (input.front == c) { - buffer ~= input.front;//app.put(input.front); + buffer ~= input.front; //app.put(input.front); input.popFront(); return true; } @@ -307,19 +309,17 @@ struct RangeLexer(T) /// ditto void advanceUntil(char c, bool included) { - enforce!LexerException(!input.empty - , "No more characters are found!");//handler(); + enforce!LexerException(!input.empty, "No more characters are found!"); //handler(); while (input.front != c) { - buffer ~= input.front;//app.put(input.front); + buffer ~= input.front; //app.put(input.front); input.popFront(); - enforce!LexerException(!input.empty - , "No more characters are found!");//handler(); + enforce!LexerException(!input.empty, "No more characters are found!"); //handler(); } if (included) { - buffer ~= input.front;//app.put(input.front); + buffer ~= input.front; //app.put(input.front); input.popFront(); } } @@ -327,20 +327,18 @@ struct RangeLexer(T) /// ditto size_t advanceUntilAny(string s, bool included) { - enforce!LexerException(!input.empty - , "No more characters are found!");//handler(); + enforce!LexerException(!input.empty, "No more characters are found!"); //handler(); size_t res; while ((res = indexOf(s, input.front)) == -1) { - buffer ~= input.front;//app.put(input.front); + buffer ~= input.front; //app.put(input.front); input.popFront; - enforce!LexerException(!input.empty - , "No more characters are found!");//handler(); + enforce!LexerException(!input.empty, "No more characters are found!"); //handler(); } if (included) { - buffer ~= input.front;// app.put(input.front); + buffer ~= input.front; // app.put(input.front); input.popFront; } return res; @@ -360,8 +358,7 @@ struct RangeLexer(T) + Params: + T = the InputRange to be used as input for this lexer +/ -struct ForwardLexer(T) - if (isForwardRange!T) +struct ForwardLexer(T) if (isForwardRange!T) { /++ @@ -376,16 +373,17 @@ struct ForwardLexer(T) //mixin UsesErrorHandler!ErrorHandler; private size_t count; - private CharacterType[] buffer;//private Appender!(CharacterType, Alloc) app; + private CharacterType[] buffer; //private Appender!(CharacterType, Alloc) app; + + import std.string : representation; - import std.string: representation; static if (is(typeof(representation!CharacterType("")))) { private typeof(representation!CharacterType("")) input; private typeof(input) input_start; void setSource(T input) { - buffer.length = 0;//app = typeof(app)(allocator); + buffer.length = 0; //app = typeof(app)(allocator); this.input = input.representation; this.input_start = this.input; } @@ -396,7 +394,7 @@ struct ForwardLexer(T) private T input_start; void setSource(T input) { - buffer.length = 0;//app = typeof(app)(allocator); + buffer.length = 0; //app = typeof(app)(allocator); this.input = input; this.input_start = input; } @@ -407,7 +405,7 @@ struct ForwardLexer(T) ForwardLexer result; result.input = input.save(); result.input_start = input.save(); - result.buffer.length = 0;//result.app = typeof(app)(allocator); + result.buffer.length = 0; //result.app = typeof(app)(allocator); result.count = count; return result; } @@ -433,12 +431,13 @@ struct ForwardLexer(T) /// ditto CharacterType[] get() { - import std.range: take; + import std.range : take; + auto diff = count - buffer.length; if (diff) { buffer.reserve(diff); - buffer ~= input_start.take(diff);//app.put(input_start.take(diff)); + buffer ~= input_start.take(diff); //app.put(input_start.take(diff)); } return buffer; } @@ -456,7 +455,7 @@ struct ForwardLexer(T) /// ditto bool testAndAdvance(char c) { - enforce!LexerException(!input.empty , "No data found!"); + enforce!LexerException(!input.empty, "No data found!"); if (input.front == c) { count++; @@ -469,14 +468,12 @@ struct ForwardLexer(T) /// ditto void advanceUntil(char c, bool included) { - enforce!LexerException(!input.empty - , "No data found!"); + enforce!LexerException(!input.empty, "No data found!"); while (input.front != c) { count++; input.popFront(); - enforce!LexerException(!input.empty - , "No data found!"); + enforce!LexerException(!input.empty, "No data found!"); } if (included) { @@ -488,15 +485,13 @@ struct ForwardLexer(T) /// ditto size_t advanceUntilAny(string s, bool included) { - enforce!LexerException(!input.empty - , "No more characters are found!"); + enforce!LexerException(!input.empty, "No more characters are found!"); size_t res; while ((res = indexOf(s, input.front)) == -1) { count++; input.popFront; - enforce!LexerException(!input.empty - , "No more characters are found!"); + enforce!LexerException(!input.empty, "No more characters are found!"); } if (included) { @@ -523,8 +518,7 @@ struct ForwardLexer(T) + Params: + T = the InputRange to be used as input for this lexer +/ -struct BufferedLexer(T) - if (isInputRange!T && isArray!(ElementType!T)) +struct BufferedLexer(T) if (isInputRange!T && isArray!(ElementType!T)) { //import newxml.appender; @@ -542,7 +536,7 @@ struct BufferedLexer(T) private size_t pos; private size_t begin; - private CharacterType[] outBuf;//private Appender!(CharacterType, Alloc) app; + private CharacterType[] outBuf; //private Appender!(CharacterType, Alloc) app; private bool onEdge; private BufferType buffer; @@ -572,7 +566,7 @@ struct BufferedLexer(T) result.buffer = buffer; result.pos = pos; result.begin = begin; - result.outBuf.length = 0;//app = typeof(app)(allocator); + result.outBuf.length = 0; //app = typeof(app)(allocator); return result; } } @@ -601,17 +595,16 @@ struct BufferedLexer(T) private void advance() { - enforce!LexerException(!empty - , "No more characters are found!"); + enforce!LexerException(!empty, "No more characters are found!"); if (pos + 1 >= buffer.length) { if (onEdge) { - outBuf ~= buffer[pos];//app.put(buffer[pos]); + outBuf ~= buffer[pos]; //app.put(buffer[pos]); } else { - outBuf ~= buffer[begin..$];//app.put(buffer[begin..$]); + outBuf ~= buffer[begin .. $]; //app.put(buffer[begin..$]); onEdge = true; } popBuffer; @@ -620,31 +613,32 @@ struct BufferedLexer(T) } else if (onEdge) { - outBuf ~= buffer[pos++];//app.put(buffer[pos++]); + outBuf ~= buffer[pos++]; //app.put(buffer[pos++]); } else { pos++; } } + private void advance(ptrdiff_t n) { - foreach(i; 0..n) + foreach (i; 0 .. n) { advance(); } } + private void advanceNextBuffer() { - enforce!LexerException(!empty - , "No more characters are found!"); + enforce!LexerException(!empty, "No more characters are found!"); if (onEdge) { - outBuf ~= buffer[pos..$]; //app.put(buffer[pos..$]); + outBuf ~= buffer[pos .. $]; //app.put(buffer[pos..$]); } else { - outBuf ~= buffer[begin..$];//app.put(buffer[begin..$]); + outBuf ~= buffer[begin .. $]; //app.put(buffer[begin..$]); onEdge = true; } popBuffer; @@ -660,17 +654,17 @@ struct BufferedLexer(T) { if (onEdge) { - return outBuf;//app.data; + return outBuf; //app.data; } else { static if (is(typeof(representation!CharacterType("")))) { - return cast(CharacterType[])buffer[begin..pos]; + return cast(CharacterType[]) buffer[begin .. pos]; } else { - return buffer[begin..pos]; + return buffer[begin .. pos]; } } } @@ -702,7 +696,7 @@ struct BufferedLexer(T) { enforce!LexerException(!empty, "No data found!"); ptrdiff_t adv; - while ((adv = indexOf(buffer[pos..$], c)) == -1) + while ((adv = indexOf(buffer[pos .. $], c)) == -1) { advanceNextBuffer(); } @@ -776,7 +770,7 @@ template lexer() } } -version(unittest) +version (unittest) { struct DumbBufferedReader { @@ -785,16 +779,15 @@ version(unittest) void popFront() @nogc { - content = content.length > chunk_size - ? content[chunk_size..$] - : []; + content = content.length > chunk_size ? content[chunk_size .. $] : [ + ]; } + string front() const @nogc { - return content.length >= chunk_size - ? content[0..chunk_size] - : content[0..$]; + return content.length >= chunk_size ? content[0 .. chunk_size] : content[0 .. $]; } + bool empty() const @nogc { return !content.length; @@ -853,4 +846,3 @@ unittest testLexer!(ForwardLexer!(string))(x => x); testLexer!(BufferedLexer!(DumbBufferedReader))(x => DumbBufferedReader(x, 10)); } - diff --git a/source/newxml/package.d b/source/newxml/package.d index e052237..1340036 100644 --- a/source/newxml/package.d +++ b/source/newxml/package.d @@ -3,7 +3,6 @@ module newxml; public import newxml.dom; public import domimpl = newxml.domimpl; public import newxml.domparser; -public import newxml.domstring; public import newxml.sax; public import newxml.writer; public import newxml.cursor; @@ -21,12 +20,8 @@ public import newxml.parser; +/ Document parseXMLString(string input) { - auto builder = - input - .lexer - .parser - .cursor - .domBuilder(new domimpl.DOMImplementation()); + auto builder = input.lexer.parser.cursor.domBuilder(new domimpl + .DOMImplementation()); builder.setSource(input); builder.buildRecursive(); @@ -34,7 +29,8 @@ Document parseXMLString(string input) } /// -unittest { +unittest +{ import std.format; string xml = q"{ @@ -49,5 +45,5 @@ unittest { Document doc = parseXMLString(xml); assert(doc !is null); - assert(doc.doctype.entities.getNamedItem(new DOMString("myent")).nodeValue == "replacement text"); + assert(doc.doctype.entities.getNamedItem("myent").nodeValue == "replacement text"); } diff --git a/source/newxml/parser.d b/source/newxml/parser.d index 3006946..95131a2 100644 --- a/source/newxml/parser.d +++ b/source/newxml/parser.d @@ -32,17 +32,21 @@ import std.algorithm.comparison : equal; import std.exception : enforce; import std.typecons : Flag, Yes, No; -public class ParserException : XMLException { - @nogc @safe pure nothrow this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable nextInChain = null) +public class ParserException : XMLException +{ + @nogc @safe pure nothrow this(string msg, string file = __FILE__, + size_t line = __LINE__, Throwable nextInChain = null) { super(msg, file, line, nextInChain); } - @nogc @safe pure nothrow this(string msg, Throwable nextInChain, string file = __FILE__, size_t line = __LINE__) + @nogc @safe pure nothrow this(string msg, Throwable nextInChain, + string file = __FILE__, size_t line = __LINE__) { super(msg, file, line, nextInChain); } } + @safe: /++ + A low level XML parser. @@ -55,8 +59,8 @@ public class ParserException : XMLException { + preserveWhitespace = if set to `Yes` (default is `No`), the parser will not remove element content whitespace + (i.e. the whitespace that separates tags), but will report it as text. +/ -struct Parser(L, Flag!"preserveWhitespace" preserveWhitespace = No.preserveWhitespace) - if (isLexer!L) +struct Parser(L, Flag!"preserveWhitespace" preserveWhitespace = No + .preserveWhitespace) if (isLexer!L) { import std.meta : staticIndexOf; @@ -89,7 +93,8 @@ struct Parser(L, Flag!"preserveWhitespace" preserveWhitespace = No.preserveWhite //mixin UsesErrorHandler!ErrorHandler; - this(L lexer) { + this(L lexer) + { this.lexer = lexer; //chrEntities = xmlPredefinedEntities!CharacterType(); } @@ -129,7 +134,7 @@ struct Parser(L, Flag!"preserveWhitespace" preserveWhitespace = No.preserveWhite private CharacterType[] fetchContent(size_t start = 0, size_t stop = 0) { - return lexer.get[start..($ - stop)]; + return lexer.get[start .. ($ - stop)]; } /++ @@ -139,7 +144,9 @@ struct Parser(L, Flag!"preserveWhitespace" preserveWhitespace = No.preserveWhite bool empty() { static if (preserveWhitespace == No.preserveWhitespace) + { lexer.dropWhile(" \r\n\t"); + } return !ready && lexer.empty; } @@ -169,7 +176,7 @@ struct Parser(L, Flag!"preserveWhitespace" preserveWhitespace = No.preserveWhite lexer.dropWhile(" \r\n\t"); } - assert(!lexer.empty); + enforce!ParserException(!lexer.empty, "Lexer must not be empty"); lexer.start(); @@ -177,8 +184,8 @@ struct Parser(L, Flag!"preserveWhitespace" preserveWhitespace = No.preserveWhite if (insideDTD && lexer.testAndAdvance(']')) { lexer.dropWhile(" \r\n\t"); - enforce!ParserException(lexer.testAndAdvance('>') - , "No \">\" character have been found after an \"<\"!"); + enforce!ParserException(lexer.testAndAdvance('>'), + "No \">\" character have been found after an \"<\"!"); next.kind = XMLKind.dtdEnd; next.content = null; insideDTD = false; @@ -252,13 +259,13 @@ struct Parser(L, Flag!"preserveWhitespace" preserveWhitespace = No.preserveWhite { lexer.advanceUntil('[', true); // cdata - if (lexer.get.length == 9 && equal(lexer.get()[3..$], "CDATA[")) + if (lexer.get.length == 9 && equal(lexer.get()[3 .. $], "CDATA[")) { do { lexer.advanceUntil('>', true); } - while (!equal(lexer.get()[($-3)..$], "]]>")); + while (!equal(lexer.get()[($ - 3) .. $], "]]>")); next.content = fetchContent(9, 3); next.kind = XMLKind.cdata; } @@ -268,11 +275,11 @@ struct Parser(L, Flag!"preserveWhitespace" preserveWhitespace = No.preserveWhite do { lexer.advanceUntilAny("[>", true); - if (lexer.get()[($-3)..$] == "]]>") + if (lexer.get()[($ - 3) .. $] == "]]>") { count--; } - else if (lexer.get()[($-3)..$] == "', true); } - while (!equal(lexer.get()[($-3)..$], "-->")); + while (!equal(lexer.get()[($ - 3) .. $], "-->")); next.content = fetchContent(4, 3); next.kind = XMLKind.comment; } @@ -309,7 +316,7 @@ struct Parser(L, Flag!"preserveWhitespace" preserveWhitespace = No.preserveWhite } // doctype - if (lexer.get.length>= 9 && equal(lexer.get()[2..9], "DOCTYPE")) + if (lexer.get.length >= 9 && equal(lexer.get()[2 .. 9], "DOCTYPE")) { next.content = fetchContent(9, 1); if (c == 2) @@ -340,22 +347,22 @@ struct Parser(L, Flag!"preserveWhitespace" preserveWhitespace = No.preserveWhite } } auto len = lexer.get().length; - if (len > 8 && equal(lexer.get()[2..9], "ATTLIST")) + if (len > 8 && equal(lexer.get()[2 .. 9], "ATTLIST")) { next.content = fetchContent(9, 1); next.kind = XMLKind.attlistDecl; } - else if (len > 8 && equal(lexer.get()[2..9], "ELEMENT")) + else if (len > 8 && equal(lexer.get()[2 .. 9], "ELEMENT")) { next.content = fetchContent(9, 1); next.kind = XMLKind.elementDecl; } - else if (len > 9 && equal(lexer.get()[2..10], "NOTATION")) + else if (len > 9 && equal(lexer.get()[2 .. 10], "NOTATION")) { next.content = fetchContent(10, 1); next.kind = XMLKind.notationDecl; } - else if (len > 7 && equal(lexer.get()[2..8], "ENTITY")) + else if (len > 7 && equal(lexer.get()[2 .. 8], "ENTITY")) { next.content = fetchContent(8, 1); next.kind = XMLKind.entityDecl; @@ -383,8 +390,8 @@ struct Parser(L, Flag!"preserveWhitespace" preserveWhitespace = No.preserveWhite + Returns: + A `Parser` instance initialized with the given lexer +/ -auto parser(Flag!"preserveWhitespace" preserveWhitespace = No.preserveWhitespace, T)(T lexer) - if (isLexer!T) +auto parser(Flag!"preserveWhitespace" preserveWhitespace = No.preserveWhitespace, T)( + T lexer) if (isLexer!T) { auto parser = Parser!(T, preserveWhitespace)(); //parser.errorHandler = handler; @@ -393,7 +400,7 @@ auto parser(Flag!"preserveWhitespace" preserveWhitespace = No.preserveWhitespace } import newxml.lexers; -import std.experimental.allocator.gc_allocator;//import stdx.allocator.gc_allocator; +import std.experimental.allocator.gc_allocator; //import stdx.allocator.gc_allocator; /++ + Instantiates a parser suitable for the given `InputType`. @@ -405,13 +412,13 @@ import std.experimental.allocator.gc_allocator;//import stdx.allocator.gc_alloca + .parser!(preserveWhitespace)(handler) + --- +/ -auto chooseParser(InputType, Flag!"preserveWhitespace" preserveWhitespace = No.preserveWhitespace)() +auto chooseParser(InputType, Flag!"preserveWhitespace" preserveWhitespace = No + .preserveWhitespace)() { - return chooseLexer!(InputType)() - .parser!(preserveWhitespace)(); + return chooseLexer!(InputType)().parser!(preserveWhitespace)(); } -unittest +@safe pure unittest { import newxml.lexers; import std.algorithm : find; diff --git a/source/newxml/sax.d b/source/newxml/sax.d index 0e77b50..9d87927 100644 --- a/source/newxml/sax.d +++ b/source/newxml/sax.d @@ -11,6 +11,7 @@ + Authors: + Lodovico Giaretta + László Szerémi ++ Robert Schadek + + License: + Boost License 1.0. @@ -24,6 +25,7 @@ module newxml.sax; import newxml.interfaces; import newxml.cursor; import newxml.faststrings; + @safe: /++ + A SAX parser built on top of a cursor. @@ -31,8 +33,7 @@ import newxml.faststrings; + Delegates are called when certain events are encountered, then it passes the necessary data to process the + element. +/ -struct SAXParser(T) - if (isCursor!T) +struct SAXParser(T) if (isCursor!T) { public T cursor; alias StringType = T.StringType; @@ -82,68 +83,91 @@ struct SAXParser(T) void processDocument() { import std.traits : hasMember; + while (!cursor.documentEnd) { switch (cursor.kind) { - case XMLKind.document: - if (onDocument !is null) - onDocument(createAArray(cursor.attributes)); - break; - case XMLKind.dtdStart: - if (onDocTypeDecl !is null) - onDocTypeDecl(cursor.content, false); - break; - case XMLKind.entityDecl: - if (checkStringBeforeChr(cursor.wholeContent, "SYSTEM", '"') || - checkStringBeforeChr(cursor.wholeContent, "SYSTEM", '\'')) - { - if (cursor.sysEntityLoader !is null) - { - cursor.chrEntities[cursor.name] = cursor.sysEntityLoader(cursor.content); - } - } - else + case XMLKind.document: + if (onDocument !is null) + { + onDocument(createAArray(cursor.attributes)); + } + break; + case XMLKind.dtdStart: + if (onDocTypeDecl !is null) + { + onDocTypeDecl(cursor.content, false); + } + break; + case XMLKind.entityDecl: + if (checkStringBeforeChr(cursor.wholeContent, "SYSTEM", '"') + || checkStringBeforeChr(cursor.wholeContent, "SYSTEM", '\'')) + { + if (cursor.sysEntityLoader !is null) { - cursor.chrEntities[cursor.name] = cursor.content; + cursor.chrEntities[cursor.name] = cursor.sysEntityLoader( + cursor.content); } - break; + } + else + { + cursor.chrEntities[cursor.name] = cursor.content; + } + break; /* case XMLKind.dtdEnd: break; */ - case XMLKind.dtdEmpty: - if (onDocTypeDecl !is null) - onDocTypeDecl(cursor.content, true); - break; - case XMLKind.elementStart: - if (onElementStart !is null) - onElementStart(cursor.name, createAArray(cursor.attributes)); - break; - case XMLKind.elementEnd: - if (onElementEnd !is null) - onElementEnd(cursor.name); - break; - case XMLKind.elementEmpty: - if (onElementEmpty !is null) - onElementEmpty(cursor.name, createAArray(cursor.attributes)); - break; - case XMLKind.text: - if (onText !is null) - onText(cursor.content); - break; - case XMLKind.comment: - if (onComment !is null) - onComment(cursor.content); - break; - case XMLKind.processingInstruction: - if (onProcessingInstruction !is null) - onProcessingInstruction(cursor.name, cursor.content); - break; - case XMLKind.cdata: - if (onCDataSection !is null) - onCDataSection(cursor.content); - break; - - default: break; + case XMLKind.dtdEmpty: + if (onDocTypeDecl !is null) + { + onDocTypeDecl(cursor.content, true); + } + break; + case XMLKind.elementStart: + if (onElementStart !is null) + { + onElementStart(cursor.name, createAArray(cursor.attributes)); + } + break; + case XMLKind.elementEnd: + if (onElementEnd !is null) + { + onElementEnd(cursor.name); + } + break; + case XMLKind.elementEmpty: + if (onElementEmpty !is null) + { + onElementEmpty(cursor.name, createAArray(cursor.attributes)); + } + break; + case XMLKind.text: + if (onText !is null) + { + onText(cursor.content); + } + break; + case XMLKind.comment: + if (onComment !is null) + { + onComment(cursor.content); + } + break; + case XMLKind.processingInstruction: + if (onProcessingInstruction !is null) + { + onProcessingInstruction(cursor.name, cursor.content); + } + break; + case XMLKind.cdata: + if (onCDataSection !is null) + { + onCDataSection(cursor.content); + } + break; + + default: + break; } if (cursor.enter) @@ -155,7 +179,9 @@ struct SAXParser(T) } } } - protected StringType[StringType] createAArray(AttrRange source) { + + protected StringType[StringType] createAArray(AttrRange source) + { StringType[StringType] result; foreach (key; source) { @@ -168,15 +194,14 @@ struct SAXParser(T) /++ + Instantiates a suitable SAX parser from the given `cursor` and `handler`. +/ -auto saxParser(CursorType)(auto ref CursorType cursor) - if (isCursor!CursorType) +auto saxParser(CursorType)(auto ref CursorType cursor) if (isCursor!CursorType) { auto res = SAXParser!(CursorType)(); res.cursor = cursor; return res; } -unittest +@safe unittest { import newxml.parser; import newxml.lexers; @@ -198,6 +223,8 @@ unittest struct MyHandler { + @safe: + int max_nesting; int current_nesting; int total_invocations; @@ -211,38 +238,49 @@ unittest max_nesting = current_nesting; } } + void onElementEnd(dstring name) { total_invocations++; current_nesting--; } - void onElementEmpty(dstring name, dstring[dstring] attributes) { total_invocations++; } - void onProcessingInstruction(dstring name, dstring content) { total_invocations++; } - void onText(dstring content) { total_invocations++; } + + void onElementEmpty(dstring name, dstring[dstring] attributes) + { + total_invocations++; + } + + void onProcessingInstruction(dstring name, dstring content) + { + total_invocations++; + } + + void onText(dstring content) + { + total_invocations++; + } + void onDocument(dstring[dstring] attribute) { assert(attribute["encoding"] == "utf-8"); total_invocations++; } + void onComment(dstring content) { assert(content == " lol "); total_invocations++; } - void onDocTypeDecl(dstring type, bool empty) { + + void onDocTypeDecl(dstring type, bool empty) + { assert(type == "somekindofdoc", type.to!string); assert(empty); } } - MyHandler handler; - auto parser = - xml - .lexer - .parser - .cursor - .saxParser; + auto parser = xml.lexer.parser.cursor.saxParser; parser.setSource(xml); parser.onDocument = &handler.onDocument; @@ -260,7 +298,7 @@ unittest assert(handler.total_invocations == 9, to!string(handler.total_invocations)); } -unittest +@safe unittest { import newxml.parser; import newxml.lexers; @@ -279,6 +317,8 @@ unittest struct MyHandler { + @safe: + int max_nesting; int current_nesting; int total_invocations; @@ -292,20 +332,26 @@ unittest max_nesting = current_nesting; } } + void onElementEnd(dstring name) { total_invocations++; current_nesting--; } - void onText(dstring content) { - assert (content == "replacement text"); - total_invocations++; + + void onText(dstring content) + { + assert(content == "replacement text"); + total_invocations++; } - void onDocTypeDecl(dstring type, bool empty) { + + void onDocTypeDecl(dstring type, bool empty) + { assert(type == "mydoc", type.to!string); assert(!empty); total_invocations++; } + void onDocument(dstring[dstring] attribute) { assert(attribute["encoding"] == "utf-8"); @@ -314,12 +360,7 @@ unittest } MyHandler handler; - auto parser = - xml - .lexer - .parser - .cursor - .saxParser; + auto parser = xml.lexer.parser.cursor.saxParser; parser.setSource(xml); parser.onElementStart = &handler.onElementStart; parser.onElementEnd = &handler.onElementEnd; @@ -331,4 +372,4 @@ unittest assert(handler.max_nesting == 1, to!string(handler.max_nesting)); assert(handler.current_nesting == 0, to!string(handler.current_nesting)); assert(handler.total_invocations == 5, to!string(handler.total_invocations)); -} \ No newline at end of file +} diff --git a/source/newxml/validation.d b/source/newxml/validation.d index ae3f1af..374b810 100644 --- a/source/newxml/validation.d +++ b/source/newxml/validation.d @@ -29,10 +29,8 @@ import newxml.interfaces; */ pure nothrow @nogc @safe bool isValidXMLCharacter10(dchar c) { - return c == '\r' || c == '\n' || c == '\t' - || (0x20 <= c && c <= 0xD7FF) - || (0xE000 <= c && c <= 0xFFFD) - || (0x10000 <= c && c <= 0x10FFFF); + return c == '\r' || c == '\n' || c == '\t' || (0x20 <= c && c <= 0xD7FF) + || (0xE000 <= c && c <= 0xFFFD) || (0x10000 <= c && c <= 0x10FFFF); } /** @@ -40,9 +38,7 @@ pure nothrow @nogc @safe bool isValidXMLCharacter10(dchar c) */ pure nothrow @nogc @safe bool isValidXMLCharacter11(dchar c) { - return (1 <= c && c <= 0xD7FF) - || (0xE000 <= c && c <= 0xFFFD) - || (0x10000 <= c && c <= 0x10FFFF); + return (1 <= c && c <= 0xD7FF) || (0xE000 <= c && c <= 0xFFFD) || (0x10000 <= c && c <= 0x10FFFF); } /** * Checks whether a text contains invalid characters for an XML 1.0 document. @@ -54,7 +50,10 @@ pure nothrow @nogc @safe bool isValidXMLText10(T)(T[] input) { foreach (elem; input) { - if (!isValidXMLCharacter10(elem)) return false; + if (!isValidXMLCharacter10(elem)) + { + return false; + } } return true; } @@ -68,7 +67,10 @@ pure nothrow @nogc @safe bool isValidXMLText11(T)(T[] input) { foreach (elem; input) { - if (!isValidXMLCharacter11(elem)) return false; + if (!isValidXMLCharacter11(elem)) + { + return false; + } } return true; } @@ -78,19 +80,13 @@ pure nothrow @nogc @safe bool isValidXMLText11(T)(T[] input) */ pure nothrow @nogc @safe bool isValidXMLNameStart(dchar c) { - return c == ':' - || ('A' <= c && c <= 'Z') - || c == '_' - || ('a' <= c && c <= 'z') - || (0xC0 <= c && c <= 0x2FF && c != 0xD7 && c != 0xF7) - || (0x370 <= c && c <= 0x1FFF && c != 0x37E) - || c == 0x200C - || c == 0x200D - || (0x2070 <= c && c <= 0x218F) - || (0x2C00 <= c && c <= 0x2FEF) - || (0x3001 <= c && c <= 0xD7FF) - || (0xF900 <= c && c <= 0xFDCF) - || (0xFDF0 <= c && c <= 0xEFFFF && c != 0xFFFE && c != 0xFFFF); + return c == ':' || ('A' <= c && c <= 'Z') || c == '_' || ('a' <= c && c <= 'z') + || (0xC0 <= c && c <= 0x2FF && c != 0xD7 && c != 0xF7) || (0x370 <= c + && c <= 0x1FFF + && c != 0x37E) || c == 0x200C || c == 0x200D || (0x2070 <= c + && c <= 0x218F) || (0x2C00 <= c && c <= 0x2FEF) || (0x3001 <= c + && c <= 0xD7FF) || (0xF900 <= c && c <= 0xFDCF) || (0xFDF0 <= c + && c <= 0xEFFFF && c != 0xFFFE && c != 0xFFFF); } /** @@ -98,13 +94,8 @@ pure nothrow @nogc @safe bool isValidXMLNameStart(dchar c) */ pure nothrow @nogc @safe bool isValidXMLNameChar(dchar c) { - return isValidXMLNameStart(c) - || c == '-' - || c == '.' - || ('0' <= c && c <= '9') - || c == 0xB7 - || (0x300 <= c && c <= 0x36F) - || (0x203F <= c && c <= 2040); + return isValidXMLNameStart(c) || c == '-' || c == '.' || ('0' <= c && c <= '9') + || c == 0xB7 || (0x300 <= c && c <= 0x36F) || (0x203F <= c && c <= 2040); } /** @@ -113,7 +104,8 @@ pure nothrow @nogc @safe bool isValidXMLNameChar(dchar c) * input = The input string. * Returns: True if XML name is valid. */ -pure nothrow @nogc @safe bool isValidXMLName(T)(T[] input) { +pure nothrow @nogc @safe bool isValidXMLName(T)(T[] input) +{ if (!input.length) { return false; @@ -123,7 +115,7 @@ pure nothrow @nogc @safe bool isValidXMLName(T)(T[] input) { return false; } - for (sizediff_t i = 1 ; i < input.length; i++) + for (sizediff_t i = 1; i < input.length; i++) { if (!isValidXMLNameChar(input[i])) { @@ -139,17 +131,14 @@ pure nothrow @nogc @safe bool isValidXMLName(T)(T[] input) { */ pure nothrow @nogc @safe bool isValidXMLPublicIdCharacter(dchar c) { - import std.string: indexOf; - return c == ' ' - || c == '\n' - || c == '\r' - || ('a' <= c && c <= 'z') - || ('A' <= c && c <= 'Z') - || ('0' <= c && c <= '9') - || "-'()+,./:=?;!*#@$_%".indexOf(c) != -1; + import std.string : indexOf; + + return c == ' ' || c == '\n' || c == '\r' || ('a' <= c && c <= 'z') || ('A' <= c + && c <= 'Z') || ('0' <= c && c <= '9') || "-'()+,./:=?;!*#@$_%" + .indexOf(c) != -1; } -unittest +@safe pure unittest { assert(isValidXMLName("foo")); assert(isValidXMLName("bar")); @@ -182,8 +171,8 @@ struct ValidationStack(StringType) { if (stack.length) { - StringType top = stack[$-1]; - stack = stack[0..$-1]; + StringType top = stack[$ - 1]; + stack = stack[0 .. $ - 1]; return top == input; } else diff --git a/source/newxml/writer.d b/source/newxml/writer.d index 0c562f1..5076663 100644 --- a/source/newxml/writer.d +++ b/source/newxml/writer.d @@ -22,10 +22,12 @@ private string ifCompiles(string code) { return "static if (__traits(compiles, " ~ code ~ ")) " ~ code ~ ";\n"; } + private string ifCompilesElse(string code, string fallback) { return "static if (__traits(compiles, " ~ code ~ ")) " ~ code ~ "; else " ~ fallback ~ ";\n"; } + private string ifAnyCompiles(string code, string[] codes...) { if (codes.length == 0) @@ -34,8 +36,8 @@ private string ifAnyCompiles(string code, string[] codes...) } else { - return "static if (__traits(compiles, " ~ code ~ ")) " ~ code ~ - "; else " ~ ifAnyCompiles(codes[0], codes[1..$]); + return "static if (__traits(compiles, " ~ code ~ ")) " ~ code ~ "; else " + ~ ifAnyCompiles(codes[0], codes[1 .. $]); } } @@ -46,14 +48,14 @@ private auto xmlDeclarationAttributes(StringType, Args...)(Args args) // version specification static if (is(Args[0] == int)) { - assert(args[0] == 10 || args[0] == 11, "Invalid xml version specified"); + enforce(args[0] == 10 || args[0] == 11, "Invalid xml version specified"); StringType versionString = args[0] == 10 ? "1.0" : "1.1"; - auto args1 = args[1..$]; + auto args1 = args[1 .. $]; } else static if (is(Args[0] == StringType)) { StringType versionString = args[0]; - auto args1 = args[1..$]; + auto args1 = args[1 .. $]; } else { @@ -65,7 +67,7 @@ private auto xmlDeclarationAttributes(StringType, Args...)(Args args) static if (is(typeof(args1[0]) == StringType)) { auto encodingString = args1[0]; - auto args2 = args1[1..$]; + auto args2 = args1[1 .. $]; } else { @@ -77,7 +79,7 @@ private auto xmlDeclarationAttributes(StringType, Args...)(Args args) static if (is(typeof(args2[0]) == bool)) { StringType standaloneString = args2[0] ? "yes" : "no"; - auto args3 = args2[1..$]; + auto args3 = args2[1 .. $]; } else { @@ -87,8 +89,8 @@ private auto xmlDeclarationAttributes(StringType, Args...)(Args args) // catch other erroneous parameters static assert(typeof(args3).length == 0, - "Unrecognized attribute type for xml declaration: " - ~ typeof(args3[0]).stringof); + "Unrecognized attribute type for xml declaration: " ~ typeof(args3[0]) + .stringof); return tuple(versionString, encodingString, standaloneString); } @@ -123,12 +125,19 @@ struct PrettyPrinters uint indentation; enum StringType tab = "\t"; - void decreaseLevel() { indentation--; } - void increaseLevel() { indentation++; } + void decreaseLevel() + { + indentation--; + } + + void increaseLevel() + { + indentation++; + } void beforeNode(Out)(ref Out output) { - foreach (i; 0..indentation) + foreach (i; 0 .. indentation) { output ~= tab; } @@ -193,7 +202,7 @@ struct PrettyPrinters + validateTagOrder = If set to `Yes`, then tag order will be validated during writing. +/ struct Writer(_StringType, alias PrettyPrinter = PrettyPrinters.Minimalizer) - if(is(_StringType == string) || is(_StringType == wstring) || is(_StringType == dstring)) + if (is(_StringType == string) || is(_StringType == wstring) || is(_StringType == dstring)) { alias StringType = _StringType; @@ -207,35 +216,38 @@ struct Writer(_StringType, alias PrettyPrinter = PrettyPrinters.Minimalizer) } else { - static assert(0, "Invalid pretty printer type for string type " ~ StringType.stringof); + static assert(0, "Invalid pretty printer type for string type " ~ StringType + .stringof); } StringType output; - bool startingTag = false, insideDTD = false; + bool startingTag = false; + bool insideDTD = false; this(typeof(prettyPrinter) pretty) { - prettyPrinter = pretty; + this.prettyPrinter = pretty; } private template expand(string methodName) { import std.meta : AliasSeq; - alias expand = AliasSeq!( - "prettyPrinter." ~ methodName ~ "(output)", - "output ~= prettyPrinter." ~ methodName - ); + + alias expand = AliasSeq!("prettyPrinter." ~ methodName ~ "(output)", + "output ~= prettyPrinter." ~ methodName); } + private template formatAttribute(string attribute) { import std.meta : AliasSeq; + alias formatAttribute = AliasSeq!( - "prettyPrinter.formatAttribute(output, " ~ attribute ~ ")", - "output ~= prettyPrinter.formatAttribute(" ~ attribute ~ ")", - "defaultFormatAttribute(" ~ attribute ~ ", prettyPrinter.attributeDelimiter)", - "defaultFormatAttribute(" ~ attribute ~ ")" - ); + "prettyPrinter.formatAttribute(output, " ~ attribute ~ ")", + "output ~= prettyPrinter.formatAttribute(" ~ attribute ~ ")", + "defaultFormatAttribute(" + ~ attribute ~ ", prettyPrinter.attributeDelimiter)", + "defaultFormatAttribute(" ~ attribute ~ ")"); } private void defaultFormatAttribute(StringType attribute, StringType delimiter = "'") @@ -293,6 +305,7 @@ struct Writer(_StringType, alias PrettyPrinter = PrettyPrinters.Minimalizer) output ~= "?>"; mixin(ifAnyCompiles(expand!"afterNode")); } + void writeXMLDeclaration(StringType version_, StringType encoding, StringType standalone) { output ~= ""; @@ -358,10 +368,7 @@ struct Writer(_StringType, alias PrettyPrinter = PrettyPrinters.Minimalizer) closeOpenThings; mixin(ifAnyCompiles(expand!"beforeNode")); - mixin(ifCompilesElse( - "prettyPrinter.formatText(output, comment)", - "output ~= text" - )); + mixin(ifCompilesElse("prettyPrinter.formatText(output, comment)", "output ~= text")); mixin(ifAnyCompiles(expand!"afterNode")); } /++ @@ -417,13 +424,11 @@ struct Writer(_StringType, alias PrettyPrinter = PrettyPrinters.Minimalizer) output ~= tagName; startingTag = true; } + void closeElement(StringType tagName) { bool selfClose; - mixin(ifCompilesElse( - "selfClose = prettyPrinter.selfClosingElements", - "selfClose = true" - )); + mixin(ifCompilesElse("selfClose = prettyPrinter.selfClosingElements", "selfClose = true")); if (selfClose && startingTag) { @@ -444,6 +449,7 @@ struct Writer(_StringType, alias PrettyPrinter = PrettyPrinters.Minimalizer) } mixin(ifAnyCompiles(expand!"afterNode")); } + void writeAttribute(StringType name, StringType value) { assert(startingTag, "Cannot write attribute outside element start"); @@ -469,6 +475,7 @@ struct Writer(_StringType, alias PrettyPrinter = PrettyPrinters.Minimalizer) mixin(ifAnyCompiles(expand!"afterNode")); mixin(ifCompiles("prettyPrinter.increaseLevel")); } + void closeDoctype() { assert(insideDTD); @@ -479,9 +486,10 @@ struct Writer(_StringType, alias PrettyPrinter = PrettyPrinters.Minimalizer) output ~= "]>"; mixin(ifAnyCompiles(expand!"afterNode")); } + void writeDeclaration(StringType decl, StringType content) { - //assert(insideDTD); + assert(insideDTD); mixin(ifAnyCompiles(expand!"beforeNode")); output ~= "", writer.output); - - //static assert(isWriter!(typeof(writer))); + assert(writer.output == "", + writer.output); } -unittest +@safe unittest { import std.array : Appender; import std.typecons : refCounted; @@ -526,6 +531,7 @@ unittest writer.closeElement("elem"); import std.string : lineSplitter; + auto splitter = writer.output.lineSplitter; assert(splitter.front == "", splitter.front); @@ -546,7 +552,6 @@ unittest } import dom = newxml.dom; -import newxml.domstring; /++ + Outputs the entire DOM tree rooted at `node` using the given `writer`. @@ -554,75 +559,82 @@ import newxml.domstring; void writeDOM(WriterType)(auto ref WriterType writer, dom.Node node) { import std.traits : ReturnType; + import std.conv : to; import newxml.faststrings; + alias Document = typeof(node.ownerDocument); alias Element = ReturnType!(Document.documentElement); alias StringType = writer.StringType; switch (node.nodeType) with (dom.NodeType) { - case document: - auto doc = cast(Document)node; - DOMString xmlVersion = doc.xmlVersion, xmlEncoding = doc.xmlEncoding; - writer.writeXMLDeclaration(xmlVersion ? xmlVersion.transcodeTo!StringType() : null, - xmlEncoding ? xmlEncoding.transcodeTo!StringType() : null, doc.xmlStandalone); - foreach (child; doc.childNodes) - { - writer.writeDOM(child); - } - break; - case element: - auto elem = cast(Element)node; - writer.startElement(elem.tagName.transcodeTo!StringType); - if (elem.hasAttributes) - { - foreach (attr; elem.attributes) - { - writer.writeAttribute(attr.nodeName.transcodeTo!StringType, - xmlEscape(attr.nodeValue.transcodeTo!StringType)); - } - } - foreach (child; elem.childNodes) + case document: + auto doc = cast(Document) node; + string xmlVersion = doc.xmlVersion; + string xmlEncoding = doc.xmlEncoding; + writer.writeXMLDeclaration(xmlVersion + ? xmlVersion.to!StringType() : null, + xmlEncoding ? xmlEncoding.to!StringType() : null, doc + .xmlStandalone); + foreach (child; doc.childNodes) + { + writer.writeDOM(child); + } + break; + case element: + auto elem = cast(Element) node; + writer.startElement(elem.tagName.to!StringType); + if (elem.hasAttributes) + { + foreach (attr; elem.attributes) { - writer.writeDOM(child); + writer.writeAttribute(attr.nodeName.to!StringType, + xmlEscape(attr.nodeValue.to!StringType)); } - writer.closeElement(elem.tagName.transcodeTo!StringType); - break; - case text: - writer.writeText(xmlEscape(node.nodeValue.transcodeTo!StringType)); - break; - case cdataSection: - writer.writeCDATA(xmlEscape(node.nodeValue.transcodeTo!StringType)); - break; - case comment: - writer.writeComment(node.nodeValue.transcodeTo!StringType); - break; - default: - break; + } + foreach (child; elem.childNodes) + { + writer.writeDOM(child); + } + writer.closeElement(elem.tagName.to!StringType); + break; + case text: + writer.writeText(xmlEscape(node.nodeValue.to!StringType)); + break; + case cdataSection: + writer.writeCDATA(xmlEscape(node.nodeValue.to!StringType)); + break; + case comment: + writer.writeComment(node.nodeValue.to!StringType); + break; + default: + break; } } -unittest +@safe unittest { import newxml.domimpl; - Writer!(string, PrettyPrinters.Minimalizer) wrt = Writer!(string)(PrettyPrinters.Minimalizer!string()); + + Writer!(string, PrettyPrinters.Minimalizer) wrt = Writer!(string)( + PrettyPrinters.Minimalizer!string()); dom.DOMImplementation domimpl = new DOMImplementation; - dom.Document doc = domimpl.createDocument(null, new DOMString("doc"), null); - dom.Element e0 = doc.createElement(new DOMString("text")); + dom.Document doc = domimpl.createDocument(null, "doc", null); + dom.Element e0 = doc.createElement("text"); doc.firstChild.appendChild(e0); - e0.setAttribute(new DOMString("something"), new DOMString("other thing")); - e0.appendChild(doc.createTextNode(new DOMString("Some text "))); - dom.Element e1 = doc.createElement(new DOMString("b")); - e1.appendChild(doc.createTextNode(new DOMString("with"))); + e0.setAttribute("something", "other thing"); + e0.appendChild(doc.createTextNode("Some text ")); + dom.Element e1 = doc.createElement("b"); + e1.appendChild(doc.createTextNode("with")); e0.appendChild(e1); - e0.appendChild(doc.createTextNode(new DOMString(" markup."))); + e0.appendChild(doc.createTextNode(" markup.")); wrt.writeDOM(doc); assert(wrt.output == "Some text with markup." - , wrt.output); + ~ "something='other thing'>Some text with markup.", + wrt.output); } import std.typecons : Flag, No, Yes; @@ -638,8 +650,8 @@ import std.typecons : Flag, No, Yes; + has to wait for other nodes to be ready (e.g. if the cursor input is generated + programmatically). +/ -auto writeCursor(Flag!"useFiber" useFiber = No.useFiber, WriterType, CursorType) - (auto ref WriterType writer, auto ref CursorType cursor) +auto writeCursor(Flag!"useFiber" useFiber = No.useFiber, WriterType, CursorType)( + auto ref WriterType writer, auto ref CursorType cursor) { alias StringType = WriterType.StringType; void inspectOneLevel() @safe @@ -648,87 +660,87 @@ auto writeCursor(Flag!"useFiber" useFiber = No.useFiber, WriterType, CursorType) { switch (cursor.kind) with (XMLKind) { - case document: - StringType version_; - StringType encoding; - StringType standalone; - foreach (attr; cursor.attributes) - { - if (attr.name == "version") - { - version_ = attr.value; - } - else if (attr.name == "encoding") - { - encoding = attr.value; - } - else if (attr.name == "standalone") - { - standalone = attr.value; - } - } - writer.writeXMLDeclaration(version_, encoding, standalone); - if (cursor.enter) - { - inspectOneLevel(); - cursor.exit; - } - break; - case dtdEmpty: - case dtdStart: - writer.startDoctype(cursor.wholeContent); - if (cursor.enter) + case document: + StringType version_; + StringType encoding; + StringType standalone; + foreach (attr; cursor.attributes) + { + if (attr.name == "version") { - inspectOneLevel(); - cursor.exit; + version_ = attr.value; } - writer.closeDoctype(); - break; - case attlistDecl: - writer.writeDeclaration("ATTLIST", cursor.wholeContent); - break; - case elementDecl: - writer.writeDeclaration("ELEMENT", cursor.wholeContent); - break; - case entityDecl: - writer.writeDeclaration("ENTITY", cursor.wholeContent); - break; - case notationDecl: - writer.writeDeclaration("NOTATION", cursor.wholeContent); - break; - case declaration: - writer.writeDeclaration(cursor.name, cursor.content); - break; - case text: - writer.writeText(cursor.content); - break; - case cdata: - writer.writeCDATA(cursor.content); - break; - case comment: - writer.writeComment(cursor.content); - break; - case processingInstruction: - writer.writeProcessingInstruction(cursor.name, cursor.content); - break; - case elementStart: - case elementEmpty: - writer.startElement(cursor.name); - for (auto attrs = cursor.attributes; !attrs.empty; attrs.popFront) + else if (attr.name == "encoding") { - auto attr = attrs.front; - writer.writeAttribute(attr.name, attr.value); + encoding = attr.value; } - if (cursor.enter) + else if (attr.name == "standalone") { - inspectOneLevel(); - cursor.exit; + standalone = attr.value; } - writer.closeElement(cursor.name); - break; - default: - break; - //assert(0); + } + writer.writeXMLDeclaration(version_, encoding, standalone); + if (cursor.enter) + { + inspectOneLevel(); + cursor.exit; + } + break; + case dtdEmpty: + case dtdStart: + writer.startDoctype(cursor.wholeContent); + if (cursor.enter) + { + inspectOneLevel(); + cursor.exit; + } + writer.closeDoctype(); + break; + case attlistDecl: + writer.writeDeclaration("ATTLIST", cursor.wholeContent); + break; + case elementDecl: + writer.writeDeclaration("ELEMENT", cursor.wholeContent); + break; + case entityDecl: + writer.writeDeclaration("ENTITY", cursor.wholeContent); + break; + case notationDecl: + writer.writeDeclaration("NOTATION", cursor.wholeContent); + break; + case declaration: + writer.writeDeclaration(cursor.name, cursor.content); + break; + case text: + writer.writeText(cursor.content); + break; + case cdata: + writer.writeCDATA(cursor.content); + break; + case comment: + writer.writeComment(cursor.content); + break; + case processingInstruction: + writer.writeProcessingInstruction(cursor.name, cursor.content); + break; + case elementStart: + case elementEmpty: + writer.startElement(cursor.name); + for (auto attrs = cursor.attributes; !attrs.empty; attrs.popFront) + { + auto attr = attrs.front; + writer.writeAttribute(attr.name, attr.value); + } + if (cursor.enter) + { + inspectOneLevel(); + cursor.exit; + } + writer.closeElement(cursor.name); + break; + default: + break; + //assert(0); } } while (cursor.next); @@ -736,16 +748,19 @@ auto writeCursor(Flag!"useFiber" useFiber = No.useFiber, WriterType, CursorType) static if (useFiber) { - import core.thread: Fiber; + import core.thread : Fiber; + auto fiber = new Fiber(&inspectOneLevel); fiber.call; return fiber; } else + { inspectOneLevel(); + } } -unittest +@safe unittest { import std.array : Appender; import newxml.parser; @@ -753,15 +768,10 @@ unittest import newxml.lexers; import std.typecons : refCounted; - string xml = - "\n" ~ - "\n" ~ - "\t\n" ~ - "\t\n" ~ - "\t\n" ~ - "\t\n" ~ - "]>\n"; + string xml = "\n" ~ "\n" ~ "\t\n" + ~ "\t\n" ~ "\t\n" + ~ "\t\n" ~ "]>\n"; auto cursor = xml.lexer.parser.cursor; cursor.setSource(xml); @@ -781,10 +791,12 @@ unittest + `withValidation`. +/ struct CheckedWriter(WriterType, CursorType = void) - if (isWriter!(WriterType) && (is(CursorType == void) || - (isCursor!CursorType && is(WriterType.StringType == CursorType.StringType)))) + if (isWriter!(WriterType) && (is(CursorType == void) + || (isCursor!CursorType + && is(WriterType.StringType == CursorType.StringType)))) { import core.thread : Fiber; + private Fiber fiber; private bool startingTag = false; @@ -797,7 +809,7 @@ struct CheckedWriter(WriterType, CursorType = void) { struct Cursor { - import newxml.cursor: Attribute; + import newxml.cursor : Attribute; import std.container.array; alias StringType = WriterType.StringType; @@ -811,23 +823,28 @@ struct CheckedWriter(WriterType, CursorType = void) void _setName(StringType name) { import newxml.faststrings; + _name = name; auto i = name.indexOf(':'); - colon = (i > 0) - ? i - : 0; + colon = (i > 0) ? i : 0; } + void _addAttribute(StringType name, StringType value) { attrs.insertBack(Attribute!StringType(name, value)); } + void _setKind(XMLKind kind) { _kind = kind; initialized = true; attrs.clear; } - void _setContent(StringType content) { _content = content; } + + void _setContent(StringType content) + { + _content = content; + } auto kind() { @@ -838,15 +855,30 @@ struct CheckedWriter(WriterType, CursorType = void) return _kind; } - auto name() { return _name; } - auto prefix() { return _name[0..colon]; } - auto content() { return _content; } - auto attributes() { return attrs[]; } + + auto name() + { + return _name; + } + + auto prefix() + { + return _name[0 .. colon]; + } + + auto content() + { + return _content; + } + + auto attributes() + { + return attrs[]; + } + StringType localName() { - return colon - ? _name[colon+1..$] - : []; + return colon ? _name[colon + 1 .. $] : []; } bool enter() @@ -865,28 +897,39 @@ struct CheckedWriter(WriterType, CursorType = void) Fiber.yield; return _kind != XMLKind.elementEnd; } + bool next() { Fiber.yield; return _kind != XMLKind.elementEnd; } - void exit() {} + + void exit() + { + } + bool atBeginning() { return !initialized || _kind == XMLKind.document; } - bool documentEnd() { return false; } + + bool documentEnd() + { + return false; + } alias InputType = void*; StringType wholeContent() { enforce(false, "Cannot call wholeContent on this type of cursor"); } + void setSource(InputType) { enforce(false, "Cannot set the source of this type of cursor"); } } + Cursor cursor; } else @@ -912,6 +955,7 @@ struct CheckedWriter(WriterType, CursorType = void) } fiber.call; } + void writeXMLDeclaration(StringType version_, StringType encoding, StringType standalone) { cursor._setKind(XMLKind.document); @@ -929,6 +973,7 @@ struct CheckedWriter(WriterType, CursorType = void) } fiber.call; } + void writeComment(StringType text) { if (startingTag) @@ -940,6 +985,7 @@ struct CheckedWriter(WriterType, CursorType = void) cursor._setContent(text); fiber.call; } + void writeText(StringType text) { if (startingTag) @@ -951,6 +997,7 @@ struct CheckedWriter(WriterType, CursorType = void) cursor._setContent(text); fiber.call; } + void writeCDATA(StringType text) { if (startingTag) @@ -962,6 +1009,7 @@ struct CheckedWriter(WriterType, CursorType = void) cursor._setContent(text); fiber.call; } + void writeProcessingInstruction(StringType target, StringType data) { if (startingTag) @@ -974,6 +1022,7 @@ struct CheckedWriter(WriterType, CursorType = void) cursor._setContent(data); fiber.call; } + void startElement(StringType tag) { if (startingTag) @@ -985,6 +1034,7 @@ struct CheckedWriter(WriterType, CursorType = void) cursor._setKind(XMLKind.elementStart); cursor._setName(tag); } + void closeElement(StringType tag) { if (startingTag) @@ -996,6 +1046,7 @@ struct CheckedWriter(WriterType, CursorType = void) cursor._setName(tag); fiber.call; } + void writeAttribute(StringType name, StringType value) { assert(startingTag); diff --git a/testsource/test.d b/testsource/test.d index fc7dc92..98ca991 100644 --- a/testsource/test.d +++ b/testsource/test.d @@ -16,7 +16,7 @@ import std.path; import std.stdio: write, writeln; import std.utf: UTFException; -auto indexes = +auto indexes = [ "tests/sun/sun-valid.xml", "tests/sun/sun-error.xml", @@ -44,7 +44,7 @@ struct Results { int[string] totals; int[string] wrong; - + static Results opCall() { Results result; @@ -52,7 +52,7 @@ struct Results result.wrong = ["valid": 0, "linted":0, "invalid": 0, "not-wf": 0, "error": 0]; return result; } - + void opOpAssign(string op)(Results other) { static if (op == "+") @@ -106,7 +106,7 @@ Results handleTestcases(T)(string directory, ref T cursor, int depth) if (att.name == "PROFILE") write(" -- ", att.value); writeln(); - + if (cursor.enter()) { results += handleTestcases(directory, cursor, depth + 1); @@ -127,7 +127,7 @@ Results handleTestcases(T)(string directory, ref T cursor, int depth) Results handleTest(T)(string directory, ref T cursor, int depth) { auto result = Results(); - + string file, kind; foreach (att; cursor.attributes) if (att.name == "ENTITIES" && att.value != "none") @@ -139,9 +139,9 @@ Results handleTest(T)(string directory, ref T cursor, int depth) kind = att.value; else if (att.name == "URI") file = att.value; - + result.totals[kind]++; - + bool passed = true, linted = false, linted_ok = false; try { @@ -193,7 +193,7 @@ Results handleTest(T)(string directory, ref T cursor, int depth) } } } - + return result; } @@ -218,22 +218,22 @@ void uselessCallback(CursorError err) +/ void main() { - auto cursor = + auto cursor = chooseLexer!string .parser .cursor(); // If an index is not well-formed, just tell us but continue parsing - + auto results = Results(); foreach (i, index; indexes) { writeln(i, " -- ", index); - + cursor.setSource(readText(index)); cursor.enter(); - + results += handleTestcases(dirName(index), cursor, 1); } - + printResults(results, 0); writeln(); } @@ -249,7 +249,7 @@ bool parseFile(string filename, ref bool lint) inspectOneLevel(cursor); cursor.exit(); } - + } while (cursor.next()); }+/ @@ -265,7 +265,7 @@ bool parseFile(string filename, ref bool lint) { auto raw = read(filename); auto bytes = cast(ubyte[])raw; - + if(bytes.length > 1 && bytes[0] == 0xFF && bytes[1] == 0xFE) { auto shorts = cast(ushort[])raw; @@ -279,28 +279,28 @@ bool parseFile(string filename, ref bool lint) throw new MyException("AAAAHHHHH"); } } - + auto something = parseXMLString(text); //auto cursor = text.lexer.parser.cursor; // lots of tests do not have an xml declaration //auto dombuilder = domBuilder(cursor, new domimpl.DOMImplementation); - + //cursor.setSource(text); - + //lint = false; /+foreach (attr; cursor.attributes) if (attr.name == "version" && attr.value == "1.0") lint = true;+/ - + //dombuilder.build; //inspectOneLevel(cursor); - + /+if (lint) { import std.process, std.stdio, std.array; import newxml.writer; - + lint = false; bool result = false; auto xmllint = executeShell("xmllint --pretty 2 --c14n11 " ~ filename ~ " > linted_input.xml"); @@ -313,12 +313,12 @@ bool parseFile(string filename, ref bool lint) auto writer = Writer!(string)(); writer.writeDOM(dombuilder.getDocument); file.write(writer.output); - + //writer.setSink(ltw); //writer.writeCursor(cursor); } - + xmllint = executeShell("xmllint --pretty 2 --c14n11 output.xml > linted_output.xml"); if (xmllint.status == 0) {