parse static method
Implementation
static UEpubNode? parse(String source) {
if (source.trim().isEmpty) return null;
final UEpubNode root = UEpubNode(tag: "#root");
final List<UEpubNode> stack = <UEpubNode>[root];
int index = 0;
int guard = 0;
while (index < source.length && guard < 2000000) {
guard++;
final int open = source.indexOf("<", index);
if (open < 0) {
_addText(stack.last, source.substring(index));
break;
}
if (open > index) _addText(stack.last, source.substring(index, open));
if (source.startsWith("<!--", open)) {
final int end = source.indexOf("-->", open);
index = end < 0 ? source.length : end + 3;
continue;
}
if (source.startsWith("<![CDATA[", open)) {
final int end = source.indexOf("]]>", open);
final int stop = end < 0 ? source.length : end;
_addText(stack.last, source.substring(open + 9, stop));
index = end < 0 ? source.length : end + 3;
continue;
}
if (source.startsWith("<!", open) || source.startsWith("<?", open)) {
final int end = source.indexOf(">", open);
index = end < 0 ? source.length : end + 1;
continue;
}
final int close = _findTagEnd(source, open);
if (close < 0) {
_addText(stack.last, source.substring(open));
break;
}
final String raw = source.substring(open + 1, close).trim();
index = close + 1;
if (raw.isEmpty) continue;
if (raw.startsWith("/")) {
final String name = _localName(raw.substring(1).trim());
for (int i = stack.length - 1; i > 0; i--) {
if (stack[i].tag == name) {
stack.removeRange(i, stack.length);
break;
}
}
continue;
}
final bool selfClosing = raw.endsWith("/");
final String body = selfClosing ? raw.substring(0, raw.length - 1) : raw;
final int space = body.indexOf(RegExp(r"\s"));
final String name = _localName(space < 0 ? body : body.substring(0, space));
final Map<String, String> attributes = space < 0 ? <String, String>{} : _attributes(body.substring(space + 1));
final UEpubNode node = UEpubNode(tag: name, attributes: attributes);
node.parent = stack.last;
stack.last.children.add(node);
if (!selfClosing && !_void.contains(name)) stack.add(node);
if (stack.length > 200) stack.removeRange(1, stack.length - 100);
}
return root;
}