source: branches/web/Common/UDatabase.pas

Last change on this file was 287, checked in by george, 14 years ago
  • Reorganizace rozmístění souborů do složek.
File size: 32.8 KB
Line 
1unit UDatabase;
2
3{$mode delphi}{$H+}
4
5interface
6
7uses
8 SysUtils, Variants, Classes, TypInfo, USqlDatabase, RTLConsts, Contnrs, Dialogs;
9
10type
11 TDatabase = class;
12 TDbList = class;
13 TDbListClass = class of TDbList;
14 TDbObject = class;
15 TDbObjectClass = class of TDbObject;
16
17 {$M+}
18 TDbObject = class
19 private
20 FDatabase: TDatabase;
21 FId: Integer;
22 FRow: TAssociativeArray;
23 procedure WriteProperty(Instance: TDbObject; PropInfo: PPropInfo; Data: TAssociativeArray; Depth: Integer);
24 procedure DeleteProperty(Instance: TDbObject; PropInfo: PPropInfo);
25 procedure DestroyProperty(Instance: TDbObject; PropInfo: PPropInfo; Depth: Integer);
26 procedure ReadProperty(Instance: TObject; PropInfo: PPropInfo; Data: TAssociativeArray; Depth: Integer);
27 function SearchPropertyByPointer(P: Pointer): string;
28 public
29 constructor Create(ADatabase: TDatabase); virtual;
30 procedure Init;
31 procedure Store(Depth: Integer = 1); virtual;
32 procedure Delete;
33 procedure LoadById(Id: Integer; Depth: Integer = 1);
34 procedure Load(Depth: Integer = 1);
35 procedure LoadFromRow(Row: TAssociativeArray; Depth: Integer = 1);
36 destructor Destroy(Depth: Integer = 100); virtual;
37 property Database: TDatabase read FDatabase write FDatabase;
38 published
39 property Id: Integer read FId write Fid;
40 end;
41
42 // TList with RTTI information
43 TPersistentList = class
44 private
45 FList: PPointerList;
46 FCount: Integer;
47 FCapacity: Integer;
48 protected
49 function Get(Index: Integer): Pointer;
50 procedure Grow; virtual;
51 procedure Put(Index: Integer; Item: Pointer);
52 procedure Notify(Ptr: Pointer; Action: TListNotification); virtual;
53 procedure SetCapacity(NewCapacity: Integer);
54 procedure SetCount(NewCount: Integer);
55 public
56 destructor Destroy; override;
57 function Add(Item: Pointer): Integer;
58 procedure Clear; virtual;
59 procedure Delete(Index: Integer);
60 class procedure Error(const Msg: string; Data: Integer); overload; virtual;
61 class procedure Error(Msg: PResStringRec; Data: Integer); overload;
62 procedure Exchange(Index1, Index2: Integer);
63 function Expand: TPersistentList;
64 function Extract(Item: Pointer): Pointer;
65 function First: Pointer;
66 function IndexOf(Item: Pointer): Integer;
67 procedure Insert(Index: Integer; Item: Pointer);
68 function Last: Pointer;
69 procedure Move(CurIndex, NewIndex: Integer);
70 function Remove(Item: Pointer): Integer;
71 procedure Pack;
72 procedure Sort(Compare: TListSortCompare);
73 procedure Assign(ListA: TPersistentList; AOperator: TListAssignOp = laCopy; ListB: TPersistentList = nil);
74 property Capacity: Integer read FCapacity write SetCapacity;
75 property Count: Integer read FCount write SetCount;
76 property Items[Index: Integer]: Pointer read Get write Put; default;
77 property List: PPointerList read FList;
78 end;
79
80 TDbList = class(TObjectList)
81 private
82 FId: Integer;
83 FItemId: Integer;
84 FDatabase: TDatabase;
85 FItemsClass: TDbObjectClass;
86 procedure Put(Index: Integer; const Value: TDbObject); virtual;
87 function Get(Index: Integer): TDbObject; virtual;
88 procedure CheckId;
89 public
90 procedure Store(Depth: Integer = 1);
91 procedure Load(Depth: Integer = 1);
92 procedure Delete;
93 constructor Create(ADatabase: TDatabase; Id: Integer); virtual;
94 destructor Destroy(Depth: Integer = 100);
95// property Id: Integer read FId write Fid;
96 property Items[Index: Integer]: TDbObject read Get write Put; default;
97 property ItemsClass: TDbObjectClass read FItemsClass write FItemsClass;
98 published
99 property ListId: Integer read FId write Fid;
100 property ItemId: Integer read FItemId write FItemId;
101 end;
102 {$M-}
103
104 TDatabase = class(TSqlDatabase)
105 private
106 FOnError: TNotifyEvent;
107 procedure HandleError(Sender: TObject);
108 public
109 ProcessedClass: TObject;
110 DbObject: TDbObject;
111 constructor Create;
112 property OnError: TNotifyEvent read FOnError write FOnError;
113 procedure CheckTable;
114 destructor Destroy; override;
115 end;
116
117 procedure LogMessage(Text: string);
118
119implementation
120
121procedure LogMessage(Text: string);
122begin
123
124end;
125
126{ TDbObject }
127
128procedure TDbObject.WriteProperty(Instance: TDbObject; PropInfo: PPropInfo; Data: TAssociativeArray; Depth: Integer);
129var
130 PropType: PTypeInfo;
131// AncestorValid: Boolean;
132
133 procedure WriteSet(Value: Longint);
134// var
135// I: Integer;
136// BaseType: PTypeInfo;
137 begin
138(*
139 BaseType := GetTypeData(PropType)^.CompType^;
140 WriteValue(vaSet);
141 for I := 0 to SizeOf(TIntegerSet) * 8 - 1 do
142 if I in TIntegerSet(Value) then WriteStr(GetEnumName(BaseType, I));
143 WriteStr('');
144*)
145 end;
146
147 procedure WriteIntProp(IntType: PTypeInfo; Value: Longint);
148 var
149 Ident: string;
150 IntToIdent: TIntToIdent;
151 begin
152 IntToIdent := FindIntToIdent(IntType);
153 if Assigned(IntToIdent) and IntToIdent(Value, Ident) then
154 //WriteIdent(Ident)
155 else
156 Data.AddKeyValue(PropInfo.Name, IntToStr(Value));
157 end;
158
159 procedure WriteOrdProp;
160 var
161 Value: Longint;
162 begin
163 Value := GetOrdProp(Instance, PropInfo);
164 case PropType^.Kind of
165 tkInteger:
166 WriteIntProp(PPropInfo(PropInfo)^.PropType, Value);
167 tkChar:
168 Data.AddKeyValue(PropInfo.Name, Chr(Value));
169 tkSet:
170 LogMessage('Unsupported writer persistent type: Set');
171 // WriteSet(Value);
172 tkEnumeration:
173 Data.AddKeyValue(PropInfo.Name, IntToStr(Value));
174 end;
175 end;
176
177 procedure WriteFloatProp;
178 var
179 Value: Extended;
180 begin
181 Value := GetFloatProp(Instance, PropInfo);
182 Data.AddKeyValue(PropInfo.Name, MysqlFloatToStr(Value));
183 end;
184
185 procedure WriteInt64Prop;
186// var
187// Value: Int64;
188 begin
189(*
190 Value := GetInt64Prop(Instance, PropInfo);
191 WritePropPath;
192 WriteInteger(Value);
193*)
194 end;
195
196 procedure WriteStrProp;
197 begin
198 Data.AddKeyValue(PropInfo.Name, GetWideStrProp(Instance, PropInfo));
199 end;
200
201 procedure WriteObjectProp;
202 var
203 Value: TObject;
204// OldAncestor: TPersistent;
205// SavePropPath, ComponentValue: string;
206 begin
207 Value := TObject(GetOrdProp(Instance, PropInfo));
208 if Value = nil then begin
209 Data.AddKeyValue(PropInfo.Name, IntToStr(0));
210 end else if Value is TDbObject then begin
211 TDbObject(Value).Store(Depth-1);
212 Data.AddKeyValue(PropInfo.Name, IntToStr(TDbObject(Value).Id));
213 end else if Value is TDbList then begin
214 if Value = nil then raise Exception.Create('WriteObjectProp: Unbelievable! Value is nil!');
215 TDbList(Value).Store(Depth-1);
216 Data.AddKeyValue(PropInfo.Name, IntToStr(TDbList(Value).FId));
217 end;
218 end;
219
220
221begin
222 // Using IsDefaultPropertyValue will tell us if we should write out
223 // a given property because it was different from the default or
224 // different from the Ancestor (if applicable).
225 if (PropInfo^.GetProc <> nil) and
226 ((PropInfo^.SetProc <> nil) or
227 ((PropInfo^.PropType^.Kind = tkClass) and
228 (TObject(GetOrdProp(Instance, PropInfo)) is TComponent) and
229 (csSubComponent in TComponent(GetOrdProp(Instance, PropInfo)).ComponentStyle))) then
230 begin
231 LogMessage('Write property: '+Instance.ClassName+'.'+PropInfo.Name);
232 //if not IsDefaultPropertyValue(Instance, PropInfo, GetLookupInfo) then
233 begin
234 //AncestorValid := AncestorIsValid(Ancestor, Root, RootAncestor);
235 PropType := PropInfo^.PropType;
236 case PropType^.Kind of
237 tkInteger, tkChar, tkEnumeration, tkSet:
238 WriteOrdProp;
239 tkFloat:
240 WriteFloatProp;
241 tkString, tkLString, tkWString:
242 WriteStrProp;
243 tkClass:
244 WriteObjectProp;
245 //tkMethod:
246 // WriteMethodProp;
247 //tkVariant:
248 // WriteVariantProp;
249 tkInt64:
250 WriteInt64Prop;
251 //tkInterface:
252 // WriteInterfaceProp;
253 else
254 raise Exception.Create('Not supported class property '+GetEnumName(PropType, Integer(PropType^.Kind)));
255 end;
256 end;
257 end;
258end;
259
260
261procedure TDbObject.ReadProperty(Instance: TObject; PropInfo: PPropInfo; Data: TAssociativeArray; Depth: Integer);
262const
263 NilMethod: TMethod = (Code: nil; Data: nil);
264var
265 PropType: PTypeInfo;
266// Method: TMethod;
267
268 procedure SetIntIdent(Instance: TPersistent; PropInfo: Pointer;
269 const Ident: string);
270 var
271 V: Longint;
272 IdentToInt: TIdentToInt;
273 begin
274 IdentToInt := FindIdentToInt(PPropInfo(PropInfo)^.PropType);
275 if Assigned(IdentToInt) and IdentToInt(Ident, V) then
276 SetOrdProp(Instance, PropInfo, V)
277 else
278 //PropValueError;
279 end;
280
281 procedure ProcessObjectProp;
282 var
283 Setter: Longint;
284 Value: TObject;
285 TypeData: PTypeData;
286 TargetObject: ^TObject;
287 TargetClass: TClass;
288 DbListClass: TDbListClass;
289 DbObjectClass: TDbObjectClass;
290 begin
291 if Depth > 0 then begin
292 // List := TDbList.Create(AClass, FDatabase, FId);
293 Value := TObject(GetOrdProp(Instance, PropInfo));
294 TypeData := GetTypeData(PropInfo.PropType);
295// ShowMessage(PropInfo.Name+' '+TypeData.ClassType.ClassName);
296 if Value = nil then begin
297 Setter := Longint(PropInfo^.SetProc);
298 if (Setter and $FF000000) = $FF000000 then
299 begin // field - Setter is the field's offset in the instance data
300 TargetObject := Pointer(Integer(Instance) + (Setter and $00FFFFFF));
301 TargetClass := TypeData.ClassType;
302// TargetClass := TargetClass.ClassParent;
303// ShowMessage(TargetClass.ClassName);
304 if TargetClass.ClassParent = TDbObject then begin
305 DbObjectClass := TDbObjectClass(TargetClass);
306 TargetObject^ := DbObjectClass.Create(FDatabase); // auto ref count
307 end else
308 if TargetClass.ClassParent = TDbList then begin
309 DbListClass := TDbListClass(TargetClass);
310 //ShowMessage(DbListClass.ClassName);
311
312 TargetObject^ := DbListClass.Create(FDatabase, StrToInt(FRow.Values[PropInfo.Name])); // auto ref count
313 end else TargetObject^ := TargetClass.Create; // auto ref count
314 Value := TObject(GetOrdProp(Instance, PropInfo));
315 end else raise Exception.Create('Object property '+PropInfo.Name+' in object '+Instance.ClassName+' is not mapped directly to variable!');
316 end;
317 //ShowMessage(IntToStr(Integer(Value.ClassInfo)));
318 if Value is TDbObject then begin
319 (Value as TDbObject).LoadById(StrToInt(FRow.Values[PropInfo.Name]), Depth-1);
320 //ShowMessage(Value.ClassName);
321 end else if Value is TDbList then begin
322 (Value as TDbList).Load(Depth);
323 //ShowMessage(Value.ClassName);
324 end;
325 end;
326 end;
327
328begin
329 LogMessage('Read property: ' + Instance.ClassName + '.' + PropInfo.Name);
330// if PPropInfo(PropInfo)^.SetProc = nil then
331// if not ((PPropInfo(PropInfo)^.PropType^.Kind = tkClass) and
332// (TObject(GetOrdProp(Instance, PropInfo)) is TComponent) and
333// (csSubComponent in TComponent(GetOrdProp(Instance, PropInfo)).ComponentStyle)) then
334// ReadError(@SReadOnlyProperty);
335 PropType := PPropInfo(PropInfo)^.PropType;
336 try
337 case PropType^.Kind of
338 tkInteger:
339// if NextValue = vaIdent then
340// SetIntIdent(Instance, PropInfo, Data)
341// else
342 SetOrdProp(Instance, PropInfo, StrToInt(Data.Values[PropInfo.Name]));
343 tkChar:
344 SetOrdProp(Instance, PropInfo, Ord(Data.Values[PropInfo.Name][1]));
345 tkEnumeration:
346 SetOrdProp(Instance, PropInfo, StrToInt(Data.Values[PropInfo.Name]));
347 tkFloat:
348 SetFloatProp(Instance, PropInfo, MySqlStrToFloat(Data.Values[PropInfo.Name]));
349 tkString, tkLString:
350 SetStrProp(Instance, PropInfo, Data.Values[PropInfo.Name]);
351 tkWString:
352 SetWideStrProp(Instance, PropInfo, Data.Values[PropInfo.Name]);
353 tkSet:
354 LogMessage('Unsupported reader property type: Set');
355 //SetOrdProp(Instance, PropInfo, ReadSet(PropType));
356 tkClass: ProcessObjectProp;
357 (*
358 case NextValue of
359 vaNil:
360 begin
361 ReadValue;
362 SetOrdProp(Instance, PropInfo, 0);
363 end;
364 vaCollection:
365 begin
366 ReadValue;
367 ReadCollection(TCollection(GetOrdProp(Instance, PropInfo)));
368 end
369 else
370 SetObjectIdent(Instance, PropInfo, ReadIdent);
371 end;
372 *)
373(* tkMethod:
374 if NextValue = vaNil then
375 begin
376 ReadValue;
377 SetMethodProp(Instance, PropInfo, NilMethod);
378 end
379 else
380 begin
381 Method.Code := FindMethod(Root, ReadIdent);
382 Method.Data := Root;
383 if Method.Code <> nil then SetMethodProp(Instance, PropInfo, Method);
384 end;
385 tkVariant:
386 SetVariantReference;
387 tkInt64:
388 SetInt64Prop(Instance, PropInfo, ReadInt64);
389 tkInterface:
390 SetInterfaceReference;
391*)
392 end;
393 finally
394 end;
395end;
396
397procedure TDbObject.Init;
398begin
399end;
400
401destructor TDbObject.Destroy(Depth: Integer = 100);
402var
403 I, Count: Integer;
404 PropInfo: PPropInfo;
405 PropList: PPropList;
406// Name: string;
407begin
408 LogMessage('Destroy class: ' + Self.ClassName);
409 if Depth >= 0 then begin
410 Store(0);
411 if Self.ClassInfo = nil then raise Exception.Create('Class ' + Self.ClassName + ' doesn''t provide RTTI information!');
412 Count := GetTypeData(Self.ClassInfo)^.PropCount;
413 if Count > 0 then with FDatabase do begin
414 ProcessedClass := Self;
415 GetMem(PropList, Count * SizeOf(Pointer));
416 try
417 GetPropInfos(Self.ClassInfo, PropList);
418 for I := 0 to Count - 1 do
419 begin
420 PropInfo := PropList^[I];
421 if PropInfo = nil then
422 Break;
423 if IsStoredProp(Self, PropInfo) then
424 DestroyProperty(Self, PropInfo, Depth);
425 end;
426 finally
427 FreeMem(PropList, Count * SizeOf(Pointer));
428 end;
429 end;
430 inherited Destroy;
431 end;
432end;
433
434procedure TDbObject.Store(Depth: Integer = 1);
435var
436 Data: TAssociativeArray;
437 I, Count: Integer;
438 PropInfo: PPropInfo;
439 PropList: PPropList;
440// Name: string;
441begin
442 LogMessage('Write class: ' + Self.ClassName);
443 if Depth >= 0 then begin
444 Data := TAssociativeArray.Create;
445 if Self.ClassInfo = nil then raise Exception.Create('Trida '+Self.ClassName+' neposkytuje RTTI informace!');
446 Count := GetTypeData(Self.ClassInfo)^.PropCount;
447 if Count > 0 then with FDatabase do begin
448 GetMem(PropList, Count * SizeOf(Pointer));
449 try
450 GetPropInfos(Self.ClassInfo, PropList);
451 for I := 0 to Count - 1 do
452 begin
453 PropInfo := PropList^[I];
454 if PropInfo = nil then
455 Break;
456 if IsStoredProp(Self, PropInfo) then
457 WriteProperty(Self, PropInfo, Data, Depth);
458 end;
459
460 ProcessedClass := Self;
461 repeat
462 Replace(Self.ClassName, Data);
463 until not RepeatLastAction;
464 FId := FDatabase.LastInsertId;
465 finally
466 FreeMem(PropList, Count * SizeOf(Pointer));
467 end;
468 end;
469// Instance.DefineProperties(Self);
470 end;
471end;
472
473function TDbObject.SearchPropertyByPointer(P: Pointer): string;
474var
475// Data: TAssocArray;
476 I, Count: Integer;
477 PropInfo: PPropInfo;
478 PropList: PPropList;
479 Instance: TObject;
480// Name: string;
481begin
482 Instance := Self;
483 if Self.ClassInfo = nil then raise Exception.Create('Trida '+Self.ClassName+' neposkytuje RTTI informace!');
484 Count := GetTypeData(Self.ClassInfo)^.PropCount;
485 if Count > 0 then with FDatabase do begin
486 ProcessedClass := Self;
487 GetMem(PropList, Count * SizeOf(Pointer));
488 try
489 GetPropInfos(Self.ClassInfo, PropList);
490 for I := 0 to Count - 1 do
491 begin
492 PropInfo := PropList^[I];
493 if PropInfo = nil then
494 Break;
495 if IsStoredProp(Instance, PropInfo) then
496 if GetObjectProp(Self,PropInfo) = P then begin
497 Result := PropInfo.Name;
498 Break;
499 end;
500 end;
501 finally
502 FreeMem(PropList, Count * SizeOf(Pointer));
503 end;
504 end;
505end;
506
507{ TDbList }
508
509procedure TDbList.CheckId;
510var
511 Rows: TDbRows;
512begin
513 if FId = 0 then begin
514 FDatabase.ProcessedClass := Self;
515 Rows := FDatabase.Select(Self.ClassName, 'MAX(ListId)');
516// ShowMessage(Rows[0].Values['MAX(ListId)']);
517 if Rows.Count > 0 then begin
518 if Rows[0].Values['MAX(ListId)'] = '' then FId := 1 else
519 FId := StrToInt(Rows[0].Values['MAX(ListId)']) + 1;
520 end else FId := 1;
521 Rows.Free;
522 end;
523end;
524
525constructor TDbList.Create(ADatabase: TDatabase; Id: Integer);
526begin
527 FId := Id;
528 FDatabase := ADatabase;
529// inherited Create(FDatabase);
530 //Load;
531end;
532
533procedure TDbList.Delete;
534var
535 I: Integer;
536begin
537 for I := 0 to Count-1 do Items[I].Delete;
538 FDatabase.Select(Self.ClassName, 'ListId=' + IntToStr(FId))
539end;
540
541destructor TDbList.Destroy(Depth: Integer = 100);
542var
543 I: Integer;
544begin
545 LogMessage('Destroy DbList: ' + Self.ClassName);
546 Store(0);
547 if Depth >= 0 then begin
548 if Depth > 0 then for I := 0 to Count-1 do if Assigned(Items[I]) then TDbObject(Items[I]).Destroy(Depth);
549 inherited Destroy;
550 end;
551end;
552
553function TDbList.Get(Index: Integer): TDbObject;
554begin
555 Result := inherited Get(Index);
556end;
557
558procedure TDbList.Load(Depth: Integer = 1);
559var
560// B: TObject;
561 DbRows: TDbRows;
562 I: Integer;
563// d: TPersistentList;
564begin
565// d:= TPersistentList.Create;
566// ShowMessage(IntToStr(Integer(d.ClassInfo)));
567 FDatabase.ProcessedClass := Self;
568 //ShowMessage(IntToStr(Integer(FDatabase.ProcessedClass.ClassInfo)));
569 FDatabase.Table := Self.ClassName;
570 DbRows := FDatabase.Select(FDatabase.Table, '*', 'ListId=' + IntToStr(FId) + ' ORDER BY id');
571 Count := DbRows.Count;
572 for I := 0 to DbRows.Count-1 do begin
573 Items[I] := ItemsClass.Create(FDatabase);
574 if Depth > 0 then Items[I].LoadById(StrToInt(DbRows.Data[I].Values['ItemId']), Depth-1);
575 // FromRow(Items[I], DbRows.Data[I]);
576 end;
577end;
578
579procedure TDbList.Put(Index: Integer; const Value: TDbObject);
580begin
581 inherited Put(Index, Value);
582end;
583
584procedure TDbList.Store(Depth: Integer = 1);
585var
586 I: Integer;
587 Data: TAssociativeArray;
588begin
589 if Depth >= 0 then begin
590 CheckId;
591 FDatabase.ProcessedClass := Self;
592 FDatabase.Delete(Self.ClassName, 'ListId='+IntToStr(FId));
593 if Self.ClassName = 'tmeasurepointsetlist' then LogMessage('d');
594 Data := TAssociativeArray.Create;
595 Data.AddKeyValue('ListId', IntToStr(FId));
596 Data.AddKeyValue('ItemId', '0');
597 for I := 0 to Count-1 do
598 if Assigned(Items[I]) then begin
599 if Depth > 0 then Items[I].Store(Depth);
600 Data.Values['ItemId'] := IntToStr(TDbObject(Items[I]).FId);
601 FDatabase.ProcessedClass := Self;
602 FDatabase.Insert(Self.ClassName, Data);
603 end else raise Exception.Create('Item '+IntToStr(I)+' in '+Self.ClassName+' is nil!');
604 Data.Free;
605// inherited StoreClassName(FItemsClass.ClassName+'_list', Depth);
606 end;
607end;
608
609{ TDatabase }
610
611procedure TDatabase.CheckTable;
612begin
613
614end;
615
616constructor TDatabase.Create;
617begin
618 inherited;
619end;
620
621destructor TDatabase.Destroy;
622begin
623 Disconnect;
624 inherited;
625end;
626
627procedure TDbObject.LoadFromRow(Row: TAssociativeArray; Depth: Integer = 1);
628var
629 I, Count: Integer;
630 PropInfo: PPropInfo;
631 PropList: PPropList;
632begin
633 LogMessage('Load class: ' + Self.ClassName);
634// ShowMessage(Row.GetAllValues);
635 FRow := Row;
636 if Self.ClassInfo = nil then raise Exception.Create('Trida '+Self.ClassName+' neposkytuje RTTI informace!');
637 Count := GetTypeData(Self.ClassInfo)^.PropCount;
638 if Count > 0 then with FDatabase do begin
639 ProcessedClass := Self;
640 GetMem(PropList, Count * SizeOf(Pointer));
641 try
642 GetPropInfos(Self.ClassInfo, PropList);
643 for I := 0 to Count - 1 do
644 begin
645 PropInfo := PropList^[I];
646 if PropInfo = nil then
647 Break;
648 if IsStoredProp(Self, PropInfo) then
649 ReadProperty(Self, PropInfo, Row, Depth);
650 end;
651 finally
652 FreeMem(PropList, Count * SizeOf(Pointer));
653 end;
654 end;
655// Instance.DefineProperties(Self);
656// Data.Free;
657 FRow := nil;
658end;
659
660procedure TDatabase.HandleError(Sender: TObject);
661var
662 Name: string;
663 PropInfo: PPropInfo;
664begin
665 if LastErrorNumber = 1146 then begin // Table doesn't exist
666 CreateTable(Table);
667 RepeatLastAction := True;
668 end else
669 if LastErrorNumber = 1054 then begin // Unknown column
670 Name := Copy(LastErrorMessage, Pos('''', LastErrorMessage) + 1, Length(LastErrorMessage));
671 Name := Copy(Name, 1, Pos('''', Name)-1);
672 //ShowMessage(IntToStr(Integer(ProcessedClass.ClassInfo)));
673 //ShowMessage(ProcessedClass.ClassName);
674 PropInfo := GetPropInfo(ProcessedClass.ClassInfo, Name);
675 if PropInfo = nil then
676 raise Exception.Create('Missing published property '+Name+' in class '+ProcessedClass.ClassName)
677 else CreateColumn(Table, PropInfo.Name, PropInfo.PropType^.Kind);
678 RepeatLastAction := True;
679 end else begin
680 if Assigned(FOnError) then FOnError(Self);
681 end;
682end;
683
684constructor TDbObject.Create(ADatabase: TDatabase);
685begin
686 FDatabase := ADatabase;
687 FId := 0;
688end;
689
690procedure TDbObject.LoadById(Id: Integer; Depth: Integer = 1);
691var
692 Rows: TDbRows;
693begin
694 FId := Id;
695 Rows := FDatabase.Select(Self.ClassName, '*', 'id=' + IntToStr(FId));
696 if Rows.Count > 0 then LoadFromRow(Rows[0], Depth) else begin
697 Store;
698 Rows.Free;
699 Rows := FDatabase.Select(Self.ClassName, '*', 'id=' + IntToStr(FId));
700 LoadFromRow(Rows[0], Depth);
701 end;
702 Rows.Free;
703end;
704
705{ TPersistentList }
706
707destructor TPersistentList.Destroy;
708begin
709 Clear;
710end;
711
712function TPersistentList.Add(Item: Pointer): Integer;
713begin
714 Result := FCount;
715 if Result = FCapacity then
716 Grow;
717 FList^[Result] := Item;
718 Inc(FCount);
719 if Item <> nil then
720 Notify(Item, lnAdded);
721end;
722
723procedure TPersistentList.Clear;
724begin
725 SetCount(0);
726 SetCapacity(0);
727end;
728
729procedure TPersistentList.Delete(Index: Integer);
730var
731 Temp: Pointer;
732begin
733 if (Index < 0) or (Index >= FCount) then
734 Error(@SListIndexError, Index);
735 Temp := Items[Index];
736 Dec(FCount);
737 if Index < FCount then
738 System.Move(FList^[Index + 1], FList^[Index],
739 (FCount - Index) * SizeOf(Pointer));
740 if Temp <> nil then
741 Notify(Temp, lnDeleted);
742end;
743
744class procedure TPersistentList.Error(const Msg: string; Data: Integer);
745
746 (*
747 function ReturnAddr: Pointer;
748 asm
749 MOV EAX,[EBP+4]
750 end;
751 *)
752
753begin
754// raise EListError.CreateFmt(Msg, [Data]) at ReturnAddr;
755end;
756
757class procedure TPersistentList.Error(Msg: PResStringRec; Data: Integer);
758begin
759 TPersistentList.Error(LoadResString(Msg), Data);
760end;
761
762procedure TPersistentList.Exchange(Index1, Index2: Integer);
763var
764 Item: Pointer;
765begin
766 if (Index1 < 0) or (Index1 >= FCount) then
767 Error(@SListIndexError, Index1);
768 if (Index2 < 0) or (Index2 >= FCount) then
769 Error(@SListIndexError, Index2);
770 Item := FList^[Index1];
771 FList^[Index1] := FList^[Index2];
772 FList^[Index2] := Item;
773end;
774
775function TPersistentList.Expand: TPersistentList;
776begin
777 if FCount = FCapacity then
778 Grow;
779 Result := Self;
780end;
781
782function TPersistentList.First: Pointer;
783begin
784 Result := Get(0);
785end;
786
787function TPersistentList.Get(Index: Integer): Pointer;
788begin
789 if (Index < 0) or (Index >= FCount) then
790 Error(@SListIndexError, Index);
791 Result := FList^[Index];
792end;
793
794procedure TPersistentList.Grow;
795var
796 Delta: Integer;
797begin
798 if FCapacity > 64 then
799 Delta := FCapacity div 4
800 else
801 if FCapacity > 8 then
802 Delta := 16
803 else
804 Delta := 4;
805 SetCapacity(FCapacity + Delta);
806end;
807
808function TPersistentList.IndexOf(Item: Pointer): Integer;
809begin
810 Result := 0;
811 while (Result < FCount) and (FList^[Result] <> Item) do
812 Inc(Result);
813 if Result = FCount then
814 Result := -1;
815end;
816
817procedure TPersistentList.Insert(Index: Integer; Item: Pointer);
818begin
819 if (Index < 0) or (Index > FCount) then
820 Error(@SListIndexError, Index);
821 if FCount = FCapacity then
822 Grow;
823 if Index < FCount then
824 System.Move(FList^[Index], FList^[Index + 1],
825 (FCount - Index) * SizeOf(Pointer));
826 FList^[Index] := Item;
827 Inc(FCount);
828 if Item <> nil then
829 Notify(Item, lnAdded);
830end;
831
832function TPersistentList.Last: Pointer;
833begin
834 Result := Get(FCount - 1);
835end;
836
837procedure TPersistentList.Move(CurIndex, NewIndex: Integer);
838var
839 Item: Pointer;
840begin
841 if CurIndex <> NewIndex then
842 begin
843 if (NewIndex < 0) or (NewIndex >= FCount) then
844 Error(@SListIndexError, NewIndex);
845 Item := Get(CurIndex);
846 FList^[CurIndex] := nil;
847 Delete(CurIndex);
848 Insert(NewIndex, nil);
849 FList^[NewIndex] := Item;
850 end;
851end;
852
853procedure TPersistentList.Put(Index: Integer; Item: Pointer);
854var
855 Temp: Pointer;
856begin
857 if (Index < 0) or (Index >= FCount) then
858 Error(@SListIndexError, Index);
859 if Item <> FList^[Index] then
860 begin
861 Temp := FList^[Index];
862 FList^[Index] := Item;
863 if Temp <> nil then
864 Notify(Temp, lnDeleted);
865 if Item <> nil then
866 Notify(Item, lnAdded);
867 end;
868end;
869
870function TPersistentList.Remove(Item: Pointer): Integer;
871begin
872 Result := IndexOf(Item);
873 if Result >= 0 then
874 Delete(Result);
875end;
876
877procedure TPersistentList.Pack;
878var
879 I: Integer;
880begin
881 for I := FCount - 1 downto 0 do
882 if Items[I] = nil then
883 Delete(I);
884end;
885
886procedure TPersistentList.SetCapacity(NewCapacity: Integer);
887begin
888 if (NewCapacity < FCount) or (NewCapacity > MaxListSize) then
889 Error(@SListCapacityError, NewCapacity);
890 if NewCapacity <> FCapacity then
891 begin
892 ReallocMem(FList, NewCapacity * SizeOf(Pointer));
893 FCapacity := NewCapacity;
894 end;
895end;
896
897procedure TPersistentList.SetCount(NewCount: Integer);
898var
899 I: Integer;
900begin
901 if (NewCount < 0) or (NewCount > MaxListSize) then
902 Error(@SListCountError, NewCount);
903 if NewCount > FCapacity then
904 SetCapacity(NewCount);
905 if NewCount > FCount then
906 FillChar(FList^[FCount], (NewCount - FCount) * SizeOf(Pointer), 0)
907 else
908 for I := FCount - 1 downto NewCount do
909 Delete(I);
910 FCount := NewCount;
911end;
912
913procedure QuickSort(SortList: PPointerList; L, R: Integer;
914 SCompare: TListSortCompare);
915var
916 I, J: Integer;
917 P, T: Pointer;
918begin
919 repeat
920 I := L;
921 J := R;
922 P := SortList^[(L + R) shr 1];
923 repeat
924 while SCompare(SortList^[I], P) < 0 do
925 Inc(I);
926 while SCompare(SortList^[J], P) > 0 do
927 Dec(J);
928 if I <= J then
929 begin
930 T := SortList^[I];
931 SortList^[I] := SortList^[J];
932 SortList^[J] := T;
933 Inc(I);
934 Dec(J);
935 end;
936 until I > J;
937 if L < J then
938 QuickSort(SortList, L, J, SCompare);
939 L := I;
940 until I >= R;
941end;
942
943procedure TPersistentList.Sort(Compare: TListSortCompare);
944begin
945 if (FList <> nil) and (Count > 0) then
946 QuickSort(FList, 0, Count - 1, Compare);
947end;
948
949function TPersistentList.Extract(Item: Pointer): Pointer;
950var
951 I: Integer;
952begin
953 Result := nil;
954 I := IndexOf(Item);
955 if I >= 0 then
956 begin
957 Result := Item;
958 FList^[I] := nil;
959 Delete(I);
960 Notify(Result, lnExtracted);
961 end;
962end;
963
964procedure TPersistentList.Notify(Ptr: Pointer; Action: TListNotification);
965begin
966end;
967
968procedure TPersistentList.Assign(ListA: TPersistentList; AOperator: TListAssignOp; ListB: TPersistentList);
969var
970 I: Integer;
971 LTemp, LSource: TPersistentList;
972begin
973 // ListB given?
974 if ListB <> nil then
975 begin
976 LSource := ListB;
977 Assign(ListA);
978 end
979 else
980 LSource := ListA;
981
982 // on with the show
983 case AOperator of
984
985 // 12345, 346 = 346 : only those in the new list
986 laCopy:
987 begin
988 Clear;
989 Capacity := LSource.Capacity;
990 for I := 0 to LSource.Count - 1 do
991 Add(LSource[I]);
992 end;
993
994 // 12345, 346 = 34 : intersection of the two lists
995 laAnd:
996 for I := Count - 1 downto 0 do
997 if LSource.IndexOf(Items[I]) = -1 then
998 Delete(I);
999
1000 // 12345, 346 = 123456 : union of the two lists
1001 laOr:
1002 for I := 0 to LSource.Count - 1 do
1003 if IndexOf(LSource[I]) = -1 then
1004 Add(LSource[I]);
1005
1006 // 12345, 346 = 1256 : only those not in both lists
1007 laXor:
1008 begin
1009 LTemp := TPersistentList.Create; // Temp holder of 4 byte values
1010 try
1011 LTemp.Capacity := LSource.Count;
1012 for I := 0 to LSource.Count - 1 do
1013 if IndexOf(LSource[I]) = -1 then
1014 LTemp.Add(LSource[I]);
1015 for I := Count - 1 downto 0 do
1016 if LSource.IndexOf(Items[I]) <> -1 then
1017 Delete(I);
1018 I := Count + LTemp.Count;
1019 if Capacity < I then
1020 Capacity := I;
1021 for I := 0 to LTemp.Count - 1 do
1022 Add(LTemp[I]);
1023 finally
1024 LTemp.Free;
1025 end;
1026 end;
1027
1028 // 12345, 346 = 125 : only those unique to source
1029 laSrcUnique:
1030 for I := Count - 1 downto 0 do
1031 if LSource.IndexOf(Items[I]) <> -1 then
1032 Delete(I);
1033
1034 // 12345, 346 = 6 : only those unique to dest
1035 laDestUnique:
1036 begin
1037 LTemp := TPersistentList.Create;
1038 try
1039 LTemp.Capacity := LSource.Count;
1040 for I := LSource.Count - 1 downto 0 do
1041 if IndexOf(LSource[I]) = -1 then
1042 LTemp.Add(LSource[I]);
1043 Assign(LTemp);
1044 finally
1045 LTemp.Free;
1046 end;
1047 end;
1048 end;
1049end;
1050
1051procedure TDbObject.DeleteProperty(Instance: TDbObject; PropInfo: PPropInfo);
1052var
1053 PropType: PTypeInfo;
1054
1055 procedure WriteObjectProp;
1056 var
1057 Value: TObject;
1058// OldAncestor: TPersistent;
1059// SavePropPath, ComponentValue: string;
1060 Setter: Longint;
1061 TargetObject: ^TObject;
1062 begin
1063 Value := TObject(GetOrdProp(Instance, PropInfo));
1064 if Value = nil then begin
1065 end else begin
1066 if Value is TDbObject then begin
1067 TDbObject(Value).Delete;
1068 end else if Value is TDbList then begin
1069 TDbList(Value).Delete;
1070 end;
1071
1072 // Set object reference to nil
1073 Setter := Longint(PropInfo^.SetProc);
1074 if (Setter and $FF000000) = $FF000000 then
1075 begin // field - Setter is the field's offset in the instance data
1076 TargetObject := Pointer(Integer(Instance) + (Setter and $00FFFFFF));
1077 TargetObject^ := nil; // auto ref count
1078 end;
1079 end;
1080 end;
1081
1082
1083begin
1084 // Using IsDefaultPropertyValue will tell us if we should write out
1085 // a given property because it was different from the default or
1086 // different from the Ancestor (if applicable).
1087 if (PropInfo^.GetProc <> nil) and
1088 ((PropInfo^.SetProc <> nil) or
1089 ((PropInfo^.PropType^.Kind = tkClass) and
1090 (TObject(GetOrdProp(Instance, PropInfo)) is TComponent) and
1091 (csSubComponent in TComponent(GetOrdProp(Instance, PropInfo)).ComponentStyle))) then
1092 begin
1093 LogMessage('Destroy property: '+Instance.ClassName+'.'+PropInfo.Name);
1094 //if not IsDefaultPropertyValue(Instance, PropInfo, GetLookupInfo) then
1095 begin
1096 //AncestorValid := AncestorIsValid(Ancestor, Root, RootAncestor);
1097 PropType := PropInfo^.PropType;
1098 case PropType^.Kind of
1099 tkClass:
1100 WriteObjectProp;
1101 end;
1102 end;
1103 end;
1104end;
1105
1106procedure TDbObject.DestroyProperty(Instance: TDbObject; PropInfo: PPropInfo; Depth: Integer);
1107var
1108 PropType: PTypeInfo;
1109
1110 procedure WriteObjectProp;
1111 var
1112 Value: TObject;
1113// OldAncestor: TPersistent;
1114// SavePropPath, ComponentValue: string;
1115 Setter: Longint;
1116 TargetObject: ^TObject;
1117 begin
1118 Value := TObject(GetOrdProp(Instance, PropInfo));
1119 if Value = nil then begin
1120 end else begin
1121 if Value is TDbObject then begin
1122 TDbObject(Value).Destroy(Depth - 1);
1123 end else if Value is TDbList then begin
1124 TDbList(Value).Destroy(Depth - 1);
1125 end;
1126
1127 // Set object reference to nil
1128 Setter := Longint(PropInfo^.SetProc);
1129 if (Setter and $FF000000) = $FF000000 then
1130 begin // field - Setter is the field's offset in the instance data
1131 TargetObject := Pointer(Integer(Instance) + (Setter and $00FFFFFF));
1132 TargetObject^ := nil; // auto ref count
1133 end;
1134 end;
1135 end;
1136
1137
1138begin
1139 // Using IsDefaultPropertyValue will tell us if we should write out
1140 // a given property because it was different from the default or
1141 // different from the Ancestor (if applicable).
1142 if (PropInfo^.GetProc <> nil) and
1143 ((PropInfo^.SetProc <> nil) or
1144 ((PropInfo^.PropType^.Kind = tkClass) and
1145 (TObject(GetOrdProp(Instance, PropInfo)) is TComponent) and
1146 (csSubComponent in TComponent(GetOrdProp(Instance, PropInfo)).ComponentStyle))) then
1147 begin
1148 LogMessage('Destroy property: '+Instance.ClassName+'.'+PropInfo.Name);
1149 //if not IsDefaultPropertyValue(Instance, PropInfo, GetLookupInfo) then
1150 begin
1151 //AncestorValid := AncestorIsValid(Ancestor, Root, RootAncestor);
1152 PropType := PropInfo^.PropType;
1153 case PropType^.Kind of
1154 tkClass:
1155 WriteObjectProp;
1156 end;
1157 end;
1158 end;
1159end;
1160
1161procedure TDbObject.Load(Depth: Integer);
1162begin
1163 LoadById(Id, Depth);
1164end;
1165
1166procedure TDbObject.Delete;
1167var
1168 I, Count: Integer;
1169 PropInfo: PPropInfo;
1170 PropList: PPropList;
1171// Name: string;
1172begin
1173 LogMessage('Delete class: '+Self.ClassName);
1174 if Self.ClassInfo = nil then raise Exception.Create('Class '+Self.ClassName+' doesn''t provide RTTI information!');
1175 Count := GetTypeData(Self.ClassInfo)^.PropCount;
1176 if Count > 0 then with FDatabase do begin
1177 ProcessedClass := Self;
1178 GetMem(PropList, Count * SizeOf(Pointer));
1179 try
1180 GetPropInfos(Self.ClassInfo, PropList);
1181 for I := 0 to Count - 1 do
1182 begin
1183 PropInfo := PropList^[I];
1184 if PropInfo = nil then
1185 Break;
1186 if IsStoredProp(Self, PropInfo) then
1187 DeleteProperty(Self, PropInfo);
1188 end;
1189 finally
1190 FreeMem(PropList, Count * SizeOf(Pointer));
1191 end;
1192 end;
1193 Database.Delete(Self.ClassName, 'id=' + IntToStr(Id));
1194 inherited Destroy;
1195end;
1196
1197end.
Note: See TracBrowser for help on using the repository browser.