mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-04-07 12:07:58 +02:00
299 lines
8.1 KiB
ObjectPascal
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.
|
|
|