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> { 36 protected subscribers_: Set<number> 37 private id_: number; 38 protected info_?: PropertyInfo; 39 40 constructor(subscribeMe?: IPropertySubscriber, info?: PropertyInfo) { 41 super(); 42 this.subscribers_ = new Set<number>(); 43 this.id_ = SubscriberManager.MakeId(); 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 abstract get(): T; 72 73 // Partial Update "*PU" classes will overwrite 74 public getUnmonitored(): T { 75 return this.get(); 76 } 77 78 public abstract set(newValue: T): void; 79 80 81 public subscribeMe(subscriber: ISinglePropertyChangeSubscriber<T>): void { 82 stateMgmtConsole.debug(`ObservedPropertyAbstract[${this.id__()}, '${this.info() || "unknown"}']: subscribeMe: Property new subscriber '${subscriber.id__()}'`); 83 this.subscribers_.add(subscriber.id__()); 84 } 85 86 /* 87 the inverse function of createOneWaySync or createTwoWaySync 88 */ 89 public unlinkSuscriber(subscriberId: number): void { 90 this.subscribers_.delete(subscriberId); 91 } 92 93 protected notifyHasChanged(newValue: T) { 94 stateMgmtConsole.debug(`ObservedPropertyAbstract[${this.id__()}, '${this.info() || "unknown"}']: notifyHasChanged, notifying.`); 95 this.subscribers_.forEach((subscribedId) => { 96 var subscriber: IPropertySubscriber = SubscriberManager.Find(subscribedId) 97 if (subscriber) { 98 // FU code path 99 if ('hasChanged' in subscriber) { 100 (subscriber as ISinglePropertyChangeSubscriber<T>).hasChanged(newValue); 101 } 102 if ('propertyHasChanged' in subscriber) { 103 (subscriber as IMultiPropertiesChangeSubscriber).propertyHasChanged(this.info_); 104 } 105 106 // PU code path, only used for ObservedPropertySimple/Object stored inside App/LocalStorage 107 // ObservedPropertySimplePU/ObjectPU used in all other PU cases, has its own notifyPropertyHasChangedPU() 108 if ('syncPeerHasChanged' in subscriber) { 109 (subscriber as unknown as PeerChangeEventReceiverPU<T>).syncPeerHasChanged(this as unknown as ObservedPropertyAbstractPU<T>); 110 } 111 } else { 112 stateMgmtConsole.warn(`ObservedPropertyAbstract[${this.id__()}, '${this.info() || "unknown"}']: notifyHasChanged: unknown subscriber ID '${subscribedId}' error!`); 113 } 114 }); 115 } 116 117 protected notifyPropertyRead() { 118 stateMgmtConsole.debug(`ObservedPropertyAbstract[${this.id__()}, '${this.info() || "unknown"}']: propertyRead.`) 119 this.subscribers_.forEach((subscribedId) => { 120 var subscriber: IPropertySubscriber = SubscriberManager.Find(subscribedId) 121 if (subscriber) { 122 if ('propertyRead' in subscriber) { 123 (subscriber as IMultiPropertiesReadSubscriber).propertyRead(this.info_); 124 } 125 } 126 }); 127 } 128 129 /* 130 return numebr of subscribers to this property 131 mostly useful for unit testin 132 */ 133 public numberOfSubscrbers(): number { 134 return this.subscribers_.size; 135 } 136 137 138 /** 139 * These functions are meant for use in connection with the App Stoage and 140 * business logic implementation. 141 * the created Link and Prop will update when 'this' property value 142 * changes. 143 * 144 * These functions are depreciated and will be removed 145 * these are replaced with createSync 146 */ 147 public abstract createLink(subscribeOwner?: IPropertySubscriber, 148 linkPropName?: PropertyInfo): ObservedPropertyAbstract<T>; 149 public abstract createProp(subscribeOwner?: IPropertySubscriber, 150 linkPropName?: PropertyInfo): ObservedPropertyAbstract<T>; 151 152 /** 153 * provide a factory function that creates a SynchedPropertyXXXX of choice 154 * that uses 'this' as source 155 * @param factoryFunc 156 * @returns 157 */ 158 public createSync(factoryFunc: SynchedPropertyFactoryFunc): ObservedPropertyAbstract<T> { 159 return factoryFunc<T>(this); 160 } 161 162 /** 163 * depreciated SDK function, not used anywhere by the framework 164 */ 165 public createTwoWaySync(subscribeMe?: IPropertySubscriber, info?: string): SubscribedAbstractProperty<T> { 166 stateMgmtConsole.warn("Using depreciated method 'createTwoWaySync'!") 167 return this.createLink(subscribeMe, info); 168 } 169 170 /** 171 * depreciated SDK function, not used anywhere by the framework 172 */ 173 public createOneWaySync(subscribeMe?: IPropertySubscriber, info?: string): SubscribedAbstractProperty<T> { 174 stateMgmtConsole.warn("Using depreciated method 'createOneWaySync' !") 175 return this.createProp(subscribeMe, info); 176 } 177 178 /** 179 * factory function for concrete 'object' or 'simple' ObservedProperty object 180 * depending if value is Class object 181 * or simple type (boolean | number | string) 182 * @param value 183 * @param owningView 184 * @param thisPropertyName 185 * @returns either 186 */ 187 static CreateObservedObject<C>(value: C, owningView: IPropertySubscriber, thisPropertyName: PropertyInfo) 188 : ObservedPropertyAbstract<C> { 189 return (typeof value === "object") ? 190 new ObservedPropertyObject(value, owningView, thisPropertyName) 191 : new ObservedPropertySimple(value, owningView, thisPropertyName); 192 } 193} 194