{
    This file is part of the Web Service Toolkit
    Copyright (c) 2006 by Inoussa OUEDRAOGO

    This file is provide under modified LGPL licence
    ( the files COPYING.modifiedLGPL and COPYING.LGPL).
    

    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.
}
{$INCLUDE wst_global.inc}
unit imp_utils;

interface

uses
  Classes, SysUtils, TypInfo,
  wst_types, base_service_intf;

Type

  EPropertyManagerException = class(EServiceException)
  End;
  
  { TPublishedPropertyManager }

  TPublishedPropertyManager = class(TInterfacedObject,IPropertyManager)
  Private
    FParent : TObject;
    FUnknownProps : IPropertyManager;
    FHandleUnknownProps : Boolean;
  private
    procedure Error(Const AMsg:string);overload;{$IFDEF USE_INLINE}inline;{$ENDIF}
    procedure Error(Const AMsg:string; Const AArgs : array of const);overload;
  Protected
    procedure SetProperty(Const AName,AValue:string);
    procedure SetProperties(Const APropsStr:string);
    function GetProperty(Const AName:String):string;
    function GetPropertyNames(ADest : TStrings):Integer;
    procedure Clear();
    procedure Copy(ASource:IPropertyManager; Const AClearBefore : Boolean);
  Public
    constructor Create(
            AParent             : TObject;
      const AHandleUnknownProps : Boolean = False
    );
  End;

  function IsStrEmpty(Const AStr:String):Boolean;{$IFDEF USE_INLINE}inline;{$ENDIF}overload;
  function IsStrEmpty(Const AStr:ShortString):Boolean;{$IFDEF USE_INLINE}inline;{$ENDIF}overload;
  function GetToken(var ABuffer : string; const ADelimiter : string): string;
  function ExtractOptionName(const ACompleteName : string):string;
  function ExtractNameFromQualifiedName(const AQualifiedName : string; const ASeparator : Char = ':') : string;
  function TranslateDotToDecimalSeperator(const Value: string) : string;
  function wst_FormatFloat(
    const ATypeInfo  : PTypeInfo;
    const AData      : Extended
  ) : string;


  function LoadBufferFromFile(const AFileName : string) : TByteDynArray;
  function LoadBufferFromStream(AStream : TStream) : TByteDynArray;

{$IFNDEF WST_HAS_STRICT_DELIMITER}
  procedure SetListData(AList : TStringList; const AText : string);
{$ENDIF WST_HAS_STRICT_DELIMITER}

implementation

function IsStrEmpty(Const AStr:String):Boolean;
begin
  Result := ( Length(Trim(AStr)) = 0 );
end;

function IsStrEmpty(Const AStr : ShortString) : Boolean;
begin
  Result := ( Length(Trim(AStr)) = 0 );
end;

function GetToken(var ABuffer : string; const ADelimiter : string): string;
var
  locPos, locOfs, locLen : PtrInt;
  locStr             : string;
begin
  locPos := Pos(ADelimiter, ABuffer);
  locLen := Length(ADelimiter);
  locOfs := locLen - 1;
  if (IsStrEmpty(ABuffer)) or ((locPos = 0) and (Length(ABuffer) > 0)) then begin
    Result := ABuffer;
    ABuffer := '';
  end else  begin
    locStr := Copy(ABuffer, 1, locPos + locOfs);
    ABuffer := Copy(ABuffer, locPos + locLen, Length(ABuffer));
    Result := Copy(locStr, 1, Length(locStr) - locLen);
  end;
end;

function ExtractOptionName(const ACompleteName : string):string;
var
  i, c : Integer;
begin
  Result := '';
  c := Length(ACompleteName);
  for i := c downto 1 do begin
    if ( ACompleteName[i] = '_' ) then
      Break;
    Result := ACompleteName[i] + Result;
  end;
  Result := Trim(Result);
end;

function ExtractNameFromQualifiedName(const AQualifiedName : string; const ASeparator : Char) : string;
var
  sepPos : Integer;
begin
  sepPos := Pos(ASeparator,AQualifiedName);
  if ( sepPos <= 0 ) then
    sepPos := 0;
  Result := Copy(AQualifiedName,(sepPos + 1),Length(AQualifiedName));
end;

{$IFDEF HAS_FORMAT_SETTINGS}
  {$IFDEF DELPHI}
var
  DefaultFormatSettings : TFormatSettings absolute FormatSettings;
  {$DEFINE HAS_DEFAULT_FORMAT_SETTINGS}
  {$ENDIF DELPHI}
{$ENDIF HAS_FORMAT_SETTINGS}
function TranslateDotToDecimalSeperator(const Value: string) : string;
var
  i : PtrInt;
begin
  Result := Value;
  for i := 1 to length(Result) do begin
    if ( Result[i] = '.' ) then
      Result[i] := {$IFDEF HAS_DEFAULT_FORMAT_SETTINGS}DefaultFormatSettings.{$ENDIF}DecimalSeparator;
  end;
end;

function wst_FormatFloat(
  const ATypeInfo  : PTypeInfo;
  const AData      : Extended
) : string;
var
  s, frmt : string;
  prcsn : Integer;
  decimal : Boolean;
{$IFNDEF HAS_FORMAT_SETTINGS}
  i : PtrInt;
{$ENDIF HAS_FORMAT_SETTINGS}
begin
  decimal := False;
  case GetTypeData(ATypeInfo)^.FloatType Of
    ftCurr      :
      begin
        decimal := True;
        frmt := '##############0.####';//15.4
      end;
    ftSingle,
    ftComp      : prcsn := 7;
    ftDouble,
    ftExtended  :
      begin
{$IF Defined(FPC_HAS_TYPE_DOUBLE) OR Defined(FPC_HAS_TYPE_EXTENDED)}
        prcsn := 14;
{$ELSE}
        prcsn := 7;
{$IFEND}
      end;
    else
      prcsn := 7;
  end;
  if not decimal then
    frmt := '#.' + StringOfChar('#',prcsn) + 'E-0';
{$IFDEF HAS_FORMAT_SETTINGS}
  s := FormatFloat(frmt,AData,wst_FormatSettings);
{$ELSE}
  s := FormatFloat(frmt,AData);
  i := Pos(',',s);
  if ( i > 0 ) then
    s[i] := '.';
{$ENDIF HAS_FORMAT_SETTINGS}
  Result := s
end;

function LoadBufferFromStream(AStream : TStream) : TByteDynArray;
var
  len : Int64;
begin
  len := AStream.Size;
  SetLength(Result,len);
  if ( len > 0 ) then begin
    try
      AStream.Seek(0,soBeginning);
      AStream.Read(Result[0],len);
    except
      SetLength(Result,0);
      raise;
    end;
  end;
end;

function LoadBufferFromFile(const AFileName : string) : TByteDynArray;
var
  locStream : TStream;
begin
  locStream := TFileStream.Create(AFileName,fmOpenRead);
  try
    Result := LoadBufferFromStream(locStream);
  finally
    locStream.Free();
  end;
end;

{ TPublishedPropertyManager }

procedure TPublishedPropertyManager.Error(const AMsg: string);
begin
  Raise EPropertyManagerException.Create(AMsg);
end;

procedure TPublishedPropertyManager.Error(const AMsg: string;const AArgs: array of const);
begin
  Raise EPropertyManagerException.CreateFmt(AMsg,AArgs);
end;

procedure TPublishedPropertyManager.SetProperty(const AName, AValue: string);
Var
  pinf : PPropInfo;
  int64Val : Int64;
begin
  pinf := GetPropInfo(FParent,AName);
  if Assigned(pinf) then begin
    if Assigned(pinf^.SetProc) then begin
      Case pinf^.PropType^.Kind of
        tkLString
        {$IFDEF WST_DELPHI},tkString{$ENDIF}
        {$IFDEF FPC},tkSString,tkAString{$ENDIF}
        {$IFDEF WST_UNICODESTRING},tkUString{$ENDIF}
        ,tkWString :
          SetStrProp(FParent,pinf,AValue);
        tkEnumeration :
          SetEnumProp(FParent,pinf,AValue);
        tkInteger,tkInt64{$IFDEF FPC},tkQWord{$ENDIF} :
          Begin
            If TryStrToInt64(AValue,int64Val) Then
              SetOrdProp(FParent,AName,int64Val);
          End;
        {$IFDEF FPC}
        tkBool :
          SetOrdProp(FParent,AName,Ord(StrToBool(AValue)));
        {$ENDIF}
      End;
    end;
  end else if FHandleUnknownProps then begin
    FUnknownProps.SetProperty(AName,AValue);
  end;
end;

{$IFNDEF WST_HAS_STRICT_DELIMITER}
procedure SetListData(AList : TStringList; const AText : string);
var
  i, oldPos, c : Integer;
  s : string;
begin
  c := Length(AText);
  oldPos := 1;
  s := '';
  i := 1;
  while (i <= c) do begin
    if (AText[i] = PROP_LIST_DELIMITER) then begin
      s := Copy(AText,oldPos,(i-oldPos));
      if (s <> '') then
        AList.Add(s);
      oldPos := i+1;
    end;
    i := i+1;
  end;
  if (i > oldPos) then begin                      
    s := Copy(AText,oldPos,(i-oldPos));
    if (s <> '') then
      AList.Add(s);
  end;
end;
{$ENDIF WST_HAS_STRICT_DELIMITER}

procedure TPublishedPropertyManager.SetProperties(const APropsStr: string);
var
  lst : TStringList;
  i : PtrInt;
begin
  If IsStrEmpty(APropsStr) Then
    Exit;
  lst := TStringList.Create();
  Try
{$IFDEF WST_HAS_STRICT_DELIMITER}
    lst.QuoteChar := #0;
    lst.Delimiter := PROP_LIST_DELIMITER;
    lst.StrictDelimiter := True;
    lst.DelimitedText := APropsStr;
{$ELSE WST_HAS_STRICT_DELIMITER}
    SetListData(lst,APropsStr);
{$ENDIF WST_HAS_STRICT_DELIMITER}
    for i := 0 to Pred(lst.Count) do
      SetProperty(lst.Names[i],lst.Values[lst.Names[i]]);
  Finally
    lst.Free();
  End;
end;

function TPublishedPropertyManager.GetProperty(const AName: String): string;
Var
  pinf : PPropInfo;
begin
  Result := '';
  pinf := GetPropInfo(FParent,AName);
  if Assigned(pinf) then begin
    if Assigned(pinf^.SetProc) then begin
      Case pinf^.PropType^.Kind of
        tkLString
        {$IFDEF WST_DELPHI},tkString{$ENDIF}
        {$IFDEF FPC},tkSString,tkAString{$ENDIF}
        {$IFDEF WST_UNICODESTRING},tkUString{$ENDIF}
        ,tkWString :
          Result := GetStrProp(FParent,pinf);
        tkEnumeration :
          Result := GetEnumProp(FParent,pinf);
        tkInteger,tkInt64{$IFDEF FPC},tkQWord{$ENDIF} :
          Result := IntToStr(GetOrdProp(FParent,pinf));
      End;
    end;
  end else if FHandleUnknownProps then begin
    Result := FUnknownProps.GetProperty(AName);
  end;
end;

function TPublishedPropertyManager.GetPropertyNames(ADest: TStrings): Integer;
Var
  propList : PPropList;
  i, propListLen : Integer;
begin
  ADest.Clear();
  if FHandleUnknownProps then
    FUnknownProps.GetPropertyNames(ADest);
  propListLen := GetPropList(PTypeInfo(FParent.ClassInfo),propList);
  if (propListLen > 0) then begin
    Try
      For i := 0 To Pred(propListLen) Do Begin
        If ( propList^[i]^.PropType^.Kind in
             [ tkLString{$IFDEF FPC},tkSString,tkAString{$ENDIF}{$IFDEF WST_UNICODESTRING},tkUString{$ENDIF}
               ,tkWString, tkEnumeration,
               tkInteger,tkInt64{$IFDEF FPC},tkQWord{$ENDIF}
             ]
           )
        Then
          ADest.Add(propList^[i]^.Name);
      End;
    Finally
      Freemem(propList,propListLen*SizeOf(Pointer));
    End;
  end;
  Result := ADest.Count;
end;

procedure TPublishedPropertyManager.Clear();
begin

end;

procedure TPublishedPropertyManager.Copy(
        ASource: IPropertyManager;
  const AClearBefore: Boolean
);
Var
  lst : TStringList;
  i : Integer;
  s : string;
begin
  If AClearBefore Then
    Clear();
  If Assigned(ASource) Then Begin
    lst := TStringList.Create();
    Try
      ASource.GetPropertyNames(lst);
      For i := 0 To Pred(lst.Count) Do Begin
        s := lst[i];
        SetProperty(s,ASource.GetProperty(s));
      End;
    Finally
      lst.Free();
    End;
  End;
end;

constructor TPublishedPropertyManager.Create(
        AParent             : TObject;
  const AHandleUnknownProps : Boolean = False
);
begin
  Assert(Assigned(AParent));
  FParent := AParent;
  FHandleUnknownProps := AHandleUnknownProps;
  if FHandleUnknownProps then
    FUnknownProps := TStoredPropertyManager.Create() as IPropertyManager;
end;


end.

