diff --git a/src/ce_dcd.pas b/src/ce_dcd.pas index d9a4dcb3..88e5f86d 100644 --- a/src/ce_dcd.pas +++ b/src/ce_dcd.pas @@ -16,7 +16,7 @@ type * Completion, hints and declaration finder automatically work on the current * document: ICEMultiDocObserver. *) - TCEDcdWrapper = class(TWritableComponent, ICEProjectObserver, ICEMultiDocObserver) + TCEDcdWrapper = class(TWritableLfmTextComponent, ICEProjectObserver, ICEMultiDocObserver) private fTempLines: TStringList; //fPortNum: Word; diff --git a/src/ce_libman.pas b/src/ce_libman.pas index 7dd06813..80c8c52c 100644 --- a/src/ce_libman.pas +++ b/src/ce_libman.pas @@ -27,7 +27,7 @@ type (** * Represents all the D libraries present on this system. *) - TLibraryManager = class(TWritableComponent) + TLibraryManager = class(TWritableLfmTextComponent) private fCol: TCollection; procedure setCol(const aValue: TCollection); diff --git a/src/ce_options.pas b/src/ce_options.pas index a7b1cc1f..3175a218 100644 --- a/src/ce_options.pas +++ b/src/ce_options.pas @@ -9,7 +9,7 @@ uses type - TCEOptions = class(TWritableComponent) + TCEOptions = class(TWritableLfmTextComponent) private fSubjPersObservers: TCECustomSubject; protected diff --git a/src/ce_project.pas b/src/ce_project.pas index 80cde6a4..01b6b250 100644 --- a/src/ce_project.pas +++ b/src/ce_project.pas @@ -21,7 +21,7 @@ type * * Basically it' s designed to provide the options for the dmd process. *) - TCEProject = class(TWritableComponent) + TCEProject = class(TWritableLfmTextComponent) private fOnChange: TNotifyEvent; fModified: boolean; diff --git a/src/ce_staticmacro.pas b/src/ce_staticmacro.pas index 357747af..d2513d3a 100644 --- a/src/ce_staticmacro.pas +++ b/src/ce_staticmacro.pas @@ -22,7 +22,7 @@ type * Shift + SPACE works automatically on the right editor (ICEMultiDocObserver) * Automatic insertion is handled in TCESynMemo.KeyUp() *) - TCEStaticEditorMacro = class(TWritableComponent, ICEMultiDocObserver) + TCEStaticEditorMacro = class(TWritableLfmTextComponent, ICEMultiDocObserver) private fCompletor: TSynAutoComplete; fMacros: TStringList; diff --git a/src/ce_synmemo.pas b/src/ce_synmemo.pas index 4d03bfa4..41999a36 100644 --- a/src/ce_synmemo.pas +++ b/src/ce_synmemo.pas @@ -24,7 +24,7 @@ type property nestedIndex: Integer read fNestedIndex write fNestedIndex; end; - TCESynMemoCache = class(TWritableComponent) + TCESynMemoCache = class(TWritableLfmTextComponent) private fMemo: TCESynMemo; fFolds: TCollection; diff --git a/src/ce_tools.pas b/src/ce_tools.pas index bf5254db..40157acf 100644 --- a/src/ce_tools.pas +++ b/src/ce_tools.pas @@ -45,7 +45,7 @@ type destructor destroy; override; end; - TCETools = class(TWritableComponent, ICEMainMenuProvider) + TCETools = class(TWritableLfmTextComponent, ICEMainMenuProvider) private fTools: TCollection; function getTool(index: Integer): TCEToolItem; diff --git a/src/ce_writablecomponent.pas b/src/ce_writablecomponent.pas index 046fa49c..1804c17b 100644 --- a/src/ce_writablecomponent.pas +++ b/src/ce_writablecomponent.pas @@ -5,17 +5,20 @@ unit ce_writableComponent; interface uses - Classes, SysUtils, ce_common; + Classes, SysUtils, ce_common, typinfo, fpjson, jsonparser, fpjsonrtti, fpjsondataset; type (** - * The ancestor of classes which can be saved or reloaded to/from - * a text file. It's used each time some options or data have to + * The ancestor of classes which can be saved or reloaded to/from a file. + * It's used each time some options or data have to * persist from a cession to another, independently from the centralized * system provided by the ICESessionOptionObserver/Subject mechanism. + * + * The descendants overrides customLoadFromFile and customSaveToFile + * to save/load to/from a specific format. *) - TWritableComponent = class(TComponent) + TCustomWritableComponent = class(TComponent) protected fFilename: string; fHasLoaded: boolean; @@ -25,10 +28,8 @@ type procedure beforeSave; virtual; procedure afterLoad; virtual; procedure afterSave; virtual; - procedure readerPropNoFound(Reader: TReader; Instance: TPersistent; - var PropName: string; IsPath: boolean; var Handled, Skip: Boolean); virtual; - procedure readerError(Reader: TReader; const Message: string; - var Handled: Boolean); virtual; + procedure customLoadFromFile(const aFilename: string); virtual; abstract; + procedure customSaveToFile(const aFilename: string); virtual; abstract; public procedure saveToFile(const aFilename: string); virtual; procedure loadFromFile(const aFilename: string); virtual; @@ -38,49 +39,66 @@ type property hasSaved: boolean read fHasSaved; end; + (** + * The ancestor of classes which can be saved or reloaded to/from + * a LFM text file. + * By default, reading errors are skipped and no exception is raised. + *) + TWritableLfmTextComponent = class(TCustomWritableComponent) + protected + procedure customLoadFromFile(const aFilename: string); override; + procedure customSaveToFile(const aFilename: string); override; + procedure readerPropNoFound(Reader: TReader; Instance: TPersistent; + var PropName: string; IsPath: boolean; var Handled, Skip: Boolean); virtual; + procedure readerError(Reader: TReader; const Message: string; + var Handled: Boolean); virtual; + end; + + (** + * The ancestor of classes which can be saved or reloaded to/from + * a JSON file. + * By default, reading errors are skipped and no exception is raised. + *) + TWritableJsonComponent = class(TCustomWritableComponent) + protected + procedure propertyError(Sender : TObject; AObject : TObject; Info : PPropInfo; + AValue : TJSONData; Error : Exception; Var doContinue : Boolean); virtual; + procedure restoreProperty(Sender : TObject; AObject : TObject; Info : PPropInfo; + AValue : TJSONData; Var Handled : Boolean); virtual; + procedure customLoadFromFile(const aFilename: string); override; + procedure customSaveToFile(const aFilename: string); override; + end; + implementation -procedure TWritableComponent.beforeSave; +{$REGION TCustomWritableComponent ----------------------------------------------} +procedure TCustomWritableComponent.beforeSave; begin end; -procedure TWritableComponent.beforeLoad; +procedure TCustomWritableComponent.beforeLoad; begin end; -procedure TWritableComponent.afterLoad; +procedure TCustomWritableComponent.afterLoad; begin end; -procedure TWritableComponent.afterSave; +procedure TCustomWritableComponent.afterSave; begin end; -procedure TWritableComponent.setFilename(const aValue: string); +procedure TCustomWritableComponent.setFilename(const aValue: string); begin fFilename := aValue; end; -procedure TWritableComponent.readerPropNoFound(Reader: TReader; Instance: TPersistent; - var PropName: string; IsPath: boolean; var Handled, Skip: Boolean); -begin - Handled := true; - Skip := true; -end; - -procedure TWritableComponent.readerError(Reader: TReader; const Message: string; - var Handled: Boolean); -begin - Handled := true; - fHasLoaded := false; -end; - -procedure TWritableComponent.saveToFile(const aFilename: string); +procedure TCustomWritableComponent.saveToFile(const aFilename: string); begin fHasSaved := true; beforeSave; try - saveCompToTxtFile(self, aFilename); + customSaveToFile(aFilename); except fHasSaved := false; end; @@ -88,15 +106,95 @@ begin afterSave; end; -procedure TWritableComponent.loadFromFile(const aFilename: string); +procedure TCustomWritableComponent.loadFromFile(const aFilename: string); begin fHasLoaded := true; beforeLoad; setFilename(aFilename); - loadCompFromTxtFile(self, aFilename, @readerPropNoFound, @readerError); + customLoadFromFile(aFilename); afterLoad; end; +{$ENDREGION} + +{$REGION TWritableLfmTextComponent ---------------------------------------------} +procedure TWritableLfmTextComponent.customSaveToFile(const aFilename: string); +begin + saveCompToTxtFile(self, aFilename); +end; + +procedure TWritableLfmTextComponent.customLoadFromFile(const aFilename: string); +begin + loadCompFromTxtFile(self, aFilename, @readerPropNoFound, @readerError); +end; + +procedure TWritableLfmTextComponent.readerPropNoFound(Reader: TReader; Instance: TPersistent; + var PropName: string; IsPath: boolean; var Handled, Skip: Boolean); +begin + Handled := true; + Skip := true; +end; + +procedure TWritableLfmTextComponent.readerError(Reader: TReader; const Message: string; + var Handled: Boolean); +begin + Handled := true; + fHasLoaded := false; +end; +{$ENDREGION} + +{$REGION TWritableJsonComponent ------------------------------------------------} +procedure TWritableJsonComponent.propertyError(Sender : TObject; AObject : TObject; Info : PPropInfo; + AValue : TJSONData; Error : Exception; Var doContinue : Boolean); +begin + doContinue := true; +end; + +procedure TWritableJsonComponent.restoreProperty(Sender : TObject; AObject : TObject; Info : PPropInfo; + AValue : TJSONData; Var Handled : Boolean); +begin + Handled := true; +end; + +procedure TWritableJsonComponent.customSaveToFile(const aFilename: string); +var + file_str: TMemoryStream; + json_str: TJSONStreamer; + json_dat: TJSONStringType; +begin + file_str := TMemoryStream.Create; + json_str := TJSONStreamer.Create(nil); + try + json_dat := json_str.ObjectToJSONString(self); + file_str.Write(json_dat[1], length(json_dat)); + file_str.SaveToFile(aFilename); + finally + file_str.Free; + json_str.Free; + end; +end; + +procedure TWritableJsonComponent.customLoadFromFile(const aFilename: string); +var + file_str: TMemoryStream; + json_str: TJSONDeStreamer; + json_dat: TJSONStringType; +begin + file_str := TMemoryStream.Create; + json_str := TJSONDeStreamer.Create(nil); + try + json_str.OnPropertyError:= @propertyError; + json_str.OnRestoreProperty := @restoreProperty; + file_str.LoadFromFile(aFilename); + setLength(json_dat, file_str.Size); + file_str.Read(json_dat[1], length(json_dat)); + json_str.JSONToObject(json_dat, self); + finally + file_str.Free; + json_str.Free; + end; +end; +{$ENDREGION} initialization - registerClasses([TWritableComponent]); + registerClasses([TCustomWritableComponent, TWritableLfmTextComponent, TWritableJsonComponent]); end.