Skip to content

Commit

Permalink
Add PrettyPrint capability, debug settings
Browse files Browse the repository at this point in the history
- Initial version of PrettyPrint capability added to genx core
- Add flag to Writer constructor to turn on PrettyPrint
- Add basic tests to validate PrettyPrint works and formats correctly
- Add "clean" and "cmake / cmake debug" scripts to package.json
  • Loading branch information
PMLavigne committed Dec 29, 2015
1 parent aa49f87 commit ced2105
Show file tree
Hide file tree
Showing 8 changed files with 168 additions and 31 deletions.
8 changes: 4 additions & 4 deletions examples/atom.js
@@ -1,10 +1,10 @@
var genx = require('genx');
var genx = require('../lib/genx');

var w = new genx.Writer();
var w = new genx.Writer(true);

w.on('data', function(data) {
process.stdout.write(data);
})
});

// Declare the elements and attributes up-front
var ns = w.declareNamespace('http://www.w3.org/2005/Atom', '');
Expand All @@ -17,7 +17,7 @@ var name = w.declareElement(ns, 'name');
var id = w.declareElement(ns, 'id');
var entry = w.declareElement(ns, 'entry');
var summary = w.declareElement(ns, 'summary');

var href = w.declareAttribute('href');

// This is not a processing instruction and as such can't be generated by Genx
Expand Down
7 changes: 5 additions & 2 deletions lib/genx.js
@@ -1,5 +1,8 @@
var EventEmitter = require('events').EventEmitter,
genx = require('../build/Release/genx');

// Check if we're in debug mode
var genxDir = (typeof v8debug !== 'undefined') ? '../build/Debug/genx' : '../build/Release/genx',
EventEmitter = require('events').EventEmitter,
genx = require(genxDir);
genx.Writer.prototype.__proto__ = EventEmitter.prototype;

module.exports = genx;
3 changes: 3 additions & 0 deletions package.json
Expand Up @@ -23,6 +23,9 @@
}
],
"scripts": {
"clean": "(mkdir build || rm -rf build) && mkdir -p build",
"cmake": "cd build && cmake .. && make -j6",
"cmake-debug": "source ~/.nvm/nvm.sh && nvm use 4.2.3 && cd build && cmake -DCMAKE_BUILD_TYPE=Debug .. && make -j6",
"test": "mocha"
},
"optionalDependencies": {
Expand Down
91 changes: 71 additions & 20 deletions src/genx.cc
Expand Up @@ -15,6 +15,8 @@
#define False 0
#define STRLEN_XMLNS_COLON 6

#define NEWLINE "\n"
#define SPACER "\t"

/*******************************
* writer state
Expand Down Expand Up @@ -122,6 +124,9 @@ struct genxWriter_rec
const char * etext[100];
void * (* alloc)(void * userData, int bytes);
void (* dealloc)(void * userData, void * data);
unsigned int depth;
Boolean shouldNewline;
Boolean prettyPrint;
};

/*******************************
Expand Down Expand Up @@ -510,7 +515,7 @@ static Boolean isNameChar(genxWriter w, int c)
*/
genxWriter genxNew(void * (* alloc)(void * userData, int bytes),
void (* dealloc)(void * userData, void * data),
void * userData)
void * userData, Boolean prettyPrint)
{
genxWriter w;
genxNamespace xml;
Expand Down Expand Up @@ -586,6 +591,10 @@ genxWriter genxNew(void * (* alloc)(void * userData, int bytes),
xml->declCount = 1;
xml->declaration = xml->defaultDecl;

w->depth = 0;
w->shouldNewline = 0;
w->prettyPrint = prettyPrint;

return w;
}

Expand Down Expand Up @@ -1152,6 +1161,17 @@ static genxStatus writeStartTag(genxWriter w, bool inlineTag = 0)
unsetDefaultNamespace(w);
w->status = GENX_SUCCESS;

if(w->prettyPrint)
{
SendCheck(w, NEWLINE);
for(unsigned int tabs = 0; tabs < w->depth; ++tabs)
{
SendCheck(w, SPACER);
}
w->depth++;
w->shouldNewline = 0;
}

SendCheck(w, "<");
if (e->ns && (e->ns->declaration != w->xmlnsEquals))
{
Expand Down Expand Up @@ -1538,6 +1558,20 @@ genxStatus genxEndElement(genxWriter w)
;
e = (genxElement) w->stack.pointers[--i];

if(w->prettyPrint)
{
w->depth--;
// We only need to add a newline to closing tags that come right after other closing tags
if(w->shouldNewline)
{
SendCheck(w, NEWLINE);
for(unsigned int tabs = 0; tabs < w->depth; ++tabs)
{
SendCheck(w, SPACER);
}
}
w->shouldNewline = 1;
}
SendCheck(w, "</");
if (e->ns && e->ns->declaration != w->xmlnsEquals)
{
Expand Down Expand Up @@ -1567,29 +1601,29 @@ genxStatus genxEndElement(genxWriter w)
*/
if (ns->baroque)
{
i = w->stack.count;
while (i > 0)
{
while (w->stack.pointers[i] != NULL)
{
genxAttribute otherDecl = (genxAttribute) w->stack.pointers[i--];
genxNamespace otherNs = (genxNamespace) w->stack.pointers[i--];

if (otherNs == ns)
{
ns->declaration = otherDecl;
i = 0;
break;
}
}
i = w->stack.count;
while (i > 0)
{
while (w->stack.pointers[i] != NULL)
{
genxAttribute otherDecl = (genxAttribute) w->stack.pointers[i--];
genxNamespace otherNs = (genxNamespace) w->stack.pointers[i--];

/* skip NULL & element */
i -= 2;
}
if (otherNs == ns)
{
ns->declaration = otherDecl;
i = 0;
break;
}
}

/* skip NULL & element */
i -= 2;
}
}
ns->declCount--;
if (ns->declCount == 0)
ns->baroque = False;
ns->baroque = False;
}
}

Expand Down Expand Up @@ -1771,6 +1805,14 @@ genxStatus genxAddText(genxWriter w, constUtf8 start)
if (w->sequence != SEQUENCE_CONTENT)
return w->status = GENX_SEQUENCE_ERROR;

if(w->prettyPrint && w->shouldNewline)
{
SendCheck(w, NEWLINE);
for(unsigned int tabs = 0; tabs < w->depth; ++tabs)
{
SendCheck(w, SPACER);
}
}
while (*start)
{
int c = genxNextUnicodeChar(&start);
Expand Down Expand Up @@ -1912,6 +1954,15 @@ genxStatus genxComment(genxWriter w, constUtf8 text)
if ((w->status = sendx(w, (utf8) "\n")) != GENX_SUCCESS)
return w->status;

if(w->prettyPrint && w->shouldNewline)
{
SendCheck(w, NEWLINE);
for(unsigned int tabs = 0; tabs < w->depth; ++tabs)
{
SendCheck(w, SPACER);
}
}

if ((w->status = sendx(w, (utf8) "<!--")) != GENX_SUCCESS)
return w->status;
if ((w->status = sendx(w, (utf8) text)) != GENX_SUCCESS)
Expand Down
2 changes: 1 addition & 1 deletion src/genx.h
Expand Up @@ -78,7 +78,7 @@ typedef struct genxAttribute_rec * genxAttribute;
*/
genxWriter genxNew(void * (*alloc)(void * userData, int bytes),
void (* dealloc)(void * userData, void * data),
void * userData);
void * userData, int prettyPrint);

/*
* Dispose of a writer, freeing all associated memory
Expand Down
19 changes: 16 additions & 3 deletions src/writer.cc
Expand Up @@ -87,10 +87,10 @@ void Writer::Initialize(Local<Object> exports)
exports->Set(Nan::New("Writer").ToLocalChecked(), tpl->GetFunction());
}

Writer::Writer()
Writer::Writer(const bool prettyPrint)
{
// alloc, free, userData
writer = genxNew(NULL, NULL, this);
writer = genxNew(NULL, NULL, this, prettyPrint);
sender.send = sender_send;
sender.sendBounded = sender_sendBounded;
sender.flush = sender_flush;
Expand All @@ -103,7 +103,20 @@ Writer::~Writer()

void Writer::New(const Nan::FunctionCallbackInfo <Value> &args)
{
Writer* writer = new Writer();
bool prettyPrint = false;
switch(args.Length()) {
case 0: break;
case 1:
if(args[0]->IsBoolean()) {
prettyPrint = args[0]->ToBoolean()->Value();
break;
}
default:
Nan::ThrowTypeError("Constructor for Writer expects no arguments, or 1 argument of type Boolean");
return;
}

Writer* writer = new Writer(prettyPrint);
writer->Wrap(args.This());
args.GetReturnValue().Set(args.This());
}
Expand Down
2 changes: 1 addition & 1 deletion src/writer.h
Expand Up @@ -52,7 +52,7 @@ class Writer: public Nan::ObjectWrap
public:
static void Initialize(v8::Local<v8::Object> target);

Writer();
Writer(const bool prettyPrint);

~Writer();

Expand Down
67 changes: 67 additions & 0 deletions test/writer_test.js
Expand Up @@ -379,6 +379,73 @@ describe('genx', function(){
});
});

describe('generating a prettyprinted document', function() {
beforeEach(function() {
w = new genx.Writer(true);
});

describe('using literal nodes', function(){
it('generates the correct XML', function() {
var result = '';

w.on('data', function(data) {
result += data;
});

w.startDocument()
.addComment(' Testing ')
.startElementLiteral('http://www.w3.org/2005/Atom', 'feed')
.startElementLiteral('title').addAttributeLiteral('type', 'text')
.addText('Testing').endElement().endElement().endDocument();
result.should.equal("<!-- Testing -->\n\n<g1:feed xmlns:g1=\"http://www.w3.org/2005/Atom\">\n\t<title type=\"text\">Testing</title>\n</g1:feed>");
});

it('correctly spaces trailing text nodes', function() {
var result = '';

w.on('data', function(data) {
result += data;
});

w.startDocument()
.addComment(' Testing ')
.startElementLiteral('http://www.w3.org/2005/Atom', 'feed')
.startElementLiteral('title').addAttributeLiteral('type', 'text')
.addText('Testing')
.endElement()
.addText('Trailing Text')
.endElement()
.endDocument();
result.should.equal("<!-- Testing -->\n\n<g1:feed xmlns:g1=\"http://www.w3.org/2005/Atom\">\n\t" +
"<title type=\"text\">Testing</title>\n\tTrailing Text\n</g1:feed>");
});

it('correctly spaces comments', function() {
var result = '';

w.on('data', function(data) {
result += data;
});

w.startDocument()
.startElementLiteral('http://www.w3.org/2005/Atom', 'feed')
.startElementLiteral('title').addAttributeLiteral('type', 'text')
.addText('Testing')
.endElement()
.addComment(' Testing ')
.addText('Trailing Text')
.startElementLiteral('comment-only')
.addComment(' Testing ')
.endElement()
.endElement()
.endDocument();
result.should.equal("\n<g1:feed xmlns:g1=\"http://www.w3.org/2005/Atom\">\n\t" +
"<title type=\"text\">Testing</title>\n\t<!-- Testing -->\n\t" +
"Trailing Text\n\t<comment-only><!-- Testing --></comment-only>\n</g1:feed>");
});
});
});

it('can generate multiple documents', function() {
var result = '';

Expand Down

0 comments on commit ced2105

Please sign in to comment.