970fdb2000-12-20Marcus Comstedt #pike __REAL_VERSION__ class DOMException(int code) { // ExceptionCode constant INDEX_SIZE_ERR = 1; constant DOMSTRING_SIZE_ERR = 2; constant HIERARCHY_REQUEST_ERR = 3; constant WRONG_DOCUMENT_ERR = 4; constant INVALID_CHARACTER_ERR = 5; constant NO_DATA_ALLOWED_ERR = 6; constant NO_MODIFICATION_ALLOWED_ERR = 7; constant NOT_FOUND_ERR = 8; constant NOT_SUPPORTED_ERR = 9; constant INUSE_ATTRIBUTE_ERR = 10; constant is_generic_error = 1; constant is_dom_exception = 1;
9eaf1d2008-06-28Martin Nilsson  protected array backtrace = predef::backtrace()[..<2];
970fdb2000-12-20Marcus Comstedt 
9eaf1d2008-06-28Martin Nilsson  protected constant symbolic = ([
970fdb2000-12-20Marcus Comstedt  INDEX_SIZE_ERR: "INDEX_SIZE_ERR", DOMSTRING_SIZE_ERR: "DOMSTRING_SIZE_ERR", HIERARCHY_REQUEST_ERR: "HIERARCHY_REQUEST_ERR", WRONG_DOCUMENT_ERR: "WRONG_DOCUMENT_ERR", INVALID_CHARACTER_ERR: "INVALID_CHARACTER_ERR", NO_DATA_ALLOWED_ERR: "NO_DATA_ALLOWED_ERR", NO_MODIFICATION_ALLOWED_ERR: "NO_MODIFICATION_ALLOWED_ERR", NOT_FOUND_ERR: "NOT_FOUND_ERR", NOT_SUPPORTED_ERR: "NOT_SUPPORTED_ERR", INUSE_ATTRIBUTE_ERR: "INUSE_ATTRIBUTE_ERR", ]);
9eaf1d2008-06-28Martin Nilsson  protected constant human_readable = ([
970fdb2000-12-20Marcus Comstedt  INDEX_SIZE_ERR: "index out of range", DOMSTRING_SIZE_ERR: "specified range of text does not fit into DOMString", HIERARCHY_REQUEST_ERR: "node inserted somewhere it doesn't belong", WRONG_DOCUMENT_ERR: "node used in a document that doesn't support it", INVALID_CHARACTER_ERR: "an invalid or illegal character was specified", NO_DATA_ALLOWED_ERR: "data was specified for a node which does not support data", NO_MODIFICATION_ALLOWED_ERR: "an unallowed attempt was made to modify an object", NOT_FOUND_ERR: "node does not exist in this context", NOT_SUPPORTED_ERR: "implementation does not support the type of object requested", INUSE_ATTRIBUTE_ERR: "attempted to add an attribute that is already in use", ]);
9eaf1d2008-06-28Martin Nilsson  protected string|array `[] (int i)
970fdb2000-12-20Marcus Comstedt  { switch (i) { case 0: return "DOMException: " + (human_readable[code] || ("unknown exception "+code)) + "\n"; case 1: return backtrace; } }
74b59e2015-12-20Martin Nilsson  protected string _sprintf(int mode)
970fdb2000-12-20Marcus Comstedt  {
1a38432002-11-29Martin Nilsson  return mode == 'O' && sprintf("%O(%s)", this_program, (string)(symbolic[code]||code) );
970fdb2000-12-20Marcus Comstedt  } }; class DOMImplementation { int has_feature(string feature, string|void version) { return lower_case(feature)=="xml" && (version == "1.0" || !version); }
5e40c92001-01-16Marcus Comstedt  Document create_document(string namespace_uri, string qualified_name, DocumentType|void doctype) {
563bd72004-01-11Martin Nilsson  return Document(this, namespace_uri, qualified_name, doctype);
5e40c92001-01-16Marcus Comstedt  } DocumentType create_document_type(string qualified_name, string|void public_id, string|void system_id) {
563bd72004-01-11Martin Nilsson  return DocumentType(this, qualified_name, public_id, system_id);
5e40c92001-01-16Marcus Comstedt  }
970fdb2000-12-20Marcus Comstedt } class NodeList {
9eaf1d2008-06-28Martin Nilsson  protected array(Node) nodes;
970fdb2000-12-20Marcus Comstedt 
9eaf1d2008-06-28Martin Nilsson  protected NodeList `+(NodeList nl) {
563bd72004-01-11Martin Nilsson  return NodeList(values(this)+values(nl));
970fdb2000-12-20Marcus Comstedt  }
9eaf1d2008-06-28Martin Nilsson  protected Node `[](int index) { return item(index); } protected int _sizeof() { return get_length(); }
ec1a0f2014-08-16Martin Nilsson  protected array(Node) cast(string to) {
d0cd6c2014-08-16Martin Nilsson  if(to == "array")
ec1a0f2014-08-16Martin Nilsson  return values(this); return UNDEFINED;
970fdb2000-12-20Marcus Comstedt  } Node item(int index) { return index >= 0 && index < sizeof(nodes) && nodes[index]; } int get_length() { return sizeof(nodes); }
9eaf1d2008-06-28Martin Nilsson  protected array(int) _indices() { return indices(nodes); } protected array(Node) _values() { return copy_value(nodes); } protected Array.Iterator _get_iterator() { return Array.Iterator(nodes); }
e121e32002-11-07Marcus Comstedt 
9eaf1d2008-06-28Martin Nilsson  protected void create(array(Node)|void elts) { nodes = elts || ({}); }
970fdb2000-12-20Marcus Comstedt } class NamedNodeMap {
9eaf1d2008-06-28Martin Nilsson  protected Document owner_document; protected mapping(string:Node) map = ([]);
970fdb2000-12-20Marcus Comstedt 
9eaf1d2008-06-28Martin Nilsson  protected int is_readonly() { return 0; } protected void bind(Node n) { } protected void unbind(Node n) { }
970fdb2000-12-20Marcus Comstedt  Node get_named_item(string name) { return map[name]; } Node set_named_item(Node arg) { if(arg->get_owner_document() != owner_document) throw(DOMException(DOMException.WRONG_DOCUMENT_ERR)); if(is_readonly()) throw(DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR)); Node old = map[arg->get_node_name()];
5e40c92001-01-16Marcus Comstedt  if(old == arg) return 0;
970fdb2000-12-20Marcus Comstedt  bind(arg); map[arg->get_node_name()] = arg; if(old) unbind(old); return old; } Node remove_named_item(string name) { Node old = map[name]; if(!old) throw(DOMException(DOMException.NOT_FOUND_ERR)); if(is_readonly()) throw(DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR)); m_delete(map, name); unbind(old); return old; }
9eaf1d2008-06-28Martin Nilsson  protected Node `[](int|string index)
5e40c92001-01-16Marcus Comstedt  { return stringp(index)? get_named_item(index) : item(index); }
9eaf1d2008-06-28Martin Nilsson  protected int _sizeof() { return get_length(); }
ec1a0f2014-08-16Martin Nilsson  protected mapping(string:Node) cast(string to) {
d0cd6c2014-08-16Martin Nilsson  if(to == "mapping")
ec1a0f2014-08-16Martin Nilsson  return copy_value(map); return UNDEFINED;
970fdb2000-12-20Marcus Comstedt  } Node item(int index) {
563bd72004-01-11Martin Nilsson  return values(this)[index];
970fdb2000-12-20Marcus Comstedt  } int get_length() { return sizeof(map); }
9eaf1d2008-06-28Martin Nilsson  protected array(string) _indices() { return indices(map); } protected array(Node) _values() { return values(map); } protected Mapping.Iterator _get_iterator() { return Mapping.Iterator(map); }
970fdb2000-12-20Marcus Comstedt 
9eaf1d2008-06-28Martin Nilsson  protected void create(Document owner)
970fdb2000-12-20Marcus Comstedt  { owner_document = owner; } } class Node { // NodeType constant ELEMENT_NODE = 1; constant ATTRIBUTE_NODE = 2; constant TEXT_NODE = 3; constant CDATA_SECTION_NODE = 4; constant ENTITY_REFERENCE_NODE = 5; constant ENTITY_NODE = 6; constant PROCESSING_INSTRUCTION_NODE = 7; constant COMMENT_NODE = 8; constant DOCUMENT_NODE = 9; constant DOCUMENT_TYPE_NODE = 10; constant DOCUMENT_FRAGMENT_NODE = 11; constant NOTATION_NODE = 12;
9eaf1d2008-06-28Martin Nilsson  protected class NodeNodeList {
970fdb2000-12-20Marcus Comstedt  inherit NodeList; protected int search(Node n) { return predef::search(nodes, n); } protected void insert_at(int pos, array(Node) ns) { nodes = nodes[..pos-1] + ns + nodes[pos..]; } protected void delete_at(int pos) { nodes = nodes[..pos-1]+nodes[pos+1..]; } }
9eaf1d2008-06-28Martin Nilsson  protected Node parent_node; protected NodeNodeList child_nodes; protected Document owner_document;
970fdb2000-12-20Marcus Comstedt  protected int is_readonly() { return parent_node && parent_node->is_readonly(); } string get_node_value() { return 0; } void set_node_value(string value) { } string get_node_name(); int get_node_type(); Node get_parent_node() { return parent_node; } NodeList get_child_nodes() { return child_nodes || (child_nodes = NodeNodeList()); } Node get_first_child() { return child_nodes && sizeof(child_nodes) && child_nodes[0]; } Node get_last_child() { return child_nodes && sizeof(child_nodes) && child_nodes[-1]; } Node get_previous_sibling(Node|void node) { if(!node)
563bd72004-01-11Martin Nilsson  return parent_node && parent_node->get_previous_sibling(this);
970fdb2000-12-20Marcus Comstedt  if(!child_nodes) return 0; int pos = child_nodes->search(node); return pos > 0 && child_nodes[pos-1]; } Node get_next_sibling(Node|void node) { if(!node)
563bd72004-01-11Martin Nilsson  return parent_node && parent_node->get_next_sibling(this);
970fdb2000-12-20Marcus Comstedt  if(!child_nodes) return 0; int pos = child_nodes->search(node); return pos >= 0 && pos < sizeof(child_nodes)-1 && child_nodes[pos+1]; } NamedNodeMap get_attributes() { return 0; } Document get_owner_document() { return owner_document; }
9eaf1d2008-06-28Martin Nilsson  protected int child_is_allowed(Node child) { return 0; }
970fdb2000-12-20Marcus Comstedt 
9eaf1d2008-06-28Martin Nilsson  protected int is_ancestor(Node node)
970fdb2000-12-20Marcus Comstedt  {
563bd72004-01-11Martin Nilsson  Node loc = this;
970fdb2000-12-20Marcus Comstedt  while(loc) { if(node == loc) return 1; loc = loc->get_parent_node(); } return 0; } protected void _set_parent(Node new_parent) { if(new_parent && parent_node)
563bd72004-01-11Martin Nilsson  parent_node->remove_child(this);
970fdb2000-12-20Marcus Comstedt  parent_node = new_parent; } Node insert_before(Node new_child, Node ref_child) { int pos = ref_child? (child_nodes? child_nodes->search(ref_child) : -1) : child_nodes && sizeof(child_nodes); if(pos<0) throw(DOMException(DOMException.NOT_FOUND_ERR)); if(is_ancestor(new_child)) throw(DOMException(DOMException.HIERARCHY_REQUEST_ERR)); if(is_readonly()) throw(DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR));
5e40c92001-01-16Marcus Comstedt  if(new_child->get_owner_document() != owner_document) if(get_node_type() == DOCUMENT_NODE && new_child->get_node_type() == DOCUMENT_TYPE_NODE && !new_child->get_owner_document() && functionp(new_child->_set_owner_document))
563bd72004-01-11Martin Nilsson  new_child->_set_owner_document(this);
5e40c92001-01-16Marcus Comstedt  else throw(DOMException(DOMException.WRONG_DOCUMENT_ERR));
3524712015-05-26Martin Nilsson 
970fdb2000-12-20Marcus Comstedt  if(new_child->get_node_type() == DOCUMENT_FRAGMENT_NODE) { array(Node) new_children = values(new_child->get_child_nodes()); foreach(new_children, Node nc) if(!child_is_allowed(nc)) throw(DOMException(DOMException.HIERARCHY_REQUEST_ERR)); foreach(new_children, Node nc)
563bd72004-01-11Martin Nilsson  nc->_set_parent(this);
970fdb2000-12-20Marcus Comstedt  get_child_nodes()->insert_at(pos, new_children); } else { if(!child_is_allowed(new_child)) throw(DOMException(DOMException.HIERARCHY_REQUEST_ERR));
563bd72004-01-11Martin Nilsson  new_child->_set_parent(this);
970fdb2000-12-20Marcus Comstedt  get_child_nodes()->insert_at(pos, ({new_child})); } return new_child; } Node replace_child(Node new_child, Node old_child) { insert_before(new_child, old_child); return remove_child(old_child); } Node remove_child(Node old_child) { int pos = child_nodes? child_nodes->search(old_child) : -1; if(pos<0) throw(DOMException(DOMException.NOT_FOUND_ERR)); if(is_readonly()) throw(DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR)); get_child_nodes()->delete_at(pos); old_child->_set_parent(0); return old_child; } Node append_child(Node new_child) { return insert_before(new_child, 0); } int has_child_nodes() { return child_nodes && sizeof(child_nodes) > 0; } Node clone_node(int|void deep) { throw(DOMException(DOMException.NOT_SUPPORTED_ERR)); }
74b59e2015-12-20Martin Nilsson  protected string _sprintf(int mode)
e121e32002-11-07Marcus Comstedt  { return mode == 'O' &&
563bd72004-01-11Martin Nilsson  sprintf("%O(%s)", this_program, get_node_name());
e121e32002-11-07Marcus Comstedt  }
970fdb2000-12-20Marcus Comstedt } class DocumentFragment { inherit Node; int get_node_type() { return DOCUMENT_FRAGMENT_NODE; } string get_node_name() { return "#document-fragment"; } Node clone_node(int|void deep) { Node new = owner_document->create_document_fragment(); if(deep) foreach((array(Node))get_child_nodes(), Node n) new->append_child(n->clone_node(1)); return new; }
9eaf1d2008-06-28Martin Nilsson  protected int child_is_allowed(Node child)
970fdb2000-12-20Marcus Comstedt  { return (<ELEMENT_NODE,PROCESSING_INSTRUCTION_NODE, COMMENT_NODE,TEXT_NODE,CDATA_SECTION_NODE, ENTITY_REFERENCE_NODE>)[child->get_node_type()]; }
9eaf1d2008-06-28Martin Nilsson  protected void create(Document owner)
970fdb2000-12-20Marcus Comstedt  { owner_document = owner; } } class Document { inherit Node;
3524712015-05-26Martin Nilsson 
9eaf1d2008-06-28Martin Nilsson  protected program ElementImpl = Element; protected program DocumentFragmentImpl = DocumentFragment; protected program TextImpl = Text; protected program CommentImpl = Comment; protected program CDATASectionImpl = CDATASection; protected program ProcessingInstructionImpl = ProcessingInstruction; protected program AttrImpl = Attr; protected program EntityReferenceImpl = EntityReference; protected DOMImplementation implementation; protected string namespace_uri, qualified_name;
5e40c92001-01-16Marcus Comstedt 
970fdb2000-12-20Marcus Comstedt  int get_node_type() { return DOCUMENT_NODE; } string get_node_name() { return "#document"; } Document get_owner_document() { return 0; }
5e40c92001-01-16Marcus Comstedt  DOMImplementation get_implementation() { return implementation; }
970fdb2000-12-20Marcus Comstedt  DocumentType get_doctype() { foreach(values(get_child_nodes()), Node cn) if(cn->get_node_type() == DOCUMENT_TYPE_NODE) return cn; return 0; } Element get_document_element() { foreach(values(get_child_nodes()), Node cn) if(cn->get_node_type() == ELEMENT_NODE) return cn; return 0; } Element create_element(string tag_name) {
563bd72004-01-11Martin Nilsson  return ElementImpl(this, tag_name);
970fdb2000-12-20Marcus Comstedt  } DocumentFragment create_document_fragment() {
563bd72004-01-11Martin Nilsson  return DocumentFragmentImpl(this);
970fdb2000-12-20Marcus Comstedt  } Text create_text_node(string data) {
563bd72004-01-11Martin Nilsson  return TextImpl(this, data);
970fdb2000-12-20Marcus Comstedt  } Comment create_comment(string data) {
563bd72004-01-11Martin Nilsson  return CommentImpl(this, data);
970fdb2000-12-20Marcus Comstedt  } CDATASection create_cdata_section(string data) {
563bd72004-01-11Martin Nilsson  return CDATASectionImpl(this, data);
970fdb2000-12-20Marcus Comstedt  } ProcessingInstruction create_processing_instruction(string target, string data) {
563bd72004-01-11Martin Nilsson  return ProcessingInstructionImpl(this, target, data);
970fdb2000-12-20Marcus Comstedt  } Attr create_attribute(string name, string|void default_value) {
563bd72004-01-11Martin Nilsson  return AttrImpl(this, name, default_value);
970fdb2000-12-20Marcus Comstedt  } EntityReference create_entity_reference(string name) {
563bd72004-01-11Martin Nilsson  return EntityReferenceImpl(this, name);
970fdb2000-12-20Marcus Comstedt  } NodeList get_elements_by_tag_name(string tagname) { return get_document_element()->get_elements_by_tag_name(tagname); }
9eaf1d2008-06-28Martin Nilsson  protected int child_is_allowed(Node child)
970fdb2000-12-20Marcus Comstedt  { if(child->get_node_type() == ELEMENT_NODE) return !get_document_element(); else return (<PROCESSING_INSTRUCTION_NODE,COMMENT_NODE, DOCUMENT_TYPE_NODE>)[child->get_node_type()]; }
9eaf1d2008-06-28Martin Nilsson  protected void create(DOMImplementation i, string ns, string qn,
5e40c92001-01-16Marcus Comstedt  DocumentType|void doctype)
970fdb2000-12-20Marcus Comstedt  {
5e40c92001-01-16Marcus Comstedt  implementation = i; namespace_uri = ns; qualified_name = qn;
563bd72004-01-11Martin Nilsson  owner_document = this;
5e40c92001-01-16Marcus Comstedt  if(doctype) append_child(doctype);
970fdb2000-12-20Marcus Comstedt  } } class CharacterData { inherit Node;
9eaf1d2008-06-28Martin Nilsson  protected string node_value;
970fdb2000-12-20Marcus Comstedt  string get_node_value() { return node_value; } void set_node_value(string data) { if(is_readonly()) throw(DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR)); node_value=data; } string get_data() { return get_node_value(); } void set_data(string data) { set_node_value(data); }
ead9722003-01-20Martin Nilsson  int get_length() { return sizeof(get_data()); }
970fdb2000-12-20Marcus Comstedt  string substring_data(int offset, int count) { if(offset < 0 || offset > get_length() || count < 0) throw(DOMException(DOMException.INDEX_SIZE_ERR)); return get_data()[offset..offset+count-1]; } void append_data(string arg) { set_data(get_data()+arg); } void insert_data(int offset, string arg) { set_data(substring_data(0, offset) + arg + substring_data(offset, get_length())); } void delete_data(int offset, int count) { if(offset > get_length() || count < 0) throw(DOMException(DOMException.INDEX_SIZE_ERR)); if(offset + count >= get_length()) set_data(substring_data(0, offset)); else
3524712015-05-26Martin Nilsson  set_data(substring_data(0, offset) +
970fdb2000-12-20Marcus Comstedt  substring_data(offset+count, get_length())); } void replace_data(int offset, int count, string arg) { if(offset > get_length() || count < 0) throw(DOMException(DOMException.INDEX_SIZE_ERR)); if(offset + count >= get_length()) set_data(substring_data(0, offset) + arg); else set_data(substring_data(0, offset) + arg +
3524712015-05-26Martin Nilsson  substring_data(offset+count, get_length()));
970fdb2000-12-20Marcus Comstedt  }
ec1a0f2014-08-16Martin Nilsson  protected string cast(string to) { if(to == "string") return get_data(); return UNDEFINED; }
970fdb2000-12-20Marcus Comstedt } class Attr { inherit Node;
9eaf1d2008-06-28Martin Nilsson  protected string name; protected int specified = 1; protected Element bound_to;
970fdb2000-12-20Marcus Comstedt  int get_node_type() { return ATTRIBUTE_NODE; } string get_node_name() { return get_name(); } string get_name() { return name; } string get_value() { return get_node_value(); } void set_value(string value) { set_node_value(value); } int get_specified() { return specified; } void set_node_value(string value) { if(is_readonly()) throw(DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR)); specified = 1; foreach((array(Node))get_child_nodes(), Node n) remove_child(n); append_child(owner_document->create_text_node(value)); } string get_node_value() { return ((array(string))get_child_nodes())*""; } Node clone_node(int|void deep) { Node new = owner_document->create_attribute(get_name()); if(deep) foreach((array(Node))get_child_nodes(), Node n) new->append_child(n->clone_node(1)); return new; } protected void _set_parent(Node new_parent) { }
5e40c92001-01-16Marcus Comstedt  protected void _bind(Element new_element) { if(new_element && bound_to) throw(DOMException(DOMException.INUSE_ATTRIBUTE_ERR)); bound_to = new_element; }
9eaf1d2008-06-28Martin Nilsson  protected int child_is_allowed(Node child)
970fdb2000-12-20Marcus Comstedt  { return child->get_node_type() == TEXT_NODE || child->get_node_type() == ENTITY_REFERENCE_NODE; }
9eaf1d2008-06-28Martin Nilsson  protected void create(Document owner, string _name, string|void default_value)
970fdb2000-12-20Marcus Comstedt  { owner_document = owner; name = _name; if(default_value) { set_node_value(default_value); specified = 0; } } } class Element {
5e40c92001-01-16Marcus Comstedt  inherit Node : node;
970fdb2000-12-20Marcus Comstedt 
9eaf1d2008-06-28Martin Nilsson  protected string tag_name; protected NamedNodeMap attributes;
970fdb2000-12-20Marcus Comstedt 
9eaf1d2008-06-28Martin Nilsson  protected NamedNodeMap create_attributes()
970fdb2000-12-20Marcus Comstedt  { return class { inherit NamedNodeMap;
9eaf1d2008-06-28Martin Nilsson  protected int is_readonly() { return node::is_readonly(); } protected void bind(Node n)
5e40c92001-01-16Marcus Comstedt  {
563bd72004-01-11Martin Nilsson  n->_bind(function_object(this_program));
5e40c92001-01-16Marcus Comstedt  }
9eaf1d2008-06-28Martin Nilsson  protected void unbind(Node n) {
5e40c92001-01-16Marcus Comstedt  n->_bind(0); }
970fdb2000-12-20Marcus Comstedt  }(owner_document); } int get_node_type() { return ELEMENT_NODE; } string get_node_name() { return get_tag_name(); } string get_tag_name() { return tag_name; } NamedNodeMap get_attributes() { return attributes; } string get_attribute(string name) { Attr a = get_attribute_node(name); return (a && a->get_value()) || ""; } void set_attribute(string name, string value) { Attr a = get_attribute_node(name); if(a) a->set_value(value); else { a = owner_document->create_attribute(name); a->set_value(value); set_attribute_node(a); } } void remove_attribute(string name) { get_attributes()->remove_named_item(name); /* reinstate default ... */ } Attr get_attribute_node(string name) { return get_attributes()->get_named_item(name); } Attr set_attribute_node(Attr new_attr) { return get_attributes()->set_named_item(new_attr); } Attr remove_attribute_node(Attr old_attr) { if(get_attribute_node(old_attr->get_name())!=old_attr) throw(DOMException(DOMException.NOT_FOUND_ERR)); remove_attribute(old_attr->get_name()); return old_attr; } NodeList get_elements_by_tag_name(string name) { NodeList res = NodeList((name == "*" || name == get_tag_name()) &&
563bd72004-01-11Martin Nilsson  ({ this }));
970fdb2000-12-20Marcus Comstedt  foreach((array(Node))get_child_nodes(), Node n) if(n->get_node_type() == ELEMENT_NODE) res += values(n->get_elements_by_tag_name(name)); return res; }
9eaf1d2008-06-28Martin Nilsson  protected void low_normalize(Node n)
970fdb2000-12-20Marcus Comstedt  { while(n) { Node s = n->get_next_sibling(); if(n->get_node_type() == TEXT_NODE && s && s->get_node_type() == TEXT_NODE) { s->set_data(n->get_data()+s->get_data()); n->get_parent_node()->remove_child(n); } else if(n->get_node_type() == ELEMENT_NODE) n->normalize(); n = s; } } void normalize() { foreach(values(get_attributes()), Attr a) low_normalize(a->get_first_child()); low_normalize(get_first_child()); } Node clone_node(int|void deep) { Node new = owner_document->create_element(get_tag_name()); if(deep) foreach((array(Node))get_child_nodes(), Node n) new->append_child(n->clone_node(1)); return new; }
9eaf1d2008-06-28Martin Nilsson  protected int child_is_allowed(Node child)
970fdb2000-12-20Marcus Comstedt  { return (<ELEMENT_NODE,PROCESSING_INSTRUCTION_NODE, COMMENT_NODE,TEXT_NODE,CDATA_SECTION_NODE, ENTITY_REFERENCE_NODE>)[child->get_node_type()]; }
9eaf1d2008-06-28Martin Nilsson  protected void create(Document owner, string name)
970fdb2000-12-20Marcus Comstedt  { owner_document = owner; tag_name = name; attributes = create_attributes(); } } class Text { inherit CharacterData; int get_node_type() { return TEXT_NODE; } string get_node_name() { return "#text"; } Text split_text(int offset) { Text new = clone_node(); new->set_data(substring_data(offset, get_length())); delete_data(offset, get_length()); return (parent_node? parent_node->insert_before(new, get_next_sibling()) : new); } Node clone_node(int|void deep) { return owner_document->create_text_node(get_data()); }
9eaf1d2008-06-28Martin Nilsson  protected void create(Document owner, string data)
970fdb2000-12-20Marcus Comstedt  { owner_document = owner; set_data(data); } } class Comment { inherit CharacterData; int get_node_type() { return COMMENT_NODE; } string get_node_name() { return "#comment"; } Node clone_node(int|void deep) { return owner_document->create_comment(get_data()); }
9eaf1d2008-06-28Martin Nilsson  protected void create(Document owner, string data)
970fdb2000-12-20Marcus Comstedt  { owner_document = owner; set_data(data); } } class CDATASection { inherit Text; int get_node_type() { return CDATA_SECTION_NODE; } string get_node_name() { return "#cdata-section"; } Node clone_node(int|void deep) { return owner_document->create_cdata_section(get_data()); }
9eaf1d2008-06-28Martin Nilsson  protected void create(Document owner, string data)
970fdb2000-12-20Marcus Comstedt  { owner_document = owner; set_data(data); } } class DocumentType { inherit Node;
9eaf1d2008-06-28Martin Nilsson  protected string name, public_id, system_id; protected NamedNodeMap entities, notations; protected DOMImplementation implementation;
970fdb2000-12-20Marcus Comstedt  int get_node_type() { return DOCUMENT_TYPE_NODE; } string get_node_name() { return get_name(); } string get_name() { return name; }
5e40c92001-01-16Marcus Comstedt  string get_public_id() { return public_id; } string get_system_id() { return system_id; }
e97f1b2001-01-17Marcus Comstedt  NamedNodeMap get_entities() { return entities || create_entities(); } NamedNodeMap get_notations() { return notations || create_notations(); }
970fdb2000-12-20Marcus Comstedt 
5e40c92001-01-16Marcus Comstedt  protected void _set_owner_document(Document d) { owner_document = d; }
9eaf1d2008-06-28Martin Nilsson  protected NamedNodeMap create_entities()
970fdb2000-12-20Marcus Comstedt  {
e97f1b2001-01-17Marcus Comstedt  return entities = NamedNodeMap(owner_document);
970fdb2000-12-20Marcus Comstedt  }
9eaf1d2008-06-28Martin Nilsson  protected NamedNodeMap create_notations()
970fdb2000-12-20Marcus Comstedt  {
e97f1b2001-01-17Marcus Comstedt  return notations = NamedNodeMap(owner_document);
970fdb2000-12-20Marcus Comstedt  }
9eaf1d2008-06-28Martin Nilsson  protected void create(DOMImplementation i, string qn,
5e40c92001-01-16Marcus Comstedt  string|void pubid, string|void sysid)
970fdb2000-12-20Marcus Comstedt  {
5e40c92001-01-16Marcus Comstedt  implementation = i; name = qn; public_id = pubid; system_id = sysid;
970fdb2000-12-20Marcus Comstedt  } } class Notation { inherit Node;
9eaf1d2008-06-28Martin Nilsson  protected string name, public_id, system_id;
970fdb2000-12-20Marcus Comstedt 
e97f1b2001-01-17Marcus Comstedt  int get_node_type() { return NOTATION_NODE; }
970fdb2000-12-20Marcus Comstedt  string get_node_name() { return name; } string get_public_id() { return public_id; } string get_system_id() { return system_id; } protected int is_readonly() { return 1; }
9eaf1d2008-06-28Martin Nilsson  protected void create(Document owner, string _name, string p_id, string s_id)
970fdb2000-12-20Marcus Comstedt  { owner_document = owner; name = _name; public_id = p_id; system_id = s_id; } } class Entity { inherit Node;
9eaf1d2008-06-28Martin Nilsson  protected string name, public_id, system_id, notation_name;
970fdb2000-12-20Marcus Comstedt  int get_node_type() { return ENTITY_NODE; } string get_node_name() { return name; } string get_public_id() { return public_id; } string get_system_id() { return system_id; } string get_notation_name() { return notation_name; } protected int is_readonly() { return name != 0; }
ec1a0f2014-08-16Martin Nilsson  protected string cast(string to)
970fdb2000-12-20Marcus Comstedt  {
ec1a0f2014-08-16Martin Nilsson  if(to == "string") return ((array(string))get_child_nodes())*""; return UNDEFINED;
970fdb2000-12-20Marcus Comstedt  }
9eaf1d2008-06-28Martin Nilsson  protected int child_is_allowed(Node child)
970fdb2000-12-20Marcus Comstedt  { return (<ELEMENT_NODE,PROCESSING_INSTRUCTION_NODE, COMMENT_NODE,TEXT_NODE,CDATA_SECTION_NODE, ENTITY_REFERENCE_NODE>)[child->get_node_type()]; }
9eaf1d2008-06-28Martin Nilsson  protected void create(Document owner, string _name, string p_id, string s_id,
970fdb2000-12-20Marcus Comstedt  string n_name, DocumentFragment|void value) { owner_document = owner; public_id = p_id; system_id = s_id; notation_name = n_name; if(value) append_child(value); name = _name; } } class EntityReference { inherit Node;
9eaf1d2008-06-28Martin Nilsson  protected string name; protected Entity entity;
970fdb2000-12-20Marcus Comstedt  int get_node_type() { return ENTITY_REFERENCE_NODE; } string get_node_name() { return name; } protected int is_readonly() { return 1; } Node clone_node(int|void deep) { return owner_document->create_entity_reference(name); }
ec1a0f2014-08-16Martin Nilsson  protected string cast(string to)
970fdb2000-12-20Marcus Comstedt  {
ec1a0f2014-08-16Martin Nilsson  if(to == "string") return (entity? (string)entity : "&"+name+";"); return UNDEFINED;
970fdb2000-12-20Marcus Comstedt  } NodeList get_child_nodes() { return entity && entity->get_child_nodes(); } Node get_first_child() { return entity && entity->get_first_child(); } Node get_last_child() { return entity && entity->get_last_child(); } Node get_previous_sibling(Node|void node) { return node? entity && entity->get_previous_sibling(node) : ::get_previous_sibling(); } Node get_next_sibling(Node|void node) { return node? entity && entity->get_next_sibling(node) : ::get_next_sibling(); } int has_child_nodes() { return entity && entity->has_child_nodes(); }
9eaf1d2008-06-28Martin Nilsson  protected void create(Document owner, string _name, Entity|void _entity)
970fdb2000-12-20Marcus Comstedt  { owner_document = owner; name = _name; entity = _entity; } } class ProcessingInstruction { inherit Node;
9eaf1d2008-06-28Martin Nilsson  protected string target, node_value;
970fdb2000-12-20Marcus Comstedt  int get_node_type() { return PROCESSING_INSTRUCTION_NODE; } string get_node_name() { return get_target(); } string get_target() { return target; } string get_node_value() { return node_value; } void set_node_value(string data) { if(is_readonly()) throw(DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR)); node_value=data; } string get_data() { return get_node_value(); } void set_data(string data) { set_node_value(data); } Node clone_node(int|void deep) { return owner_document->create_processing_instruction(target, get_data()); }
9eaf1d2008-06-28Martin Nilsson  protected void create(Document owner, string _target, string data)
970fdb2000-12-20Marcus Comstedt  { owner_document = owner; target = _target; set_data(data); } } class ParseException {
9eaf1d2008-06-28Martin Nilsson  protected string message, pubid, sysid; protected int loc;
970fdb2000-12-20Marcus Comstedt  constant is_generic_error = 1;
9eaf1d2008-06-28Martin Nilsson  protected array backtrace = predef::backtrace()[..<2];
970fdb2000-12-20Marcus Comstedt  int get_location() { return loc; } string get_public_id() { return pubid; } string get_system_id() { return sysid; } string get_message() { return message; }
9eaf1d2008-06-28Martin Nilsson  protected string|array `[] (int i)
970fdb2000-12-20Marcus Comstedt  { switch (i) { case 0:
eeb3b22000-12-20Marcus Comstedt  return "ParseException: " + sysid + ":" + loc + ": " + message+"\n";
970fdb2000-12-20Marcus Comstedt  case 1: return backtrace; } }
74b59e2015-12-20Martin Nilsson  protected string _sprintf(int mode)
970fdb2000-12-20Marcus Comstedt  { return mode == 'O' &&
1a38432002-11-29Martin Nilsson  sprintf("%O(%O /* %O char %d */)", this_program, message, sysid, loc);
970fdb2000-12-20Marcus Comstedt  }
9eaf1d2008-06-28Martin Nilsson  protected void create(string _message, string|void _sysid, string|void _pubid,
970fdb2000-12-20Marcus Comstedt  int|void _loc) { message = _message; sysid = _sysid; pubid = _pubid; loc = _loc; } } class InputSource {
3524712015-05-26Martin Nilsson 
9eaf1d2008-06-28Martin Nilsson  protected string sysid, pubid, encoding; protected Stdio.File file;
3524712015-05-26Martin Nilsson 
970fdb2000-12-20Marcus Comstedt  string get_public_id() { return pubid; } string get_system_id() { return sysid; } string get_encoding() { return encoding; } void set_public_id(string id) { pubid = id; } void set_system_id(string id) { sysid = id; } void set_encoding(string enc) { encoding = enc; }
3524712015-05-26Martin Nilsson 
970fdb2000-12-20Marcus Comstedt  Stdio.File get_file() { return file; } void set_file(Stdio.File f) { file = f; }
3524712015-05-26Martin Nilsson 
970fdb2000-12-20Marcus Comstedt  string get_data() { string data = get_file()->read(); return data; }
3524712015-05-26Martin Nilsson 
9eaf1d2008-06-28Martin Nilsson  protected Stdio.File get_external_file(string sysid, string|void pubid)
970fdb2000-12-20Marcus Comstedt  { Stdio.File f = Stdio.File(); if(!f->open(sysid, "r")) throw(ParseException(sysid+": file not found", sysid, pubid)); return f; }
3524712015-05-26Martin Nilsson 
9eaf1d2008-06-28Martin Nilsson  protected void create(Stdio.File|string|void input)
970fdb2000-12-20Marcus Comstedt  { if(input) if(stringp(input)) { set_system_id(input); set_file(get_external_file(input)); } else set_file(input); } } class AbstractDOMParser {
9eaf1d2008-06-28Martin Nilsson  protected class ErrorHandler {
970fdb2000-12-20Marcus Comstedt  void error(ParseException exception); void fatal_error(ParseException exception); void warning(ParseException exception); };
9eaf1d2008-06-28Martin Nilsson  protected ErrorHandler error_handler = this;
970fdb2000-12-20Marcus Comstedt  void error(ParseException exception) { throw(exception); } void fatal_error(ParseException exception) { throw(exception); } void warning(ParseException exception) { } void set_error_handler(ErrorHandler handler) { error_handler = handler; }
9eaf1d2008-06-28Martin Nilsson  protected Document document; protected Node current_node; protected array(Node) node_stack;
970fdb2000-12-20Marcus Comstedt  Document get_document() { return document; }
9eaf1d2008-06-28Martin Nilsson  protected DOMImplementation get_dom_implementation()
5e40c92001-01-16Marcus Comstedt  { return DOMImplementation(); }
9eaf1d2008-06-28Martin Nilsson  protected Document create_document(InputSource s)
970fdb2000-12-20Marcus Comstedt  {
5e40c92001-01-16Marcus Comstedt  return get_dom_implementation()->create_document(0, 0, 0);
970fdb2000-12-20Marcus Comstedt  }
9eaf1d2008-06-28Martin Nilsson  protected object|void parse_callback(string ty, string name, mapping attributes,
e97f1b2001-01-17Marcus Comstedt  array|string contents, mapping info, InputSource input)
970fdb2000-12-20Marcus Comstedt  { switch(ty) { case "<!--": current_node->append_child(document->create_comment(contents)); break; case "<": case "<>": Element e = document->create_element(name); foreach(indices(attributes), string att_name) e->set_attribute(att_name, attributes[att_name]); current_node->append_child(e);
5bfacf2001-03-22Marcus Comstedt  node_stack += ({ current_node }); current_node = e ;
970fdb2000-12-20Marcus Comstedt  if(ty == "<") break; case ">": current_node = node_stack[-1];
8a531a2006-11-04Martin Nilsson  node_stack = node_stack[..<1];
970fdb2000-12-20Marcus Comstedt  break; case "": current_node->append_child(document->create_text_node(contents)); break; case "<![CDATA[": current_node->append_child(document->create_cdata_section(contents)); break; case "<?": current_node->append_child(document-> create_processing_instruction(name, contents)); break; case "<?xml": break; case "<!DOCTYPE":
e97f1b2001-01-17Marcus Comstedt  { DocumentType doctype = document->get_implementation()-> create_document_type(name, attributes->PUBLIC, attributes->SYSTEM);
3524712015-05-26Martin Nilsson 
e97f1b2001-01-17Marcus Comstedt  current_node->append_child(doctype); if(contents) foreach(contents, object elt) if(elt) switch(elt->get_node_type()) { case Node.NOTATION_NODE: doctype->get_notations()->set_named_item(elt); break; case Node.ENTITY_NODE: doctype->get_entities()->set_named_item(elt); break; } break; } case "<!NOTATION": return Notation(document, name, attributes->PUBLIC, attributes->SYSTEM);
970fdb2000-12-20Marcus Comstedt  break; case "<!ENTITY":
e97f1b2001-01-17Marcus Comstedt  if(name[0] != '%') if(contents) { DocumentFragment frag = document->create_document_fragment(); frag->append_child(document->create_text_node(contents)); return Entity(document, name, 0, 0, 0, frag); } else return Entity(document, name, attributes->PUBLIC, attributes->SYSTEM, attributes->NDATA); break;
970fdb2000-12-20Marcus Comstedt  case "<!ELEMENT": case "<!ATTLIST": break; case "error":
eeb3b22000-12-20Marcus Comstedt  throw(ParseException(contents, input->get_system_id(), input->get_public_id(), info->location));
970fdb2000-12-20Marcus Comstedt  } }
9eaf1d2008-06-28Martin Nilsson  protected void _parse(string data, function cb, InputSource input);
970fdb2000-12-20Marcus Comstedt  void parse(InputSource|string source) { if(stringp(source)) source = InputSource(source); current_node = document = create_document(source); node_stack = ({});
eeb3b22000-12-20Marcus Comstedt  _parse(source->get_data(), parse_callback, source);
970fdb2000-12-20Marcus Comstedt  } } class NonValidatingDOMParser { inherit AbstractDOMParser;
9eaf1d2008-06-28Martin Nilsson  protected inherit .Simple : xml;
5bfacf2001-03-22Marcus Comstedt 
9eaf1d2008-06-28Martin Nilsson  protected string autoconvert(string data, InputSource input)
5bfacf2001-03-22Marcus Comstedt  { mixed err = catch{ return xml::autoconvert(data); }; throw(ParseException(err[0], input->get_system_id(), input->get_public_id(), 0)); }
9eaf1d2008-06-28Martin Nilsson  protected void _parse(string data, function cb, InputSource input)
970fdb2000-12-20Marcus Comstedt  {
5bfacf2001-03-22Marcus Comstedt  xml::parse(autoconvert(data, input), cb, input);
970fdb2000-12-20Marcus Comstedt  } }
eeb3b22000-12-20Marcus Comstedt  class DOMParser { inherit AbstractDOMParser;
9eaf1d2008-06-28Martin Nilsson  protected inherit Parser.XML.Validating : xml;
eeb3b22000-12-20Marcus Comstedt 
9eaf1d2008-06-28Martin Nilsson  protected string autoconvert(string data, InputSource input)
5bfacf2001-03-22Marcus Comstedt  { mixed err = catch{ return xml::autoconvert(data); }; throw(ParseException(err[0]-"\n", input->get_system_id(), input->get_public_id(), 0)); }
eeb3b22000-12-20Marcus Comstedt  string get_external_entity(string sysid, string|void pubid,
5bfacf2001-03-22Marcus Comstedt  int|void unparsed, InputSource input)
eeb3b22000-12-20Marcus Comstedt  {
5bfacf2001-03-22Marcus Comstedt  InputSource is = InputSource(combine_path(combine_path(input->get_system_id(), ".."), sysid)); return (unparsed? is->get_data() : autoconvert(is->get_data(), is));
eeb3b22000-12-20Marcus Comstedt  }
9eaf1d2008-06-28Martin Nilsson  protected void _parse(string data, function cb, InputSource input)
eeb3b22000-12-20Marcus Comstedt  {
5bfacf2001-03-22Marcus Comstedt  xml::parse(autoconvert(data, input), cb, input);
eeb3b22000-12-20Marcus Comstedt  } }