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 {PropertyTreeNode} from 'trace/tree_node/property_tree_node'; 19import {TreeNode} from 'trace/tree_node/tree_node'; 20import {IsModifiedCallbackType} from './add_diffs'; 21import {AddDiffsPropertiesTree} from './add_diffs_properties_tree'; 22import {Filter} from './operations/filter'; 23import {UiPropertyTreeNode} from './ui_property_tree_node'; 24import {UiTreeFormatter} from './ui_tree_formatter'; 25import {TreeNodeFilter, UiTreeUtils} from './ui_tree_utils'; 26import {UserOptions} from './user_options'; 27 28export class PropertiesPresenter { 29 private propertiesFilter: TreeNodeFilter = UiTreeUtils.makePropertyFilter(''); 30 private highlightedProperty = ''; 31 private propertiesTree: PropertyTreeNode | undefined; 32 private formattedTree: UiPropertyTreeNode | undefined; 33 34 constructor( 35 private userOptions: UserOptions, 36 private denylistProperties: string[], 37 ) {} 38 39 getUserOptions() { 40 return this.userOptions; 41 } 42 43 setPropertiesTree(tree: PropertyTreeNode) { 44 this.propertiesTree = tree; 45 } 46 47 getPropertiesTree() { 48 return this.propertiesTree; 49 } 50 51 getFormattedTree() { 52 return this.formattedTree; 53 } 54 55 getHighlightedProperty() { 56 return this.highlightedProperty; 57 } 58 59 applyHighlightedPropertyChange(id: string) { 60 if (this.highlightedProperty === id) { 61 this.highlightedProperty = ''; 62 } else { 63 this.highlightedProperty = id; 64 } 65 } 66 67 applyPropertiesFilterChange(filterString: string) { 68 this.propertiesFilter = UiTreeUtils.makePropertyFilter(filterString); 69 } 70 71 applyPropertiesUserOptionsChange(userOptions: UserOptions) { 72 this.userOptions = userOptions; 73 } 74 75 async formatPropertiesTree( 76 previousHierarchyTree: HierarchyTreeNode | undefined, 77 displayName: string | undefined, 78 keepCalculated: boolean, 79 ): Promise<void> { 80 if (!this.propertiesTree) { 81 this.formattedTree = undefined; 82 return; 83 } 84 const uiTree = UiPropertyTreeNode.from(this.propertiesTree); 85 86 if ( 87 this.userOptions['showDiff']?.enabled && 88 !this.userOptions['showDiff']?.isUnavailable 89 ) { 90 const prevEntryNode = previousHierarchyTree?.findDfs( 91 UiTreeUtils.makeIdMatchFilter(this.propertiesTree.id), 92 ); 93 const prevEntryUiTree = prevEntryNode 94 ? UiPropertyTreeNode.from(await prevEntryNode.getAllProperties()) 95 : undefined; 96 await new AddDiffsPropertiesTree( 97 PropertiesPresenter.isPropertyNodeModified, 98 this.denylistProperties, 99 ).executeInPlace(uiTree, prevEntryUiTree); 100 } 101 102 if (displayName) { 103 uiTree.setDisplayName(displayName); 104 } 105 106 const predicatesKeepingChildren = [this.propertiesFilter]; 107 const predicatesDiscardingChildren = []; 108 109 if (this.denylistProperties) { 110 predicatesDiscardingChildren.push( 111 UiTreeUtils.makeDenyListFilterByName(this.denylistProperties), 112 ); 113 } 114 115 if (!this.userOptions['showDefaults']?.enabled) { 116 predicatesDiscardingChildren.push(UiTreeUtils.isNotDefault); 117 predicatesDiscardingChildren.push( 118 UiTreeUtils.makePropertyMatchFilter('IDENTITY'), 119 ); 120 } 121 122 if (!keepCalculated) { 123 predicatesDiscardingChildren.push(UiTreeUtils.isNotCalculated); 124 } 125 this.formattedTree = new UiTreeFormatter<UiPropertyTreeNode>() 126 .setUiTree(uiTree) 127 .addOperation(new Filter(predicatesDiscardingChildren, false)) 128 .addOperation(new Filter(predicatesKeepingChildren, true)) 129 .format(); 130 } 131 132 clear() { 133 this.propertiesTree = undefined; 134 this.formattedTree = undefined; 135 } 136 137 static isPropertyNodeModified: IsModifiedCallbackType = async ( 138 newTree: TreeNode | undefined, 139 oldTree: TreeNode | undefined, 140 ) => { 141 if (!newTree && !oldTree) return false; 142 if (!newTree || !oldTree) return true; 143 144 const newValue = (newTree as UiPropertyTreeNode).formattedValue(); 145 const oldValue = (oldTree as UiPropertyTreeNode).formattedValue(); 146 147 return oldValue !== newValue; 148 }; 149} 150