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 * SynchedPropertyNestedObjectPU 18 * implementation of @ObjectLink decorated variables 19 * 20 * all definitions in this file are framework internal 21 * 22 */ 23class SynchedPropertyNestedObjectPU<C extends Object> 24 extends ObservedPropertyAbstractPU<C> 25 implements ObservedObjectEventsPUReceiver<C> { 26 27 private obsObject_: C; 28 29 /** 30 * Construct a Property of a su component that links to a variable of parent view that holds an ObservedObject 31 * example 32 * this.b.$a with b of type PC and a of type C, or 33 * this.$b[5] with this.b of type PC and array item b[5] of type C; 34 * 35 * @param subscribeMe 36 * @param propName 37 */ 38 constructor(obsObject: C, 39 owningChildView: IPropertySubscriber, propertyName: PropertyInfo) { 40 super(owningChildView, propertyName); 41 this.obsObject_ = obsObject; 42 this.createSourceDependency(obsObject); 43 this.setValueInternal(obsObject); 44 } 45 46 /* 47 like a destructor, need to call this before deleting 48 the property. 49 */ 50 aboutToBeDeleted() { 51 // unregister from the ObservedObject 52 ObservedObject.removeOwningProperty(this.obsObject_, this); 53 super.aboutToBeDeleted(); 54 } 55 56 public debugInfoDecorator() : string { 57 return `@ObjectLink (class SynchedPropertyNestedObjectPU)`; 58 } 59 60 public getUnmonitored(): C { 61 stateMgmtConsole.propertyAccess(`${this.debugInfo()}: getUnmonitored.`); 62 // unmonitored get access , no call to notifyPropertyRead ! 63 return this.obsObject_; 64 } 65 66 // get 'read through` from the ObservedProperty 67 public get(): C { 68 stateMgmtProfiler.begin("SynchedPropertyNestedObjectPU.get"); 69 stateMgmtConsole.propertyAccess(`${this.debugInfo()}: get`) 70 this.recordPropertyDependentUpdate(); 71 if (this.shouldInstallTrackedObjectReadCb) { 72 stateMgmtConsole.propertyAccess(`${this.debugInfo()}: get: @Track optimised mode. Will install read cb func if value is an object`); 73 ObservedObject.registerPropertyReadCb(this.obsObject_, this.onOptimisedObjectPropertyRead.bind(this)); 74 } else { 75 stateMgmtConsole.propertyAccess(`${this.debugInfo()}: get: compatibility mode. `); 76 } 77 stateMgmtProfiler.end(); 78 return this.obsObject_; 79 } 80 81 // parent ViewPU rerender, runs update lambda with child ViewPU that contains a @ObjectLink 82 // calls ViewPU.updateStateVarsByElmtId, calls updateStateVars in application class, calls this 'set' function 83 public set(newValue: C): void { 84 if (this.obsObject_ === newValue) { 85 stateMgmtConsole.debug(`SynchedPropertyNestedObjectPU[${this.id__()}IP, '${this.info() || "unknown"}']: set @ObjectLink with unchanged value - nothing to do.`); 86 return; 87 } 88 89 stateMgmtConsole.propertyAccess(`${this.debugInfo()}: set: value about to change.`); 90 const oldValue = this.obsObject_; 91 if (this.setValueInternal(newValue)) { 92 this.createSourceDependency(newValue); 93 // notify value change to subscribing View 94 TrackedObject.notifyObjectValueAssignment(/* old value */ oldValue, /* new value */ this.obsObject_, 95 this.notifyPropertyHasChangedPU.bind(this), 96 this.notifyTrackedObjectPropertyHasChanged.bind(this)); 97 } 98 } 99 100 protected onOptimisedObjectPropertyRead(readObservedObject: C, readPropertyName: string, isTracked: boolean): void { 101 stateMgmtProfiler.begin("SynchedPropertyNestedObjectPU.onOptimisedObjectPropertyRead"); 102 const renderingElmtId = this.getRenderingElmtId(); 103 if (renderingElmtId >= 0) { 104 if (!isTracked) { 105 stateMgmtConsole.applicationError(`${this.debugInfo()}: onOptimisedObjectPropertyRead read NOT TRACKED property '${readPropertyName}' during rendering!`); 106 throw new Error(`Illegal usage of not @Track'ed property '${readPropertyName}' on UI!`); 107 } else { 108 stateMgmtConsole.debug(`${this.debugInfo()}: onOptimisedObjectPropertyRead: ObservedObject property '@Track ${readPropertyName}' read.`); 109 if (this.getUnmonitored() === readObservedObject) { 110 this.recordTrackObjectPropertyDependencyForElmtId(renderingElmtId, readPropertyName) 111 } 112 } 113 } 114 stateMgmtProfiler.end(); 115 } 116 117 private createSourceDependency(sourceObject: C): void { 118 if (ObservedObject.IsObservedObject(sourceObject)) { 119 stateMgmtConsole.debug(`${this.debugInfo()} createSourceDependency: create dependency on source ObservedObject ...`); 120 const fake = (sourceObject as Object)[TrackedObject.___TRACKED_OPTI_ASSIGNMENT_FAKE_OBJLINK_PROPERTY]; 121 } 122 } 123 124 private setValueInternal(newValue: C): boolean { 125 if (!this.checkIsObject(newValue)) { 126 return false; 127 } 128 129 if (this.obsObject_ != undefined) { 130 if (this.obsObject_ instanceof SubscribableAbstract) { 131 // unregister from SubscribableAbstract object 132 (this.obsObject_ as SubscribableAbstract).removeOwningProperty(this); 133 } else if (ObservedObject.IsObservedObject(this.obsObject_)) { 134 // unregister from the ObservedObject 135 ObservedObject.removeOwningProperty(this.obsObject_, this); 136 137 // make sure the ObservedObject no longer has a read callback function 138 // assigned to it 139 ObservedObject.unregisterPropertyReadCb(this.obsObject_); 140 } 141 } 142 143 this.obsObject_ = newValue; 144 145 if (this.obsObject_ != undefined) { 146 if (this.obsObject_ instanceof SubscribableAbstract) { 147 // register to SubscribableAbstract object 148 (this.obsObject_ as SubscribableAbstract).addOwningProperty(this); 149 } else if (ObservedObject.IsObservedObject(this.obsObject_)) { 150 // register to the ObservedObject 151 ObservedObject.addOwningProperty(this.obsObject_, this); 152 this.shouldInstallTrackedObjectReadCb = TrackedObject.needsPropertyReadCb(this.obsObject_); 153 } else { 154 stateMgmtConsole.applicationError(`${this.debugInfo()}: set/init (method setValueInternal): assigned value is neither ObservedObject nor SubscribableAbstract. \ 155 value changes will bot be observed and UI will not update. forgot @Observed class decorator? Application error.`); 156 } 157 } 158 return true; 159 } 160} 161 162/** backward compatibility after typo in classname fix */ 163class SynchedPropertyNesedObjectPU<C extends Object> extends SynchedPropertyNestedObjectPU<C> { 164 165}