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