File indexing completed on 2024-04-28 03:53:09

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 1999 Carsten Pfeiffer <pfeiffer@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #ifndef KCOMPTREENODE_P_H
0009 #define KCOMPTREENODE_P_H
0010 
0011 #include "kcompletion_export.h"
0012 
0013 #include <QSharedPointer>
0014 #include <kzoneallocator_p.h>
0015 
0016 class KCompTreeNode;
0017 
0018 class KCOMPLETION_EXPORT KCompTreeChildren
0019 {
0020 public:
0021     KCompTreeChildren()
0022         : m_first(nullptr)
0023         , m_last(nullptr)
0024         , m_count(0)
0025     {
0026     }
0027 
0028     KCompTreeNode *begin() const
0029     {
0030         return m_first;
0031     }
0032 
0033     KCompTreeNode *end() const
0034     {
0035         return m_last;
0036     }
0037 
0038     inline KCompTreeNode *at(uint index) const;
0039     inline void append(KCompTreeNode *item);
0040     inline void prepend(KCompTreeNode *item);
0041     inline void insert(KCompTreeNode *after, KCompTreeNode *item);
0042     inline KCompTreeNode *remove(KCompTreeNode *item);
0043 
0044     uint count() const
0045     {
0046         return m_count;
0047     }
0048 
0049 private:
0050     KCompTreeNode *m_first;
0051     KCompTreeNode *m_last;
0052     uint m_count;
0053 };
0054 
0055 /**
0056  * A helper class for KCompletion. Implements a tree of QChar.
0057  * Every node is a QChar and has a list of children, which  are Nodes as well.
0058  *
0059  * QChar( 0x0 ) is used as the delimiter of a string; the last child of each
0060  * inserted string is 0x0.
0061  *
0062  * The tree looks like this (containing the items "kde", "kde-ui",
0063  * "kde-core" and "pfeiffer". Every item is delimited with QChar( 0x0 )
0064  *
0065  *              some_root_node
0066  *                  /     \
0067  *                 k       p
0068  *                 |       |
0069  *                 d       f
0070  *                 |       |
0071  *                 e       e
0072  *                /|       |
0073  *             0x0 -       i
0074  *                / \      |
0075  *               u   c     f
0076  *               |   |     |
0077  *               i   o     f
0078  *               |   |     |
0079  *              0x0  r     e
0080  *                   |     |
0081  *                   e     r
0082  *                   |     |
0083  *                  0x0   0x0
0084  *
0085  * @author Carsten Pfeiffer <pfeiffer@kde.org>
0086  * @internal
0087  */
0088 class KCOMPLETION_EXPORT KCompTreeNode : public QChar
0089 {
0090 public:
0091     KCompTreeNode()
0092         : QChar()
0093         , m_next(nullptr)
0094         , m_weight(0)
0095     {
0096     }
0097 
0098     explicit KCompTreeNode(const QChar &ch, uint weight = 0)
0099         : QChar(ch)
0100         , m_next(nullptr)
0101         , m_weight(weight)
0102     {
0103     }
0104 
0105     ~KCompTreeNode()
0106     {
0107         // delete all children
0108         KCompTreeNode *cur = m_children.begin();
0109         while (cur) {
0110             KCompTreeNode *next = cur->m_next;
0111             delete m_children.remove(cur);
0112             cur = next;
0113         }
0114     }
0115 
0116     KCompTreeNode(const KCompTreeNode &) = delete;
0117     KCompTreeNode &operator=(const KCompTreeNode &) = delete;
0118 
0119     void *operator new(size_t s)
0120     {
0121         Q_ASSERT(m_alloc);
0122         return m_alloc->allocate(s);
0123     }
0124 
0125     void operator delete(void *s)
0126     {
0127         Q_ASSERT(m_alloc);
0128         m_alloc->deallocate(s);
0129     }
0130 
0131     // Returns a child of this node matching ch, if available.
0132     // Otherwise, returns 0L
0133     KCompTreeNode *find(const QChar &ch) const
0134     {
0135         KCompTreeNode *cur = m_children.begin();
0136         while (cur && (*cur != ch)) {
0137             cur = cur->m_next;
0138         }
0139         return cur;
0140     }
0141 
0142     // Adds a child-node "ch" to this node. If such a node is already existent,
0143     // it will not be created. Returns the new/existing node.
0144     inline KCompTreeNode *insert(const QChar &ch, bool sorted);
0145 
0146     // Iteratively removes a string from the tree. The nicer recursive
0147     // version apparently was a little memory hungry (see #56757)
0148     inline void remove(const QString &str);
0149 
0150     int childrenCount() const
0151     {
0152         return m_children.count();
0153     }
0154 
0155     void confirm()
0156     {
0157         m_weight++;
0158     }
0159 
0160     void confirm(uint w)
0161     {
0162         m_weight += w;
0163     }
0164 
0165     void decline()
0166     {
0167         m_weight--;
0168     }
0169 
0170     uint weight() const
0171     {
0172         return m_weight;
0173     }
0174 
0175     const KCompTreeChildren *children() const
0176     {
0177         return &m_children;
0178     }
0179 
0180     const KCompTreeNode *childAt(int index) const
0181     {
0182         return m_children.at(index);
0183     }
0184 
0185     const KCompTreeNode *firstChild() const
0186     {
0187         return m_children.begin();
0188     }
0189 
0190     const KCompTreeNode *lastChild() const
0191     {
0192         return m_children.end();
0193     }
0194 
0195     /* We want to handle a list of KCompTreeNodes on our own, to not
0196        need to use QValueList<>. And to make it even faster we don't
0197        use an accessor, but just a public member. */
0198     KCompTreeNode *m_next;
0199 
0200     /**
0201      * Custom allocator used for all KCompTreeNode instances
0202      */
0203     static QSharedPointer<KZoneAllocator> allocator()
0204     {
0205         return m_alloc;
0206     }
0207 
0208 private:
0209     uint m_weight;
0210     KCompTreeChildren m_children;
0211     static QSharedPointer<KZoneAllocator> m_alloc;
0212 };
0213 
0214 KCompTreeNode *KCompTreeNode::insert(const QChar &ch, bool sorted)
0215 {
0216     KCompTreeNode *child = find(ch);
0217     if (!child) {
0218         child = new KCompTreeNode(ch);
0219 
0220         // FIXME, first (slow) sorted insertion implementation
0221         if (sorted) {
0222             KCompTreeNode *prev = nullptr;
0223             KCompTreeNode *cur = m_children.begin();
0224             while (cur) {
0225                 if (ch > *cur) {
0226                     prev = cur;
0227                     cur = cur->m_next;
0228                 } else {
0229                     break;
0230                 }
0231             }
0232             if (prev) {
0233                 m_children.insert(prev, child);
0234             } else {
0235                 m_children.prepend(child);
0236             }
0237         }
0238 
0239         else {
0240             m_children.append(child);
0241         }
0242     }
0243 
0244     // implicit weighting: the more often an item is inserted, the higher
0245     // priority it gets.
0246     child->confirm();
0247 
0248     return child;
0249 }
0250 
0251 void KCompTreeNode::remove(const QString &str)
0252 {
0253     QString string = str;
0254     string += QChar(0x0);
0255 
0256     QList<KCompTreeNode *> deletables(string.length() + 1);
0257 
0258     KCompTreeNode *child = nullptr;
0259     KCompTreeNode *parent = this;
0260     deletables.replace(0, parent);
0261 
0262     int i = 0;
0263     for (; i < string.length(); i++) {
0264         child = parent->find(string.at(i));
0265         if (child) {
0266             deletables.replace(i + 1, child);
0267         } else {
0268             break;
0269         }
0270 
0271         parent = child;
0272     }
0273 
0274     for (; i >= 1; i--) {
0275         parent = deletables.at(i - 1);
0276         child = deletables.at(i);
0277         if (child->m_children.count() == 0) {
0278             delete parent->m_children.remove(child);
0279         }
0280     }
0281 }
0282 
0283 KCompTreeNode *KCompTreeChildren::at(uint index) const
0284 {
0285     KCompTreeNode *cur = m_first;
0286     while (index-- && cur) {
0287         cur = cur->m_next;
0288     }
0289     return cur;
0290 }
0291 
0292 void KCompTreeChildren::append(KCompTreeNode *item)
0293 {
0294     m_count++;
0295     if (!m_last) {
0296         m_last = item;
0297         m_last->m_next = nullptr;
0298         m_first = item;
0299         return;
0300     }
0301     m_last->m_next = item;
0302     item->m_next = nullptr;
0303     m_last = item;
0304 }
0305 
0306 void KCompTreeChildren::prepend(KCompTreeNode *item)
0307 {
0308     m_count++;
0309     if (!m_last) {
0310         m_last = item;
0311         m_last->m_next = nullptr;
0312         m_first = item;
0313         return;
0314     }
0315     item->m_next = m_first;
0316     m_first = item;
0317 }
0318 
0319 void KCompTreeChildren::insert(KCompTreeNode *after, KCompTreeNode *item)
0320 {
0321     if (!after) {
0322         append(item);
0323         return;
0324     }
0325 
0326     m_count++;
0327 
0328     item->m_next = after->m_next;
0329     after->m_next = item;
0330 
0331     if (after == m_last) {
0332         m_last = item;
0333     }
0334 }
0335 
0336 KCompTreeNode *KCompTreeChildren::remove(KCompTreeNode *item)
0337 {
0338     if (!m_first || !item) {
0339         return nullptr;
0340     }
0341     KCompTreeNode *cur = nullptr;
0342 
0343     if (item == m_first) {
0344         m_first = m_first->m_next;
0345     } else {
0346         cur = m_first;
0347         while (cur && cur->m_next != item) {
0348             cur = cur->m_next;
0349         }
0350         if (!cur) {
0351             return nullptr;
0352         }
0353         cur->m_next = item->m_next;
0354     }
0355     if (item == m_last) {
0356         m_last = cur;
0357     }
0358     m_count--;
0359     return item;
0360 }
0361 
0362 #endif // KCOMPTREENODE_P_H