]> git.proxmox.com Git - extjs.git/blame - extjs/packages/core/src/util/DelimitedValue.js
add extjs 6.0.1 sources
[extjs.git] / extjs / packages / core / src / util / DelimitedValue.js
CommitLineData
6527f429
DM
1/**\r
2 * This base class contains utility methods for dealing with formats such as CSV (Comma\r
3 * Separated Values) as specified in <a href="http://tools.ietf.org/html/rfc4180">RFC 4180</a>.\r
4 *\r
5 * The base class implements the mechanics and is governed by these config options:\r
6 *\r
7 * * `{@link #delimiter}`\r
8 * * `{@link #lineBreak}`\r
9 * * `{@link #quote}`\r
10 *\r
11 * These options affect the `{@link #method-encode}` and `{@link #method-decode}` methods.\r
12 * When *decoding*, however, `{@link #lineBreak}` is ignored and instead each line can\r
13 * be separated by any standard line terminator character or character sequence:\r
14 *\r
15 * * ```\u000a```\r
16 * * ```\u000d```\r
17 * * ```\u000d\u000a```\r
18 *\r
19 * Strings which contain the {@link #delimiter} character are quoted using the\r
20 * {@link #quote} character, and any internal {@link #quote} characters are doubled.\r
21 *\r
22 * *Important*\r
23 * While the primary use case is to encode strings, other atomic data types can be encoded\r
24 * as values within a line such as:\r
25 *\r
26 * * Number\r
27 * * Boolean\r
28 * * Date (encoded as an <a href="http://www.iso.org/iso/home/standards/iso8601.htm">ISO 8601</a> date string.)\r
29 * * null (encoded as an empty string.)\r
30 * * undefined (encoded as an empty string.)\r
31 *\r
32 * Not that when *decoding*, all data is read as strings. This class does not convert\r
33 * incoming data. To do that, use an {@link Ext.data.reader.Array ArrayReader}.\r
34 *\r
35 * See `{@link Ext.util.CSV}` and `{@link Ext.util.TSV}` for pre-configured instances.\r
36 *\r
37 * @since 5.1.0\r
38 */\r
39Ext.define('Ext.util.DelimitedValue', {\r
40 /**\r
41 * @cfg {String} dateFormat\r
42 * The {@link Ext.Date#format format} to use for dates\r
43 */\r
44 dateFormat: 'C',\r
45\r
46 /**\r
47 * @cfg {String} delimiter\r
48 * The string used to separate the values in a row. Common values for this config\r
49 * are comma (",") and tab ("\t"). See `{@link Ext.util.CSV}` and `{@link Ext.util.TSV}`\r
50 * for pre-configured instances of these formats.\r
51 */\r
52 delimiter: '\t',\r
53\r
54 /**\r
55 * @cfg {String} lineBreak\r
56 * The string used by `{@link #encode}` to separate each row. The `{@link #decode}`\r
57 * method accepts all forms of line break.\r
58 */\r
59 lineBreak: '\n',\r
60\r
61 /**\r
62 * @cfg {String} quote\r
63 * The character to use as to quote values that contain the special `delimiter`\r
64 * or `{@link #lineBreak}` characters.\r
65 */\r
66 quote: '"',\r
67 \r
68 parseREs: {},\r
69 quoteREs: {},\r
70\r
71 lineBreakRe: /\r?\n/g,\r
72\r
73 constructor: function (config) {\r
74 if (config) {\r
75 Ext.apply(this, config);\r
76 }\r
77 },\r
78\r
79 /**\r
80 * Decodes a string of encoded values into an array of rows. Each row is an array of\r
81 * strings.\r
82 *\r
83 * Note that this function does not convert the string values in each column into\r
84 * other data types. To do that, use an {@link Ext.data.reader.Array ArrayReader}.\r
85 *\r
86 * For example:\r
87 *\r
88 * Ext.util.CSV.decode('"foo ""bar"", bletch",Normal String,2010-01-01T21:45:32.004Z\u000a3.141592653589793,1,false');\r
89 *\r
90 * produces the following array of string arrays:\r
91 *\r
92 * [\r
93 * ['foo "bar", bletch','Normal String', '2010-01-01T21:45:32.004Z'],\r
94 * ['3.141592653589793', '1', 'false']\r
95 * ]\r
96 *\r
97 * @param {String} input The string to parse.\r
98 *\r
99 * @param {String} [delimiter] The column delimiter to use if the default value\r
100 * of {@link #cfg-delimiter delimiter} is not desired.\r
101 *\r
102 * @return {String[][]} An array of rows where each row is an array of Strings.\r
103 */\r
104 decode: function (input, delimiter) {\r
105 var me = this,\r
106 // Check to see if the column delimiter is defined. If not,\r
107 // then default to comma.\r
108 delim = (delimiter || me.delimiter),\r
109 row = [],\r
110 result = [row],\r
111 quote = me.quote,\r
112 quoteREs = me.quoteREs,\r
113 parseREs = me.parseREs,\r
114\r
115 // Create a regular expression to parse the CSV values unless we already have\r
116 // one for this delimiter.\r
117 parseRE = parseREs[delim] ||\r
118 (parseREs[delim] = new RegExp(\r
119 // Delimiters.\r
120 "(\\" + delim + "|\\r?\\n|\\r|^)" +\r
121\r
122 // Quoted fields.\r
123 "(?:\\" + quote + "([^\\" + quote + "]*(?:\\" + quote + "\\" + quote +\r
124 "[^\\" + quote + "]*)*)\\" + quote + "|" +\r
125\r
126 // Standard fields.\r
127 "([^\"\\" + delim + "\\r\\n]*))",\r
128 "gi")),\r
129\r
130 dblQuoteRE = quoteREs[quote] ||\r
131 (quoteREs[quote] = new RegExp('\\' + quote + '\\' + quote, 'g')),\r
132\r
133 arrMatches, strMatchedDelimiter, strMatchedValue;\r
134\r
135 // Keep looping over the regular expression matches\r
136 // until we can no longer find a match.\r
137 while (arrMatches = parseRE.exec(input)) {\r
138 strMatchedDelimiter = arrMatches[1];\r
139\r
140 // Check to see if the given delimiter has a length\r
141 // (is not the start of string) and if it matches\r
142 // field delimiter. If id does not, then we know\r
143 // that this delimiter is a row delimiter.\r
144 if (strMatchedDelimiter.length && strMatchedDelimiter !== delim) {\r
145 // Since we have reached a new row of data,\r
146 // add an empty row to our data array.\r
147 result.push(row = []);\r
148 }\r
149\r
150 // Now that we have our delimiter out of the way,\r
151 // let's check to see which kind of value we\r
152 // captured (quoted or unquoted).\r
153 if (arrMatches[2]) {\r
154 // We found a quoted value. When we capture\r
155 // this value, unescape any double quotes.\r
156 strMatchedValue = arrMatches[2].replace(dblQuoteRE, '"');\r
157 } else {\r
158 // We found a non-quoted value.\r
159 strMatchedValue = arrMatches[3];\r
160 }\r
161\r
162 row.push(strMatchedValue);\r
163 }\r
164\r
165 return result;\r
166 },\r
167\r
168 /**\r
169 * Converts a two-dimensional array into an encoded string.\r
170 *\r
171 * For example:\r
172 *\r
173 * Ext.util.CSV.encode([\r
174 * ['foo "bar", bletch', 'Normal String', new Date()],\r
175 * [Math.PI, 1, false]\r
176 * ]);\r
177 *\r
178 * The above produces the following string:\r
179 *\r
180 * '"foo ""bar"", bletch",Normal String,2010-01-01T21:45:32.004Z\u000a3.141592653589793,1,false'\r
181 *\r
182 * @param {Mixed[][]} input An array of row data arrays.\r
183 *\r
184 * @param {String} [delimiter] The column delimiter to use if the default value\r
185 * of {@link #cfg-delimiter delimiter} is not desired.\r
186 *\r
187 * @return {String} A string in which data items are separated by {@link #delimiter}\r
188 * characters, and rows are separated by {@link #lineBreak} characters.\r
189 */\r
190 encode: function (input, delimiter) {\r
191 var me = this,\r
192 delim = delimiter || me.delimiter,\r
193 dateFormat = me.dateFormat,\r
194 quote = me.quote,\r
195 twoQuotes = quote + quote,\r
196 rowIndex = input.length,\r
197 lineBreakRe = me.lineBreakRe,\r
198 result = [],\r
199 outputRow = [],\r
200 col, columnIndex, inputRow;\r
201\r
202 while (rowIndex-- > 0) {\r
203 inputRow = input[rowIndex];\r
204 outputRow.length = columnIndex = inputRow.length;\r
205\r
206 while (columnIndex-- > 0) {\r
207 col = inputRow[columnIndex];\r
208\r
209 if (col == null) { // == null || === undefined\r
210 col = '';\r
211 } else if (typeof col === 'string') {\r
212 if (col) {\r
213 // If the value contains quotes, double them up, and wrap with quotes\r
214 if (col.indexOf(quote) > -1) {\r
215 col = quote + col.split(quote).join(twoQuotes) + quote;\r
216 } else if (col.indexOf(delim) > -1 || lineBreakRe.test(col)) {\r
217 col = quote + col + quote;\r
218 }\r
219 }\r
220 } else if (Ext.isDate(col)) {\r
221 col = Ext.Date.format(col, dateFormat);\r
222 }\r
223 //<debug>\r
224 else if (col && (isNaN(col) || Ext.isArray(col))) {\r
225 Ext.raise('Cannot serialize ' + Ext.typeOf(col) + ' into CSV');\r
226 }\r
227 //</debug>\r
228\r
229 outputRow[columnIndex] = col;\r
230 }\r
231\r
232 result[rowIndex] = outputRow.join(delim);\r
233 }\r
234\r
235 return result.join(me.lineBreak);\r
236 }\r
237});