messengercustom-servicesmacoslinuxwindowsinboxwhatsappicloudtweetdeckhipchattelegramhangoutsslackgmailskypefacebook-workplaceoutlookemailmicrosoft-teamsdiscord
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
238 lines
8.4 KiB
238 lines
8.4 KiB
9 years ago
|
/**
|
||
|
* This base class contains utility methods for dealing with formats such as CSV (Comma
|
||
|
* Separated Values) as specified in <a href="http://tools.ietf.org/html/rfc4180">RFC 4180</a>.
|
||
|
*
|
||
|
* The base class implements the mechanics and is governed by these config options:
|
||
|
*
|
||
|
* * `{@link #delimiter}`
|
||
|
* * `{@link #lineBreak}`
|
||
|
* * `{@link #quote}`
|
||
|
*
|
||
|
* These options affect the `{@link #method-encode}` and `{@link #method-decode}` methods.
|
||
|
* When *decoding*, however, `{@link #lineBreak}` is ignored and instead each line can
|
||
|
* be separated by any standard line terminator character or character sequence:
|
||
|
*
|
||
|
* * ```\u000a```
|
||
|
* * ```\u000d```
|
||
|
* * ```\u000d\u000a```
|
||
|
*
|
||
|
* Strings which contain the {@link #delimiter} character are quoted using the
|
||
|
* {@link #quote} character, and any internal {@link #quote} characters are doubled.
|
||
|
*
|
||
|
* *Important*
|
||
|
* While the primary use case is to encode strings, other atomic data types can be encoded
|
||
|
* as values within a line such as:
|
||
|
*
|
||
|
* * Number
|
||
|
* * Boolean
|
||
|
* * Date (encoded as an <a href="http://www.iso.org/iso/home/standards/iso8601.htm">ISO 8601</a> date string.)
|
||
|
* * null (encoded as an empty string.)
|
||
|
* * undefined (encoded as an empty string.)
|
||
|
*
|
||
|
* Not that when *decoding*, all data is read as strings. This class does not convert
|
||
|
* incoming data. To do that, use an {@link Ext.data.reader.Array ArrayReader}.
|
||
|
*
|
||
|
* See `{@link Ext.util.CSV}` and `{@link Ext.util.TSV}` for pre-configured instances.
|
||
|
*
|
||
|
* @since 5.1.0
|
||
|
*/
|
||
|
Ext.define('Ext.util.DelimitedValue', {
|
||
|
/**
|
||
|
* @cfg {String} dateFormat
|
||
|
* The {@link Ext.Date#format format} to use for dates
|
||
|
*/
|
||
|
dateFormat: 'C',
|
||
|
|
||
|
/**
|
||
|
* @cfg {String} delimiter
|
||
|
* The string used to separate the values in a row. Common values for this config
|
||
|
* are comma (",") and tab ("\t"). See `{@link Ext.util.CSV}` and `{@link Ext.util.TSV}`
|
||
|
* for pre-configured instances of these formats.
|
||
|
*/
|
||
|
delimiter: '\t',
|
||
|
|
||
|
/**
|
||
|
* @cfg {String} lineBreak
|
||
|
* The string used by `{@link #encode}` to separate each row. The `{@link #decode}`
|
||
|
* method accepts all forms of line break.
|
||
|
*/
|
||
|
lineBreak: '\n',
|
||
|
|
||
|
/**
|
||
|
* @cfg {String} quote
|
||
|
* The character to use as to quote values that contain the special `delimiter`
|
||
|
* or `{@link #lineBreak}` characters.
|
||
|
*/
|
||
|
quote: '"',
|
||
|
|
||
|
parseREs: {},
|
||
|
quoteREs: {},
|
||
|
|
||
|
lineBreakRe: /\r?\n/g,
|
||
|
|
||
|
constructor: function (config) {
|
||
|
if (config) {
|
||
|
Ext.apply(this, config);
|
||
|
}
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Decodes a string of encoded values into an array of rows. Each row is an array of
|
||
|
* strings.
|
||
|
*
|
||
|
* Note that this function does not convert the string values in each column into
|
||
|
* other data types. To do that, use an {@link Ext.data.reader.Array ArrayReader}.
|
||
|
*
|
||
|
* For example:
|
||
|
*
|
||
|
* Ext.util.CSV.decode('"foo ""bar"", bletch",Normal String,2010-01-01T21:45:32.004Z\u000a3.141592653589793,1,false');
|
||
|
*
|
||
|
* produces the following array of string arrays:
|
||
|
*
|
||
|
* [
|
||
|
* ['foo "bar", bletch','Normal String', '2010-01-01T21:45:32.004Z'],
|
||
|
* ['3.141592653589793', '1', 'false']
|
||
|
* ]
|
||
|
*
|
||
|
* @param {String} input The string to parse.
|
||
|
*
|
||
|
* @param {String} [delimiter] The column delimiter to use if the default value
|
||
|
* of {@link #cfg-delimiter delimiter} is not desired.
|
||
|
*
|
||
|
* @return {String[][]} An array of rows where each row is an array of Strings.
|
||
|
*/
|
||
|
decode: function (input, delimiter) {
|
||
|
var me = this,
|
||
|
// Check to see if the column delimiter is defined. If not,
|
||
|
// then default to comma.
|
||
|
delim = (delimiter || me.delimiter),
|
||
|
row = [],
|
||
|
result = [row],
|
||
|
quote = me.quote,
|
||
|
quoteREs = me.quoteREs,
|
||
|
parseREs = me.parseREs,
|
||
|
|
||
|
// Create a regular expression to parse the CSV values unless we already have
|
||
|
// one for this delimiter.
|
||
|
parseRE = parseREs[delim] ||
|
||
|
(parseREs[delim] = new RegExp(
|
||
|
// Delimiters.
|
||
|
"(\\" + delim + "|\\r?\\n|\\r|^)" +
|
||
|
|
||
|
// Quoted fields.
|
||
|
"(?:\\" + quote + "([^\\" + quote + "]*(?:\\" + quote + "\\" + quote +
|
||
|
"[^\\" + quote + "]*)*)\\" + quote + "|" +
|
||
|
|
||
|
// Standard fields.
|
||
|
"([^\"\\" + delim + "\\r\\n]*))",
|
||
|
"gi")),
|
||
|
|
||
|
dblQuoteRE = quoteREs[quote] ||
|
||
|
(quoteREs[quote] = new RegExp('\\' + quote + '\\' + quote, 'g')),
|
||
|
|
||
|
arrMatches, strMatchedDelimiter, strMatchedValue;
|
||
|
|
||
|
// Keep looping over the regular expression matches
|
||
|
// until we can no longer find a match.
|
||
|
while (arrMatches = parseRE.exec(input)) {
|
||
|
strMatchedDelimiter = arrMatches[1];
|
||
|
|
||
|
// Check to see if the given delimiter has a length
|
||
|
// (is not the start of string) and if it matches
|
||
|
// field delimiter. If id does not, then we know
|
||
|
// that this delimiter is a row delimiter.
|
||
|
if (strMatchedDelimiter.length && strMatchedDelimiter !== delim) {
|
||
|
// Since we have reached a new row of data,
|
||
|
// add an empty row to our data array.
|
||
|
result.push(row = []);
|
||
|
}
|
||
|
|
||
|
// Now that we have our delimiter out of the way,
|
||
|
// let's check to see which kind of value we
|
||
|
// captured (quoted or unquoted).
|
||
|
if (arrMatches[2]) {
|
||
|
// We found a quoted value. When we capture
|
||
|
// this value, unescape any double quotes.
|
||
|
strMatchedValue = arrMatches[2].replace(dblQuoteRE, '"');
|
||
|
} else {
|
||
|
// We found a non-quoted value.
|
||
|
strMatchedValue = arrMatches[3];
|
||
|
}
|
||
|
|
||
|
row.push(strMatchedValue);
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Converts a two-dimensional array into an encoded string.
|
||
|
*
|
||
|
* For example:
|
||
|
*
|
||
|
* Ext.util.CSV.encode([
|
||
|
* ['foo "bar", bletch', 'Normal String', new Date()],
|
||
|
* [Math.PI, 1, false]
|
||
|
* ]);
|
||
|
*
|
||
|
* The above produces the following string:
|
||
|
*
|
||
|
* '"foo ""bar"", bletch",Normal String,2010-01-01T21:45:32.004Z\u000a3.141592653589793,1,false'
|
||
|
*
|
||
|
* @param {Mixed[][]} input An array of row data arrays.
|
||
|
*
|
||
|
* @param {String} [delimiter] The column delimiter to use if the default value
|
||
|
* of {@link #cfg-delimiter delimiter} is not desired.
|
||
|
*
|
||
|
* @return {String} A string in which data items are separated by {@link #delimiter}
|
||
|
* characters, and rows are separated by {@link #lineBreak} characters.
|
||
|
*/
|
||
|
encode: function (input, delimiter) {
|
||
|
var me = this,
|
||
|
delim = delimiter || me.delimiter,
|
||
|
dateFormat = me.dateFormat,
|
||
|
quote = me.quote,
|
||
|
twoQuotes = quote + quote,
|
||
|
rowIndex = input.length,
|
||
|
lineBreakRe = me.lineBreakRe,
|
||
|
result = [],
|
||
|
outputRow = [],
|
||
|
col, columnIndex, inputRow;
|
||
|
|
||
|
while (rowIndex-- > 0) {
|
||
|
inputRow = input[rowIndex];
|
||
|
outputRow.length = columnIndex = inputRow.length;
|
||
|
|
||
|
while (columnIndex-- > 0) {
|
||
|
col = inputRow[columnIndex];
|
||
|
|
||
|
if (col == null) { // == null || === undefined
|
||
|
col = '';
|
||
|
} else if (typeof col === 'string') {
|
||
|
if (col) {
|
||
|
// If the value contains quotes, double them up, and wrap with quotes
|
||
|
if (col.indexOf(quote) > -1) {
|
||
|
col = quote + col.split(quote).join(twoQuotes) + quote;
|
||
|
} else if (col.indexOf(delim) > -1 || lineBreakRe.test(col)) {
|
||
|
col = quote + col + quote;
|
||
|
}
|
||
|
}
|
||
|
} else if (Ext.isDate(col)) {
|
||
|
col = Ext.Date.format(col, dateFormat);
|
||
|
}
|
||
|
//<debug>
|
||
|
else if (col && (isNaN(col) || Ext.isArray(col))) {
|
||
|
Ext.Error.raise('Cannot serialize ' + Ext.typeOf(col) + ' into CSV');
|
||
|
}
|
||
|
//</debug>
|
||
|
|
||
|
outputRow[columnIndex] = col;
|
||
|
}
|
||
|
|
||
|
result[rowIndex] = outputRow.join(delim);
|
||
|
}
|
||
|
|
||
|
return result.join(me.lineBreak);
|
||
|
}
|
||
|
});
|