Archive
Dataset for Smart Mobile Studio
I’ve only had a couple of hours to work on this, but here is the first incarnation of TDataset for Smart Mobile Studio. It will need some work (and a lot of testing) before it’s released into the wild – so feel free to play around with it, but dont use it in production level projects just yet.
Notice the heavy use of Property Expressions, a unique feature of Smart Pascal. Especially in the field classes, where property expressions remove the need for at least 2 getter/setter methods. Fast, elegant and very pleasant to write.
Using the dataset
Fairly straight forward stuff. Here is how you create a dataset:
FDataset.Fields.Add('id',ftInteger); FDataset.Fields.add('name',ftString); FDataset.CreateDataset;
And you insert data in much the same way as under Delphi:
for x:=0 to 50 do Begin FDataset.Insert; FDataset.fields.fieldByName('id').asInteger:=x+1; FDataset.Fields.FieldByName('name').asString:='this is String #' + x.ToString; FDataset.Post; end;
And you also edit stuff in much the same way as you would in Delphi or Lazarus:
FDataset.first; FDataset.edit; FDataset.Fields.FieldByName('name').asString:='We altered this one!'; FDataset.post;
And last but not least, navigation should be en-par with what you are used to:
FDataset.first; repeat writeln(FDataset.Fields.FieldByName('name').asString); FDataset.next; until FDataset.EOF;
Note: The data is stored in the FCache array, to export the data, just use JSON to export it – and voila, you can now use datasets to deal with in-app orders, posts, news or whatever. When updating a real database, send the whole dataset as a packet — easy as apple pie 🙂
A simple “Save” routine would look something like this:
Function TW3CustomDataset.saveToString:String; Begin asm @result = JSON.stringify((@self).FCache); end; end;
Well, enjoy!
unit w3dataset; //Copyright Jon Lennart Aasenden, All right reserved. interface uses W3System; type TW3CustomDatasetField = Class; TW3BooleanField = Class; TW3IntegerField = Class; TW3FloatField = Class; TW3StringField = Class; TW3DatasetFields = Class; TW3CustomDataset = Class; EW3DatasetField = Class(EW3Exception); EW3Dataset = Class(EW3Exception); TW3DatasetPacket = Variant; TW3DatasetFieldType = ( ftUnknown, ftBoolean, ftInteger, ftFloat, ftString, ftDateTime ); IDatasetFieldsAccess = Interface Procedure ResetValues; Procedure setReadOnly(const aValue:Boolean); Function ExportDefs:String; Procedure ImportDefs(const aDefs:String); end; TW3CustomDatasetField = Class(TObject) private FName: String; FKind: TW3DatasetFieldType; FReadOnly: Boolean; FValue: Variant; FParent: TW3DatasetFields; Procedure setAsDateTime(const aValue:TDateTime); function getAsDateTime:TDateTime; protected function getValue:Variant;virtual; procedure setValue(const aValue:variant);virtual; procedure setName (const aValue:String); procedure setKind (const aKind:TW3DatasetFieldType); Procedure setReadOnly(const aValue:Boolean); public Property Parent:TW3DatasetFields read FParent; Property Kind:TW3DatasetFieldType read FKind; Property Name:String read FName write setName; Property Value:Variant read FValue write FValue; Property AsString:String read (TVariant.AsString(FValue)) write (FValue:=Value); Property AsInteger:Integer read (TVariant.asInteger(FValue)) write (FValue:=Value); Property AsBoolean:Boolean read (TVariant.AsBool(FValue)) write (FValue:=Value); Property AsFloat:Float read (TVariant.asFloat(FValue)) write (FValue:=Value); Property AsDateTime:TDateTime read getAsDateTime write setAsDateTime; Constructor Create(const aParent:TW3DatasetFields);virtual; End; TW3BooleanField = Class(TW3CustomDatasetField) public Property Value:Boolean read (TVariant.AsBool(Inherited getValue)) write (inherited setValue(Value)); End; TW3IntegerField = Class(TW3CustomDatasetField) public Property Value:Integer read (TVariant.AsInteger(Inherited getValue)) write (inherited setValue(Value)); end; TW3FloatField = Class(TW3CustomDatasetField) public Property Value:Float read (TVariant.AsFloat(Inherited getValue)) write (inherited setValue(Value)); end; TW3StringField = Class(TW3CustomDatasetField) public Property Value:String read (TVariant.AsString(Inherited getValue)) write (inherited setValue(Value)); end; TW3DateTimeField = Class(TW3CustomDatasetField) protected function getValue:TDateTime;reintroduce; procedure setValue(const aValue:TDateTime);reintroduce; public Property Value:TDateTime read getValue write setValue; End; TW3DatasetFields = Class(TObject,IDatasetFieldsAccess) private FFields: Array of TW3CustomDatasetField; protected (* IMPLEMENTS:: IDatasetFieldsAccess *) Procedure ResetValues; Procedure setReadOnly(const aValue:Boolean); Function ExportDefs:String;virtual; Procedure ImportDefs(const aDefs:String);virtual; public Property Count:Integer read (FFields.count); Property Items[index:Integer]:TW3CustomDatasetField read (FFields[index]); function DataExport:TW3DatasetPacket;virtual; Procedure DataImport(const aValue:TW3DatasetPacket);virtual; function IndexOf(aName:String):Integer; function Add(aName:String; const aKind:TW3DatasetFieldType):TW3CustomDatasetField; Procedure DeleteByName(aName:String); Procedure Delete(const aIndex:Integer); Procedure Clear; function FieldByName(aName:String):TW3CustomDatasetField; Destructor Destroy;Override; End; TW3DatasetState =(dsIdle,dsInsert,dsEdit); TDatasetStateChangeEvent = Procedure (sender:TObject;aState:TW3DatasetState); TDatasetPositionChangeEvent = procedure (sender:TObject;aOldPos,aNewPos:Integer); TW3CustomDataset = Class(TObject) private FFields: TW3DatasetFields; FCache: Array of TW3DatasetPacket; FState: TW3DatasetState; FActive: Boolean; FDestroying:Boolean; FOnCreated: TNotifyEvent; FOnClosed: TNotifyEvent; FOnState: TDatasetStateChangeEvent; FOnPos: TDatasetPositionChangeEvent; FDsIndex: Integer; procedure setActive(const aValue:Boolean); Procedure setPosition(const aNewPosition:Integer); protected Procedure DoBeforeDatasetCreated;virtual; Procedure DoAfterDatasetCreated;virtual; Procedure DoBeforeDatasetClosed;virtual; Procedure DoAfterDatasetClosed;virtual; function getRecCount:Integer;virtual; function getRecNo:Integer;virtual; function getEOF:Boolean;virtual; function getBOF:Boolean;virtual; procedure getPacketToFields;virtual; Procedure setPacketFromFields;virtual; procedure setState(const aState:TW3DatasetState); public Property Active:Boolean read FActive write setActive; Property State:TW3DatasetState read FState; Property Fields:TW3DatasetFields read FFields; Property EOF:Boolean read getEOF; Property BOF:Boolean read getBOF; Property Count:Integer read getRecCount; Property RecNo:Integer read getRecNo; procedure Insert; Procedure Delete; Procedure Post; procedure Edit; Procedure Next; Procedure Back; Procedure First; Procedure Last; Procedure CreateDataset; Procedure Close; Procedure Cancel; Constructor Create;virtual; Destructor Destroy;Override; published Property OnDatasetCreated:TNotifyEvent read FOnCreated write FOnCreated; Property OnDatasetClosed:TNotifyevent read FOnClosed write FOnClosed; Property OnStateChanged:TDatasetStateChangeEvent read FOnState write FOnState; Property OnPositionChanged:TDatasetPositionChangeEvent read FOnPos write FOnPos; end; TW3Dataset = class(TW3CustomDataset) End; TW3DataSource = Class(TObject) end; implementation resourcestring CNT_DATASET_FIELD_READONLY = 'Failed to alter value, field is read-only error'; CNT_DATASET_NOT_ACTIVE = 'Operation failed, dataset is not active error'; CNT_DATASET_INVALID_STATE = 'Invalid state for operation error'; //############################################################################# // TW3CustomDataset //############################################################################# Constructor TW3CustomDataset.Create; Begin inherited Create; FFields:=TW3DatasetFields.Create; FState:=dsIdle; FDestroying:=False; FDsIndex:=-1; end; Destructor TW3CustomDataset.Destroy; Begin FDestroying:=true; if FActive then Close; FFields.free; inherited; end; Procedure TW3CustomDataset.Close; begin if FActive then Begin DoBeforeDatasetClosed; try try FFields.Clear; FCache.Clear; finally FActive:=False; FState:=dsIdle; FDsIndex:=-1; (FFields as IDatasetFieldsAccess).resetValues; end; finally DoAfterDatasetClosed; end; end else raise EW3Dataset.Create(CNT_DATASET_NOT_ACTIVE); end; procedure TW3CustomDataset.setState(const aState:TW3DatasetState); Begin FState:=aState; if assigned(FOnState) then FOnState(self,aState); end; Procedure TW3CustomDataset.getPacketToFields; Begin if (FdsIndex>=0) and (FdsIndex<FCache.Length) then FFields.DataImport(FCache[FdsIndex]); end; Procedure TW3CustomDataset.setPacketFromFields; Begin if (FdsIndex>=0) and (FdsIndex<FCache.Length) then FCache[FDsIndex]:=FFields.DataExport; end; Procedure TW3CustomDataset.setPosition(const aNewPosition:Integer); var mOld: Integer; Begin if aNewPosition<>FdsIndex then begin mOld:=FdsIndex; FDsIndex:=aNewPosition; if (FdsIndex>=0) and (FdsIndex<FCache.Length) then self.getPacketToFields else //FFields.DataImport(FCache[FdsIndex]) else (FFields as IDatasetFieldsAccess).resetValues; if assigned(FOnPos) then FOnPos(self,mOld,aNewPosition); end; end; Procedure TW3CustomDataset.CreateDataset; Begin if not FActive then Begin if FFields.Count>0 then Begin DoBeforeDatasetCreated; try FActive:=True; setState(dsIdle); setPosition(-1); finally DoAfterDatasetCreated; end; end else Raise EW3Dataset.Create('No fields defined for dataset error'); end; end; procedure TW3CustomDataset.setActive(const aValue:Boolean); Begin if aValue<>FActive then begin Case aValue of true: CreateDataset; false: Close; end; end; end; procedure TW3CustomDataset.Insert; Begin if FActive then Begin if FState=dsIdle then Begin Last; setState(dsInsert); (FFields as IDatasetFieldsAccess).resetValues; end else raise EW3Dataset.Create(CNT_DATASET_INVALID_STATE); end else raise EW3Dataset.Create(CNT_DATASET_NOT_ACTIVE); end; Procedure TW3CustomDataset.Delete; Begin end; Procedure TW3CustomDataset.Cancel; Begin if FActive then begin if (FState=dsInsert) or (FState=dsEdit) then begin setState(dsIdle); (FFields as IDatasetFieldsAccess).resetValues; end; end; end; Procedure TW3CustomDataset.Post; var mDummy: TW3DatasetPacket; Begin if FActive then Begin Case FState of dsInsert: Begin (* Grow the internal cache *) FCache.Add(mDummy); FdsIndex:=FCache.Length-1; setPacketFromFields; (* position record PTR on last record *) setPosition(FFields.Count-1); (* set state to idle *) setState(dsIdle); end; dsEdit: begin setPacketFromFields; setState(dsIdle); end; else raise EW3Dataset.Create(CNT_DATASET_INVALID_STATE); end; end else raise EW3Dataset.Create(CNT_DATASET_NOT_ACTIVE); end; procedure TW3CustomDataset.Edit; Begin if FActive then Begin if FState=dsIdle then Begin if FCache.Length>0 then Begin if (FdsIndex=-1) then self.setPosition(0) else if (FdsIndex=FCache.length) then setPosition(FCache.length-1); setState(dsEdit); end; end else raise EW3Dataset.Create(CNT_DATASET_INVALID_STATE); end else raise EW3Dataset.Create(CNT_DATASET_NOT_ACTIVE); end; Procedure TW3CustomDataset.First; var mpos: Integer; Begin if FActive then begin if FState=dsIdle then Begin if FCache.Length>0 then mPos:=0 else mpos:=-1; setPosition(mPos); end else raise EW3Dataset.Create(CNT_DATASET_INVALID_STATE); end else raise EW3Dataset.Create(CNT_DATASET_NOT_ACTIVE); end; Procedure TW3CustomDataset.Last; var mpos: Integer; Begin if FActive then begin if FState=dsIdle then Begin if FCache.Length>0 then mPos:=FCache.Length-1 else mpos:=-1; setPosition(mPos); end else raise EW3Dataset.Create(CNT_DATASET_INVALID_STATE); end else raise EW3Dataset.Create(CNT_DATASET_NOT_ACTIVE); end; Procedure TW3CustomDataset.Next; Begin if FActive then Begin if FState=dsIdle then setPosition(TInteger.EnsureRange(FDsIndex+1,-1,FCache.Length)) else raise EW3Dataset.Create(CNT_DATASET_INVALID_STATE); end else raise EW3Dataset.Create(CNT_DATASET_NOT_ACTIVE); end; Procedure TW3CustomDataset.Back; Begin if FActive then Begin if FState=dsIdle then setPosition(TInteger.EnsureRange(FDsIndex+1,-1,FCache.Length)) else raise EW3Dataset.Create(CNT_DATASET_INVALID_STATE); end else raise EW3Dataset.Create(CNT_DATASET_NOT_ACTIVE); end; Procedure TW3CustomDataset.DoBeforeDatasetCreated; Begin end; Procedure TW3CustomDataset.DoAfterDatasetCreated; Begin if assigned(FOnCreated) then Begin if not FDestroying then FOnCreated(self); end; end; Procedure TW3CustomDataset.DoBeforeDatasetClosed; Begin end; Procedure TW3CustomDataset.DoAfterDatasetClosed; Begin if assigned(FOnClosed) then Begin if not FDestroying then FOnClosed(self); end; end; function TW3CustomDataset.getRecCount:Integer; Begin result:=FCache.Length; end; function TW3CustomDataset.getRecNo:Integer; Begin if FActive then result:=FDsIndex else result:=-1; end; function TW3CustomDataset.getEOF:Boolean; Begin if FActive then result:=FDsIndex>=FCache.Length else result:=True; end; function TW3CustomDataset.getBOF:Boolean; Begin if FActive then result:=FDsIndex<=0 else result:=True; end; //############################################################################# // TW3DatasetFields //############################################################################# Destructor TW3DatasetFields.Destroy; Begin Clear; inherited; end; Procedure TW3DatasetFields.setReadOnly(const aValue:Boolean); var x: Integer; begin if FFields.Length>0 then Begin for x:=FFields.Low to FFields.High do FFields[x].setReadOnly(aValue); end; end; Function TW3DatasetFields.ExportDefs:String; Begin end; Procedure TW3DatasetFields.ImportDefs(const aDefs:String); Begin end; Procedure TW3DatasetFields.ResetValues; var mItem: TW3CustomDatasetField; Begin if FFields.Length>0 then Begin for mItem in FFields do mItem.setValue(null); end; end; Procedure TW3DatasetFields.Clear; var x: Integer; Begin if FFields.Count>0 then Begin try for x:=FFields.Low to FFields.High do FFields[x].free; finally FFields.Clear; end; end; end; Procedure TW3DatasetFields.DeleteByName(aName:String); var mIndex: Integer; Begin mIndex:=IndexOf(aName); if mIndex>=0 then Begin FFields[mIndex].free; FFields.Delete(mIndex,1); end; end; Procedure TW3DatasetFields.Delete(const aIndex:Integer); Begin if (aIndex>=0) and (aIndex<FFields.Count) then Begin FFields[aIndex].free; FFields.Delete(aIndex,1); end; end; function TW3DatasetFields.Add(aName:String; const aKind:TW3DatasetFieldType):TW3CustomDatasetField; Begin result:=NIL; aName:=lowercase(trim(aName)); if aName.Length>0 then Begin if IndexOf(aName)=-1 then Begin case aKind of ftBoolean: result:=TW3BooleanField.Create(self); ftInteger: result:=TW3IntegerField.Create(self); ftFloat: result:=TW3FloatField.Create(self); ftString: result:=TW3StringField.Create(self); ftDateTime: result:=TW3DateTimeField.Create(self); else result:=NIL; end; if result<>NIL then Begin result.Name:=aName; FFields.add(result); end; end; end; end; function TW3DatasetFields.DataExport:TW3DatasetPacket; var x: Integer; mField:TW3CustomDatasetField; Begin result:=TVariant.CreateObject; if FFields.count>0 then Begin for mField in FFields do result[mField.Name]:=mField.getValue; end; end; Procedure TW3DatasetFields.DataImport(const aValue:TW3DatasetPacket); var mField:TW3CustomDatasetField; Begin if FFields.count>0 then Begin for mField in FFields do mField.setvalue(aValue[mField.Name]); end; end; function TW3DatasetFields.IndexOf(aName:String):Integer; var x: Integer; Begin result:=-1; aName:=lowercase(trim(aName)); if aName.Length>0 then Begin for x:=FFields.Low to FFIelds.High do Begin If FFIelds[x].Name=aName then Begin result:=x; break; end; end; end; end; function TW3DatasetFields.FieldByName(aName:String):TW3CustomDatasetField; var x: Integer; Begin result:=NIL; aName:=lowercase(trim(aName)); if aName.Length>0 then Begin for x:=FFields.Low to FFields.High do Begin If FFields[x].Name=aName then Begin result:=FFields[x]; break; end; end; end; end; //############################################################################# // TW3DateTimeField //############################################################################# function TW3DateTimeField.getValue:TDateTime; Begin result:=inherited getValue; end; procedure TW3DateTimeField.setValue(const aValue:TDateTime); Begin inherited setValue(aValue); end; //############################################################################# // TW3CustomDatasetField //############################################################################# Constructor TW3CustomDatasetField.Create(const aParent:TW3DatasetFields); Begin inherited Create; FParent:=aParent; FKind:=ftUnknown; FName:='Field' + IntToStr( w3_GetUniqueNumber ); end; Procedure TW3CustomDatasetField.setAsDateTime(const aValue:TDateTime); Begin FValue:=aValue; end; function TW3CustomDatasetField.getAsDateTime:TDateTime; Begin result:=FValue; end; function TW3CustomDatasetField.getValue:Variant; Begin result:=FValue; end; procedure TW3CustomDatasetField.setValue(const aValue:variant); Begin FValue:=aValue; end; procedure TW3CustomDatasetField.setName(const aValue:String); Begin if not FReadOnly then FName:=lowercase(trim(aValue)) else Raise EW3DatasetField.Create(CNT_DATASET_FIELD_READONLY); end; procedure TW3CustomDatasetField.setKind(const aKind:TW3DatasetFieldType); Begin if not FReadOnly then FKind:=aKind else Raise EW3DatasetField.Create(CNT_DATASET_FIELD_READONLY); end; Procedure TW3CustomDatasetField.setReadOnly(const aValue:Boolean); Begin FReadOnly:=aValue; end; end.
Recent
The vatican vault
- January 2022
- October 2021
- March 2021
- November 2020
- September 2020
- July 2020
- June 2020
- April 2020
- March 2020
- February 2020
- January 2020
- November 2019
- October 2019
- September 2019
- August 2019
- July 2019
- June 2019
- May 2019
- April 2019
- March 2019
- February 2019
- January 2019
- December 2018
- November 2018
- October 2018
- September 2018
- August 2018
- July 2018
- June 2018
- May 2018
- April 2018
- March 2018
- February 2018
- January 2018
- December 2017
- November 2017
- October 2017
- August 2017
- July 2017
- June 2017
- May 2017
- April 2017
- March 2017
- February 2017
- January 2017
- December 2016
- November 2016
- October 2016
- September 2016
- August 2016
- July 2016
- June 2016
- May 2016
- April 2016
- March 2016
- January 2016
- December 2015
- November 2015
- October 2015
- September 2015
- August 2015
- June 2015
- May 2015
- April 2015
- March 2015
- February 2015
- January 2015
- December 2014
- November 2014
- October 2014
- September 2014
- August 2014
- July 2014
- June 2014
- May 2014
- April 2014
- March 2014
- February 2014
- January 2014
- December 2013
- November 2013
- October 2013
- September 2013
- August 2013
- July 2013
- June 2013
- May 2013
- February 2013
- August 2012
- June 2012
- May 2012
- April 2012
You must be logged in to post a comment.