7 #include "outputlist.h"
12 #include "ftextstream.h"
14 //----------------------------------------------------------------------
15 // method implementation
17 static int g_dirCount=0;
19 DirDef::DirDef(const char *path) : Definition(path,1,path)
21 bool fullPathNames = Config_getBool("FULL_PATH_NAMES");
22 // get display name (stipping the paths mentioned in STRIP_FROM_PATH)
23 // get short name (last part of path)
26 if (m_shortName.at(m_shortName.length()-1)=='/')
28 m_shortName = m_shortName.left(m_shortName.length()-1);
30 int pi=m_shortName.findRev('/');
32 { // remove everything till the last /
33 m_shortName = m_shortName.mid(pi+1);
35 setLocalName(m_shortName);
36 m_dispName = fullPathNames ? stripFromPath(path) : m_shortName;
37 if (m_dispName.at(m_dispName.length()-1)=='/')
39 m_dispName = m_dispName.left(m_dispName.length()-1);
42 m_fileList = new FileList;
43 m_usedDirs = new QDict<UsedDir>(257);
44 m_usedDirs->setAutoDelete(TRUE);
45 m_dirCount = g_dirCount++;
56 bool DirDef::isLinkableInProject() const
58 return !isReference();
61 bool DirDef::isLinkable() const
63 return isReference() || isLinkableInProject();
66 void DirDef::addSubDir(DirDef *subdir)
68 m_subdirs.inSort(subdir);
69 subdir->setOuterScope(this);
70 subdir->m_parent=this;
73 void DirDef::addFile(FileDef *fd)
75 m_fileList->inSort(fd);
79 static QCString encodeDirName(const QCString &anchor)
83 // convert to md5 hash
86 MD5Buffer((const unsigned char *)anchor.data(),anchor.length(),md5_sig);
87 MD5SigToString(md5_sig,sigStr.data(),33);
92 // int l = anchor.length(),i;
95 // char c = anchor.at(i);
96 // if ((c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9'))
102 // static char hexStr[]="0123456789ABCDEF";
103 // char escChar[]={ '_', 0, 0, 0 };
104 // escChar[1]=hexStr[c>>4];
105 // escChar[2]=hexStr[c&0xf];
112 QCString DirDef::getOutputFileBase() const
114 //printf("DirDef::getOutputFileBase() %s->dir_%s\n",
115 // m_diskName.data(),encodeDirName(m_diskName).data());
116 return "dir_"+encodeDirName(m_diskName);
119 void DirDef::writeDetailedDescription(OutputList &ol,const QCString &title)
121 if ((!briefDescription().isEmpty() && Config_getBool("REPEAT_BRIEF")) ||
122 !documentation().isEmpty())
124 ol.pushGeneratorState();
125 ol.disable(OutputGenerator::Html);
127 ol.popGeneratorState();
128 ol.pushGeneratorState();
129 ol.disableAllBut(OutputGenerator::Html);
130 ol.writeAnchor(0,"details");
131 ol.popGeneratorState();
132 ol.startGroupHeader();
136 // repeat brief description
137 if (!briefDescription().isEmpty() && Config_getBool("REPEAT_BRIEF"))
139 ol.parseDoc(briefFile(),briefLine(),this,0,briefDescription(),FALSE,FALSE);
141 // separator between brief and details
142 if (!briefDescription().isEmpty() && Config_getBool("REPEAT_BRIEF") &&
143 !documentation().isEmpty())
145 ol.pushGeneratorState();
146 ol.disable(OutputGenerator::Man);
147 ol.disable(OutputGenerator::RTF);
148 // ol.newParagraph(); // FIXME:PARA
150 ol.disableAllBut(OutputGenerator::Man);
151 ol.writeString("\n\n");
152 ol.popGeneratorState();
155 // write documentation
156 if (!documentation().isEmpty())
158 ol.parseDoc(docFile(),docLine(),this,0,documentation()+"\n",TRUE,FALSE);
163 void DirDef::writeBriefDescription(OutputList &ol)
165 if (!briefDescription().isEmpty() && Config_getBool("BRIEF_MEMBER_DESC"))
168 ol.parseDoc(briefFile(),briefLine(),this,0,briefDescription(),TRUE,FALSE);
169 ol.pushGeneratorState();
170 ol.disable(OutputGenerator::RTF);
171 ol.writeString(" \n");
172 ol.enable(OutputGenerator::RTF);
174 if (Config_getBool("REPEAT_BRIEF") ||
175 !documentation().isEmpty()
178 ol.disableAllBut(OutputGenerator::Html);
179 ol.startTextLink(0,"details");
180 ol.parseText(theTranslator->trMore());
183 ol.popGeneratorState();
185 //ol.pushGeneratorState();
186 //ol.disable(OutputGenerator::RTF);
188 //ol.popGeneratorState();
194 void DirDef::writeDirectoryGraph(OutputList &ol)
196 // write graph dependency graph
197 if (Config_getBool("DIRECTORY_GRAPH") && Config_getBool("HAVE_DOT"))
199 DotDirDeps dirDep(this);
200 if (!dirDep.isTrivial())
202 msg("Generating dependency graph for directory %s\n",displayName().data());
203 ol.disable(OutputGenerator::Man);
204 //ol.startParagraph();
205 ol.startDirDepGraph();
206 ol.parseText(theTranslator->trDirDepGraph(shortName()));
207 ol.endDirDepGraph(dirDep);
214 void DirDef::writeSubDirList(OutputList &ol)
217 if (m_subdirs.count()>0)
219 ol.startMemberHeader("subdirs");
220 ol.parseText(theTranslator->trDir(TRUE,FALSE));
221 ol.endMemberHeader();
222 ol.startMemberList();
223 DirDef *dd=m_subdirs.first();
226 ol.startMemberDeclaration();
227 ol.startMemberItem(dd->getOutputFileBase(),0);
228 ol.parseText(theTranslator->trDir(FALSE,TRUE)+" ");
229 ol.insertMemberAlign();
230 ol.writeObjectLink(dd->getReference(),dd->getOutputFileBase(),0,dd->shortName());
232 if (!Config_getString("GENERATE_TAGFILE").isEmpty())
234 Doxygen::tagFile << " <dir>" << convertToXML(dd->displayName()) << "</dir>" << endl;
236 if (!dd->briefDescription().isEmpty() && Config_getBool("BRIEF_MEMBER_DESC"))
238 ol.startMemberDescription(dd->getOutputFileBase());
239 ol.parseDoc(briefFile(),briefLine(),dd,0,dd->briefDescription(),
244 TRUE // link from index
246 ol.endMemberDescription();
248 ol.endMemberDeclaration(0,0);
256 void DirDef::writeFileList(OutputList &ol)
259 if (m_fileList->count()>0)
261 ol.startMemberHeader("files");
262 ol.parseText(theTranslator->trFile(TRUE,FALSE));
263 ol.endMemberHeader();
264 ol.startMemberList();
265 FileDef *fd=m_fileList->first();
268 ol.startMemberDeclaration();
269 ol.startMemberItem(fd->getOutputFileBase(),0);
270 ol.docify(theTranslator->trFile(FALSE,TRUE)+" ");
271 ol.insertMemberAlign();
272 if (fd->isLinkable())
274 ol.writeObjectLink(fd->getReference(),fd->getOutputFileBase(),0,fd->name());
279 ol.docify(fd->name());
282 if (fd->generateSourceFile())
284 ol.pushGeneratorState();
285 ol.disableAllBut(OutputGenerator::Html);
287 ol.startTextLink(fd->includeName(),0);
289 ol.parseText(theTranslator->trCode());
292 ol.popGeneratorState();
294 if (!Config_getString("GENERATE_TAGFILE").isEmpty())
296 Doxygen::tagFile << " <file>" << convertToXML(fd->name()) << "</file>" << endl;
299 if (!fd->briefDescription().isEmpty() && Config_getBool("BRIEF_MEMBER_DESC"))
301 ol.startMemberDescription(fd->getOutputFileBase());
302 ol.parseDoc(briefFile(),briefLine(),fd,0,fd->briefDescription(),
307 TRUE // link from index
309 ol.endMemberDescription();
311 ol.endMemberDeclaration(0,0);
312 fd=m_fileList->next();
318 void DirDef::startMemberDeclarations(OutputList &ol)
320 ol.startMemberSections();
323 void DirDef::endMemberDeclarations(OutputList &ol)
325 ol.endMemberSections();
328 void DirDef::writeDocumentation(OutputList &ol)
330 static bool generateTreeView = Config_getBool("GENERATE_TREEVIEW");
331 ol.pushGeneratorState();
333 QCString shortTitle=theTranslator->trDirReference(m_shortName);
334 QCString title=theTranslator->trDirReference(m_dispName);
335 startFile(ol,getOutputFileBase(),name(),title,HLI_None,!generateTreeView);
337 if (!generateTreeView)
339 // write navigation path
340 writeNavigationPath(ol);
341 ol.endQuickIndices();
344 startTitle(ol,getOutputFileBase());
345 ol.pushGeneratorState();
346 ol.disableAllBut(OutputGenerator::Html);
347 ol.parseText(shortTitle);
349 ol.disable(OutputGenerator::Html);
351 ol.popGeneratorState();
352 endTitle(ol,getOutputFileBase(),title);
355 if (!Config_getString("GENERATE_TAGFILE").isEmpty())
357 Doxygen::tagFile << " <compound kind=\"dir\">" << endl;
358 Doxygen::tagFile << " <name>" << convertToXML(displayName()) << "</name>" << endl;
359 Doxygen::tagFile << " <path>" << convertToXML(name()) << "</path>" << endl;
360 Doxygen::tagFile << " <filename>" << convertToXML(getOutputFileBase()) << Doxygen::htmlFileExtension << "</filename>" << endl;
363 //---------------------------------------- start flexible part -------------------------------
365 SrcLangExt lang = getLanguage();
366 QListIterator<LayoutDocEntry> eli(
367 LayoutDocManager::instance().docEntries(LayoutDocManager::Directory));
369 for (eli.toFirst();(lde=eli.current());++eli)
373 case LayoutDocEntry::BriefDesc:
374 writeBriefDescription(ol);
376 case LayoutDocEntry::DirGraph:
377 writeDirectoryGraph(ol);
379 case LayoutDocEntry::MemberDeclStart:
380 startMemberDeclarations(ol);
382 case LayoutDocEntry::DirSubDirs:
385 case LayoutDocEntry::DirFiles:
388 case LayoutDocEntry::MemberDeclEnd:
389 endMemberDeclarations(ol);
391 case LayoutDocEntry::DetailedDesc:
393 LayoutDocEntrySection *ls = (LayoutDocEntrySection*)lde;
394 writeDetailedDescription(ol,ls->title(lang));
397 case LayoutDocEntry::ClassIncludes:
398 case LayoutDocEntry::ClassInlineClasses:
399 case LayoutDocEntry::ClassInheritanceGraph:
400 case LayoutDocEntry::ClassNestedClasses:
401 case LayoutDocEntry::ClassCollaborationGraph:
402 case LayoutDocEntry::ClassAllMembersLink:
403 case LayoutDocEntry::ClassUsedFiles:
404 case LayoutDocEntry::NamespaceNestedNamespaces:
405 case LayoutDocEntry::NamespaceClasses:
406 case LayoutDocEntry::NamespaceInlineClasses:
407 case LayoutDocEntry::FileClasses:
408 case LayoutDocEntry::FileNamespaces:
409 case LayoutDocEntry::FileIncludes:
410 case LayoutDocEntry::FileIncludeGraph:
411 case LayoutDocEntry::FileIncludedByGraph:
412 case LayoutDocEntry::FileSourceLink:
413 case LayoutDocEntry::FileInlineClasses:
414 case LayoutDocEntry::GroupClasses:
415 case LayoutDocEntry::GroupInlineClasses:
416 case LayoutDocEntry::GroupNamespaces:
417 case LayoutDocEntry::GroupDirs:
418 case LayoutDocEntry::GroupNestedGroups:
419 case LayoutDocEntry::GroupFiles:
420 case LayoutDocEntry::GroupGraph:
421 case LayoutDocEntry::GroupPageDocs:
422 case LayoutDocEntry::AuthorSection:
423 case LayoutDocEntry::MemberGroups:
424 case LayoutDocEntry::MemberDecl:
425 case LayoutDocEntry::MemberDef:
426 case LayoutDocEntry::MemberDefStart:
427 case LayoutDocEntry::MemberDefEnd:
428 err("Internal inconsistency: member %d should not be part of "
429 "LayoutDocManager::Directory entry list\n",lde->kind());
434 //---------------------------------------- end flexible part -------------------------------
436 if (!Config_getString("GENERATE_TAGFILE").isEmpty())
438 writeDocAnchorsToTagFile();
439 Doxygen::tagFile << " </compound>" << endl;
444 endFileWithNavPath(this,ol);
446 ol.popGeneratorState();
449 void DirDef::setLevel()
451 if (m_level==-1) // level not set before
453 DirDef *p = parent();
457 m_level = p->level()+1;
466 /** Add as "uses" dependency between \a this dir and \a dir,
467 * that was caused by a dependency on file \a fd.
469 void DirDef::addUsesDependency(DirDef *dir,FileDef *srcFd,
470 FileDef *dstFd,bool inherited)
472 if (this==dir) return; // do not add self-dependencies
473 //static int count=0;
474 //printf(" %d add dependency %s->%s due to %s->%s\n",
475 // count++,shortName().data(),
476 // dir->shortName().data(),
477 // srcFd->name().data(),
478 // dstFd->name().data());
480 // levels match => add direct dependency
482 UsedDir *usedDir = m_usedDirs->find(dir->getOutputFileBase());
483 if (usedDir) // dir dependency already present
485 FilePair *usedPair = usedDir->findFilePair(
486 srcFd->getOutputFileBase()+dstFd->getOutputFileBase());
487 if (usedPair==0) // new file dependency
489 //printf(" => new file\n");
490 usedDir->addFileDep(srcFd,dstFd);
495 // dir & file dependency already added
498 else // new directory dependency
500 //printf(" => new file\n");
501 usedDir = new UsedDir(dir,inherited);
502 usedDir->addFileDep(srcFd,dstFd);
503 m_usedDirs->insert(dir->getOutputFileBase(),usedDir);
510 // add relation to parent of used dir
511 addUsesDependency(dir->parent(),srcFd,dstFd,inherited);
515 // add relation for the parent of this dir as well
516 parent()->addUsesDependency(dir,srcFd,dstFd,TRUE);
521 /** Computes the dependencies between directories
523 void DirDef::computeDependencies()
525 FileList *fl = m_fileList;
528 QListIterator<FileDef> fli(*fl);
530 for (fli.toFirst();(fd=fli.current());++fli) // foreach file in dir dd
532 //printf(" File %s\n",fd->name().data());
533 //printf("** dir=%s file=%s\n",shortName().data(),fd->name().data());
534 QList<IncludeInfo> *ifl = fd->includeFileList();
537 QListIterator<IncludeInfo> ifli(*ifl);
539 for (ifli.toFirst();(ii=ifli.current());++ifli) // foreach include file
541 //printf(" > %s\n",ii->includeName.data());
542 //printf(" #include %s\n",ii->includeName.data());
543 if (ii->fileDef && ii->fileDef->isLinkable()) // linkable file
545 DirDef *usedDir = ii->fileDef->getDirDef();
548 // add dependency: thisDir->usedDir
549 //static int count=0;
550 //printf(" %d: add dependency %s->%s\n",count++,name().data(),usedDir->name().data());
551 addUsesDependency(usedDir,fd,ii->fileDef,FALSE);
560 bool DirDef::isParentOf(DirDef *dir) const
562 if (dir->parent()==this) // this is a parent of dir
564 else if (dir->parent()) // repeat for the parent of dir
565 return isParentOf(dir->parent());
570 bool DirDef::depGraphIsTrivial() const
575 //----------------------------------------------------------------------
577 int FilePairDict::compareItems(GCI item1,GCI item2)
579 FilePair *left = (FilePair*)item1;
580 FilePair *right = (FilePair*)item2;
581 int orderHi = stricmp(left->source()->name(),right->source()->name());
582 int orderLo = stricmp(left->destination()->name(),right->destination()->name());
583 return orderHi==0 ? orderLo : orderHi;
586 //----------------------------------------------------------------------
588 UsedDir::UsedDir(DirDef *dir,bool inherited) :
589 m_dir(dir), m_filePairs(7), m_inherited(inherited)
591 m_filePairs.setAutoDelete(TRUE);
599 void UsedDir::addFileDep(FileDef *srcFd,FileDef *dstFd)
601 m_filePairs.inSort(srcFd->getOutputFileBase()+dstFd->getOutputFileBase(),
602 new FilePair(srcFd,dstFd));
605 FilePair *UsedDir::findFilePair(const char *name)
608 return n.isEmpty() ? 0 : m_filePairs.find(n);
611 DirDef *DirDef::createNewDir(const char *path)
614 DirDef *dir = Doxygen::directories->find(path);
615 if (dir==0) // new dir
617 //printf("Adding new dir %s\n",path);
618 dir = new DirDef(path);
619 //printf("createNewDir %s short=%s\n",path,dir->shortName().data());
620 Doxygen::directories->inSort(path,dir);
625 bool DirDef::matchPath(const QCString &path,QStrList &l)
627 const char *s=l.first();
631 if (stricmp(prefix.left(path.length()),path)==0) // case insensitive compare
640 /*! strip part of \a path if it matches
641 * one of the paths in the Config_getList("STRIP_FROM_PATH") list
643 DirDef *DirDef::mergeDirectoryInTree(const QCString &path)
645 //printf("DirDef::mergeDirectoryInTree(%s)\n",path.data());
648 while ((i=path.find('/',p))!=-1)
650 QCString part=path.left(i+1);
651 if (!matchPath(part,Config_getList("STRIP_FROM_PATH")) && part!="/")
653 dir=createNewDir(part);
660 void DirDef::writeDepGraph(FTextStream &t)
662 writeDotDirDepGraph(t,this);
665 //----------------------------------------------------------------------
667 static void writePartialDirPath(OutputList &ol,const DirDef *root,const DirDef *target)
669 if (target->parent()!=root)
671 writePartialDirPath(ol,root,target->parent());
672 ol.writeString(" / ");
674 ol.writeObjectLink(target->getReference(),target->getOutputFileBase(),0,target->shortName());
677 static void writePartialFilePath(OutputList &ol,const DirDef *root,const FileDef *fd)
679 if (fd->getDirDef() && fd->getDirDef()!=root)
681 writePartialDirPath(ol,root,fd->getDirDef());
682 ol.writeString(" / ");
684 if (fd->isLinkable())
686 ol.writeObjectLink(fd->getReference(),fd->getOutputFileBase(),0,fd->name());
691 ol.docify(fd->name());
696 void DirRelation::writeDocumentation(OutputList &ol)
698 static bool generateTreeView = Config_getBool("GENERATE_TREEVIEW");
699 ol.pushGeneratorState();
700 ol.disableAllBut(OutputGenerator::Html);
702 QCString shortTitle=theTranslator->trDirRelation(
703 m_src->shortName()+" → "+
704 m_dst->dir()->shortName());
705 QCString title=theTranslator->trDirRelation(
706 m_src->displayName()+" -> "+
707 m_dst->dir()->shortName());
708 startFile(ol,getOutputFileBase(),getOutputFileBase(),
709 title,HLI_None,!generateTreeView,m_src->getOutputFileBase());
711 if (!generateTreeView)
713 // write navigation path
714 m_src->writeNavigationPath(ol);
715 ol.endQuickIndices();
719 ol.writeString("<h3>"+shortTitle+"</h3>");
720 ol.writeString("<table class=\"dirtab\">");
721 ol.writeString("<tr class=\"dirtab\">");
722 ol.writeString("<th class=\"dirtab\">");
723 ol.parseText(theTranslator->trFileIn(m_src->pathFragment()));
724 ol.writeString("</th>");
725 ol.writeString("<th class=\"dirtab\">");
726 ol.parseText(theTranslator->trIncludesFileIn(m_dst->dir()->pathFragment()));
727 ol.writeString("</th>");
728 ol.writeString("</tr>");
730 SDict<FilePair>::Iterator fpi(m_dst->filePairs());
732 for (fpi.toFirst();(fp=fpi.current());++fpi)
734 ol.writeString("<tr class=\"dirtab\">");
735 ol.writeString("<td class=\"dirtab\">");
736 writePartialFilePath(ol,m_src,fp->source());
737 ol.writeString("</td>");
738 ol.writeString("<td class=\"dirtab\">");
739 writePartialFilePath(ol,m_dst->dir(),fp->destination());
740 ol.writeString("</td>");
741 ol.writeString("</tr>");
743 ol.writeString("</table>");
747 endFileWithNavPath(m_src,ol);
749 ol.popGeneratorState();
752 //----------------------------------------------------------------------
753 // external functions
755 /** In order to create stable, but unique directory names,
756 * we compute the common part of the path shared by all directories.
758 static void computeCommonDirPrefix()
762 DirSDict::Iterator sdi(*Doxygen::directories);
763 if (Doxygen::directories->count()>0) // we have at least one dir
765 // start will full path of first dir
769 int i=path.findRev('/',path.length()-2);
780 int l = path.length();
782 for (sdi.toFirst();(dir=sdi.current());++sdi)
784 QCString dirName = dir->name();
785 if (dirName.length()>path.length())
787 if (strncmp(dirName,path,l)!=0) // dirName does not start with path
789 int i=path.findRev('/',l-2);
790 if (i==-1) // no unique prefix -> stop
795 else // restart with shorter path
802 else // dir is shorter than path -> take path of dir as new start
805 int i=path.findRev('/',l-2);
806 if (i==-1) // no unique prefix -> stop
811 else // restart with shorter path
819 if (count==Doxygen::directories->count())
820 // path matches for all directories -> found the common prefix
827 for (sdi.toFirst();(dir=sdi.current());++sdi)
829 QCString diskName = dir->name().right(dir->name().length()-path.length());
830 dir->setDiskName(diskName);
831 //printf("set disk name: %s -> %s\n",dir->name().data(),diskName.data());
835 void buildDirectories()
837 // for each input file
838 FileNameListIterator fnli(*Doxygen::inputNameList);
840 for (fnli.toFirst();(fn=fnli.current());++fnli)
842 FileNameIterator fni(*fn);
844 for (;(fd=fni.current());++fni)
846 //printf("buildDirectories %s\n",fd->name().data());
847 if (fd->getReference().isEmpty() && !fd->isDocumentationFile())
850 if ((dir=Doxygen::directories->find(fd->getPath()))==0) // new directory
852 dir = DirDef::mergeDirectoryInTree(fd->getPath());
854 if (dir) dir->addFile(fd);
858 // do something for file imported via tag files.
863 //DirDef *root = new DirDef("root:");
864 // compute relations between directories => introduce container dirs.
866 DirSDict::Iterator sdi(*Doxygen::directories);
867 for (sdi.toFirst();(dir=sdi.current());++sdi)
869 //printf("New dir %s\n",dir->displayName().data());
870 QCString name = dir->name();
871 int i=name.findRev('/',name.length()-2);
874 DirDef *parent = Doxygen::directories->find(name.left(i+1));
875 //if (parent==0) parent=root;
878 parent->addSubDir(dir);
879 //printf("DirDef::addSubdir(): Adding subdir\n%s to\n%s\n",
880 // dir->displayName().data(), parent->displayName().data());
884 computeCommonDirPrefix();
887 void computeDirDependencies()
890 DirSDict::Iterator sdi(*Doxygen::directories);
891 // compute nesting level for each directory
892 for (sdi.toFirst();(dir=sdi.current());++sdi)
896 // compute uses dependencies between directories
897 for (sdi.toFirst();(dir=sdi.current());++sdi)
899 //printf("computeDependencies for %s: #dirs=%d\n",dir->name().data(),Doxygen::directories.count());
900 dir->computeDependencies();
905 void generateDirDocs(OutputList &ol)
908 DirSDict::Iterator sdi(*Doxygen::directories);
909 for (sdi.toFirst();(dir=sdi.current());++sdi)
911 dir->writeDocumentation(ol);
913 if (Config_getBool("DIRECTORY_GRAPH"))
915 SDict<DirRelation>::Iterator rdi(Doxygen::dirRelations);
917 for (rdi.toFirst();(dr=rdi.current());++rdi)
919 dr->writeDocumentation(ol);