Statistics
| Branch: | Revision:

root / src / scave / export.cc @ e1750c09

History | View | Annotate | Download (28.1 KB)

1
//==========================================================================
2
//  EXPORT.CC - part of
3
//                     OMNeT++/OMNEST
4
//            Discrete System Simulation in C++
5
//
6
//  Author: Tamas Borbely
7
//
8
//==========================================================================
9

    
10
/*--------------------------------------------------------------*
11
  Copyright (C) 1992-2008 Andras Varga
12
  Copyright (C) 2006-2008 OpenSim Ltd.
13

14
  This file is distributed WITHOUT ANY WARRANTY. See the file
15
  `license' for details on this and other legal matters.
16
*--------------------------------------------------------------*/
17

    
18
#include <cstdio>
19
#include <iostream>
20
#include <sstream>
21
#include <algorithm>
22
#include <functional>
23
#include "opp_ctype.h"
24
#include "commonutil.h"
25
#include "scaveutils.h"
26
#include "export.h"
27

    
28
USING_NAMESPACE
29

    
30
using namespace std;
31

    
32
//XXX unfortunately, VC8.0 doesn't like the following lines, so Column needs to be fully qualified in the source...
33
//using DataTable::Column;
34
//using DataTable::ColumnType;
35

    
36
/*=====================
37
 *       Vectors
38
 *=====================*/
39
bool DataTable::CellPtr::operator <(const DataTable::CellPtr &other) const
40
{
41
    if (this->isNull())
42
        return false;
43
    if (other.isNull())
44
        return true;
45

    
46
    ColumnType keyColumnType = this->table->getColumn(this->column).type;
47
    switch (keyColumnType)
48
    {
49
    case DOUBLE:
50
        return this->table->getDoubleValue(this->row, this->column) <
51
                 other.table->getDoubleValue(other.row, other.column);
52
    case BIGDECIMAL:
53
        return this->table->getBigDecimalValue(this->row, this->column) <
54
                 other.table->getBigDecimalValue(other.row, other.column);
55
    case STRING:
56
        return this->table->getStringValue(this->row, this->column) <
57
                 other.table->getStringValue(other.row, other.column);
58
    }
59
    Assert(false);
60
    return false;
61
}
62

    
63
/*=====================
64
 *       Vectors
65
 *=====================*/
66
XYDataTable::XYDataTable(const string &name, const string &description,
67
                         const string &xColumnName, const string &yColumnName, const XYArray *vec)
68
    : DataTable(name, description), vec(vec)
69
{
70
    header.push_back(Column(xColumnName, BIGDECIMAL));
71
    header.push_back(Column(yColumnName, DOUBLE));
72
}
73

    
74
int XYDataTable::getNumRows() const
75
{
76
    return vec->length();
77
}
78

    
79
bool XYDataTable::isNull(int row, int col) const
80
{
81
    return false;
82
}
83

    
84
string XYDataTable::getStringValue(int row, int col) const
85
{
86
    // no string column
87
    return "";
88
}
89

    
90
BigDecimal XYDataTable::getBigDecimalValue(int row, int col) const
91
{
92
    if (col == 0)
93
    {
94
        BigDecimal xp = vec->getPreciseX(row);
95
        return xp.isNil() ? BigDecimal(vec->getX(row)) : xp;
96
    }
97
    else
98
        return BigDecimal::NaN;
99
}
100

    
101
double XYDataTable::getDoubleValue(int row, int col) const
102
{
103
    if (col == 1)
104
        return vec->getY(row);
105
    else
106
        return NaN;
107
}
108

    
109
static string createNameForXYDatasetRow(const XYDataset &data, int row, const string &separator = "/")
110
{
111
    string name;
112
    ResultItemFields rowFields = data.getRowFields();
113
    bool first = true;
114
    for (ResultItemFields::const_iterator rowField = rowFields.begin(); rowField != rowFields.end(); ++rowField)
115
    {
116
        if (!first) name += separator;
117
        name += data.getRowField(row, *rowField);
118
        first = false;
119
    }
120
    return name;
121
}
122

    
123
/*=====================
124
 *     Scatter plots
125
 *=====================*/
126
ScatterDataTable::ScatterDataTable(const string &name, const string &description, const XYDataset &data)
127
    : DataTable(name, description), dataset(data)
128
{
129
    for (int row = 0; row < data.getRowCount(); ++row)
130
    {
131
        string columnName = createNameForXYDatasetRow(data, row);
132
        header.push_back(Column(columnName, DOUBLE));
133
    }
134
}
135

    
136
int ScatterDataTable::getNumRows() const
137
{
138
    return dataset.getColumnCount();
139
}
140

    
141
bool ScatterDataTable::isNull(int row, int col) const
142
{
143
    return dataset.getValue(col, row).getCount() == 0;
144
}
145

    
146
string ScatterDataTable::getStringValue(int row, int col) const
147
{
148
    // no string column
149
    return "";
150
}
151

    
152
BigDecimal ScatterDataTable::getBigDecimalValue(int row, int col) const
153
{
154
    // no BigDecimal column
155
    return BigDecimal::Nil;
156
}
157

    
158
double ScatterDataTable::getDoubleValue(int row, int col) const
159
{
160
    return dataset.getValue(col, row).getMean();
161
}
162

    
163
/*================================
164
 *          Scalars
165
 *================================*/
166
ScalarDataTable::ScalarDataTable(const std::string name, const std::string description,
167
            const IDList &idlist, ResultItemFields groupBy, ResultFileManager &manager)
168
            : DataTable(name, description), manager(manager)
169
{
170
    DataSorter sorter(&manager);
171
    scalars = sorter.groupAndAlign(idlist, groupBy);
172

    
173
    // add a column for each grouping field
174
    if (groupBy.hasField(ResultItemField::FILE))   header.push_back(Column("File", STRING));
175
    if (groupBy.hasField(ResultItemField::RUN))    header.push_back(Column("Run", STRING));
176
    if (groupBy.hasField(ResultItemField::MODULE)) header.push_back(Column("Module", STRING));
177
    if (groupBy.hasField(ResultItemField::NAME))   header.push_back(Column("Name", STRING));
178

    
179
    // add a column for each column in scalars headed by the non-grouping fields
180
    const IDVector firstRow = scalars[0]; // XXX empty idlist?
181
    for (int col = 0; col < (int)firstRow.size(); ++col)
182
    {
183
        ID id = -1;
184
        for (int row = 0; row < (int)scalars.size(); ++row)
185
            if ((id = scalars[row][col]) != -1)
186
                break;
187

    
188
        if (id != -1)
189
        {
190
            const ScalarResult &scalar = manager.getScalar(id);
191
            string name;
192
            if (!groupBy.hasField(ResultItemField::FILE))   name += scalar.fileRunRef->fileRef->filePath+"_";
193
            if (!groupBy.hasField(ResultItemField::RUN))    name += scalar.fileRunRef->runRef->runName+"_";
194
            if (!groupBy.hasField(ResultItemField::MODULE)) name += *scalar.moduleNameRef+"_";
195
            if (!groupBy.hasField(ResultItemField::NAME))   name += *scalar.nameRef+"_";
196
            if (!name.empty()) name.erase(name.end()-1); // remove last '_'
197
            header.push_back(Column(name, DOUBLE));
198
        }
199
        else
200
        {
201
            header.push_back(Column("?", DOUBLE));
202
        }
203
    }
204
}
205

    
206
int ScalarDataTable::getNumRows() const
207
{
208
    return scalars.size();
209
}
210

    
211
bool ScalarDataTable::isNull(int row, int col) const
212
{
213
    return false;
214
}
215

    
216
double ScalarDataTable::getDoubleValue(int row, int col) const
217
{
218
    if (row >= 0 && row < getNumRows() && col >= 0 && col < getNumColumns())
219
    {
220
        IDVector r = scalars[row];
221
        ID id = r[col-(header.size() - r.size())];
222
        if (id != -1)
223
        {
224
            const ScalarResult &scalar = manager.getScalar(id);
225
            return scalar.value;
226
        }
227
    }
228
    return NaN;
229
}
230

    
231
BigDecimal ScalarDataTable::getBigDecimalValue(int row, int col) const
232
{
233
    // no BigDecimal scalars yet
234
    return BigDecimal::NaN;
235
}
236

    
237
std::string ScalarDataTable::getStringValue(int row, int col) const
238
{
239
    if (row >= 0 && row < getNumRows() && col >= 0 && col < getNumColumns())
240
    {
241
        IDVector r = scalars[row];
242
        ID id = -1;
243
        for (int i = 0; i < getNumColumns(); ++i)
244
            if ((id=r[i]) != -1)
245
                break;
246
        if (id != -1)
247
        {
248
            const ScalarResult &scalar = manager.getScalar(id);
249
            Column c = getColumn(col);
250
            if (c.name == "File") return scalar.fileRunRef->fileRef->filePath;
251
            else if (c.name == "Run") return scalar.fileRunRef->runRef->runName;
252
            else if (c.name == "Module") return *scalar.moduleNameRef;
253
            else if (c.name == "Name") return *scalar.nameRef;
254
        }
255
    }
256
    return "";
257
}
258

    
259
/*=====================
260
 *  Join data tables
261
 *=====================*/
262
class DataTableIterator
263
{
264
    private:
265
        vector<DataTable::CellPtr> cells;
266
        vector<int> currentRows;
267
    public:
268
        DataTableIterator(const vector<DataTable*> &tables, int keyColumn)
269
            : cells(tables.size()), currentRows(tables.size())
270
        {
271
            for (int i = 0; i < (int)tables.size(); ++i)
272
            {
273
                this->cells[i] = DataTable::CellPtr(tables[i], 0, keyColumn);
274
                currentRows[i] = -1;
275
            }
276
        }
277

    
278
        bool hasNext() const
279
        {
280
            return find_if(cells.begin(), cells.end(), not1(mem_fun_ref(&DataTable::CellPtr::isNull)))
281
                    != cells.end();
282
        }
283

    
284
        int currentRow(int index) const { return currentRows[index]; }
285
        void reset() {  for_each(cells.begin(), cells.end(), mem_fun_ref(&DataTable::CellPtr::resetRow)); }
286
        void next();
287
};
288

    
289
void DataTableIterator::next()
290
{
291
    vector<DataTable::CellPtr>::iterator minElementPtr = min_element(cells.begin(), cells.end());
292
    if (minElementPtr != cells.end())
293
    {
294
        DataTable::CellPtr minElement = *minElementPtr;
295
        currentRows.clear();
296
        for (vector<DataTable::CellPtr>::iterator cellPtr = cells.begin(); cellPtr != cells.end(); ++cellPtr)
297
        {
298
            if (cellPtr->isNull() || minElement < *cellPtr)
299
                currentRows.push_back(-1);
300
            else
301
            {
302
                currentRows.push_back(cellPtr->getRow());
303
                cellPtr->nextRow();
304
            }
305
        }
306
    }
307
}
308

    
309
JoinedDataTable::JoinedDataTable(const string name, const string description,
310
                         const vector<DataTable*> &joinedTables, int joinOnColumn)
311
    : DataTable(name, description), joinedTables(joinedTables), rowMap(NULL)
312
{
313
   tableCount = joinedTables.size();
314
   // checks
315
   for (int tableIndex = 0; tableIndex < tableCount; ++tableIndex)
316
   {
317
       DataTable *table = joinedTables[tableIndex];
318
       Assert(table && joinOnColumn < table->getNumColumns());
319
       Assert(table->getColumn(joinOnColumn).type == joinedTables[0]->getColumn(joinOnColumn).type);
320
   }
321

    
322
   // compute columns
323
   if (tableCount > 0)
324
       addColumn(joinedTables[0]->getColumn(joinOnColumn), 0, joinOnColumn);
325
   for (int tableIndex = 0; tableIndex < tableCount; ++tableIndex)
326
   {
327
       DataTable *table = joinedTables[tableIndex];
328
       int numColumns = table->getNumColumns();
329
       for (int col = 0; col < numColumns; ++col)
330
       {
331
           Column column = table->getColumn(col);
332
           if (col != joinOnColumn)
333
           {
334
               if (!table-name.empty())
335
                   column.name = table->name + "/" + column.name;
336
               addColumn(column, tableIndex, col);
337
           }
338
       }
339
   }
340

    
341
   // compute rows
342
   DataTableIterator iterator(joinedTables, joinOnColumn);
343
   rowCount = 0;
344
   while (iterator.hasNext())
345
   {
346
       rowCount++;
347
       iterator.next();
348
   }
349

    
350
   rowMap = new int[rowCount*tableCount];
351

    
352
   iterator.reset();
353
   for (int row = 0; row < rowCount; ++row)
354
   {
355
       Assert(iterator.hasNext());
356
       iterator.next();
357

    
358
       for (int j = 0; j < (int)joinedTables.size(); ++j)
359
           rowMap[row*tableCount+j]=iterator.currentRow(j);
360
   }
361
}
362

    
363
JoinedDataTable::~JoinedDataTable()
364
{
365
   if (rowMap)
366
       delete[] rowMap;
367

    
368
   for (vector<DataTable*>::const_iterator it = joinedTables.begin(); it != joinedTables.end(); ++it)
369
   {
370
       if (*it)
371
           delete (*it);
372
   }
373
}
374

    
375
int JoinedDataTable::getNumRows() const
376
{
377
    return rowCount;
378
}
379

    
380
bool JoinedDataTable::isNull(int row, int col) const
381
{
382
    DataTable *table;
383
    int tableRow, tableCol;
384
    mapTableCell(row, col, table, tableRow, tableCol);
385
    return table == NULL;
386
}
387

    
388
string JoinedDataTable::getStringValue(int row, int col) const
389
{
390
    DataTable *table;
391
    int tableRow, tableCol;
392
    mapTableCell(row, col, table, tableRow, tableCol);
393
    if (table)
394
        return table->getStringValue(tableRow, tableCol);
395
    return "";
396
}
397

    
398
BigDecimal JoinedDataTable::getBigDecimalValue(int row, int col) const
399
{
400
    DataTable *table;
401
    int tableRow, tableCol;
402
    mapTableCell(row, col, table, tableRow, tableCol);
403
    if (table)
404
        return table->getBigDecimalValue(tableRow, tableCol);
405
    return BigDecimal::Nil;
406
}
407

    
408
double JoinedDataTable::getDoubleValue(int row, int col) const
409
{
410
    DataTable *table;
411
    int tableRow, tableCol;
412
    mapTableCell(row, col, table, tableRow, tableCol);
413
    if (table)
414
        return table->getDoubleValue(tableRow, tableCol);
415
    return NaN;
416
}
417

    
418
void JoinedDataTable::mapTableCell(int row, int col, DataTable* &table, int &tableRow, int &tableCol) const
419
{
420
    Assert(0 <= row && row < rowCount && 0 <= col && col < (int)columnMap.size());
421

    
422
    if (col == 0)
423
    {
424
        tableCol = columnMap[col].second;
425
        table = NULL;
426
        for (int tableIndex = 0; tableIndex < tableCount; ++tableIndex) {
427
            tableRow = rowMap[row*tableCount+tableIndex];
428
            if (tableRow >= 0)
429
            {
430
                table = joinedTables[tableIndex];
431
                return;
432
            }
433
        }
434
    }
435
    else
436
    {
437
        const pair<int,int> &tableAndColumn = columnMap[col];
438
        int tableIndex = tableAndColumn.first;
439
        tableCol = tableAndColumn.second;
440
        tableRow = rowMap[row*tableCount+tableIndex];
441
        table = tableRow >= 0 ? joinedTables[tableIndex] : NULL;
442
    }
443
}
444

    
445

    
446
/*===============================
447
 *           Export
448
 *===============================*/
449

    
450
ScaveExport::~ScaveExport()
451
{
452
    close();
453
}
454

    
455
void ScaveExport::open()
456
{
457
    if (!out.is_open())
458
    {
459
        fileName = makeFileName(baseFileName);
460
        out.open(fileName.c_str(), ios_base::binary); // no \n translation
461
        if (out.fail())
462
            throw opp_runtime_error("Cannot open file '%s'", fileName.c_str());
463
    }
464
}
465

    
466
void ScaveExport::close()
467
{
468
    if (out.is_open())
469
    {
470
        out.close();
471
    }
472
}
473

    
474
void ScaveExport::saveVector(const string &name, const string &description,
475
                             ID vectorID, bool computed, const XYArray *xyarray, ResultFileManager &manager,
476
                             int startIndex, int endIndex)
477
{
478
    const XYDataTable table(name, description, "X", "Y", xyarray);
479
    if (endIndex == -1)
480
        endIndex = table.getNumRows();
481
    saveTable(table, startIndex, endIndex);
482
}
483

    
484
void ScaveExport::saveVectors(const string &name, const string &description,
485
                             const IDList &vectors, const vector<XYArray*> xyArrays, const ResultFileManager &manager)
486
{
487
    Assert((int)vectors.size() == (int)xyArrays.size());
488

    
489
    vector<DataTable*> tables;
490
    for (int i = 0; i < (int)xyArrays.size(); ++i)
491
    {
492
        const VectorResult& vector = manager.getVector(vectors.get(i));
493
        std::string yColumnName = makeUniqueIdentifier(*vector.moduleNameRef + "/" + *vector.nameRef);
494

    
495
        tables.push_back(new XYDataTable(name, description, "X", yColumnName, xyArrays[i]));
496
    }
497
    JoinedDataTable table(name, description, tables, 0);
498
    saveTable(table, 0, table.getNumRows());
499
}
500

    
501
void ScaveExport::saveScalars(const string &name, const string &description, const IDList &scalars, ResultItemFields groupBy, ResultFileManager &manager)
502
{
503
    const ScalarDataTable table(name, description, scalars, groupBy, manager);
504
    saveTable(table, 0, table.getNumRows());
505
}
506

    
507
void ScaveExport::saveScalars(const string &name, const string &description,
508
                                const IDList &scalars, const string &moduleName, const string &scalarName,
509
                                ResultItemFields columnFields,
510
                                const std::vector<std::string> &isoModuleNames, const StringVector &isoScalarNames,
511
                                ResultItemFields isoFields, ResultFileManager &manager)
512
{
513
    DataSorter sorter(&manager);
514
    StringVector rowFields;
515
    rowFields.push_back(ResultItemField::MODULE);
516
    rowFields.push_back(ResultItemField::NAME);
517
    XYDatasetVector xyDatasets = sorter.prepareScatterPlot3(scalars, moduleName.c_str(), scalarName.c_str(),
518
                                                            ResultItemFields(rowFields), columnFields,
519
                                                            isoModuleNames, isoScalarNames, isoFields);
520
    vector<DataTable *> tables;
521
    for (XYDatasetVector::const_iterator it = xyDatasets.begin(); it != xyDatasets.end(); it++)
522
    {
523
        string name = ""; // TODO iso attr values
524
        string description = "";
525
        tables.push_back(new ScatterDataTable(name, description, *it));
526
    }
527

    
528
    JoinedDataTable table(name, description, tables, 0 /*first column is X*/);
529
    saveTable(table, 0, table.getNumRows());
530
}
531

    
532
string ScaveExport::makeUniqueIdentifier(const string &name)
533
{
534
    string result = makeIdentifier(name);
535

    
536
    if (identifiers.find(result) != identifiers.end())
537
    {
538
        string base = result;
539
        for (int i=0; ; ++i)
540
        {
541
            char suffix[32];
542
            sprintf(suffix, "_%d", i);
543
            result = base + suffix;
544
            if (identifiers.find(result) == identifiers.end())
545
                break;
546
        }
547
    }
548

    
549
    identifiers.insert(result);
550
    return result;
551
}
552

    
553
/*===============================
554
 *           Matlab
555
 *===============================*/
556
string MatlabStructExport::makeIdentifier(const string &name)
557
{
558
    string result(name);
559
    for (string::iterator it=result.begin(); it!=result.end(); ++it)
560
        if (!opp_isalnum(*it))
561
            *it = '_';
562
    if (result[0] != '_' && !opp_isalpha(result[0]))
563
        result.insert(0, "_");
564
    return result;
565
}
566

    
567
string MatlabStructExport::quoteString(const string &str)
568
{
569
    string result;
570
    result.push_back('"');
571
    for (string::const_iterator it = str.begin(); it != str.end(); ++it)
572
    {
573
        if (*it == '\\' || *it == '\"')
574
            result.push_back('\\');
575
        result.push_back(*it);
576
    }
577
    result.push_back('"');
578

    
579
    return result;
580
}
581

    
582
void MatlabStructExport::writeDouble(double value)
583
{
584
    if (isNaN(value))
585
        out << "NaN";
586
    else if (isPositiveInfinity(value))
587
        out << "Inf";
588
    else if (isNegativeInfinity(value))
589
        out << "-Inf";
590
    else
591
        out << value;
592
}
593

    
594

    
595
string MatlabScriptExport::makeFileName(const string name)
596
{
597
    string fileName = name;
598
    if (fileName.empty())
599
        return "matlab.m";
600
    else if (fileName.find('.') == string::npos)
601
        fileName.append(".m");
602
    return fileName;
603
}
604

    
605
void MatlabScriptExport::saveTable(const DataTable &table, int startRow, int endRow)
606
{
607
    open();
608
    string tableName = makeUniqueIdentifier(table.name);
609
    writeDescriptionField(table, tableName);
610
    writeColumnFields(table, startRow, endRow, tableName);
611
}
612

    
613
void MatlabScriptExport::writeDescriptionField(const DataTable &table, const string tableName)
614
{
615
    out << tableName << ".description=" << quoteString(table.description) << '\n';
616
}
617

    
618
void MatlabScriptExport::writeColumnFields(const DataTable &table, int startRow, int endRow, const string tableName)
619
{
620
    for (int col = 0; col < table.getNumColumns(); ++col)
621
    {
622
        DataTable::Column column = table.getColumn(col);
623

    
624
        out << tableName + "." + makeIdentifier(column.name) << "=[" << '\n';
625
        switch (column.type)
626
        {
627
        case DataTable::DOUBLE:
628
            writeDoubleColumn(table, col, startRow, endRow);
629
            break;
630
        case DataTable::BIGDECIMAL:
631
            writeBigDecimalColumn(table, col, startRow, endRow);
632
            break;
633
        case DataTable::STRING:
634
            writeStringColumn(table, col, startRow, endRow);
635
            break;
636
        }
637
        out << "]\n\n";
638
    }
639
}
640

    
641
void MatlabScriptExport::writeStringColumn(const DataTable &table, int col, int startRow, int endRow)
642
{
643
    for (int row = startRow; row < endRow; ++row)
644
    {
645
        out << '"' << table.getStringValue(row, col) << '"' << ";\n";
646
    }
647
}
648

    
649
void MatlabScriptExport::writeDoubleColumn(const DataTable &table, int col, int startRow, int endRow)
650
{
651
    out.precision(prec);
652
    for (int row = startRow; row < endRow; ++row)
653
    {
654
        writeDouble(table.getDoubleValue(row, col));
655
        out << ";\n";
656
    }
657
}
658

    
659
void MatlabScriptExport::writeBigDecimalColumn(const DataTable &table, int col, int startRow, int endRow)
660
{
661
    // TODO: precision
662
    for (int row = startRow; row < endRow; ++row)
663
    {
664
        out << table.getBigDecimalValue(row, col).str() << ";\n";
665
    }
666
}
667

    
668
/*===============================
669
 *           Octave
670
 *===============================*/
671
string OctaveTextExport::makeFileName(const string name)
672
{
673
    string fileName = name;
674
    if (fileName.empty())
675
        return "octave.dat";
676
    else
677
        return fileName;
678
}
679

    
680
void OctaveTextExport::saveTable(const DataTable &table, int startIndex, int endIndex)
681
{
682
    open();
683
    writeStructHeader(table);
684
    writeDescriptionField(table);
685
    writeColumnFields(table, startIndex, endIndex);
686
}
687

    
688
void OctaveTextExport::writeStructHeader(const DataTable &table)
689
{
690
    out << "# name: " << makeUniqueIdentifier(table.name) << "\n"
691
           "# type: struct\n"
692
           "# length: " << table.getNumColumns() + 1 // description + columns
693
        << "\n";
694
}
695

    
696
void OctaveTextExport::writeDescriptionField(const DataTable &table)
697
{
698
    out << "# name: description\n"
699
           "# type: cell\n"
700
           "# rows: 1\n"
701
           "# columns: 1\n"
702
           "# name: <cell-element>\n"
703
           "# type: string\n"
704
           "# elements: 1\n"
705
           "# length: " << table.description.size() << "\n"
706
        << table.description << "\n";
707
}
708

    
709
void OctaveTextExport::writeColumnFields(const DataTable &table, int startRow, int endRow)
710
{
711
    for (int col=0; col<table.getNumColumns(); ++col)
712
    {
713
        DataTable::Column column = table.getColumn(col);
714
        switch (column.type)
715
        {
716
        case DataTable::DOUBLE: writeDoubleColumn(table, col, startRow, endRow); break;
717
        case DataTable::BIGDECIMAL: writeBigDecimalColumn(table, col, startRow, endRow); break;
718
        case DataTable::STRING: writeStringColumn(table, col, startRow, endRow); break;
719
        }
720
    }
721
}
722

    
723
void OctaveTextExport::writeDoubleColumn(const DataTable &table, int col, int startRow, int endRow)
724
{
725
    DataTable::Column column = table.getColumn(col);
726
    out << "# name: " << makeIdentifier(column.name) << "\n"
727
           "# type: cell\n"
728
           "# rows: 1\n"
729
           "# columns: 1\n"
730
           "# name: <cell-element>\n"
731
           "# type: matrix\n"
732
           "# rows: " << (endRow - startRow) << "\n"
733
           "# columns: 1\n";
734

    
735
    out.precision(prec);
736
    for (int row = startRow; row <endRow; ++row)
737
    {
738
        double value = table.getDoubleValue(row, col);
739
        if (isNaN(value))
740
            out << "NaN";
741
        else if (isPositiveInfinity(value))
742
            out << "Inf";
743
        else if (isNegativeInfinity(value))
744
            out << "-Inf";
745
        else
746
            out << value;
747
        out << "\n";
748
    }
749
}
750

    
751
void OctaveTextExport::writeBigDecimalColumn(const DataTable &table, int col, int startRow, int endRow)
752
{
753
    DataTable::Column column = table.getColumn(col);
754
    out << "# name: " << makeIdentifier(column.name) << "\n"
755
           "# type: cell\n"
756
           "# rows: 1\n"
757
           "# columns: 1\n"
758
           "# name: <cell-element>\n"
759
           "# type: matrix\n"
760
           "# rows: " << (endRow - startRow) << "\n"
761
           "# columns: 1\n";
762
    for (int row = startRow; row <endRow; ++row)
763
    {
764
        BigDecimal value = table.getBigDecimalValue(row, col);
765
        out << value.str() << "\n";
766
    }
767
}
768

    
769
void OctaveTextExport::writeStringColumn(const DataTable &table, int col, int startRow, int endRow)
770
{
771
    DataTable::Column column = table.getColumn(col);
772
    out << "# name: " << makeIdentifier(column.name) << "\n"
773
           "# type: cell\n"
774
           "# rows: 1\n"
775
           "# columns: 1\n"
776
           "# name: <cell-element>\n"
777
           "# type: string\n"
778
           "# elements: " << (endRow - startRow) << "\n";
779
    for (int row = startRow; row < endRow; ++row)
780
    {
781
        std::string value = table.getStringValue(row, col);
782
        out << "# length: " << value.size() << "\n"
783
            << value << "\n";
784
    }
785
}
786

    
787
/*===============================
788
 *           Csv
789
 *===============================*/
790

    
791
/**
792
 * Generate a new filename for each table by appending '-1','-2',... suffixes to the base filename.
793
 */
794
string CsvExport::makeFileName(const string name)
795
{
796
    string file(name), extension(".csv");
797
    stringstream suffix;
798

    
799
    if (fileNameSuffix > 0)
800
        suffix << '-' << (fileNameSuffix++);
801

    
802
    if (name.empty())
803
        file = "table";
804
    else {
805
        string::size_type pos = name.rfind('.');
806
        if (pos == string::npos)
807
            file = name;
808
        else {
809
            file = name.substr(0, pos);
810
            extension = name.substr(pos, name.size() - pos);
811
        }
812
    }
813
    return file + suffix.str() + extension;
814
}
815

    
816
void CsvExport::saveVector(const string name, const string description,
817
                             ID vectorID, bool computed, const XYArray *xyarray, ResultFileManager &manager,
818
                             int startIndex, int endIndex)
819
{
820
    string xColumn, yColumn;
821
    if (computed)
822
    {
823
        xColumn = "X"; // TODO generate proper names derived from the computation
824
        yColumn = "Y";
825
    }
826
    else
827
    {
828
        const VectorResult &vector = manager.getVector(vectorID);
829
        xColumn = "time";
830
        yColumn = *vector.moduleNameRef + "." + *vector.nameRef;
831
    }
832
    const XYDataTable table(name, description, xColumn, yColumn, xyarray);
833
    if (endIndex == -1)
834
        endIndex = table.getNumRows();
835
    saveTable(table, startIndex, endIndex);
836
}
837

    
838
void CsvExport::saveTable(const DataTable &table, int startRow, int endRow)
839
{
840
    open();
841
    writeHeader(table);
842
    for (int row = startRow; row < endRow; ++row)
843
        writeRow(table, row);
844
    if (fileNameSuffix > 0)
845
        close();
846
}
847

    
848
void CsvExport::writeHeader(const DataTable &table)
849
{
850
    if (columnNames)
851
    {
852
        for (int col = 0; col < table.getNumColumns(); ++col)
853
        {
854
            if (col > 0)
855
                out << separator;
856
            DataTable::Column column = table.getColumn(col);
857
            writeString(column.name);
858
        }
859
        out << eol;
860
    }
861
}
862

    
863
void CsvExport::writeRow(const DataTable &table, int row)
864
{
865
    for (int col = 0; col < table.getNumColumns(); ++col)
866
    {
867
        DataTable::Column column = table.getColumn(col);
868
        if (col > 0)
869
            out << separator;
870
        if (!table.isNull(row, col))
871
        {
872
            switch (column.type)
873
            {
874
            case DataTable::DOUBLE: writeDouble(table.getDoubleValue(row, col)); break;
875
            case DataTable::BIGDECIMAL: writeBigDecimal(table.getBigDecimalValue(row, col)); break;
876
            case DataTable::STRING: writeString(table.getStringValue(row, col)); break;
877
            }
878
        }
879
    }
880
    out << eol;
881
}
882

    
883
void CsvExport::writeDouble(double value)
884
{
885
    if (isNaN(value))
886
        out << "NaN";
887
    else if (isPositiveInfinity(value))
888
        out << "Inf";
889
    else if (isNegativeInfinity(value))
890
        out << "-Inf";
891
    else
892
        out << value;
893
}
894

    
895
void CsvExport::writeBigDecimal(BigDecimal value)
896
{
897
    out << value.str();
898
}
899

    
900
void CsvExport::writeString(const string &value)
901
{
902
    if (needsQuote(value))
903
    {
904
        out << quoteChar;
905
        for (string::const_iterator it = value.begin(); it != value.end(); ++it)
906
        {
907
            writeChar(*it);
908
        }
909
        out << quoteChar;
910
    }
911
    else
912
    {
913
        out << value;
914
    }
915
}
916

    
917
bool CsvExport::needsQuote(const string &value)
918
{
919
    switch (quoteMethod)
920
    {
921
    case ESCAPE:
922
        for (string::const_iterator it = value.begin(); it != value.end(); ++it)
923
        {
924
            if (*it == separator || *it == quoteChar || *it == '\\')
925
                return true;
926
        }
927
        return value.find(eol) != string::npos;
928
    case DOUBLE:
929
        for (string::const_iterator it = value.begin(); it != value.end(); ++it)
930
        {
931
            if (*it == separator || *it == quoteChar || eol.find(*it) != string::npos)
932
                return true;
933
        }
934
        return value.find(eol) != string::npos;
935
    default:
936
        throw opp_runtime_error("Unknown quote method.");
937
    }
938
}
939

    
940
void CsvExport::writeChar(char ch)
941
{
942
    switch (quoteMethod)
943
    {
944
    case ESCAPE:
945
        if (ch == '\\' || ch == separator)
946
            out << '\\';
947
        out << ch;
948
        break;
949
    case DOUBLE:
950
        if (ch == separator)
951
            out << ch;
952
        out << ch;
953
        break;
954
    }
955
}
956

    
957
string CsvExport::makeIdentifier(const string &name)
958
{
959
    return name;
960
}
961

    
962

    
963
ScaveExport *ExporterFactory::createExporter(const string format)
964
{
965
    if (format == "octave")
966
        return new OctaveTextExport;
967
    else if (format == "matlab")
968
        return new MatlabScriptExport;
969
    else if (format == "csv")
970
        return new CsvExport;
971
    else
972
        return NULL;
973
}
974