sourcefiletemplate.cpp 11.3 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/*
 * 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.
 */

#include "sourcefiletemplate.h"
22
#include "templaterenderer.h"
23
#include <debug.h>
24
25
26
27

#include <interfaces/icore.h>

#include <KArchive>
Kevin Funk's avatar
Kevin Funk committed
28
#include <KConfig>
29
30
31
32
33
#include <KZip>
#include <KTar>
#include <KConfigGroup>

#include <QFileInfo>
34
#include <QDomDocument>
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
35
36
#include <QStandardPaths>
#include <QDir>
37
38

using namespace KDevelop;
39
using ConfigOption = SourceFileTemplate::ConfigOption;
40
41
42
43
44
45

class KDevelop::SourceFileTemplatePrivate
{
public:
    KArchive* archive;
    QString descriptionFileName;
46
    QStringList searchLocations;
47

48
    ConfigOption readEntry(const QDomElement& element, TemplateRenderer* renderer) const;
49
50
};

Milian Wolff's avatar
Milian Wolff committed
51
ConfigOption SourceFileTemplatePrivate::readEntry(const QDomElement& element,
52
                                                  TemplateRenderer* renderer) const
53
{
Milian Wolff's avatar
Milian Wolff committed
54
    ConfigOption entry;
55

56
57
    entry.name = element.attribute(QStringLiteral("name"));
    entry.type = element.attribute(QStringLiteral("type"), QStringLiteral("String"));
58

59
    bool isDefaultValueSet = false;
60
    for (QDomElement e = element.firstChildElement(); !e.isNull(); e = e.nextSiblingElement()) {
61
62
        QString tag = e.tagName();

63
        if (tag == QLatin1String("label")) {
64
            entry.label = e.text();
65
        } else if (tag == QLatin1String("tooltip")) {
66
            entry.label = e.text();
67
        } else if (tag == QLatin1String("whatsthis")) {
68
            entry.label = e.text();
69
        } else if (tag == QLatin1String("min")) {
70
            entry.minValue = e.text();
71
        } else if (tag == QLatin1String("max")) {
72
            entry.maxValue = e.text();
73
        } else if (tag == QLatin1String("default")) {
74
            entry.value = renderer->render(e.text(), entry.name);
75
            isDefaultValueSet = true;
76
        } else if (tag == QLatin1String("choices")) {
77
78
            QStringList values;
            QDomNodeList choices = element.elementsByTagName(QStringLiteral("choice"));
79
            values.reserve(choices.size());
80
81
82
83
            for (int j = 0; j < choices.size(); ++j) {
                QDomElement choiceElement = choices.at(j).toElement();
                values << choiceElement.attribute(QStringLiteral("name"));
            }
84

85
86
87
88
89
            Q_ASSERT(!values.isEmpty());
            if (values.isEmpty()) {
                qCWarning(LANGUAGE) << "Entry " << entry.name << "has an enum without any choices";
            }
            entry.values = values;
90
91
92
        }
    }

Dāvis Mosāns's avatar
Dāvis Mosāns committed
93
    qCDebug(LANGUAGE) << "Read entry" << entry.name << "with default value" << entry.value;
94
95
96
97
98
99
100
101
102
103
104
105
106
107

    // preset value for enum if needed
    if (!entry.values.isEmpty()) {
        if (isDefaultValueSet) {
            const bool isSaneDefaultValue = entry.values.contains(entry.value.toString());
            Q_ASSERT(isSaneDefaultValue);
            if (!isSaneDefaultValue) {
                qCWarning(LANGUAGE) << "Default value" << entry.value << "not in enum" << entry.values;
                entry.value = entry.values.at(0);
            }
        } else {
            entry.value = entry.values.at(0);
        }
    }
108
109
110
111
    return entry;
}

SourceFileTemplate::SourceFileTemplate (const QString& templateDescription)
112
    : d_ptr(new KDevelop::SourceFileTemplatePrivate)
113
{
114
115
    Q_D(SourceFileTemplate);

116
    d->archive = nullptr;
117
118
119
120
    setTemplateDescription(templateDescription);
}

SourceFileTemplate::SourceFileTemplate()
121
    : d_ptr(new KDevelop::SourceFileTemplatePrivate)
122
{
123
124
    Q_D(SourceFileTemplate);

125
    d->archive = nullptr;
126
127
}

128
SourceFileTemplate::SourceFileTemplate (const SourceFileTemplate& other)
129
    : d_ptr(new KDevelop::SourceFileTemplatePrivate)
130
{
131
132
    Q_D(SourceFileTemplate);

133
    d->archive = nullptr;
134
    *this = other;
135
136
}

137
138
SourceFileTemplate::~SourceFileTemplate()
{
139
140
    Q_D(SourceFileTemplate);

141
142
143
144
    delete d->archive;
}

SourceFileTemplate& SourceFileTemplate::operator=(const SourceFileTemplate& other)
145
{
146
147
148
    Q_D(SourceFileTemplate);

    if (other.d_ptr == d_ptr) {
149
150
151
152
        return *this;
    }

    delete d->archive;
153
154
155
    if (other.d_ptr->archive) {
        if (other.d_ptr->archive->fileName().endsWith(QLatin1String(".zip"))) {
            d->archive = new KZip(other.d_ptr->archive->fileName());
156
        } else {
157
            d->archive = new KTar(other.d_ptr->archive->fileName());
158
159
160
        }
        d->archive->open(QIODevice::ReadOnly);
    } else {
161
        d->archive = nullptr;
162
    }
163
    d->descriptionFileName = other.d_ptr->descriptionFileName;
164
165
166
    return *this;
}

167
void SourceFileTemplate::setTemplateDescription(const QString& templateDescription)
168
{
169
170
    Q_D(SourceFileTemplate);

171
172
    delete d->archive;

173
174
175
    d->descriptionFileName = templateDescription;
    QString archiveFileName;

176
177
    const QString templateBaseName = QFileInfo(templateDescription).baseName();

178
179
180
    d->searchLocations.append(QStandardPaths::locateAll(QStandardPaths::GenericDataLocation,
                                                        QStringLiteral("/kdevfiletemplates/templates/"),
                                                        QStandardPaths::LocateDirectory));
181

182
183
184
    for (const QString& dir : qAsConst(d->searchLocations)) {
        const auto fileEntries = QDir(dir).entryInfoList(QDir::Files);
        for (const auto& entry : fileEntries) {
185
186
187
188
189
            if (entry.baseName() == templateBaseName) {
                archiveFileName = entry.absoluteFilePath();
                qCDebug(LANGUAGE) << "Found template archive" << archiveFileName;
                break;
            }
190
        }
Aleix Pol Gonzalez's avatar
Aleix Pol Gonzalez committed
191
192
    }

193
    if (archiveFileName.isEmpty() || !QFileInfo::exists(archiveFileName)) {
194
195
        qCWarning(LANGUAGE) << "Could not find a template archive for description" << templateDescription <<
            ", archive file" << archiveFileName;
196
        d->archive = nullptr;
197
    } else {
198
199
        QFileInfo info(archiveFileName);

200
        if (info.suffix() == QLatin1String("zip")) {
201
            d->archive = new KZip(archiveFileName);
202
        } else {
203
204
205
206
207
208
            d->archive = new KTar(archiveFileName);
        }
        d->archive->open(QIODevice::ReadOnly);
    }
}

209
210
bool SourceFileTemplate::isValid() const
{
211
212
    Q_D(const SourceFileTemplate);

213
214
215
    return d->archive;
}

216
217
QString SourceFileTemplate::name() const
{
218
219
    Q_D(const SourceFileTemplate);

220
221
222
223
224
    KConfig templateConfig(d->descriptionFileName);
    KConfigGroup cg(&templateConfig, "General");
    return cg.readEntry("Name");
}

225
QString SourceFileTemplate::type() const
226
{
227
228
    Q_D(const SourceFileTemplate);

229
230
    KConfig templateConfig(d->descriptionFileName);
    KConfigGroup cg(&templateConfig, "General");
231
    return cg.readEntry("Type", QString());
232
233
}

234
235
QString SourceFileTemplate::languageName() const
{
236
237
    Q_D(const SourceFileTemplate);

238
239
    KConfig templateConfig(d->descriptionFileName);
    KConfigGroup cg(&templateConfig, "General");
240
241
242
243
244
    return cg.readEntry("Language", QString());
}

QStringList SourceFileTemplate::category() const
{
245
246
    Q_D(const SourceFileTemplate);

247
248
249
    KConfig templateConfig(d->descriptionFileName);
    KConfigGroup cg(&templateConfig, "General");
    return cg.readEntry("Category", QStringList());
250
251
}

252
253
QStringList SourceFileTemplate::defaultBaseClasses() const
{
254
255
    Q_D(const SourceFileTemplate);

256
257
258
259
260
    KConfig templateConfig(d->descriptionFileName);
    KConfigGroup cg(&templateConfig, "General");
    return cg.readEntry("BaseClasses", QStringList());
}

261
const KArchiveDirectory* SourceFileTemplate::directory() const
262
{
263
264
    Q_D(const SourceFileTemplate);

265
    Q_ASSERT(isValid());
266
267
268
    return d->archive->directory();
}

269
QVector<SourceFileTemplate::OutputFile> SourceFileTemplate::outputFiles() const
270
{
271
272
    Q_D(const SourceFileTemplate);

273
    QVector<SourceFileTemplate::OutputFile> outputFiles;
274

275
276
277
    KConfig templateConfig(d->descriptionFileName);
    KConfigGroup group(&templateConfig, "General");

278
    const QStringList files = group.readEntry("Files", QStringList());
Dāvis Mosāns's avatar
Dāvis Mosāns committed
279
    qCDebug(LANGUAGE) << "Files in template" << files;
280
281

    outputFiles.reserve(files.size());
282
    for (const QString& fileGroup : files) {
283
284
285
286
287
288
289
290
291
292
293
294
        KConfigGroup cg(&templateConfig, fileGroup);
        OutputFile f;
        f.identifier = cg.name();
        f.label = cg.readEntry("Name");
        f.fileName = cg.readEntry("File");
        f.outputName = cg.readEntry("OutputFile");
        outputFiles << f;
    }

    return outputFiles;
}

295
bool SourceFileTemplate::hasCustomOptions() const
296
{
297
298
    Q_D(const SourceFileTemplate);

299
300
    Q_ASSERT(isValid());

301
302
303
304
    KConfig templateConfig(d->descriptionFileName);
    KConfigGroup cg(&templateConfig, "General");
    bool hasOptions = d->archive->directory()->entries().contains(cg.readEntry("OptionsFile", "options.kcfg"));

Dāvis Mosāns's avatar
Dāvis Mosāns committed
305
    qCDebug(LANGUAGE) << cg.readEntry("OptionsFile", "options.kcfg") << hasOptions;
306
307
308
    return hasOptions;
}

309
QVector<SourceFileTemplate::ConfigOptionGroup> SourceFileTemplate::customOptions(TemplateRenderer* renderer) const
310
{
311
312
    Q_D(const SourceFileTemplate);

313
314
    Q_ASSERT(isValid());

315
316
317
318
    KConfig templateConfig(d->descriptionFileName);
    KConfigGroup cg(&templateConfig, "General");
    const KArchiveEntry* entry = d->archive->directory()->entry(cg.readEntry("OptionsFile", "options.kcfg"));

319
    QVector<ConfigOptionGroup> optionGroups;
320

321
    if (!entry->isFile()) {
322
        return optionGroups;
323
    }
324
    const auto* file = static_cast<const KArchiveFile*>(entry);
325
326
327
328
329
330
331
332

    /*
     * Copied from kconfig_compiler.kcfg
     */
    QDomDocument doc;
    QString errorMsg;
    int errorRow;
    int errorCol;
333
    if (!doc.setContent(file->data(), &errorMsg, &errorRow, &errorCol)) {
Dāvis Mosāns's avatar
Dāvis Mosāns committed
334
335
        qCDebug(LANGUAGE) << "Unable to load document.";
        qCDebug(LANGUAGE) << "Parse error in line " << errorRow << ", col " << errorCol << ": " << errorMsg;
336
        return optionGroups;
337
338
    }

339
    QDomElement cfgElement = doc.documentElement();
340
    if (cfgElement.isNull()) {
Dāvis Mosāns's avatar
Dāvis Mosāns committed
341
        qCDebug(LANGUAGE) << "No document in kcfg file";
342
        return optionGroups;
343
344
    }

345
    QDomNodeList groups = cfgElement.elementsByTagName(QStringLiteral("group"));
346
    optionGroups.reserve(groups.size());
347
    for (int i = 0; i < groups.size(); ++i) {
348
        QDomElement group = groups.at(i).toElement();
349
350
        ConfigOptionGroup optionGroup;
        optionGroup.name = group.attribute(QStringLiteral("name"));
351

352
        QDomNodeList entries = group.elementsByTagName(QStringLiteral("entry"));
353
        optionGroup.options.reserve(entries.size());
354
        for (int j = 0; j < entries.size(); ++j) {
355
            QDomElement entry = entries.at(j).toElement();
356
            optionGroup.options << d->readEntry(entry, renderer);
357
358
        }

359
        optionGroups << optionGroup;
360
    }
361

362
    return optionGroups;
363
}
364
365
366

void SourceFileTemplate::addAdditionalSearchLocation(const QString& location)
{
367
368
    Q_D(SourceFileTemplate);

369
    if (!d->searchLocations.contains(location))
370
371
        d->searchLocations.append(location);
}