xref: /aosp_15_r20/development/tools/winscope/src/viewers/common/properties_presenter.ts (revision 90c8c64db3049935a07c6143d7fd006e26f8ecca)
1/*
2 * Copyright (C) 2024 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17import {HierarchyTreeNode} from 'trace/tree_node/hierarchy_tree_node';
18import {Operation} from 'trace/tree_node/operations/operation';
19import {PropertyTreeNode} from 'trace/tree_node/property_tree_node';
20import {TreeNode} from 'trace/tree_node/tree_node';
21import {TextFilter} from 'viewers/common/text_filter';
22import {IsModifiedCallbackType} from './add_diffs';
23import {AddDiffsPropertiesTree} from './add_diffs_properties_tree';
24import {Filter} from './operations/filter';
25import {UiPropertyTreeNode} from './ui_property_tree_node';
26import {UiTreeFormatter} from './ui_tree_formatter';
27import {TreeNodeFilter, UiTreeUtils} from './ui_tree_utils';
28import {UserOptions} from './user_options';
29
30export class PropertiesPresenter {
31  private propertiesFilter: TreeNodeFilter;
32  private highlightedProperty = '';
33  private propertiesTree: PropertyTreeNode | undefined;
34  private formattedTree: UiPropertyTreeNode | undefined;
35
36  constructor(
37    private userOptions: UserOptions,
38    private textFilter: TextFilter,
39    private propertiesDenylist: string[],
40    private customOperations?: Array<Operation<UiPropertyTreeNode>>,
41    private defaultAllowlist: string[] = [],
42  ) {
43    this.propertiesFilter = UiTreeUtils.makeNodeFilter(
44      this.textFilter.getFilterPredicate(),
45    );
46  }
47
48  getUserOptions(): UserOptions {
49    return this.userOptions;
50  }
51
52  setPropertiesTree(tree: PropertyTreeNode | undefined) {
53    this.propertiesTree = tree;
54  }
55
56  getPropertiesTree(): PropertyTreeNode | undefined {
57    return this.propertiesTree;
58  }
59
60  getFormattedTree(): UiPropertyTreeNode | undefined {
61    return this.formattedTree;
62  }
63
64  getHighlightedProperty(): string {
65    return this.highlightedProperty;
66  }
67
68  applyHighlightedPropertyChange(id: string) {
69    if (this.highlightedProperty === id) {
70      this.highlightedProperty = '';
71    } else {
72      this.highlightedProperty = id;
73    }
74  }
75
76  getTextFilter(): TextFilter | undefined {
77    return this.textFilter;
78  }
79
80  applyPropertiesFilterChange(textFilter: TextFilter) {
81    this.textFilter = textFilter;
82    this.propertiesFilter = UiTreeUtils.makeNodeFilter(
83      textFilter.getFilterPredicate(),
84    );
85  }
86
87  applyPropertiesUserOptionsChange(userOptions: UserOptions) {
88    this.userOptions = userOptions;
89  }
90
91  updateDefaultAllowList(value: string[]) {
92    this.defaultAllowlist = value;
93  }
94
95  async formatPropertiesTree(
96    previousHierarchyTree: HierarchyTreeNode | undefined,
97    displayName: string | undefined,
98    keepCalculated: boolean,
99  ): Promise<void> {
100    if (!this.propertiesTree) {
101      this.formattedTree = undefined;
102      return;
103    }
104    const uiTree = UiPropertyTreeNode.from(this.propertiesTree);
105
106    if (
107      this.userOptions['showDiff']?.enabled &&
108      !this.userOptions['showDiff']?.isUnavailable
109    ) {
110      const prevEntryNode = previousHierarchyTree?.findDfs(
111        UiTreeUtils.makeIdMatchFilter(this.propertiesTree.id),
112      );
113      const prevEntryUiTree = prevEntryNode
114        ? UiPropertyTreeNode.from(await prevEntryNode.getAllProperties())
115        : undefined;
116      await new AddDiffsPropertiesTree(
117        PropertiesPresenter.isPropertyNodeModified,
118        this.propertiesDenylist,
119      ).executeInPlace(uiTree, prevEntryUiTree);
120    }
121
122    if (displayName) {
123      uiTree.setDisplayName(displayName);
124    }
125
126    const predicatesKeepingChildren = [this.propertiesFilter];
127    const predicatesDiscardingChildren = [];
128
129    if (this.propertiesDenylist) {
130      predicatesDiscardingChildren.push(
131        UiTreeUtils.makeDenyListFilterByName(this.propertiesDenylist),
132      );
133    }
134
135    if (!this.userOptions['showDefaults']?.enabled) {
136      predicatesDiscardingChildren.push(
137        UiTreeUtils.makeIsNotDefaultFilter(this.defaultAllowlist),
138      );
139    }
140
141    if (!keepCalculated) {
142      predicatesDiscardingChildren.push(UiTreeUtils.isNotCalculated);
143    }
144    const formatter = new UiTreeFormatter<UiPropertyTreeNode>().setUiTree(
145      uiTree,
146    );
147    if (predicatesDiscardingChildren.length > 0) {
148      formatter.addOperation(new Filter(predicatesDiscardingChildren, false));
149    }
150    formatter.addOperation(new Filter(predicatesKeepingChildren, true));
151
152    this.customOperations?.forEach((op) => formatter.addOperation(op));
153
154    this.formattedTree = formatter.format();
155  }
156
157  clear() {
158    this.propertiesTree = undefined;
159    this.formattedTree = undefined;
160  }
161
162  static isPropertyNodeModified: IsModifiedCallbackType = async (
163    newTree: TreeNode,
164    oldTree: TreeNode,
165  ) => {
166    const newValue = (newTree as UiPropertyTreeNode).formattedValue();
167    const oldValue = (oldTree as UiPropertyTreeNode).formattedValue();
168
169    return oldValue !== newValue;
170  };
171}
172