Revision control
++ // Copyright (C) 2016 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
++
++ #include "qdocgenerator.h"
++
++ #include <QtCore/qdir.h>
++
++ #include <iostream>
++
++ using namespace Qt::Literals::StringLiterals;
++
++ namespace QDocGenerator {
++
++ // See definition of idstring and licenseid in https://spdx.org/spdx-specification-21-web-version
++ static bool isSpdxLicenseId(const QString &str) {
++ if (str.isEmpty())
++ return false;
++ for (auto iter(str.cbegin()); iter != str.cend(); ++iter) {
++ const QChar c = *iter;
++ if (!((c >= QLatin1Char('A') && c <= QLatin1Char('Z'))
++ || (c >= QLatin1Char('a') && c <= QLatin1Char('z'))
++ || (c >= QLatin1Char('0') && c <= QLatin1Char('9'))
++ || (c == QLatin1Char('-')) || (c == QLatin1Char('.'))))
++ return false;
++ }
++ return true;
++ }
++
++ static QString languageJoin(const QStringList &list)
++ {
++ QString result;
++ for (int i = 0; i < list.size(); ++i) {
++ QString delimiter = u", "_s;
++ if (i == list.size() - 1) // last item
++ delimiter.clear();
++ else if (list.size() == 2)
++ delimiter = u" and "_s;
++ else if (list.size() > 2 && i == list.size() - 2)
++ delimiter = u", and "_s; // oxford comma
++ result += list[i] + delimiter;
++ }
++
++ return result;
++ }
++
++ // Embed source code between \badcode ... \endbadcode
++ // Also, avoid '*/' breaking qdoc by passing the star as argument
++ static void sourceCode(QTextStream &out, const QString &src)
++ {
++ out << "\\badcode *\n";
++ out << QString(src).replace(u"*/"_s, u"\\1/"_s);
++ out << "\n\\endcode\n\n";
++ }
++
++ static void generate(QTextStream &out, const Package &package, const QDir &baseDir)
++ {
++ out << "/*!\n\n";
++ for (const QString &part : package.qtParts) {
++ out << "\\ingroup attributions-" << package.qdocModule << "-" << part << "\n";
++ out << "\\ingroup attributions-" << part << "\n";
++ }
++
++ if (package.qtParts.contains("libs"_L1)) {
++ // show up in xxx-index.html page of module
++ out << "\\ingroup attributions-" << package.qdocModule << "\n";
++ // include in '\generatelist annotatedattributions'
++ out << "\\page " << package.qdocModule << "-attribution-" << package.id
++ << ".html\n";
++ out << "\\attribution\n";
++ } else {
++ out << "\\page " << package.qdocModule << "-attribution-" << package.id
++ << ".html \n";
++ }
++
++ // Suppress a potential QDoc warning by marking an attribution as an auxiliary
++ // page that's not required to be included in a TOC.
++ out << "\\meta {toc} {nonessential}\n";
++
++ out << "\\target " << package.id << "\n\n";
++ out << "\\title " << package.name;
++ if (!package.version.isEmpty())
++ out << ", version " << package.version;
++ out << "\n\n\\brief " << package.license << "\n\n";
++
++ if (!package.description.isEmpty())
++ out << package.description << "\n\n";
++
++ if (!package.qtUsage.isEmpty())
++ out << package.qtUsage << "\n\n";
++
++ if (package.origin == Package::Origin::InSource) {
++ QStringList sourcePaths;
++ if (package.files.isEmpty()) {
++ sourcePaths << baseDir.relativeFilePath(package.path);
++ } else {
++ const QDir packageDir(package.path);
++ for (const QString &filePath : package.files) {
++ const QString absolutePath = packageDir.absoluteFilePath(filePath);
++ sourcePaths << baseDir.relativeFilePath(absolutePath);
++ }
++ }
++
++ out << "The sources can be found in " << languageJoin(sourcePaths) << ".\n\n";
++ }
++
++ const bool hasPackageVersion = !package.version.isEmpty();
++ const bool hasPackageDownloadLocation = !package.downloadLocation.isEmpty();
++ if (!package.homepage.isEmpty()) {
++ out << "\\l{" << package.homepage << "}{Project Homepage}";
++ if (hasPackageVersion)
++ out << ", ";
++ }
++ if (hasPackageVersion) {
++ out << "upstream version: ";
++ if (hasPackageDownloadLocation)
++ out << "\\l{" << package.downloadLocation << "}{";
++ out << package.version;
++ if (hasPackageDownloadLocation)
++ out << "}";
++ }
++
++ out << "\n\n";
++
++ QString copyright;
++ if (!package.copyright.isEmpty())
++ copyright = package.copyright;
++ else if (!package.copyrightFileContents.isEmpty())
++ copyright = package.copyrightFileContents;
++
++ if (!copyright.isEmpty()) {
++ out << "\n";
++ sourceCode(out, copyright);
++ }
++
++ if (isSpdxLicenseId(package.licenseId) && !package.licenseId.startsWith("LicenseRef-"_L1)
++ && package.licenseId != "NONE"_L1) {
++ out << "\\l{https://spdx.org/licenses/" << package.licenseId << ".html}"
++ << "{" << package.license << "}.\n\n";
++ } else {
++ out << package.license << ".\n\n";
++ }
++
++ for (const QString &license : package.licenseFilesContents)
++ sourceCode(out, license);
++
++ out << "*/\n";
++ }
++
++ void generate(QTextStream &out, const QList<Package> &packages, const QString &baseDirectory,
++ LogLevel logLevel)
++ {
++ if (logLevel == VerboseLog)
++ std::cerr << qPrintable(tr("Generating qdoc file...")) << std::endl;
++
++ QDir baseDir(baseDirectory);
++ for (const Package &package : packages)
++ generate(out, package, baseDir);
++ }
++
++ } // namespace QDocGenerator