1/* 2 * Copyright (c) 2021 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 * Copyright (c) 2021 Huawei Device Co., Ltd. 17 * Licensed under the Apache License, Version 2.0 (the "License"); 18 * you may not use this file except in compliance with the License. 19 * You may obtain a copy of the License at 20 * 21 * http://www.apache.org/licenses/LICENSE-2.0 22 * 23 * Unless required by applicable law or agreed to in writing, software 24 * distributed under the License is distributed on an "AS IS" BASIS, 25 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 26 * See the License for the specific language governing permissions and 27 * limitations under the License. 28 */ 29/* 30 * Copyright (c) 2021 Huawei Device Co., Ltd. 31 * Licensed under the Apache License, Version 2.0 (the "License"); 32 * you may not use this file except in compliance with the License. 33 * You may obtain a copy of the License at 34 * 35 * http://www.apache.org/licenses/LICENSE-2.0 36 * 37 * Unless required by applicable law or agreed to in writing, software 38 * distributed under the License is distributed on an "AS IS" BASIS, 39 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 40 * See the License for the specific language governing permissions and 41 * limitations under the License. 42 */ 43/* 44 * Copyright (c) 2021 Huawei Device Co., Ltd. 45 * Licensed under the Apache License, Version 2.0 (the "License"); 46 * you may not use this file except in compliance with the License. 47 * You may obtain a copy of the License at 48 * 49 * http://www.apache.org/licenses/LICENSE-2.0 50 * 51 * Unless required by applicable law or agreed to in writing, software 52 * distributed under the License is distributed on an "AS IS" BASIS, 53 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 54 * See the License for the specific language governing permissions and 55 * limitations under the License. 56 */ 57/** 58 * 59 * LocalStorage 60 * 61 * Class implements a Map of ObservableObjectBase UI state variables. 62 * Instances can be created to manage UI state within a limited "local" 63 * access, and life cycle as defined by the app. 64 * AppStorage singleton is sub-class of LocalStorage for 65 * UI state of app-wide access and same life cycle as the app. 66 * 67 * @since 9 68 */ 69 class LocalStorage extends NativeLocalStorage { 70 /** 71 * Construct new instance of LocalStorage 72 * initialzie with all properties and their values that Object.keys(params) returns 73 * Property values must not be undefined. 74 * @param initializingProperties Object containing keys and values. @see set() for valid values 75 * 76 * @since 9 77 */ 78 constructor(initializingProperties = {}) { 79 super(); 80 81 this.storage_ = new Map(); 82 if (Object.keys(initializingProperties).length) { 83 this.initializeProps(initializingProperties); 84 } 85 } 86 /** 87 * clear storage and init with given properties 88 * @param initializingProperties 89 * 90 * not a public / sdk function 91 */ 92 initializeProps(initializingProperties = {}) { 93 94 this.storage_.clear(); 95 Object.keys(initializingProperties).filter((propName) => initializingProperties[propName] != undefined).forEach((propName) => this.addNewPropertyInternal(propName, initializingProperties[propName])); 96 } 97 /** 98 * Use before deleting owning Ability, window, or service UI 99 * (letting it go out of scope). 100 * 101 * This method orderly closes down a LocalStorage instance by calling @see clear(). 102 * This requires that no property is left with one or more subscribers. 103 * @see clear() and @see delete() 104 * @returns true if all properties could be removed from storage 105 */ 106 aboutToBeDeleted() { 107 return this.clear(); 108 } 109 /** 110 * Check if LocalStorage has a property with given name 111 * return true if prooperty with given name exists 112 * same as ES6 Map.prototype.has() 113 * @param propName searched property 114 * @returns true if property with such name exists in LocalStorage 115 * 116 * @since 9 117 */ 118 has(propName) { 119 return this.storage_.has(propName); 120 } 121 /** 122 * Provide names of all properties in LocalStorage 123 * same as ES6 Map.prototype.keys() 124 * @returns return a Map Iterator 125 * 126 * @since 9 127 */ 128 keys() { 129 return this.storage_.keys(); 130 } 131 /** 132 * Returns number of properties in LocalStorage 133 * same as Map.prototype.size() 134 * @param propName 135 * @returns return number of properties 136 * 137 * @since 9 138 */ 139 size() { 140 return this.storage_.size; 141 } 142 /** 143 * Returns value of given property 144 * return undefined if no property with this name 145 * @param propName 146 * @returns property value if found or undefined 147 * 148 * @since 9 149 */ 150 get(propName) { 151 var p = this.storage_.get(propName); 152 return (p) ? p.get() : undefined; 153 } 154 /** 155 * Set value of given property in LocalStorage 156 * Methosd sets nothing and returns false if property with this name does not exist 157 * or if newValue is `undefined` or `null` (`undefined`, `null` value are not allowed for state variables). 158 * @param propName 159 * @param newValue must be of type T and must not be undefined or null 160 * @returns true on success, i.e. when above conditions are satisfied, otherwise false 161 * 162 * @since 9 163 */ 164 set(propName, newValue) { 165 if (newValue == undefined) { 166 stateMgmtConsole.warn(`${this.constructor.name}: set('${propName}') with newValue == undefined not allowed.`); 167 return false; 168 } 169 var p = this.storage_.get(propName); 170 if (p == undefined) { 171 stateMgmtConsole.warn(`${this.constructor.name}: set: no property ${propName} error.`); 172 return false; 173 } 174 p.set(newValue); 175 return true; 176 } 177 /** 178 * Set value of given property, if it exists, @see set() . 179 * Add property if no property with given name and initialize with given value. 180 * Do nothing and return false if newValuue is undefined or null 181 * (undefined, null value is not allowed for state variables) 182 * @param propName 183 * @param newValue must be of type T and must not be undefined or null 184 * @returns true on success, i.e. when above conditions are satisfied, otherwise false 185 * 186 * @since 9 187 */ 188 setOrCreate(propName, newValue) { 189 if (newValue == undefined) { 190 stateMgmtConsole.warn(`${this.constructor.name}: setOrCreate('${propName}') with newValue == undefined not allowed.`); 191 return false; 192 } 193 var p = this.storage_.get(propName); 194 if (p) { 195 196 p.set(newValue); 197 } 198 else { 199 200 this.addNewPropertyInternal(propName, newValue); 201 } 202 return true; 203 } 204 /** 205 * Internal use helper function to create and initialize a new property. 206 * caller needs to be all the checking beforehand 207 * @param propName 208 * @param value 209 * 210 * Not a public / sdk method. 211 */ 212 addNewPropertyInternal(propName, value) { 213 const newProp = (typeof value === "object") ? 214 new ObservedPropertyObject(value, undefined, propName) 215 : new ObservedPropertySimple(value, undefined, propName); 216 this.storage_.set(propName, newProp); 217 return newProp; 218 } 219 /** 220 * create and return a two-way sync "(link") to named property 221 * @param propName name of source property in LocalStorage 222 * @param linkUser IPropertySubscriber to be notified when source changes, 223 * @param subscribersName optional, the linkUser (subscriber) uses this name for the property 224 * this name will be used in propertyChange(propName) callback of IMultiPropertiesChangeSubscriber 225 * @returns SynchedPropertyTwoWay{Simple|Object| object with given LocalStoage prop as its source. 226 * Apps can use SDK functions of base class SubscribedAbstractProperty<S> 227 * return undefiend if named property does not already exist in LocalStorage 228 * Apps can use SDK functions of base class SubscribedPropertyAbstract<S> 229 * return undefiend if named property does not already exist in LocalStorage 230 * 231 * @since 9 232 */ 233 link(propName, linkUser, subscribersName) { 234 var p = this.storage_.get(propName); 235 if (p == undefined) { 236 stateMgmtConsole.warn(`${this.constructor.name}: link: no property ${propName} error.`); 237 return undefined; 238 } 239 let linkResult = p.createLink(linkUser, propName); 240 linkResult.setInfo(subscribersName); 241 return linkResult; 242 } 243 /** 244 * Like @see link(), but will create and initialize a new source property in LocalStorge if missing 245 * @param propName name of source property in LocalStorage 246 * @param defaultValue value to be used for initializing if new creating new property in LocalStorage 247 * default value must be of type S, must not be undefined or null. 248 * @param linkUser IPropertySubscriber to be notified when return 'link' changes, 249 * @param subscribersName the linkUser (subscriber) uses this name for the property 250 * this name will be used in propertyChange(propName) callback of IMultiPropertiesChangeSubscriber 251 * @returns SynchedPropertyTwoWay{Simple|Object| object with given LocalStoage prop as its source. 252 * Apps can use SDK functions of base class SubscribedAbstractProperty<S> 253 * 254 * @since 9 255 */ 256 setAndLink(propName, defaultValue, linkUser, subscribersName) { 257 var p = this.storage_.get(propName); 258 if (!p) { 259 this.setOrCreate(propName, defaultValue); 260 } 261 return this.link(propName, linkUser, subscribersName); 262 } 263 /** 264 * create and return a one-way sync ('prop') to named property 265 * @param propName name of source property in LocalStorage 266 * @param propUser IPropertySubscriber to be notified when source changes, 267 * @param subscribersName the linkUser (subscriber) uses this name for the property 268 * this name will be used in propertyChange(propName) callback of IMultiPropertiesChangeSubscriber 269 * @returns SynchedPropertyOneWay{Simple|Object| object with given LocalStoage prop as its source. 270 * Apps can use SDK functions of base class SubscribedAbstractProperty<S> 271 * return undefiend if named property does not already exist in LocalStorage. 272 * Apps can use SDK functions of base class SubscribedPropertyAbstract<S> 273 * return undefiend if named property does not already exist in LocalStorage. 274 * @since 9 275 */ 276 prop(propName, propUser, subscribersName) { 277 var p = this.storage_.get(propName); 278 if (p == undefined) { 279 stateMgmtConsole.warn(`${this.constructor.name}: prop: no property ${propName} error.`); 280 return undefined; 281 } 282 let propResult = p.createProp(propUser, propName); 283 propResult.setInfo(subscribersName); 284 return propResult; 285 } 286 /** 287 * Like @see prop(), will create and initialize a new source property in LocalStorage if missing 288 * @param propName name of source property in LocalStorage 289 * @param defaultValue value to be used for initializing if new creating new property in LocalStorage. 290 * default value must be of type S, must not be undefined or null. 291 * @param propUser IPropertySubscriber to be notified when returned 'prop' changes, 292 * @param subscribersName the propUser (subscriber) uses this name for the property 293 * this name will be used in propertyChange(propName) callback of IMultiPropertiesChangeSubscriber 294 * @returns SynchedPropertyOneWay{Simple|Object| object with given LocalStoage prop as its source. 295 * Apps can use SDK functions of base class SubscribedAbstractProperty<S> 296 * @since 9 297 */ 298 setAndProp(propName, defaultValue, propUser, subscribersName) { 299 var p = this.storage_.get(propName); 300 if (!p) { 301 this.setOrCreate(propName, defaultValue); 302 } 303 return this.prop(propName, propUser, subscribersName); 304 } 305 /** 306 * Delete property from StorageBase 307 * Use with caution: 308 * Before deleting a prop from LocalStorage all its subscribers need to 309 * unsubscribe from the property. 310 * This method fails and returns false if given property still has subscribers 311 * Another reason for failing is unkmown property. 312 * 313 * Developer advise: 314 * Subscribers are created with @see link(), @see prop() 315 * and also via @LocalStorageLink and @LocalStorageProp state variable decorators. 316 * That means as long as their is a @Component instance that uses such decorated variable 317 * or a sync relationship with a SubscribedAbstractProperty variable the property can nit 318 * (and also should not!) be deleted from LocalStorage. 319 * 320 * @param propName 321 * @returns false if method failed 322 * 323 * @since 9 324 */ 325 delete(propName) { 326 var p = this.storage_.get(propName); 327 if (p) { 328 if (p.numberOfSubscrbers()) { 329 stateMgmtConsole.error(`${this.constructor.name}: Attempt to delete property ${propName} that has \ 330 ${p.numberOfSubscrbers()} subscribers. Subscribers need to unsubscribe before prop deletion.`); 331 return false; 332 } 333 p.aboutToBeDeleted(); 334 this.storage_.delete(propName); 335 return true; 336 } 337 else { 338 stateMgmtConsole.warn(`${this.constructor.name}: Attempt to delete unknown property ${propName}.`); 339 return false; 340 } 341 } 342 /** 343 * delete all properties from the LocalStorage instance 344 * @see delete(). 345 * precondition is that there are no subscribers. 346 * method returns false and deletes no poperties if there is any property 347 * that still has subscribers 348 * 349 * @since 9 350 */ 351 clear() { 352 for (let propName of this.keys()) { 353 var p = this.storage_.get(propName); 354 if (p.numberOfSubscrbers()) { 355 stateMgmtConsole.error(`${this.constructor.name}.deleteAll: Attempt to delete property ${propName} that \ 356 has ${p.numberOfSubscrbers()} subscribers. Subscribers need to unsubscribe before prop deletion. 357 Any @Component instance with a @StorageLink/Prop or @LocalStorageLink/Prop is a subscriber.`); 358 return false; 359 } 360 } 361 for (let propName of this.keys()) { 362 var p = this.storage_.get(propName); 363 p.aboutToBeDeleted(); 364 } 365 this.storage_.clear(); 366 return true; 367 } 368 /** 369 * Subscribe to value change notifications of named property 370 * Any object implementing ISinglePropertyChangeSubscriber interface 371 * and registerign itself to SubscriberManager can register 372 * Caution: do remember to unregister, otherwise the property will block 373 * cleanup, @see delete() and @see clear() 374 * 375 * @param propName property in LocalStorage to subscribe to 376 * @param subscriber object that implements ISinglePropertyChangeSubscriber interface 377 * @returns false if named property does not exist 378 * 379 * @since 9 380 */ 381 subscribeToChangesOf(propName, subscriber) { 382 var p = this.storage_.get(propName); 383 if (p) { 384 p.subscribeMe(subscriber); 385 return true; 386 } 387 return false; 388 } 389 /** 390 * inverse of @see subscribeToChangesOf 391 * @param propName property in LocalStorage to subscribe to 392 * @param subscriberId id of the subscrber passed to @see subscribeToChangesOf 393 * @returns false if named property does not exist 394 * 395 * @since 9 396 */ 397 unsubscribeFromChangesOf(propName, subscriberId) { 398 var p = this.storage_.get(propName); 399 if (p) { 400 p.unlinkSuscriber(subscriberId); 401 return true; 402 } 403 return false; 404 } 405 /** 406 * return number of subscribers to named property 407 * useful for debug purposes 408 * 409 * Not a public / sdk function 410 */ 411 numberOfSubscrbersTo(propName) { 412 var p = this.storage_.get(propName); 413 if (p) { 414 return p.numberOfSubscrbers(); 415 } 416 return undefined; 417 } 418 __createSync(storagePropName, defaultValue, factoryFunc) { 419 let p = this.storage_.get(storagePropName); 420 if (p == undefined) { 421 // property named 'storagePropName' not yet in storage 422 // add new property to storage 423 if (defaultValue === undefined) { 424 stateMgmtConsole.error(`${this.constructor.name}.__createSync(${storagePropName}, non-existing property and undefined default value. ERROR.`); 425 return undefined; 426 } 427 p = this.addNewPropertyInternal(storagePropName, defaultValue); 428 } 429 return factoryFunc(p); 430 } 431} 432/* 433 * Copyright (c) 2021-2022 Huawei Device Co., Ltd. 434 * Licensed under the Apache License, Version 2.0 (the "License"); 435 * you may not use this file except in compliance with the License. 436 * You may obtain a copy of the License at 437 * 438 * http://www.apache.org/licenses/LICENSE-2.0 439 * 440 * Unless required by applicable law or agreed to in writing, software 441 * distributed under the License is distributed on an "AS IS" BASIS, 442 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 443 * See the License for the specific language governing permissions and 444 * limitations under the License. 445 */ 446/** 447 * 448 * AppStorage 449 * 450 * Class implements a Map of ObservableObjectBase UI state variables. 451 * AppStorage singleton is sub-class of @see LocalStorage for 452 * UI state of app-wide access and same life cycle as the app. 453 * 454 * @since 7 455 */ 456class AppStorage extends LocalStorage { 457 /** singleton class, app can not create instances 458 * 459 * not a public / sdk function 460 */ 461 constructor(initializingProperties) { 462 super(initializingProperties); 463 } 464 /** 465 * create and initialize singleton 466 * initialzie with all properties and their values that Object.keys(params) returns 467 * Property values must not be undefined. 468 * 469 * not a public / sdk function 470 */ 471 static CreateSingleton(initializingPropersties) { 472 if (!AppStorage.Instance_) { 473 474 AppStorage.Instance_ = new AppStorage(initializingPropersties); 475 } 476 else { 477 stateMgmtConsole.error("AppStorage.CreateNewInstance(..): instance exists already, internal error!"); 478 } 479 } 480 /** 481 * create and return a two-way sync "(link") to named property 482 * 483 * Same as @see LocalStorage.link() 484 * 485 * @param propName name of source property in AppStorage 486 * @param linkUser IPropertySubscriber to be notified when source changes, 487 * @param subscribersName the linkUser (subscriber) uses this name for the property 488 * this name will be used in propertyChange(propName) callback of IMultiPropertiesChangeSubscriber 489 * @returns SynchedPropertyTwoWay{Simple|Object| object with given LocalStoage prop as its source. 490 * Apps can use SDK functions of base class SubscribedAbstractProperty<S> 491 * return undefiend if named property does not already exist in AppStorage 492 * 493 * @since 7 494 */ 495 static Link(key, linkUser, subscribersName) { 496 return AppStorage.GetOrCreate().link(key, linkUser, subscribersName); 497 } 498 /** 499 * Like @see link(), but will create and initialize a new source property in LocalStorge if missing 500 * 501 * Same as @see LocalStorage.setAndLink() 502 * 503 * @param propName name of source property in AppStorage 504 * @param defaultValue value to be used for initializing if new creating new property in AppStorage 505 * default value must be of type S, must not be undefined or null. 506 * @param linkUser IPropertySubscriber to be notified when return 'link' changes, 507 * @param subscribersName the linkUser (subscriber) uses this name for the property 508 * this name will be used in propertyChange(propName) callback of IMultiPropertiesChangeSubscriber 509 * @returns SynchedPropertyTwoWay{Simple|Object| object with given LocalStoage prop as its source. 510 * Apps can use SDK functions of base class SubscribedAbstractProperty<S> 511 * 512 * @since 7 513 */ 514 static SetAndLink(key, defaultValue, linkUser, subscribersName) { 515 return AppStorage.GetOrCreate().setAndLink(key, defaultValue, linkUser, subscribersName); 516 } 517 /** 518 * create and return a one-way sync ('prop') to named property 519 * 520 * Same as @see LocalStorage.prop() 521 * 522 * @param propName name of source property in AppStorage 523 * @param propUser IPropertySubscriber to be notified when source changes, 524 * @param subscribersName the linkUser (subscriber) uses this name for the property 525 * this name will be used in propertyChange(propName) callback of IMultiPropertiesChangeSubscriber 526 * @returns SynchedPropertyOneWay{Simple|Object| object with given LocalStoage prop as its source. 527 * Apps can use SDK functions of base class SubscribedAbstractProperty<S> 528 * return undefiend if named property does not already exist in AppStorage. 529 * @since 7 530 */ 531 static Prop(propName, propUser, subscribersName) { 532 return AppStorage.GetOrCreate().prop(propName, propUser, subscribersName); 533 } 534 /** 535 * Like @see prop(), will create and initialize a new source property in AppStorage if missing 536 * 537 * Same as @see LocalStorage.setAndProp() 538 * 539 * @param propName name of source property in AppStorage 540 * @param defaultValue value to be used for initializing if new creating new property in AppStorage. 541 * default value must be of type S, must not be undefined or null. 542 * @param propUser IPropertySubscriber to be notified when returned 'prop' changes, 543 * @param subscribersName the propUser (subscriber) uses this name for the property 544 * this name will be used in propertyChange(propName) callback of IMultiPropertiesChangeSubscriber 545 * @returns SynchedPropertyOneWay{Simple|Object| object with given LocalStoage prop as its source. 546 * Apps can use SDK functions of base class SubscribedAbstractProperty<S> 547 * 548 * @since 7 549 */ 550 static SetAndProp(key, defaultValue, propUser, subscribersName) { 551 return AppStorage.GetOrCreate().setAndProp(key, defaultValue, propUser, subscribersName); 552 } 553 /** 554 * Check if AppStorge has a property with given name 555 * return true if prooperty with given name exists 556 * same as ES6 Map.prototype.has() 557 * 558 * Same as @see LocalStorage.has() 559 * 560 * @param propName searched property 561 * @returns true if property with such name exists in AppStorage 562 * 563 * @since 7 564 */ 565 static Has(key) { 566 return AppStorage.GetOrCreate().has(key); 567 } 568 /** 569 * Returns value of given property 570 * return undefined if no property with this name 571 * 572 * 573 * @Same as see LocalStorage.get() 574 * 575 * @param propName 576 * @returns property value if found or undefined 577 * 578 */ 579 static Get(key) { 580 return AppStorage.GetOrCreate().get(key); 581 } 582 /** 583 * Set value of given property in AppStorage 584 * Methosd sets nothing and returns false if property with this name does not exist 585 * or if newValue is `undefined` or `null` (`undefined`, `null` value are not allowed for state variables). 586 * 587 * Same as @see LocalStorage.set 588 * 589 * @param propName 590 * @param newValue must be of type T and must not be undefined or null 591 * @returns true on success, i.e. when above conditions are satisfied, otherwise false 592 * 593 * @since 7 594 */ 595 static Set(key, newValue) { 596 return AppStorage.GetOrCreate().set(key, newValue); 597 } 598 /** 599 * Set value of given property, if it exists, @see set() . 600 * Add property if no property with given name and initialize with given value. 601 * Do nothing and return false if newValuue is undefined or null 602 * (undefined, null value is not allowed for state variables) 603 * 604 * @see LocalStorage.setOrCreate() 605 * 606 * @param propName 607 * @param newValue must be of type T and must not be undefined or null 608 * @returns true on success, i.e. when above conditions are satisfied, otherwise false 609 * 610 * @since 7 611 */ 612 static SetOrCreate(key, newValue) { 613 AppStorage.GetOrCreate().setOrCreate(key, newValue); 614 } 615 /** 616 * Delete property from StorageBase 617 * Use with caution: 618 * Before deleting a prop from AppStorage all its subscribers need to 619 * unsubscribe from the property. 620 * This method fails and returns false if given property still has subscribers 621 * Another reason for failing is unkmown property. 622 * 623 * Developer advise: 624 * Subscribers are created with @see link(), @see prop() 625 * and also via @LocalStorageLink and @LocalStorageProp state variable decorators. 626 * That means as long as their is a @Component instance that uses such decorated variable 627 * or a sync relationship with a SubscribedAbstractProperty variable the property can nit 628 * (and also should not!) be deleted from AppStorage. 629 * 630 * Same as @see LocalStorage.delete() 631 * 632 * @param propName 633 * @returns false if method failed 634 * 635 * @since 7 636 */ 637 static Delete(key) { 638 return AppStorage.GetOrCreate().delete(key); 639 } 640 /** 641 * Provide names of all properties in AppStorage 642 * same as ES6 Map.prototype.keys() 643 * 644 * Same as @see LocalStorage.keys() 645 * 646 * @returns return a Map Iterator 647 * 648 * @since 7 649 */ 650 static Keys() { 651 return AppStorage.GetOrCreate().keys(); 652 } 653 /** 654 * Returns number of properties in AppStorage 655 * same as Map.prototype.size() 656 * 657 * Same as @see LocalStorage.size() 658 * 659 * @param propName 660 * @returns return number of properties 661 * 662 * @since 7 663 */ 664 static Size() { 665 return AppStorage.GetOrCreate().size(); 666 } 667 /** 668 * delete all properties from the AppStorage 669 * 670 * @see delete(), same as @see LocalStorage.clear() 671 * 672 * precondition is that there are no subscribers. 673 * method returns false and deletes no poperties if there is any property 674 * that still has subscribers 675 * 676 * @since 7 677 */ 678 static Clear() { 679 return AppStorage.GetOrCreate().clear(); 680 } 681 /** 682 * Same as @see Clear(). 683 * 684 * @since 7, depreciated, used Clear() instead! 685 * 686 */ 687 static StaticClear() { 688 return AppStorage.Clear(); 689 } 690 /** 691 * not a public / sdk function 692 */ 693 static AboutToBeDeleted() { 694 AppStorage.GetOrCreate().aboutToBeDeleted(); 695 } 696 /** 697 * return number of subscribers to named property 698 * useful for debug purposes 699 * 700 * not a public / sdk function 701 */ 702 static NumberOfSubscribersTo(propName) { 703 return AppStorage.GetOrCreate().numberOfSubscrbersTo(propName); 704 } 705 /** 706 * Subscribe to value change notifications of named property 707 * Any object implementing ISinglePropertyChangeSubscriber interface 708 * and registerign itself to SubscriberManager can register 709 * Caution: do remember to unregister, otherwise the property will block 710 * cleanup, @see delete() and @see clear() 711 * 712 * Same as @see LocalStorage.subscribeToChangesOf() 713 * 714 * @param propName property in AppStorage to subscribe to 715 * @param subscriber object that implements ISinglePropertyChangeSubscriber interface 716 * @returns false if named property does not exist 717 * 718 * @since 7 719 */ 720 static SubscribeToChangesOf(propName, subscriber) { 721 return AppStorage.GetOrCreate().subscribeToChangesOf(propName, subscriber); 722 } 723 /** 724 * inverse of @see SubscribeToChangesOf, 725 * same as @see LocalStorage.subscribeToChangesOf() 726 * 727 * @param propName property in AppStorage to subscribe to 728 * @param subscriberId id of the subscrber passed to @see subscribeToChangesOf 729 * @returns false if named property does not exist 730 * 731 * @since 7 732 */ 733 static UnsubscribeFromChangesOf(propName, subscriberId) { 734 return AppStorage.GetOrCreate().unsubscribeFromChangesOf(propName, subscriberId); 735 } 736 /** 737 * Unimplemenrted, currently all properties of AppStorage are mutable. 738 * 739 * @since 7, depreciated 740 */ 741 static IsMutable(key) { 742 return true; 743 } 744 /** 745 * not a public / sdk function 746 */ 747 static __CreateSync(storagePropName, defaultValue, factoryFunc) { 748 return AppStorage.GetOrCreate().__createSync(storagePropName, defaultValue, factoryFunc); 749 } 750 /** 751 * not a public / sdk function 752 */ 753 static GetOrCreate() { 754 if (!AppStorage.Instance_) { 755 stateMgmtConsole.warn("AppStorage instance missing. Use AppStorage.CreateInstance(initObj). Creating instance without any initialization."); 756 AppStorage.Instance_ = new AppStorage({}); 757 } 758 return AppStorage.Instance_; 759 } 760} 761// instance functions below: 762// Should all be protected, but TS lang does not allow access from static member to protected member 763AppStorage.Instance_ = undefined; 764/* 765 * Copyright (c) 2022 Huawei Device Co., Ltd. 766 * Licensed under the Apache License, Version 2.0 (the "License"); 767 * you may not use this file except in compliance with the License. 768 * You may obtain a copy of the License at 769 * 770 * http://www.apache.org/licenses/LICENSE-2.0 771 * 772 * Unless required by applicable law or agreed to in writing, software 773 * distributed under the License is distributed on an "AS IS" BASIS, 774 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 775 * See the License for the specific language governing permissions and 776 * limitations under the License. 777 */ 778/** 779 * Singleton class SubscriberManager implements IPropertySubscriberLookup 780 * public API to manage IPropertySubscriber 781 */ 782class SubscriberManager { 783 /** 784 * SubscriberManager is a singleton created by the framework 785 * do not use 786 * 787 * internal method 788 */ 789 constructor() { 790 this.subscriberById_ = new Map(); 791 792 } 793 /** 794 * check subscriber is known 795 * same as ES6 Map.prototype.has() 796 * 797 * @since 9 798 */ 799 static Has(id) { 800 return SubscriberManager.GetInstance().has(id); 801 } 802 /** 803 * 804 * retrieve subscriber by id 805 * same as ES6 Map.prototype.get() 806 * 807 * @since 9 808 */ 809 static Find(id) { 810 return SubscriberManager.GetInstance().get(id); 811 } 812 /** 813 * unregister a subscriber 814 * same as ES6 Map.prototype.delete() 815 * @return boolean success or failure to delete 816 * 817 * @since 9 818 */ 819 static Delete(id) { 820 return SubscriberManager.GetInstance().delete(id); 821 } 822 /** 823 * add a new subscriber. 824 * The subscriber must have a new (unused) id (@see MakeId() ) 825 * for add() to succeed. 826 * same as Map.prototype.set() 827 * 828 * @since 9 829 */ 830 static Add(newSubsriber) { 831 return SubscriberManager.GetInstance().add(newSubsriber); 832 } 833 /** 834 * 835 * @returns a globally unique id to be assigned to a IPropertySubscriber objet 836 * Use MakeId() to assign a IPropertySubscriber object an id before calling @see add() . 837 * 838 * @since 9 839 */ 840 static MakeId() { 841 return SubscriberManager.GetInstance().makeId(); 842 } 843 /** 844 * Check number of registered Subscriber / registered IDs. 845 * @returns number of registered unique ids. 846 * 847 * @since 9 848 */ 849 static NumberOfSubscribers() { 850 return SubscriberManager.GetInstance().numberOfSubscribers(); 851 } 852 /** 853 * 854 * internal (non-SDK) methods below 855 * 856 */ 857 /** 858 * Get singleton, create it on first call 859 * @returns SubscriberManager singleton 860 * 861 * internal function 862 * This function will be removed soon, use static functions instead! 863 * Note: Fnction gets used by transpiler output for both full update and partial update 864 */ 865 static Get() { 866 if (!SubscriberManager.instance_) { 867 SubscriberManager.instance_ = new SubscriberManager(); 868 } 869 return SubscriberManager.instance_; 870 } 871 /** 872 * Get singleton, create it on first call 873 * @returns SubscriberManager singleton 874 * 875 * internal function 876 */ 877 static GetInstance() { 878 if (!SubscriberManager.instance_) { 879 SubscriberManager.instance_ = new SubscriberManager(); 880 } 881 return SubscriberManager.instance_; 882 } 883 /** 884 * for debug purposes dump all known subscriber's info to comsole 885 * 886 * not a public / sdk function 887 */ 888 static DumpSubscriberInfo() { 889 SubscriberManager.GetInstance().dumpSubscriberInfo(); 890 } 891 /** 892 * not a public / sdk function 893 * @see Has 894 */ 895 has(id) { 896 return this.subscriberById_.has(id); 897 } 898 /** 899 * not a public / sdk function 900 * @see Get 901 */ 902 get(id) { 903 return this.subscriberById_.get(id); 904 } 905 /** 906 * not a public / sdk function 907 * @see Delete 908 */ 909 delete(id) { 910 if (!this.has(id)) { 911 stateMgmtConsole.warn(`SubscriberManager.delete unknown id ${id} `); 912 return false; 913 } 914 return this.subscriberById_.delete(id); 915 } 916 /** 917 * not a public / sdk function 918 * @see Add 919 */ 920 add(newSubsriber) { 921 if (this.has(newSubsriber.id__())) { 922 return false; 923 } 924 this.subscriberById_.set(newSubsriber.id__(), newSubsriber); 925 return true; 926 } 927 /** 928 * Method for testing purposes 929 * @returns number of subscribers 930 * 931 * not a public / sdk function 932 */ 933 numberOfSubscribers() { 934 return this.subscriberById_.size; 935 } 936 /** 937 * for debug purposes dump all known subscriber's info to comsole 938 * 939 * not a public / sdk function 940 */ 941 dumpSubscriberInfo() { 942 943 for (let [id, subscriber] of this.subscriberById_) { 944 945 } 946 947 } 948 /** 949 * 950 * @returns a globally unique id to be assigned to a Subscriber 951 */ 952 makeId() { 953 return ViewStackProcessor.MakeUniqueId(); 954 } 955} 956/* 957 * Copyright (c) 2022 Huawei Device Co., Ltd. 958 * Licensed under the Apache License, Version 2.0 (the "License"); 959 * you may not use this file except in compliance with the License. 960 * You may obtain a copy of the License at 961 * 962 * http://www.apache.org/licenses/LICENSE-2.0 963 * 964 * Unless required by applicable law or agreed to in writing, software 965 * distributed under the License is distributed on an "AS IS" BASIS, 966 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 967 * See the License for the specific language governing permissions and 968 * limitations under the License. 969 */ 970/** 971 * 972 * SubscribedAbstractProperty is base class of ObservedPropertyAbstract 973 * and includes these 3 functions that are part of the SDK. 974 * 975 * SubscribedAbstractProperty<T> is the return type of 976 * - AppStorage static functions Link(), Prop(), SetAndLink(), and SetAndProp() 977 * - LocalStorage methods link(), prop(), setAndLink(), and setAndProp() 978 * 979 * 'T' can be boolean, string, number or custom class. 980 * 981 * Main functions 982 * @see get() reads the linked AppStorage/LocalStorage property value, 983 * @see set(newValue) write a new value to the synched AppStorage/LocalStorage property value 984 * @see aboutToBeDeleted() ends the sync relationship with the AppStorage/LocalStorage property 985 * The app must call this function before the SubscribedAbstractProperty<T> object 986 * goes out of scope. 987 * 988 * @since 7 989*/ 990class SubscribedAbstractProperty { 991} 992/* 993 * Copyright (c) 2021-2022 Huawei Device Co., Ltd. 994 * Licensed under the Apache License, Version 2.0 (the "License"); 995 * you may not use this file except in compliance with the License. 996 * You may obtain a copy of the License at 997 * 998 * http://www.apache.org/licenses/LICENSE-2.0 999 * 1000 * Unless required by applicable law or agreed to in writing, software 1001 * distributed under the License is distributed on an "AS IS" BASIS, 1002 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1003 * See the License for the specific language governing permissions and 1004 * limitations under the License. 1005 */ 1006/** 1007 * 1008 * SubscriableAbstract 1009 * 1010 * This class is part of the SDK. 1011 * @since 9 1012 * 1013 * SubscriableAbstract is an abstract class that manages subscribers 1014 * to value changes. These subscribers are the implementation of 1015 * @State, @Link, @Provide, @Consume decorated variables inside the 1016 * framework. Each using @State, @Link, etc., decorated varibale in 1017 * a @Component will make its own subscription. When the component 1018 * is created the subscription is added, and before the component 1019 * is deleted it unsubscribes 1020 * 1021 * An application may extend SubscriableAbstract for a custom class 1022 * that manages state data. @State, @Link, @Provide, @Consume 1023 * decorated variables can hold an Object that is instance of 1024 * SubscribaleAbstract. 1025 * 1026 * About lifecycle: It is legal use for two @Components with two @State 1027 * decorated variables to share the same SubscribaleAbstract object. 1028 * Each such decorated variable implementation makes its own 1029 * subscription to the SubscribaleAbstract object. Hence, when both variables 1030 * have unsubscribed the SubscribaleAbstract custom class may do its own 1031 * de-initilialization, e.g. release held external resources. 1032 * 1033 * How to extend: 1034 * A subclass manages the get and set to one or several properties on its own. 1035 * The subclass needs to notify all relevant value changes to the framework for the 1036 * UI to be updated. Notification should only be given for class properties that 1037 * are used to generate the UI. 1038 * 1039 * A subclass must call super() in its constructor to let this base class 1040 * initialize itself. 1041 * 1042 * A subclass must call 'notifyPropertyHasChanged*(' after the relevant property 1043 * has changes. The framework will notify all dependent components to re-render. 1044 * 1045 * A sub-class may overwrite the 'addOwningProperty' function to add own 1046 * functionality, but it must call super.addowningOwningProperty(..). E.g. 1047 * the sub-class could connect to external resources upon the first subscriber. 1048 * 1049 * A sub-class may also overwrite the 'removeOwningProperty' function or 1050 * 'removeOwningPropertyById' function to add own functionality, 1051 * but it must call super.removeOwningProperty(..). 1052 * E.g. the sub-class could release held external resources upon loosing the 1053 * last subscriber. 1054 * 1055 */ 1056class SubscribaleAbstract { 1057 /** 1058 * make sure to call super() from subclass constructor! 1059 * 1060 * @since 9 1061 */ 1062 constructor() { 1063 this.owningProperties_ = new Set(); 1064 1065 } 1066 /** 1067 * A subsclass must call this function whenever one of its properties has 1068 * changed that is used to construct the UI. 1069 * @param propName name of the change property 1070 * @param newValue the property value after the change 1071 * 1072 * @since 9 1073 */ 1074 notifyPropertyHasChanged(propName, newValue) { 1075 1076 this.owningProperties_.forEach((subscribedId) => { 1077 var owningProperty = SubscriberManager.Find(subscribedId); 1078 if (owningProperty) { 1079 if ('hasChanged' in owningProperty) { 1080 owningProperty.hasChanged(newValue); 1081 } 1082 if ('propertyHasChanged' in owningProperty) { 1083 owningProperty.propertyHasChanged(propName); 1084 } 1085 } 1086 else { 1087 stateMgmtConsole.error(`SubscribaleAbstract: notifyHasChanged: unknown subscriber.'${subscribedId}' error!.`); 1088 } 1089 }); 1090 } 1091 /** 1092 * Method used by the framework to add subscribing decorated variables 1093 * Subclass may overwrite this function but must call the function of the base 1094 * class from its own implementation. 1095 * @param subscriber new subscriber that implements ISinglePropertyChangeSubscriber 1096 * and/or IMultiPropertiesChangeSubscriber interfaces 1097 * 1098 * @since 9 1099 */ 1100 addOwningProperty(subscriber) { 1101 1102 this.owningProperties_.add(subscriber.id__()); 1103 } 1104 /** 1105 * Method used by the framework to ubsubscribing decorated variables 1106 * Subclass may overwrite this function but must call the function of the base 1107 * class from its own implementation. 1108 * @param subscriber subscriber that implements ISinglePropertyChangeSubscriber 1109 * and/or IMultiPropertiesChangeSubscriber interfaces 1110 * 1111 * @since 9 1112 */ 1113 removeOwningProperty(property) { 1114 return this.removeOwningPropertyById(property.id__()); 1115 } 1116 /** 1117 * Same as @see removeOwningProperty() but by Subscriber id. 1118 * @param subscriberId 1119 * 1120 * @since 9 1121 */ 1122 removeOwningPropertyById(subscriberId) { 1123 1124 this.owningProperties_.delete(subscriberId); 1125 } 1126} 1127/* 1128 * Copyright (c) 2021 Huawei Device Co., Ltd. 1129 * Licensed under the Apache License, Version 2.0 (the "License"); 1130 * you may not use this file except in compliance with the License. 1131 * You may obtain a copy of the License at 1132 * 1133 * http://www.apache.org/licenses/LICENSE-2.0 1134 * 1135 * Unless required by applicable law or agreed to in writing, software 1136 * distributed under the License is distributed on an "AS IS" BASIS, 1137 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1138 * See the License for the specific language governing permissions and 1139 * limitations under the License. 1140 */ 1141/** 1142 * PersistentStorage 1143 * 1144 * Keeps current values of select AppStorage property properties persisted to file. 1145 * 1146 * since 9 1147 */ 1148class PersistentStorage { 1149 /** 1150 * all following methods are framework internal 1151 */ 1152 constructor() { 1153 this.links_ = new Map(); 1154 this.id_ = SubscriberManager.MakeId(); 1155 SubscriberManager.Add(this); 1156 } 1157 /** 1158 * 1159 * @param storage method to be used by the framework to set the backend 1160 * this is to be done during startup 1161 * 1162 * internal function, not part of the SDK 1163 * 1164 */ 1165 static ConfigureBackend(storage) { 1166 PersistentStorage.Storage_ = storage; 1167 } 1168 /** 1169 * private, use static functions! 1170 */ 1171 static GetOrCreate() { 1172 if (PersistentStorage.Instance_) { 1173 // already initialized 1174 return PersistentStorage.Instance_; 1175 } 1176 PersistentStorage.Instance_ = new PersistentStorage(); 1177 return PersistentStorage.Instance_; 1178 } 1179 /** 1180 * 1181 * internal function, not part of the SDK 1182 */ 1183 static AboutToBeDeleted() { 1184 if (!PersistentStorage.Instance_) { 1185 return; 1186 } 1187 PersistentStorage.GetOrCreate().aboutToBeDeleted(); 1188 PersistentStorage.Instance_ = undefined; 1189 } 1190 /** 1191 * Add property 'key' to AppStorage properties whose current value will be 1192 * persistemt. 1193 * If AppStorage does not include this property it will be added and initializes 1194 * with given value 1195 * 1196 * @since 9 1197 * 1198 * @param key property name 1199 * @param defaultValue If AppStorage does not include this property it will be initialized with this value 1200 * 1201 */ 1202 static PersistProp(key, defaultValue) { 1203 PersistentStorage.GetOrCreate().persistProp(key, defaultValue); 1204 } 1205 /** 1206 * Reverse of @see PersistProp 1207 * @param key no longer persist the property named key 1208 * 1209 * @since 9 1210 */ 1211 static DeleteProp(key) { 1212 PersistentStorage.GetOrCreate().deleteProp(key); 1213 } 1214 /** 1215 * Persist given AppStorage properties with given names. 1216 * If a property does not exist in AppStorage, add it and initialize it with given value 1217 * works as @see PersistProp for multiple properties. 1218 * 1219 * @param properties 1220 * 1221 * @since 9 1222 * 1223 */ 1224 static PersistProps(properties) { 1225 PersistentStorage.GetOrCreate().persistProps(properties); 1226 } 1227 /** 1228 * Inform persisted AppStorage property names 1229 * @returns array of AppStorage keys 1230 * 1231 * @since 9 1232 */ 1233 static Keys() { 1234 let result = []; 1235 const it = PersistentStorage.GetOrCreate().keys(); 1236 let val = it.next(); 1237 while (!val.done) { 1238 result.push(val.value); 1239 val = it.next(); 1240 } 1241 return result; 1242 } 1243 /** 1244 * This methid offers a way to force writing the property value with given 1245 * key to persistent storage. 1246 * In the general case this is unnecessary as the framework observed changes 1247 * and triggers writing to disk by itself. For nested objects (e.g. array of 1248 * objects) however changes of a property of a property as not observed. This 1249 * is the case where the application needs to signal to the framework. 1250 * 1251 * @param key property that has changed 1252 * 1253 * @since 9 1254 * 1255 */ 1256 static NotifyHasChanged(propName) { 1257 1258 PersistentStorage.Storage_.set(propName, PersistentStorage.GetOrCreate().links_.get(propName).get()); 1259 } 1260 keys() { 1261 return this.links_.keys(); 1262 } 1263 persistProp(propName, defaultValue) { 1264 if (this.persistProp1(propName, defaultValue)) { 1265 // persist new prop 1266 1267 PersistentStorage.Storage_.set(propName, this.links_.get(propName).get()); 1268 } 1269 } 1270 // helper function to persist a property 1271 // does everything except writing prop to disk 1272 persistProp1(propName, defaultValue) { 1273 if (defaultValue == null || defaultValue == undefined) { 1274 stateMgmtConsole.error(`PersistentStorage: persistProp for ${propName} called with 'null' or 'undefined' default value!`); 1275 return false; 1276 } 1277 if (this.links_.get(propName)) { 1278 stateMgmtConsole.warn(`PersistentStorage: persistProp: ${propName} is already persisted`); 1279 return false; 1280 } 1281 let link = AppStorage.Link(propName, this); 1282 if (link) { 1283 1284 this.links_.set(propName, link); 1285 } 1286 else { 1287 let newValue = PersistentStorage.Storage_.get(propName); 1288 let returnValue; 1289 if (!newValue) { 1290 1291 returnValue = defaultValue; 1292 } 1293 else { 1294 returnValue = newValue; 1295 } 1296 link = AppStorage.SetAndLink(propName, returnValue, this); 1297 this.links_.set(propName, link); 1298 1299 } 1300 return true; 1301 } 1302 persistProps(properties) { 1303 properties.forEach(property => this.persistProp1(property.key, property.defaultValue)); 1304 this.write(); 1305 } 1306 deleteProp(propName) { 1307 let link = this.links_.get(propName); 1308 if (link) { 1309 link.aboutToBeDeleted(); 1310 this.links_.delete(propName); 1311 PersistentStorage.Storage_.delete(propName); 1312 1313 } 1314 else { 1315 stateMgmtConsole.warn(`PersistentStorage: '${propName}' is not a persisted property warning.`); 1316 } 1317 } 1318 write() { 1319 this.links_.forEach((link, propName, map) => { 1320 1321 PersistentStorage.Storage_.set(propName, link.get()); 1322 }); 1323 } 1324 propertyHasChanged(info) { 1325 1326 this.write(); 1327 } 1328 // public required by the interface, use the static method instead! 1329 aboutToBeDeleted() { 1330 1331 this.links_.forEach((val, key, map) => { 1332 1333 val.aboutToBeDeleted(); 1334 }); 1335 this.links_.clear(); 1336 SubscriberManager.Delete(this.id__()); 1337 PersistentStorage.Storage_.clear(); 1338 } 1339 id__() { 1340 return this.id_; 1341 } 1342} 1343PersistentStorage.Instance_ = undefined; 1344; 1345/* 1346 * Copyright (c) 2021 Huawei Device Co., Ltd. 1347 * Licensed under the Apache License, Version 2.0 (the "License"); 1348 * you may not use this file except in compliance with the License. 1349 * You may obtain a copy of the License at 1350 * 1351 * http://www.apache.org/licenses/LICENSE-2.0 1352 * 1353 * Unless required by applicable law or agreed to in writing, software 1354 * distributed under the License is distributed on an "AS IS" BASIS, 1355 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1356 * See the License for the specific language governing permissions and 1357 * limitations under the License. 1358 */ 1359/** 1360 * Environment 1361 * 1362 * Injects device properties ("environment") into AppStorage 1363 * 1364 */ 1365class Environment { 1366 constructor() { 1367 this.props_ = new Map(); 1368 Environment.EnvBackend_.onValueChanged(this.onValueChanged.bind(this)); 1369 } 1370 static GetOrCreate() { 1371 if (Environment.Instance_) { 1372 // already initialized 1373 return Environment.Instance_; 1374 } 1375 Environment.Instance_ = new Environment(); 1376 return Environment.Instance_; 1377 } 1378 static ConfigureBackend(envBackend) { 1379 Environment.EnvBackend_ = envBackend; 1380 } 1381 static AboutToBeDeleted() { 1382 if (!Environment.Instance_) { 1383 return; 1384 } 1385 Environment.GetOrCreate().aboutToBeDeleted(); 1386 Environment.Instance_ = undefined; 1387 } 1388 static EnvProp(key, value) { 1389 return Environment.GetOrCreate().envProp(key, value); 1390 } 1391 static EnvProps(props) { 1392 Environment.GetOrCreate().envProps(props); 1393 } 1394 static Keys() { 1395 return Environment.GetOrCreate().keys(); 1396 } 1397 envProp(key, value) { 1398 let prop = AppStorage.Prop(key); 1399 if (prop) { 1400 stateMgmtConsole.warn(`Environment: envProp '${key}': Property already exists in AppStorage. Not using environment property.`); 1401 return false; 1402 } 1403 let tmp; 1404 switch (key) { 1405 case "accessibilityEnabled": 1406 tmp = Environment.EnvBackend_.getAccessibilityEnabled(); 1407 break; 1408 case "colorMode": 1409 tmp = Environment.EnvBackend_.getColorMode(); 1410 break; 1411 case "fontScale": 1412 tmp = Environment.EnvBackend_.getFontScale(); 1413 break; 1414 case "fontWeightScale": 1415 tmp = Environment.EnvBackend_.getFontWeightScale().toFixed(2); 1416 break; 1417 case "layoutDirection": 1418 tmp = Environment.EnvBackend_.getLayoutDirection(); 1419 break; 1420 case "languageCode": 1421 tmp = Environment.EnvBackend_.getLanguageCode(); 1422 break; 1423 default: 1424 tmp = value; 1425 } 1426 prop = AppStorage.SetAndProp(key, tmp); 1427 this.props_.set(key, prop); 1428 1429 } 1430 envProps(properties) { 1431 properties.forEach(property => { 1432 this.envProp(property.key, property.defaultValue); 1433 1434 }); 1435 } 1436 keys() { 1437 let result = []; 1438 const it = this.props_.keys(); 1439 let val = it.next(); 1440 while (!val.done) { 1441 result.push(val.value); 1442 val = it.next(); 1443 } 1444 return result; 1445 } 1446 onValueChanged(key, value) { 1447 let ok = AppStorage.Set(key, value); 1448 if (ok) { 1449 1450 } 1451 else { 1452 stateMgmtConsole.warn(`Environment: onValueChanged: error changing ${key}! See results above.`); 1453 } 1454 } 1455 aboutToBeDeleted() { 1456 this.props_.forEach((val, key, map) => { 1457 val.aboutToBeDeleted(); 1458 AppStorage.Delete(key); 1459 }); 1460 } 1461} 1462Environment.Instance_ = undefined; 1463/* 1464 * Copyright (c) 2022 Huawei Device Co., Ltd. 1465 * Licensed under the Apache License, Version 2.0 (the "License"); 1466 * you may not use this file except in compliance with the License. 1467 * You may obtain a copy of the License at 1468 * 1469 * http://www.apache.org/licenses/LICENSE-2.0 1470 * 1471 * Unless required by applicable law or agreed to in writing, software 1472 * distributed under the License is distributed on an "AS IS" BASIS, 1473 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1474 * See the License for the specific language governing permissions and 1475 * limitations under the License. 1476 */ 1477/** 1478 * state mgmt library uses its own class for logging 1479* allows to remap separately from other use of aceConsole 1480* 1481* everything in this file is framework internal 1482*/ 1483class stateMgmtConsole { 1484 static log(...args) { 1485 aceConsole.log(...args); 1486 } 1487 static debug(...args) { 1488 aceConsole.debug(...args); 1489 } 1490 static info(...args) { 1491 aceConsole.info(...args); 1492 } 1493 static warn(...args) { 1494 aceConsole.warn(...args); 1495 } 1496 static error(...args) { 1497 aceConsole.error(...args); 1498 } 1499} 1500/* 1501 * Copyright (c) 2022 Huawei Device Co., Ltd. 1502 * Licensed under the Apache License, Version 2.0 (the "License"); 1503 * you may not use this file except in compliance with the License. 1504 * You may obtain a copy of the License at 1505 * 1506 * http://www.apache.org/licenses/LICENSE-2.0 1507 * 1508 * Unless required by applicable law or agreed to in writing, software 1509 * distributed under the License is distributed on an "AS IS" BASIS, 1510 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1511 * See the License for the specific language governing permissions and 1512 * limitations under the License. 1513 */ 1514class DistributedStorage { 1515 constructor(sessionId, notifier) { 1516 this.links_ = new Map(); 1517 this.id_ = SubscriberManager.MakeId(); 1518 SubscriberManager.Add(this); 1519 this.aviliable_ = false; 1520 this.notifier_ = notifier; 1521 } 1522 keys() { 1523 let result = []; 1524 const it = this.links_.keys(); 1525 let val = it.next(); 1526 while (!val.done) { 1527 result.push(val.value); 1528 val = it.next(); 1529 } 1530 return result; 1531 } 1532 distributeProp(propName, defaultValue) { 1533 if (this.link(propName, defaultValue)) { 1534 1535 } 1536 } 1537 distributeProps(properties) { 1538 properties.forEach(property => this.link(property.key, property.defaultValue)); 1539 } 1540 link(propName, defaultValue) { 1541 if (defaultValue == null || defaultValue == undefined) { 1542 stateMgmtConsole.error(`DistributedStorage: linkProp for ${propName} called with 'null' or 'undefined' default value!`); 1543 return false; 1544 } 1545 if (this.links_.get(propName)) { 1546 stateMgmtConsole.warn(`DistributedStorage: linkProp: ${propName} is already exist`); 1547 return false; 1548 } 1549 let link = AppStorage.Link(propName, this); 1550 if (link) { 1551 1552 this.links_.set(propName, link); 1553 this.setDistributedProp(propName, defaultValue); 1554 } 1555 else { 1556 let returnValue = defaultValue; 1557 if (this.aviliable_) { 1558 let newValue = this.getDistributedProp(propName); 1559 if (newValue == null) { 1560 1561 this.setDistributedProp(propName, defaultValue); 1562 } 1563 else { 1564 returnValue = newValue; 1565 } 1566 } 1567 link = AppStorage.SetAndLink(propName, returnValue, this); 1568 this.links_.set(propName, link); 1569 1570 } 1571 return true; 1572 } 1573 deleteProp(propName) { 1574 let link = this.links_.get(propName); 1575 if (link) { 1576 link.aboutToBeDeleted(); 1577 this.links_.delete(propName); 1578 if (this.aviliable_) { 1579 this.storage_.delete(propName); 1580 } 1581 } 1582 else { 1583 stateMgmtConsole.warn(`DistributedStorage: '${propName}' is not a distributed property warning.`); 1584 } 1585 } 1586 write(key) { 1587 let link = this.links_.get(key); 1588 if (link) { 1589 this.setDistributedProp(key, link.get()); 1590 } 1591 } 1592 // public required by the interface, use the static method instead! 1593 aboutToBeDeleted() { 1594 1595 this.links_.forEach((val, key, map) => { 1596 1597 val.aboutToBeDeleted(); 1598 }); 1599 this.links_.clear(); 1600 SubscriberManager.Delete(this.id__()); 1601 } 1602 id__() { 1603 return this.id_; 1604 } 1605 propertyHasChanged(info) { 1606 1607 this.write(info); 1608 } 1609 onDataOnChange(propName) { 1610 let link = this.links_.get(propName); 1611 let newValue = this.getDistributedProp(propName); 1612 if (link && newValue != null) { 1613 1614 link.set(newValue); 1615 } 1616 } 1617 onConnected(status) { 1618 1619 if (!this.aviliable_) { 1620 this.syncProp(); 1621 this.aviliable_ = true; 1622 } 1623 if (this.notifier_ != null) { 1624 this.notifier_(status); 1625 } 1626 } 1627 syncProp() { 1628 this.links_.forEach((val, key) => { 1629 let newValue = this.getDistributedProp(key); 1630 if (newValue == null) { 1631 this.setDistributedProp(key, val.get()); 1632 } 1633 else { 1634 val.set(newValue); 1635 } 1636 }); 1637 } 1638 setDistributedProp(key, value) { 1639 if (!this.aviliable_) { 1640 stateMgmtConsole.warn(`DistributedStorage is not aviliable`); 1641 return; 1642 } 1643 stateMgmtConsole.error(`DistributedStorage value is object ${key}-${JSON.stringify(value)}`); 1644 if (typeof value == 'object') { 1645 this.storage_.set(key, JSON.stringify(value)); 1646 return; 1647 } 1648 this.storage_.set(key, value); 1649 } 1650 getDistributedProp(key) { 1651 let value = this.storage_.get(key); 1652 if (typeof value == 'string') { 1653 try { 1654 let returnValue = JSON.parse(value); 1655 return returnValue; 1656 } 1657 finally { 1658 return value; 1659 } 1660 } 1661 return value; 1662 } 1663} 1664; 1665/* 1666 * Copyright (c) 2021-2022 Huawei Device Co., Ltd. 1667 * Licensed under the Apache License, Version 2.0 (the "License"); 1668 * you may not use this file except in compliance with the License. 1669 * You may obtain a copy of the License at 1670 * 1671 * http://www.apache.org/licenses/LICENSE-2.0 1672 * 1673 * Unless required by applicable law or agreed to in writing, software 1674 * distributed under the License is distributed on an "AS IS" BASIS, 1675 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1676 * See the License for the specific language governing permissions and 1677 * limitations under the License. 1678 */ 1679/** 1680* @Observed Decorator function, use 1681* @Observed class ClassA { ... } 1682* when defining ClassA 1683* 1684* Can also be used to create a new Object and wrap it in 1685* ObservedObject by calling 1686* obsObj = Observed(ClassA)(params to ClassA constructor) 1687* 1688* Note this works only for classes, not for ClassA[] 1689* Also does not work for classes with genetics it seems 1690* In that case use factory function 1691* obsObj = ObservedObject.createNew<ClassA[]>([]) 1692*/ 1693function Observed(target) { 1694 var original = target; 1695 // the new constructor behaviour 1696 var f = function (...args) { 1697 1698 return ObservedObject.createNew(new original(...args), undefined); 1699 // return new ObservedObject<C>(new original(...args), undefined); 1700 }; 1701 Object.setPrototypeOf(f, Object.getPrototypeOf(original)); 1702 // return new constructor (will override original) 1703 return f; 1704} 1705class SubscribableHandler { 1706 constructor(owningProperty) { 1707 this.owningProperties_ = new Set(); 1708 if (owningProperty) { 1709 this.addOwningProperty(owningProperty); 1710 } 1711 1712 } 1713 addOwningProperty(subscriber) { 1714 if (subscriber) { 1715 1716 this.owningProperties_.add(subscriber.id__()); 1717 } 1718 else { 1719 stateMgmtConsole.warn(`SubscribableHandler: addOwningProperty: undefined subscriber. - Internal error?`); 1720 } 1721 } 1722 /* 1723 the inverse function of createOneWaySync or createTwoWaySync 1724 */ 1725 removeOwningProperty(property) { 1726 return this.removeOwningPropertyById(property.id__()); 1727 } 1728 removeOwningPropertyById(subscriberId) { 1729 1730 this.owningProperties_.delete(subscriberId); 1731 } 1732 notifyPropertyHasChanged(propName, newValue) { 1733 1734 this.owningProperties_.forEach((subscribedId) => { 1735 var owningProperty = SubscriberManager.Find(subscribedId); 1736 if (owningProperty) { 1737 if ('hasChanged' in owningProperty) { 1738 owningProperty.hasChanged(newValue); 1739 } 1740 if ('propertyHasChanged' in owningProperty) { 1741 owningProperty.propertyHasChanged(propName); 1742 } 1743 } 1744 else { 1745 stateMgmtConsole.warn(`SubscribableHandler: notifyHasChanged: unknown subscriber.'${subscribedId}' error!.`); 1746 } 1747 }); 1748 } 1749 get(target, property) { 1750 return (property === SubscribableHandler.IS_OBSERVED_OBJECT) ? true : 1751 (property === SubscribableHandler.RAW_OBJECT) ? target : target[property]; 1752 } 1753 set(target, property, newValue) { 1754 switch (property) { 1755 case SubscribableHandler.SUBSCRIBE: 1756 // assignment obsObj[SubscribableHandler.SUBSCRCRIBE] = subscriber 1757 this.addOwningProperty(newValue); 1758 return true; 1759 break; 1760 case SubscribableHandler.UNSUBSCRIBE: 1761 // assignment obsObj[SubscribableHandler.UN_SUBSCRCRIBE] = subscriber 1762 this.removeOwningProperty(newValue); 1763 return true; 1764 break; 1765 default: 1766 if (target[property] == newValue) { 1767 return true; 1768 } 1769 target[property] = newValue; 1770 this.notifyPropertyHasChanged(property.toString(), newValue); 1771 return true; 1772 break; 1773 } 1774 // unreachable 1775 return false; 1776 } 1777} 1778SubscribableHandler.IS_OBSERVED_OBJECT = Symbol("_____is_observed_object__"); 1779SubscribableHandler.RAW_OBJECT = Symbol("_____raw_object__"); 1780SubscribableHandler.SUBSCRIBE = Symbol("_____subscribe__"); 1781SubscribableHandler.UNSUBSCRIBE = Symbol("_____unsubscribe__"); 1782class ExtendableProxy { 1783 constructor(obj, handler) { 1784 return new Proxy(obj, handler); 1785 } 1786} 1787class ObservedObject extends ExtendableProxy { 1788 /** 1789 * Factory function for ObservedObjects / 1790 * wrapping of objects for proxying 1791 * 1792 * @param rawObject unproxied Object or ObservedObject 1793 * @param objOwner owner of this Object to sign uop for propertyChange 1794 * notifications 1795 * @returns the rawObject if object is already an ObservedObject, 1796 * otherwise the newly created ObservedObject 1797 */ 1798 static createNew(rawObject, owningProperty) { 1799 if (rawObject === null || rawObject === undefined) { 1800 stateMgmtConsole.error(`ObservedObject.CreateNew, input object must not be null or undefined.`); 1801 return null; 1802 } 1803 if (ObservedObject.IsObservedObject(rawObject)) { 1804 ObservedObject.addOwningProperty(rawObject, owningProperty); 1805 return rawObject; 1806 } 1807 else { 1808 return new ObservedObject(rawObject, owningProperty); 1809 } 1810 } 1811 /* 1812 Return the unproxied object 'inside' the ObservedObject / the ES6 Proxy 1813 no set observation, no notification of changes! 1814 Use with caution, do not store any references 1815 */ 1816 static GetRawObject(obj) { 1817 return !ObservedObject.IsObservedObject(obj) ? obj : obj[SubscribableHandler.RAW_OBJECT]; 1818 } 1819 /** 1820 * 1821 * @param obj anything 1822 * @returns true if the parameter is an Object wrpped with a ObservedObject 1823 * Note: Since ES6 Proying is transparent, 'instance of' will not work. Use 1824 * this static function instead. 1825 */ 1826 static IsObservedObject(obj) { 1827 return obj ? (obj[SubscribableHandler.IS_OBSERVED_OBJECT] === true) : false; 1828 } 1829 static addOwningProperty(obj, subscriber) { 1830 if (!ObservedObject.IsObservedObject(obj)) { 1831 return false; 1832 } 1833 obj[SubscribableHandler.SUBSCRIBE] = subscriber; 1834 return true; 1835 } 1836 static removeOwningProperty(obj, subscriber) { 1837 if (!ObservedObject.IsObservedObject(obj)) { 1838 return false; 1839 } 1840 obj[SubscribableHandler.UNSUBSCRIBE] = subscriber; 1841 return true; 1842 } 1843 /** 1844 * Create a new ObservableObject and subscribe its owner to propertyHasChanged 1845 * ntifications 1846 * @param obj raw Object, if obj is a ObservableOject throws an error 1847 * @param objectOwner 1848 */ 1849 constructor(obj, objectOwningProperty) { 1850 if (ObservedObject.IsObservedObject(obj)) { 1851 throw new Error("Invalid constructor argument error: ObservableObject contructor called with an ObservedObject as parameer"); 1852 } 1853 let handler = new SubscribableHandler(objectOwningProperty); 1854 super(obj, handler); 1855 if (ObservedObject.IsObservedObject(obj)) { 1856 stateMgmtConsole.error("ObservableOject constructor: INTERNAL ERROR: after jsObj is observedObject already"); 1857 } 1858 } // end of constructor 1859} 1860/* 1861 * Copyright (c) 2021 Huawei Device Co., Ltd. 1862 * Licensed under the Apache License, Version 2.0 (the "License"); 1863 * you may not use this file except in compliance with the License. 1864 * You may obtain a copy of the License at 1865 * 1866 * http://www.apache.org/licenses/LICENSE-2.0 1867 * 1868 * Unless required by applicable law or agreed to in writing, software 1869 * distributed under the License is distributed on an "AS IS" BASIS, 1870 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1871 * See the License for the specific language governing permissions and 1872 * limitations under the License. 1873 */ 1874/* 1875 manage subscriptions to a property 1876 managing the property is left to sub 1877 classes 1878 Extended by ObservedProperty, SyncedPropertyOneWay 1879 and SyncedPropertyTwoWay 1880*/ 1881class ObservedPropertyAbstract extends SubscribedAbstractProperty { 1882 constructor(subscribeMe, info) { 1883 super(); 1884 this.subscribers_ = new Set(); 1885 this.id_ = SubscriberManager.MakeId(); 1886 SubscriberManager.Add(this); 1887 if (subscribeMe) { 1888 this.subscribers_.add(subscribeMe.id__()); 1889 } 1890 if (info) { 1891 this.info_ = info; 1892 } 1893 } 1894 aboutToBeDeleted() { 1895 SubscriberManager.Delete(this.id__()); 1896 } 1897 id__() { 1898 return this.id_; 1899 } 1900 info() { 1901 return this.info_; 1902 } 1903 setInfo(propName) { 1904 if (propName && propName != "") { 1905 this.info_ = propName; 1906 } 1907 } 1908 // Partial Update "*PU" classes will overwrite 1909 getUnmonitored() { 1910 return this.get(); 1911 } 1912 subscribeMe(subscriber) { 1913 1914 this.subscribers_.add(subscriber.id__()); 1915 } 1916 /* 1917 the inverse function of createOneWaySync or createTwoWaySync 1918 */ 1919 unlinkSuscriber(subscriberId) { 1920 this.subscribers_.delete(subscriberId); 1921 } 1922 notifyHasChanged(newValue) { 1923 1924 this.subscribers_.forEach((subscribedId) => { 1925 var subscriber = SubscriberManager.Find(subscribedId); 1926 if (subscriber) { 1927 if ('hasChanged' in subscriber) { 1928 subscriber.hasChanged(newValue); 1929 } 1930 if ('propertyHasChanged' in subscriber) { 1931 subscriber.propertyHasChanged(this.info_); 1932 } 1933 } 1934 else { 1935 stateMgmtConsole.warn(`ObservedPropertyAbstract[${this.id__()}, '${this.info() || "unknown"}']: notifyHasChanged: unknown subscriber ID '${subscribedId}' error!`); 1936 } 1937 }); 1938 } 1939 notifyPropertyRead() { 1940 1941 this.subscribers_.forEach((subscribedId) => { 1942 var subscriber = SubscriberManager.Find(subscribedId); 1943 if (subscriber) { 1944 if ('propertyRead' in subscriber) { 1945 subscriber.propertyRead(this.info_); 1946 } 1947 } 1948 }); 1949 } 1950 /* 1951 return numebr of subscribers to this property 1952 mostly useful for unit testin 1953 */ 1954 numberOfSubscrbers() { 1955 return this.subscribers_.size; 1956 } 1957 /** 1958 * provide a factory function that creates a SynchedPropertyXXXX of choice 1959 * that uses 'this' as source 1960 * @param factoryFunc 1961 * @returns 1962 */ 1963 createSync(factoryFunc) { 1964 return factoryFunc(this); 1965 } 1966 /** 1967 * depreciated SDK function, not used anywhere by the framework 1968 */ 1969 createTwoWaySync(subscribeMe, info) { 1970 stateMgmtConsole.warn("Using depreciated method 'createTwoWaySync'!"); 1971 return this.createLink(subscribeMe, info); 1972 } 1973 /** 1974 * depreciated SDK function, not used anywhere by the framework 1975 */ 1976 createOneWaySync(subscribeMe, info) { 1977 stateMgmtConsole.warn("Using depreciated method 'createOneWaySync' !"); 1978 return this.createProp(subscribeMe, info); 1979 } 1980 /** 1981 * factory function for concrete 'object' or 'simple' ObservedProperty object 1982 * depending if value is Class object 1983 * or simple type (boolean | number | string) 1984 * @param value 1985 * @param owningView 1986 * @param thisPropertyName 1987 * @returns either 1988 */ 1989 static CreateObservedObject(value, owningView, thisPropertyName) { 1990 return (typeof value === "object") ? 1991 new ObservedPropertyObject(value, owningView, thisPropertyName) 1992 : new ObservedPropertySimple(value, owningView, thisPropertyName); 1993 } 1994} 1995/* 1996 * Copyright (c) 2021 Huawei Device Co., Ltd. 1997 * Licensed under the Apache License, Version 2.0 (the "License"); 1998 * you may not use this file except in compliance with the License. 1999 * You may obtain a copy of the License at 2000 * 2001 * http://www.apache.org/licenses/LICENSE-2.0 2002 * 2003 * Unless required by applicable law or agreed to in writing, software 2004 * distributed under the License is distributed on an "AS IS" BASIS, 2005 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2006 * See the License for the specific language governing permissions and 2007 * limitations under the License. 2008 */ 2009/** 2010 * ObservedPropertyObjectAbstract 2011 * 2012 * all definitions in this file are framework internal 2013 * 2014 * common base class of ObservedPropertyObject and 2015 * SyncedObjectPropertyTwoWay 2016 * adds the createObjectLink to the ObservedPropertyAbstract base 2017 */ 2018class ObservedPropertyObjectAbstract extends ObservedPropertyAbstract { 2019 constructor(owningView, thisPropertyName) { 2020 super(owningView, thisPropertyName); 2021 } 2022} 2023/* 2024 * Copyright (c) 2021 Huawei Device Co., Ltd. 2025 * Licensed under the Apache License, Version 2.0 (the "License"); 2026 * you may not use this file except in compliance with the License. 2027 * You may obtain a copy of the License at 2028 * 2029 * http://www.apache.org/licenses/LICENSE-2.0 2030 * 2031 * Unless required by applicable law or agreed to in writing, software 2032 * distributed under the License is distributed on an "AS IS" BASIS, 2033 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2034 * See the License for the specific language governing permissions and 2035 * limitations under the License. 2036 */ 2037/** 2038 * 2039 * ObservedPropertySimpleAbstract 2040 * 2041 * all definitions in this file are framework internal 2042 */ 2043class ObservedPropertySimpleAbstract extends ObservedPropertyAbstract { 2044 constructor(owningView, propertyName) { 2045 super(owningView, propertyName); 2046 } 2047} 2048/* 2049 * Copyright (c) 2021-2022 Huawei Device Co., Ltd. 2050 * Licensed under the Apache License, Version 2.0 (the "License"); 2051 * you may not use this file except in compliance with the License. 2052 * You may obtain a copy of the License at 2053 * 2054 * http://www.apache.org/licenses/LICENSE-2.0 2055 * 2056 * Unless required by applicable law or agreed to in writing, software 2057 * distributed under the License is distributed on an "AS IS" BASIS, 2058 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2059 * See the License for the specific language governing permissions and 2060 * limitations under the License. 2061 */ 2062/** 2063 * ObservedPropertyObject 2064 * 2065 * all definitions in this file are framework internal 2066 * 2067 * class that holds an actual property value of type T 2068 * uses its base class to manage subscribers to this 2069 * property. 2070*/ 2071class ObservedPropertyObject extends ObservedPropertyObjectAbstract { 2072 constructor(value, owningView, propertyName) { 2073 super(owningView, propertyName); 2074 this.setValueInternal(value); 2075 } 2076 aboutToBeDeleted(unsubscribeMe) { 2077 this.unsubscribeFromOwningProperty(); 2078 if (unsubscribeMe) { 2079 this.unlinkSuscriber(unsubscribeMe.id__()); 2080 } 2081 super.aboutToBeDeleted(); 2082 } 2083 // notification from ObservedObject value one of its 2084 // props has chnaged. Implies the ObservedProperty has changed 2085 // Note: this function gets called when in this case: 2086 // thisProp.aObsObj.aProp = 47 a object prop gets changed 2087 // It is NOT called when 2088 // thisProp.aObsObj = new ClassA 2089 hasChanged(newValue) { 2090 2091 this.notifyHasChanged(this.wrappedValue_); 2092 } 2093 unsubscribeFromOwningProperty() { 2094 if (this.wrappedValue_) { 2095 if (this.wrappedValue_ instanceof SubscribaleAbstract) { 2096 this.wrappedValue_.removeOwningProperty(this); 2097 } 2098 else { 2099 ObservedObject.removeOwningProperty(this.wrappedValue_, this); 2100 } 2101 } 2102 } 2103 /* 2104 actually update this.wrappedValue_ 2105 called needs to do value change check 2106 and also notify with this.aboutToChange(); 2107 */ 2108 setValueInternal(newValue) { 2109 if (typeof newValue !== 'object') { 2110 2111 return false; 2112 } 2113 this.unsubscribeFromOwningProperty(); 2114 if (ObservedObject.IsObservedObject(newValue)) { 2115 2116 ObservedObject.addOwningProperty(newValue, this); 2117 this.wrappedValue_ = newValue; 2118 } 2119 else if (newValue instanceof SubscribaleAbstract) { 2120 2121 this.wrappedValue_ = newValue; 2122 this.wrappedValue_.addOwningProperty(this); 2123 } 2124 else { 2125 2126 this.wrappedValue_ = ObservedObject.createNew(newValue, this); 2127 } 2128 return true; 2129 } 2130 get() { 2131 2132 this.notifyPropertyRead(); 2133 return this.wrappedValue_; 2134 } 2135 set(newValue) { 2136 if (this.wrappedValue_ == newValue) { 2137 2138 return; 2139 } 2140 2141 this.setValueInternal(newValue); 2142 this.notifyHasChanged(newValue); 2143 } 2144 /** 2145 * These functions are used 2146 * LocalStorage.link (also in partial update config) 2147 * (FU)View.initializeConsumeinitializeConsume 2148 */ 2149 createLink(subscribeOwner, linkPropName) { 2150 return new SynchedPropertyObjectTwoWay(this, subscribeOwner, linkPropName); 2151 } 2152 createProp(subscribeOwner, linkPropName) { 2153 throw new Error("Creating a 'Prop' property is unsupported for Object type property value."); 2154 } 2155} 2156/* 2157 * Copyright (c) 2021-2022 Huawei Device Co., Ltd. 2158 * Licensed under the Apache License, Version 2.0 (the "License"); 2159 * you may not use this file except in compliance with the License. 2160 * You may obtain a copy of the License at 2161 * 2162 * http://www.apache.org/licenses/LICENSE-2.0 2163 * 2164 * Unless required by applicable law or agreed to in writing, software 2165 * distributed under the License is distributed on an "AS IS" BASIS, 2166 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2167 * See the License for the specific language governing permissions and 2168 * limitations under the License. 2169 */ 2170/** 2171 * ObservedPropertySimple 2172 * 2173 * all definitions in this file are framework internal 2174 */ 2175class ObservedPropertySimple extends ObservedPropertySimpleAbstract { 2176 constructor(value, owningView, propertyName) { 2177 super(owningView, propertyName); 2178 if (typeof value === "object") { 2179 throw new SyntaxError("ObservedPropertySimple value must not be an object"); 2180 } 2181 this.setValueInternal(value); 2182 } 2183 aboutToBeDeleted(unsubscribeMe) { 2184 if (unsubscribeMe) { 2185 this.unlinkSuscriber(unsubscribeMe.id__()); 2186 } 2187 super.aboutToBeDeleted(); 2188 } 2189 hasChanged(newValue) { 2190 2191 this.notifyHasChanged(this.wrappedValue_); 2192 } 2193 /* 2194 actually update this.wrappedValue_ 2195 called needs to do value change check 2196 and also notify with this.aboutToChange(); 2197 */ 2198 setValueInternal(newValue) { 2199 2200 this.wrappedValue_ = newValue; 2201 } 2202 get() { 2203 2204 this.notifyPropertyRead(); 2205 return this.wrappedValue_; 2206 } 2207 set(newValue) { 2208 if (this.wrappedValue_ == newValue) { 2209 2210 return; 2211 } 2212 2213 this.setValueInternal(newValue); 2214 this.notifyHasChanged(newValue); 2215 } 2216 /** 2217 * These functions are meant for use in connection with the App Stoage and 2218 * business logic implementation. 2219 * the created Link and Prop will update when 'this' property value 2220 * changes. 2221 */ 2222 createLink(subscribeOwner, linkPropName) { 2223 return ((subscribeOwner !== undefined) && ("rerender" in subscribeOwner)) ? 2224 new SynchedPropertySimpleTwoWayPU(this, subscribeOwner, linkPropName) : 2225 new SynchedPropertySimpleTwoWay(this, subscribeOwner, linkPropName); 2226 } 2227 createProp(subscribeOwner, linkPropName) { 2228 return new SynchedPropertySimpleOneWaySubscribing(this, subscribeOwner, linkPropName); 2229 } 2230} 2231/* 2232 * Copyright (c) 2021 Huawei Device Co., Ltd. 2233 * Licensed under the Apache License, Version 2.0 (the "License"); 2234 * you may not use this file except in compliance with the License. 2235 * You may obtain a copy of the License at 2236 * 2237 * http://www.apache.org/licenses/LICENSE-2.0 2238 * 2239 * Unless required by applicable law or agreed to in writing, software 2240 * distributed under the License is distributed on an "AS IS" BASIS, 2241 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2242 * See the License for the specific language governing permissions and 2243 * limitations under the License. 2244 */ 2245/** 2246 * SynchedPropertyObjectTwoWay 2247 * 2248 * all definitions in this file are framework internal 2249 */ 2250class SynchedPropertyObjectTwoWay extends ObservedPropertyObjectAbstract { 2251 constructor(linkSource, owningChildView, thisPropertyName) { 2252 super(owningChildView, thisPropertyName); 2253 this.changeNotificationIsOngoing_ = false; 2254 this.linkedParentProperty_ = linkSource; 2255 // register to the parent property 2256 this.linkedParentProperty_.subscribeMe(this); 2257 // register to the ObservedObject 2258 ObservedObject.addOwningProperty(this.getObject(), this); 2259 } 2260 /* 2261 like a destructor, need to call this before deleting 2262 the property. 2263 */ 2264 aboutToBeDeleted() { 2265 if (this.linkedParentProperty_) { 2266 // unregister from parent of this link 2267 this.linkedParentProperty_.unlinkSuscriber(this.id__()); 2268 // unregister from the ObservedObject 2269 ObservedObject.removeOwningProperty(this.getObject(), this); 2270 } 2271 super.aboutToBeDeleted(); 2272 } 2273 getObject() { 2274 this.notifyPropertyRead(); 2275 return (this.linkedParentProperty_ ? this.linkedParentProperty_.get() : undefined); 2276 } 2277 setObject(newValue) { 2278 if (this.linkedParentProperty_) { 2279 this.linkedParentProperty_.set(newValue); 2280 } 2281 } 2282 // this object is subscriber to ObservedObject 2283 // will call this cb function when property has changed 2284 hasChanged(newValue) { 2285 if (!this.changeNotificationIsOngoing_) { 2286 2287 this.notifyHasChanged(this.getObject()); 2288 } 2289 } 2290 // get 'read through` from the ObservedProperty 2291 get() { 2292 2293 return this.getObject(); 2294 } 2295 // set 'writes through` to the ObservedProperty 2296 set(newValue) { 2297 if (this.getObject() == newValue) { 2298 2299 return; 2300 } 2301 2302 ObservedObject.removeOwningProperty(this.getObject(), this); 2303 // the purpose of the changeNotificationIsOngoing_ is to avoid 2304 // circular notifications @Link -> source @State -> other but alos same @Link 2305 this.changeNotificationIsOngoing_ = true; 2306 this.setObject(newValue); 2307 ObservedObject.addOwningProperty(this.getObject(), this); 2308 this.notifyHasChanged(newValue); 2309 this.changeNotificationIsOngoing_ = false; 2310 } 2311 /** 2312 * These functions are meant for use in connection with the App Stoage and 2313 * business logic implementation. 2314 * the created Link and Prop will update when 'this' property value 2315 * changes. 2316 */ 2317 createLink(subscribeOwner, linkPropName) { 2318 return new SynchedPropertyObjectTwoWay(this, subscribeOwner, linkPropName); 2319 } 2320 createProp(subscribeOwner, linkPropName) { 2321 throw new Error("Creating a 'Prop' property is unsupported for Object type property value."); 2322 } 2323} 2324/* 2325 * Copyright (c) 2021-2022 Huawei Device Co., Ltd. 2326 * Licensed under the Apache License, Version 2.0 (the "License"); 2327 * you may not use this file except in compliance with the License. 2328 * You may obtain a copy of the License at 2329 * 2330 * http://www.apache.org/licenses/LICENSE-2.0 2331 * 2332 * Unless required by applicable law or agreed to in writing, software 2333 * distributed under the License is distributed on an "AS IS" BASIS, 2334 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2335 * See the License for the specific language governing permissions and 2336 * limitations under the License. 2337 */ 2338/** 2339 * SynchedPropertySimpleOneWay 2340 * 2341 * all definitions in this file are framework internal 2342 */ 2343class SynchedPropertySimpleOneWay extends ObservedPropertySimpleAbstract { 2344 constructor(value, subscribeMe, info) { 2345 super(subscribeMe, info); 2346 // add a test here that T is a simple type 2347 this.wrappedValue_ = value; 2348 } 2349 /* 2350 like a destructor, need to call this before deleting 2351 the property. 2352 */ 2353 aboutToBeDeleted() { 2354 super.aboutToBeDeleted(); 2355 } 2356 // get 'read through` from the ObservedProperty 2357 get() { 2358 2359 this.notifyPropertyRead(); 2360 return this.wrappedValue_; 2361 } 2362 set(newValue) { 2363 if (this.wrappedValue_ == newValue) { 2364 2365 return; 2366 } 2367 2368 this.wrappedValue_ = newValue; 2369 this.notifyHasChanged(newValue); 2370 } 2371 /** 2372 * These functions are meant for use in connection with the App Stoage and 2373 * business logic implementation. 2374 * the created Link and Prop will update when 'this' property value 2375 * changes. 2376 */ 2377 createLink(subscribeOwner, linkPropName) { 2378 throw new Error("Can not create a 'Link' from a 'Prop' property. "); 2379 } 2380 createProp(subscribeOwner, linkPropName) { 2381 throw new Error("Method not supported, create a SynchedPropertySimpleOneWaySubscribing from, where to create a Prop."); 2382 } 2383} 2384/* 2385 This exrension of SynchedPropertySimpleOneWay needs to be used for AppStorage 2386 because it needs to be notified about the source property changing 2387 ( there is no re-render process as in Views to update the wrappedValue ) 2388*/ 2389class SynchedPropertySimpleOneWaySubscribing extends SynchedPropertySimpleOneWay { 2390 constructor(linkedProperty, subscribeMe, info) { 2391 super(linkedProperty.get(), subscribeMe, info); 2392 this.linkedParentProperty_ = linkedProperty; 2393 this.linkedParentProperty_.subscribeMe(this); 2394 } 2395 aboutToBeDeleted() { 2396 // unregister from parent of this prop 2397 this.linkedParentProperty_.unlinkSuscriber(this.id__()); 2398 super.aboutToBeDeleted(); 2399 } 2400 hasChanged(newValue) { 2401 2402 this.set(newValue); 2403 } 2404 /** 2405 * These functions are meant for use in connection with the App Stoage and 2406 * business logic implementation. 2407 * the created Link and Prop will update when 'this' property value 2408 * changes. 2409 */ 2410 createLink(subscribeOwner, linkPropName) { 2411 throw new Error("Can not create a 'Link' from a 'Prop' property. "); 2412 } 2413 createProp(subscribeOwner, propPropName) { 2414 return new SynchedPropertySimpleOneWaySubscribing(this, subscribeOwner, propPropName); 2415 } 2416} 2417/* 2418 * Copyright (c) 2021-2022 Huawei Device Co., Ltd. 2419 * Licensed under the Apache License, Version 2.0 (the "License"); 2420 * you may not use this file except in compliance with the License. 2421 * You may obtain a copy of the License at 2422 * 2423 * http://www.apache.org/licenses/LICENSE-2.0 2424 * 2425 * Unless required by applicable law or agreed to in writing, software 2426 * distributed under the License is distributed on an "AS IS" BASIS, 2427 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2428 * See the License for the specific language governing permissions and 2429 * limitations under the License. 2430 */ 2431/** 2432 * SynchedPropertySimpleTwoWay 2433 * 2434 * all definitions in this file are framework internal 2435 */ 2436class SynchedPropertySimpleTwoWay extends ObservedPropertySimpleAbstract { 2437 constructor(source, owningView, owningViewPropNme) { 2438 super(owningView, owningViewPropNme); 2439 this.changeNotificationIsOngoing_ = false; 2440 this.source_ = source; 2441 this.source_.subscribeMe(this); 2442 } 2443 /* 2444 like a destructor, need to call this before deleting 2445 the property. 2446 */ 2447 aboutToBeDeleted() { 2448 if (this.source_) { 2449 this.source_.unlinkSuscriber(this.id__()); 2450 this.source_ = undefined; 2451 } 2452 super.aboutToBeDeleted(); 2453 } 2454 // this object is subscriber to SynchedPropertySimpleTwoWay 2455 // will call this cb function when property has changed 2456 // a set (newValue) is not done because get reads through for the source_ 2457 hasChanged(newValue) { 2458 if (!this.changeNotificationIsOngoing_) { 2459 2460 this.notifyHasChanged(newValue); 2461 } 2462 } 2463 // get 'read through` from the ObservedProperty 2464 get() { 2465 2466 if (!this.source_) { 2467 stateMgmtConsole.error(`SynchedPropertySimpleTwoWay[${this.id__()}IP, '${this.info() || "unknown"}'] source_ is undefined: get value is undefined.`); 2468 return undefined; 2469 } 2470 this.notifyPropertyRead(); 2471 return this.source_.get(); 2472 } 2473 // set 'writes through` to the ObservedProperty 2474 set(newValue) { 2475 if (!this.source_) { 2476 stateMgmtConsole.error(`SynchedPropertySimpleTwoWay[${this.id__()}IP, '${this.info() || "unknown"}'] source_ is undefined: set '${newValue}' ignoring.`); 2477 return; 2478 } 2479 if (this.source_.get() == newValue) { 2480 2481 return; 2482 } 2483 2484 // the source_ ObservedProeprty will call: this.hasChanged(newValue); 2485 // the purpose of the changeNotificationIsOngoing_ is to avoid 2486 // circular notifications @Link -> source @State -> other but alos same @Link 2487 this.changeNotificationIsOngoing_ = true; 2488 this.source_.set(newValue); 2489 this.notifyHasChanged(newValue); 2490 this.changeNotificationIsOngoing_ = false; 2491 } 2492 /** 2493 * These functions are meant for use in connection with the App Stoage and 2494 * business logic implementation. 2495 * the created Link and Prop will update when 'this' property value 2496 * changes. 2497 */ 2498 createLink(subscribeOwner, linkPropName) { 2499 return new SynchedPropertySimpleTwoWay(this, subscribeOwner, linkPropName); 2500 } 2501 createProp(subscribeOwner, propPropName) { 2502 return new SynchedPropertySimpleOneWaySubscribing(this, subscribeOwner, propPropName); 2503 } 2504} 2505/* 2506 * Copyright (c) 2021-2022 Huawei Device Co., Ltd. 2507 * Licensed under the Apache License, Version 2.0 (the "License"); 2508 * you may not use this file except in compliance with the License. 2509 * You may obtain a copy of the License at 2510 * 2511 * http://www.apache.org/licenses/LICENSE-2.0 2512 * 2513 * Unless required by applicable law or agreed to in writing, software 2514 * distributed under the License is distributed on an "AS IS" BASIS, 2515 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2516 * See the License for the specific language governing permissions and 2517 * limitations under the License. 2518 */ 2519/** 2520 * SynchedPropertyNesedObject 2521 * 2522 * all definitions in this file are framework internal 2523 */ 2524class SynchedPropertyNesedObject extends ObservedPropertyObjectAbstract { 2525 /** 2526 * Construct a Property of a su component that links to a variable of parent view that holds an ObservedObject 2527 * example 2528 * this.b.$a with b of type PC and a of type C, or 2529 * this.$b[5] with this.b of type PC and array item b[5] of type C; 2530 * 2531 * @param subscribeMe 2532 * @param propName 2533 */ 2534 constructor(obsObject, owningChildView, propertyName) { 2535 super(owningChildView, propertyName); 2536 this.obsObject_ = obsObject; 2537 // register to the ObservedObject 2538 ObservedObject.addOwningProperty(this.obsObject_, this); 2539 } 2540 /* 2541 like a destructor, need to call this before deleting 2542 the property. 2543 */ 2544 aboutToBeDeleted() { 2545 // unregister from the ObservedObject 2546 ObservedObject.removeOwningProperty(this.obsObject_, this); 2547 super.aboutToBeDeleted(); 2548 } 2549 // this object is subscriber to ObservedObject 2550 // will call this cb function when property has changed 2551 hasChanged(newValue) { 2552 2553 this.notifyHasChanged(this.obsObject_); 2554 } 2555 // get 'read through` from the ObservedProperty 2556 get() { 2557 2558 this.notifyPropertyRead(); 2559 return this.obsObject_; 2560 } 2561 // set 'writes through` to the ObservedProperty 2562 set(newValue) { 2563 if (this.obsObject_ == newValue) { 2564 2565 return; 2566 } 2567 2568 // unsubscribe from the old value ObservedObject 2569 ObservedObject.removeOwningProperty(this.obsObject_, this); 2570 this.obsObject_ = newValue; 2571 // subscribe to the new value ObservedObject 2572 ObservedObject.addOwningProperty(this.obsObject_, this); 2573 // notify value change to subscribing View 2574 this.notifyHasChanged(this.obsObject_); 2575 } 2576 /** 2577 * These functions are meant for use in connection with the App Stoage and 2578 * business logic implementation. 2579 * the created Link and Prop will update when 'this' property value 2580 * changes. 2581 */ 2582 createLink(subscribeOwner, linkPropName) { 2583 throw new Error("Method not supported for property linking to a nested objects."); 2584 } 2585 createProp(subscribeOwner, linkPropName) { 2586 throw new Error("Creating a 'Prop' proerty is unsuppoeted for Object type prperty value."); 2587 } 2588} 2589/* 2590 * Copyright (c) 2021-2022 Huawei Device Co., Ltd. 2591 * Licensed under the Apache License, Version 2.0 (the "License"); 2592 * you may not use this file except in compliance with the License. 2593 * You may obtain a copy of the License at 2594 * 2595 * http://www.apache.org/licenses/LICENSE-2.0 2596 * 2597 * Unless required by applicable law or agreed to in writing, software 2598 * distributed under the License is distributed on an "AS IS" BASIS, 2599 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2600 * See the License for the specific language governing permissions and 2601 * limitations under the License. 2602 */ 2603// Nativeview 2604// implemented in C++ for release 2605// and in utest/view_native_mock.ts for testing 2606class View extends NativeViewFullUpdate { 2607 /** 2608 * Create a View 2609 * 2610 * 1. option: top level View, specify 2611 * - compilerAssignedUniqueChildId must specify 2612 * - parent=undefined 2613 * - localStorage must provide if @LocalSTorageLink/Prop variables are used 2614 * in this View or descendant Views. 2615 * 2616 * 2. option: not a top level View 2617 * - compilerAssignedUniqueChildId must specify 2618 * - parent must specify 2619 * - localStorage do not specify, will inherit from parent View. 2620 * 2621 * @param compilerAssignedUniqueChildId Tw 2622 * @param parent 2623 * @param localStorage 2624 */ 2625 constructor(compilerAssignedUniqueChildId, parent, localStorage) { 2626 super(compilerAssignedUniqueChildId, parent); 2627 this.propsUsedForRender = new Set(); 2628 this.isRenderingInProgress = false; 2629 this.watchedProps = new Map(); 2630 // my LocalStorge instance, shared with ancestor Views. 2631 // create a default instance on demand if none is initialized 2632 this.localStoragebackStore_ = undefined; 2633 this.id_ = SubscriberManager.MakeId(); 2634 this.providedVars_ = parent ? new Map(parent.providedVars_) 2635 : new Map(); 2636 this.localStoragebackStore_ = undefined; 2637 if (parent) { 2638 // this View is not a top-level View 2639 2640 this.setCardId(parent.getCardId()); 2641 this.localStorage_ = parent.localStorage_; 2642 } 2643 else if (localStorage) { 2644 this.localStorage_ = localStorage; 2645 2646 } 2647 SubscriberManager.Add(this); 2648 2649 } 2650 get localStorage_() { 2651 if (!this.localStoragebackStore_) { 2652 2653 this.localStoragebackStore_ = new LocalStorage({ /* emty */}); 2654 } 2655 return this.localStoragebackStore_; 2656 } 2657 set localStorage_(instance) { 2658 if (!instance) { 2659 // setting to undefined not allowed 2660 return; 2661 } 2662 if (this.localStoragebackStore_) { 2663 stateMgmtConsole.error(`${this.constructor.name} is setting LocalStorage instance twice`); 2664 } 2665 this.localStoragebackStore_ = instance; 2666 } 2667 // globally unique id, this is different from compilerAssignedUniqueChildId! 2668 id__() { 2669 return this.id_; 2670 } 2671 // temporary function, do not use, it will be removed soon! 2672 // prupsoe is to allow eDSL transpiler to fix a bug that 2673 // relies on this method 2674 id() { 2675 return this.id__(); 2676 } 2677 propertyHasChanged(info) { 2678 if (info) { 2679 // need to sync container instanceId to switch instanceId in C++ side. 2680 this.syncInstanceId(); 2681 if (this.propsUsedForRender.has(info)) { 2682 2683 this.markNeedUpdate(); 2684 } 2685 else { 2686 2687 } 2688 let cb = this.watchedProps.get(info); 2689 if (cb) { 2690 2691 cb.call(this, info); 2692 } 2693 this.restoreInstanceId(); 2694 } // if info avail. 2695 } 2696 propertyRead(info) { 2697 2698 if (info && (info != "unknown") && this.isRenderingInProgress) { 2699 this.propsUsedForRender.add(info); 2700 } 2701 } 2702 // for test purposes 2703 propertiesNeededToRender() { 2704 return this.propsUsedForRender; 2705 } 2706 aboutToRender() { 2707 2708 // reset 2709 this.propsUsedForRender = new Set(); 2710 this.isRenderingInProgress = true; 2711 } 2712 aboutToContinueRender() { 2713 // do not reset 2714 this.isRenderingInProgress = true; 2715 } 2716 onRenderDone() { 2717 this.isRenderingInProgress = false; 2718 2719 } 2720 /** 2721 * Function to be called from the constructor of the sub component 2722 * to register a @Watch varibale 2723 * @param propStr name of the variable. Note from @Provide and @Consume this is 2724 * the variable name and not the alias! 2725 * @param callback application defined member function of sub-class 2726 */ 2727 declareWatch(propStr, callback) { 2728 this.watchedProps.set(propStr, callback); 2729 } 2730 /** 2731 * This View @Provide's a variable under given name 2732 * Call this function from the constructor of the sub class 2733 * @param providedPropName either the variable name or the alias defined as 2734 * decorator param 2735 * @param store the backing store object for this variable (not the get/set variable!) 2736 */ 2737 addProvidedVar(providedPropName, store) { 2738 if (this.providedVars_.has(providedPropName)) { 2739 throw new ReferenceError(`${this.constructor.name}: duplicate @Provide property with name ${providedPropName}. 2740 Property with this name is provided by one of the ancestor Views already.`); 2741 } 2742 this.providedVars_.set(providedPropName, store); 2743 } 2744 /** 2745 * Method for the sub-class to call from its constructor for resolving 2746 * a @Consume variable and initializing its backing store 2747 * with the yncedPropertyTwoWay<T> object created from the 2748 * @Provide variable's backing store. 2749 * @param providedPropName the name of the @Provide'd variable. 2750 * This is either the @Consume decortor parameter, or variable name. 2751 * @param consumeVarName the @Consume variable name (not the 2752 * @Consume decortor parameter) 2753 * @returns initiaizing value of the @Consume backing store 2754 */ 2755 initializeConsume(providedPropName, consumeVarName) { 2756 let providedVarStore = this.providedVars_.get(providedPropName); 2757 if (providedVarStore === undefined) { 2758 throw new ReferenceError(`${this.constructor.name}: missing @Provide property with name ${providedPropName}. 2759 Fail to resolve @Consume(${providedPropName}).`); 2760 } 2761 return providedVarStore.createLink(this, consumeVarName); 2762 } 2763} 2764/* 2765 * Copyright (c) 2022 Huawei Device Co., Ltd. 2766 * Licensed under the Apache License, Version 2.0 (the "License"); 2767 * you may not use this file except in compliance with the License. 2768 * You may obtain a copy of the License at 2769 * 2770 * http://www.apache.org/licenses/LICENSE-2.0 2771 * 2772 * Unless required by applicable law or agreed to in writing, software 2773 * distributed under the License is distributed on an "AS IS" BASIS, 2774 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2775 * See the License for the specific language governing permissions and 2776 * limitations under the License. 2777 */ 2778/** 2779 * ObservedPropertyAbstractPU aka ObservedPropertyAbstract for partial update 2780 * 2781 * all definitions in this file are framework internal 2782 */ 2783class ObservedPropertyAbstractPU extends ObservedPropertyAbstract { 2784 constructor(subscribingView, viewName) { 2785 super(subscribingView, viewName); 2786 this.dependentElementIds_ = new Set(); 2787 } 2788 notifyHasChanged(newValue) { 2789 2790 this.subscribers_.forEach((subscribedId) => { 2791 var subscriber = SubscriberManager.Find(subscribedId); 2792 if (subscriber) { 2793 if ('hasChanged' in subscriber) { 2794 subscriber.hasChanged(newValue); 2795 } 2796 if ('viewPropertyHasChanged' in subscriber) { 2797 subscriber.viewPropertyHasChanged(this.info_, this.dependentElementIds_); 2798 } 2799 else if ('propertyHasChanged' in subscriber) { 2800 subscriber.propertyHasChanged(this.info_); 2801 } 2802 } 2803 else { 2804 stateMgmtConsole.warn(`ObservedPropertyAbstract[${this.id__()}, '${this.info() || "unknown"}']: notifyHasChanged: unknown subscriber ID '${subscribedId}' error!`); 2805 } 2806 }); 2807 } 2808 notifyPropertyRead() { 2809 super.notifyPropertyRead(); 2810 this.recordDependentUpdate(); 2811 } 2812 markDependentElementsDirty(view) { 2813 // TODO ace-ets2bundle, framework, compilated apps need to update together 2814 // this function will be removed after a short transiition periode 2815 stateMgmtConsole.warn(`markDependentElementsDirty no longer supported. App will work ok, but 2816 please update your ace-ets2bundle and recompile your application!`); 2817 } 2818 /** 2819 * factory function for concrete 'object' or 'simple' ObservedProperty object 2820 * depending if value is Class object 2821 * or simple type (boolean | number | string) 2822 * @param value 2823 * @param owningView 2824 * @param thisPropertyName 2825 * @returns either 2826 */ 2827 static CreateObservedObject(value, owningView, thisPropertyName) { 2828 return (typeof value === "object") ? 2829 new ObservedPropertyObject(value, owningView, thisPropertyName) 2830 : new ObservedPropertySimple(value, owningView, thisPropertyName); 2831 } 2832 /** 2833 * during 'get' access recording take note of the created component and its elmtId 2834 * and add this component to the list of components who are dependent on this property 2835 */ 2836 recordDependentUpdate() { 2837 const elmtId = ViewStackProcessor.GetElmtIdToAccountFor(); 2838 if (elmtId < 0) { 2839 // not access recording 2840 return; 2841 } 2842 2843 this.dependentElementIds_.add(elmtId); 2844 } 2845 purgeDependencyOnElmtId(rmElmtId) { 2846 2847 this.dependentElementIds_.delete(rmElmtId); 2848 } 2849 SetPropertyUnchanged() { 2850 // function to be removed 2851 // keep it here until transpiler is updated. 2852 } 2853 // FIXME check, is this used from AppStorage. 2854 // unified Appstorage, what classes to use, and the API 2855 createLink(subscribeOwner, linkPropName) { 2856 throw new Error("Can not create a AppStorage 'Link' from a @State property. "); 2857 } 2858 createProp(subscribeOwner, linkPropName) { 2859 throw new Error("Can not create a AppStorage 'Prop' from a @State property. "); 2860 } 2861} 2862/* 2863 * Copyright (c) 2021 Huawei Device Co., Ltd. 2864 * Licensed under the Apache License, Version 2.0 (the "License"); 2865 * you may not use this file except in compliance with the License. 2866 * You may obtain a copy of the License at 2867 * 2868 * http://www.apache.org/licenses/LICENSE-2.0 2869 * 2870 * Unless required by applicable law or agreed to in writing, software 2871 * distributed under the License is distributed on an "AS IS" BASIS, 2872 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2873 * See the License for the specific language governing permissions and 2874 * limitations under the License. 2875 */ 2876/** 2877 * ObservedPropertyObjectAbstractPU 2878 * 2879 * common bbase class of ObservedPropertyObjectPU and 2880 * SyncedObjectPropertyTwoWayPU 2881 * adds the createObjectLink to the ObservedPropertyAbstract base 2882 * 2883 * all definitions in this file are framework internal 2884 */ 2885class ObservedPropertyObjectAbstractPU extends ObservedPropertyAbstractPU { 2886 constructor(owningView, thisPropertyName) { 2887 super(owningView, thisPropertyName); 2888 } 2889} 2890/* 2891 * Copyright (c) 2021 Huawei Device Co., Ltd. 2892 * Licensed under the Apache License, Version 2.0 (the "License"); 2893 * you may not use this file except in compliance with the License. 2894 * You may obtain a copy of the License at 2895 * 2896 * http://www.apache.org/licenses/LICENSE-2.0 2897 * 2898 * Unless required by applicable law or agreed to in writing, software 2899 * distributed under the License is distributed on an "AS IS" BASIS, 2900 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2901 * See the License for the specific language governing permissions and 2902 * limitations under the License. 2903 */ 2904/** 2905 * ObservedPropertySimpleAbstractPU 2906 * 2907 * all definitions in this file are framework internal 2908 */ 2909class ObservedPropertySimpleAbstractPU extends ObservedPropertyAbstractPU { 2910 constructor(owningView, propertyName) { 2911 super(owningView, propertyName); 2912 } 2913} 2914/* 2915 * Copyright (c) 2022 Huawei Device Co., Ltd. 2916 * Licensed under the Apache License, Version 2.0 (the "License"); 2917 * you may not use this file except in compliance with the License. 2918 * You may obtain a copy of the License at 2919 * 2920 * http://www.apache.org/licenses/LICENSE-2.0 2921 * 2922 * Unless required by applicable law or agreed to in writing, software 2923 * distributed under the License is distributed on an "AS IS" BASIS, 2924 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2925 * See the License for the specific language governing permissions and 2926 * limitations under the License. 2927 */ 2928/** 2929 * ObservedPropertyObjectPU 2930 * 2931 * all definitions in this file are framework internal 2932 * 2933 * class that holds an actual property value of type T 2934 * uses its base class to manage subscribers to this 2935 * property. 2936*/ 2937class ObservedPropertyObjectPU extends ObservedPropertyObjectAbstractPU { 2938 constructor(value, owningView, propertyName) { 2939 super(owningView, propertyName); 2940 this.setValueInternal(value); 2941 } 2942 aboutToBeDeleted(unsubscribeMe) { 2943 this.unsubscribeFromOwningProperty(); 2944 if (unsubscribeMe) { 2945 this.unlinkSuscriber(unsubscribeMe.id__()); 2946 } 2947 super.aboutToBeDeleted(); 2948 } 2949 // notification from ObservedObject value one of its 2950 // props has chnaged. Implies the ObservedProperty has changed 2951 // Note: this function gets called when in this case: 2952 // thisProp.aObsObj.aProp = 47 a object prop gets changed 2953 // It is NOT called when 2954 // thisProp.aObsObj = new ClassA 2955 hasChanged(newValue) { 2956 2957 this.notifyHasChanged(this.wrappedValue_); 2958 } 2959 unsubscribeFromOwningProperty() { 2960 if (this.wrappedValue_) { 2961 if (this.wrappedValue_ instanceof SubscribaleAbstract) { 2962 this.wrappedValue_.removeOwningProperty(this); 2963 } 2964 else { 2965 ObservedObject.removeOwningProperty(this.wrappedValue_, this); 2966 } 2967 } 2968 } 2969 /* 2970 actually update this.wrappedValue_ 2971 called needs to do value change check 2972 and also notify with this.aboutToChange(); 2973 */ 2974 setValueInternal(newValue) { 2975 if (typeof newValue !== 'object') { 2976 2977 return false; 2978 } 2979 this.unsubscribeFromOwningProperty(); 2980 if (ObservedObject.IsObservedObject(newValue)) { 2981 2982 ObservedObject.addOwningProperty(newValue, this); 2983 this.wrappedValue_ = newValue; 2984 } 2985 else if (newValue instanceof SubscribaleAbstract) { 2986 2987 this.wrappedValue_ = newValue; 2988 this.wrappedValue_.addOwningProperty(this); 2989 } 2990 else { 2991 2992 this.wrappedValue_ = ObservedObject.createNew(newValue, this); 2993 } 2994 return true; 2995 } 2996 get() { 2997 2998 this.notifyPropertyRead(); 2999 return this.wrappedValue_; 3000 } 3001 getUnmonitored() { 3002 3003 // unmonitored get access , no call to otifyPropertyRead ! 3004 return this.wrappedValue_; 3005 } 3006 set(newValue) { 3007 if (this.wrappedValue_ == newValue) { 3008 3009 return; 3010 } 3011 3012 this.setValueInternal(newValue); 3013 this.notifyHasChanged(newValue); 3014 } 3015} 3016/* 3017 * Copyright (c) 2022 Huawei Device Co., Ltd. 3018 * Licensed under the Apache License, Version 2.0 (the "License"); 3019 * you may not use this file except in compliance with the License. 3020 * You may obtain a copy of the License at 3021 * 3022 * http://www.apache.org/licenses/LICENSE-2.0 3023 * 3024 * Unless required by applicable law or agreed to in writing, software 3025 * distributed under the License is distributed on an "AS IS" BASIS, 3026 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3027 * See the License for the specific language governing permissions and 3028 * limitations under the License. 3029 */ 3030/** 3031 * ObservedPropertySimplePU 3032 * 3033 * class that holds an actual property value of type T 3034 * uses its base class to manage subscribers to this 3035 * property. 3036 * 3037 * all definitions in this file are framework internal 3038*/ 3039class ObservedPropertySimplePU extends ObservedPropertySimpleAbstractPU { 3040 constructor(value, owningView, propertyName) { 3041 super(owningView, propertyName); 3042 if (typeof value === "object") { 3043 throw new SyntaxError("ObservedPropertySimple value must not be an object"); 3044 } 3045 this.setValueInternal(value); 3046 } 3047 aboutToBeDeleted(unsubscribeMe) { 3048 if (unsubscribeMe) { 3049 this.unlinkSuscriber(unsubscribeMe.id__()); 3050 } 3051 super.aboutToBeDeleted(); 3052 } 3053 hasChanged(newValue) { 3054 3055 this.notifyHasChanged(this.wrappedValue_); 3056 } 3057 /* 3058 actually update this.wrappedValue_ 3059 called needs to do value change check 3060 and also notify with this.aboutToChange(); 3061 */ 3062 setValueInternal(newValue) { 3063 3064 this.wrappedValue_ = newValue; 3065 } 3066 getUnmonitored() { 3067 3068 // unmonitored get access , no call to otifyPropertyRead ! 3069 return this.wrappedValue_; 3070 } 3071 get() { 3072 3073 this.notifyPropertyRead(); 3074 return this.wrappedValue_; 3075 } 3076 set(newValue) { 3077 if (this.wrappedValue_ == newValue) { 3078 3079 return; 3080 } 3081 3082 this.setValueInternal(newValue); 3083 this.notifyHasChanged(newValue); 3084 } 3085} 3086/* 3087 * Copyright (c) 2022 Huawei Device Co., Ltd. 3088 * Licensed under the Apache License, Version 2.0 (the "License"); 3089 * you may not use this file except in compliance with the License. 3090 * You may obtain a copy of the License at 3091 * 3092 * http://www.apache.org/licenses/LICENSE-2.0 3093 * 3094 * Unless required by applicable law or agreed to in writing, software 3095 * distributed under the License is distributed on an "AS IS" BASIS, 3096 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3097 * See the License for the specific language governing permissions and 3098 * limitations under the License. 3099 */ 3100class SynchedPropertyObjectOneWayPU extends ObservedPropertyObjectAbstractPU { 3101 constructor(source, owningChildView, thisPropertyName) { 3102 super(owningChildView, thisPropertyName); 3103 if (source && (typeof (source) === "object") && ("notifyHasChanged" in source) && ("subscribeMe" in source)) { 3104 // code path for @(Local)StorageProp 3105 this.source_ = source; 3106 // subscribe to receive value change updates from LocalStorage source property 3107 this.source_.subscribeMe(this); 3108 } 3109 else { 3110 // code path for @Prop 3111 if (!ObservedObject.IsObservedObject(source)) { 3112 stateMgmtConsole.warn(`@Prop ${this.info()} Provided source object's class 3113 lacks @Observed class decorator. Object property changes will not be observed.`); 3114 } 3115 this.source_ = new ObservedPropertyObjectPU(source, this, thisPropertyName); 3116 } 3117 // deep copy source Object and wrap it 3118 this.setWrapperValue(this.source_.get()); 3119 3120 } 3121 /* 3122 like a destructor, need to call this before deleting 3123 the property. 3124 */ 3125 aboutToBeDeleted() { 3126 if (this.source_) { 3127 this.source_.unlinkSuscriber(this.id__()); 3128 this.source_ = undefined; 3129 } 3130 super.aboutToBeDeleted(); 3131 } 3132 // this object is subscriber to this.source_ 3133 // when source notifies a property change, copy its value to local backing store 3134 // the guard for newValue being an Object is needed because also property changes of wrappedValue_ 3135 // are notified via this function. We ignore those, these are handled correctly by propertyHasChanged 3136 hasChanged(newValue) { 3137 if (typeof newValue == "object") { 3138 3139 this.setWrapperValue(newValue); 3140 this.notifyHasChanged(ObservedObject.GetRawObject(this.wrappedValue_)); 3141 } 3142 } 3143 propertyHasChanged(propName) { 3144 3145 this.notifyHasChanged(ObservedObject.GetRawObject(this.wrappedValue_)); 3146 } 3147 getUnmonitored() { 3148 3149 // unmonitored get access , no call to notifyPropertyRead ! 3150 return this.wrappedValue_; 3151 } 3152 // get 'read through` from the ObservedObject 3153 get() { 3154 3155 this.notifyPropertyRead(); 3156 return this.wrappedValue_; 3157 } 3158 // assignment to local variable in the form of this.aProp = <object value> 3159 // set 'writes through` to the ObservedObject 3160 set(newValue) { 3161 if (this.wrappedValue_ == newValue) { 3162 3163 return; 3164 } 3165 3166 if (!ObservedObject.IsObservedObject(newValue)) { 3167 stateMgmtConsole.warn(`@Prop ${this.info()} Set: Provided new object's class 3168 lacks @Observed class decorator. Object property changes will not be observed.`); 3169 } 3170 this.setWrapperValue(newValue); 3171 this.notifyHasChanged(this.wrappedValue_); 3172 } 3173 reset(sourceChangedValue) { 3174 3175 // if set causes an actual change, then, ObservedPropertyObject source_ will call hasChanged 3176 this.source_.set(sourceChangedValue); 3177 } 3178 setWrapperValue(value) { 3179 let rawValue = ObservedObject.GetRawObject(value); 3180 if (rawValue instanceof Array) { 3181 this.wrappedValue_ = ObservedObject.createNew([...rawValue], this); 3182 } 3183 else { 3184 this.wrappedValue_ = ObservedObject.createNew(Object.assign({}, rawValue), this); 3185 } 3186 } 3187} 3188/* 3189 * Copyright (c) 2022 Huawei Device Co., Ltd. 3190 * Licensed under the Apache License, Version 2.0 (the "License"); 3191 * you may not use this file except in compliance with the License. 3192 * You may obtain a copy of the License at 3193 * 3194 * http://www.apache.org/licenses/LICENSE-2.0 3195 * 3196 * Unless required by applicable law or agreed to in writing, software 3197 * distributed under the License is distributed on an "AS IS" BASIS, 3198 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3199 * See the License for the specific language governing permissions and 3200 * limitations under the License. 3201 */ 3202/** 3203 * SynchedPropertyObjectTwoWayPU 3204 * 3205 * all definitions in this file are framework internal 3206 */ 3207class SynchedPropertyObjectTwoWayPU extends ObservedPropertyObjectAbstractPU { 3208 constructor(linkSource, owningChildView, thisPropertyName) { 3209 super(owningChildView, thisPropertyName); 3210 this.changeNotificationIsOngoing_ = false; 3211 if (linkSource) { 3212 this.linkedParentProperty_ = linkSource; 3213 // register to the parent property 3214 this.linkedParentProperty_.subscribeMe(this); 3215 // register to the ObservedObject 3216 ObservedObject.addOwningProperty(this.linkedParentProperty_.get(), this); 3217 } 3218 } 3219 /* 3220 like a destructor, need to call this before deleting 3221 the property. 3222 */ 3223 aboutToBeDeleted() { 3224 // unregister from parent of this link 3225 if (this.linkedParentProperty_) { 3226 this.linkedParentProperty_.unlinkSuscriber(this.id__()); 3227 // unregister from the ObservedObject 3228 ObservedObject.removeOwningProperty(this.linkedParentProperty_.getUnmonitored(), this); 3229 } 3230 super.aboutToBeDeleted(); 3231 } 3232 setObject(newValue) { 3233 if (!this.linkedParentProperty_) { 3234 stateMgmtConsole.warn(`SynchedPropertyObjectTwoWayPU[${this.id__()}, '${this.info() || "unknown"}']: setObject, no linked parent property.`); 3235 return; 3236 } 3237 this.linkedParentProperty_.set(newValue); 3238 } 3239 // this object is subscriber to ObservedObject 3240 // will call this cb function when property has changed 3241 hasChanged(newValue) { 3242 if (!this.changeNotificationIsOngoing_) { 3243 3244 this.notifyHasChanged(this.getUnmonitored()); 3245 } 3246 } 3247 getUnmonitored() { 3248 3249 // unmonitored get access , no call to otifyPropertyRead ! 3250 return (this.linkedParentProperty_ ? this.linkedParentProperty_.getUnmonitored() : undefined); 3251 } 3252 // get 'read through` from the ObservedProperty 3253 get() { 3254 3255 this.notifyPropertyRead(); 3256 return this.getUnmonitored(); 3257 } 3258 // set 'writes through` to the ObservedProperty 3259 set(newValue) { 3260 if (this.getUnmonitored() == newValue) { 3261 3262 return; 3263 } 3264 3265 ObservedObject.removeOwningProperty(this.getUnmonitored(), this); 3266 // avoid circular notifications @Link -> source @State -> other but also back to same @Link 3267 this.changeNotificationIsOngoing_ = true; 3268 this.setObject(newValue); 3269 ObservedObject.addOwningProperty(this.getUnmonitored(), this); 3270 this.notifyHasChanged(newValue); 3271 this.changeNotificationIsOngoing_ = false; 3272 } 3273} 3274/* 3275 * Copyright (c) 2022 Huawei Device Co., Ltd. 3276 * Licensed under the Apache License, Version 2.0 (the "License"); 3277 * you may not use this file except in compliance with the License. 3278 * You may obtain a copy of the License at 3279 * 3280 * http://www.apache.org/licenses/LICENSE-2.0 3281 * 3282 * Unless required by applicable law or agreed to in writing, software 3283 * distributed under the License is distributed on an "AS IS" BASIS, 3284 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3285 * See the License for the specific language governing permissions and 3286 * limitations under the License. 3287 */ 3288/** 3289 * SynchedPropertySimpleOneWayPU 3290 * 3291 * all definitions in this file are framework internal 3292 */ 3293class SynchedPropertySimpleOneWayPU extends ObservedPropertySimpleAbstractPU { 3294 constructor(source, subscribeMe, thisPropertyName) { 3295 super(subscribeMe, thisPropertyName); 3296 if (source && (typeof (source) === "object") && ("notifyHasChanged" in source) && ("subscribeMe" in source)) { 3297 // code path for @(Local)StorageProp 3298 this.source_ = source; 3299 // subscribe to receive value chnage updates from LocalStorge source property 3300 this.source_.subscribeMe(this); 3301 } 3302 else { 3303 // code path for @Prop 3304 this.source_ = new ObservedPropertySimple(source, this, thisPropertyName); 3305 } 3306 // use own backing store for value to avoid 3307 // value changes to be propagated back to source 3308 this.wrappedValue_ = this.source_.getUnmonitored(); 3309 } 3310 /* 3311 like a destructor, need to call this before deleting 3312 the property. 3313 */ 3314 aboutToBeDeleted() { 3315 if (this.source_) { 3316 this.source_.unlinkSuscriber(this.id__()); 3317 this.source_ = undefined; 3318 } 3319 super.aboutToBeDeleted(); 3320 } 3321 // implements ISinglePropertyChangeSubscriber<T>: 3322 // this object is subscriber to this.source_ 3323 // when source notifies a change, copy its value to local backing store 3324 hasChanged(newValue) { 3325 3326 this.wrappedValue_ = newValue; 3327 this.notifyHasChanged(newValue); 3328 } 3329 getUnmonitored() { 3330 3331 // unmonitored get access , no call to otifyPropertyRead ! 3332 return this.wrappedValue_; 3333 } 3334 // get 'read through` from the ObservedProperty 3335 get() { 3336 3337 this.notifyPropertyRead(); 3338 return this.wrappedValue_; 3339 } 3340 set(newValue) { 3341 if (this.wrappedValue_ == newValue) { 3342 3343 return; 3344 } 3345 3346 this.wrappedValue_ = newValue; 3347 this.notifyHasChanged(newValue); 3348 } 3349 reset(sourceChangedValue) { 3350 3351 // if set causes an actual change, then, ObservedPropertySimple source_ will call hasChanged 3352 this.source_.set(sourceChangedValue); 3353 } 3354} 3355/* 3356 * Copyright (c) 2022 Huawei Device Co., Ltd. 3357 * Licensed under the Apache License, Version 2.0 (the "License"); 3358 * you may not use this file except in compliance with the License. 3359 * You may obtain a copy of the License at 3360 * 3361 * http://www.apache.org/licenses/LICENSE-2.0 3362 * 3363 * Unless required by applicable law or agreed to in writing, software 3364 * distributed under the License is distributed on an "AS IS" BASIS, 3365 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3366 * See the License for the specific language governing permissions and 3367 * limitations under the License. 3368 */ 3369/** 3370 * SynchedPropertySimpleTwoWayPU 3371 * 3372 * all definitions in this file are framework internal 3373 */ 3374class SynchedPropertySimpleTwoWayPU extends ObservedPropertySimpleAbstractPU { 3375 constructor(source, owningView, owningViewPropNme) { 3376 super(owningView, owningViewPropNme); 3377 this.changeNotificationIsOngoing_ = false; 3378 this.source_ = source; 3379 this.source_.subscribeMe(this); 3380 } 3381 /* 3382 like a destructor, need to call this before deleting 3383 the property. 3384 */ 3385 aboutToBeDeleted() { 3386 if (this.source_) { 3387 this.source_.unlinkSuscriber(this.id__()); 3388 this.source_ = undefined; 3389 } 3390 super.aboutToBeDeleted(); 3391 } 3392 // this object is subscriber to SynchedPropertySimpleTwoWayPU 3393 // will call this cb function when property has changed 3394 // a set (newValue) is not done because get reads through for the source_ 3395 hasChanged(newValue) { 3396 if (!this.changeNotificationIsOngoing_) { 3397 3398 this.notifyHasChanged(newValue); 3399 } 3400 } 3401 getUnmonitored() { 3402 3403 return (this.source_ ? this.source_.getUnmonitored() : undefined); 3404 } 3405 // get 'read through` from the ObservedProperty 3406 get() { 3407 3408 this.notifyPropertyRead(); 3409 return this.getUnmonitored(); 3410 } 3411 // set 'writes through` to the ObservedProperty 3412 set(newValue) { 3413 if (!this.source_) { 3414 3415 return; 3416 } 3417 if (this.source_.get() == newValue) { 3418 3419 return; 3420 } 3421 3422 // avoid circular notifications @Link -> source @State -> other but also to same @Link 3423 this.changeNotificationIsOngoing_ = true; 3424 // the source_ ObservedProeprty will call: this.hasChanged(newValue); 3425 this.source_.set(newValue); 3426 this.notifyHasChanged(newValue); 3427 this.changeNotificationIsOngoing_ = false; 3428 } 3429} 3430/* 3431 * Copyright (c) 2022 Huawei Device Co., Ltd. 3432 * Licensed under the Apache License, Version 2.0 (the "License"); 3433 * you may not use this file except in compliance with the License. 3434 * You may obtain a copy of the License at 3435 * 3436 * http://www.apache.org/licenses/LICENSE-2.0 3437 * 3438 * Unless required by applicable law or agreed to in writing, software 3439 * distributed under the License is distributed on an "AS IS" BASIS, 3440 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3441 * See the License for the specific language governing permissions and 3442 * limitations under the License. 3443 */ 3444/** 3445 * SynchedPropertyNesedObjectPU 3446 * 3447 * all definitions in this file are framework internal 3448 * 3449 */ 3450class SynchedPropertyNesedObjectPU extends ObservedPropertyObjectAbstractPU { 3451 /** 3452 * Construct a Property of a su component that links to a variable of parent view that holds an ObservedObject 3453 * example 3454 * this.b.$a with b of type PC and a of type C, or 3455 * this.$b[5] with this.b of type PC and array item b[5] of type C; 3456 * 3457 * @param subscribeMe 3458 * @param propName 3459 */ 3460 constructor(obsObject, owningChildView, propertyName) { 3461 super(owningChildView, propertyName); 3462 this.obsObject_ = obsObject; 3463 // register to the ObservedObject 3464 ObservedObject.addOwningProperty(this.obsObject_, this); 3465 } 3466 /* 3467 like a destructor, need to call this before deleting 3468 the property. 3469 */ 3470 aboutToBeDeleted() { 3471 // unregister from the ObservedObject 3472 ObservedObject.removeOwningProperty(this.obsObject_, this); 3473 super.aboutToBeDeleted(); 3474 } 3475 // this object is subscriber to ObservedObject 3476 // will call this cb function when property has changed 3477 hasChanged(newValue) { 3478 3479 this.notifyHasChanged(this.obsObject_); 3480 } 3481 getUnmonitored() { 3482 // 3483 // unmonitored get access , no call to otifyPropertyRead ! 3484 return this.obsObject_; 3485 } 3486 // get 'read through` from the ObservedProperty 3487 get() { 3488 3489 this.notifyPropertyRead(); 3490 return this.obsObject_; 3491 } 3492 // set 'writes through` to the ObservedProperty 3493 set(newValue) { 3494 if (this.obsObject_ == newValue) { 3495 3496 return; 3497 } 3498 3499 // unsubscribe from the old value ObservedObject 3500 ObservedObject.removeOwningProperty(this.obsObject_, this); 3501 this.obsObject_ = newValue; 3502 // subscribe to the new value ObservedObject 3503 ObservedObject.addOwningProperty(this.obsObject_, this); 3504 // notify value change to subscribing View 3505 this.notifyHasChanged(this.obsObject_); 3506 } 3507} 3508/* 3509 * Copyright (c) 2022 Huawei Device Co., Ltd. 3510 * Licensed under the Apache License, Version 2.0 (the "License"); 3511 * you may not use this file except in compliance with the License. 3512 * You may obtain a copy of the License at 3513 * 3514 * http://www.apache.org/licenses/LICENSE-2.0 3515 * 3516 * Unless required by applicable law or agreed to in writing, software 3517 * distributed under the License is distributed on an "AS IS" BASIS, 3518 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3519 * See the License for the specific language governing permissions and 3520 * limitations under the License. 3521 * 3522 * * ViewPU - View for Partial Update 3523 * 3524* all definitions in this file are framework internal 3525*/ 3526// denotes a missing elemntId, this is the case during initial render 3527const UndefinedElmtId = -1; 3528// Nativeview 3529// implemented in C++ for release 3530// and in utest/view_native_mock.ts for testing 3531class ViewPU extends NativeViewPartialUpdate { 3532 /** 3533 * Create a View 3534 * 3535 * 1. option: top level View, specify 3536 * - compilerAssignedUniqueChildId must specify 3537 * - parent=undefined 3538 * - localStorage must provide if @LocalSTorageLink/Prop variables are used 3539 * in this View or descendant Views. 3540 * 3541 * 2. option: not a top level View 3542 * - compilerAssignedUniqueChildId must specify 3543 * - parent must specify 3544 * - localStorage do not specify, will inherit from parent View. 3545 * 3546 */ 3547 constructor(parent, localStorage, elmtId = -1) { 3548 super(); 3549 this.parent_ = undefined; 3550 this.childrenWeakrefMap_ = new Map(); 3551 this.watchedProps = new Map(); 3552 // Set of dependent elmtIds that need partial update 3553 // during next re-render 3554 this.dirtDescendantElementIds_ = new Set(); 3555 // registry of update functions 3556 // the key is the elementId of the Component/Element that's the result of this function 3557 this.updateFuncByElmtId = new Map(); 3558 // my LocalStorge instance, shared with ancestor Views. 3559 // create a default instance on demand if none is initialized 3560 this.localStoragebackStore_ = undefined; 3561 // if set use the elmtId also as the ViewPU object's subscribable id. 3562 // these matching is requiremrnt for updateChildViewById(elmtId) being able to 3563 // find the child ViewPU object by given elmtId 3564 this.id_ = elmtId == -1 ? SubscriberManager.MakeId() : elmtId; 3565 this.providedVars_ = parent ? new Map(parent.providedVars_) 3566 : new Map(); 3567 this.localStoragebackStore_ = undefined; 3568 if (parent) { 3569 // this View is not a top-level View 3570 3571 this.setCardId(parent.getCardId()); 3572 this.localStorage_ = parent.localStorage_; 3573 parent.addChild(this); 3574 } 3575 else if (localStorage) { 3576 this.localStorage_ = localStorage; 3577 3578 } 3579 SubscriberManager.Add(this); 3580 3581 } 3582 get localStorage_() { 3583 if (!this.localStoragebackStore_) { 3584 3585 this.localStoragebackStore_ = new LocalStorage({ /* emty */}); 3586 } 3587 return this.localStoragebackStore_; 3588 } 3589 set localStorage_(instance) { 3590 if (!instance) { 3591 // setting to undefined not allowed 3592 return; 3593 } 3594 if (this.localStoragebackStore_) { 3595 stateMgmtConsole.error(`${this.constructor.name} is setting LocalStorage instance twice`); 3596 } 3597 this.localStoragebackStore_ = instance; 3598 } 3599 // globally unique id, this is different from compilerAssignedUniqueChildId! 3600 id__() { 3601 return this.id_; 3602 } 3603 // super class will call this function from 3604 // its aboutToBeDeleted implementation 3605 aboutToBeDeletedInternal() { 3606 // When a custom component is deleted, need to notify the C++ side to clean the corresponding deletion cache Map, 3607 // because after the deletion, can no longer clean the RemoveIds cache on the C++ side through the 3608 // updateDirtyElements function. 3609 let removedElmtIds = []; 3610 this.updateFuncByElmtId.forEach((value, key) => { 3611 this.purgeVariableDependenciesOnElmtId(key); 3612 removedElmtIds.push(key); 3613 }); 3614 this.deletedElmtIdsHaveBeenPurged(removedElmtIds); 3615 this.updateFuncByElmtId.clear(); 3616 this.watchedProps.clear(); 3617 this.providedVars_.clear(); 3618 if (this.parent_) { 3619 this.parent_.removeChild(this); 3620 } 3621 } 3622 setParent(parent) { 3623 if (this.parent_ && parent) { 3624 stateMgmtConsole.warn(`ViewPU('${this.constructor.name}', ${this.id__()}).setChild: changing parent to '${parent.constructor.name}', id ${parent.id__()} (unsafe operation)`); 3625 } 3626 this.parent_ = parent; 3627 } 3628 /** 3629 * add given child and set 'this' as its parent 3630 * @param child child to add 3631 * @returns returns false if child with given child's id already exists 3632 * 3633 * framework internal function 3634 * Note: Use of WeakRef ensures child and parent do not generate a cycle dependency. 3635 * The add. Set<ids> is required to reliably tell what children still exist. 3636 */ 3637 addChild(child) { 3638 if (this.childrenWeakrefMap_.has(child.id__())) { 3639 stateMgmtConsole.warn(`ViewPU('${this.constructor.name}', ${this.id__()}).addChild '${child.constructor.name}' id already exists ${child.id__()} !`); 3640 return false; 3641 } 3642 this.childrenWeakrefMap_.set(child.id__(), new WeakRef(child)); 3643 child.setParent(this); 3644 return true; 3645 } 3646 /** 3647 * remove given child and remove 'this' as its parent 3648 * @param child child to add 3649 * @returns returns false if child with given child's id does not exist 3650 */ 3651 removeChild(child) { 3652 const hasBeenDeleted = this.childrenWeakrefMap_.delete(child.id__()); 3653 if (!hasBeenDeleted) { 3654 stateMgmtConsole.warn(`ViewPU('${this.constructor.name}', ${this.id__()}).removeChild '${child.constructor.name}', child id ${child.id__()} not known!`); 3655 } 3656 else { 3657 child.setParent(undefined); 3658 } 3659 return hasBeenDeleted; 3660 } 3661 /** 3662 * Retrieve child by given id 3663 * @param id 3664 * @returns child if in map and weak ref can still be downreferenced 3665 */ 3666 getChildById(id) { 3667 const childWeakRef = this.childrenWeakrefMap_.get(id); 3668 return childWeakRef ? childWeakRef.deref() : undefined; 3669 } 3670 updateStateVars(params) { 3671 stateMgmtConsole.warn("ViewPU.updateStateVars unimplemented. Pls upgrade to latest eDSL transpiler version."); 3672 } 3673 initialRenderView() { 3674 this.initialRender(); 3675 } 3676 UpdateElement(elmtId) { 3677 // do not process an Element that has been marked to be deleted 3678 const updateFunc = this.updateFuncByElmtId.get(elmtId); 3679 if ((updateFunc == undefined) || (typeof updateFunc !== "function")) { 3680 stateMgmtConsole.error(`${this.constructor.name}[${this.id__()}]: update function of ElementId ${elmtId} not found, internal error!`); 3681 } 3682 else { 3683 3684 updateFunc(elmtId, /* isFirstRender */ false); 3685 // continue in native JSView 3686 // Finish the Update in JSView::JsFinishUpdateFunc 3687 // this function appends no longer used elmtIds (as recrded by VSP) to the given allRmElmtIds array 3688 this.finishUpdateFunc(elmtId); 3689 3690 } 3691 } 3692 /** 3693 * force a complete rerender / update by executing all update functions 3694 * exec a regular rerender first 3695 * 3696 * @param deep recurse all children as well 3697 * 3698 * framework internal functions, apps must not call 3699 */ 3700 forceCompleteRerender(deep = false) { 3701 stateMgmtConsole.warn(`ViewPU('${this.constructor.name}', ${this.id__()}).forceCompleteRerender - start.`); 3702 // request list of all (gloabbly) deleted elmtIds; 3703 let deletedElmtIds = []; 3704 this.getDeletedElemtIds(deletedElmtIds); 3705 // see which elmtIds are managed by this View 3706 // and clean up all book keeping for them 3707 this.purgeDeletedElmtIds(deletedElmtIds); 3708 Array.from(this.updateFuncByElmtId.keys()).sort(ViewPU.compareNumber).forEach(elmtId => this.UpdateElement(elmtId)); 3709 if (deep) { 3710 this.childrenWeakrefMap_.forEach((weakRefChild) => { 3711 const child = weakRefChild.deref(); 3712 if (child) { 3713 child.forceCompleteRerender(true); 3714 } 3715 }); 3716 } 3717 stateMgmtConsole.warn(`ViewPU('${this.constructor.name}', ${this.id__()}).forceCompleteRerender - end`); 3718 } 3719 /** 3720 * force a complete rerender / update on specific node by executing update function. 3721 * 3722 * @param elmtId which node needs to update. 3723 * 3724 * framework internal functions, apps must not call 3725 */ 3726 forceRerenderNode(elmtId) { 3727 // request list of all (gloabbly) deleted elmtIds; 3728 let deletedElmtIds = []; 3729 this.getDeletedElemtIds(deletedElmtIds); 3730 // see which elmtIds are managed by this View 3731 // and clean up all book keeping for them 3732 this.purgeDeletedElmtIds(deletedElmtIds); 3733 this.UpdateElement(elmtId); 3734 // remove elemtId from dirtDescendantElementIds. 3735 this.dirtDescendantElementIds_.delete(elmtId); 3736 } 3737 updateStateVarsOfChildByElmtId(elmtId, params) { 3738 3739 if (elmtId < 0) { 3740 stateMgmtConsole.warn(`ViewPU('${this.constructor.name}', ${this.id__()}).updateChildViewById(${elmtId}) - invalid elmtId - internal error!`); 3741 return; 3742 } 3743 let child = this.getChildById(elmtId); 3744 if (!child) { 3745 stateMgmtConsole.warn(`ViewPU('${this.constructor.name}', ${this.id__()}).updateChildViewById(${elmtId}) - no child with this elmtId - internal error!`); 3746 return; 3747 } 3748 child.updateStateVars(params); 3749 3750 } 3751 // implements IMultiPropertiesChangeSubscriber 3752 viewPropertyHasChanged(varName, dependentElmtIds) { 3753 3754 this.syncInstanceId(); 3755 if (dependentElmtIds.size && !this.isFirstRender()) { 3756 if (!this.dirtDescendantElementIds_.size) { 3757 // mark Composedelement dirty when first elmtIds are added 3758 // do not need to do this every time 3759 this.markNeedUpdate(); 3760 } 3761 3762 const union = new Set([...this.dirtDescendantElementIds_, ...dependentElmtIds]); 3763 this.dirtDescendantElementIds_ = union; 3764 3765 } 3766 let cb = this.watchedProps.get(varName); 3767 if (cb) { 3768 3769 cb.call(this, varName); 3770 } 3771 this.restoreInstanceId(); 3772 } 3773 /** 3774 * Function to be called from the constructor of the sub component 3775 * to register a @Watch varibale 3776 * @param propStr name of the variable. Note from @Provide and @Consume this is 3777 * the variable name and not the alias! 3778 * @param callback application defined member function of sub-class 3779 */ 3780 declareWatch(propStr, callback) { 3781 this.watchedProps.set(propStr, callback); 3782 } 3783 /** 3784 * This View @Provide's a variable under given name 3785 * Call this function from the constructor of the sub class 3786 * @param providedPropName either the variable name or the alias defined as 3787 * decorator param 3788 * @param store the backing store object for this variable (not the get/set variable!) 3789 */ 3790 addProvidedVar(providedPropName, store) { 3791 if (this.providedVars_.has(providedPropName)) { 3792 throw new ReferenceError(`${this.constructor.name}: duplicate @Provide property with name ${providedPropName}. 3793 Property with this name is provided by one of the ancestor Views already.`); 3794 } 3795 this.providedVars_.set(providedPropName, store); 3796 } 3797 /** 3798 * Method for the sub-class to call from its constructor for resolving 3799 * a @Consume variable and initializing its backing store 3800 * with the yncedPropertyTwoWay<T> object created from the 3801 * @Provide variable's backing store. 3802 * @param providedPropName the name of the @Provide'd variable. 3803 * This is either the @Consume decortor parameter, or variable name. 3804 * @param consumeVarName the @Consume variable name (not the 3805 * @Consume decortor parameter) 3806 * @returns initiaizing value of the @Consume backing store 3807 */ 3808 initializeConsume(providedPropName, consumeVarName) { 3809 let providedVarStore = this.providedVars_.get(providedPropName); 3810 if (providedVarStore === undefined) { 3811 throw new ReferenceError(`${this.constructor.name}: missing @Provide property with name ${providedPropName}. 3812 Fail to resolve @Consume(${providedPropName}).`); 3813 } 3814 return providedVarStore.createSync((source) => (source instanceof ObservedPropertySimple) 3815 ? new SynchedPropertySimpleTwoWayPU(source, this, consumeVarName) 3816 : new SynchedPropertyObjectTwoWayPU(source, this, consumeVarName)); 3817 } 3818 /** 3819 * given the elmtid of a child or child of child within this custom component 3820 * remember this component needs a partial update 3821 * @param elmtId 3822 */ 3823 markElemenDirtyById(elmtId) { 3824 // TODO ace-ets2bundle, framework, compilated apps need to update together 3825 // this function will be removed after a short transiition periode 3826 stateMgmtConsole.error(`markElemenDirtyById no longer supported. 3827 Please update your ace-ets2bundle and recompile your application!`); 3828 } 3829 /** 3830 * For each recorded dirty Element in this custom component 3831 * run its update function 3832 * 3833 */ 3834 updateDirtyElements() { 3835 do { 3836 3837 // request list of all (gloabbly) deleteelmtIds; 3838 let deletedElmtIds = []; 3839 this.getDeletedElemtIds(deletedElmtIds); 3840 // see which elmtIds are managed by this View 3841 // and clean up all book keeping for them 3842 this.purgeDeletedElmtIds(deletedElmtIds); 3843 // process all elmtIds marked as needing update in ascending order. 3844 // ascending order ensures parent nodes will be updated before their children 3845 // prior cleanup ensure no already deleted Elements have their update func executed 3846 Array.from(this.dirtDescendantElementIds_).sort(ViewPU.compareNumber).forEach(elmtId => { 3847 this.UpdateElement(elmtId); 3848 this.dirtDescendantElementIds_.delete(elmtId); 3849 }); 3850 } while (this.dirtDescendantElementIds_.size); 3851 } 3852 // given a list elementIds removes these from state variables dependency list and from elmtId -> updateFunc map 3853 purgeDeletedElmtIds(rmElmtIds) { 3854 if (rmElmtIds.length == 0) { 3855 return; 3856 } 3857 3858 // rmElmtIds is the array of ElemntIds that 3859 let removedElmtIds = []; 3860 rmElmtIds.forEach((elmtId) => { 3861 // remove entry from Map elmtId -> update function 3862 if (this.updateFuncByElmtId.delete(elmtId)) { 3863 // for each state var, remove dependent elmtId (if present) 3864 // purgeVariableDependenciesOnElmtId needs to be generated by the compiler 3865 this.purgeVariableDependenciesOnElmtId(elmtId); 3866 // keep track of elmtId that has been de-registered 3867 removedElmtIds.push(elmtId); 3868 } 3869 }); 3870 this.deletedElmtIdsHaveBeenPurged(removedElmtIds); 3871 3872 3873 } 3874 // the current executed update function 3875 observeComponentCreation(compilerAssignedUpdateFunc) { 3876 const elmtId = ViewStackProcessor.AllocateNewElmetIdForNextComponent(); 3877 3878 compilerAssignedUpdateFunc(elmtId, /* is first rneder */ true); 3879 this.updateFuncByElmtId.set(elmtId, compilerAssignedUpdateFunc); 3880 3881 } 3882 // performs the update on a branch within if() { branch } else if (..) { branch } else { branch } 3883 ifElseBranchUpdateFunction(branchId, branchfunc) { 3884 const oldBranchid = If.getBranchId(); 3885 if (branchId == oldBranchid) { 3886 3887 return; 3888 } 3889 If.branchId(branchId); 3890 branchfunc(); 3891 } 3892 /** 3893 Partial updates for ForEach. 3894 * @param elmtId ID of element. 3895 * @param itemArray Array of items for use of itemGenFunc. 3896 * @param itemGenFunc Item generation function to generate new elements. If index parameter is 3897 * given set itemGenFuncUsesIndex to true. 3898 * @param idGenFunc ID generation function to generate unique ID for each element. If index parameter is 3899 * given set idGenFuncUsesIndex to true. 3900 * @param itemGenFuncUsesIndex itemGenFunc optional index parameter is given or not. 3901 * @param idGenFuncUsesIndex idGenFunc optional index parameter is given or not. 3902 */ 3903 forEachUpdateFunction(elmtId, itemArray, itemGenFunc, idGenFunc, itemGenFuncUsesIndex = false, idGenFuncUsesIndex = false) { 3904 3905 if (itemArray === null || itemArray === undefined) { 3906 stateMgmtConsole.error(`ForEach input array is null or undefined error.`); 3907 return; 3908 } 3909 if (itemGenFunc === null || itemGenFunc === undefined) { 3910 stateMgmtConsole.error(`Error: Item generation function not defined in forEach function.`); 3911 return; 3912 } 3913 if (idGenFunc === undefined) { 3914 3915 idGenFunc = (item, index) => `${index}__${JSON.stringify(item)}`; 3916 idGenFuncUsesIndex = true; 3917 } 3918 let diffIndexArray = []; // New indexes compared to old one. 3919 let newIdArray = []; 3920 let idDuplicates = []; 3921 const arr = itemArray; // just to trigger a 'get' onto the array 3922 // ID gen is with index. 3923 if (idGenFuncUsesIndex) { 3924 3925 // Create array of new ids. 3926 arr.forEach((item, indx) => { 3927 newIdArray.push(idGenFunc(item, indx)); 3928 }); 3929 } 3930 else { 3931 // Create array of new ids. 3932 3933 arr.forEach((item, index) => { 3934 newIdArray.push(`${itemGenFuncUsesIndex ? index + '_' : ''}` + idGenFunc(item)); 3935 }); 3936 } 3937 // Set new array on C++ side. 3938 // C++ returns array of indexes of newly added array items. 3939 // these are indexes in new child list. 3940 ForEach.setIdArray(elmtId, newIdArray, diffIndexArray, idDuplicates); 3941 // Its error if there are duplicate IDs. 3942 if (idDuplicates.length > 0) { 3943 idDuplicates.forEach((indx) => { 3944 stateMgmtConsole.error(`Error: ${newIdArray[indx]} generated for ${indx}${indx < 4 ? indx == 2 ? "nd" : "rd" : "th"} array item ${arr[indx]}.`); 3945 }); 3946 stateMgmtConsole.error(`Ids generated by the ForEach id gen function must be unique, error.`); 3947 } 3948 3949 // Item gen is with index. 3950 3951 // Create new elements if any. 3952 diffIndexArray.forEach((indx) => { 3953 ForEach.createNewChildStart(newIdArray[indx], this); 3954 if (itemGenFuncUsesIndex) { 3955 itemGenFunc(arr[indx], indx); 3956 } 3957 else { 3958 itemGenFunc(arr[indx]); 3959 } 3960 ForEach.createNewChildFinish(newIdArray[indx], this); 3961 }); 3962 } 3963 /** 3964 * CreateStorageLink and CreateStorageLinkPU are used by the implementation of @StorageLink and 3965 * @LocalStotrageLink in full update and partial update solution respectively. 3966 * These are not part of the public AppStorage API , apps should not use. 3967 * @param storagePropName - key in LocalStorage 3968 * @param defaultValue - value to use when creating a new prop in the LocalStotage 3969 * @param owningView - the View/ViewPU owning the @StorageLink/@LocalStorageLink variable 3970 * @param viewVariableName - @StorageLink/@LocalStorageLink variable name 3971 * @returns SynchedPropertySimple/ObjectTwoWay/PU 3972 */ 3973 createStorageLink(storagePropName, defaultValue, viewVariableName) { 3974 return AppStorage.__CreateSync(storagePropName, defaultValue, (source) => (source === undefined) 3975 ? undefined 3976 : (source instanceof ObservedPropertySimple) 3977 ? new SynchedPropertySimpleTwoWayPU(source, this, viewVariableName) 3978 : new SynchedPropertyObjectTwoWayPU(source, this, viewVariableName)); 3979 } 3980 createStorageProp(storagePropName, defaultValue, viewVariableName) { 3981 return AppStorage.__CreateSync(storagePropName, defaultValue, (source) => (source === undefined) 3982 ? undefined 3983 : (source instanceof ObservedPropertySimple) 3984 ? new SynchedPropertySimpleOneWayPU(source, this, viewVariableName) 3985 : new SynchedPropertyObjectOneWayPU(source, this, viewVariableName)); 3986 } 3987 createLocalStorageLink(storagePropName, defaultValue, viewVariableName) { 3988 return this.localStorage_.__createSync(storagePropName, defaultValue, (source) => (source === undefined) 3989 ? undefined 3990 : (source instanceof ObservedPropertySimple) 3991 ? new SynchedPropertySimpleTwoWayPU(source, this, viewVariableName) 3992 : new SynchedPropertyObjectTwoWayPU(source, this, viewVariableName)); 3993 } 3994 createLocalStorageProp(storagePropName, defaultValue, viewVariableName) { 3995 return this.localStorage_.__createSync(storagePropName, defaultValue, (source) => (source === undefined) 3996 ? undefined 3997 : (source instanceof ObservedPropertySimple) 3998 ? new SynchedPropertySimpleOneWayPU(source, this, viewVariableName) 3999 : new SynchedPropertyObjectOneWayPU(source, this, viewVariableName)); 4000 } 4001} 4002// Array.sort() converts array items to string to compare them, sigh! 4003ViewPU.compareNumber = (a, b) => { 4004 return (a < b) ? -1 : (a > b) ? 1 : 0; 4005}; 4006/* 4007 * Copyright (c) 2022 Huawei Device Co., Ltd. 4008 * Licensed under the Apache License, Version 2.0 (the "License"); 4009 * you may not use this file except in compliance with the License. 4010 * You may obtain a copy of the License at 4011 * 4012 * http://www.apache.org/licenses/LICENSE-2.0 4013 * 4014 * Unless required by applicable law or agreed to in writing, software 4015 * distributed under the License is distributed on an "AS IS" BASIS, 4016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 4017 * See the License for the specific language governing permissions and 4018 * limitations under the License. 4019 * 4020 * * ViewPU - View for Partial Update 4021 * 4022* all definitions in this file are framework internal 4023*/ 4024/** 4025 given parameters for calling a @Builder function 4026 this function wraps the Object of type T inside a ES6 Proxy. 4027 Each param, i.e. Object property is either a function or a value. 4028 If it is a function the function can either return a value of expected 4029 parameter type or an ObservedPropertyabstract<T> where T is the exected 4030 parameter type. The latter is the case when passing a state variable by 4031 reference. 4032 4033 Two purposes: 4034 1 - @Builder function boxy accesses params a '$$.paramA' 4035 However paramA can be a function, so to obtain the value the 4036 access would need to be '$$.param()' The proxy executes 4037 the function and return s the result 4038 2 - said function returns to ObservedPropertyAbstract backing store of 4039 a calling @Component state variable (whenever the state var is 4040 provided to the @Builder function). For this case the proxy can provide 4041 - the value by executing paramA() to return the ObservedPropertyAbstract 4042 and further (monitored!) get() to read its value 4043 - when requested to return '__param1' it returns the ObservedPropertyAbstract 4044 object. The scenario is to use to init a @Link source. 4045 */ 4046function makeBuilderParameterProxy(builderName, source) { 4047 return new Proxy(source, { 4048 set(target, prop, val) { 4049 throw Error(`@Builder '${builderName}': Invalid attempt to set(write to) parameter '${prop.toString()}' error!`); 4050 }, 4051 get(target, prop) { 4052 const prop1 = prop.toString().trim().startsWith("__") 4053 ? prop.toString().trim().substring(2) 4054 : prop.toString().trim(); 4055 4056 if (!(typeof target === "object") && (prop1 in target)) { 4057 throw Error(`@Builder '${builderName}': '${prop1}' used but not a function parameter error!`); 4058 } 4059 const value = target[prop1]; 4060 if (typeof value !== "function") { 4061 4062 return value; 4063 } 4064 const funcRet = value(); 4065 if ((typeof funcRet === "object") && ('get' in funcRet)) { 4066 if (prop1 !== prop) { 4067 4068 return funcRet; 4069 } 4070 else { 4071 4072 const result = funcRet.get(); 4073 4074 return result; 4075 } 4076 } 4077 4078 return funcRet; 4079 } // get 4080 }); // new Proxy 4081} 4082/* 4083 * Copyright (c) 2021 Huawei Device Co., Ltd. 4084 * Licensed under the Apache License, Version 2.0 (the "License"); 4085 * you may not use this file except in compliance with the License. 4086 * You may obtain a copy of the License at 4087 * 4088 * http://www.apache.org/licenses/LICENSE-2.0 4089 * 4090 * Unless required by applicable law or agreed to in writing, software 4091 * distributed under the License is distributed on an "AS IS" BASIS, 4092 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 4093 * See the License for the specific language governing permissions and 4094 * limitations under the License. 4095 */ 4096 4097PersistentStorage.ConfigureBackend(new Storage()); 4098Environment.ConfigureBackend(new EnvironmentSetting()); 4099 4100