]> git.saurik.com Git - wxWidgets.git/blob - utils/HelpGen/src/docripper.cpp
fixed HTML parsing in regard to spaces between tags
[wxWidgets.git] / utils / HelpGen / src / docripper.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: No names yet.
3 // Purpose: Contrib. demo
4 // Author: Aleksandras Gluchovas
5 // Modified by:
6 // Created: 22/09/98
7 // RCS-ID: $Id$
8 // Copyright: (c) Aleskandars Gluchovas
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // For compilers that support precompilation, includes "wx/wx.h".
13 #include "wx/wxprec.h"
14
15 #ifdef __BORLANDC__
16 #pragma hdrstop
17 #endif
18
19 #ifndef WX_PRECOMP
20 #include "wx/wx.h"
21 #endif
22
23 #include "docripper.h"
24
25 #include <iostream.h>
26
27 // script templates
28
29 // ***** currently only HTML versions of variouse templates available ***** //
30
31 static const char* HTM_TopTempl =
32
33 "<html><body bgcolor=#FFFFFF>\n\
34 \n\n<!------ Automatically Generated by \"wxDocRipper\"------->\n\n\n\
35 <p><h2>$(NAME)</h2><p>\n\
36 <ul>\n\
37 $(REFLIST)\
38 </ul><p>\n\n\
39 ";
40
41 static const char* HTM_ContentIdxTempl =
42
43 "\
44 <a name=\"r$(ID)_$(NAME)\">\n\
45 <p><hr>\n\
46 <h2><p>$(NAME)<p></h2>\
47 <ul>\n\
48 $(REFLIST)\
49 </ul><p>\n\n\
50 ";
51
52 static const char* HTM_SuperContentTempl =
53
54 "\
55 <a name=\"r$(ID)_$(NAME)\">\n\
56 <p><hr>\n\
57 <p><h2>$(NAME)<p></h2>\
58 $(BODY)\n\
59 ";
60
61 static const char* HTM_SubContentTempl =
62
63 "\
64 <a name=\"r$(ID)_$(NAME)\">\n\
65 <p><hr>\n\
66 <p><h3>$(NAME)<p></h3>\
67 $(BODY)\n\
68 ";
69
70 static const char* HTM_OutLineTempl =
71
72 "\
73 <p>\n\
74 <b><font color=\"#FF0000\">$(NAME)</font></b><p>\n\
75 ";
76
77 static const char* HTM_OutLine1Templ =
78
79 "\
80 <p>\n\
81 <b><i><font color=\"#101010\">$(NAME)</font></i></b>\n\
82 <ul>\n\
83 $(REFLIST)\
84 </ul>\n\n\
85 ";
86
87 static const char* HTM_RefTempl =
88
89 "\
90 <li><a href=\"#r$(ID)_$(NAME)\">$(NAME)</A>\n\
91 ";
92
93 static const char* HTM_DeadRefTempl =
94
95 "\
96 <li></b>$(NAME)\n\
97 ";
98
99 /***** Implementation for class RipperDocGen *****/
100
101 RipperDocGen::RipperDocGen()
102
103 : mTopTempl ( HTM_TopTempl ),
104 mContentIdxTempl ( HTM_ContentIdxTempl ),
105 mSuperContentTempl( HTM_SuperContentTempl ),
106 mSubContentTempl ( HTM_SubContentTempl ),
107 mOutLineTempl ( HTM_OutLineTempl ),
108 mOutLine1Templ ( HTM_OutLine1Templ ),
109
110 mRefTempl ( HTM_RefTempl ),
111 mDeadRefTempl ( HTM_DeadRefTempl ),
112
113 mpCurClassSect(0)
114 { // topIndex is not referenced
115 mpTopIdx = new ScriptSection( "Source Code Contents" , "", &mTopTempl , 0 );
116 mpClassIdx = new ScriptSection( "Classes Reference" , "", &mContentIdxTempl, &mRefTempl );
117 mpEnumIdx = new ScriptSection( "Enumerations Reference" , "", &mContentIdxTempl, &mRefTempl );
118 mpTypeDefIdx = new ScriptSection( "Type Definitions Reference" , "", &mContentIdxTempl, &mRefTempl );
119 mpMacroIdx = new ScriptSection( "Macros Reference" , "", &mContentIdxTempl, &mRefTempl );
120 mpGlobalVarsIdx = new ScriptSection( "Global Variables Reference" , "", &mContentIdxTempl, &mRefTempl );
121 mpGlobalFuncIdx = new ScriptSection( "Global Functions Reference", "", &mContentIdxTempl, &mRefTempl );
122 mpConstIdx = new ScriptSection( "Constants Reference" , "", &mContentIdxTempl, &mRefTempl );
123
124 // assemble top index
125 mpTopIdx->AddSection( mpClassIdx , 1 );
126 mpTopIdx->AddSection( mpEnumIdx , 1 );
127 mpTopIdx->AddSection( mpTypeDefIdx , 1 );
128 mpTopIdx->AddSection( mpMacroIdx , 1 );
129 mpTopIdx->AddSection( mpGlobalVarsIdx, 1 );
130 mpTopIdx->AddSection( mpGlobalFuncIdx, 1 );
131 mpTopIdx->AddSection( mpConstIdx , 1 );
132
133 // register reserved variables for index and description templates
134 ScriptSection::RegisterTemplate( mTopTempl );
135 ScriptSection::RegisterTemplate( mContentIdxTempl );
136 ScriptSection::RegisterTemplate( mSuperContentTempl );
137 ScriptSection::RegisterTemplate( mSubContentTempl );
138 ScriptSection::RegisterTemplate( mOutLineTempl );
139 ScriptSection::RegisterTemplate( mOutLine1Templ );
140 ScriptSection::RegisterTemplate( mRefTempl );
141 ScriptSection::RegisterTemplate( mDeadRefTempl );
142
143 // create the top-most (interfile) context
144 mpFileBinderCtx = new spFile();
145
146 // the default script is HTML
147 mTags = get_HTML_markup_tags();
148
149 mpParser = 0; // no default parser!
150 }
151
152 void RipperDocGen::Init( SourceParserBase* pParser )
153 {
154 mpParser = pParser;
155 }
156
157 RipperDocGen::~RipperDocGen()
158 {
159 delete mpFileBinderCtx;
160 }
161
162 void RipperDocGen::AppendComments( spContext& fromContext, string& str )
163 {
164 if ( !fromContext.HasComments() ) return;
165
166 size_t start = str.length();
167
168 str += mTags[TAG_BOLD].end;
169 str += mTags[TAG_PARAGRAPH].start;
170
171 MCommentListT& lst = fromContext.GetCommentList();
172
173 for( size_t i = 0; i != lst.size(); ++i )
174 {
175
176 if ( i != 0 )
177
178 if ( lst[i]->StartsParagraph() )
179 {
180 str += mTags[TAG_PARAGRAPH].start;
181 }
182
183 str += lst[i]->mText;
184 }
185
186 // remove new lines, and insert paragraph breaks
187 // if empty lines found
188
189 size_t len = str.length();
190
191 for( size_t n = start; n != len; ++n )
192
193 if ( str[n] == 10 ||
194 str[n] == 13 )
195 {
196 if ( n + 2 < len )
197 {
198 if ( ( str[n] == 13 && str[n+1] == 10 && // FIXME:: quick-hack
199 str[n+2] == 13 ) ||
200 ( str[n] == 10 && str[n+1] == 10 )
201 )
202 {
203 str.insert( n + 1, "<p>" ); // FIXME:: quick-hack
204 len += 3;
205 }
206 }
207
208 str[n] = ' ';
209 }
210
211 str += mTags[TAG_PARAGRAPH].end;
212 }
213
214 void RipperDocGen::AppendMulitilineStr( string& st, string& mlStr )
215 {
216 st = mTags[TAG_FIXED_FONT].start;
217 st += mlStr;
218 st += mTags[TAG_FIXED_FONT].end;
219 }
220
221 void RipperDocGen::AppendHighlightedSource( string& st, string source )
222 {
223 // FIXME:: below should not be fixed :)
224 char buf[1024*32];
225
226 // DBG:::
227 // ASSERT( source.length() + 1 < sizeof(buf) );
228
229 strcpy( buf, source.c_str() );
230
231 // highlight things
232 mSrcPainter.Init();
233 mSrcPainter.ProcessSource( buf, strlen(buf) );
234 mSrcPainter.GetResultString( st, mTags );
235 }
236
237 bool RipperDocGen::CheckIfUncommented( spContext& ctx, ScriptSection& toSect )
238 {
239 if ( ctx.HasComments() ) return 0;
240
241 toSect.AddReference(
242 new ScriptSection( GetScopedName( ctx ), "", 0, &mDeadRefTempl )
243 );
244
245 return 1;
246 }
247
248 ScriptTemplate* RipperDocGen::GetRefTemplFor( spContext& ctx )
249 {
250 if ( ctx.HasComments() )
251
252 return &mRefTempl;
253 else
254 return &mDeadRefTempl;
255 }
256
257 string RipperDocGen::GetScopedName( spContext& ofCtx )
258 {
259 if ( ofCtx.IsInFile() ) return ofCtx.GetName();
260 else
261 return ofCtx.GetOutterContext()->GetName() +
262 "::" + ofCtx.GetName();
263 }
264
265 void RipperDocGen::AddToCurrentClass( ScriptSection* pSection, spContext& ctx,
266 const char* subSectionName )
267 {
268 string sName;
269
270 if ( ctx.mVisibility == SP_VIS_PROTECTED )
271
272 sName = "Protected members/";
273 else
274 if ( ctx.mVisibility == SP_VIS_PRIVATE )
275
276 sName = "Private members/";
277 else
278 sName = "Public members/";
279
280 sName += subSectionName;
281
282 ScriptSection* pSect = mpCurClassSect->GetSubsection( sName.c_str() );
283
284 if ( CheckIfUncommented( ctx, *pSect ) )
285 {
286 delete pSection;
287 return;
288 }
289
290 pSect->AddReference( pSection );
291
292 mpCurClassSect->AddSection( pSection );
293 }
294
295 void RipperDocGen::LinkSuperClassRefs()
296 {
297 MMemberListT clLst;
298
299 // collect all classes in the context tree
300 mpFileBinderCtx->GetContextList( clLst, SP_CTX_CLASS );
301
302 for( size_t i = 0; i != clLst.size(); ++i )
303 {
304 spClass& cl = *((spClass*)clLst[i]);
305
306 // FIXME:: why sometimes GetUserData() returns NULL?
307 if ( !cl.GetUserData() )
308 continue;
309
310 ScriptSection* pClSect = (ScriptSection*)cl.GetUserData();
311 ScriptSection* pSuperSect = pClSect->GetSubsection("Derived from");
312
313 for( size_t n = 0; n != cl.mSuperClassNames.size(); ++n )
314 {
315 string& superClName = cl.mSuperClassNames[n];
316
317 spClass* pFound = NULL;
318
319 string* name;
320
321 for( size_t k = 0; k != clLst.size(); ++k )
322 {
323 name = &clLst[k]->GetName();
324
325 if ( clLst[k]->GetName() == superClName )
326 {
327 pFound = (spClass*)clLst[k];
328 break;
329 }
330 }
331
332 if ( !pFound )
333 {
334 ScriptSection* pNotFound =
335 new ScriptSection( superClName, "", 0, &mDeadRefTempl );
336
337 pSuperSect->AddReference( pNotFound );
338 }
339 else
340 if ( pFound->GetUserData() )
341
342 pSuperSect->AddReference(
343 (ScriptSection*)pFound->GetUserData() );
344 }
345 }
346 }
347
348 void RipperDocGen::ProcessFile( const char* sourceFile )
349 {
350 cout << "Processing file " << sourceFile << "..." << endl;
351
352 spFile* pCtx = mpParser->ParseFile( sourceFile );
353
354 if ( pCtx == NULL )
355 {
356 cout << "Cannot open file " << sourceFile << ", skipped..." << endl;
357
358 return;
359 }
360
361 VisitAll( *pCtx, TRUE );
362
363 mpFileBinderCtx->AddMember( pCtx );
364 }
365
366 // implementations of "visiting procedures"
367
368 void RipperDocGen::VisitEnumeration( spEnumeration& en )
369 {
370 // FOR NOW:: do not reference "nameless" enums
371 if ( en.GetName() == "" ) return;
372
373 if ( CheckIfUncommented( en, *mpEnumIdx ) )
374 return;
375
376 string body;
377 body += mTags[TAG_BOLD].start;
378
379 AppendMulitilineStr( body, en.mEnumContent );
380
381 body += mTags[TAG_BOLD].end;
382
383 string line;
384 AppendHighlightedSource( line, body );
385 AppendComments( en, line );
386
387 mpEnumIdx->AddSection(
388 new ScriptSection( en.GetName(), line,
389 &mSubContentTempl,
390 GetRefTemplFor( en ) ), 1
391 );
392 }
393
394 void RipperDocGen::VisitTypeDef( spTypeDef& td )
395 {
396 if ( CheckIfUncommented( td, *mpTypeDefIdx ) )
397 return;
398
399 string body;
400 body += mTags[TAG_BOLD].start;
401 body += "typdef ";
402 body += mTags[TAG_BOLD].end;
403
404 AppendMulitilineStr( body, td.mOriginalType );
405 body += td.mOriginalType;
406 body += ' ';
407
408 body += mTags[TAG_BOLD].start;
409 body += td.GetName();
410 body += mTags[TAG_BOLD].end;
411
412 string line;
413 AppendHighlightedSource( line, body );
414 AppendComments( td, line );
415
416 mpTypeDefIdx->AddSection(
417 new ScriptSection( td.GetName(), line,
418 &mSubContentTempl,
419 GetRefTemplFor( td ) ), TRUE
420 );
421 }
422
423 void RipperDocGen::VisitPreprocessorLine( spPreprocessorLine& pd )
424 {
425 if ( pd.mDefType != SP_PREP_DEF_REDEFINE_SYMBOL )
426
427 return;
428
429 if ( CheckIfUncommented( pd, *mpMacroIdx ) )
430 return;
431
432 string body;
433 body += mTags[TAG_FIXED_FONT].start;
434
435 string coloredLine = pd.mLine;
436 AppendHighlightedSource( coloredLine, pd.mLine );
437
438 AppendMulitilineStr( body, coloredLine );
439
440 body += mTags[TAG_FIXED_FONT].end;
441
442 AppendComments( pd, body );
443
444 mpMacroIdx->AddSection(
445 new ScriptSection( pd.GetName(), body,
446 &mSubContentTempl,
447 GetRefTemplFor( pd ) ), TRUE
448 );
449 }
450
451 void RipperDocGen::VisitClass( spClass& cl )
452 {
453 // FOR NOW:: do not document nested classes -
454 // nicier visiting method yet needed
455
456 if ( cl.IsInClass() )
457 {
458 SkipChildren(); // spVisitor's method
459 return;
460 }
461
462 string body;
463 AppendComments( cl, body );
464
465 mpCurClassSect =
466 new ScriptSection( cl.GetName(), body, &mSuperContentTempl, &mRefTempl );
467
468 // set up reference in the class context, pointing back
469 // to the section where this class is represented
470 cl.SetUserData( mpCurClassSect );
471
472 ScriptSection* pSuper = new ScriptSection( "Derived from" ,"", &mOutLine1Templ,0, 1 );
473
474 ScriptSection* pPublic = new ScriptSection( "Public members" ,"", &mOutLineTempl,0, 1 );
475 ScriptSection* pProtected = new ScriptSection( "Protected members" ,"", &mOutLineTempl,0, 1 );
476 ScriptSection* pPrivate = new ScriptSection( "Private members" ,"", &mOutLineTempl,0, 1 );
477
478 pPublic->AddSection( new ScriptSection( "Operations", "", &mOutLine1Templ, 0, 1 ) );
479 pPublic->AddSection( new ScriptSection( "Attributes", "", &mOutLine1Templ, 0, 1 ) );
480
481
482 pProtected->AddSection( new ScriptSection( "Operations", "", &mOutLine1Templ, 0, 1 ) );
483 pProtected->AddSection( new ScriptSection( "Attributes", "", &mOutLine1Templ, 0, 1 ) );
484
485
486 pPrivate->AddSection( new ScriptSection( "Operations", "", &mOutLine1Templ, 0, 1 ) );
487 pPrivate->AddSection( new ScriptSection( "Attributes", "", &mOutLine1Templ, 0, 1 ) );
488
489 mpCurClassSect->AddSection( pSuper );
490 mpCurClassSect->AddSection( pPublic );
491 mpCurClassSect->AddSection( pProtected );
492 mpCurClassSect->AddSection( pPrivate );
493
494 mpClassIdx->AddSection( mpCurClassSect, TRUE );
495 }
496
497 void RipperDocGen::VisitAttribute( spAttribute& attr )
498 {
499 string body;
500 body += mTags[TAG_BOLD].start;
501 body += attr.mType;
502 body += mTags[TAG_BOLD].end;
503
504 body += mTags[TAG_ITALIC].start;
505 body += ' ';
506 body += attr.GetName();
507 body += mTags[TAG_ITALIC].end;
508
509 string line;
510 AppendHighlightedSource( line, body );
511 AppendComments( attr, line );
512
513 ScriptSection* pSection =
514 new ScriptSection( GetScopedName( attr ), line,
515 &mSubContentTempl,
516 GetRefTemplFor( attr ) );
517
518 if ( attr.mIsConstant )
519
520 mpConstIdx->AddSection( pSection, TRUE );
521
522 else
523 if ( !attr.IsInClass() )
524 {
525 if ( CheckIfUncommented( attr, *mpGlobalVarsIdx ) )
526 return;
527
528 mpGlobalVarsIdx->AddSection( pSection, TRUE );
529 }
530 else
531
532 AddToCurrentClass( pSection, attr, "Attributes" );
533 }
534
535 void RipperDocGen::VisitOperation( spOperation& op )
536 {
537 string body;
538
539 AppendHighlightedSource( body, op.GetFullName(mTags) );
540
541 AppendComments( op, body );
542
543 ScriptSection* pSection =
544 new ScriptSection( GetScopedName( op ), body,
545 &mSubContentTempl,
546 GetRefTemplFor( op ) );
547
548 if ( !op.IsInClass() )
549 {
550 if ( CheckIfUncommented( op, *mpGlobalFuncIdx ) )
551 return;
552
553 mpGlobalFuncIdx->AddSection( pSection, 1 );
554 }
555 else
556 AddToCurrentClass( pSection, op, "Operations" );
557 }
558
559 bool RipperDocGen::OnSaveDocument( ScriptStream& stm )
560 {
561 LinkSuperClassRefs();
562
563 // FOR NOW:: doesn't work yet
564 //mpTopIdx->RemoveEmptySections();
565
566 return 1; // saving can proceed now
567 }
568