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 * SynchedPropertyObjectTwoWayPU 18 * implementation of @Link and @Consume decorated variables of type class object 19 * 20 * all definitions in this file are framework internal 21 */ 22class SynchedPropertyObjectTwoWayPU<C extends Object> 23 extends ObservedPropertyObjectAbstractPU<C> 24 implements PeerChangeEventReceiverPU<C>, 25 ObservedObjectEventsPUReceiver<C> { 26 27 private linkedParentProperty_: ObservedPropertyObjectAbstract<C>; 28 private changeNotificationIsOngoing_: boolean = false; 29 30 constructor(linkSource: ObservedPropertyObjectAbstract<C>, 31 owningChildView: IPropertySubscriber, 32 thisPropertyName: PropertyInfo) { 33 super(owningChildView, thisPropertyName); 34 this.linkedParentProperty_ = linkSource; 35 if (this.linkedParentProperty_) { 36 // register to the parent property 37 this.linkedParentProperty_.subscribeMe(this); 38 } 39 40 // register to the ObservedObject 41 ObservedObject.addOwningProperty(this.linkedParentProperty_.get(), this); 42 } 43 44 /* 45 like a destructor, need to call this before deleting 46 the property. 47 */ 48 aboutToBeDeleted() { 49 // unregister from parent of this link 50 if (this.linkedParentProperty_) { 51 this.linkedParentProperty_.unlinkSuscriber(this.id__()); 52 53 // unregister from the ObservedObject 54 ObservedObject.removeOwningProperty(this.linkedParentProperty_.getUnmonitored(), this); 55 } 56 super.aboutToBeDeleted(); 57 } 58 59 private setObject(newValue: C): void { 60 if (!this.linkedParentProperty_) { 61 stateMgmtConsole.warn(`SynchedPropertyObjectTwoWayPU[${this.id__()}, '${this.info() || "unknown"}']: setObject, no linked parent property.`); 62 return; 63 } 64 this.linkedParentProperty_.set(newValue) 65 } 66 67 /** 68 * Called when sync peer ObservedPropertyObject or SynchedPropertyObjectTwoWay has chnaged value 69 * that peer can be in either parent or child component if 'this' is used for a @Link 70 * that peer can be in either acestor or descendant component if 'this' is used for a @Consume 71 * @param eventSource 72 */ 73 syncPeerHasChanged(eventSource : ObservedPropertyAbstractPU<C>) { 74 if (!this.changeNotificationIsOngoing_) { 75 stateMgmtConsole.debug(`SynchedPropertyObjectTwoWayPU[${this.id__()}, '${this.info() || "unknown"}']: propertyHasChangedPU: contained ObservedObject '${eventSource.info()}' hasChanged'.`) 76 this.notifyPropertyHasChangedPU(); 77 } 78 } 79 80 /** 81 * called when wrapped ObservedObject has changed poperty 82 * @param souceObject 83 * @param changedPropertyName 84 */ 85 public objectPropertyHasChangedPU(souceObject: ObservedObject<C>, changedPropertyName : string) { 86 stateMgmtConsole.debug(`SynchedPropertyObjectTwoWayPU[${this.id__()}, '${this.info() || "unknown"}']: \ 87 objectPropertyHasChangedPU: contained ObservedObject property '${changedPropertyName}' has changed.`) 88 this.notifyPropertyHasChangedPU(); 89 } 90 91 public objectPropertyHasBeenReadPU(souceObject: ObservedObject<C>, changedPropertyName : string) { 92 stateMgmtConsole.debug(`SynchedPropertyObjectTwoWayPU[${this.id__()}, '${this.info() || "unknown"}']: \ 93 objectPropertyHasBeenReadPU: contained ObservedObject property '${changedPropertyName}' has been read.`); 94 this.notifyPropertyHasBeenReadPU(); 95 } 96 97 public getUnmonitored(): C { 98 stateMgmtConsole.debug(`SynchedPropertyObjectTwoWayPU[${this.id__()}, '${this.info() || "unknown"}']: getUnmonitored returns '${(this.linkedParentProperty_ ? JSON.stringify(this.linkedParentProperty_.getUnmonitored()) : "undefined")}' .`); 99 // unmonitored get access , no call to otifyPropertyRead ! 100 return (this.linkedParentProperty_ ? this.linkedParentProperty_.getUnmonitored() : undefined); 101 } 102 103 // get 'read through` from the ObservedProperty 104 public get(): C { 105 stateMgmtConsole.debug(`SynchedPropertyObjectTwoWayPU[${this.id__()}, '${this.info() || "unknown"}']: get`) 106 this.notifyPropertyHasBeenReadPU() 107 return this.getUnmonitored(); 108 } 109 110 // set 'writes through` to the ObservedProperty 111 public set(newValue: C): void { 112 if (this.getUnmonitored() == newValue) { 113 stateMgmtConsole.debug(`SynchedPropertyObjectTwoWayPU[${this.id__()}IP, '${this.info() || "unknown"}']: set with unchanged value '${newValue}'- ignoring.`); 114 return; 115 } 116 117 stateMgmtConsole.debug(`SynchedPropertyObjectTwoWayPU[${this.id__()}, '${this.info() || "unknown"}']: set to newValue: '${newValue}'.`); 118 119 ObservedObject.removeOwningProperty(this.getUnmonitored(), this); 120 121 // avoid circular notifications @Link -> source @State -> other but also back to same @Link 122 this.changeNotificationIsOngoing_ = true; 123 this.setObject(newValue); 124 ObservedObject.addOwningProperty(this.getUnmonitored(), this); 125 this.notifyPropertyHasChangedPU(); 126 this.changeNotificationIsOngoing_ = false; 127 } 128} 129