#include "dirdef.h" #include "filename.h" #include "doxygen.h" #include "util.h" #include "outputlist.h" #include "language.h" #include "message.h" //---------------------------------------------------------------------- // method implementation static int g_dirCount=0; DirDef::DirDef(const char *path) : Definition(path,1,path) { // get display name (stipping the paths mentioned in STRIP_FROM_PATH) m_dispName = stripFromPath(path); // get short name (last part of path) m_shortName = path; if (m_shortName.at(m_shortName.length()-1)=='/') { // strip trailing / m_shortName = m_shortName.left(m_shortName.length()-1); } int pi=m_shortName.findRev('/'); if (pi!=-1) { // remove everything till the last / m_shortName = m_shortName.mid(pi+1); } m_subdirs.setAutoDelete(TRUE); m_fileList = new FileList; m_classSDict = new ClassSDict(17); m_usedDirs = new QDict(257); m_usedDirs->setAutoDelete(TRUE); m_dirCount = g_dirCount++; m_level=-1; m_parent=0; } DirDef::~DirDef() { } void DirDef::addSubDir(DirDef *subdir) { m_subdirs.inSort(subdir); subdir->setOuterScope(this); subdir->m_parent=this; } void DirDef::addFile(FileDef *fd) { m_fileList->inSort(fd); fd->setDirDef(this); } QCString DirDef::getOutputFileBase() const { //return "dir_"+convertNameToFile(name()); return QCString().sprintf("dir_%06d",m_dirCount); } void DirDef::writeDetailedDocumentation(OutputList &ol) { if (!briefDescription().isEmpty() || !documentation().isEmpty()) { ol.writeRuler(); ol.pushGeneratorState(); ol.disable(OutputGenerator::Latex); ol.disable(OutputGenerator::RTF); ol.writeAnchor(0,"_details"); ol.popGeneratorState(); ol.startGroupHeader(); ol.parseText(theTranslator->trDetailedDescription()); ol.endGroupHeader(); // repeat brief description if (!briefDescription().isEmpty() && Config_getBool("REPEAT_BRIEF")) { ol.parseDoc(briefFile(),briefLine(),this,0,briefDescription(),FALSE,FALSE); ol.newParagraph(); } // write documentation if (!documentation().isEmpty()) { ol.parseDoc(docFile(),docLine(),this,0,documentation()+"\n",TRUE,FALSE); } } } void DirDef::writeDocumentation(OutputList &ol) { ol.pushGeneratorState(); QCString shortTitle=theTranslator->trDirReference(m_shortName); QCString title=theTranslator->trDirReference(m_dispName); startFile(ol,getOutputFileBase(),name(),title); // write navigation path writeNavigationPath(ol); startTitle(ol,getOutputFileBase()); ol.pushGeneratorState(); ol.disableAllBut(OutputGenerator::Html); ol.parseText(shortTitle); ol.enableAll(); ol.disable(OutputGenerator::Html); ol.parseText(title); ol.popGeneratorState(); endTitle(ol,getOutputFileBase(),title); // write brief or details (if DETAILS_AT_TOP) if (Config_getBool("DETAILS_AT_TOP")) { writeDetailedDocumentation(ol); ol.newParagraph(); } else if (!briefDescription().isEmpty()) { ol.parseDoc(briefFile(),briefLine(),this,0,briefDescription(),TRUE,FALSE); ol.writeString(" \n"); ol.pushGeneratorState(); ol.disable(OutputGenerator::Latex); ol.disable(OutputGenerator::RTF); ol.disable(OutputGenerator::Man); ol.startTextLink(0,"_details"); ol.parseText(theTranslator->trMore()); ol.endTextLink(); ol.enableAll(); ol.disableAllBut(OutputGenerator::Man); ol.newParagraph(); ol.popGeneratorState(); } if (!Config_getString("GENERATE_TAGFILE").isEmpty()) { Doxygen::tagFile << " " << endl; Doxygen::tagFile << " " << convertToXML(displayName()) << "" << endl; Doxygen::tagFile << " " << convertToXML(name()) << "" << endl; Doxygen::tagFile << " " << convertToXML(getOutputFileBase()) << Doxygen::htmlFileExtension << "" << endl; } ol.startMemberSections(); // write subdir list if (m_subdirs.count()>0) { ol.startMemberHeader(); ol.parseText(theTranslator->trDir(TRUE,FALSE)); ol.endMemberHeader(); ol.startMemberList(); DirDef *dd=m_subdirs.first(); while (dd) { ol.startMemberItem(0); ol.parseText(theTranslator->trDir(FALSE,TRUE)); ol.insertMemberAlign(); ol.writeObjectLink(dd->getReference(),dd->getOutputFileBase(),0,dd->shortName()); ol.endMemberItem(); if (!Config_getString("GENERATE_TAGFILE").isEmpty()) { Doxygen::tagFile << " " << convertToXML(dd->displayName()) << "" << endl; } if (!dd->briefDescription().isEmpty() && Config_getBool("BRIEF_MEMBER_DESC")) { ol.startMemberDescription(); ol.parseDoc(briefFile(),briefLine(),dd,0,dd->briefDescription(),FALSE,FALSE); ol.endMemberDescription(); ol.newParagraph(); } dd=m_subdirs.next(); } ol.endMemberList(); } // write file list if (m_fileList->count()>0) { ol.startMemberHeader(); ol.parseText(theTranslator->trFile(TRUE,FALSE)); ol.endMemberHeader(); ol.startMemberList(); FileDef *fd=m_fileList->first(); while (fd) { ol.startMemberItem(0); ol.docify(theTranslator->trFile(FALSE,TRUE)+" "); ol.insertMemberAlign(); if (fd->isLinkable()) { ol.writeObjectLink(fd->getReference(),fd->getOutputFileBase(),0,fd->name()); } else { ol.startBold(); ol.writeString(fd->name()); ol.endBold(); } if (!Config_getString("GENERATE_TAGFILE").isEmpty()) { Doxygen::tagFile << " " << convertToXML(fd->name()) << "" << endl; } ol.endMemberItem(); if (!fd->briefDescription().isEmpty() && Config_getBool("BRIEF_MEMBER_DESC")) { ol.startMemberDescription(); ol.parseDoc(briefFile(),briefLine(),fd,0,fd->briefDescription(),FALSE,FALSE); ol.endMemberDescription(); ol.newParagraph(); } fd=m_fileList->next(); } ol.endMemberList(); } ol.endMemberSections(); if (!Config_getString("GENERATE_TAGFILE").isEmpty()) { writeDocAnchorsToTagFile(); Doxygen::tagFile << " " << endl; } if (!Config_getBool("DETAILS_AT_TOP")) { writeDetailedDocumentation(ol); } endFile(ol); ol.popGeneratorState(); } void DirDef::writePathFragment(OutputList &ol) { if (getOuterScope()!=Doxygen::globalScope && getOuterScope()->definitionType()==Definition::TypeDir) { //printf("getOuterScope %s\n",getOuterScope()->name().data()); ((DirDef*)getOuterScope())->writePathFragment(ol); ol.writeString(" / "); } ol.writeObjectLink(getReference(),getOutputFileBase(),0,shortName()); } void DirDef::writeNavigationPath(OutputList &ol) { ol.pushGeneratorState(); ol.disableAllBut(OutputGenerator::Html); ol.writeString("
\n"); writePathFragment(ol); ol.writeString("
\n"); ol.popGeneratorState(); } void DirDef::setLevel() { if (m_level==-1) // level not set before { DirDef *p = parent(); if (p) { p->setLevel(); m_level = p->level()+1; } else { m_level = 0; } } } /** Add as "uses" dependency between \a this dir and \a dir, * that was caused by a dependency on file \a fd. */ void DirDef::addUsesDependency(DirDef *dir,FileDef *fd,bool inherited) { if (this==dir) return; // do not add self-dependencies //printf(" > add dependency %s->%s due to %s\n",shortName().data(), // dir->shortName().data(),fd->name().data()); // levels match => add direct dependency bool added=FALSE; UsedDir *usedDir = m_usedDirs->find(dir->getOutputFileBase()); if (usedDir) // dir dependency already present { FileDef *usedFd = usedDir->findFile(fd->getOutputFileBase()); if (usedFd==0) // new file dependency { //printf(" => new file\n"); usedDir->addFile(fd); added=TRUE; } else { // dir & file dependency already added } } else // new directory dependency { //printf(" => new file\n"); usedDir = new UsedDir(dir,inherited); usedDir->addFile(fd); m_usedDirs->insert(dir->getOutputFileBase(),usedDir); added=TRUE; } if (added && dir->parent()) { // add relation to parent of used dir addUsesDependency(dir->parent(),fd,inherited); } if (parent()) { // add relation for the parent of this dir as well parent()->addUsesDependency(dir,fd,TRUE); } } /** Computes the dependencies between directories */ void DirDef::computeDependencies() { FileList *fl = m_fileList; if (fl) { QListIterator fli(*fl); FileDef *fd; for (fli.toFirst();(fd=fli.current());++fli) // foreach file in dir dd { //printf("** dir=%s file=%s\n",shortName().data(),fd->name().data()); QList *ifl = fd->includeFileList(); if (ifl) { QListIterator ifli(*ifl); IncludeInfo *ii; for (ifli.toFirst();(ii=ifli.current());++ifli) // foreach include file { //printf(" > %s\n",ii->includeName.data()); if (ii->fileDef && ii->fileDef->isLinkable()) // linkable file { DirDef *usedDir = ii->fileDef->getDirDef(); if (usedDir) { // add dependency: thisDir->usedDir addUsesDependency(usedDir,ii->fileDef,FALSE); } } } } } } } bool DirDef::isParentOf(DirDef *dir) const { if (dir->parent()==this) // this is a parent of dir return TRUE; else if (dir->parent()) // repeat for the parent of dir return isParentOf(dir->parent()); else return FALSE; } //---------------------------------------------------------------------- UsedDir::UsedDir(DirDef *dir,bool inherited) : m_dir(dir), m_inherited(inherited) { } UsedDir::~UsedDir() { } void UsedDir::addFile(FileDef *fd) { m_files.insert(fd->getOutputFileBase(),fd); } FileDef *UsedDir::findFile(const char *name) { QCString n=name; return n.isEmpty() ? 0 : m_files.find(n); } //---------------------------------------------------------------------- // helper functions DirDef *DirDef::createNewDir(const char *path) { ASSERT(path!=0); DirDef *dir = Doxygen::directories.find(path); if (dir==0) // new dir { //printf("Adding new dir %s\n",path); dir = new DirDef(path); //printf("createNewDir %s short=%s\n",path,dir->shortName().data()); Doxygen::directories.inSort(path,dir); } return dir; } bool DirDef::matchPath(const QCString &path,QStrList &l) { const char *s=l.first(); while (s) { QCString prefix = s; if (stricmp(prefix.left(path.length()),path)==0) // case insensitive compare { return TRUE; } s = l.next(); } return FALSE; } /*! strip part of \a path if it matches * one of the paths in the Config_getList("STRIP_FROM_PATH") list */ DirDef *DirDef::mergeDirectoryInTree(const QCString &path) { //printf("DirDef::mergeDirectoryInTree(%s)\n",path.data()); int p=0,i=0; DirDef *dir=0; while ((i=path.find('/',p))!=-1) { QCString part=path.left(i+1); if (!matchPath(part,Config_getList("STRIP_FROM_PATH")) && part!="/") { dir=createNewDir(part); } p=i+1; } return dir; } void DirDef::writeDepGraph(QTextStream &t) { t << "digraph G {\n"; t << " compound=true\n"; QDict dirsInGraph(257); dirsInGraph.insert(getOutputFileBase(),this); if (isCluster()) { t << " subgraph cluster" << getOutputFileBase() << " {\n"; t << " " << getOutputFileBase() << " [shape=plaintext label=\"" << shortName() << "\"];\n"; // add nodes for sub directories QListIterator sdi(m_subdirs); DirDef *sdir; for (sdi.toFirst();(sdir=sdi.current());++sdi) { t << " " << sdir->getOutputFileBase() << " [shape=box label=\"" << sdir->shortName() << "\""; if (sdir->isCluster()) { t << " color=\"red\" fillcolor=\"white\" style=\"filled\""; } t << "];\n"; dirsInGraph.insert(sdir->getOutputFileBase(),sdir); } t << " }\n"; } else { t << getOutputFileBase() << " [shape=box label=\"" << shortName() << "\"];\n"; } // add nodes for other used directories QDictIterator udi(*m_usedDirs); UsedDir *udir; //printf("*** For dir %s\n",shortName().data()); for (udi.toFirst();(udir=udi.current());++udi) // for each used dir (=directly used or a parent of a directly used dir) { const DirDef *usedDir=udir->dir(); DirDef *dir=this; while (dir) { //printf("*** check relation %s->%s same_parent=%d !%s->isParentOf(%s)=%d\n", // dir->shortName().data(),usedDir->shortName().data(), // dir->parent()==usedDir->parent(), // usedDir->shortName().data(), // shortName().data(), // !usedDir->isParentOf(this) // ); if (dir!=usedDir && dir->parent()==usedDir->parent() && !usedDir->isParentOf(this)) // include if both have the same parent (or no parent) { t << " " << usedDir->getOutputFileBase() << " [shape=box label=\"" << usedDir->shortName() << "\""; if (usedDir->isCluster()) { t << " color=\"red\" fillcolor=\"white\" style=\"filled\""; } t << "];\n"; dirsInGraph.insert(usedDir->getOutputFileBase(),usedDir); break; } dir=dir->parent(); } } // add relations between all selected directories DirDef *dir; QDictIterator di(dirsInGraph); for (di.toFirst();(dir=di.current());++di) // foreach dir in the graph { QDictIterator udi(*dir->usedDirs()); UsedDir *udir; for (udi.toFirst();(udir=udi.current());++udi) // foreach used dir { const DirDef *usedDir=udir->dir(); if ((dir!=this || !udir->inherited()) && // only show direct dependendies for this dir (usedDir!=this || !udir->inherited()) && // only show direct dependendies for this dir !usedDir->isParentOf(dir) && // don't point to own parent dirsInGraph.find(usedDir->getOutputFileBase())) // only point to nodes that are in the graph { int nrefs = udir->files().count(); t << " " << dir->getOutputFileBase() << "->" << usedDir->getOutputFileBase(); t << " [headlabel=\"" << nrefs << "\" headhref=\"http://www.doxygen.org\"]"; t << ";\n"; } } } t << "}\n"; } //---------------------------------------------------------------------- // external functions void buildDirectories() { // for each input file FileNameListIterator fnli(Doxygen::inputNameList); FileName *fn; for (fnli.toFirst();(fn=fnli.current());++fnli) { FileNameIterator fni(*fn); FileDef *fd; for (;(fd=fni.current());++fni) { //printf("buildDirectories %s\n",fd->name().data()); if (fd->getReference().isEmpty() && !fd->isDocumentationFile()) { DirDef *dir; if ((dir=Doxygen::directories.find(fd->getPath()))==0) // new directory { dir = DirDef::mergeDirectoryInTree(fd->getPath()); } if (dir) dir->addFile(fd); } else { // do something for file imported via tag files. } } } //DirDef *root = new DirDef("root:"); // compute relations between directories => introduce container dirs. DirDef *dir; DirSDict::Iterator sdi(Doxygen::directories); for (sdi.toFirst();(dir=sdi.current());++sdi) { //printf("New dir %s\n",dir->displayName().data()); QCString name = dir->name(); int i=name.findRev('/',name.length()-2); if (i>0) { DirDef *parent = Doxygen::directories.find(name.left(i+1)); //if (parent==0) parent=root; if (parent) { parent->addSubDir(dir); //printf("DirDef::addSubdir(): Adding subdir\n%s to\n%s\n", // dir->displayName().data(), parent->displayName().data()); } } } } void computeDirDependencies() { DirDef *dir; DirSDict::Iterator sdi(Doxygen::directories); // compute nesting level for each directory for (sdi.toFirst();(dir=sdi.current());++sdi) { dir->setLevel(); } // compute uses dependencies between directories for (sdi.toFirst();(dir=sdi.current());++sdi) { dir->computeDependencies(); } #if 0 printf("-------------------------------------------------------------\n"); // print dependencies (for debugging) for (sdi.toFirst();(dir=sdi.current());++sdi) { if (dir->usedDirs()) { QDictIterator udi(*dir->usedDirs()); UsedDir *usedDir; for (udi.toFirst();(usedDir=udi.current());++udi) { printf("%s depends on %s due to ", dir->shortName().data(),usedDir->dir()->shortName().data()); QDictIterator fdi(usedDir->files()); FileDef *fd; for (fdi.toFirst();(fd=fdi.current());++fdi) { printf("%s ",fd->name().data()); } printf("\n"); } } } printf("^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^-^\n"); #endif } void writeDirDependencyGraph(const char *dirName) { QString path; DirDef *dir; DirSDict::Iterator sdi(Doxygen::directories); QFile htmlPage(QCString(dirName)+"/dirdeps.html"); if (htmlPage.open(IO_WriteOnly)) { QTextStream out(&htmlPage); out << ""; for (sdi.toFirst();(dir=sdi.current());++sdi) { path=dirName; path+="/"; path+=dir->getOutputFileBase(); path+="_dep.dot"; out << "

" << dir->displayName() << "

" << endl; out << "getOutputFileBase() << "_dep.gif\">" << endl; QFile f(path); if (f.open(IO_WriteOnly)) { QTextStream t(&f); dir->writeDepGraph(t); } f.close(); QCString dotArgs(4096); QCString outFile = QCString(dirName)+"/"+dir->getOutputFileBase()+"_dep.gif"; dotArgs.sprintf("%s -Tgif -o %s",path.data(),outFile.data()); if (iSystem(Config_getString("DOT_PATH")+"dot",dotArgs)!=0) { err("Problems running dot. Check your installation!\n"); } } out << ""; } htmlPage.close(); } void generateDirDocs(OutputList &ol) { DirDef *dir; DirSDict::Iterator sdi(Doxygen::directories); for (sdi.toFirst();(dir=sdi.current());++sdi) { dir->writeDocumentation(ol); } }