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