Skip to content

Commit 82b39b4

Browse files
committed
imp: readding loadSchemas, validateAgainstSchames, freeSchemes, and clearAll functions
1 parent 83a9cc6 commit 82b39b4

File tree

3 files changed

+205
-50
lines changed

3 files changed

+205
-50
lines changed

libxml.cpp

Lines changed: 159 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,15 @@ Napi::Object Libxml::Init(Napi::Env env, Napi::Object exports)
1515
InstanceMethod("loadXml", &Libxml::loadXml),
1616
InstanceMethod("loadXmlFromString", &Libxml::loadXmlFromString),
1717
InstanceMethod("loadDtds", &Libxml::loadDtds),
18+
InstanceMethod("loadSchemas", &Libxml::loadSchemas),
1819
InstanceMethod("validateAgainstDtds", &Libxml::validateAgainstDtds),
20+
InstanceMethod("validateAgainstSchemas", &Libxml::validateAgainstSchemas),
1921
InstanceMethod("xpathSelect", &Libxml::xpathSelect),
2022
InstanceMethod("getDtd", &Libxml::getDtd),
2123
InstanceMethod("freeXml", &Libxml::freeXml),
22-
InstanceMethod("freeDtds", &Libxml::freeDtds)
24+
InstanceMethod("freeDtds", &Libxml::freeDtds),
25+
InstanceMethod("freeSchemas", &Libxml::freeSchemas),
26+
InstanceMethod("clearAll", &Libxml::clearAll)
2327
});
2428

2529
// Create a peristent reference to the class constructor. This will allow
@@ -163,6 +167,57 @@ Napi::Value Libxml::loadDtds(const Napi::CallbackInfo& info) {
163167
return env.Undefined();
164168
}
165169

170+
Napi::Value Libxml::loadSchemas(const Napi::CallbackInfo& info) {
171+
Napi::Env env = info.Env();
172+
if (info.Length() < 1){
173+
Napi::TypeError::New(env, "loadSchemas requires at least 1 argument, an array of Schemas").ThrowAsJavaScriptException();
174+
return env.Undefined();
175+
}
176+
if(!info[0].IsArray()){
177+
Napi::TypeError::New(env, "loadSchemas requires an array").ThrowAsJavaScriptException();
178+
return env.Undefined();
179+
}
180+
Napi::EscapableHandleScope scope(env);
181+
Napi::Array schemasPathsLocal = info[0].As<Napi::Array>();
182+
//set up error handlers
183+
Napi::Array errors = Napi::Array::New(env);
184+
xmlResetLastError();
185+
XmlSyntaxError::env = &env;
186+
xmlSetStructuredErrorFunc(reinterpret_cast<void*>(&errors),
187+
XmlSyntaxError::PushToArray);
188+
for (unsigned int i = 0; i < schemasPathsLocal.Length(); i++){
189+
// Handle value if string and drop it silently otherwise
190+
if(schemasPathsLocal.Get(i).IsString()) {
191+
Napi::String value = schemasPathsLocal.Get(i).As<Napi::String>();
192+
string pathStr (value.Utf8Value());
193+
const char* path (pathStr.c_str());
194+
xmlSchemaParserCtxtPtr pctxt;
195+
xmlSchemaPtr schema;
196+
// If cannot create Parse schema, just continue
197+
if ((pctxt = xmlSchemaNewParserCtxt(path)) == NULL) {
198+
XmlSyntaxError::PushToArray(errors, path);
199+
continue;
200+
}
201+
// Loading XML Schema content
202+
schema = xmlSchemaParse(pctxt);
203+
xmlSchemaFreeParserCtxt(pctxt);
204+
if (schema == nullptr) {
205+
XmlSyntaxError::PushToArray(errors, path);
206+
continue;
207+
}
208+
this->schemasPaths.push_back(schema);
209+
}
210+
}
211+
xmlSetStructuredErrorFunc(NULL, NULL);
212+
// We set dtdLoadedErrors property for js side
213+
if(errors.Length()){
214+
this->Value().Set("schemasLoadedErrors", errors);
215+
} else {
216+
this->Value().Delete("schemasLoadedErrors");
217+
}
218+
return env.Undefined();
219+
}
220+
166221
Napi::Value Libxml::validateAgainstDtds(const Napi::CallbackInfo& info) {
167222
Napi::Env env = info.Env();
168223

@@ -234,6 +289,74 @@ Napi::Value Libxml::validateAgainstDtds(const Napi::CallbackInfo& info) {
234289
}
235290
}
236291

292+
Napi::Value Libxml::validateAgainstSchemas(const Napi::CallbackInfo& info) {
293+
Napi::Env env = info.Env();
294+
295+
if(this->schemasPaths.empty()){
296+
return env.Null();;
297+
}
298+
299+
if(info[0].IsNumber()){
300+
XmlSyntaxError::ChangeMaxNumberOfError(info[0].ToNumber().Int32Value());
301+
}
302+
//Setting context of validation
303+
const char* schemaValidationErrorsPath;
304+
bool oneOfTheSchemaValidate = false;
305+
string schemaValidateName;
306+
307+
308+
//If length 0, return null; to implement
309+
//Local<Object> errorsValidations = Nan::New<Object>();
310+
Napi::Object errorsValidations = Napi::Object::New(env);
311+
312+
for (vector<xmlSchemaPtr>::iterator xsd = this->schemasPaths.begin(); xsd != this->schemasPaths.end() ; ++xsd){
313+
//set up error handling
314+
Napi::Array errors = Napi::Array::New(env);
315+
xmlResetLastError();
316+
XmlSyntaxError::env = &env;
317+
xmlSetStructuredErrorFunc(reinterpret_cast<void*>(&errors),
318+
XmlSyntaxError::PushToArray);
319+
320+
const char* xsdName = (const char *)(*xsd)->doc->URL;
321+
//Local<String> urlSchema = Nan::New<String>(xsdName).ToLocalChecked();
322+
Napi::String urlSchema = Napi::String::New(env, xsdName);
323+
// Creating the validation context
324+
xmlSchemaValidCtxtPtr vctxt;
325+
if ((vctxt = xmlSchemaNewValidCtxt(*xsd)) == nullptr) {
326+
continue;
327+
}
328+
//Instead we could set this to disable output : xmlSetStructuredErrorFunc(vctxt,errorsHandler);
329+
// xmlSchemaSetValidErrors(vctxt, (xmlSchemaValidityErrorFunc) Libxml::errorsHandler, (xmlSchemaValidityWarningFunc) Libxml::errorsHandler, (void *) Libxml::errorsHandler);
330+
xmlSchemaSetValidErrors(vctxt, nullptr, nullptr, nullptr);
331+
int result = xmlSchemaValidateDoc(vctxt, this->docPtr);
332+
// Stop listening for errors
333+
xmlSetStructuredErrorFunc(nullptr, nullptr);
334+
xmlSchemaFreeValidCtxt(vctxt);
335+
if(result == 0){
336+
oneOfTheSchemaValidate = true;
337+
schemaValidateName = xsdName;
338+
break;
339+
}
340+
errorsValidations.Set(urlSchema, errors);
341+
schemaValidationErrorsPath = xsdName;
342+
}
343+
if(oneOfTheSchemaValidate){
344+
this->Value().Delete("validationSchemaErrors");
345+
if(schemaValidateName.length()){
346+
//info.GetReturnValue().Set(Nan::New<v8::String>(schemaValidateName).ToLocalChecked());
347+
return Napi::String::New(env, schemaValidateName);
348+
}else{
349+
//info.GetReturnValue().Set(Nan::True());
350+
return Napi::Boolean::New(env, true);
351+
}
352+
}else{
353+
// info.Holder()->Set(Nan::New<v8::String>("validationSchemaErrors").ToLocalChecked(), errorsValidations);
354+
// info.GetReturnValue().Set(Nan::False());
355+
this->Value().Set("validationSchemaErrors", errorsValidations);
356+
return Napi::Boolean::New(env, false);
357+
}
358+
}
359+
237360
Napi::Value Libxml::xpathSelect(const Napi::CallbackInfo& info) {
238361
Napi::Env env = info.Env();
239362
if (info.Length() < 1){
@@ -328,15 +451,15 @@ void Libxml::freeXml(const Napi::CallbackInfo& info) {
328451
this->docPtr = nullptr;
329452
}
330453

331-
Napi::Value Libxml::freeDtds(const Napi::CallbackInfo& info) {
454+
void Libxml::freeDtds(const Napi::CallbackInfo& info) {
332455
Napi::Env env = info.Env();
333456
Napi::HandleScope scope(env);
334457
// Delete Javascript property
335458
this->Value().Delete("dtdsLoadedErrors");
336459
this->Value().Delete("validationDtdErrors");
337460
// If dtds is already empty, just stop here
338461
if(this->dtdsPaths.empty()){
339-
return env.Undefined();
462+
return;
340463
}
341464
for (vector<xmlDtdPtr>::iterator dtd = this->dtdsPaths.begin(); dtd != this->dtdsPaths.end() ; ++dtd){
342465
if(*dtd != nullptr){
@@ -347,7 +470,39 @@ Napi::Value Libxml::freeDtds(const Napi::CallbackInfo& info) {
347470
}
348471
// clear the vector of dtds
349472
this->dtdsPaths.clear();
350-
return env.Undefined();
473+
return;
474+
}
475+
476+
void Libxml::freeSchemas(const Napi::CallbackInfo& info) {
477+
Napi::Env env = info.Env();
478+
Napi::HandleScope scope(env);
479+
//Libxml* libxml = Nan::ObjectWrap::Unwrap<Libxml>(info.Holder());
480+
// Delete Javascript property
481+
// bool deletedLoaded = Nan::Delete(info.Holder(), Nan::New<v8::String>("schemasLoadedErrors").ToLocalChecked()).FromMaybe(false);
482+
// bool deleted = Nan::Delete(info.Holder(), Nan::New<v8::String>("validationSchemasErrors").ToLocalChecked()).FromMaybe(false);
483+
this->Value().Delete("schemasLoadedErrors");
484+
this->Value().Delete("validationSchemasErrors");
485+
// If dtds is already empty, just stop here
486+
if(this->schemasPaths.empty()){
487+
return;
488+
}
489+
for (vector<xmlSchemaPtr>::iterator xsd = this->schemasPaths.begin(); xsd != this->schemasPaths.end() ; ++xsd){
490+
if(*xsd != nullptr){
491+
// Force clear memory xsd loaded
492+
xmlSchemaFree(*xsd);
493+
*xsd = nullptr;
494+
}
495+
}
496+
// clear the vector of dtds
497+
this->schemasPaths.clear();
498+
//
499+
}
500+
501+
void Libxml::clearAll(const Napi::CallbackInfo& info) {
502+
this->freeXml(info);
503+
this->freeDtds(info);
504+
this->freeSchemas(info);
505+
xmlCleanupParser();
351506
}
352507

353508
// Initialize native add-on

libxml.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,15 +42,15 @@ class Libxml : public Napi::ObjectWrap<Libxml>
4242
Napi::Value loadXmlFromString(const Napi::CallbackInfo& info);
4343
Napi::Value loadDtds(const Napi::CallbackInfo& info);
4444
// // static Napi::Value loadDtdsFromString(const Napi::CallbackInfo& info);
45-
// static Napi::Value loadSchemas(const Napi::CallbackInfo& info);
45+
Napi::Value loadSchemas(const Napi::CallbackInfo& info);
4646
Napi::Value validateAgainstDtds(const Napi::CallbackInfo& info);
47-
// static Napi::Value validateAgainstSchemas(const Napi::CallbackInfo& info);
47+
Napi::Value validateAgainstSchemas(const Napi::CallbackInfo& info);
4848
Napi::Value xpathSelect(const Napi::CallbackInfo& info);
4949
Napi::Value getDtd(const Napi::CallbackInfo& info);
5050
void freeXml(const Napi::CallbackInfo& info);
51-
Napi::Value freeDtds(const Napi::CallbackInfo& info);
52-
// static Napi::Value freeSchemas(const Napi::CallbackInfo& info);
53-
// static Napi::Value clearAll(const Napi::CallbackInfo& info);
51+
void freeDtds(const Napi::CallbackInfo& info);
52+
void freeSchemas(const Napi::CallbackInfo& info);
53+
void clearAll(const Napi::CallbackInfo& info);
5454
};
5555

5656
#endif

test/libxml-test.js

Lines changed: 41 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -200,45 +200,45 @@ describe('Node-Libxml', function () {
200200
libxml.freeDtds();
201201
});
202202
// // SCHEMAS
203-
// it('Should return wellformed & valid on a wellformed & valid xml SCHEMA', function () {
204-
// let libxml = new Libxml();
205-
// let testDefaultWf = libxml.loadXml('test/data/test-valid-schema.xml');
206-
// libxml.loadSchemas(['test/xsd/MARC21slim.xsd']);
207-
// let testDefaultV = libxml.validateAgainstSchemas();
208-
// expect(testDefaultWf).to.be.true;
209-
// expect(testDefaultV).to.be.a('string');
210-
// libxml.freeXml();
211-
// libxml.freeSchemas();
212-
// });
213-
// it('Should return errors for invalid against schema', function () {
214-
// let libxml = new Libxml();
215-
// let testDefaultWf = libxml.loadXml('test/data/test-valid-schema.xml');
216-
// libxml.loadSchemas(['test/xsd/MARC21slim-bad.xsd']);
217-
// let testDefaultV = libxml.validateAgainstSchemas();
218-
// expect(testDefaultWf).to.be.true;
219-
// expect(testDefaultV).to.be.false;
220-
// expect(libxml).to.have.a.property('validationSchemaErrors');
221-
// expect(libxml.validationSchemaErrors).to.be.a('object');
222-
// expect(libxml.validationSchemaErrors).to.have.a.property('test/xsd/MARC21slim-bad.xsd');
223-
// expect(libxml.validationSchemaErrors['test/xsd/MARC21slim-bad.xsd']).to.be.a('array');
224-
// expect(libxml.validationSchemaErrors['test/xsd/MARC21slim-bad.xsd'][0]).to.have.a.property('column');
225-
// expect(libxml.validationSchemaErrors['test/xsd/MARC21slim-bad.xsd'][0]).to.have.a.property('line');
226-
// expect(libxml.validationSchemaErrors['test/xsd/MARC21slim-bad.xsd'][0]).to.have.a.property('message');
227-
// libxml.freeXml();
228-
// libxml.freeSchemas();
229-
// });
230-
// it('Should return expected value and not crash when multiple clean of schemas', function () {
231-
// let libxml = new Libxml();
232-
// let testDefaultWf = libxml.loadXml('test/data/test-valid-schema.xml');
233-
// libxml.loadSchemas(['test/xsd/MARC21slim-bad.xsd','test/xsd/MARC21slim.xsd']);
234-
// let testDefaultV = libxml.validateAgainstSchemas();
235-
// expect(testDefaultWf).to.be.true;
236-
// expect(testDefaultV).to.be.a('string');
237-
// libxml.freeXml();
238-
// libxml.freeSchemas();
239-
// });
240-
// it('Should not throw error when use clearAll function to clear all memory libxml', function () {
241-
// let libxml = new Libxml();
242-
// libxml.clearAll();
243-
// });
203+
it('Should return wellformed & valid on a wellformed & valid xml SCHEMA', function () {
204+
let libxml = new Libxml();
205+
let testDefaultWf = libxml.loadXml('test/data/test-valid-schema.xml');
206+
libxml.loadSchemas(['test/xsd/MARC21slim.xsd']);
207+
let testDefaultV = libxml.validateAgainstSchemas();
208+
expect(testDefaultWf).to.be.true;
209+
expect(testDefaultV).to.be.a('string');
210+
libxml.freeXml();
211+
libxml.freeSchemas();
212+
});
213+
it('Should return errors for invalid against schema', function () {
214+
let libxml = new Libxml();
215+
let testDefaultWf = libxml.loadXml('test/data/test-valid-schema.xml');
216+
libxml.loadSchemas(['test/xsd/MARC21slim-bad.xsd']);
217+
let testDefaultV = libxml.validateAgainstSchemas();
218+
expect(testDefaultWf).to.be.true;
219+
expect(testDefaultV).to.be.false;
220+
expect(libxml).to.have.a.property('validationSchemaErrors');
221+
expect(libxml.validationSchemaErrors).to.be.a('object');
222+
expect(libxml.validationSchemaErrors).to.have.a.property('test/xsd/MARC21slim-bad.xsd');
223+
expect(libxml.validationSchemaErrors['test/xsd/MARC21slim-bad.xsd']).to.be.a('array');
224+
expect(libxml.validationSchemaErrors['test/xsd/MARC21slim-bad.xsd'][0]).to.have.a.property('column');
225+
expect(libxml.validationSchemaErrors['test/xsd/MARC21slim-bad.xsd'][0]).to.have.a.property('line');
226+
expect(libxml.validationSchemaErrors['test/xsd/MARC21slim-bad.xsd'][0]).to.have.a.property('message');
227+
libxml.freeXml();
228+
libxml.freeSchemas();
229+
});
230+
it('Should return expected value and not crash when multiple clean of schemas', function () {
231+
let libxml = new Libxml();
232+
let testDefaultWf = libxml.loadXml('test/data/test-valid-schema.xml');
233+
libxml.loadSchemas(['test/xsd/MARC21slim-bad.xsd','test/xsd/MARC21slim.xsd']);
234+
let testDefaultV = libxml.validateAgainstSchemas();
235+
expect(testDefaultWf).to.be.true;
236+
expect(testDefaultV).to.be.a('string');
237+
libxml.freeXml();
238+
libxml.freeSchemas();
239+
});
240+
it('Should not throw error when use clearAll function to clear all memory libxml', function () {
241+
let libxml = new Libxml();
242+
libxml.clearAll();
243+
});
244244
});

0 commit comments

Comments
 (0)