static VALUE rxml_node_modify_dom(VALUE self, VALUE target, xmlNodePtr (*xmlFunc)(xmlNodePtr, xmlNodePtr)) { xmlNodePtr xnode, xtarget, xresult; if (rb_obj_is_kind_of(target, cXMLNode) == Qfalse) rb_raise(rb_eTypeError, "Must pass an XML::Node object"); xnode = rxml_get_xnode(self); xtarget = rxml_get_xnode(target); if (xtarget->doc != NULL && xtarget->doc != xnode->doc) rb_raise(eXMLError, "Nodes belong to different documents. You must first import the node by calling XML::Document.import"); xmlUnlinkNode(xtarget); /* This target node could be freed here. */ xresult = xmlFunc(xnode, xtarget); if (!xresult) rxml_raise(&xmlLastError); /* Was the target freed? If yes, then wrap the new node */ if (xresult != xtarget) { RDATA(target)->data = xresult; xresult->_private = (void*) target; } return target; }
/* * call-seq: * node.doc -> document * * Obtain the XML::Document this node belongs to. */ static VALUE rxml_node_doc(VALUE self) { xmlDocPtr xdoc = NULL; xmlNodePtr xnode = rxml_get_xnode(self); switch (xnode->type) { case XML_DOCUMENT_NODE: #ifdef LIBXML_DOCB_ENABLED case XML_DOCB_DOCUMENT_NODE: #endif case XML_HTML_DOCUMENT_NODE: case XML_NAMESPACE_DECL: break; case XML_ATTRIBUTE_NODE: xdoc = (xmlDocPtr)((xmlAttrPtr) xnode->doc); break; default: xdoc = xnode->doc; } if (xdoc == NULL) return (Qnil); else if (xdoc->_private) return (VALUE) xdoc->_private; else return (Qnil); }
/* * call-seq: * text_node.output_escaping = true|false * element_node.output_escaping = true|false * attribute_node.output_escaping = true|false * * Controls whether this text node or the immediate text node children of an * element or attribute node escapes their output. Any other type of node * will simply ignore this operation. * * Text nodes which are added to an element or attribute node will be affected * by any previous setting of this property. */ static VALUE rxml_node_output_escaping_set(VALUE self, VALUE bool) { xmlNodePtr xnode; xnode = rxml_get_xnode(self); switch (xnode->type) { case XML_TEXT_NODE: xnode->name = (bool!=Qfalse && bool!=Qnil) ? xmlStringText : xmlStringTextNoenc; break; case XML_ELEMENT_NODE: case XML_ATTRIBUTE_NODE: { const xmlChar *name = (bool!=Qfalse && bool!=Qnil) ? xmlStringText : xmlStringTextNoenc; xmlNodePtr tmp; for (tmp = xnode->children; tmp; tmp = tmp->next) if (tmp->type == XML_TEXT_NODE) tmp->name = name; } break; default: return Qnil; } return (bool!=Qfalse && bool!=Qnil) ? Qtrue : Qfalse; }
/* * call-seq: * node.space_preserve -> (true|false) * * Determine whether this node preserves whitespace. */ static VALUE rxml_node_space_preserve_get(VALUE self) { xmlNodePtr xnode; xnode = rxml_get_xnode(self); return (INT2NUM(xmlNodeGetSpacePreserve(xnode))); }
/* * call-seq: * text_node.output_escaping? -> (true|false) * element_node.output_escaping? -> (true|false|nil) * attribute_node.output_escaping? -> (true|false|nil) * other_node.output_escaping? -> (nil) * * Determine whether this node escapes it's output or not. * * Text nodes return only +true+ or +false+. Element and attribute nodes * examine their immediate text node children to determine the value. * Any other type of node always returns +nil+. * * If an element or attribute node has at least one immediate child text node * and all the immediate text node children have the same +output_escaping?+ * value, that value is returned. Otherwise, +nil+ is returned. */ static VALUE rxml_node_output_escaping_q(VALUE self) { xmlNodePtr xnode; xnode = rxml_get_xnode(self); switch (xnode->type) { case XML_TEXT_NODE: return xnode->name==xmlStringTextNoenc ? Qfalse : Qtrue; case XML_ELEMENT_NODE: case XML_ATTRIBUTE_NODE: { xmlNodePtr tmp = xnode->children; const xmlChar *match = NULL; /* Find the first text node and use it as the reference. */ while (tmp && tmp->type != XML_TEXT_NODE) tmp = tmp->next; if (! tmp) return Qnil; match = tmp->name; /* Walk the remaining text nodes until we run out or one doesn't match. */ while (tmp && (tmp->type != XML_TEXT_NODE || match == tmp->name)) tmp = tmp->next; /* We're left with either the mismatched node or the aggregate result. */ return tmp ? Qnil : (match==xmlStringTextNoenc ? Qfalse : Qtrue); } break; default: return Qnil; } }
static VALUE rxml_node_remove_ex(VALUE self) { xmlNodePtr xnode, xresult; xnode = rxml_get_xnode(self); /* First unlink the node from its parent. */ xmlUnlinkNode(xnode); /* Now copy the node we want to remove and make the current Ruby object point to it. We do this because a node has a number of dependencies on its parent document - its name (if using a dictionary), entities, namespaces, etc. For a node to live on its own, it needs to get its own copies of this information.*/ xresult = xmlDocCopyNode(xnode, NULL, 1); /* Now free the original node. */ xmlFreeNode(xnode); /* Now wrap the new node */ RDATA(self)->data = xresult; xresult->_private = (void*) self; /* Now return the removed node so the user can do something with it.*/ return self; }
/* * call-seq: * node.attributes -> attributes * * Returns the XML::Attributes for this node. */ static VALUE rxml_node_attributes_get(VALUE self) { xmlNodePtr xnode; xnode = rxml_get_xnode(self); return rxml_attributes_new(xnode); }
/* * call-seq: * node.prev -> XML::Node * * Obtain the previous sibling, if any. */ static VALUE rxml_node_prev_get(VALUE self) { xmlNodePtr xnode; xmlNodePtr node; xnode = rxml_get_xnode(self); switch (xnode->type) { case XML_DOCUMENT_NODE: #ifdef LIBXML_DOCB_ENABLED case XML_DOCB_DOCUMENT_NODE: #endif case XML_HTML_DOCUMENT_NODE: case XML_NAMESPACE_DECL: node = NULL; break; case XML_ATTRIBUTE_NODE: { xmlAttrPtr attr = (xmlAttrPtr) xnode; node = (xmlNodePtr) attr->prev; } break; default: node = xnode->prev; break; } if (node == NULL) return (Qnil); else return (rxml_node_wrap(node)); }
/* * call-seq: * curr_node << "Some text" * curr_node << node * * Add the specified text or XML::Node as a new child node to the * current node. * * If the specified argument is a string, it should be a raw string * that contains unescaped XML special characters. Entity references * are not supported. * * The method will return the current node. */ static VALUE rxml_node_content_add(VALUE self, VALUE obj) { xmlNodePtr xnode; VALUE str; xnode = rxml_get_xnode(self); /* XXX This should only be legal for a CDATA type node, I think, * resulting in a merge of content, as if a string were passed * danj 070827 */ if (rb_obj_is_kind_of(obj, cXMLNode)) { rxml_node_modify_dom(self, obj, xmlAddChild); } else { str = rb_obj_as_string(obj); if (NIL_P(str) || TYPE(str) != T_STRING) rb_raise(rb_eTypeError, "invalid argument: must be string or XML::Node"); xmlNodeAddContent(xnode, (xmlChar*) StringValuePtr(str)); } return self; }
/* * call-seq: * node.space_preserve = true|false * * Control whether this node preserves whitespace. */ static VALUE rxml_node_space_preserve_set(VALUE self, VALUE bool) { xmlNodePtr xnode; xnode = rxml_get_xnode(self); if (TYPE(bool) == T_FALSE) xmlNodeSetSpacePreserve(xnode, 0); else
/* * call-seq: * node.empty? -> (true|false) * * Determine whether this node is an empty or whitespace only text-node. */ static VALUE rxml_node_empty_q(VALUE self) { xmlNodePtr xnode; xnode = rxml_get_xnode(self); if (xnode == NULL) return (Qnil); return ((xmlIsBlankNode(xnode) == 1) ? Qtrue : Qfalse); }
/* * call-seq: * node.content = "string" * * Set this node's content to the specified string. */ static VALUE rxml_node_content_set(VALUE self, VALUE content) { xmlNodePtr xnode; Check_Type(content, T_STRING); xnode = rxml_get_xnode(self); // XXX docs indicate need for escaping entites, need to be done? danj xmlNodeSetContent(xnode, (xmlChar*) StringValuePtr(content)); return (Qtrue); }
/* * call-seq: * node.lang = "string" * * Set the language for this node. This affects the value * of the xml:lang attribute. */ static VALUE rxml_node_lang_set(VALUE self, VALUE lang) { xmlNodePtr xnode; Check_Type(lang, T_STRING); xnode = rxml_get_xnode(self); xmlNodeSetLang(xnode, (xmlChar*) StringValuePtr(lang)); return (Qtrue); }
/* * call-seq: * node.first -> XML::Node * * Returns this node's first child node if any. */ static VALUE rxml_node_first_get(VALUE self) { xmlNodePtr xnode; xnode = rxml_get_xnode(self); if (xnode->children) return (rxml_node_wrap(xnode->children)); else return (Qnil); }
/* * call-seq: * node.content = "string" * * Set this node's content to the specified string. */ static VALUE rxml_node_content_set(VALUE self, VALUE content) { xmlNodePtr xnode; xmlChar* encoded_content; Check_Type(content, T_STRING); xnode = rxml_get_xnode(self); encoded_content = xmlEncodeSpecialChars(xnode->doc, (xmlChar*) StringValuePtr(content)); xmlNodeSetContent(xnode, encoded_content); return (Qtrue); }
/* * call-seq: * node.parent -> XML::Node * * Obtain this node's parent node, if any. */ static VALUE rxml_node_parent_get(VALUE self) { xmlNodePtr xnode; xnode = rxml_get_xnode(self); if (xnode->parent) return (rxml_node_wrap(xnode->parent)); else return (Qnil); }
static VALUE rxml_node_to_s(int argc, VALUE *argv, VALUE self) { VALUE result = Qnil; VALUE options = Qnil; xmlNodePtr xnode; xmlCharEncodingHandlerPtr encodingHandler; xmlOutputBufferPtr output; int level = 0; int indent = 1; const char *xencoding = "UTF-8"; rb_scan_args(argc, argv, "01", &options); if (!NIL_P(options)) { VALUE rencoding, rindent, rlevel; Check_Type(options, T_HASH); rencoding = rb_hash_aref(options, ID2SYM(rb_intern("encoding"))); rindent = rb_hash_aref(options, ID2SYM(rb_intern("indent"))); rlevel = rb_hash_aref(options, ID2SYM(rb_intern("level"))); if (rindent == Qfalse) indent = 0; if (rlevel != Qnil) level = NUM2INT(rlevel); if (rencoding != Qnil) { xencoding = xmlGetCharEncodingName((xmlCharEncoding)NUM2INT(rencoding)); if (!xencoding) rb_raise(rb_eArgError, "Unknown encoding value: %d", NUM2INT(rencoding)); } } encodingHandler = xmlFindCharEncodingHandler(xencoding); output = xmlAllocOutputBuffer(encodingHandler); xnode = rxml_get_xnode(self); xmlNodeDumpOutput(output, xnode->doc, xnode, level, indent, xencoding); xmlOutputBufferFlush(output); if (output->conv) result = rxml_new_cstr((const char*) output->conv->content, xencoding); else result = rxml_new_cstr((const char*) output->buffer->content, xencoding); xmlOutputBufferClose(output); return result; }
/* * call-seq: * node.debug -> true|false * * Print libxml debugging information to stdout. * Requires that libxml was compiled with debugging enabled. */ static VALUE rxml_node_debug(VALUE self) { #ifdef LIBXML_DEBUG_ENABLED xmlNodePtr xnode; xnode = rxml_get_xnode(self); xmlDebugDumpNode(NULL, xnode, 2); return Qtrue; #else rb_warn("libxml was compiled without debugging support.") return Qfalse; #endif }
/* * call-seq: * node.base_uri = "uri" * * Set this node's base URI. */ static VALUE rxml_node_base_uri_set(VALUE self, VALUE uri) { xmlNodePtr xnode; Check_Type(uri, T_STRING); xnode = rxml_get_xnode(self); if (xnode->doc == NULL) return (Qnil); xmlNodeSetBase(xnode, (xmlChar*) StringValuePtr(uri)); return (Qtrue); }
/* * call-seq: * node.path -> path * * Obtain this node's path. */ static VALUE rxml_node_path(VALUE self) { xmlNodePtr xnode; xmlChar *path; xnode = rxml_get_xnode(self); path = xmlGetNodePath(xnode); if (path == NULL) return (Qnil); else return (rxml_new_cstr((const char*) path, NULL)); }
/* * call-seq: * node.xlink_type -> num * * Obtain the type identifier for this xlink, if applicable. * If this is not an xlink node (see +xlink?+), will return * nil. */ static VALUE rxml_node_xlink_type(VALUE self) { xmlNodePtr xnode; xlinkType xlt; xnode = rxml_get_xnode(self); xlt = xlinkIsLink(xnode->doc, xnode); if (xlt == XLINK_TYPE_NONE) return (Qnil); else return (INT2NUM(xlt)); }
/* * call-seq: * node.xlink? -> (true|false) * * Determine whether this node is an xlink node. */ static VALUE rxml_node_xlink_q(VALUE self) { xmlNodePtr xnode; xlinkType xlt; xnode = rxml_get_xnode(self); xlt = xlinkIsLink(xnode->doc, xnode); if (xlt == XLINK_TYPE_NONE) return (Qfalse); else return (Qtrue); }
/* * call-seq: * node.name = "string" * * Set this node's name. */ static VALUE rxml_node_name_set(VALUE self, VALUE name) { xmlNodePtr xnode; const xmlChar *xname; Check_Type(name, T_STRING); xnode = rxml_get_xnode(self); xname = (const xmlChar*)StringValuePtr(name); /* Note: calling xmlNodeSetName() for a text node is ignored by libXML. */ xmlNodeSetName(xnode, xname); return (Qtrue); }
/* * call-seq: * node.line_num -> num * * Obtain the line number (in the XML document) that this * node was read from. If +default_line_numbers+ is set * false (the default), this method returns zero. */ static VALUE rxml_node_line_num(VALUE self) { xmlNodePtr xnode; long line_num; xnode = rxml_get_xnode(self); if (!xmlLineNumbersDefaultValue) rb_warn( "Line numbers were not retained: use XML::Parser::default_line_numbers=true"); line_num = xmlGetLineNo(xnode); if (line_num == -1) return (Qnil); else return (INT2NUM((long) line_num)); }
/* * call-seq: * node.content -> "string" * * Obtain this node's content as a string. */ static VALUE rxml_node_content_get(VALUE self) { xmlNodePtr xnode; xmlChar *content; VALUE result = Qnil; xnode = rxml_get_xnode(self); content = xmlNodeGetContent(xnode); if (content) { result = rxml_new_cstr((const char *) content, NULL); xmlFree(content); } return result; }
/* * call-seq: * node.lang -> "string" * * Obtain the language set for this node, if any. * This is set in XML via the xml:lang attribute. */ static VALUE rxml_node_lang_get(VALUE self) { xmlNodePtr xnode; xmlChar *lang; VALUE result = Qnil; xnode = rxml_get_xnode(self); lang = xmlNodeGetLang(xnode); if (lang) { result = rxml_new_cstr((const char*) lang, NULL); xmlFree(lang); } return (result); }
/* * call-seq: * node.each -> XML::Node * * Iterates over this node's children, including text * nodes, element nodes, etc. If you wish to iterate * only over child elements, use XML::Node#each_element. * * doc = XML::Document.new('model/books.xml') * doc.root.each {|node| puts node} */ static VALUE rxml_node_each(VALUE self) { xmlNodePtr xnode; xmlNodePtr xcurrent; xnode = rxml_get_xnode(self); xcurrent = xnode->children; while (xcurrent) { /* The user could remove this node, so first stache away the next node. */ xmlNodePtr xnext = xcurrent->next; rb_yield(rxml_node_wrap(xcurrent)); xcurrent = xnext; } return Qnil; }
/* * call-seq: * node.base_uri -> "uri" * * Obtain this node's base URI. */ static VALUE rxml_node_base_uri_get(VALUE self) { xmlNodePtr xnode; xmlChar* base_uri; VALUE result = Qnil; xnode = rxml_get_xnode(self); if (xnode->doc == NULL) return (result); base_uri = xmlNodeGetBase(xnode->doc, xnode); if (base_uri) { result = rxml_new_cstr((const char*) base_uri, NULL); xmlFree(base_uri); } return (result); }
/* * call-seq: * node.name -> "string" * * Obtain this node's name. */ static VALUE rxml_node_name_get(VALUE self) { xmlNodePtr xnode; const xmlChar *name; xnode = rxml_get_xnode(self); switch (xnode->type) { case XML_DOCUMENT_NODE: #ifdef LIBXML_DOCB_ENABLED case XML_DOCB_DOCUMENT_NODE: #endif case XML_HTML_DOCUMENT_NODE: { xmlDocPtr doc = (xmlDocPtr) xnode; name = doc->URL; break; } case XML_ATTRIBUTE_NODE: { xmlAttrPtr attr = (xmlAttrPtr) xnode; name = attr->name; break; } case XML_NAMESPACE_DECL: { xmlNsPtr ns = (xmlNsPtr) xnode; name = ns->prefix; break; } default: name = xnode->name; break; } if (xnode->name == NULL) return (Qnil); else return (rxml_new_cstr((const char*) name, NULL)); }
/* * call-seq: * node.xlink_type_name -> "string" * * Obtain the type name for this xlink, if applicable. * If this is not an xlink node (see +xlink?+), will return * nil. */ static VALUE rxml_node_xlink_type_name(VALUE self) { xmlNodePtr xnode; xlinkType xlt; xnode = rxml_get_xnode(self); xlt = xlinkIsLink(xnode->doc, xnode); switch (xlt) { case XLINK_TYPE_NONE: return (Qnil); case XLINK_TYPE_SIMPLE: return (rxml_new_cstr("simple", NULL)); case XLINK_TYPE_EXTENDED: return (rxml_new_cstr("extended", NULL)); case XLINK_TYPE_EXTENDED_SET: return (rxml_new_cstr("extended_set", NULL)); default: rb_fatal("Unknowng xlink type, %d", xlt); } }