• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2022-2023 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 * ObservedPropertyObjectPU
18 * implementation of @State and @Provide decorated variables of type class object
19 *
20 * all definitions in this file are framework internal
21 *
22 * class that holds an actual property value of type T
23 * uses its base class to manage subscribers to this
24 * property.
25*/
26
27class ObservedPropertyPU<T> extends ObservedPropertyAbstractPU<T>
28  implements PeerChangeEventReceiverPU<T>, ObservedObjectEventsPUReceiver<T> {
29
30  private wrappedValue_: T;
31
32  constructor(localInitValue: T, owningView: IPropertySubscriber, propertyName: PropertyInfo) {
33    super(owningView, propertyName);
34
35    this.setValueInternal(localInitValue);
36  }
37
38  aboutToBeDeleted(unsubscribeMe?: IPropertySubscriber) {
39    this.unsubscribeWrappedObject();
40    this.removeSubscriber(unsubscribeMe);
41    super.aboutToBeDeleted();
42  }
43
44
45  public debugInfoDecorator() : string {
46    return `@State/@Provide (class ObservedPropertyPU)`;
47  }
48
49
50  /**
51   * Called by a SynchedPropertyObjectTwoWayPU (@Link, @Consume) that uses this as sync peer when it has changed
52   * @param eventSource
53   */
54  syncPeerHasChanged(eventSource : ObservedPropertyAbstractPU<T>) {
55    stateMgmtConsole.debug(`${this.debugInfo()}: syncPeerHasChanged: from peer \
56          '${eventSource && eventSource.debugInfo && eventSource.debugInfo()}'.`);
57    this.notifyPropertyHasChangedPU();
58  }
59
60  /**
61   * Wraped ObservedObjectPU has changed
62   * @param souceObject
63   * @param changedPropertyName
64   */
65  public objectPropertyHasChangedPU(souceObject: ObservedObject<T>, changedPropertyName : string) {
66    stateMgmtConsole.debug(`${this.debugInfo()}: objectPropertyHasChangedPU: contained ObservedObject property \
67                                                '${changedPropertyName}' has changed.`)
68    this.notifyPropertyHasChangedPU();
69  }
70
71  public objectPropertyHasBeenReadPU(souceObject: ObservedObject<T>, changedPropertyName : string) {
72    stateMgmtConsole.debug(`${this.debugInfo()}: objectPropertyHasBeenReadPU: contained ObservedObject property \
73                            '${changedPropertyName}' has been read.`);
74
75    this.notifyPropertyHasBeenReadPU();
76  }
77
78  private unsubscribeWrappedObject() {
79    if (this.wrappedValue_) {
80      if (this.wrappedValue_ instanceof SubscribableAbstract) {
81        (this.wrappedValue_ as SubscribableAbstract).removeOwningProperty(this);
82      } else {
83        ObservedObject.removeOwningProperty(this.wrappedValue_, this);
84      }
85    }
86  }
87
88  /*
89    actually update this.wrappedValue_
90    called needs to do value change check
91    and also notify with this.aboutToChange();
92  */
93  private setValueInternal(newValue: T): boolean {
94    if (newValue === this.wrappedValue_) {
95      stateMgmtConsole.debug(`ObservedPropertyObjectPU[${this.id__()}, '${this.info() || "unknown"}'] newValue unchanged`);
96      return false;
97    }
98
99    if (!this.checkIsSupportedValue(newValue)) {
100      return false;
101    }
102
103    this.unsubscribeWrappedObject();
104    if (!newValue || typeof newValue !== 'object') {
105      // undefined, null, simple type:
106      // nothing to subscribe to in case of new value undefined || null || simple type
107      this.wrappedValue_ = newValue;
108    } else if (newValue instanceof SubscribableAbstract) {
109      stateMgmtConsole.propertyAccess(`${this.debugInfo()}: setValueInternal: new value is an SubscribableAbstract, subscribing to it.`);
110      this.wrappedValue_ = newValue;
111      (this.wrappedValue_ as unknown as SubscribableAbstract).addOwningProperty(this);
112    } else if (ObservedObject.IsObservedObject(newValue)) {
113      stateMgmtConsole.propertyAccess(`${this.debugInfo()}: setValueInternal: new value is an ObservedObject already`);
114      ObservedObject.addOwningProperty(newValue, this);
115      this.wrappedValue_ = newValue;
116    } else {
117      stateMgmtConsole.propertyAccess(`${this.debugInfo()}: setValueInternal: new value is an Object, needs to be wrapped in an ObservedObject.`);
118      this.wrappedValue_ = ObservedObject.createNew(newValue, this);
119    }
120    return true;
121  }
122
123  public get(): T {
124    stateMgmtConsole.propertyAccess(`${this.debugInfo()}: get`);
125    this.notifyPropertyHasBeenReadPU();
126    return this.wrappedValue_;
127  }
128
129  public getUnmonitored(): T {
130    stateMgmtConsole.propertyAccess(`${this.debugInfo()}: getUnmonitored.`);
131    // unmonitored get access , no call to notifyPropertyRead !
132    return this.wrappedValue_;
133  }
134
135  public set(newValue: T): void {
136    if (this.wrappedValue_ === newValue) {
137      stateMgmtConsole.debug(`ObservedPropertyObjectPU[${this.id__()}, '${this.info() || "unknown"}']: set with unchanged value - ignoring.`);
138      return;
139    }
140    stateMgmtConsole.propertyAccess(`${this.debugInfo()}: set: value about to changed.`);
141    if (this.setValueInternal(newValue)) {
142      this.notifyPropertyHasChangedPU();
143    }
144  }
145}
146
147// class definitions for backward compatibility
148class ObservedPropertyObjectPU<T> extends ObservedPropertyPU<T> {
149
150}
151
152class ObservedPropertySimplePU<T> extends ObservedPropertyPU<T> {
153
154}
155