File indexing completed on 2024-04-21 03:56:05

0001 /*
0002     SPDX-FileCopyrightText: 2010 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.net>
0003     SPDX-FileContributor: Stephen Kelly <stephen@kdab.com>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "modeleventlogger.h"
0009 #include "indexfinder.h"
0010 #include "modeldumper.h"
0011 
0012 #include "eventloggerregister.h"
0013 
0014 #include <QDebug>
0015 #include <QFile>
0016 #include <QStringList>
0017 
0018 #ifdef Grantlee_FOUND
0019 #include "grantlee_paths.h"
0020 #include <grantlee_core.h>
0021 
0022 /**
0023   Don't escape the code generation output.
0024 
0025   'const QString &' should not become 'const QString &amp;'
0026 */
0027 class NoEscapeOutputStream : public Grantlee::OutputStream
0028 {
0029 public:
0030     NoEscapeOutputStream()
0031         : Grantlee::OutputStream()
0032     {
0033     }
0034 
0035     NoEscapeOutputStream(QTextStream *stream)
0036         : OutputStream(stream)
0037     {
0038     }
0039 
0040     virtual QSharedPointer<Grantlee::OutputStream> clone() const
0041     {
0042         return QSharedPointer<Grantlee::OutputStream>(new NoEscapeOutputStream);
0043     }
0044 
0045     virtual QString escape(const QString &input) const
0046     {
0047         return input;
0048     }
0049 };
0050 #endif
0051 
0052 class ModelWrapper : public QAbstractItemModel
0053 {
0054 public:
0055     ModelWrapper(QAbstractItemModel * /*model*/, QObject *parent = nullptr)
0056         : QAbstractItemModel(parent)
0057     {
0058     }
0059 
0060     QModelIndexList per() const
0061     {
0062         return persistentIndexList();
0063     }
0064 
0065     QModelIndex index(int /*row*/, int /*column*/, const QModelIndex & /*parent*/ = QModelIndex()) const override
0066     {
0067         return QModelIndex();
0068     }
0069     int rowCount(const QModelIndex & /*parent*/ = QModelIndex()) const override
0070     {
0071         return 0;
0072     }
0073     QModelIndex parent(const QModelIndex & /*child*/) const override
0074     {
0075         return QModelIndex();
0076     }
0077     int columnCount(const QModelIndex & /*parent*/ = QModelIndex()) const override
0078     {
0079         return 0;
0080     }
0081     QVariant data(const QModelIndex & /*index*/, int /*role*/ = Qt::DisplayRole) const override
0082     {
0083         return QVariant();
0084     }
0085 };
0086 
0087 ModelEvent::ModelEvent(QObject *parent)
0088     : QObject(parent)
0089 {
0090 }
0091 
0092 static const char *const sTypes[] = {"Init", "RowsInserted", "RowsRemoved", "DataChanged", "LayoutChanged", "ModelReset"};
0093 
0094 QString ModelEvent::type() const
0095 {
0096     return QLatin1String(*(sTypes + m_type));
0097 }
0098 
0099 // ModelEvent::Type ModelEvent::type() const
0100 // {
0101 //   return m_type;
0102 // }
0103 
0104 void ModelEvent::setType(ModelEvent::Type type)
0105 {
0106     m_type = type;
0107 }
0108 
0109 int ModelEvent::start() const
0110 {
0111     return m_start;
0112 }
0113 
0114 void ModelEvent::setStart(int start)
0115 {
0116     m_start = start;
0117 }
0118 
0119 int ModelEvent::end() const
0120 {
0121     return m_end;
0122 }
0123 
0124 void ModelEvent::setEnd(int end)
0125 {
0126     m_end = end;
0127 }
0128 
0129 QString ModelEvent::rowAncestors() const
0130 {
0131     QString result(QStringLiteral("QList<int>()"));
0132 
0133     for (const int row : std::as_const(m_rowAncestors)) {
0134         result.append(" << ");
0135         result.append(QString::number(row));
0136     }
0137     return result;
0138 }
0139 
0140 // QList< int > ModelEvent::rowAncestors() const
0141 // {
0142 //   return m_rowAncestors;
0143 // }
0144 
0145 void ModelEvent::setRowAncestors(QList<int> rowAncestors)
0146 {
0147     m_rowAncestors = rowAncestors;
0148 }
0149 
0150 bool ModelEvent::hasInterpretString() const
0151 {
0152     return !m_interpretString.isEmpty();
0153 }
0154 
0155 QString ModelEvent::interpretString() const
0156 {
0157     return m_interpretString;
0158 }
0159 
0160 void ModelEvent::setInterpretString(const QString &interpretString)
0161 {
0162     m_interpretString = interpretString;
0163 }
0164 
0165 ModelEventLogger::ModelEventLogger(QAbstractItemModel *model, QObject *parent)
0166     : QObject(parent)
0167     , m_model(model)
0168     , m_modelDumper(new ModelDumper)
0169     , m_numLogs(0)
0170 {
0171     connect(model, &QAbstractItemModel::dataChanged, this, &ModelEventLogger::dataChanged);
0172     connect(model, &QAbstractItemModel::layoutAboutToBeChanged, this, &ModelEventLogger::layoutAboutToBeChanged);
0173     connect(model, &QAbstractItemModel::layoutChanged, this, &ModelEventLogger::layoutChanged);
0174     connect(model, &QAbstractItemModel::modelReset, this, &ModelEventLogger::modelReset);
0175     connect(model, &QAbstractItemModel::rowsInserted, this, &ModelEventLogger::rowsInserted);
0176     connect(model, &QAbstractItemModel::rowsRemoved, this, &ModelEventLogger::rowsRemoved);
0177 
0178     ModelEvent *modelEvent = new ModelEvent(this);
0179     modelEvent->setType(ModelEvent::Init);
0180     modelEvent->setInterpretString(m_modelDumper->dumpModel(model));
0181 
0182     m_modelName = QString::fromLatin1(model->metaObject()->className()).toLower();
0183 
0184     m_initEvent = QVariant::fromValue(static_cast<QObject *>(modelEvent));
0185 
0186     EventLoggerRegister::instance()->registerLogger(this);
0187 }
0188 
0189 void ModelEventLogger::writeLog()
0190 {
0191 #ifdef Grantlee_FOUND
0192     QString logFileName = QString("main.%1.%2.%3.cpp").arg(m_modelName).arg(reinterpret_cast<qint64>(this)).arg(m_numLogs++);
0193     qDebug() << "Writing to " << logFileName;
0194     QFile outputFile(logFileName);
0195     const bool logFileOpened = outputFile.open(QFile::WriteOnly | QFile::Text);
0196     Q_ASSERT(logFileOpened);
0197 
0198     Grantlee::Engine engine;
0199     Grantlee::FileSystemTemplateLoader::Ptr loader(new Grantlee::FileSystemTemplateLoader);
0200     loader->setTemplateDirs(QStringList() << ":/templates");
0201     engine.addTemplateLoader(loader);
0202     engine.setPluginPaths(QStringList() << GRANTLEE_PLUGIN_PATH);
0203 
0204     // Write out.
0205     Grantlee::Template t = engine.loadByName("main.cpp");
0206     if (!t->error()) {
0207         Grantlee::Context c;
0208         c.insert("initEvent", m_initEvent);
0209         c.insert("events", m_events);
0210 
0211         QTextStream textStream(&outputFile);
0212         NoEscapeOutputStream outputStream(&textStream);
0213         t->render(&outputStream, &c);
0214     }
0215     outputFile.close();
0216 
0217     if (t->error()) {
0218         qDebug() << t->errorString();
0219     }
0220 #else
0221     qDebug() << "Grantlee not found. No log written.";
0222 #endif
0223 }
0224 
0225 ModelEventLogger::~ModelEventLogger()
0226 {
0227     writeLog();
0228     delete m_modelDumper;
0229     EventLoggerRegister::instance()->unregisterLogger(this);
0230 }
0231 
0232 void ModelEventLogger::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
0233 {
0234     ModelEvent *modelEvent = new ModelEvent(this);
0235     modelEvent->setType(ModelEvent::DataChanged);
0236     modelEvent->setRowAncestors(IndexFinder::indexToIndexFinder(topLeft.parent()).rows());
0237     modelEvent->setStart(topLeft.row());
0238     modelEvent->setEnd(bottomRight.row());
0239 
0240     m_events.append(QVariant::fromValue(static_cast<QObject *>(modelEvent)));
0241 }
0242 
0243 void ModelEventLogger::persistChildren(const QModelIndex & /*parent*/)
0244 {
0245 }
0246 
0247 void ModelEventLogger::layoutAboutToBeChanged()
0248 {
0249     m_oldPaths.clear();
0250     m_persistentIndexes.clear();
0251     const QModelIndexList list = static_cast<const ModelWrapper *>(m_model)->per();
0252     for (const QModelIndex &idx : list) {
0253         m_persistentIndexes.append(QPersistentModelIndex(idx));
0254         m_oldPaths.append(IndexFinder::indexToIndexFinder(idx).rows());
0255     }
0256 }
0257 
0258 void ModelEventLogger::layoutChanged()
0259 {
0260     ModelEvent *modelEvent = new ModelEvent(this);
0261     modelEvent->setType(ModelEvent::LayoutChanged);
0262     modelEvent->setInterpretString(m_modelDumper->dumpModel(m_model));
0263 
0264     QList<PersistentChange *> changes;
0265 
0266     for (int i = 0; i < m_persistentIndexes.size(); ++i) {
0267         const QPersistentModelIndex pIdx = m_persistentIndexes.at(i);
0268         if (!pIdx.isValid()) {
0269             PersistentChange *change = new PersistentChange(this);
0270             change->newPath = QList<int>();
0271             change->oldPath = m_oldPaths.at(i);
0272             changes.append(change);
0273             continue;
0274         }
0275         const QList<int> rows = IndexFinder::indexToIndexFinder(pIdx).rows();
0276         if (m_oldPaths.at(i) == rows) {
0277             continue;
0278         }
0279 
0280         PersistentChange *change = new PersistentChange(this);
0281         change->newPath = rows;
0282         change->oldPath = m_oldPaths.at(i);
0283         changes.append(change);
0284     }
0285 
0286     modelEvent->setChanges(changes);
0287 
0288     m_events.append(QVariant::fromValue(static_cast<QObject *>(modelEvent)));
0289 }
0290 
0291 void ModelEventLogger::modelReset()
0292 {
0293     ModelEvent *modelEvent = new ModelEvent(this);
0294     modelEvent->setType(ModelEvent::ModelReset);
0295     modelEvent->setInterpretString(m_modelDumper->dumpModel(m_model));
0296 
0297     m_events.append(QVariant::fromValue(static_cast<QObject *>(modelEvent)));
0298 }
0299 
0300 void ModelEventLogger::rowsInserted(const QModelIndex &parent, int start, int end)
0301 {
0302     ModelEvent *modelEvent = new ModelEvent(this);
0303     modelEvent->setType(ModelEvent::RowsInserted);
0304     modelEvent->setRowAncestors(IndexFinder::indexToIndexFinder(parent).rows());
0305     modelEvent->setStart(start);
0306     QString s = m_modelDumper->dumpTree(m_model, parent, start, end);
0307     modelEvent->setInterpretString(s);
0308 
0309     m_events.append(QVariant::fromValue(static_cast<QObject *>(modelEvent)));
0310 }
0311 
0312 void ModelEventLogger::rowsRemoved(const QModelIndex &parent, int start, int end)
0313 {
0314     ModelEvent *modelEvent = new ModelEvent(this);
0315     modelEvent->setType(ModelEvent::RowsRemoved);
0316     modelEvent->setRowAncestors(IndexFinder::indexToIndexFinder(parent).rows());
0317     modelEvent->setStart(start);
0318     modelEvent->setEnd(end);
0319 
0320     m_events.append(QVariant::fromValue(static_cast<QObject *>(modelEvent)));
0321 }
0322 
0323 #include "moc_modeleventlogger.cpp"