]> git.saurik.com Git - wxWidgets.git/blob - utils/HelpGen/src/HelpGen.cpp
documented wxSpinCtrl
[wxWidgets.git] / utils / HelpGen / src / HelpGen.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: HelpGen.cpp
3 // Purpose: Main program file for HelpGen
4 // Author: Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
5 // Modified by:
6 // Created: 06/01/99
7 // RCS-ID: $Id$
8 // Copyright: (c) 1999 VZ
9 // Licence: GPL
10 /////////////////////////////////////////////////////////////////////////////
11
12 /*
13 BUGS
14
15 1. wx/string.h confuses C++ parser terribly
16 2. C++ parser doesn't know about virtual functions, nor static ones
17 3. param checking is not done for vararg functions
18 4. type comparison is dumb: it doesn't know that "char *" is the same
19 that "char []" nor that "const char *" is the same as "char const *"
20
21 TODO (+ means fixed), see also the change log at the end of the file.
22
23 (i) small fixes in the current version
24
25 +1. Quote special TeX characters like '&' and '_' (=> derive from wxFile)
26 2. Document typedefs
27 3. Document global variables
28 4. Document #defines
29 +5. Program options
30 6. Include file name/line number in the "diff" messages?
31 +7. Support for vararg functions
32
33 (ii) plans for version 2
34 1. Use wxTextFile for direct file access to avoid one scan method problems
35 2. Use command line parser class for the options
36 3. support for overloaded functions in diff mode (search for OVER)
37
38 (iii) plans for version 3
39 1. Merging with existing files
40 2. GUI
41 */
42
43 // =============================================================================
44 // declarations
45 // =============================================================================
46
47 // -----------------------------------------------------------------------------
48 // headers
49 // -----------------------------------------------------------------------------
50
51 // wxWindows
52 #include "wx/wxprec.h"
53
54 #ifndef WX_PRECOMP
55 #include <wx/string.h>
56 #include <wx/log.h>
57 #include <wx/dynarray.h>
58 #include <wx/wx.h>
59 #endif // WX_PRECOMP
60
61 #include <wx/file.h>
62
63 // C++ parsing classes
64 #include "cjparser.h"
65
66 // standard headers
67 #include <stdio.h>
68 #include <time.h>
69
70 // -----------------------------------------------------------------------------
71 // global vars
72 // -----------------------------------------------------------------------------
73
74 // just a copy of argv
75 static char **g_argv = NULL;
76
77 class HelpGenApp: public wxApp
78 {
79 public:
80 HelpGenApp() {};
81
82 #if wxUSE_GUI
83 bool OnInit();
84 #else
85 int OnRun();
86 #endif
87 };
88
89 IMPLEMENT_APP(HelpGenApp);
90
91 // -----------------------------------------------------------------------------
92 // private functions
93 // -----------------------------------------------------------------------------
94
95 // return the label for the given function name (i.e. argument of \label)
96 static wxString MakeLabel(const char *classname, const char *funcname = NULL);
97
98 // return the whole \helpref{arg}{arg_label} string
99 static wxString MakeHelpref(const char *argument);
100
101 // [un]quote special TeX characters (in place)
102 static void TeXFilter(wxString* str);
103 static void TeXUnfilter(wxString* str); // also trims spaces
104
105 // get all comments associated with this context
106 static wxString GetAllComments(const spContext& ctx);
107
108 // get the string with current time (returns pointer to static buffer)
109 // timeFormat is used for the call of strftime(3)
110 #ifdef GetCurrentTime
111 #undef GetCurrentTime
112 #endif
113
114 static const char *GetCurrentTime(const char *timeFormat);
115
116 // -----------------------------------------------------------------------------
117 // private classes
118 // -----------------------------------------------------------------------------
119
120 // add a function which sanitazes the string before writing it to the file
121 class wxTeXFile : public wxFile
122 {
123 public:
124 wxTeXFile() { }
125
126 bool WriteTeX(const wxString& s)
127 {
128 wxString t(s);
129 TeXFilter(&t);
130
131 return wxFile::Write(t);
132 }
133
134 private:
135 wxTeXFile(const wxTeXFile&);
136 wxTeXFile& operator=(const wxTeXFile&);
137 };
138
139 // helper class which manages the classes and function names to ignore for
140 // the documentation purposes (used by both HelpGenVisitor and DocManager)
141 class IgnoreNamesHandler
142 {
143 public:
144 IgnoreNamesHandler() : m_ignore(CompareIgnoreListEntries) { }
145 ~IgnoreNamesHandler() { WX_CLEAR_ARRAY(m_ignore); }
146
147 // load file with classes/functions to ignore (add them to the names we
148 // already have)
149 bool AddNamesFromFile(const wxString& filename);
150
151 // return TRUE if we ignore this function
152 bool IgnoreMethod(const wxString& classname,
153 const wxString& funcname) const
154 {
155 if ( IgnoreClass(classname) )
156 return TRUE;
157
158 IgnoreListEntry ignore(classname, funcname);
159
160 return m_ignore.Index(&ignore) != wxNOT_FOUND;
161 }
162
163 // return TRUE if we ignore this class entirely
164 bool IgnoreClass(const wxString& classname) const
165 {
166 IgnoreListEntry ignore(classname, "");
167
168 return m_ignore.Index(&ignore) != wxNOT_FOUND;
169 }
170
171 protected:
172 struct IgnoreListEntry
173 {
174 IgnoreListEntry(const wxString& classname,
175 const wxString& funcname)
176 : m_classname(classname), m_funcname(funcname)
177 {
178 }
179
180 wxString m_classname;
181 wxString m_funcname; // if empty, ignore class entirely
182 };
183
184 static int CompareIgnoreListEntries(IgnoreListEntry *first,
185 IgnoreListEntry *second);
186
187 // for efficiency, let's sort it
188 WX_DEFINE_SORTED_ARRAY(IgnoreListEntry *, ArrayNamesToIgnore);
189
190 ArrayNamesToIgnore m_ignore;
191
192 private:
193 IgnoreNamesHandler(const IgnoreNamesHandler&);
194 IgnoreNamesHandler& operator=(const IgnoreNamesHandler&);
195 };
196
197 // visitor implementation which writes all collected data to a .tex file
198 class HelpGenVisitor : public spVisitor
199 {
200 public:
201 // ctor
202 HelpGenVisitor(const wxString& directoryOut, bool overwrite);
203
204 virtual void VisitFile( spFile& fl );
205 virtual void VisitClass( spClass& cl );
206 virtual void VisitEnumeration( spEnumeration& en );
207 virtual void VisitTypeDef( spTypeDef& td );
208 virtual void VisitPreprocessorLine( spPreprocessorLine& pd );
209 virtual void VisitAttribute( spAttribute& attr );
210 virtual void VisitOperation( spOperation& op );
211 virtual void VisitParameter( spParameter& param );
212
213 void EndVisit();
214
215 // get our `ignore' object
216 IgnoreNamesHandler& GetIgnoreHandler() { return m_ignoreNames; }
217
218 // shut up g++ warning (ain't it stupid?)
219 virtual ~HelpGenVisitor() { }
220
221 protected:
222 // (re)initialize the state
223 void Reset();
224
225 // insert documentation for enums/typedefs coming immediately before the
226 // class declaration into the class documentation
227 void InsertTypedefDocs();
228 void InsertEnumDocs();
229
230 // write the headers for corresponding sections (only once)
231 void InsertDataStructuresHeader();
232 void InsertMethodsHeader();
233
234 // terminate the function documentation if it was started
235 void CloseFunction();
236
237 wxString m_directoryOut, // directory for the output
238 m_fileHeader; // name of the .h file we parse
239 bool m_overwrite; // overwrite existing files?
240 wxTeXFile m_file; // file we're writing to now
241
242 // state variables
243 bool m_inClass, // TRUE after file successfully opened
244 m_inTypesSection, // enums & typedefs go there
245 m_inMethodSection, // functions go here
246 m_isFirstParam, // first parameter of current function?
247 m_inFunction; // we're parsing a function declaration
248
249 // holders for "saved" documentation
250 wxString m_textStoredEnums,
251 m_textStoredTypedefs,
252 m_textStoredFunctionComment;
253
254 // headers included by this file
255 wxArrayString m_headers;
256
257 // ignore handler: tells us which classes to ignore for doc generation
258 // purposes
259 IgnoreNamesHandler m_ignoreNames;
260
261 private:
262 HelpGenVisitor(const HelpGenVisitor&);
263 HelpGenVisitor& operator=(const HelpGenVisitor&);
264 };
265
266 // documentation manager - a class which parses TeX files and remembers the
267 // functions documented in them and can later compare them with all functions
268 // found under ctxTop by C++ parser
269 class DocManager
270 {
271 public:
272 DocManager(bool checkParamNames);
273 ~DocManager();
274
275 // returns FALSE on failure
276 bool ParseTeXFile(const wxString& filename);
277
278 // returns FALSE if there were any differences
279 bool DumpDifferences(spContext *ctxTop) const;
280
281 // get our `ignore' object
282 IgnoreNamesHandler& GetIgnoreHandler() { return m_ignoreNames; }
283
284 protected:
285 // parsing TeX files
286 // -----------------
287
288 // returns the length of 'match' if the string 'str' starts with it or 0
289 // otherwise
290 static size_t TryMatch(const char *str, const char *match);
291
292 // skip spaces: returns pointer to first non space character (also
293 // updates the value of m_line)
294 const char *SkipSpaces(const char *p)
295 {
296 while ( isspace(*p) ) {
297 if ( *p++ == '\n' )
298 m_line++;
299 }
300
301 return p;
302 }
303
304 // skips characters until the next 'c' in '*pp' unless it ends before in
305 // which case FALSE is returned and pp points to '\0', otherwise TRUE is
306 // returned and pp points to 'c'
307 bool SkipUntil(const char **pp, char c);
308
309 // the same as SkipUntil() but only spaces are skipped: on first non space
310 // character different from 'c' the function stops and returns FALSE
311 bool SkipSpaceUntil(const char **pp, char c);
312
313 // extract the string between {} and modify '*pp' to point at the
314 // character immediately after the closing '}'. The returned string is empty
315 // on error.
316 wxString ExtractStringBetweenBraces(const char **pp);
317
318 // the current file and line while we're in ParseTeXFile (for error
319 // messages)
320 wxString m_filename;
321 size_t m_line;
322
323 // functions and classes to ignore during diff
324 // -------------------------------------------
325
326 IgnoreNamesHandler m_ignoreNames;
327
328 // information about all functions documented in the TeX file(s)
329 // -------------------------------------------------------------
330
331 // info about a type: for now stored as text string, but must be parsed
332 // further later (to know that "char *" == "char []" - TODO)
333 class TypeInfo
334 {
335 public:
336 TypeInfo(const wxString& type) : m_type(type) { }
337
338 bool operator==(const wxString& type) const { return m_type == type; }
339 bool operator!=(const wxString& type) const { return m_type != type; }
340
341 const wxString& GetName() const { return m_type; }
342
343 private:
344 wxString m_type;
345 };
346
347 // info abotu a function parameter
348 class ParamInfo
349 {
350 public:
351 ParamInfo(const wxString& type,
352 const wxString& name,
353 const wxString& value)
354 : m_type(type), m_name(name), m_value(value)
355 {
356 }
357
358 const TypeInfo& GetType() const { return m_type; }
359 const wxString& GetName() const { return m_name; }
360 const wxString& GetDefValue() const { return m_value; }
361
362 private:
363 TypeInfo m_type; // type of parameter
364 wxString m_name; // name
365 wxString m_value; // default value
366 };
367
368 WX_DEFINE_ARRAY(ParamInfo *, ArrayParamInfo);
369
370 // info about a function
371 struct MethodInfo
372 {
373 public:
374 enum MethodFlags
375 {
376 Const = 0x0001,
377 Virtual = 0x0002,
378 Pure = 0x0004,
379 Static = 0x0008,
380 Vararg = 0x0010
381 };
382
383 MethodInfo(const wxString& type,
384 const wxString& name,
385 const ArrayParamInfo& params)
386 : m_typeRet(type), m_name(name), m_params(params)
387 {
388 m_flags = 0;
389 }
390
391 void SetFlag(MethodFlags flag) { m_flags |= flag; }
392
393 const TypeInfo& GetType() const { return m_typeRet; }
394 const wxString& GetName() const { return m_name; }
395 const ParamInfo& GetParam(size_t n) const { return *(m_params[n]); }
396 size_t GetParamCount() const { return m_params.GetCount(); }
397
398 bool HasFlag(MethodFlags flag) const { return (m_flags & flag) != 0; }
399
400 ~MethodInfo() { WX_CLEAR_ARRAY(m_params); }
401
402 private:
403 TypeInfo m_typeRet; // return type
404 wxString m_name;
405 int m_flags; // bit mask of the value from the enum above
406
407 ArrayParamInfo m_params;
408 };
409
410 WX_DEFINE_ARRAY(MethodInfo *, ArrayMethodInfo);
411 WX_DEFINE_ARRAY(ArrayMethodInfo *, ArrayMethodInfos);
412
413 // first array contains the names of all classes we found, the second has a
414 // pointer to the array of methods of the given class at the same index as
415 // the class name appears in m_classes
416 wxArrayString m_classes;
417 ArrayMethodInfos m_methods;
418
419 // are we checking parameter names?
420 bool m_checkParamNames;
421
422 private:
423 DocManager(const DocManager&);
424 DocManager& operator=(const DocManager&);
425 };
426
427 // -----------------------------------------------------------------------------
428 // private functions
429 // -----------------------------------------------------------------------------
430
431 // =============================================================================
432 // implementation
433 // =============================================================================
434
435 // this function never returns
436 static void usage()
437 {
438 wxString prog = g_argv[0];
439 wxString basename = prog.BeforeLast('/');
440 #ifdef __WXMSW__
441 if ( !basename )
442 basename = prog.BeforeLast('\\');
443 #endif
444 if ( !basename )
445 basename = prog;
446
447 wxLogError(
448 "usage: %s [global options] <mode> [mode options] <files...>\n"
449 "\n"
450 " where global options are:\n"
451 " -q be quiet\n"
452 " -v be verbose\n"
453 " -H give this usage message\n"
454 " -V print the version info\n"
455 " -i file file with classes/function to ignore\n"
456 "\n"
457 " where mode is one of: dump, diff\n"
458 "\n"
459 " dump means generate .tex files for TeX2RTF converter from specified\n"
460 " headers files, mode options are:\n"
461 " -f overwrite existing files\n"
462 " -o outdir directory for generated files\n"
463 "\n"
464 " diff means compare the set of methods documented .tex file with the\n"
465 " methods declared in the header:\n"
466 " %s diff <file.h> <files.tex...>.\n"
467 " mode specific options are:\n"
468 " -p do check parameter names (not done by default)\n"
469 "\n", basename.c_str(), basename.c_str());
470
471 exit(1);
472 }
473
474 /*
475 int main(int argc, char **argv)
476 {
477 */
478
479 #if wxUSE_GUI
480 bool HelpGenApp::OnInit()
481 #else
482 int HelpGenApp::OnRun()
483 #endif
484 {
485 enum
486 {
487 Mode_None,
488 Mode_Dump,
489 Mode_Diff
490 } mode = Mode_None;
491
492 g_argv = argv;
493
494 if ( argc < 2 ) {
495 usage();
496 }
497
498 wxArrayString filesH, filesTeX;
499 wxString directoryOut, // directory for 'dmup' output
500 ignoreFile; // file with classes/functions to ignore
501 bool overwrite = FALSE, // overwrite existing files during 'dump'?
502 paramNames = FALSE; // check param names during 'diff'?
503
504 for ( int current = 1; current < argc ; current++ ) {
505 // all options have one letter
506 if ( argv[current][0] == '-' ) {
507 if ( argv[current][2] == '\0' ) {
508 switch ( argv[current][1] ) {
509 case 'v':
510 // be verbose
511 wxLog::GetActiveTarget()->SetVerbose();
512 continue;
513
514 case 'q':
515 // be quiet
516 wxLog::GetActiveTarget()->SetVerbose(FALSE);
517 continue;
518
519 case 'H':
520 // help requested
521 usage();
522
523 case 'i':
524 current++;
525 if ( current >= argc ) {
526 wxLogError("-i option requires an argument.");
527
528 break;
529 }
530
531 ignoreFile = argv[current];
532 continue;
533
534 case 'p':
535 if ( mode != Mode_Diff ) {
536 wxLogError("-p is only valid with diff.");
537
538 break;
539 }
540
541 paramNames = TRUE;
542 continue;
543
544 case 'f':
545 if ( mode != Mode_Dump ) {
546 wxLogError("-f is only valid with dump.");
547
548 break;
549 }
550
551 overwrite = TRUE;
552 continue;
553
554 case 'o':
555 if ( mode != Mode_Dump ) {
556 wxLogError("-o is only valid with dump.");
557
558 break;
559 }
560
561 current++;
562 if ( current >= argc ) {
563 wxLogError("-o option requires an argument.");
564
565 break;
566 }
567
568 directoryOut = argv[current];
569 if ( !!directoryOut ) {
570 // terminate with a '/' if it doesn't have it
571 switch ( directoryOut.Last() ) {
572 case '/':
573 #ifdef __WXMSW__
574 case '\\':
575 #endif
576 break;
577
578 default:
579 directoryOut += '/';
580 }
581 }
582 //else: it's empty, do nothing
583
584 continue;
585
586 default:
587 wxLogError("unknown option '%s'", argv[current]);
588 break;
589 }
590 }
591 else {
592 wxLogError("only one letter options are allowed, not '%s'.",
593 argv[current]);
594 }
595
596 // only get here after a break from switch or from else branch of if
597
598 usage();
599 }
600 else {
601 if ( mode == Mode_None ) {
602 if ( strcmp(argv[current], "diff") == 0 )
603 mode = Mode_Diff;
604 else if ( strcmp(argv[current], "dump") == 0 )
605 mode = Mode_Dump;
606 else {
607 wxLogError("unknown mode '%s'.", argv[current]);
608
609 usage();
610 }
611 }
612 else {
613 if ( mode == Mode_Dump || filesH.IsEmpty() ) {
614 filesH.Add(argv[current]);
615 }
616 else {
617 // 2nd files and further are TeX files in diff mode
618 wxASSERT( mode == Mode_Diff );
619
620 filesTeX.Add(argv[current]);
621 }
622 }
623 }
624 }
625
626 // create a parser object and a visitor derivation
627 CJSourceParser parser;
628 HelpGenVisitor visitor(directoryOut, overwrite);
629 if ( !!ignoreFile && mode == Mode_Dump )
630 visitor.GetIgnoreHandler().AddNamesFromFile(ignoreFile);
631
632 spContext *ctxTop = NULL;
633
634 // parse all header files
635 size_t nFiles = filesH.GetCount();
636 for ( size_t n = 0; n < nFiles; n++ ) {
637 wxString header = filesH[n];
638 ctxTop = parser.ParseFile(header);
639 if ( !ctxTop ) {
640 wxLogWarning("Header file '%s' couldn't be processed.",
641 header.c_str());
642 }
643 else if ( mode == Mode_Dump ) {
644 ((spFile *)ctxTop)->mFileName = header;
645 visitor.VisitAll(*ctxTop);
646 visitor.EndVisit();
647 }
648
649 #ifdef __WXDEBUG__
650 if ( 0 && ctxTop )
651 ctxTop->Dump("");
652 #endif // __WXDEBUG__
653 }
654
655 // parse all TeX files
656 if ( mode == Mode_Diff ) {
657 if ( !ctxTop ) {
658 wxLogError("Can't complete diff.");
659
660 // failure
661 return false;
662 }
663
664 DocManager docman(paramNames);
665
666 size_t nFiles = filesTeX.GetCount();
667 for ( size_t n = 0; n < nFiles; n++ ) {
668 wxString file = filesTeX[n];
669 if ( !docman.ParseTeXFile(file) ) {
670 wxLogWarning("TeX file '%s' couldn't be processed.",
671 file.c_str());
672 }
673 }
674
675 if ( !!ignoreFile )
676 docman.GetIgnoreHandler().AddNamesFromFile(ignoreFile);
677
678 docman.DumpDifferences(ctxTop);
679 }
680
681 return false;
682 }
683
684 // -----------------------------------------------------------------------------
685 // HelpGenVisitor implementation
686 // -----------------------------------------------------------------------------
687
688 HelpGenVisitor::HelpGenVisitor(const wxString& directoryOut,
689 bool overwrite)
690 : m_directoryOut(directoryOut)
691 {
692 m_overwrite = overwrite;
693
694 Reset();
695 }
696
697 void HelpGenVisitor::Reset()
698 {
699 m_inClass =
700 m_inFunction =
701 m_inTypesSection =
702 m_inMethodSection = FALSE;
703
704 m_textStoredTypedefs =
705 m_textStoredEnums =
706 m_textStoredFunctionComment = "";
707 m_headers.Empty();
708 }
709
710 void HelpGenVisitor::InsertTypedefDocs()
711 {
712 m_file.WriteTeX(m_textStoredTypedefs);
713 m_textStoredTypedefs.Empty();
714 }
715
716 void HelpGenVisitor::InsertEnumDocs()
717 {
718 m_file.WriteTeX(m_textStoredEnums);
719 m_textStoredEnums.Empty();
720 }
721
722 void HelpGenVisitor::InsertDataStructuresHeader()
723 {
724 if ( !m_inTypesSection ) {
725 m_inTypesSection = TRUE;
726
727 m_file.WriteTeX("\\wxheading{Data structures}\n\n");
728 }
729 }
730
731 void HelpGenVisitor::InsertMethodsHeader()
732 {
733 if ( !m_inMethodSection ) {
734 m_inMethodSection = TRUE;
735
736 m_file.WriteTeX( "\\latexignore{\\rtfignore{\\wxheading{Members}}}\n\n");
737 }
738 }
739
740 void HelpGenVisitor::CloseFunction()
741 {
742 if ( m_inFunction ) {
743 m_inFunction = FALSE;
744
745 wxString totalText;
746 if ( m_isFirstParam ) {
747 // no params found
748 totalText << "\\void";
749 }
750
751 totalText << "}\n\n";
752
753 if ( !m_textStoredFunctionComment.IsEmpty() )
754 totalText << m_textStoredFunctionComment << '\n';
755
756 m_file.WriteTeX(totalText);
757 }
758 }
759
760 void HelpGenVisitor::EndVisit()
761 {
762 CloseFunction();
763
764 m_fileHeader.Empty();
765
766 wxLogVerbose("%s: finished generating for the current file.",
767 GetCurrentTime("%H:%M:%S"));
768 }
769
770 void HelpGenVisitor::VisitFile( spFile& file )
771 {
772 m_fileHeader = file.mFileName;
773 wxLogVerbose("%s: started generating docs for classes from file '%s'...",
774 GetCurrentTime("%H:%M:%S"), m_fileHeader.c_str());
775 }
776
777 void HelpGenVisitor::VisitClass( spClass& cl )
778 {
779 m_inClass = FALSE; // will be left FALSE on error
780
781 wxString name = cl.GetName();
782
783 if ( m_ignoreNames.IgnoreClass(name) ) {
784 wxLogVerbose("Skipping ignored class '%s'.", name.c_str());
785
786 return;
787 }
788
789 // the file name is built from the class name by removing the leading "wx"
790 // if any and converting it to the lower case
791 wxString filename = m_directoryOut;
792 if ( name(0, 2) == "wx" ) {
793 filename << name.c_str() + 2;
794 }
795 else {
796 filename << name;
797 }
798
799 filename.MakeLower();
800 filename += ".tex";
801
802 if ( !m_overwrite && wxFile::Exists(filename) ) {
803 wxLogError("Won't overwrite existing file '%s' - please use '-f'.",
804 filename.c_str());
805
806 return;
807 }
808
809 m_inClass = m_file.Open(filename, wxFile::write);
810 if ( !m_inClass ) {
811 wxLogError("Can't generate documentation for the class '%s'.",
812 name.c_str());
813
814 return;
815 }
816
817 m_inMethodSection =
818 m_inTypesSection = FALSE;
819
820 wxLogInfo("Created new file '%s' for class '%s'.",
821 filename.c_str(), name.c_str());
822
823 // the entire text we're writing to file
824 wxString totalText;
825
826 // write out the header
827 {
828 wxString header;
829 header.Printf("%%\n"
830 "%% automatically generated by HelpGen from\n"
831 "%% %s at %s\n"
832 "%%\n"
833 "\n"
834 "\n"
835 "\\section{\\class{%s}}\\label{%s}\n",
836 m_fileHeader.c_str(), GetCurrentTime("%d/%b/%y %H:%M:%S"),
837 name.c_str(), wxString(name).MakeLower().c_str());
838
839 totalText << header << '\n';
840 }
841
842 // if the header includes other headers they must be related to it... try to
843 // automatically generate the "See also" clause
844 if ( !m_headers.IsEmpty() ) {
845 // correspondence between wxWindows headers and class names
846 static const char *headers[] = {
847 "object",
848 "defs",
849 "string",
850 "dynarray",
851 "file",
852 "time",
853 };
854
855 // NULL here means not to insert anything in "See also" for the
856 // corresponding header
857 static const char *classes[] = {
858 NULL,
859 NULL,
860 NULL,
861 NULL,
862 "wxFile",
863 "wxTime",
864 };
865
866 wxASSERT_MSG( WXSIZEOF(headers) == WXSIZEOF(classes),
867 "arrays must be in sync!" );
868
869 wxArrayInt interestingClasses;
870
871 size_t count = m_headers.Count(), index;
872 for ( size_t n = 0; n < count; n++ ) {
873 wxString baseHeaderName = m_headers[n].Before('.');
874 if ( baseHeaderName(0, 3) != "wx/" )
875 continue;
876
877 baseHeaderName.erase(0, 3);
878 for ( index = 0; index < WXSIZEOF(headers); index++ ) {
879 if ( Stricmp(baseHeaderName, headers[index]) == 0 )
880 break;
881 }
882
883 if ( (index < WXSIZEOF(headers)) && classes[index] ) {
884 // interesting header
885 interestingClasses.Add(index);
886 }
887 }
888
889 if ( !interestingClasses.IsEmpty() ) {
890 // do generate "See also" clause
891 totalText << "\\wxheading{See also:}\n\n";
892
893 count = interestingClasses.Count();
894 for ( index = 0; index < count; index++ ) {
895 if ( index > 0 )
896 totalText << ", ";
897
898 totalText << MakeHelpref(classes[interestingClasses[index]]);
899 }
900
901 totalText << "\n\n";
902 }
903 }
904
905 // the comment before the class generally explains what is it for so put it
906 // in place of the class description
907 if ( cl.HasComments() ) {
908 wxString comment = GetAllComments(cl);
909
910 totalText << '\n' << comment << '\n';
911 }
912
913 // derived from section
914 wxString derived = "\\wxheading{Derived from}\n\n";
915
916 const StrListT& baseClasses = cl.mSuperClassNames;
917 if ( baseClasses.size() == 0 ) {
918 derived << "No base class";
919 }
920 else {
921 bool first = TRUE;
922 for ( StrListT::const_iterator i = baseClasses.begin();
923 i != baseClasses.end();
924 i++ ) {
925 if ( !first ) {
926 // separate from the previous one
927 derived << "\\\\\n";
928 }
929 else {
930 first = FALSE;
931 }
932
933 wxString baseclass = *i;
934 derived << "\\helpref{" << baseclass << "}";
935 derived << "{" << baseclass.MakeLower() << "}";
936 }
937 }
938 totalText << derived << "\n\n";
939
940 // write all this to file
941 m_file.WriteTeX(totalText);
942
943 // if there were any enums/typedefs before, insert their documentation now
944 InsertDataStructuresHeader();
945 InsertTypedefDocs();
946 InsertEnumDocs();
947 }
948
949 void HelpGenVisitor::VisitEnumeration( spEnumeration& en )
950 {
951 CloseFunction();
952
953 if ( m_inMethodSection ) {
954 // FIXME that's a bug, but tell the user aboit it nevertheless... we
955 // should be smart enough to process even the enums which come after the
956 // functions
957 wxLogWarning("enum '%s' ignored, please put it before the class "
958 "methods.", en.GetName().c_str());
959 return;
960 }
961
962 // simply copy the enum text in the docs
963 wxString enumeration = GetAllComments(en);
964 enumeration << "{\\small \\begin{verbatim}\n"
965 << en.mEnumContent
966 << "\n\\end{verbatim}}\n";
967
968 // remember for later use if we're not inside a class yet
969 if ( !m_inClass ) {
970 if ( !m_textStoredEnums.IsEmpty() ) {
971 m_textStoredEnums << '\n';
972 }
973
974 m_textStoredEnums << enumeration;
975 }
976 else {
977 // write the header for this section if not done yet
978 InsertDataStructuresHeader();
979
980 enumeration << '\n';
981 m_file.WriteTeX(enumeration);
982 }
983 }
984
985 void HelpGenVisitor::VisitTypeDef( spTypeDef& td )
986 {
987 CloseFunction();
988
989 if ( m_inMethodSection ) {
990 // FIXME that's a bug, but tell the user aboit it nevertheless...
991 wxLogWarning("typedef '%s' ignored, please put it before the class "
992 "methods.", td.GetName().c_str());
993 return;
994 }
995
996 wxString typedefdoc;
997 typedefdoc << "{\\small \\begin{verbatim}\n"
998 << "typedef " << td.mOriginalType << ' ' << td.GetName()
999 << "\n\\end{verbatim}}\n"
1000 << GetAllComments(td);
1001
1002 // remember for later use if we're not inside a class yet
1003 if ( !m_inClass ) {
1004 if ( !m_textStoredTypedefs.IsEmpty() ) {
1005 m_textStoredTypedefs << '\n';
1006 }
1007
1008 m_textStoredTypedefs << typedefdoc;
1009 }
1010 else {
1011 // write the header for this section if not done yet
1012 InsertDataStructuresHeader();
1013
1014 typedefdoc << '\n';
1015 m_file.WriteTeX(typedefdoc);
1016 }
1017 }
1018
1019 void HelpGenVisitor::VisitPreprocessorLine( spPreprocessorLine& pd )
1020 {
1021 switch ( pd.GetStatementType() ) {
1022 case SP_PREP_DEF_INCLUDE_FILE:
1023 m_headers.Add(pd.CPP_GetIncludedFileNeme());
1024 break;
1025
1026 case SP_PREP_DEF_DEFINE_SYMBOL:
1027 // TODO decide if it's a constant and document it if it is
1028 break;
1029 }
1030 }
1031
1032 void HelpGenVisitor::VisitAttribute( spAttribute& attr )
1033 {
1034 CloseFunction();
1035
1036 // only document the public member variables
1037 if ( !m_inClass || !attr.IsPublic() )
1038 return;
1039
1040 wxLogWarning("Ignoring member variable '%s'.", attr.GetName().c_str());
1041 }
1042
1043 void HelpGenVisitor::VisitOperation( spOperation& op )
1044 {
1045 CloseFunction();
1046
1047 if ( !m_inClass ) {
1048 // we don't generate docs right now - either we ignore this class
1049 // entirely or we couldn't open the file
1050 return;
1051 }
1052
1053 if ( !op.IsInClass() ) {
1054 // TODO document global functions
1055 wxLogWarning("skipped global function '%s'.", op.GetName().c_str());
1056
1057 return;
1058 }
1059
1060 if ( op.mVisibility == SP_VIS_PRIVATE ) {
1061 // FIXME should we document protected functions?
1062 return;
1063 }
1064
1065 wxString funcname = op.GetName(),
1066 classname = op.GetClass().GetName();
1067 if ( m_ignoreNames.IgnoreMethod(classname, funcname) ) {
1068 wxLogVerbose("Skipping ignored '%s::%s'.",
1069 classname.c_str(), funcname.c_str());
1070
1071 return;
1072 }
1073
1074 InsertMethodsHeader();
1075
1076 // save state info
1077 m_inFunction =
1078 m_isFirstParam = TRUE;
1079
1080 m_textStoredFunctionComment = GetAllComments(op);
1081
1082 // start function documentation
1083 wxString totalText;
1084
1085 // check for the special case of dtor
1086 wxString dtor;
1087 if ( (funcname[0] == '~') && (classname == funcname.c_str() + 1) ) {
1088 dtor.Printf("\\destruct{%s}", classname.c_str());
1089 funcname = dtor;
1090 }
1091
1092 totalText.Printf("\n"
1093 "\\membersection{%s::%s}\\label{%s}\n"
1094 "\n"
1095 "\\%sfunc{%s%s}{%s}{",
1096 classname.c_str(), funcname.c_str(),
1097 MakeLabel(classname, funcname).c_str(),
1098 op.mIsConstant ? "const" : "",
1099 op.mIsVirtual ? "virtual " : "",
1100 op.mRetType.c_str(),
1101 funcname.c_str());
1102
1103 m_file.WriteTeX(totalText);
1104 }
1105
1106 void HelpGenVisitor::VisitParameter( spParameter& param )
1107 {
1108 if ( !m_inFunction )
1109 return;
1110
1111 wxString totalText;
1112 if ( m_isFirstParam ) {
1113 m_isFirstParam = FALSE;
1114 }
1115 else {
1116 totalText << ", ";
1117 }
1118
1119 totalText << "\\param{" << param.mType << " }{" << param.GetName();
1120 wxString defvalue = param.mInitVal;
1121 if ( !defvalue.IsEmpty() ) {
1122 totalText << " = " << defvalue;
1123 }
1124
1125 totalText << '}';
1126
1127 m_file.WriteTeX(totalText);
1128 }
1129
1130 // ---------------------------------------------------------------------------
1131 // DocManager
1132 // ---------------------------------------------------------------------------
1133
1134 DocManager::DocManager(bool checkParamNames)
1135 {
1136 m_checkParamNames = checkParamNames;
1137 }
1138
1139 size_t DocManager::TryMatch(const char *str, const char *match)
1140 {
1141 size_t lenMatch = 0;
1142 while ( str[lenMatch] == match[lenMatch] ) {
1143 lenMatch++;
1144
1145 if ( match[lenMatch] == '\0' )
1146 return lenMatch;
1147 }
1148
1149 return 0;
1150 }
1151
1152 bool DocManager::SkipUntil(const char **pp, char c)
1153 {
1154 const char *p = *pp;
1155 while ( *p != c ) {
1156 if ( *p == '\0' )
1157 break;
1158
1159 if ( *p == '\n' )
1160 m_line++;
1161
1162 p++;
1163 }
1164
1165 *pp = p;
1166
1167 return *p == c;
1168 }
1169
1170 bool DocManager::SkipSpaceUntil(const char **pp, char c)
1171 {
1172 const char *p = *pp;
1173 while ( *p != c ) {
1174 if ( !isspace(*p) || *p == '\0' )
1175 break;
1176
1177 if ( *p == '\n' )
1178 m_line++;
1179
1180 p++;
1181 }
1182
1183 *pp = p;
1184
1185 return *p == c;
1186 }
1187
1188 wxString DocManager::ExtractStringBetweenBraces(const char **pp)
1189 {
1190 wxString result;
1191
1192 if ( !SkipSpaceUntil(pp, '{') ) {
1193 wxLogWarning("file %s(%d): '{' expected after '\\param'",
1194 m_filename.c_str(), m_line);
1195
1196 }
1197 else {
1198 const char *startParam = ++*pp; // skip '{'
1199
1200 if ( !SkipUntil(pp, '}') ) {
1201 wxLogWarning("file %s(%d): '}' expected after '\\param'",
1202 m_filename.c_str(), m_line);
1203 }
1204 else {
1205 result = wxString(startParam, (*pp)++ - startParam);
1206 }
1207 }
1208
1209 return result;
1210 }
1211
1212 bool DocManager::ParseTeXFile(const wxString& filename)
1213 {
1214 m_filename = filename;
1215
1216 wxFile file(m_filename, wxFile::read);
1217 if ( !file.IsOpened() )
1218 return FALSE;
1219
1220 off_t len = file.Length();
1221 if ( len == wxInvalidOffset )
1222 return FALSE;
1223
1224 char *buf = new char[len + 1];
1225 buf[len] = '\0';
1226
1227 if ( file.Read(buf, len) == wxInvalidOffset ) {
1228 delete [] buf;
1229
1230 return FALSE;
1231 }
1232
1233 // reinit everything
1234 m_line = 1;
1235
1236 wxLogVerbose("%s: starting to parse doc file '%s'.",
1237 GetCurrentTime("%H:%M:%S"), m_filename.c_str());
1238
1239 // the name of the class from the last "\membersection" command: we assume
1240 // that the following "\func" or "\constfunc" always documents a method of
1241 // this class (and it should always be like that in wxWindows documentation)
1242 wxString classname;
1243
1244 for ( const char *current = buf; current - buf < len; current++ ) {
1245 // FIXME parsing is awfully inefficient
1246
1247 if ( *current == '%' ) {
1248 // comment, skip until the end of line
1249 current++;
1250 SkipUntil(&current, '\n');
1251
1252 continue;
1253 }
1254
1255 // all the command we're interested in start with '\\'
1256 while ( *current != '\\' && *current != '\0' ) {
1257 if ( *current++ == '\n' )
1258 m_line++;
1259 }
1260
1261 if ( *current == '\0' ) {
1262 // no more TeX commands left
1263 break;
1264 }
1265
1266 current++; // skip '\\'
1267
1268 enum
1269 {
1270 Nothing,
1271 Func,
1272 ConstFunc,
1273 MemberSect
1274 } foundCommand = Nothing;
1275
1276 size_t lenMatch = TryMatch(current, "func");
1277 if ( lenMatch ) {
1278 foundCommand = Func;
1279 }
1280 else {
1281 lenMatch = TryMatch(current, "constfunc");
1282 if ( lenMatch )
1283 foundCommand = ConstFunc;
1284 else {
1285 lenMatch = TryMatch(current, "membersection");
1286
1287 if ( lenMatch )
1288 foundCommand = MemberSect;
1289 }
1290 }
1291
1292 if ( foundCommand == Nothing )
1293 continue;
1294
1295 current += lenMatch;
1296
1297 if ( !SkipSpaceUntil(&current, '{') ) {
1298 wxLogWarning("file %s(%d): '{' expected after \\func, "
1299 "\\constfunc or \\membersection.",
1300 m_filename.c_str(), m_line);
1301
1302 continue;
1303 }
1304
1305 current++;
1306
1307 if ( foundCommand == MemberSect ) {
1308 // what follows has the form <classname>::<funcname>
1309 const char *startClass = current;
1310 if ( !SkipUntil(&current, ':') || *(current + 1) != ':' ) {
1311 wxLogWarning("file %s(%d): '::' expected after "
1312 "\\membersection.", m_filename.c_str(), m_line);
1313 }
1314 else {
1315 classname = wxString(startClass, current - startClass);
1316 TeXUnfilter(&classname);
1317 }
1318
1319 continue;
1320 }
1321
1322 // extract the return type
1323 const char *startRetType = current;
1324
1325 if ( !SkipUntil(&current, '}') ) {
1326 wxLogWarning("file %s(%d): '}' expected after return type",
1327 m_filename.c_str(), m_line);
1328
1329 continue;
1330 }
1331
1332 wxString returnType = wxString(startRetType, current - startRetType);
1333 TeXUnfilter(&returnType);
1334
1335 current++;
1336 if ( !SkipSpaceUntil(&current, '{') ) {
1337 wxLogWarning("file %s(%d): '{' expected after return type",
1338 m_filename.c_str(), m_line);
1339
1340 continue;
1341 }
1342
1343 current++;
1344 const char *funcEnd = current;
1345 if ( !SkipUntil(&funcEnd, '}') ) {
1346 wxLogWarning("file %s(%d): '}' expected after function name",
1347 m_filename.c_str(), m_line);
1348
1349 continue;
1350 }
1351
1352 wxString funcName = wxString(current, funcEnd - current);
1353 current = funcEnd + 1;
1354
1355 // trim spaces from both sides
1356 funcName.Trim(FALSE);
1357 funcName.Trim(TRUE);
1358
1359 // special cases: '$...$' may be used for LaTeX inline math, remove the
1360 // '$'s
1361 if ( funcName.Find('$') != wxNOT_FOUND ) {
1362 wxString name;
1363 for ( const char *p = funcName.c_str(); *p != '\0'; p++ ) {
1364 if ( *p != '$' && !isspace(*p) )
1365 name += *p;
1366 }
1367
1368 funcName = name;
1369 }
1370
1371 // \destruct{foo} is really ~foo
1372 if ( funcName[0u] == '\\' ) {
1373 size_t len = strlen("\\destruct{");
1374 if ( funcName(0, len) != "\\destruct{" ) {
1375 wxLogWarning("file %s(%d): \\destruct expected",
1376 m_filename.c_str(), m_line);
1377
1378 continue;
1379 }
1380
1381 funcName.erase(0, len);
1382 funcName.Prepend('~');
1383
1384 if ( !SkipSpaceUntil(&current, '}') ) {
1385 wxLogWarning("file %s(%d): '}' expected after destructor",
1386 m_filename.c_str(), m_line);
1387
1388 continue;
1389 }
1390
1391 funcEnd++; // there is an extra '}' to count
1392 }
1393
1394 TeXUnfilter(&funcName);
1395
1396 // extract params
1397 current = funcEnd + 1; // skip '}'
1398 if ( !SkipSpaceUntil(&current, '{') ||
1399 (current++, !SkipSpaceUntil(&current, '\\')) ) {
1400 wxLogWarning("file %s(%d): '\\param' or '\\void' expected",
1401 m_filename.c_str(), m_line);
1402
1403 continue;
1404 }
1405
1406 wxArrayString paramNames, paramTypes, paramValues;
1407
1408 bool isVararg = FALSE;
1409
1410 current++; // skip '\\'
1411 lenMatch = TryMatch(current, "void");
1412 if ( !lenMatch ) {
1413 lenMatch = TryMatch(current, "param");
1414 while ( lenMatch && (current - buf < len) ) {
1415 current += lenMatch;
1416
1417 // now come {paramtype}{paramname}
1418 wxString paramType = ExtractStringBetweenBraces(&current);
1419 if ( !!paramType ) {
1420 wxString paramText = ExtractStringBetweenBraces(&current);
1421 if ( !!paramText ) {
1422 // the param declaration may contain default value
1423 wxString paramName = paramText.BeforeFirst('='),
1424 paramValue = paramText.AfterFirst('=');
1425
1426 // sanitize all strings
1427 TeXUnfilter(&paramValue);
1428 TeXUnfilter(&paramName);
1429 TeXUnfilter(&paramType);
1430
1431 paramValues.Add(paramValue);
1432 paramNames.Add(paramName);
1433 paramTypes.Add(paramType);
1434 }
1435 }
1436 else {
1437 // vararg function?
1438 wxString paramText = ExtractStringBetweenBraces(&current);
1439 if ( paramText == "..." ) {
1440 isVararg = TRUE;
1441 }
1442 else {
1443 wxLogWarning("Parameters of '%s::%s' are in "
1444 "incorrect form.",
1445 classname.c_str(), funcName.c_str());
1446 }
1447 }
1448
1449 // what's next?
1450 current = SkipSpaces(current);
1451 if ( *current == ',' || *current == '}' ) {
1452 current = SkipSpaces(++current);
1453
1454 lenMatch = TryMatch(current, "\\param");
1455 }
1456 else {
1457 wxLogWarning("file %s(%d): ',' or '}' expected after "
1458 "'\\param'", m_filename.c_str(), m_line);
1459
1460 continue;
1461 }
1462 }
1463
1464 // if we got here there was no '\\void', so must have some params
1465 if ( paramNames.IsEmpty() ) {
1466 wxLogWarning("file %s(%d): '\\param' or '\\void' expected",
1467 m_filename.c_str(), m_line);
1468
1469 continue;
1470 }
1471 }
1472
1473 // verbose diagnostic output
1474 wxString paramsAll;
1475 size_t param, paramCount = paramNames.GetCount();
1476 for ( param = 0; param < paramCount; param++ ) {
1477 if ( param != 0 ) {
1478 paramsAll << ", ";
1479 }
1480
1481 paramsAll << paramTypes[param] << ' ' << paramNames[param];
1482 }
1483
1484 wxLogVerbose("file %s(%d): found '%s %s::%s(%s)%s'",
1485 m_filename.c_str(), m_line,
1486 returnType.c_str(),
1487 classname.c_str(),
1488 funcName.c_str(),
1489 paramsAll.c_str(),
1490 foundCommand == ConstFunc ? " const" : "");
1491
1492 // store the info about the just found function
1493 ArrayMethodInfo *methods;
1494 int index = m_classes.Index(classname);
1495 if ( index == wxNOT_FOUND ) {
1496 m_classes.Add(classname);
1497
1498 methods = new ArrayMethodInfo;
1499 m_methods.Add(methods);
1500 }
1501 else {
1502 methods = m_methods[(size_t)index];
1503 }
1504
1505 ArrayParamInfo params;
1506 for ( param = 0; param < paramCount; param++ ) {
1507 params.Add(new ParamInfo(paramTypes[param],
1508 paramNames[param],
1509 paramValues[param]));
1510 }
1511
1512 MethodInfo *method = new MethodInfo(returnType, funcName, params);
1513 if ( foundCommand == ConstFunc )
1514 method->SetFlag(MethodInfo::Const);
1515 if ( isVararg )
1516 method->SetFlag(MethodInfo::Vararg);
1517
1518 methods->Add(method);
1519 }
1520
1521 delete [] buf;
1522
1523 wxLogVerbose("%s: finished parsing doc file '%s'.\n",
1524 GetCurrentTime("%H:%M:%S"), m_filename.c_str());
1525
1526 return TRUE;
1527 }
1528
1529 bool DocManager::DumpDifferences(spContext *ctxTop) const
1530 {
1531 typedef MMemberListT::const_iterator MemberIndex;
1532
1533 bool foundDiff = FALSE;
1534
1535 // flag telling us whether the given class was found at all in the header
1536 size_t nClass, countClassesInDocs = m_classes.GetCount();
1537 bool *classExists = new bool[countClassesInDocs];
1538 for ( nClass = 0; nClass < countClassesInDocs; nClass++ ) {
1539 classExists[nClass] = FALSE;
1540 }
1541
1542 // ctxTop is normally an spFile
1543 wxASSERT( ctxTop->GetContextType() == SP_CTX_FILE );
1544
1545 const MMemberListT& classes = ctxTop->GetMembers();
1546 for ( MemberIndex i = classes.begin(); i != classes.end(); i++ ) {
1547 spContext *ctx = *i;
1548 if ( ctx->GetContextType() != SP_CTX_CLASS ) {
1549 // TODO process also global functions, macros, ...
1550 continue;
1551 }
1552
1553 spClass *ctxClass = (spClass *)ctx;
1554 const wxString& nameClass = ctxClass->mName;
1555 int index = m_classes.Index(nameClass);
1556 if ( index == wxNOT_FOUND ) {
1557 if ( !m_ignoreNames.IgnoreClass(nameClass) ) {
1558 foundDiff = TRUE;
1559
1560 wxLogError("Class '%s' is not documented at all.",
1561 nameClass.c_str());
1562 }
1563
1564 // it makes no sense to check for its functions
1565 continue;
1566 }
1567 else {
1568 classExists[index] = TRUE;
1569 }
1570
1571 // array of method descriptions for this class
1572 const ArrayMethodInfo& methods = *(m_methods[index]);
1573 size_t nMethod, countMethods = methods.GetCount();
1574
1575 // flags telling if we already processed given function
1576 bool *methodExists = new bool[countMethods];
1577 for ( nMethod = 0; nMethod < countMethods; nMethod++ ) {
1578 methodExists[nMethod] = FALSE;
1579 }
1580
1581 wxArrayString aOverloadedMethods;
1582
1583 const MMemberListT& functions = ctxClass->GetMembers();
1584 for ( MemberIndex j = functions.begin(); j != functions.end(); j++ ) {
1585 ctx = *j;
1586 if ( ctx->GetContextType() != SP_CTX_OPERATION )
1587 continue;
1588
1589 spOperation *ctxMethod = (spOperation *)ctx;
1590 const wxString& nameMethod = ctxMethod->mName;
1591
1592 // find all functions with the same name
1593 wxArrayInt aMethodsWithSameName;
1594 for ( nMethod = 0; nMethod < countMethods; nMethod++ ) {
1595 if ( methods[nMethod]->GetName() == nameMethod )
1596 aMethodsWithSameName.Add(nMethod);
1597 }
1598
1599 if ( aMethodsWithSameName.IsEmpty() && ctxMethod->IsPublic() ) {
1600 if ( !m_ignoreNames.IgnoreMethod(nameClass, nameMethod) ) {
1601 foundDiff = TRUE;
1602
1603 wxLogError("'%s::%s' is not documented.",
1604 nameClass.c_str(),
1605 nameMethod.c_str());
1606 }
1607
1608 // don't check params
1609 continue;
1610 }
1611 else if ( aMethodsWithSameName.GetCount() == 1 ) {
1612 index = (size_t)aMethodsWithSameName[0u];
1613 methodExists[index] = TRUE;
1614
1615 if ( m_ignoreNames.IgnoreMethod(nameClass, nameMethod) )
1616 continue;
1617
1618 if ( !ctxMethod->IsPublic() ) {
1619 wxLogWarning("'%s::%s' is documented but not public.",
1620 nameClass.c_str(),
1621 nameMethod.c_str());
1622 }
1623
1624 // check that the flags match
1625 const MethodInfo& method = *(methods[index]);
1626
1627 bool isVirtual = ctxMethod->mIsVirtual;
1628 if ( isVirtual != method.HasFlag(MethodInfo::Virtual) ) {
1629 wxLogWarning("'%s::%s' is incorrectly documented as %s"
1630 "virtual.",
1631 nameClass.c_str(),
1632 nameMethod.c_str(),
1633 isVirtual ? "not " : "");
1634 }
1635
1636 bool isConst = ctxMethod->mIsConstant;
1637 if ( isConst != method.HasFlag(MethodInfo::Const) ) {
1638 wxLogWarning("'%s::%s' is incorrectly documented as %s"
1639 "constant.",
1640 nameClass.c_str(),
1641 nameMethod.c_str(),
1642 isConst ? "not " : "");
1643 }
1644
1645 // check that the params match
1646 const MMemberListT& params = ctxMethod->GetMembers();
1647
1648 if ( params.size() != method.GetParamCount() ) {
1649 wxLogError("Incorrect number of parameters for '%s::%s' "
1650 "in the docs: should be %d instead of %d.",
1651 nameClass.c_str(),
1652 nameMethod.c_str(),
1653 params.size(), method.GetParamCount());
1654 }
1655 else {
1656 size_t nParam = 0;
1657 for ( MemberIndex k = params.begin();
1658 k != params.end();
1659 k++, nParam++ ) {
1660 ctx = *k;
1661
1662 // what else can a function have?
1663 wxASSERT( ctx->GetContextType() == SP_CTX_PARAMETER );
1664
1665 spParameter *ctxParam = (spParameter *)ctx;
1666 const ParamInfo& param = method.GetParam(nParam);
1667 if ( m_checkParamNames &&
1668 (param.GetName() != ctxParam->mName) ) {
1669 foundDiff = TRUE;
1670
1671 wxLogError("Parameter #%d of '%s::%s' should be "
1672 "'%s' and not '%s'.",
1673 nParam + 1,
1674 nameClass.c_str(),
1675 nameMethod.c_str(),
1676 ctxParam->mName.c_str(),
1677 param.GetName().c_str());
1678
1679 continue;
1680 }
1681
1682 if ( param.GetType() != ctxParam->mType ) {
1683 foundDiff = TRUE;
1684
1685 wxLogError("Type of parameter '%s' of '%s::%s' "
1686 "should be '%s' and not '%s'.",
1687 ctxParam->mName.c_str(),
1688 nameClass.c_str(),
1689 nameMethod.c_str(),
1690 ctxParam->mType.c_str(),
1691 param.GetType().GetName().c_str());
1692
1693 continue;
1694 }
1695
1696 if ( param.GetDefValue() != ctxParam->mInitVal ) {
1697 wxLogWarning("Default value of parameter '%s' of "
1698 "'%s::%s' should be '%s' and not "
1699 "'%s'.",
1700 ctxParam->mName.c_str(),
1701 nameClass.c_str(),
1702 nameMethod.c_str(),
1703 ctxParam->mInitVal.c_str(),
1704 param.GetDefValue().c_str());
1705 }
1706 }
1707 }
1708 }
1709 else {
1710 // TODO OVER add real support for overloaded methods
1711
1712 if ( m_ignoreNames.IgnoreMethod(nameClass, nameMethod) )
1713 continue;
1714
1715 if ( aOverloadedMethods.Index(nameMethod) == wxNOT_FOUND ) {
1716 // mark all methods with this name as existing
1717 for ( nMethod = 0; nMethod < countMethods; nMethod++ ) {
1718 if ( methods[nMethod]->GetName() == nameMethod )
1719 methodExists[nMethod] = TRUE;
1720 }
1721
1722 aOverloadedMethods.Add(nameMethod);
1723
1724 wxLogVerbose("'%s::%s' is overloaded and I'm too "
1725 "stupid to find the right match - skipping "
1726 "the param and flags checks.",
1727 nameClass.c_str(),
1728 nameMethod.c_str());
1729 }
1730 //else: warning already given
1731 }
1732 }
1733
1734 for ( nMethod = 0; nMethod < countMethods; nMethod++ ) {
1735 if ( !methodExists[nMethod] ) {
1736 const wxString& nameMethod = methods[nMethod]->GetName();
1737 if ( !m_ignoreNames.IgnoreMethod(nameClass, nameMethod) ) {
1738 foundDiff = TRUE;
1739
1740 wxLogError("'%s::%s' is documented but doesn't exist.",
1741 nameClass.c_str(),
1742 nameMethod.c_str());
1743 }
1744 }
1745 }
1746
1747 delete [] methodExists;
1748 }
1749
1750 // check that all classes we found in the docs really exist
1751 for ( nClass = 0; nClass < countClassesInDocs; nClass++ ) {
1752 if ( !classExists[nClass] ) {
1753 foundDiff = TRUE;
1754
1755 wxLogError("Class '%s' is documented but doesn't exist.",
1756 m_classes[nClass].c_str());
1757 }
1758 }
1759
1760 delete [] classExists;
1761
1762 return !foundDiff;
1763 }
1764
1765 DocManager::~DocManager()
1766 {
1767 WX_CLEAR_ARRAY(m_methods);
1768 }
1769
1770 // ---------------------------------------------------------------------------
1771 // IgnoreNamesHandler implementation
1772 // ---------------------------------------------------------------------------
1773
1774 int IgnoreNamesHandler::CompareIgnoreListEntries(IgnoreListEntry *first,
1775 IgnoreListEntry *second)
1776 {
1777 // first compare the classes
1778 int rc = first->m_classname.Cmp(second->m_classname);
1779 if ( rc == 0 )
1780 rc = first->m_funcname.Cmp(second->m_funcname);
1781
1782 return rc;
1783 }
1784
1785 bool IgnoreNamesHandler::AddNamesFromFile(const wxString& filename)
1786 {
1787 wxFile file(filename, wxFile::read);
1788 if ( !file.IsOpened() )
1789 return FALSE;
1790
1791 off_t len = file.Length();
1792 if ( len == wxInvalidOffset )
1793 return FALSE;
1794
1795 char *buf = new char[len + 1];
1796 buf[len] = '\0';
1797
1798 if ( file.Read(buf, len) == wxInvalidOffset ) {
1799 delete [] buf;
1800
1801 return FALSE;
1802 }
1803
1804 wxString line;
1805 for ( const char *current = buf; ; current++ ) {
1806 #ifdef __WXMSW__
1807 // skip DOS line separator
1808 if ( *current == '\r' )
1809 current++;
1810 #endif // wxMSW
1811
1812 if ( *current == '\n' || *current == '\0' ) {
1813 if ( line[0u] != '#' ) {
1814 if ( line.Find(':') != wxNOT_FOUND ) {
1815 wxString classname = line.BeforeFirst(':'),
1816 funcname = line.AfterLast(':');
1817 m_ignore.Add(new IgnoreListEntry(classname, funcname));
1818 }
1819 else {
1820 // entire class
1821 m_ignore.Add(new IgnoreListEntry(line, ""));
1822 }
1823 }
1824 //else: comment
1825
1826 if ( *current == '\0' )
1827 break;
1828
1829 line.Empty();
1830 }
1831 else {
1832 line += *current;
1833 }
1834 }
1835
1836 delete [] buf;
1837
1838 return TRUE;
1839 }
1840
1841 // -----------------------------------------------------------------------------
1842 // global function implementation
1843 // -----------------------------------------------------------------------------
1844
1845 static wxString MakeLabel(const char *classname, const char *funcname)
1846 {
1847 wxString label(classname);
1848 if ( funcname && funcname[0] == '\\' ) {
1849 // we may have some special TeX macro - so far only \destruct exists,
1850 // but may be later others will be added
1851 static const char *macros[] = { "destruct" };
1852 static const char *replacement[] = { "dtor" };
1853
1854 size_t n;
1855 for ( n = 0; n < WXSIZEOF(macros); n++ ) {
1856 if ( strncmp(funcname + 1, macros[n], strlen(macros[n])) == 0 ) {
1857 // found
1858 break;
1859 }
1860 }
1861
1862 if ( n == WXSIZEOF(macros) ) {
1863 wxLogWarning("unknown function name '%s' - leaving as is.",
1864 funcname);
1865 }
1866 else {
1867 funcname = replacement[n];
1868 }
1869 }
1870
1871 if ( funcname )
1872 label << funcname;
1873
1874 label.MakeLower();
1875
1876 return label;
1877 }
1878
1879 static wxString MakeHelpref(const char *argument)
1880 {
1881 wxString helpref;
1882 helpref << "\\helpref{" << argument << "}{" << MakeLabel(argument) << '}';
1883
1884 return helpref;
1885 }
1886
1887 static void TeXUnfilter(wxString* str)
1888 {
1889 // FIXME may be done much more quickly
1890 str->Trim(TRUE);
1891 str->Trim(FALSE);
1892
1893 str->Replace("\\&", "&");
1894 str->Replace("\\_", "_");
1895 }
1896
1897 static void TeXFilter(wxString* str)
1898 {
1899 // FIXME may be done much more quickly
1900 str->Replace("&", "\\&");
1901 str->Replace("_", "\\_");
1902 }
1903
1904 static wxString GetAllComments(const spContext& ctx)
1905 {
1906 wxString comments;
1907 const MCommentListT& commentsList = ctx.GetCommentList();
1908 for ( MCommentListT::const_iterator i = commentsList.begin();
1909 i != commentsList.end();
1910 i++ ) {
1911 wxString comment = (*i)->GetText();
1912
1913 // don't take comments like "// ----------" &c
1914 comment.Trim(FALSE);
1915 if ( !!comment &&
1916 comment == wxString(comment[0u], comment.length() - 1) + '\n' )
1917 comments << "\n";
1918 else
1919 comments << comment;
1920 }
1921
1922 return comments;
1923 }
1924
1925 static const char *GetCurrentTime(const char *timeFormat)
1926 {
1927 static char s_timeBuffer[128];
1928 time_t timeNow;
1929 struct tm *ptmNow;
1930
1931 time(&timeNow);
1932 ptmNow = localtime(&timeNow);
1933
1934 strftime(s_timeBuffer, WXSIZEOF(s_timeBuffer), timeFormat, ptmNow);
1935
1936 return s_timeBuffer;
1937 }
1938
1939 /*
1940 $Log$
1941 Revision 1.10 2000/03/11 10:05:23 VS
1942 now compiles with wxBase
1943
1944 Revision 1.9 2000/01/16 13:25:21 VS
1945 compilation fixes (gcc)
1946
1947 Revision 1.8 1999/09/13 14:29:39 JS
1948
1949 Made HelpGen into a wxWin app (still uses command-line args); moved includes
1950 into src for simplicity; added VC++ 5 project file
1951
1952 Revision 1.7 1999/02/21 22:32:32 VZ
1953 1. more C++ parser fixes - now it almost parses wx/string.h
1954 a) #if/#ifdef/#else (very) limited support
1955 b) param type fix - now indirection chars are correctly handled
1956 c) class/struct/union distinction
1957 d) public/private fixes
1958 e) Dump() function added - very useful for debugging
1959
1960 2. option to ignore parameter names during 'diff' (in fact, they're ignored
1961 by default, and this option switches it on)
1962
1963 Revision 1.6 1999/02/20 23:00:26 VZ
1964 1. new 'diff' mode which seems to work
1965 2. output files are not overwritten in 'dmup' mode
1966 3. fixes for better handling of const functions and operators
1967 ----------------------------
1968 revision 1.5
1969 date: 1999/02/15 23:07:25; author: VZ; state: Exp; lines: +106 -45
1970 1. Parser improvements
1971 a) const and virtual methods are parsed correctly (not static yet)
1972 b) "const" which is part of the return type is not swallowed
1973
1974 2. HelpGen improvements: -o outputdir parameter added to the cmd line,
1975 "//---------" kind comments discarded now.
1976 ----------------------------
1977 revision 1.4
1978 date: 1999/01/13 14:23:31; author: JS; state: Exp; lines: +4 -4
1979
1980 some tweaks to HelpGen
1981 ----------------------------
1982 revision 1.3
1983 date: 1999/01/09 20:18:03; author: JS; state: Exp; lines: +7 -2
1984
1985 HelpGen starting to compile with VC++
1986 ----------------------------
1987 revision 1.2
1988 date: 1999/01/08 19:46:22; author: VZ; state: Exp; lines: +208 -35
1989
1990 supports typedefs, generates "See also:" and adds "virtual " for virtual
1991 functions
1992 ----------------------------
1993 revision 1.1
1994 date: 1999/01/08 17:45:55; author: VZ; state: Exp;
1995
1996 HelpGen is a prototype of the tool for automatic generation of the .tex files
1997 for wxWindows documentation from C++ headers
1998 */
1999
2000 /* vi: set tw=80 et ts=4 sw=4: */