///////////////////////////////////////////////////////////////////////////// // Name: No names yet. // Purpose: Contrib. demo // Author: Aleksandras Gluchovas // Modified by: // Created: 22/09/98 // RCS-ID: $Id$ // Copyright: (c) Aleskandars Gluchovas // Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// // For compilers that support precompilation, includes "wx/wx.h". #include "wx/wxprec.h" #ifdef __BORLANDC__ #pragma hdrstop #endif #ifndef WX_PRECOMP #include "wx/wx.h" #endif #include "docripper.h" #if wxUSE_IOSTREAMH #include #else #include #endif // script templates // ***** currently only HTML versions of variouse templates available ***** // static const char* HTM_TopTempl = "\n\ \n\n\n\n\n\

$(NAME)

\n\

\n\n\ "; static const char* HTM_ContentIdxTempl = "\ \n\


\n\

$(NAME)

\
    \n\ $(REFLIST)\

\n\n\ "; static const char* HTM_SuperContentTempl = "\ \n\


\n\

$(NAME)

\ $(BODY)\n\ "; static const char* HTM_SubContentTempl = "\
\n\


\n\

$(NAME)

\ $(BODY)\n\ "; static const char* HTM_OutLineTempl = "\

\n\ $(NAME)

\n\ "; static const char* HTM_OutLine1Templ = "\

\n\ $(NAME)\n\

    \n\ $(REFLIST)\
\n\n\ "; static const char* HTM_RefTempl = "\
  • $(NAME)\n\ "; static const char* HTM_DeadRefTempl = "\
  • $(NAME)\n\ "; /***** Implementation for class RipperDocGen *****/ RipperDocGen::RipperDocGen() : mTopTempl ( HTM_TopTempl ), mContentIdxTempl ( HTM_ContentIdxTempl ), mSuperContentTempl( HTM_SuperContentTempl ), mSubContentTempl ( HTM_SubContentTempl ), mOutLineTempl ( HTM_OutLineTempl ), mOutLine1Templ ( HTM_OutLine1Templ ), mRefTempl ( HTM_RefTempl ), mDeadRefTempl ( HTM_DeadRefTempl ), mpCurClassSect(0) { // topIndex is not referenced mpTopIdx = new ScriptSection( "Source Code Contents" , "", &mTopTempl , 0 ); mpClassIdx = new ScriptSection( "Classes Reference" , "", &mContentIdxTempl, &mRefTempl ); mpEnumIdx = new ScriptSection( "Enumerations Reference" , "", &mContentIdxTempl, &mRefTempl ); mpTypeDefIdx = new ScriptSection( "Type Definitions Reference" , "", &mContentIdxTempl, &mRefTempl ); mpMacroIdx = new ScriptSection( "Macros Reference" , "", &mContentIdxTempl, &mRefTempl ); mpGlobalVarsIdx = new ScriptSection( "Global Variables Reference" , "", &mContentIdxTempl, &mRefTempl ); mpGlobalFuncIdx = new ScriptSection( "Global Functions Reference", "", &mContentIdxTempl, &mRefTempl ); mpConstIdx = new ScriptSection( "Constants Reference" , "", &mContentIdxTempl, &mRefTempl ); // assemble top index mpTopIdx->AddSection( mpClassIdx , 1 ); mpTopIdx->AddSection( mpEnumIdx , 1 ); mpTopIdx->AddSection( mpTypeDefIdx , 1 ); mpTopIdx->AddSection( mpMacroIdx , 1 ); mpTopIdx->AddSection( mpGlobalVarsIdx, 1 ); mpTopIdx->AddSection( mpGlobalFuncIdx, 1 ); mpTopIdx->AddSection( mpConstIdx , 1 ); // register reserved variables for index and description templates ScriptSection::RegisterTemplate( mTopTempl ); ScriptSection::RegisterTemplate( mContentIdxTempl ); ScriptSection::RegisterTemplate( mSuperContentTempl ); ScriptSection::RegisterTemplate( mSubContentTempl ); ScriptSection::RegisterTemplate( mOutLineTempl ); ScriptSection::RegisterTemplate( mOutLine1Templ ); ScriptSection::RegisterTemplate( mRefTempl ); ScriptSection::RegisterTemplate( mDeadRefTempl ); // create the top-most (interfile) context mpFileBinderCtx = new spFile(); // the default script is HTML mTags = get_HTML_markup_tags(); mpParser = 0; // no default parser! } void RipperDocGen::Init( SourceParserBase* pParser ) { mpParser = pParser; } RipperDocGen::~RipperDocGen() { delete mpFileBinderCtx; } void RipperDocGen::AppendComments( spContext& fromContext, string& str ) { if ( !fromContext.HasComments() ) return; size_t start = str.length(); str += mTags[TAG_BOLD].end; str += mTags[TAG_PARAGRAPH].start; MCommentListT& lst = fromContext.GetCommentList(); for( size_t i = 0; i != lst.size(); ++i ) { if ( i != 0 ) if ( lst[i]->StartsParagraph() ) { str += mTags[TAG_PARAGRAPH].start; } str += lst[i]->mText; } // remove new lines, and insert paragraph breaks // if empty lines found size_t len = str.length(); for( size_t n = start; n != len; ++n ) if ( str[n] == 10 || str[n] == 13 ) { if ( n + 2 < len ) { if ( ( str[n] == 13 && str[n+1] == 10 && // FIXME:: quick-hack str[n+2] == 13 ) || ( str[n] == 10 && str[n+1] == 10 ) ) { str.insert( n + 1, "

    " ); // FIXME:: quick-hack len += 3; } } str[n] = ' '; } str += mTags[TAG_PARAGRAPH].end; } void RipperDocGen::AppendMulitilineStr( string& st, string& mlStr ) { st = mTags[TAG_FIXED_FONT].start; st += mlStr; st += mTags[TAG_FIXED_FONT].end; } void RipperDocGen::AppendHighlightedSource( string& st, string source ) { // FIXME:: below should not be fixed :) char buf[1024*32]; // DBG::: // ASSERT( source.length() + 1 < sizeof(buf) ); strcpy( buf, source.c_str() ); // highlight things mSrcPainter.Init(); mSrcPainter.ProcessSource( buf, strlen(buf) ); mSrcPainter.GetResultString( st, mTags ); } bool RipperDocGen::CheckIfUncommented( spContext& ctx, ScriptSection& toSect ) { if ( ctx.HasComments() ) return 0; toSect.AddReference( new ScriptSection( GetScopedName( ctx ), "", 0, &mDeadRefTempl ) ); return 1; } ScriptTemplate* RipperDocGen::GetRefTemplFor( spContext& ctx ) { if ( ctx.HasComments() ) return &mRefTempl; else return &mDeadRefTempl; } string RipperDocGen::GetScopedName( spContext& ofCtx ) { if ( ofCtx.IsInFile() ) return ofCtx.GetName(); else return ofCtx.GetOutterContext()->GetName() + "::" + ofCtx.GetName(); } void RipperDocGen::AddToCurrentClass( ScriptSection* pSection, spContext& ctx, const char* subSectionName ) { string sName; if ( ctx.mVisibility == SP_VIS_PROTECTED ) sName = "Protected members/"; else if ( ctx.mVisibility == SP_VIS_PRIVATE ) sName = "Private members/"; else sName = "Public members/"; sName += subSectionName; ScriptSection* pSect = mpCurClassSect->GetSubsection( sName.c_str() ); if ( CheckIfUncommented( ctx, *pSect ) ) { delete pSection; return; } pSect->AddReference( pSection ); mpCurClassSect->AddSection( pSection ); } void RipperDocGen::LinkSuperClassRefs() { MMemberListT clLst; // collect all classes in the context tree mpFileBinderCtx->GetContextList( clLst, SP_CTX_CLASS ); for( size_t i = 0; i != clLst.size(); ++i ) { spClass& cl = *((spClass*)clLst[i]); // FIXME:: why sometimes GetUserData() returns NULL? if ( !cl.GetUserData() ) continue; ScriptSection* pClSect = (ScriptSection*)cl.GetUserData(); ScriptSection* pSuperSect = pClSect->GetSubsection("Derived from"); for( size_t n = 0; n != cl.mSuperClassNames.size(); ++n ) { string& superClName = cl.mSuperClassNames[n]; spClass* pFound = NULL; string* name; for( size_t k = 0; k != clLst.size(); ++k ) { name = &clLst[k]->GetName(); if ( clLst[k]->GetName() == superClName ) { pFound = (spClass*)clLst[k]; break; } } if ( !pFound ) { ScriptSection* pNotFound = new ScriptSection( superClName, "", 0, &mDeadRefTempl ); pSuperSect->AddReference( pNotFound ); } else if ( pFound->GetUserData() ) pSuperSect->AddReference( (ScriptSection*)pFound->GetUserData() ); } } } void RipperDocGen::ProcessFile( const char* sourceFile ) { wxSTD cout << "Processing file " << sourceFile << "..." << wxSTD endl; spFile* pCtx = mpParser->ParseFile( sourceFile ); if ( pCtx == NULL ) { wxSTD cout << "Cannot open file " << sourceFile << ", skipped..." << wxSTD endl; return; } VisitAll( *pCtx, TRUE ); mpFileBinderCtx->AddMember( pCtx ); } // implementations of "visiting procedures" void RipperDocGen::VisitEnumeration( spEnumeration& en ) { // FOR NOW:: do not reference "nameless" enums if ( en.GetName() == "" ) return; if ( CheckIfUncommented( en, *mpEnumIdx ) ) return; string body; body += mTags[TAG_BOLD].start; AppendMulitilineStr( body, en.mEnumContent ); body += mTags[TAG_BOLD].end; string line; AppendHighlightedSource( line, body ); AppendComments( en, line ); mpEnumIdx->AddSection( new ScriptSection( en.GetName(), line, &mSubContentTempl, GetRefTemplFor( en ) ), 1 ); } void RipperDocGen::VisitTypeDef( spTypeDef& td ) { if ( CheckIfUncommented( td, *mpTypeDefIdx ) ) return; string body; body += mTags[TAG_BOLD].start; body += "typdef "; body += mTags[TAG_BOLD].end; AppendMulitilineStr( body, td.mOriginalType ); body += td.mOriginalType; body += ' '; body += mTags[TAG_BOLD].start; body += td.GetName(); body += mTags[TAG_BOLD].end; string line; AppendHighlightedSource( line, body ); AppendComments( td, line ); mpTypeDefIdx->AddSection( new ScriptSection( td.GetName(), line, &mSubContentTempl, GetRefTemplFor( td ) ), TRUE ); } void RipperDocGen::VisitPreprocessorLine( spPreprocessorLine& pd ) { if ( pd.mDefType != SP_PREP_DEF_REDEFINE_SYMBOL ) return; if ( CheckIfUncommented( pd, *mpMacroIdx ) ) return; string body; body += mTags[TAG_FIXED_FONT].start; string coloredLine = pd.mLine; AppendHighlightedSource( coloredLine, pd.mLine ); AppendMulitilineStr( body, coloredLine ); body += mTags[TAG_FIXED_FONT].end; AppendComments( pd, body ); mpMacroIdx->AddSection( new ScriptSection( pd.GetName(), body, &mSubContentTempl, GetRefTemplFor( pd ) ), TRUE ); } void RipperDocGen::VisitClass( spClass& cl ) { // FOR NOW:: do not document nested classes - // nicier visiting method yet needed if ( cl.IsInClass() ) { SkipChildren(); // spVisitor's method return; } string body; AppendComments( cl, body ); mpCurClassSect = new ScriptSection( cl.GetName(), body, &mSuperContentTempl, &mRefTempl ); // set up reference in the class context, pointing back // to the section where this class is represented cl.SetUserData( mpCurClassSect ); ScriptSection* pSuper = new ScriptSection( "Derived from" ,"", &mOutLine1Templ,0, 1 ); ScriptSection* pPublic = new ScriptSection( "Public members" ,"", &mOutLineTempl,0, 1 ); ScriptSection* pProtected = new ScriptSection( "Protected members" ,"", &mOutLineTempl,0, 1 ); ScriptSection* pPrivate = new ScriptSection( "Private members" ,"", &mOutLineTempl,0, 1 ); pPublic->AddSection( new ScriptSection( "Operations", "", &mOutLine1Templ, 0, 1 ) ); pPublic->AddSection( new ScriptSection( "Attributes", "", &mOutLine1Templ, 0, 1 ) ); pProtected->AddSection( new ScriptSection( "Operations", "", &mOutLine1Templ, 0, 1 ) ); pProtected->AddSection( new ScriptSection( "Attributes", "", &mOutLine1Templ, 0, 1 ) ); pPrivate->AddSection( new ScriptSection( "Operations", "", &mOutLine1Templ, 0, 1 ) ); pPrivate->AddSection( new ScriptSection( "Attributes", "", &mOutLine1Templ, 0, 1 ) ); mpCurClassSect->AddSection( pSuper ); mpCurClassSect->AddSection( pPublic ); mpCurClassSect->AddSection( pProtected ); mpCurClassSect->AddSection( pPrivate ); mpClassIdx->AddSection( mpCurClassSect, TRUE ); } void RipperDocGen::VisitAttribute( spAttribute& attr ) { string body; body += mTags[TAG_BOLD].start; body += attr.mType; body += mTags[TAG_BOLD].end; body += mTags[TAG_ITALIC].start; body += ' '; body += attr.GetName(); body += mTags[TAG_ITALIC].end; string line; AppendHighlightedSource( line, body ); AppendComments( attr, line ); ScriptSection* pSection = new ScriptSection( GetScopedName( attr ), line, &mSubContentTempl, GetRefTemplFor( attr ) ); if ( attr.mIsConstant ) mpConstIdx->AddSection( pSection, TRUE ); else if ( !attr.IsInClass() ) { if ( CheckIfUncommented( attr, *mpGlobalVarsIdx ) ) return; mpGlobalVarsIdx->AddSection( pSection, TRUE ); } else AddToCurrentClass( pSection, attr, "Attributes" ); } void RipperDocGen::VisitOperation( spOperation& op ) { string body; AppendHighlightedSource( body, op.GetFullName(mTags) ); AppendComments( op, body ); ScriptSection* pSection = new ScriptSection( GetScopedName( op ), body, &mSubContentTempl, GetRefTemplFor( op ) ); if ( !op.IsInClass() ) { if ( CheckIfUncommented( op, *mpGlobalFuncIdx ) ) return; mpGlobalFuncIdx->AddSection( pSection, 1 ); } else AddToCurrentClass( pSection, op, "Operations" ); } bool RipperDocGen::OnSaveDocument( ScriptStream& stm ) { LinkSuperClassRefs(); // FOR NOW:: doesn't work yet //mpTopIdx->RemoveEmptySections(); return 1; // saving can proceed now }