18.02.2012

Delphi and Redis

Redis is short for Remote Dictionary Server and it is a NoSQL-Database or more specific, a Key-Value-Store. Since there was no Client for Delphi, I decided to write one by myself.


The project is available on Google Code: http://code.google.com/p/delphi-redis/
It uses the Indy TCP Client by default, but you can use either Constructor or Property Injection to switch to another implementation.

01.02.2012

Delphi and JSON

I am currently developing my own JSON Library for Delphi. There are already libraries for that, but none of them suited my needs.
There is the built-in DBXJSON, which is used in the DataSnap part of Delphi. However, it is a bit of a hassle to work with.
There is TlkJSON from Simon Stuart, which has (or had, I don't know the current status) some problems with Umlauts (ä,ö,ü,ß, etc.)
There is the Delphi Web Utils from Jose Fabio N Almeida, which has some memory issues and other bugs, and most importantly, does not work well under XE2. It also hasn't been updated for a while.
And there is the SuperObject which is very powerful, but it has IMO a horrible syntax.

Since I was using the Delphi Web Utils before and I didn't want to rewrite all my other code when switching libraries, I designed my Interfaces and Classes in a similar way, e.g. the objects and array have methods named "Put" and "GetString".

I have released my library on Google Code. You can check it out via subversion or you can download the zipfile. Any feedback or help is appreciated. It uses Generics, Rtti (for TValue) and Regular Expressions, which is why it is targeted only for newer versions of Delphi (XE and XE2).

23.10.2011

Delphi and RTTI / Class Helper

In many of my applications I have some data-objects which look something like this:
type
  TMyObject = class
  private
    fInt: integer;
    fDouble: double;
    fBool: boolean;
  public
    property IntegerProp : integer read fInt write fInt;
    property DoubleProp : double read fDouble write fDouble;
    property BoolProp : boolean read fBool write fBool;
  end;
Writing a SaveToFile and LoadFromFile for each is tedious work and if I change the object I have to change the two procedures as well. With Delphis support for RTTI and class helpers we can get rid of the tedious work. I am using a class helper for TObject, so technically its possible to do this with every object.
  TSerialHelper = class helper for TObject
  private
    function isPublicProperty(aProperty : TRttiProperty): boolean;
  public
    function Serialize(aHumanreadable : boolean = true) : string;
    procedure Deserialize(const text : string);
  end;
And here is the implementation:
function TSerialHelper.Serialize(aHumanreadable : boolean = true): string;
var
  context : TRttiContext;
  typ : TRttiType;
  prop : TRttiProperty;
  soJSON : ISuperObject;
  value : TValue;
begin
  context := TRttiContext.Create;
  try
    typ := context.GetType(self.ClassType);
    soJSON := SO; // Get Superobject Interface
    for prop in typ.GetProperties do
    begin
      if isPublicProperty(prop) and prop.IsWritable then
      begin
        value := prop.GetValue(self);
        case value.Kind of
          tkInteger, tkInt64 :
            soJSON.I[prop.Name] := value.AsInteger;
          tkEnumeration:
            if value.IsType<boolean> then
              soJSON.B[prop.Name] := value.AsBoolean;
          tkFloat:
            soJSON.D[prop.Name] := value.AsExtended;
          tkChar, tkString, tkWChar, tkLString, tkWString, tkUString:
            soJSON.S[prop.Name] := value.AsString;
        end;
      end;
    end;
    result := soJSON.AsJSon(aHumanreadable);
  finally
    context.Free;
  end;
end;

procedure TSerialHelper.Deserialize(const text: string);
var
  context : TRttiContext;
  typ : TRttiType;
  prop : TRttiProperty;
  soJSON : ISuperObject;
begin
  context := TRttiContext.Create;
  try
    soJSON := SO(text); // Init the Superobject Interface
    typ := context.GetType(self.ClassType);
    for prop in typ.GetProperties do
    begin
      if isPublicProperty(prop) and prop.IsWritable then
      begin
        case prop.PropertyType.TypeKind of
          tkInteger, tkInt64 :
            prop.SetValue(self, TValue.From<int64>(soJSON.I[prop.Name]));
          tkEnumeration:
            if prop.GetValue(self).IsType<boolean> then
              prop.SetValue(self, TValue.From<boolean>(soJSON.B[prop.Name]));
          tkFloat:
            prop.SetValue(self, TValue.From<double>(soJSON.D[prop.Name]));
          tkChar, tkString, tkWChar, tkLString, tkWString, tkUString:
            prop.SetValue(self, TValue.From<string>(soJSON.S[prop.Name]));
        end;
      end;
    end;
  finally
    context.Free;
  end;
end;

function TSerialHelper.isPublicProperty(aProperty: TRttiProperty): boolean;
begin
  result := aProperty.Visibility in [mvPublic, mvPublished];
end;
It is not finished, since it can't serialize sets, arrays or childobjects, but so far it works for simple objects.

You can download the unit here.
This unit uses the SuperObject by Henri Gourvest

24.07.2011

Delphi and TPropertyObserver

In Delphi you can use RTTI (RunTime Type Information) to get information about objects at runtime without knowing about them at compile time. To find out more about RTTI, you should check out these articles by Robert Love.

I used the RTTI to make an observer, that informs me, whenever a specific property gets changed. It is far from being complete, there are a lot of TypeKinds that I didn't implement a compare for. The observer could also be extended to support multiple properties.
uses
  Rtti, Classes;

type
  TProc = reference to procedure;
  TPropertyObserver = class
  private
    fTerminated : boolean;
  public
    constructor Create(aInstance : TObject; aPropertyName : string; aOnChange : TProc);
    procedure Terminate;
  end;

implementation

uses
  SysUtils, TypInfo;

{ TPropertyObserver }

constructor TPropertyObserver.Create(aInstance: TObject; aPropertyName: string; aOnChange : TProc);
begin
  TThread.CreateAnonymousThread(procedure
  var
    ctx : TRttiContext;
    t : TRttiType;
    oldValue, curValue : TValue;
    b : boolean;
    p : TRttiProperty;
  begin
    ctx := TRttiContext.Create;
    try
      t := ctx.GetType(aInstance.ClassType);
      p := t.GetProperty(aPropertyName);
      oldValue := p.GetValue(aInstance);
      while not fTerminated do
      begin
        curValue := p.GetValue(aInstance);
        b := false;
        case curValue.Kind of
          tkInt64,
          tkEnumeration,
          tkInteger: b := oldValue.AsOrdinal = curValue.AsOrdinal;
          tkWChar,
          tkLString,
          tkWString,
          tkString,
          tkUString,
          tkChar: b := oldValue.AsString = curValue.AsString;
          tkFloat: b := oldValue.AsExtended = curValue.AsExtended;
          // Some of these should get a compare, too
          tkUnknown: ;
          tkSet: ;
          tkClass: ;
          tkMethod: ;
          tkVariant: ;
          tkArray: ;
          tkRecord: ;
          tkInterface: ;
          tkDynArray: ;
          tkClassRef: ;
          tkPointer: ;
          tkProcedure: ;
        end;
        if not b then
        begin
          aOnChange;
          oldValue := curValue;
        end;
        sleep(10);
      end;
      finally
      ctx.Free;
    end;
  end).Start;
end;

procedure TPropertyObserver.Terminate;
begin
  fTerminated := true;
end;

And how it is used:
procedure TForm1.FormCreate(Sender: TObject);
begin
  fObserver := TPropertyObserver.Create(self,'Left',procedure
  begin
    TThread.Synchronize(TThread.CurrentThread,procedure // Changing the VCL has to be done in the main thread
    begin
      edit1.Text := IntToStr(self.Left);
    end);
  end);
end;

Download is available here.

17.07.2011

Delphi and TAppSettings

Delphi has had Inifle-Support for as long as I can remember. Inifiles are great for storing Application- and Usersettings, but I found myself doing the same things in each Application I wrote. Retrieve an appropiate directory for storing the actual file, creating and freeing the TInifile-Object.
I wanted something that I can just add to my project to store and retrieve simple values in a TInifile-like manner. Here is the result: TAppSettings. It relies on the aforementioned JSON-Library "Delphi Web Utils".
type
  TAppSettings = class
  protected
    fFilename : string;
    fSaveOnFree: boolean;
    fSettings : TJSONObject;
    function createOrGetSection(aSection : string) : TJSONObject;
  public
    constructor Create(aFilename : string);
    destructor Destroy; override;
    procedure Save;

    function getInteger(aSection, aKey : string; aDefault : integer) : integer;
    function getString(aSection, aKey : string; aDefault : string) : string;
    function getFloat(aSection, aKey : string; aDefault : double ) : double;
    function getBool(aSection, aKey : string; aDefault : boolean) : boolean;

    procedure setValue(aSection, aKey : string; aValue : integer); overload;
    procedure setValue(aSection, aKey : string; aValue : string); overload;
    procedure setValue(aSection, aKey : string; aValue : double); overload;
    procedure setValue(aSection, aKey : string; aValue : boolean); overload;

    property SaveOnFree : boolean read fSaveOnFree write fSaveOnFree;
    procedure OpenPathInExplorer;
  end;

As an example the getter and setter for integer:
function TAppSettings.getInteger(aSection, aKey: string;
  aDefault: integer): integer;
var
  section : TJSONObject;
begin
  result := aDefault;
  section := fSettings.optJSONObject(aSection);
  if section <> nil then result := section.optInt(aKey);
end;

function TAppSettings.createOrGetSection(aSection: string): TJSONObject;
begin
  result := fSettings.optJSONObject(aSection);
  if result = nil then
  begin
    result := TJSONObject.create;
    fSettings.put(aSection,result);
  end;
end;

procedure TAppSettings.setValue(aSection, aKey: string; aValue: integer);
var
  section : TJSONObject;
begin
  section := createOrGetSection(aSection);
  section.put(aKey, aValue);
end;

This alone doesn't help with the initial problem, it just changes the format of the file. The interesting part is at the bottom of the unit.
interface

const
  COMPANY_NAME = 'MyCompany';

[...]

var
  AppSettings, UserSettings : TAppSettings;

[...]

function GetSpecialFolder(Folder: Integer): String;
var
  Path: array[0..MAX_PATH] of char;
begin
  If SHGetSpecialFolderPath(0, @Path, Folder, false)
    then Result:=Path
    else Result:='';
end;

function GetApplicationName : string;
begin
  result := ExtractFilename(ParamStr(0));
  delete(result,length(result)-3,4);
end;

initialization
begin
  ForceDirectories(GetSpecialFolder(CSIDL_COMMON_APPDATA)+'\'+COMPANY_NAME+'\'+GetApplicationName);
  ForceDirectories(GetSpecialFolder(CSIDL_APPDATA)+'\'+COMPANY_NAME+'\'+GetApplicationName);
  AppSettings := TAppSettings.Create(GetSpecialFolder(CSIDL_COMMON_APPDATA)+'\'+COMPANY_NAME+'\'+GetApplicationName+'\settings.json');
  UserSettings := TAppSettings.Create(GetSpecialFolder(CSIDL_APPDATA)+'\'+COMPANY_NAME+'\'+GetApplicationName+'\settings.json');
end;

finalization
begin
  AppSettings.Free;
  UserSettings.Free;
end;

Adding this unit to your project automatically creates two Objects of TAppSettings, one for UserSettings and one for ApplicationSettings. Before I wrote TAppSettings I always had two procedures "LoadIni" and "SaveIni" in my application where I would load and save all my application settings, because that were the only places I could access the settings. Now I just use the settings wherever I need them.

You can download the complete unit here.

09.07.2011

Delphi and TTaskList

I was reading about some of the new features in Delphi XE in the new book from Marco Cantu, Delphi XE Handbook. (He also told me about them earlier this year in Frankfurt on the Delphi Developer Days, but I didn't found time to play around with it until now)
The newest Delphi comes with anonymous Threads, which are a very easy way to make your application multithreaded, which is very useful to make some big calculations in the background, while the UI is still responding. However, sometimes you need things to be done in order. Enter TTasklist:

type
  TProc = reference to procedure;
  TTaskList = class
  private
    fTasks : TThreadedQueue<TProc>;
    fTerminated : boolean;
    procedure start;
  public
    constructor Create;
    destructor Destroy; override;
    procedure addTask(aTask : TProc);
    procedure Terminate;
  end;

TTaskList uses a ThreadSafe Queue to store the tasks, which are basically anonymous methods. The constructor creates the queue and starts a new thread which executes one task after another.
constructor TTaskList.Create;
begin
  fTasks := TThreadedQueue<TProc>.Create;
  fTerminated := false;
  start;
end;

procedure TTaskList.addTask(aTask: TProc);
begin
  fTasks.PushItem(aTask);
end;

procedure TTaskList.start;
begin
  TThread.CreateAnonymousThread(
  procedure
  begin
    while not fTerminated do
    begin
      if fTasks.QueueSize > 0 then fTasks.PopItem.Invoke;
    end;
  end
  ).Start;
end;

procedure TTaskList.Terminate;
begin
  fTerminated := true;
end;

destructor TTaskList.Destroy;
begin
  Terminate;
  fTasks.Free;
  inherited;
end;

Download is available here.

19.06.2011

Delphi and Geocoding

Google has some powerful APIs. One of them is the Maps API which is capable of geocoding an address to longitude and latitude information.

It is pretty easy to use this functionality from Delphi. All you need is TIdHttp and a JSON library. (I'm using the Delphi Web Utils)
TLocationType = (ltRooftop, ltRangeInterpolated, ltGeometricCenter, ltApproximate);
  TAddress = class
  private
    fAddress : string;
    fLatitude: double;
    fLongitude: double;
    fLocationType: TLocationType;
  public
    constructor Create(aAddress : string; aHttp : TIdHttp = nil);
    property Address : string read fAddress write fAddress;
    property Latitude : double read fLatitude;
    property Longitude : double read fLongitude;
    property LocationType : TLocationType read fLocationType;
    procedure Geocode(aHttp : TIdHttp);
  end;

The Google API returns a JSON structure with a couple of fields. However, I just grab the longitude and latitude information as well as the location_type, to determine the quality of the coordinates.
procedure TAddress.GeoCode(aHttp: TIdHttp);
var
  url : string;
  jo, location, geometry : TJSONObject;
  sc : TStringCase;
begin
  url := TIdURI.URLEncode('http://maps.google.com/maps/api/geocode/json?address='+fAddress+'&sensor=false');
  jo := TJSONObject.create(aHttp.Get(url));
  if jo.getString('status') = 'OK' then
  begin
    geometry := jo.getJSONArray('results').getJSONObject(0).getJSONObject('geometry');
    location := geometry.getJSONObject('location');
    fLongitude := location.getDouble('lng');
    fLatitude := location.getDouble('lat');

    sc := TStringCase.Create;
    try
      sc.addEntry('ROOFTOP',procedure
      begin
        fLocationType := ltRooftop;
      end);
      sc.addEntry('RANGE_INTERPOLATED',procedure
      begin
        fLocationType := ltRangeInterpolated;
      end);
      sc.addEntry('GEOMETRIC_CENTER',procedure
      begin
        fLocationType := ltGeometricCenter;
      end);
      sc.addEntry('APPROXIMATE',procedure
      begin
        fLocationType := ltApproximate;
      end);
    finally
      sc.switch(geometry.getString('location_type'));
    end;
    sc.Free;
  end
  else raise Exception.Create(jo.getString('status'));
  jo.Free;
end;

I used this and TGoMaps to create a simple application which takes a list of addresses, geocodes them and displays them on a GoogleMap.

Source and binary are available here.

TAddress uses TStringCase from the previous post.