nokogiri 1.13.0-aarch64-linux
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.
- checksums.yaml +7 -0
- data/Gemfile +5 -0
- data/LICENSE-DEPENDENCIES.md +1903 -0
- data/LICENSE.md +9 -0
- data/README.md +280 -0
- data/bin/nokogiri +131 -0
- data/dependencies.yml +73 -0
- data/ext/nokogiri/depend +38 -0
- data/ext/nokogiri/extconf.rb +1000 -0
- data/ext/nokogiri/gumbo.c +584 -0
- data/ext/nokogiri/html4_document.c +166 -0
- data/ext/nokogiri/html4_element_description.c +294 -0
- data/ext/nokogiri/html4_entity_lookup.c +37 -0
- data/ext/nokogiri/html4_sax_parser_context.c +120 -0
- data/ext/nokogiri/html4_sax_push_parser.c +95 -0
- data/ext/nokogiri/include/libexslt/exslt.h +102 -0
- data/ext/nokogiri/include/libexslt/exsltconfig.h +70 -0
- data/ext/nokogiri/include/libexslt/exsltexports.h +140 -0
- data/ext/nokogiri/include/libxml2/libxml/DOCBparser.h +96 -0
- data/ext/nokogiri/include/libxml2/libxml/HTMLparser.h +306 -0
- data/ext/nokogiri/include/libxml2/libxml/HTMLtree.h +147 -0
- data/ext/nokogiri/include/libxml2/libxml/SAX.h +173 -0
- data/ext/nokogiri/include/libxml2/libxml/SAX2.h +178 -0
- data/ext/nokogiri/include/libxml2/libxml/c14n.h +128 -0
- data/ext/nokogiri/include/libxml2/libxml/catalog.h +182 -0
- data/ext/nokogiri/include/libxml2/libxml/chvalid.h +230 -0
- data/ext/nokogiri/include/libxml2/libxml/debugXML.h +217 -0
- data/ext/nokogiri/include/libxml2/libxml/dict.h +79 -0
- data/ext/nokogiri/include/libxml2/libxml/encoding.h +245 -0
- data/ext/nokogiri/include/libxml2/libxml/entities.h +151 -0
- data/ext/nokogiri/include/libxml2/libxml/globals.h +508 -0
- data/ext/nokogiri/include/libxml2/libxml/hash.h +236 -0
- data/ext/nokogiri/include/libxml2/libxml/list.h +137 -0
- data/ext/nokogiri/include/libxml2/libxml/nanoftp.h +163 -0
- data/ext/nokogiri/include/libxml2/libxml/nanohttp.h +81 -0
- data/ext/nokogiri/include/libxml2/libxml/parser.h +1243 -0
- data/ext/nokogiri/include/libxml2/libxml/parserInternals.h +644 -0
- data/ext/nokogiri/include/libxml2/libxml/pattern.h +100 -0
- data/ext/nokogiri/include/libxml2/libxml/relaxng.h +217 -0
- data/ext/nokogiri/include/libxml2/libxml/schemasInternals.h +958 -0
- data/ext/nokogiri/include/libxml2/libxml/schematron.h +142 -0
- data/ext/nokogiri/include/libxml2/libxml/threads.h +89 -0
- data/ext/nokogiri/include/libxml2/libxml/tree.h +1311 -0
- data/ext/nokogiri/include/libxml2/libxml/uri.h +94 -0
- data/ext/nokogiri/include/libxml2/libxml/valid.h +458 -0
- data/ext/nokogiri/include/libxml2/libxml/xinclude.h +129 -0
- data/ext/nokogiri/include/libxml2/libxml/xlink.h +189 -0
- data/ext/nokogiri/include/libxml2/libxml/xmlIO.h +368 -0
- data/ext/nokogiri/include/libxml2/libxml/xmlautomata.h +146 -0
- data/ext/nokogiri/include/libxml2/libxml/xmlerror.h +946 -0
- data/ext/nokogiri/include/libxml2/libxml/xmlexports.h +77 -0
- data/ext/nokogiri/include/libxml2/libxml/xmlmemory.h +224 -0
- data/ext/nokogiri/include/libxml2/libxml/xmlmodule.h +57 -0
- data/ext/nokogiri/include/libxml2/libxml/xmlreader.h +428 -0
- data/ext/nokogiri/include/libxml2/libxml/xmlregexp.h +222 -0
- data/ext/nokogiri/include/libxml2/libxml/xmlsave.h +88 -0
- data/ext/nokogiri/include/libxml2/libxml/xmlschemas.h +246 -0
- data/ext/nokogiri/include/libxml2/libxml/xmlschemastypes.h +151 -0
- data/ext/nokogiri/include/libxml2/libxml/xmlstring.h +140 -0
- data/ext/nokogiri/include/libxml2/libxml/xmlunicode.h +202 -0
- data/ext/nokogiri/include/libxml2/libxml/xmlversion.h +485 -0
- data/ext/nokogiri/include/libxml2/libxml/xmlwriter.h +488 -0
- data/ext/nokogiri/include/libxml2/libxml/xpath.h +564 -0
- data/ext/nokogiri/include/libxml2/libxml/xpathInternals.h +632 -0
- data/ext/nokogiri/include/libxml2/libxml/xpointer.h +114 -0
- data/ext/nokogiri/include/libxslt/attributes.h +38 -0
- data/ext/nokogiri/include/libxslt/documents.h +93 -0
- data/ext/nokogiri/include/libxslt/extensions.h +262 -0
- data/ext/nokogiri/include/libxslt/extra.h +72 -0
- data/ext/nokogiri/include/libxslt/functions.h +78 -0
- data/ext/nokogiri/include/libxslt/imports.h +75 -0
- data/ext/nokogiri/include/libxslt/keys.h +53 -0
- data/ext/nokogiri/include/libxslt/namespaces.h +68 -0
- data/ext/nokogiri/include/libxslt/numbersInternals.h +73 -0
- data/ext/nokogiri/include/libxslt/pattern.h +84 -0
- data/ext/nokogiri/include/libxslt/preproc.h +43 -0
- data/ext/nokogiri/include/libxslt/security.h +104 -0
- data/ext/nokogiri/include/libxslt/templates.h +77 -0
- data/ext/nokogiri/include/libxslt/transform.h +207 -0
- data/ext/nokogiri/include/libxslt/variables.h +118 -0
- data/ext/nokogiri/include/libxslt/xslt.h +110 -0
- data/ext/nokogiri/include/libxslt/xsltInternals.h +1978 -0
- data/ext/nokogiri/include/libxslt/xsltconfig.h +180 -0
- data/ext/nokogiri/include/libxslt/xsltexports.h +142 -0
- data/ext/nokogiri/include/libxslt/xsltlocale.h +76 -0
- data/ext/nokogiri/include/libxslt/xsltutils.h +313 -0
- data/ext/nokogiri/libxml2_backwards_compat.c +121 -0
- data/ext/nokogiri/nokogiri.c +278 -0
- data/ext/nokogiri/nokogiri.h +223 -0
- data/ext/nokogiri/test_global_handlers.c +40 -0
- data/ext/nokogiri/xml_attr.c +103 -0
- data/ext/nokogiri/xml_attribute_decl.c +70 -0
- data/ext/nokogiri/xml_cdata.c +57 -0
- data/ext/nokogiri/xml_comment.c +62 -0
- data/ext/nokogiri/xml_document.c +680 -0
- data/ext/nokogiri/xml_document_fragment.c +44 -0
- data/ext/nokogiri/xml_dtd.c +208 -0
- data/ext/nokogiri/xml_element_content.c +128 -0
- data/ext/nokogiri/xml_element_decl.c +69 -0
- data/ext/nokogiri/xml_encoding_handler.c +104 -0
- data/ext/nokogiri/xml_entity_decl.c +112 -0
- data/ext/nokogiri/xml_entity_reference.c +50 -0
- data/ext/nokogiri/xml_namespace.c +120 -0
- data/ext/nokogiri/xml_node.c +2144 -0
- data/ext/nokogiri/xml_node_set.c +498 -0
- data/ext/nokogiri/xml_processing_instruction.c +54 -0
- data/ext/nokogiri/xml_reader.c +719 -0
- data/ext/nokogiri/xml_relax_ng.c +185 -0
- data/ext/nokogiri/xml_sax_parser.c +310 -0
- data/ext/nokogiri/xml_sax_parser_context.c +281 -0
- data/ext/nokogiri/xml_sax_push_parser.c +168 -0
- data/ext/nokogiri/xml_schema.c +284 -0
- data/ext/nokogiri/xml_syntax_error.c +85 -0
- data/ext/nokogiri/xml_text.c +48 -0
- data/ext/nokogiri/xml_xpath_context.c +406 -0
- data/ext/nokogiri/xslt_stylesheet.c +264 -0
- data/gumbo-parser/CHANGES.md +63 -0
- data/gumbo-parser/Makefile +101 -0
- data/gumbo-parser/THANKS +27 -0
- data/lib/nokogiri/2.6/nokogiri.so +0 -0
- data/lib/nokogiri/2.7/nokogiri.so +0 -0
- data/lib/nokogiri/3.0/nokogiri.so +0 -0
- data/lib/nokogiri/3.1/nokogiri.so +0 -0
- data/lib/nokogiri/class_resolver.rb +67 -0
- data/lib/nokogiri/css/node.rb +54 -0
- data/lib/nokogiri/css/parser.rb +759 -0
- data/lib/nokogiri/css/parser.y +280 -0
- data/lib/nokogiri/css/parser_extras.rb +94 -0
- data/lib/nokogiri/css/syntax_error.rb +9 -0
- data/lib/nokogiri/css/tokenizer.rb +155 -0
- data/lib/nokogiri/css/tokenizer.rex +56 -0
- data/lib/nokogiri/css/xpath_visitor.rb +359 -0
- data/lib/nokogiri/css.rb +60 -0
- data/lib/nokogiri/decorators/slop.rb +44 -0
- data/lib/nokogiri/extension.rb +31 -0
- data/lib/nokogiri/gumbo.rb +15 -0
- data/lib/nokogiri/html.rb +48 -0
- data/lib/nokogiri/html4/builder.rb +37 -0
- data/lib/nokogiri/html4/document.rb +331 -0
- data/lib/nokogiri/html4/document_fragment.rb +54 -0
- data/lib/nokogiri/html4/element_description.rb +25 -0
- data/lib/nokogiri/html4/element_description_defaults.rb +578 -0
- data/lib/nokogiri/html4/entity_lookup.rb +15 -0
- data/lib/nokogiri/html4/sax/parser.rb +61 -0
- data/lib/nokogiri/html4/sax/parser_context.rb +20 -0
- data/lib/nokogiri/html4/sax/push_parser.rb +37 -0
- data/lib/nokogiri/html4.rb +46 -0
- data/lib/nokogiri/html5/document.rb +88 -0
- data/lib/nokogiri/html5/document_fragment.rb +83 -0
- data/lib/nokogiri/html5/node.rb +96 -0
- data/lib/nokogiri/html5.rb +477 -0
- data/lib/nokogiri/jruby/dependencies.rb +21 -0
- data/lib/nokogiri/syntax_error.rb +6 -0
- data/lib/nokogiri/version/constant.rb +6 -0
- data/lib/nokogiri/version/info.rb +221 -0
- data/lib/nokogiri/version.rb +4 -0
- data/lib/nokogiri/xml/attr.rb +17 -0
- data/lib/nokogiri/xml/attribute_decl.rb +20 -0
- data/lib/nokogiri/xml/builder.rb +485 -0
- data/lib/nokogiri/xml/cdata.rb +13 -0
- data/lib/nokogiri/xml/character_data.rb +9 -0
- data/lib/nokogiri/xml/document.rb +418 -0
- data/lib/nokogiri/xml/document_fragment.rb +162 -0
- data/lib/nokogiri/xml/dtd.rb +34 -0
- data/lib/nokogiri/xml/element_content.rb +38 -0
- data/lib/nokogiri/xml/element_decl.rb +15 -0
- data/lib/nokogiri/xml/entity_decl.rb +21 -0
- data/lib/nokogiri/xml/entity_reference.rb +20 -0
- data/lib/nokogiri/xml/namespace.rb +16 -0
- data/lib/nokogiri/xml/node/save_options.rb +65 -0
- data/lib/nokogiri/xml/node.rb +1402 -0
- data/lib/nokogiri/xml/node_set.rb +364 -0
- data/lib/nokogiri/xml/notation.rb +19 -0
- data/lib/nokogiri/xml/parse_options.rb +133 -0
- data/lib/nokogiri/xml/pp/character_data.rb +21 -0
- data/lib/nokogiri/xml/pp/node.rb +55 -0
- data/lib/nokogiri/xml/pp.rb +4 -0
- data/lib/nokogiri/xml/processing_instruction.rb +10 -0
- data/lib/nokogiri/xml/reader.rb +107 -0
- data/lib/nokogiri/xml/relax_ng.rb +38 -0
- data/lib/nokogiri/xml/sax/document.rb +167 -0
- data/lib/nokogiri/xml/sax/parser.rb +125 -0
- data/lib/nokogiri/xml/sax/parser_context.rb +21 -0
- data/lib/nokogiri/xml/sax/push_parser.rb +61 -0
- data/lib/nokogiri/xml/sax.rb +6 -0
- data/lib/nokogiri/xml/schema.rb +73 -0
- data/lib/nokogiri/xml/searchable.rb +259 -0
- data/lib/nokogiri/xml/syntax_error.rb +71 -0
- data/lib/nokogiri/xml/text.rb +11 -0
- data/lib/nokogiri/xml/xpath/syntax_error.rb +13 -0
- data/lib/nokogiri/xml/xpath.rb +21 -0
- data/lib/nokogiri/xml/xpath_context.rb +16 -0
- data/lib/nokogiri/xml.rb +75 -0
- data/lib/nokogiri/xslt/stylesheet.rb +27 -0
- data/lib/nokogiri/xslt.rb +58 -0
- data/lib/nokogiri.rb +128 -0
- data/lib/xsd/xmlparser/nokogiri.rb +104 -0
- metadata +539 -0
@@ -0,0 +1,2144 @@
|
|
1
|
+
#include <nokogiri.h>
|
2
|
+
|
3
|
+
// :stopdoc:
|
4
|
+
|
5
|
+
VALUE cNokogiriXmlNode ;
|
6
|
+
static ID id_decorate, id_decorate_bang;
|
7
|
+
|
8
|
+
typedef xmlNodePtr(*pivot_reparentee_func)(xmlNodePtr, xmlNodePtr);
|
9
|
+
|
10
|
+
|
11
|
+
#ifdef DEBUG
|
12
|
+
static void
|
13
|
+
_xml_node_dealloc(xmlNodePtr x)
|
14
|
+
{
|
15
|
+
NOKOGIRI_DEBUG_START(x)
|
16
|
+
NOKOGIRI_DEBUG_END(x)
|
17
|
+
}
|
18
|
+
#else
|
19
|
+
# define _xml_node_dealloc 0
|
20
|
+
#endif
|
21
|
+
|
22
|
+
|
23
|
+
static void
|
24
|
+
_xml_node_mark(xmlNodePtr node)
|
25
|
+
{
|
26
|
+
xmlDocPtr doc = node->doc;
|
27
|
+
if (doc->type == XML_DOCUMENT_NODE || doc->type == XML_HTML_DOCUMENT_NODE) {
|
28
|
+
if (DOC_RUBY_OBJECT_TEST(doc)) {
|
29
|
+
rb_gc_mark(DOC_RUBY_OBJECT(doc));
|
30
|
+
}
|
31
|
+
} else if (node->doc->_private) {
|
32
|
+
rb_gc_mark((VALUE)doc->_private);
|
33
|
+
}
|
34
|
+
}
|
35
|
+
|
36
|
+
|
37
|
+
static void
|
38
|
+
relink_namespace(xmlNodePtr reparented)
|
39
|
+
{
|
40
|
+
xmlNodePtr child;
|
41
|
+
xmlAttrPtr attr;
|
42
|
+
|
43
|
+
if (reparented->type != XML_ATTRIBUTE_NODE &&
|
44
|
+
reparented->type != XML_ELEMENT_NODE) { return; }
|
45
|
+
|
46
|
+
if (reparented->ns == NULL || reparented->ns->prefix == NULL) {
|
47
|
+
xmlNsPtr ns = NULL;
|
48
|
+
xmlChar *name = NULL, *prefix = NULL;
|
49
|
+
|
50
|
+
name = xmlSplitQName2(reparented->name, &prefix);
|
51
|
+
|
52
|
+
if (reparented->type == XML_ATTRIBUTE_NODE) {
|
53
|
+
if (prefix == NULL || strcmp((char *)prefix, XMLNS_PREFIX) == 0) {
|
54
|
+
xmlFree(name);
|
55
|
+
xmlFree(prefix);
|
56
|
+
return;
|
57
|
+
}
|
58
|
+
}
|
59
|
+
|
60
|
+
ns = xmlSearchNs(reparented->doc, reparented, prefix);
|
61
|
+
|
62
|
+
if (ns != NULL) {
|
63
|
+
xmlNodeSetName(reparented, name);
|
64
|
+
xmlSetNs(reparented, ns);
|
65
|
+
}
|
66
|
+
|
67
|
+
xmlFree(name);
|
68
|
+
xmlFree(prefix);
|
69
|
+
}
|
70
|
+
|
71
|
+
/* Avoid segv when relinking against unlinked nodes. */
|
72
|
+
if (reparented->type != XML_ELEMENT_NODE || !reparented->parent) { return; }
|
73
|
+
|
74
|
+
/* Make sure that our reparented node has the correct namespaces */
|
75
|
+
if (!reparented->ns &&
|
76
|
+
(reparented->doc != (xmlDocPtr)reparented->parent) &&
|
77
|
+
(rb_iv_get(DOC_RUBY_OBJECT(reparented->doc), "@namespace_inheritance") == Qtrue)) {
|
78
|
+
xmlSetNs(reparented, reparented->parent->ns);
|
79
|
+
}
|
80
|
+
|
81
|
+
/* Search our parents for an existing definition */
|
82
|
+
if (reparented->nsDef) {
|
83
|
+
xmlNsPtr curr = reparented->nsDef;
|
84
|
+
xmlNsPtr prev = NULL;
|
85
|
+
|
86
|
+
while (curr) {
|
87
|
+
xmlNsPtr ns = xmlSearchNsByHref(
|
88
|
+
reparented->doc,
|
89
|
+
reparented->parent,
|
90
|
+
curr->href
|
91
|
+
);
|
92
|
+
/* If we find the namespace is already declared, remove it from this
|
93
|
+
* definition list. */
|
94
|
+
if (ns && ns != curr && xmlStrEqual(ns->prefix, curr->prefix)) {
|
95
|
+
if (prev) {
|
96
|
+
prev->next = curr->next;
|
97
|
+
} else {
|
98
|
+
reparented->nsDef = curr->next;
|
99
|
+
}
|
100
|
+
noko_xml_document_pin_namespace(curr, reparented->doc);
|
101
|
+
} else {
|
102
|
+
prev = curr;
|
103
|
+
}
|
104
|
+
curr = curr->next;
|
105
|
+
}
|
106
|
+
}
|
107
|
+
|
108
|
+
/*
|
109
|
+
* Search our parents for an existing definition of current namespace,
|
110
|
+
* because the definition it's pointing to may have just been removed nsDef.
|
111
|
+
*
|
112
|
+
* And although that would technically probably be OK, I'd feel better if we
|
113
|
+
* referred to a namespace that's still present in a node's nsDef somewhere
|
114
|
+
* in the doc.
|
115
|
+
*/
|
116
|
+
if (reparented->ns) {
|
117
|
+
xmlNsPtr ns = xmlSearchNs(reparented->doc, reparented, reparented->ns->prefix);
|
118
|
+
if (ns
|
119
|
+
&& ns != reparented->ns
|
120
|
+
&& xmlStrEqual(ns->prefix, reparented->ns->prefix)
|
121
|
+
&& xmlStrEqual(ns->href, reparented->ns->href)
|
122
|
+
) {
|
123
|
+
xmlSetNs(reparented, ns);
|
124
|
+
}
|
125
|
+
}
|
126
|
+
|
127
|
+
/* Only walk all children if there actually is a namespace we need to */
|
128
|
+
/* reparent. */
|
129
|
+
if (NULL == reparented->ns) { return; }
|
130
|
+
|
131
|
+
/* When a node gets reparented, walk it's children to make sure that */
|
132
|
+
/* their namespaces are reparented as well. */
|
133
|
+
child = reparented->children;
|
134
|
+
while (NULL != child) {
|
135
|
+
relink_namespace(child);
|
136
|
+
child = child->next;
|
137
|
+
}
|
138
|
+
|
139
|
+
if (reparented->type == XML_ELEMENT_NODE) {
|
140
|
+
attr = reparented->properties;
|
141
|
+
while (NULL != attr) {
|
142
|
+
relink_namespace((xmlNodePtr)attr);
|
143
|
+
attr = attr->next;
|
144
|
+
}
|
145
|
+
}
|
146
|
+
}
|
147
|
+
|
148
|
+
|
149
|
+
/* internal function meant to wrap xmlReplaceNode
|
150
|
+
and fix some issues we have with libxml2 merging nodes */
|
151
|
+
static xmlNodePtr
|
152
|
+
xmlReplaceNodeWrapper(xmlNodePtr pivot, xmlNodePtr new_node)
|
153
|
+
{
|
154
|
+
xmlNodePtr retval ;
|
155
|
+
|
156
|
+
retval = xmlReplaceNode(pivot, new_node) ;
|
157
|
+
|
158
|
+
if (retval == pivot) {
|
159
|
+
retval = new_node ; /* return semantics for reparent_node_with */
|
160
|
+
}
|
161
|
+
|
162
|
+
/* work around libxml2 issue: https://bugzilla.gnome.org/show_bug.cgi?id=615612 */
|
163
|
+
if (retval && retval->type == XML_TEXT_NODE) {
|
164
|
+
if (retval->prev && retval->prev->type == XML_TEXT_NODE) {
|
165
|
+
retval = xmlTextMerge(retval->prev, retval);
|
166
|
+
}
|
167
|
+
if (retval->next && retval->next->type == XML_TEXT_NODE) {
|
168
|
+
retval = xmlTextMerge(retval, retval->next);
|
169
|
+
}
|
170
|
+
}
|
171
|
+
|
172
|
+
return retval ;
|
173
|
+
}
|
174
|
+
|
175
|
+
|
176
|
+
static void
|
177
|
+
raise_if_ancestor_of_self(xmlNodePtr self)
|
178
|
+
{
|
179
|
+
for (xmlNodePtr ancestor = self->parent ; ancestor ; ancestor = ancestor->parent) {
|
180
|
+
if (self == ancestor) {
|
181
|
+
rb_raise(rb_eRuntimeError, "cycle detected: node '%s' is an ancestor of itself", self->name);
|
182
|
+
}
|
183
|
+
}
|
184
|
+
}
|
185
|
+
|
186
|
+
|
187
|
+
static VALUE
|
188
|
+
reparent_node_with(VALUE pivot_obj, VALUE reparentee_obj, pivot_reparentee_func prf)
|
189
|
+
{
|
190
|
+
VALUE reparented_obj ;
|
191
|
+
xmlNodePtr reparentee, original_reparentee, pivot, reparented, next_text, new_next_text, parent ;
|
192
|
+
int original_ns_prefix_is_default = 0 ;
|
193
|
+
|
194
|
+
if (!rb_obj_is_kind_of(reparentee_obj, cNokogiriXmlNode)) {
|
195
|
+
rb_raise(rb_eArgError, "node must be a Nokogiri::XML::Node");
|
196
|
+
}
|
197
|
+
if (rb_obj_is_kind_of(reparentee_obj, cNokogiriXmlDocument)) {
|
198
|
+
rb_raise(rb_eArgError, "node must be a Nokogiri::XML::Node");
|
199
|
+
}
|
200
|
+
|
201
|
+
Data_Get_Struct(reparentee_obj, xmlNode, reparentee);
|
202
|
+
Data_Get_Struct(pivot_obj, xmlNode, pivot);
|
203
|
+
|
204
|
+
/*
|
205
|
+
* Check if nodes given are appropriate to have a parent-child
|
206
|
+
* relationship, based on the DOM specification.
|
207
|
+
*
|
208
|
+
* cf. http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/core.html#ID-1590626202
|
209
|
+
*/
|
210
|
+
if (prf == xmlAddChild) {
|
211
|
+
parent = pivot;
|
212
|
+
} else {
|
213
|
+
parent = pivot->parent;
|
214
|
+
}
|
215
|
+
|
216
|
+
if (parent) {
|
217
|
+
switch (parent->type) {
|
218
|
+
case XML_DOCUMENT_NODE:
|
219
|
+
case XML_HTML_DOCUMENT_NODE:
|
220
|
+
switch (reparentee->type) {
|
221
|
+
case XML_ELEMENT_NODE:
|
222
|
+
case XML_PI_NODE:
|
223
|
+
case XML_COMMENT_NODE:
|
224
|
+
case XML_DOCUMENT_TYPE_NODE:
|
225
|
+
/*
|
226
|
+
* The DOM specification says no to adding text-like nodes
|
227
|
+
* directly to a document, but we allow it for compatibility.
|
228
|
+
*/
|
229
|
+
case XML_TEXT_NODE:
|
230
|
+
case XML_CDATA_SECTION_NODE:
|
231
|
+
case XML_ENTITY_REF_NODE:
|
232
|
+
goto ok;
|
233
|
+
default:
|
234
|
+
break;
|
235
|
+
}
|
236
|
+
break;
|
237
|
+
case XML_DOCUMENT_FRAG_NODE:
|
238
|
+
case XML_ENTITY_REF_NODE:
|
239
|
+
case XML_ELEMENT_NODE:
|
240
|
+
switch (reparentee->type) {
|
241
|
+
case XML_ELEMENT_NODE:
|
242
|
+
case XML_PI_NODE:
|
243
|
+
case XML_COMMENT_NODE:
|
244
|
+
case XML_TEXT_NODE:
|
245
|
+
case XML_CDATA_SECTION_NODE:
|
246
|
+
case XML_ENTITY_REF_NODE:
|
247
|
+
goto ok;
|
248
|
+
default:
|
249
|
+
break;
|
250
|
+
}
|
251
|
+
break;
|
252
|
+
case XML_ATTRIBUTE_NODE:
|
253
|
+
switch (reparentee->type) {
|
254
|
+
case XML_TEXT_NODE:
|
255
|
+
case XML_ENTITY_REF_NODE:
|
256
|
+
goto ok;
|
257
|
+
default:
|
258
|
+
break;
|
259
|
+
}
|
260
|
+
break;
|
261
|
+
case XML_TEXT_NODE:
|
262
|
+
/*
|
263
|
+
* xmlAddChild() breaks the DOM specification in that it allows
|
264
|
+
* adding a text node to another, in which case text nodes are
|
265
|
+
* coalesced, but since our JRuby version does not support such
|
266
|
+
* operation, we should inhibit it.
|
267
|
+
*/
|
268
|
+
break;
|
269
|
+
default:
|
270
|
+
break;
|
271
|
+
}
|
272
|
+
|
273
|
+
rb_raise(rb_eArgError, "cannot reparent %s there", rb_obj_classname(reparentee_obj));
|
274
|
+
}
|
275
|
+
|
276
|
+
ok:
|
277
|
+
original_reparentee = reparentee;
|
278
|
+
|
279
|
+
if (reparentee->doc != pivot->doc || reparentee->type == XML_TEXT_NODE) {
|
280
|
+
/*
|
281
|
+
* if the reparentee is a text node, there's a very good chance it will be
|
282
|
+
* merged with an adjacent text node after being reparented, and in that case
|
283
|
+
* libxml will free the underlying C struct.
|
284
|
+
*
|
285
|
+
* since we clearly have a ruby object which references the underlying
|
286
|
+
* memory, we can't let the C struct get freed. let's pickle the original
|
287
|
+
* reparentee by rooting it; and then we'll reparent a duplicate of the
|
288
|
+
* node that we don't care about preserving.
|
289
|
+
*
|
290
|
+
* alternatively, if the reparentee is from a different document than the
|
291
|
+
* pivot node, libxml2 is going to get confused about which document's
|
292
|
+
* "dictionary" the node's strings belong to (this is an otherwise
|
293
|
+
* uninteresting libxml2 implementation detail). as a result, we cannot
|
294
|
+
* reparent the actual reparentee, so we reparent a duplicate.
|
295
|
+
*/
|
296
|
+
if (reparentee->type == XML_TEXT_NODE && reparentee->_private) {
|
297
|
+
/*
|
298
|
+
* additionally, since we know this C struct isn't going to be related to
|
299
|
+
* a Ruby object anymore, let's break the relationship on this end as
|
300
|
+
* well.
|
301
|
+
*
|
302
|
+
* this is not absolutely necessary unless libxml-ruby is also in effect,
|
303
|
+
* in which case its global callback `rxml_node_deregisterNode` will try
|
304
|
+
* to do things to our data.
|
305
|
+
*
|
306
|
+
* for more details on this particular (and particularly nasty) edge
|
307
|
+
* case, see:
|
308
|
+
*
|
309
|
+
* https://github.com/sparklemotion/nokogiri/issues/1426
|
310
|
+
*/
|
311
|
+
reparentee->_private = NULL ;
|
312
|
+
}
|
313
|
+
|
314
|
+
if (reparentee->ns != NULL && reparentee->ns->prefix == NULL) {
|
315
|
+
original_ns_prefix_is_default = 1;
|
316
|
+
}
|
317
|
+
|
318
|
+
noko_xml_document_pin_node(reparentee);
|
319
|
+
|
320
|
+
if (!(reparentee = xmlDocCopyNode(reparentee, pivot->doc, 1))) {
|
321
|
+
rb_raise(rb_eRuntimeError, "Could not reparent node (xmlDocCopyNode)");
|
322
|
+
}
|
323
|
+
|
324
|
+
if (original_ns_prefix_is_default && reparentee->ns != NULL && reparentee->ns->prefix != NULL) {
|
325
|
+
/*
|
326
|
+
* issue #391, where new node's prefix may become the string "default"
|
327
|
+
* see libxml2 tree.c xmlNewReconciliedNs which implements this behavior.
|
328
|
+
*/
|
329
|
+
xmlFree(DISCARD_CONST_QUAL_XMLCHAR(reparentee->ns->prefix));
|
330
|
+
reparentee->ns->prefix = NULL;
|
331
|
+
}
|
332
|
+
}
|
333
|
+
|
334
|
+
xmlUnlinkNode(original_reparentee);
|
335
|
+
|
336
|
+
if (prf != xmlAddPrevSibling && prf != xmlAddNextSibling
|
337
|
+
&& reparentee->type == XML_TEXT_NODE && pivot->next && pivot->next->type == XML_TEXT_NODE) {
|
338
|
+
/*
|
339
|
+
* libxml merges text nodes in a right-to-left fashion, meaning that if
|
340
|
+
* there are two text nodes who would be adjacent, the right (or following,
|
341
|
+
* or next) node will be merged into the left (or preceding, or previous)
|
342
|
+
* node.
|
343
|
+
*
|
344
|
+
* and by "merged" I mean the string contents will be concatenated onto the
|
345
|
+
* left node's contents, and then the node will be freed.
|
346
|
+
*
|
347
|
+
* which means that if we have a ruby object wrapped around the right node,
|
348
|
+
* its memory would be freed out from under it.
|
349
|
+
*
|
350
|
+
* so, we detect this edge case and unlink-and-root the text node before it gets
|
351
|
+
* merged. then we dup the node and insert that duplicate back into the
|
352
|
+
* document where the real node was.
|
353
|
+
*
|
354
|
+
* yes, this is totally lame.
|
355
|
+
*/
|
356
|
+
next_text = pivot->next ;
|
357
|
+
new_next_text = xmlDocCopyNode(next_text, pivot->doc, 1) ;
|
358
|
+
|
359
|
+
xmlUnlinkNode(next_text);
|
360
|
+
noko_xml_document_pin_node(next_text);
|
361
|
+
|
362
|
+
xmlAddNextSibling(pivot, new_next_text);
|
363
|
+
}
|
364
|
+
|
365
|
+
if (!(reparented = (*prf)(pivot, reparentee))) {
|
366
|
+
rb_raise(rb_eRuntimeError, "Could not reparent node");
|
367
|
+
}
|
368
|
+
|
369
|
+
/*
|
370
|
+
* make sure the ruby object is pointed at the just-reparented node, which
|
371
|
+
* might be a duplicate (see above) or might be the result of merging
|
372
|
+
* adjacent text nodes.
|
373
|
+
*/
|
374
|
+
DATA_PTR(reparentee_obj) = reparented ;
|
375
|
+
reparented_obj = noko_xml_node_wrap(Qnil, reparented);
|
376
|
+
|
377
|
+
rb_funcall(reparented_obj, id_decorate_bang, 0);
|
378
|
+
|
379
|
+
/* if we've created a cycle, raise an exception */
|
380
|
+
raise_if_ancestor_of_self(reparented);
|
381
|
+
|
382
|
+
relink_namespace(reparented);
|
383
|
+
|
384
|
+
return reparented_obj ;
|
385
|
+
}
|
386
|
+
|
387
|
+
// :startdoc:
|
388
|
+
|
389
|
+
/*
|
390
|
+
* :call-seq:
|
391
|
+
* add_namespace_definition(prefix, href) → Nokogiri::XML::Namespace
|
392
|
+
* add_namespace(prefix, href) → Nokogiri::XML::Namespace
|
393
|
+
*
|
394
|
+
* :category: Manipulating Document Structure
|
395
|
+
*
|
396
|
+
* Adds a namespace definition to this node with +prefix+ using +href+ value, as if this node had
|
397
|
+
* included an attribute "xmlns:prefix=href".
|
398
|
+
*
|
399
|
+
* A default namespace definition for this node can be added by passing +nil+ for +prefix+.
|
400
|
+
*
|
401
|
+
* [Parameters]
|
402
|
+
* - +prefix+ (String, +nil+) An {XML Name}[https://www.w3.org/TR/xml-names/#ns-decl]
|
403
|
+
* - +href+ (String) The {URI reference}[https://www.w3.org/TR/xml-names/#sec-namespaces]
|
404
|
+
*
|
405
|
+
* [Returns] The new Nokogiri::XML::Namespace
|
406
|
+
*
|
407
|
+
* *Example:* adding a non-default namespace definition
|
408
|
+
*
|
409
|
+
* doc = Nokogiri::XML("<store><inventory></inventory></store>")
|
410
|
+
* inventory = doc.at_css("inventory")
|
411
|
+
* inventory.add_namespace_definition("automobile", "http://alices-autos.com/")
|
412
|
+
* inventory.add_namespace_definition("bicycle", "http://bobs-bikes.com/")
|
413
|
+
* inventory.add_child("<automobile:tire>Michelin model XGV, size 75R</automobile:tire>")
|
414
|
+
* doc.to_xml
|
415
|
+
* # => "<?xml version=\"1.0\"?>\n" +
|
416
|
+
* # "<store>\n" +
|
417
|
+
* # " <inventory xmlns:automobile=\"http://alices-autos.com/\" xmlns:bicycle=\"http://bobs-bikes.com/\">\n" +
|
418
|
+
* # " <automobile:tire>Michelin model XGV, size 75R</automobile:tire>\n" +
|
419
|
+
* # " </inventory>\n" +
|
420
|
+
* # "</store>\n"
|
421
|
+
*
|
422
|
+
* *Example:* adding a default namespace definition
|
423
|
+
*
|
424
|
+
* doc = Nokogiri::XML("<store><inventory><tire>Michelin model XGV, size 75R</tire></inventory></store>")
|
425
|
+
* doc.at_css("tire").add_namespace_definition(nil, "http://bobs-bikes.com/")
|
426
|
+
* doc.to_xml
|
427
|
+
* # => "<?xml version=\"1.0\"?>\n" +
|
428
|
+
* # "<store>\n" +
|
429
|
+
* # " <inventory>\n" +
|
430
|
+
* # " <tire xmlns=\"http://bobs-bikes.com/\">Michelin model XGV, size 75R</tire>\n" +
|
431
|
+
* # " </inventory>\n" +
|
432
|
+
* # "</store>\n"
|
433
|
+
*
|
434
|
+
*/
|
435
|
+
static VALUE
|
436
|
+
rb_xml_node_add_namespace_definition(VALUE rb_node, VALUE rb_prefix, VALUE rb_href)
|
437
|
+
{
|
438
|
+
xmlNodePtr c_node, element;
|
439
|
+
xmlNsPtr c_namespace;
|
440
|
+
const xmlChar *c_prefix = (const xmlChar *)(NIL_P(rb_prefix) ? NULL : StringValueCStr(rb_prefix));
|
441
|
+
|
442
|
+
Data_Get_Struct(rb_node, xmlNode, c_node);
|
443
|
+
element = c_node ;
|
444
|
+
|
445
|
+
c_namespace = xmlSearchNs(c_node->doc, c_node, c_prefix);
|
446
|
+
|
447
|
+
if (!c_namespace) {
|
448
|
+
if (c_node->type != XML_ELEMENT_NODE) {
|
449
|
+
element = c_node->parent;
|
450
|
+
}
|
451
|
+
c_namespace = xmlNewNs(element, (const xmlChar *)StringValueCStr(rb_href), c_prefix);
|
452
|
+
}
|
453
|
+
|
454
|
+
if (!c_namespace) {
|
455
|
+
return Qnil ;
|
456
|
+
}
|
457
|
+
|
458
|
+
if (NIL_P(rb_prefix) || c_node != element) {
|
459
|
+
xmlSetNs(c_node, c_namespace);
|
460
|
+
}
|
461
|
+
|
462
|
+
return noko_xml_namespace_wrap(c_namespace, c_node->doc);
|
463
|
+
}
|
464
|
+
|
465
|
+
|
466
|
+
/*
|
467
|
+
* :call-seq: attribute(name) → Nokogiri::XML::Attr
|
468
|
+
*
|
469
|
+
* :category: Working With Node Attributes
|
470
|
+
*
|
471
|
+
* [Returns] Attribute (Nokogiri::XML::Attr) belonging to this node with name +name+.
|
472
|
+
*
|
473
|
+
* ⚠ Note that attribute namespaces are ignored and only the simple (non-namespace-prefixed) name is
|
474
|
+
* used to find a matching attribute. In case of a simple name collision, only one of the matching
|
475
|
+
* attributes will be returned. In this case, you will need to use #attribute_with_ns.
|
476
|
+
*
|
477
|
+
* *Example:*
|
478
|
+
*
|
479
|
+
* doc = Nokogiri::XML("<root><child size='large' class='big wide tall'/></root>")
|
480
|
+
* child = doc.at_css("child")
|
481
|
+
* child.attribute("size") # => #<Nokogiri::XML::Attr:0x550 name="size" value="large">
|
482
|
+
* child.attribute("class") # => #<Nokogiri::XML::Attr:0x564 name="class" value="big wide tall">
|
483
|
+
*
|
484
|
+
* *Example* showing that namespaced attributes will not be returned:
|
485
|
+
*
|
486
|
+
* ⚠ Note that only one of the two matching attributes is returned.
|
487
|
+
*
|
488
|
+
* doc = Nokogiri::XML(<<~EOF)
|
489
|
+
* <root xmlns:width='http://example.com/widths'
|
490
|
+
* xmlns:height='http://example.com/heights'>
|
491
|
+
* <child width:size='broad' height:size='tall'/>
|
492
|
+
* </root>
|
493
|
+
* EOF
|
494
|
+
* doc.at_css("child").attribute("size")
|
495
|
+
* # => #(Attr:0x550 {
|
496
|
+
* # name = "size",
|
497
|
+
* # namespace = #(Namespace:0x564 {
|
498
|
+
* # prefix = "width",
|
499
|
+
* # href = "http://example.com/widths"
|
500
|
+
* # }),
|
501
|
+
* # value = "broad"
|
502
|
+
* # })
|
503
|
+
*/
|
504
|
+
static VALUE
|
505
|
+
rb_xml_node_attribute(VALUE self, VALUE name)
|
506
|
+
{
|
507
|
+
xmlNodePtr node;
|
508
|
+
xmlAttrPtr prop;
|
509
|
+
Data_Get_Struct(self, xmlNode, node);
|
510
|
+
prop = xmlHasProp(node, (xmlChar *)StringValueCStr(name));
|
511
|
+
|
512
|
+
if (! prop) { return Qnil; }
|
513
|
+
return noko_xml_node_wrap(Qnil, (xmlNodePtr)prop);
|
514
|
+
}
|
515
|
+
|
516
|
+
|
517
|
+
/*
|
518
|
+
* :call-seq: attribute_nodes() → Array<Nokogiri::XML::Attr>
|
519
|
+
*
|
520
|
+
* :category: Working With Node Attributes
|
521
|
+
*
|
522
|
+
* [Returns] Attributes (an Array of Nokogiri::XML::Attr) belonging to this node.
|
523
|
+
*
|
524
|
+
* Note that this is the preferred alternative to #attributes when the simple
|
525
|
+
* (non-namespace-prefixed) attribute names may collide.
|
526
|
+
*
|
527
|
+
* *Example:*
|
528
|
+
*
|
529
|
+
* Contrast this with the colliding-name example from #attributes.
|
530
|
+
*
|
531
|
+
* doc = Nokogiri::XML(<<~EOF)
|
532
|
+
* <root xmlns:width='http://example.com/widths'
|
533
|
+
* xmlns:height='http://example.com/heights'>
|
534
|
+
* <child width:size='broad' height:size='tall'/>
|
535
|
+
* </root>
|
536
|
+
* EOF
|
537
|
+
* doc.at_css("child").attribute_nodes
|
538
|
+
* # => [#(Attr:0x550 {
|
539
|
+
* # name = "size",
|
540
|
+
* # namespace = #(Namespace:0x564 {
|
541
|
+
* # prefix = "width",
|
542
|
+
* # href = "http://example.com/widths"
|
543
|
+
* # }),
|
544
|
+
* # value = "broad"
|
545
|
+
* # }),
|
546
|
+
* # #(Attr:0x578 {
|
547
|
+
* # name = "size",
|
548
|
+
* # namespace = #(Namespace:0x58c {
|
549
|
+
* # prefix = "height",
|
550
|
+
* # href = "http://example.com/heights"
|
551
|
+
* # }),
|
552
|
+
* # value = "tall"
|
553
|
+
* # })]
|
554
|
+
*/
|
555
|
+
static VALUE
|
556
|
+
rb_xml_node_attribute_nodes(VALUE rb_node)
|
557
|
+
{
|
558
|
+
xmlNodePtr c_node;
|
559
|
+
|
560
|
+
Data_Get_Struct(rb_node, xmlNode, c_node);
|
561
|
+
|
562
|
+
return noko_xml_node_attrs(c_node);
|
563
|
+
}
|
564
|
+
|
565
|
+
|
566
|
+
/*
|
567
|
+
* :call-seq: attribute_with_ns(name, namespace) → Nokogiri::XML::Attr
|
568
|
+
*
|
569
|
+
* :category: Working With Node Attributes
|
570
|
+
*
|
571
|
+
* [Returns]
|
572
|
+
* Attribute (Nokogiri::XML::Attr) belonging to this node with matching +name+ and +namespace+.
|
573
|
+
*
|
574
|
+
* [Parameters]
|
575
|
+
* - +name+ (String): the simple (non-namespace-prefixed) name of the attribute
|
576
|
+
* - +namespace+ (String): the URI of the attribute's namespace
|
577
|
+
*
|
578
|
+
* See related: #attribute
|
579
|
+
*
|
580
|
+
* *Example:*
|
581
|
+
*
|
582
|
+
* doc = Nokogiri::XML(<<~EOF)
|
583
|
+
* <root xmlns:width='http://example.com/widths'
|
584
|
+
* xmlns:height='http://example.com/heights'>
|
585
|
+
* <child width:size='broad' height:size='tall'/>
|
586
|
+
* </root>
|
587
|
+
* EOF
|
588
|
+
* doc.at_css("child").attribute_with_ns("size", "http://example.com/widths")
|
589
|
+
* # => #(Attr:0x550 {
|
590
|
+
* # name = "size",
|
591
|
+
* # namespace = #(Namespace:0x564 {
|
592
|
+
* # prefix = "width",
|
593
|
+
* # href = "http://example.com/widths"
|
594
|
+
* # }),
|
595
|
+
* # value = "broad"
|
596
|
+
* # })
|
597
|
+
* doc.at_css("child").attribute_with_ns("size", "http://example.com/heights")
|
598
|
+
* # => #(Attr:0x578 {
|
599
|
+
* # name = "size",
|
600
|
+
* # namespace = #(Namespace:0x58c {
|
601
|
+
* # prefix = "height",
|
602
|
+
* # href = "http://example.com/heights"
|
603
|
+
* # }),
|
604
|
+
* # value = "tall"
|
605
|
+
* # })
|
606
|
+
*/
|
607
|
+
static VALUE
|
608
|
+
rb_xml_node_attribute_with_ns(VALUE self, VALUE name, VALUE namespace)
|
609
|
+
{
|
610
|
+
xmlNodePtr node;
|
611
|
+
xmlAttrPtr prop;
|
612
|
+
Data_Get_Struct(self, xmlNode, node);
|
613
|
+
prop = xmlHasNsProp(node, (xmlChar *)StringValueCStr(name),
|
614
|
+
NIL_P(namespace) ? NULL : (xmlChar *)StringValueCStr(namespace));
|
615
|
+
|
616
|
+
if (! prop) { return Qnil; }
|
617
|
+
return noko_xml_node_wrap(Qnil, (xmlNodePtr)prop);
|
618
|
+
}
|
619
|
+
|
620
|
+
|
621
|
+
|
622
|
+
/*
|
623
|
+
* call-seq: blank? → Boolean
|
624
|
+
*
|
625
|
+
* [Returns] +true+ if the node is an empty or whitespace-only text or cdata node, else +false+.
|
626
|
+
*
|
627
|
+
* *Example:*
|
628
|
+
*
|
629
|
+
* Nokogiri("<root><child/></root>").root.child.blank? # => false
|
630
|
+
* Nokogiri("<root>\t \n</root>").root.child.blank? # => true
|
631
|
+
* Nokogiri("<root><![CDATA[\t \n]]></root>").root.child.blank? # => true
|
632
|
+
* Nokogiri("<root>not-blank</root>").root.child
|
633
|
+
* .tap { |n| n.content = "" }.blank # => true
|
634
|
+
*/
|
635
|
+
static VALUE
|
636
|
+
rb_xml_node_blank_eh(VALUE self)
|
637
|
+
{
|
638
|
+
xmlNodePtr node;
|
639
|
+
Data_Get_Struct(self, xmlNode, node);
|
640
|
+
return (1 == xmlIsBlankNode(node)) ? Qtrue : Qfalse ;
|
641
|
+
}
|
642
|
+
|
643
|
+
|
644
|
+
/*
|
645
|
+
* :call-seq: child() → Nokogiri::XML::Node
|
646
|
+
*
|
647
|
+
* :category: Traversing Document Structure
|
648
|
+
*
|
649
|
+
* [Returns] First of this node's children, or +nil+ if there are no children
|
650
|
+
*
|
651
|
+
* This is a convenience method and is equivalent to:
|
652
|
+
*
|
653
|
+
* node.children.first
|
654
|
+
*
|
655
|
+
* See related: #children
|
656
|
+
*/
|
657
|
+
static VALUE
|
658
|
+
rb_xml_node_child(VALUE self)
|
659
|
+
{
|
660
|
+
xmlNodePtr node, child;
|
661
|
+
Data_Get_Struct(self, xmlNode, node);
|
662
|
+
|
663
|
+
child = node->children;
|
664
|
+
if (!child) { return Qnil; }
|
665
|
+
|
666
|
+
return noko_xml_node_wrap(Qnil, child);
|
667
|
+
}
|
668
|
+
|
669
|
+
|
670
|
+
/*
|
671
|
+
* :call-seq: children() → Nokogiri::XML::NodeSet
|
672
|
+
*
|
673
|
+
* :category: Traversing Document Structure
|
674
|
+
*
|
675
|
+
* [Returns] Nokogiri::XML::NodeSet containing this node's children.
|
676
|
+
*/
|
677
|
+
static VALUE
|
678
|
+
rb_xml_node_children(VALUE self)
|
679
|
+
{
|
680
|
+
xmlNodePtr node;
|
681
|
+
xmlNodePtr child;
|
682
|
+
xmlNodeSetPtr set;
|
683
|
+
VALUE document;
|
684
|
+
VALUE node_set;
|
685
|
+
|
686
|
+
Data_Get_Struct(self, xmlNode, node);
|
687
|
+
|
688
|
+
child = node->children;
|
689
|
+
set = xmlXPathNodeSetCreate(child);
|
690
|
+
|
691
|
+
document = DOC_RUBY_OBJECT(node->doc);
|
692
|
+
|
693
|
+
if (!child) { return noko_xml_node_set_wrap(set, document); }
|
694
|
+
|
695
|
+
child = child->next;
|
696
|
+
while (NULL != child) {
|
697
|
+
xmlXPathNodeSetAddUnique(set, child);
|
698
|
+
child = child->next;
|
699
|
+
}
|
700
|
+
|
701
|
+
node_set = noko_xml_node_set_wrap(set, document);
|
702
|
+
|
703
|
+
return node_set;
|
704
|
+
}
|
705
|
+
|
706
|
+
|
707
|
+
/*
|
708
|
+
* :call-seq:
|
709
|
+
* content() → String
|
710
|
+
* inner_text() → String
|
711
|
+
* text() → String
|
712
|
+
* to_str() → String
|
713
|
+
*
|
714
|
+
* [Returns]
|
715
|
+
* Contents of all the text nodes in this node's subtree, concatenated together into a single
|
716
|
+
* String.
|
717
|
+
*
|
718
|
+
* ⚠ Note that entities will _always_ be expanded in the returned String.
|
719
|
+
*
|
720
|
+
* See related: #inner_html
|
721
|
+
*
|
722
|
+
* *Example* of how entities are handled:
|
723
|
+
*
|
724
|
+
* Note that <tt><</tt> becomes <tt><</tt> in the returned String.
|
725
|
+
*
|
726
|
+
* doc = Nokogiri::XML.fragment("<child>a < b</child>")
|
727
|
+
* doc.at_css("child").content
|
728
|
+
* # => "a < b"
|
729
|
+
*
|
730
|
+
* *Example* of how a subtree is handled:
|
731
|
+
*
|
732
|
+
* Note that the <tt><span></tt> tags are omitted and only the text node contents are returned,
|
733
|
+
* concatenated into a single string.
|
734
|
+
*
|
735
|
+
* doc = Nokogiri::XML.fragment("<child><span>first</span> <span>second</span></child>")
|
736
|
+
* doc.at_css("child").content
|
737
|
+
* # => "first second"
|
738
|
+
*/
|
739
|
+
static VALUE
|
740
|
+
rb_xml_node_content(VALUE self)
|
741
|
+
{
|
742
|
+
xmlNodePtr node;
|
743
|
+
xmlChar *content;
|
744
|
+
|
745
|
+
Data_Get_Struct(self, xmlNode, node);
|
746
|
+
|
747
|
+
content = xmlNodeGetContent(node);
|
748
|
+
if (content) {
|
749
|
+
VALUE rval = NOKOGIRI_STR_NEW2(content);
|
750
|
+
xmlFree(content);
|
751
|
+
return rval;
|
752
|
+
}
|
753
|
+
return Qnil;
|
754
|
+
}
|
755
|
+
|
756
|
+
|
757
|
+
/*
|
758
|
+
* :call-seq: document() → Nokogiri::XML::Document
|
759
|
+
*
|
760
|
+
* :category: Traversing Document Structure
|
761
|
+
*
|
762
|
+
* [Returns] Parent Nokogiri::XML::Document for this node
|
763
|
+
*/
|
764
|
+
static VALUE
|
765
|
+
rb_xml_node_document(VALUE self)
|
766
|
+
{
|
767
|
+
xmlNodePtr node;
|
768
|
+
Data_Get_Struct(self, xmlNode, node);
|
769
|
+
return DOC_RUBY_OBJECT(node->doc);
|
770
|
+
}
|
771
|
+
|
772
|
+
/*
|
773
|
+
* :call-seq: pointer_id() → Integer
|
774
|
+
*
|
775
|
+
* [Returns]
|
776
|
+
* A unique id for this node based on the internal memory structures. This method is used by #==
|
777
|
+
* to determine node identity.
|
778
|
+
*/
|
779
|
+
static VALUE
|
780
|
+
rb_xml_node_pointer_id(VALUE self)
|
781
|
+
{
|
782
|
+
xmlNodePtr node;
|
783
|
+
Data_Get_Struct(self, xmlNode, node);
|
784
|
+
|
785
|
+
return INT2NUM((long)(node));
|
786
|
+
}
|
787
|
+
|
788
|
+
/*
|
789
|
+
* :call-seq: encode_special_chars(string) → String
|
790
|
+
*
|
791
|
+
* Encode any special characters in +string+
|
792
|
+
*/
|
793
|
+
static VALUE
|
794
|
+
encode_special_chars(VALUE self, VALUE string)
|
795
|
+
{
|
796
|
+
xmlNodePtr node;
|
797
|
+
xmlChar *encoded;
|
798
|
+
VALUE encoded_str;
|
799
|
+
|
800
|
+
Data_Get_Struct(self, xmlNode, node);
|
801
|
+
encoded = xmlEncodeSpecialChars(
|
802
|
+
node->doc,
|
803
|
+
(const xmlChar *)StringValueCStr(string)
|
804
|
+
);
|
805
|
+
|
806
|
+
encoded_str = NOKOGIRI_STR_NEW2(encoded);
|
807
|
+
xmlFree(encoded);
|
808
|
+
|
809
|
+
return encoded_str;
|
810
|
+
}
|
811
|
+
|
812
|
+
/*
|
813
|
+
* :call-seq:
|
814
|
+
* create_internal_subset(name, external_id, system_id)
|
815
|
+
*
|
816
|
+
* Create the internal subset of a document.
|
817
|
+
*
|
818
|
+
* doc.create_internal_subset("chapter", "-//OASIS//DTD DocBook XML//EN", "chapter.dtd")
|
819
|
+
* # => <!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML//EN" "chapter.dtd">
|
820
|
+
*
|
821
|
+
* doc.create_internal_subset("chapter", nil, "chapter.dtd")
|
822
|
+
* # => <!DOCTYPE chapter SYSTEM "chapter.dtd">
|
823
|
+
*/
|
824
|
+
static VALUE
|
825
|
+
create_internal_subset(VALUE self, VALUE name, VALUE external_id, VALUE system_id)
|
826
|
+
{
|
827
|
+
xmlNodePtr node;
|
828
|
+
xmlDocPtr doc;
|
829
|
+
xmlDtdPtr dtd;
|
830
|
+
|
831
|
+
Data_Get_Struct(self, xmlNode, node);
|
832
|
+
|
833
|
+
doc = node->doc;
|
834
|
+
|
835
|
+
if (xmlGetIntSubset(doc)) {
|
836
|
+
rb_raise(rb_eRuntimeError, "Document already has an internal subset");
|
837
|
+
}
|
838
|
+
|
839
|
+
dtd = xmlCreateIntSubset(
|
840
|
+
doc,
|
841
|
+
NIL_P(name) ? NULL : (const xmlChar *)StringValueCStr(name),
|
842
|
+
NIL_P(external_id) ? NULL : (const xmlChar *)StringValueCStr(external_id),
|
843
|
+
NIL_P(system_id) ? NULL : (const xmlChar *)StringValueCStr(system_id)
|
844
|
+
);
|
845
|
+
|
846
|
+
if (!dtd) { return Qnil; }
|
847
|
+
|
848
|
+
return noko_xml_node_wrap(Qnil, (xmlNodePtr)dtd);
|
849
|
+
}
|
850
|
+
|
851
|
+
/*
|
852
|
+
* :call-seq:
|
853
|
+
* create_external_subset(name, external_id, system_id)
|
854
|
+
*
|
855
|
+
* Create an external subset
|
856
|
+
*/
|
857
|
+
static VALUE
|
858
|
+
create_external_subset(VALUE self, VALUE name, VALUE external_id, VALUE system_id)
|
859
|
+
{
|
860
|
+
xmlNodePtr node;
|
861
|
+
xmlDocPtr doc;
|
862
|
+
xmlDtdPtr dtd;
|
863
|
+
|
864
|
+
Data_Get_Struct(self, xmlNode, node);
|
865
|
+
|
866
|
+
doc = node->doc;
|
867
|
+
|
868
|
+
if (doc->extSubset) {
|
869
|
+
rb_raise(rb_eRuntimeError, "Document already has an external subset");
|
870
|
+
}
|
871
|
+
|
872
|
+
dtd = xmlNewDtd(
|
873
|
+
doc,
|
874
|
+
NIL_P(name) ? NULL : (const xmlChar *)StringValueCStr(name),
|
875
|
+
NIL_P(external_id) ? NULL : (const xmlChar *)StringValueCStr(external_id),
|
876
|
+
NIL_P(system_id) ? NULL : (const xmlChar *)StringValueCStr(system_id)
|
877
|
+
);
|
878
|
+
|
879
|
+
if (!dtd) { return Qnil; }
|
880
|
+
|
881
|
+
return noko_xml_node_wrap(Qnil, (xmlNodePtr)dtd);
|
882
|
+
}
|
883
|
+
|
884
|
+
/*
|
885
|
+
* :call-seq:
|
886
|
+
* external_subset()
|
887
|
+
*
|
888
|
+
* Get the external subset
|
889
|
+
*/
|
890
|
+
static VALUE
|
891
|
+
external_subset(VALUE self)
|
892
|
+
{
|
893
|
+
xmlNodePtr node;
|
894
|
+
xmlDocPtr doc;
|
895
|
+
xmlDtdPtr dtd;
|
896
|
+
|
897
|
+
Data_Get_Struct(self, xmlNode, node);
|
898
|
+
|
899
|
+
if (!node->doc) { return Qnil; }
|
900
|
+
|
901
|
+
doc = node->doc;
|
902
|
+
dtd = doc->extSubset;
|
903
|
+
|
904
|
+
if (!dtd) { return Qnil; }
|
905
|
+
|
906
|
+
return noko_xml_node_wrap(Qnil, (xmlNodePtr)dtd);
|
907
|
+
}
|
908
|
+
|
909
|
+
/*
|
910
|
+
* :call-seq:
|
911
|
+
* internal_subset()
|
912
|
+
*
|
913
|
+
* Get the internal subset
|
914
|
+
*/
|
915
|
+
static VALUE
|
916
|
+
internal_subset(VALUE self)
|
917
|
+
{
|
918
|
+
xmlNodePtr node;
|
919
|
+
xmlDocPtr doc;
|
920
|
+
xmlDtdPtr dtd;
|
921
|
+
|
922
|
+
Data_Get_Struct(self, xmlNode, node);
|
923
|
+
|
924
|
+
if (!node->doc) { return Qnil; }
|
925
|
+
|
926
|
+
doc = node->doc;
|
927
|
+
dtd = xmlGetIntSubset(doc);
|
928
|
+
|
929
|
+
if (!dtd) { return Qnil; }
|
930
|
+
|
931
|
+
return noko_xml_node_wrap(Qnil, (xmlNodePtr)dtd);
|
932
|
+
}
|
933
|
+
|
934
|
+
/*
|
935
|
+
* :call-seq:
|
936
|
+
* dup → Nokogiri::XML::Node
|
937
|
+
* dup(depth) → Nokogiri::XML::Node
|
938
|
+
* dup(depth, new_parent_doc) → Nokogiri::XML::Node
|
939
|
+
*
|
940
|
+
* Copy this node.
|
941
|
+
*
|
942
|
+
* [Parameters]
|
943
|
+
* - +depth+ 0 is a shallow copy, 1 (the default) is a deep copy.
|
944
|
+
* - +new_parent_doc+
|
945
|
+
* The new node's parent Document. Defaults to the this node's document.
|
946
|
+
*
|
947
|
+
* [Returns] The new Nokgiri::XML::Node
|
948
|
+
*/
|
949
|
+
static VALUE
|
950
|
+
duplicate_node(int argc, VALUE *argv, VALUE self)
|
951
|
+
{
|
952
|
+
VALUE r_level, r_new_parent_doc;
|
953
|
+
int level;
|
954
|
+
int n_args;
|
955
|
+
xmlDocPtr new_parent_doc;
|
956
|
+
xmlNodePtr node, dup;
|
957
|
+
|
958
|
+
Data_Get_Struct(self, xmlNode, node);
|
959
|
+
|
960
|
+
n_args = rb_scan_args(argc, argv, "02", &r_level, &r_new_parent_doc);
|
961
|
+
|
962
|
+
if (n_args < 1) {
|
963
|
+
r_level = INT2NUM((long)1);
|
964
|
+
}
|
965
|
+
level = (int)NUM2INT(r_level);
|
966
|
+
|
967
|
+
if (n_args < 2) {
|
968
|
+
new_parent_doc = node->doc;
|
969
|
+
} else {
|
970
|
+
Data_Get_Struct(r_new_parent_doc, xmlDoc, new_parent_doc);
|
971
|
+
}
|
972
|
+
|
973
|
+
dup = xmlDocCopyNode(node, new_parent_doc, level);
|
974
|
+
if (dup == NULL) { return Qnil; }
|
975
|
+
|
976
|
+
noko_xml_document_pin_node(dup);
|
977
|
+
|
978
|
+
return noko_xml_node_wrap(rb_obj_class(self), dup);
|
979
|
+
}
|
980
|
+
|
981
|
+
/*
|
982
|
+
* :call-seq:
|
983
|
+
* unlink() → self
|
984
|
+
*
|
985
|
+
* Unlink this node from its current context.
|
986
|
+
*/
|
987
|
+
static VALUE
|
988
|
+
unlink_node(VALUE self)
|
989
|
+
{
|
990
|
+
xmlNodePtr node;
|
991
|
+
Data_Get_Struct(self, xmlNode, node);
|
992
|
+
xmlUnlinkNode(node);
|
993
|
+
noko_xml_document_pin_node(node);
|
994
|
+
return self;
|
995
|
+
}
|
996
|
+
|
997
|
+
|
998
|
+
/*
|
999
|
+
* call-seq:
|
1000
|
+
* next_sibling
|
1001
|
+
*
|
1002
|
+
* Returns the next sibling node
|
1003
|
+
*/
|
1004
|
+
static VALUE
|
1005
|
+
next_sibling(VALUE self)
|
1006
|
+
{
|
1007
|
+
xmlNodePtr node, sibling;
|
1008
|
+
Data_Get_Struct(self, xmlNode, node);
|
1009
|
+
|
1010
|
+
sibling = node->next;
|
1011
|
+
if (!sibling) { return Qnil; }
|
1012
|
+
|
1013
|
+
return noko_xml_node_wrap(Qnil, sibling) ;
|
1014
|
+
}
|
1015
|
+
|
1016
|
+
/*
|
1017
|
+
* call-seq:
|
1018
|
+
* previous_sibling
|
1019
|
+
*
|
1020
|
+
* Returns the previous sibling node
|
1021
|
+
*/
|
1022
|
+
static VALUE
|
1023
|
+
previous_sibling(VALUE self)
|
1024
|
+
{
|
1025
|
+
xmlNodePtr node, sibling;
|
1026
|
+
Data_Get_Struct(self, xmlNode, node);
|
1027
|
+
|
1028
|
+
sibling = node->prev;
|
1029
|
+
if (!sibling) { return Qnil; }
|
1030
|
+
|
1031
|
+
return noko_xml_node_wrap(Qnil, sibling);
|
1032
|
+
}
|
1033
|
+
|
1034
|
+
/*
|
1035
|
+
* call-seq:
|
1036
|
+
* next_element
|
1037
|
+
*
|
1038
|
+
* Returns the next Nokogiri::XML::Element type sibling node.
|
1039
|
+
*/
|
1040
|
+
static VALUE
|
1041
|
+
next_element(VALUE self)
|
1042
|
+
{
|
1043
|
+
xmlNodePtr node, sibling;
|
1044
|
+
Data_Get_Struct(self, xmlNode, node);
|
1045
|
+
|
1046
|
+
sibling = xmlNextElementSibling(node);
|
1047
|
+
if (!sibling) { return Qnil; }
|
1048
|
+
|
1049
|
+
return noko_xml_node_wrap(Qnil, sibling);
|
1050
|
+
}
|
1051
|
+
|
1052
|
+
/*
|
1053
|
+
* call-seq:
|
1054
|
+
* previous_element
|
1055
|
+
*
|
1056
|
+
* Returns the previous Nokogiri::XML::Element type sibling node.
|
1057
|
+
*/
|
1058
|
+
static VALUE
|
1059
|
+
previous_element(VALUE self)
|
1060
|
+
{
|
1061
|
+
xmlNodePtr node, sibling;
|
1062
|
+
Data_Get_Struct(self, xmlNode, node);
|
1063
|
+
|
1064
|
+
/*
|
1065
|
+
* note that we don't use xmlPreviousElementSibling here because it's buggy pre-2.7.7.
|
1066
|
+
*/
|
1067
|
+
sibling = node->prev;
|
1068
|
+
if (!sibling) { return Qnil; }
|
1069
|
+
|
1070
|
+
while (sibling && sibling->type != XML_ELEMENT_NODE) {
|
1071
|
+
sibling = sibling->prev;
|
1072
|
+
}
|
1073
|
+
|
1074
|
+
return sibling ? noko_xml_node_wrap(Qnil, sibling) : Qnil ;
|
1075
|
+
}
|
1076
|
+
|
1077
|
+
/* :nodoc: */
|
1078
|
+
static VALUE
|
1079
|
+
replace(VALUE self, VALUE new_node)
|
1080
|
+
{
|
1081
|
+
VALUE reparent = reparent_node_with(self, new_node, xmlReplaceNodeWrapper);
|
1082
|
+
|
1083
|
+
xmlNodePtr pivot;
|
1084
|
+
Data_Get_Struct(self, xmlNode, pivot);
|
1085
|
+
noko_xml_document_pin_node(pivot);
|
1086
|
+
|
1087
|
+
return reparent;
|
1088
|
+
}
|
1089
|
+
|
1090
|
+
/*
|
1091
|
+
* :call-seq:
|
1092
|
+
* element_children() → NodeSet
|
1093
|
+
* elements() → NodeSet
|
1094
|
+
*
|
1095
|
+
* [Returns]
|
1096
|
+
* The node's child elements as a NodeSet. Only children that are elements will be returned, which
|
1097
|
+
* notably excludes Text nodes.
|
1098
|
+
*
|
1099
|
+
* *Example:*
|
1100
|
+
*
|
1101
|
+
* Note that #children returns the Text node "hello" while #element_children does not.
|
1102
|
+
*
|
1103
|
+
* div = Nokogiri::HTML5("<div>hello<span>world</span>").at_css("div")
|
1104
|
+
* div.element_children
|
1105
|
+
* # => [#<Nokogiri::XML::Element:0x50 name="span" children=[#<Nokogiri::XML::Text:0x3c "world">]>]
|
1106
|
+
* div.children
|
1107
|
+
* # => [#<Nokogiri::XML::Text:0x64 "hello">,
|
1108
|
+
* # #<Nokogiri::XML::Element:0x50 name="span" children=[#<Nokogiri::XML::Text:0x3c "world">]>]
|
1109
|
+
*/
|
1110
|
+
static VALUE
|
1111
|
+
rb_xml_node_element_children(VALUE self)
|
1112
|
+
{
|
1113
|
+
xmlNodePtr node;
|
1114
|
+
xmlNodePtr child;
|
1115
|
+
xmlNodeSetPtr set;
|
1116
|
+
VALUE document;
|
1117
|
+
VALUE node_set;
|
1118
|
+
|
1119
|
+
Data_Get_Struct(self, xmlNode, node);
|
1120
|
+
|
1121
|
+
child = xmlFirstElementChild(node);
|
1122
|
+
set = xmlXPathNodeSetCreate(child);
|
1123
|
+
|
1124
|
+
document = DOC_RUBY_OBJECT(node->doc);
|
1125
|
+
|
1126
|
+
if (!child) { return noko_xml_node_set_wrap(set, document); }
|
1127
|
+
|
1128
|
+
child = xmlNextElementSibling(child);
|
1129
|
+
while (NULL != child) {
|
1130
|
+
xmlXPathNodeSetAddUnique(set, child);
|
1131
|
+
child = xmlNextElementSibling(child);
|
1132
|
+
}
|
1133
|
+
|
1134
|
+
node_set = noko_xml_node_set_wrap(set, document);
|
1135
|
+
|
1136
|
+
return node_set;
|
1137
|
+
}
|
1138
|
+
|
1139
|
+
/*
|
1140
|
+
* :call-seq:
|
1141
|
+
* first_element_child() → Node
|
1142
|
+
*
|
1143
|
+
* [Returns] The first child Node that is an element.
|
1144
|
+
*
|
1145
|
+
* *Example:*
|
1146
|
+
*
|
1147
|
+
* Note that the "hello" child, which is a Text node, is skipped and the <tt><span></tt> element is
|
1148
|
+
* returned.
|
1149
|
+
*
|
1150
|
+
* div = Nokogiri::HTML5("<div>hello<span>world</span>").at_css("div")
|
1151
|
+
* div.first_element_child
|
1152
|
+
* # => #(Element:0x3c { name = "span", children = [ #(Text "world")] })
|
1153
|
+
*/
|
1154
|
+
static VALUE
|
1155
|
+
rb_xml_node_first_element_child(VALUE self)
|
1156
|
+
{
|
1157
|
+
xmlNodePtr node, child;
|
1158
|
+
Data_Get_Struct(self, xmlNode, node);
|
1159
|
+
|
1160
|
+
child = xmlFirstElementChild(node);
|
1161
|
+
if (!child) { return Qnil; }
|
1162
|
+
|
1163
|
+
return noko_xml_node_wrap(Qnil, child);
|
1164
|
+
}
|
1165
|
+
|
1166
|
+
/*
|
1167
|
+
* :call-seq:
|
1168
|
+
* last_element_child() → Node
|
1169
|
+
*
|
1170
|
+
* [Returns] The last child Node that is an element.
|
1171
|
+
*
|
1172
|
+
* *Example:*
|
1173
|
+
*
|
1174
|
+
* Note that the "hello" child, which is a Text node, is skipped and the <tt><span>yes</span></tt>
|
1175
|
+
* element is returned.
|
1176
|
+
*
|
1177
|
+
* div = Nokogiri::HTML5("<div><span>no</span><span>yes</span>skip</div>").at_css("div")
|
1178
|
+
* div.last_element_child
|
1179
|
+
* # => #(Element:0x3c { name = "span", children = [ #(Text "yes")] })
|
1180
|
+
*/
|
1181
|
+
static VALUE
|
1182
|
+
rb_xml_node_last_element_child(VALUE self)
|
1183
|
+
{
|
1184
|
+
xmlNodePtr node, child;
|
1185
|
+
Data_Get_Struct(self, xmlNode, node);
|
1186
|
+
|
1187
|
+
child = xmlLastElementChild(node);
|
1188
|
+
if (!child) { return Qnil; }
|
1189
|
+
|
1190
|
+
return noko_xml_node_wrap(Qnil, child);
|
1191
|
+
}
|
1192
|
+
|
1193
|
+
/*
|
1194
|
+
* call-seq:
|
1195
|
+
* key?(attribute)
|
1196
|
+
*
|
1197
|
+
* Returns true if +attribute+ is set
|
1198
|
+
*/
|
1199
|
+
static VALUE
|
1200
|
+
key_eh(VALUE self, VALUE attribute)
|
1201
|
+
{
|
1202
|
+
xmlNodePtr node;
|
1203
|
+
Data_Get_Struct(self, xmlNode, node);
|
1204
|
+
if (xmlHasProp(node, (xmlChar *)StringValueCStr(attribute))) {
|
1205
|
+
return Qtrue;
|
1206
|
+
}
|
1207
|
+
return Qfalse;
|
1208
|
+
}
|
1209
|
+
|
1210
|
+
/*
|
1211
|
+
* call-seq:
|
1212
|
+
* namespaced_key?(attribute, namespace)
|
1213
|
+
*
|
1214
|
+
* Returns true if +attribute+ is set with +namespace+
|
1215
|
+
*/
|
1216
|
+
static VALUE
|
1217
|
+
namespaced_key_eh(VALUE self, VALUE attribute, VALUE namespace)
|
1218
|
+
{
|
1219
|
+
xmlNodePtr node;
|
1220
|
+
Data_Get_Struct(self, xmlNode, node);
|
1221
|
+
if (xmlHasNsProp(node, (xmlChar *)StringValueCStr(attribute),
|
1222
|
+
NIL_P(namespace) ? NULL : (xmlChar *)StringValueCStr(namespace))) {
|
1223
|
+
return Qtrue;
|
1224
|
+
}
|
1225
|
+
return Qfalse;
|
1226
|
+
}
|
1227
|
+
|
1228
|
+
/*
|
1229
|
+
* call-seq:
|
1230
|
+
* []=(property, value)
|
1231
|
+
*
|
1232
|
+
* Set the +property+ to +value+
|
1233
|
+
*/
|
1234
|
+
static VALUE
|
1235
|
+
set(VALUE self, VALUE property, VALUE value)
|
1236
|
+
{
|
1237
|
+
xmlNodePtr node, cur;
|
1238
|
+
xmlAttrPtr prop;
|
1239
|
+
Data_Get_Struct(self, xmlNode, node);
|
1240
|
+
|
1241
|
+
/* If a matching attribute node already exists, then xmlSetProp will destroy
|
1242
|
+
* the existing node's children. However, if Nokogiri has a node object
|
1243
|
+
* pointing to one of those children, we are left with a broken reference.
|
1244
|
+
*
|
1245
|
+
* We can avoid this by unlinking these nodes first.
|
1246
|
+
*/
|
1247
|
+
if (node->type != XML_ELEMENT_NODE) {
|
1248
|
+
return (Qnil);
|
1249
|
+
}
|
1250
|
+
prop = xmlHasProp(node, (xmlChar *)StringValueCStr(property));
|
1251
|
+
if (prop && prop->children) {
|
1252
|
+
for (cur = prop->children; cur; cur = cur->next) {
|
1253
|
+
if (cur->_private) {
|
1254
|
+
noko_xml_document_pin_node(cur);
|
1255
|
+
xmlUnlinkNode(cur);
|
1256
|
+
}
|
1257
|
+
}
|
1258
|
+
}
|
1259
|
+
|
1260
|
+
xmlSetProp(node, (xmlChar *)StringValueCStr(property),
|
1261
|
+
(xmlChar *)StringValueCStr(value));
|
1262
|
+
|
1263
|
+
return value;
|
1264
|
+
}
|
1265
|
+
|
1266
|
+
/*
|
1267
|
+
* call-seq:
|
1268
|
+
* get(attribute)
|
1269
|
+
*
|
1270
|
+
* Get the value for +attribute+
|
1271
|
+
*/
|
1272
|
+
static VALUE
|
1273
|
+
get(VALUE self, VALUE rattribute)
|
1274
|
+
{
|
1275
|
+
xmlNodePtr node;
|
1276
|
+
xmlChar *value = 0;
|
1277
|
+
VALUE rvalue;
|
1278
|
+
xmlChar *colon;
|
1279
|
+
xmlChar *attribute, *attr_name, *prefix;
|
1280
|
+
xmlNsPtr ns;
|
1281
|
+
|
1282
|
+
if (NIL_P(rattribute)) { return Qnil; }
|
1283
|
+
|
1284
|
+
Data_Get_Struct(self, xmlNode, node);
|
1285
|
+
attribute = xmlCharStrdup(StringValueCStr(rattribute));
|
1286
|
+
|
1287
|
+
colon = DISCARD_CONST_QUAL_XMLCHAR(xmlStrchr(attribute, (const xmlChar)':'));
|
1288
|
+
if (colon) {
|
1289
|
+
/* split the attribute string into separate prefix and name by
|
1290
|
+
* null-terminating the prefix at the colon */
|
1291
|
+
prefix = attribute;
|
1292
|
+
attr_name = colon + 1;
|
1293
|
+
(*colon) = 0;
|
1294
|
+
|
1295
|
+
ns = xmlSearchNs(node->doc, node, prefix);
|
1296
|
+
if (ns) {
|
1297
|
+
value = xmlGetNsProp(node, attr_name, ns->href);
|
1298
|
+
} else {
|
1299
|
+
value = xmlGetProp(node, (xmlChar *)StringValueCStr(rattribute));
|
1300
|
+
}
|
1301
|
+
} else {
|
1302
|
+
value = xmlGetNoNsProp(node, attribute);
|
1303
|
+
}
|
1304
|
+
|
1305
|
+
xmlFree((void *)attribute);
|
1306
|
+
if (!value) { return Qnil; }
|
1307
|
+
|
1308
|
+
rvalue = NOKOGIRI_STR_NEW2(value);
|
1309
|
+
xmlFree((void *)value);
|
1310
|
+
|
1311
|
+
return rvalue ;
|
1312
|
+
}
|
1313
|
+
|
1314
|
+
/*
|
1315
|
+
* call-seq:
|
1316
|
+
* set_namespace(namespace)
|
1317
|
+
*
|
1318
|
+
* Set the namespace to +namespace+
|
1319
|
+
*/
|
1320
|
+
static VALUE
|
1321
|
+
set_namespace(VALUE self, VALUE namespace)
|
1322
|
+
{
|
1323
|
+
xmlNodePtr node;
|
1324
|
+
xmlNsPtr ns = NULL;
|
1325
|
+
|
1326
|
+
Data_Get_Struct(self, xmlNode, node);
|
1327
|
+
|
1328
|
+
if (!NIL_P(namespace)) {
|
1329
|
+
Data_Get_Struct(namespace, xmlNs, ns);
|
1330
|
+
}
|
1331
|
+
|
1332
|
+
xmlSetNs(node, ns);
|
1333
|
+
|
1334
|
+
return self;
|
1335
|
+
}
|
1336
|
+
|
1337
|
+
/*
|
1338
|
+
* :call-seq:
|
1339
|
+
* namespace() → Namespace
|
1340
|
+
*
|
1341
|
+
* [Returns] The Namespace of the element or attribute node, or +nil+ if there is no namespace.
|
1342
|
+
*
|
1343
|
+
* *Example:*
|
1344
|
+
*
|
1345
|
+
* doc = Nokogiri::XML(<<~EOF)
|
1346
|
+
* <root>
|
1347
|
+
* <first/>
|
1348
|
+
* <second xmlns="http://example.com/child"/>
|
1349
|
+
* <foo:third xmlns:foo="http://example.com/foo"/>
|
1350
|
+
* </root>
|
1351
|
+
* EOF
|
1352
|
+
* doc.at_xpath("//first").namespace
|
1353
|
+
* # => nil
|
1354
|
+
* doc.at_xpath("//xmlns:second", "xmlns" => "http://example.com/child").namespace
|
1355
|
+
* # => #(Namespace:0x3c { href = "http://example.com/child" })
|
1356
|
+
* doc.at_xpath("//foo:third", "foo" => "http://example.com/foo").namespace
|
1357
|
+
* # => #(Namespace:0x50 { prefix = "foo", href = "http://example.com/foo" })
|
1358
|
+
*/
|
1359
|
+
static VALUE
|
1360
|
+
rb_xml_node_namespace(VALUE rb_node)
|
1361
|
+
{
|
1362
|
+
xmlNodePtr c_node ;
|
1363
|
+
Data_Get_Struct(rb_node, xmlNode, c_node);
|
1364
|
+
|
1365
|
+
if (c_node->ns) {
|
1366
|
+
return noko_xml_namespace_wrap(c_node->ns, c_node->doc);
|
1367
|
+
}
|
1368
|
+
|
1369
|
+
return Qnil ;
|
1370
|
+
}
|
1371
|
+
|
1372
|
+
/*
|
1373
|
+
* :call-seq:
|
1374
|
+
* namespace_definitions() → Array<Nokogiri::XML::Namespace>
|
1375
|
+
*
|
1376
|
+
* [Returns]
|
1377
|
+
* Namespaces that are defined directly on this node, as an Array of Namespace objects. The array
|
1378
|
+
* will be empty if no namespaces are defined on this node.
|
1379
|
+
*
|
1380
|
+
* *Example:*
|
1381
|
+
*
|
1382
|
+
* doc = Nokogiri::XML(<<~EOF)
|
1383
|
+
* <root xmlns="http://example.com/root">
|
1384
|
+
* <first/>
|
1385
|
+
* <second xmlns="http://example.com/child" xmlns:unused="http://example.com/unused"/>
|
1386
|
+
* <foo:third xmlns:foo="http://example.com/foo"/>
|
1387
|
+
* </root>
|
1388
|
+
* EOF
|
1389
|
+
* doc.at_xpath("//root:first", "root" => "http://example.com/root").namespace_definitions
|
1390
|
+
* # => []
|
1391
|
+
* doc.at_xpath("//xmlns:second", "xmlns" => "http://example.com/child").namespace_definitions
|
1392
|
+
* # => [#(Namespace:0x3c { href = "http://example.com/child" }),
|
1393
|
+
* # #(Namespace:0x50 {
|
1394
|
+
* # prefix = "unused",
|
1395
|
+
* # href = "http://example.com/unused"
|
1396
|
+
* # })]
|
1397
|
+
* doc.at_xpath("//foo:third", "foo" => "http://example.com/foo").namespace_definitions
|
1398
|
+
* # => [#(Namespace:0x64 { prefix = "foo", href = "http://example.com/foo" })]
|
1399
|
+
*/
|
1400
|
+
static VALUE
|
1401
|
+
namespace_definitions(VALUE rb_node)
|
1402
|
+
{
|
1403
|
+
/* this code in the mode of xmlHasProp() */
|
1404
|
+
xmlNodePtr c_node ;
|
1405
|
+
xmlNsPtr c_namespace;
|
1406
|
+
VALUE definitions = rb_ary_new();
|
1407
|
+
|
1408
|
+
Data_Get_Struct(rb_node, xmlNode, c_node);
|
1409
|
+
|
1410
|
+
c_namespace = c_node->nsDef;
|
1411
|
+
if (!c_namespace) {
|
1412
|
+
return definitions;
|
1413
|
+
}
|
1414
|
+
|
1415
|
+
while (c_namespace != NULL) {
|
1416
|
+
rb_ary_push(definitions, noko_xml_namespace_wrap(c_namespace, c_node->doc));
|
1417
|
+
c_namespace = c_namespace->next;
|
1418
|
+
}
|
1419
|
+
|
1420
|
+
return definitions;
|
1421
|
+
}
|
1422
|
+
|
1423
|
+
/*
|
1424
|
+
* :call-seq:
|
1425
|
+
* namespace_scopes() → Array<Nokogiri::XML::Namespace>
|
1426
|
+
*
|
1427
|
+
* [Returns] Array of all the Namespaces on this node and its ancestors.
|
1428
|
+
*
|
1429
|
+
* See also #namespaces
|
1430
|
+
*
|
1431
|
+
* *Example:*
|
1432
|
+
*
|
1433
|
+
* doc = Nokogiri::XML(<<~EOF)
|
1434
|
+
* <root xmlns="http://example.com/root" xmlns:bar="http://example.com/bar">
|
1435
|
+
* <first/>
|
1436
|
+
* <second xmlns="http://example.com/child"/>
|
1437
|
+
* <third xmlns:foo="http://example.com/foo"/>
|
1438
|
+
* </root>
|
1439
|
+
* EOF
|
1440
|
+
* doc.at_xpath("//root:first", "root" => "http://example.com/root").namespace_scopes
|
1441
|
+
* # => [#(Namespace:0x3c { href = "http://example.com/root" }),
|
1442
|
+
* # #(Namespace:0x50 { prefix = "bar", href = "http://example.com/bar" })]
|
1443
|
+
* doc.at_xpath("//child:second", "child" => "http://example.com/child").namespace_scopes
|
1444
|
+
* # => [#(Namespace:0x64 { href = "http://example.com/child" }),
|
1445
|
+
* # #(Namespace:0x50 { prefix = "bar", href = "http://example.com/bar" })]
|
1446
|
+
* doc.at_xpath("//root:third", "root" => "http://example.com/root").namespace_scopes
|
1447
|
+
* # => [#(Namespace:0x78 { prefix = "foo", href = "http://example.com/foo" }),
|
1448
|
+
* # #(Namespace:0x3c { href = "http://example.com/root" }),
|
1449
|
+
* # #(Namespace:0x50 { prefix = "bar", href = "http://example.com/bar" })]
|
1450
|
+
*/
|
1451
|
+
static VALUE
|
1452
|
+
rb_xml_node_namespace_scopes(VALUE rb_node)
|
1453
|
+
{
|
1454
|
+
xmlNodePtr c_node ;
|
1455
|
+
xmlNsPtr *namespaces;
|
1456
|
+
VALUE scopes = rb_ary_new();
|
1457
|
+
int j;
|
1458
|
+
|
1459
|
+
Data_Get_Struct(rb_node, xmlNode, c_node);
|
1460
|
+
|
1461
|
+
namespaces = xmlGetNsList(c_node->doc, c_node);
|
1462
|
+
if (!namespaces) {
|
1463
|
+
return scopes;
|
1464
|
+
}
|
1465
|
+
|
1466
|
+
for (j = 0 ; namespaces[j] != NULL ; ++j) {
|
1467
|
+
rb_ary_push(scopes, noko_xml_namespace_wrap(namespaces[j], c_node->doc));
|
1468
|
+
}
|
1469
|
+
|
1470
|
+
xmlFree(namespaces);
|
1471
|
+
return scopes;
|
1472
|
+
}
|
1473
|
+
|
1474
|
+
/*
|
1475
|
+
* call-seq:
|
1476
|
+
* node_type
|
1477
|
+
*
|
1478
|
+
* Get the type for this Node
|
1479
|
+
*/
|
1480
|
+
static VALUE
|
1481
|
+
node_type(VALUE self)
|
1482
|
+
{
|
1483
|
+
xmlNodePtr node;
|
1484
|
+
Data_Get_Struct(self, xmlNode, node);
|
1485
|
+
return INT2NUM((long)node->type);
|
1486
|
+
}
|
1487
|
+
|
1488
|
+
/*
|
1489
|
+
* call-seq:
|
1490
|
+
* content=
|
1491
|
+
*
|
1492
|
+
* Set the content for this Node
|
1493
|
+
*/
|
1494
|
+
static VALUE
|
1495
|
+
set_native_content(VALUE self, VALUE content)
|
1496
|
+
{
|
1497
|
+
xmlNodePtr node, child, next ;
|
1498
|
+
Data_Get_Struct(self, xmlNode, node);
|
1499
|
+
|
1500
|
+
child = node->children;
|
1501
|
+
while (NULL != child) {
|
1502
|
+
next = child->next ;
|
1503
|
+
xmlUnlinkNode(child) ;
|
1504
|
+
noko_xml_document_pin_node(child);
|
1505
|
+
child = next ;
|
1506
|
+
}
|
1507
|
+
|
1508
|
+
xmlNodeSetContent(node, (xmlChar *)StringValueCStr(content));
|
1509
|
+
return content;
|
1510
|
+
}
|
1511
|
+
|
1512
|
+
/*
|
1513
|
+
* call-seq:
|
1514
|
+
* lang=
|
1515
|
+
*
|
1516
|
+
* Set the language of a node, i.e. the values of the xml:lang attribute.
|
1517
|
+
*/
|
1518
|
+
static VALUE
|
1519
|
+
set_lang(VALUE self_rb, VALUE lang_rb)
|
1520
|
+
{
|
1521
|
+
xmlNodePtr self ;
|
1522
|
+
xmlChar *lang ;
|
1523
|
+
|
1524
|
+
Data_Get_Struct(self_rb, xmlNode, self);
|
1525
|
+
lang = (xmlChar *)StringValueCStr(lang_rb);
|
1526
|
+
|
1527
|
+
xmlNodeSetLang(self, lang);
|
1528
|
+
|
1529
|
+
return Qnil ;
|
1530
|
+
}
|
1531
|
+
|
1532
|
+
/*
|
1533
|
+
* call-seq:
|
1534
|
+
* lang
|
1535
|
+
*
|
1536
|
+
* Searches the language of a node, i.e. the values of the xml:lang attribute or
|
1537
|
+
* the one carried by the nearest ancestor.
|
1538
|
+
*/
|
1539
|
+
static VALUE
|
1540
|
+
get_lang(VALUE self_rb)
|
1541
|
+
{
|
1542
|
+
xmlNodePtr self ;
|
1543
|
+
xmlChar *lang ;
|
1544
|
+
VALUE lang_rb ;
|
1545
|
+
|
1546
|
+
Data_Get_Struct(self_rb, xmlNode, self);
|
1547
|
+
|
1548
|
+
lang = xmlNodeGetLang(self);
|
1549
|
+
if (lang) {
|
1550
|
+
lang_rb = NOKOGIRI_STR_NEW2(lang);
|
1551
|
+
xmlFree(lang);
|
1552
|
+
return lang_rb ;
|
1553
|
+
}
|
1554
|
+
|
1555
|
+
return Qnil ;
|
1556
|
+
}
|
1557
|
+
|
1558
|
+
/* :nodoc: */
|
1559
|
+
static VALUE
|
1560
|
+
add_child(VALUE self, VALUE new_child)
|
1561
|
+
{
|
1562
|
+
return reparent_node_with(self, new_child, xmlAddChild);
|
1563
|
+
}
|
1564
|
+
|
1565
|
+
/*
|
1566
|
+
* call-seq:
|
1567
|
+
* parent
|
1568
|
+
*
|
1569
|
+
* Get the parent Node for this Node
|
1570
|
+
*/
|
1571
|
+
static VALUE
|
1572
|
+
get_parent(VALUE self)
|
1573
|
+
{
|
1574
|
+
xmlNodePtr node, parent;
|
1575
|
+
Data_Get_Struct(self, xmlNode, node);
|
1576
|
+
|
1577
|
+
parent = node->parent;
|
1578
|
+
if (!parent) { return Qnil; }
|
1579
|
+
|
1580
|
+
return noko_xml_node_wrap(Qnil, parent) ;
|
1581
|
+
}
|
1582
|
+
|
1583
|
+
/*
|
1584
|
+
* call-seq:
|
1585
|
+
* name=(new_name)
|
1586
|
+
*
|
1587
|
+
* Set the name for this Node
|
1588
|
+
*/
|
1589
|
+
static VALUE
|
1590
|
+
set_name(VALUE self, VALUE new_name)
|
1591
|
+
{
|
1592
|
+
xmlNodePtr node;
|
1593
|
+
Data_Get_Struct(self, xmlNode, node);
|
1594
|
+
xmlNodeSetName(node, (xmlChar *)StringValueCStr(new_name));
|
1595
|
+
return new_name;
|
1596
|
+
}
|
1597
|
+
|
1598
|
+
/*
|
1599
|
+
* call-seq:
|
1600
|
+
* name
|
1601
|
+
*
|
1602
|
+
* Returns the name for this Node
|
1603
|
+
*/
|
1604
|
+
static VALUE
|
1605
|
+
get_name(VALUE self)
|
1606
|
+
{
|
1607
|
+
xmlNodePtr node;
|
1608
|
+
Data_Get_Struct(self, xmlNode, node);
|
1609
|
+
if (node->name) {
|
1610
|
+
return NOKOGIRI_STR_NEW2(node->name);
|
1611
|
+
}
|
1612
|
+
return Qnil;
|
1613
|
+
}
|
1614
|
+
|
1615
|
+
/*
|
1616
|
+
* call-seq:
|
1617
|
+
* path
|
1618
|
+
*
|
1619
|
+
* Returns the path associated with this Node
|
1620
|
+
*/
|
1621
|
+
static VALUE
|
1622
|
+
rb_xml_node_path(VALUE rb_node)
|
1623
|
+
{
|
1624
|
+
xmlNodePtr c_node;
|
1625
|
+
xmlChar *c_path ;
|
1626
|
+
VALUE rval;
|
1627
|
+
|
1628
|
+
Data_Get_Struct(rb_node, xmlNode, c_node);
|
1629
|
+
|
1630
|
+
c_path = xmlGetNodePath(c_node);
|
1631
|
+
if (c_path == NULL) {
|
1632
|
+
// see https://github.com/sparklemotion/nokogiri/issues/2250
|
1633
|
+
// this behavior is clearly undesirable, but is what libxml <= 2.9.10 returned, and so we
|
1634
|
+
// do this for now to preserve the behavior across libxml2 versions.
|
1635
|
+
rval = NOKOGIRI_STR_NEW2("?");
|
1636
|
+
} else {
|
1637
|
+
rval = NOKOGIRI_STR_NEW2(c_path);
|
1638
|
+
xmlFree(c_path);
|
1639
|
+
}
|
1640
|
+
|
1641
|
+
return rval ;
|
1642
|
+
}
|
1643
|
+
|
1644
|
+
/* :nodoc: */
|
1645
|
+
static VALUE
|
1646
|
+
add_next_sibling(VALUE self, VALUE new_sibling)
|
1647
|
+
{
|
1648
|
+
return reparent_node_with(self, new_sibling, xmlAddNextSibling) ;
|
1649
|
+
}
|
1650
|
+
|
1651
|
+
/* :nodoc: */
|
1652
|
+
static VALUE
|
1653
|
+
add_previous_sibling(VALUE self, VALUE new_sibling)
|
1654
|
+
{
|
1655
|
+
return reparent_node_with(self, new_sibling, xmlAddPrevSibling) ;
|
1656
|
+
}
|
1657
|
+
|
1658
|
+
/*
|
1659
|
+
* call-seq:
|
1660
|
+
* native_write_to(io, encoding, options)
|
1661
|
+
*
|
1662
|
+
* Write this Node to +io+ with +encoding+ and +options+
|
1663
|
+
*/
|
1664
|
+
static VALUE
|
1665
|
+
native_write_to(
|
1666
|
+
VALUE self,
|
1667
|
+
VALUE io,
|
1668
|
+
VALUE encoding,
|
1669
|
+
VALUE indent_string,
|
1670
|
+
VALUE options
|
1671
|
+
)
|
1672
|
+
{
|
1673
|
+
xmlNodePtr node;
|
1674
|
+
const char *before_indent;
|
1675
|
+
xmlSaveCtxtPtr savectx;
|
1676
|
+
|
1677
|
+
Data_Get_Struct(self, xmlNode, node);
|
1678
|
+
|
1679
|
+
xmlIndentTreeOutput = 1;
|
1680
|
+
|
1681
|
+
before_indent = xmlTreeIndentString;
|
1682
|
+
|
1683
|
+
xmlTreeIndentString = StringValueCStr(indent_string);
|
1684
|
+
|
1685
|
+
savectx = xmlSaveToIO(
|
1686
|
+
(xmlOutputWriteCallback)noko_io_write,
|
1687
|
+
(xmlOutputCloseCallback)noko_io_close,
|
1688
|
+
(void *)io,
|
1689
|
+
RTEST(encoding) ? StringValueCStr(encoding) : NULL,
|
1690
|
+
(int)NUM2INT(options)
|
1691
|
+
);
|
1692
|
+
|
1693
|
+
xmlSaveTree(savectx, node);
|
1694
|
+
xmlSaveClose(savectx);
|
1695
|
+
|
1696
|
+
xmlTreeIndentString = before_indent;
|
1697
|
+
return io;
|
1698
|
+
}
|
1699
|
+
|
1700
|
+
/*
|
1701
|
+
* :call-seq:
|
1702
|
+
* line() → Integer
|
1703
|
+
*
|
1704
|
+
* [Returns] The line number of this Node.
|
1705
|
+
*
|
1706
|
+
* ---
|
1707
|
+
*
|
1708
|
+
* <b> ⚠ The CRuby and JRuby implementations differ in important ways! </b>
|
1709
|
+
*
|
1710
|
+
* Semantic differences:
|
1711
|
+
* - The CRuby method reflects the node's line number <i>in the parsed string</i>
|
1712
|
+
* - The JRuby method reflects the node's line number <i>in the final DOM structure</i> after
|
1713
|
+
* corrections have been applied
|
1714
|
+
*
|
1715
|
+
* Performance differences:
|
1716
|
+
* - The CRuby method is {O(1)}[https://en.wikipedia.org/wiki/Time_complexity#Constant_time]
|
1717
|
+
* (constant time)
|
1718
|
+
* - The JRuby method is {O(n)}[https://en.wikipedia.org/wiki/Time_complexity#Linear_time] (linear
|
1719
|
+
* time, where n is the number of nodes before/above the element in the DOM)
|
1720
|
+
*
|
1721
|
+
* If you'd like to help improve the JRuby implementation, please review these issues and reach out
|
1722
|
+
* to the maintainers:
|
1723
|
+
* - https://github.com/sparklemotion/nokogiri/issues/1223
|
1724
|
+
* - https://github.com/sparklemotion/nokogiri/pull/2177
|
1725
|
+
* - https://github.com/sparklemotion/nokogiri/issues/2380
|
1726
|
+
*/
|
1727
|
+
static VALUE
|
1728
|
+
rb_xml_node_line(VALUE rb_node)
|
1729
|
+
{
|
1730
|
+
xmlNodePtr c_node;
|
1731
|
+
Data_Get_Struct(rb_node, xmlNode, c_node);
|
1732
|
+
|
1733
|
+
return INT2NUM(xmlGetLineNo(c_node));
|
1734
|
+
}
|
1735
|
+
|
1736
|
+
/*
|
1737
|
+
* call-seq:
|
1738
|
+
* line=(num)
|
1739
|
+
*
|
1740
|
+
* Sets the line for this Node. num must be less than 65535.
|
1741
|
+
*/
|
1742
|
+
static VALUE
|
1743
|
+
rb_xml_node_line_set(VALUE rb_node, VALUE rb_line_number)
|
1744
|
+
{
|
1745
|
+
xmlNodePtr c_node;
|
1746
|
+
int line_number = NUM2INT(rb_line_number);
|
1747
|
+
|
1748
|
+
Data_Get_Struct(rb_node, xmlNode, c_node);
|
1749
|
+
|
1750
|
+
// libxml2 optionally uses xmlNode.psvi to store longer line numbers, but only for text nodes.
|
1751
|
+
// search for "psvi" in SAX2.c and tree.c to learn more.
|
1752
|
+
if (line_number < 65535) {
|
1753
|
+
c_node->line = (short) line_number;
|
1754
|
+
} else {
|
1755
|
+
c_node->line = 65535;
|
1756
|
+
if (c_node->type == XML_TEXT_NODE) {
|
1757
|
+
c_node->psvi = (void *)(ptrdiff_t) line_number;
|
1758
|
+
}
|
1759
|
+
}
|
1760
|
+
|
1761
|
+
return rb_line_number;
|
1762
|
+
}
|
1763
|
+
|
1764
|
+
/* :nodoc: documented in lib/nokogiri/xml/node.rb */
|
1765
|
+
static VALUE
|
1766
|
+
rb_xml_node_new(int argc, VALUE *argv, VALUE klass)
|
1767
|
+
{
|
1768
|
+
xmlNodePtr c_document_node;
|
1769
|
+
xmlNodePtr c_node;
|
1770
|
+
VALUE rb_name;
|
1771
|
+
VALUE rb_document_node;
|
1772
|
+
VALUE rest;
|
1773
|
+
VALUE rb_node;
|
1774
|
+
|
1775
|
+
rb_scan_args(argc, argv, "2*", &rb_name, &rb_document_node, &rest);
|
1776
|
+
|
1777
|
+
if (!rb_obj_is_kind_of(rb_document_node, cNokogiriXmlNode)) {
|
1778
|
+
rb_raise(rb_eArgError, "document must be a Nokogiri::XML::Node");
|
1779
|
+
}
|
1780
|
+
if (!rb_obj_is_kind_of(rb_document_node, cNokogiriXmlDocument)) {
|
1781
|
+
// TODO: deprecate allowing Node
|
1782
|
+
rb_warn("Passing a Node as the second parameter to Node.new is deprecated. Please pass a Document instead, or prefer an alternative constructor like Node#add_child. This will become an error in a future release of Nokogiri.");
|
1783
|
+
}
|
1784
|
+
Data_Get_Struct(rb_document_node, xmlNode, c_document_node);
|
1785
|
+
|
1786
|
+
c_node = xmlNewNode(NULL, (xmlChar *)StringValueCStr(rb_name));
|
1787
|
+
c_node->doc = c_document_node->doc;
|
1788
|
+
noko_xml_document_pin_node(c_node);
|
1789
|
+
|
1790
|
+
rb_node = noko_xml_node_wrap(
|
1791
|
+
klass == cNokogiriXmlNode ? (VALUE)NULL : klass,
|
1792
|
+
c_node
|
1793
|
+
);
|
1794
|
+
rb_obj_call_init(rb_node, argc, argv);
|
1795
|
+
|
1796
|
+
if (rb_block_given_p()) { rb_yield(rb_node); }
|
1797
|
+
|
1798
|
+
return rb_node;
|
1799
|
+
}
|
1800
|
+
|
1801
|
+
/*
|
1802
|
+
* call-seq:
|
1803
|
+
* dump_html
|
1804
|
+
*
|
1805
|
+
* Returns the Node as html.
|
1806
|
+
*/
|
1807
|
+
static VALUE
|
1808
|
+
dump_html(VALUE self)
|
1809
|
+
{
|
1810
|
+
xmlBufferPtr buf ;
|
1811
|
+
xmlNodePtr node ;
|
1812
|
+
VALUE html;
|
1813
|
+
|
1814
|
+
Data_Get_Struct(self, xmlNode, node);
|
1815
|
+
|
1816
|
+
buf = xmlBufferCreate() ;
|
1817
|
+
htmlNodeDump(buf, node->doc, node);
|
1818
|
+
html = NOKOGIRI_STR_NEW2(buf->content);
|
1819
|
+
xmlBufferFree(buf);
|
1820
|
+
return html ;
|
1821
|
+
}
|
1822
|
+
|
1823
|
+
/*
|
1824
|
+
* call-seq:
|
1825
|
+
* compare(other)
|
1826
|
+
*
|
1827
|
+
* Compare this Node to +other+ with respect to their Document
|
1828
|
+
*/
|
1829
|
+
static VALUE
|
1830
|
+
compare(VALUE self, VALUE _other)
|
1831
|
+
{
|
1832
|
+
xmlNodePtr node, other;
|
1833
|
+
Data_Get_Struct(self, xmlNode, node);
|
1834
|
+
Data_Get_Struct(_other, xmlNode, other);
|
1835
|
+
|
1836
|
+
return INT2NUM((long)xmlXPathCmpNodes(other, node));
|
1837
|
+
}
|
1838
|
+
|
1839
|
+
|
1840
|
+
/*
|
1841
|
+
* call-seq:
|
1842
|
+
* process_xincludes(options)
|
1843
|
+
*
|
1844
|
+
* Loads and substitutes all xinclude elements below the node. The
|
1845
|
+
* parser context will be initialized with +options+.
|
1846
|
+
*/
|
1847
|
+
static VALUE
|
1848
|
+
process_xincludes(VALUE self, VALUE options)
|
1849
|
+
{
|
1850
|
+
int rcode ;
|
1851
|
+
xmlNodePtr node;
|
1852
|
+
VALUE error_list = rb_ary_new();
|
1853
|
+
|
1854
|
+
Data_Get_Struct(self, xmlNode, node);
|
1855
|
+
|
1856
|
+
xmlSetStructuredErrorFunc((void *)error_list, Nokogiri_error_array_pusher);
|
1857
|
+
rcode = xmlXIncludeProcessTreeFlags(node, (int)NUM2INT(options));
|
1858
|
+
xmlSetStructuredErrorFunc(NULL, NULL);
|
1859
|
+
|
1860
|
+
if (rcode < 0) {
|
1861
|
+
xmlErrorPtr error;
|
1862
|
+
|
1863
|
+
error = xmlGetLastError();
|
1864
|
+
if (error) {
|
1865
|
+
rb_exc_raise(Nokogiri_wrap_xml_syntax_error(error));
|
1866
|
+
} else {
|
1867
|
+
rb_raise(rb_eRuntimeError, "Could not perform xinclude substitution");
|
1868
|
+
}
|
1869
|
+
}
|
1870
|
+
|
1871
|
+
return self;
|
1872
|
+
}
|
1873
|
+
|
1874
|
+
|
1875
|
+
/* TODO: DOCUMENT ME */
|
1876
|
+
static VALUE
|
1877
|
+
in_context(VALUE self, VALUE _str, VALUE _options)
|
1878
|
+
{
|
1879
|
+
xmlNodePtr node, list = 0, tmp, child_iter, node_children, doc_children;
|
1880
|
+
xmlNodeSetPtr set;
|
1881
|
+
xmlParserErrors error;
|
1882
|
+
VALUE doc, err;
|
1883
|
+
int doc_is_empty;
|
1884
|
+
|
1885
|
+
Data_Get_Struct(self, xmlNode, node);
|
1886
|
+
|
1887
|
+
doc = DOC_RUBY_OBJECT(node->doc);
|
1888
|
+
err = rb_iv_get(doc, "@errors");
|
1889
|
+
doc_is_empty = (node->doc->children == NULL) ? 1 : 0;
|
1890
|
+
node_children = node->children;
|
1891
|
+
doc_children = node->doc->children;
|
1892
|
+
|
1893
|
+
xmlSetStructuredErrorFunc((void *)err, Nokogiri_error_array_pusher);
|
1894
|
+
|
1895
|
+
/* Twiddle global variable because of a bug in libxml2.
|
1896
|
+
* http://git.gnome.org/browse/libxml2/commit/?id=e20fb5a72c83cbfc8e4a8aa3943c6be8febadab7
|
1897
|
+
*/
|
1898
|
+
#ifndef HTML_PARSE_NOIMPLIED
|
1899
|
+
htmlHandleOmittedElem(0);
|
1900
|
+
#endif
|
1901
|
+
|
1902
|
+
/* This function adds a fake node to the child of +node+. If the parser
|
1903
|
+
* does not exit cleanly with XML_ERR_OK, the list is freed. This can
|
1904
|
+
* leave the child pointers in a bad state if they were originally empty.
|
1905
|
+
*
|
1906
|
+
* http://git.gnome.org/browse/libxml2/tree/parser.c#n13177
|
1907
|
+
* */
|
1908
|
+
error = xmlParseInNodeContext(node, StringValuePtr(_str),
|
1909
|
+
(int)RSTRING_LEN(_str),
|
1910
|
+
(int)NUM2INT(_options), &list);
|
1911
|
+
|
1912
|
+
/* xmlParseInNodeContext should not mutate the original document or node,
|
1913
|
+
* so reassigning these pointers should be OK. The reason we're reassigning
|
1914
|
+
* is because if there were errors, it's possible for the child pointers
|
1915
|
+
* to be manipulated. */
|
1916
|
+
if (error != XML_ERR_OK) {
|
1917
|
+
node->doc->children = doc_children;
|
1918
|
+
node->children = node_children;
|
1919
|
+
}
|
1920
|
+
|
1921
|
+
/* make sure parent/child pointers are coherent so an unlink will work
|
1922
|
+
* properly (#331)
|
1923
|
+
*/
|
1924
|
+
child_iter = node->doc->children ;
|
1925
|
+
while (child_iter) {
|
1926
|
+
child_iter->parent = (xmlNodePtr)node->doc;
|
1927
|
+
child_iter = child_iter->next;
|
1928
|
+
}
|
1929
|
+
|
1930
|
+
#ifndef HTML_PARSE_NOIMPLIED
|
1931
|
+
htmlHandleOmittedElem(1);
|
1932
|
+
#endif
|
1933
|
+
|
1934
|
+
xmlSetStructuredErrorFunc(NULL, NULL);
|
1935
|
+
|
1936
|
+
/* Workaround for a libxml2 bug where a parsing error may leave a broken
|
1937
|
+
* node reference in node->doc->children.
|
1938
|
+
* This workaround is limited to when a parse error occurs, the document
|
1939
|
+
* went from having no children to having children, and the context node is
|
1940
|
+
* part of a document fragment.
|
1941
|
+
* https://bugzilla.gnome.org/show_bug.cgi?id=668155
|
1942
|
+
*/
|
1943
|
+
if (error != XML_ERR_OK && doc_is_empty && node->doc->children != NULL) {
|
1944
|
+
child_iter = node;
|
1945
|
+
while (child_iter->parent) {
|
1946
|
+
child_iter = child_iter->parent;
|
1947
|
+
}
|
1948
|
+
|
1949
|
+
if (child_iter->type == XML_DOCUMENT_FRAG_NODE) {
|
1950
|
+
node->doc->children = NULL;
|
1951
|
+
}
|
1952
|
+
}
|
1953
|
+
|
1954
|
+
/* FIXME: This probably needs to handle more constants... */
|
1955
|
+
switch (error) {
|
1956
|
+
case XML_ERR_INTERNAL_ERROR:
|
1957
|
+
case XML_ERR_NO_MEMORY:
|
1958
|
+
rb_raise(rb_eRuntimeError, "error parsing fragment (%d)", error);
|
1959
|
+
break;
|
1960
|
+
default:
|
1961
|
+
break;
|
1962
|
+
}
|
1963
|
+
|
1964
|
+
set = xmlXPathNodeSetCreate(NULL);
|
1965
|
+
|
1966
|
+
while (list) {
|
1967
|
+
tmp = list->next;
|
1968
|
+
list->next = NULL;
|
1969
|
+
xmlXPathNodeSetAddUnique(set, list);
|
1970
|
+
noko_xml_document_pin_node(list);
|
1971
|
+
list = tmp;
|
1972
|
+
}
|
1973
|
+
|
1974
|
+
return noko_xml_node_set_wrap(set, doc);
|
1975
|
+
}
|
1976
|
+
|
1977
|
+
|
1978
|
+
VALUE
|
1979
|
+
noko_xml_node_wrap(VALUE rb_class, xmlNodePtr c_node)
|
1980
|
+
{
|
1981
|
+
VALUE rb_document, rb_node_cache, rb_node;
|
1982
|
+
nokogiriTuplePtr node_has_a_document;
|
1983
|
+
xmlDocPtr c_doc;
|
1984
|
+
void (*f_mark)(xmlNodePtr) = NULL ;
|
1985
|
+
|
1986
|
+
assert(c_node);
|
1987
|
+
|
1988
|
+
if (c_node->type == XML_DOCUMENT_NODE || c_node->type == XML_HTML_DOCUMENT_NODE) {
|
1989
|
+
return DOC_RUBY_OBJECT(c_node->doc);
|
1990
|
+
}
|
1991
|
+
|
1992
|
+
/* It's OK if the node doesn't have a fully-realized document (as in XML::Reader). */
|
1993
|
+
/* see https://github.com/sparklemotion/nokogiri/issues/95 */
|
1994
|
+
/* and https://github.com/sparklemotion/nokogiri/issues/439 */
|
1995
|
+
c_doc = c_node->doc;
|
1996
|
+
if (c_doc->type == XML_DOCUMENT_FRAG_NODE) { c_doc = c_doc->doc; }
|
1997
|
+
node_has_a_document = DOC_RUBY_OBJECT_TEST(c_doc);
|
1998
|
+
|
1999
|
+
if (c_node->_private && node_has_a_document) {
|
2000
|
+
return (VALUE)c_node->_private;
|
2001
|
+
}
|
2002
|
+
|
2003
|
+
if (!RTEST(rb_class)) {
|
2004
|
+
switch (c_node->type) {
|
2005
|
+
case XML_ELEMENT_NODE:
|
2006
|
+
rb_class = cNokogiriXmlElement;
|
2007
|
+
break;
|
2008
|
+
case XML_TEXT_NODE:
|
2009
|
+
rb_class = cNokogiriXmlText;
|
2010
|
+
break;
|
2011
|
+
case XML_ATTRIBUTE_NODE:
|
2012
|
+
rb_class = cNokogiriXmlAttr;
|
2013
|
+
break;
|
2014
|
+
case XML_ENTITY_REF_NODE:
|
2015
|
+
rb_class = cNokogiriXmlEntityReference;
|
2016
|
+
break;
|
2017
|
+
case XML_COMMENT_NODE:
|
2018
|
+
rb_class = cNokogiriXmlComment;
|
2019
|
+
break;
|
2020
|
+
case XML_DOCUMENT_FRAG_NODE:
|
2021
|
+
rb_class = cNokogiriXmlDocumentFragment;
|
2022
|
+
break;
|
2023
|
+
case XML_PI_NODE:
|
2024
|
+
rb_class = cNokogiriXmlProcessingInstruction;
|
2025
|
+
break;
|
2026
|
+
case XML_ENTITY_DECL:
|
2027
|
+
rb_class = cNokogiriXmlEntityDecl;
|
2028
|
+
break;
|
2029
|
+
case XML_CDATA_SECTION_NODE:
|
2030
|
+
rb_class = cNokogiriXmlCData;
|
2031
|
+
break;
|
2032
|
+
case XML_DTD_NODE:
|
2033
|
+
rb_class = cNokogiriXmlDtd;
|
2034
|
+
break;
|
2035
|
+
case XML_ATTRIBUTE_DECL:
|
2036
|
+
rb_class = cNokogiriXmlAttributeDecl;
|
2037
|
+
break;
|
2038
|
+
case XML_ELEMENT_DECL:
|
2039
|
+
rb_class = cNokogiriXmlElementDecl;
|
2040
|
+
break;
|
2041
|
+
default:
|
2042
|
+
rb_class = cNokogiriXmlNode;
|
2043
|
+
}
|
2044
|
+
}
|
2045
|
+
|
2046
|
+
f_mark = node_has_a_document ? _xml_node_mark : NULL ;
|
2047
|
+
|
2048
|
+
rb_node = Data_Wrap_Struct(rb_class, f_mark, _xml_node_dealloc, c_node) ;
|
2049
|
+
c_node->_private = (void *)rb_node;
|
2050
|
+
|
2051
|
+
if (node_has_a_document) {
|
2052
|
+
rb_document = DOC_RUBY_OBJECT(c_doc);
|
2053
|
+
rb_node_cache = DOC_NODE_CACHE(c_doc);
|
2054
|
+
rb_ary_push(rb_node_cache, rb_node);
|
2055
|
+
rb_funcall(rb_document, id_decorate, 1, rb_node);
|
2056
|
+
}
|
2057
|
+
|
2058
|
+
return rb_node ;
|
2059
|
+
}
|
2060
|
+
|
2061
|
+
|
2062
|
+
/*
|
2063
|
+
* return Array<Nokogiri::XML::Attr> containing the node's attributes
|
2064
|
+
*/
|
2065
|
+
VALUE
|
2066
|
+
noko_xml_node_attrs(xmlNodePtr c_node)
|
2067
|
+
{
|
2068
|
+
VALUE rb_properties = rb_ary_new();
|
2069
|
+
xmlAttrPtr c_property;
|
2070
|
+
|
2071
|
+
c_property = c_node->properties ;
|
2072
|
+
while (c_property != NULL) {
|
2073
|
+
rb_ary_push(rb_properties, noko_xml_node_wrap(Qnil, (xmlNodePtr)c_property));
|
2074
|
+
c_property = c_property->next ;
|
2075
|
+
}
|
2076
|
+
|
2077
|
+
return rb_properties;
|
2078
|
+
}
|
2079
|
+
|
2080
|
+
void
|
2081
|
+
noko_init_xml_node()
|
2082
|
+
{
|
2083
|
+
cNokogiriXmlNode = rb_define_class_under(mNokogiriXml, "Node", rb_cObject);
|
2084
|
+
|
2085
|
+
rb_undef_alloc_func(cNokogiriXmlNode);
|
2086
|
+
|
2087
|
+
rb_define_singleton_method(cNokogiriXmlNode, "new", rb_xml_node_new, -1);
|
2088
|
+
|
2089
|
+
rb_define_method(cNokogiriXmlNode, "add_namespace_definition", rb_xml_node_add_namespace_definition, 2);
|
2090
|
+
rb_define_method(cNokogiriXmlNode, "attribute", rb_xml_node_attribute, 1);
|
2091
|
+
rb_define_method(cNokogiriXmlNode, "attribute_nodes", rb_xml_node_attribute_nodes, 0);
|
2092
|
+
rb_define_method(cNokogiriXmlNode, "attribute_with_ns", rb_xml_node_attribute_with_ns, 2);
|
2093
|
+
rb_define_method(cNokogiriXmlNode, "blank?", rb_xml_node_blank_eh, 0);
|
2094
|
+
rb_define_method(cNokogiriXmlNode, "child", rb_xml_node_child, 0);
|
2095
|
+
rb_define_method(cNokogiriXmlNode, "children", rb_xml_node_children, 0);
|
2096
|
+
rb_define_method(cNokogiriXmlNode, "content", rb_xml_node_content, 0);
|
2097
|
+
rb_define_method(cNokogiriXmlNode, "create_external_subset", create_external_subset, 3);
|
2098
|
+
rb_define_method(cNokogiriXmlNode, "create_internal_subset", create_internal_subset, 3);
|
2099
|
+
rb_define_method(cNokogiriXmlNode, "document", rb_xml_node_document, 0);
|
2100
|
+
rb_define_method(cNokogiriXmlNode, "dup", duplicate_node, -1);
|
2101
|
+
rb_define_method(cNokogiriXmlNode, "element_children", rb_xml_node_element_children, 0);
|
2102
|
+
rb_define_method(cNokogiriXmlNode, "encode_special_chars", encode_special_chars, 1);
|
2103
|
+
rb_define_method(cNokogiriXmlNode, "external_subset", external_subset, 0);
|
2104
|
+
rb_define_method(cNokogiriXmlNode, "first_element_child", rb_xml_node_first_element_child, 0);
|
2105
|
+
rb_define_method(cNokogiriXmlNode, "internal_subset", internal_subset, 0);
|
2106
|
+
rb_define_method(cNokogiriXmlNode, "key?", key_eh, 1);
|
2107
|
+
rb_define_method(cNokogiriXmlNode, "lang", get_lang, 0);
|
2108
|
+
rb_define_method(cNokogiriXmlNode, "lang=", set_lang, 1);
|
2109
|
+
rb_define_method(cNokogiriXmlNode, "last_element_child", rb_xml_node_last_element_child, 0);
|
2110
|
+
rb_define_method(cNokogiriXmlNode, "line", rb_xml_node_line, 0);
|
2111
|
+
rb_define_method(cNokogiriXmlNode, "line=", rb_xml_node_line_set, 1);
|
2112
|
+
rb_define_method(cNokogiriXmlNode, "namespace", rb_xml_node_namespace, 0);
|
2113
|
+
rb_define_method(cNokogiriXmlNode, "namespace_definitions", namespace_definitions, 0);
|
2114
|
+
rb_define_method(cNokogiriXmlNode, "namespace_scopes", rb_xml_node_namespace_scopes, 0);
|
2115
|
+
rb_define_method(cNokogiriXmlNode, "namespaced_key?", namespaced_key_eh, 2);
|
2116
|
+
rb_define_method(cNokogiriXmlNode, "native_content=", set_native_content, 1);
|
2117
|
+
rb_define_method(cNokogiriXmlNode, "next_element", next_element, 0);
|
2118
|
+
rb_define_method(cNokogiriXmlNode, "next_sibling", next_sibling, 0);
|
2119
|
+
rb_define_method(cNokogiriXmlNode, "node_name", get_name, 0);
|
2120
|
+
rb_define_method(cNokogiriXmlNode, "node_name=", set_name, 1);
|
2121
|
+
rb_define_method(cNokogiriXmlNode, "node_type", node_type, 0);
|
2122
|
+
rb_define_method(cNokogiriXmlNode, "parent", get_parent, 0);
|
2123
|
+
rb_define_method(cNokogiriXmlNode, "path", rb_xml_node_path, 0);
|
2124
|
+
rb_define_method(cNokogiriXmlNode, "pointer_id", rb_xml_node_pointer_id, 0);
|
2125
|
+
rb_define_method(cNokogiriXmlNode, "previous_element", previous_element, 0);
|
2126
|
+
rb_define_method(cNokogiriXmlNode, "previous_sibling", previous_sibling, 0);
|
2127
|
+
rb_define_method(cNokogiriXmlNode, "unlink", unlink_node, 0);
|
2128
|
+
|
2129
|
+
rb_define_private_method(cNokogiriXmlNode, "add_child_node", add_child, 1);
|
2130
|
+
rb_define_private_method(cNokogiriXmlNode, "add_next_sibling_node", add_next_sibling, 1);
|
2131
|
+
rb_define_private_method(cNokogiriXmlNode, "add_previous_sibling_node", add_previous_sibling, 1);
|
2132
|
+
rb_define_private_method(cNokogiriXmlNode, "compare", compare, 1);
|
2133
|
+
rb_define_private_method(cNokogiriXmlNode, "dump_html", dump_html, 0);
|
2134
|
+
rb_define_private_method(cNokogiriXmlNode, "get", get, 1);
|
2135
|
+
rb_define_private_method(cNokogiriXmlNode, "in_context", in_context, 2);
|
2136
|
+
rb_define_private_method(cNokogiriXmlNode, "native_write_to", native_write_to, 4);
|
2137
|
+
rb_define_private_method(cNokogiriXmlNode, "process_xincludes", process_xincludes, 1);
|
2138
|
+
rb_define_private_method(cNokogiriXmlNode, "replace_node", replace, 1);
|
2139
|
+
rb_define_private_method(cNokogiriXmlNode, "set", set, 2);
|
2140
|
+
rb_define_private_method(cNokogiriXmlNode, "set_namespace", set_namespace, 1);
|
2141
|
+
|
2142
|
+
id_decorate = rb_intern("decorate");
|
2143
|
+
id_decorate_bang = rb_intern("decorate!");
|
2144
|
+
}
|