Serenity Operating System
at master 216 lines 6.4 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include "RemoteObjectPropertyModel.h" 8#include "RemoteObject.h" 9#include "RemoteProcess.h" 10 11namespace Inspector { 12 13RemoteObjectPropertyModel::RemoteObjectPropertyModel(RemoteObject& object) 14 : m_object(object) 15{ 16} 17 18int RemoteObjectPropertyModel::row_count(const GUI::ModelIndex& index) const 19{ 20 Function<int(JsonValue const&)> do_count = [&](JsonValue const& value) { 21 if (value.is_array()) 22 return value.as_array().size(); 23 else if (value.is_object()) 24 return value.as_object().size(); 25 return (size_t)0; 26 }; 27 28 if (index.is_valid()) { 29 auto* path = static_cast<JsonPath const*>(index.internal_data()); 30 return do_count(path->resolve(m_object.json)); 31 } else { 32 return do_count(m_object.json); 33 } 34} 35 36DeprecatedString RemoteObjectPropertyModel::column_name(int column) const 37{ 38 switch (column) { 39 case Column::Name: 40 return "Name"; 41 case Column::Value: 42 return "Value"; 43 } 44 VERIFY_NOT_REACHED(); 45} 46 47GUI::Variant RemoteObjectPropertyModel::data(const GUI::ModelIndex& index, GUI::ModelRole role) const 48{ 49 auto* path = static_cast<JsonPath const*>(index.internal_data()); 50 if (!path) 51 return {}; 52 53 if (role == GUI::ModelRole::Display) { 54 switch (index.column()) { 55 case Column::Name: 56 return path->last().to_deprecated_string(); 57 case Column::Value: { 58 auto data = path->resolve(m_object.json); 59 if (data.is_array()) 60 return DeprecatedString::formatted("<Array with {} element{}", data.as_array().size(), data.as_array().size() == 1 ? ">" : "s>"); 61 if (data.is_object()) 62 return DeprecatedString::formatted("<Object with {} entr{}", data.as_object().size(), data.as_object().size() == 1 ? "y>" : "ies>"); 63 return data; 64 } 65 } 66 } 67 return {}; 68} 69 70void RemoteObjectPropertyModel::set_data(const GUI::ModelIndex& index, const GUI::Variant& new_value) 71{ 72 if (!index.is_valid()) 73 return; 74 75 auto* path = static_cast<JsonPath const*>(index.internal_data()); 76 if (path->size() != 1) 77 return; 78 79 FlatPtr address = m_object.address; 80 RemoteProcess::the().set_property(address, path->first().to_deprecated_string(), new_value.to_deprecated_string()); 81 did_update(); 82} 83 84GUI::ModelIndex RemoteObjectPropertyModel::index(int row, int column, const GUI::ModelIndex& parent) const 85{ 86 auto const& parent_path = parent.is_valid() ? *static_cast<JsonPath const*>(parent.internal_data()) : JsonPath {}; 87 88 auto nth_child = [&](int n, JsonValue const& value) -> JsonPath const* { 89 auto path = make<JsonPath>(); 90 path->extend(parent_path); 91 int row_index = n; 92 if (value.is_object()) { 93 DeprecatedString property_name; 94 auto& object = value.as_object(); 95 object.for_each_member([&](auto& name, auto&) { 96 if (row_index > 0) { 97 --row_index; 98 } else if (row_index == 0) { 99 property_name = name; 100 --row_index; 101 } 102 }); 103 if (property_name.is_null()) 104 return nullptr; 105 106 path->append({ property_name }); 107 m_paths.append(move(path)); 108 } else if (value.is_array()) { 109 path->append(JsonPathElement { (size_t)n }); 110 m_paths.append(move(path)); 111 } else { 112 return nullptr; 113 } 114 return m_paths.last(); 115 }; 116 117 if (!parent.is_valid()) { 118 if (m_object.json.is_empty()) 119 return {}; 120 } 121 122 auto index_path = cached_path_at(row, parent_path); 123 124 if (!index_path) 125 index_path = nth_child(row, parent_path.resolve(m_object.json)); 126 127 if (!index_path) 128 return {}; 129 130 return create_index(row, column, index_path); 131} 132 133GUI::ModelIndex RemoteObjectPropertyModel::parent_index(const GUI::ModelIndex& index) const 134{ 135 if (!index.is_valid()) 136 return index; 137 138 auto path = *static_cast<JsonPath const*>(index.internal_data()); 139 if (path.is_empty()) 140 return {}; 141 142 path.take_last(); 143 if (path.is_empty()) 144 return {}; 145 146 auto* cpath = find_cached_path(path); 147 if (cpath) { 148 int index_in_parent = 0; 149 if (cpath->last().kind() == JsonPathElement::Kind::Index) 150 index_in_parent = cpath->last().index(); 151 else if (cpath->last().kind() == JsonPathElement::Kind::Key) { 152 auto path_copy = path; 153 auto last = path_copy.take_last(); 154 bool found = false; 155 path_copy.resolve(m_object.json).as_object().for_each_member([&](auto& name, auto&) { 156 if (!found) { 157 if (last.key() == name) 158 found = true; 159 else 160 index_in_parent++; 161 } 162 }); 163 } 164 return create_index(index_in_parent, 0, cpath); 165 } 166 167 dbgln("No cached path found for path {}", path.to_deprecated_string()); 168 return {}; 169} 170 171JsonPath const* RemoteObjectPropertyModel::cached_path_at(int n, Vector<JsonPathElement> const& prefix) const 172{ 173 // FIXME: ModelIndex wants a void*, so we have to keep these 174 // indices alive, but allocating a new path every time 175 // we're asked for an index is silly, so we have to look for existing ones first. 176 JsonPath const* index_path = nullptr; 177 int row_index = n; 178 for (auto& path : m_paths) { 179 if (path->size() != prefix.size() + 1) 180 continue; 181 182 for (size_t i = 0; i < prefix.size(); ++i) { 183 if ((*path)[i] != prefix[i]) 184 goto do_continue; 185 } 186 187 if (row_index == 0) { 188 index_path = path; 189 break; 190 } 191 --row_index; 192 do_continue:; 193 } 194 195 return index_path; 196}; 197 198JsonPath const* RemoteObjectPropertyModel::find_cached_path(Vector<JsonPathElement> const& path) const 199{ 200 for (auto& cpath : m_paths) { 201 if (cpath->size() != path.size()) 202 continue; 203 204 for (size_t i = 0; i < cpath->size(); ++i) { 205 if ((*cpath)[i] != path[i]) 206 goto do_continue; 207 } 208 209 return cpath; 210 do_continue:; 211 } 212 213 return nullptr; 214} 215 216}