示例#1
0
/*
 * call-seq:
 *  unlink
 *
 * Unlink this node from its current context.
 */
static VALUE unlink_node(VALUE self)
{
  xmlNodePtr node;
  Data_Get_Struct(self, xmlNode, node);
  xmlUnlinkNode(node);
  NOKOGIRI_ROOT_NODE(node);
  return self;
}
示例#2
0
/* :nodoc: */
static VALUE replace(VALUE self, VALUE new_node)
{
    VALUE reparent = reparent_node_with(self, new_node, xmlReplaceNodeWrapper);

    xmlNodePtr pivot;
    Data_Get_Struct(self, xmlNode, pivot);
    NOKOGIRI_ROOT_NODE(pivot);

    return reparent;
}
示例#3
0
/*
 * call-seq:
 *  root=
 *
 * Set the root element on this document
 */
static VALUE set_root(VALUE self, VALUE root)
{
    xmlDocPtr doc;
    xmlNodePtr new_root;

    Data_Get_Struct(self, xmlDoc, doc);

    xmlNodePtr old_root = NULL;

    if(NIL_P(root)) {
        old_root = xmlDocGetRootElement(doc);

        if(old_root) {
            xmlUnlinkNode(old_root);
            NOKOGIRI_ROOT_NODE(old_root);
        }

        return root;
    }

    Data_Get_Struct(root, xmlNode, new_root);


    /* If the new root's document is not the same as the current document,
     * then we need to dup the node in to this document. */
    if(new_root->doc != doc) {
        old_root = xmlDocGetRootElement(doc);
        if (!(new_root = xmlDocCopyNode(new_root, doc, 1))) {
            rb_raise(rb_eRuntimeError, "Could not reparent node (xmlDocCopyNode)");
        }
    }

    xmlDocSetRootElement(doc, new_root);
    if(old_root) NOKOGIRI_ROOT_NODE(old_root);
    return root;
}
示例#4
0
/*
 * call-seq:
 *  dup
 *
 * Copy this node.  An optional depth may be passed in, but it defaults
 * to a deep copy.  0 is a shallow copy, 1 is a deep copy.
 */
static VALUE duplicate_node(int argc, VALUE *argv, VALUE self)
{
  VALUE level;
  xmlNodePtr node, dup;

  if(rb_scan_args(argc, argv, "01", &level) == 0)
    level = INT2NUM((long)1);

  Data_Get_Struct(self, xmlNode, node);

  dup = xmlDocCopyNode(node, node->doc, (int)NUM2INT(level));
  if(dup == NULL) return Qnil;

  NOKOGIRI_ROOT_NODE(dup);

  return Nokogiri_wrap_xml_node(rb_obj_class(self), dup);
}
示例#5
0
/* :nodoc: */
static VALUE reparent_node_with(VALUE pivot_obj, VALUE reparentee_obj, pivot_reparentee_func prf)
{
  VALUE reparented_obj ;
  xmlNodePtr reparentee, pivot, reparented, next_text, new_next_text ;

  if(!rb_obj_is_kind_of(reparentee_obj, cNokogiriXmlNode))
    rb_raise(rb_eArgError, "node must be a Nokogiri::XML::Node");
  if(rb_obj_is_kind_of(reparentee_obj, cNokogiriXmlDocument))
    rb_raise(rb_eArgError, "node must be a Nokogiri::XML::Node");

  Data_Get_Struct(reparentee_obj, xmlNode, reparentee);
  Data_Get_Struct(pivot_obj, xmlNode, pivot);

  if(XML_DOCUMENT_NODE == reparentee->type || XML_HTML_DOCUMENT_NODE == reparentee->type)
    rb_raise(rb_eArgError, "cannot reparent a document node");

  xmlUnlinkNode(reparentee);

  if (reparentee->doc != pivot->doc || reparentee->type == XML_TEXT_NODE) {
    /*
     *  if the reparentee is a text node, there's a very good chance it will be
     *  merged with an adjacent text node after being reparented, and in that case
     *  libxml will free the underlying C struct.
     *
     *  since we clearly have a ruby object which references the underlying
     *  memory, we can't let the C struct get freed. let's pickle the original
     *  reparentee by rooting it; and then we'll reparent a duplicate of the
     *  node that we don't care about preserving.
     *
     *  alternatively, if the reparentee is from a different document than the
     *  pivot node, libxml2 is going to get confused about which document's
     *  "dictionary" the node's strings belong to (this is an otherwise
     *  uninteresting libxml2 implementation detail). as a result, we cannot
     *  reparent the actual reparentee, so we reparent a duplicate.
     */
    NOKOGIRI_ROOT_NODE(reparentee);
    if (!(reparentee = xmlDocCopyNode(reparentee, pivot->doc, 1))) {
      rb_raise(rb_eRuntimeError, "Could not reparent node (xmlDocCopyNode)");
    }
  }

  if (reparentee->type == XML_TEXT_NODE && pivot->next && pivot->next->type == XML_TEXT_NODE) {
    /*
     *  libxml merges text nodes in a right-to-left fashion, meaning that if
     *  there are two text nodes who would be adjacent, the right (or following,
     *  or next) node will be merged into the left (or preceding, or previous)
     *  node.
     *
     *  and by "merged" I mean the string contents will be concatenated onto the
     *  left node's contents, and then the node will be freed.
     *
     *  which means that if we have a ruby object wrapped around the right node,
     *  its memory would be freed out from under it.
     *
     *  so, we detect this edge case and unlink-and-root the text node before it gets
     *  merged. then we dup the node and insert that duplicate back into the
     *  document where the real node was.
     *
     *  yes, this is totally lame.
     */
    next_text     = pivot->next ;
    new_next_text = xmlDocCopyNode(next_text, pivot->doc, 1) ;

    xmlUnlinkNode(next_text);
    NOKOGIRI_ROOT_NODE(next_text);

    xmlAddNextSibling(pivot, new_next_text);
  }

  if(!(reparented = (*prf)(pivot, reparentee))) {
    rb_raise(rb_eRuntimeError, "Could not reparent node");
  }

  /*
   *  make sure the ruby object is pointed at the just-reparented node, which
   *  might be a duplicate (see above) or might be the result of merging
   *  adjacent text nodes.
   */
  DATA_PTR(reparentee_obj) = reparented ;

  relink_namespace(reparented);

  reparented_obj = Nokogiri_wrap_xml_node(Qnil, reparented);

  rb_funcall(reparented_obj, decorate_bang, 0);

  return reparented_obj ;
}
示例#6
0
/* :nodoc: */
static VALUE reparent_node_with(VALUE node_obj, VALUE other_obj, node_other_func func)
{
  VALUE reparented_obj ;
  xmlNodePtr node, other, reparented ;

  if(!rb_obj_is_kind_of(node_obj, cNokogiriXmlNode))
    rb_raise(rb_eArgError, "node must be a Nokogiri::XML::Node");

  Data_Get_Struct(node_obj, xmlNode, node);
  Data_Get_Struct(other_obj, xmlNode, other);

  if(XML_DOCUMENT_NODE == node->type || XML_HTML_DOCUMENT_NODE == node->type)
    rb_raise(rb_eArgError, "cannot reparent a document node");

  if(node->type == XML_TEXT_NODE) {
    NOKOGIRI_ROOT_NODE(node);
    node = xmlDocCopyNode(node, other->doc, 1);
  }

  if (node->doc == other->doc) {
    xmlUnlinkNode(node) ;

    // TODO: I really want to remove this.  We shouldn't support 2.6.16 anymore
    if ( node->type == XML_TEXT_NODE
         && other->type == XML_TEXT_NODE
         && is_2_6_16() ) {

      // we'd rather leak than segfault.
      other->content = xmlStrdup(other->content);

    }

    if(!(reparented = (*func)(other, node))) {
      rb_raise(rb_eRuntimeError, "Could not reparent node (%s:%d)", __FILE__, __LINE__);
    }
  } else {
    xmlNodePtr duped_node ;
    // recursively copy to the new document
    if (!(duped_node = xmlDocCopyNode(node, other->doc, 1))) {
      rb_raise(rb_eRuntimeError, "Could not reparent node (xmlDocCopyNode)");
    }
    if(!(reparented = (*func)(other, duped_node))) {
      rb_raise(rb_eRuntimeError, "Could not reparent node (%s:%d)", __FILE__, __LINE__);
    }
    xmlUnlinkNode(node);
    NOKOGIRI_ROOT_NODE(node);
  }

  // the child was a text node that was coalesced. we need to have the object
  // point at SOMETHING, or we'll totally bomb out.
  if (reparented != node) {
    DATA_PTR(node_obj) = reparented ;
  }

  // Appropriately link in namespaces
  relink_namespace(reparented);

  reparented_obj = Nokogiri_wrap_xml_node(Qnil, reparented);

  rb_funcall(reparented_obj, decorate_bang, 0);

  return reparented_obj ;
}