• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2021-2023 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
16/**
17 * PersistentStorage
18 *
19 * Keeps current values of select AppStorage property properties persisted to file.
20 *
21 * since 9
22 */
23
24class PersistentStorage implements IMultiPropertiesChangeSubscriber {
25  private static storage_: IStorage;
26  private static instance_: PersistentStorage = undefined;
27
28  private id_: number;
29  private links_: Map<string, SubscribedAbstractProperty<any>>;
30
31  /**
32   *
33   * @param storage method to be used by the framework to set the backend
34   * this is to be done during startup
35   *
36   * internal function, not part of the SDK
37   *
38   */
39  public static configureBackend(storage: IStorage): void {
40    PersistentStorage.storage_ = storage;
41  }
42
43  /**
44   * private, use static functions!
45   */
46  private static getOrCreate(): PersistentStorage {
47    if (PersistentStorage.instance_) {
48      // already initialized
49      return PersistentStorage.instance_;
50    }
51
52    PersistentStorage.instance_ = new PersistentStorage();
53    return PersistentStorage.instance_;
54  }
55
56  /**
57   *
58   * internal function, not part of the SDK
59   */
60  public static aboutToBeDeleted(): void {
61    if (!PersistentStorage.instance_) {
62      return;
63    }
64
65    PersistentStorage.getOrCreate().aboutToBeDeleted();
66    PersistentStorage.instance_ = undefined;
67  }
68
69
70  /**
71   * Add property 'key' to AppStorage properties whose current value will be
72   * persistent.
73   * If AppStorage does not include this property it will be added and initializes
74   * with given value
75   *
76   * @since 10
77   *
78   * @param key property name
79   * @param defaultValue If AppStorage does not include this property it will be initialized with this value
80   *
81   */
82  public static persistProp<T>(key: string, defaultValue: T): void {
83    PersistentStorage.getOrCreate().persistProp(key, defaultValue);
84  }
85
86  /**
87   * @see persistProp
88   * @deprecated
89   */
90  public static PersistProp<T>(key: string, defaultValue: T): void {
91    PersistentStorage.getOrCreate().persistProp(key, defaultValue);
92  }
93
94
95  /**
96   * Reverse of @see persistProp
97   * @param key no longer persist the property named key
98   *
99   * @since 10
100   */
101  public static deleteProp(key: string): void {
102    PersistentStorage.getOrCreate().deleteProp(key);
103  }
104
105  /**
106   * @see deleteProp
107   * @deprecated
108   */
109  public static DeleteProp(key: string): void {
110    PersistentStorage.getOrCreate().deleteProp(key);
111  }
112
113  /**
114   * Persist given AppStorage properties with given names.
115   * If a property does not exist in AppStorage, add it and initialize it with given value
116   * works as @see persistProp for multiple properties.
117   *
118   * @param properties
119   *
120   * @since 10
121   *
122   */
123  public static persistProps(properties: {
124    key: string,
125    defaultValue: any
126  }[]): void {
127    PersistentStorage.getOrCreate().persistProps(properties);
128  }
129
130  /**
131   * @see persistProps
132   * @deprecated
133   */
134  public static PersistProps(properties: {
135    key: string,
136    defaultValue: any
137  }[]): void {
138    PersistentStorage.getOrCreate().persistProps(properties);
139  }
140
141  /**
142   * Inform persisted AppStorage property names
143   * @returns array of AppStorage keys
144   *
145   * @since 10
146   */
147  public static keys(): Array<string> {
148    let result = [];
149    const it = PersistentStorage.getOrCreate().keys();
150    let val = it.next();
151
152    while (!val.done) {
153      result.push(val.value);
154      val = it.next();
155    }
156
157    return result;
158  }
159
160  /**
161   * @see keys
162   * @deprecated
163   */
164  public static Keys(): Array<string> {
165    return PersistentStorage.keys();
166  }
167
168/**
169  * This methid offers a way to force writing the property value with given
170  * key to persistent storage.
171  * In the general case this is unnecessary as the framework observed changes
172  * and triggers writing to disk by itself. For nested objects (e.g. array of
173  * objects) however changes of a property of a property as not observed. This
174  * is the case where the application needs to signal to the framework.
175  *
176  * @param key property that has changed
177  *
178  * @since 10
179  *
180  */
181  public static notifyHasChanged(propName: string) {
182  stateMgmtConsole.debug(`PersistentStorage: force writing '${propName}'-
183      '${PersistentStorage.getOrCreate().links_.get(propName)}' to storage`);
184  PersistentStorage.storage_.set(propName,
185    PersistentStorage.getOrCreate().links_.get(propName).get());
186  }
187
188 /**
189  * @see notifyHasChanged
190  * @deprecated
191  */
192  public static NotifyHasChanged(propName: string) {
193    stateMgmtConsole.debug(`PersistentStorage: force writing '${propName}'-
194        '${PersistentStorage.getOrCreate().links_.get(propName)}' to storage`);
195    PersistentStorage.storage_.set(propName,
196      PersistentStorage.getOrCreate().links_.get(propName).get());
197  }
198
199  /**
200   * all following methods are framework internal
201   */
202
203  private constructor() {
204    this.links_ = new Map<string, SubscribedAbstractProperty<any>>();
205    this.id_ = SubscriberManager.MakeId();
206    SubscriberManager.Add(this);
207  }
208
209  private keys(): IterableIterator<string> {
210    return this.links_.keys();
211  }
212
213  private persistProp<T>(propName: string, defaultValue: T): void {
214    if (this.persistProp1(propName, defaultValue)) {
215      // persist new prop
216      stateMgmtConsole.debug(`PersistentStorage: writing '${propName}' - '${this.links_.get(propName)}' to storage`);
217      PersistentStorage.storage_.set(propName, this.links_.get(propName).get());
218    }
219  }
220
221
222  // helper function to persist a property
223  // does everything except writing prop to disk
224  private persistProp1<T>(propName: string, defaultValue: T): boolean {
225    stateMgmtConsole.debug(`PersistentStorage: persistProp1 ${propName} ${defaultValue}`);
226    if (defaultValue == null || defaultValue == undefined) {
227      stateMgmtConsole.error(`PersistentStorage: persistProp for ${propName} called with 'null' or 'undefined' default value!`);
228      return false;
229    }
230
231    if (this.links_.get(propName)) {
232      stateMgmtConsole.warn(`PersistentStorage: persistProp: ${propName} is already persisted`);
233      return false;
234    }
235
236    let link = AppStorage.link(propName, this);
237    if (link) {
238      stateMgmtConsole.debug(`PersistentStorage: persistProp ${propName} in AppStorage, using that`);
239      this.links_.set(propName, link);
240    } else {
241      let newValue: T = PersistentStorage.storage_.get(propName);
242      let returnValue: T;
243      if (newValue == undefined || newValue == null) {
244        stateMgmtConsole.debug(`PersistentStorage: no entry for ${propName}, will initialize with default value`);
245        returnValue = defaultValue;
246      }
247      else {
248        returnValue = newValue;
249      }
250      link = AppStorage.setAndLink(propName, returnValue, this);
251      this.links_.set(propName, link);
252      stateMgmtConsole.debug(`PersistentStorage: created new persistent prop for ${propName}`);
253    }
254    return true;
255  }
256
257  private persistProps(properties: {
258    key: string,
259    defaultValue: any
260  }[]): void {
261    properties.forEach(property => this.persistProp1(property.key, property.defaultValue));
262    this.write();
263  }
264
265  private deleteProp(propName: string): void {
266    let link = this.links_.get(propName);
267    if (link) {
268      link.aboutToBeDeleted();
269      this.links_.delete(propName);
270      PersistentStorage.storage_.delete(propName);
271      stateMgmtConsole.debug(`PersistentStorage: deleteProp: no longer persisting '${propName}'.`);
272    } else {
273      stateMgmtConsole.warn(`PersistentStorage: '${propName}' is not a persisted property warning.`);
274    }
275  }
276
277  private write(): void {
278    this.links_.forEach((link, propName, map) => {
279      stateMgmtConsole.debug(`PersistentStorage: writing ${propName} to storage`);
280      PersistentStorage.storage_.set(propName, link.get());
281    });
282  }
283
284  public propertyHasChanged(info?: PropertyInfo): void {
285    stateMgmtConsole.debug("PersistentStorage: property changed");
286    this.write();
287  }
288
289  public syncPeerHasChanged(eventSource: ObservedPropertyAbstractPU<any>) {
290    stateMgmtConsole.debug(`PersistentStorage: sync peer ${eventSource.info()} has changed`);
291    this.write();
292  }
293
294  // public required by the interface, use the static method instead!
295  public aboutToBeDeleted(): void {
296    stateMgmtConsole.debug("PersistentStorage: about to be deleted");
297    this.links_.forEach((val, key, map) => {
298      stateMgmtConsole.debug(`PersistentStorage: removing ${key}`);
299      val.aboutToBeDeleted();
300    });
301
302    this.links_.clear();
303    SubscriberManager.Delete(this.id__());
304    PersistentStorage.storage_.clear();
305  }
306
307  public id__(): number {
308    return this.id_;
309  }
310};
311
312