diff options
Diffstat (limited to 'src/translators')
22 files changed, 1785 insertions, 21 deletions
diff --git a/src/translators/Makefile.am b/src/translators/Makefile.am index fbbb11b..f0e8520 100644 --- a/src/translators/Makefile.am +++ b/src/translators/Makefile.am @@ -12,9 +12,9 @@ libtranslators_a_SOURCES = alexandriaexporter.cpp alexandriaimporter.cpp \ filelistingimporter.cpp freedb_util.cpp freedbimporter.cpp gcfilmsexporter.cpp \ gcfilmsimporter.cpp griffithimporter.cpp grs1importer.cpp htmlexporter.cpp libcsv.c \ onixexporter.cpp pdfimporter.cpp pilotdbexporter.cpp referencerimporter.cpp \ - risimporter.cpp tellico_xml.cpp tellicoimporter.cpp tellicoxmlexporter.cpp \ - tellicozipexporter.cpp textimporter.cpp xmlimporter.cpp xsltexporter.cpp xslthandler.cpp \ - xsltimporter.cpp + risimporter.cpp tellico_xml.cpp tellicoimporter.cpp tellicosaximporter.cpp \ + tellicoxmlexporter.cpp tellicoxmlhandler.cpp tellicozipexporter.cpp textimporter.cpp \ + xmlimporter.cpp xmlstatehandler.cpp xsltexporter.cpp xslthandler.cpp xsltimporter.cpp if !USE_LIBBTPARSE SUBDIR_LIBBTPARSE = btparse @@ -51,7 +51,10 @@ dcimporter.h dcimporter.cpp griffithimporter.h \ griffithimporter.cpp griffith2tellico.py pdfimporter.h \ pdfimporter.cpp referencerimporter.h referencerimporter.cpp \ libcsv.h libcsv.c \ -deliciousimporter.h deliciousimporter.cpp +deliciousimporter.h deliciousimporter.cpp \ +tellicosaximporter.h tellicosaximporter.cpp \ +tellicoxmlhandler.h tellicoxmlhandler.cpp \ +xmlstatehandler.h xmlstatehandler.cpp ####### tdevelop will overwrite this part!!! (end)############ @@ -68,3 +71,4 @@ KDE_OPTIONS = noautodist appdir = $(kde_datadir)/tellico app_DATA = bibtex-translation.xml app_SCRIPTS = griffith2tellico.py + diff --git a/src/translators/alexandriaexporter.cpp b/src/translators/alexandriaexporter.cpp index de64fbb..633864f 100644 --- a/src/translators/alexandriaexporter.cpp +++ b/src/translators/alexandriaexporter.cpp @@ -77,7 +77,7 @@ bool AlexandriaExporter::exec() { ProgressItem& item = ProgressManager::self()->newProgressItem(this, TQString(), false); item.setTotalSteps(entries().count()); ProgressItem::Done done(this); - const uint stepSize = TQMIN(1, entries().count()/100); + const uint stepSize = TQMAX(1, entries().count()/100); const bool showProgress = options() & ExportProgress; GUI::CursorSaver cs; diff --git a/src/translators/bibteximporter.cpp b/src/translators/bibteximporter.cpp index fb52f95..d3668d4 100644 --- a/src/translators/bibteximporter.cpp +++ b/src/translators/bibteximporter.cpp @@ -308,5 +308,47 @@ TQWidget* BibtexImporter::widget(TQWidget* parent_, const char* name_/*=0*/) { return m_widget; } +bool BibtexImporter::maybeBibtex(const KURL& url_) { + TQString text = FileHandler::readTextFile(url_, true /*quiet*/); + if(text.isEmpty()) { + return false; + } + + bt_initialize(); + TQRegExp rx(TQString::fromLatin1("[{}]")); + + ushort bt_options = 0; // ushort is defined in btparse.h + boolean ok; // boolean is defined in btparse.h as an int + bool foundOne = false; + int brace = 0; + int startpos = 0; + int pos = text.find(rx, 0); + while(pos > 0) { + if(text[pos] == '{') { + ++brace; + } else if(text[pos] == '}' && brace > 0) { + --brace; + } + if(brace == 0) { + TQString entry = text.mid(startpos, pos-startpos+1).stripWhiteSpace(); + // All the downstream text processing on the AST node will assume utf-8 + AST* node = bt_parse_entry_s(const_cast<char*>(entry.utf8().data()), + const_cast<char*>(url_.fileName().local8Bit().data()), + 0, bt_options, &ok); + if(ok && node) { + foundOne = true; + break; + } + startpos = pos+1; + } + pos = text.find(rx, pos+1); + } + if(foundOne) { + // clean up some structures + bt_parse_entry_s(0, 0, 1, 0, 0); + } + bt_cleanup(); + return foundOne; +} #include "bibteximporter.moc" diff --git a/src/translators/bibteximporter.h b/src/translators/bibteximporter.h index 6d1b878..09e1ec3 100644 --- a/src/translators/bibteximporter.h +++ b/src/translators/bibteximporter.h @@ -67,6 +67,8 @@ public: virtual TQWidget* widget(TQWidget* parent, const char* name=0); virtual bool canImport(int type) const; + static bool maybeBibtex(const KURL& url); + public slots: void slotCancel(); diff --git a/src/translators/csvimporter.cpp b/src/translators/csvimporter.cpp index 8a53ff9..d6198d0 100644 --- a/src/translators/csvimporter.cpp +++ b/src/translators/csvimporter.cpp @@ -50,15 +50,26 @@ extern "C" { using Tellico::Import::CSVImporter; +typedef int(*SpaceFunc)(char); + static void writeToken(char* buffer, size_t len, void* data); static void writeRow(char buffer, void* data); +static int isSpace(char c); +static int isSpaceOrTab(char c); +static int isTab(char c); class CSVImporter::Parser { public: Parser(const TQString& str) : stream(new TQTextIStream(&str)) { csv_init(&parser, 0); } ~Parser() { csv_free(parser); delete stream; stream = 0; } - void setDelimiter(const TQString& s) { Q_ASSERT(s.length() == 1); csv_set_delim(parser, s[0].latin1()); } + void setDelimiter(const TQString& s) { + Q_ASSERT(s.length() == 1); + csv_set_delim(parser, s[0].latin1()); + if(s[0] == '\t') csv_set_space_func(parser, isSpace); + else if(s[0] == ' ') csv_set_space_func(parser, isTab); + else csv_set_space_func(parser, isSpaceOrTab); + } void reset(const TQString& str) { delete stream; stream = new TQTextIStream(&str); }; bool hasNext() { return !stream->atEnd(); } void skipLine() { stream->readLine(); } @@ -95,6 +106,21 @@ static void writeRow(char c, void* data) { p->setRowDone(true); } +static int isSpace(char c) { + if (c == CSV_SPACE) return 1; + return 0; +} + +static int isSpaceOrTab(char c) { + if (c == CSV_SPACE || c == CSV_TAB) return 1; + return 0; +} + +static int isTab(char c) { + if (c == CSV_TAB) return 1; + return 0; +} + CSVImporter::CSVImporter(const KURL& url_) : Tellico::Import::TextImporter(url_), m_coll(0), m_existingCollection(0), diff --git a/src/translators/deliciousimporter.cpp b/src/translators/deliciousimporter.cpp index a82c006..be6fe3e 100644 --- a/src/translators/deliciousimporter.cpp +++ b/src/translators/deliciousimporter.cpp @@ -35,7 +35,9 @@ DeliciousImporter::DeliciousImporter(const KURL& url_) : XSLTImporter(url_) { } bool DeliciousImporter::canImport(int type) const { - return type == Data::Collection::Book; + return type == Data::Collection::Book || + type == Data::Collection::Video || + type == Data::Collection::Game; } Tellico::Data::CollPtr DeliciousImporter::collection() { @@ -51,7 +53,18 @@ Tellico::Data::CollPtr DeliciousImporter::collection() { << TQString::fromLatin1("Medium Covers/") << TQString::fromLatin1("Small Covers/") << TQString::fromLatin1("Plain Covers/"); - const TQString commField = TQString::fromLatin1("comments"); + TQString commField; + switch(coll->type()) { + case Data::Collection::Book: + commField = TQString::fromLatin1("comments"); break; + case Data::Collection::Video: + commField = TQString::fromLatin1("plot"); break; + case Data::Collection::Game: + commField = TQString::fromLatin1("description"); break; + default: + myWarning() << "bad collection type:" << coll->type() << endl; + } + const TQString uuidField = TQString::fromLatin1("uuid"); const TQString coverField = TQString::fromLatin1("cover"); const bool isLocal = url().isLocalFile(); diff --git a/src/translators/freedb_util.cpp b/src/translators/freedb_util.cpp index 07292af..2a2e5ed 100644 --- a/src/translators/freedb_util.cpp +++ b/src/translators/freedb_util.cpp @@ -365,7 +365,7 @@ FreeDBImporter::CDText FreeDBImporter::getCDText(const TQCString& drive_) { } } if(cdtext.trackTitles.size() != cdtext.trackArtists.size()) { - int size = TQMAX(cdtext.trackTitles.size(), cdtext.trackArtists.size()); + size_t size = TQMAX(cdtext.trackTitles.size(), cdtext.trackArtists.size()); cdtext.trackTitles.resize(size); cdtext.trackArtists.resize(size); } diff --git a/src/translators/htmlexporter.cpp b/src/translators/htmlexporter.cpp index e27d6e5..e8bb95d 100644 --- a/src/translators/htmlexporter.cpp +++ b/src/translators/htmlexporter.cpp @@ -31,6 +31,7 @@ #include <tdeio/netaccess.h> #include <tdeapplication.h> #include <tdelocale.h> +#include <kuser.h> #include <tqdom.h> #include <tqgroupbox.h> @@ -192,6 +193,9 @@ bool HTMLExporter::loadXSLTFile() { m_handler = 0; return false; } + m_handler->addStringParam("date", TQDate::currentDate().toString(TQt::ISODate).latin1()); + m_handler->addStringParam("time", TQTime::currentTime().toString(TQt::ISODate).latin1()); + m_handler->addStringParam("user", KUser(KUser::UseRealUserID).loginName().latin1()); if(m_exportEntryFiles) { // export entries to same place as all the other date files diff --git a/src/translators/risimporter.cpp b/src/translators/risimporter.cpp index 735368c..0158708 100644 --- a/src/translators/risimporter.cpp +++ b/src/translators/risimporter.cpp @@ -188,7 +188,7 @@ void RISImporter::readURL(const KURL& url_, int n, const TQDict<Data::Field>& ri // technically, the spec requires a space immediately after the hyphen // however, at least one website (Springer) outputs RIS with no space after the final "ER -" // so just strip the white space later - // also be gracious and allow only any amount of space before hyphen + // also be gracious and allow any amount of space before hyphen TQRegExp rx(TQString::fromLatin1("^(\\w\\w)\\s+-(.*)$")); TQString currLine, nextLine; for(currLine = t.readLine(); !m_cancelled && !currLine.isNull(); currLine = nextLine, j += currLine.length()) { @@ -312,4 +312,25 @@ void RISImporter::slotCancel() { m_cancelled = true; } +bool RISImporter::maybeRIS(const KURL& url_) { + TQString text = FileHandler::readTextFile(url_, true /*quiet*/); + if(text.isEmpty()) { + return false; + } + + // bare bones check, strip white space at beginning + // and then first text line must be valid RIS + TQTextIStream t(&text); + + TQRegExp rx(TQString::fromLatin1("^(\\w\\w)\\s+-(.*)$")); + TQString currLine; + for(currLine = t.readLine(); !currLine.isNull(); currLine = t.readLine()) { + if(currLine.stripWhiteSpace().isEmpty()) { + continue; + } + break; + } + return rx.exactMatch(currLine); +} + #include "risimporter.moc" diff --git a/src/translators/risimporter.h b/src/translators/risimporter.h index fa581ba..a572283 100644 --- a/src/translators/risimporter.h +++ b/src/translators/risimporter.h @@ -50,6 +50,8 @@ public: virtual TQWidget* widget(TQWidget*, const char*) { return 0; } virtual bool canImport(int type) const; + static bool maybeRIS(const KURL& url); + public slots: void slotCancel(); diff --git a/src/translators/tellico_xml.cpp b/src/translators/tellico_xml.cpp index bcfb412..3a927d6 100644 --- a/src/translators/tellico_xml.cpp +++ b/src/translators/tellico_xml.cpp @@ -63,6 +63,12 @@ TQString Tellico::XML::dtdTellico(int version) { return TQString::fromLatin1("http://periapsis.org/tellico/dtd/v%1/tellico.dtd").arg(version); } +// returns true if the file has to be converted +// version 9 to 10 requires no conversion since it only added board games +bool Tellico::XML::versionConversion(uint from, uint to) { + return from < to && (from != 9 || to != 10); +} + bool Tellico::XML::validXMLElementName(const TQString& name_) { return xmlValidateNameValue((xmlChar *)name_.utf8().data()); } diff --git a/src/translators/tellico_xml.h b/src/translators/tellico_xml.h index 6ff4c1b..6ffda10 100644 --- a/src/translators/tellico_xml.h +++ b/src/translators/tellico_xml.h @@ -28,6 +28,8 @@ namespace Tellico { TQString pubTellico(int version = syntaxVersion); TQString dtdTellico(int version = syntaxVersion); + bool versionConversion(uint from, uint to); + extern const TQString nsBookcase; extern const TQString nsDublinCore; extern const TQString nsZing; diff --git a/src/translators/tellicoimporter.cpp b/src/translators/tellicoimporter.cpp index a8dfb62..d722c0c 100644 --- a/src/translators/tellicoimporter.cpp +++ b/src/translators/tellicoimporter.cpp @@ -39,11 +39,6 @@ using Tellico::Import::TellicoImporter; -bool TellicoImporter::versionConversion(uint from, uint to) { - // version 10 only added board games to version 9 - return from < to && (from != 9 || to != 10); -} - TellicoImporter::TellicoImporter(const KURL& url_, bool loadAllImages_) : DataImporter(url_), m_coll(0), m_loadAllImages(loadAllImages_), m_format(Unknown), m_modified(false), m_cancelled(false), m_hasImages(false), m_buffer(0), m_zip(0), m_imgDir(0) { @@ -157,7 +152,7 @@ void TellicoImporter::loadXMLData(const TQByteArray& data_, bool loadImages_) { } m_format = Error; return; - } else if(versionConversion(syntaxVersion, XML::syntaxVersion)) { + } else if(XML::versionConversion(syntaxVersion, XML::syntaxVersion)) { // going form version 9 to 10, there's no conversion needed TQString str = i18n("Tellico is converting the file to a more recent document format. " "Information loss may occur if an older version of Tellico is used " @@ -374,7 +369,7 @@ void TellicoImporter::readField(uint syntaxVersion_, const TQDomElement& elem_) Data::FieldPtr field; if(type == Data::Field::Choice) { - TQStringList allowed = TQStringList::split(TQString::fromLatin1(";"), + TQStringList allowed = TQStringList::split(TQRegExp(TQString::fromLatin1("\\s*;\\s*")), elem_.attribute(TQString::fromLatin1("allowed"))); if(isI18n) { for(TQStringList::Iterator it = allowed.begin(); it != allowed.end(); ++it) { @@ -652,8 +647,9 @@ void TellicoImporter::readEntry(uint syntaxVersion_, const TQDomElement& entryEl void TellicoImporter::readImage(const TQDomElement& elem_, bool loadImage_) { TQString format = elem_.attribute(TQString::fromLatin1("format")); const bool link = elem_.attribute(TQString::fromLatin1("link")) == Latin1Literal("true"); - TQString id = shareString(link ? elem_.attribute(TQString::fromLatin1("id")) - : Data::Image::idClean(elem_.attribute(TQString::fromLatin1("id")))); + // idClean() already calls shareString() + TQString id = link ? shareString(elem_.attribute(TQString::fromLatin1("id"))) + : Data::Image::idClean(elem_.attribute(TQString::fromLatin1("id"))); bool readInfo = true; if(loadImage_) { diff --git a/src/translators/tellicoimporter.h b/src/translators/tellicoimporter.h index 93cf7da..6ea0ac5 100644 --- a/src/translators/tellicoimporter.h +++ b/src/translators/tellicoimporter.h @@ -70,8 +70,6 @@ public slots: void slotCancel(); private: - static bool versionConversion(uint from, uint to); - void loadXMLData(const TQByteArray& data, bool loadImages); void loadZipData(); diff --git a/src/translators/tellicosaximporter.cpp b/src/translators/tellicosaximporter.cpp new file mode 100644 index 0000000..de60c63 --- /dev/null +++ b/src/translators/tellicosaximporter.cpp @@ -0,0 +1,293 @@ +/*************************************************************************** + copyright : (C) 2008 by Robby Stephenson + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of version 2 of the GNU General Public License as * + * published by the Free Software Foundation; * + * * + ***************************************************************************/ + +// before tellicosaxmporter.h because of QT_NO_CAST_ASCII issues +#include "tellicoxmlhandler.h" +#include "tellicosaximporter.h" +#include "tellico_xml.h" +#include "../collectionfactory.h" +#include "../collections/bibtexcollection.h" +#include "../entry.h" +#include "../field.h" +#include "../imagefactory.h" +#include "../image.h" +#include "../isbnvalidator.h" +#include "../latin1literal.h" +#include "../tellico_strings.h" +#include "../tellico_kernel.h" +#include "../tellico_utils.h" +#include "../tellico_debug.h" +#include "../progressmanager.h" + +#include <tdelocale.h> +#include <kmdcodec.h> +#include <kzip.h> +#include <tdeapplication.h> + +#include <tqbuffer.h> +#include <tqfile.h> +#include <tqtimer.h> + +using Tellico::Import::TellicoSaxImporter; + +TellicoSaxImporter::TellicoSaxImporter(const KURL& url_, bool loadAllImages_) : DataImporter(url_), + m_coll(0), m_loadAllImages(loadAllImages_), m_format(Unknown), m_modified(false), + m_cancelled(false), m_hasImages(false), m_buffer(0), m_zip(0), m_imgDir(0) { +} + +TellicoSaxImporter::TellicoSaxImporter(const TQString& text_) : DataImporter(text_), + m_coll(0), m_loadAllImages(true), m_format(Unknown), m_modified(false), + m_cancelled(false), m_hasImages(false), m_buffer(0), m_zip(0), m_imgDir(0) { +} + +TellicoSaxImporter::~TellicoSaxImporter() { + if(m_zip) { + m_zip->close(); + } + delete m_zip; + m_zip = 0; + delete m_buffer; + m_buffer = 0; +} + +Tellico::Data::CollPtr TellicoSaxImporter::collection() { + if(m_coll) { + return m_coll; + } + + TQCString s; // read first 5 characters + if(source() == URL) { + if(!fileRef().open()) { + return 0; + } + TQIODevice* f = fileRef().file(); + for(uint i = 0; i < 5; ++i) { + s += static_cast<char>(f->getch()); + } + f->reset(); + } else { + if(data().size() < 5) { + m_format = Error; + return 0; + } + s = TQCString(data(), 6); + } + + // need to decide if the data is xml text, or a zip file + // if the first 5 characters are <?xml then treat it like text + if(s[0] == '<' && s[1] == '?' && s[2] == 'x' && s[3] == 'm' && s[4] == 'l') { + m_format = XML; + loadXMLData(source() == URL ? fileRef().file()->readAll() : data(), true); + } else { + m_format = Zip; + loadZipData(); + } + return m_coll; +} + +void TellicoSaxImporter::loadXMLData(const TQByteArray& data_, bool loadImages_) { + ProgressItem& item = ProgressManager::self()->newProgressItem(this, progressLabel(), true); + item.setTotalSteps(data_.size()); + connect(&item, SIGNAL(signalCancelled(ProgressItem*)), SLOT(slotCancel())); + ProgressItem::Done done(this); + + const bool showProgress = options() & ImportProgress; + + TellicoXMLHandler handler; + handler.setLoadImages(loadImages_); + + TQXmlSimpleReader reader; + reader.setContentHandler(&handler); + + TQXmlInputSource source; + bool success = reader.parse(&source, true); + + const uint blockSize = data_.size()/100 + 1; + uint pos = 0; + TQByteArray block; + + while(success && !m_cancelled && pos < data_.size()) { + uint size = TQMIN(blockSize, data_.size() - pos); + block.setRawData(data_.data() + pos, size); + source.setData(block); + success = reader.parseContinue(); + block.resetRawData(data_.data() + pos, size); + pos += blockSize; + if(showProgress) { + ProgressManager::self()->setProgress(this, pos); + kapp->processEvents(); + } + } + + if(!success) { + m_format = Error; + TQString error; + if(!url().isEmpty()) { + error = i18n(errorLoad).arg(url().fileName()) + TQChar('\n'); + } + error += handler.errorString(); + myDebug() << error << endl; + setStatusMessage(error); + return; + } + + if(!m_cancelled) { + m_hasImages = handler.hasImages(); + m_coll = handler.collection(); + } +} + +void TellicoSaxImporter::loadZipData() { + delete m_buffer; + delete m_zip; + if(source() == URL) { + m_buffer = 0; + m_zip = new KZip(fileRef().fileName()); + } else { + m_buffer = new TQBuffer(data()); + m_zip = new KZip(m_buffer); + } + if(!m_zip->open(IO_ReadOnly)) { + setStatusMessage(i18n(errorLoad).arg(url().fileName())); + m_format = Error; + delete m_zip; + m_zip = 0; + delete m_buffer; + m_buffer = 0; + return; + } + + const KArchiveDirectory* dir = m_zip->directory(); + if(!dir) { + TQString str = i18n(errorLoad).arg(url().fileName()) + TQChar('\n'); + str += i18n("The file is empty."); + setStatusMessage(str); + m_format = Error; + m_zip->close(); + delete m_zip; + m_zip = 0; + delete m_buffer; + m_buffer = 0; + return; + } + + // main file was changed from bookcase.xml to tellico.xml as of version 0.13 + const KArchiveEntry* entry = dir->entry(TQString::fromLatin1("tellico.xml")); + if(!entry) { + entry = dir->entry(TQString::fromLatin1("bookcase.xml")); + } + if(!entry || !entry->isFile()) { + TQString str = i18n(errorLoad).arg(url().fileName()) + TQChar('\n'); + str += i18n("The file contains no collection data."); + setStatusMessage(str); + m_format = Error; + m_zip->close(); + delete m_zip; + m_zip = 0; + delete m_buffer; + m_buffer = 0; + return; + } + + const TQByteArray xmlData = static_cast<const KArchiveFile*>(entry)->data(); + loadXMLData(xmlData, false); + if(!m_coll) { + m_format = Error; + m_zip->close(); + delete m_zip; + m_zip = 0; + delete m_buffer; + m_buffer = 0; + return; + } + + if(m_cancelled) { + m_zip->close(); + delete m_zip; + m_zip = 0; + delete m_buffer; + m_buffer = 0; + return; + } + + const KArchiveEntry* imgDirEntry = dir->entry(TQString::fromLatin1("images")); + if(!imgDirEntry || !imgDirEntry->isDirectory()) { + m_zip->close(); + delete m_zip; + m_zip = 0; + delete m_buffer; + m_buffer = 0; + return; + } + m_imgDir = static_cast<const KArchiveDirectory*>(imgDirEntry); + m_images.clear(); + m_images.add(m_imgDir->entries()); + m_hasImages = !m_images.isEmpty(); + + // if all the images are not to be loaded, then we're done + if(!m_loadAllImages) { +// myLog() << "TellicoSaxImporter::loadZipData() - delayed loading for " << m_images.count() << " images" << endl; + return; + } + + const TQStringList images = static_cast<const KArchiveDirectory*>(imgDirEntry)->entries(); + const uint stepSize = TQMAX(s_stepSize, images.count()/100); + + uint j = 0; + for(TQStringList::ConstIterator it = images.begin(); !m_cancelled && it != images.end(); ++it, ++j) { + const KArchiveEntry* file = m_imgDir->entry(*it); + if(file && file->isFile()) { + ImageFactory::addImage(static_cast<const KArchiveFile*>(file)->data(), + (*it).section('.', -1).upper(), (*it)); + m_images.remove(*it); + } + if(j%stepSize == 0) { + kapp->processEvents(); + } + } + + if(m_images.isEmpty()) { + // give it some time + TQTimer::singleShot(3000, this, SLOT(deleteLater())); + } +} + +bool TellicoSaxImporter::hasImages() const { + return m_hasImages; +} + +bool TellicoSaxImporter::loadImage(const TQString& id_) { +// myLog() << "TellicoSaxImporter::loadImage() - id = " << id_ << endl; + if(m_format != Zip || !m_imgDir) { + return false; + } + const KArchiveEntry* file = m_imgDir->entry(id_); + if(!file || !file->isFile()) { + return false; + } + TQString newID = ImageFactory::addImage(static_cast<const KArchiveFile*>(file)->data(), + id_.section('.', -1).upper(), id_); + m_images.remove(id_); + if(m_images.isEmpty()) { + // give it some time + TQTimer::singleShot(3000, this, SLOT(deleteLater())); + } + return !newID.isEmpty(); +} + +void TellicoSaxImporter::slotCancel() { + m_cancelled = true; + m_format = Cancel; +} + +#include "tellicosaximporter.moc" diff --git a/src/translators/tellicosaximporter.h b/src/translators/tellicosaximporter.h new file mode 100644 index 0000000..056f60f --- /dev/null +++ b/src/translators/tellicosaximporter.h @@ -0,0 +1,87 @@ +/*************************************************************************** + copyright : (C) 2008 by Robby Stephenson + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of version 2 of the GNU General Public License as * + * published by the Free Software Foundation; * + * * + ***************************************************************************/ + +#ifndef TELLICO_IMPORT_TELLICOSAXIMPORTER_H +#define TELLICO_IMPORT_TELLICOSAXIMPORTER_H + +#include "dataimporter.h" +#include "../datavectors.h" +#include "../stringset.h" + +class TQBuffer; +class KZip; +class KArchiveDirectory; + +namespace Tellico { + namespace Import { + +/** + * @author Robby Stephenson + */ +class TellicoSaxImporter : public DataImporter { +Q_OBJECT + +public: + enum Format { Unknown, Error, XML, Zip, Cancel }; + + /** + * @param url The tellico data file. + */ + TellicoSaxImporter(const KURL& url, bool loadAllImages=true); + /** + * Constructor used to convert arbitrary text to a @ref Collection + * + * @param text The text + */ + TellicoSaxImporter(const TQString& text); + virtual ~TellicoSaxImporter(); + + /** + * sometimes, a new document format might add data + */ + bool modifiedOriginal() const { return m_modified; } + + /** + */ + virtual Data::CollPtr collection(); + Format format() const { return m_format; } + + bool hasImages() const; + bool loadImage(const TQString& id_); + + static bool loadAllImages(const KURL& url); + +public slots: + void slotCancel(); + +private: + void loadXMLData(const TQByteArray& data, bool loadImages); + void loadZipData(); + + Data::CollPtr m_coll; + bool m_loadAllImages; + TQString m_namespace; + Format m_format; + bool m_modified : 1; + bool m_cancelled : 1; + bool m_hasImages : 1; + StringSet m_images; + + TQBuffer* m_buffer; + KZip* m_zip; + const KArchiveDirectory* m_imgDir; +}; + + } // end namespace +} // end namespace +#endif diff --git a/src/translators/tellicoxmlhandler.cpp b/src/translators/tellicoxmlhandler.cpp new file mode 100644 index 0000000..5b3f063 --- /dev/null +++ b/src/translators/tellicoxmlhandler.cpp @@ -0,0 +1,74 @@ +/*************************************************************************** + copyright : (C) 2008 by Robby Stephenson + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of version 2 of the GNU General Public License as * + * published by the Free Software Foundation; * + * * + ***************************************************************************/ + +#include "tellicoxmlhandler.h" +#include "../collection.h" +#include "../tellico_debug.h" + +using Tellico::Import::TellicoXMLHandler; + +TellicoXMLHandler::TellicoXMLHandler() : TQXmlDefaultHandler(), m_data(new SAX::StateData) { + m_handlers.setAutoDelete(true); + m_handlers.push(new SAX::RootHandler(m_data)); +} + +TellicoXMLHandler::~TellicoXMLHandler() { + delete m_data; + m_data = 0; +} + +bool TellicoXMLHandler::startElement(const TQString& nsURI_, const TQString& localName_, + const TQString& qName_, const TQXmlAttributes& atts_) { + SAX::StateHandler* handler = m_handlers.top()->nextHandler(nsURI_, localName_, qName_); + Q_ASSERT(handler); + m_handlers.push(handler); + return handler->start(nsURI_, localName_, qName_, atts_); +} + +bool TellicoXMLHandler::endElement(const TQString& nsURI_, const TQString& localName_, + const TQString& qName_) { + m_data->text = m_data->text.stripWhiteSpace(); +/* + if(!m_data->text.isEmpty()) { + myDebug() << " text: " << m_text << endl; + } +*/ + + SAX::StateHandler* handler = m_handlers.pop(); + bool res = handler->end(nsURI_, localName_, qName_); + // need to reset character data, too + m_data->text = TQString(); + delete handler; + return res; +} + +bool TellicoXMLHandler::characters(const TQString& ch_) { + m_data->text += ch_; + return true; +} + +TQString TellicoXMLHandler::errorString() { + return m_data->error; +} + +Tellico::Data::CollPtr TellicoXMLHandler::collection() const { + return m_data->coll; +} + +bool TellicoXMLHandler::hasImages() const { + return m_data->hasImages; +} + +void TellicoXMLHandler::setLoadImages(bool loadImages_) { + m_data->loadImages = loadImages_; +} diff --git a/src/translators/tellicoxmlhandler.h b/src/translators/tellicoxmlhandler.h new file mode 100644 index 0000000..1edeacc --- /dev/null +++ b/src/translators/tellicoxmlhandler.h @@ -0,0 +1,49 @@ +/*************************************************************************** + copyright : (C) 2008 by Robby Stephenson + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of version 2 of the GNU General Public License as * + * published by the Free Software Foundation; * + * * + ***************************************************************************/ + +#ifndef TELLICO_IMPORT_TELLICOXMLHANDLER_H +#define TELLICO_IMPORT_TELLICOXMLHANDLER_H + +#include "xmlstatehandler.h" + +#include <tqptrstack.h> + +namespace Tellico { + namespace Import { + +class TellicoXMLHandler : public TQXmlDefaultHandler { +public: + TellicoXMLHandler(); + ~TellicoXMLHandler(); + + virtual bool startElement(const TQString& namespaceURI, const TQString& localName, + const TQString& qName, const TQXmlAttributes& atts); + virtual bool endElement(const TQString& namespaceURI, const TQString& localName, + const TQString& qName); + virtual bool characters(const TQString& ch); + + virtual TQString errorString(); + + Data::CollPtr collection() const; + bool hasImages() const; + + void setLoadImages(bool loadImages); + +private: + TQPtrStack<SAX::StateHandler> m_handlers; + SAX::StateData* m_data; +}; + + } +} +#endif diff --git a/src/translators/xmlstatehandler.cpp b/src/translators/xmlstatehandler.cpp new file mode 100644 index 0000000..48d52d6 --- /dev/null +++ b/src/translators/xmlstatehandler.cpp @@ -0,0 +1,772 @@ +/*************************************************************************** + copyright : (C) 2008 by Robby Stephenson + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of version 2 of the GNU General Public License as * + * published by the Free Software Foundation; * + * * + ***************************************************************************/ + +#include "xmlstatehandler.h" +#include "tellico_xml.h" +#include "../latin1literal.h" +#include "../collection.h" +#include "../collectionfactory.h" +#include "../collections/bibtexcollection.h" +#include "../image.h" +#include "../imagefactory.h" +#include "../isbnvalidator.h" +#include "../tellico_utils.h" +#include "../tellico_debug.h" + +#include <tdelocale.h> +#include <kmdcodec.h> + +namespace { + +inline +TQString attValue(const TQXmlAttributes& atts, const char* name, const TQString& defaultValue=TQString()) { + int idx = atts.index(TQString::fromLatin1(name)); + return idx < 0 ? defaultValue : atts.value(idx); +} + +inline +TQString attValue(const TQXmlAttributes& atts, const char* name, const char* defaultValue) { + Q_ASSERT(defaultValue); + return attValue(atts, name, TQString::fromLatin1(defaultValue)); +} + +} + +using Tellico::Import::SAX::StateHandler; +using Tellico::Import::SAX::NullHandler; +using Tellico::Import::SAX::RootHandler; +using Tellico::Import::SAX::DocumentHandler; +using Tellico::Import::SAX::CollectionHandler; +using Tellico::Import::SAX::FieldsHandler; +using Tellico::Import::SAX::FieldHandler; +using Tellico::Import::SAX::FieldPropertyHandler; +using Tellico::Import::SAX::BibtexPreambleHandler; +using Tellico::Import::SAX::BibtexMacrosHandler; +using Tellico::Import::SAX::BibtexMacroHandler; +using Tellico::Import::SAX::EntryHandler; +using Tellico::Import::SAX::FieldValueContainerHandler; +using Tellico::Import::SAX::FieldValueHandler; +using Tellico::Import::SAX::DateValueHandler; +using Tellico::Import::SAX::TableColumnHandler; +using Tellico::Import::SAX::ImagesHandler; +using Tellico::Import::SAX::ImageHandler; +using Tellico::Import::SAX::FiltersHandler; +using Tellico::Import::SAX::FilterHandler; +using Tellico::Import::SAX::FilterRuleHandler; +using Tellico::Import::SAX::BorrowersHandler; +using Tellico::Import::SAX::BorrowerHandler; +using Tellico::Import::SAX::LoanHandler; + +StateHandler* StateHandler::nextHandler(const TQString& ns_, const TQString& localName_, const TQString& qName_) { + StateHandler* handler = nextHandlerImpl(ns_, localName_, qName_); + if(!handler) { + myWarning() << "StateHandler::nextHandler() - no handler for " << localName_ << endl; + } + return handler ? handler : new NullHandler(d); +} + +StateHandler* RootHandler::nextHandlerImpl(const TQString&, const TQString& localName_, const TQString&) { + if(localName_ == Latin1Literal("tellico") || localName_ == Latin1Literal("bookcase")) { + return new DocumentHandler(d); + } + return new RootHandler(d); +} + +StateHandler* DocumentHandler::nextHandlerImpl(const TQString&, const TQString& localName_, const TQString&) { + if(localName_ == Latin1Literal("collection")) { + return new CollectionHandler(d); + } else if(localName_ == Latin1Literal("filters")) { + return new FiltersHandler(d); + } else if(localName_ == Latin1Literal("borrowers")) { + return new BorrowersHandler(d); + } + return 0; +} + +bool DocumentHandler::start(const TQString&, const TQString& localName_, const TQString&, const TQXmlAttributes& atts_) { + // the syntax version field name changed from "version" to "syntaxVersion" in version 3 + int idx = atts_.index(TQString::fromLatin1("syntaxVersion")); + if(idx < 0) { + idx = atts_.index(TQString::fromLatin1("version")); + } + if(idx < 0) { + myWarning() << "RootHandler::start() - no syntax version" << endl; + return false; + } + d->syntaxVersion = atts_.value(idx).toUInt(); + if(d->syntaxVersion > Tellico::XML::syntaxVersion) { + d->error = i18n("It is from a future version of Tellico."); + return false; + } else if(Tellico::XML::versionConversion(d->syntaxVersion, Tellico::XML::syntaxVersion)) { + // going from version 9 to 10, there's no conversion needed + TQString str = i18n("Tellico is converting the file to a more recent document format. " + "Information loss may occur if an older version of Tellico is used " + "to read this file in the future."); + myDebug() << str << endl; + } + if((d->syntaxVersion > 6 && localName_ != Latin1Literal("tellico")) || + (d->syntaxVersion < 7 && localName_ != Latin1Literal("bookcase"))) { + // no error message + myWarning() << "RootHandler::start() - bad root element name" << endl; + return false; + } + d->ns = d->syntaxVersion > 6 ? Tellico::XML::nsTellico : Tellico::XML::nsBookcase; + return true; +} + +bool DocumentHandler::end(const TQString&, const TQString&, const TQString&) { + return true; +} + +StateHandler* CollectionHandler::nextHandlerImpl(const TQString&, const TQString& localName_, const TQString&) { + if((d->syntaxVersion > 3 && localName_ == Latin1Literal("fields")) || + (d->syntaxVersion < 4 && localName_ == Latin1Literal("attributes"))) { + return new FieldsHandler(d); + } else if(localName_ == Latin1Literal("bibtex-preamble")) { + return new BibtexPreambleHandler(d); + } else if(localName_ == Latin1Literal("macros")) { + return new BibtexMacrosHandler(d); + } else if(localName_ == d->entryName) { + return new EntryHandler(d); + } else if(localName_ == Latin1Literal("images")) { + return new ImagesHandler(d); + } + return 0; +} + +bool CollectionHandler::start(const TQString&, const TQString&, const TQString&, const TQXmlAttributes& atts_) { + d->collTitle = attValue(atts_, "title"); + d->collType = attValue(atts_, "type").toInt(); + d->entryName = attValue(atts_, "unit"); + + Q_ASSERT(d->collType); + return true; +} + +bool CollectionHandler::end(const TQString&, const TQString&, const TQString&) { + d->coll->addEntries(d->entries); + // a little hidden capability was to just have a local path as an image file name + // and on reading the xml file, Tellico would load the image file, too + // here, we need to scan all the image values in all the entries and check + // maybe this is too costly, especially since the capability wasn't advertised? + Data::FieldVec fields = d->coll->imageFields(); + for(Data::EntryVecIt entry = d->entries.begin(); entry != d->entries.end(); ++entry) { + for(Data::FieldVecIt field = fields.begin(); field != fields.end(); ++field) { + TQString value = entry->field(field, false); + // image info should have already been loaded + const Data::ImageInfo& info = ImageFactory::imageInfo(value); + // possible that value needs to be cleaned first in which case info is null + if(info.isNull() || !info.linkOnly) { + // for local files only, allow paths here + KURL u = KURL::fromPathOrURL(value); + if(u.isValid() && u.isLocalFile()) { + TQString result = ImageFactory::addImage(u, false /* quiet */); + if(!result.isEmpty()) { + value = result; + } + } + value = Data::Image::idClean(value); + entry->setField(field->name(), value); + } + } + } + return true; +} + +StateHandler* FieldsHandler::nextHandlerImpl(const TQString&, const TQString& localName_, const TQString&) { + if((d->syntaxVersion > 3 && localName_ == Latin1Literal("field")) || + (d->syntaxVersion < 4 && localName_ == Latin1Literal("attribute"))) { + return new FieldHandler(d); + } + return 0; +} + +bool FieldsHandler::start(const TQString&, const TQString&, const TQString&, const TQXmlAttributes&) { + d->defaultFields = false; + return true; +} + +bool FieldsHandler::end(const TQString&, const TQString&, const TQString&) { + // add default fields if there was a default field name, or no names at all + const bool addFields = d->defaultFields || d->fields.isEmpty(); + // in syntax 4, the element name was changed to "entry", always, rather than depending on + // on the entryName of the collection. + if(d->syntaxVersion > 3) { + d->entryName = TQString::fromLatin1("entry"); + Data::Collection::Type type = static_cast<Data::Collection::Type>(d->collType); + d->coll = CollectionFactory::collection(type, addFields); + } else { + d->coll = CollectionFactory::collection(d->entryName, addFields); + } + + if(!d->collTitle.isEmpty()) { + d->coll->setTitle(d->collTitle); + } + + d->coll->addFields(d->fields); + +// as a special case, for old book collections with a bibtex-id field, convert to Bibtex + if(d->syntaxVersion < 4 && d->collType == Data::Collection::Book + && d->coll->hasField(TQString::fromLatin1("bibtex-id"))) { + d->coll = Data::BibtexCollection::convertBookCollection(d->coll); + } + + return true; +} + +StateHandler* FieldHandler::nextHandlerImpl(const TQString&, const TQString& localName_, const TQString&) { + if(localName_ == Latin1Literal("prop")) { + return new FieldPropertyHandler(d); + } + return 0; +} + +bool FieldHandler::start(const TQString&, const TQString&, const TQString&, const TQXmlAttributes& atts_) { + // special case: if the i18n attribute equals true, then translate the title, description, and category + const bool isI18n = attValue(atts_, "i18n") == Latin1Literal("true"); + + TQString name = attValue(atts_, "name", "unknown"); + if(name == Latin1Literal("_default")) { + d->defaultFields = true; + return true; + } + + TQString title = attValue(atts_, "title", i18n("Unknown")); + if(isI18n) { + title = i18n(title.utf8()); + } + + TQString typeStr = attValue(atts_, "type", TQString::number(Data::Field::Line)); + Data::Field::Type type = static_cast<Data::Field::Type>(typeStr.toInt()); + + Data::FieldPtr field; + if(type == Data::Field::Choice) { + TQStringList allowed = TQStringList::split(TQRegExp(TQString::fromLatin1("\\s*;\\s*")), + attValue(atts_, "allowed")); + if(isI18n) { + for(TQStringList::Iterator word = allowed.begin(); word != allowed.end(); ++word) { + (*word) = i18n((*word).utf8()); + } + } + field = new Data::Field(name, title, allowed); + } else { + field = new Data::Field(name, title, type); + } + + int idx = atts_.index(TQString::fromLatin1("category")); + if(idx > -1) { + // at one point, the categories had keyboard accels + TQString cat = atts_.value(idx); + if(d->syntaxVersion < 9 && cat.find('&') > -1) { + cat.remove('&'); + } + if(isI18n) { + cat = i18n(cat.utf8()); + } + field->setCategory(cat); + } + + idx = atts_.index(TQString::fromLatin1("flags")); + if(idx > -1) { + int flags = atts_.value(idx).toInt(); + // I also changed the enum values for syntax 3, but the only custom field + // would have been bibtex-id + if(d->syntaxVersion < 3 && name == Latin1Literal("bibtex-id")) { + flags = 0; + } + + // in syntax version 4, added a flag to disallow deleting attributes + // if it's a version before that and is the title, then add the flag + if(d->syntaxVersion < 4 && name == Latin1Literal("title")) { + flags |= Data::Field::NoDelete; + } + field->setFlags(flags); + } + + TQString formatStr = attValue(atts_, "format", TQString::number(Data::Field::FormatNone)); + Data::Field::FormatFlag format = static_cast<Data::Field::FormatFlag>(formatStr.toInt()); + field->setFormatFlag(format); + + idx = atts_.index(TQString::fromLatin1("description")); + if(idx > -1) { + TQString desc = atts_.value(idx); + if(isI18n) { + desc = i18n(desc.utf8()); + } + field->setDescription(desc); + } + + if(d->syntaxVersion < 5 && atts_.index(TQString::fromLatin1("bibtex-field")) > -1) { + field->setProperty(TQString::fromLatin1("bibtex"), attValue(atts_, "bibtex-field")); + } + + // Table2 is deprecated + if(type == Data::Field::Table2) { + field->setType(Data::Field::Table); + field->setProperty(TQString::fromLatin1("columns"), TQChar('2')); + } + + // for syntax 8, rating fields got their own type + if(d->syntaxVersion < 8) { + Data::Field::convertOldRating(field); // does all its own checking + } + d->fields.append(field); + + return true; +} + +bool FieldHandler::end(const TQString&, const TQString&, const TQString&) { + return true; +} + +bool FieldPropertyHandler::start(const TQString&, const TQString&, const TQString&, const TQXmlAttributes& atts_) { + // there should be at least one field already so we can add properties to it + Q_ASSERT(!d->fields.isEmpty()); + Data::FieldPtr field = d->fields.back(); + + m_propertyName = attValue(atts_, "name"); + + // all track fields in music collections prior to version 9 get converted to three columns + if(d->syntaxVersion < 9) { + if(d->collType == Data::Collection::Album && field->name() == Latin1Literal("track")) { + field->setProperty(TQString::fromLatin1("columns"), TQChar('3')); + field->setProperty(TQString::fromLatin1("column1"), i18n("Title")); + field->setProperty(TQString::fromLatin1("column2"), i18n("Artist")); + field->setProperty(TQString::fromLatin1("column3"), i18n("Length")); + } else if(d->collType == Data::Collection::Video && field->name() == Latin1Literal("cast")) { + field->setProperty(TQString::fromLatin1("column1"), i18n("Actor/Actress")); + field->setProperty(TQString::fromLatin1("column2"), i18n("Role")); + } + } + + return true; +} + +bool FieldPropertyHandler::end(const TQString&, const TQString&, const TQString&) { + Q_ASSERT(!m_propertyName.isEmpty()); + // add the previous property + Data::FieldPtr field = d->fields.back(); + field->setProperty(m_propertyName, d->text); + return true; +} + +bool BibtexPreambleHandler::start(const TQString&, const TQString&, const TQString&, const TQXmlAttributes&) { + return true; +} + +bool BibtexPreambleHandler::end(const TQString&, const TQString&, const TQString&) { + Q_ASSERT(d->coll); + if(d->coll && d->collType == Data::Collection::Bibtex && !d->text.isEmpty()) { + Data::BibtexCollection* c = static_cast<Data::BibtexCollection*>(d->coll.data()); + c->setPreamble(d->text); + } + return true; +} + +StateHandler* BibtexMacrosHandler::nextHandlerImpl(const TQString&, const TQString& localName_, const TQString&) { + if(localName_ == Latin1Literal("macro")) { + return new BibtexMacroHandler(d); + } + return 0; +} + +bool BibtexMacrosHandler::start(const TQString&, const TQString&, const TQString&, const TQXmlAttributes&) { + return true; +} + +bool BibtexMacrosHandler::end(const TQString&, const TQString&, const TQString&) { + return true; +} + +bool BibtexMacroHandler::start(const TQString&, const TQString&, const TQString&, const TQXmlAttributes& atts_) { + m_macroName = attValue(atts_, "name"); + return true; +} + +bool BibtexMacroHandler::end(const TQString&, const TQString&, const TQString&) { + if(d->coll && d->collType == Data::Collection::Bibtex && !m_macroName.isEmpty() && !d->text.isEmpty()) { + Data::BibtexCollection* c = static_cast<Data::BibtexCollection*>(d->coll.data()); + c->addMacro(m_macroName, d->text); + } + return true; +} + +StateHandler* EntryHandler::nextHandlerImpl(const TQString&, const TQString& localName_, const TQString&) { + if(d->coll->hasField(localName_)) { + return new FieldValueHandler(d); + } + return new FieldValueContainerHandler(d); +} + +bool EntryHandler::start(const TQString&, const TQString&, const TQString&, const TQXmlAttributes& atts_) { + // the entries must come after the fields + if(!d->coll || d->coll->fields().isEmpty()) { + myWarning() << "EntryHandler::start() - entries must come after fields are defined" << endl; + // TODO: i18n + d->error = TQString::fromLatin1("File format error: entries must come after fields are defined"); + return false; + } + int id = attValue(atts_, "id").toInt(); + Data::EntryPtr entry; + if(id > 0) { + entry = new Data::Entry(d->coll, id); + } else { + entry = new Data::Entry(d->coll); + } + d->entries.append(entry); + return true; +} + +bool EntryHandler::end(const TQString&, const TQString&, const TQString&) { + return true; +} + +StateHandler* FieldValueContainerHandler::nextHandlerImpl(const TQString&, const TQString& localName_, const TQString&) { + if(d->coll->hasField(localName_)) { + return new FieldValueHandler(d); + } + return new FieldValueContainerHandler(d); +} + +bool FieldValueContainerHandler::start(const TQString&, const TQString&, const TQString&, const TQXmlAttributes&) { + return true; +} + +bool FieldValueContainerHandler::end(const TQString&, const TQString&, const TQString&) { + return true; +} + +StateHandler* FieldValueHandler::nextHandlerImpl(const TQString&, const TQString& localName_, const TQString&) { + if(localName_ == Latin1Literal("year") || + localName_ == Latin1Literal("month") || + localName_ == Latin1Literal("day")) { + return new DateValueHandler(d); + } else if(localName_ == Latin1Literal("column")) { + return new TableColumnHandler(d); + } + return 0; +} + +bool FieldValueHandler::start(const TQString&, const TQString&, const TQString& localName_, const TQXmlAttributes& atts_) { + d->currentField = d->coll->fieldByName(localName_); + m_i18n = attValue(atts_, "i18n") == Latin1Literal("true"); + m_validateISBN = attValue(atts_, "validate") != Latin1Literal("no"); + return true; +} + +bool FieldValueHandler::end(const TQString&, const TQString& localName_, const TQString&) { + Data::FieldPtr f = d->coll->fieldByName(localName_); + if(!f) { + myWarning() << "FieldValueHandler::end() - no field named " << localName_ << endl; + return true; + } + // if it's a derived value, no field value is added + if(f->type() == Data::Field::Dependent) { + return true; + } + + Data::EntryPtr entry = d->entries.back(); + Q_ASSERT(entry); + TQString fieldName = localName_; + TQString fieldValue = d->text; + + if(d->syntaxVersion < 2 && fieldName == Latin1Literal("keywords")) { + // in version 2, "keywords" changed to "keyword" + fieldName = TQString::fromLatin1("keyword"); + } else if(d->syntaxVersion < 4 && f->type() == Data::Field::Bool) { + // in version 3 and prior, checkbox attributes had no text(), set it to "true" + fieldValue = TQString::fromLatin1("true"); + } else if(d->syntaxVersion < 8 && f->type() == Data::Field::Rating) { + // in version 8, old rating fields get changed + bool ok; + uint i = Tellico::toUInt(fieldValue, &ok); + if(ok) { + fieldValue = TQString::number(i); + } + } else if(!d->textBuffer.isEmpty()) { + // for dates and tables, the value is built up from child elements +#ifndef NDEBUG + if(!d->text.isEmpty()) { + myWarning() << "FieldValueHandler::end() - ignoring value for field " << localName_ << ": " << d->text << endl; + } +#endif + fieldValue = d->textBuffer; + d->textBuffer = TQString(); + } + // this is not an else branch, the data may be in the textBuffer + if(d->syntaxVersion < 9 && d->coll->type() == Data::Collection::Album && fieldName == Latin1Literal("track")) { + // yes, this assumes the artist has already been set + fieldValue += TQString::fromLatin1("::"); + fieldValue += entry->field(TQString::fromLatin1("artist")); + } + // special case: if the i18n attribute equals true, then translate the title, description, and category + if(m_i18n) { + fieldValue = i18n(fieldValue.utf8()); + } + // special case for isbn fields, go ahead and validate + if(m_validateISBN && fieldName == Latin1Literal("isbn")) { + ISBNValidator val(0); + val.fixup(fieldValue); + } + if(fieldValue.isEmpty()) { + return true; + } + // for fields with multiple values, we need to add on the new value + TQString oldValue = entry->field(fieldName); + if(!oldValue.isEmpty()) { + fieldValue = oldValue + TQString::fromLatin1("; ") + fieldValue; + } + entry->setField(fieldName, fieldValue); + return true; +} + +bool DateValueHandler::start(const TQString&, const TQString&, const TQString&, const TQXmlAttributes&) { + return true; +} + +bool DateValueHandler::end(const TQString&, const TQString& localName_, const TQString&) { + // the data value is y-m-d even if there are no date values + if(d->textBuffer.isEmpty()) { + d->textBuffer = TQString::fromLatin1("--"); + } + TQStringList tokens = TQStringList::split('-', d->textBuffer, true /* allow empty */); + Q_ASSERT(tokens.size() == 3); + if(localName_ == Latin1Literal("year")) { + tokens[0] = d->text; + } else if(localName_ == Latin1Literal("month")) { + tokens[1] = d->text; + } else if(localName_ == Latin1Literal("day")) { + tokens[2] = d->text; + } + d->textBuffer = tokens.join(TQChar('-')); + return true; +} + +bool TableColumnHandler::start(const TQString&, const TQString&, const TQString&, const TQXmlAttributes&) { + return true; +} + +bool TableColumnHandler::end(const TQString&, const TQString&, const TQString&) { + // for old collections, if the second column holds the track length, bump it to next column + if(d->syntaxVersion < 9 && + d->coll->type() == Data::Collection::Album && + d->currentField->name() == Latin1Literal("track") && + !d->textBuffer.isEmpty() && + d->textBuffer.contains(TQString::fromLatin1("::")) == 0) { + TQRegExp rx(TQString::fromLatin1("\\d+:\\d\\d")); + if(rx.exactMatch(d->text)) { + d->text += TQString::fromLatin1("::"); + d->text += d->entries.back()->field(TQString::fromLatin1("artist")); + } + } + + if(!d->textBuffer.isEmpty()) { + d->textBuffer += TQString::fromLatin1("::"); + } + d->textBuffer += d->text; + return true; +} + +StateHandler* ImagesHandler::nextHandlerImpl(const TQString&, const TQString& localName_, const TQString&) { + if(localName_ == Latin1Literal("image")) { + return new ImageHandler(d); + } + return 0; +} + +bool ImagesHandler::start(const TQString&, const TQString&, const TQString&, const TQXmlAttributes&) { + // reset variable that gets updated in the image handler + d->hasImages = false; + return true; +} + +bool ImagesHandler::end(const TQString&, const TQString&, const TQString&) { + return true; +} + +bool ImageHandler::start(const TQString&, const TQString&, const TQString&, const TQXmlAttributes& atts_) { + m_format = attValue(atts_, "format"); + m_link = attValue(atts_, "link") == Latin1Literal("true"); + // idClean() already calls shareString() + m_imageId = m_link ? shareString(attValue(atts_, "id")) + : Data::Image::idClean(attValue(atts_, "id")); + m_width = attValue(atts_, "width").toInt(); + m_height = attValue(atts_, "height").toInt(); + return true; +} + +bool ImageHandler::end(const TQString&, const TQString&, const TQString&) { + bool readInfo = true; + if(d->loadImages) { + TQByteArray ba; + KCodecs::base64Decode(TQCString(d->text.latin1()), ba); + if(!ba.isEmpty()) { + TQString result = ImageFactory::addImage(ba, m_format, m_imageId); + if(result.isEmpty()) { + myDebug() << "TellicoImporter::readImage(XML) - null image for " << m_imageId << endl; + } + d->hasImages = true; + readInfo = false; + } + } + if(readInfo) { + // a width or height of 0 is ok here + Data::ImageInfo info(m_imageId, m_format.latin1(), m_width, m_height, m_link); + ImageFactory::cacheImageInfo(info); + } + return true; +} + +StateHandler* FiltersHandler::nextHandlerImpl(const TQString&, const TQString& localName_, const TQString&) { + if(localName_ == Latin1Literal("filter")) { + return new FilterHandler(d); + } + return 0; +} + +bool FiltersHandler::start(const TQString&, const TQString&, const TQString&, const TQXmlAttributes&) { + return true; +} + +bool FiltersHandler::end(const TQString&, const TQString&, const TQString&) { + return true; +} + +StateHandler* FilterHandler::nextHandlerImpl(const TQString&, const TQString& localName_, const TQString&) { + if(localName_ == Latin1Literal("rule")) { + return new FilterRuleHandler(d); + } + return 0; +} + +bool FilterHandler::start(const TQString&, const TQString&, const TQString&, const TQXmlAttributes& atts_) { + d->filter = new Filter(Filter::MatchAny); + d->filter->setName(attValue(atts_, "name")); + + if(attValue(atts_, "match") == Latin1Literal("all")) { + d->filter->setMatch(Filter::MatchAll); + } + return true; +} + +bool FilterHandler::end(const TQString&, const TQString&, const TQString&) { + if(d->coll && !d->filter->isEmpty()) { + d->coll->addFilter(d->filter); + } + d->filter = FilterPtr(); + return true; +} + +bool FilterRuleHandler::start(const TQString&, const TQString&, const TQString&, const TQXmlAttributes& atts_) { + TQString field = attValue(atts_, "field"); + // empty field means match any of them + TQString pattern = attValue(atts_, "pattern"); + // empty pattern is bad + if(pattern.isEmpty()) { + myWarning() << "FilterRuleHandler::start() - empty rule!" << endl; + return true; + } + TQString function = attValue(atts_, "function").lower(); + FilterRule::Function func; + if(function == Latin1Literal("contains")) { + func = FilterRule::FuncContains; + } else if(function == Latin1Literal("notcontains")) { + func = FilterRule::FuncNotContains; + } else if(function == Latin1Literal("equals")) { + func = FilterRule::FuncEquals; + } else if(function == Latin1Literal("notequals")) { + func = FilterRule::FuncNotEquals; + } else if(function == Latin1Literal("regexp")) { + func = FilterRule::FuncRegExp; + } else if(function == Latin1Literal("notregexp")) { + func = FilterRule::FuncNotRegExp; + } else { + myWarning() << "FilterRuleHandler::start() - invalid rule function: " << function << endl; + return true; + } + d->filter->append(new FilterRule(field, pattern, func)); + return true; +} + +bool FilterRuleHandler::end(const TQString&, const TQString&, const TQString&) { + return true; +} + +StateHandler* BorrowersHandler::nextHandlerImpl(const TQString&, const TQString& localName_, const TQString&) { + if(localName_ == Latin1Literal("borrower")) { + return new BorrowerHandler(d); + } + return 0; +} + +bool BorrowersHandler::start(const TQString&, const TQString&, const TQString&, const TQXmlAttributes&) { + return true; +} + +bool BorrowersHandler::end(const TQString&, const TQString&, const TQString&) { + return true; +} + +StateHandler* BorrowerHandler::nextHandlerImpl(const TQString&, const TQString& localName_, const TQString&) { + if(localName_ == Latin1Literal("loan")) { + return new LoanHandler(d); + } + return 0; +} + +bool BorrowerHandler::start(const TQString&, const TQString&, const TQString&, const TQXmlAttributes& atts_) { + TQString name = attValue(atts_, "name"); + TQString uid = attValue(atts_, "uid"); + d->borrower = new Data::Borrower(name, uid); + + return true; +} + +bool BorrowerHandler::end(const TQString&, const TQString&, const TQString&) { + if(d->coll && !d->borrower->isEmpty()) { + d->coll->addBorrower(d->borrower); + } + d->borrower = Data::BorrowerPtr(); + return true; +} + +bool LoanHandler::start(const TQString&, const TQString&, const TQString&, const TQXmlAttributes& atts_) { + m_id = attValue(atts_, "entryRef").toInt(); + m_uid = attValue(atts_, "uid"); + m_loanDate = attValue(atts_, "loanDate"); + m_dueDate = attValue(atts_, "dueDate"); + m_inCalendar = attValue(atts_, "calendar") == Latin1Literal("true"); + return true; +} + +bool LoanHandler::end(const TQString&, const TQString&, const TQString&) { + Data::EntryPtr entry = d->coll->entryById(m_id); + if(!entry) { + myWarning() << "LoanHandler::end() - no entry with id = " << m_id << endl; + return true; + } + TQDate loanDate, dueDate; + if(!m_loanDate.isEmpty()) { + loanDate = TQDate::fromString(m_loanDate, TQt::ISODate); + } + if(!m_dueDate.isEmpty()) { + dueDate = TQDate::fromString(m_dueDate, TQt::ISODate); + } + + Data::LoanPtr loan = new Data::Loan(entry, loanDate, dueDate, d->text); + loan->setUID(m_uid); + loan->setInCalendar(m_inCalendar); + d->borrower->addLoan(loan); + return true; +} + diff --git a/src/translators/xmlstatehandler.h b/src/translators/xmlstatehandler.h new file mode 100644 index 0000000..8b5c137 --- /dev/null +++ b/src/translators/xmlstatehandler.h @@ -0,0 +1,345 @@ +/*************************************************************************** + copyright : (C) 2008 by Robby Stephenson + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of version 2 of the GNU General Public License as * + * published by the Free Software Foundation; * + * * + ***************************************************************************/ + +#ifndef TELLICO_IMPORT_XMLSTATEHANDLER_H +#define TELLICO_IMPORT_XMLSTATEHANDLER_H + +#ifdef QT_NO_CAST_ASCII +#define HAD_QT_NO_CAST_ASCII +#undef QT_NO_CAST_ASCII +#endif + +#include <tqxml.h> + +#ifdef HAD_QT_NO_CAST_ASCII +#define QT_NO_CAST_ASCII +#undef HAD_QT_NO_CAST_ASCII +#endif + +#include "../datavectors.h" + +namespace Tellico { + namespace Import { + namespace SAX { + +class StateData { +public: + TQString text; + TQString error; + TQString ns; // namespace + TQString textBuffer; + uint syntaxVersion; + TQString collTitle; + int collType; + TQString entryName; + Data::CollPtr coll; + Data::FieldVec fields; + Data::FieldPtr currentField; + Data::EntryVec entries; + FilterPtr filter; + Data::BorrowerPtr borrower; + bool defaultFields; + bool loadImages; + bool hasImages; +}; + +class StateHandler { +public: + StateHandler(StateData* data) : d(data) {} + virtual ~StateHandler() {} + + virtual bool start(const TQString&, const TQString&, const TQString&, const TQXmlAttributes&) = 0; + virtual bool end(const TQString&, const TQString&, const TQString&) = 0; + + StateHandler* nextHandler(const TQString&, const TQString&, const TQString&); +protected: + StateData* d; +private: + virtual StateHandler* nextHandlerImpl(const TQString&, const TQString&, const TQString&) { return 0; } +}; + +class NullHandler : public StateHandler { +public: + NullHandler(StateData* data) : StateHandler(data) {} + virtual ~NullHandler() {} + + virtual bool start(const TQString&, const TQString&, const TQString&, const TQXmlAttributes&) { return true; } + virtual bool end(const TQString&, const TQString&, const TQString&) { return true; } +}; + +class RootHandler : public StateHandler { +public: + RootHandler(StateData* data) : StateHandler(data) {} + virtual ~RootHandler() {} + + virtual bool start(const TQString&, const TQString&, const TQString&, const TQXmlAttributes&) { return true; } + virtual bool end(const TQString&, const TQString&, const TQString&) { return true; } + +private: + virtual StateHandler* nextHandlerImpl(const TQString&, const TQString&, const TQString&); +}; + +class DocumentHandler : public StateHandler { +public: + DocumentHandler(StateData* data) : StateHandler(data) {} + virtual ~DocumentHandler() {} + + virtual bool start(const TQString&, const TQString&, const TQString&, const TQXmlAttributes&); + virtual bool end(const TQString&, const TQString&, const TQString&); + +private: + virtual StateHandler* nextHandlerImpl(const TQString&, const TQString&, const TQString&); +}; + +class CollectionHandler : public StateHandler { +public: + CollectionHandler(StateData* data) : StateHandler(data) {} + virtual ~CollectionHandler() {} + + virtual bool start(const TQString&, const TQString&, const TQString&, const TQXmlAttributes&); + virtual bool end(const TQString&, const TQString&, const TQString&); + +private: + virtual StateHandler* nextHandlerImpl(const TQString&, const TQString&, const TQString&); +}; + +class FieldsHandler : public StateHandler { +public: + FieldsHandler(StateData* data) : StateHandler(data) {} + virtual ~FieldsHandler() {} + + virtual bool start(const TQString&, const TQString&, const TQString&, const TQXmlAttributes&); + virtual bool end(const TQString&, const TQString&, const TQString&); + +private: + virtual StateHandler* nextHandlerImpl(const TQString&, const TQString&, const TQString&); +}; + +class FieldHandler : public StateHandler { +public: + FieldHandler(StateData* data) : StateHandler(data) {} + virtual ~FieldHandler() {} + + virtual bool start(const TQString&, const TQString&, const TQString&, const TQXmlAttributes&); + virtual bool end(const TQString&, const TQString&, const TQString&); + +private: + virtual StateHandler* nextHandlerImpl(const TQString&, const TQString&, const TQString&); +}; + +class FieldPropertyHandler : public StateHandler { +public: + FieldPropertyHandler(StateData* data) : StateHandler(data) {} + virtual ~FieldPropertyHandler() {} + + virtual bool start(const TQString&, const TQString&, const TQString&, const TQXmlAttributes&); + virtual bool end(const TQString&, const TQString&, const TQString&); + +private: + TQString m_propertyName; +}; + +class BibtexPreambleHandler : public StateHandler { +public: + BibtexPreambleHandler(StateData* data) : StateHandler(data) {} + virtual ~BibtexPreambleHandler() {} + + virtual bool start(const TQString&, const TQString&, const TQString&, const TQXmlAttributes&); + virtual bool end(const TQString&, const TQString&, const TQString&); +}; + +class BibtexMacrosHandler : public StateHandler { +public: + BibtexMacrosHandler(StateData* data) : StateHandler(data) {} + virtual ~BibtexMacrosHandler() {} + + virtual bool start(const TQString&, const TQString&, const TQString&, const TQXmlAttributes&); + virtual bool end(const TQString&, const TQString&, const TQString&); + +private: + virtual StateHandler* nextHandlerImpl(const TQString&, const TQString&, const TQString&); +}; + +class BibtexMacroHandler : public StateHandler { +public: + BibtexMacroHandler(StateData* data) : StateHandler(data) {} + virtual ~BibtexMacroHandler() {} + + virtual bool start(const TQString&, const TQString&, const TQString&, const TQXmlAttributes&); + virtual bool end(const TQString&, const TQString&, const TQString&); + +private: + TQString m_macroName; +}; + +class EntryHandler : public StateHandler { +public: + EntryHandler(StateData* data) : StateHandler(data) {} + virtual ~EntryHandler() {} + + virtual bool start(const TQString&, const TQString&, const TQString&, const TQXmlAttributes&); + virtual bool end(const TQString&, const TQString&, const TQString&); + +private: + virtual StateHandler* nextHandlerImpl(const TQString&, const TQString&, const TQString&); +}; + +class FieldValueContainerHandler : public StateHandler { +public: + FieldValueContainerHandler(StateData* data) : StateHandler(data) {} + virtual ~FieldValueContainerHandler() {} + + virtual bool start(const TQString&, const TQString&, const TQString&, const TQXmlAttributes&); + virtual bool end(const TQString&, const TQString&, const TQString&); + +private: + virtual StateHandler* nextHandlerImpl(const TQString&, const TQString&, const TQString&); +}; + +class FieldValueHandler : public StateHandler { +public: + FieldValueHandler(StateData* data) : StateHandler(data) {} + virtual ~FieldValueHandler() {} + + virtual bool start(const TQString&, const TQString&, const TQString&, const TQXmlAttributes&); + virtual bool end(const TQString&, const TQString&, const TQString&); + +private: + virtual StateHandler* nextHandlerImpl(const TQString&, const TQString&, const TQString&); + bool m_i18n; + bool m_validateISBN; +}; + +class DateValueHandler : public StateHandler { +public: + DateValueHandler(StateData* data) : StateHandler(data) {} + virtual ~DateValueHandler() {} + + virtual bool start(const TQString&, const TQString&, const TQString&, const TQXmlAttributes&); + virtual bool end(const TQString&, const TQString&, const TQString&); +}; + +class TableColumnHandler : public StateHandler { +public: + TableColumnHandler(StateData* data) : StateHandler(data) {} + virtual ~TableColumnHandler() {} + + virtual bool start(const TQString&, const TQString&, const TQString&, const TQXmlAttributes&); + virtual bool end(const TQString&, const TQString&, const TQString&); +}; + +class ImagesHandler : public StateHandler { +public: + ImagesHandler(StateData* data) : StateHandler(data) {} + virtual ~ImagesHandler() {} + + virtual bool start(const TQString&, const TQString&, const TQString&, const TQXmlAttributes&); + virtual bool end(const TQString&, const TQString&, const TQString&); + +private: + virtual StateHandler* nextHandlerImpl(const TQString&, const TQString&, const TQString&); +}; + +class ImageHandler : public StateHandler { +public: + ImageHandler(StateData* data) : StateHandler(data) {} + virtual ~ImageHandler() {} + + virtual bool start(const TQString&, const TQString&, const TQString&, const TQXmlAttributes&); + virtual bool end(const TQString&, const TQString&, const TQString&); + +private: + TQString m_format; + bool m_link; + TQString m_imageId; + int m_width; + int m_height; +}; + +class FiltersHandler : public StateHandler { +public: + FiltersHandler(StateData* data) : StateHandler(data) {} + virtual ~FiltersHandler() {} + + virtual bool start(const TQString&, const TQString&, const TQString&, const TQXmlAttributes&); + virtual bool end(const TQString&, const TQString&, const TQString&); + +private: + virtual StateHandler* nextHandlerImpl(const TQString&, const TQString&, const TQString&); +}; + +class FilterHandler : public StateHandler { +public: + FilterHandler(StateData* data) : StateHandler(data) {} + virtual ~FilterHandler() {} + + virtual bool start(const TQString&, const TQString&, const TQString&, const TQXmlAttributes&); + virtual bool end(const TQString&, const TQString&, const TQString&); + +private: + virtual StateHandler* nextHandlerImpl(const TQString&, const TQString&, const TQString&); +}; + +class FilterRuleHandler : public StateHandler { +public: + FilterRuleHandler(StateData* data) : StateHandler(data) {} + virtual ~FilterRuleHandler() {} + + virtual bool start(const TQString&, const TQString&, const TQString&, const TQXmlAttributes&); + virtual bool end(const TQString&, const TQString&, const TQString&); +}; + +class BorrowersHandler : public StateHandler { +public: + BorrowersHandler(StateData* data) : StateHandler(data) {} + virtual ~BorrowersHandler() {} + + virtual bool start(const TQString&, const TQString&, const TQString&, const TQXmlAttributes&); + virtual bool end(const TQString&, const TQString&, const TQString&); + +private: + virtual StateHandler* nextHandlerImpl(const TQString&, const TQString&, const TQString&); +}; + +class BorrowerHandler : public StateHandler { +public: + BorrowerHandler(StateData* data) : StateHandler(data) {} + virtual ~BorrowerHandler() {} + + virtual bool start(const TQString&, const TQString&, const TQString&, const TQXmlAttributes&); + virtual bool end(const TQString&, const TQString&, const TQString&); + +private: + virtual StateHandler* nextHandlerImpl(const TQString&, const TQString&, const TQString&); +}; + +class LoanHandler : public StateHandler { +public: + LoanHandler(StateData* data) : StateHandler(data) {} + virtual ~LoanHandler() {} + + virtual bool start(const TQString&, const TQString&, const TQString&, const TQXmlAttributes&); + virtual bool end(const TQString&, const TQString&, const TQString&); + +private: + int m_id; + TQString m_uid; + TQString m_loanDate; + TQString m_dueDate; + bool m_inCalendar; +}; + + } + } +} +#endif diff --git a/src/translators/xsltexporter.cpp b/src/translators/xsltexporter.cpp index c08ee0f..b0642b3 100644 --- a/src/translators/xsltexporter.cpp +++ b/src/translators/xsltexporter.cpp @@ -18,6 +18,8 @@ #include <tdelocale.h> #include <kurlrequester.h> +#include <kuser.h> +#include <tdeconfig.h> #include <tqlabel.h> #include <tqgroupbox.h> @@ -49,6 +51,9 @@ bool XSLTExporter::exec() { } // XSLTHandler handler(FileHandler::readXMLFile(url)); XSLTHandler handler(u); + handler.addStringParam("date", TQDate::currentDate().toString(TQt::ISODate).latin1()); + handler.addStringParam("time", TQTime::currentTime().toString(TQt::ISODate).latin1()); + handler.addStringParam("user", KUser(KUser::UseRealUserID).loginName().latin1()); TellicoXMLExporter exporter; exporter.setEntries(entries()); @@ -75,6 +80,25 @@ TQWidget* XSLTExporter::widget(TQWidget* parent_, const char* name_/*=0*/) { m_URLRequester = new KURLRequester(box); TQWhatsThis::add(m_URLRequester, i18n("Choose the XSLT file used to transform the Tellico XML data.")); + TQString filter = i18n("*.xsl|XSL Files (*.xsl)") + TQChar('\n'); + filter += i18n("*|All Files"); + m_URLRequester->setFilter(filter); + m_URLRequester->setMode(static_cast<KFile::Mode>(KFile::File | KFile::ExistingOnly)); + if(!m_xsltFile.isEmpty()) { + m_URLRequester->setURL(m_xsltFile); + } + l->addStretch(1); return m_widget; } + +void XSLTExporter::readOptions(TDEConfig* config_) { + TDEConfigGroup group(config_, TQString::fromLatin1("ExportOptions - %1").arg(formatString())); + m_xsltFile = group.readEntry("Last File", TQString()); +} + +void XSLTExporter::saveOptions(TDEConfig* config_) { + TDEConfigGroup group(config_, TQString::fromLatin1("ExportOptions - %1").arg(formatString())); + m_xsltFile = m_URLRequester->url(); + group.writeEntry("Last File", m_xsltFile); +} diff --git a/src/translators/xsltexporter.h b/src/translators/xsltexporter.h index b14afd3..8b808e5 100644 --- a/src/translators/xsltexporter.h +++ b/src/translators/xsltexporter.h @@ -34,9 +34,13 @@ public: virtual TQWidget* widget(TQWidget* parent, const char* name=0); + virtual void readOptions(TDEConfig* cfg); + virtual void saveOptions(TDEConfig* cfg); + private: TQWidget* m_widget; KURLRequester* m_URLRequester; + TQString m_xsltFile; }; } // end namespace |