• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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}