File indexing completed on 2024-05-19 05:05:34

0001 /***************************************************************************
0002  *   SPDX-License-Identifier: GPL-2.0-or-later
0003  *                                                                         *
0004  *   SPDX-FileCopyrightText: 2004-2023 Thomas Fischer <fischer@unix-ag.uni-kl.de>
0005  *                                                                         *
0006  *   This program is free software; you can redistribute it and/or modify  *
0007  *   it under the terms of the GNU General Public License as published by  *
0008  *   the Free Software Foundation; either version 2 of the License, or     *
0009  *   (at your option) any later version.                                   *
0010  *                                                                         *
0011  *   This program is distributed in the hope that it will be useful,       *
0012  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
0013  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
0014  *   GNU General Public License for more details.                          *
0015  *                                                                         *
0016  *   You should have received a copy of the GNU General Public License     *
0017  *   along with this program; if not, see <https://www.gnu.org/licenses/>. *
0018  ***************************************************************************/
0019 
0020 #include "fileexporterris.h"
0021 
0022 #include <QIODevice>
0023 #include <QRegularExpression>
0024 #include <QStringList>
0025 
0026 #include <Entry>
0027 #include <KBibTeX>
0028 #include "fileexporter_p.h"
0029 #include "logging_io.h"
0030 
0031 class FileExporterRIS::Private
0032 {
0033 private:
0034     FileExporterRIS *parent;
0035 
0036 public:
0037     Private(FileExporterRIS *p)
0038             : parent(p)
0039     {
0040         // nothing
0041     }
0042 
0043     bool writeEntry(QTextStream &stream, const QSharedPointer<const Entry> entry)
0044     {
0045         bool result = true;
0046         QString type = entry->type();
0047 
0048         if (type == Entry::etBook)
0049             writeKeyValue(stream, QStringLiteral("TY"), QStringLiteral("BOOK"));
0050         else if (type == Entry::etInBook)
0051             writeKeyValue(stream, QStringLiteral("TY"), QStringLiteral("CHAP"));
0052         else if (type == Entry::etInProceedings)
0053             writeKeyValue(stream, QStringLiteral("TY"), QStringLiteral("CONF"));
0054         else if (type == Entry::etArticle)
0055             writeKeyValue(stream, QStringLiteral("TY"), QStringLiteral("JOUR"));
0056         else if (type == Entry::etTechReport)
0057             writeKeyValue(stream, QStringLiteral("TY"), QStringLiteral("RPRT"));
0058         else if (type == Entry::etPhDThesis || type == Entry::etMastersThesis)
0059             writeKeyValue(stream, QStringLiteral("TY"), QStringLiteral("THES"));
0060         else if (type == Entry::etUnpublished)
0061             writeKeyValue(stream, QStringLiteral("TY"), QStringLiteral("UNPB"));
0062         else
0063             writeKeyValue(stream, QStringLiteral("TY"), QStringLiteral("GEN"));
0064 
0065         writeKeyValue(stream, QStringLiteral("ID"), entry->id());
0066 
0067         QString year, month;
0068 
0069         for (Entry::ConstIterator it = entry->constBegin(); result && it != entry->constEnd(); ++it) {
0070             const QString &key = it.key();
0071             const Value &value = it.value();
0072 
0073             if (key.startsWith(QStringLiteral("RISfield_")))
0074                 result &= writeKeyValue(stream, key.right(2), PlainTextValue::text(value));
0075             else if (key == Entry::ftAuthor) {
0076                 for (Value::ConstIterator it = value.constBegin(); result && it != value.constEnd(); ++it) {
0077                     QSharedPointer<const Person> person = (*it).dynamicCast<const Person>();
0078                     if (!person.isNull())
0079                         result &= writeKeyValue(stream, QStringLiteral("AU"), PlainTextValue::text(**it));
0080                     else
0081                         qCWarning(LOG_KBIBTEX_IO) << "Cannot write value " << PlainTextValue::text(**it) << " for field AU (author), not supported by RIS format";
0082                 }
0083             } else if (key.toLower() == Entry::ftEditor) {
0084                 for (Value::ConstIterator it = value.constBegin(); result && it != value.constEnd(); ++it) {
0085                     QSharedPointer<const Person> person = (*it).dynamicCast<const Person>();
0086                     if (!person.isNull())
0087                         result &= writeKeyValue(stream, QStringLiteral("ED"), PlainTextValue::text(**it));
0088                     else
0089                         qCWarning(LOG_KBIBTEX_IO) << "Cannot write value " << PlainTextValue::text(**it) << " for field ED (editor), not supported by RIS format";
0090                 }
0091             } else if (key == Entry::ftTitle)
0092                 result &= writeKeyValue(stream, QStringLiteral("TI"), PlainTextValue::text(value));
0093             else if (key == Entry::ftBookTitle)
0094                 result &= writeKeyValue(stream, QStringLiteral("BT"), PlainTextValue::text(value));
0095             else if (key == Entry::ftSeries)
0096                 result &= writeKeyValue(stream, QStringLiteral("T3"), PlainTextValue::text(value));
0097             else if (key == Entry::ftJournal)
0098                 result &= writeKeyValue(stream, QStringLiteral("JO"), PlainTextValue::text(value)); ///< "JF" instead?
0099             else if (key == Entry::ftChapter)
0100                 result &= writeKeyValue(stream, QStringLiteral("CP"), PlainTextValue::text(value));
0101             else if (key == Entry::ftISSN)
0102                 result &= writeKeyValue(stream, QStringLiteral("SN"), PlainTextValue::text(value));
0103             else if (key == Entry::ftISBN)
0104                 result &= writeKeyValue(stream, QStringLiteral("SN"), PlainTextValue::text(value));
0105             else if (key == Entry::ftSchool) /// == "institution"
0106                 result &= writeKeyValue(stream, QStringLiteral("IN"), PlainTextValue::text(value));
0107             else if (key == Entry::ftVolume)
0108                 result &= writeKeyValue(stream, QStringLiteral("VL"), PlainTextValue::text(value));
0109             else if (key == Entry::ftNumber) /// == "issue"
0110                 result &= writeKeyValue(stream, QStringLiteral("IS"), PlainTextValue::text(value));
0111             else if (key == Entry::ftNote)
0112                 result &= writeKeyValue(stream, QStringLiteral("N1"), PlainTextValue::text(value));
0113             else if (key == Entry::ftAbstract)
0114                 result &= writeKeyValue(stream, QStringLiteral("N2"), PlainTextValue::text(value)); ///< "AB" instead?
0115             else if (key == Entry::ftPublisher)
0116                 result &= writeKeyValue(stream, QStringLiteral("PB"), PlainTextValue::text(value));
0117             else if (key == Entry::ftLocation)
0118                 result &= writeKeyValue(stream, QStringLiteral("CY"), PlainTextValue::text(value));
0119             else if (key == Entry::ftDOI)
0120                 result &= writeKeyValue(stream, QStringLiteral("DO"), PlainTextValue::text(value));
0121             else if (key == Entry::ftKeywords)
0122                 result &= writeKeyValue(stream, QStringLiteral("KW"), PlainTextValue::text(value));
0123             else if (key == Entry::ftYear)
0124                 year = PlainTextValue::text(value);
0125             else if (key == Entry::ftMonth)
0126                 month = PlainTextValue::text(value);
0127             else if (key == Entry::ftAddress)
0128                 result &= writeKeyValue(stream, QStringLiteral("AD"), PlainTextValue::text(value));
0129             else if (key == Entry::ftUrl) {
0130                 // FIXME one "UR" line per URL
0131                 // FIXME for local files, use "L1"
0132                 result &= writeKeyValue(stream, QStringLiteral("UR"), PlainTextValue::text(value));
0133             } else if (key == Entry::ftPages) {
0134                 static const QRegularExpression splitRegExp(QString(QStringLiteral("-{1,2}|%1")).arg(QChar(0x2013)));
0135                 QStringList pageRange = PlainTextValue::text(value).split(splitRegExp);
0136                 if (pageRange.count() == 2) {
0137                     result &= writeKeyValue(stream, QStringLiteral("SP"), pageRange[ 0 ]);
0138                     result &= writeKeyValue(stream, QStringLiteral("EP"), pageRange[ 1 ]);
0139                 }
0140             }
0141         }
0142 
0143         if (!year.isEmpty() || !month.isEmpty()) {
0144             int monthAsInt = -1;
0145             for (int i = 0; monthAsInt < 0 && i < 12; ++i)
0146                 if (KBibTeX::MonthsTriple[i] == month)
0147                     monthAsInt = i + 1;
0148             if (monthAsInt > 0)
0149                 result &= writeKeyValue(stream, QStringLiteral("PY"), QString(QStringLiteral("%1/%2//")).arg(year).arg(monthAsInt, 2, 10, QLatin1Char('0')));
0150             else
0151                 result &= writeKeyValue(stream, QStringLiteral("PY"), QString(QStringLiteral("%1///%2")).arg(year, month));
0152         }
0153 
0154         result &= writeKeyValue(stream, QStringLiteral("ER"), QString());
0155 #if QT_VERSION >= 0x050e00
0156         stream << Qt::endl;
0157 #else // QT_VERSION < 0x050e00
0158         stream << endl;
0159 #endif // QT_VERSION >= 0x050e00
0160 
0161         return result;
0162     }
0163 
0164     bool writeKeyValue(QTextStream &stream, const QString &key, const QString &value)
0165     {
0166         stream << key << "  - ";
0167         if (!value.isEmpty())
0168             stream << value;
0169 #if QT_VERSION >= 0x050e00
0170         stream << Qt::endl;
0171 #else // QT_VERSION < 0x050e00
0172         stream << endl;
0173 #endif // QT_VERSION >= 0x050e00
0174 
0175         return true;
0176     }
0177 };
0178 
0179 FileExporterRIS::FileExporterRIS(QObject *parent)
0180         : FileExporter(parent), d(new Private(this))
0181 {
0182     /// nothing
0183 }
0184 
0185 FileExporterRIS::~FileExporterRIS()
0186 {
0187     delete d;
0188 }
0189 
0190 bool FileExporterRIS::save(QIODevice *iodevice, const QSharedPointer<const Element> &element, const File *bibtexfile)
0191 {
0192     Q_UNUSED(bibtexfile)
0193 
0194     check_if_iodevice_invalid(iodevice);
0195 
0196     bool result = false;
0197     QTextStream stream(iodevice);
0198 
0199     const QSharedPointer<const Entry> &entry = element.dynamicCast<const Entry>();
0200     if (!entry.isNull())
0201         result = d->writeEntry(stream, entry);
0202 
0203     return result;
0204 }
0205 
0206 bool FileExporterRIS::save(QIODevice *iodevice, const File *bibtexfile)
0207 {
0208     check_if_bibtexfile_or_iodevice_invalid(bibtexfile, iodevice);
0209 
0210     bool result = true;
0211     QTextStream stream(iodevice);
0212 
0213     for (File::ConstIterator it = bibtexfile->constBegin(); it != bibtexfile->constEnd() && result; ++it) {
0214         const QSharedPointer<const Entry> &entry = (*it).dynamicCast<Entry>();
0215         if (!entry.isNull()) {
0216             const QSharedPointer<const Entry> resolvedEntry(entry->resolveCrossref(bibtexfile));
0217             result |= d->writeEntry(stream, resolvedEntry);
0218         }
0219     }
0220 
0221     return result;
0222 }