1 /****************************************************************************
2 **
3 ** Copyright (C) 2018 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the tools applications of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21 ** included in the packaging of this file. Please review the following
22 ** information to ensure the GNU General Public License requirements will
23 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24 **
25 ** $QT_END_LICENSE$
26 **
27 ****************************************************************************/
28 
29 #include "language.h"
30 
31 #include <QtCore/qtextstream.h>
32 
33 namespace language {
34 
35 static Encoding encoding = Encoding::Utf8;
36 static Language _language = Language::Cpp;
37 
language()38 Language language() { return _language; }
39 
setLanguage(Language l)40 void setLanguage(Language l)
41 {
42     _language = l;
43     switch (_language) {
44     case Language::Cpp:
45         derefPointer = QLatin1String("->");
46         nullPtr = QLatin1String("nullptr");
47         operatorNew = QLatin1String("new ");
48         qtQualifier = QLatin1String("Qt::");
49         qualifier = QLatin1String("::");
50         self = QLatin1String("");  // for testing: change to "this->";
51         eol = QLatin1String(";\n");
52         emptyString = QLatin1String("QString()");
53         encoding = Encoding::Utf8;
54         break;
55     case Language::Python:
56         derefPointer = QLatin1String(".");
57         nullPtr = QLatin1String("None");
58         operatorNew = QLatin1String("");
59         qtQualifier = QLatin1String("Qt.");
60         qualifier = QLatin1String(".");
61         self = QLatin1String("self.");
62         eol = QLatin1String("\n");
63         emptyString = QLatin1String("\"\"");
64         encoding = Encoding::Unicode;
65         break;
66     }
67 }
68 
69 QString derefPointer;
70 QString nullPtr;
71 QString operatorNew;
72 QString qtQualifier;
73 QString qualifier;
74 QString self;
75 QString eol;
76 QString emptyString;
77 
78 QString cppQualifier = QLatin1String("::");
79 QString cppTrue = QLatin1String("true");
80 QString cppFalse = QLatin1String("false");
81 
operator <<(QTextStream & str,const qtConfig & c)82 QTextStream &operator<<(QTextStream &str, const qtConfig &c)
83 {
84     str << "QT_CONFIG(" << c.parameter() << ')';
85     return str;
86 }
87 
operator <<(QTextStream & str,const openQtConfig & c)88 QTextStream &operator<<(QTextStream &str, const openQtConfig &c)
89 {
90     str << "#if " << qtConfig(c.parameter())  << '\n';
91     return str;
92 }
93 
operator <<(QTextStream & str,const closeQtConfig & c)94 QTextStream &operator<<(QTextStream &str, const closeQtConfig &c)
95 {
96     str << "#endif // " << qtConfig(c.parameter()) << '\n';
97     return str;
98 }
99 
100 struct EnumLookup
101 {
102     int value;
103     const char *valueString;
104 };
105 
106 template <int N>
lookupEnum(const EnumLookup (& array)[N],int value,int defaultIndex=0)107 const char *lookupEnum(const EnumLookup(&array)[N], int value, int defaultIndex = 0)
108 {
109     for (int i = 0; i < N; ++i) {
110         if (value == array[i].value)
111             return array[i].valueString;
112     }
113     const char *defaultValue = array[defaultIndex].valueString;
114     qWarning("uic: Warning: Invalid enumeration value %d, defaulting to %s",
115              value, defaultValue);
116     return defaultValue;
117 }
118 
fixClassName(QString className)119 QString fixClassName(QString className)
120 {
121     if (language() == Language::Python)
122         className.replace(cppQualifier, QLatin1String("_"));
123     return className;
124 }
125 
toolbarArea(int v)126 const char *toolbarArea(int v)
127 {
128     static const EnumLookup toolBarAreas[] =
129     {
130         {0,   "NoToolBarArea"},
131         {0x1, "LeftToolBarArea"},
132         {0x2, "RightToolBarArea"},
133         {0x4, "TopToolBarArea"},
134         {0x8, "BottomToolBarArea"},
135         {0xf, "AllToolBarAreas"}
136     };
137     return lookupEnum(toolBarAreas, v);
138 }
139 
sizePolicy(int v)140 const char *sizePolicy(int v)
141 {
142     static const EnumLookup sizePolicies[] =
143     {
144         {0,   "Fixed"},
145         {0x1, "Minimum"},
146         {0x4, "Maximum"},
147         {0x5, "Preferred"},
148         {0x3, "MinimumExpanding"},
149         {0x7, "Expanding"},
150         {0xD, "Ignored"}
151     };
152     return lookupEnum(sizePolicies, v, 3);
153 }
154 
dockWidgetArea(int v)155 const char *dockWidgetArea(int v)
156 {
157     static const EnumLookup dockWidgetAreas[] =
158     {
159         {0,   "NoDockWidgetArea"},
160         {0x1, "LeftDockWidgetArea"},
161         {0x2, "RightDockWidgetArea"},
162         {0x4, "TopDockWidgetArea"},
163         {0x8, "BottomDockWidgetArea"},
164         {0xf, "AllDockWidgetAreas"}
165     };
166     return lookupEnum(dockWidgetAreas, v);
167 }
168 
paletteColorRole(int v)169 const char *paletteColorRole(int v)
170 {
171     static const EnumLookup colorRoles[] =
172     {
173         {0, "WindowText"},
174         {1, "Button"},
175         {2, "Light"},
176         {3, "Midlight"},
177         {4, "Dark"},
178         {5, "Mid"},
179         {6, "Text"},
180         {7, "BrightText"},
181         {8, "ButtonText"},
182         {9, "Base"},
183         {10, "Window"},
184         {11, "Shadow"},
185         {12, "Highlight"},
186         {13, "HighlightedText"},
187         {14, "Link"},
188         {15, "LinkVisited"},
189         {16, "AlternateBase"},
190         {17, "NoRole"},
191         {18, "ToolTipBase"},
192         {19, "ToolTipText"},
193         {20, "PlaceholderText"},
194     };
195     return lookupEnum(colorRoles, v);
196 }
197 
198 // Helpers for formatting a character sequences
199 
200 // Format a special character like '\x0a'
formatEscapedNumber(QTextStream & str,ushort value,int base,int width,char prefix=0)201 static int formatEscapedNumber(QTextStream &str, ushort value, int base, int width,
202                                char prefix = 0)
203 {
204     int length = 1 + width;
205     str << '\\';
206     if (prefix) {
207         str << prefix;
208         ++length;
209     }
210     const auto oldPadChar = str.padChar();
211     const auto oldFieldWidth = str.fieldWidth();
212     const auto oldFieldAlignment = str.fieldAlignment();
213     const auto oldIntegerBase = str.integerBase();
214     str.setPadChar(QLatin1Char('0'));
215     str.setFieldWidth(width);
216     str.setFieldAlignment(QTextStream::AlignRight);
217     str.setIntegerBase(base);
218     str << value;
219     str.setIntegerBase(oldIntegerBase);
220     str.setFieldAlignment(oldFieldAlignment);
221     str.setFieldWidth(oldFieldWidth);
222     str.setPadChar(oldPadChar);
223     return length;
224 }
225 
formatSpecialCharacter(QTextStream & str,ushort value)226 static int formatSpecialCharacter(QTextStream &str, ushort value)
227 {
228     int length = 0;
229     switch (value) {
230     case '\\':
231         str << "\\\\";
232         length += 2;
233         break;
234     case '\"':
235         str << "\\\"";
236         length += 2;
237         break;
238     case '\n':
239         str << "\\n\"\n\"";
240         length += 5;
241         break;
242     default:
243         break;
244     }
245     return length;
246 }
247 
248 // Format a sequence of characters for C++ with special characters numerically
249 // escaped (non-raw string literals), wrappped at maxSegmentSize. FormattingTraits
250 // are used to transform characters into (unsigned) codes, which can be used
251 // for either normal escapes or Unicode code points as used in Unicode literals.
252 
253 enum : int { maxSegmentSize = 1024 };
254 
255 template <Encoding e>
256 struct FormattingTraits
257 {
258 };
259 
260 template <>
261 struct FormattingTraits<Encoding::Utf8>
262 {
codelanguage::FormattingTraits263     static ushort code(char c) { return uchar(c); }
264 };
265 
266 template <>
267 struct FormattingTraits<Encoding::Unicode>
268 {
codelanguage::FormattingTraits269     static ushort code(QChar c) { return c.unicode(); }
270 };
271 
272 template <Encoding e, class Iterator>
formatStringSequence(QTextStream & str,Iterator it,Iterator end,const QString & indent,int escapeIntegerBase,int escapeWidth,char escapePrefix=0)273 static void formatStringSequence(QTextStream &str, Iterator it, Iterator end,
274                                  const QString &indent,
275                                  int escapeIntegerBase, int escapeWidth,
276                                  char escapePrefix = 0)
277 {
278     str << '"';
279     int length = 0;
280     while (it != end) {
281         const auto code = FormattingTraits<e>::code(*it);
282         if (code >= 0x80) {
283             length += formatEscapedNumber(str, code, escapeIntegerBase, escapeWidth, escapePrefix);
284         } else if (const int l = formatSpecialCharacter(str, code)) {
285             length += l;
286         } else if (code != '\r') {
287             str << *it;
288             ++length;
289         }
290         ++it;
291         if (it != end && length > maxSegmentSize) {
292             str << "\"\n" << indent << indent << '"';
293             length = 0;
294         }
295     }
296     str << '"';
297 }
298 
_formatString(QTextStream & str,const QString & value,const QString & indent,bool qString)299 void _formatString(QTextStream &str, const QString &value, const QString &indent,
300                    bool qString)
301 {
302     switch (encoding) {
303     // Special characters as 3 digit octal escapes (u8"\303\234mlaut")
304     case Encoding::Utf8: {
305         if (qString && _language == Language::Cpp)
306             str << "QString::fromUtf8(";
307         const QByteArray utf8 = value.toUtf8();
308         formatStringSequence<Encoding::Utf8>(str, utf8.cbegin(), utf8.cend(), indent,
309                                              8, 3);
310         if (qString && _language == Language::Cpp)
311             str << ')';
312     }
313         break;
314     // Special characters as 4 digit hex Unicode points (u8"\u00dcmlaut")
315     case Encoding::Unicode:
316         str << 'u'; // Python Unicode literal (would be UTF-16 in C++)
317         formatStringSequence<Encoding::Unicode>(str, value.cbegin(), value.cend(), indent,
318                                                 16, 4, 'u');
319         break;
320     }
321 }
322 
operator <<(QTextStream & str,const repeat & r)323 QTextStream &operator<<(QTextStream &str, const repeat &r)
324 {
325     for (int i = 0; i < r.m_count; ++i)
326         str << r.m_char;
327     return str;
328 }
329 
startFunctionDefinition1(const char * name,const QString & parameterType,const QString & parameterName,const QString & indent,const char * returnType)330 startFunctionDefinition1::startFunctionDefinition1(const char *name, const QString &parameterType,
331                                                    const QString &parameterName,
332                                                    const QString &indent,
333                                                    const char *returnType) :
334     m_name(name), m_parameterType(parameterType), m_parameterName(parameterName),
335     m_indent(indent), m_return(returnType)
336 {
337 }
338 
operator <<(QTextStream & str,const startFunctionDefinition1 & f)339 QTextStream &operator<<(QTextStream &str, const startFunctionDefinition1 &f)
340 {
341     switch (language()) {
342     case Language::Cpp:
343         str << (f.m_return ? f.m_return : "void") << ' ' << f.m_name << '('
344             << f.m_parameterType;
345         if (f.m_parameterType.cend()->isLetter())
346             str << ' ';
347         str << f.m_parameterName << ')' << '\n' << f.m_indent << "{\n";
348         break;
349     case Language::Python:
350         str << "def " << f.m_name << "(self, " << f.m_parameterName << "):\n";
351         break;
352     }
353     return str;
354 }
355 
endFunctionDefinition(const char * name)356 endFunctionDefinition::endFunctionDefinition(const char *name) : m_name(name)
357 {
358 }
359 
operator <<(QTextStream & str,const endFunctionDefinition & f)360 QTextStream &operator<<(QTextStream &str, const endFunctionDefinition &f)
361 {
362     switch (language()) {
363     case Language::Cpp:
364         str << "} // " << f.m_name << "\n\n";
365         break;
366     case Language::Python:
367         str << "# " << f.m_name << "\n\n";
368         break;
369     }
370     return str;
371 }
372 
_formatStackVariable(QTextStream & str,const char * className,QStringView varName,bool withInitParameters)373 void _formatStackVariable(QTextStream &str, const char *className, QStringView varName,
374                           bool withInitParameters)
375 {
376     switch (language()) {
377     case Language::Cpp:
378         str << className << ' ' << varName;
379         if (withInitParameters)
380             str << '(';
381         break;
382     case Language::Python:
383         str << varName << " = " << className << '(';
384         if (!withInitParameters)
385             str << ')';
386         break;
387     }
388 }
389 
formatConnection(QTextStream & str,const SignalSlot & sender,const SignalSlot & receiver)390 void formatConnection(QTextStream &str, const SignalSlot &sender, const SignalSlot &receiver)
391 {
392     switch (language()) {
393     case Language::Cpp:
394         str << "QObject::connect(" << sender.name << ", SIGNAL("<< sender.signature
395             << "), " << receiver.name << ", SLOT("<< receiver.signature << "))";
396         break;
397     case Language::Python:
398         str << sender.name << '.'
399             << sender.signature.leftRef(sender.signature.indexOf(QLatin1Char('(')))
400             << ".connect(" << receiver.name << '.'
401             << receiver.signature.leftRef(receiver.signature.indexOf(QLatin1Char('(')))
402             << ')';
403         break;
404     }
405 }
406 
boolValue(bool v)407 QString boolValue(bool v)
408 {
409     switch (language()) {
410     case Language::Cpp:
411         return v ? cppTrue : cppFalse;
412     case Language::Python:
413         return v ? QStringLiteral("True") : QStringLiteral("False");
414     }
415     Q_UNREACHABLE();
416 }
417 
dot()418 static inline QString dot() { return QStringLiteral("."); }
419 
enumValue(const QString & value)420 QString enumValue(const QString &value)
421 {
422     if (language() == Language::Cpp || !value.contains(cppQualifier))
423         return value;
424     QString fixed = value;
425     fixed.replace(cppQualifier, dot());
426     return fixed;
427 }
428 
429 } // namespace language
430