1/**************************************************************************/
2/* node_path.cpp */
3/**************************************************************************/
4/* This file is part of: */
5/* GODOT ENGINE */
6/* https://godotengine.org */
7/**************************************************************************/
8/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10/* */
11/* Permission is hereby granted, free of charge, to any person obtaining */
12/* a copy of this software and associated documentation files (the */
13/* "Software"), to deal in the Software without restriction, including */
14/* without limitation the rights to use, copy, modify, merge, publish, */
15/* distribute, sublicense, and/or sell copies of the Software, and to */
16/* permit persons to whom the Software is furnished to do so, subject to */
17/* the following conditions: */
18/* */
19/* The above copyright notice and this permission notice shall be */
20/* included in all copies or substantial portions of the Software. */
21/* */
22/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29/**************************************************************************/
30
31#include "node_path.h"
32
33#include "core/string/print_string.h"
34
35void NodePath::_update_hash_cache() const {
36 uint32_t h = data->absolute ? 1 : 0;
37 int pc = data->path.size();
38 const StringName *sn = data->path.ptr();
39 for (int i = 0; i < pc; i++) {
40 h = h ^ sn[i].hash();
41 }
42 int spc = data->subpath.size();
43 const StringName *ssn = data->subpath.ptr();
44 for (int i = 0; i < spc; i++) {
45 h = h ^ ssn[i].hash();
46 }
47
48 data->hash_cache_valid = true;
49 data->hash_cache = h;
50}
51
52void NodePath::prepend_period() {
53 if (data->path.size() && data->path[0].operator String() != ".") {
54 data->path.insert(0, ".");
55 data->hash_cache_valid = false;
56 }
57}
58
59bool NodePath::is_absolute() const {
60 if (!data) {
61 return false;
62 }
63
64 return data->absolute;
65}
66
67int NodePath::get_name_count() const {
68 if (!data) {
69 return 0;
70 }
71
72 return data->path.size();
73}
74
75StringName NodePath::get_name(int p_idx) const {
76 ERR_FAIL_NULL_V(data, StringName());
77 ERR_FAIL_INDEX_V(p_idx, data->path.size(), StringName());
78 return data->path[p_idx];
79}
80
81int NodePath::get_subname_count() const {
82 if (!data) {
83 return 0;
84 }
85
86 return data->subpath.size();
87}
88
89StringName NodePath::get_subname(int p_idx) const {
90 ERR_FAIL_NULL_V(data, StringName());
91 ERR_FAIL_INDEX_V(p_idx, data->subpath.size(), StringName());
92 return data->subpath[p_idx];
93}
94
95void NodePath::unref() {
96 if (data && data->refcount.unref()) {
97 memdelete(data);
98 }
99 data = nullptr;
100}
101
102bool NodePath::operator==(const NodePath &p_path) const {
103 if (data == p_path.data) {
104 return true;
105 }
106
107 if (!data || !p_path.data) {
108 return false;
109 }
110
111 if (data->absolute != p_path.data->absolute) {
112 return false;
113 }
114
115 int path_size = data->path.size();
116
117 if (path_size != p_path.data->path.size()) {
118 return false;
119 }
120
121 int subpath_size = data->subpath.size();
122
123 if (subpath_size != p_path.data->subpath.size()) {
124 return false;
125 }
126
127 const StringName *l_path_ptr = data->path.ptr();
128 const StringName *r_path_ptr = p_path.data->path.ptr();
129
130 for (int i = 0; i < path_size; i++) {
131 if (l_path_ptr[i] != r_path_ptr[i]) {
132 return false;
133 }
134 }
135
136 const StringName *l_subpath_ptr = data->subpath.ptr();
137 const StringName *r_subpath_ptr = p_path.data->subpath.ptr();
138
139 for (int i = 0; i < subpath_size; i++) {
140 if (l_subpath_ptr[i] != r_subpath_ptr[i]) {
141 return false;
142 }
143 }
144
145 return true;
146}
147
148bool NodePath::operator!=(const NodePath &p_path) const {
149 return (!(*this == p_path));
150}
151
152void NodePath::operator=(const NodePath &p_path) {
153 if (this == &p_path) {
154 return;
155 }
156
157 unref();
158
159 if (p_path.data && p_path.data->refcount.ref()) {
160 data = p_path.data;
161 }
162}
163
164NodePath::operator String() const {
165 if (!data) {
166 return String();
167 }
168
169 String ret;
170 if (data->absolute) {
171 ret = "/";
172 }
173
174 for (int i = 0; i < data->path.size(); i++) {
175 if (i > 0) {
176 ret += "/";
177 }
178 ret += data->path[i].operator String();
179 }
180
181 for (int i = 0; i < data->subpath.size(); i++) {
182 ret += ":" + data->subpath[i].operator String();
183 }
184
185 return ret;
186}
187
188Vector<StringName> NodePath::get_names() const {
189 if (data) {
190 return data->path;
191 }
192 return Vector<StringName>();
193}
194
195Vector<StringName> NodePath::get_subnames() const {
196 if (data) {
197 return data->subpath;
198 }
199 return Vector<StringName>();
200}
201
202StringName NodePath::get_concatenated_names() const {
203 ERR_FAIL_NULL_V(data, StringName());
204
205 if (!data->concatenated_path) {
206 int pc = data->path.size();
207 String concatenated;
208 const StringName *sn = data->path.ptr();
209 for (int i = 0; i < pc; i++) {
210 concatenated += i == 0 ? sn[i].operator String() : "/" + sn[i];
211 }
212 data->concatenated_path = concatenated;
213 }
214 return data->concatenated_path;
215}
216
217StringName NodePath::get_concatenated_subnames() const {
218 ERR_FAIL_NULL_V(data, StringName());
219
220 if (!data->concatenated_subpath) {
221 int spc = data->subpath.size();
222 String concatenated;
223 const StringName *ssn = data->subpath.ptr();
224 for (int i = 0; i < spc; i++) {
225 concatenated += i == 0 ? ssn[i].operator String() : ":" + ssn[i];
226 }
227 data->concatenated_subpath = concatenated;
228 }
229 return data->concatenated_subpath;
230}
231
232NodePath NodePath::rel_path_to(const NodePath &p_np) const {
233 ERR_FAIL_COND_V(!is_absolute(), NodePath());
234 ERR_FAIL_COND_V(!p_np.is_absolute(), NodePath());
235
236 Vector<StringName> src_dirs = get_names();
237 Vector<StringName> dst_dirs = p_np.get_names();
238
239 //find common parent
240 int common_parent = 0;
241
242 while (true) {
243 if (src_dirs.size() == common_parent) {
244 break;
245 }
246 if (dst_dirs.size() == common_parent) {
247 break;
248 }
249 if (src_dirs[common_parent] != dst_dirs[common_parent]) {
250 break;
251 }
252 common_parent++;
253 }
254
255 common_parent--;
256
257 Vector<StringName> relpath;
258 relpath.resize(src_dirs.size() + dst_dirs.size() + 1);
259
260 StringName *relpath_ptr = relpath.ptrw();
261
262 int path_size = 0;
263 StringName back_str("..");
264 for (int i = common_parent + 1; i < src_dirs.size(); i++) {
265 relpath_ptr[path_size++] = back_str;
266 }
267
268 for (int i = common_parent + 1; i < dst_dirs.size(); i++) {
269 relpath_ptr[path_size++] = dst_dirs[i];
270 }
271
272 if (path_size == 0) {
273 relpath_ptr[path_size++] = ".";
274 }
275
276 relpath.resize(path_size);
277
278 return NodePath(relpath, p_np.get_subnames(), false);
279}
280
281NodePath NodePath::get_as_property_path() const {
282 if (!data || !data->path.size()) {
283 return *this;
284 } else {
285 Vector<StringName> new_path = data->subpath;
286
287 String initial_subname = data->path[0];
288
289 for (int i = 1; i < data->path.size(); i++) {
290 initial_subname += "/" + data->path[i];
291 }
292 new_path.insert(0, initial_subname);
293
294 return NodePath(Vector<StringName>(), new_path, false);
295 }
296}
297
298bool NodePath::is_empty() const {
299 return !data;
300}
301
302void NodePath::simplify() {
303 if (!data) {
304 return;
305 }
306 for (int i = 0; i < data->path.size(); i++) {
307 if (data->path.size() == 1) {
308 break;
309 }
310 if (data->path[i].operator String() == ".") {
311 data->path.remove_at(i);
312 i--;
313 } else if (i > 0 && data->path[i].operator String() == ".." && data->path[i - 1].operator String() != "." && data->path[i - 1].operator String() != "..") {
314 //remove both
315 data->path.remove_at(i - 1);
316 data->path.remove_at(i - 1);
317 i -= 2;
318 if (data->path.size() == 0) {
319 data->path.push_back(".");
320 break;
321 }
322 }
323 }
324 data->hash_cache_valid = false;
325}
326
327NodePath NodePath::simplified() const {
328 NodePath np = *this;
329 np.simplify();
330 return np;
331}
332
333NodePath::NodePath(const Vector<StringName> &p_path, bool p_absolute) {
334 if (p_path.size() == 0) {
335 return;
336 }
337
338 data = memnew(Data);
339 data->refcount.init();
340 data->absolute = p_absolute;
341 data->path = p_path;
342 data->hash_cache_valid = false;
343}
344
345NodePath::NodePath(const Vector<StringName> &p_path, const Vector<StringName> &p_subpath, bool p_absolute) {
346 if (p_path.size() == 0 && p_subpath.size() == 0) {
347 return;
348 }
349
350 data = memnew(Data);
351 data->refcount.init();
352 data->absolute = p_absolute;
353 data->path = p_path;
354 data->subpath = p_subpath;
355 data->hash_cache_valid = false;
356}
357
358NodePath::NodePath(const NodePath &p_path) {
359 if (p_path.data && p_path.data->refcount.ref()) {
360 data = p_path.data;
361 }
362}
363
364NodePath::NodePath(const String &p_path) {
365 if (p_path.length() == 0) {
366 return;
367 }
368
369 String path = p_path;
370 Vector<StringName> subpath;
371
372 bool absolute = (path[0] == '/');
373 bool last_is_slash = true;
374 int slices = 0;
375 int subpath_pos = path.find(":");
376
377 if (subpath_pos != -1) {
378 int from = subpath_pos + 1;
379
380 for (int i = from; i <= path.length(); i++) {
381 if (path[i] == ':' || path[i] == 0) {
382 String str = path.substr(from, i - from);
383 if (str.is_empty()) {
384 if (path[i] == 0) {
385 continue; // Allow end-of-path :
386 }
387
388 ERR_FAIL_MSG("Invalid NodePath '" + p_path + "'.");
389 }
390 subpath.push_back(str);
391
392 from = i + 1;
393 }
394 }
395
396 path = path.substr(0, subpath_pos);
397 }
398
399 for (int i = (int)absolute; i < path.length(); i++) {
400 if (path[i] == '/') {
401 last_is_slash = true;
402 } else {
403 if (last_is_slash) {
404 slices++;
405 }
406
407 last_is_slash = false;
408 }
409 }
410
411 if (slices == 0 && !absolute && !subpath.size()) {
412 return;
413 }
414
415 data = memnew(Data);
416 data->refcount.init();
417 data->absolute = absolute;
418 data->subpath = subpath;
419 data->hash_cache_valid = false;
420
421 if (slices == 0) {
422 return;
423 }
424 data->path.resize(slices);
425 last_is_slash = true;
426 int from = (int)absolute;
427 int slice = 0;
428
429 for (int i = (int)absolute; i < path.length() + 1; i++) {
430 if (path[i] == '/' || path[i] == 0) {
431 if (!last_is_slash) {
432 String name = path.substr(from, i - from);
433 ERR_FAIL_INDEX(slice, data->path.size());
434 data->path.write[slice++] = name;
435 }
436 from = i + 1;
437 last_is_slash = true;
438 } else {
439 last_is_slash = false;
440 }
441 }
442}
443
444NodePath::~NodePath() {
445 unref();
446}
447