• 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
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   * persistemt.
73   * If AppStorage does not include this property it will be added and initializes
74   * with given value
75   *
76   * @since 9
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   * Reverse of @see PersistProp
88   * @param key no longer persist the property named key
89   *
90   * @since 9
91   */
92  public static DeleteProp(key: string): void {
93    PersistentStorage.GetOrCreate().deleteProp(key);
94  }
95
96  /**
97   * Persist given AppStorage properties with given names.
98   * If a property does not exist in AppStorage, add it and initialize it with given value
99   * works as @see PersistProp for multiple properties.
100   *
101   * @param properties
102   *
103   * @since 9
104   *
105   */
106  public static PersistProps(properties: {
107    key: string,
108    defaultValue: any
109  }[]): void {
110    PersistentStorage.GetOrCreate().persistProps(properties);
111  }
112
113  /**
114   * Inform persisted AppStorage property names
115   * @returns array of AppStorage keys
116   *
117   * @since 9
118   */
119  public static Keys(): Array<string> {
120    let result = [];
121    const it = PersistentStorage.GetOrCreate().keys();
122    let val = it.next();
123
124    while (!val.done) {
125      result.push(val.value);
126      val = it.next();
127    }
128
129    return result;
130  }
131
132/**
133  * This methid offers a way to force writing the property value with given
134  * key to persistent storage.
135  * In the general case this is unnecessary as the framework observed changes
136  * and triggers writing to disk by itself. For nested objects (e.g. array of
137  * objects) however changes of a property of a property as not observed. This
138  * is the case where the application needs to signal to the framework.
139  *
140  * @param key property that has changed
141  *
142  * @since 9
143  *
144  */
145 public static NotifyHasChanged(propName: string) {
146  stateMgmtConsole.debug(`PersistentStorage: force writing '${propName}'-
147      '${PersistentStorage.GetOrCreate().links_.get(propName)}' to storage`);
148  PersistentStorage.Storage_.set(propName,
149    PersistentStorage.GetOrCreate().links_.get(propName).get());
150}
151  /**
152   * all following methods are framework internal
153   */
154
155  private constructor() {
156    this.links_ = new Map<string, SubscribedAbstractProperty<any>>();
157    this.id_ = SubscriberManager.MakeId();
158    SubscriberManager.Add(this);
159  }
160
161  private keys(): IterableIterator<string> {
162    return this.links_.keys();
163  }
164
165  private persistProp<T>(propName: string, defaultValue: T): void {
166    if (this.persistProp1(propName, defaultValue)) {
167      // persist new prop
168      stateMgmtConsole.debug(`PersistentStorage: writing '${propName}' - '${this.links_.get(propName)}' to storage`);
169      PersistentStorage.Storage_.set(propName, this.links_.get(propName).get());
170    }
171  }
172
173
174  // helper function to persist a property
175  // does everything except writing prop to disk
176  private persistProp1<T>(propName: string, defaultValue: T): boolean {
177    if (defaultValue == null || defaultValue == undefined) {
178      stateMgmtConsole.error(`PersistentStorage: persistProp for ${propName} called with 'null' or 'undefined' default value!`);
179      return false;
180    }
181
182    if (this.links_.get(propName)) {
183      stateMgmtConsole.warn(`PersistentStorage: persistProp: ${propName} is already persisted`);
184      return false;
185    }
186
187    let link = AppStorage.Link(propName, this);
188    if (link) {
189      stateMgmtConsole.debug(`PersistentStorage: persistProp ${propName} in AppStorage, using that`);
190      this.links_.set(propName, link);
191    } else {
192      let newValue: T = PersistentStorage.Storage_.get(propName);
193      let returnValue: T;
194      if (!newValue) {
195        stateMgmtConsole.debug(`PersistentStorage: no entry for ${propName}, will initialize with default value`);
196        returnValue = defaultValue;
197      }
198      else {
199        returnValue = newValue;
200      }
201      link = AppStorage.SetAndLink(propName, returnValue, this);
202      this.links_.set(propName, link);
203      stateMgmtConsole.debug(`PersistentStorage: created new persistent prop for ${propName}`);
204    }
205    return true;
206  }
207
208  private persistProps(properties: {
209    key: string,
210    defaultValue: any
211  }[]): void {
212    properties.forEach(property => this.persistProp1(property.key, property.defaultValue));
213    this.write();
214  }
215
216  private deleteProp(propName: string): void {
217    let link = this.links_.get(propName);
218    if (link) {
219      link.aboutToBeDeleted();
220      this.links_.delete(propName);
221      PersistentStorage.Storage_.delete(propName);
222      stateMgmtConsole.debug(`PersistentStorage: deleteProp: no longer persisting '${propName}'.`);
223    } else {
224      stateMgmtConsole.warn(`PersistentStorage: '${propName}' is not a persisted property warning.`);
225    }
226  }
227
228  private write(): void {
229    this.links_.forEach((link, propName, map) => {
230      stateMgmtConsole.debug(`PersistentStorage: writing ${propName} to storage`);
231      PersistentStorage.Storage_.set(propName, link.get());
232    });
233  }
234
235  public propertyHasChanged(info?: PropertyInfo): void {
236    stateMgmtConsole.debug("PersistentStorage: property changed");
237    this.write();
238  }
239
240  // public required by the interface, use the static method instead!
241  public aboutToBeDeleted(): void {
242    stateMgmtConsole.debug("PersistentStorage: about to be deleted");
243    this.links_.forEach((val, key, map) => {
244      stateMgmtConsole.debug(`PersistentStorage: removing ${key}`);
245      val.aboutToBeDeleted();
246    });
247
248    this.links_.clear();
249    SubscriberManager.Delete(this.id__());
250    PersistentStorage.Storage_.clear();
251  }
252
253  public id__(): number {
254    return this.id_;
255  }
256};
257
258