nokogiri 1.5.5.rc3-java → 1.5.6-java

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of nokogiri might be problematic. Click here for more details.

Files changed (67) hide show
  1. data/CHANGELOG.ja.rdoc +42 -1
  2. data/CHANGELOG.rdoc +41 -1
  3. data/Manifest.txt +8 -1
  4. data/README.ja.rdoc +1 -1
  5. data/README.rdoc +5 -8
  6. data/ROADMAP.md +6 -2
  7. data/Rakefile +29 -7
  8. data/bin/nokogiri +19 -4
  9. data/build_all +56 -17
  10. data/ext/java/nokogiri/HtmlDocument.java +26 -0
  11. data/ext/java/nokogiri/NokogiriService.java +7 -1
  12. data/ext/java/nokogiri/XmlDocument.java +24 -6
  13. data/ext/java/nokogiri/XmlDocumentFragment.java +2 -26
  14. data/ext/java/nokogiri/XmlDtd.java +13 -2
  15. data/ext/java/nokogiri/XmlElement.java +3 -12
  16. data/ext/java/nokogiri/XmlEntityReference.java +32 -8
  17. data/ext/java/nokogiri/XmlNamespace.java +2 -1
  18. data/ext/java/nokogiri/XmlNode.java +83 -31
  19. data/ext/java/nokogiri/XmlSaxPushParser.java +55 -53
  20. data/ext/java/nokogiri/XmlText.java +2 -14
  21. data/ext/java/nokogiri/XsltStylesheet.java +4 -2
  22. data/ext/java/nokogiri/internals/ClosedStreamException.java +10 -0
  23. data/ext/java/nokogiri/internals/HtmlDomParserContext.java +2 -2
  24. data/ext/java/nokogiri/internals/NokogiriBlockingQueueInputStream.java +151 -0
  25. data/ext/java/nokogiri/internals/{XmlDomParser.java → NokogiriDomParser.java} +25 -14
  26. data/ext/java/nokogiri/internals/NokogiriEncodingReaderWrapper.java +109 -0
  27. data/ext/java/nokogiri/internals/NokogiriEntityResolver.java +123 -0
  28. data/ext/java/nokogiri/internals/NokogiriHandler.java +22 -14
  29. data/ext/java/nokogiri/internals/NokogiriHelpers.java +25 -7
  30. data/ext/java/nokogiri/internals/ParserContext.java +2 -1
  31. data/ext/java/nokogiri/internals/ReaderNode.java +2 -1
  32. data/ext/java/nokogiri/internals/SaveContextVisitor.java +100 -102
  33. data/ext/java/nokogiri/internals/XmlDomParserContext.java +10 -4
  34. data/ext/nokogiri/extconf.rb +1 -0
  35. data/ext/nokogiri/xml_document.c +2 -2
  36. data/ext/nokogiri/xml_node.c +31 -14
  37. data/ext/nokogiri/xml_sax_parser.c +16 -0
  38. data/ext/nokogiri/xslt_stylesheet.c +19 -2
  39. data/lib/nekodtd.jar +0 -0
  40. data/lib/nokogiri/nokogiri.jar +0 -0
  41. data/lib/nokogiri/version.rb +4 -1
  42. data/lib/nokogiri/xml/document.rb +8 -6
  43. data/lib/nokogiri/xml/document_fragment.rb +10 -1
  44. data/lib/nokogiri/xml/node.rb +58 -61
  45. data/lib/nokogiri/xml/sax/document.rb +7 -0
  46. data/lib/nokogiri/xml/sax/parser.rb +7 -0
  47. data/lib/nokogiri/xml/xpath_context.rb +1 -1
  48. data/lib/nokogiri/xslt.rb +1 -1
  49. data/tasks/cross_compile.rb +5 -8
  50. data/test/files/test_document_url/bar.xml +2 -0
  51. data/test/files/test_document_url/document.dtd +4 -0
  52. data/test/files/test_document_url/document.xml +6 -0
  53. data/test/helper.rb +6 -0
  54. data/test/html/test_document.rb +23 -0
  55. data/test/html/test_document_fragment.rb +5 -0
  56. data/test/test_xslt_transforms.rb +30 -0
  57. data/test/xml/sax/test_parser.rb +20 -1
  58. data/test/xml/test_builder.rb +42 -0
  59. data/test/xml/test_document.rb +64 -9
  60. data/test/xml/test_document_fragment.rb +7 -0
  61. data/test/xml/test_entity_reference.rb +12 -0
  62. data/test/xml/test_namespace.rb +20 -0
  63. data/test/xml/test_node.rb +79 -0
  64. data/test/xml/test_node_attributes.rb +29 -0
  65. data/test/xml/test_unparented_node.rb +9 -0
  66. data/test_all +11 -14
  67. metadata +744 -560
@@ -72,6 +72,8 @@ public class XmlDomParserContext extends ParserContext {
72
72
  "http://apache.org/xml/features/nonvalidating/load-dtd-grammar";
73
73
  protected static final String FEATURE_INCLUDE_IGNORABLE_WHITESPACE =
74
74
  "http://apache.org/xml/features/dom/include-ignorable-whitespace";
75
+ protected static final String CONTINUE_AFTER_FATAL_ERROR =
76
+ "http://apache.org/xml/features/continue-after-fatal-error";
75
77
  protected static final String FEATURE_NOT_EXPAND_ENTITY =
76
78
  "http://apache.org/xml/features/dom/create-entity-ref-nodes";
77
79
  protected static final String FEATURE_VALIDATION = "http://xml.org/sax/features/validation";
@@ -110,17 +112,21 @@ public class XmlDomParserContext extends ParserContext {
110
112
  System.setProperty("org.apache.xerces.xni.parser.XMLParserConfiguration",
111
113
  "org.apache.xerces.parsers.XIncludeParserConfiguration");
112
114
  }
113
-
114
- parser = new XmlDomParser(options);
115
+
116
+ parser = new NokogiriDomParser(options);
115
117
  parser.setErrorHandler(errorHandler);
116
118
 
117
119
  // Fix for Issue#586. This limits entity expansion up to 100000 and nodes up to 3000.
118
- setProperty(SECURITY_MANAGER, new org.apache.xerces.util.SecurityManager());
119
-
120
+ setProperty(SECURITY_MANAGER, new org.apache.xerces.util.SecurityManager());
121
+
120
122
  if (options.noBlanks) {
121
123
  setFeature(FEATURE_INCLUDE_IGNORABLE_WHITESPACE, false);
122
124
  }
123
125
 
126
+ if (options.recover) {
127
+ setFeature(CONTINUE_AFTER_FATAL_ERROR, true);
128
+ }
129
+
124
130
  if (options.dtdValid) {
125
131
  setFeature(FEATURE_VALIDATION, true);
126
132
  }
@@ -27,6 +27,7 @@ end
27
27
 
28
28
  if RbConfig::MAKEFILE_CONFIG['CC'] =~ /mingw/
29
29
  $CFLAGS << " -DIN_LIBXML"
30
+ $LIBS << " -lz" # TODO why is this necessary?
30
31
  end
31
32
 
32
33
  if RbConfig::MAKEFILE_CONFIG['CC'] =~ /gcc/
@@ -156,7 +156,7 @@ static VALUE set_encoding(VALUE self, VALUE encoding)
156
156
  Data_Get_Struct(self, xmlDoc, doc);
157
157
 
158
158
  if (doc->encoding)
159
- free((char *) doc->encoding); // this may produce a gcc cast warning
159
+ free((char *) doc->encoding); /* this may produce a gcc cast warning */
160
160
 
161
161
  doc->encoding = xmlStrdup((xmlChar *)StringValuePtr(encoding));
162
162
 
@@ -367,7 +367,7 @@ static VALUE new(int argc, VALUE *argv, VALUE klass)
367
367
  *
368
368
  * For more information on why this probably is *not* a good thing in general,
369
369
  * please direct your browser to
370
- * http://tenderlovemaking.com/2009/04/23/namespaces-in-xml/
370
+ * http://tenderlovemaking.com/2009/04/23/namespaces-in-xml.html
371
371
  */
372
372
  VALUE remove_namespaces_bang(VALUE self)
373
373
  {
@@ -84,7 +84,7 @@ static xmlNodePtr xmlReplaceNodeWrapper(xmlNodePtr pivot, xmlNodePtr new_node)
84
84
  }
85
85
 
86
86
  /* work around libxml2 issue: https://bugzilla.gnome.org/show_bug.cgi?id=615612 */
87
- if (retval->type == XML_TEXT_NODE) {
87
+ if (retval && retval->type == XML_TEXT_NODE) {
88
88
  if (retval->prev && retval->prev->type == XML_TEXT_NODE) {
89
89
  retval = xmlTextMerge(retval->prev, retval);
90
90
  }
@@ -699,23 +699,40 @@ static VALUE set(VALUE self, VALUE property, VALUE value)
699
699
  *
700
700
  * Get the value for +attribute+
701
701
  */
702
- static VALUE get(VALUE self, VALUE attribute)
702
+ static VALUE get(VALUE self, VALUE rattribute)
703
703
  {
704
704
  xmlNodePtr node;
705
- xmlChar* propstr ;
706
- VALUE rval ;
707
- Data_Get_Struct(self, xmlNode, node);
705
+ xmlChar* value = 0;
706
+ VALUE rvalue ;
707
+ char* attribute = 0;
708
+ char *colon = 0, *attr_name = 0, *prefix = 0;
709
+ xmlNsPtr ns;
708
710
 
709
- if(NIL_P(attribute)) return Qnil;
711
+ if (NIL_P(rattribute)) return Qnil;
710
712
 
711
- propstr = xmlGetProp(node, (xmlChar *)StringValuePtr(attribute));
713
+ Data_Get_Struct(self, xmlNode, node);
714
+ attribute = strdup(StringValuePtr(rattribute));
715
+
716
+ colon = strchr(attribute, ':');
717
+ if (colon) {
718
+ (*colon) = 0 ; /* create two null-terminated strings of the prefix and attribute name */
719
+ prefix = attribute ;
720
+ attr_name = colon + 1 ;
721
+ ns = xmlSearchNs(node->doc, node, (const xmlChar *)(prefix));
722
+ if (ns) {
723
+ value = xmlGetNsProp(node, (xmlChar*)(attr_name), ns->href);
724
+ }
725
+ } else {
726
+ value = xmlGetNoNsProp(node, (xmlChar*)attribute);
727
+ }
712
728
 
713
- if(!propstr) return Qnil;
729
+ free(attribute);
730
+ if (!value) return Qnil;
714
731
 
715
- rval = NOKOGIRI_STR_NEW2(propstr);
732
+ rvalue = NOKOGIRI_STR_NEW2(value);
733
+ xmlFree(value);
716
734
 
717
- xmlFree(propstr);
718
- return rval ;
735
+ return rvalue ;
719
736
  }
720
737
 
721
738
  /*
@@ -892,7 +909,7 @@ static VALUE node_type(VALUE self)
892
909
  *
893
910
  * Set the content for this Node
894
911
  */
895
- static VALUE set_content(VALUE self, VALUE content)
912
+ static VALUE native_content(VALUE self, VALUE content)
896
913
  {
897
914
  xmlNodePtr node, child, next ;
898
915
  Data_Get_Struct(self, xmlNode, node);
@@ -1288,7 +1305,7 @@ static VALUE in_context(VALUE self, VALUE _str, VALUE _options)
1288
1305
  child_iter = node;
1289
1306
  while (child_iter->parent)
1290
1307
  child_iter = child_iter->parent;
1291
-
1308
+
1292
1309
  if (child_iter->type == XML_DOCUMENT_FRAG_NODE)
1293
1310
  node->doc->children = NULL;
1294
1311
  }
@@ -1458,6 +1475,7 @@ void init_xml_node()
1458
1475
  rb_define_method(klass, "create_external_subset", create_external_subset, 3);
1459
1476
  rb_define_method(klass, "pointer_id", pointer_id, 0);
1460
1477
  rb_define_method(klass, "line", line, 0);
1478
+ rb_define_method(klass, "native_content=", native_content, 1);
1461
1479
 
1462
1480
  rb_define_private_method(klass, "process_xincludes", process_xincludes, 1);
1463
1481
  rb_define_private_method(klass, "in_context", in_context, 2);
@@ -1467,7 +1485,6 @@ void init_xml_node()
1467
1485
  rb_define_private_method(klass, "replace_node", replace, 1);
1468
1486
  rb_define_private_method(klass, "dump_html", dump_html, 0);
1469
1487
  rb_define_private_method(klass, "native_write_to", native_write_to, 4);
1470
- rb_define_private_method(klass, "native_content=", set_content, 1);
1471
1488
  rb_define_private_method(klass, "get", get, 1);
1472
1489
  rb_define_private_method(klass, "set", set, 2);
1473
1490
  rb_define_private_method(klass, "set_namespace", set_namespace, 1);
@@ -7,6 +7,7 @@ static ID id_start_document, id_end_document, id_start_element, id_end_element;
7
7
  static ID id_start_element_namespace, id_end_element_namespace;
8
8
  static ID id_comment, id_characters, id_xmldecl, id_error, id_warning;
9
9
  static ID id_cdata_block, id_cAttribute;
10
+ static ID id_processing_instruction;
10
11
 
11
12
  #define STRING_OR_NULL(str) \
12
13
  (RTEST(str) ? StringValuePtr(str) : NULL)
@@ -236,6 +237,19 @@ static void cdata_block(void * ctx, const xmlChar * value, int len)
236
237
  rb_funcall(doc, id_cdata_block, 1, string);
237
238
  }
238
239
 
240
+ static void processing_instruction(void * ctx, const xmlChar * name, const xmlChar * content)
241
+ {
242
+ VALUE self = NOKOGIRI_SAX_SELF(ctx);
243
+ VALUE doc = rb_iv_get(self, "@document");
244
+
245
+ rb_funcall( doc,
246
+ id_processing_instruction,
247
+ 2,
248
+ NOKOGIRI_STR_NEW2(name),
249
+ NOKOGIRI_STR_NEW2(content)
250
+ );
251
+ }
252
+
239
253
  static void deallocate(xmlSAXHandlerPtr handler)
240
254
  {
241
255
  NOKOGIRI_DEBUG_START(handler);
@@ -260,6 +274,7 @@ static VALUE allocate(VALUE klass)
260
274
  handler->warning = warning_func;
261
275
  handler->error = error_func;
262
276
  handler->cdataBlock = cdata_block;
277
+ handler->processingInstruction = processing_instruction;
263
278
  handler->initialized = XML_SAX2_MAGIC;
264
279
 
265
280
  return Data_Wrap_Struct(klass, NULL, deallocate, handler);
@@ -290,4 +305,5 @@ void init_xml_sax_parser()
290
305
  id_cAttribute = rb_intern("Attribute");
291
306
  id_start_element_namespace = rb_intern("start_element_namespace");
292
307
  id_end_element_namespace = rb_intern("end_element_namespace");
308
+ id_processing_instruction = rb_intern("processing_instruction");
293
309
  }
@@ -109,6 +109,10 @@ static VALUE serialize(VALUE self, VALUE xmlobj)
109
109
  return rval ;
110
110
  }
111
111
 
112
+ static void swallow_superfluous_xml_errors(void * userdata, xmlErrorPtr error, ...)
113
+ {
114
+ }
115
+
112
116
  /*
113
117
  * call-seq:
114
118
  * transform(document, params = [])
@@ -126,12 +130,13 @@ static VALUE serialize(VALUE self, VALUE xmlobj)
126
130
  */
127
131
  static VALUE transform(int argc, VALUE* argv, VALUE self)
128
132
  {
129
- VALUE xmldoc, paramobj ;
133
+ VALUE xmldoc, paramobj, errstr, exception ;
130
134
  xmlDocPtr xml ;
131
135
  xmlDocPtr result ;
132
136
  nokogiriXsltStylesheetTuple *wrapper;
133
137
  const char** params ;
134
138
  long param_len, j ;
139
+ int parse_error_occurred ;
135
140
 
136
141
  rb_scan_args(argc, argv, "11", &xmldoc, &paramobj);
137
142
  if (NIL_P(paramobj)) { paramobj = rb_ary_new2(0L) ; }
@@ -158,10 +163,22 @@ static VALUE transform(int argc, VALUE* argv, VALUE self)
158
163
  }
159
164
  params[param_len] = 0 ;
160
165
 
166
+ errstr = rb_str_new(0, 0);
167
+ xsltSetGenericErrorFunc((void *)errstr, xslt_generic_error_handler);
168
+ xmlSetGenericErrorFunc(NULL, (xmlGenericErrorFunc)&swallow_superfluous_xml_errors);
169
+
161
170
  result = xsltApplyStylesheet(wrapper->ss, xml, params);
162
171
  free(params);
163
172
 
164
- if (!result) rb_raise(rb_eRuntimeError, "could not perform xslt transform on document");
173
+ xsltSetGenericErrorFunc(NULL, NULL);
174
+ xmlSetGenericErrorFunc(NULL, NULL);
175
+
176
+ parse_error_occurred = (Qfalse == rb_funcall(errstr, rb_intern("empty?"), 0));
177
+
178
+ if (parse_error_occurred) {
179
+ exception = rb_exc_new3(rb_eRuntimeError, errstr);
180
+ rb_exc_raise(exception);
181
+ }
165
182
 
166
183
  return Nokogiri_wrap_xml_document((VALUE)0, result) ;
167
184
  }
Binary file
Binary file
@@ -1,6 +1,6 @@
1
1
  module Nokogiri
2
2
  # The version of Nokogiri you are using
3
- VERSION = '1.5.5.rc3'
3
+ VERSION = '1.5.6'
4
4
 
5
5
  class VersionInfo # :nodoc:
6
6
  def jruby?
@@ -52,6 +52,9 @@ module Nokogiri
52
52
  hash_info['libxml']['compiled'] = compiled_parser_version
53
53
  hash_info['libxml']['loaded'] = loaded_parser_version
54
54
  hash_info['warnings'] = warnings
55
+ elsif jruby?
56
+ hash_info['xerces'] = Nokogiri::XERCES_VERSION
57
+ hash_info['nekohtml'] = Nokogiri::NEKO_VERSION
55
58
  end
56
59
 
57
60
  hash_info
@@ -149,13 +149,15 @@ module Nokogiri
149
149
  # Non-prefixed default namespaces (as in "xmlns=") are not included
150
150
  # in the hash.
151
151
  #
152
- # Note this is a very expensive operation in current implementation, as it
153
- # traverses the entire graph, and also has to bring each node across the
154
- # libxml bridge into a ruby object.
152
+ # Note that this method does an xpath lookup for nodes with
153
+ # namespaces, and as a result the order may be dependent on the
154
+ # implementation of the underlying XML library.
155
+ #
155
156
  def collect_namespaces
156
- ns = {}
157
- traverse { |j| ns.merge!(j.namespaces) }
158
- ns
157
+ xpath("//namespace::*").inject({}) do |hash, ns|
158
+ hash[["xmlns",ns.prefix].compact.join(":")] = ns.href if ns.prefix != "xml"
159
+ hash
160
+ end
159
161
  end
160
162
 
161
163
  # Get the list of decorators given +key+
@@ -13,7 +13,8 @@ module Nokogiri
13
13
  children = if ctx
14
14
  # Fix for issue#490
15
15
  if Nokogiri.jruby?
16
- ctx.parse("<root>#{tags}</root>").xpath("/root/node()")
16
+ # fix for issue #770
17
+ ctx.parse("<root #{namespace_declarations(ctx)}>#{tags}</root>").children
17
18
  else
18
19
  ctx.parse(tags)
19
20
  end
@@ -93,6 +94,14 @@ module Nokogiri
93
94
 
94
95
  private
95
96
 
97
+ # fix for issue 770
98
+ def namespace_declarations ctx
99
+ ctx.namespace_scopes.map do |namespace|
100
+ prefix = namespace.prefix.nil? ? "" : ":#{namespace.prefix}"
101
+ %Q{xmlns#{prefix}="#{namespace.href}"}
102
+ end.join ' '
103
+ end
104
+
96
105
  def coerce data
97
106
  return super unless String === data
98
107
 
@@ -251,14 +251,13 @@ module Nokogiri
251
251
  ###
252
252
  # Get the attribute value for the attribute +name+
253
253
  def [] name
254
- return nil unless key?(name.to_s)
255
254
  get(name.to_s)
256
255
  end
257
256
 
258
257
  ###
259
258
  # Set the attribute value for the attribute +name+ to +value+
260
259
  def []= name, value
261
- set name.to_s, value
260
+ set name.to_s, value.to_s
262
261
  end
263
262
 
264
263
  ###
@@ -299,20 +298,7 @@ module Nokogiri
299
298
  def add_previous_sibling node_or_tags
300
299
  raise ArgumentError.new("A document may not have multiple root nodes.") if parent.is_a?(XML::Document) && !node_or_tags.is_a?(XML::ProcessingInstruction)
301
300
 
302
- node_or_tags = coerce(node_or_tags)
303
- if node_or_tags.is_a?(XML::NodeSet)
304
- if text?
305
- pivot = Nokogiri::XML::Node.new 'dummy', document
306
- add_previous_sibling_node pivot
307
- else
308
- pivot = self
309
- end
310
- node_or_tags.each { |n| pivot.send :add_previous_sibling_node, n }
311
- pivot.unlink if text?
312
- else
313
- add_previous_sibling_node node_or_tags
314
- end
315
- node_or_tags
301
+ add_sibling :previous, node_or_tags
316
302
  end
317
303
 
318
304
  ###
@@ -325,20 +311,7 @@ module Nokogiri
325
311
  def add_next_sibling node_or_tags
326
312
  raise ArgumentError.new("A document may not have multiple root nodes.") if parent.is_a?(XML::Document)
327
313
 
328
- node_or_tags = coerce(node_or_tags)
329
- if node_or_tags.is_a?(XML::NodeSet)
330
- if text?
331
- pivot = Nokogiri::XML::Node.new 'dummy', document
332
- add_next_sibling_node pivot
333
- else
334
- pivot = self
335
- end
336
- node_or_tags.reverse_each { |n| pivot.send :add_next_sibling_node, n }
337
- pivot.unlink if text?
338
- else
339
- add_next_sibling_node node_or_tags
340
- end
341
- node_or_tags
314
+ add_sibling :next, node_or_tags
342
315
  end
343
316
 
344
317
  ####
@@ -403,17 +376,22 @@ module Nokogiri
403
376
  #
404
377
  # Also see related method +swap+.
405
378
  def replace node_or_tags
379
+ # We cannot replace a text node directly, otherwise libxml will return
380
+ # an internal error at parser.c:13031, I don't know exactly why
381
+ # libxml is trying to find a parent node that is an element or document
382
+ # so I can't tell if this is bug in libxml or not. issue #775.
383
+ if text?
384
+ replacee = Nokogiri::XML::Node.new 'dummy', document
385
+ add_previous_sibling_node replacee
386
+ unlink
387
+ return replacee.replace node_or_tags
388
+ end
389
+
406
390
  node_or_tags = coerce(node_or_tags)
391
+
407
392
  if node_or_tags.is_a?(XML::NodeSet)
408
- if text?
409
- replacee = Nokogiri::XML::Node.new 'dummy', document
410
- add_previous_sibling_node replacee
411
- unlink
412
- else
413
- replacee = self
414
- end
415
- node_or_tags.each { |n| replacee.add_previous_sibling n }
416
- replacee.unlink
393
+ node_or_tags.each { |n| add_previous_sibling n }
394
+ unlink
417
395
  else
418
396
  replace_node node_or_tags
419
397
  end
@@ -761,12 +739,7 @@ module Nokogiri
761
739
  # See Node#write_to for a list of +options+. For formatted output,
762
740
  # use Node#to_xhtml instead.
763
741
  def to_html options = {}
764
- # FIXME: this is a hack around broken libxml versions
765
- return dump_html if Nokogiri.uses_libxml? && %w[2 6] === LIBXML_VERSION.split('.')[0..1]
766
-
767
- options[:save_with] |= SaveOptions::DEFAULT_HTML if options[:save_with]
768
- options[:save_with] = SaveOptions::DEFAULT_HTML unless options[:save_with]
769
- serialize(options)
742
+ to_format SaveOptions::DEFAULT_HTML, options
770
743
  end
771
744
 
772
745
  ###
@@ -787,12 +760,7 @@ module Nokogiri
787
760
  #
788
761
  # See Node#write_to for a list of +options+
789
762
  def to_xhtml options = {}
790
- # FIXME: this is a hack around broken libxml versions
791
- return dump_html if Nokogiri.uses_libxml? && %w[2 6] === LIBXML_VERSION.split('.')[0..1]
792
-
793
- options[:save_with] |= SaveOptions::DEFAULT_XHTML if options[:save_with]
794
- options[:save_with] = SaveOptions::DEFAULT_XHTML unless options[:save_with]
795
- serialize(options)
763
+ to_format SaveOptions::DEFAULT_XHTML, options
796
764
  end
797
765
 
798
766
  ###
@@ -835,11 +803,7 @@ module Nokogiri
835
803
  #
836
804
  # See Node#write_to for a list of +options+
837
805
  def write_html_to io, options = {}
838
- # FIXME: this is a hack around broken libxml versions
839
- return (io << dump_html) if Nokogiri.uses_libxml? && %w[2 6] === LIBXML_VERSION.split('.')[0..1]
840
-
841
- options[:save_with] ||= SaveOptions::DEFAULT_HTML
842
- write_to io, options
806
+ write_format_to SaveOptions::DEFAULT_HTML, io, options
843
807
  end
844
808
 
845
809
  ###
@@ -847,11 +811,7 @@ module Nokogiri
847
811
  #
848
812
  # See Node#write_to for a list of +options+
849
813
  def write_xhtml_to io, options = {}
850
- # FIXME: this is a hack around broken libxml versions
851
- return (io << dump_html) if Nokogiri.uses_libxml? && %w[2 6] === LIBXML_VERSION.split('.')[0..1]
852
-
853
- options[:save_with] ||= SaveOptions::DEFAULT_XHTML
854
- write_to io, options
814
+ write_format_to SaveOptions::DEFAULT_XHTML, io, options
855
815
  end
856
816
 
857
817
  ###
@@ -898,6 +858,43 @@ module Nokogiri
898
858
 
899
859
  private
900
860
 
861
+ def add_sibling next_or_previous, node_or_tags
862
+ impl = (next_or_previous == :next) ? :add_next_sibling_node : :add_previous_sibling_node
863
+ iter = (next_or_previous == :next) ? :reverse_each : :each
864
+
865
+ node_or_tags = coerce node_or_tags
866
+ if node_or_tags.is_a?(XML::NodeSet)
867
+ if text?
868
+ pivot = Nokogiri::XML::Node.new 'dummy', document
869
+ send impl, pivot
870
+ else
871
+ pivot = self
872
+ end
873
+ node_or_tags.send(iter) { |n| pivot.send impl, n }
874
+ pivot.unlink if text?
875
+ else
876
+ send impl, node_or_tags
877
+ end
878
+ node_or_tags
879
+ end
880
+
881
+ def to_format save_option, options
882
+ # FIXME: this is a hack around broken libxml versions
883
+ return dump_html if Nokogiri.uses_libxml? && %w[2 6] === LIBXML_VERSION.split('.')[0..1]
884
+
885
+ options[:save_with] |= save_option if options[:save_with]
886
+ options[:save_with] = save_option unless options[:save_with]
887
+ serialize(options)
888
+ end
889
+
890
+ def write_format_to save_option, io, options
891
+ # FIXME: this is a hack around broken libxml versions
892
+ return (io << dump_html) if Nokogiri.uses_libxml? && %w[2 6] === LIBXML_VERSION.split('.')[0..1]
893
+
894
+ options[:save_with] ||= save_option
895
+ write_to io, options
896
+ end
897
+
901
898
  def extract_params params # :nodoc:
902
899
  # Pop off our custom function handler if it exists
903
900
  handler = params.find { |param|