• 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 * SynchedPropertyObjectOneWayPU
18 * implementatio  of @Prop decorated variables of type class object
19 *
20 * all definitions in this file are framework internal
21 *
22 */
23
24
25type SyncSourceHasChangedCb<T> = (source : ObservedPropertyAbstract<T>) => void;
26
27class SynchedPropertyObjectOneWayPU<C extends Object>
28  extends ObservedPropertyObjectAbstractPU<C>
29  implements PeerChangeEventReceiverPU<C>,
30  ObservedObjectEventsPUReceiver<C> {
31
32  private wrappedValue_: C;
33  private source_: ObservedPropertyAbstract<C>;
34
35  // true for @Prop code path,
36  // false for @(Local)StorageProp
37  private sourceIsOwnObject : boolean;
38
39  constructor(source: ObservedPropertyAbstract<C> | C,
40    owningChildView: IPropertySubscriber,
41    thisPropertyName: PropertyInfo) {
42    super(owningChildView, thisPropertyName);
43
44    if (source && (typeof (source) === "object") && ("subscribeMe" in source)) {
45      // code path for @(Local)StorageProp, the souce is a ObservedPropertyObject in aLocalStorage)
46      this.source_ = source as ObservedPropertyAbstractPU<C>;
47      this.sourceIsOwnObject = false;
48      // subscribe to receive value change updates from LocalStorage source property
49      this.source_.subscribeMe(this);
50    } else {
51      // code path for @Prop
52      if (!ObservedObject.IsObservedObject(source)) {
53        stateMgmtConsole.warn(`@Prop ${this.info()}  Provided source object's class
54           lacks @Observed class decorator. Object property changes will not be observed.`);
55      }
56      stateMgmtConsole.debug(`SynchedPropertyObjectOneWayPU[${this.id__()}, '${this.info() || "unknown"}']: constructor @Prop wrapping source in a new ObservedPropertyObjectPU`);
57      this.source_ = new ObservedPropertyObjectPU<C>(source as C, this, thisPropertyName);
58      this.sourceIsOwnObject = true;
59    }
60
61    // deep copy source Object and wrap it
62    this.setWrappedValue(this.source_.get());
63
64    stateMgmtConsole.debug(`SynchedPropertyObjectOneWayPU[${this.id__()}, '${this.info() || "unknown"}']: constructor ready with wrappedValue '${JSON.stringify(this.wrappedValue_)}'.`);
65  }
66
67  /*
68  like a destructor, need to call this before deleting
69  the property.
70  */
71  aboutToBeDeleted() {
72    if (this.source_) {
73      this.source_.unlinkSuscriber(this.id__());
74      if (this.sourceIsOwnObject == true && this.source_.numberOfSubscrbers()==0){
75        stateMgmtConsole.debug(`SynchedPropertyObjectOneWayPU[${this.id__()}, '${this.info() || "unknown"}']: aboutToBeDeleted. owning source_ ObservedPropertySimplePU, calling its aboutToBeDeleted`);
76        this.source_.aboutToBeDeleted();
77     }
78
79      this.source_ = undefined;
80    }
81    super.aboutToBeDeleted();
82  }
83
84  public syncPeerHasChanged(eventSource: ObservedPropertyAbstractPU<C>) {
85    if (eventSource && this.source_ == eventSource) {
86      // defensive programming: should always be the case!
87      stateMgmtConsole.debug(`SynchedPropertyNesedObjectPU[${this.id__()}]: syncPeerHasChanged(): Source '${eventSource.info()}' has changed'.`)
88      const newValue = this.source_.getUnmonitored();
89      if (typeof newValue == "object") {
90        stateMgmtConsole.debug(`SynchedPropertyObjectOneWayPU[${this.id__()}, '${this.info() || "unknown"}']: hasChanged:  newValue '${JSON.stringify(newValue)}'.`);
91        this.setWrappedValue(newValue);
92        this.notifyPropertyHasChangedPU();
93      }
94    } else {
95      stateMgmtConsole.warn(`SynchedPropertyNesedObjectPU[${this.id__()}]: syncPeerHasChanged Unexpected situation. Ignorning event.`)
96    }
97  }
98
99  /**
100   * event emited by wrapped ObservedObject, when one of its property values changes
101   * @param souceObject
102   * @param changedPropertyName
103   */
104  public objectPropertyHasChangedPU(souceObject: ObservedObject<C>, changedPropertyName : string) {
105    stateMgmtConsole.debug(`SynchedPropertyObjectOneWayPU[${this.id__()}, '${this.info() || "unknown"}']: \
106        objectPropertyHasChangedPU: contained ObservedObject property '${changedPropertyName}' has changed.`)
107    this.notifyPropertyHasChangedPU();
108  }
109
110  public objectPropertyHasBeenReadPU(souceObject: ObservedObject<C>, changedPropertyName : string) {
111    stateMgmtConsole.debug(`SynchedPropertyObjectOneWayPU[${this.id__()}, '${this.info() || "unknown"}']: \
112    objectPropertyHasBeenReadPU: contained ObservedObject property '${changedPropertyName}' has been read.`);
113    this.notifyPropertyHasBeenReadPU();
114  }
115
116  public getUnmonitored(): C {
117    stateMgmtConsole.debug(`SynchedPropertyObjectOneWayPU[${this.id__()}, '${this.info() || "unknown"}']: getUnmonitored returns '${JSON.stringify(this.wrappedValue_)}'.`);
118    // unmonitored get access , no call to notifyPropertyRead !
119    return this.wrappedValue_;
120  }
121
122  // get 'read through` from the ObservedObject
123  public get(): C {
124    stateMgmtConsole.debug(`SynchedPropertyObjectOneWayPU[${this.id__()}, '${this.info() || "unknown"}']: get returning ${JSON.stringify(this.wrappedValue_)}.`)
125    this.notifyPropertyHasBeenReadPU()
126    return this.wrappedValue_;
127  }
128
129  // assignment to local variable in the form of this.aProp = <object value>
130  // set 'writes through` to the ObservedObject
131  public set(newValue: C): void {
132    if (this.wrappedValue_ == newValue) {
133      stateMgmtConsole.debug(`SynchedPropertyObjectOneWayPU[${this.id__()}IP, '${this.info() || "unknown"}']: set with unchanged value '${JSON.stringify(newValue)}'- ignoring.`);
134      return;
135    }
136
137    stateMgmtConsole.debug(`SynchedPropertyObjectOneWayPU[${this.id__()}, '${this.info() || "unknown"}']: set to newValue: '${JSON.stringify(newValue)}'.`);
138    if (!ObservedObject.IsObservedObject(newValue)) {
139      stateMgmtConsole.warn(`@Prop ${this.info()} Set: Provided new object's class
140         lacks @Observed class decorator. Object property changes will not be observed.`);
141    }
142
143    this.setWrappedValue(newValue);
144    this.notifyPropertyHasChangedPU();
145  }
146
147  public reset(sourceChangedValue: C): void {
148    stateMgmtConsole.debug(`SynchedPropertyObjectOneWayPU[${this.id__()}, '${this.info() || "unknown"}']: reset from '${JSON.stringify(this.wrappedValue_)}' to '${JSON.stringify(sourceChangedValue)}'.`);
149    // if set causes an actual change, then, ObservedPropertyObject source_ will call syncPeerHasChanged
150    this.source_.set(sourceChangedValue);
151  }
152
153  private setWrappedValue(value: C): void {
154    let rawValue = ObservedObject.GetRawObject(value);
155    let copy: C;
156
157    // FIXME: Proper object deep copy missing here!
158    if (rawValue instanceof Array) {
159      copy = ObservedObject.createNew([ ...rawValue ], this) as unknown as C;
160    } else {
161      copy = ObservedObject.createNew({ ...rawValue }, this);
162    }
163    Object.setPrototypeOf(copy, Object.getPrototypeOf(rawValue));
164    ObservedObject.addOwningProperty(this.wrappedValue_, this);
165    this.wrappedValue_ = copy;
166  }
167}
168