xref: /aosp_15_r20/development/tools/winscope/src/common/object_utils.ts (revision 90c8c64db3049935a07c6143d7fd006e26f8ecca)
1*90c8c64dSAndroid Build Coastguard Worker/*
2*90c8c64dSAndroid Build Coastguard Worker * Copyright (C) 2023 The Android Open Source Project
3*90c8c64dSAndroid Build Coastguard Worker *
4*90c8c64dSAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*90c8c64dSAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*90c8c64dSAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*90c8c64dSAndroid Build Coastguard Worker *
8*90c8c64dSAndroid Build Coastguard Worker *      http://www.apache.org/licenses/LICENSE-2.0
9*90c8c64dSAndroid Build Coastguard Worker *
10*90c8c64dSAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*90c8c64dSAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*90c8c64dSAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*90c8c64dSAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*90c8c64dSAndroid Build Coastguard Worker * limitations under the License.
15*90c8c64dSAndroid Build Coastguard Worker */
16*90c8c64dSAndroid Build Coastguard Worker
17*90c8c64dSAndroid Build Coastguard Workerimport {assertDefined, assertTrue} from './assert_utils';
18*90c8c64dSAndroid Build Coastguard Worker
19*90c8c64dSAndroid Build Coastguard Workerclass Key {
20*90c8c64dSAndroid Build Coastguard Worker  constructor(public key: string, public index?: number) {}
21*90c8c64dSAndroid Build Coastguard Worker
22*90c8c64dSAndroid Build Coastguard Worker  isArrayKey(): boolean {
23*90c8c64dSAndroid Build Coastguard Worker    return this.index !== undefined;
24*90c8c64dSAndroid Build Coastguard Worker  }
25*90c8c64dSAndroid Build Coastguard Worker}
26*90c8c64dSAndroid Build Coastguard Worker
27*90c8c64dSAndroid Build Coastguard Workerexport class ObjectUtils {
28*90c8c64dSAndroid Build Coastguard Worker  static readonly ARRAY_KEY_REGEX = new RegExp('(.+)\\[(\\d+)\\]');
29*90c8c64dSAndroid Build Coastguard Worker
30*90c8c64dSAndroid Build Coastguard Worker  static getProperty(obj: object, path: string): any {
31*90c8c64dSAndroid Build Coastguard Worker    const keys = ObjectUtils.parseKeys(path);
32*90c8c64dSAndroid Build Coastguard Worker    keys.forEach((key) => {
33*90c8c64dSAndroid Build Coastguard Worker      if (obj === undefined) {
34*90c8c64dSAndroid Build Coastguard Worker        return;
35*90c8c64dSAndroid Build Coastguard Worker      }
36*90c8c64dSAndroid Build Coastguard Worker
37*90c8c64dSAndroid Build Coastguard Worker      if (key.isArrayKey()) {
38*90c8c64dSAndroid Build Coastguard Worker        if ((obj as any)[key.key] === undefined) {
39*90c8c64dSAndroid Build Coastguard Worker          return;
40*90c8c64dSAndroid Build Coastguard Worker        }
41*90c8c64dSAndroid Build Coastguard Worker        assertTrue(
42*90c8c64dSAndroid Build Coastguard Worker          Array.isArray((obj as any)[key.key]),
43*90c8c64dSAndroid Build Coastguard Worker          () => 'Expected to be array',
44*90c8c64dSAndroid Build Coastguard Worker        );
45*90c8c64dSAndroid Build Coastguard Worker        obj = (obj as any)[key.key][assertDefined(key.index)];
46*90c8c64dSAndroid Build Coastguard Worker      } else {
47*90c8c64dSAndroid Build Coastguard Worker        obj = (obj as any)[key.key];
48*90c8c64dSAndroid Build Coastguard Worker      }
49*90c8c64dSAndroid Build Coastguard Worker    });
50*90c8c64dSAndroid Build Coastguard Worker    return obj;
51*90c8c64dSAndroid Build Coastguard Worker  }
52*90c8c64dSAndroid Build Coastguard Worker
53*90c8c64dSAndroid Build Coastguard Worker  static setProperty(obj: object, path: string, value: any) {
54*90c8c64dSAndroid Build Coastguard Worker    const keys = ObjectUtils.parseKeys(path);
55*90c8c64dSAndroid Build Coastguard Worker
56*90c8c64dSAndroid Build Coastguard Worker    keys.slice(0, -1).forEach((key) => {
57*90c8c64dSAndroid Build Coastguard Worker      if (key.isArrayKey()) {
58*90c8c64dSAndroid Build Coastguard Worker        ObjectUtils.initializePropertyArrayIfNeeded(obj, key);
59*90c8c64dSAndroid Build Coastguard Worker        obj = (obj as any)[key.key][assertDefined(key.index)];
60*90c8c64dSAndroid Build Coastguard Worker      } else {
61*90c8c64dSAndroid Build Coastguard Worker        ObjectUtils.initializePropertyIfNeeded(obj, key.key);
62*90c8c64dSAndroid Build Coastguard Worker        obj = (obj as any)[key.key];
63*90c8c64dSAndroid Build Coastguard Worker      }
64*90c8c64dSAndroid Build Coastguard Worker    });
65*90c8c64dSAndroid Build Coastguard Worker
66*90c8c64dSAndroid Build Coastguard Worker    const lastKey = assertDefined(keys.at(-1));
67*90c8c64dSAndroid Build Coastguard Worker    if (lastKey.isArrayKey()) {
68*90c8c64dSAndroid Build Coastguard Worker      ObjectUtils.initializePropertyArrayIfNeeded(obj, lastKey);
69*90c8c64dSAndroid Build Coastguard Worker      (obj as any)[lastKey.key][assertDefined(lastKey.index)] = value;
70*90c8c64dSAndroid Build Coastguard Worker    } else {
71*90c8c64dSAndroid Build Coastguard Worker      (obj as any)[lastKey.key] = value;
72*90c8c64dSAndroid Build Coastguard Worker    }
73*90c8c64dSAndroid Build Coastguard Worker  }
74*90c8c64dSAndroid Build Coastguard Worker
75*90c8c64dSAndroid Build Coastguard Worker  private static parseKeys(path: string): Key[] {
76*90c8c64dSAndroid Build Coastguard Worker    return path.split('.').map((rawKey) => {
77*90c8c64dSAndroid Build Coastguard Worker      const match = ObjectUtils.ARRAY_KEY_REGEX.exec(rawKey);
78*90c8c64dSAndroid Build Coastguard Worker      if (match) {
79*90c8c64dSAndroid Build Coastguard Worker        return new Key(match[1], Number(match[2]));
80*90c8c64dSAndroid Build Coastguard Worker      }
81*90c8c64dSAndroid Build Coastguard Worker      return new Key(rawKey);
82*90c8c64dSAndroid Build Coastguard Worker    });
83*90c8c64dSAndroid Build Coastguard Worker  }
84*90c8c64dSAndroid Build Coastguard Worker
85*90c8c64dSAndroid Build Coastguard Worker  private static initializePropertyIfNeeded(obj: object, key: string) {
86*90c8c64dSAndroid Build Coastguard Worker    if ((obj as any)[key] === undefined) {
87*90c8c64dSAndroid Build Coastguard Worker      (obj as any)[key] = {};
88*90c8c64dSAndroid Build Coastguard Worker    }
89*90c8c64dSAndroid Build Coastguard Worker    assertTrue(
90*90c8c64dSAndroid Build Coastguard Worker      typeof (obj as any)[key] === 'object',
91*90c8c64dSAndroid Build Coastguard Worker      () => 'Expected to be object',
92*90c8c64dSAndroid Build Coastguard Worker    );
93*90c8c64dSAndroid Build Coastguard Worker  }
94*90c8c64dSAndroid Build Coastguard Worker
95*90c8c64dSAndroid Build Coastguard Worker  private static initializePropertyArrayIfNeeded(obj: object, key: Key) {
96*90c8c64dSAndroid Build Coastguard Worker    if ((obj as any)[key.key] === undefined) {
97*90c8c64dSAndroid Build Coastguard Worker      (obj as any)[key.key] = [];
98*90c8c64dSAndroid Build Coastguard Worker    }
99*90c8c64dSAndroid Build Coastguard Worker    if ((obj as any)[key.key][assertDefined(key.index)] === undefined) {
100*90c8c64dSAndroid Build Coastguard Worker      (obj as any)[key.key][assertDefined(key.index)] = {};
101*90c8c64dSAndroid Build Coastguard Worker    }
102*90c8c64dSAndroid Build Coastguard Worker    assertTrue(
103*90c8c64dSAndroid Build Coastguard Worker      Array.isArray((obj as any)[key.key]),
104*90c8c64dSAndroid Build Coastguard Worker      () => 'Expected to be array',
105*90c8c64dSAndroid Build Coastguard Worker    );
106*90c8c64dSAndroid Build Coastguard Worker  }
107*90c8c64dSAndroid Build Coastguard Worker}
108