RemoteObjectPropertyModel.cpp 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. /*
  2. * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
  3. * All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. *
  8. * 1. Redistributions of source code must retain the above copyright notice, this
  9. * list of conditions and the following disclaimer.
  10. *
  11. * 2. Redistributions in binary form must reproduce the above copyright notice,
  12. * this list of conditions and the following disclaimer in the documentation
  13. * and/or other materials provided with the distribution.
  14. *
  15. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  16. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  17. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  18. * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  19. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  20. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  21. * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  22. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  23. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  24. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25. */
  26. #include "RemoteObjectPropertyModel.h"
  27. #include "RemoteObject.h"
  28. #include "RemoteProcess.h"
  29. namespace Inspector {
  30. RemoteObjectPropertyModel::RemoteObjectPropertyModel(RemoteObject& object)
  31. : m_object(object)
  32. {
  33. }
  34. int RemoteObjectPropertyModel::row_count(const GUI::ModelIndex& index) const
  35. {
  36. Function<int(const JsonValue&)> do_count = [&](const JsonValue& value) {
  37. if (value.is_array())
  38. return value.as_array().size();
  39. else if (value.is_object())
  40. return value.as_object().size();
  41. return 0;
  42. };
  43. if (index.is_valid()) {
  44. auto* path = static_cast<const JsonPath*>(index.internal_data());
  45. return do_count(path->resolve(m_object.json));
  46. } else {
  47. return do_count(m_object.json);
  48. }
  49. }
  50. String RemoteObjectPropertyModel::column_name(int column) const
  51. {
  52. switch (column) {
  53. case Column::Name:
  54. return "Name";
  55. case Column::Value:
  56. return "Value";
  57. }
  58. ASSERT_NOT_REACHED();
  59. }
  60. GUI::Variant RemoteObjectPropertyModel::data(const GUI::ModelIndex& index, GUI::ModelRole role) const
  61. {
  62. auto* path = static_cast<const JsonPath*>(index.internal_data());
  63. if (!path)
  64. return {};
  65. if (role == GUI::ModelRole::Display) {
  66. switch (index.column()) {
  67. case Column::Name:
  68. return path->last().to_string();
  69. case Column::Value: {
  70. auto data = path->resolve(m_object.json);
  71. if (data.is_array())
  72. return String::format("<Array with %d element%s", data.as_array().size(), data.as_array().size() == 1 ? ">" : "s>");
  73. if (data.is_object())
  74. return String::format("<Object with %d entr%s", data.as_object().size(), data.as_object().size() == 1 ? "y>" : "ies>");
  75. return data;
  76. }
  77. }
  78. }
  79. return {};
  80. }
  81. void RemoteObjectPropertyModel::update()
  82. {
  83. did_update();
  84. }
  85. void RemoteObjectPropertyModel::set_data(const GUI::ModelIndex& index, const GUI::Variant& new_value)
  86. {
  87. if (!index.is_valid())
  88. return;
  89. auto* path = static_cast<const JsonPath*>(index.internal_data());
  90. if (path->size() != 1)
  91. return;
  92. FlatPtr address = m_object.address;
  93. RemoteProcess::the().set_property(address, path->first().to_string(), new_value.to_string());
  94. did_update();
  95. }
  96. GUI::ModelIndex RemoteObjectPropertyModel::index(int row, int column, const GUI::ModelIndex& parent) const
  97. {
  98. const auto& parent_path = parent.is_valid() ? *static_cast<const JsonPath*>(parent.internal_data()) : JsonPath {};
  99. auto nth_child = [&](int n, const JsonValue& value) -> const JsonPath* {
  100. auto path = make<JsonPath>();
  101. path->append(parent_path);
  102. int row_index = n;
  103. if (value.is_object()) {
  104. String property_name;
  105. auto& object = value.as_object();
  106. object.for_each_member([&](auto& name, auto&) {
  107. if (row_index > 0) {
  108. --row_index;
  109. } else if (row_index == 0) {
  110. property_name = name;
  111. --row_index;
  112. }
  113. });
  114. if (property_name.is_null())
  115. return nullptr;
  116. path->append({ property_name });
  117. m_paths.append(move(path));
  118. } else if (value.is_array()) {
  119. path->append(JsonPathElement { (size_t)n });
  120. m_paths.append(move(path));
  121. } else {
  122. return nullptr;
  123. }
  124. return &m_paths.last();
  125. };
  126. if (!parent.is_valid()) {
  127. if (m_object.json.is_empty())
  128. return {};
  129. }
  130. auto index_path = cached_path_at(row, parent_path);
  131. if (!index_path)
  132. index_path = nth_child(row, parent_path.resolve(m_object.json));
  133. if (!index_path)
  134. return {};
  135. return create_index(row, column, index_path);
  136. }
  137. GUI::ModelIndex RemoteObjectPropertyModel::parent_index(const GUI::ModelIndex& index) const
  138. {
  139. if (!index.is_valid())
  140. return index;
  141. auto path = *static_cast<const JsonPath*>(index.internal_data());
  142. if (path.is_empty())
  143. return {};
  144. path.take_last();
  145. if (path.is_empty())
  146. return {};
  147. auto* cpath = find_cached_path(path);
  148. if (cpath) {
  149. int index_in_parent = 0;
  150. if (cpath->last().kind() == JsonPathElement::Kind::Index)
  151. index_in_parent = cpath->last().index();
  152. else if (cpath->last().kind() == JsonPathElement::Kind::Key) {
  153. auto path_copy = path;
  154. auto last = path_copy.take_last();
  155. bool found = false;
  156. path_copy.resolve(m_object.json).as_object().for_each_member([&](auto& name, auto&) {
  157. if (!found) {
  158. if (last.key() == name)
  159. found = true;
  160. else
  161. index_in_parent++;
  162. }
  163. });
  164. }
  165. return create_index(index_in_parent, 0, cpath);
  166. }
  167. dbg() << "No cached path found for path " << path.to_string();
  168. return {};
  169. }
  170. const JsonPath* RemoteObjectPropertyModel::cached_path_at(int n, const Vector<JsonPathElement>& prefix) const
  171. {
  172. // FIXME: ModelIndex wants a void*, so we have to keep these
  173. // indices alive, but allocating a new path every time
  174. // we're asked for an index is silly, so we have to look for existing ones first.
  175. const JsonPath* index_path = nullptr;
  176. int row_index = n;
  177. for (auto& path : m_paths) {
  178. if (path.size() != prefix.size() + 1)
  179. continue;
  180. for (size_t i = 0; i < prefix.size(); ++i) {
  181. if (path[i] != prefix[i])
  182. goto do_continue;
  183. }
  184. if (row_index == 0) {
  185. index_path = &path;
  186. break;
  187. }
  188. --row_index;
  189. do_continue:;
  190. }
  191. return index_path;
  192. };
  193. const JsonPath* RemoteObjectPropertyModel::find_cached_path(const Vector<JsonPathElement>& path) const
  194. {
  195. for (auto& cpath : m_paths) {
  196. if (cpath.size() != path.size())
  197. continue;
  198. for (size_t i = 0; i < cpath.size(); ++i) {
  199. if (cpath[i] != path[i])
  200. goto do_continue;
  201. }
  202. return &cpath;
  203. do_continue:;
  204. }
  205. return nullptr;
  206. }
  207. }