Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Rewrite of almost everything to fix matching

  • Loading branch information...
commit 9212893f4e1dd70a19eaa273e38d384c69022046 1 parent 689108f
Steven Looman authored
77 lib/child_state.js
... ... @@ -0,0 +1,77 @@
  1 +var debug = true;
  2 +function trace(message) {
  3 + if (debug) {
  4 + console.log(message);
  5 + }
  6 +}
  7 +
  8 +
  9 +function ChildState(axis, name, predicates) {
  10 + this.axis = 'child';
  11 + this.name = name;
  12 +
  13 + if (predicates) {
  14 + // strip '[' and ']'
  15 + this.predicates = predicates.slice(1).slice(0, -1);
  16 + }
  17 +}
  18 +
  19 +
  20 +ChildState.prototype.matches = function(node, depth) {
  21 + trace('/ match? node: ' + node.name + ' depth: ' + depth);
  22 + var match = this.matchesName(node) && this.matchesDepth(depth);
  23 + if (match && this.predicates) {
  24 + match = match && this.matchesPredicate(node);
  25 + }
  26 +
  27 + if (match) {
  28 + trace('/ match');
  29 +
  30 + this.enteredDepth = depth;
  31 + }
  32 +
  33 + return match;
  34 +};
  35 +
  36 +ChildState.prototype.unmatches = function(tag, depth) {
  37 + trace('/ unmatch? depth: ' + depth + ' enteredDepth: ' + this.enteredDepth);
  38 + var unmatch = depth <= this.enteredDepth;
  39 + if (unmatch) {
  40 + trace('/ unmatch');
  41 + }
  42 + return unmatch;
  43 +};
  44 +
  45 +ChildState.prototype.matchesDepth = function(depth) {
  46 + var parentDepth = this.previous.enteredDepth;
  47 + return depth === parentDepth + 1;
  48 +};
  49 +
  50 +ChildState.prototype.matchesName = function(node) {
  51 + return node.name === this.name;
  52 +};
  53 +
  54 +ChildState.prototype.matchesPredicate = function(node) {
  55 + // XXX: hardcoded to test @attr = literal
  56 + var predicate = this.predicates[0];
  57 +
  58 + var left = predicate[0];
  59 + var op = predicate[1];
  60 + var right = predicate[2];
  61 +
  62 + var lValue = node.attributes[left[1]];
  63 + var rValue = right;
  64 + if (op === '=') {
  65 + return lValue === rValue;
  66 + }
  67 +
  68 + return false;
  69 +};
  70 +
  71 +
  72 +ChildState.prototype.toString = function() {
  73 + return '/' + this.name;
  74 +};
  75 +
  76 +
  77 +module.exports = ChildState;
19 lib/recorder.js
... ... @@ -1,4 +1,13 @@
  1 +var debug = true;
  2 +function trace(message) {
  3 + if (debug) {
  4 + console.log(message);
  5 + }
  6 +}
  7 +
1 8 function Recorder() {
  9 + this.recording = false;
  10 +
2 11 // this.document = null;
3 12 // this.currentNode = null;
4 13 }
@@ -6,7 +15,7 @@ function Recorder() {
6 15
7 16 // XXX: move this to own class, bind to opentag, text, closetag evens and control recording with a boolean
8 17 Recorder.prototype.start = function() {
9   - console.log('start recording');
  18 + trace('start recording');
10 19
11 20 this.recording = true;
12 21
@@ -15,7 +24,7 @@ Recorder.prototype.start = function() {
15 24 };
16 25
17 26 Recorder.prototype.stop = function() {
18   - console.log('stop recording');
  27 + trace('stop recording');
19 28
20 29 this.recording = false;
21 30
@@ -33,7 +42,7 @@ Recorder.prototype.onOpenTag = function(node) {
33 42 return;
34 43 }
35 44
36   - console.log('record open tag');
  45 + trace('record open tag');
37 46
38 47 // var e = new Element(node.name);
39 48 //
@@ -51,7 +60,7 @@ Recorder.prototype.onCloseTag = function(tag) {
51 60 return;
52 61 }
53 62
54   - console.log('record close tag');
  63 + trace('record close tag');
55 64
56 65 // this.currentNode = this.currentNode.parentNode;
57 66 };
@@ -61,7 +70,7 @@ Recorder.prototype.onText = function(text) {
61 70 return;
62 71 }
63 72
64   - console.log('record text');
  73 + trace('record text');
65 74
66 75 // var e = new TextNode(test);
67 76 // this.currentNode.appendChild(e);
158 lib/saxpath.js
... ... @@ -1,12 +1,29 @@
1   -var util = require('util');
  1 +var util = require('util');
2 2 var events = require('events');
3 3
4 4 var XPathParser = require('./xpath_parser');
5   -var State = require('./state');
6   -var Recorder = require('./recorder');
  5 +var Recorder = require('./recorder');
7 6
  7 +var StartState = require('./start_state');
  8 +var ChildState = require('./child_state');
  9 +var SelfOrDescendantState = require('./self_or_descendant_state');
  10 +
  11 +var debug = true;
  12 +function trace(depth, message) {
  13 + var i;
  14 +
  15 + if (debug) {
  16 + var prefix = '';
  17 + for (i = 0; i < depth; ++i) {
  18 + prefix += ' ';
  19 + }
  20 + console.log(prefix + message);
  21 + }
  22 +}
8 23
9 24 function SaXPath(saxParser, xpath, recorder) {
  25 + this.currentDepth = 0;
  26 +
10 27 this.saxParser = saxParser;
11 28
12 29 this.saxParser.on('opentag', this.onOpenTag.bind(this));
@@ -21,105 +38,132 @@ function SaXPath(saxParser, xpath, recorder) {
21 38
22 39 this.xpathExpr = XPathParser.parse(xpath);
23 40 this.states = this.parseXPathExpr(this.xpathExpr);
24   -
25   - this.currentDepth = -1;
26   - this.currentState = 0;
  41 + this.currentState = this.states[0];
27 42 }
28 43
29 44
30 45 util.inherits(SaXPath, events.EventEmitter);
31 46
32 47
  48 +SaXPath.prototype.buildState = function(part) {
  49 + var axis = part[0];
  50 + var name = part[1];
  51 + var predicates = part[2];
  52 +
  53 + if (axis === '/') {
  54 + return new ChildState(axis, name, predicates);
  55 + } else if (axis === '//') {
  56 + return new SelfOrDescendantState(axis, name, predicates);
  57 + }
  58 +};
  59 +
  60 +
33 61 SaXPath.prototype.parseXPathExpr = function(expr) {
34 62 var stack = [];
35 63
  64 + var start = new StartState();
  65 + stack.push(start);
  66 + var previousState = start;
  67 +
36 68 var i;
37   - var previousState = null;
38 69 for (i = 0; i < expr.length; ++i) {
39 70 var part = expr[i];
40   - var state = new State(part);
  71 + var state = this.buildState(part);
41 72
42   - if (previousState) {
43   - state.previous = previousState;
44   - previousState.next = state;
45   - } else {
46   - state.previous = null;
47   - }
  73 + state.previous = previousState;
  74 + previousState.next = state;
48 75
49 76 stack.push(state);
50 77 previousState = state;
51 78 }
52 79
53   - // activate first state
54   - stack[0].enter(-1);
55   -
56 80 return stack;
57 81 };
58 82
59 83
60 84 SaXPath.prototype.onOpenTag = function(node) {
61 85 this.currentDepth += 1;
62   - console.log('=== open tag, node:', node, 'depth:', this.currentDepth, 'state:', this.currentState);
  86 + trace(this.currentDepth, '=== open tag, node: ' + node.name + ' depth: ' + this.currentDepth + ' current state: ' + this.currentState.toString());
63 87
64   - if (this.currentState < this.states.length) {
  88 + var state = this.currentState;
  89 + if (state.next) {
65 90 // still matching states
66   - var state = this.states[this.currentState];
67   - console.log('current state:', state.name);
  91 + if (state.next.matches(node, this.currentDepth)) {
  92 + // hop to next state
  93 + state = state.next;
68 94
69   - if (state.matches(node, this.currentDepth)) {
70   - // move forward a state
71   - this.currentState += 1;
72   -
73   - if (this.currentState !== this.states.length) {
74   - var nextState = this.states[this.currentState];
75   - nextState.enter(this.currentDepth);
  95 + // start recording if the top of the stack has been reached
  96 + if (!state.next) {
  97 + this.recorder.start();
76 98 }
77   - }
78   - }
79 99
80   - // have we reached the top of the state-stack?
81   - if (this.currentState === this.states.length && !this.recorder.isRecording()) {
82   - this.recorder.start();
  100 + this.currentState = state;
  101 + }
83 102 }
84 103 };
85 104
86 105 SaXPath.prototype.onCloseTag = function(tag) {
87   - this.currentDepth -= 1;
88   - console.log('=== close tag, node:', tag, 'depth:', this.currentDepth, 'state:', this.currentState);
  106 + trace(this.currentDepth, '=== close tag, node: ' + tag + ' depth: ' + this.currentDepth + ' current state: ' + this.currentState.toString());
89 107
90   - var state;
91   - if (this.currentState === this.states.length) {
92   - state = this.states[this.currentState - 1];
93   - if (state.unmatches(tag, this.currentDepth)) {
94   - state.leave(this.currentDepth);
  108 + var state = this.currentState;
  109 + var stopRecorder = !state.next;
95 110
96   - if (this.recorder.isRecording()) {
97   - this.recorder.stop();
98   - }
99   -
100   - // go back a state
101   - this.currentState -= 1;
  111 + if (state.unmatches(tag, this.currentDepth)) {
  112 + if (stopRecorder) {
  113 + this.recorder.stop();
102 114 }
  115 +
  116 + state = state.previous;
  117 + this.currentState = state;
103 118 }
104 119
105   - if (this.currentState < this.states.length) {
106   - state = this.states[this.currentState];
107   - if (state.unmatches(tag, this.currentDepth)) {
108   - state.leave(this.currentDepth);
  120 +// if (this.currentState === this.states.length) {
  121 +// // top of the state, recording nodes
  122 +// state = this.states[this.states.length - 1];
  123 +// if (state.unmatches(tag, this.currentDepth)) {
  124 +// this.recorder.stop();
  125 +// this.currentState -= 1;
  126 +// }
  127 +// } else {
  128 +// // still matching nodes
  129 +// state = this.states[this.currentState];
  130 +// if (state.unmatches(tag, this.currentDepth)) {
  131 +// this.currentState -= 1;
  132 +// }
  133 +// }
109 134
110   - // go back a state
111   - this.currentState -= 1;
112   - }
113   - }
  135 + this.currentDepth -= 1;
  136 +
  137 +// var state;
  138 +// var prev;
  139 +// if (this.currentState === this.states.length) {
  140 +// // at the top of the stack, recording
  141 +// state = this.states[this.currentState - 1];
  142 +//
  143 +// prev = state.previous;
  144 +// if (prev.unmatches(tag, this.currentDepth)) {
  145 +// this.recorder.stop();
  146 +//
  147 +// // go back a state
  148 +// this.currentState -= 1;
  149 +// }
  150 +// } else if (this.currentState < this.states.length) {
  151 +// state = this.states[this.currentState];
  152 +//
  153 +// prev = state.previous;
  154 +// if (state.unmatches(tag, this.currentDepth)) {
  155 +// // go back a state
  156 +// this.currentState -= 1;
  157 +// }
  158 +// }
114 159 };
115 160
116 161 SaXPath.prototype.onText = function(text) {
117   -// console.log('=== text', text);
118   - var state = this.states[this.currentState];
  162 +// trace(this.currentDepth, '=== text');
119 163 };
120 164
121 165 SaXPath.prototype.onEnd = function() {
122   - console.log('=== end');
  166 + trace(0, '=== end');
123 167 this.emit('end');
124 168 };
125 169
80 lib/self_or_descendant_state.js
... ... @@ -0,0 +1,80 @@
  1 +var debug = true;
  2 +function trace(message) {
  3 + if (debug) {
  4 + console.log(message);
  5 + }
  6 +}
  7 +
  8 +
  9 +function SelfOrDescendantState(axis, name, predicates) {
  10 + this.axis = 'self-or-descendant';
  11 + this.name = name;
  12 +
  13 + if (predicates) {
  14 + // strip '[' and ']'
  15 + this.predicates = predicates.slice(1).slice(0, -1);
  16 + }
  17 +}
  18 +
  19 +
  20 +//SelfOrDescendantState.prototype.enter = function(depth) {
  21 +// trace('// state enter, state: ' + this.name + ' depth: ' + depth);
  22 +// this.enteredDepth = depth;
  23 +//};
  24 +//
  25 +//SelfOrDescendantState.prototype.leave = function(depth) {
  26 +// trace('// state leave, state: ' + this.name + ' depth: ' + depth);
  27 +//};
  28 +
  29 +
  30 +SelfOrDescendantState.prototype.matches = function(node, depth) {
  31 + trace('// match? node: ' + node.name + ' depth: ' + depth);
  32 + var match = this.matchesName(node);
  33 + if (match && this.predicates) {
  34 + match = match && this.matchesPredicate(node);
  35 + }
  36 + if (match) {
  37 + trace('// match');
  38 +
  39 + this.enteredDepth = depth;
  40 + }
  41 + return match;
  42 +};
  43 +
  44 +SelfOrDescendantState.prototype.unmatches = function(tag, depth) {
  45 + trace('// unmatch? depth: ' + depth + ' enteredDepth: ' + this.enteredDepth);
  46 + var unmatch = depth <= this.enteredDepth;
  47 + if (unmatch) {
  48 + trace('// unmatch');
  49 + }
  50 + return unmatch;
  51 +};
  52 +
  53 +SelfOrDescendantState.prototype.matchesName = function(node) {
  54 + return node.name === this.name;
  55 +};
  56 +
  57 +SelfOrDescendantState.prototype.matchesPredicate = function(node) {
  58 + // XXX: hardcoded to test @attr = literal
  59 + var predicate = this.predicates[0];
  60 +
  61 + var left = predicate[0];
  62 + var op = predicate[1];
  63 + var right = predicate[2];
  64 +
  65 + var lValue = node.attributes[left[1]];
  66 + var rValue = right;
  67 + if (op === '=') {
  68 + return lValue === rValue;
  69 + }
  70 +
  71 + return false;
  72 +};
  73 +
  74 +
  75 +SelfOrDescendantState.prototype.toString = function() {
  76 + return '//' + this.name;
  77 +};
  78 +
  79 +
  80 +module.exports = SelfOrDescendantState;
32 lib/start_state.js
... ... @@ -0,0 +1,32 @@
  1 +var debug = true;
  2 +function trace(message) {
  3 + if (debug) {
  4 + console.log(message);
  5 + }
  6 +}
  7 +
  8 +
  9 +function StartState() {
  10 + this.axis = '0';
  11 + this.enteredDepth = 0;
  12 +}
  13 +
  14 +
  15 +StartState.prototype.matches = function(node, depth) {
  16 + trace('0 match? node: ' + node.name + ' depth: ' + depth);
  17 + trace('0 match');
  18 + return true;
  19 +};
  20 +
  21 +StartState.prototype.unmatches = function(tag, depth) {
  22 + trace('0 unmatch? depth: ' + depth + ' enteredDepth: ' + this.enteredDepth);
  23 + return false;
  24 +};
  25 +
  26 +
  27 +StartState.prototype.toString = function() {
  28 + return '0';
  29 +};
  30 +
  31 +
  32 +module.exports = StartState;
91 lib/state.js
... ... @@ -1,91 +0,0 @@
1   -function State(part) {
2   - var axis = part[0];
3   - var name = part[1];
4   - var predicates = part[2];
5   -
6   - if (axis === '/') {
7   - this.axis = 'child';
8   -
9   - if (name) {
10   - this.name = name;
11   - }
12   -
13   - if (predicates) {
14   - // strip '[' and ']'
15   - this.predicates = predicates.slice(1).slice(0, -1);
16   - }
17   - } else if (axis === '//') {
18   - this.axis = 'self-or-descendant';
19   -
20   - if (name) {
21   - this.name = name;
22   - }
23   -
24   - if (predicates) {
25   - // strip '[' and ']'
26   - this.predicates = predicates.slice(1).slice(0, -1);
27   - }
28   - }
29   -}
30   -
31   -
32   -State.prototype.enter = function(depth) {
33   - console.log('state enter, state:', this.name, 'depth:', depth);
34   - this.enteredDepth = depth;
35   -};
36   -
37   -State.prototype.leave = function(depth) {
38   - console.log('state leave, state:', this.name, 'depth:', depth);
39   - this.enteredDepth = null;
40   -};
41   -
42   -
43   -State.prototype.matches = function(node, depth) {
44   - var match = this.matchesName(node);
45   - if (match && this.predicates) {
46   - match = match && this.matchesPredicate(node);
47   - }
48   - if (match && this.axis === 'child') {
49   - match = match && this.matchesDepth(depth);
50   - }
51   - if (match) {
52   - console.log('match');
53   - }
54   - return match;
55   -};
56   -
57   -State.prototype.unmatches = function(tag, depth) {
58   - var unmatch = depth < this.enteredDepth;
59   - if (unmatch) {
60   - console.log('unmatch');
61   - }
62   - return unmatch;
63   -};
64   -
65   -State.prototype.matchesDepth = function(depth) {
66   - return depth === this.enteredDepth + 1;
67   -};
68   -
69   -State.prototype.matchesName = function(node) {
70   - return node.name === this.name;
71   -};
72   -
73   -State.prototype.matchesPredicate = function(node) {
74   - // XXX: hardcoded to test @attr = literal
75   - var predicate = this.predicates[0];
76   -
77   - var left = predicate[0];
78   - var op = predicate[1];
79   - var right = predicate[2];
80   -
81   - var lValue = node.attributes[left[1]];
82   - var rValue = right;
83   - if (op === '=') {
84   - return lValue === rValue;
85   - }
86   -
87   - return false;
88   -};
89   -
90   -
91   -module.exports = State;
39 test/recorder.js
... ... @@ -1,39 +0,0 @@
1   -function TestRecorder() {
2   - this.tape = [];
3   -
4   - this.tapeOn = false;
5   -}
6   -
7   -
8   -TestRecorder.prototype.start = function() {
9   - this.tapeOn = true;
10   -};
11   -
12   -TestRecorder.prototype.stop = function() {
13   - this.tapeOn = false;
14   -};
15   -
16   -TestRecorder.prototype.isRecording = function() {
17   - return this.tapeOn;
18   -};
19   -
20   -TestRecorder.prototype.onOpenTag = function(node) {
21   - if (this.tapeOn) {
22   - this.tape.push(node);
23   - }
24   -};
25   -
26   -TestRecorder.prototype.onCloseTag = function(tag) {
27   - if (this.tapeOn) {
28   - this.tape.push(tag);
29   - }
30   -};
31   -
32   -TestRecorder.prototype.onText = function (text) {
33   - if (this.tapeOn) {
34   - this.tape.push(text);
35   - }
36   -};
37   -
38   -
39   -module.exports = TestRecorder;
112 test/saxpath.js
... ... @@ -1,15 +1,38 @@
1 1 var assert = require('assert');
2   -var TestRecorder = require('./recorder');
  2 +var TapeRecorder = require('./tape_recorder');
3 3
4 4 var fs = require('fs');
5 5 var sax = require('sax');
6 6 var saxpath = require('..');
7 7
8 8
  9 +var eyes = require('eyes'); // XXX: DEBUG
  10 +
  11 +
9 12 describe('SaXPath', function() {
  13 + it('should match /bookstore', function(done) {
  14 + var fileStream = fs.createReadStream('test/bookstore.xml');
  15 + var recorder = new TapeRecorder();
  16 + var saxParser = sax.createStream(true);
  17 + var streamer = new saxpath.SaXPath(saxParser, '/bookstore', recorder);
  18 +
  19 + saxParser.on('end', testNodesRecorded);
  20 + fileStream.pipe(saxParser);
  21 +
  22 + function testNodesRecorded() {
  23 + assert.equal(recorder.box.length, 1);
  24 +
  25 + var tape = recorder.box[0];
  26 + assert.ok(tape.length > 0);
  27 + assert.equal(tape[1].openTag.name, 'bookstore');
  28 +
  29 + done();
  30 + }
  31 + });
  32 +
10 33 it('should match /bookstore/book', function(done) {
11 34 var fileStream = fs.createReadStream('test/bookstore.xml');
12   - var recorder = new TestRecorder();
  35 + var recorder = new TapeRecorder();
13 36 var saxParser = sax.createStream(true);
14 37 var streamer = new saxpath.SaXPath(saxParser, '/bookstore/book', recorder);
15 38
@@ -17,15 +40,23 @@ describe('SaXPath', function() {
17 40 fileStream.pipe(saxParser);
18 41
19 42 function testNodesRecorded() {
20   - assert.ok(recorder.tape.length > 0);
21   - assert.equal(recorder.tape[0].name, 'book');
  43 + assert.equal(recorder.box.length, 4);
  44 +
  45 + var tape;
  46 + var i;
  47 + for (i = 0; i < 4; ++i) {
  48 + tape = recorder.box[i];
  49 + assert.ok(tape.length > 0);
  50 + assert.equal(tape[1].openTag.name, 'book');
  51 + }
  52 +
22 53 done();
23 54 }
24 55 });
25 56
26 57 it('should match /bookstore/book[@category="COOKING"]', function(done) {
27 58 var fileStream = fs.createReadStream('test/bookstore.xml');
28   - var recorder = new TestRecorder();
  59 + var recorder = new TapeRecorder();
29 60 var saxParser = sax.createStream(true);
30 61 var streamer = new saxpath.SaXPath(saxParser, '/bookstore/book[@category="COOKING"]', recorder);
31 62
@@ -33,16 +64,23 @@ describe('SaXPath', function() {
33 64 fileStream.pipe(saxParser);
34 65
35 66 function testNodesRecorded() {
36   - assert.ok(recorder.tape.length > 0);
37   - assert.equal(recorder.tape[0].name, 'book');
38   - assert.deepEqual(recorder.tape[0].attributes, { category: 'COOKING' });
  67 + assert.equal(recorder.box.length, 1);
  68 +
  69 + var tape;
  70 + var i;
  71 + for (i = 0; i < 1; ++i) {
  72 + tape = recorder.box[i];
  73 + assert.ok(tape.length > 0);
  74 + assert.equal(tape[1].openTag.name, 'book');
  75 + }
  76 +
39 77 done();
40 78 }
41 79 });
42 80
43 81 it('should not match /bookstore/title', function(done) {
44 82 var fileStream = fs.createReadStream('test/bookstore.xml');
45   - var recorder = new TestRecorder();
  83 + var recorder = new TapeRecorder();
46 84 var saxParser = sax.createStream(true);
47 85 var streamer = new saxpath.SaXPath(saxParser, '/bookstore/title', recorder);
48 86
@@ -50,14 +88,14 @@ describe('SaXPath', function() {
50 88 fileStream.pipe(saxParser);
51 89
52 90 function testNodesRecorded() {
53   - assert.ok(recorder.tape.length === 0);
  91 + assert.equal(recorder.box.length, 0);
54 92 done();
55 93 }
56 94 });
57 95
58 96 it('should match //book', function(done) {
59 97 var fileStream = fs.createReadStream('test/bookstore.xml');
60   - var recorder = new TestRecorder();
  98 + var recorder = new TapeRecorder();
61 99 var saxParser = sax.createStream(true);
62 100 var streamer = new saxpath.SaXPath(saxParser, '//book', recorder);
63 101
@@ -65,15 +103,23 @@ describe('SaXPath', function() {
65 103 fileStream.pipe(saxParser);
66 104
67 105 function testNodesRecorded() {
68   - assert.ok(recorder.tape.length > 0);
69   - assert.equal(recorder.tape[0].name, 'book');
  106 + assert.equal(recorder.box.length, 4);
  107 +
  108 + var tape;
  109 + var i;
  110 + for (i = 0; i < 4; ++i) {
  111 + tape = recorder.box[i];
  112 + assert.ok(tape.length > 0);
  113 + assert.equal(tape[1].openTag.name, 'book');
  114 + }
  115 +
70 116 done();
71 117 }
72 118 });
73 119
74 120 it('should match //book[@category="COOKING"]', function(done) {
75 121 var fileStream = fs.createReadStream('test/bookstore.xml');
76   - var recorder = new TestRecorder();
  122 + var recorder = new TapeRecorder();
77 123 var saxParser = sax.createStream(true);
78 124 var streamer = new saxpath.SaXPath(saxParser, '//book[@category="COOKING"]', recorder);
79 125
@@ -81,16 +127,20 @@ describe('SaXPath', function() {
81 127 fileStream.pipe(saxParser);
82 128
83 129 function testNodesRecorded() {
84   - assert.ok(recorder.tape.length > 0);
85   - assert.equal(recorder.tape[0].name, 'book');
86   - assert.deepEqual(recorder.tape[0].attributes, { category: 'COOKING' });
  130 + assert.equal(recorder.box.length, 1);
  131 +
  132 + var tape = recorder.box[0];
  133 + assert.ok(tape.length > 0);
  134 + assert.equal(tape[1].openTag.name, 'book');
  135 + assert.deepEqual(tape[1].openTag.attributes, { category: 'COOKING' });
  136 +
87 137 done();
88 138 }
89 139 });
90 140
91 141 it('should match //book/title', function(done) {
92 142 var fileStream = fs.createReadStream('test/bookstore.xml');
93   - var recorder = new TestRecorder();
  143 + var recorder = new TapeRecorder();
94 144 var saxParser = sax.createStream(true);
95 145 var streamer = new saxpath.SaXPath(saxParser, '//book/title', recorder);
96 146
@@ -98,15 +148,23 @@ describe('SaXPath', function() {
98 148 fileStream.pipe(saxParser);
99 149
100 150 function testNodesRecorded() {
101   - assert.ok(recorder.tape.length > 0);
102   - assert.equal(recorder.tape[0].name, 'title');
  151 + assert.equal(recorder.box.length, 4);
  152 +
  153 + var tape;
  154 + var i;
  155 + for (i = 0; i < 4; ++i) {
  156 + tape = recorder.box[i];
  157 + assert.ok(tape.length > 0);
  158 + assert.equal(tape[1].openTag.name, 'title');
  159 + }
  160 +
103 161 done();
104 162 }
105 163 });
106 164
107 165 it('should match //book//title', function(done) {
108 166 var fileStream = fs.createReadStream('test/bookstore.xml');
109   - var recorder = new TestRecorder();
  167 + var recorder = new TapeRecorder();
110 168 var saxParser = sax.createStream(true);
111 169 var streamer = new saxpath.SaXPath(saxParser, '//book//title', recorder);
112 170
@@ -114,8 +172,16 @@ describe('SaXPath', function() {
114 172 fileStream.pipe(saxParser);
115 173
116 174 function testNodesRecorded() {
117   - assert.ok(recorder.tape.length > 0);
118   - assert.equal(recorder.tape[0].name, 'title');
  175 + assert.equal(recorder.box.length, 4);
  176 +
  177 + var tape;
  178 + var i;
  179 + for (i = 0; i < 4; ++i) {
  180 + tape = recorder.box[i];
  181 + assert.ok(tape.length > 0);
  182 + assert.equal(tape[1].openTag.name, 'title');
  183 + }
  184 +
119 185 done();
120 186 }
121 187 });
54 test/tape_recorder.js
... ... @@ -0,0 +1,54 @@
  1 +// XXX: TODO: rename to TapeRecorder
  2 +function TapeRecorder() {
  3 + this.box = [];
  4 + this.tape = null;
  5 +
  6 + this.tapeOn = false;
  7 +}
  8 +
  9 +
  10 +TapeRecorder.prototype.start = function() {
  11 + console.log('o_o TapeRecorder.start');
  12 + if (this.tapeOn) {
  13 + throw new Error("Tape is already on");
  14 + }
  15 +
  16 + this.tapeOn = true;
  17 +
  18 + this.tape = [];
  19 + this.tape.push({ start: true });
  20 +};
  21 +
  22 +TapeRecorder.prototype.stop = function() {
  23 + console.log('o_o TapeRecorder.stop');
  24 + this.tapeOn = false;
  25 +
  26 + this.tape.push({ stop: true });
  27 + this.box.push(this.tape);
  28 + this.tape = null;
  29 +};
  30 +
  31 +TapeRecorder.prototype.isRecording = function() {
  32 + return this.tapeOn;
  33 +};
  34 +
  35 +TapeRecorder.prototype.onOpenTag = function(node) {
  36 + if (this.tapeOn) {
  37 + this.tape.push({ openTag: node });
  38 + }
  39 +};
  40 +
  41 +TapeRecorder.prototype.onCloseTag = function(tag) {
  42 + if (this.tapeOn) {
  43 + this.tape.push({ closeTag: tag });
  44 + }
  45 +};
  46 +
  47 +TapeRecorder.prototype.onText = function (text) {
  48 + if (this.tapeOn) {
  49 + this.tape.push({ text: text });
  50 + }
  51 +};
  52 +
  53 +
  54 +module.exports = TapeRecorder;

0 comments on commit 9212893

Please sign in to comment.
Something went wrong with that request. Please try again.