/*************************************************************************** * Copyright (C) 1999 by Jonas Nordin * * jonas.nordin@syncom.se * * Copyright (C) 2000-2001 by Bernd Gehrmann * * bernd@kdevelop.org * * Copyright (C) 2002-2003 by Roberto Raggi * * roberto@kdevelop.org * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "javasupportpart.h" #include "javasupport_events.h" #include "problemreporter.h" #include "backgroundparser.h" #include "KDevJavaSupportIface.h" #include "javasupportfactory.h" #include "catalog.h" #include "kdevdriver.h" #include "javasupport_utils.h" #include "JavaStoreWalker.h" #include "JavaAST.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include enum { KDEV_DB_VERSION = 7 }; enum { KDEV_PCS_VERSION = 8 }; class JavaDriver: public KDevDriver { public: JavaDriver( JavaSupportPart* javaSupport ) : KDevDriver( javaSupport ) { } void fileParsed( const TQString& fileName ) { //kdDebug(9013) << "-----> file " << fileName << " parsed!" << endl; RefJavaAST ast = takeTranslationUnit( fileName ); if( javaSupport()->problemReporter() ){ javaSupport()->problemReporter()->removeAllProblems( fileName ); TQValueList pl = problems( fileName ); TQValueList::ConstIterator it = pl.begin(); while( it != pl.end() ){ const Problem& p = *it++; javaSupport()->problemReporter()->reportProblem( fileName, p ); } } if( javaSupport()->codeModel()->hasFile(fileName) ){ FileDom file = javaSupport()->codeModel()->fileByName( fileName ); javaSupport()->removeWithReferences( fileName ); } FileDom file = javaSupport()->codeModel()->create(); file->setName( fileName ); JavaStoreWalker walker; walker.setFile( file ); walker.setCodeModel( javaSupport()->codeModel() ); walker.compilationUnit( ast ); javaSupport()->codeModel()->addFile( file ); remove( fileName ); } }; JavaSupportPart::JavaSupportPart(TQObject *parent, const char *name, const TQStringList &/*args*/) : KDevLanguageSupport(JavaSupportFactory::info(), parent, name ? name : "KDevJavaSupport"), m_activeDocument( 0 ), m_activeView( 0 ), m_activeSelection( 0 ), m_activeEditor( 0 ), m_activeViewCursor( 0 ), m_projectClosed( true ), m_valid( false ) { setInstance(JavaSupportFactory::instance()); m_driver = new JavaDriver( this ); setXMLFile( "kdevjavasupport.rc" ); m_catalogList.setAutoDelete( true ); setupCatalog(); m_backgroundParser = new BackgroundParser( this, &m_eventConsumed ); m_backgroundParser->start(); connect( core(), TQ_SIGNAL(projectOpened()), this, TQ_SLOT(projectOpened()) ); connect( core(), TQ_SIGNAL(projectClosed()), this, TQ_SLOT(projectClosed()) ); connect( partController(), TQ_SIGNAL(savedFile(const KURL&)), this, TQ_SLOT(savedFile(const KURL&)) ); connect( core(), TQ_SIGNAL(contextMenu(TQPopupMenu *, const Context *)), this, TQ_SLOT(contextMenu(TQPopupMenu *, const Context *)) ); connect( partController(), TQ_SIGNAL(activePartChanged(KParts::Part*)), this, TQ_SLOT(activePartChanged(KParts::Part*))); connect( partController(), TQ_SIGNAL(partRemoved(KParts::Part*)), this, TQ_SLOT(partRemoved(KParts::Part*))); m_problemReporter = new ProblemReporter( this, 0, "problemReporterWidget" ); m_problemReporter->setIcon( SmallIcon("application-vnd.tde.info") ); mainWindow( )->embedOutputView( m_problemReporter, i18n("Problems"), i18n("Problem reporter")); connect( core(), TQ_SIGNAL(configWidget(KDialogBase*)), m_problemReporter, TQ_SLOT(configWidget(KDialogBase*)) ); connect( core(), TQ_SIGNAL(configWidget(KDialogBase*)), this, TQ_SLOT(configWidget(KDialogBase*)) ); TDEAction *action; action = new TDEAction(i18n("New Class..."), "classnew", 0, this, TQ_SLOT(slotNewClass()), actionCollection(), "project_newclass"); action->setToolTip( i18n("Generate a new class") ); action->setWhatsThis( i18n("New ClassGenerates a new class.

") ); // daniel connect( core( ), TQ_SIGNAL( projectConfigWidget( KDialogBase* ) ), this, TQ_SLOT( projectConfigWidget( KDialogBase* ) ) ); new KDevJavaSupportIface( this ); //(void) dcopClient(); } JavaSupportPart::~JavaSupportPart() { delete( m_driver ); m_driver = 0; if( m_backgroundParser ){ m_backgroundParser->close(); m_backgroundParser->wait(); delete m_backgroundParser; m_backgroundParser = 0; } codeRepository()->setMainCatalog( 0 ); TQPtrListIterator it( m_catalogList ); while( Catalog* catalog = it.current() ){ ++it; codeRepository()->unregisterCatalog( catalog ); } mainWindow( )->removeView( m_problemReporter ); delete m_problemReporter; m_problemReporter = 0; } void JavaSupportPart::customEvent( TQCustomEvent* ev ) { //kdDebug(9013) << "JavaSupportPart::customEvent()" << endl; if( ev->type() == int(Event_FileParsed) ){ FileParsedEvent* event = (FileParsedEvent*) ev; TQString fileName = event->fileName(); if( m_problemReporter ){ m_problemReporter->removeAllProblems( fileName ); bool hasErrors = false; TQValueList problems = event->problems(); TQValueList::ConstIterator it = problems.begin(); while( it != problems.end() ){ const Problem& p = *it++; if( p.level() == Problem::Level_Error ) hasErrors = true; m_problemReporter->reportProblem( fileName, p ); } m_backgroundParser->lock(); if( RefJavaAST ast = m_backgroundParser->translationUnit(fileName) ){ if( !hasErrors ){ if( codeModel()->hasFile(fileName) ){ FileDom file = codeModel()->fileByName( fileName ); removeWithReferences( fileName ); } FileDom file = codeModel()->create(); file->setName( fileName ); JavaStoreWalker walker; walker.setFile( file ); walker.setCodeModel( codeModel() ); walker.compilationUnit( ast ); codeModel()->addFile( file ); emit addedSourceInfo( fileName ); } } m_backgroundParser->unlock(); } emit fileParsed( fileName ); } } void JavaSupportPart::projectConfigWidget( KDialogBase* /*dlg*/ ) { } void JavaSupportPart::configWidget(KDialogBase */*dlg*/) { } void JavaSupportPart::activePartChanged(KParts::Part *part) { kdDebug(9032) << "JavaSupportPart::activePartChanged()" << endl; bool enabled = false; m_activeDocument = dynamic_cast( part ); m_activeView = part ? dynamic_cast( part->widget() ) : 0; m_activeEditor = dynamic_cast( part ); m_activeSelection = dynamic_cast( part ); m_activeViewCursor = part ? dynamic_cast( m_activeView ) : 0; m_activeFileName = TQString(); if (m_activeDocument) { m_activeFileName = URLUtil::canonicalPath( m_activeDocument->url().path() ); TQFileInfo fi( m_activeFileName ); TQString ext = fi.extension(); if (fileExtensions().contains(ext)) enabled = true; } if( !part ) return; if( !m_activeView ) return; #if 0 KTextEditor::TextHintInterface* textHintIface = dynamic_cast( m_activeView ); if( !textHintIface ) return; connect( view, TQ_SIGNAL(needTextHint(int,int,TQString&)), this, TQ_SLOT(slotNeedTextHint(int,int,TQString&)) ); textHintIface->enableTextHints( 1000 ); #endif } void JavaSupportPart::projectOpened( ) { kdDebug( 9013 ) << "projectOpened( )" << endl; m_projectDirectory = URLUtil::canonicalPath( project()->projectDirectory() ); connect( project( ), TQ_SIGNAL( addedFilesToProject( const TQStringList & ) ), this, TQ_SLOT( addedFilesToProject( const TQStringList & ) ) ); connect( project( ), TQ_SIGNAL( removedFilesFromProject( const TQStringList &) ), this, TQ_SLOT( removedFilesFromProject( const TQStringList & ) ) ); connect( project( ), TQ_SIGNAL( changedFilesInProject( const TQStringList & ) ), this, TQ_SLOT( changedFilesInProject( const TQStringList & ) ) ); connect( project(), TQ_SIGNAL(projectCompiled()), this, TQ_SLOT(slotProjectCompiled()) ); m_timestamp.clear(); m_projectClosed = false; TQTimer::singleShot( 500, this, TQ_SLOT( initialParse( ) ) ); } void JavaSupportPart::projectClosed( ) { kdDebug( 9013 ) << "projectClosed( )" << endl; saveProjectSourceInfo(); if( m_backgroundParser ) m_backgroundParser->removeAllFiles(); m_projectClosed = true; } void JavaSupportPart::contextMenu(TQPopupMenu */*popup*/, const Context *context) { m_activeClass = 0; m_activeFunction = 0; m_activeVariable = 0; if( context->hasType(Context::EditorContext) ){ // nothing! } else if( context->hasType(Context::CodeModelItemContext) ){ const CodeModelItemContext* mcontext = static_cast( context ); if( mcontext->item()->isClass() ){ m_activeClass = (ClassModel*) mcontext->item(); } else if( mcontext->item()->isFunction() ){ m_activeFunction = (FunctionModel*) mcontext->item(); } } } void JavaSupportPart::addedFilesToProject(const TQStringList &fileList) { TQStringList files = fileList; for ( TQStringList::ConstIterator it = files.begin(); it != files.end(); ++it ) { TQString path = URLUtil::canonicalPath( m_projectDirectory + "/" + (*it) ); maybeParse( path ); emit addedSourceInfo( path ); } } void JavaSupportPart::removedFilesFromProject(const TQStringList &fileList) { for ( TQStringList::ConstIterator it = fileList.begin(); it != fileList.end(); ++it ) { TQString path = URLUtil::canonicalPath( m_projectDirectory + "/" + *it ); removeWithReferences( path ); m_backgroundParser->removeFile( path ); } } void JavaSupportPart::changedFilesInProject( const TQStringList & fileList ) { TQStringList files = fileList; for ( TQStringList::ConstIterator it = files.begin(); it != files.end(); ++it ) { TQString path = URLUtil::canonicalPath( m_projectDirectory + "/" + *it ); maybeParse( path ); emit addedSourceInfo( path ); } } void JavaSupportPart::savedFile(const KURL &fileName) { Q_UNUSED( fileName.path() ); #if 0 // not needed anymore kdDebug(9013) << "savedFile(): " << fileName.mid ( m_projectDirectory.length() + 1 ) << endl; TQStringList projectFileList = project()->allFiles(); if (projectFileList.contains(fileName.mid ( m_projectDirectory.length() + 1 ))) { maybeParse( fileName ); emit addedSourceInfo( fileName ); } #endif } TQString JavaSupportPart::findSourceFile() { TQFileInfo fi( m_activeFileName ); TQString path = fi.filePath(); TQString ext = fi.extension(); TQString base = path.left( path.length() - ext.length() ); TQStringList candidates; if (ext == "h" || ext == "H" || ext == "hh" || ext == "hxx" || ext == "hpp" || ext == "tlh") { candidates << (base + "c"); candidates << (base + "cc"); candidates << (base + "java"); candidates << (base + "java"); candidates << (base + "cxx"); candidates << (base + "C"); candidates << (base + "m"); candidates << (base + "mm"); candidates << (base + "M"); candidates << (base + "inl"); } TQStringList::ConstIterator it; for (it = candidates.begin(); it != candidates.end(); ++it) { kdDebug(9013) << "Trying " << (*it) << endl; if (TQFileInfo(*it).exists()) { return *it; } } return m_activeFileName; } KDevLanguageSupport::Features JavaSupportPart::features() { return Features( Classes | Functions | Variables ); } TQString JavaSupportPart::formatClassName(const TQString &name) { return name; } TQString JavaSupportPart::unformatClassName(const TQString &name) { return name; } TQStringList JavaSupportPart::fileExtensions() const { return TQStringList::split(",", "java"); } void JavaSupportPart::slotNewClass() { } void JavaSupportPart::addMethod( ClassDom /*klass*/ ) { } void JavaSupportPart::addAttribute( ClassDom /*klass*/ ) { } void JavaSupportPart::initialParse( ) { // For debugging if( !project( ) ){ // messagebox ? kdDebug( 9013 ) << "No project" << endl; return; } parseProject( ); emit updatedSourceInfo(); m_valid = true; return; } bool JavaSupportPart::parseProject( ) { //TQLabel* label = new TQLabel( "", mainWindow( )->statusBar( ) ); //label->setMinimumWidth( 600 ); //mainWindow( )->statusBar( )->addWidget( label ); //label->show( ); mainWindow()->statusBar()->message( i18n("Updating...") ); kapp->processEvents( ); kapp->setOverrideCursor( waitCursor ); TQStringList files = modifiedFileList(); TQProgressBar* bar = new TQProgressBar( files.count( ), mainWindow( )->statusBar( ) ); bar->setMinimumWidth( 120 ); bar->setCenterIndicator( true ); mainWindow( )->statusBar( )->addWidget( bar ); bar->show( ); TQDir d( m_projectDirectory ); TQDataStream stream; TQMap< TQString, TQPair > pcs; if( TQFileInfo( project()->projectDirectory() + "/" + project()->projectName().lower() + ".kdevelop.pcs" ).exists() ) { d.rename(project()->projectName().lower() + ".kdevelop.pcs", project()->projectName() + ".kdevelop.pcs"); } TQFile f(project()->projectDirectory() + "/" + project()->projectName() + ".kdevelop.pcs"); if( f.open(IO_ReadOnly) ){ stream.setDevice( &f ); TQString sig; int pcs_version = 0; stream >> sig >> pcs_version; if( sig == "PCS" && pcs_version == KDEV_PCS_VERSION ){ int numFiles = 0; stream >> numFiles; for( int i=0; i> fn >> ts >> offset; pcs[ fn ] = qMakePair( ts, offset ); } } } int n = 0; for( TQStringList::Iterator it = files.begin( ); it != files.end( ); ++it ) { bar->setProgress( n++ ); TQFileInfo fileInfo( d, *it ); if( fileInfo.exists() && fileInfo.isFile() && fileInfo.isReadable() ){ TQString absFilePath = URLUtil::canonicalPath( fileInfo.absFilePath() ); kdDebug(9013) << "parse file: " << absFilePath << endl; if( (n%5) == 0 ){ kapp->processEvents(); if( m_projectClosed ){ delete( bar ); return false; } } if( isValidSource(absFilePath) ){ TQDateTime t = fileInfo.lastModified(); if( m_timestamp.contains(absFilePath) && m_timestamp[absFilePath] == t ) continue; if( pcs.contains(absFilePath) && t.toTime_t() == pcs[absFilePath].first ){ stream.device()->at( pcs[absFilePath].second ); FileDom file = codeModel()->create(); file->read( stream ); codeModel()->addFile( file ); } else { m_driver->parseFile( absFilePath ); } m_timestamp[ absFilePath ] = t; } } if( m_projectClosed ){ kdDebug(9013) << "ABORT" << endl; kapp->restoreOverrideCursor( ); return false; } } kdDebug( 9013 ) << "updating sourceinfo" << endl; emit updatedSourceInfo(); mainWindow( )->statusBar( )->removeWidget( bar ); delete bar; //mainWindow( )->statusBar( )->removeWidget( label ); //delete label; kapp->restoreOverrideCursor( ); mainWindow( )->statusBar( )->message( i18n( "Done" ), 2000 ); return true; } void JavaSupportPart::maybeParse( const TQString& fileName ) { if( !isValidSource(fileName) ) return; TQFileInfo fileInfo( fileName ); TQString path = URLUtil::canonicalPath( fileName ); TQDateTime t = fileInfo.lastModified(); if( !fileInfo.exists() ){ removeWithReferences( path ); return; } TQMap::Iterator it = m_timestamp.find( path ); if( it != m_timestamp.end() && *it == t ){ return; } m_timestamp[ path ] = t; m_driver->parseFile( path ); } void JavaSupportPart::slotNeedTextHint( int /*line*/, int /*column*/, TQString& /*textHint*/ ) { } TQStringList JavaSupportPart::subclassWidget(const TQString& /*formName*/) { TQStringList newFileNames; return newFileNames; } TQStringList JavaSupportPart::updateWidget(const TQString& /*formName*/, const TQString& /*fileName*/) { TQStringList dummy; return dummy; } void JavaSupportPart::partRemoved( KParts::Part* part ) { kdDebug(9032) << "JavaSupportPart::partRemoved()" << endl; if( KTextEditor::Document* doc = dynamic_cast( part ) ){ TQString fileName = doc->url().path(); if( fileName.isEmpty() ) return; TQString canonicalFileName = URLUtil::canonicalPath( fileName ); m_backgroundParser->removeFile( canonicalFileName ); m_backgroundParser->addFile( canonicalFileName, true ); } } void JavaSupportPart::slotProjectCompiled() { kdDebug(9013) << "JavaSupportPart::slotProjectCompiled()" << endl; parseProject(); } TQStringList JavaSupportPart::modifiedFileList() { TQStringList lst; TQStringList fileList = project()->allFiles(); TQStringList::Iterator it = fileList.begin(); while( it != fileList.end() ){ TQString fileName = *it; ++it; TQFileInfo fileInfo( m_projectDirectory, fileName ); if( !fileExtensions().contains(fileInfo.extension()) ) continue; TQDateTime t = fileInfo.lastModified(); TQString path = URLUtil::canonicalPath( fileInfo.absFilePath() ); TQMap::Iterator dictIt = m_timestamp.find( path ); if( fileInfo.exists() && dictIt != m_timestamp.end() && *dictIt == t ) continue; lst << fileName; } return lst; } KTextEditor::Document * JavaSupportPart::findDocument( const KURL & url ) { if( !partController()->parts() ) return 0; TQPtrList parts( *partController()->parts() ); TQPtrListIterator it( parts ); while( KParts::Part* part = it.current() ){ KTextEditor::Document* doc = dynamic_cast( part ); if( doc && doc->url() == url ) return doc; ++it; } return 0; } void JavaSupportPart::setupCatalog( ) { kdDebug(9013) << "JavaSupportPart::setupCatalog()" << endl; TQStringList indexList = TQStringList() << "kind" << "name" << "scope" << "fileName"; TDEStandardDirs *dirs = JavaSupportFactory::instance()->dirs(); TQStringList pcsList = dirs->findAllResources( "pcs", "*.db", false, true ); TQStringList pcsIdxList = dirs->findAllResources( "pcs", "*.idx", false, true ); if( pcsList.size() && pcsVersion() < KDEV_DB_VERSION ){ TQStringList l = pcsList + pcsIdxList; int rtn = KMessageBox::questionYesNoList( 0, i18n("Persistent class store will be disabled: you have a wrong version of pcs installed.\nRemove old pcs files?"), l, i18n("Java Support"), KStdGuiItem::remove(), i18n("Keep Them") ); if( rtn == KMessageBox::Yes ){ TQStringList::Iterator it = l.begin(); while( it != l.end() ){ TQFile::remove( *it ); ++it; } // @todo regenerate the pcs list pcsList.clear(); } else { return; } } TQStringList::Iterator it = pcsList.begin(); while( it != pcsList.end() ){ Catalog* catalog = new Catalog(); catalog->open( *it ); ++it; for( TQStringList::Iterator idxIt=indexList.begin(); idxIt!=indexList.end(); ++idxIt ) catalog->addIndex( (*idxIt).utf8() ); m_catalogList.append( catalog ); codeRepository()->registerCatalog( catalog ); } setPcsVersion( KDEV_DB_VERSION ); } KMimeType::List JavaSupportPart::mimeTypes( ) { KMimeType::List list; KMimeType::Ptr mime; mime = KMimeType::mimeType( "text/x-java" ); if( mime ) list << mime; return list; } int JavaSupportPart::pcsVersion() { TDEConfig* config = JavaSupportFactory::instance()->config(); TDEConfigGroupSaver cgs( config, "PCS" ); return config->readNumEntry( "Version", 0 ); } void JavaSupportPart::setPcsVersion( int version ) { TDEConfig* config = JavaSupportFactory::instance()->config(); TDEConfigGroupSaver cgs( config, "PCS" ); config->writeEntry( "Version", version ); config->sync(); } TQString JavaSupportPart::formatTag( const Tag & /*inputTag*/ ) { return TQString(); } void JavaSupportPart::removeWithReferences( const TQString & fileName ) { kdDebug(9013) << "remove with references: " << fileName << endl; m_timestamp.remove( fileName ); if( !codeModel()->hasFile(fileName) ) return; emit aboutToRemoveSourceInfo( fileName ); codeModel()->removeFile( codeModel()->fileByName(fileName) ); } bool JavaSupportPart::isValidSource( const TQString& fileName ) const { TQFileInfo fileInfo( fileName ); return fileExtensions().contains( fileInfo.extension() ) && !TQFile::exists(fileInfo.dirPath(true) + "/.tdev_ignore"); } TQString JavaSupportPart::formatModelItem( const CodeModelItem *item, bool shortDescription ) { if (item->isFunction()) { const FunctionModel *model = static_cast(item); TQString function; TQString args; ArgumentList argumentList = model->argumentList(); for (ArgumentList::const_iterator it = argumentList.begin(); it != argumentList.end(); ++it) { args.isEmpty() ? args += "" : args += ", " ; args += formatModelItem((*it).data()); } if( !shortDescription ) function += model->resultType() + " "; function += model->name() + "(" + args + ")" + (model->isAbstract() ? TQString(" = 0") : TQString("") ); return function; } else if (item->isVariable()) { const VariableModel *model = static_cast(item); if( shortDescription ) return model->name(); return model->type() + " " + model->name(); } else if (item->isArgument()) { const ArgumentModel *model = static_cast(item); TQString arg; if( !shortDescription ) arg += model->type() + " "; arg += model->name(); if( !shortDescription ) arg += model->defaultValue().isEmpty() ? TQString("") : TQString(" = ") + model->defaultValue(); return arg.stripWhiteSpace(); } else return KDevLanguageSupport::formatModelItem( item, shortDescription ); } void JavaSupportPart::addClass( ) { slotNewClass(); } void JavaSupportPart::saveProjectSourceInfo( ) { const FileList fileList = codeModel()->fileList(); if( !project() || fileList.isEmpty() ) return; TQFile f( project()->projectDirectory() + "/" + project()->projectName() + ".kdevelop.pcs" ); if( !f.open( IO_WriteOnly ) ) return; TQDataStream stream( &f ); TQMap offsets; TQString pcs( "PCS" ); stream << pcs << KDEV_PCS_VERSION; stream << int( fileList.size() ); for( FileList::ConstIterator it=fileList.begin(); it!=fileList.end(); ++it ){ const FileDom dom = (*it); stream << dom->name() << m_timestamp[ dom->name() ].toTime_t(); offsets.insert( dom->name(), stream.device()->at() ); stream << (uint)0; // dummy offset } for( FileList::ConstIterator it=fileList.begin(); it!=fileList.end(); ++it ){ const FileDom dom = (*it); int offset = stream.device()->at(); dom->write( stream ); int end = stream.device()->at(); stream.device()->at( offsets[dom->name()] ); stream << offset; stream.device()->at( end ); } } #include "javasupportpart.moc"