kexi

kexicsvexport.cpp

00001 /* This file is part of the KDE project
00002    Copyright (C) 2005,2006 Jaroslaw Staniek <js@iidea.pl>
00003 
00004    This library is free software; you can redistribute it and/or
00005    modify it under the terms of the GNU Library General Public
00006    License as published by the Free Software Foundation; either
00007    version 2 of the License, or (at your option) any later version.
00008 
00009    This library is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012    Library General Public License for more details.
00013 
00014    You should have received a copy of the GNU Library General Public License
00015    along with this library; see the file COPYING.LIB.  If not, write to
00016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00017  * Boston, MA 02110-1301, USA.
00018 */
00019 
00020 #include "kexicsvexport.h"
00021 #include "kexicsvwidgets.h"
00022 #include <main/startup/KexiStartupFileDialog.h>
00023 #include <kexidb/cursor.h>
00024 #include <kexidb/utils.h>
00025 #include <core/keximainwindow.h>
00026 #include <core/kexiproject.h>
00027 #include <core/kexipartinfo.h>
00028 #include <core/kexipartmanager.h>
00029 #include <core/kexiguimsghandler.h>
00030 #include <kexiutils/utils.h>
00031 #include <widget/kexicharencodingcombobox.h>
00032 
00033 #include <qcheckbox.h>
00034 #include <qgroupbox.h>
00035 #include <qclipboard.h>
00036 #include <kapplication.h>
00037 #include <klocale.h>
00038 #include <kiconloader.h>
00039 #include <kactivelabel.h>
00040 #include <kpushbutton.h>
00041 #include <kapplication.h>
00042 #include <kdebug.h>
00043 #include <ksavefile.h>
00044 
00045 
00046 using namespace KexiCSVExport;
00047 
00048 Options::Options()
00049  : mode(File), itemId(0), addColumnNames(true)
00050 {
00051 }
00052 
00053 bool Options::assign( QMap<QString,QString>& args )
00054 {
00055     mode = (args["destinationType"]=="file")
00056         ? KexiCSVExport::File : KexiCSVExport::Clipboard;
00057 
00058     if (args.contains("delimiter"))
00059         delimiter = args["delimiter"];
00060     else
00061         delimiter = (mode==File) ? KEXICSV_DEFAULT_FILE_DELIMITER : KEXICSV_DEFAULT_CLIPBOARD_DELIMITER;
00062 
00063     if (args.contains("textQuote"))
00064         textQuote = args["textQuote"];
00065     else
00066         textQuote = (mode==File) ? KEXICSV_DEFAULT_FILE_TEXT_QUOTE : KEXICSV_DEFAULT_CLIPBOARD_TEXT_QUOTE;
00067 
00068     bool ok;
00069     itemId = args["itemId"].toInt(&ok);
00070     if (!ok || itemId<=0)
00071         return false;
00072     if (args.contains("forceDelimiter"))
00073         forceDelimiter = args["forceDelimiter"];
00074     if (args.contains("addColumnNames"))
00075         addColumnNames = (args["addColumnNames"]=="1");
00076     return true;
00077 }
00078 
00079 //------------------------------------
00080 
00081 bool KexiCSVExport::exportData(KexiDB::TableOrQuerySchema& tableOrQuery, 
00082     const Options& options, int rowCount, QTextStream *predefinedTextStream)
00083 {
00084     KexiDB::Connection* conn = tableOrQuery.connection();
00085     if (!conn)
00086         return false;
00087 
00088     if (rowCount == -1)
00089         rowCount = KexiDB::rowCount(tableOrQuery);
00090     if (rowCount == -1)
00091         return false;
00092 
00097 
00100 
00101     KexiDB::QuerySchema* query = tableOrQuery.query();
00102     if (!query)
00103         query = tableOrQuery.table()->query();
00104 
00105     KexiDB::QueryColumnInfo::Vector fields( query->fieldsExpanded( KexiDB::QuerySchema::WithInternalFields ) );
00106     QString buffer;
00107 
00108     KSaveFile *kSaveFile = 0;
00109     QTextStream *stream = 0;
00110 
00111     const bool copyToClipboard = options.mode==Clipboard;
00112     if (copyToClipboard) {
00114         uint bufSize = QMIN((rowCount<0 ? 10 : rowCount) * fields.count() * 20, 128000);
00115         buffer.reserve( bufSize );
00116         if (buffer.capacity() < bufSize) {
00117             kdWarning() << "KexiCSVExportWizard::exportData() cannot allocate memory for " << bufSize 
00118                 << " characters" << endl;
00119             return false;
00120         }
00121     }
00122     else {
00123         if (predefinedTextStream) {
00124             stream = predefinedTextStream;
00125         }
00126         else {
00127             if (options.fileName.isEmpty()) {//sanity
00128                 kdWarning() << "KexiCSVExportWizard::exportData(): fname is empty" << endl;
00129                 return false;
00130             }
00131             kSaveFile = new KSaveFile(options.fileName);
00132             if (0 == kSaveFile->status())
00133                 stream = kSaveFile->textStream();
00134             if (0 != kSaveFile->status() || !stream) {//sanity
00135                 kdWarning() << "KexiCSVExportWizard::exportData(): status != 0 or stream == 0" << endl;
00136                 delete kSaveFile;
00137                 return false;
00138             }
00139         }
00140     }
00141 
00143 
00144 #define _ERR \
00145     delete [] isText; \
00146     if (kSaveFile) { kSaveFile->abort(); delete kSaveFile; } \
00147     return false
00148 
00149 #define APPEND(what) \
00150         if (copyToClipboard) buffer.append(what); else (*stream) << (what)
00151 
00152 // line endings should be as in RFC 4180
00153 #define CSV_EOLN "\r\n"
00154 
00155     // 0. Cache information
00156     const uint fieldsCount = query->fieldsExpanded().count(); //real fields count without internals
00157     const QCString delimiter( options.delimiter.left(1).latin1() );
00158     const bool hasTextQuote = !options.textQuote.isEmpty();
00159     const QString textQuote( options.textQuote.left(1) );
00160     const QCString escapedTextQuote( (textQuote + textQuote).latin1() ); //ok?
00161     //cache for faster checks
00162     bool *isText = new bool[fieldsCount]; 
00163     bool *isDateTime = new bool[fieldsCount]; 
00164     bool *isTime = new bool[fieldsCount]; 
00165     bool *isBLOB = new bool[fieldsCount]; 
00166     uint *visibleFieldIndex = new uint[fieldsCount];
00167 //  bool isInteger[fieldsCount]; //cache for faster checks
00168 //  bool isFloatingPoint[fieldsCount]; //cache for faster checks
00169     for (uint i=0; i<fieldsCount; i++) {
00170         KexiDB::QueryColumnInfo* ci;
00171         const int indexForVisibleLookupValue = fields[i]->indexForVisibleLookupValue();
00172         if (-1 != indexForVisibleLookupValue) {
00173             ci = query->expandedOrInternalField( indexForVisibleLookupValue );
00174             visibleFieldIndex[i] = indexForVisibleLookupValue;
00175         }
00176         else {
00177             ci = fields[i];
00178             visibleFieldIndex[i] = i;
00179         }
00180 
00181         isText[i] = ci->field->isTextType();
00182         isDateTime[i] = ci->field->type()==KexiDB::Field::DateTime;
00183         isTime[i] = ci->field->type()==KexiDB::Field::Time;
00184         isBLOB[i] = ci->field->type()==KexiDB::Field::BLOB;
00185 //      isInteger[i] = fields[i]->field->isIntegerType() 
00186 //          || fields[i]->field->type()==KexiDB::Field::Boolean;
00187 //      isFloatingPoint[i] = fields[i]->field->isFPNumericType();
00188     }
00189 
00190     // 1. Output column names
00191     if (options.addColumnNames) {
00192         for (uint i=0; i<fieldsCount; i++) {
00193             if (i>0)
00194                 APPEND( delimiter );
00195             if (hasTextQuote){
00196                 APPEND( textQuote + fields[i]->captionOrAliasOrName().replace(textQuote, escapedTextQuote) + textQuote );
00197             }
00198             else {
00199                 APPEND( fields[i]->captionOrAliasOrName() );
00200             }
00201         }
00202         APPEND(CSV_EOLN);
00203     }
00204     
00205     KexiGUIMessageHandler handler;
00206     KexiDB::Cursor *cursor = conn->executeQuery(*query);
00207     if (!cursor) {
00208         handler.showErrorMessage(conn);
00209         _ERR;
00210     }
00211     for (cursor->moveFirst(); !cursor->eof() && !cursor->error(); cursor->moveNext()) {
00212         const uint realFieldCount = QMIN(cursor->fieldCount(), fieldsCount);
00213         for (uint i=0; i<realFieldCount; i++) {
00214             const uint real_i = visibleFieldIndex[i];
00215             if (i>0)
00216                 APPEND( delimiter );
00217             if (cursor->value(real_i).isNull())
00218                 continue;
00219             if (isText[real_i]) {
00220                 if (hasTextQuote)
00221                     APPEND( textQuote + QString(cursor->value(real_i).toString()).replace(textQuote, escapedTextQuote) + textQuote );
00222                 else
00223                     APPEND( cursor->value(real_i).toString() );
00224             }
00225             else if (isDateTime[real_i]) { //avoid "T" in ISO DateTime
00226                 APPEND( cursor->value(real_i).toDateTime().date().toString(Qt::ISODate)+" "
00227                     + cursor->value(real_i).toDateTime().time().toString(Qt::ISODate) );
00228             }
00229             else if (isTime[real_i]) { //time is temporarily stored as null date + time...
00230                 APPEND( cursor->value(real_i).toTime().toString(Qt::ISODate) );
00231             }
00232             else if (isBLOB[real_i]) { //BLOB is escaped in a special way
00233                 if (hasTextQuote)
00235                     APPEND( textQuote + KexiDB::escapeBLOB(cursor->value(real_i).toByteArray(), KexiDB::BLOBEscapeHex) + textQuote );
00236                 else
00237                     APPEND( KexiDB::escapeBLOB(cursor->value(real_i).toByteArray(), KexiDB::BLOBEscapeHex) );
00238             }
00239             else {//other types
00240                 APPEND( cursor->value(real_i).toString() );
00241             }
00242         }
00243         APPEND(CSV_EOLN);
00244     }
00245 
00246     if (copyToClipboard)
00247         buffer.squeeze();
00248 
00249     if (!conn->deleteCursor(cursor)) {
00250         handler.showErrorMessage(conn);
00251         _ERR;
00252     }
00253 
00254     if (copyToClipboard)
00255         kapp->clipboard()->setText(buffer, QClipboard::Clipboard);
00256 
00257     delete [] isText;
00258     delete [] isDateTime;
00259     delete [] isTime;
00260     delete [] isBLOB;
00261     delete [] visibleFieldIndex;
00262 
00263     if (kSaveFile) {
00264         if (!kSaveFile->close()) {
00265             kdWarning() << "KexiCSVExportWizard::exportData(): error close(); status == " 
00266                 << kSaveFile->status() << endl;
00267         }
00268         delete kSaveFile;
00269     }
00270     return true;
00271 }
KDE Home | KDE Accessibility Home | Description of Access Keys