123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754 |
- {
- FPClass chart - Free Pascal class chart generation tool
- Copyright (c) 2008 - Michael Van Canneyt, [email protected]
- * Free Pascal class chart generation tool
- See the file COPYING, included in this distribution,
- for details about the copyright.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- }
- {$mode objfpc}
- {$h+}
- program fpclasschart;
- uses
- SysUtils, Classes, Typinfo, Gettext, dom, xmlread,
- dGlobals, PasTree, PParser,PScanner, xmlwrite, fpdocclasstree;
- resourcestring
- STitle = 'fpClassTree - Create class tree from pascal sources';
- SVersion = 'Version %s [%s]';
- SCopyright = '(c) 2008 - Michael Van Canneyt, [email protected]';
- SCmdLineHelp = 'See documentation for usage.';
- SCmdLineInvalidOption = 'Ignoring unknown option "%s"';
- SDone = 'Done.';
- SSkipMerge = 'Cannot merge %s into %s tree.';
- SErrNoSuchMergeFile = 'Merge file %s does not exist.';
- SMergedFile = 'Merged %d classes from file %s.';
- SClassesAdded = 'Added %d classes from %d files.';
- type
- { TClassTreeEngine }
- TClassTreeEngine = class(TFPDocEngine)
- Private
- FTree : TClassTreeBuilder;
- FObjects : TStringList;
- public
- Constructor Create(AClassTree : TXMLDocument; AObjectKind : TPasObjKind);
- Destructor Destroy; override;
- function CreateElement(AClass: TPTreeElement; const AName: String;
- AParent: TPasElement; AVisibility :TPasMemberVisibility;
- const ASourceFilename: String; ASourceLinenumber: Integer): TPasElement; override;
- end;
- { TClassChartFormatter }
- TClassMode = (cmNormal,cmSubClass,cmheadClass,cmFirstClass);
- TClassChartFormatter = Class (TObject)
- private
- FClassMode: TClassMode;
- FClassTree: TXMLDocument;
- FCurrentColCount: Integer;
- FCurrentRowCount: Integer;
- FFileName: String;
- FLargeHeadClassObjects: TStrings;
- FLevel: Integer;
- FMaxObjectsPerColumn: Integer;
- FStartColumnObjects: TStrings;
- Protected
- procedure FirstClass(E : TDomElement); virtual;
- procedure DoEmitClass(E : TDomElement); virtual;
- procedure DoHeadClass(E: TDomElement); virtual;
- procedure DoNextColumn(E: TDomElement); virtual;
- procedure EndSubClass(E: TDomElement; HasSiblings : Boolean); virtual;
- procedure StartSubClass(E: TDomElement); virtual;
- Procedure StartChart; virtual;
- Procedure EndChart; virtual;
- procedure EmitClass(E : TDomElement; HasSiblings : Boolean);
- Public
- Constructor Create (AXML : TXMLDocument); virtual;
- Destructor Destroy; override;
- Procedure CreateChart;
- Property CurrentColCount : Integer Read FCurrentColCount;
- Property CurrentRowCount : Integer Read FCurrentRowCount;
- Property ClassTree : TXMLDocument Read FClassTree;
- Property Level : Integer Read FLevel Write FLevel;
- Property ClassMode : TClassMode Read FClassMode;
- Published
- Property FileName : String Read FFileName Write FFilename;
- Property StartColumnObjects : TStrings Read FStartColumnObjects;
- Property LargeHeadClassObjects : TStrings Read FLargeHeadClassObjects;
- Property MaxObjectsPerColumn : Integer Read FMaxObjectsPerColumn Write FMaxObjectsPerColumn;
- end;
- { TClassTreeBuilder }
- { TChartFormatter }
- constructor TClassChartFormatter.Create(AXML: TXMLDocument);
- begin
- FClassTree:=AXML;
- MaxObjectsPerColumn:=60;
- FStartColumnObjects:=TStringList.Create;
- FLargeHeadClassObjects:=TStringList.Create;
- FLargeHeadClassObjects.Add('TPersistent');
- FLargeHeadClassObjects.Add('TComponent');
- end;
- destructor TClassChartFormatter.Destroy;
- begin
- FreeAndNil(FStartColumnObjects);
- FreeAndNil(FLargeHeadClassObjects);
- Inherited;
- end;
- procedure TClassChartFormatter.CreateChart;
- Var
- N : TDomNode;
- E : TDomElement;
- I : Integer;
- L : TFPList;
- begin
- (FStartColumnObjects as TStringList).Sorted:=False;
- (FLargeHeadClassObjects as TStringList).Sorted:=False;
- StartChart;
- try
- N:=FClassTree.DocumentElement.FirstChild;
- FCurrentColCount:=0;
- FCurrentRowCount:=0;
- FLevel:=0;
- L:=TFPList.Create;
- try
- While (N<>nil) do
- begin
- If (N.NodeType=ELEMENT_NODE) then
- L.Add(N);
- N:=N.NextSibling;
- end;
- If (L.Count>0) then
- begin
- FirstClass(TDomElement(L[0]));
- For I:=0 to L.Count-1 do
- EmitClass(TDomElement(L[i]),I<L.Count-1);
- end;
- finally
- L.Free;
- end;
- L:=TFPList.Create;
- try
- For I:=0 to FLargeHeadClassObjects.Count-1 do
- If Assigned(FLargeHeadClassObjects.Objects[i]) then
- L.Add(FLargeHeadClassObjects.Objects[i]);
- FLargeHeadClassObjects.Clear;
- For I:=0 to L.Count-1 do
- begin
- E:= TDomElement(L[i]);
- DoHeadClass(E);
- EmitClass(E,I<L.Count-1);
- end;
- finally
- L.Free;
- end;
- finally
- EndChart;
- end;
- end;
- procedure TClassChartFormatter.FirstClass(E : TDomElement);
- begin
- FClassMode:=cmFirstClass;
- end;
- procedure TClassChartFormatter.DoEmitClass(E : TDomElement);
- begin
- //Reset
- FClassMode:=cmNormal;
- end;
- procedure TClassChartFormatter.DoHeadClass(E : TDomElement);
- begin
- DoNextColumn(E);
- FClassMode:=cmHeadClass;
- // Do nothing
- end;
- procedure TClassChartFormatter.StartSubClass(E : TDomElement);
- begin
- FClassMode:=cmSubClass;
- end;
- procedure TClassChartFormatter.EndSubClass(E : TDomElement; HasSiblings : Boolean);
- begin
- FClassMode:=cmNormal;
- end;
- procedure TClassChartFormatter.DoNextColumn(E : TDomElement);
- begin
- Inc(FCurrentColCount);
- FCurrentRowCount:=0;
- end;
- procedure TClassChartFormatter.StartChart;
- begin
- // Do nothing
- end;
- procedure TClassChartFormatter.EndChart;
- begin
- // Do nothing
- end;
- procedure TClassChartFormatter.EmitClass(E : TDomElement; HasSiblings: Boolean);
- Var
- DidSub : Boolean;
- N : TDomNode;
- I : Integer;
- L : TFPList;
- begin
- Inc(Flevel);
- try
- I:=FStartColumnObjects.IndexOf(E.NodeName);
- if (-1<>I) or ((FCurrentRowCount>MaxObjectsPerColumn) and (FLevel=2)) then
- DoNextColumn(E)
- else
- begin
- I:=FLargeHeadClassObjects.IndexOf(E.NodeName);
- if (-1<>I) then
- begin
- FLargeHeadClassObjects.Objects[i]:=E;
- Exit; // Must be picked up later.
- end;
- end;
- DoEmitClass(E);
- N:=E.FirstChild;
- DidSub:=False;
- L:=TFPList.Create;
- try
- While (N<>Nil) do
- begin
- if (N.NodeType=ELEMENT_NODE) then
- L.Add(N);
- N:=N.NextSibling;
- end;
- If L.Count>0 then
- begin
- StartSubClass(TDomElement(L[0]));
- For I:=0 to L.Count-1 do
- begin
- EmitClass(TDomElement(L[i]),I<L.Count-1);
- FClassMode:=cmNormal;
- end;
- EndSubClass(E,HasSiblings);
- end;
- Finally
- L.Free;
- end;
- Inc(FCurrentRowCount);
- finally
- Dec(Flevel);
- end;
- end;
- Type
- { TPostScriptClassChartFormatter }
- TPostScriptClassChartFormatter = Class(TClassChartFormatter)
- FFile : Text;
- FMode : TClassMode;
- FIndent : Integer;
- Procedure EmitLine(S : String);
- Protected
- procedure DoEmitClass(E : TDomElement); override;
- procedure DoNextColumn(E: TDomElement); override;
- procedure DoHeadClass(E: TDomElement); override;
- procedure StartSubClass(E: TDomElement); override;
- procedure EndSubClass(E: TDomElement; HasSiblings : Boolean); override;
- Procedure StartChart; override;
- Procedure EndChart; override;
- end;
- { TPostScriptClassChartFormatter }
- procedure TPostScriptClassChartFormatter.EmitLine(S: String);
- begin
- Writeln(FFile,StringofChar(' ',Findent*2),S);
- end;
- procedure TPostScriptClassChartFormatter.DoEmitClass(E: TDomElement);
- begin
- Case ClassMode of
- cmFirstClass : EmitLine(Format('(%s) Ready drawlargebox',[E.NodeName]));
- cmNormal : EmitLine(Format('(%s) Ready newclass',[E.NodeName]));
- cmSubClass : EmitLine(Format('(%s) Ready newchildclass',[E.NodeName]));
- cmHeadClass : EmitLine(Format('(%s) Ready newlargeheadclass',[E.NodeName]));
- end;
- end;
- procedure TPostScriptClassChartFormatter.DoNextColumn(E: TDomElement);
- begin
- Inherited;
- FIndent:=0;
- EmitLine('newcolumn');
- end;
- procedure TPostScriptClassChartFormatter.DoHeadClass(E: TDomElement);
- begin
- // DoNextColumn(E);
- inherited DoHeadClass(E);
- end;
- procedure TPostScriptClassChartFormatter.EndSubClass(E: TDomElement; HasSiblings : Boolean);
- begin
- if HasSiblings then
- EmitLine('onelevelback')
- else
- EmitLine('onelevelbackempty');
- If FIndent>0 then
- Dec(Findent);
- end;
- procedure TPostScriptClassChartFormatter.StartSubClass(E: TDomElement);
- begin
- inherited StartSubClass(E);
- Inc(Findent);
- end;
- procedure TPostScriptClassChartFormatter.StartChart;
- begin
- Assign(FFile,FileName);
- Rewrite(FFile);
- end;
- procedure TPostScriptClassChartFormatter.EndChart;
- begin
- Close(FFile);
- end;
- type
- { TGraphVizClassChartFormatter }
- TGraphVizClassChartFormatter = class(TClassChartFormatter)
- FFile : Text;
- FMode : TClassMode;
- FIndent : integer;
- Procedure EmitLine(S : string);
- Protected
- procedure DoEmitClass(E : TDomElement); override;
- procedure DoNextColumn(E: TDomElement); override;
- procedure DoHeadClass(E: TDomElement); override;
- procedure StartSubClass(E: TDomElement); override;
- procedure EndSubClass(E: TDomElement; HasSiblings : Boolean); override;
- Procedure StartChart; override;
- Procedure EndChart; override;
- end;
- { TGraphVizClassChartFormatter }
- procedure TGraphVizClassChartFormatter.EmitLine(S: String);
- begin
- Writeln(FFile,StringofChar(' ',Findent*2),S);
- end;
- procedure TGraphVizClassChartFormatter.DoEmitClass(E: TDomElement);
- begin
- Case ClassMode of
- cmFirstClass : EmitLine(Format('%s -> %s', [E.ParentNode.NodeName, E.NodeName]));
- cmNormal : EmitLine(Format('%s -> %s', [E.ParentNode.NodeName, E.NodeName]));
- cmSubClass : EmitLine(Format('%s -> %s', [E.ParentNode.NodeName, E.NodeName]));
- cmHeadClass : EmitLine(Format('%s -> %s', [E.ParentNode.NodeName, E.NodeName]));
- end;
- end;
- procedure TGraphVizClassChartFormatter.DoNextColumn(E: TDomElement);
- begin
- Inherited;
- FIndent:=0;
- end;
- procedure TGraphVizClassChartFormatter.DoHeadClass(E: TDomElement);
- begin
- // DoNextColumn(E);
- inherited DoHeadClass(E);
- end;
- procedure TGraphVizClassChartFormatter.EndSubClass(E: TDomElement; HasSiblings : Boolean);
- begin
- If FIndent>0 then
- Dec(Findent);
- end;
- procedure TGraphVizClassChartFormatter.StartSubClass(E: TDomElement);
- begin
- inherited StartSubClass(E);
- Inc(Findent);
- end;
- procedure TGraphVizClassChartFormatter.StartChart;
- begin
- Assign(FFile,FileName);
- Rewrite(FFile);
- EmitLine('digraph G {');
- end;
- procedure TGraphVizClassChartFormatter.EndChart;
- begin
- EmitLine('}');
- Close(FFile);
- end;
- Type
- TOutputFormat = (ofXML,ofPostscript, ofGraphViz);
- Var
- OutputFormat : TOutputFormat = ofXML;
- const
- OSTarget: String = {$I %FPCTARGETOS%};
- CPUTarget: String = {$I %FPCTARGETCPU%};
- FPCVersion: String = {$I %FPCVERSION%};
- FPCDate: String = {$I %FPCDATE%};
- function TClassTreeEngine.CreateElement(AClass: TPTreeElement; const AName: String;
- AParent: TPasElement; AVisibility : TPasMemberVisibility;
- const ASourceFilename: String; ASourceLinenumber: Integer): TPasElement;
- Var
- DN : TDocNode;
- begin
- Result := AClass.Create(AName, AParent);
- Result.Visibility:=AVisibility;
- if AClass.InheritsFrom(TPasModule) then
- CurModule := TPasModule(Result);
- If AClass.InheritsFrom(TPasClassType) then
- FObjects.AddObject(AName,Result);
- end;
- Constructor TClassTreeEngine.Create(AClassTree : TXMLDocument; AObjectKind : TPasObjKind);
- begin
- FPackage:=TPasPackage.Create('dummy',Nil);
- FTree:=TClassTreeBuilder.Create(FPackage,AObjectKind);
- FObjects:=TStringList.Create;
- Inherited Create;
- end;
- destructor TClassTreeEngine.Destroy;
- begin
- FreeAndNil(FObjects);
- inherited Destroy;
- end;
- { ---------------------------------------------------------------------
- Main program. Document all units.
- ---------------------------------------------------------------------}
- Function MergeNodes(Doc : TXMLDocument;Dest,Source : TDomElement) : Integer;
- Var
- N : TDomNode;
- S,E : TDomElement;
- begin
- N:=Source.FirstChild;
- While (N<>Nil) do
- begin
- if (N.NodeType=ELEMENT_NODE) then
- begin
- S:=N as TDomElement;
- E:=Dest.FindNode(N.NodeName) as TDomElement;
- If (E=Nil) then
- begin
- E:=Doc.CreateElement(N.NodeName);
- If S['unit']<>'' then
- E['Unit']:=S['unit'];
- Dest.AppendChild(E);
- Inc(Result);
- end;
- Result:=Result+MergeNodes(Doc,E,S);
- end;
- N:=N.NextSibling;
- end;
- end;
- Function MergeTrees (Dest,Source : TXMLDocument) : Integer;
- Var
- S,D : TDomElement;
- Count : Integer;
- begin
- Result:=0;
- D:=Dest.DocumentElement;
- S:=Source.DocumentElement;
- If (S.NodeName=D.NodeName) then
- Result:=MergeNodes(Dest,D,S)
- else
- Writeln(StdErr,Format(SSkipMerge,[S.NodeName,D.NodeName]));
- end;
-
- Function AnalyseFiles(Const AOutputName : String; InputFiles,MergeFiles : TStrings; AObjectKind : TPasObjKind) : String;
- Var
- XML,XML2 : TXMLDocument;
- I,ACount : Integer;
- Engine: TClassTreeEngine;
- begin
- XML:=TXMLDocument.Create;
- Try
- //XML.
- XML.AppendChild(XML.CreateElement(ObjKindNames[AObjectKind]));
- For I:=0 to MergeFiles.Count-1 do
- begin
- XMl2:=TXMLDocument.Create;
- ReadXMLFile(XML2,MergeFiles[i]);
- try
- ACount:=MergeTrees(XML,XML2);
- WriteLn(StdErr,Format(SMergedFile,[ACount,MergeFiles[i]]));
- Finally
- FreeAndNil(XML2);
- end;
- end;
- ACount:=0;
- For I:=0 to InputFiles.Count-1 do
- begin
- Engine := TClassTreeEngine.Create(XML,AObjectKind);
- Try
- ParseSource(Engine,InputFiles[I],OSTarget,CPUTarget);
- ACount:=ACount+Engine.Ftree.BuildTree(Engine.FObjects);
- Finally
- Engine.Free;
- end;
- end;
- Case OutputFormat of
- ofXML :
- WriteXMlFile(XML,AOutputName);
- ofPostScript :
- With TPostScriptClassChartFormatter.Create(XML) do
- try
- FileName:=AOutputName;
- CreateChart;
- finally
- Free;
- end;
- ofGraphViz :
- With TGraphVizClassChartFormatter.Create(XML) do
- try
- FileName:=AOutputName;
- CreateChart;
- finally
- Free;
- end;
- end;
- Writeln(StdErr,Format(SClassesAdded,[ACount,InputFiles.Count]));
- Finally
- XML.Free;
- end;
- end;
- { ---------------------------------------------------------------------
- Option management
- ---------------------------------------------------------------------}
- var
- cmdObjectKind : TPasObjKind;
- InputFiles,
- MergeFiles : TStringList;
- DocLang : String;
- PackageName,
- OutputName: String;
- procedure InitOptions;
- begin
- InputFiles := TStringList.Create;
- MergeFiles := TStringList.Create;
- end;
- procedure FreeOptions;
- begin
- MergeFiles.Free;
- InputFiles.Free;
- end;
- { ---------------------------------------------------------------------
- Usage
- ---------------------------------------------------------------------}
-
- Procedure Usage;
- begin
- Writeln('Usage : ',ExtractFileName(Paramstr(0)),' [options]');
- Writeln('Where [options] is one or more of :');
- Writeln(' --merge=filename Filename with object tree to merge.');
- Writeln(' --help Emit help.');
- Writeln(' --input=cmdline Input file to create skeleton for.');
- Writeln(' Use options are as for compiler.');
- Writeln(' --kind=objectkind Specify object kind. One of object, class, interface.');
- Writeln(' --lang=language Use selected language.');
- Writeln(' --output=filename Send output to file.');
- Writeln(' --format=name Kind of output to create: XML, PostScript, GraphViz.');
- end;
- procedure ParseOption(const s: String);
- procedure AddToFileList(List: TStringList; const FileName: String);
- var
- f: Text;
- s: String;
- begin
- if Copy(FileName, 1, 1) = '@' then
- begin
- Assign(f, Copy(FileName, 2, Length(FileName)));
- Reset(f);
- while not EOF(f) do
- begin
- ReadLn(f, s);
- List.Add(s);
- end;
- Close(f);
- end else
- List.Add(FileName);
- end;
- var
- i: Integer;
- Cmd, Arg: String;
- begin
- cmdObjectKind:=okClass;
- if (s = '-h') or (s = '--help') then
- begin
- Usage;
- Halt(0);
- end;
- i := Pos('=', s);
- if i > 0 then
- begin
- Cmd := Copy(s, 1, i - 1);
- Arg := Copy(s, i + 1, Length(s));
- end else
- begin
- Cmd := s;
- SetLength(Arg, 0);
- end;
- if (Cmd = '-i') or (Cmd = '--input') then
- AddToFileList(InputFiles, Arg)
- else if (Cmd = '-l') or (Cmd = '--lang') then
- DocLang := Arg
- else if (Cmd = '-o') or (Cmd = '--output') then
- OutputName := Arg
- else if (Cmd = '-k') or (Cmd = '--kind') then
- cmdObjectKind:=TPasObjKind(GetEnumValue(TypeInfo(TPasObjKind),'ok'+Arg))
- else if (Cmd = '-f') or (Cmd = '--format') then
- OutputFormat:=TOutputFormat(GetEnumValue(TypeInfo(TOutputFormat),'of'+Arg))
- else if Cmd = '--merge' then
- begin
- if FileExists(Arg) then
- MergeFiles.Add(Arg)
- else
- Writeln(StdErr,Format(SErrNoSuchMergeFile,[arg]));
- end
- else
- begin
- WriteLn(StdErr, Format(SCmdLineInvalidOption, [s]));
- Usage;
- Halt(1);
- end;
- end;
- Function ParseCommandLine : Integer;
- Const
- {$IFDEF Unix}
- MoFileTemplate = '/usr/local/share/locale/%s/LC_MESSAGES/makeskel.mo';
- {$ELSE}
- MoFileTemplate ='intl/makeskel.%s.mo';
- {$ENDIF}
- var
- MOFilename: string;
- i: Integer;
- begin
- Result:=0;
- if ParamCount=0 then
- begin
- Usage;
- Halt(0);
- end;
- DocLang:='';
- for i := 1 to ParamCount do
- ParseOption(ParamStr(i));
- If (DocLang<>'') then
- begin
- MOFilename:=Format(MOFileTemplate,[DocLang]);
- if FileExists(MOFilename) then
- gettext.TranslateResourceStrings(MoFileName)
- else
- writeln('NOTE: unable to find tranlation file ',MOFilename);
- // Translate internal documentation strings
- TranslateDocStrings(DocLang);
- end;
- end;
- { ---------------------------------------------------------------------
- Main Program
- ---------------------------------------------------------------------}
-
- Procedure Run;
-
- var
- E: Integer;
- begin
- WriteLn(STitle);
- WriteLn(Format(SVersion, [FPCVersion, FPCDate]));
- WriteLn(SCopyright);
-
- InitOptions;
- Try
- E:=ParseCommandLine;
- If E<>0 then
- Halt(E);
- WriteLn;
- AnalyseFiles(OutputName,InputFiles,MergeFiles,cmdObjectKind);
- WriteLn(StdErr,SDone);
- Finally
- FreeOptions;
- end;
- end;
- Begin
- Run;
- end.
|