summaryrefslogtreecommitdiffstats
path: root/dgbuilder/core_nodes/parsers/70-CSV.js
blob: 56aa7c722082db524a84d8a327aab03adfcb3c23 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
/**
 * Copyright 2014 IBM Corp.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 **/

module.exports = function(RED) {
    "use strict";
    function CSVNode(n) {
        RED.nodes.createNode(this,n);
        this.template = n.temp.split(",");
        this.sep = (n.sep || ',').replace("\\t","\t").replace("\\n","\n").replace("\\r","\r");
        this.quo = '"';
        this.ret = (n.ret || "\n").replace("\\n","\n").replace("\\r","\r");
        this.winflag = (this.ret === "\r\n");
        this.lineend = "\n";
        this.multi = n.multi || "one";
        this.hdrin = n.hdrin || false;
        this.hdrout = n.hdrout || false;
        this.goodtmpl = true;
        var node = this;

        // pass in an array of column names to be trimed, de-quoted and retrimed
        var clean = function(col) {
            for (var t = 0; t < col.length; t++) {
                col[t] = col[t].trim(); // remove leading and trailing whitespace
                if (col[t].charAt(0) === '"' && col[t].charAt(col[t].length -1) === '"') {
                    // remove leading and trailing quotes (if they exist) - and remove whitepace again.
                    col[t] = col[t].substr(1,col[t].length -2).trim();
                }
            }
            if ((col.length === 1) && (col[0] === "")) { node.goodtmpl = false; }
            else { node.goodtmpl = true; }
            return col;
        }
        node.template = clean(node.template);

        this.on("input", function(msg) {
            if (msg.hasOwnProperty("payload")) {
                if (typeof msg.payload == "object") { // convert object to CSV string
                    try {
                        var ou = "";
                        if (node.hdrout) {
                            ou += node.template.join(node.sep) + node.ret;
                        }
                        if (!Array.isArray(msg.payload)) { msg.payload = [ msg.payload ]; }
                        for (var s = 0; s < msg.payload.length; s++) {
                            for (var t=0; t < node.template.length; t++) {

                                // aaargh - resorting to eval here - but fairly contained front and back.
                                var p = RED.util.ensureString(eval("msg.payload[s]."+node.template[t]));

                                if (p === "undefined") { p = ""; }
                                if (p.indexOf(node.sep) != -1) { // add quotes if any "commas"
                                    ou += node.quo + p + node.quo + node.sep;
                                }
                                else if (p.indexOf(node.quo) != -1) { // add double quotes if any quotes
                                    p = p.replace(/"/g, '""');
                                    ou += node.quo + p + node.quo + node.sep;
                                }
                                else { ou += p + node.sep; } // otherwise just add
                            }
                            ou = ou.slice(0,-1) + node.ret; // remove final "comma" and add "newline"
                        }
                        node.send({payload:ou});
                    }
                    catch(e) { node.log(e); }
                }
                else if (typeof msg.payload == "string") { // convert CSV string to object
                    try {
                        var f = true; // flag to indicate if inside or outside a pair of quotes true = outside.
                        var j = 0; // pointer into array of template items
                        var k = [""]; // array of data for each of the template items
                        var o = {}; // output object to build up
                        var a = []; // output array is needed for multiline option
                        var first = true; // is this the first line
                        var tmp = "";

                        // For now we are just going to assume that any \r or \n means an end of line...
                        //   got to be a weird csv that has singleton \r \n in it for another reason...

                        // Now process the whole file/line
                        for (var i = 0; i < msg.payload.length; i++) {
                            if ((node.hdrin === true) && first) { // if the template is in the first line
                                if ((msg.payload[i] === "\n")||(msg.payload[i] === "\r")) { // look for first line break
                                    node.template = clean(tmp.split(node.sep));
                                    first = false;
                                }
                                else { tmp += msg.payload[i]; }
                            }
                            else {
                                if (msg.payload[i] === node.quo) { // if it's a quote toggle inside or outside
                                    f = !f;
                                    if (msg.payload[i-1] === node.quo) { k[j] += '\"'; } // if it's a quotequote then it's actually a quote
                                }
                                else if ((msg.payload[i] === node.sep) && f) { // if we are outside of quote (ie valid separator
                                    if (!node.goodtmpl) { node.template[j] = "col"+(j+1); }
                                    if ( node.template[j] && (node.template[j] !== "") && (k[j] !== "" ) ) {
                                        if (!isNaN(Number(k[j]))) { k[j] = Number(k[j]); }
                                        o[node.template[j]] = k[j];
                                    }
                                    j += 1;
                                    k[j] = "";
                                }
                                else if (f && ((msg.payload[i] === "\n") || (msg.payload[i] === "\r"))) { // handle multiple lines
                                    //console.log(j,k,o,k[j]);
                                    if ( node.template[j] && (node.template[j] !== "") && (k[j] !== "") ) {
                                        if (!isNaN(Number(k[j]))) { k[j] = Number(k[j]); }
                                        else { k[j].replace(/\r$/,''); }
                                        o[node.template[j]] = k[j];
                                    }
                                    if (JSON.stringify(o) !== "{}") { // don't send empty objects
                                        if (node.multi === "one") { node.send({payload:o}); } // either send
                                        else { a.push(o); } // or add to the array
                                    }
                                    j = 0;
                                    k = [""];
                                    o = {};
                                }
                                else { // just add to the part of the message
                                    k[j] += msg.payload[i];
                                }
                            }
                        }
                        // Finished so finalize and send anything left
                        //console.log(j,k,o,k[j]);
                        if (!node.goodtmpl) { node.template[j] = "col"+(j+1); }
                        if ( node.template[j] && (node.template[j] !== "") && (k[j] !== "") ) {
                            if (!isNaN(Number(k[j]))) { k[j] = Number(k[j]); }
                            else { k[j].replace(/\r$/,''); }
                            o[node.template[j]] = k[j];
                        }
                        msg.payload = o;
                        if (JSON.stringify(o) !== "{}") { // don't send empty objects
                            if (node.multi === "one") { node.send({payload:o}); } // either send
                            else { a.push(o); } // or add to the aray
                        }
                        if (node.multi !== "one") { node.send({payload:a}); } // finally send the array
                    }
                    catch(e) { node.log(e); }
                }
                else { node.log("This node only handles csv strings or js objects."); }
            }
        });
    }
    RED.nodes.registerType("csv",CSVNode);
}