views:

120

answers:

2

Hi everyone:

I need help on this:

im storing object properties in a DataPacket class. The properties are defined like this

type
  TProperty = class
  private
    FName: string;
  public
    constructor Create(const AName: string);
    property Name: string read FName;
  end;

  TIntegerProperty = class(TProperty)
  private
    FValue: Integer;
  protected
    procedure SetValue(const AValue:integer);
  public
    property Value: Integer read FValue write SetValue;
  end;

the DataPacket class:

type
  TDataPacket = class
  private
    FProperties: TStringList;
  public
    function GetIntegerValue(const APropertyName: string): integer;
    .....
    procedure SetIntegerValue(const APropertyName: string; AValue: integer);
end;

and they are implemented like:

function TDataPacket.GetIntegerValue(const APropertyName: string): integer;
var
  Pos: integer;
begin
  Pos := FProperties.IndexOf(APropertyName);
  if Pos > -1 then
    Result := TIntegerProperty(FProperties.Objects[Pos]).Value
  else
    Result := 0;
end;

procedure TDataPacket.SetIntegerValue(const APropertyName: string; AValue: integer);
var
  Pos: integer;
  AProperty: TIntegerProperty;
begin
  Pos := FProperties.IndexOf(APropertyName);
  if Pos >- 1 then
    TIntegerProperty(FProperties.Objects[Pos]).Value := AValue
  else
    begin
      AProperty:= TIntegerProperty.Create(APropertyName);
      AProperty.Value := AValue;
      FProperties.AddObject(APropertyName, AProperty);
    end;
end;

now the question: i need to define an Status property defined as TObjectStatus where:

type
  TStatus = (Deleted, Unchanged, Added , Modified, ChildsModified);

  TObjectStatus = Set of TStatus;

any idea on how can i define, store and retrieve it?

sorry for the long explanation and thanks in advance for you help

Michael

+1  A: 

First:

Result := TIntegerProperty(FProperties.Objects[Pos]).Value

Is risky because you will crash if it is not a TIntegerProperty. Use something like:

Pos := FProperties.IndexOf(APropertyName);
if (Pos >= 0) and (FProperties.Objects[Pos] is TIntegerProperty) then
  Result := TIntegerProperty(FProperties.Objects[Pos]).Value
else
  Result := 0;

Next the status, I don't think you need them al:

For the list - Deleted a child: Deleted - Added a child: Added - Child had been changed: ChildsModified

You don't need unchanged because in that case, the set is empty. And you don't need Modified because in that case the set is not empty.

For the properties you can just add a Changed value. You can add ChildsModified directly if a child is changed. Or you can use lazy evaluation and walk all children to check for Changed.

Ok you can do something like this:

type
  TStatus = (stDeleted, stAdded , stModified, stChildsModified);
  TObjectStatus = Set of TStatus;


  TDataPacket = class;
  TProperty = class
  private
    FName   : string;
    FParent : TDataPacket; 
  protected
    procedure NotifyChange(const AStatus: TStatus);
  public
    constructor Create(const AParent: TDataPacket; const AName: string);
    property Name: string read FName;
  end;

  TIntegerProperty = class(TProperty)
  private
    FValue: Integer;
    procedure SetValue(const AValue:integer);
  public
    property Value: Integer read FValue write SetValue;
  end;

  TDataPacket = class
  private
    FProperties: TStringList;
    FStatus : TObjectStatus;
  protected 
    procedure NotifyChange(const AStatus: TStatus);

    function GetProperty(const AName: string): TProperty;
  public
    function GetIntegerValue(const APropertyName: string): integer;
    procedure SetIntegerValue(const APropertyName: string; AValue: integer);
  end;


procedure TProperty.NotifyChange(const AStatus: TStatus);
begin
  FParent.NotifyChange(AStatus);
end;

constructor TProperty.Create(const AParent: TDataPacket; const AName: string);
begin
  Assert(AParent<>nil);
  FName := AName;
  FParent := AParent;
end;

procedure TIntegerProperty.SetValue(const AValue:integer);
begin
  if AValue<>FValue then begin
    FValue := AValue;
    NotifyChange(stChildsModified);
  end;
end;

procedure TDataPacket.NotifyChange(const AStatus: TStatus);
begin
  if AProp=nil then begin
    case AStatus of

  TStatus = (stDeleted, stAdded , stModified, stChildsModified);

  FStatus := FStatus + [AStatus];
end;

function TDataPacket.GetProperty(const AName: string): TProperty;
var
  i : Integer;
begin
  i := FProperties.IndexOf(AName);
  if i>=0 then
    Result := TProperty(FProperties.Objects[i])
  else
    Result := nil;
end;

function TDataPacket.GetIntegerValue(const APropertyName: string): integer;
var
  prop : TProperty;
begin
  prop := GetProperty(APropertyName);
  if (prop<>nil) and (prop is TIntegerProperty) then
    Result := TIntegerProperty(prop).Value
  else
    Result := 0; 
end;

procedure TDataPacket.SetIntegerValue(const APropertyName: string; AValue: integer);
var
  prop : TProperty;
  intprop : TIntegerProperty; 
begin
  prop := GetProperty(APropertyName);
  if (prop<>nil) and not (AProperty is TIntegerProperty) then begin
    // PANIC!
  end else begin
    if prop=nil then begin
      intprop := TIntegerProperty.Create(self, APropertyName);
      intprop.Value := AValue;
      FProperties.AddObject(APropertyName, intprop);
      NotifyChange(stAdded);
    end else begin
      TIntegerProperty(prop).Value := AValue;   
    end;
  end;
end;

And off course add support for deletion.

You can let the Property handle all changes (Add when constructed and Delete when freed).

Gamecat
+1  A: 

If you're storing an object's properties, and one of those properties should be a status property, then all you ultimately need to do is the same as what you did for TIntegerProperty, but replacing Integer with TObjectStatus.

First, define another property class that holds your TObjectStatus value:

type
  TObjectStatusProperty = class(TProperty)
  private
    FValue: TObjectStatus;
  protected
    procedure SetValue(const AValue: TObjectStatus);
  public
    property Value: TObjectStatus read FValue write SetValue;
  end;

Next, add methods to your data packet to work with that type of property:

function TDataPacket.GetObjectStatusValue(
  const APropertyName: string): TObjectStatus;
var
  Pos: integer;
  Prop: TProperty;
begin
  Pos := FProperties.IndexOf(APropertyName);
  if Pos >= 0 then begin
    Prop := FProperties.Objects[Pos] as TProperty;
    Assert(Prop.Name = APropertyName);
    if Prop is TObjectStatusProperty then
      Result := TObjectStatusProperty(Prop).Value
    else
      raise EWrongPropertyType.CreateFmt('Expected %s but got %s',
        [TObjectStatusProperty.ClassName, Prop.ClassName]);
  end else
    Result := [];
end;

procedure TDataPacket.SetObjectStatusValue(
  const APropertyName: string; AValue: TObjectStatus);
var
  Pos: integer;
  Prop: TProperty;
begin
  Pos := FProperties.IndexOf(APropertyName);
  if Pos >= 0 then begin
    Prop := FProperties.Objects[Pos] as TProperty;
    Assert(Prop.Name = APropertyName);
    if Prop is TObjectStatusProperty then
      TObjectStatusProperty(Prop).Value := AValue
    else
      raise EWrongPropertyType.CreateFmt('Expected %s but got %s',
        [TObjectStatusProperty.ClassName, Prop.ClassName]);
  end else begin
    Prop := TObjectStatusProperty.Create(APropertyName);
    TObjectStatusProperty(Prop).Value := AValue;
    FProperties.AddObject(APropertyName, Prop);
  end;
end;

This could be a great opportunity to use generics to reduce the number of TXProperty classes you need to write, if you had Delphi 2009 and if Delphi's generics supported sets.

Rob Kennedy