Skip to content

Commit

Permalink
Add libxml2 processing instructions support (libxmljs#521)
Browse files Browse the repository at this point in the history
  • Loading branch information
perrin4869 authored and rchipka committed Aug 28, 2018
1 parent 51a1e51 commit 974612d
Show file tree
Hide file tree
Showing 8 changed files with 241 additions and 1 deletion.
1 change: 1 addition & 0 deletions binding.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
'src/xml_sax_parser.cc',
'src/xml_syntax_error.cc',
'src/xml_text.cc',
'src/xml_pi.cc',
'src/xml_xpath_context.cc',
'vendor/libxml/buf.c',
'vendor/libxml/catalog.c',
Expand Down
1 change: 1 addition & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ module.exports.features = bindings.features;
module.exports.Comment = require('./lib/comment');
module.exports.Document = Document;
module.exports.Element = require('./lib/element');
module.exports.ProcessingInstruction = require('./lib/pi');
module.exports.Text = require('./lib/text');

// Compatibility synonyms
Expand Down
2 changes: 1 addition & 1 deletion lib/element.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ var bindings = require('./bindings');
/// create a new element on the given document
/// @param doc the Document to create the element for
/// @param name the element name
/// @param {String} [contenn] element content
/// @param {String} [content] element content
/// @constructor
function Element(doc, name, content) {
if (!doc) {
Expand Down
23 changes: 23 additions & 0 deletions lib/pi.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
var bindings = require('./bindings');

/// create a new processing instruction on the given document
/// @param doc the Document to create the processing instruction for
/// @param name the processing instruction name
/// @param {String} [content] processing instruction content
/// @constructor
function ProcessingInstruction(doc, name, content) {
if (!doc) {
throw new Error('document argument required');
} else if (! (doc instanceof bindings.Document)) {
throw new Error('document argument must be an ' +
'instance of Document');
} else if (!name) {
throw new Error('name argument required');
}

return new bindings.ProcessingInstruction(doc, name, content);
}

ProcessingInstruction.prototype = bindings.ProcessingInstruction.prototype;

module.exports = ProcessingInstruction;
2 changes: 2 additions & 0 deletions src/xml_node.cc
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "xml_comment.h"
#include "xml_text.h"
#include "xml_attribute.h"
#include "xml_pi.h"

namespace libxmljs {

Expand Down Expand Up @@ -781,6 +782,7 @@ XmlNode::Initialize(v8::Handle<v8::Object> target) {
XmlElement::Initialize(target);
XmlText::Initialize(target);
XmlComment::Initialize(target);
XmlProcessingInstruction::Initialize(target);
XmlAttribute::Initialize(target);
}
} // namespace libxmljs
148 changes: 148 additions & 0 deletions src/xml_pi.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
#include <node.h>

#include <cstring>

#include "libxmljs.h"

#include "xml_pi.h"
#include "xml_document.h"
#include "xml_attribute.h"
#include "xml_xpath_context.h"

namespace libxmljs {

Nan::Persistent<v8::FunctionTemplate> XmlProcessingInstruction::constructor_template;

// doc, content
NAN_METHOD(XmlProcessingInstruction::New) {
Nan::HandleScope scope;

// if we were created for an existing xml node, then we don't need
// to create a new node on the document
if (info.Length() == 0) {
return info.GetReturnValue().Set(info.Holder());
}

XmlDocument* document = Nan::ObjectWrap::Unwrap<XmlDocument>(info[0]->ToObject());
assert(document);

v8::String::Utf8Value name(info[1]);

v8::Local<v8::Value> contentOpt;
if (info[2]->IsString()) {
contentOpt = info[2];
}
v8::String::Utf8Value contentRaw(contentOpt);
const char* content = (contentRaw.length()) ? *contentRaw : NULL;

xmlNode* pi = xmlNewDocPI(document->xml_obj, (const xmlChar *) *name, (xmlChar *) content);

XmlProcessingInstruction* processing_instruction = new XmlProcessingInstruction(pi);
pi->_private = processing_instruction;
processing_instruction->Wrap(info.Holder());

// this prevents the document from going away
info.Holder()->Set(Nan::New<v8::String>("document").ToLocalChecked(), info[0]);

return info.GetReturnValue().Set(info.Holder());
}

NAN_METHOD(XmlProcessingInstruction::Name) {
Nan::HandleScope scope;
XmlProcessingInstruction *processing_instruction = Nan::ObjectWrap::Unwrap<XmlProcessingInstruction>(info.Holder());
assert(processing_instruction);

if (info.Length() == 0)
return info.GetReturnValue().Set(processing_instruction->get_name());

v8::String::Utf8Value name(info[0]->ToString());
processing_instruction->set_name(*name);
return info.GetReturnValue().Set(info.Holder());
}

NAN_METHOD(XmlProcessingInstruction::Text) {
Nan::HandleScope scope;
XmlProcessingInstruction *processing_instruction = Nan::ObjectWrap::Unwrap<XmlProcessingInstruction>(info.Holder());
assert(processing_instruction);

if (info.Length() == 0) {
return info.GetReturnValue().Set(processing_instruction->get_content());
} else {
processing_instruction->set_content(*v8::String::Utf8Value(info[0]));
}

return info.GetReturnValue().Set(info.Holder());
}

void
XmlProcessingInstruction::set_name(const char* name) {
xmlNodeSetName(xml_obj, (const xmlChar*)name);
}

v8::Local<v8::Value>
XmlProcessingInstruction::get_name() {
Nan::EscapableHandleScope scope;
if(xml_obj->name) return scope.Escape(Nan::New<v8::String>((const char*)xml_obj->name).ToLocalChecked());
else return scope.Escape(Nan::Undefined());
}

void
XmlProcessingInstruction::set_content(const char* content) {
xmlNodeSetContent(xml_obj, (xmlChar*) content);
}

v8::Local<v8::Value>
XmlProcessingInstruction::get_content() {
Nan::EscapableHandleScope scope;
xmlChar* content = xmlNodeGetContent(xml_obj);
if (content) {
v8::Local<v8::String> ret_content =
Nan::New<v8::String>((const char *)content).ToLocalChecked();
xmlFree(content);
return scope.Escape(ret_content);
}

return scope.Escape(Nan::New<v8::String>("").ToLocalChecked());
}

v8::Local<v8::Object>
XmlProcessingInstruction::New(xmlNode* node)
{
Nan::EscapableHandleScope scope;
if (node->_private) {
return scope.Escape(static_cast<XmlNode*>(node->_private)->handle());
}

XmlProcessingInstruction* processing_instruction = new XmlProcessingInstruction(node);
v8::Local<v8::Object> obj = Nan::NewInstance(Nan::New(constructor_template)->GetFunction()).ToLocalChecked();
processing_instruction->Wrap(obj);
return scope.Escape(obj);
}

XmlProcessingInstruction::XmlProcessingInstruction(xmlNode* node)
: XmlNode(node)
{
}

void
XmlProcessingInstruction::Initialize(v8::Handle<v8::Object> target)
{
Nan::HandleScope scope;
v8::Local<v8::FunctionTemplate> t = Nan::New<v8::FunctionTemplate>(static_cast<NAN_METHOD((*))>(New));
t->Inherit(Nan::New(XmlNode::constructor_template));
t->InstanceTemplate()->SetInternalFieldCount(1);
constructor_template.Reset( t);

Nan::SetPrototypeMethod(t,
"name",
XmlProcessingInstruction::Name);

Nan::SetPrototypeMethod(t,
"text",
XmlProcessingInstruction::Text);

Nan::Set(target, Nan::New<v8::String>("ProcessingInstruction").ToLocalChecked(),
t->GetFunction());
}

} // namespace libxmljs
36 changes: 36 additions & 0 deletions src/xml_pi.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#ifndef SRC_XML_PI_H_
#define SRC_XML_PI_H_

#include "libxmljs.h"
#include "xml_node.h"

namespace libxmljs {

class XmlProcessingInstruction : public XmlNode {
public:

explicit XmlProcessingInstruction(xmlNode* node);

static void Initialize(v8::Handle<v8::Object> target);

static Nan::Persistent<v8::FunctionTemplate> constructor_template;

// create new xml comment to wrap the node
static v8::Local<v8::Object> New(xmlNode* node);

protected:

static NAN_METHOD(New);
static NAN_METHOD(Name);
static NAN_METHOD(Text);

void set_name(const char* name);

v8::Local<v8::Value> get_name();
void set_content(const char* content);
v8::Local<v8::Value> get_content();
};

} // namespace libxmljs

#endif // SRC_XML_PI_H_
29 changes: 29 additions & 0 deletions test/pi.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
var libxml = require('../index');

module.exports.new = function(assert) {
var doc = libxml.Document();
var pi = libxml.ProcessingInstruction(doc, 'mypi', 'mycontent');
doc.root(new libxml.Element(doc, 'myelem'));
doc.root().addPrevSibling(pi);

assert.equal(doc.root().prevSibling(), pi);
assert.equal('mypi', pi.name());
assert.equal('mycontent', pi.text());
assert.done();
};

module.exports.name = function(assert) {
var doc = libxml.Document();
var pi = libxml.ProcessingInstruction(doc, 'mypi');
pi.name('mynewpi');
assert.equal('mynewpi', pi.name());
assert.done();
};

module.exports.text = function(assert) {
var doc = libxml.Document();
var pi = libxml.ProcessingInstruction(doc, 'mypi');
pi.text('pi3');
assert.equal('pi3', pi.text());
assert.done();
};

0 comments on commit 974612d

Please sign in to comment.