Jump to content


Check out our Community Blogs

Register and join over 40,000 other developers!


Recent Status Updates

View All Updates

Photo
* * * * * 1 votes

Easily Store and Retrieve Object from Ini Files Using Attributes

delphi delphi language feature attributes custom attribute rtti

  • Please log in to reply
No replies to this topic

#1 Luthfi

Luthfi

    CC Leader

  • Expert Member
  • PipPipPipPipPipPipPip
  • 1320 posts
  • Programming Language:PHP, Delphi/Object Pascal, Pascal, Transact-SQL
  • Learning:C, Java, PHP

Posted 14 September 2013 - 06:53 AM

Previously I have explained the basics of working with ini files. I also introduced a Delphi language feature called Attributes. This time I want to show you how these two could work together in real project. Once you through with this tutorial, you will find managing information that needs to be stored to or written to ini files will very too easy with the help of some attribute classes.
 
Let's get started, then!
 
 
Ini File Requirements
 
If you never worked with ini files before, I urge you to head to this tutorial and work through it. Basically to store or read data from ini file, you need two information. They are section and value names. Also when reading a data, you might need default value in case when the value did not exist at the time. Therefore we need a custom attribute class that must be able to return these 3 information.
 
Let's name this custom attribute class IniMemberAttribute.
 
However, there is high possibility that all information of an object only needs to be stored in a single section. To handle this scenario, we need another custom attribute class which purpose is to provide default section name. And this attribute should be linked to the class, not to class members.
 
Let's name this custom attribute class IniSection.
 
Later when we are querying attributes, we will use section name from IniSection whenever current IniMemberAttribute lacks of section name information.
 
 
IniSection Class
 
This class is very simple. Implemented this way.

interface

type
  IniSection=class(TCustomAttribute)
  private
    FSection: string;
  public
    constructor Create(const ASection: string);

    property Section: string read FSection;
  end;

implementation

constructor IniSection.Create(const ASection: string);
begin
  FSection := ASection;
end;

IniMemberAttribute Class
 
This class requires two constructors, one to which handle when we specifically add section name, and one without. So we will add two overloaded constructors. And here is it's implementation.


interface

type
  IniMemberAttribute=class(TCustomAttribute)
  private
    FSection: string;
    FIdent  : string;
    FDefValue: string;
  public
    constructor Create(const AIdent, ADefValue: string); overload;
    constructor Create(const ASection, AIdent, ADefValue: string); overload;

    property Section : string read FSection;
    property Ident   : string read FIdent;
    property DefValue: string read FDefValue;
  end;

implementation

constructor IniMemberAttribute.Create(const AIdent, ADefValue: string);
begin
  Create('', AIdent, ADefValue);
end;

constructor IniMemberAttribute.Create(const ASection, AIdent, ADefValue: string);
begin
  FSection := ASection;
  FIdent   := AIdent;
  FDefValue := ADefValue;
end;

Simple, right?
 
Now to easily use these custom attributes to store and load data from ini file, we want to add 3 routines, i.e.:

  procedure SaveToIniFile(const AIniFile: string; AObj: TObject);
  procedure LoadFromIniFile(const AIniFile: string; AObj: TObject);
  procedure LoadDefValues(const AObj: TObject);

I believe the names already told us what they were suppose to do.
 
To save space, you will need to look into the attached source code (see link in the end of this tutorial) to see how I implemented them. But the implementation is not far from the pattern code explain in this tutorial. These 3 routines were declared in interface section of the unit. So they are accessible to anyone that uses their unit.
 
 
Demo Project
 
Let's say that I have the following class to handle configuration of a project.

  TMyConfig=class
  public
    // to return the information contained
    // in this class into their default values
    procedure Reset;

    // store configuration to the specified ini file
    procedure SaveToIni(const AIniFile: string);
    // load configuration from the specified ini file
    procedure LoadFromIni(const AIniFile: string);
  published
    property WorkingDir: string read GetWorkingDir write SetWorkingDir;
    property DefaultDay: Integer read GetDefaultDay write SetDefaultDay;
  end;

Conventionally, I would write quite many lines of code in SaveToIni, LoadFromIni, and Reset methods. And each time I add new member to that class that also needs to be stored in ini file, I will need to add more codes in those 3 methods. But using our custom attribute library, you will see that initially we only need one line of code for each of those 3 methods and of course add proper annotations, which in this case only require 3 lines. But later when we need to add new information to that class that needs to be stored in ini file, you only need to add a line of annotation. Neat!
 
For this example, we will implement that class like this (irrelevant parts were omitted to save space):

interface

type
  [IniSection('Gen001')]
  TMyConfig=class
  public
    constructor Create;
    procedure Reset;
    procedure SaveToIni(const AIniFile: string);
    procedure LoadFromIni(const AIniFile: string);
  published
    [IniMember('WorkingDir', 'please specify working dir')]
    property WorkingDir: string read GetWorkingDir write SetWorkingDir;

    [IniMemberAttribute('Section02', 'DefDay', '100')]
    property DefaultDay: Integer read GetDefaultDay write SetDefaultDay;
  end;

implementation

{ TMyConfig }

constructor TMyConfig.Create;
begin
  Reset;
end;

procedure TMyConfig.LoadFromIni(const AIniFile: string);
begin
  Attributes.ini.LoadFromIniFile(AIniFile, Self);
end;

procedure TMyConfig.Reset;
begin
  Attributes.ini.LoadDefValues(Self);
end;

procedure TMyConfig.SaveToIni(const AIniFile: string);
begin
  Attributes.ini.SaveToIniFile(AIniFile, Self);
end;

Let's say now we want to add another property to the configuration that explains the default city to use in the project. And this default city also should have a default value (let's say the default value is Jakarta). All you need to do is adding this lines (and of course the obvious codes of GetDefaultCity and SetDefaultCity).

  TMyConfig=class
  ...
  published
    ...
    [IniMember('Section01', 'DefCity', 'Jakarta')]
    property DefaultCity: string read GetDefaultCity write SetDefaultCity;
  end;

And everything else will be taken care of automatically.

 

I have attached the source code of our custom attributes library along with the demo project at the end of this tutorial.

 

Upon running the demo project you will get a display like this:

 

Demo_Run_000.png

 

If you inspect closely, you will notice that the shown values are the default values assigned to the attribute annotations. Note that in none of TMyConfig methods we directly did specify the default values.

 

Now let's change the Working directory value to an existing directory. In my case I used "C:\Demo\Attributes\Temp". Change the default day to 6, and default city to London. Now click the Save Config button. You will get something like this.

 

Demo_Run_010_SavedConfig.png

 

Now that we altered the configuration, we want it to back to the default values. That's easy, just click the Load default button. And the demo project form will display the default values again.

 

Demo_Run_020_DefaultsLoaded.png

 

This time, we will try to see if we can read information that previously saved in our ini file back to our project. Click on Load Config button. And you will get the form showing information that previously saved. Like this:

 

Demo_Run_030_ConfigLoaded.png

 

 

Very nice, right? With less than 2 hours of time invested writing the custom attributes library and you will save a lot of time when you need to work with ini files.

 

And here is the complete source code: Attached File  PersistingWIthIniFileWithAttributes_Demo.zip   987.26KB   461 downloads. Feel free to use them for whatever you want.

 

Please provide me feedback, or ask away any details that you need.


Edited by Luthfi, 16 September 2013 - 02:47 PM.

  • 0





Also tagged with one or more of these keywords: delphi, delphi language feature, attributes, custom attribute, rtti

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download