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 Overview of the Observed Property class hiararchy 18 19 ObservedPropertyAbstract 20 |-- ObservedSimplePropertyAbstract - boolean, number, string 21 | |-- ObservedSimpleProperty - owns the property 22 | |-- SynchedSimplePropertyOneWay - one way sync from ObservedSimpleProperty 23 | | |--SynchedPropertySimpleOneWaySubscribing - one way sync 24 | | from ObservedSimpleProperty, return value of AppStorage.prop(..) 25 | |-- SynchedSimplePropertyTwoWay - two way sync with ObservedSimpleProperty 26 | 27 |-- ObservedObjectPropertyAbstract - Object proxied by ObservedObject 28 |-- ObservedObjectProperty - owns the property 29 |-- SynchedObjectPropertyTwoWay - two way sync with ObservedObjectProperty 30 31*/ 32 33/* 34 manage subscriptions to a property 35 managing the property is left to sub 36 classes 37 Extended by ObservedProperty, SyncedPropertyOneWay 38 and SyncedPropertyTwoWay 39*/ 40abstract class ObservedPropertyAbstract<T> { 41 protected subscribers_: Set<number> 42 private id_: number; 43 private info_?: PropertyInfo; 44 45 constructor(subscribeMe?: IPropertySubscriber, info?: PropertyInfo) { 46 this.subscribers_ = new Set<number>(); 47 this.id_ = SubscriberManager.Get().MakeId(); 48 SubscriberManager.Get().add(this); 49 if (subscribeMe) { 50 this.subscribers_.add(subscribeMe.id__()); 51 } 52 if (info) { 53 this.info_ = info; 54 } 55 } 56 57 aboutToBeDeleted() { 58 SubscriberManager.Get().delete(this.id__()) 59 } 60 61 id__(): number { 62 return this.id_; 63 } 64 65 public info(): PropertyInfo { 66 return this.info_; 67 } 68 69 public abstract get(): T; 70 public abstract set(newValue: T): void; 71 72 73 public subscribeMe(subscriber: ISinglePropertyChangeSubscriber<T>): void { 74 console.debug(`ObservedPropertyAbstract[${this.id__()}, '${this.info() || "unknown"}']: subscribeMe: Property new subscriber '${subscriber.id__()}'`); 75 this.subscribers_.add(subscriber.id__()); 76 } 77 78 /* 79 the inverse function of createOneWaySync or createTwoWaySync 80 */ 81 public unlinkSuscriber(subscriberId: number): void { 82 this.subscribers_.delete(subscriberId); 83 } 84 85 protected notifyHasChanged(newValue: T) { 86 //console.debug(`ObservedPropertyAbstract[${this.id()}, '${this.info() || "unknown"}']: notifyHasChanged to newValue '${JSON.stringify(newValue)}', notifying.`) 87 console.debug(`ObservedPropertyAbstract[${this.id__()}, '${this.info() || "unknown"}']: notifyHasChanged, notifying.`); 88 var registry: IPropertySubscriberLookup = SubscriberManager.Get(); 89 this.subscribers_.forEach((subscribedId) => { 90 var subscriber: IPropertySubscriber = registry!.get(subscribedId) 91 if (subscriber) { 92 if ('hasChanged' in subscriber) { 93 (subscriber as ISinglePropertyChangeSubscriber<T>).hasChanged(newValue); 94 } 95 if ('propertyHasChanged' in subscriber) { 96 (subscriber as IMultiPropertiesChangeSubscriber).propertyHasChanged(this.info_); 97 } 98 } else { 99 console.error(`ObservedPropertyAbstract[${this.id__()}, '${this.info() || "unknown"}']: notifyHasChanged: unknown subscriber ID '${subscribedId}' error!`); 100 } 101 }); 102 } 103 104 protected notifyPropertyRead() { 105 console.debug(`ObservedPropertyAbstract[${this.id__()}, '${this.info() || "unknown"}']: propertyRead.`) 106 var registry: IPropertySubscriberLookup = SubscriberManager.Get(); 107 this.subscribers_.forEach((subscribedId) => { 108 var subscriber: IPropertySubscriber = registry!.get(subscribedId) 109 if (subscriber) { 110 if ('propertyRead' in subscriber) { 111 (subscriber as IMultiPropertiesReadSubscriber).propertyRead(this.info_); 112 } 113 } 114 }); 115 } 116 117 /* 118 return numebr of subscribers to this property 119 mostly useful for unit testin 120 */ 121 public numberOfSubscrbers(): number { 122 return this.subscribers_.size; 123 } 124 125 126 /** 127 * These functions are meant for use in connection with the App Stoage and 128 * business logic implementation. 129 * the created Link and Prop will update when 'this' property value 130 * changes. 131 */ 132 public abstract createLink(subscribeOwner?: IPropertySubscriber, 133 linkPropName?: PropertyInfo, contentObserver?: ObservedPropertyAbstract<T>): ObservedPropertyAbstract<T>; 134 public abstract createProp(subscribeOwner?: IPropertySubscriber, 135 linkPropName?: PropertyInfo, contentObserver?: ObservedPropertyAbstract<T>): ObservedPropertyAbstract<T>; 136 137 /** 138 * factory function for concrete 'object' or 'simple' ObservedProperty object 139 * depending if value is Class object 140 * or simple type (boolean | number | string) 141 * @param value 142 * @param owningView 143 * @param thisPropertyName 144 * @returns either 145 */ 146 static CreateObservedObject<C>(value: C, owningView: IPropertySubscriber, thisPropertyName: PropertyInfo) 147 : ObservedPropertyAbstract<C> { 148 return (typeof value === "object") ? 149 new ObservedPropertyObject(value, owningView, thisPropertyName) 150 : new ObservedPropertySimple(value, owningView, thisPropertyName); 151 } 152} 153