• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16class PersistentStorage implements IMultiPropertiesChangeSubscriber {
17  private static Storage_: IStorage;
18  private static Instance_: PersistentStorage = undefined;
19
20  private id_: number;
21  private links_: Map<string, ObservedPropertyAbstract<any>>;
22
23  /**
24   *
25   * @param storage method to be used by the framework to set the backend
26   * this is to be done during startup
27   */
28  public static ConfigureBackend(storage: IStorage): void {
29    PersistentStorage.Storage_ = storage;
30  }
31
32  private static GetOrCreate(): PersistentStorage {
33    if (PersistentStorage.Instance_) {
34      // already initialized
35      return PersistentStorage.Instance_;
36    }
37
38    PersistentStorage.Instance_ = new PersistentStorage();
39    return PersistentStorage.Instance_;
40  }
41
42  public static AboutToBeDeleted(): void {
43    if (!PersistentStorage.Instance_) {
44      return;
45    }
46
47    PersistentStorage.GetOrCreate().aboutToBeDeleted();
48    PersistentStorage.Instance_ = undefined;
49  }
50
51  public static PersistProp<T>(key: string, defaultValue: T): void {
52    PersistentStorage.GetOrCreate().persistProp(key, defaultValue);
53  }
54
55  public static DeleteProp(key: string): void {
56    PersistentStorage.GetOrCreate().deleteProp(key);
57  }
58
59  public static PersistProps(properties: {
60    key: string,
61    defaultValue: any
62  }[]): void {
63    PersistentStorage.GetOrCreate().persistProps(properties);
64  }
65
66  public static Keys(): Array<string> {
67    let result = [];
68    const it = PersistentStorage.GetOrCreate().keys();
69    let val = it.next();
70
71    while (!val.done) {
72      result.push(val.value);
73      val = it.next();
74    }
75
76    return result;
77  }
78
79  private constructor() {
80    this.links_ = new Map<string, ObservedPropertyAbstract<any>>();
81    this.id_ = SubscriberManager.Get().MakeId();
82    SubscriberManager.Get().add(this);
83  }
84
85  private keys(): IterableIterator<string> {
86    return this.links_.keys();
87  }
88
89  private persistProp<T>(propName: string, defaultValue: T): void {
90    if (this.persistProp1(propName, defaultValue)) {
91      // persist new prop
92      console.debug(`PersistentStorage: writing '${propName}' - '${this.links_.get(propName)}' to storage`);
93      PersistentStorage.Storage_.set(propName, JSON.stringify(this.links_.get(propName).get()));
94    }
95  }
96
97
98  // helper function to persist a property
99  // does everything except writing prop to disk
100  private persistProp1<T>(propName: string, defaultValue: T): boolean {
101    if (defaultValue == null || defaultValue == undefined) {
102      console.error(`PersistentStorage: persistProp for ${propName} called with 'null' or 'undefined' default value!`);
103      return false;
104    }
105
106    if (this.links_.get(propName)) {
107      console.warn(`PersistentStorage: persistProp: ${propName} is already persisted`);
108      return false;
109    }
110
111    let link = AppStorage.GetOrCreate().link(propName, this);
112    if (link) {
113      console.debug(`PersistentStorage: persistProp ${propName} in AppStorage, using that`);
114      this.links_.set(propName, link);
115    } else {
116      let newValue: string = PersistentStorage.Storage_.get(propName);
117      let returnValue: T;
118      if (!newValue || newValue == "") {
119        console.debug(`PersistentStorage: no entry for ${propName}, will initialize with default value`);
120        returnValue = defaultValue;
121      }
122      try {
123        returnValue = JSON.parse(newValue);
124      } catch (error) {
125        console.error(`PersistentStorage: convert for ${propName} has error: ` + error.toString());
126      }
127      link = AppStorage.GetOrCreate().setAndLink(propName, returnValue, this);
128      this.links_.set(propName, link);
129      console.debug(`PersistentStorage: created new persistent prop for ${propName}`);
130    }
131    return true;
132  }
133
134  private persistProps(properties: {
135    key: string,
136    defaultValue: any
137  }[]): void {
138    properties.forEach(property => this.persistProp1(property.key, property.defaultValue));
139    this.write();
140  }
141
142  private deleteProp(propName: string): void {
143    let link = this.links_.get(propName);
144    if (link) {
145      link.aboutToBeDeleted();
146      this.links_.delete(propName);
147      PersistentStorage.Storage_.delete(propName);
148      console.debug(`PersistentStorage: deleteProp: no longer persisting '${propName}'.`);
149    } else {
150      console.warn(`PersistentStorage: '${propName}' is not a persisted property warning.`);
151    }
152  }
153
154  private write(): void {
155    this.links_.forEach((link, propName, map) => {
156      console.debug(`PersistentStorage: writing ${propName} to storage`);
157      PersistentStorage.Storage_.set(propName, JSON.stringify(link.get()));
158    });
159  }
160
161  public propertyHasChanged(info?: PropertyInfo): void {
162    console.debug("PersistentStorage: property changed");
163    this.write();
164  }
165
166  // public required by the interface, use the static method instead!
167  public aboutToBeDeleted(): void {
168    console.debug("PersistentStorage: about to be deleted");
169    this.links_.forEach((val, key, map) => {
170      console.debug(`PersistentStorage: removing ${key}`);
171      val.aboutToBeDeleted();
172    });
173
174    this.links_.clear();
175    SubscriberManager.Get().delete(this.id__());
176    PersistentStorage.Storage_.clear();
177  }
178
179  public id__(): number {
180    return this.id_;
181  }
182
183  /**
184  * This methid offers a way to force writing the property value with given
185  * key to persistent storage.
186  * In the general case this is unnecessary as the framework observed changes
187  * and triggers writing to disk by itself. For nested objects (e.g. array of
188  * objects) however changes of a property of a property as not observed. This
189  * is the case where the application needs to signal to the framework.
190  * @param key property that has changed
191  */
192  public static NotifyHasChanged(propName: string) {
193    console.debug(`PersistentStorage: force writing '${propName}'-
194        '${PersistentStorage.GetOrCreate().links_.get(propName)}' to storage`);
195    PersistentStorage.Storage_.set(propName,
196      JSON.stringify(PersistentStorage.GetOrCreate().links_.get(propName).get()));
197  }
198};
199