/*************************************************************************** copyright : (C) 2008 by Robby Stephenson email : robby@periapsis.org ***************************************************************************/ /*************************************************************************** * * * 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 #include 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(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(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(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(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(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; }