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/** 18 * ObservedPropertyAbstract base class for the implementation o all state variables 19 * Its base class SubscribedAbstractProperty<T> provides all public / SDK functions. 20 * 21 * This class and all other definitons in this file are framework 22 * internal / non-SDK. 23 * 24 */ 25 26type SynchedPropertyFactoryFunc = <T>(source: ObservedPropertyAbstract<T>) => ObservedPropertyAbstract<T>; 27 28/* 29 manage subscriptions to a property 30 managing the property is left to sub 31 classes 32 Extended by ObservedProperty, SyncedPropertyOneWay 33 and SyncedPropertyTwoWay 34*/ 35abstract class ObservedPropertyAbstract<T> extends SubscribedAbstractProperty<T> implements AbstractProperty<T> { 36 protected subscribers_: Set<number>; 37 private id_: number; 38 protected info_?: PropertyInfo; 39 protected isFake_: boolean = false; 40 constructor(subscribeMe?: IPropertySubscriber, info?: PropertyInfo) { 41 super(); 42 this.subscribers_ = new Set<number>(); 43 this.id_ = SubscriberManager.MakeStateVariableId(); 44 SubscriberManager.Add(this); 45 if (subscribeMe) { 46 this.subscribers_.add(subscribeMe.id__()); 47 } 48 if (info) { 49 this.info_ = info; 50 } 51 } 52 53 aboutToBeDeleted() { 54 SubscriberManager.Delete(this.id__()); 55 } 56 57 id__(): number { 58 return this.id_; 59 } 60 61 public info(): PropertyInfo { 62 return this.info_; 63 } 64 65 public setInfo(propName: PropertyInfo) { 66 if (propName && propName !== '') { 67 this.info_ = propName; 68 } 69 } 70 71 public __isFake_ObservedPropertyAbstract_Internal(): boolean { 72 return this.isFake_; 73 } 74 75 public __setIsFake_ObservedPropertyAbstract_Internal(isFake: boolean): void { 76 this.isFake_ = isFake; 77 } 78 79 // Partial Update "*PU" classes will overwrite 80 public getUnmonitored(): T { 81 return this.get(); 82 } 83 84 // update the element id for recycle custom component 85 public updateElmtId(oldElmtId: number, newElmtId: number): void { 86 if (this.subscribers_.has(oldElmtId)) { 87 this.subscribers_.delete(oldElmtId); 88 this.subscribers_.add(newElmtId); 89 } 90 } 91 92 // Method name is used to check object is of type ObservedPropertyAbstract 93 // Do NOT override in derived classed, use addSubscriber 94 public subscribeMe(subscriber: ISinglePropertyChangeSubscriber<T>): void { 95 stateMgmtConsole.debug(`ObservedPropertyAbstract[${this.id__()}, '${this.info() || 'unknown'}']: subscribeMe: Property new subscriber '${subscriber.id__()}'`); 96 this.subscribers_.add(subscriber.id__()); 97 } 98 99 /* 100 the inverse function of createOneWaySync or createTwoWaySync 101 Do NOT override in derived classed, use removeSubscriber 102 */ 103 public unlinkSuscriber(subscriberId: number): void { 104 this.subscribers_.delete(subscriberId); 105 } 106 107 /* 108 Virtualized version of the subscription mechanism - add subscriber 109 */ 110 public addSubscriber(subscriber: ISinglePropertyChangeSubscriber<T>):void { 111 if (subscriber) { 112 this.subscribeMe(subscriber); 113 } 114 } 115 116 /* 117 Virtualized version of the subscription mechanism - remove subscriber 118 */ 119 public removeSubscriber(subscriber: IPropertySubscriber, id?: number):void { 120 if (id) { 121 this.unlinkSuscriber(id); 122 } else if (subscriber) { 123 this.unlinkSuscriber(subscriber.id__()); 124 } 125 } 126 127 // FU code path callback 128 protected notifyHasChanged(newValue: T) { 129 stateMgmtProfiler.begin('ObservedPropertyAbstract.notifyHasChanged'); 130 stateMgmtConsole.debug(`ObservedPropertyAbstract[${this.id__()}, '${this.info() || 'unknown'}']: notifyHasChanged, notifying.`); 131 this.subscribers_.forEach((subscribedId) => { 132 let subscriber: IPropertySubscriber = SubscriberManager.Find(subscribedId); 133 if (subscriber) { 134 // FU code path 135 if ('hasChanged' in subscriber) { 136 (subscriber as ISinglePropertyChangeSubscriber<T>).hasChanged(newValue); 137 } 138 if ('propertyHasChanged' in subscriber) { 139 (subscriber as IMultiPropertiesChangeSubscriber).propertyHasChanged(this.info_); 140 } 141 } else { 142 stateMgmtConsole.warn(`ObservedPropertyAbstract[${this.id__()}, '${this.info() || 'unknown'}']: notifyHasChanged: unknown subscriber ID '${subscribedId}' error!`); 143 } 144 }); 145 stateMgmtProfiler.end(); 146 } 147 148 protected notifyPropertyRead() { 149 stateMgmtProfiler.begin('ObservedPropertyAbstract.notifyPropertyRead'); 150 stateMgmtConsole.debug(`ObservedPropertyAbstract[${this.id__()}, '${this.info() || 'unknown'}']: propertyRead.`); 151 this.subscribers_.forEach((subscribedId) => { 152 var subscriber: IPropertySubscriber = SubscriberManager.Find(subscribedId) 153 if (subscriber) { 154 if ('propertyRead' in subscriber) { 155 (subscriber as IMultiPropertiesReadSubscriber).propertyRead(this.info_); 156 } 157 } 158 }); 159 stateMgmtProfiler.end(); 160 } 161 162 /* 163 return numebr of subscribers to this property 164 mostly useful for unit testin 165 */ 166 public numberOfSubscrbers(): number { 167 return this.subscribers_.size; 168 } 169 170 171 /** 172 * These functions are meant for use in connection with the App Stoage and 173 * business logic implementation. 174 * the created Link and Prop will update when 'this' property value 175 * changes. 176 * 177 * These functions are depreciated and will be removed 178 * these are replaced with createSync 179 */ 180 public abstract createLink(subscribeOwner?: IPropertySubscriber, 181 linkPropName?: PropertyInfo): ObservedPropertyAbstract<T>; 182 public abstract createProp(subscribeOwner?: IPropertySubscriber, 183 linkPropName?: PropertyInfo): ObservedPropertyAbstract<T>; 184 185 /** 186 * provide a factory function that creates a SynchedPropertyXXXX of choice 187 * that uses 'this' as source 188 * @param factoryFunc 189 * @returns 190 */ 191 public createSync(factoryFunc: SynchedPropertyFactoryFunc): ObservedPropertyAbstract<T> { 192 return factoryFunc<T>(this); 193 } 194 195 /** 196 * depreciated SDK function, not used anywhere by the framework 197 */ 198 public createTwoWaySync(subscribeMe?: IPropertySubscriber, info?: string): SubscribedAbstractProperty<T> { 199 stateMgmtConsole.warn("Using depreciated method 'createTwoWaySync'!"); 200 return this.createLink(subscribeMe, info); 201 } 202 203 /** 204 * depreciated SDK function, not used anywhere by the framework 205 */ 206 public createOneWaySync(subscribeMe?: IPropertySubscriber, info?: string): SubscribedAbstractProperty<T> { 207 stateMgmtConsole.warn("Using depreciated method 'createOneWaySync' !"); 208 return this.createProp(subscribeMe, info); 209 } 210 211 /** 212 * factory function for concrete 'object' or 'simple' ObservedProperty object 213 * depending if value is Class object 214 * or simple type (boolean | number | string) 215 * @param value 216 * @param owningView 217 * @param thisPropertyName 218 * @returns either 219 */ 220 static CreateObservedObject<C>(value: C, owningView: IPropertySubscriber, thisPropertyName: PropertyInfo) 221 : ObservedPropertyAbstract<C> { 222 return (typeof value === 'object') ? 223 new ObservedPropertyObject(value, owningView, thisPropertyName) 224 : new ObservedPropertySimple(value, owningView, thisPropertyName); 225 } 226} 227