Rev 1221 | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
671 | werner | 1 | /******************************************************************************************** |
2 | ** iLand - an individual based forest landscape and disturbance model |
||
3 | ** http://iland.boku.ac.at |
||
4 | ** Copyright (C) 2009- Werner Rammer, Rupert Seidl |
||
5 | ** |
||
6 | ** This program is free software: you can redistribute it and/or modify |
||
7 | ** it under the terms of the GNU General Public License as published by |
||
8 | ** the Free Software Foundation, either version 3 of the License, or |
||
9 | ** (at your option) any later version. |
||
10 | ** |
||
11 | ** This program is distributed in the hope that it will be useful, |
||
12 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
13 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
14 | ** GNU General Public License for more details. |
||
15 | ** |
||
16 | ** You should have received a copy of the GNU General Public License |
||
17 | ** along with this program. If not, see <http://www.gnu.org/licenses/>. |
||
18 | ********************************************************************************************/ |
||
19 | |||
173 | werner | 20 | #include "global.h" |
21 | #include "output.h" |
||
22 | #include <QtCore> |
||
23 | #include <QtSql> |
||
24 | |||
25 | |||
26 | /** @class Output |
||
27 | The Output class abstracts output data (database, textbased, ...). |
||
177 | werner | 28 | To create a new output, create a class derived from Output and perform the following steps: |
29 | - Overwrite constructor: |
||
30 | Create columns and set fixed properties (e.g. table name) |
||
31 | - overwrite setup() |
||
32 | this function is called after the project file is read. You can access a XmlHelper calling settings() |
||
257 | werner | 33 | which is set to the top-node of the output (defined by tableName() which is set in the constructor). Access settings |
177 | werner | 34 | using relative xml-pathes (see example). |
35 | - overwrite exec() |
||
36 | add data using the stream operators or add() function of Output. Call writeRow() after each row. Each invokation |
||
37 | of exec() is a database transaction. |
||
38 | - Add the output to the constructor of @c OutputManager |
||
39 | |||
40 | @par Example |
||
173 | werner | 41 | @code |
177 | werner | 42 | // (1) Overwrite constructor and set name, description and columns |
43 | TreeOut::TreeOut() |
||
44 | { |
||
45 | setName("Tree Output", "tree"); |
||
46 | setDescription("Output of indivdual trees."); |
||
47 | columns() << OutputColumn("id", "id of the tree", OutInteger) |
||
48 | << OutputColumn("name", "tree species name", OutString) |
||
49 | << OutputColumn("v1", "a double value", OutDouble); |
||
50 | } |
||
51 | // (2) optionally: some special settings (here: filter) |
||
52 | void TreeOut::setup() |
||
53 | { |
||
54 | QString filter = settings().value(".filter",""); |
||
55 | if (filter!="") |
||
56 | mFilter = QSharedPointer<Expression>(new Expression(filter)); |
||
57 | } |
||
173 | werner | 58 | |
177 | werner | 59 | // (3) the execution |
60 | void TreeOut::exec() |
||
61 | { |
||
62 | AllTreeIterator at(GlobalSettings::instance()->model()); |
||
63 | while (Tree *t=at.next()) { |
||
64 | if (mFilter && !mFilter->execute()) // skip if filter present |
||
65 | continue; |
||
66 | *this << t->id() << t->species()->id() << t->dbh(); // stream operators |
||
67 | writeRow(); // executes DB insert |
||
68 | } |
||
69 | } |
||
70 | // in outputmanager.cpp: |
||
71 | OutputManager::OutputManager() |
||
72 | { |
||
73 | ... |
||
74 | mOutputs.append(new TreeOut); // add the output |
||
75 | ... |
||
76 | } |
||
77 | @endcode |
||
173 | werner | 78 | |
79 | */ |
||
257 | werner | 80 | const GlobalSettings *Output::gl = GlobalSettings::instance(); |
81 | |||
82 | |||
173 | werner | 83 | void Output::exec() |
84 | { |
||
85 | qDebug() << "Output::exec() called! (should be overrided!)"; |
||
86 | } |
||
87 | |||
88 | void Output::setup() |
||
89 | { |
||
90 | } |
||
91 | |||
92 | Output::~Output() |
||
93 | { |
||
265 | werner | 94 | mInserter.clear(); |
173 | werner | 95 | } |
96 | |||
97 | Output::Output() |
||
98 | { |
||
99 | mCount=0; |
||
100 | mMode = OutDatabase; |
||
101 | mOpen = false; |
||
181 | werner | 102 | mEnabled = false; |
176 | werner | 103 | newRow(); |
173 | werner | 104 | } |
105 | |||
106 | |||
107 | /** create the database table and opens up the output. |
||
108 | */ |
||
109 | void Output::openDatabase() |
||
110 | { |
||
111 | QSqlDatabase db = GlobalSettings::instance()->dbout(); |
||
112 | // create the "create table" statement |
||
176 | werner | 113 | QString sql = "create table " +mTableName + "("; |
114 | QString insert="insert into " + mTableName + " ("; |
||
173 | werner | 115 | QString values; |
116 | |||
117 | foreach(const OutputColumn &col, columns()) { |
||
118 | switch(col.mDatatype) { |
||
176 | werner | 119 | case OutInteger: sql+=col.mName + " integer"; break; |
120 | case OutDouble: sql+=col.mName + " real"; break; |
||
121 | case OutString: sql+=col.mName + " text"; break; |
||
173 | werner | 122 | } |
176 | werner | 123 | insert+=col.mName+","; |
124 | values+=QString(":")+col.mName+","; |
||
173 | werner | 125 | |
126 | sql+=","; |
||
127 | } |
||
128 | sql[sql.length()-1]=')'; // replace last "," with ) |
||
230 | werner | 129 | //qDebug()<< sql; |
407 | werner | 130 | if (mInserter.isValid()) |
131 | mInserter.clear(); |
||
176 | werner | 132 | QSqlQuery creator(db); |
133 | QString drop=QString("drop table if exists %1").arg(tableName()); |
||
184 | werner | 134 | creator.exec(drop); // drop table (if exists) |
176 | werner | 135 | creator.exec(sql); // (re-)create table |
539 | werner | 136 | //creator.exec("delete from " + tableName()); // clear table??? necessary? |
176 | werner | 137 | |
138 | if (creator.lastError().isValid()){ |
||
139 | throw IException(QString("Error creating output: %1").arg( creator.lastError().text()) ); |
||
173 | werner | 140 | } |
141 | insert[insert.length()-1]=')'; |
||
142 | values[values.length()-1]=')'; |
||
143 | insert += QString(" values (") + values; |
||
230 | werner | 144 | //qDebug() << insert; |
176 | werner | 145 | mInserter = QSqlQuery(db); |
173 | werner | 146 | mInserter.prepare(insert); |
176 | werner | 147 | if (mInserter.lastError().isValid()){ |
148 | throw IException(QString("Error creating output: %1").arg( mInserter.lastError().text()) ); |
||
149 | } |
||
173 | werner | 150 | for (int i=0;i<columns().count();i++) |
151 | mInserter.bindValue(i,mRow[i]); |
||
152 | |||
153 | mOpen = true; |
||
154 | } |
||
155 | |||
156 | void Output::newRow() |
||
157 | { |
||
158 | mIndex = 0; |
||
159 | } |
||
160 | |||
161 | |||
162 | |||
176 | werner | 163 | void Output::writeRow() |
173 | werner | 164 | { |
165 | DBG_IF(mIndex!=mCount, "Output::save()", "received invalid number of values!"); |
||
166 | if (!isOpen()) |
||
167 | open(); |
||
168 | switch(mMode) { |
||
169 | case OutDatabase: |
||
170 | saveDatabase(); break; |
||
171 | default: throw IException("Invalid output mode"); |
||
172 | } |
||
173 | |||
174 | } |
||
175 | void Output::open() |
||
176 | { |
||
177 | if (isOpen()) |
||
178 | return; |
||
176 | werner | 179 | // setup columns |
180 | mCount = columns().count(); |
||
181 | mRow.resize(mCount); |
||
539 | werner | 182 | mOpen = true; |
176 | werner | 183 | newRow(); |
184 | // setup output |
||
173 | werner | 185 | switch(mMode) { |
186 | case OutDatabase: |
||
187 | openDatabase(); break; |
||
188 | default: throw IException("Invalid output mode"); |
||
189 | } |
||
190 | } |
||
191 | |||
176 | werner | 192 | void Output::close() |
173 | werner | 193 | { |
232 | werner | 194 | if (!isOpen()) |
195 | return; |
||
196 | mOpen = false; |
||
197 | switch (mMode) { |
||
198 | case OutDatabase: |
||
199 | // calling finish() ensures, that the query and all locks are freed. |
||
200 | // having (old) locks on database connections, degrades insert performance. |
||
201 | if (mInserter.isValid()) |
||
202 | mInserter.finish(); |
||
407 | werner | 203 | mInserter = QSqlQuery(); // clear inserter |
232 | werner | 204 | break; |
205 | default: |
||
206 | qWarning() << "Output::close with invalid mode"; |
||
207 | } |
||
208 | |||
173 | werner | 209 | } |
210 | |||
211 | |||
176 | werner | 212 | void Output::saveDatabase() |
173 | werner | 213 | { |
176 | werner | 214 | for (int i=0;i<mCount;i++) |
215 | mInserter.bindValue(i,mRow[i]); |
||
216 | mInserter.exec(); |
||
217 | if (mInserter.lastError().isValid()){ |
||
1023 | werner | 218 | throw IException(QString("Error during saving of output tables: '%1'' (native code: '%2', driver: '%3')") |
219 | .arg( mInserter.lastError().text()) |
||
220 | .arg(mInserter.lastError().nativeErrorCode()) |
||
221 | .arg(mInserter.lastError().driverText()) ); |
||
176 | werner | 222 | } |
173 | werner | 223 | |
176 | werner | 224 | newRow(); |
225 | } |
||
173 | werner | 226 | |
254 | werner | 227 | QString Output::wikiFormat() const |
253 | werner | 228 | { |
254 | werner | 229 | QString result=QString("!!%1\nTable Name: %2\n%3\n\n").arg(name(), tableName(), description()); |
253 | werner | 230 | // loop over columns... |
254 | werner | 231 | result += "||__caption__|__datatype__|__description__\n"; // table begin |
253 | werner | 232 | foreach(const OutputColumn &col, mColumns) |
254 | werner | 233 | result+=QString("%1|%2|%3\n").arg(col.name(), col.datatype(), col.description()); |
253 | werner | 234 | result[result.length()-1]=' '; // clear last newline |
235 | result+="||\n"; |
||
236 | return result; |
||
237 | } |