{ girnamespaces.pas Copyright (C) 2011 Andrew Haines andrewd207@aol.com This program 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 program 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. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. } unit girNameSpaces; {$mode objfpc}{$H+} {$INTERFACES CORBA} interface uses Classes, DOM, girParser, girTokens, girObjects, contnrs; type TgirNeedGirFileEvent = function (AGirFile: TObject; BaseNamespaceName: String) : TXMLDocument of object; { TgirNamespace } TgirNamespace = class(IgirParser) private FCIncludeName: String; FConstants: TList; FCPackageName: String; FFunctions: TList; FNameSpace: String; FOnlyImplied: Boolean; FOnNeedGirFile: TgirNeedGirFileEvent; FOwner: TObject; FRequiredNameSpaces: TList; FSharedLibrary: String; FTypes: TFPHashObjectList; FUnresolvedTypes: TList; FVersion: String; procedure SetOnNeedGirFile(AValue: TgirNeedGirFileEvent); protected function AddFuzzyType(AName: String; ACType: String): TGirBaseType; procedure HandleAlias(ANode: TDomNode); procedure HandleConstant(ANode: TDomNode); procedure HandleEnumeration(ANode: TDomNode); procedure HandleBitField(ANode: TDomNode); procedure HandleCallback(ANode: TDOMNode); procedure HandleFunction(ANode: TDOMNode); procedure HandleUnion(ANode: TDOMNode); { Some 'records' have methods these corelate to pascal 'object' GType extends this 'object' type to have a sort of vmt GObject and subclasses extend gtype and adds more vmt method entries and method entries to the instance itself. } procedure HandleRecord(ANode: TDomNode); //could be struct, object, gtype, gobject, or gobject descendant procedure HandlePlainObject(ANode: TDomNode); // is a record/object with methods but no gtype procedure HandleGType(ANode: TDomNode); // one step above plain object procedure HandleClassStruct(ANode: TDomNode); // one step above GType. Is the 'Virtual' part of an object (VMT) procedure HandleClass(ANode: TDomNode); // one step above GType. Is the object structure and it's methods. ClassStruct is like the VMT procedure HandleInterface(ANode: TDomNode); procedure AddGLibBaseTypes; public function LookupTypeByName(AName: String; const ACType: String; const SearchOnly: Boolean = False): TGirBaseType; function ResolveFuzzyType(AFuzzyType: TgirFuzzyType): TGirBaseType; function UsesGLib: Boolean; procedure ResolveFuzzyTypes; // called after done procedure ParseNode(ANode: TDomNode); procedure ParseSubNode(ANode: TDomNode); // generally do not use outside of TgirNameSpace constructor Create(AOwner:TObject; AImpliedNamespace: String); constructor CreateFromRepositoryNode(AOwner:TObject; ANode: TDOMNode; AIncludes: TList); destructor Destroy; override; property NameSpace: String read FNameSpace; property CIncludeName: String read FCIncludeName; property CPackageName: String read FCPackageName; property RequiredNameSpaces: TList Read FRequiredNameSpaces; property SharedLibrary: String read FSharedLibrary; property Version: String read FVersion; property OnlyImplied: Boolean read FOnlyImplied; property Owner: TObject Read FOwner; // has all types in it (records classes classstructs bitfields callbacks gtypes unions etc) does not contain consts or functions property Types: TFPHashObjectList read FTypes; property Functions: TList read FFunctions; property Constants: TList read FConstants; property UnresolvedTypes: TList read FUnresolvedTypes write FUnresolvedTypes; end; { TgirNamespaces } TgirNamespaces = class(TList) private FOnNeedGirFile: TgirNeedGirFileEvent; FOwner: TObject; function GetNameSpace(AIndex: Integer): TgirNamespace; procedure SetNameSpace(AIndex: Integer; const AValue: TgirNamespace); procedure SetOnNeedGirFile(AValue: TgirNeedGirFileEvent); public constructor Create(AOwner: TObject); function FindNameSpace(AName: String; Version: String = ''): TgirNamespace; property NameSpace[AIndex: Integer]: TgirNamespace read GetNameSpace write SetNameSpace; property Owner: TObject read FOwner; property OnNeedGirFile: TgirNeedGirFileEvent read FOnNeedGirFile write SetOnNeedGirFile; end; implementation uses girErrors, SysUtils, girCTypesMapping; { TgirNamespaces } function TgirNamespaces.GetNameSpace(AIndex: Integer): TgirNamespace; begin Result := TgirNamespace(Items[AIndex]); end; procedure TgirNamespaces.SetNameSpace(AIndex: Integer; const AValue: TgirNamespace); begin Items[AIndex] := AValue; end; procedure TgirNamespaces.SetOnNeedGirFile(AValue: TgirNeedGirFileEvent); begin if FOnNeedGirFile=AValue then Exit; FOnNeedGirFile:=AValue; end; constructor TgirNamespaces.Create(AOwner: TObject); begin FOwner := AOwner; inherited Create; end; function TgirNamespaces.FindNameSpace(AName: String; Version: String=''): TgirNamespace; var i: Integer; NameSpaceSearchedFor: Boolean; Doc: TXMLDocument; begin Result := nil; NameSpaceSearchedFor := False; while Result = nil do begin for i := 0 to Count-1 do begin if NameSpace[i].NameSpace = AName then Exit(NameSpace[i]); end; if NameSpaceSearchedFor then Exit; NameSpaceSearchedFor := True; if Assigned(FOnNeedGirFile) then begin Doc := FOnNeedGirFile(Owner, AName+'-'+Version); if Doc <> nil then begin (Owner as IgirParser).ParseNode(Doc.DocumentElement); Doc.Free; end; end; end; end; { TgirNamespace } procedure TgirNamespace.ParseNode(ANode: TDomNode); begin ANode := ANode.FirstChild; while ANode <> nil do begin //girError(geDebug, 'Parsing Node "'+ANode.NodeName+'"'); ParseSubNode(ANode); ANode := ANode.NextSibling; end; end; procedure TgirNamespace.SetOnNeedGirFile(AValue: TgirNeedGirFileEvent); begin if FOnNeedGirFile=AValue then Exit; FOnNeedGirFile:=AValue; end; function TgirNamespace.AddFuzzyType(AName: String; ACType: String ): TGirBaseType; begin Result := TgirFuzzyType.Create(Self, AName, ACType); FTypes.Add(AName, Result); FUnresolvedTypes.Add(Result); end; procedure TgirNamespace.HandleAlias(ANode: TDomNode); var Item: TgirAlias; begin Item := TgirAlias.Create(Self, ANode); Types.Add(Item.Name, Item); end; procedure TgirNamespace.HandleConstant(ANode: TDomNode); var Item: TgirConstant; begin Item := TgirConstant.Create(Self, ANode); FConstants.Add(Item); end; procedure TgirNamespace.HandleEnumeration(ANode: TDomNode); var Item : TgirEnumeration; begin Item := TgirEnumeration.Create(Self, ANode); Types.Add(Item.Name, Item); end; procedure TgirNamespace.HandleBitField(ANode: TDomNode); var Item : TgirBitField; begin Item := TgirBitField.Create(Self, ANode); Types.Add(Item.Name, Item); end; procedure TgirNamespace.HandleCallback(ANode: TDOMNode); var Item: TgirCallback; begin Item := TgirCallback.Create(Self, ANode); Types.Add(Item.Name, Item); end; procedure TgirNamespace.HandleFunction(ANode: TDOMNode); var Item: TgirFunction; begin Item := TgirFunction.Create(Self, ANode); Functions.Add(Item); end; procedure TgirNamespace.HandleUnion(ANode: TDOMNode); var Item: TgirUnion; begin Item := TgirUnion.Create(Self, ANode); Types.Add(Item.Name, Item); end; procedure TgirNamespace.HandleRecord(ANode: TDomNode); var Item: tgirRecord; begin if TDOMElement(ANode).GetAttribute('glib:is-gtype-struct-for') <> '' then // is gobject class begin HandleClassStruct(ANode); end else if TDOMElement(ANode).GetAttribute('glib:get-type') <> '' then // is GType HandleGType(ANode) else if (ANode.FindNode('method') <> nil) or (ANode.FindNode('constructor') <> nil) or (ANode.FindNode('function') <> nil) then // is Plain object that is not gtype HandlePlainObject(ANode) else begin Item := tgirRecord.Create(Self, ANode); Types.Add(Item.Name, Item); end; end; procedure TgirNamespace.HandlePlainObject(ANode: TDomNode); var Item: TgirObject; begin Item := TgirObject.Create(Self, ANode); Types.Add(Item.Name, Item); end; procedure TgirNamespace.HandleGType(ANode: TDomNode); var Item: TgirGType; begin Item := TgirGType.Create(Self, ANode); Types.Add(Item.Name, Item); end; procedure TgirNamespace.HandleClassStruct(ANode: TDomNode); var Item: TgirClassStruct; begin Item := TgirClassStruct.Create(Self, ANode); FTypes.Add(Item.Name, Item); end; procedure TgirNamespace.HandleClass(ANode: TDomNode); var Item: TgirClass; begin Item := TgirClass.Create(Self, ANode); FTypes.Add(Item.Name, Item); end; procedure TgirNamespace.HandleInterface(ANode: TDomNode); var Item: TgirInterface; begin Item := TgirInterface.Create(Self, ANode); FTypes.Add(Item.Name, Item); end; procedure TgirNamespace.AddGLibBaseTypes; procedure AddNativeTypeDef(GType: String; PascalCName: String); var NativeType: TgirNativeTypeDef; begin NativeType:= TgirNativeTypeDef.Create(Self, GType, PascalCName); Types.Add(NativeType.Name, NativeType); end; var i: Integer; begin for i := 0 to CTypesMax-1 do AddNativeTypeDef(TypesGTypes[i], TypesPascalCTypes[i]); end; procedure TgirNamespace.ResolveFuzzyTypes; var i: Integer; FuzzyI: Integer; Fuzzy: TgirFuzzyType; Tmp: TGirBaseType; begin i:= 0; FuzzyI := 0; Fuzzy := nil; while (i < FTypes.Count) or (Fuzzy <> nil) do begin // make our loop safe if i >= FTypes.Count then begin i := FuzzyI+1; Fuzzy := nil; continue; end; Tmp := TGirBaseType(FTypes.Items[i]); if Fuzzy <> nil then begin if {(Tmp.CType = Fuzzy.CType) or} (Tmp.Name = Fuzzy.Name) then begin Fuzzy.ResolvedType := Tmp; Tmp.ImpliedPointerLevel:=Fuzzy.ImpliedPointerLevel; i := FuzzyI+1; Fuzzy := nil; //WriteLn('Resolved Fuzzy Type: ', Tmp.CType); continue; end; end; if (Fuzzy = nil) and (Tmp.ObjectType = otFuzzyType) and (TgirFuzzyType(Tmp).ResolvedType = nil) then begin if i >= FTypes.Count then break; FuzzyI:=i; Fuzzy := TgirFuzzyType(Tmp); //WriteLn('Looking For: ',Fuzzy.CType); end; inc(i); end; end; procedure TgirNamespace.ParseSubNode(ANode: TDomNode); begin case GirTokenNameToToken(ANode.NodeName) of gtAlias: HandleAlias(ANode); gtConstant: HandleConstant(ANode); gtRecord: HandleRecord(ANode); gtBitField: HandleBitField(ANode); gtEnumeration: HandleEnumeration(ANode); gtCallback: HandleCallback(ANode); gtUnion: HandleUnion(ANode); gtFunction: HandleFunction(ANode); gtClass: HandleClass(ANode); gtInterface: HandleInterface(ANode); gtMethod: HandleFunction(ANode); else girError(geError, 'Unknown NodeType: '+ANode.NodeName); end; ResolveFuzzyTypes; end; function TgirNamespace.LookupTypeByName(AName: String; const ACType: String; const SearchOnly: Boolean = False): TGirBaseType; function StripPointers(ACPointeredType: String; PtrLevel: PInteger = nil): String; var i: Integer; begin for i := Length(ACPointeredType) downto 1 do if ACPointeredType[i] = '*' then begin Delete(ACPointeredType, i, 1); end; if PtrLevel <> nil then Inc(PtrLevel^); Result := ACPointeredType; end; var ReqNS, NS: TgirNamespace; NSString: String; FPos: Integer; i: Integer; Current: TGirBaseType; PointerLevel: Integer = 0; PlainCType: String; begin Result := nil; NS := Self; FPos := Pos('.', AName); PlainCType:=StringReplace(StripPointers(ACType, @PointerLevel), ' ', '_', [rfReplaceAll]); if FPos > 0 then // type includes namespace "NameSpace.Type" begin NSString:=Copy(AName,1,FPos-1); NS := (Owner As TgirNamespaces).FindNameSpace(NSString); if NS = nil then girError(geError, 'Referenced Namespace "'+NSString+'" not found while looking for '+AName); AName := Copy(AName, FPos+1, Length(AName)); end; // some basic fixes if PlainCType = 'gchar' then AName := 'utf8'; if PlainCType = 'GType' then AName := 'Type'; Result := TGirBaseType(NS.Types.Find(AName)); if (Result <> nil) and (Result.ObjectType = otFuzzyType) and (TgirFuzzyType(Result).ResolvedType <> nil) then Result := TgirFuzzyType(Result).ResolvedType; if (Result = nil) and not SearchOnly then begin for i := 0 to NS.RequiredNameSpaces.Count-1 do begin ReqNS := TgirNamespace(NS.RequiredNameSpaces.Items[i]); Current := ReqNS.LookupTypeByName(AName, ACType, True); if Current <> nil then begin if (Current.ObjectType = otFuzzyType) and (TgirFuzzyType(Current).ResolvedType <> nil) then Current := TgirFuzzyType(Current).ResolvedType; Result := Current; Break; end; end; if Result = nil then Result := NS.AddFuzzyType(AName, ACType); end; if Result <> nil then Result.ImpliedPointerLevel:=PointerLevel; end; function TgirNamespace.ResolveFuzzyType(AFuzzyType: TgirFuzzyType): TGirBaseType; var i: Integer; begin for i := 0 to FTypes.Count-1 do begin if (TGirBaseType(FTypes[i]) <> AFuzzyType) and (TGirBaseType(FTypes[i]).Name = AFuzzyType.Name) then Exit(TGirBaseType(FTypes[i])); end; end; function TgirNamespace.UsesGLib: Boolean; var Tmp: Pointer; NS: TgirNamespace absolute Tmp; begin Result := False; if Pos('glib', LowerCase(NameSpace)) = 1 then Exit(True); for Tmp in RequiredNameSpaces do if Pos('glib',LowerCase(NS.NameSpace)) = 1 then Exit(True); end; constructor TgirNamespace.Create(AOwner:TObject; AImpliedNamespace: String); begin Fowner:=AOwner; FOnlyImplied:=True; FNameSpace:=AImpliedNamespace; girError(geDebug, 'Creating Stub for namespace: '+ AImpliedNamespace); end; constructor TgirNamespace.CreateFromRepositoryNode(AOwner:TObject; ANode: TDOMNode; AIncludes: TList); procedure SetCInclude; var Child: TDomElement; begin Child := TDOMElement(ANode.FindNode('c:include name')); if (Child <> nil) and Child.InheritsFrom(TDOMElement) then FCIncludeName:= Child.GetAttribute('name'); end; procedure SetPackage; var Child: TDOMElement; begin Child := TDOMElement(ANode.FindNode('package')); if (Child <> nil) and Child.InheritsFrom(TDOMElement) then FCPackageName:=Child.GetAttribute('name'); end; var Node: TDOMElement; begin FOwner := AOwner; if ANode = nil then girError(geError, 'expected namespace got nil'); if ANode.NodeName <> 'repository' then girError(geError, 'expected "repository" got '+ANode.NodeName); Node := TDOMElement( ANode.FindNode('namespace') ); FNameSpace:=Node.GetAttribute('name'); FRequiredNameSpaces := AIncludes; FSharedLibrary:=Node.GetAttribute('shared-library'); FVersion:=Node.GetAttribute('version'); SetCInclude; SetPackage; girError(geDebug, Format('Creating namespace=%s Version=%s LibName=%s',[FNameSpace, FVersion, FSharedLibrary])); FConstants := TList.Create; FFunctions := TList.Create; FTypes := TFPHashObjectList.Create(True); FUnresolvedTypes := TList.Create; if FNameSpace = 'GLib' then AddGLibBaseTypes; end; destructor TgirNamespace.Destroy; begin FConstants.Free; FFunctions.Free; FTypes.Free; FUnresolvedTypes.Free; if Assigned(FRequiredNameSpaces) then FRequiredNameSpaces.Free; inherited Destroy; end; end.