public void abort() { _aborted = true; _parser.abort(); _results.meta.aborted = true; if (Papa.isFunction(_config.complete)) { _config.complete(_results); } _input = ""; }
protected void _sendError(Error error) { if (Papa.isFunction(this._config.error)) { this._config.error(error); } else if (Papa.IS_WORKER && this._config.error != null) { //global.postMessage({ // workerId: Papa.WORKER_ID, // error: error, // finished: false //}); } }
public Result parseChunk(string chunk) { // First chunk pre-processing if (this.isFirstChunk && Papa.isFunction(this._config.beforeFirstChunk)) { string modifiedChunk = this._config.beforeFirstChunk(chunk); if (modifiedChunk != null) { chunk = modifiedChunk; } } this.isFirstChunk = false; // Rejoin the line we likely just split in two by chunking the file string aggregate = this._partialLine + chunk; this._partialLine = ""; Result results = this._handle.parse(aggregate, this._baseIndex, !this._finished); if (this._handle.paused() || this._handle.aborted()) { return(null); } int lastIndex = results.meta.cursor; if (!this._finished) { this._partialLine = Papa.Substring(aggregate, lastIndex - this._baseIndex); this._baseIndex = lastIndex; } if (results != null && results.data != null) { this._rowCount += results.data.Count; } bool finishedIncludingPreview = this._finished || (this._config.preview > 0 && this._rowCount >= this._config.preview); if (Papa.IS_WORKER) { //global.postMessage({ // results: results, // workerId: Papa.WORKER_ID, // finished: finishedIncludingPreview //}); } else if (Papa.isFunction(this._config.chunk)) { this._config.chunk(results, this._handle); if (this._paused) { return(null); } results = null; this._completeResults = null; } if (this._config.step == null && this._config.chunk == null) { this._completeResults.data = this._completeResults.data.Concat(results.data).ToList(); this._completeResults.dataWithHeader = this._completeResults.dataWithHeader.Concat(results.dataWithHeader).ToList(); this._completeResults.errors = this._completeResults.errors.Concat(results.errors).ToList(); this._completeResults.meta = results.meta; } if (finishedIncludingPreview && Papa.isFunction(this._config.complete) && (results == null || !results.meta.aborted)) { this._config.complete(this._completeResults); } if (!finishedIncludingPreview && (results == null || !results.meta.paused)) { this._nextChunk(); } return(results); }
// Parses input. Most users won't need, and shouldn't mess with, the baseIndex // and ignoreLastRow parameters. They are used by streamers (wrapper functions) // when an input comes in multiple chunks, like from a file. public Result parse(string input, int baseIndex = 0, bool ignoreLastRow = false) { Func <bool> needsHeaderRow = () => { return(_config.header && _fields.Count == 0); }; Action fillHeaderFields = () => { if (_results == null || _results.data.Count == 0) { return; } for (int i = 0; needsHeaderRow() && i < _results.data.Count; i++) { for (int j = 0; j < _results.data[i].Count; j++) { _fields.Add(_results.data[i][j]); } } _results.data.RemoveRange(0, 1); }; Func <Result> applyHeaderAndDynamicTyping = () => { if (_results == null || (!_config.header && !_config.dynamicTyping)) { return(_results); } for (int i = 0; i < _results.data.Count; i++) { Dictionary <string, string> rowWithHeader = new Dictionary <string, string>(); int j; for (j = 0; j < _results.data[i].Count; j++) { //[TODO] //if (_config.dynamicTyping) //{ // var value = _results.data[i][j]; // if (value == "true" || value == "TRUE") // _results.data[i][j] = true; // else if (value == "false" || value == "FALSE") // _results.data[i][j] = false; // else // _results.data[i][j] = tryParseFloat(value); //} if (_config.header) { if (j >= _fields.Count) { if (!rowWithHeader.ContainsKey("__parsed_extra")) { rowWithHeader.Add("__parsed_extra", ""); } rowWithHeader["__parsed_extra"] += _results.data[i][j]; //[CR we can not simply put an Array into __parsed_extra, so juste pipe it] if (j < _results.data[i].Count - 1) { rowWithHeader["__parsed_extra"] += "|"; } } else { rowWithHeader[_fields[j]] = _results.data[i][j]; } } } if (_config.header) { _results.dataWithHeader.Add(rowWithHeader); //[CR we are not overwriting _results.data here but instead fill another List] if (j > _fields.Count) { addError("FieldMismatch", "TooManyFields", "Too many fields: expected " + _fields.Count + " fields but parsed " + j, i); } else if (j < _fields.Count) { addError("FieldMismatch", "TooFewFields", "Too few fields: expected " + _fields.Count + " fields but parsed " + j, i); } } } if (_config.header && _results.meta != null) { _results.meta.fields = _fields; } return(_results); }; Func <Result> processResults = () => { if (_results != null && _delimiterError) { addError("Delimiter", "UndetectableDelimiter", "Unable to auto-detect delimiting character; defaulted to '" + Papa.DefaultDelimiter + "'"); _delimiterError = false; } if (_config.skipEmptyLines) { for (int i = 0; i < _results.data.Count; i++) { if (_results.data[i].Count == 1 && _results.data[i][0] == "") { _results.data.RemoveRange(i--, 1); } } } if (needsHeaderRow()) { fillHeaderFields(); } return(applyHeaderAndDynamicTyping()); }; //------------------------------------------------------------------------------------------------------------------------------------------------------ if (String.IsNullOrEmpty(_config.newline)) { _config.newline = guessLineEndings(input); } _delimiterError = false; if (String.IsNullOrEmpty(_config.delimiter)) { DelimiterResult delimGuess = guessDelimiter(input); if (delimGuess.successful) { _config.delimiter = delimGuess.bestDelimiter; } else { _delimiterError = true; // add error after parsing (otherwise it would be overwritten) _config.delimiter = Papa.DefaultDelimiter; } _results.meta.delimiter = _config.delimiter; } if (_config.quoteChar == Char.MinValue) { if (Papa.Substr(input, 0, 1) == "'" && Papa.Substr(input, input.IndexOf(_config.delimiter, 0) - 1, 1) == "'") { _config.quoteChar = '\''; } else { _config.quoteChar = '"'; } } Config parserConfig = Papa.copy(_config); if (_config.preview > 0 && _config.header) { parserConfig.preview++; // to compensate for header row } if (Papa.isFunction(_config.step)) { Action <Result, ParserHandle> userStep = _config.step; parserConfig.step = (results, parser) => { _results = results; if (needsHeaderRow()) { processResults(); } else // only call user's step function after header row { processResults(); // It's possbile that this line was empty and there's no row here after all if (_results.data.Count == 0) { return; } _stepCounter += results.data.Count; if (parserConfig.preview > 0 && _stepCounter > parserConfig.preview) { _parser.abort(); } else { userStep(_results, this); } } }; } //---------------------------------------------------------------------- _input = input; _parser = new Parser(parserConfig); _results = _parser.parse(_input, baseIndex, ignoreLastRow); processResults(); if (_paused) { return new Result() { meta = new Meta() { paused = true } } } ; else if (_results != null) { return(_results); } else { return new Result() { meta = new Meta() { paused = false } } }; }