1/* 2 * Copyright (c) 2022 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 * Singleton class SubscriberManager implements IPropertySubscriberLookup 18 * public API to manage IPropertySubscriber 19 */ 20 21class SubscriberManager { 22 23 private subscriberById_: Map<number, WeakRef<IPropertySubscriber>>; 24 25 private static instance_: SubscriberManager; 26 27 private static nextId_: number = 0; 28 29 private static finalizationRegistry_ = new FinalizationRegistry(subscriberId => { 30 SubscriberManager.GetInstance().subscriberById_?.delete(subscriberId); 31 }); 32 33 /** 34 * check subscriber is known 35 * same as ES6 Map.prototype.has() 36 * 37 * @since 9 38 */ 39 public static Has(id: number): boolean { 40 return SubscriberManager.GetInstance().has(id); 41 } 42 43 /** 44 * 45 * retrieve subscriber by id 46 * same as ES6 Map.prototype.get() 47 * 48 * @since 9 49 */ 50 public static Find(id: number): IPropertySubscriber { 51 return SubscriberManager.GetInstance().get(id); 52 } 53 54 /** 55 * unregister a subscriber 56 * same as ES6 Map.prototype.delete() 57 * @return boolean success or failure to delete 58 * 59 * @since 9 60 */ 61 public static Delete(id: number): boolean { 62 return SubscriberManager.GetInstance().delete(id); 63 } 64 65 /** 66 * add a new subscriber. 67 * The subscriber must have a new (unused) id (@see MakeId() ) 68 * for add() to succeed. 69 * same as Map.prototype.set() 70 * 71 * @since 9 72 */ 73 public static Add(newSubsriber: IPropertySubscriber): boolean { 74 return SubscriberManager.GetInstance().add(newSubsriber); 75 } 76 77 /** 78 * Update recycle custom node element id. 79 */ 80 public static UpdateRecycleElmtId(oldId: number, newId: number): boolean { 81 return SubscriberManager.GetInstance().updateRecycleElmtId(oldId, newId); 82 } 83 84 /** 85 * 86 * @returns a globally unique id to be assigned to a IPropertySubscriber objet 87 * Use MakeId() to assign a IPropertySubscriber object an id before calling @see add() . 88 * 89 * @since 9 90 */ 91 public static MakeId(): number { 92 return SubscriberManager.GetInstance().makeId(); 93 } 94 95 /** 96 * 97 * @returns a global unique id for state variables. 98 * Unlike MakeId, no need to get id from native side. 99 * 100 * @since 12 101 */ 102 public static MakeStateVariableId(): number { 103 return SubscriberManager.nextId_--; 104 } 105 106 /** 107 * Check number of registered Subscriber / registered IDs. 108 * @returns number of registered unique ids. 109 * 110 * @since 9 111 */ 112 113 public static NumberOfSubscribers(): number { 114 return SubscriberManager.GetInstance().numberOfSubscribers(); 115 } 116 117 /** 118 * 119 * internal (non-SDK) methods below 120 * 121 */ 122 123 /** 124 * Get singleton, create it on first call 125 * @returns SubscriberManager singleton 126 * 127 * internal function 128 * This function will be removed soon, use static functions instead! 129 * Note: Fnction gets used by transpiler output for both full update and partial update 130 */ 131 public static Get() : SubscriberManager { 132 if (!SubscriberManager.instance_) { 133 SubscriberManager.instance_ = new SubscriberManager(); 134 } 135 return SubscriberManager.instance_; 136 } 137 138 /** 139 * Get singleton, create it on first call 140 * @returns SubscriberManager singleton 141 * 142 * internal function 143 */ 144 private static GetInstance() : SubscriberManager { 145 if (!SubscriberManager.instance_) { 146 SubscriberManager.instance_ = new SubscriberManager(); 147 } 148 return SubscriberManager.instance_; 149 } 150 151 /** 152 * for debug purposes dump all known subscriber's info to comsole 153 * 154 * not a public / sdk function 155 */ 156 public static DumpSubscriberInfo(): void { 157 SubscriberManager.GetInstance().dumpSubscriberInfo(); 158 } 159 160 /** 161 * not a public / sdk function 162 * @see Has 163 */ 164 public has(id: number): boolean { 165 return !!this.subscriberById_.get(id)?.deref(); 166 } 167 168 /** 169 * not a public / sdk function 170 * @see Get 171 */ 172 public get(id: number): IPropertySubscriber { 173 return this.subscriberById_.get(id)?.deref(); 174 } 175 176 /** 177 * not a public / sdk function 178 * @see Delete 179 */ 180 public delete(id: number): boolean { 181 const subscriber = this.get(id); 182 if (!subscriber) { 183 stateMgmtConsole.warn(`SubscriberManager.delete unknown id ${id} `); 184 return false; 185 } 186 SubscriberManager.finalizationRegistry_.unregister(subscriber); 187 return this.subscriberById_.delete(id); 188 } 189 190 /** 191 * not a public / sdk function 192 * @see Add 193 */ 194 public add(newSubscriber: IPropertySubscriber): boolean { 195 if (this.has(newSubscriber.id__())) { 196 return false; 197 } 198 this.set(newSubscriber.id__(), newSubscriber); 199 return true; 200 } 201 202 private set(id: number, subscriber: IPropertySubscriber): void { 203 SubscriberManager.finalizationRegistry_.unregister(subscriber); 204 SubscriberManager.finalizationRegistry_.register(subscriber, id, subscriber); 205 this.subscriberById_.set(id, new WeakRef(subscriber)); 206 } 207 208 public updateRecycleElmtId(oldId: number, newId: number): boolean { 209 if (!this.has(oldId)) { 210 return false; 211 } 212 const subscriber = this.get(oldId); 213 this.delete(oldId); 214 this.set(newId, subscriber); 215 return true; 216 } 217 218 /** 219 * Method for testing purposes 220 * @returns number of subscribers 221 * 222 * not a public / sdk function 223 */ 224 public numberOfSubscribers(): number { 225 return this.subscriberById_.size; 226 } 227 228 /** 229 * for debug purposes dump all known subscriber's info to comsole 230 * 231 * not a public / sdk function 232 */ 233 public dumpSubscriberInfo(): void { 234 stateMgmtConsole.debug('Dump of SubscriberManager +++ (sart)'); 235 for (let [id, weakRef] of this.subscriberById_) { 236 stateMgmtConsole.debug(`Id: ${id} -> ${weakRef.deref()?.['info'] ? weakRef.deref()?.['info']() : 'unknown'}`); 237 } 238 stateMgmtConsole.debug('Dump of SubscriberManager +++ (end)'); 239 } 240 241 /** 242 * 243 * @returns a globally unique id to be assigned to a Subscriber 244 */ 245 makeId(): number { 246 return ViewStackProcessor.MakeUniqueId(); 247 } 248 249 /** 250 * SubscriberManager is a singleton created by the framework 251 * do not use 252 * 253 * internal method 254 */ 255 private constructor() { 256 this.subscriberById_ = new Map<number, WeakRef<IPropertySubscriber>>(); 257 stateMgmtConsole.debug('SubscriberManager has been created.'); 258 } 259} 260