1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
4#include "cppwriteincludes.h"
5#include "driver.h"
6#include "ui4.h"
7#include "uic.h"
8#include "databaseinfo.h"
9
10#include <qdebug.h>
11#include <qfileinfo.h>
12#include <qtextstream.h>
13
14#include <stdio.h>
15
16QT_BEGIN_NAMESPACE
17
18using namespace Qt::StringLiterals;
19
20enum { debugWriteIncludes = 0 };
21enum { warnHeaderGeneration = 0 };
22
23// Format a module header as 'QtCore/QObject'
24static inline QString moduleHeader(const QString &module, const QString &header)
25{
26 QString rc = module;
27 rc += u'/';
28 rc += header;
29 return rc;
30}
31
32namespace CPP {
33
34WriteIncludes::WriteIncludes(Uic *uic) : WriteIncludesBase(uic),
35 m_output(uic->output())
36{
37 // When possible (no namespace) use the "QtModule/QClass" convention
38 // and create a re-mapping of the old header "qclass.h" to it. Do not do this
39 // for the "Phonon::Someclass" classes, however.
40 const QLatin1StringView namespaceDelimiter = "::"_L1;
41 for (const auto &e : classInfoEntries()) {
42 const QString klass = QLatin1StringView(e.klass);
43 const QString module = QLatin1StringView(e.module);
44 QLatin1StringView header = QLatin1StringView(e.header);
45 if (klass.contains(s: namespaceDelimiter)) {
46 m_classToHeader.insert(key: klass, value: moduleHeader(module, header));
47 } else {
48 const QString newHeader = moduleHeader(module, header: klass);
49 m_classToHeader.insert(key: klass, value: newHeader);
50 m_oldHeaderToNewHeader.insert(key: header, value: newHeader);
51 }
52 }
53}
54
55void WriteIncludes::acceptUI(DomUI *node)
56{
57 m_localIncludes.clear();
58 m_globalIncludes.clear();
59 m_includeBaseNames.clear();
60
61 WriteIncludesBase::acceptUI(node);
62
63 const auto includeFile = uic()->option().includeFile;
64 if (!includeFile.isEmpty())
65 m_globalIncludes.insert(x: includeFile);
66
67 writeHeaders(headers: m_globalIncludes, global: true);
68 writeHeaders(headers: m_localIncludes, global: false);
69
70 m_output << '\n';
71}
72
73void WriteIncludes::insertIncludeForClass(const QString &className, QString header, bool global)
74{
75 if (debugWriteIncludes)
76 fprintf(stderr, format: "%s %s '%s' %d\n", Q_FUNC_INFO, qPrintable(className), qPrintable(header), global);
77
78 do {
79 if (!header.isEmpty())
80 break;
81
82 // Known class
83 const StringMap::const_iterator it = m_classToHeader.constFind(key: className);
84 if (it != m_classToHeader.constEnd()) {
85 header = it.value();
86 global = true;
87 break;
88 }
89
90 // Quick check by class name to detect includehints provided for custom widgets.
91 // Remove namespaces
92 QString lowerClassName = className.toLower();
93 static const auto namespaceSeparator = "::"_L1;
94 const int namespaceIndex = lowerClassName.lastIndexOf(s: namespaceSeparator);
95 if (namespaceIndex != -1)
96 lowerClassName.remove(i: 0, len: namespaceIndex + namespaceSeparator.size());
97 if (m_includeBaseNames.contains(value: lowerClassName)) {
98 header.clear();
99 break;
100 }
101
102 // Last resort: Create default header
103 if (!uic()->option().implicitIncludes)
104 break;
105 header = lowerClassName;
106 header += ".h"_L1;
107 if (warnHeaderGeneration) {
108 qWarning(msg: "%s: Warning: generated header '%s' for class '%s'.",
109 qPrintable(uic()->option().messagePrefix()),
110 qPrintable(header), qPrintable(className));
111
112 }
113
114 global = true;
115 } while (false);
116
117 if (!header.isEmpty())
118 insertInclude(header, global);
119}
120
121void WriteIncludes::doAdd(const QString &className, const DomCustomWidget *dcw)
122{
123 if (dcw != nullptr)
124 addCppCustomWidget(className, dcw);
125 else
126 insertIncludeForClass(className, header: {}, global: false);
127}
128
129void WriteIncludes::addCppCustomWidget(const QString &className, const DomCustomWidget *dcw)
130{
131 const DomHeader *domHeader = dcw->elementHeader();
132 if (domHeader != nullptr && !domHeader->text().isEmpty()) {
133 // custom header unless it is a built-in qt class
134 QString header;
135 bool global = false;
136 if (!m_classToHeader.contains(key: className)) {
137 global = domHeader->attributeLocation().toLower() == "global"_L1;
138 header = domHeader->text();
139 }
140 insertIncludeForClass(className, header, global);
141 return;
142 }
143}
144
145void WriteIncludes::acceptInclude(DomInclude *node)
146{
147 bool global = true;
148 if (node->hasAttributeLocation())
149 global = node->attributeLocation() == "global"_L1;
150 insertInclude(header: node->text(), global);
151}
152
153void WriteIncludes::insertInclude(const QString &header, bool global)
154{
155 if (debugWriteIncludes)
156 fprintf(stderr, format: "%s %s %d\n", Q_FUNC_INFO, qPrintable(header), global);
157
158 OrderedSet &includes = global ? m_globalIncludes : m_localIncludes;
159 // Insert (if not already done).
160 const bool isNewHeader = includes.insert(x: header).second;
161 if (!isNewHeader)
162 return;
163 // Also remember base name for quick check of suspicious custom plugins
164 const QString lowerBaseName = QFileInfo(header).completeBaseName ().toLower();
165 m_includeBaseNames.insert(value: lowerBaseName);
166}
167
168void WriteIncludes::writeHeaders(const OrderedSet &headers, bool global)
169{
170 const QChar openingQuote = global ? u'<' : u'"';
171 const QChar closingQuote = global ? u'>' : u'"';
172
173 // Check for the old headers 'qslider.h' and replace by 'QtGui/QSlider'
174 for (const QString &header : headers) {
175 const QString value = m_oldHeaderToNewHeader.value(key: header, defaultValue: header);
176 const auto trimmed = QStringView(value).trimmed();
177 if (!trimmed.isEmpty())
178 m_output << "#include " << openingQuote << trimmed << closingQuote << '\n';
179 }
180}
181
182} // namespace CPP
183
184QT_END_NAMESPACE
185

source code of qtbase/src/tools/uic/cpp/cppwriteincludes.cpp