kde-extraapps/kdevplatform/language/codegen/codegenerator.cpp

242 lines
6.3 KiB
C++
Raw Normal View History

/*
Copyright 2008 Hamish Rodda <rodda@kde.org>
Copyright 2009 Ramon Zarazua <killerfox512 gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2 as published by the Free Software Foundation.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "codegenerator.h"
#include "documentchangeset.h"
#include "duchainchangeset.h"
#include <klocale.h>
#include <interfaces/icore.h>
#include <interfaces/idocumentcontroller.h>
#include <language/duchain/duchainlock.h>
#include <qtimer.h>
#include "applychangeswidget.h"
namespace KDevelop
{
class CodeGeneratorPrivate
{
public:
CodeGeneratorPrivate() : autoGen(false), context(0) {}
QMap<IndexedString, DUChainChangeSet *> duchainChanges;
DocumentChangeSet documentChanges;
bool autoGen;
DUContext * context;
DocumentRange range;
QString error;
};
CodeGeneratorBase::CodeGeneratorBase()
: d(new CodeGeneratorPrivate)
{
}
CodeGeneratorBase::~CodeGeneratorBase()
{
clearChangeSets();
delete d;
}
void CodeGeneratorBase::autoGenerate ( DUContext* context, const KDevelop::DocumentRange* range )
{
d->autoGen = true;
d->context = context;
d->range = *range;
}
void CodeGeneratorBase::addChangeSet(DUChainChangeSet * duchainChange)
{
IndexedString file = duchainChange->topDuContext().data()->url() ;
QMap<IndexedString, DUChainChangeSet *>::iterator it = d->duchainChanges.find(file);
//if we already have an entry for this file, merge it
if(it !=d->duchainChanges.end())
{
**it << *duchainChange;
delete duchainChange;
}
else
d->duchainChanges.insert(file, duchainChange);
}
void CodeGeneratorBase::addChangeSet(DocumentChangeSet & docChangeSet)
{
d->documentChanges << docChangeSet;
}
DocumentChangeSet & CodeGeneratorBase::documentChangeSet()
{
return d->documentChanges;
}
const QString & CodeGeneratorBase::errorText() const
{
return d->error;
}
bool CodeGeneratorBase::autoGeneration() const
{
return d->autoGen;
}
void CodeGeneratorBase::setErrorText(const QString & errorText)
{
d->error = errorText;
}
void CodeGeneratorBase::clearChangeSets()
{
kDebug() << "Cleaning up all the changesets registered by the generator";
foreach(DUChainChangeSet * changeSet, d->duchainChanges)
delete changeSet;
d->duchainChanges.clear();
d->documentChanges = DocumentChangeSet();
}
bool CodeGeneratorBase::execute()
{
kDebug() << "Checking Preconditions for the codegenerator";
//Shouldn't there be a method in iDocument to get a DocumentRange as well?
KUrl document;
if(!d->autoGen)
{
if( !ICore::self()->documentController()->activeDocument() )
{
setErrorText( i18n("Could not find an open document" ) );
return false;
}
document = ICore::self()->documentController()->activeDocument()->url();
if(d->range.isEmpty())
{
DUChainReadLocker lock(DUChain::lock());
d->range = DocumentRange(document.url(), ICore::self()->documentController()->activeDocument()->textSelection());
}
}
if(!d->context)
{
DUChainReadLocker lock(DUChain::lock());
TopDUContext * documentChain = DUChain::self()->chainForDocument(document);
if(!documentChain)
{
setErrorText(QString("Could not find chain for selected document: %1").arg(document.url()));
return false;
}
d->context = documentChain->findContextIncluding(d->range);
if(!d->context)
{
//Attempt to get the context again
QList<TopDUContext *> contexts = DUChain::self()->chainsForDocument(document);
foreach(TopDUContext * top, contexts)
{
kDebug() << "Checking top context with range: " << top->range().textRange() << " for a context";
if((d->context = top->findContextIncluding(d->range)))
break;
}
}
}
if(!d->context)
{
setErrorText(i18n("Error finding context for selection range"));
return false;
}
if(!checkPreconditions(d->context,d->range))
{
setErrorText(i18n("Error checking conditions to generate code: %1",errorText()));
return false;
}
if(!d->autoGen)
{
kDebug() << "Gathering user information for the codegenerator";
if(!gatherInformation())
{
setErrorText(i18n("Error Gathering user information: %1",errorText()));
return false;
}
}
kDebug() << "Generating code";
if(!process())
{
setErrorText(i18n("Error generating code: %1",errorText()));
return false;
}
if(!d->autoGen)
{
kDebug() << "Submitting to the user for review";
return displayChanges();
}
//If it is autogenerated, it shouldn't need to apply changes, instead return them to client that my be another generator
DocumentChangeSet::ChangeResult result(true);
if(!d->autoGen && !(result = d->documentChanges.applyAllChanges()))
{
setErrorText(result.m_failureReason);
return false;
}
return true;
}
bool CodeGeneratorBase::displayChanges()
{
DocumentChangeSet::ChangeResult result = d->documentChanges.applyAllToTemp();
if(!result)
{
setErrorText(result.m_failureReason);
return false;
}
ApplyChangesWidget widget;
//TODO: Find some good information to put
widget.setInformation("Info?");
QMap<IndexedString, IndexedString> temps = d->documentChanges.tempNamesForAll();
for(QMap<IndexedString, IndexedString>::iterator it = temps.begin();
it != temps.end(); ++it)
widget.addDocuments(it.key() , it.value());
if(widget.exec())
return widget.applyAllChanges();
else
return false;
}
}