| // |
| // Copyright 2014 Google Inc. All rights reserved. |
| // |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file or at |
| // https://developers.google.com/open-source/licenses/bsd |
| // |
| |
| part of charted.charts; |
| |
| class DefaultChartDataImpl extends Observable implements ChartData { |
| List<ChartColumnSpec> _columns; |
| List<List> _rows; |
| |
| bool _hasObservableRows = false; |
| SubscriptionsDisposer _disposer = new SubscriptionsDisposer(); |
| |
| DefaultChartDataImpl( |
| Iterable<ChartColumnSpec> columns, Iterable<Iterable> rows) { |
| this.columns = new List<ChartColumnSpec>.from(columns); |
| if (rows is List && rows.every((row) => row is List)) { |
| this.rows = rows as List<List>; |
| } else { |
| List<Iterable> rowsList = new List.from(rows); |
| this.rows = new List<List>.generate( |
| rowsList.length, (i) => new List.from(rowsList[i])); |
| } |
| } |
| |
| set columns(Iterable<ChartColumnSpec> value) { |
| assert(value != null); |
| |
| // Create a copy of columns. We do not currently support |
| // changes to the list of columns. Any changes to the spec |
| // will be applied at the next ChartBase.draw(); |
| this._columns = new List<ChartColumnSpec>.from(value); |
| } |
| |
| List<ChartColumnSpec> get columns => _columns; |
| |
| set rows(List<List> value) { |
| assert(value != null); |
| |
| _rows = value; |
| if (_rows is ObservableList) { |
| _disposer.add((_rows as ObservableList).listChanges.listen(rowsChanged)); |
| } |
| |
| if (_rows.every((row) => row is ObservableList)) { |
| _hasObservableRows = true; |
| for (int i = 0; i < _rows.length; i++) { |
| var row = _rows.elementAt(i); |
| _disposer.add( |
| (row as ObservableList) |
| .listChanges |
| .listen((changes) => _valuesChanged(i, changes)), |
| row); |
| } |
| ; |
| } else if (_rows is Observable) { |
| logger.info('List of rows is Observable, but not rows themselves!'); |
| } |
| } |
| |
| List<List> get rows => _rows; |
| |
| rowsChanged(List<ListChangeRecord> changes) { |
| if (_rows is! ObservableList) return; |
| notifyChange(new ChartRowChangeRecord(changes)); |
| |
| if (!_hasObservableRows) return; |
| changes.forEach((ListChangeRecord change) { |
| change.removed.forEach((item) => _disposer.unsubscribe(item)); |
| |
| for (int i = 0; i < change.addedCount; i++) { |
| var index = change.index + i, row = _rows.elementAt(index); |
| |
| if (row is! ObservableList) { |
| logger.severe('A non-observable row was added! ' |
| 'Changes on this row will not be monitored'); |
| } else { |
| _disposer.add( |
| (row as ObservableList) |
| .listChanges |
| .listen((changes) => _valuesChanged(index, changes)), |
| row); |
| } |
| } |
| }); |
| } |
| |
| _valuesChanged(int index, List<ListChangeRecord> changes) { |
| if (!_hasObservableRows) return; |
| notifyChange(new ChartValueChangeRecord(index, changes)); |
| } |
| |
| String toString() { |
| var cellDataLength = new List.filled(rows.elementAt(0).length, 0); |
| for (var i = 0; i < columns.length; i++) { |
| if (cellDataLength[i] < columns.elementAt(i).label.toString().length) { |
| cellDataLength[i] = columns.elementAt(i).label.toString().length; |
| } |
| } |
| for (var row in rows) { |
| for (var i = 0; i < row.length; i++) { |
| if (cellDataLength[i] < row.elementAt(i).toString().length) { |
| cellDataLength[i] = row.elementAt(i).toString().length; |
| } |
| } |
| } |
| |
| var totalLength = 1; // 1 for the leading '|'. |
| for (var length in cellDataLength) { |
| // 3 for the leading and trailing ' ' padding and trailing '|'. |
| totalLength += length + 3; |
| } |
| |
| // Second pass for building the string buffer and pad each cell with space |
| // according to the difference between cell string length and max length. |
| var strBuffer = new StringBuffer(); |
| strBuffer.write('-' * totalLength + '\n'); |
| strBuffer.write('|'); |
| |
| // Process columns. |
| for (var i = 0; i < columns.length; i++) { |
| var label = columns.elementAt(i).label; |
| var lengthDiff = cellDataLength[i] - label.length; |
| strBuffer.write(' ' * lengthDiff + ' ${label} |'); |
| } |
| strBuffer.write('\n' + '-' * totalLength + '\n'); |
| |
| // Process rows. |
| for (var row in rows) { |
| strBuffer.write('|'); |
| for (var i = 0; i < row.length; i++) { |
| var data = row.elementAt(i).toString(); |
| var lengthDiff = cellDataLength[i] - data.length; |
| strBuffer.write(' ' * lengthDiff + ' ${data} |'); |
| |
| if (i == row.length - 1) { |
| strBuffer.write('\n' + '-' * totalLength + '\n'); |
| } |
| } |
| } |
| return strBuffer.toString(); |
| } |
| } |