fpc/utils/fpdoc/fpdocclasstree.pp
2021-01-23 14:29:56 +00:00

299 lines
8.1 KiB
ObjectPascal

unit fpdocclasstree;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, dGlobals, pastree, contnrs, DOM ,XMLWrite;
Type
TPasObjKindSet = set of TPasObjKind;
{ TPasElementNode }
TPasElementNode = Class
Private
FElement : TPasType;
FParentNode: TPasElementNode;
FChildren : TFPObjectList;
function GetChild(aIndex : Integer): TPasElementNode;
function GetChildCount: Integer;
Public
Constructor Create (aElement : TPasType);
Destructor Destroy; override;
Procedure AddChild(C : TPasElementNode);
Procedure SortChildren;
Property Element : TPasType Read FElement;
Property ParentNode : TPasElementNode read FParentNode;
Property Children [aIndex : Integer] : TPasElementNode Read GetChild;
Property ChildCount : Integer Read GetChildCount;
end;
{ TClassTreeBuilder }
TClassTreeBuilder = Class
Private
FEngine:TFPDocEngine;
FElementList : TFPObjectHashTable;
FObjectKind : TPasObjKindSet;
FPackage: TPasPackage;
FParentObject : TPasClassType;
FRootNode : TPasElementNode;
FRootObjectName : string;
FRootObjectPathName : string;
Protected
function AddToList(aElement: TPasType): TPasElementNode;
Public
Constructor Create(AEngine:TFPDocEngine; APackage : TPasPackage;
AObjectKind : TPasObjKindSet = okWithFields);
Destructor Destroy; override;
Function BuildTree(AObjects : TStringList) : Integer;
Procedure SaveToXml(AFileName: String);
Property RootNode : TPasElementNode Read FRootNode;
Property PasElToNodes: TFPObjectHashTable read FElementList;
function GetPasElNode (APasEl: TPasElement) : TPasElementNode;
end;
implementation
uses
fpdocstrs, pasresolver;
{ TPasElementNode }
function SortOnElementName(Item1, Item2: Pointer): Integer;
begin
Result:=CompareText(TPasElementNode(Item1).Element.Name,TPasElementNode(Item2).Element.Name);
end;
function TPasElementNode.GetChild(aIndex : Integer): TPasElementNode;
begin
if Assigned(FChildren) then
Result:=TPasElementNode(FChildren[aIndex])
else
Raise EListError.Create('Index out of range');
end;
function TPasElementNode.GetChildCount: Integer;
begin
if Assigned(FChildren) then
Result:=FChildren.Count
else
Result:=0
end;
constructor TPasElementNode.Create(aElement: TPasType);
begin
FElement:=aElement;
end;
destructor TPasElementNode.Destroy;
begin
FreeAndNil(FChildren);
inherited Destroy;
end;
procedure TPasElementNode.AddChild(C: TPasElementNode);
begin
if FChildren=Nil then
FChildren:=TFPObjectList.Create(True);
FChildren.Add(C);
end;
procedure TPasElementNode.SortChildren;
begin
if Assigned(FChildren) then
FChildren.Sort(@SortOnElementName);
end;
constructor TClassTreeBuilder.Create(AEngine:TFPDocEngine; APackage : TPasPackage;
AObjectKind: TPasObjKindSet);
begin
FEngine:= AEngine;
FPackage:= APAckage;
FObjectKind:=AObjectKind;
if (okInterface in FObjectkind) then
begin
FRootObjectPathName:='#rtl.System.IInterface';
FRootObjectName:= 'IInterface';
end
else if (FObjectkind * okWithFields) <> [] then
begin
FRootObjectPathName:='#rtl.System.TObject';
FRootObjectName:= 'TObject';
end
else // TODO: I don`t know need it ? Without that the code may be simplified.
begin
FRootObjectPathName:='#rtl.System.TObject';
FRootObjectName:= 'TObject';
end;
FParentObject:=TPasClassType.Create(FRootObjectName,FEngine.FindModule('System'));
if not Assigned(FParentObject) then
FParentObject:=TPasClassType.Create(FRootObjectName,FPackage);
if (okInterface in FObjectkind) then
FParentObject.ObjKind:=okInterface
else if (FObjectkind * okWithFields) <> [] then
FParentObject.ObjKind:=okClass
else
FParentObject.ObjKind:=okClass;
FRootNode:=TPasElementNode.Create(FParentObject);
FRootNode.FParentNode := nil;
FElementList:=TFPObjectHashTable.Create(False);
FElementList.Add(FRootObjectPathName,FRootNode);
end;
destructor TClassTreeBuilder.Destroy;
begin
FreeAndNil(FParentObject);
FreeAndNil(FRootNode);
FreeAndNil(FElementList);
Inherited;
end;
function TClassTreeBuilder.AddToList ( aElement: TPasType
) : TPasElementNode;
Var
aParentNode : TPasElementNode;
aName : String;
aElementClass: TPasClassType;
begin
Result:= nil; aElementClass:=nil;
if (aElement is TPasClassType) then
aElementClass:= TPasClassType(aElement);
if Assigned(aElementClass) and not (aElementClass.ObjKind in FObjectKind) then exit;
if not Assigned(aElementClass) and not (aElement is TPasAliasType) then exit;
aParentNode:= nil;
if aElement=Nil then
aName:=FRootObjectName
else if (aElement is TPasAliasType) then
aName:=TPasAliasType(aElement).DestType.FullName
else
aName:=aElement.PathName;
Result:=TPasElementNode(FElementList.Items[aName]);
if (Result=Nil) then
begin
if Assigned(aElementClass) and (
(aElementClass.AncestorType is TPasClassType) or
(aElementClass.AncestorType is TPasAliasType)
) then
aParentNode:=AddToList(aElementClass.AncestorType);
if not Assigned(aParentNode) then
aParentNode:=FRootNode;
if (aElement is TPasAliasType) then
Result:=TPasElementNode.Create(TPasAliasType(TPasType(aElement)).DestType)
else
Result:=TPasElementNode.Create(aElement);
aParentNode.AddChild(Result);
Result.FParentNode := aParentNode;
FElementList.Add(aName,Result);
end;
end;
function TClassTreeBuilder.BuildTree ( AObjects: TStringList ) : Integer;
(*
Procedure DumpNode(Prefix : String; N : TPasElementNode);
Var
I : Integer;
begin
Writeln(Prefix,N.FElement.Name);
if Assigned(N.FChildren) then
For I:=0 to N.FChildren.Count-1 do
DumpNode(Prefix+' ',TPasElementNode(N.FChildren[i]));
end;
*)
Var
I : Integer;
PC : TPasClassType;
begin
Result:=0;
For I:=0 to AObjects.Count-1 do
// Advanced records
if AObjects.Objects[i] is TPasClassType then
begin
PC:=AObjects.Objects[i] as TPasClassType;
AddToList(PC);
end;
end;
function TClassTreeBuilder.GetPasElNode ( APasEl: TPasElement
) : TPasElementNode;
begin
Result:= TPasElementNode(FElementList.Items[APasEl.PathName]);
end;
procedure TClassTreeBuilder.SaveToXml ( AFileName: String );
procedure AddPasElChildsToXml (ParentxmlEl : TDOMElement ; ParentPasEl: TPasElementNode ) ;
var
CounterVar: Integer;
PasElNode: TPasElementNode;
AXmlDoc: TDOMDocument;
xmlEl: TDOMElement;
M: TPasModule;
begin
if not Assigned(ParentPasEl) or (ParentPasEl.ChildCount = 0) then exit;
AXmlDoc:= ParentxmlEl.OwnerDocument;
for CounterVar := 0 to ParentPasEl.ChildCount-1 do
begin
PasElNode:= ParentPasEl.Children[CounterVar];
xmlEl:= AXmlDoc.CreateElement(UTF8Decode(PasElNode.Element.Name));
M:= PasElNode.Element.GetModule;
xmlEl['unit'] := UTF8Decode(M.Name);
xmlEl['package'] := UTF8Decode(M.PackageName);
xmlEl['type'] := UTF8Decode(GetElementTypeName(PasElNode.Element));
ParentxmlEl.AppendChild(xmlEl);
AddPasElChildsToXml(xmlEl, PasElNode);
end;
end;
var
XmlDoc: TXMLDocument;
XmlRootEl: TDOMElement;
M: TPasModule;
begin
XmlDoc:= TXMLDocument.Create;
XmlDoc.AppendChild(XmlDoc.CreateComment(UTF8Decode(SDocGeneratedByComment)));
try
XmlRootEl:= XmlDoc.CreateElement(UTF8Decode(FRootNode.Element.Name));
M:= FRootNode.Element.GetModule;
if Assigned(M) then
begin
XmlRootEl['unit'] := UTF8Decode(M.Name);
XmlRootEl['package'] := UTF8Decode(M.PackageName);
XmlRootEl['type'] := UTF8Decode(GetElementTypeName(FRootNode.Element));
end
else
begin
XmlRootEl['unit'] := 'system';
XmlRootEl['package'] := 'rtl';
if (okWithFields * FObjectKind) <> [] then
XmlRootEl['type'] := 'class'
else if (okInterface in FObjectKind) then
XmlRootEl['type'] := 'interface'
else
XmlRootEl['type'] := 'class';
end;
XmlDoc.AppendChild(XmlRootEl);
AddPasElChildsToXml(XmlRootEl, FRootNode);
WriteXMLFile(XmlDoc, AFileName);
finally
XmlDoc.Free;
end;
end;
end.