1 /****************************************************************************
2 **
3 ** Copyright (C) 2019 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 /*
30   text.cpp
31 */
32 
33 #include "text.h"
34 
35 #include <QtCore/qregexp.h>
36 
37 #include <stdio.h>
38 
39 QT_BEGIN_NAMESPACE
40 
Text()41 Text::Text() : first(nullptr), last(nullptr) {}
42 
Text(const QString & str)43 Text::Text(const QString &str) : first(nullptr), last(nullptr)
44 {
45     operator<<(str);
46 }
47 
Text(const Text & text)48 Text::Text(const Text &text) : first(nullptr), last(nullptr)
49 {
50     operator=(text);
51 }
52 
~Text()53 Text::~Text()
54 {
55     clear();
56 }
57 
operator =(const Text & text)58 Text &Text::operator=(const Text &text)
59 {
60     if (this != &text) {
61         clear();
62         operator<<(text);
63     }
64     return *this;
65 }
66 
operator <<(Atom::AtomType atomType)67 Text &Text::operator<<(Atom::AtomType atomType)
68 {
69     return operator<<(Atom(atomType));
70 }
71 
operator <<(const QString & string)72 Text &Text::operator<<(const QString &string)
73 {
74     return operator<<(Atom(Atom::String, string));
75 }
76 
operator <<(const Atom & atom)77 Text &Text::operator<<(const Atom &atom)
78 {
79     if (atom.count() < 2) {
80         if (first == nullptr) {
81             first = new Atom(atom.type(), atom.string());
82             last = first;
83         } else
84             last = new Atom(last, atom.type(), atom.string());
85     } else {
86         if (first == nullptr) {
87             first = new Atom(atom.type(), atom.string(), atom.string(1));
88             last = first;
89         } else
90             last = new Atom(last, atom.type(), atom.string(), atom.string(1));
91     }
92     return *this;
93 }
94 
95 /*!
96   Special output operator for LinkAtom. It makes a copy of
97   the LinkAtom \a atom and connects the cop;y to the list
98   in this Text.
99  */
operator <<(const LinkAtom & atom)100 Text &Text::operator<<(const LinkAtom &atom)
101 {
102     if (first == nullptr) {
103         first = new LinkAtom(atom);
104         last = first;
105     } else
106         last = new LinkAtom(last, atom);
107     return *this;
108 }
109 
operator <<(const Text & text)110 Text &Text::operator<<(const Text &text)
111 {
112     const Atom *atom = text.firstAtom();
113     while (atom != nullptr) {
114         operator<<(*atom);
115         atom = atom->next();
116     }
117     return *this;
118 }
119 
stripFirstAtom()120 void Text::stripFirstAtom()
121 {
122     if (first != nullptr) {
123         if (first == last)
124             last = nullptr;
125         Atom *oldFirst = first;
126         first = first->next();
127         delete oldFirst;
128     }
129 }
130 
stripLastAtom()131 void Text::stripLastAtom()
132 {
133     if (last != nullptr) {
134         Atom *oldLast = last;
135         if (first == last) {
136             first = nullptr;
137             last = nullptr;
138         } else {
139             last = first;
140             while (last->next() != oldLast)
141                 last = last->next();
142             last->setNext(nullptr);
143         }
144         delete oldLast;
145     }
146 }
147 
148 /*!
149   This function traverses the atom list of the Text object,
150   extracting all the string parts. It concatenates them to
151   a result string and returns it.
152  */
toString() const153 QString Text::toString() const
154 {
155     QString str;
156     const Atom *atom = firstAtom();
157     while (atom != nullptr) {
158         if (atom->type() == Atom::String || atom->type() == Atom::AutoLink
159             || atom->type() == Atom::C)
160             str += atom->string();
161         atom = atom->next();
162     }
163     return str;
164 }
165 
166 /*!
167   Returns true if this Text contains the substring \a str.
168  */
contains(const QString & str) const169 bool Text::contains(const QString &str) const
170 {
171     const Atom *atom = firstAtom();
172     while (atom != nullptr) {
173         if (atom->type() == Atom::String || atom->type() == Atom::AutoLink
174             || atom->type() == Atom::C)
175             if (atom->string().contains(str, Qt::CaseInsensitive))
176                 return true;
177         atom = atom->next();
178     }
179     return false;
180 }
181 
subText(Atom::AtomType left,Atom::AtomType right,const Atom * from,bool inclusive) const182 Text Text::subText(Atom::AtomType left, Atom::AtomType right, const Atom *from,
183                    bool inclusive) const
184 {
185     const Atom *begin = from ? from : firstAtom();
186     const Atom *end;
187 
188     while (begin != nullptr && begin->type() != left)
189         begin = begin->next();
190     if (begin != nullptr) {
191         if (!inclusive)
192             begin = begin->next();
193     }
194 
195     end = begin;
196     while (end != nullptr && end->type() != right)
197         end = end->next();
198     if (end == nullptr)
199         begin = nullptr;
200     else if (inclusive)
201         end = end->next();
202     return subText(begin, end);
203 }
204 
sectionHeading(const Atom * sectionLeft)205 Text Text::sectionHeading(const Atom *sectionLeft)
206 {
207     if (sectionLeft != nullptr) {
208         const Atom *begin = sectionLeft;
209         while (begin != nullptr && begin->type() != Atom::SectionHeadingLeft)
210             begin = begin->next();
211         if (begin != nullptr)
212             begin = begin->next();
213 
214         const Atom *end = begin;
215         while (end != nullptr && end->type() != Atom::SectionHeadingRight)
216             end = end->next();
217 
218         if (end != nullptr)
219             return subText(begin, end);
220     }
221     return Text();
222 }
223 
sectionHeadingAtom(const Atom * sectionLeft)224 const Atom *Text::sectionHeadingAtom(const Atom *sectionLeft)
225 {
226     if (sectionLeft != nullptr) {
227         const Atom *begin = sectionLeft;
228         while (begin != nullptr && begin->type() != Atom::SectionHeadingLeft)
229             begin = begin->next();
230         if (begin != nullptr)
231             begin = begin->next();
232 
233         return begin;
234     }
235     return nullptr;
236 }
237 
dump() const238 void Text::dump() const
239 {
240     const Atom *atom = firstAtom();
241     while (atom != nullptr) {
242         QString str = atom->string();
243         str.replace("\\", "\\\\");
244         str.replace("\"", "\\\"");
245         str.replace("\n", "\\n");
246         str.replace(QRegExp("[^\x20-\x7e]"), "?");
247         if (!str.isEmpty())
248             str = " \"" + str + QLatin1Char('"');
249         fprintf(stderr, "    %-15s%s\n", atom->typeString().toLatin1().data(),
250                 str.toLatin1().data());
251         atom = atom->next();
252     }
253 }
254 
subText(const Atom * begin,const Atom * end)255 Text Text::subText(const Atom *begin, const Atom *end)
256 {
257     Text text;
258     if (begin != nullptr) {
259         while (begin != end) {
260             text << *begin;
261             begin = begin->next();
262         }
263     }
264     return text;
265 }
266 
clear()267 void Text::clear()
268 {
269     while (first != nullptr) {
270         Atom *atom = first;
271         first = first->next();
272         delete atom;
273     }
274     first = nullptr;
275     last = nullptr;
276 }
277 
compare(const Text & text1,const Text & text2)278 int Text::compare(const Text &text1, const Text &text2)
279 {
280     if (text1.isEmpty())
281         return text2.isEmpty() ? 0 : -1;
282     if (text2.isEmpty())
283         return 1;
284 
285     const Atom *atom1 = text1.firstAtom();
286     const Atom *atom2 = text2.firstAtom();
287 
288     for (;;) {
289         if (atom1->type() != atom2->type())
290             return (int)atom1->type() - (int)atom2->type();
291         int cmp = QString::compare(atom1->string(), atom2->string());
292         if (cmp != 0)
293             return cmp;
294 
295         if (atom1 == text1.lastAtom())
296             return atom2 == text2.lastAtom() ? 0 : -1;
297         if (atom2 == text2.lastAtom())
298             return 1;
299         atom1 = atom1->next();
300         atom2 = atom2->next();
301     }
302 }
303 
304 QT_END_NAMESPACE
305