/* ============================================================ * rekonq * ============================================================ * SPDX-License-Identifier: GPL-3.0-only * Copyright (C) 2022 aqua * ============================================================ * Description: rekonq bookmarks model * ============================================================ */ #include "bookmarkstreeformat_xbel.h" #include "bookmarkstreeitem.hpp" #include #include inline void readNodeAttributes(BookmarksTreeItem *item, const QXmlStreamAttributes &attributes) { // id if (attributes.hasAttribute(xbel::attr_id)) item->setData(BookmarksTreeItem::Id, attributes.value(xbel::attr_id).toString()); // added if (attributes.hasAttribute(xbel::attr_added)) { #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) auto dateTime = QDateTime::fromString(attributes.value(xbel::attr_added), Qt::ISODate); #else auto dateTime = QDateTime::fromString(attributes.value(xbel::attr_added).toString(), Qt::ISODate); #endif if (!dateTime.isNull() && dateTime.isValid()) item->setData(BookmarksTreeItem::Added, dateTime); } } inline void readUrlAttributes(BookmarksTreeItem *item, const QXmlStreamAttributes &attributes) { // href (required) if (attributes.hasAttribute(xbel::attr_href)) item->setData(BookmarksTreeItem::Href, attributes.value(xbel::attr_href).toString()); // visited if (attributes.hasAttribute(xbel::attr_visited)) { #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) auto dateTime = QDateTime::fromString(attributes.value(xbel::attr_visited), Qt::ISODate); #else auto dateTime = QDateTime::fromString(attributes.value(xbel::attr_visited).toString(), Qt::ISODate); #endif if (!dateTime.isNull() && dateTime.isValid()) item->setData(BookmarksTreeItem::Visited, dateTime); } // modified if (attributes.hasAttribute(xbel::attr_modified)) { #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) auto dateTime = QDateTime::fromString(attributes.value(xbel::attr_modified), Qt::ISODate); #else auto dateTime = QDateTime::fromString(attributes.value(xbel::attr_modified).toString(), Qt::ISODate); #endif if (!dateTime.isNull() && dateTime.isValid()) item->setData(BookmarksTreeItem::Modified, dateTime); } } [[nodiscard]] inline bool readItemAttributes(BookmarksTreeItem *item, const QXmlStreamAttributes &attr) { bool has_errors = false; switch (item->type()) { case BookmarksTreeItem::Root: if (!attr.hasAttribute(xbel::attr_version)) { has_errors = true; // spdlog::error("xbel::read: xbel has no version"); } else if (attr.value(xbel::attr_version).compare(QLatin1String{xbel::attr_version_value}) != 0) { has_errors = true; // spdlog::error("xbel::read: unknown xbel version"); } readNodeAttributes(item, attr); break; case BookmarksTreeItem::Folder: readNodeAttributes(item, attr); if (attr.hasAttribute(xbel::attr_folded)) item->setExpanded(attr.value(xbel::attr_folded).compare(QLatin1String{xbel::attr_folded_yes}) != 0); break; case BookmarksTreeItem::Separator: break; case BookmarksTreeItem::Bookmark: readNodeAttributes(item, attr); readUrlAttributes(item, attr); break; case BookmarksTreeItem::Alias: if (!attr.hasAttribute(xbel::attr_ref)) { has_errors = true; // spdlog::error("xbel::read: Alias has no ref"); } else item->setData(BookmarksTreeItem::Id, attr.value(xbel::attr_ref).toString()); break; default: return false; } return has_errors; } /** * Recursively parse XBEL * @param reader * @param parent * @return true if there were errors during parsing */ [[nodiscard]] inline bool readChildElements(QXmlStreamReader &reader, BookmarksTreeItem *parent) { bool has_errors = false; while (reader.readNextStartElement()) { if (reader.hasError()) { has_errors = true; // spdlog::warn("xbel::read: error during parsing: {}", qUtf8Printable(reader.errorString())); } const auto name = reader.name(); if (name == xbel::elem_title) { parent->setData(BookmarksTreeItem::Title, reader.readElementText()); } else if (name == xbel::elem_desc) { parent->setData(BookmarksTreeItem::Description, reader.readElementText()); } else if (name == xbel::elem_info) { has_errors += readChildElements(reader, parent); } else if (name == xbel::elem_metadata) { if (reader.attributes().value(xbel::elem_metadata_owner) == QLatin1String{xbel::elem_metadata_owner_value}) parent->metadata.append(reader.readElementText()); else reader.skipCurrentElement(); } else if (name == xbel::elem_bookmark) { auto *item = new BookmarksTreeItem(BookmarksTreeItem::Bookmark, {}, parent); has_errors += readItemAttributes(item, reader.attributes()); if (!parent->appendChild(item)) has_errors = true; has_errors += readChildElements(reader, item); } else if (name == xbel::elem_folder) { auto *item = new BookmarksTreeItem(BookmarksTreeItem::Folder, {}, parent); has_errors += readItemAttributes(item, reader.attributes()); if (!parent->appendChild(item)) has_errors = true; has_errors += readChildElements(reader, item); } else if (name == xbel::elem_separator) { auto *item = new BookmarksTreeItem(BookmarksTreeItem::Separator, {}, parent); if (!parent->appendChild(item)) has_errors = true; reader.skipCurrentElement(); } else if (name == xbel::elem_alias) { auto *item = new BookmarksTreeItem(BookmarksTreeItem::Alias, {}, parent); item->setData(BookmarksTreeItem::Id, reader.attributes().value(xbel::attr_ref).toString()); if (!parent->appendChild(item)) has_errors = true; reader.skipCurrentElement(); } else { // spdlog::error("xbel::read: skipping unknown element {} on line {}", qUtf8Printable(name.toString()), // reader.lineNumber()); has_errors = true; reader.skipCurrentElement(); } } return has_errors; } bool xbel::read(QIODevice *device, BookmarksTreeItem *root) { if (!device->isOpen()) return false; QXmlStreamReader reader(device); if (!reader.readNextStartElement()) return false; // parse the top-level item attributes if (reader.name().compare(QLatin1String{elem_xbel}) != 0) return false; if (readItemAttributes(root, reader.attributes())) return false; return !readChildElements(reader, root); }