• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2021-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 *
18 * SubscribableAbstract
19 *
20 * This class is part of the SDK.
21 * @since 10
22 *
23 * SubscribableAbstract is an abstract class that manages subscribers
24 * to value changes. These subscribers are the implementation of
25 * @State, @Link, @Provide, @Consume decorated variables inside the
26 * framework. Each using @State, @Link, etc., decorated variable in
27 * a @Component will make its own subscription. When the component
28 * is created the subscription is added, and before the component
29 * is deleted it unsubscribes
30 *
31 * An application may extend SubscribableAbstract for a custom class
32 * that manages state data. @State, @Link, @Provide, @Consume
33 * decorated variables can hold an Object that is instance of
34 * SubscribableAbstract.
35 *
36 * About lifecycle: It is legal use for two @Components with two @State
37 * decorated variables to share the same SubscribableAbstract object.
38 * Each such decorated variable implementation makes its own
39 * subscription to the SubscribableAbstract object. Hence, when both variables
40 * have unsubscribed the SubscribableAbstract custom class may do its own
41 * de-initialization, e.g. release held external resources.
42 *
43 * How to extend:
44 * A subclass manages the get and set to one or several properties on its own.
45 * The subclass needs to notify all relevant value changes to the framework for the
46 * UI to be updated. Notification should only be given for class properties that
47 * are used to generate the UI.
48 *
49 * A subclass must call super() in its constructor to let this base class
50 * initialize itself.
51 *
52 * A subclass must call 'notifyPropertyHasChanged*(' after the relevant property
53 * has changes. The framework will notify all dependent components to re-render.
54 *
55 * A sub-class may overwrite the 'addOwningProperty' function to add own
56 * functionality, but it must call super.addowningOwningProperty(..). E.g.
57 * the sub-class could connect to external resources upon the first subscriber.
58 *
59 * A sub-class may also overwrite the 'removeOwningProperty' function or
60 * 'removeOwningPropertyById' function to add own functionality,
61 * but it must call super.removeOwningProperty(..).
62 * E.g. the sub-class could release held external resources upon loosing the
63 * last subscriber.
64 *
65 */
66
67abstract class SubscribableAbstract {
68
69  // keeps track of all subscribing properties
70  private owningProperties_: Set<number>;
71
72  /**
73   * make sure to call super() from subclass constructor!
74   *
75   * @since 10
76   */
77  constructor() {
78    this.owningProperties_ = new Set<number>();
79    stateMgmtConsole.debug(`SubscribableAbstract: construcstor done`);
80  }
81
82  /**
83  * A subsclass must call this function whenever one of its properties has
84   * changed that is used to construct the UI.
85   * @param propName name of the change property
86   * @param newValue the property value after the change
87   *
88   * @since 10
89   */
90  protected notifyPropertyHasChanged(propName: string, newValue: any) {
91    stateMgmtConsole.debug(`SubscribableAbstract: notifyPropertyHasChanged '${propName}'.`);
92    this.owningProperties_.forEach((subscribedId) => {
93      let owningProperty: IPropertySubscriber = SubscriberManager.Find(subscribedId);
94      if (!owningProperty) {
95        stateMgmtConsole.error(`SubscribableAbstract: notifyHasChanged: unknown subscriber.'${subscribedId}' error!.`);
96        return;
97      }
98
99      // PU code path
100      if ('onTrackedObjectPropertyCompatModeHasChangedPU' in owningProperty) {
101        (owningProperty as unknown as ObservedObjectEventsPUReceiver<any>).onTrackedObjectPropertyCompatModeHasChangedPU(this, propName);
102      }
103
104      // FU code path
105      if ('hasChanged' in owningProperty) {
106        (owningProperty as ISinglePropertyChangeSubscriber<any>).hasChanged(newValue);
107      }
108      if ('propertyHasChanged' in owningProperty) {
109        (owningProperty as IMultiPropertiesChangeSubscriber).propertyHasChanged(propName);
110      }
111    });
112  }
113
114  /**
115   * Provides the current number of subscribers.
116   * Application may use this function to determine a shared object has no more remaining subscribers and can be deleted.
117   * @returns number of current subscribers
118   *
119   * @since 10
120   */
121  public numberOfSubscribers(): number {
122    return this.owningProperties_.size;
123  }
124
125  /**
126   * Method used by the framework to add subscribing decorated variables
127   * Subclass may overwrite this function but must call the function of the base
128   * class from its own implementation.
129   * @param subscriber new subscriber that implements ISinglePropertyChangeSubscriber
130   * and/or IMultiPropertiesChangeSubscriber interfaces
131   *
132   * @since 10
133   */
134
135  public addOwningProperty(subscriber: IPropertySubscriber): void {
136    stateMgmtConsole.debug(`SubscribableAbstract: addOwningProperty: subscriber '${subscriber.id__()}'.`);
137    this.owningProperties_.add(subscriber.id__());
138  }
139
140  /**
141   * Method used by the framework to unsubscribing decorated variables
142   * Subclass may overwrite this function but must call the function of the base
143   * class from its own implementation.
144   * @param subscriber subscriber that implements ISinglePropertyChangeSubscriber
145   * and/or IMultiPropertiesChangeSubscriber interfaces
146   *
147   * @since 10
148   */
149  public removeOwningProperty(property: IPropertySubscriber): void {
150    return this.removeOwningPropertyById(property.id__());
151  }
152
153  /**
154   * Same as @see removeOwningProperty() but by Subscriber id.
155   * @param subscriberId
156  *
157   * framework internal function, not to be used by applications.
158   */
159  public removeOwningPropertyById(subscriberId: number): void {
160    stateMgmtConsole.debug(`SubscribableAbstract: removeOwningProperty '${subscriberId}'.`);
161    this.owningProperties_.delete(subscriberId);
162  }
163
164  /**
165   * flush all subscribers / owning properties
166   * This is needed when copying a SubscribableAbstract object to the localObject or @prop / SynchedPropertyObjectOneWay
167   * - shallowCopy: copies the _reference to original_ Set. Hence, we must not modify this Set but assign a new Set
168   * - deepCopy also (deep-) copies this class' owningProperties_ Set, incl. the numbers it includes. Assigning a new Set fixes.
169   *
170   */
171  public clearOwningProperties() {
172    this.owningProperties_ = new Set<number>();
173  }
174}
175
176/**
177 *  SubscribaleAbstract class with typo in its nam,e
178 *
179 * @depreciated, use SubscribableAbstract
180 */
181
182abstract class SubscribaleAbstract extends SubscribableAbstract {
183}
184