fpclasschart.pp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455
  1. {
  2. FPClass chart - Free Pascal class chart generation tool
  3. Copyright (c) 2008 - Michael Van Canneyt, [email protected]
  4. * Free Pascal class chart generation tool
  5. See the file COPYING, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. }
  11. {$mode objfpc}
  12. {$h+}
  13. program fpclasschart;
  14. uses
  15. SysUtils, Classes, Typinfo, Gettext, dom, xmlread,
  16. dGlobals, PasTree, PParser,PScanner, xmlwrite;
  17. resourcestring
  18. STitle = 'fpClassTree - Create class tree from pascal sources';
  19. SVersion = 'Version %s [%s]';
  20. SCopyright = '(c) 2008 - Michael Van Canneyt, [email protected]';
  21. SCmdLineHelp = 'See documentation for usage.';
  22. SCmdLineInvalidOption = 'Ignoring unknown option "%s"';
  23. SDone = 'Done.';
  24. SSkipMerge = 'Cannot merge %s into %s tree.';
  25. SErrNoSuchMergeFile = 'Merge file %s does not exist.';
  26. SMergedFile = 'Merged %d classes from file %s.';
  27. SClassesAdded = 'Added %d classes from %d files.';
  28. Const
  29. RootNames : Array[TPasObjKind] of string
  30. = ('Objects', 'Classes', 'Interfaces');
  31. type
  32. { TClassTreeEngine }
  33. TClassTreeEngine = class(TFPDocEngine)
  34. Private
  35. FClassTree : TXMLDocument;
  36. FTreeStart : TDomElement;
  37. FObjects : TStringList;
  38. FObjectKind : TPasObjKind;
  39. FParentObject : TPasClassType;
  40. function LookForElement(PE: TDomElement; AElement: TPasElement): TDomNode;
  41. function NodeMatch(N: TDomNode; AElement: TPasElement): Boolean;
  42. Function AddToClassTree(AElement : TPasElement; Var ACount : Integer) : TDomElement;
  43. public
  44. Constructor Create(AClassTree : TXMLDocument; AObjectKind : TPasObjKind);
  45. Destructor Destroy; override;
  46. Function BuildTree : Integer;
  47. function CreateElement(AClass: TPTreeElement; const AName: String;
  48. AParent: TPasElement; AVisibility :TPasMemberVisibility;
  49. const ASourceFilename: String; ASourceLinenumber: Integer): TPasElement; override;
  50. end;
  51. const
  52. OSTarget: String = {$I %FPCTARGETOS%};
  53. CPUTarget: String = {$I %FPCTARGETCPU%};
  54. FPCVersion: String = {$I %FPCVERSION%};
  55. FPCDate: String = {$I %FPCDATE%};
  56. function TClassTreeEngine.CreateElement(AClass: TPTreeElement; const AName: String;
  57. AParent: TPasElement; AVisibility : TPasMemberVisibility;
  58. const ASourceFilename: String; ASourceLinenumber: Integer): TPasElement;
  59. Var
  60. DN : TDocNode;
  61. begin
  62. Result := AClass.Create(AName, AParent);
  63. Result.Visibility:=AVisibility;
  64. if AClass.InheritsFrom(TPasModule) then
  65. CurModule := TPasModule(Result);
  66. If AClass.InheritsFrom(TPasClassType) then
  67. FObjects.AddObject(AName,Result);
  68. end;
  69. Constructor TClassTreeEngine.Create(AClassTree : TXMLDocument; AObjectKind : TPasObjKind);
  70. Var
  71. N : TDomNode;
  72. begin
  73. FClassTree:=AClassTree;
  74. FTreeStart:=FClassTree.DocumentElement;
  75. FPackage:=TPasPackage.Create('dummy',Nil);
  76. FObjectKind:=AObjectKind;
  77. FObjects:=TStringList.Create;
  78. Case FObjectkind of
  79. okObject : FParentObject:=TPasClassType.Create('TObject',FPackage);
  80. okClass : FParentObject:=TPasClassType.Create('TObject',FPackage);
  81. okInterface : FParentObject:=TPasClassType.Create('IInterface',FPackage);
  82. end;
  83. FParentObject.ObjKind:=FObjectKind;
  84. Inherited Create;
  85. end;
  86. destructor TClassTreeEngine.Destroy;
  87. begin
  88. FreeAndNil(FObjects);
  89. inherited Destroy;
  90. end;
  91. Function TClassTreeEngine.BuildTree : Integer;
  92. Var
  93. I : Integer;
  94. PC : TPasClassType;
  95. begin
  96. Result:=0;
  97. FObjects.Sorted:=True;
  98. For I:=0 to FObjects.Count-1 do
  99. begin
  100. PC:=TPasClassType(FObjects.Objects[i]);
  101. If (PC.ObjKind=FObjectKind) and Not PC.IsForward then
  102. AddToClassTree(PC as TPasElement,Result)
  103. end;
  104. end;
  105. Function TClassTreeEngine.NodeMatch(N : TDomNode; AElement : TPasElement) : Boolean;
  106. begin
  107. Result:=(N.NodeType=ELEMENT_NODE) and (CompareText(N.NodeName,AElement.Name)=0)
  108. end;
  109. Function TClassTreeEngine.LookForElement(PE : TDomElement; AElement : TPasElement) : TDomNode;
  110. Var
  111. N : TDomNode;
  112. begin
  113. Result:=PE.FirstChild;
  114. While (Result<>Nil) and Not NodeMatch(Result,AElement) do
  115. Result:=Result.NextSibling;
  116. If (Result=Nil) then
  117. begin
  118. N:=PE.FirstChild;
  119. While (Result=Nil) and (N<>Nil) do
  120. begin
  121. if (N.NodeType=ELEMENT_NODE) then
  122. begin
  123. Result:=LookForElement(N as TDomElement,AElement);
  124. end;
  125. N:=N.NextSibling;
  126. end;
  127. end
  128. end;
  129. Function TClassTreeEngine.AddToClassTree(AElement : TPasElement; Var ACount : Integer) : TDomElement;
  130. Var
  131. PC : TPasClassType;
  132. PE : TDomElement;
  133. M : TPasModule;
  134. N : TDomNode;
  135. begin
  136. PE:=Nil;
  137. If (AElement is TPasClassType) then
  138. begin
  139. PC:=AElement as TPasClassType;
  140. If not Assigned(PC.AncestorType) and (CompareText(PC.Name,FParentObject.Name)<>0) then
  141. PC.AncestorType:=FParentObject;
  142. If Assigned(PC.AncestorType) then
  143. PE:=AddToClassTree(PC.AncestorType,ACount);
  144. end;
  145. If (PE=Nil) then
  146. PE:=FTreeStart;
  147. N:=LookForElement(PE,AElement);
  148. If (N<>Nil) then
  149. Result:=N as TDomElement
  150. else
  151. begin
  152. Inc(ACount);
  153. Result:=FClassTree.CreateElement(AElement.Name);
  154. If Not (AElement is TPasUnresolvedTypeRef) then
  155. begin
  156. M:=AElement.GetModule;
  157. if Assigned(M) then
  158. Result['unit']:=M.Name;
  159. end;
  160. PE.AppendChild(Result);
  161. end;
  162. end;
  163. { ---------------------------------------------------------------------
  164. Main program. Document all units.
  165. ---------------------------------------------------------------------}
  166. Function MergeNodes(Doc : TXMLDocument;Dest,Source : TDomElement) : Integer;
  167. Var
  168. N : TDomNode;
  169. S,E : TDomElement;
  170. begin
  171. N:=Source.FirstChild;
  172. While (N<>Nil) do
  173. begin
  174. if (N.NodeType=ELEMENT_NODE) then
  175. begin
  176. S:=N as TDomElement;
  177. E:=Dest.FindNode(N.NodeName) as TDomElement;
  178. If (E=Nil) then
  179. begin
  180. E:=Doc.CreateElement(N.NodeName);
  181. If S['unit']<>'' then
  182. E['Unit']:=S['unit'];
  183. Dest.AppendChild(E);
  184. Inc(Result);
  185. end;
  186. Result:=Result+MergeNodes(Doc,E,S);
  187. end;
  188. N:=N.NextSibling;
  189. end;
  190. end;
  191. Function MergeTrees (Dest,Source : TXMLDocument) : Integer;
  192. Var
  193. S,D : TDomElement;
  194. Count : Integer;
  195. begin
  196. Result:=0;
  197. D:=Dest.DocumentElement;
  198. S:=Source.DocumentElement;
  199. If (S.NodeName=D.NodeName) then
  200. Result:=MergeNodes(Dest,D,S)
  201. else
  202. Writeln(StdErr,Format(SSkipMerge,[S.NodeName,D.NodeName]));
  203. end;
  204. Function AnalyseFiles(Const AOutputName : String; InputFiles,MergeFiles : TStrings; AObjectKind : TPasObjKind) : String;
  205. Var
  206. XML,XML2 : TXMLDocument;
  207. I,ACount : Integer;
  208. Engine: TClassTreeEngine;
  209. begin
  210. XML:=TXMLDocument.Create;
  211. Try
  212. //XML.
  213. XML.AppendChild(XML.CreateElement(RootNames[AObjectKind]));
  214. For I:=0 to MergeFiles.Count-1 do
  215. begin
  216. XMl2:=TXMLDocument.Create;
  217. ReadXMLFile(XML2,MergeFiles[i]);
  218. try
  219. ACount:=MergeTrees(XML,XML2);
  220. WriteLn(StdErr,Format(SMergedFile,[ACount,MergeFiles[i]]));
  221. Finally
  222. FreeAndNil(XML2);
  223. end;
  224. end;
  225. ACount:=0;
  226. For I:=0 to InputFiles.Count-1 do
  227. begin
  228. Engine := TClassTreeEngine.Create(XML,AObjectKind);
  229. Try
  230. ParseSource(Engine,InputFiles[I],OSTarget,CPUTarget);
  231. ACount:=ACount+Engine.BuildTree;
  232. Finally
  233. Engine.Free;
  234. end;
  235. end;
  236. WriteXMlFile(XML,AOutputName);
  237. Writeln(StdErr,Format(SClassesAdded,[ACount,InputFiles.Count]));
  238. Finally
  239. XML.Free;
  240. end;
  241. end;
  242. { ---------------------------------------------------------------------
  243. Option management
  244. ---------------------------------------------------------------------}
  245. var
  246. cmdObjectKind : TPasObjKind;
  247. InputFiles,
  248. MergeFiles : TStringList;
  249. DocLang : String;
  250. PackageName,
  251. OutputName: String;
  252. procedure InitOptions;
  253. begin
  254. InputFiles := TStringList.Create;
  255. MergeFiles := TStringList.Create;
  256. end;
  257. procedure FreeOptions;
  258. begin
  259. MergeFiles.Free;
  260. InputFiles.Free;
  261. end;
  262. { ---------------------------------------------------------------------
  263. Usage
  264. ---------------------------------------------------------------------}
  265. Procedure Usage;
  266. begin
  267. Writeln('Usage : ',ExtractFileName(Paramstr(0)),' [options]');
  268. Writeln('Where [options] is one or more of :');
  269. Writeln(' --merge=filename Filename with object tree to merge.');
  270. Writeln(' --help Emit help.');
  271. Writeln(' --input=cmdline Input file to create skeleton for.');
  272. Writeln(' Use options are as for compiler.');
  273. Writeln(' --kind=objectkind Specify object kind. One of object, class, interface.');
  274. Writeln(' --lang=language Use selected language.');
  275. Writeln(' --output=filename Send output to file.');
  276. end;
  277. procedure ParseOption(const s: String);
  278. procedure AddToFileList(List: TStringList; const FileName: String);
  279. var
  280. f: Text;
  281. s: String;
  282. begin
  283. if Copy(FileName, 1, 1) = '@' then
  284. begin
  285. Assign(f, Copy(FileName, 2, Length(FileName)));
  286. Reset(f);
  287. while not EOF(f) do
  288. begin
  289. ReadLn(f, s);
  290. List.Add(s);
  291. end;
  292. Close(f);
  293. end else
  294. List.Add(FileName);
  295. end;
  296. var
  297. i: Integer;
  298. Cmd, Arg: String;
  299. begin
  300. cmdObjectKind:=okClass;
  301. if (s = '-h') or (s = '--help') then
  302. begin
  303. Usage;
  304. Halt(0);
  305. end;
  306. i := Pos('=', s);
  307. if i > 0 then
  308. begin
  309. Cmd := Copy(s, 1, i - 1);
  310. Arg := Copy(s, i + 1, Length(s));
  311. end else
  312. begin
  313. Cmd := s;
  314. SetLength(Arg, 0);
  315. end;
  316. if (Cmd = '-i') or (Cmd = '--input') then
  317. AddToFileList(InputFiles, Arg)
  318. else if (Cmd = '-l') or (Cmd = '--lang') then
  319. DocLang := Arg
  320. else if (Cmd = '-o') or (Cmd = '--output') then
  321. OutputName := Arg
  322. else if (Cmd = '-k') or (Cmd = '--kind') then
  323. cmdObjectKind:=TPasObjKind(GetEnumValue(TypeInfo(TPasObjKind),'ok'+Arg))
  324. else if Cmd = '--merge' then
  325. begin
  326. if FileExists(Arg) then
  327. MergeFiles.Add(Arg)
  328. else
  329. Writeln(StdErr,Format(SErrNoSuchMergeFile,[arg]));
  330. end
  331. else
  332. begin
  333. WriteLn(StdErr, Format(SCmdLineInvalidOption, [s]));
  334. Usage;
  335. Halt(1);
  336. end;
  337. end;
  338. Function ParseCommandLine : Integer;
  339. Const
  340. {$IFDEF Unix}
  341. MoFileTemplate = '/usr/local/share/locale/%s/LC_MESSAGES/makeskel.mo';
  342. {$ELSE}
  343. MoFileTemplate ='intl/makeskel.%s.mo';
  344. {$ENDIF}
  345. var
  346. MOFilename: string;
  347. i: Integer;
  348. begin
  349. Result:=0;
  350. DocLang:='';
  351. for i := 1 to ParamCount do
  352. ParseOption(ParamStr(i));
  353. If (DocLang<>'') then
  354. begin
  355. MOFilename:=Format(MOFileTemplate,[DocLang]);
  356. if FileExists(MOFilename) then
  357. gettext.TranslateResourceStrings(MoFileName)
  358. else
  359. writeln('NOTE: unable to find tranlation file ',MOFilename);
  360. // Translate internal documentation strings
  361. TranslateDocStrings(DocLang);
  362. end;
  363. end;
  364. { ---------------------------------------------------------------------
  365. Main Program
  366. ---------------------------------------------------------------------}
  367. Procedure Run;
  368. var
  369. E: Integer;
  370. begin
  371. WriteLn(STitle);
  372. WriteLn(Format(SVersion, [FPCVersion, FPCDate]));
  373. WriteLn(SCopyright);
  374. InitOptions;
  375. Try
  376. E:=ParseCommandLine;
  377. If E<>0 then
  378. Halt(E);
  379. WriteLn;
  380. AnalyseFiles(OutputName,InputFiles,MergeFiles,cmdObjectKind);
  381. WriteLn(StdErr,SDone);
  382. Finally
  383. FreeOptions;
  384. end;
  385. end;
  386. Begin
  387. Run;
  388. end.