fpdocclasstree.pp 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. unit fpdocclasstree;
  2. {$mode objfpc}{$H+}
  3. interface
  4. uses
  5. Classes, SysUtils, dGlobals, pastree, contnrs, DOM ,XMLWrite;
  6. Type
  7. TPasObjKindSet = set of TPasObjKind;
  8. { TPasElementNode }
  9. TPasElementNode = Class
  10. Private
  11. FElement : TPasType;
  12. FParentNode: TPasElementNode;
  13. FChildren : TFPObjectList;
  14. function GetChild(aIndex : Integer): TPasElementNode;
  15. function GetChildCount: Integer;
  16. Public
  17. Constructor Create (aElement : TPasType);
  18. Destructor Destroy; override;
  19. Procedure AddChild(C : TPasElementNode);
  20. Procedure SortChildren;
  21. Property Element : TPasType Read FElement;
  22. Property ParentNode : TPasElementNode read FParentNode;
  23. Property Children [aIndex : Integer] : TPasElementNode Read GetChild;
  24. Property ChildCount : Integer Read GetChildCount;
  25. end;
  26. { TClassTreeBuilder }
  27. TClassTreeBuilder = Class
  28. Private
  29. FEngine:TFPDocEngine;
  30. FElementList : TFPObjectHashTable;
  31. FObjectKind : TPasObjKindSet;
  32. FPackage: TPasPackage;
  33. FParentObject : TPasClassType;
  34. FRootNode : TPasElementNode;
  35. FRootObjectName : string;
  36. FRootObjectPathName : string;
  37. Protected
  38. function AddToList(aElement: TPasType): TPasElementNode;
  39. Public
  40. Constructor Create(AEngine:TFPDocEngine; APackage : TPasPackage;
  41. AObjectKind : TPasObjKindSet = okWithFields);
  42. Destructor Destroy; override;
  43. Function BuildTree(AObjects : TStringList) : Integer;
  44. Procedure SaveToXml(AFileName: String);
  45. Property RootNode : TPasElementNode Read FRootNode;
  46. Property PasElToNodes: TFPObjectHashTable read FElementList;
  47. function GetPasElNode (APasEl: TPasElement) : TPasElementNode;
  48. end;
  49. implementation
  50. uses
  51. fpdocstrs, pasresolver;
  52. { TPasElementNode }
  53. function SortOnElementName(Item1, Item2: Pointer): Integer;
  54. begin
  55. Result:=CompareText(TPasElementNode(Item1).Element.Name,TPasElementNode(Item2).Element.Name);
  56. end;
  57. function TPasElementNode.GetChild(aIndex : Integer): TPasElementNode;
  58. begin
  59. if Assigned(FChildren) then
  60. Result:=TPasElementNode(FChildren[aIndex])
  61. else
  62. Raise EListError.Create('Index out of range');
  63. end;
  64. function TPasElementNode.GetChildCount: Integer;
  65. begin
  66. if Assigned(FChildren) then
  67. Result:=FChildren.Count
  68. else
  69. Result:=0
  70. end;
  71. constructor TPasElementNode.Create(aElement: TPasType);
  72. begin
  73. FElement:=aElement;
  74. end;
  75. destructor TPasElementNode.Destroy;
  76. begin
  77. FreeAndNil(FChildren);
  78. inherited Destroy;
  79. end;
  80. procedure TPasElementNode.AddChild(C: TPasElementNode);
  81. begin
  82. if FChildren=Nil then
  83. FChildren:=TFPObjectList.Create(True);
  84. FChildren.Add(C);
  85. end;
  86. procedure TPasElementNode.SortChildren;
  87. begin
  88. if Assigned(FChildren) then
  89. FChildren.Sort(@SortOnElementName);
  90. end;
  91. constructor TClassTreeBuilder.Create(AEngine:TFPDocEngine; APackage : TPasPackage;
  92. AObjectKind: TPasObjKindSet);
  93. begin
  94. FEngine:= AEngine;
  95. FPackage:= APAckage;
  96. FObjectKind:=AObjectKind;
  97. if (okInterface in FObjectkind) then
  98. begin
  99. FRootObjectPathName:='#rtl.System.IInterface';
  100. FRootObjectName:= 'IInterface';
  101. end
  102. else if (FObjectkind * okWithFields) <> [] then
  103. begin
  104. FRootObjectPathName:='#rtl.System.TObject';
  105. FRootObjectName:= 'TObject';
  106. end
  107. else // TODO: I don`t know need it ? Without that the code may be simplified.
  108. begin
  109. FRootObjectPathName:='#rtl.System.TObject';
  110. FRootObjectName:= 'TObject';
  111. end;
  112. FParentObject:=TPasClassType.Create(FRootObjectName,FEngine.FindModule('System'));
  113. if not Assigned(FParentObject) then
  114. FParentObject:=TPasClassType.Create(FRootObjectName,FPackage);
  115. if (okInterface in FObjectkind) then
  116. FParentObject.ObjKind:=okInterface
  117. else if (FObjectkind * okWithFields) <> [] then
  118. FParentObject.ObjKind:=okClass
  119. else
  120. FParentObject.ObjKind:=okClass;
  121. FRootNode:=TPasElementNode.Create(FParentObject);
  122. FRootNode.FParentNode := nil;
  123. FElementList:=TFPObjectHashTable.Create(False);
  124. FElementList.Add(FRootObjectPathName,FRootNode);
  125. end;
  126. destructor TClassTreeBuilder.Destroy;
  127. begin
  128. FreeAndNil(FParentObject);
  129. FreeAndNil(FRootNode);
  130. FreeAndNil(FElementList);
  131. Inherited;
  132. end;
  133. function TClassTreeBuilder.AddToList ( aElement: TPasType
  134. ) : TPasElementNode;
  135. Var
  136. aParentNode : TPasElementNode;
  137. aName : String;
  138. aElementClass: TPasClassType;
  139. begin
  140. Result:= nil; aElementClass:=nil;
  141. if (aElement is TPasClassType) then
  142. aElementClass:= TPasClassType(aElement);
  143. if Assigned(aElementClass) and not (aElementClass.ObjKind in FObjectKind) then exit;
  144. if not Assigned(aElementClass) and not (aElement is TPasAliasType) then exit;
  145. aParentNode:= nil;
  146. if aElement=Nil then
  147. aName:=FRootObjectName
  148. else if (aElement is TPasAliasType) then
  149. aName:=TPasAliasType(aElement).DestType.FullName
  150. else
  151. aName:=aElement.PathName;
  152. Result:=TPasElementNode(FElementList.Items[aName]);
  153. if (Result=Nil) then
  154. begin
  155. if Assigned(aElementClass) and (
  156. (aElementClass.AncestorType is TPasClassType) or
  157. (aElementClass.AncestorType is TPasAliasType)
  158. ) then
  159. aParentNode:=AddToList(aElementClass.AncestorType);
  160. if not Assigned(aParentNode) then
  161. aParentNode:=FRootNode;
  162. if (aElement is TPasAliasType) then
  163. Result:=TPasElementNode.Create(TPasAliasType(TPasType(aElement)).DestType)
  164. else
  165. Result:=TPasElementNode.Create(aElement);
  166. aParentNode.AddChild(Result);
  167. Result.FParentNode := aParentNode;
  168. FElementList.Add(aName,Result);
  169. end;
  170. end;
  171. function TClassTreeBuilder.BuildTree ( AObjects: TStringList ) : Integer;
  172. (*
  173. Procedure DumpNode(Prefix : String; N : TPasElementNode);
  174. Var
  175. I : Integer;
  176. begin
  177. Writeln(Prefix,N.FElement.Name);
  178. if Assigned(N.FChildren) then
  179. For I:=0 to N.FChildren.Count-1 do
  180. DumpNode(Prefix+' ',TPasElementNode(N.FChildren[i]));
  181. end;
  182. *)
  183. Var
  184. I : Integer;
  185. PC : TPasClassType;
  186. begin
  187. Result:=0;
  188. For I:=0 to AObjects.Count-1 do
  189. // Advanced records
  190. if AObjects.Objects[i] is TPasClassType then
  191. begin
  192. PC:=AObjects.Objects[i] as TPasClassType;
  193. AddToList(PC);
  194. end;
  195. end;
  196. function TClassTreeBuilder.GetPasElNode ( APasEl: TPasElement
  197. ) : TPasElementNode;
  198. begin
  199. Result:= TPasElementNode(FElementList.Items[APasEl.PathName]);
  200. end;
  201. procedure TClassTreeBuilder.SaveToXml ( AFileName: String );
  202. procedure AddPasElChildsToXml (ParentxmlEl : TDOMElement ; ParentPasEl: TPasElementNode ) ;
  203. var
  204. CounterVar: Integer;
  205. PasElNode: TPasElementNode;
  206. AXmlDoc: TDOMDocument;
  207. xmlEl: TDOMElement;
  208. M: TPasModule;
  209. begin
  210. if not Assigned(ParentPasEl) or (ParentPasEl.ChildCount = 0) then exit;
  211. AXmlDoc:= ParentxmlEl.OwnerDocument;
  212. for CounterVar := 0 to ParentPasEl.ChildCount-1 do
  213. begin
  214. PasElNode:= ParentPasEl.Children[CounterVar];
  215. xmlEl:= AXmlDoc.CreateElement(UTF8Decode(PasElNode.Element.Name));
  216. M:= PasElNode.Element.GetModule;
  217. xmlEl['unit'] := UTF8Decode(M.Name);
  218. xmlEl['package'] := UTF8Decode(M.PackageName);
  219. xmlEl['type'] := UTF8Decode(GetElementTypeName(PasElNode.Element));
  220. ParentxmlEl.AppendChild(xmlEl);
  221. AddPasElChildsToXml(xmlEl, PasElNode);
  222. end;
  223. end;
  224. var
  225. XmlDoc: TXMLDocument;
  226. XmlRootEl: TDOMElement;
  227. M: TPasModule;
  228. begin
  229. XmlDoc:= TXMLDocument.Create;
  230. XmlDoc.AppendChild(XmlDoc.CreateComment(UTF8Decode(SDocGeneratedByComment)));
  231. try
  232. XmlRootEl:= XmlDoc.CreateElement(UTF8Decode(FRootNode.Element.Name));
  233. M:= FRootNode.Element.GetModule;
  234. if Assigned(M) then
  235. begin
  236. XmlRootEl['unit'] := UTF8Decode(M.Name);
  237. XmlRootEl['package'] := UTF8Decode(M.PackageName);
  238. XmlRootEl['type'] := UTF8Decode(GetElementTypeName(FRootNode.Element));
  239. end
  240. else
  241. begin
  242. XmlRootEl['unit'] := 'system';
  243. XmlRootEl['package'] := 'rtl';
  244. if (okWithFields * FObjectKind) <> [] then
  245. XmlRootEl['type'] := 'class'
  246. else if (okInterface in FObjectKind) then
  247. XmlRootEl['type'] := 'interface'
  248. else
  249. XmlRootEl['type'] := 'class';
  250. end;
  251. XmlDoc.AppendChild(XmlRootEl);
  252. AddPasElChildsToXml(XmlRootEl, FRootNode);
  253. WriteXMLFile(XmlDoc, AFileName);
  254. finally
  255. XmlDoc.Free;
  256. end;
  257. end;
  258. end.