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 ContentStorage { 17 private storage_: Map<string, ObservedPropertyAbstract<any>>; 18 19 constructor() { 20 this.storage_ = new Map<string, ObservedPropertyAbstract<any>>(); 21 } 22 23 public has(propName: string): boolean { 24 return this.storage_.has(propName); 25 } 26 27 public get<T>(key: string): T | undefined { 28 var p: ObservedPropertyAbstract<T> | undefined = this.storage_.get(key); 29 return (p) ? p.get() : undefined; 30 } 31 32 public set<T>(propName: string, newValue: T): boolean { 33 var p: ObservedPropertyAbstract<T> | undefined = this.storage_.get(propName); 34 if (p) { 35 p.set(newValue); 36 return true; 37 } else { 38 return false; 39 } 40 } 41 42 public setOrCreate<T>(propName: string, newValue: T, linkUser?: IPropertySubscriber): void { 43 var p: ObservedPropertyAbstract<T> = this.storage_.get(propName); 44 if (p) { 45 console.log(`ContentStorage.setOrCreate(${propName}, ${newValue}) update existing property`); 46 p.set(newValue); 47 } else { 48 console.log(`ContentStorage.setOrCreate(${propName}, ${newValue}) create new entry and set value`); 49 const newProp = (typeof newValue === "object") ? 50 new ObservedPropertyObject<T>(newValue, linkUser, propName) 51 : new ObservedPropertySimple<T>(newValue, linkUser, propName); 52 this.storage_.set(propName, newProp); 53 } 54 } 55 56 public delete(propName: string): boolean { 57 var p: ObservedPropertyAbstract<any> | undefined = this.storage_.get(propName); 58 if (p) { 59 if (p.numberOfSubscrbers()) { 60 console.error(`Attempt to delete property ${propName} that has ${p.numberOfSubscrbers()} subscribers. Subscribers need to unsubscribe before prop deletion.`); 61 return false; 62 } 63 p.aboutToBeDeleted(); 64 this.storage_.delete(propName); 65 return true; 66 } else { 67 console.warn(`Attempt to delete unknown property ${propName}.`); 68 return false; 69 } 70 } 71 72 public keys(): IterableIterator<string> { 73 return this.storage_.keys(); 74 } 75 76 public size(): number { 77 return this.storage_.size; 78 } 79 80 public aboutToBeDeleted(): boolean { 81 return this.clear(); 82 } 83 84 public numberOfSubscribersTo(propName: string): number | undefined { 85 var p: ObservedPropertyAbstract<any> | undefined = this.storage_.get(propName); 86 if (p) { 87 return p.numberOfSubscrbers(); 88 } 89 return undefined; 90 } 91 92 public subscribeToChangesOf<T>(propName: string, subscriber: ISinglePropertyChangeSubscriber<T>): boolean { 93 var p: ObservedPropertyAbstract<T> | undefined = this.storage_.get(propName); 94 if (p) { 95 p.subscribeMe(subscriber); 96 return true; 97 } 98 return false; 99 } 100 101 public unsubscribeFromChangesOf(propName: string, subscriberId: number): boolean { 102 var p: ObservedPropertyAbstract<any> | undefined = this.storage_.get(propName); 103 if (p) { 104 p.unlinkSuscriber(subscriberId); 105 return true; 106 } 107 return false; 108 } 109 110 public isMutable(key: string): boolean { 111 return true; 112 } 113 114 public clear(): boolean { 115 for (let propName of this.keys()) { 116 var p: ObservedPropertyAbstract<any> = this.storage_.get(propName); 117 if (p.numberOfSubscrbers()) { 118 console.error(`ContentStorage.deleteAll: Attempt to delete property ${propName} that has ${p.numberOfSubscrbers()} subscribers. Subscribers need to unsubscribe before prop deletion.`); 119 return false; 120 } 121 } 122 for (let propName of this.keys()) { 123 var p: ObservedPropertyAbstract<any> = this.storage_.get(propName); 124 p.aboutToBeDeleted(); 125 } 126 console.error(`ContentStorage.deleteAll: success`); 127 } 128 129 public link<T>(propName: string, linkUser?: IPropertySubscriber): ObservedPropertyAbstract<T> | undefined { 130 var p: ObservedPropertyAbstract<T> | undefined = this.storage_.get(propName); 131 return (p) ? p.createLink(linkUser, propName) : undefined 132 } 133 134 public setAndLink<T>(propName: string, defaultValue: T, linkUser?: IPropertySubscriber): ObservedPropertyAbstract<T> { 135 var p: ObservedPropertyAbstract<T> | undefined = this.storage_.get(propName); 136 if (!p) { 137 this.setOrCreate(propName, defaultValue, linkUser); 138 } 139 return this.link(propName, linkUser); 140 } 141 142 public prop<S>(propName: string, propUser?: IPropertySubscriber): ObservedPropertyAbstract<S> | undefined { 143 var p: ObservedPropertyAbstract<S> | undefined = this.storage_.get(propName); 144 return (p) ? p.createProp(propUser, propName) : undefined 145 } 146 147 public setAndProp<S>(propName: string, defaultValue: S, propUser?: IPropertySubscriber): ObservedPropertyAbstract<S> { 148 var p: ObservedPropertyAbstract<S> | undefined = this.storage_.get(propName); 149 150 if (!p) { 151 if (typeof defaultValue === "boolean" || 152 typeof defaultValue === "number" || typeof defaultValue === "string") { 153 this.setOrCreate(propName, defaultValue, propUser); 154 } else { 155 return undefined; 156 } 157 } 158 return this.prop(propName, propUser); 159 } 160}