29b7616f443b3a6a96363f7959cbc056855df1e9
[platform/upstream/doxygen.git] / src / docsets.cpp
1 /******************************************************************************
2  *
3  * Copyright (C) 1997-2015 by Dimitri van Heesch.
4  *
5  * Permission to use, copy, modify, and distribute this software and its
6  * documentation under the terms of the GNU General Public License is hereby 
7  * granted. No representations are made about the suitability of this software 
8  * for any purpose. It is provided "as is" without express or implied warranty.
9  * See the GNU General Public License for more details.
10  *
11  * Documents produced by Doxygen are derivative works derived from the
12  * input used in their production; they are not affected by this license.
13  *
14  */
15
16 #include <qfile.h>
17 #include "docsets.h"
18 #include "config.h"
19 #include "message.h"
20 #include "doxygen.h"
21 #include "groupdef.h"
22 #include "classdef.h"
23 #include "filedef.h"
24 #include "memberdef.h"
25 #include "namespacedef.h"
26 #include "util.h"
27
28 DocSets::DocSets() : m_nodes(17), m_scopes(17)
29 {
30   m_nf = 0;
31   m_tf = 0;
32   m_dc = 0;
33   m_id = 0;
34   m_nodes.setAutoDelete(TRUE);
35 }
36
37 DocSets::~DocSets()
38 {
39   delete m_nf;
40   delete m_tf;
41 }
42
43 void DocSets::initialize()
44 {
45   // -- get config options
46   QCString projectName = Config_getString(PROJECT_NAME);
47   if (projectName.isEmpty()) projectName="root";
48   QCString bundleId = Config_getString(DOCSET_BUNDLE_ID);
49   if (bundleId.isEmpty()) bundleId="org.doxygen.Project";
50   QCString feedName = Config_getString(DOCSET_FEEDNAME);
51   if (feedName.isEmpty()) feedName="FeedName";
52   QCString publisherId = Config_getString(DOCSET_PUBLISHER_ID);
53   if (publisherId.isEmpty()) publisherId="PublisherId";
54   QCString publisherName = Config_getString(DOCSET_PUBLISHER_NAME);
55   if (publisherName.isEmpty()) publisherName="PublisherName";
56   QCString projectNumber = Config_getString(PROJECT_NUMBER);
57   if (projectNumber.isEmpty()) projectNumber="ProjectNumber";
58
59   // -- write Makefile
60   {
61   QCString mfName = Config_getString(HTML_OUTPUT) + "/Makefile";
62   QFile makefile(mfName);
63   if (!makefile.open(IO_WriteOnly))
64   {
65     err("Could not open file %s for writing\n",mfName.data());
66     exit(1);
67   }
68   FTextStream ts(&makefile);
69
70   ts << "DOCSET_NAME=" << bundleId << ".docset\n" 
71         "DOCSET_CONTENTS=$(DOCSET_NAME)/Contents\n"
72         "DOCSET_RESOURCES=$(DOCSET_CONTENTS)/Resources\n"
73         "DOCSET_DOCUMENTS=$(DOCSET_RESOURCES)/Documents\n"
74         "DESTDIR=~/Library/Developer/Shared/Documentation/DocSets\n"
75         "XCODE_INSTALL=\"$(shell xcode-select -print-path)\"\n"
76         "\n"
77         "all: docset\n"
78         "\n"
79         "docset:\n"
80         "\tmkdir -p $(DOCSET_DOCUMENTS)\n"
81         "\tcp Nodes.xml $(DOCSET_RESOURCES)\n"
82         "\tcp Tokens.xml $(DOCSET_RESOURCES)\n"
83         "\tcp Info.plist $(DOCSET_CONTENTS)\n"
84         "\ttar --exclude $(DOCSET_NAME) \\\n"
85         "\t    --exclude Nodes.xml \\\n"
86         "\t    --exclude Tokens.xml \\\n"
87         "\t    --exclude Info.plist \\\n"
88         "\t    --exclude Makefile -c -f - . \\\n"
89         "\t    | (cd $(DOCSET_DOCUMENTS); tar xvf -)\n"
90         "\t$(XCODE_INSTALL)/usr/bin/docsetutil index $(DOCSET_NAME)\n"
91         "\trm -f $(DOCSET_DOCUMENTS)/Nodes.xml\n"
92         "\trm -f $(DOCSET_DOCUMENTS)/Info.plist\n"
93         "\trm -f $(DOCSET_DOCUMENTS)/Makefile\n"
94         "\trm -f $(DOCSET_RESOURCES)/Nodes.xml\n"
95         "\trm -f $(DOCSET_RESOURCES)/Tokens.xml\n"
96         "\n"
97         "clean:\n"
98         "\trm -rf $(DOCSET_NAME)\n"
99         "\n"
100         "install: docset\n"
101         "\tmkdir -p $(DESTDIR)\n"
102         "\tcp -R $(DOCSET_NAME) $(DESTDIR)\n"
103         "\n"
104         "uninstall:\n"
105         "\trm -rf $(DESTDIR)/$(DOCSET_NAME)\n"
106         "\n"
107         "always:\n";
108   }
109
110   // -- write Info.plist
111   {
112   QCString plName = Config_getString(HTML_OUTPUT) + "/Info.plist";
113   QFile plist(plName);
114   if (!plist.open(IO_WriteOnly))
115   {
116     err("Could not open file %s for writing\n",plName.data());
117     exit(1);
118   }
119   FTextStream ts(&plist);
120
121   ts << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" 
122         "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\"\n" 
123         "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n" 
124         "<plist version=\"1.0\">\n" 
125         "<dict>\n" 
126         "     <key>CFBundleName</key>\n" 
127         "     <string>" << projectName << "</string>\n" 
128         "     <key>CFBundleIdentifier</key>\n"
129         "     <string>" << bundleId << "</string>\n" 
130         "     <key>CFBundleVersion</key>\n"
131         "     <string>" << projectNumber << "</string>\n"
132         "     <key>DocSetFeedName</key>\n" 
133         "     <string>" << feedName << "</string>\n"
134         "     <key>DocSetPublisherIdentifier</key>\n"
135         "     <string>" << publisherId << "</string>\n"
136         "     <key>DocSetPublisherName</key>\n"
137         "     <string>" << publisherName << "</string>\n"
138         // markers for Dash
139         "     <key>DashDocSetFamily</key>\n" 
140         "     <string>doxy</string>\n"
141         "     <key>DocSetPlatformFamily</key>\n"
142         "     <string>doxygen</string>\n"
143         "</dict>\n"
144         "</plist>\n";
145   }
146
147   // -- start Nodes.xml
148   QCString notes = Config_getString(HTML_OUTPUT) + "/Nodes.xml";
149   m_nf = new QFile(notes);
150   if (!m_nf->open(IO_WriteOnly))
151   {
152     err("Could not open file %s for writing\n",notes.data());
153     exit(1);
154   }
155   //QCString indexName=Config_getBool(GENERATE_TREEVIEW)?"main":"index";
156   QCString indexName="index";
157   m_nts.setDevice(m_nf);
158   m_nts << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << endl;
159   m_nts << "<DocSetNodes version=\"1.0\">" << endl;
160   m_nts << "  <TOC>" << endl;
161   m_nts << "    <Node>" << endl;
162   m_nts << "      <Name>Root</Name>" << endl;
163   m_nts << "      <Path>" << indexName << Doxygen::htmlFileExtension << "</Path>" << endl;
164   m_nts << "      <Subnodes>" << endl;
165   m_dc = 1;
166   m_firstNode.resize(m_dc);
167   m_firstNode.at(0)=TRUE;
168
169   QCString tokens = Config_getString(HTML_OUTPUT) + "/Tokens.xml";
170   m_tf = new QFile(tokens);
171   if (!m_tf->open(IO_WriteOnly))
172   {
173     err("Could not open file %s for writing\n",tokens.data());
174     exit(1);
175   }
176   m_tts.setDevice(m_tf);
177   m_tts << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << endl;
178   m_tts << "<Tokens version=\"1.0\">" << endl;
179 }
180
181 void DocSets::finalize()
182 {
183   if (!m_firstNode.at(m_dc-1))
184   {
185     m_nts << indent() << " </Node>" << endl;
186   }
187   m_dc--;
188   m_nts << "      </Subnodes>" << endl;
189   m_nts << "    </Node>" << endl;
190   m_nts << "  </TOC>" << endl;
191   m_nts << "</DocSetNodes>" << endl;
192   m_nf->close();
193   delete m_nf;
194   m_nf=0;
195
196   m_tts << "</Tokens>" << endl;
197   m_tf->close();
198   delete m_tf;
199   m_tf=0;
200 }
201
202 QCString DocSets::indent()
203 {
204   QCString result;
205   result.fill(' ',(m_dc+2)*2);
206   return result;
207 }
208
209 void DocSets::incContentsDepth()
210 {
211   //printf("DocSets::incContentsDepth() m_dc=%d\n",m_dc);
212   ++m_dc;
213   m_nts << indent() << "<Subnodes>" << endl;
214   m_firstNode.resize(m_dc);
215   if (m_dc>0)
216   {
217     m_firstNode.at(m_dc-1)=TRUE;
218   }
219 }
220
221 void DocSets::decContentsDepth()
222 {
223   if (!m_firstNode.at(m_dc-1))
224   {
225     m_nts << indent() << " </Node>" << endl;
226   }
227   m_nts << indent() << "</Subnodes>" << endl;
228   --m_dc;
229   //printf("DocSets::decContentsDepth() m_dc=%d\n",m_dc);
230 }
231
232 void DocSets::addContentsItem(bool isDir,
233                               const char *name, 
234                               const char *ref, 
235                               const char *file,
236                               const char *anchor,
237                               bool /* separateIndex */,
238                               bool /* addToNavIndex */,
239                               Definition * /*def*/)
240 {
241   (void)isDir;
242   //printf("DocSets::addContentsItem(%s) m_dc=%d\n",name,m_dc);
243   if (ref==0)
244   {
245     if (!m_firstNode.at(m_dc-1))
246     {
247       m_nts << indent() << " </Node>" << endl;
248     }
249     m_firstNode.at(m_dc-1)=FALSE;
250     m_nts << indent() << " <Node>" << endl;
251     m_nts << indent() << "  <Name>" << convertToXML(name) << "</Name>" << endl;
252     if (file && file[0]=='^') // URL marker
253     {
254       m_nts << indent() << "  <URL>" << convertToXML(&file[1]) 
255             << "</URL>" << endl;
256     }
257     else // relative file
258     {
259       m_nts << indent() << "  <Path>";
260       if (file && file[0]=='!') // user specified file
261       {
262         m_nts << convertToXML(&file[1]);
263       }
264       else if (file) // doxygen generated file
265       {
266         m_nts << file << Doxygen::htmlFileExtension;
267       }
268       m_nts << "</Path>" << endl;
269       if (file && anchor)
270       {
271         m_nts << indent() << "  <Anchor>" << anchor << "</Anchor>" << endl;
272       }
273     }
274   }
275 }
276
277 void DocSets::addIndexItem(Definition *context,MemberDef *md,
278                            const char *,const char *)
279 {
280   if (md==0 && context==0) return;
281
282   FileDef *fd      = 0;
283   ClassDef *cd     = 0;
284   NamespaceDef *nd = 0;
285
286   if (md)
287   {
288     fd = md->getFileDef();
289     cd = md->getClassDef();
290     nd = md->getNamespaceDef();
291     if (!md->isLinkable()) return; // internal symbol
292   }
293
294   QCString scope;
295   QCString type;
296   QCString decl;
297
298   // determine language
299   QCString lang;
300   SrcLangExt langExt = SrcLangExt_Cpp;
301   if (md) 
302   {
303     langExt = md->getLanguage();
304   }
305   else if (context) 
306   {
307     langExt = context->getLanguage();
308   }
309   switch (langExt)
310   {
311     case SrcLangExt_Cpp:
312     case SrcLangExt_ObjC:
313       {
314         if (md && (md->isObjCMethod() || md->isObjCProperty()))
315           lang="occ";  // Objective C/C++
316         else if (fd && fd->name().right(2).lower()==".c") 
317           lang="c";    // Plain C
318         else if (cd==0 && nd==0)
319           lang="c";    // Plain C symbol outside any class or namespace
320         else
321           lang="cpp";  // C++
322       }
323       break;
324     case SrcLangExt_IDL:     lang="idl"; break;        // IDL
325     case SrcLangExt_CSharp:  lang="csharp"; break;     // C#
326     case SrcLangExt_PHP:     lang="php"; break;        // PHP4/5
327     case SrcLangExt_D:       lang="d"; break;          // D
328     case SrcLangExt_Java:    lang="java"; break;       // Java
329     case SrcLangExt_JS:      lang="javascript"; break; // Javascript
330     case SrcLangExt_Python:  lang="python"; break;     // Python
331     case SrcLangExt_Fortran: lang="fortran"; break;    // Fortran
332     case SrcLangExt_VHDL:    lang="vhdl"; break;       // VHDL
333     case SrcLangExt_XML:     lang="xml"; break;        // DBUS XML
334     case SrcLangExt_SQL:     lang="sql"; break;        // Sql
335     case SrcLangExt_Tcl:     lang="tcl"; break;        // Tcl
336     case SrcLangExt_Markdown:lang="markdown"; break;   // Markdown
337     case SrcLangExt_Unknown: lang="unknown"; break;    // should not happen!
338   }
339
340   if (md)
341   {
342     if (context==0)
343     {
344       if (md->getGroupDef())
345         context = md->getGroupDef();
346       else if (md->getFileDef())
347         context = md->getFileDef();
348     }
349     if (context==0) return; // should not happen
350
351     switch (md->memberType())
352     {
353       case MemberType_Define:
354         type="macro"; break;
355       case MemberType_Function:
356         if (cd && (cd->compoundType()==ClassDef::Interface ||
357               cd->compoundType()==ClassDef::Class))
358         {
359           if (md->isStatic())
360             type="clm";         // class member
361           else
362             type="instm";       // instance member
363         }
364         else if (cd && cd->compoundType()==ClassDef::Protocol)
365         {
366           if (md->isStatic())
367             type="intfcm";     // interface class member
368           else
369             type="intfm";      // interface member
370         }
371         else
372           type="func";
373         break;
374       case MemberType_Variable:
375         type="data"; break;
376       case MemberType_Typedef:
377         type="tdef"; break;
378       case MemberType_Enumeration:
379         type="enum"; break;
380       case MemberType_EnumValue:
381         type="econst"; break;
382         //case MemberDef::Prototype:
383         //  type="prototype"; break;
384       case MemberType_Signal:
385         type="signal"; break;
386       case MemberType_Slot:
387         type="slot"; break;
388       case MemberType_Friend:
389         type="ffunc"; break;
390       case MemberType_DCOP:
391         type="dcop"; break;
392       case MemberType_Property:
393         if (cd && cd->compoundType()==ClassDef::Protocol) 
394           type="intfp";         // interface property
395         else 
396           type="instp";         // instance property
397         break;
398       case MemberType_Event:
399         type="event"; break;
400       case MemberType_Interface:
401         type="ifc"; break;
402       case MemberType_Service:
403         type="svc"; break;
404     }
405     cd = md->getClassDef();
406     nd = md->getNamespaceDef();
407     if (cd) 
408     {
409       scope = cd->qualifiedName();
410     }
411     else if (nd)
412     {
413       scope = nd->name();
414     }
415     MemberDef *declMd = md->memberDeclaration();
416     if (declMd==0) declMd = md;
417     {
418       fd = md->getFileDef();
419       if (fd)
420       {
421         decl = fd->name();
422       }
423     }
424     writeToken(m_tts,md,type,lang,scope,md->anchor(),decl);
425   }
426   else if (context && context->isLinkable())
427   {
428     if (fd==0 && context->definitionType()==Definition::TypeFile)
429     {
430       fd = (FileDef*)context;
431     }
432     if (cd==0 && context->definitionType()==Definition::TypeClass)
433     {
434       cd = (ClassDef*)context;
435     }
436     if (nd==0 && context->definitionType()==Definition::TypeNamespace)
437     {
438       nd = (NamespaceDef*)context;
439     }
440     if (fd)
441     {
442       type="file";
443     }
444     else if (cd) 
445     {
446       scope = cd->qualifiedName();
447       if (cd->isTemplate())
448       {
449         type="tmplt";
450       }
451       else if (cd->compoundType()==ClassDef::Protocol) 
452       {
453         type="intf";
454         if (scope.right(2)=="-p") scope=scope.left(scope.length()-2);
455       }
456       else if (cd->compoundType()==ClassDef::Interface)
457       {
458         type="cl";
459       }
460       else if (cd->compoundType()==ClassDef::Category)
461       {
462         type="cat";
463       }
464       else 
465       {
466         type = "cl";
467       }
468       IncludeInfo *ii = cd->includeInfo();
469       if (ii)
470       {
471         decl=ii->includeName;
472       }
473     }
474     else if (nd)
475     {
476       scope = nd->name();
477       type = "ns";
478     }
479     if (m_scopes.find(context->getOutputFileBase())==0)
480     {
481       writeToken(m_tts,context,type,lang,scope,0,decl);
482       m_scopes.append(context->getOutputFileBase(),(void*)0x8);
483     }
484   }
485 }
486
487 void DocSets::writeToken(FTextStream &t,
488                          const Definition *d,
489                          const QCString &type,
490                          const QCString &lang,
491                          const char *scope,
492                          const char *anchor,
493                          const char *decl)
494 {
495   t << "  <Token>" << endl;
496   t << "    <TokenIdentifier>" << endl;
497   QCString name = d->name();
498   if (name.right(2)=="-p")  name=name.left(name.length()-2);
499   t << "      <Name>" << convertToXML(name) << "</Name>" << endl;
500   if (!lang.isEmpty())
501   {
502     t << "      <APILanguage>" << lang << "</APILanguage>" << endl;
503   }
504   if (!type.isEmpty())
505   {
506     t << "      <Type>" << type << "</Type>" << endl;
507   }
508   if (scope)
509   {
510     t << "      <Scope>" << convertToXML(scope) << "</Scope>" << endl;
511   }
512   t << "    </TokenIdentifier>" << endl;
513   t << "    <Path>" << d->getOutputFileBase() 
514                     << Doxygen::htmlFileExtension << "</Path>" << endl;
515   if (anchor)
516   {
517     t << "    <Anchor>" << anchor << "</Anchor>" << endl;
518   }
519   QCString tooltip = d->briefDescriptionAsTooltip();
520   if (!tooltip.isEmpty())
521   {
522     t << "    <Abstract>" << convertToXML(tooltip) << "</Abstract>" << endl;
523   }
524   if (decl)
525   {
526     t << "    <DeclaredIn>" << convertToXML(decl) << "</DeclaredIn>" << endl;
527   }
528   t << "  </Token>" << endl;
529 }
530
531 void DocSets::addIndexFile(const char *name)
532 {
533   (void)name;
534 }
535