00001 /****************************************************************************** 00002 * 00003 * Copyright (C) 1997-2008 by Dimitri van Heesch. 00004 * 00005 * Permission to use, copy, modify, and distribute this software and its 00006 * documentation under the terms of the GNU General Public License is hereby 00007 * granted. No representations are made about the suitability of this software 00008 * for any purpose. It is provided "as is" without express or implied warranty. 00009 * See the GNU General Public License for more details. 00010 * 00011 * Documents produced by Doxygen are derivative works derived from the 00012 * input used in their production; they are not affected by this license. 00013 * 00014 */ 00015 00016 #include "docsets.h" 00017 #include "config.h" 00018 #include "message.h" 00019 #include "doxygen.h" 00020 #include <qfile.h> 00021 00022 00023 DocSets::DocSets() : m_nodes(17), m_scopes(17) 00024 { 00025 m_nf = 0; 00026 m_tf = 0; 00027 m_dc = 0; 00028 m_id = 0; 00029 m_nodes.setAutoDelete(TRUE); 00030 } 00031 00032 DocSets::~DocSets() 00033 { 00034 delete m_nf; 00035 delete m_tf; 00036 } 00037 00038 void DocSets::initialize() 00039 { 00040 // -- get config options 00041 QCString projectName = Config_getString("PROJECT_NAME"); 00042 if (projectName.isEmpty()) projectName="root"; 00043 QCString bundleId = Config_getString("DOCSET_BUNDLE_ID"); 00044 if (bundleId.isEmpty()) bundleId="org.doxygen.Project"; 00045 QCString feedName = Config_getString("DOCSET_FEEDNAME"); 00046 if (feedName.isEmpty()) feedName="FeedName"; 00047 00048 // -- write Makefile 00049 { 00050 QCString mfName = Config_getString("HTML_OUTPUT") + "/Makefile"; 00051 QFile makefile(mfName); 00052 if (!makefile.open(IO_WriteOnly)) 00053 { 00054 err("Could not open file %s for writing\n",mfName.data()); 00055 exit(1); 00056 } 00057 QTextStream ts(&makefile); 00058 ts.setEncoding(QTextStream::UnicodeUTF8); 00059 00060 ts << "DOCSET_NAME=" << bundleId << ".docset\n" 00061 "DOCSET_CONTENTS=$(DOCSET_NAME)/Contents\n" 00062 "DOCSET_RESOURCES=$(DOCSET_CONTENTS)/Resources\n" 00063 "DOCSET_DOCUMENTS=$(DOCSET_RESOURCES)/Documents\n" 00064 "DOCSET_INSTALL=~/Library/Developer/Shared/Documentation/DocSets\n" 00065 "XCODE_INSTALL=$(shell xcode-select -print-path)\n" 00066 "\n" 00067 "all: docset\n" 00068 "\n" 00069 "docset:\n" 00070 "\tmkdir -p $(DOCSET_DOCUMENTS)\n" 00071 "\tcp Nodes.xml $(DOCSET_RESOURCES)\n" 00072 "\tcp Tokens.xml $(DOCSET_RESOURCES)\n" 00073 "\tcp Info.plist $(DOCSET_CONTENTS)\n" 00074 "\ttar --exclude $(DOCSET_NAME) \\\n" 00075 "\t --exclude Nodes.xml \\\n" 00076 "\t --exclude Tokens.xml \\\n" 00077 "\t --exclude Info.plist \\\n" 00078 "\t --exclude Makefile -c -f - . \\\n" 00079 "\t | (cd $(DOCSET_DOCUMENTS); tar xvf -)\n" 00080 "\t$(XCODE_INSTALL)/usr/bin/docsetutil index $(DOCSET_NAME)\n" 00081 "\trm -f $(DOCSET_DOCUMENTS)/Nodes.xml\n" 00082 "\trm -f $(DOCSET_DOCUMENTS)/Info.plist\n" 00083 "\trm -f $(DOCSET_DOCUMENTS)/Makefile\n" 00084 "\trm -f $(DOCSET_RESOURCES)/Nodes.xml\n" 00085 "\trm -f $(DOCSET_RESOURCES)/Tokens.xml\n" 00086 "\n" 00087 "install: docset\n" 00088 "\tmkdir -p $(DOCSET_INSTALL)\n" 00089 "\tcp -R $(DOCSET_NAME) $(DOCSET_INSTALL)\n" 00090 "\n" 00091 "uninstall:\n" 00092 "\trm -rf $(DOCSET_INSTALL)\n" 00093 "\n" 00094 "always:\n"; 00095 } 00096 00097 // -- write Info.plist 00098 { 00099 QCString plName = Config_getString("HTML_OUTPUT") + "/Info.plist"; 00100 QFile plist(plName); 00101 if (!plist.open(IO_WriteOnly)) 00102 { 00103 err("Could not open file %s for writing\n",plName.data()); 00104 exit(1); 00105 } 00106 QTextStream ts(&plist); 00107 ts.setEncoding(QTextStream::UnicodeUTF8); 00108 00109 ts << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" 00110 "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\"\n" 00111 "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n" 00112 "<plist version=\"1.0\">\n" 00113 "<dict>\n" 00114 " <key>CFBundleName</key>\n" 00115 " <string>" << projectName << "</string>\n" 00116 " <key>CFBundleIdentifier</key>\n" 00117 " <string>" << bundleId << ".docset</string>\n" 00118 " <key>DocSetFeedName</key>\n" 00119 " <string>" << feedName << "</string>\n" 00120 "</dict>\n" 00121 "</plist>\n"; 00122 } 00123 00124 // -- start Nodes.xml 00125 QCString notes = Config_getString("HTML_OUTPUT") + "/Nodes.xml"; 00126 m_nf = new QFile(notes); 00127 if (!m_nf->open(IO_WriteOnly)) 00128 { 00129 err("Could not open file %s for writing\n",notes.data()); 00130 exit(1); 00131 } 00132 QCString indexName="index"; 00133 if (Config_getBool("GENERATE_TREEVIEW")) indexName="main"; 00134 m_nts.setDevice(m_nf); 00135 m_nts.setEncoding(QTextStream::UnicodeUTF8); 00136 m_nts << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << endl; 00137 m_nts << "<DocSetNodes version=\"1.0\">" << endl; 00138 m_nts << " <TOC>" << endl; 00139 m_nts << " <Node>" << endl; 00140 m_nts << " <Name>Root</Name>" << endl; 00141 m_nts << " <Path>" << indexName << Doxygen::htmlFileExtension << "</Path>" << endl; 00142 m_nts << " <Subnodes>" << endl; 00143 m_dc = 1; 00144 m_firstNode.resize(m_dc); 00145 m_firstNode.at(0)=TRUE; 00146 00147 QCString tokens = Config_getString("HTML_OUTPUT") + "/Tokens.xml"; 00148 m_tf = new QFile(tokens); 00149 if (!m_tf->open(IO_WriteOnly)) 00150 { 00151 err("Could not open file %s for writing\n",tokens.data()); 00152 exit(1); 00153 } 00154 m_tts.setDevice(m_tf); 00155 m_tts.setEncoding(QTextStream::UnicodeUTF8); 00156 m_tts << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << endl; 00157 m_tts << "<Tokens version=\"1.0\">" << endl; 00158 } 00159 00160 void DocSets::finalize() 00161 { 00162 m_nts << indent() << " </Node>" << endl; 00163 m_dc--; 00164 m_nts << " </Subnodes>" << endl; 00165 m_nts << " </Node>" << endl; 00166 m_nts << " </TOC>" << endl; 00167 m_nts << "</DocSetNodes>" << endl; 00168 m_nf->close(); 00169 delete m_nf; 00170 m_nf=0; 00171 00172 m_tts << "</Tokens>" << endl; 00173 m_tf->close(); 00174 delete m_tf; 00175 m_tf=0; 00176 } 00177 00178 QCString DocSets::indent() 00179 { 00180 QCString result; 00181 result.fill(' ',(m_dc+2)*2); 00182 return result; 00183 } 00184 00185 void DocSets::incContentsDepth() 00186 { 00187 ++m_dc; 00188 m_nts << indent() << "<Subnodes>" << endl; 00189 m_firstNode.resize(m_dc); 00190 if (m_dc>0) 00191 { 00192 m_firstNode.at(m_dc-1)=TRUE; 00193 } 00194 } 00195 00196 void DocSets::decContentsDepth() 00197 { 00198 if (!m_firstNode.at(m_dc-1)) 00199 { 00200 m_nts << indent() << " </Node>" << endl; 00201 } 00202 m_nts << indent() << "</Subnodes>" << endl; 00203 --m_dc; 00204 } 00205 00206 void DocSets::addContentsItem(bool isDir, 00207 const char *name, 00208 const char *ref, 00209 const char *file, 00210 const char *anchor) 00211 { 00212 (void)isDir; 00213 if (file && ref==0) 00214 { 00215 if (!m_firstNode.at(m_dc-1)) 00216 { 00217 m_nts << indent() << " </Node>" << endl; 00218 } 00219 m_firstNode.at(m_dc-1)=FALSE; 00220 m_nts << indent() << " <Node>" << endl; 00221 m_nts << indent() << " <Name>" << convertToXML(name) << "</Name>" << endl; 00222 m_nts << indent() << " <Path>" << file << Doxygen::htmlFileExtension; 00223 if (anchor) 00224 { 00225 m_nts << "#" << anchor; 00226 } 00227 m_nts << "</Path>" << endl; 00228 } 00229 } 00230 00231 void DocSets::addIndexItem(const char *, const char *, 00232 const char *, const char *, 00233 const char *,const MemberDef *md) 00234 { 00235 if (!md->isLinkable()) return; // internal symbol 00236 00237 ClassDef *cd = md->getClassDef(); 00238 NamespaceDef *nd = md->getNamespaceDef(); 00239 FileDef *fd = md->getFileDef(); 00240 00241 // determine language 00242 QCString lang; 00243 SrcLangExt langExt = SrcLangExt_Cpp; 00244 if (fd) langExt = getLanguageFromFileName(fd->name()); 00245 switch (langExt) 00246 { 00247 case SrcLangExt_Cpp: 00248 { 00249 if (md->isObjCMethod()) 00250 lang="occ"; // Objective C/C++ 00251 else if (fd && fd->name().right(2).lower()==".c") 00252 lang="c"; // Plain C 00253 else 00254 lang="cpp"; // C++ 00255 } 00256 break; 00257 case SrcLangExt_ObjC: lang="occ"; break; // Objective C++ 00258 case SrcLangExt_IDL: lang="idl"; break; // IDL 00259 case SrcLangExt_CSharp: lang="csharp"; break; // C# 00260 case SrcLangExt_PHP: lang="php"; break; // PHP4/5 00261 case SrcLangExt_D: lang="d"; break; // D 00262 case SrcLangExt_Java: lang="java"; break; // Java 00263 case SrcLangExt_JS: lang="javascript"; break; // Javascript 00264 case SrcLangExt_Python: lang="python"; break; // Python 00265 case SrcLangExt_F90: lang="fortran"; break; // Fortran 00266 case SrcLangExt_VHDL: lang="vhdl"; break; // VHDL 00267 } 00268 00269 // determine scope 00270 QCString scope; 00271 QCString type; 00272 QCString decl; 00273 Definition *d = 0; 00274 if (fd && fd->isLinkable() && m_scopes.find(fd->getOutputFileBase())==0) 00275 { 00276 writeToken(m_tts,fd,"file",lang,0,0,0); 00277 m_scopes.append(fd->getOutputFileBase(),(void*)0x8); 00278 } 00279 if (cd) 00280 { 00281 scope = cd->qualifiedName(); 00282 if (cd->isTemplate()) 00283 type="tmplt"; 00284 else if (cd->compoundType()==ClassDef::Interface) 00285 type="intf"; 00286 else if (cd->compoundType()==ClassDef::Category) 00287 type="cat"; 00288 else 00289 type = "cl"; 00290 d = cd; 00291 IncludeInfo *ii = cd->includeInfo(); 00292 if (ii) 00293 { 00294 decl=ii->includeName; 00295 if (decl.isEmpty()) 00296 { 00297 decl=ii->local; 00298 } 00299 } 00300 } 00301 else if (nd) 00302 { 00303 scope = nd->name(); 00304 type = "ns"; 00305 d = cd; 00306 } 00307 if (d && d->isLinkable() && m_scopes.find(d->getOutputFileBase())==0) 00308 { 00309 writeToken(m_tts,d,type,lang,0,0,decl); 00310 m_scopes.append(d->getOutputFileBase(),(void*)0x8); 00311 } 00312 00313 switch (md->memberType()) 00314 { 00315 case MemberDef::Define: 00316 type="macro"; break; 00317 case MemberDef::Function: 00318 if (cd && cd->compoundType()==ClassDef::Interface) 00319 type="intfm"; 00320 else if (cd && cd->compoundType()==ClassDef::Class) 00321 type="clm"; 00322 else 00323 type="func"; 00324 break; 00325 case MemberDef::Variable: 00326 type="data"; break; 00327 case MemberDef::Typedef: 00328 type="tdef"; break; 00329 case MemberDef::Enumeration: 00330 type="enum"; break; 00331 case MemberDef::EnumValue: 00332 type="econst"; break; 00333 case MemberDef::Prototype: 00334 type="prototype"; break; 00335 case MemberDef::Signal: 00336 type="signal"; break; 00337 case MemberDef::Slot: 00338 type="slot"; break; 00339 case MemberDef::Friend: 00340 type="ffunc"; break; 00341 case MemberDef::DCOP: 00342 type="dcop"; break; 00343 case MemberDef::Property: 00344 type="property"; break; 00345 case MemberDef::Event: 00346 type="event"; break; 00347 } 00348 writeToken(m_tts,md,type,lang,scope,md->anchor()); 00349 } 00350 00351 void DocSets::writeToken(QTextStream &t, 00352 const Definition *d, 00353 const QCString &type, 00354 const QCString &lang, 00355 const char *scope, 00356 const char *anchor, 00357 const char *decl) 00358 { 00359 t << " <Token>" << endl; 00360 t << " <TokenIdentifier>" << endl; 00361 t << " <Name>" << convertToXML(d->name()) << "</Name>" << endl; 00362 t << " <APILanguage>" << lang << "</APILanguage>" << endl; 00363 t << " <Type>" << type << "</Type>" << endl; 00364 t << " <Scope>" << convertToXML(scope) << "</Scope>" << endl; 00365 t << " </TokenIdentifier>" << endl; 00366 t << " <Path>" << d->getOutputFileBase() 00367 << Doxygen::htmlFileExtension << "</Path>" << endl; 00368 if (anchor) 00369 { 00370 t << " <Anchor>" << anchor << "</Anchor>" << endl; 00371 } 00372 QCString tooltip = d->briefDescriptionAsTooltip(); 00373 if (!tooltip.isEmpty()) 00374 { 00375 t << " <Abstract>" << tooltip << "</Abstract>" << endl; 00376 } 00377 if (decl) 00378 { 00379 t << " <DeclaredIn>" << convertToXML(decl) << "</DeclaredIn>" << endl; 00380 } 00381 t << " </Token>" << endl; 00382 } 00383 00384 void DocSets::addIndexFile(const char *name) 00385 { 00386 (void)name; 00387 } 00388