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() );
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 = fields.count();
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 //  bool isInteger[fieldsCount]; //cache for faster checks
00167 //  bool isFloatingPoint[fieldsCount]; //cache for faster checks
00168     for (uint i=0; i<fieldsCount; i++) {
00169         isText[i] = fields[i]->field->isTextType();
00170         isDateTime[i] = fields[i]->field->type()==KexiDB::Field::DateTime;
00171         isTime[i] = fields[i]->field->type()==KexiDB::Field::Time;
00172         isBLOB[i] = fields[i]->field->type()==KexiDB::Field::BLOB;
00173 //      isInteger[i] = fields[i]->field->isIntegerType() 
00174 //          || fields[i]->field->type()==KexiDB::Field::Boolean;
00175 //      isFloatingPoint[i] = fields[i]->field->isFPNumericType();
00176     }
00177 
00178     // 1. Output column names
00179     if (options.addColumnNames) {
00180         for (uint i=0; i<fieldsCount; i++) {
00181             if (i>0)
00182                 APPEND( delimiter );
00183             if (hasTextQuote){
00184                 APPEND( textQuote + fields[i]->captionOrAliasOrName().replace(textQuote, escapedTextQuote) + textQuote );
00185             }
00186             else {
00187                 APPEND( fields[i]->captionOrAliasOrName() );
00188             }
00189         }
00190         APPEND(CSV_EOLN);
00191     }
00192     
00193     KexiGUIMessageHandler handler;
00194     KexiDB::Cursor *cursor = conn->executeQuery(*query);
00195     if (!cursor) {
00196         handler.showErrorMessage(conn);
00197         _ERR;
00198     }
00199     for (cursor->moveFirst(); !cursor->eof() && !cursor->error(); cursor->moveNext()) {
00200         const uint realFieldCount = QMIN(cursor->fieldCount(), fieldsCount);
00201         for (uint i=0; i<realFieldCount; i++) {
00202             if (i>0)
00203                 APPEND( delimiter );
00204             if (cursor->value(i).isNull())
00205                 continue;
00206             if (isText[i]) {
00207                 if (hasTextQuote)
00208                     APPEND( textQuote + QString(cursor->value(i).toString()).replace(textQuote, escapedTextQuote) + textQuote );
00209                 else
00210                     APPEND( cursor->value(i).toString() );
00211             }
00212             else if (isDateTime[i]) { //avoid "T" in ISO DateTime
00213                 APPEND( cursor->value(i).toDateTime().date().toString(Qt::ISODate)+" "
00214                     + cursor->value(i).toDateTime().time().toString(Qt::ISODate) );
00215             }
00216             else if (isTime[i]) { //time is temporarily stored as null date + time...
00217                 APPEND( cursor->value(i).toTime().toString(Qt::ISODate) );
00218             }
00219             else if (isBLOB[i]) { //BLOB is escaped in a special way
00220                 if (hasTextQuote)
00222                     APPEND( textQuote + KexiDB::escapeBLOB(cursor->value(i).toByteArray(), KexiDB::BLOBEscapeHex) + textQuote );
00223                 else
00224                     APPEND( KexiDB::escapeBLOB(cursor->value(i).toByteArray(), KexiDB::BLOBEscapeHex) );
00225             }
00226             else {//other types
00227                 APPEND( cursor->value(i).toString() );
00228             }
00229         }
00230         APPEND(CSV_EOLN);
00231     }
00232 
00233     if (copyToClipboard)
00234         buffer.squeeze();
00235 
00236     if (!conn->deleteCursor(cursor)) {
00237         handler.showErrorMessage(conn);
00238         _ERR;
00239     }
00240 
00241     if (copyToClipboard)
00242         kapp->clipboard()->setText(buffer, QClipboard::Clipboard);
00243 
00244     delete [] isText;
00245     delete [] isDateTime;
00246     delete [] isTime;
00247     delete [] isBLOB;
00248 
00249     if (kSaveFile) {
00250         if (!kSaveFile->close()) {
00251             kdWarning() << "KexiCSVExportWizard::exportData(): error close(); status == " 
00252                 << kSaveFile->status() << endl;
00253         }
00254         delete kSaveFile;
00255     }
00256     return true;
00257 }
KDE Home | KDE Accessibility Home | Description of Access Keys