Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/core/string/node_path.cpp
20844 views
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/variant/variant.h"
34
35
void NodePath::_update_hash_cache() const {
36
StringName path = get_concatenated_names();
37
StringName subpath = get_concatenated_subnames();
38
uint32_t hash = HashMapHasherDefault::hash(Pair<const StringName &, const StringName &>(path, subpath));
39
data->hash_cache = is_absolute() ? hash : ~hash;
40
data->hash_cache_valid = true;
41
}
42
43
void NodePath::prepend_period() {
44
if (data->path.size() && data->path[0].operator String() != ".") {
45
data->path.insert(0, ".");
46
data->concatenated_path = StringName();
47
data->hash_cache_valid = false;
48
}
49
}
50
51
bool NodePath::is_absolute() const {
52
if (!data) {
53
return false;
54
}
55
56
return data->absolute;
57
}
58
59
int NodePath::get_name_count() const {
60
if (!data) {
61
return 0;
62
}
63
64
return data->path.size();
65
}
66
67
StringName NodePath::get_name(int p_idx) const {
68
ERR_FAIL_NULL_V(data, StringName());
69
ERR_FAIL_INDEX_V(p_idx, data->path.size(), StringName());
70
return data->path[p_idx];
71
}
72
73
int NodePath::get_subname_count() const {
74
if (!data) {
75
return 0;
76
}
77
78
return data->subpath.size();
79
}
80
81
StringName NodePath::get_subname(int p_idx) const {
82
ERR_FAIL_NULL_V(data, StringName());
83
ERR_FAIL_INDEX_V(p_idx, data->subpath.size(), StringName());
84
return data->subpath[p_idx];
85
}
86
87
int NodePath::get_total_name_count() const {
88
if (!data) {
89
return 0;
90
}
91
92
return data->path.size() + data->subpath.size();
93
}
94
95
void NodePath::unref() {
96
if (data && data->refcount.unref()) {
97
memdelete(data);
98
}
99
data = nullptr;
100
}
101
102
bool 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->hash_cache_valid && p_path.data->hash_cache_valid) {
112
if (data->hash_cache != p_path.data->hash_cache) {
113
return false;
114
}
115
}
116
117
if (data->absolute != p_path.data->absolute) {
118
return false;
119
}
120
121
int path_size = data->path.size();
122
123
if (path_size != p_path.data->path.size()) {
124
return false;
125
}
126
127
int subpath_size = data->subpath.size();
128
129
if (subpath_size != p_path.data->subpath.size()) {
130
return false;
131
}
132
133
const StringName *l_path_ptr = data->path.ptr();
134
const StringName *r_path_ptr = p_path.data->path.ptr();
135
136
for (int i = 0; i < path_size; i++) {
137
if (l_path_ptr[i] != r_path_ptr[i]) {
138
return false;
139
}
140
}
141
142
const StringName *l_subpath_ptr = data->subpath.ptr();
143
const StringName *r_subpath_ptr = p_path.data->subpath.ptr();
144
145
for (int i = 0; i < subpath_size; i++) {
146
if (l_subpath_ptr[i] != r_subpath_ptr[i]) {
147
return false;
148
}
149
}
150
151
return true;
152
}
153
154
bool NodePath::operator!=(const NodePath &p_path) const {
155
return (!(*this == p_path));
156
}
157
158
void NodePath::operator=(const NodePath &p_path) {
159
if (this == &p_path) {
160
return;
161
}
162
163
unref();
164
165
if (p_path.data && p_path.data->refcount.ref()) {
166
data = p_path.data;
167
}
168
}
169
170
NodePath::operator String() const {
171
if (!data) {
172
return String();
173
}
174
175
String ret;
176
if (data->absolute) {
177
ret = "/";
178
}
179
180
ret += get_concatenated_names();
181
182
String subpath = get_concatenated_subnames();
183
if (!subpath.is_empty()) {
184
ret += ":" + subpath;
185
}
186
187
return ret;
188
}
189
190
Vector<StringName> NodePath::get_names() const {
191
if (data) {
192
return data->path;
193
}
194
return Vector<StringName>();
195
}
196
197
Vector<StringName> NodePath::get_subnames() const {
198
if (data) {
199
return data->subpath;
200
}
201
return Vector<StringName>();
202
}
203
204
StringName NodePath::get_concatenated_names() const {
205
ERR_FAIL_NULL_V(data, StringName());
206
207
if (!data->concatenated_path) {
208
int pc = data->path.size();
209
String concatenated;
210
const StringName *sn = data->path.ptr();
211
for (int i = 0; i < pc; i++) {
212
if (i > 0) {
213
concatenated += "/";
214
}
215
concatenated += sn[i].operator String();
216
}
217
data->concatenated_path = concatenated;
218
}
219
return data->concatenated_path;
220
}
221
222
StringName NodePath::get_concatenated_subnames() const {
223
ERR_FAIL_NULL_V(data, StringName());
224
225
if (!data->concatenated_subpath) {
226
int spc = data->subpath.size();
227
String concatenated;
228
const StringName *ssn = data->subpath.ptr();
229
for (int i = 0; i < spc; i++) {
230
if (i > 0) {
231
concatenated += ":";
232
}
233
concatenated += ssn[i].operator String();
234
}
235
data->concatenated_subpath = concatenated;
236
}
237
return data->concatenated_subpath;
238
}
239
240
NodePath NodePath::slice(int p_begin, int p_end) const {
241
const int name_count = get_name_count();
242
const int total_count = get_total_name_count();
243
244
int begin = CLAMP(p_begin, -total_count, total_count);
245
if (begin < 0) {
246
begin += total_count;
247
}
248
int end = CLAMP(p_end, -total_count, total_count);
249
if (end < 0) {
250
end += total_count;
251
}
252
const int sub_begin = MAX(begin - name_count, 0);
253
const int sub_end = MAX(end - name_count, 0);
254
255
const Vector<StringName> names = get_names().slice(begin, end);
256
const Vector<StringName> sub_names = get_subnames().slice(sub_begin, sub_end);
257
const bool absolute = is_absolute() && (begin == 0);
258
return NodePath(names, sub_names, absolute);
259
}
260
261
NodePath NodePath::rel_path_to(const NodePath &p_np) const {
262
ERR_FAIL_COND_V(!is_absolute(), NodePath());
263
ERR_FAIL_COND_V(!p_np.is_absolute(), NodePath());
264
265
Vector<StringName> src_dirs = get_names();
266
Vector<StringName> dst_dirs = p_np.get_names();
267
268
//find common parent
269
int common_parent = 0;
270
271
while (true) {
272
if (src_dirs.size() == common_parent) {
273
break;
274
}
275
if (dst_dirs.size() == common_parent) {
276
break;
277
}
278
if (src_dirs[common_parent] != dst_dirs[common_parent]) {
279
break;
280
}
281
common_parent++;
282
}
283
284
common_parent--;
285
286
Vector<StringName> relpath;
287
relpath.resize(src_dirs.size() + dst_dirs.size() + 1);
288
289
StringName *relpath_ptr = relpath.ptrw();
290
291
int path_size = 0;
292
StringName back_str("..");
293
for (int i = common_parent + 1; i < src_dirs.size(); i++) {
294
relpath_ptr[path_size++] = back_str;
295
}
296
297
for (int i = common_parent + 1; i < dst_dirs.size(); i++) {
298
relpath_ptr[path_size++] = dst_dirs[i];
299
}
300
301
if (path_size == 0) {
302
relpath_ptr[path_size++] = ".";
303
}
304
305
relpath.resize(path_size);
306
307
return NodePath(relpath, p_np.get_subnames(), false);
308
}
309
310
NodePath NodePath::get_as_property_path() const {
311
if (!data || !data->path.size()) {
312
return *this;
313
} else {
314
Vector<StringName> new_path = data->subpath;
315
316
String initial_subname = data->path[0];
317
318
for (int i = 1; i < data->path.size(); i++) {
319
initial_subname += "/" + data->path[i];
320
}
321
new_path.insert(0, initial_subname);
322
323
return NodePath(Vector<StringName>(), new_path, false);
324
}
325
}
326
327
bool NodePath::is_empty() const {
328
return !data;
329
}
330
331
void NodePath::simplify() {
332
if (!data) {
333
return;
334
}
335
for (int i = 0; i < data->path.size(); i++) {
336
if (data->path.size() == 1) {
337
break;
338
}
339
if (data->path[i].operator String() == ".") {
340
data->path.remove_at(i);
341
i--;
342
} else if (i > 0 && data->path[i].operator String() == ".." && data->path[i - 1].operator String() != "." && data->path[i - 1].operator String() != "..") {
343
//remove both
344
data->path.remove_at(i - 1);
345
data->path.remove_at(i - 1);
346
i -= 2;
347
if (data->path.is_empty()) {
348
data->path.push_back(".");
349
break;
350
}
351
}
352
}
353
data->concatenated_path = StringName();
354
data->hash_cache_valid = false;
355
}
356
357
NodePath NodePath::simplified() const {
358
NodePath np = *this;
359
np.simplify();
360
return np;
361
}
362
363
NodePath::NodePath(const Vector<StringName> &p_path, bool p_absolute) {
364
if (p_path.is_empty() && !p_absolute) {
365
return;
366
}
367
368
data = memnew(Data);
369
data->refcount.init();
370
data->absolute = p_absolute;
371
data->path = p_path;
372
data->hash_cache_valid = false;
373
}
374
375
NodePath::NodePath(const Vector<StringName> &p_path, const Vector<StringName> &p_subpath, bool p_absolute) {
376
if (p_path.is_empty() && p_subpath.is_empty() && !p_absolute) {
377
return;
378
}
379
380
data = memnew(Data);
381
data->refcount.init();
382
data->absolute = p_absolute;
383
data->path = p_path;
384
data->subpath = p_subpath;
385
data->hash_cache_valid = false;
386
}
387
388
NodePath::NodePath(const NodePath &p_path) {
389
if (p_path.data && p_path.data->refcount.ref()) {
390
data = p_path.data;
391
}
392
}
393
394
NodePath::NodePath(const String &p_path) {
395
if (p_path.length() == 0) {
396
return;
397
}
398
399
String path = p_path;
400
Vector<StringName> subpath;
401
402
bool absolute = (path[0] == '/');
403
bool last_is_slash = true;
404
int slices = 0;
405
int subpath_pos = path.find_char(':');
406
407
if (subpath_pos != -1) {
408
int from = subpath_pos + 1;
409
410
for (int i = from; i <= path.length(); i++) {
411
if (path[i] == ':' || path[i] == 0) {
412
String str = path.substr(from, i - from);
413
if (str.is_empty()) {
414
if (path[i] == 0) {
415
continue; // Allow end-of-path :
416
}
417
418
ERR_FAIL_MSG(vformat("Invalid NodePath '%s'.", p_path));
419
}
420
subpath.push_back(str);
421
422
from = i + 1;
423
}
424
}
425
426
path = path.substr(0, subpath_pos);
427
}
428
429
for (int i = (int)absolute; i < path.length(); i++) {
430
if (path[i] == '/') {
431
last_is_slash = true;
432
} else {
433
if (last_is_slash) {
434
slices++;
435
}
436
437
last_is_slash = false;
438
}
439
}
440
441
if (slices == 0 && !absolute && !subpath.size()) {
442
return;
443
}
444
445
data = memnew(Data);
446
data->refcount.init();
447
data->absolute = absolute;
448
data->subpath = subpath;
449
data->hash_cache_valid = false;
450
451
if (slices == 0) {
452
return;
453
}
454
data->path.resize(slices);
455
last_is_slash = true;
456
int from = (int)absolute;
457
int slice = 0;
458
459
for (int i = (int)absolute; i < path.length() + 1; i++) {
460
if (path[i] == '/' || path[i] == 0) {
461
if (!last_is_slash) {
462
String name = path.substr(from, i - from);
463
ERR_FAIL_INDEX(slice, data->path.size());
464
data->path.write[slice++] = name;
465
}
466
from = i + 1;
467
last_is_slash = true;
468
} else {
469
last_is_slash = false;
470
}
471
}
472
}
473
474
NodePath::~NodePath() {
475
unref();
476
}
477
478