You've already forked lazarus-ccr
iphonelazext: added handling of unregistered classes and unknown properties, so the data would not be lost, even if not recognized by rtti reader.
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@4428 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
@ -23,13 +23,27 @@ unit pbxcontainer;
|
|||||||
* for array of strings TPBXStringArray must be used *
|
* for array of strings TPBXStringArray must be used *
|
||||||
* for key-value set use TPBXKeyValue class *
|
* for key-value set use TPBXKeyValue class *
|
||||||
* string and integer properties are supported... anything else? *
|
* string and integer properties are supported... anything else? *
|
||||||
* booleans are (always) written as 0 or 1 to the file
|
* booleans are (always) written as 0 or 1 to the file *
|
||||||
*
|
* *
|
||||||
* *
|
* *
|
||||||
* todo: add more documentions *
|
* todo: add more documentions *
|
||||||
* *
|
* *
|
||||||
* todo: memoty allocation and release. ObjC is using ref-counted structure. *
|
* todo: memoty allocation and release. ObjC is using ref-counted structure. *
|
||||||
* do the same? similar? *
|
* do the same? similar? *
|
||||||
|
* *
|
||||||
|
* Class naming convention. *
|
||||||
|
* Any "utility" class should follow Object Pascal naming convention and start *
|
||||||
|
* with "T" (i.e. TPBXValue) *
|
||||||
|
* Any class that's expected to map an actual class (objective-c?), stored *
|
||||||
|
* in .pbx file, should go without "T" prefix. The name of the class *
|
||||||
|
* should match the one loaded/saved to the PBX file. *
|
||||||
|
* This is necessary for serializing purposes *
|
||||||
|
* Any mapped class must inherit from the base PBXObject. *
|
||||||
|
* *
|
||||||
|
* TPBXUnkObject inherites from PBXObject, but since it has an utility role *
|
||||||
|
* it falls under Object Pascal naming convention. It also stores ObjC *
|
||||||
|
* class name as a property (which is serialized as a classname) *
|
||||||
|
* *
|
||||||
-------------------------------------------------------------------------------*)
|
-------------------------------------------------------------------------------*)
|
||||||
|
|
||||||
interface
|
interface
|
||||||
@ -40,6 +54,18 @@ uses
|
|||||||
Classes, SysUtils, typinfo, pbxfile, contnrs;
|
Classes, SysUtils, typinfo, pbxfile, contnrs;
|
||||||
|
|
||||||
type
|
type
|
||||||
|
TPBXValue = class;
|
||||||
|
{ TPBXUnkProperty }
|
||||||
|
|
||||||
|
TPBXUnkProperty = class(TObject)
|
||||||
|
private
|
||||||
|
fname: string;
|
||||||
|
public
|
||||||
|
value : TPBXValue;
|
||||||
|
constructor Create;
|
||||||
|
destructor Destroy; override;
|
||||||
|
property name : string read fname;
|
||||||
|
end;
|
||||||
|
|
||||||
{ PBXObject }
|
{ PBXObject }
|
||||||
|
|
||||||
@ -47,26 +73,36 @@ type
|
|||||||
private
|
private
|
||||||
_id : string;
|
_id : string;
|
||||||
_fheaderComment : string;
|
_fheaderComment : string;
|
||||||
|
_funkProp : TFPHashObjectList;
|
||||||
|
function GetHasUnkProp: Boolean;
|
||||||
protected
|
protected
|
||||||
// collects the name of string properties that should be written out
|
// collects the name of string properties that should be written out
|
||||||
// even if their values is an empty string.
|
// even if their values is an empty string.
|
||||||
// if property value is an empty string, it would not be written to the file
|
// if property value is an empty string, it would not be written to the file
|
||||||
class procedure _WriteEmpty(propnames: TStrings); virtual;
|
class procedure _WriteEmpty(propnames: TStrings); virtual;
|
||||||
|
function GetUnkProp: TFPHashObjectList;
|
||||||
public
|
public
|
||||||
property __id: string read _id;
|
property __id: string read _id;
|
||||||
property _headerComment: string read _fheaderComment write _fheaderComment;
|
property _headerComment: string read _fheaderComment write _fheaderComment;
|
||||||
constructor Create; virtual;
|
constructor Create; virtual;
|
||||||
|
destructor Destroy; override;
|
||||||
function GetIsaName: string; virtual;
|
function GetIsaName: string; virtual;
|
||||||
|
//todo: unknown properties are read as "strings"
|
||||||
|
// however, some strings are actually object references
|
||||||
|
// these references must be resolved (as actual objects!)
|
||||||
|
function _addUnkProp(const aname: string): TPBXUnkProperty;
|
||||||
|
property _unkProp: TFPHashObjectList read GetUnkProp;
|
||||||
|
property _hasUnkProp: Boolean read GetHasUnkProp;
|
||||||
end;
|
end;
|
||||||
PBXObjectClass = class of PBXObject;
|
PBXObjectClass = class of PBXObject;
|
||||||
|
|
||||||
TPBXObjectsList = class(TObjectList);
|
TPBXObjectsList = class(TObjectList);
|
||||||
TPBXStringArray = class(TStringList);
|
TPBXStringArray = class(TStringList);
|
||||||
|
|
||||||
TPBXKeyValue = class;
|
|
||||||
|
|
||||||
TPBXValueType = (vtString, vtArrayOfStr, vtKeyVal);
|
TPBXValueType = (vtString, vtArrayOfStr, vtKeyVal);
|
||||||
|
|
||||||
|
TPBXKeyValue = class;
|
||||||
|
|
||||||
{ TPBXValue }
|
{ TPBXValue }
|
||||||
|
|
||||||
TPBXValue = class(TObject)
|
TPBXValue = class(TObject)
|
||||||
@ -266,6 +302,20 @@ begin
|
|||||||
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{ TPBXUnkProperty }
|
||||||
|
|
||||||
|
constructor TPBXUnkProperty.Create;
|
||||||
|
begin
|
||||||
|
inherited Create;
|
||||||
|
value:=TPBXValue.Create;
|
||||||
|
end;
|
||||||
|
|
||||||
|
destructor TPBXUnkProperty.Destroy;
|
||||||
|
begin
|
||||||
|
value.Free;
|
||||||
|
inherited Destroy;
|
||||||
|
end;
|
||||||
|
|
||||||
{ TPBXUnkClass }
|
{ TPBXUnkClass }
|
||||||
|
|
||||||
constructor TPBXUnkClass.CreateWithName(const AISA: string);
|
constructor TPBXUnkClass.CreateWithName(const AISA: string);
|
||||||
@ -350,14 +400,30 @@ end;
|
|||||||
|
|
||||||
{ TPBXObject }
|
{ TPBXObject }
|
||||||
|
|
||||||
|
function PBXObject.GetHasUnkProp: Boolean;
|
||||||
|
begin
|
||||||
|
Result:=Assigned(_funkProp) and (_funkProp.Count>0);
|
||||||
|
end;
|
||||||
|
|
||||||
class procedure PBXObject._WriteEmpty(propnames: TStrings);
|
class procedure PBXObject._WriteEmpty(propnames: TStrings);
|
||||||
begin
|
begin
|
||||||
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function PBXObject.GetUnkProp: TFPHashObjectList;
|
||||||
|
begin
|
||||||
|
if not Assigned(_funkProp) then _funkProp:=TFPHashObjectList.Create(true);
|
||||||
|
Result:=_funkProp;
|
||||||
|
end;
|
||||||
|
|
||||||
constructor PBXObject.Create;
|
constructor PBXObject.Create;
|
||||||
begin
|
begin
|
||||||
|
end;
|
||||||
|
|
||||||
|
destructor PBXObject.Destroy;
|
||||||
|
begin
|
||||||
|
_funkProp.Free;
|
||||||
|
inherited Destroy;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function PBXObject.GetIsaName: string;
|
function PBXObject.GetIsaName: string;
|
||||||
@ -365,6 +431,13 @@ begin
|
|||||||
Result:=ClassName;
|
Result:=ClassName;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function PBXObject._addUnkProp(const aname: string): TPBXUnkProperty;
|
||||||
|
begin
|
||||||
|
Result:=TPBXUnkProperty.Create;
|
||||||
|
Result.fname:=aname;
|
||||||
|
_unkProp.Add(aname, Result);
|
||||||
|
end;
|
||||||
|
|
||||||
{ TPBXContainer }
|
{ TPBXContainer }
|
||||||
|
|
||||||
procedure PBXReref(objs: TObjHashList; refs: TList);
|
procedure PBXReref(objs: TObjHashList; refs: TList);
|
||||||
@ -535,6 +608,31 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function GuessProperty(p: TPBXParser; uprop: TPBXUnkProperty): Boolean;
|
||||||
|
begin
|
||||||
|
case p.CurEntity of
|
||||||
|
etValue: begin
|
||||||
|
// assuming string. Object Ref will be resolved later!
|
||||||
|
uprop.value.str:=p.Value;
|
||||||
|
uprop.value.valType:=vtString;
|
||||||
|
Result:=true;
|
||||||
|
end;
|
||||||
|
etOpenArray: begin
|
||||||
|
// assuming array of strings. Array of Objects will be resolved later!
|
||||||
|
uprop.value.arr:=TPBXStringArray.Create;
|
||||||
|
uprop.value.valType:=vtArrayOfStr;
|
||||||
|
Result:=PBXReadStringArray(p, uprop.value.arr);
|
||||||
|
end;
|
||||||
|
etOpenObject: begin
|
||||||
|
uprop.value.keyval:=TPBXKeyValue.Create;
|
||||||
|
uprop.value.valType:=vtKeyVal;
|
||||||
|
Result:=PBXReadKeyValue(p, uprop.value.keyval);
|
||||||
|
end;
|
||||||
|
else
|
||||||
|
Result:=false;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
function PBXReadClass(p: TPBXParser; obj: PBXObject; refs: TList): Boolean;
|
function PBXReadClass(p: TPBXParser; obj: PBXObject; refs: TList): Boolean;
|
||||||
var
|
var
|
||||||
tk : TPBXEntity;
|
tk : TPBXEntity;
|
||||||
@ -542,6 +640,7 @@ var
|
|||||||
prp : PPropInfo;
|
prp : PPropInfo;
|
||||||
pobj : TObject;
|
pobj : TObject;
|
||||||
pk : TTypeKind;
|
pk : TTypeKind;
|
||||||
|
uprop : TPBXUnkProperty;
|
||||||
begin
|
begin
|
||||||
lvl:=p.Level;
|
lvl:=p.Level;
|
||||||
tk:=p.FetchNextEntity;
|
tk:=p.FetchNextEntity;
|
||||||
@ -588,9 +687,11 @@ begin
|
|||||||
PBXParserSkipLevel(p);
|
PBXParserSkipLevel(p);
|
||||||
end;
|
end;
|
||||||
end else begin
|
end else begin
|
||||||
writeln(obj.ClassName, ': property not found: ', p.Name);
|
writeln(obj.ClassName, ': unkown property: ', p.Name);
|
||||||
if tk <> etValue then
|
uprop:=obj._addUnkProp(p.Name);
|
||||||
PBXParserSkipLevel(p);
|
GuessProperty(p,uprop);
|
||||||
|
{if tk <> etValue then
|
||||||
|
PBXParserSkipLevel(p);}
|
||||||
end;
|
end;
|
||||||
|
|
||||||
tk:=p.FetchNextEntity;
|
tk:=p.FetchNextEntity;
|
||||||
@ -712,6 +813,17 @@ begin
|
|||||||
w.CloseBlock( '}' );
|
w.CloseBlock( '}' );
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
type
|
||||||
|
|
||||||
|
{ TWriteProp }
|
||||||
|
|
||||||
|
TWriteProp = class(TObject)
|
||||||
|
propIdx : integer;
|
||||||
|
unk : TPBXUnkProperty;
|
||||||
|
constructor Create(aidx: Integer); overload;
|
||||||
|
constructor Create(aunk: TPBXUnkProperty); overload;
|
||||||
|
end;
|
||||||
|
|
||||||
procedure PBXWriteObj(pbx: PBXObject; w: TPBXWriter; WriteEmpty: TStrings);
|
procedure PBXWriteObj(pbx: PBXObject; w: TPBXWriter; WriteEmpty: TStrings);
|
||||||
var
|
var
|
||||||
p : PPropList;
|
p : PPropList;
|
||||||
@ -724,8 +836,10 @@ var
|
|||||||
vcmt : string;
|
vcmt : string;
|
||||||
isstr : Boolean;
|
isstr : Boolean;
|
||||||
|
|
||||||
// used for sorting. todo: find a better way for sort by names!
|
names : TStringList; // used for sorting.
|
||||||
names : TStringList;
|
// todo: find a better way for sort by names
|
||||||
|
wp : TWriteProp;
|
||||||
|
up : TPBXUnkProperty;
|
||||||
begin
|
begin
|
||||||
|
|
||||||
w.WriteName(pbx._id, pbx._headerComment);
|
w.WriteName(pbx._id, pbx._headerComment);
|
||||||
@ -739,23 +853,69 @@ begin
|
|||||||
p:=nil;
|
p:=nil;
|
||||||
cnt:=GetPropList(pbx, p);
|
cnt:=GetPropList(pbx, p);
|
||||||
|
|
||||||
//todo: I don't like this soritng at all!
|
//todo: I don't like this sorting at all!
|
||||||
// but it appears to be the most common available
|
// but it appears to be the most common available
|
||||||
names:=TStringList.Create;
|
names:=TStringList.Create;
|
||||||
try
|
try
|
||||||
for i:=0 to cnt-1 do names.AddObject(p^[i].Name, TObject(PtrUInt(i)));
|
names.OwnsObjects:=true;
|
||||||
|
|
||||||
|
for i:=0 to cnt-1 do begin
|
||||||
|
writeln('idx = ', i);
|
||||||
|
wp:=TWriteProp.Create(i);
|
||||||
|
names.AddObject(p^[i].Name, wp);
|
||||||
|
//names.AddObject(p^[i].Name, TObject(PtrInt(i)));
|
||||||
|
end;
|
||||||
|
//for i:=0 to
|
||||||
|
if pbx._hasUnkProp then begin
|
||||||
|
for i:=0 to pbx._unkProp.Count-1 do begin
|
||||||
|
writeln('udx = ', i);
|
||||||
|
up:=TPBXUnkProperty(pbx._unkProp[i]);
|
||||||
|
wp:=TWriteProp.Create(up);
|
||||||
|
names.AddObject(up.name, wp);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
names.Sort;
|
names.Sort;
|
||||||
|
|
||||||
for j:=0 to names.Count-1 do begin
|
for j:=0 to names.Count-1 do begin
|
||||||
i:=Integer(PtrUInt(names.Objects[j]));
|
//i:=Integer(PtrInt(names.Objects[j]));
|
||||||
|
|
||||||
vl:='';
|
vl:='';
|
||||||
vcmt:='';
|
vcmt:='';
|
||||||
isstr:=false;
|
isstr:=false;
|
||||||
|
sobj:=nil;
|
||||||
|
|
||||||
|
wp:=TWriteProp(names.Objects[j]);
|
||||||
|
if not Assigned(wp.unk) then begin
|
||||||
|
i:=wp.propIdx;
|
||||||
|
writeln('idx = ', i);
|
||||||
nm:=p^[i].Name;
|
nm:=p^[i].Name;
|
||||||
if p^[i].PropType.Kind=tkClass then begin
|
if p^[i].PropType.Kind=tkClass then begin
|
||||||
sobj:=GetObjectProp(pbx, p^[i]);
|
sobj:=GetObjectProp(pbx, p^[i])
|
||||||
|
end else begin
|
||||||
|
if p^[i].PropType.Kind in [tkAString, tkString] then begin
|
||||||
|
vl:=GetStrProp(pbx,p^[i]);
|
||||||
|
isstr:=(vl<>'') or (WriteEmpty.indexOf(nm)>=0);
|
||||||
|
end else if p^[i].PropType.Kind in [tkInteger, tkInt64, tkQWord] then begin
|
||||||
|
vl:=IntToStr(GetInt64Prop(pbx, p^[i]));
|
||||||
|
isstr:=(vl<>'') or (WriteEmpty.indexOf(nm)>=0);
|
||||||
|
end else if p^[i].PropType.Kind = tkBool then begin
|
||||||
|
isstr:=PtrUInt(p^[i].Default)<>PtrUInt(p^[i].GetProc);
|
||||||
|
if isstr then vl:=IntToStr(GetOrdProp(pbx, p^[i]));
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end else begin
|
||||||
|
nm:=wp.unk.name;
|
||||||
|
case wp.unk.value.valType of
|
||||||
|
vtArrayOfStr: sobj:=wp.unk.value.arr;
|
||||||
|
vtKeyVal: sobj:=wp.unk.value.keyval;
|
||||||
|
else
|
||||||
|
isstr:=true;
|
||||||
|
vl:=wp.unk.value.str;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
if Assigned(sobj) then begin
|
||||||
if sobj is PBXObject then begin
|
if sobj is PBXObject then begin
|
||||||
vl:=PBXObject(sobj)._id;
|
vl:=PBXObject(sobj)._id;
|
||||||
vcmt:=PBXObject(sobj)._headerComment;
|
vcmt:=PBXObject(sobj)._headerComment;
|
||||||
@ -771,15 +931,6 @@ begin
|
|||||||
w.WriteName(nm);
|
w.WriteName(nm);
|
||||||
PBXWriteKeyValue(w, TPBXKeyValue(sobj));
|
PBXWriteKeyValue(w, TPBXKeyValue(sobj));
|
||||||
end;
|
end;
|
||||||
end else if p^[i].PropType.Kind in [tkAString, tkString] then begin
|
|
||||||
vl:=GetStrProp(pbx,p^[i]);
|
|
||||||
isstr:=(vl<>'') or (WriteEmpty.indexOf(nm)>=0);
|
|
||||||
end else if p^[i].PropType.Kind in [tkInteger, tkInt64, tkQWord] then begin
|
|
||||||
vl:=IntToStr(GetInt64Prop(pbx, p^[i]));
|
|
||||||
isstr:=(vl<>'') or (WriteEmpty.indexOf(nm)>=0);
|
|
||||||
end else if p^[i].PropType.Kind = tkBool then begin
|
|
||||||
isstr:=PtrUInt(p^[i].Default)<>PtrUInt(p^[i].GetProc);
|
|
||||||
if isstr then vl:=IntToStr(GetOrdProp(pbx, p^[i]));
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
if isstr then begin
|
if isstr then begin
|
||||||
@ -861,6 +1012,20 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{ TWriteProp }
|
||||||
|
|
||||||
|
constructor TWriteProp.Create(aidx: Integer);
|
||||||
|
begin
|
||||||
|
inherited Create;
|
||||||
|
propIdx:=aidx;
|
||||||
|
end;
|
||||||
|
|
||||||
|
constructor TWriteProp.Create(aunk: TPBXUnkProperty);
|
||||||
|
begin
|
||||||
|
inherited Create;
|
||||||
|
unk:=aunk;
|
||||||
|
end;
|
||||||
|
|
||||||
initialization
|
initialization
|
||||||
pbxClassList := TStringList.Create;
|
pbxClassList := TStringList.Create;
|
||||||
|
|
||||||
|
@ -20,8 +20,8 @@ interface
|
|||||||
* * line breaks (note OSX is typically using \n, unlike Unix \r) *
|
* * line breaks (note OSX is typically using \n, unlike Unix \r) *
|
||||||
* *
|
* *
|
||||||
* *
|
* *
|
||||||
* PBXScanner - scans through the file *
|
* TPBXScanner - scans through the file *
|
||||||
* PBXParser - parses the file, returning a higher level entities of the file: *
|
* TPBXParser - parses the file, returning a higher level entities of the file: *
|
||||||
* values, open/close of object/array *
|
* values, open/close of object/array *
|
||||||
* The parser doesn't produce any kind of structure. Instead it only allows to *
|
* The parser doesn't produce any kind of structure. Instead it only allows to *
|
||||||
* build one. i.e. PBXContainer *
|
* build one. i.e. PBXContainer *
|
||||||
|
Reference in New Issue
Block a user