lazarus/doceditor/fpdocupdater/fpdocfiles.pas
paul 28db99f799 doceditor: fix fpdocupdater compilation
git-svn-id: trunk@23304 -
2009-12-28 06:17:13 +00:00

1291 lines
32 KiB
ObjectPascal

{
***************************************************************************
* *
* This source is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This code 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. See the GNU *
* General Public License for more details. *
* *
* A copy of the GNU General Public License is available on the World *
* Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also *
* obtain it by writing to the Free Software Foundation, *
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
* *
***************************************************************************
Author: Tom Gregorovic
}
unit FPDocFiles;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, Contnrs, FileUtil, DOM, XMLWrite, XMLRead;
type
TFPDocInfo = record
Packages: Integer;
Modules: Integer;
Topics: Integer;
Elements: Integer;
ElementsNonEmpty: Integer;
Shorts: Integer;
Descriptions: Integer;
Errors: Integer;
SeeAlsos: Integer;
Examples: Integer;
end;
const
EmptyFPDocInfo: TFPDocInfo = (
Packages: 0;
Modules: 0;
Topics: 0;
Elements: 0;
ElementsNonEmpty: 0;
Shorts: 0;
Descriptions: 0;
Errors: 0;
SeeAlsos: 0;
Examples: 0;
);
type
TUniqueName = record
Name: String;
Indexes: Array of Integer;
NonEmptyCount: Integer;
end;
TUniqueNameArray = Array of TUniqueName;
TFPDocNode = class;
{ TFPDocItem }
TFPDocItem = class
private
FParent: TFPDocItem;
FModified: Boolean;
public
constructor Create(const AParent: TFPDocItem);
function GetInfo: TFPDocInfo; virtual; abstract;
procedure Modify; virtual;
procedure Reset; virtual;
function AddNode(const AName: String): TFPDocNode; virtual; abstract;
function InsertNode(Index: Integer; const AName: String): TFPDocNode; virtual; abstract;
procedure DeleteNode(ANode: TFPDocNode); virtual; abstract;
procedure RenameNode(ANode: TFPDocNode; const AName: String); virtual; abstract;
public
property Modified: Boolean read FModified write FModified;
property Parent: TFPDocItem read FParent;
end;
{ TFPDocNode }
TFPDocNodeClass = class of TFPDocNode;
TFPDocNode = class(TFPDocItem)
private
FName: String;
FDOMNode: TDOMNode;
function GetDOMNodeValue(const AName: String): String;
function GetDescription: String;
function GetShort: String;
procedure SetDOMNodeValue(const AName, AValue: String);
procedure SetDescription(const AValue: String);
procedure SetShort(const AValue: String);
protected
function InsertDOMNode(const AName: String; const ADOMNode: TDOMNode): TDOMNode; virtual;
function GetEmpty: Boolean; virtual;
public
constructor Create(const AParent: TFPDocItem; const ADOMNode: TDOMNode);
procedure Assign(ASource: TFPDocNode);
function GetInfo: TFPDocInfo; override;
procedure Rename(const S: String);
procedure Delete;
public
property Name: String read FName;
property Description: String read GetDescription write SetDescription;
property Short: String read GetShort write SetShort;
property Empty: Boolean read GetEmpty;
end;
{ TFPDocElement }
TFPDocElement = class(TFPDocNode)
private
FExamples: TList;
function GetErrors: String;
function GetExample(Index: Integer): String;
function GetExamplesCount: Integer;
function GetSeeAlso: String;
procedure SetErrors(const AValue: String);
procedure SetExample(Index: Integer; const AValue: String);
procedure SetSeeAlso(const AValue: String);
protected
function GetEmpty: Boolean; override;
public
constructor Create(const AParent: TFPDocItem; const ADOMNode: TDOMNode);
destructor Destroy; override;
procedure Assign(ASource: TFPDocElement);
function GetInfo: TFPDocInfo; override;
procedure ParseExamples; virtual;
function AddExample(const AFileName: String): Integer; virtual;
function InsertExample(Index: Integer; const AFileName: String): Integer; virtual;
procedure DeleteExample(Index: Integer); virtual;
function AddNode(const AName: String): TFPDocNode; override;
procedure DeleteNode(ANode: TFPDocNode); override;
function InsertNode(Index: Integer; const AName: String): TFPDocNode;
override;
procedure RenameNode(ANode: TFPDocNode; const AName: String); override;
public
property Errors: String read GetErrors write SetErrors;
property SeeAlso: String read GetSeeAlso write SetSeeAlso;
property Examples[Index: Integer]: String read GetExample write SetExample;
property ExamplesCount: Integer read GetExamplesCount;
end;
{ TFPDocTopic }
TFPDocTopic = class(TFPDocNode)
private
FTopics: TObjectList;
function GetTopicsCount: Integer;
function GetTopic(Index: Integer): TFPDocTopic;
public
constructor Create(const AParent: TFPDocItem; const ADOMNode: TDOMNode);
destructor Destroy; override;
procedure ParseTopics; virtual;
procedure AddTopic(const ATopic: TFPDocTopic);
procedure Assign(ASource: TFPDocTopic);
function GetInfo: TFPDocInfo; override;
procedure Reset; override;
function AddTopic(const AName: String): TFPDocTopic; virtual;
function InsertTopic(Index: Integer; const AName: String): TFPDocTopic; virtual;
procedure DeleteNode(ANode: TFPDocNode); override;
procedure RenameNode(ANode: TFPDocNode; const AName: String); override;
public
function AddNode(const AName: String): TFPDocNode; override;
function InsertNode(Index: Integer; const AName: String): TFPDocNode;
override;
property Topics[Index: Integer]: TFPDocTopic read GetTopic;
property TopicsCount: Integer read GetTopicsCount;
end;
{ TFPDocNodeWithList }
TFPDocNodeWithList = class(TFPDocTopic)
private
FNodes: TObjectList;
FNames: TStringList;
function GetCount: Integer;
function GetNode(Index: Integer): TFPDocNode;
function GetNodeByName(const Index: String): TFPDocNode;
public
constructor Create(const AParent: TFPDocItem; const ADOMNode: TDOMNode);
destructor Destroy; override;
procedure ParseNodes; virtual; abstract;
procedure Reset; override;
function GetUniqueNames: TUniqueNameArray;
function AddNode(const AName: String): TFPDocNode; override;
procedure DeleteNode(ANode: TFPDocNode); override;
procedure RenameNode(ANode: TFPDocNode; const AName: String); override;
public
property Nodes[Index: Integer]: TFPDocNode read GetNode;
property NodesByName[const Index: String]: TFPDocNode read GetNodeByName;
property Count: Integer read GetCount;
property Names: TStringList read FNames;
end;
{ TFPDocModule }
TFPDocModule = class(TFPDocNodeWithList)
private
function GetElement(Index: Integer): TFPDocElement;
function GetElementByName(const Index: String): TFPDocElement;
public
function GetInfo: TFPDocInfo; override;
procedure ParseNodes; override;
function InsertNode(Index: Integer; const AName: String): TFPDocNode; override;
public
property Elements[Index: Integer]: TFPDocElement read GetElement;
property ElementsByName[const Index: String]: TFPDocElement read GetElementByName;
end;
{ TFPDocPackage }
TFPDocPackage = class(TFPDocNodeWithList)
private
function GetModule(Index: Integer): TFPDocModule;
function GetModuleByName(const Index: String): TFPDocModule;
public
function GetInfo: TFPDocInfo; override;
procedure ParseNodes; override;
function InsertNode(Index: Integer; const AName: String): TFPDocNode; override;
public
property Modules[Index: Integer]: TFPDocModule read GetModule;
property ModulesByName[const Index: String]: TFPDocModule read GetModuleByName;
end;
TMoveElementEvent = procedure (const SrcPackage: TFPDocPackage;
const SrcModule: TFPDocModule; const Src: TFPDocElement;
const DestList: TStrings; var Dest: Integer) of object;
{ TFPDocFile }
TFPDocFile = class(TFPDocNodeWithList)
private
FDocument: TXMLDocument;
function GetPackage(Index: Integer): TFPDocPackage;
function GetPackageByName(const Index: String): TFPDocPackage;
public
constructor Create(const FileName: String);
constructor Create(Stream: TStream);
destructor Destroy; override;
procedure ParseNodes; override;
procedure SaveToFile(const FileName: String);
procedure AssignToSkeleton(const SkeletonFile: TFPDocFile;
OnMoveElement: TMoveElementEvent);
function GetInfo: TFPDocInfo; override;
function InsertNode(Index: Integer; const AName: String): TFPDocNode; override;
public
property Packages[Index: Integer]: TFPDocPackage read GetPackage;
property PackagesByName[const Index: String]: TFPDocPackage read GetPackageByName;
end;
const
SFPDocHeader = 'fpdoc-descriptions';
SFPDocPackage = 'package';
SFPDocModule = 'module';
SFPDocTopic = 'topic';
SFPDocElement = 'element';
SFPDocName = 'name';
SFPDocFile = 'file';
SFPDocDescr = 'descr';
SFPDocShort = 'short';
SFPDocErrors = 'errors';
SFPDocSeeAlso = 'seealso';
SFPDocExample = 'example';
function DbgS(const AInfo: TFPDocInfo): String; overload;
implementation
uses LCLProc;
function DbgS(const AInfo: TFPDocInfo): String;
begin
Result :=
SFPDocPackage + 's: ' + IntToStr(AInfo.Packages) + LineEnding +
SFPDocModule + 's: ' + IntToStr(AInfo.Modules) + LineEnding +
SFPDocTopic + 's: ' + IntToStr(AInfo.Topics) + LineEnding +
SFPDocElement + 's: ' + IntToStr(AInfo.Elements) + LineEnding +
SFPDocElement + 's NonEmpty: ' + IntToStr(AInfo.ElementsNonEmpty) + LineEnding +
SFPDocShort + 's: ' + IntToStr(AInfo.Shorts) + LineEnding +
SFPDocDescr + 's: ' + IntToStr(AInfo.Descriptions) + LineEnding +
SFPDocErrors + 's: ' + IntToStr(AInfo.Errors) + LineEnding +
SFPDocSeeAlso + 's: ' + IntToStr(AInfo.SeeAlsos) + LineEnding +
SFPDocExample + 's: ' + IntToStr(AInfo.Examples);
end;
{ TFPDocItem }
constructor TFPDocItem.Create(const AParent: TFPDocItem);
begin
FParent := AParent;
FModified := False;
end;
procedure TFPDocItem.Modify;
begin
FModified := True;
if FParent <> nil then FParent.Modify;
end;
procedure TFPDocItem.Reset;
begin
FModified := False;
end;
{ TFPDocNode }
function TFPDocNode.GetDOMNodeValue(const AName: String): String;
var
N: TDOMNode;
S: TStringStream;
D: TDOMNode;
begin
Result := '';
N := FDOMNode.FindNode(AName);
if N = nil then Exit;
if N.FirstChild = nil then Exit;
S := TStringStream.Create('');
try
D := N.FirstChild;
while D <> nil do
begin
WriteXML(D, S);
D := D.NextSibling;
end;
Result := S.DataString;
finally
S.Free;
end;
end;
function TFPDocNode.GetDescription: String;
begin
Result := GetDOMNodeValue(SFPDocDescr);
end;
function TFPDocNode.GetShort: String;
begin
Result := GetDOMNodeValue(SFPDocShort);
end;
procedure TFPDocNode.SetDOMNodeValue(const AName, AValue: String);
var
N: TDOMNode;
S: TStream;
begin
//DebugLn(FName, ' ', AName);
N := FDOMNode.FindNode(AName);
if N = nil then
begin
if AValue = '' then Exit;
N := FDOMNode.OwnerDocument.CreateElement(AName);
N := InsertDOMNode(AName, N);
//DebugLn('New');
end
else
while N.LastChild <> nil do N.RemoveChild(N.LastChild);
if AValue = '' then Exit;
S := TStringStream.Create(AValue);
try
ReadXMLFragment(N, S);
finally
S.Free;
end;
Modify;
end;
procedure TFPDocNode.SetDescription(const AValue: String);
begin
SetDOMNodeValue(SFPDocDescr, AValue);
end;
procedure TFPDocNode.SetShort(const AValue: String);
begin
SetDOMNodeValue(SFPDocShort, AValue);
end;
function TFPDocNode.InsertDOMNode(const AName: String; const ADOMNode: TDOMNode): TDOMNode;
begin
if not FDOMNode.HasChildNodes then Result := FDOMNode.AppendChild(ADOMNode)
else
begin
if AName = SFPDocShort then Result := FDOMNode.InsertBefore(ADOMNode, FDOMNode.FirstChild);
if AName = SFPDocDescr then
begin
Result := FDOMNode.FindNode(SFPDocShort);
if Result = nil then Result := FDOMNode.InsertBefore(ADOMNode, FDOMNode.FirstChild)
else Result := FDOMNode.InsertBefore(ADOMNode, Result.NextSibling);
end
else
if AName = SFPDocErrors then
begin
Result := FDOMNode.FindNode(SFPDocDescr);
if Result = nil then Result := FDOMNode.FindNode(SFPDocShort);
if Result = nil then Result := FDOMNode.InsertBefore(ADOMNode, FDOMNode.FirstChild)
else Result := FDOMNode.InsertBefore(ADOMNode, Result.NextSibling);
end
else
if AName = SFPDocSeeAlso then
begin
Result := FDOMNode.FindNode(SFPDocErrors);
if Result = nil then Result := FDOMNode.FindNode(SFPDocDescr);
if Result = nil then Result := FDOMNode.FindNode(SFPDocShort);
if Result = nil then Result := FDOMNode.InsertBefore(ADOMNode, FDOMNode.FirstChild)
else Result := FDOMNode.InsertBefore(ADOMNode, Result.NextSibling);
end;
end;
end;
function TFPDocNode.GetEmpty: Boolean;
begin
Result := (Description = '') and (Short = '');
end;
constructor TFPDocNode.Create(const AParent: TFPDocItem; const ADOMNode: TDOMNode);
begin
inherited Create(AParent);
FDOMNode := ADOMNode;
FName := (FDOMNode as TDOMElement).GetAttribute(SFPDocName);
end;
procedure TFPDocNode.Assign(ASource: TFPDocNode);
begin
Short := ASource.Short;
Description := ASource.Description;
end;
function TFPDocNode.GetInfo: TFPDocInfo;
begin
Result.Packages := 0;
Result.Modules := 0;
Result.Topics := 0;
Result.Elements := 0;
Result.ElementsNonEmpty := 0;
if Short <> '' then Result.Shorts := 1
else Result.Shorts := 0;
if Description <> '' then Result.Descriptions := 1
else Result.Descriptions := 0;
Result.Errors := 0;
Result.SeeAlsos := 0;
Result.Examples := 0;
end;
procedure TFPDocNode.Rename(const S: String);
begin
if S <> Name then
Parent.RenameNode(Self, S);
end;
procedure TFPDocNode.Delete;
begin
Parent.DeleteNode(Self);
end;
{ TFPDocElement }
function TFPDocElement.GetEmpty: Boolean;
begin
Result := (Description = '') and (Short = '') and (Errors = '') and
(SeeAlso = '') and (ExamplesCount = 0);
end;
function TFPDocElement.GetErrors: String;
begin
Result := GetDOMNodeValue(SFPDocErrors);
end;
function TFPDocElement.GetExample(Index: Integer): String;
begin
Result := TDOMElement(FExamples[Index]).GetAttribute(SFPDocFile);
end;
function TFPDocElement.GetExamplesCount: Integer;
begin
Result := FExamples.Count;
end;
function TFPDocElement.GetSeeAlso: String;
begin
Result := GetDOMNodeValue(SFPDocSeeAlso);
end;
procedure TFPDocElement.SetErrors(const AValue: String);
begin
SetDOMNodeValue(SFPDocErrors, AValue);
end;
procedure TFPDocElement.SetExample(Index: Integer; const AValue: String);
begin
TDOMElement(FExamples[Index]).SetAttribute(SFPDocFile, AValue);
end;
procedure TFPDocElement.SetSeeAlso(const AValue: String);
begin
SetDOMNodeValue(SFPDocSeeAlso, AValue);
end;
constructor TFPDocElement.Create(const AParent: TFPDocItem;
const ADOMNode: TDOMNode);
begin
inherited;
FExamples := TList.Create;
ParseExamples;
end;
destructor TFPDocElement.Destroy;
begin
FExamples.Free;
inherited Destroy;
end;
procedure TFPDocElement.Assign(ASource: TFPDocElement);
var
I: Integer;
begin
inherited Assign(ASource);
Errors := ASource.Errors;
SeeAlso := ASource.SeeAlso;
FExamples.Clear;
DebugLn('Assign Examples: ' + ASource.Name + ' ' + DbgS(ASource.ExamplesCount));
for I := 0 to ASource.ExamplesCount - 1 do AddExample(ASource.Examples[I]);
end;
function TFPDocElement.GetInfo: TFPDocInfo;
begin
Result := inherited GetInfo;
Result.Elements := 1;
if Empty then Result.ElementsNonEmpty := 0
else Result.ElementsNonEmpty := 1;
if Errors <> '' then Result.Errors := 1
else Result.Errors := 0;
if SeeAlso <> '' then Result.SeeAlsos := 1
else Result.SeeAlsos := 0;
Result.Examples := ExamplesCount;
end;
procedure TFPDocElement.ParseExamples;
var
I: TDOMNode;
begin
FExamples.Clear;
I := FDOMNode.FirstChild;
while I <> nil do
begin
if I.NodeName = SFPDocExample then
begin
FExamples.Add(Pointer(I));
end;
I := I.NextSibling;
end;
end;
function TFPDocElement.AddExample(const AFileName: String): Integer;
begin
Result := InsertExample(ExamplesCount, AFileName);
end;
function TFPDocElement.InsertExample(Index: Integer; const AFileName: String): Integer;
var
E: TDOMElement;
N: TDOMNode;
begin
if Index < 0 then Index := 0;
if Index > ExamplesCount then Index := ExamplesCount;
E := FDOMNode.OwnerDocument.CreateElement(SFPDocExample);
E.SetAttribute(SFPDocFile, AFileName);
if Index >= ExamplesCount then
N := FDOMNode.AppendChild(E)
else
N := FDOMNode.InsertBefore(E, TDOMNode(FExamples[Index]));
FExamples.Insert(Index, Pointer(N));
Result := Index;
end;
procedure TFPDocElement.DeleteExample(Index: Integer);
begin
FDOMNode.RemoveChild(TDOMNode(FExamples[Index]));
FExamples.Delete(Index);
end;
function TFPDocElement.AddNode(const AName: String): TFPDocNode;
begin
raise Exception.Create('');
Result:=nil;
end;
procedure TFPDocElement.DeleteNode(ANode: TFPDocNode);
begin
raise Exception.Create('');
end;
function TFPDocElement.InsertNode(Index: Integer; const AName: String
): TFPDocNode;
begin
raise Exception.Create('');
Result:=nil;
end;
procedure TFPDocElement.RenameNode(ANode: TFPDocNode; const AName: String);
begin
raise Exception.Create('');
end;
{ TFPDocTopic }
function TFPDocTopic.GetTopicsCount: Integer;
begin
Result := FTopics.Count;
end;
function TFPDocTopic.GetTopic(Index: Integer): TFPDocTopic;
begin
Result := FTopics[Index] as TFPDocTopic;
end;
constructor TFPDocTopic.Create(const AParent: TFPDocItem; const ADOMNode: TDOMNode);
begin
inherited;
FTopics := TObjectList.Create(True);
//DebugLn(Name);
ParseTopics;
end;
destructor TFPDocTopic.Destroy;
begin
FTopics.Free;
inherited Destroy;
end;
procedure TFPDocTopic.ParseTopics;
var
I: TDOMNode;
T: TFPDocTopic;
begin
FTopics.Clear;
I := FDOMNode.FirstChild;
while I <> nil do
begin
if I.NodeName = SFPDocTopic then
begin
T := TFPDocTopic.Create(Self, I);
FTopics.Add(T);
end;
I := I.NextSibling;
end;
end;
procedure TFPDocTopic.AddTopic(const ATopic: TFPDocTopic);
begin
AddTopic(ATopic.Name).Assign(ATopic);
end;
procedure TFPDocTopic.Assign(ASource: TFPDocTopic);
var
I: Integer;
begin
inherited;
for I := 0 to ASource.TopicsCount - 1 do
AddTopic(ASource.Topics[I]);
end;
function TFPDocTopic.GetInfo: TFPDocInfo;
var
I: Integer;
Info: TFPDocInfo;
begin
Result := inherited GetInfo;
if ClassType <> TFPDocTopic then Result.Topics := 0
else Result.Topics := 1;
for I := 0 to TopicsCount - 1 do
begin
Info := Topics[I].GetInfo;
Result.Topics := Result.Topics + Info.Topics;
Result.Shorts := Result.Shorts + Info.Shorts;
Result.Descriptions := Result.Descriptions + Info.Descriptions;
end;
end;
procedure TFPDocTopic.Reset;
var
I: Integer;
begin
inherited;
for I := 0 to TopicsCount - 1 do Topics[I].Reset;
end;
function TFPDocTopic.AddTopic(const AName: String): TFPDocTopic;
begin
Result := InsertTopic(TopicsCount, AName);
end;
function TFPDocTopic.InsertTopic(Index: Integer; const AName: String): TFPDocTopic;
var
N: TDOMElement;
begin
N := FDOMNode.OwnerDocument.CreateElement(SFPDocTopic);
N.AttribStrings[SFPDocName] := AName;
if Index < 0 then Index := 0;
if Index > TopicsCount then Index := TopicsCount;
if Index < TopicsCount then
Result := TFPDocTopic.Create(Self, FDOMNode.InsertBefore(N, Topics[Index].FDOMNode))
else
Result := TFPDocTopic.Create(Self, FDOMNode.AppendChild(N));
FTopics.Insert(Index, Result);
Result.Modify;
end;
procedure TFPDocTopic.DeleteNode(ANode: TFPDocNode);
begin
FDOMNode.RemoveChild(ANode.FDOMNode);
FTopics.Remove(ANode);
Modify;
end;
procedure TFPDocTopic.RenameNode(ANode: TFPDocNode; const AName: String);
begin
(ANode.FDOMNode as TDOMElement).SetAttribute(SFPDocName, AName);
ANode.FName := AName;
ANode.Modify;
end;
function TFPDocTopic.AddNode(const AName: String): TFPDocNode;
begin
raise Exception.Create('');
Result:=nil;
end;
function TFPDocTopic.InsertNode(Index: Integer; const AName: String
): TFPDocNode;
begin
raise Exception.Create('');
Result:=nil;
end;
{ TFPDocNodeWithList }
function TFPDocNodeWithList.GetCount: Integer;
begin
Result := FNodes.Count;
end;
function TFPDocNodeWithList.GetNode(Index: Integer): TFPDocNode;
begin
Result := FNodes[Index] as TFPDocNode;
end;
function TFPDocNodeWithList.GetNodeByName(const Index: String): TFPDocNode;
var
I: Integer;
begin
I := FNames.IndexOf(Index);
if I = -1 then
Result := nil
else
Result := FNames.Objects[I] as TFPDocNode;
end;
constructor TFPDocNodeWithList.Create(const AParent: TFPDocItem; const ADOMNode: TDOMNode);
begin
inherited;
FNodes := TObjectList.Create(True);
FNames := TStringList.Create;
FNames.Sorted := True;
ParseNodes;
end;
destructor TFPDocNodeWithList.Destroy;
begin
FNames.Free;
FNodes.Free;
inherited Destroy;
end;
procedure TFPDocNodeWithList.Reset;
var
I: Integer;
begin
inherited;
for I := 0 to Count - 1 do Nodes[I].Reset;
end;
function TFPDocNodeWithList.GetUniqueNames: TUniqueNameArray;
var
I, J: Integer;
Found: Boolean;
begin
SetLength(Result, 0);
for I := 0 to Count - 1 do
begin
Found := False;
for J := 0 to High(Result) do
begin
if Result[J].Name = Nodes[I].Name then
begin
Found := True;
SetLength(Result[J].Indexes, Length(Result[J].Indexes) + 1);
Result[J].Indexes[High(Result[J].Indexes)] := I;
if not Nodes[I].Empty then Inc(Result[High(Result)].NonEmptyCount);
Break;
end;
end;
if not Found then
begin
SetLength(Result, Length(Result) + 1);
Result[High(Result)].Name := Nodes[I].Name;
SetLength(Result[High(Result)].Indexes, 1);
Result[High(Result)].Indexes[0] := I;
if Nodes[I].Empty then Result[High(Result)].NonEmptyCount := 0
else Result[High(Result)].NonEmptyCount := 1;
end;
end;
end;
function TFPDocNodeWithList.AddNode(const AName: String): TFPDocNode;
begin
Result := InsertNode(Count, AName);
end;
procedure TFPDocNodeWithList.DeleteNode(ANode: TFPDocNode);
begin
if ANode is TFPDocTopic then inherited
else
begin
FDOMNode.RemoveChild(ANode.FDOMNode);
FNames.Delete(FNames.IndexOfObject(ANode));
FNodes.Remove(ANode);
Modify;
end;
end;
procedure TFPDocNodeWithList.RenameNode(ANode: TFPDocNode; const AName: String);
begin
if not (ANode is TFPDocTopic) then
FNames.Strings[FNames.IndexOfObject(ANode)] := AName;
inherited;
end;
{ TFPDocModule }
function TFPDocModule.GetElement(Index: Integer): TFPDocElement;
begin
Result := FNodes[Index] as TFPDocElement;
end;
function TFPDocModule.GetElementByName(const Index: String): TFPDocElement;
var
I: Integer;
begin
I := FNames.IndexOf(Index);
if I = -1 then
Result := nil
else
Result := FNames.Objects[I] as TFPDocElement;
end;
function TFPDocModule.GetInfo: TFPDocInfo;
var
I: Integer;
Info: TFPDocInfo;
begin
Result := inherited GetInfo;
Result.Modules := 1;
for I := 0 to Count - 1 do
begin
Info := Elements[I].GetInfo;
Result.Elements := Result.Elements + Info.Elements;
Result.ElementsNonEmpty := Result.ElementsNonEmpty + Info.ElementsNonEmpty;
Result.Shorts := Result.Shorts + Info.Shorts;
Result.Descriptions := Result.Descriptions + Info.Descriptions;
Result.Errors := Result.Errors + Info.Errors;
Result.SeeAlsos := Result.SeeAlsos + Info.SeeAlsos;
Result.Examples := Result.Examples + Info.Examples;
end;
end;
procedure TFPDocModule.ParseNodes;
var
I: TDOMNode;
N: TFPDocElement;
begin
FNodes.Clear;
FNames.Clear;
I := FDOMNode.FirstChild;
while I <> nil do
begin
if I.NodeName = SFPDocElement then
begin
N := TFPDocElement.Create(Self, I);
FNodes.Add(N);
FNames.AddObject(N.Name, N);
end;
I := I.NextSibling;
end;
end;
function TFPDocModule.InsertNode(Index: Integer; const AName: String): TFPDocNode;
var
N: TDOMElement;
begin
N := FDOMNode.OwnerDocument.CreateElement(SFPDocElement);
N.AttribStrings[SFPDocName] := AName;
if Index < 0 then Index := 0;
if Index > Count then Index := Count;
if Index < Count then
Result := TFPDocElement.Create(Self, FDOMNode.InsertBefore(N, Elements[Index].FDOMNode))
else
if TopicsCount = 0 then
Result := TFPDocElement.Create(Self, FDOMNode.AppendChild(N))
else
Result := TFPDocElement.Create(Self, FDOMNode.InsertBefore(N, Topics[0].FDOMNode));
FNodes.Insert(Index, Result);
FNames.AddObject(AName, Result);
Result.Modify;
end;
{ TFPDocPackage }
function TFPDocPackage.GetModule(Index: Integer): TFPDocModule;
begin
Result := FNodes[Index] as TFPDocModule;
end;
function TFPDocPackage.GetModuleByName(const Index: String): TFPDocModule;
var
I: Integer;
begin
I := FNames.IndexOf(Index);
if I = -1 then
Result := nil
else
Result := FNames.Objects[I] as TFPDocModule;
end;
function TFPDocPackage.GetInfo: TFPDocInfo;
var
I: Integer;
Info: TFPDocInfo;
begin
Result := inherited GetInfo;
Result.Packages := 1;
for I := 0 to Count - 1 do
begin
Info := Modules[I].GetInfo;
Result.Modules := Result.Modules + Info.Modules;
Result.Elements := Result.Elements + Info.Elements;
Result.ElementsNonEmpty := Result.ElementsNonEmpty + Info.ElementsNonEmpty;
Result.Shorts := Result.Shorts + Info.Shorts;
Result.Descriptions := Result.Descriptions + Info.Descriptions;
Result.Errors := Result.Errors + Info.Errors;
Result.SeeAlsos := Result.SeeAlsos + Info.SeeAlsos;
Result.Examples := Result.Examples + Info.Examples;
end;
end;
procedure TFPDocPackage.ParseNodes;
var
I: TDOMNode;
N: TFPDocModule;
begin
FNodes.Clear;
FNames.Clear;
I := FDOMNode.FirstChild;
while I <> nil do
begin
if I.NodeName = SFPDocModule then
begin
N := TFPDocModule.Create(Self, I);
FNodes.Add(N);
FNames.AddObject(N.Name, N);
end;
I := I.NextSibling;
end;
end;
function TFPDocPackage.InsertNode(Index: Integer; const AName: String
): TFPDocNode;
var
N: TDOMElement;
begin
N := FDOMNode.OwnerDocument.CreateElement(SFPDocModule);
N.AttribStrings[SFPDocName] := AName;
if Index < 0 then Index := 0;
if Index > Count then Index := Count;
if Index < Count then
Result := TFPDocModule.Create(Self, FDOMNode.InsertBefore(N, Modules[Index].FDOMNode))
else
if TopicsCount = 0 then
Result := TFPDocModule.Create(Self, FDOMNode.AppendChild(N))
else
Result := TFPDocModule.Create(Self, FDOMNode.InsertBefore(N, Topics[0].FDOMNode));
FNodes.Insert(Index, Result);
FNames.AddObject(AName, Result);
Result.Modify;
end;
{ TFPDocFile }
function TFPDocFile.GetPackage(Index: Integer): TFPDocPackage;
begin
Result := Nodes[Index] as TFPDocPackage;
end;
function TFPDocFile.GetPackageByName(const Index: String): TFPDocPackage;
begin
Result := NodesByName[Index] as TFPDocPackage;
end;
constructor TFPDocFile.Create(const FileName: String);
var
F: TFileStream;
begin
F := TFileStream.Create(UTF8ToSys(FileName), fmOpenRead);
try
Create(F);
finally
F.Free;
end;
end;
constructor TFPDocFile.Create(Stream: TStream);
var
R: TDOMNode;
begin
ReadXMLFile(FDocument, Stream);
R := FDocument.FindNode(SFPDocHeader);
if R = nil then
raise Exception.Create('Invalid FPDoc file!');
inherited Create(nil, R);
end;
destructor TFPDocFile.Destroy;
begin
FDocument.Free;
inherited Destroy;
end;
procedure TFPDocFile.ParseNodes;
var
I: TDOMNode;
P: TFPDocPackage;
begin
FNodes.Clear;
FNames.Clear;
I := FDOMNode.FirstChild;
while I <> nil do
begin
if I.NodeName = SFPDocPackage then
begin
P := TFPDocPackage.Create(Self, I);
FNodes.Add(P);
FNames.AddObject(P.Name, P);
end;
I := I.NextSibling;
end;
end;
procedure TFPDocFile.SaveToFile(const FileName: String);
begin
WriteXMLFile(FDocument, FileName);
Reset;
end;
procedure TFPDocFile.AssignToSkeleton(const SkeletonFile: TFPDocFile;
OnMoveElement: TMoveElementEvent);
var
I, J, K, L, M: Integer;
P1, P2: TFPDocPackage;
M1, M2: TFPDocModule;
E1, E2: TFPDocElement;
Elements1, Elements2: TUniqueNameArray;
DestList: TStringList;
procedure AskMove;
var
N: Integer;
begin
N := M;
OnMoveElement(P1, M1, E1, DestList, N);
if N <> -1 then
begin
M2.Elements[N].Assign(E1);
if not M2.Elements[N].Empty then
DestList.Objects[N] := TObject(1);
end;
end;
begin
for I := 0 to Count - 1 do
begin
P1 := Packages[I];
P2 := SkeletonFile.PackagesByName[P1.Name];
P2.Assign(P1);
for J := 0 to P1.Count - 1 do
begin
M1 := P1.Modules[J];
M2 := P2.ModulesByName[M1.Name];
M2.Assign(M1);
DestList := TStringList.Create;
try
for K := 0 to M2.Count - 1 do
begin
E2 := M2.Elements[K];
DestList.Add(E2.Name);
end;
Elements1 := M1.GetUniqueNames;
Elements2 := M2.GetUniqueNames;
for K := 0 to High(Elements1) do
begin
if Elements1[K].NonEmptyCount = 0 then Continue;
M := -1;
for L := 0 to High(Elements2) do
begin
if Elements1[K].Name = Elements2[L].Name then
begin
M := L;
end;
end;
for L := 0 to High(Elements1[K].Indexes) do
begin
E1 := M1.Elements[Elements1[K].Indexes[L]];
if E1.Empty then Continue;
if M = -1 then AskMove
else
if L > High(Elements2[M].Indexes) then AskMove
else
begin
E2 := M2.Elements[Elements2[M].Indexes[L]];
if not E2.Empty then AskMove
else
begin
E2.Assign(E1);
if not E2.Empty then
DestList.Objects[Elements2[M].Indexes[L]] := TObject(1);
end;
end;
end;
end;
finally
DestList.Free;
end;
end;
end;
end;
function TFPDocFile.GetInfo: TFPDocInfo;
var
I: Integer;
Info: TFPDocInfo;
begin
Result := EmptyFPDocInfo;
for I := 0 to Count - 1 do
begin
Info := Packages[I].GetInfo;
Result.Packages := Result.Packages + Info.Packages;
Result.Modules := Result.Modules + Info.Modules;
Result.Elements := Result.Elements + Info.Elements;
Result.ElementsNonEmpty := Result.ElementsNonEmpty + Info.ElementsNonEmpty;
Result.Shorts := Result.Shorts + Info.Shorts;
Result.Descriptions := Result.Descriptions + Info.Descriptions;
Result.Errors := Result.Errors + Info.Errors;
Result.SeeAlsos := Result.SeeAlsos + Info.SeeAlsos;
Result.Examples := Result.Examples + Info.Examples;
end;
end;
function TFPDocFile.InsertNode(Index: Integer; const AName: String): TFPDocNode;
var
N: TDOMElement;
begin
N := FDocument.CreateElement(SFPDocPackage);
N.AttribStrings[SFPDocName] := AName;
if Index < 0 then Index := 0;
if Index > Count then Index := Count;
if Index < Count then
Result := TFPDocPackage.Create(Self, FDocument.InsertBefore(N, Packages[Index].FDOMNode))
else
Result := TFPDocPackage.Create(Self, FDocument.AppendChild(N));
FNodes.Insert(Index, Result);
FNames.AddObject(AName, Result);
Result.Modify;
end;
end.