Warning, file /office/kile/src/parser/parserthread.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /**************************************************************************
0002 *   Copyright (C) 2011-2012 by Michel Ludwig (michel.ludwig@kdemail.net)  *
0003 ***************************************************************************/
0004 
0005 /***************************************************************************
0006  *                                                                         *
0007  *   This program is free software; you can redistribute it and/or modify  *
0008  *   it under the terms of the GNU General Public License as published by  *
0009  *   the Free Software Foundation; either version 2 of the License, or     *
0010  *   (at your option) any later version.                                   *
0011  *                                                                         *
0012  ***************************************************************************/
0013 
0014 #include "parserthread.h"
0015 
0016 #include "documentinfo.h"
0017 #include "kiledocmanager.h"
0018 #include "kileinfo.h"
0019 #include "bibtexparser.h"
0020 #include "latexparser.h"
0021 #include "latexoutputparser.h"
0022 
0023 namespace KileParser {
0024 
0025 ParserThread::ParserThread(KileInfo *info, QObject *parent) :
0026     QThread(parent),
0027     m_ki(info),
0028     m_keepParserThreadAlive(true)
0029 {
0030 }
0031 
0032 ParserThread::~ParserThread()
0033 {
0034     qCDebug(LOG_KILE_PARSER) << "destroying parser thread" << this;
0035     stopParsing();
0036     // wait for the thread to finish before it is deleted at
0037     // the end of this destructor
0038     qCDebug(LOG_KILE_PARSER) << "waiting for parser thread to finish...";
0039     wait();
0040     // and delete remaining queue items (no mutex is required
0041     // as the thread's execution has stopped)
0042     qDeleteAll(m_parserQueue);
0043 }
0044 
0045 void ParserThread::addParserInput(ParserInput *input)
0046 {
0047     qCDebug(LOG_KILE_PARSER) << input;
0048     qCDebug(LOG_KILE_PARSER) << "trying to obtain m_parserMutex";
0049 
0050     m_parserMutex.lock();
0051     // first, check whether the document is queued already
0052     QQueue<ParserInput*>::iterator it = m_parserQueue.begin();
0053     for(; it != m_parserQueue.end(); ++it) {
0054         if((*it)->url == input->url) {
0055             break;
0056         }
0057     }
0058 
0059     if(it != m_parserQueue.end()) {
0060         qCDebug(LOG_KILE_PARSER) << "document in queue already";
0061         *it = input;
0062     }
0063     else {
0064         if(m_currentlyParsedUrl == input->url) {
0065             qCDebug(LOG_KILE_PARSER) << "re-parsing document";
0066             // stop the parsing of the document
0067             m_keepParsingDocument = false;
0068             // and add it as first element to the queue
0069             m_parserQueue.push_front(input);
0070         }
0071         else {
0072             qCDebug(LOG_KILE_PARSER) << "adding to the end";
0073             m_parserQueue.push_back(input);
0074         }
0075     }
0076     m_parserMutex.unlock();
0077 
0078     // finally, wake up threads waiting for the queue to be filled
0079     m_queueEmptyWaitCondition.wakeAll();
0080 }
0081 
0082 void ParserThread::removeParserInput(const QUrl &url)
0083 {
0084     qCDebug(LOG_KILE_PARSER) << url;
0085     m_parserMutex.lock();
0086     // first, if the document is currently parsed, we stop the parsing
0087     if(m_currentlyParsedUrl == url) {
0088         qCDebug(LOG_KILE_PARSER) << "document currently being parsed";
0089         m_keepParsingDocument = false;
0090     }
0091     // nevertheless, we remove all traces of the document from the queue
0092     for(QQueue<ParserInput*>::iterator it = m_parserQueue.begin(); it != m_parserQueue.end();) {
0093         ParserInput *input = *it;
0094         if(input->url == url) {
0095             qCDebug(LOG_KILE_PARSER) << "found it";
0096             it = m_parserQueue.erase(it);
0097             delete input;
0098         }
0099         else {
0100             ++it;
0101         }
0102     }
0103     m_parserMutex.unlock();
0104 }
0105 
0106 void ParserThread::stopParsing()
0107 {
0108     qCDebug(LOG_KILE_PARSER);
0109     m_parserMutex.lock();
0110 
0111     m_keepParserThreadAlive = false;
0112     m_keepParsingDocument = false;
0113     m_parserMutex.unlock();
0114     // wake all the threads that are still waiting for the queue to fill up
0115     m_queueEmptyWaitCondition.wakeAll();
0116 }
0117 
0118 bool ParserThread::shouldContinueDocumentParsing()
0119 {
0120     QMutexLocker locker(&m_parserMutex);
0121     return m_keepParsingDocument;
0122 }
0123 
0124 bool ParserThread::isParsingComplete()
0125 {
0126     QMutexLocker locker(&m_parserMutex);
0127     // as the parser queue might be empty but a document is still being parsed,
0128     // we additionally have to check whether 'm_currentlyParsedUrl' is empty or not
0129     return m_parserQueue.isEmpty() && m_currentlyParsedUrl.isEmpty();
0130 }
0131 
0132 // the document that is currently parsed is always the head of the queue
0133 void ParserThread::run()
0134 {
0135     ParserInput* currentParsedItem;
0136     qCDebug(LOG_KILE_PARSER) << "starting up...";
0137     while(true) {
0138         // first, try to extract the head of the queue
0139         m_parserMutex.lock();
0140         // clear the variable currently parsed url; might be necessary from the previous iteration
0141         m_currentlyParsedUrl = QUrl();
0142         // check if we should still be running before going to sleep
0143         if(!m_keepParserThreadAlive) {
0144             m_parserMutex.unlock();
0145             // remaining queue elements are deleted in the destructor
0146             return;
0147         }
0148         // but if there are no items to be parsed, we go to sleep.
0149         // However, we have to be careful and use a 'while' loop here
0150         // as it can happen that an item is added to the queue but this
0151         // thread is woken up only after it has been removed again.
0152         while(m_parserQueue.size() == 0 && m_keepParserThreadAlive) {
0153             qCDebug(LOG_KILE_PARSER) << "going to sleep...";
0154             emit(parsingQueueEmpty());
0155             m_queueEmptyWaitCondition.wait(&m_parserMutex);
0156             qCDebug(LOG_KILE_PARSER) << "woken up...";
0157         }
0158         // threads are woken up when an object of this class is destroyed; in
0159         // that case the queue might still be empty
0160         if(!m_keepParserThreadAlive) {
0161             m_parserMutex.unlock();
0162             // remaining queue elements are deleted in the destructor
0163             return;
0164         }
0165         Q_ASSERT(m_parserQueue.size() > 0);
0166         qCDebug(LOG_KILE_PARSER) << "queue length" << m_parserQueue.length();
0167         // now, extract the head
0168         currentParsedItem = m_parserQueue.dequeue();
0169 
0170         m_keepParsingDocument = true;
0171         m_currentlyParsedUrl = currentParsedItem->url;
0172         emit(parsingStarted());
0173         m_parserMutex.unlock();
0174 
0175         Parser *parser = createParser(currentParsedItem);
0176 
0177         ParserOutput *parserOutput = Q_NULLPTR;
0178         if(parser) {
0179             parserOutput = parser->parse();
0180         }
0181 
0182         delete currentParsedItem;
0183         delete parser;
0184 
0185         // we also emit when 'parserOutput == Q_NULLPTR' as this will be used to indicate
0186         // that some error has occurred;
0187         // as this call will be blocking, one has to make sure that no mutex is held
0188         emit(parsingComplete(m_currentlyParsedUrl, parserOutput));
0189     }
0190     qCDebug(LOG_KILE_PARSER) << "leaving...";
0191     // remaining queue elements are deleted in the destructor
0192 }
0193 
0194 DocumentParserThread::DocumentParserThread(KileInfo *info, QObject *parent)
0195     : ParserThread(info, parent)
0196 {
0197 }
0198 
0199 DocumentParserThread::~DocumentParserThread()
0200 {
0201 }
0202 
0203 Parser* DocumentParserThread::createParser(ParserInput *input)
0204 {
0205     if(dynamic_cast<LaTeXParserInput*>(input)) {
0206         return new LaTeXParser(this, dynamic_cast<LaTeXParserInput*>(input));
0207     }
0208     else if(dynamic_cast<BibTeXParserInput*>(input)) {
0209         return new BibTeXParser(this, dynamic_cast<BibTeXParserInput*>(input));
0210     }
0211 
0212     return Q_NULLPTR;
0213 }
0214 
0215 void DocumentParserThread::addDocument(KileDocument::TextInfo *textInfo)
0216 {
0217     qCDebug(LOG_KILE_PARSER) << textInfo;
0218     const QUrl url = m_ki->docManager()->urlFor(textInfo);
0219     if(url.isEmpty()) { // if the url is empty (which can happen with new documents),
0220         return;     // we can't do anything as not even the results of the parsing can be displayed
0221     }
0222 
0223     ParserInput* newItem = Q_NULLPTR;
0224     if(dynamic_cast<KileDocument::BibInfo*>(textInfo)) {
0225         newItem = new BibTeXParserInput(url, textInfo->documentContents());
0226     }
0227     else {
0228         newItem = new LaTeXParserInput(url, textInfo->documentContents(),
0229                                        m_ki->extensions(),
0230                                        textInfo->dictStructLevel(),
0231                                        KileConfig::svShowSectioningLabels(),
0232                                        KileConfig::svShowTodo());
0233     }
0234     addParserInput(newItem);
0235 
0236     // It is not very useful to watch for the destruction of 'textInfo' here and stop the parsing
0237     // for 'textInfo' whenever that happens as at that moment it probably won't have a document
0238     // anymore nor would it still be associated with a project item.
0239     // It is better to call 'removeDocument' from the point when 'textInfo' is going to be deleted!
0240 }
0241 
0242 void DocumentParserThread::removeDocument(KileDocument::TextInfo *textInfo)
0243 {
0244     qCDebug(LOG_KILE_PARSER);
0245     KTextEditor::Document *document = textInfo->getDoc();
0246     if(!document) {
0247         return;
0248     }
0249     removeParserInput(document->url());
0250 }
0251 
0252 void DocumentParserThread::removeDocument(const QUrl &url)
0253 {
0254     removeParserInput(url);
0255 }
0256 
0257 OutputParserThread::OutputParserThread(KileInfo *info, QObject *parent)
0258     : ParserThread(info, parent)
0259 {
0260 }
0261 
0262 OutputParserThread::~OutputParserThread()
0263 {
0264 }
0265 
0266 Parser* OutputParserThread::createParser(ParserInput *input)
0267 {
0268     if(dynamic_cast<LaTeXOutputParserInput*>(input)) {
0269         return new LaTeXOutputParser(this, dynamic_cast<LaTeXOutputParserInput*>(input));
0270     }
0271     return Q_NULLPTR;
0272 }
0273 
0274 void OutputParserThread::addLaTeXLogFile(const QString& logFile, const QString& sourceFile,
0275         const QString& texFileName, int selrow, int docrow)
0276 {
0277     qCDebug(LOG_KILE_PARSER) << logFile << sourceFile;
0278 
0279     ParserInput* newItem = new LaTeXOutputParserInput(QUrl::fromLocalFile(logFile), m_ki->extensions(),
0280             sourceFile,
0281             texFileName, selrow, docrow);
0282     addParserInput(newItem);
0283 }
0284 
0285 void OutputParserThread::removeFile(const QString& fileName)
0286 {
0287     removeParserInput(QUrl::fromLocalFile(fileName));
0288 }
0289 
0290 }
0291