templateclassgenerator.cpp 10.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/*  This file is part of KDevelop
    Copyright 2012 Miha Čančula <miha@noughmad.eu>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to
    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
    Boston, MA 02110-1301, USA.
18
 */
19 20

#include "templateclassgenerator.h"
21
#include "archivetemplateloader.h"
22
#include <debug.h>
23

24
#include <interfaces/iproject.h>
Miha Čančula's avatar
Miha Čančula committed
25
#include "language/codegen/documentchangeset.h"
26
#include "codedescription.h"
27
#include "templaterenderer.h"
28
#include "sourcefiletemplate.h"
Kevin Funk's avatar
Kevin Funk committed
29
#include <duchain/declaration.h>
30 31 32 33
#include <duchain/duchainlock.h>
#include <duchain/persistentsymboltable.h>
#include <duchain/types/structuretype.h>
#include <project/projectmodel.h>
Miha Čančula's avatar
Miha Čančula committed
34 35

#include <QFileInfo>
36

37 38
#include <array>

39 40
using namespace KDevelop;

41 42 43
/// @param base String such as 'public QObject' or 'QObject'
InheritanceDescription descriptionFromString(const QString& base)
{
44
    QStringList splitBase = base.split(QLatin1Char(' '));
45
    QString identifier = splitBase.takeLast();
46
    QString inheritanceMode = splitBase.join(QLatin1Char(' '));
47 48 49 50 51 52 53

    InheritanceDescription desc;
    desc.baseType = identifier;
    desc.inheritanceMode = inheritanceMode;
    return desc;
}

54 55
class KDevelop::TemplateClassGeneratorPrivate
{
Miha Čančula's avatar
Miha Čančula committed
56
public:
57
    SourceFileTemplate fileTemplate;
Milian Wolff's avatar
Milian Wolff committed
58
    QUrl baseUrl;
59 60
    // changes state when rendering
    mutable TemplateRenderer renderer;
61 62 63 64 65 66

    QString name;
    QString identifier;
    QStringList namespaces;
    QString license;

67 68
    // lazily estimated
    mutable QHash<QString, QUrl> fileUrls;
69
    QHash<QString, KTextEditor::Cursor> filePositions;
70
    ClassDescription description;
Dāvis Mosāns's avatar
Dāvis Mosāns committed
71

72 73
    QList<DeclarationPointer> directBaseClasses;
    QList<DeclarationPointer> allBaseClasses;
74 75

    void fetchSuperClasses(const DeclarationPointer& declaration);
76 77
};

Milian Wolff's avatar
Milian Wolff committed
78
void TemplateClassGeneratorPrivate::fetchSuperClasses(const DeclarationPointer& declaration)
79 80
{
    DUChainReadLocker lock;
81

82
    //Prevent duplicity
83
    if (allBaseClasses.contains(declaration)) {
84 85 86
        return;
    }

87 88
    allBaseClasses << declaration;

89 90
    DUContext* context = declaration->internalContext();
    if (context) {
91 92
        const auto importedParentContexts = context->importedParentContexts();
        for (const DUContext::Import& import : importedParentContexts) {
93
            if (DUContext* parentContext = import.context(context->topContext())) {
94
                if (parentContext->type() == DUContext::Class) {
95
                    fetchSuperClasses(DeclarationPointer(parentContext->owner()));
96 97 98 99 100 101
                }
            }
        }
    }
}

Milian Wolff's avatar
Milian Wolff committed
102
TemplateClassGenerator::TemplateClassGenerator(const QUrl& baseUrl)
103
    : d_ptr(new TemplateClassGeneratorPrivate)
104
{
105 106
    Q_D(TemplateClassGenerator);

David Narváez's avatar
David Narváez committed
107
    Q_ASSERT(QFileInfo(baseUrl.toLocalFile()).isDir()); // assume folder
Kevin Funk's avatar
Kevin Funk committed
108

109
    d->baseUrl = baseUrl;
110
    d->renderer.setEmptyLinesPolicy(TemplateRenderer::TrimEmptyLines);
111 112
}

113
TemplateClassGenerator::~TemplateClassGenerator() = default;
114

Milian Wolff's avatar
Milian Wolff committed
115
void TemplateClassGenerator::setTemplateDescription(const SourceFileTemplate& fileTemplate)
116
{
117 118
    Q_D(TemplateClassGenerator);

119
    d->fileTemplate = fileTemplate;
120
    Q_ASSERT(fileTemplate.isValid());
121 122
}

123 124
DocumentChangeSet TemplateClassGenerator::generate()
{
125 126
    Q_D(TemplateClassGenerator);

127
    return d->renderer.renderFileTemplate(d->fileTemplate, d->baseUrl, fileUrls());
128 129
}

Milian Wolff's avatar
Milian Wolff committed
130
QHash<QString, QString> TemplateClassGenerator::fileLabels() const
131
{
132 133
    Q_D(const TemplateClassGenerator);

134
    Q_ASSERT(d->fileTemplate.isValid());
135
    QHash<QString, QString> labels;
136

137 138 139
    const auto outputFiles = d->fileTemplate.outputFiles();
    labels.reserve(outputFiles.size());
    for (const SourceFileTemplate::OutputFile& outputFile : outputFiles) {
140
        labels.insert(outputFile.identifier, outputFile.label);
141
    }
142

143 144 145
    return labels;
}

Milian Wolff's avatar
Milian Wolff committed
146
TemplateClassGenerator::UrlHash TemplateClassGenerator::fileUrls() const
147
{
148 149
    Q_D(const TemplateClassGenerator);

150
    if (d->fileUrls.isEmpty()) {
151 152
        const auto outputFiles = d->fileTemplate.outputFiles();
        for (const SourceFileTemplate::OutputFile& outputFile : outputFiles) {
153
            QString outputName = d->renderer.render(outputFile.outputName, outputFile.identifier);
Kevin Funk's avatar
Kevin Funk committed
154
            QUrl url = d->baseUrl.resolved(QUrl(outputName));
155
            d->fileUrls.insert(outputFile.identifier, url);
156
        }
157
    }
158

159 160 161
    return d->fileUrls;
}

Milian Wolff's avatar
Milian Wolff committed
162
QUrl TemplateClassGenerator::baseUrl() const
Miha Čančula's avatar
Miha Čančula committed
163
{
164 165
    Q_D(const TemplateClassGenerator);

Miha Čančula's avatar
Miha Čančula committed
166 167 168
    return d->baseUrl;
}

Milian Wolff's avatar
Milian Wolff committed
169
QUrl TemplateClassGenerator::fileUrl(const QString& outputFile) const
170 171 172 173
{
    return fileUrls().value(outputFile);
}

Milian Wolff's avatar
Milian Wolff committed
174
void TemplateClassGenerator::setFileUrl(const QString& outputFile, const QUrl& url)
175
{
176 177
    Q_D(TemplateClassGenerator);

178
    d->fileUrls.insert(outputFile, url);
179 180 181 182
    d->renderer.addVariable(QLatin1String("output_file_") + outputFile.toLower(), QDir(
                                d->baseUrl.path()).relativeFilePath(url.path()));
    d->renderer.addVariable(QLatin1String("output_file_") + outputFile.toLower() + QLatin1String(
                                "_absolute"), url.toLocalFile());
183 184
}

185
KTextEditor::Cursor TemplateClassGenerator::filePosition(const QString& outputFile) const
186
{
187 188
    Q_D(const TemplateClassGenerator);

189 190 191
    return d->filePositions.value(outputFile);
}

192
void TemplateClassGenerator::setFilePosition(const QString& outputFile, const KTextEditor::Cursor& position)
193
{
194 195
    Q_D(TemplateClassGenerator);

196
    d->filePositions.insert(outputFile, position);
197
}
198 199 200

void TemplateClassGenerator::addVariables(const QVariantHash& variables)
{
201 202
    Q_D(TemplateClassGenerator);

203
    d->renderer.addVariables(variables);
204 205
}

Milian Wolff's avatar
Milian Wolff committed
206
QString TemplateClassGenerator::renderString(const QString& text) const
207
{
208 209
    Q_D(const TemplateClassGenerator);

210 211
    return d->renderer.render(text);
}
212

213
SourceFileTemplate TemplateClassGenerator::sourceFileTemplate() const
214
{
215 216
    Q_D(const TemplateClassGenerator);

217 218
    return d->fileTemplate;
}
219

Milian Wolff's avatar
Milian Wolff committed
220
TemplateRenderer* TemplateClassGenerator::renderer() const
221
{
222 223
    Q_D(const TemplateClassGenerator);

224 225 226
    return &(d->renderer);
}

227 228
QString TemplateClassGenerator::name() const
{
229 230
    Q_D(const TemplateClassGenerator);

231 232 233 234 235
    return d->name;
}

void TemplateClassGenerator::setName(const QString& newName)
{
236 237
    Q_D(TemplateClassGenerator);

238
    d->name = newName;
239
    d->renderer.addVariable(QStringLiteral("name"), newName);
240 241 242 243 244 245 246 247 248
}

QString TemplateClassGenerator::identifier() const
{
    return name();
}

void TemplateClassGenerator::setIdentifier(const QString& identifier)
{
249 250
    Q_D(TemplateClassGenerator);

251
    d->renderer.addVariable(QStringLiteral("identifier"), identifier);
252
    const std::array<QString,5> separators {
253 254 255 256 257 258
        QStringLiteral("::"),
        QStringLiteral("."),
        QStringLiteral(":"),
        QStringLiteral("\\"),
        QStringLiteral("/"),
    };
259
    QStringList ns;
260
    for (const QString& separator : separators) {
261
        ns = identifier.split(separator);
262
        if (ns.size() > 1) {
263 264 265
            break;
        }
    }
266

267 268 269 270 271 272
    setName(ns.takeLast());
    setNamespaces(ns);
}

QStringList TemplateClassGenerator::namespaces() const
{
273 274
    Q_D(const TemplateClassGenerator);

275 276 277
    return d->namespaces;
}

278
void TemplateClassGenerator::setNamespaces(const QStringList& namespaces)
279
{
280 281
    Q_D(TemplateClassGenerator);

282
    d->namespaces = namespaces;
283
    d->renderer.addVariable(QStringLiteral("namespaces"), namespaces);
284 285 286 287 288
}

/// Specify license for this class
void TemplateClassGenerator::setLicense(const QString& license)
{
289 290
    Q_D(TemplateClassGenerator);

Dāvis Mosāns's avatar
Dāvis Mosāns committed
291
    qCDebug(LANGUAGE) << "New Class: " << d->name << "Set license: " << d->license;
292
    d->license = license;
293
    d->renderer.addVariable(QStringLiteral("license"), license);
294 295 296 297 298
}

/// Get the license specified for this classes
QString TemplateClassGenerator::license() const
{
299 300
    Q_D(const TemplateClassGenerator);

301 302 303
    return d->license;
}

Milian Wolff's avatar
Milian Wolff committed
304
void TemplateClassGenerator::setDescription(const ClassDescription& description)
305
{
306 307
    Q_D(TemplateClassGenerator);

308
    d->description = description;
309 310

    QVariantHash variables;
311 312 313 314
    variables[QStringLiteral("description")] = QVariant::fromValue(description);
    variables[QStringLiteral("members")] = CodeDescription::toVariantList(description.members);
    variables[QStringLiteral("functions")] = CodeDescription::toVariantList(description.methods);
    variables[QStringLiteral("base_classes")] = CodeDescription::toVariantList(description.baseClasses);
315
    d->renderer.addVariables(variables);
316 317 318 319
}

ClassDescription TemplateClassGenerator::description() const
{
320 321
    Q_D(const TemplateClassGenerator);

322 323 324
    return d->description;
}

Milian Wolff's avatar
Milian Wolff committed
325
void TemplateClassGenerator::addBaseClass(const QString& base)
326
{
327 328
    Q_D(TemplateClassGenerator);

329
    const InheritanceDescription desc = descriptionFromString(base);
330 331 332 333

    ClassDescription cd = description();
    cd.baseClasses << desc;
    setDescription(cd);
Milian Wolff's avatar
Milian Wolff committed
334

335
    DUChainReadLocker lock;
Milian Wolff's avatar
Milian Wolff committed
336

337 338
    PersistentSymbolTable::Declarations decl =
        PersistentSymbolTable::self().declarations(IndexedQualifiedIdentifier(QualifiedIdentifier(desc.baseType)));
Milian Wolff's avatar
Milian Wolff committed
339

340
    //Search for all super classes
341
    for (PersistentSymbolTable::Declarations::Iterator it = decl.iterator(); it; ++it) {
342
        DeclarationPointer declaration = DeclarationPointer(it->declaration());
343
        if (declaration->isForwardDeclaration()) {
344 345 346 347
            continue;
        }

        // Check if it's a class/struct/etc
348
        if (declaration->type<StructureType>()) {
349
            d->fetchSuperClasses(declaration);
350
            d->directBaseClasses << declaration;
351 352 353 354 355
            break;
        }
    }
}

356 357
void TemplateClassGenerator::setBaseClasses(const QList<QString>& bases)
{
358 359
    Q_D(TemplateClassGenerator);

360 361 362 363 364 365 366 367 368
    // clear
    ClassDescription cd = description();
    cd.baseClasses.clear();
    setDescription(cd);

    d->directBaseClasses.clear();
    d->allBaseClasses.clear();

    // add all bases
369
    for (const QString& base : bases) {
370 371 372 373
        addBaseClass(base);
    }
}

374
QList<DeclarationPointer> TemplateClassGenerator::directBaseClasses() const
375
{
376 377
    Q_D(const TemplateClassGenerator);

378
    return d->directBaseClasses;
379
}
380

381
QList<DeclarationPointer> TemplateClassGenerator::allBaseClasses() const
382
{
383 384
    Q_D(const TemplateClassGenerator);

385 386
    return d->allBaseClasses;
}