Componentes.Terceros.jvcl/official/3.32/devtools/clxgettextResstr/Parser.pas

533 lines
13 KiB
ObjectPascal

{******************************************************************************}
{* WARNING: JEDI VCL To CLX Converter generated unit. *}
{* Manual modifications will be lost on next release. *}
{******************************************************************************}
{-----------------------------------------------------------------------------
The contents of this file are subject to the Mozilla Public License
Version 1.1 (the "License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/MPL-1.1.html
Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either expressed or implied. See the License for
the specific language governing rights and limitations under the License.
The Original Code is: Parser.pas, released on 2003-10-04.
The Initial Developer of the Original Code is Andreas Hausladen [Andreas dott Hausladen att gmx dott de]
Portions created by Andreas Hausladen are Copyright (C) 2003 Andreas Hausladen.
All Rights Reserved.
Contributor(s): -
Last Modified: 2003-10-04
You may retrieve the latest version of this file at the Project JEDI's JVCL home page,
located at http://jvcl.sourceforge.net
Known Issues:
-----------------------------------------------------------------------------}
unit Parser;
{$I jvcl.inc}
interface
uses
SysUtils, Classes, Contnrs, dpp_PascalParser, QDialogs;
type
TProgressEvent = procedure(Sender: TObject; const Text: string; Percentage: Integer) of object;
TGetResNameEvent = procedure(Sender: TObject; var ResName: string; const Value: string) of object;
TResStrings = class;
TParser = class(TObject)
private
FOnProgress: TProgressEvent;
FOnGetResName: TGetResNameEvent;
FSubDirs: Boolean;
FSingleResFile: Boolean;
FResStrings: TResStrings;
procedure DoProgress(const Text: string; Percentage: Integer);
function GetResName(const Value: string): string;
private
function ReadString(Parser: TPascalParser): string;
procedure ParseUnderscore(Parser: TPascalParser);
function Parse(const FileName: string): Boolean;
public
constructor Create;
destructor Destroy; override;
function ParseFiles(const Dir: string): Integer;
// returns the number of parsed files
property SubDirs: Boolean read FSubDirs write FSubDirs;
property SingleResFile: Boolean read FSingleResFile write FSingleResFile;
property OnProgress: TProgressEvent read FOnProgress write FOnProgress;
property OnGetResName: TGetResNameEvent read FOnGetResName write FOnGetResName;
end;
TResItem = class(TObject)
public
Name: string;
Value: string;
RefCount: Integer;
procedure AddRef;
end;
TResStrings = class(TObject)
private
FItems: TObjectList;
function GetItems(Index: Integer): TResItem;
function GetCount: Integer;
public
constructor Create;
destructor Destroy; override;
function HasResValue(const Value: string): TResItem;
function NewResString(const Name, Value: string): TResItem;
procedure Clear;
function GetAsString: string;
procedure SaveToFile(const Filename: string);
property Count: Integer read GetCount;
property Items[Index: Integer]: TResItem read GetItems;
end;
implementation
uses
JvQConsts; // (rom) for sLineBreak no dependencies to packages
function SubStr(const Value: string; StartIndex, EndIndex: Integer): string;
begin
Result := Copy(Value, StartIndex, EndIndex - StartIndex + 1);
end;
function GetCleanValue(const Value: string): string;
// removes all white chars from the string for better comparision
var
Token: PTokenInfo;
Parser: TPascalParser;
begin
Result := '';
Parser := TPascalParser.Create('', Value);
while Parser.GetToken(Token) do
Result := Result + Token.Value;
end;
function RemoveWhiteChars(const Value: string): string;
var i, j: Integer;
begin
SetLength(Result, Length(Value));
j := 0;
for i := 1 to Length(Value) do
begin
if (Value[i] in ['a'..'z', 'A'..'Z']) then
begin
Inc(j);
Result[j] := Value[i];
end;
end;
SetLength(Result, j);
end;
function ProperOut(const Indent, Value: string; MaxLen: Integer): string;
begin
Result := Value;
end;
function ReadFileToString(const FileName: string): AnsiString;
var Stream: TFileStream;
begin
Stream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
try
SetLength(Result, Stream.Size);
if Result <> '' then
Stream.Read(Result[1], Length(Result));
finally
Stream.Free;
end;
end;
procedure WriteStringToFile(const FileName: string; const Data: AnsiString);
var Stream: TFileStream;
begin
Stream := TFileStream.Create(FileName, fmCreate);
try
if Data <> '' then
Stream.Write(Data[1], Length(Data));
finally
Stream.Free;
end;
end;
function IsInList(const S: string; const A: array of string): Boolean;
var i: Integer;
begin
Result := True;
for i := 0 to High(A) do
if CompareText(A[i], S) = 0 then
Exit;
Result := False;
end;
procedure FindFiles(const Dir: string; Files: TStrings; SubDirs: Boolean);
var sr: TSearchRec;
begin
if FindFirst(Dir + '\*.*', faAnyFile or faDirectory, sr) = 0 then
try
repeat
if sr.Attr and faDirectory <> 0 then
begin
if (SubDirs) and (sr.Name <> '.') and (sr.Name <> '..') then
FindFiles(Dir, Files, SubDirs);
end
else
Files.Add(Dir + '\' + sr.Name);
until FindNext(sr) <> 0;
finally
FindClose(sr);
end;
end;
{ TParser }
constructor TParser.Create;
begin
inherited Create;
FResStrings := TResStrings.Create;
end;
destructor TParser.Destroy;
begin
FResStrings.Free;
inherited Destroy;
end;
procedure TParser.DoProgress(const Text: string; Percentage: Integer);
begin
if Assigned(FOnProgress) then
FOnProgress(Self, Text, Percentage);
end;
function TParser.GetResName(const Value: string): string;
var
Parser: TPascalParser;
Token: PTokenInfo;
S: string;
P: PChar;
begin
Result := '';
Parser := TPascalParser.Create('', Value);
try
if Parser.GetToken(Token) then
begin
if Token.Kind = tkString then
begin
S := Token.Value;
Delete(S, 1, 1);
Delete(S, Length(S), 1);
P := Pointer(S);
if P <> nil then
begin
P[0] := UpCase(P[0]);
Inc(P);
while P[0] <> #0 do
begin
if (P[0] = ' ') and (P[1] in ['a'..'z']) then
P[1] := UpCase(P[1]);
Inc(P);
end;
Result := 's' + Copy(RemoveWhiteChars(S), 1, 35);
end;
end;
end;
finally
Parser.Free;
end;
if Assigned(FOnGetResName) then
FOnGetResName(Self, Result, Value);
end;
function TParser.ParseFiles(const Dir: string): Integer;
var
i: Integer;
Files: TStrings;
begin
FResStrings.Clear;
Result := 0;
Files := TStringList.Create;
try
DoProgress('Starting...', 0);
FindFiles(ExcludeTrailingPathDelimiter(Dir), Files, FSubDirs);
for i := Files.Count - 1 downto 0 do
if not IsInList(ExtractFileExt(Files[i]), ['.pas']) then
Files.Delete(i);
for i := 0 to Files.Count - 1 do
begin
DoProgress(Files[i], i * 100 div Files.Count);
if Parse(Files[i]) then
Inc(Result);
end;
DoProgress('Finished.', 100);
finally
Files.Free;
end;
if FSingleResFile and (FResStrings.Count > 0) then
FResStrings.SaveToFile(ExcludeTrailingPathDelimiter(Dir) + 'Strings.res.pas');
end;
function TParser.Parse(const FileName: string): Boolean;
var
Parser: TPascalParser;
Token: PTokenInfo;
InsertPos: Integer;
S: string;
begin
if not FSingleResFile then
FResStrings.Clear;
InsertPos := 0;
Result := False;
Parser := TPascalParser.Create('', ReadFileToString(FileName));
try
while Parser.GetToken(Token) do
begin
if Token.Kind = tkIdent then
begin
if Token.Value = '_' then
begin
ParseUnderscore(Parser);
Result := True;
end
else if CompareText(Token.Value, 'implementation') = 0 then
begin
InsertPos := Token.StartIndex;
end;
end;
end;
if Result and (FResStrings.Count > 0) then
begin
S := Parser.Text;
if not FSingleResFile then
begin
// FResStrings.SaveToFile(ChangeFileExt(FileName, '.res.inc'));
if InsertPos > 0 then
Insert(sLineBreak + FResStrings.GetAsString + sLineBreak,
S, InsertPos)
else
begin
ShowMessage('no "implementation" in unit ' + ExtractFileName(FileName));
Exit;
end;
end;
// WriteStringToFile(ChangeFileExt(FileName, '.pas.txt'), S);
WriteStringToFile(FileName, S);
S := '';
end;
finally
Parser.Free;
end;
end;
procedure TParser.ParseUnderscore(Parser: TPascalParser);
var
Token: PTokenInfo;
Item: TResItem;
Value: string;
StartIndex, EndIndex: Integer;
begin // CurToken='_'
StartIndex := Parser.CurToken.StartIndex;
if (not Parser.GetToken(Token)) or (Token.Value <> '(') then // -> '('
Exit;
if not Parser.GetToken(Token) then // -> string
Exit;
if Token.Kind = tkString then
begin
Value := ReadString(Parser);
if Value <> '' then
begin
Item := FResStrings.HasResValue(Value);
if Item = nil then
Item := FResStrings.NewResString(GetResName(Value), Value);
end
else
Exit;
Token := Parser.CurToken;
if Token = nil then
Exit;
if Token.Value = ',' then
begin
// needs Format()
EndIndex := Parser.PreToken.EndIndex;
Parser.Replace(StartIndex, EndIndex - StartIndex + 1, 'Format(' + Item.Name);
end
else if Token.Value = ')' then
begin
EndIndex := Token.EndIndex;
Parser.Replace(StartIndex, EndIndex - StartIndex + 1, Item.Name);
Parser.Index := StartIndex; // rescan
end;
end;
// CurToken=',' or ')'
end;
function TParser.ReadString(Parser: TPascalParser): string;
var
Token: PTokenInfo;
Bracket1, // ()
Bracket2: Integer; // []
StartIndex, EndIndex: Integer;
begin // CurToken='string'
Token := Parser.CurToken;
StartIndex := Token.StartIndex;
EndIndex := Token.EndIndex;
Result := Token.Value;
Bracket1 := 1;
Bracket2 := 0;
while Parser.GetToken(Token) do
begin
if (Token.Kind = tkSymbol) then
begin
if Token.Value = '(' then Inc(Bracket1)
else if Token.Value = ')' then Dec(Bracket1)
else if Token.Value = '[' then Inc(Bracket2)
else if Token.Value = ']' then Inc(Bracket2);
if Bracket2 = 0 then
begin
if ((Bracket1 = 1) and (Token.Value = ',')) or
((Bracket1 = 0) and (Token.Value = ')')) then
begin
Result := SubStr(Parser.Text, StartIndex, EndIndex);
Break;
end;
end;
end;
EndIndex := Token.EndIndex;
end;
// CurToken=',' oder ')'
end;
{ TResStrings }
constructor TResStrings.Create;
begin
inherited Create;
FItems := TObjectList.Create;
end;
destructor TResStrings.Destroy;
begin
FItems.Free;
inherited Destroy;
end;
procedure TResStrings.Clear;
begin
FItems.Clear;
end;
function TResStrings.GetCount: Integer;
begin
Result := FItems.Count;
end;
function TResStrings.GetItems(Index: Integer): TResItem;
begin
Result := TResItem(FItems[Index]);
end;
function TResStrings.HasResValue(const Value: string): TResItem;
var
i: Integer;
V: string;
begin
V := GetCleanValue(Value);
for i := 0 to Count - 1 do
begin
Result := Items[i];
if GetCleanValue(Result.Value) = V then
Exit;
end;
Result := nil;
end;
function TResStrings.NewResString(const Name, Value: string): TResItem;
var i: Integer;
begin
if Trim(Name) = '' then
raise Exception.CreateFmt('Invalid resourcestring name "%s"', [Name]);
Result := TResItem.Create;
Result.Name := Name;
for i := 0 to Count - 1 do
if SameText(Items[i].Name, Name) then
begin
Result.Name := Result.Name + '_';
Break;
end;
Result.Value := Value;
Result.RefCount := 1;
FItems.Add(Result);
end;
procedure TResStrings.SaveToFile(const Filename: string);
var
Lines: TStrings;
i: Integer;
begin
Lines := TStringList.Create;
try
Lines.Add('resourcestring');
for i := 0 to Count - 1 do
Lines.Add(' ' + Items[i].Name + ' = ' + ProperOut(' ', Items[i].Value + ';', 70));
Lines.SaveToFile(Filename);
finally
Lines.Free;
end;
end;
function TResStrings.GetAsString: string;
var
Lines: TStrings;
i: Integer;
begin
Lines := TStringList.Create;
try
Lines.Add('resourcestring');
for i := 0 to Count - 1 do
Lines.Add(' ' + Items[i].Name + ' = ' + ProperOut(' ', Items[i].Value + ';', 70));
Result := Lines.Text;
finally
Lines.Free;
end;
end;
{ TResItem }
procedure TResItem.AddRef;
begin
Inc(RefCount);
end;
end.