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 "qqmljsstreamwriter_p.h" |
5 | #include "qanystringviewutils_p.h" |
6 | |
7 | #include <QtCore/QBuffer> |
8 | #include <QtCore/QStringList> |
9 | |
10 | QT_BEGIN_NAMESPACE |
11 | |
12 | // TODO: All of this could be improved by using and re-using one buffer for all the writing. |
13 | // We don't really need to allocate any temporary byte arrays. |
14 | |
15 | static QByteArray enquoteByteArray(QByteArrayView string) |
16 | { |
17 | const qsizetype length = string.length(); |
18 | QByteArray buffer; |
19 | buffer.reserve(asize: length + 2); |
20 | buffer.append(c: ' '); |
21 | buffer.append(a: string); |
22 | buffer.append(c: ' '); |
23 | buffer.replace(before: '\\', after: "\\\\").replace(before: '"', after: "\\\""); |
24 | buffer[0] = '"'; |
25 | buffer[buffer.length() - 1] = '"'; |
26 | return buffer; |
27 | } |
28 | |
29 | static QByteArray enquoteAnyString(QAnyStringView string) |
30 | { |
31 | return string.visit(v: [](auto data) { |
32 | return QAnyStringViewUtils::processAsUtf8(data, [](QByteArrayView view) { |
33 | return enquoteByteArray(string: view); |
34 | }); |
35 | }); |
36 | } |
37 | |
38 | QQmlJSStreamWriter::QQmlJSStreamWriter(QByteArray *array) |
39 | : m_indentDepth(0) |
40 | , m_pendingLineLength(0) |
41 | , m_maybeOneline(false) |
42 | , m_stream(new QBuffer(array)) |
43 | { |
44 | m_stream->open(mode: QIODevice::WriteOnly); |
45 | } |
46 | |
47 | void QQmlJSStreamWriter::writeStartDocument() |
48 | { |
49 | } |
50 | |
51 | void QQmlJSStreamWriter::writeEndDocument() |
52 | { |
53 | } |
54 | |
55 | void QQmlJSStreamWriter::writeLibraryImport( |
56 | QByteArrayView uri, int majorVersion, int minorVersion, QByteArrayView as) |
57 | { |
58 | m_stream->write(data: "import "); |
59 | m_stream->write(data: uri.data(), len: uri.length()); |
60 | m_stream->write(data: " "); |
61 | m_stream->write(data: QByteArray::number(majorVersion)); |
62 | m_stream->write(data: "."); |
63 | m_stream->write(data: QByteArray::number(minorVersion)); |
64 | if (!as.isEmpty()) { |
65 | m_stream->write(data: " as "); |
66 | m_stream->write(data: as.data(), len: as.length()); |
67 | } |
68 | m_stream->write(data: "\n"); |
69 | } |
70 | |
71 | void QQmlJSStreamWriter::writeStartObject(QByteArrayView component) |
72 | { |
73 | flushPotentialLinesWithNewlines(); |
74 | writeIndent(); |
75 | m_stream->write(data: component.data(), len: component.length()); |
76 | m_stream->write(data: " {"); |
77 | ++m_indentDepth; |
78 | m_maybeOneline = true; |
79 | } |
80 | |
81 | void QQmlJSStreamWriter::writeEndObject() |
82 | { |
83 | if (m_maybeOneline) { |
84 | --m_indentDepth; |
85 | for (int i = 0; i < m_pendingLines.size(); ++i) { |
86 | m_stream->write(data: " "); |
87 | m_stream->write(data: m_pendingLines.at(i).trimmed()); |
88 | if (i != m_pendingLines.size() - 1) |
89 | m_stream->write(data: ";"); |
90 | } |
91 | |
92 | if (!m_pendingLines.isEmpty()) |
93 | m_stream->write(data: " }\n"); |
94 | else |
95 | m_stream->write(data: "}\n"); |
96 | |
97 | m_pendingLines.clear(); |
98 | m_pendingLineLength = 0; |
99 | m_maybeOneline = false; |
100 | } else { |
101 | flushPotentialLinesWithNewlines(); |
102 | --m_indentDepth; |
103 | writeIndent(); |
104 | m_stream->write(data: "}\n"); |
105 | } |
106 | } |
107 | |
108 | void QQmlJSStreamWriter::writeScriptBinding(QByteArrayView name, QByteArrayView rhs) |
109 | { |
110 | QByteArray buffer; |
111 | buffer.reserve(asize: name.length() + 2 + rhs.length()); |
112 | buffer.append(a: name); |
113 | buffer.append(s: ": "); |
114 | buffer.append(a: rhs); |
115 | writePotentialLine(line: buffer); |
116 | } |
117 | |
118 | void QQmlJSStreamWriter::writeStringBinding(QByteArrayView name, QAnyStringView value) |
119 | { |
120 | writeScriptBinding(name, rhs: enquoteAnyString(string: value)); |
121 | } |
122 | |
123 | void QQmlJSStreamWriter::writeNumberBinding(QByteArrayView name, qint64 value) |
124 | { |
125 | writeScriptBinding(name, rhs: QByteArray::number(value)); |
126 | } |
127 | |
128 | void QQmlJSStreamWriter::writeBooleanBinding(QByteArrayView name, bool value) |
129 | { |
130 | writeScriptBinding(name, rhs: value ? "true": "false"); |
131 | } |
132 | |
133 | template<typename String, typename ElementHandler> |
134 | void QQmlJSStreamWriter::doWriteArrayBinding( |
135 | QByteArrayView name, const QList<String> &elements, ElementHandler &&handler) |
136 | { |
137 | flushPotentialLinesWithNewlines(); |
138 | writeIndent(); |
139 | |
140 | // try to use a single line |
141 | QByteArray singleLine(name.data(), name.length()); |
142 | singleLine += ": ["; |
143 | for (int i = 0; i < elements.size(); ++i) { |
144 | QAnyStringViewUtils::processAsUtf8(elements.at(i), [&](QByteArrayView element) { |
145 | singleLine += handler(element); |
146 | }); |
147 | if (i != elements.size() - 1) |
148 | singleLine += ", "; |
149 | } |
150 | singleLine += "]\n"; |
151 | if (singleLine.size() + m_indentDepth * 4 < 80) { |
152 | m_stream->write(data: singleLine); |
153 | return; |
154 | } |
155 | |
156 | // write multi-line |
157 | m_stream->write(data: name.data(), len: name.length()); |
158 | m_stream->write(data: ": [\n"); |
159 | ++m_indentDepth; |
160 | for (int i = 0; i < elements.size(); ++i) { |
161 | writeIndent(); |
162 | QAnyStringViewUtils::processAsUtf8(elements.at(i), [&](QByteArrayView element) { |
163 | const auto handled = handler(element); |
164 | m_stream->write(handled.data(), handled.length()); |
165 | }); |
166 | if (i != elements.size() - 1) { |
167 | m_stream->write(data: ",\n"); |
168 | } else { |
169 | m_stream->write(data: "\n"); |
170 | } |
171 | } |
172 | --m_indentDepth; |
173 | writeIndent(); |
174 | m_stream->write(data: "]\n"); |
175 | } |
176 | |
177 | void QQmlJSStreamWriter::writeArrayBinding(QByteArrayView name, const QByteArrayList &elements) |
178 | { |
179 | doWriteArrayBinding(name, elements, handler: [](QByteArrayView view) { return view; }); |
180 | } |
181 | |
182 | void QQmlJSStreamWriter::writeStringListBinding( |
183 | QByteArrayView name, const QList<QAnyStringView> &elements) |
184 | { |
185 | doWriteArrayBinding(name, elements, handler&: enquoteByteArray); |
186 | } |
187 | |
188 | void QQmlJSStreamWriter::write(QByteArrayView data) |
189 | { |
190 | flushPotentialLinesWithNewlines(); |
191 | m_stream->write(data: data.data(), len: data.length()); |
192 | } |
193 | |
194 | void QQmlJSStreamWriter::writeEnumObjectLiteralBinding( |
195 | QByteArrayView name, const QList<QPair<QAnyStringView, int> > &keyValue) |
196 | { |
197 | flushPotentialLinesWithNewlines(); |
198 | writeIndent(); |
199 | m_stream->write(data: name.data(), len: name.length()); |
200 | m_stream->write(data: ": {\n"); |
201 | ++m_indentDepth; |
202 | for (int i = 0, end = keyValue.size(); i != end; ++i) { |
203 | writeIndent(); |
204 | const auto &entry = keyValue[i]; |
205 | m_stream->write(data: enquoteAnyString(string: entry.first)); |
206 | m_stream->write(data: ": "); |
207 | m_stream->write(data: QByteArray::number(entry.second)); |
208 | if (i != end - 1) |
209 | m_stream->write(data: ",\n"); |
210 | else |
211 | m_stream->write(data: "\n"); |
212 | } |
213 | --m_indentDepth; |
214 | writeIndent(); |
215 | m_stream->write(data: "}\n"); |
216 | } |
217 | |
218 | void QQmlJSStreamWriter::writeIndent() |
219 | { |
220 | for (int i = 0; i < m_indentDepth; ++i) |
221 | m_stream->write(data: " "); |
222 | } |
223 | |
224 | void QQmlJSStreamWriter::writePotentialLine(const QByteArray &line) |
225 | { |
226 | m_pendingLines.append(t: line); |
227 | m_pendingLineLength += line.size(); |
228 | if (m_pendingLineLength >= 80) { |
229 | flushPotentialLinesWithNewlines(); |
230 | } |
231 | } |
232 | |
233 | void QQmlJSStreamWriter::flushPotentialLinesWithNewlines() |
234 | { |
235 | if (m_maybeOneline) |
236 | m_stream->write(data: "\n"); |
237 | for (const QByteArray &line : std::as_const(t&: m_pendingLines)) { |
238 | writeIndent(); |
239 | m_stream->write(data: line); |
240 | m_stream->write(data: "\n"); |
241 | } |
242 | m_pendingLines.clear(); |
243 | m_pendingLineLength = 0; |
244 | m_maybeOneline = false; |
245 | } |
246 | |
247 | QT_END_NAMESPACE |
248 |
Definitions
- enquoteByteArray
- enquoteAnyString
- QQmlJSStreamWriter
- writeStartDocument
- writeEndDocument
- writeLibraryImport
- writeStartObject
- writeEndObject
- writeScriptBinding
- writeStringBinding
- writeNumberBinding
- writeBooleanBinding
- doWriteArrayBinding
- writeArrayBinding
- writeStringListBinding
- write
- writeEnumObjectLiteralBinding
- writeIndent
- writePotentialLine
Learn Advanced QML with KDAB
Find out more