Panda3D
findApproxPath.cxx
1 // Filename: findApproxPath.cxx
2 // Created by: drose (13Mar02)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "findApproxPath.h"
16 #include "config_pgraph.h"
17 
18 #include "string_utils.h"
19 #include "pandaNode.h"
20 
21 
22 ////////////////////////////////////////////////////////////////////
23 // Function: FindApproxPath::Component::matches
24 // Access: Public
25 // Description: Returns true if the indicated node matches this
26 // component, false otherwise.
27 ////////////////////////////////////////////////////////////////////
28 bool FindApproxPath::Component::
29 matches(PandaNode *node) const {
30  string node_name;
31 
32  switch (_type) {
33  case CT_match_name:
34  // Match the node's name exactly.
35  return (_name == node->get_name());
36 
37  case CT_match_name_insensitive:
38  // Match the node's name exactly, with case-insensitive comparison.
39  return cmp_nocase(_name, node->get_name()) == 0;
40 
41  case CT_match_name_glob:
42  // Match the node's name according to filename globbing rules.
43  return (_glob.matches(node->get_name()));
44 
45  case CT_match_exact_type:
46  // Match the node's type exactly.
47  return (node->is_exact_type(_type_handle));
48 
49  case CT_match_inexact_type:
50  // Match the node's type inexactly: it's a match if the node
51  // is the type, or is derived from the type.
52  return (node->is_of_type(_type_handle));
53 
54  case CT_match_tag:
55  // Match the node's tag only.
56  return (node->has_tag(_name));
57 
58  case CT_match_tag_value:
59  // Match the node's tag and value.
60  if (node->has_tag(_name)) {
61  return _glob.matches(node->get_tag(_name));
62  }
63  return false;
64 
65  case CT_match_one:
66  case CT_match_many:
67  // Match any node.
68  return true;
69 
70  case CT_match_pointer:
71  // Match only this one particular node.
72  return (_pointer == node);
73  }
74 
75  pgraph_cat.error()
76  << "Invalid component in FindApproxPath\n";
77  return false;
78 }
79 
80 ////////////////////////////////////////////////////////////////////
81 // Function: FindApproxPath::Component::output
82 // Access: Public
83 // Description:
84 ////////////////////////////////////////////////////////////////////
85 void FindApproxPath::Component::
86 output(ostream &out) const {
87  out << _type;
88  switch (_type) {
89  case CT_match_name:
90  case CT_match_name_insensitive:
91  case CT_match_name_glob:
92  case CT_match_tag:
93  out << " \"" << _name << "\"";
94  break;
95 
96  case CT_match_tag_value:
97  out << " \"" << _name << "\"=\"" << _glob << "\"";
98  break;
99 
100  case CT_match_exact_type:
101  case CT_match_inexact_type:
102  out << " " << _type_handle;
103  break;
104 
105  case CT_match_pointer:
106  out << " (" << *_pointer << ")";
107  break;
108 
109  default:
110  break;
111  }
112 }
113 
114 ////////////////////////////////////////////////////////////////////
115 // Function: FindApproxPath::add_string
116 // Access: Public
117 // Description: Adds a sequence of components separated by slashes,
118 // followed optionally by a semicolon and a sequence of
119 // control flags, to the path sequence. Returns true if
120 // successful, false if the string contained an error.
121 ////////////////////////////////////////////////////////////////////
122 bool FindApproxPath::
123 add_string(const string &str_path) {
124  // First, chop the string up by slashes into its components.
125  vector_string components;
126 
127  size_t start = 0;
128  size_t slash = str_path.find('/');
129  while (slash != string::npos) {
130  components.push_back(str_path.substr(start, slash - start));
131  start = slash + 1;
132  slash = str_path.find('/', start);
133  }
134 
135  size_t semicolon = str_path.rfind(';');
136 
137  // We want to find the *last* semicolon at start or later, if there
138  // happens to be more than one. rfind will find the rightmost
139  // semicolon in the entire string; if this is less than start, there
140  // is no semicolon right of start.
141  if (semicolon < start) {
142  semicolon = string::npos;
143  }
144 
145  components.push_back(str_path.substr(start, semicolon - start));
146 
147  if (semicolon != string::npos) {
148  if (!add_flags(str_path.substr(semicolon + 1))) {
149  return false;
150  }
151  }
152 
153  // Now decode each component and add it to the path.
154  vector_string::const_iterator ci;
155  for (ci = components.begin(); ci != components.end(); ++ci) {
156  if (!add_component(*ci)) {
157  return false;
158  }
159  }
160 
161  return true;
162 }
163 
164 ////////////////////////////////////////////////////////////////////
165 // Function: FindApproxPath::add_flags
166 // Access: Public
167 // Description: Adds a sequence of control flags. This will be a
168 // sequence of letters preceded by either '+' or '-',
169 // with no intervening punctuation. Returns true if
170 // successful, false otherwise.
171 ////////////////////////////////////////////////////////////////////
172 bool FindApproxPath::
173 add_flags(const string &str_flags) {
174  string::const_iterator pi = str_flags.begin();
175  while (pi != str_flags.end()) {
176  bool on;
177  switch (*pi) {
178  case '+':
179  on = true;
180  break;
181  case '-':
182  on = false;
183  break;
184  default:
185  pgraph_cat.error()
186  << "Invalid control flag string: " << str_flags << "\n";
187  return false;
188  }
189 
190  ++pi;
191  if (pi == str_flags.end()) {
192  pgraph_cat.error()
193  << "Invalid control flag string: " << str_flags << "\n";
194  return false;
195  }
196 
197  switch (*pi) {
198  case 'h':
199  _return_hidden = on;
200  break;
201 
202  case 's':
203  _return_stashed = on;
204  break;
205 
206  case 'i':
207  _case_insensitive = on;
208  break;
209 
210  default:
211  pgraph_cat.error()
212  << "Invalid control flag string: " << str_flags << "\n";
213  return false;
214  }
215 
216  ++pi;
217  }
218 
219  return true;
220 }
221 
222 
223 ////////////////////////////////////////////////////////////////////
224 // Function: FindApproxPath::add_component
225 // Access: Public
226 // Description: Adds a single component to the path sequence, defined
227 // by a string as might appear between slashes in the
228 // path string. Returns true if successful, false if
229 // the string component was in some way invalid.
230 ////////////////////////////////////////////////////////////////////
231 bool FindApproxPath::
232 add_component(string str_component) {
233  int flags = 0;
234  if (str_component.size() >= 2 && str_component.substr(0, 2) == "@@") {
235  flags |= CF_stashed;
236  str_component = str_component.substr(2);
237  }
238 
239  if (str_component == "*") {
240  add_match_one(flags);
241 
242  } else if (str_component == "**") {
243  if ((flags & CF_stashed) != 0) {
244  pgraph_cat.error()
245  << "@@** is undefined; use @@*/** or **/@@* instead.\n";
246  return false;
247  }
248  add_match_many(flags);
249 
250  } else if (!str_component.empty() && str_component[0] == '-') {
251  string type_name = str_component.substr(1);
252  TypeHandle handle = TypeRegistry::ptr()->find_type(type_name);
253 
254  if (handle == TypeHandle::none()) {
255  pgraph_cat.error()
256  << "Invalid type name: " << type_name << "\n";
257  return false;
258 
259  } else {
260  add_match_exact_type(handle, flags);
261  }
262 
263  } else if (!str_component.empty() && str_component[0] == '+') {
264  string type_name = str_component.substr(1);
265  TypeHandle handle = TypeRegistry::ptr()->find_type(type_name);
266 
267  if (handle == TypeHandle::none()) {
268  pgraph_cat.error()
269  << "Invalid type name: " << type_name << "\n";
270  return false;
271 
272  } else {
273  add_match_inexact_type(handle, flags);
274  }
275 
276  } else if (!str_component.empty() && str_component[0] == '=') {
277  size_t equals = str_component.find('=', 1);
278  if (equals != string::npos) {
279  // =key=value
280  string tag_key = str_component.substr(1, equals - 1);
281  string tag_value = str_component.substr(equals + 1);
282  add_match_tag_value(tag_key, tag_value, flags);
283  } else {
284  // =key
285  string tag_key = str_component.substr(1);
286  add_match_tag(tag_key, flags);
287  }
288 
289  } else {
290  add_match_name_glob(str_component, flags);
291  }
292 
293  return true;
294 }
295 
296 ////////////////////////////////////////////////////////////////////
297 // Function: FindApproxPath::add_match_name
298 // Access: Public
299 // Description: Adds a component that must match the name of a node
300 // exactly.
301 ////////////////////////////////////////////////////////////////////
302 void FindApproxPath::
303 add_match_name(const string &name, int flags) {
304  Component comp;
305  comp._type = _case_insensitive ? CT_match_name_insensitive : CT_match_name;
306  comp._name = name;
307  comp._flags = flags;
308  _path.push_back(comp);
309 }
310 
311 ////////////////////////////////////////////////////////////////////
312 // Function: FindApproxPath::add_match_name_glob
313 // Access: Public
314 // Description: Adds a component that must match the name of a node
315 // using standard shell globbing rules, with wildcard
316 // characters accepted.
317 ////////////////////////////////////////////////////////////////////
318 void FindApproxPath::
319 add_match_name_glob(const string &name, int flags) {
320  Component comp;
321  comp._type = CT_match_name_glob;
322  comp._name = name;
323  comp._glob.set_pattern(name);
324  comp._glob.set_case_sensitive(!_case_insensitive);
325  comp._flags = flags;
326  if (!comp._glob.has_glob_characters()) {
327  // The glob pattern contains no special characters; make it a
328  // literal match for efficiency.
329  add_match_name(name, flags);
330  } else {
331  _path.push_back(comp);
332  }
333 }
334 
335 ////////////////////////////////////////////////////////////////////
336 // Function: FindApproxPath::add_match_exact_type
337 // Access: Public
338 // Description: Adds a component that must match the type of a node
339 // exactly, with no derived types matching.
340 ////////////////////////////////////////////////////////////////////
341 void FindApproxPath::
343  Component comp;
344  comp._type = CT_match_exact_type;
345  comp._type_handle = type;
346  comp._flags = flags;
347  _path.push_back(comp);
348 }
349 
350 ////////////////////////////////////////////////////////////////////
351 // Function: FindApproxPath::add_match_inexact_type
352 // Access: Public
353 // Description: Adds a component that must match the type of a node
354 // or be a base class of the node's type.
355 ////////////////////////////////////////////////////////////////////
356 void FindApproxPath::
358  Component comp;
359  comp._type = CT_match_inexact_type;
360  comp._type_handle = type;
361  comp._flags = flags;
362  _path.push_back(comp);
363 }
364 
365 ////////////////////////////////////////////////////////////////////
366 // Function: FindApproxPath::add_match_tag
367 // Access: Public
368 // Description: Adds a component that will match a node that has a
369 // tag with the indicated key, no matter what the value
370 // is.
371 ////////////////////////////////////////////////////////////////////
372 void FindApproxPath::
373 add_match_tag(const string &name, int flags) {
374  Component comp;
375  comp._type = CT_match_tag;
376  comp._name = name;
377  comp._flags = flags;
378  _path.push_back(comp);
379 }
380 
381 ////////////////////////////////////////////////////////////////////
382 // Function: FindApproxPath::add_match_tag_value
383 // Access: Public
384 // Description: Adds a component that will match a node that has a
385 // tag with the indicated key. The value may be "*" to
386 // match any value, or a particular glob pattern to
387 // match only those nodes with the indicated value.
388 ////////////////////////////////////////////////////////////////////
389 void FindApproxPath::
390 add_match_tag_value(const string &name, const string &value, int flags) {
391  Component comp;
392  comp._type = CT_match_tag_value;
393  comp._name = name;
394  comp._glob.set_pattern(value);
395  comp._flags = flags;
396  _path.push_back(comp);
397 }
398 
399 ////////////////////////////////////////////////////////////////////
400 // Function: FindApproxPath::add_match_one
401 // Access: Public
402 // Description: Adds a component that will match any node (but not a
403 // chain of many nodes).
404 ////////////////////////////////////////////////////////////////////
405 void FindApproxPath::
406 add_match_one(int flags) {
407  Component comp;
408  comp._type = CT_match_one;
409  comp._flags = flags;
410  _path.push_back(comp);
411 }
412 
413 ////////////////////////////////////////////////////////////////////
414 // Function: FindApproxPath::add_match_many
415 // Access: Public
416 // Description: Adds a component that will match a chain of zero or
417 // more consecutive nodes.
418 ////////////////////////////////////////////////////////////////////
419 void FindApproxPath::
420 add_match_many(int flags) {
421  Component comp;
422  comp._type = CT_match_many;
423  comp._flags = flags;
424  _path.push_back(comp);
425 }
426 
427 ////////////////////////////////////////////////////////////////////
428 // Function: FindApproxPath::add_match_pointer
429 // Access: Public
430 // Description: Adds a component that must match a particular node
431 // exactly, by pointer.
432 ////////////////////////////////////////////////////////////////////
433 void FindApproxPath::
434 add_match_pointer(PandaNode *pointer, int flags) {
435  Component comp;
436  comp._type = CT_match_pointer;
437  comp._pointer = pointer;
438  comp._flags = flags;
439  _path.push_back(comp);
440 }
441 
442 ////////////////////////////////////////////////////////////////////
443 // Function: FindApproxPath::output
444 // Access: Public
445 // Description:
446 ////////////////////////////////////////////////////////////////////
447 void FindApproxPath::
448 output(ostream &out) const {
449  out << "(";
450  if (!_path.empty()) {
451  Path::const_iterator pi = _path.begin();
452  out << *pi;
453  ++pi;
454  while (pi != _path.end()) {
455  out << " / " << *pi;
456  ++pi;
457  }
458  }
459  out << ")";
460 }
461 
462 ostream &
463 operator << (ostream &out, FindApproxPath::ComponentType type) {
464  switch (type) {
465  case FindApproxPath::CT_match_name:
466  return out << "match_name";
467 
468  case FindApproxPath::CT_match_name_insensitive:
469  return out << "match_name_insensitive";
470 
471  case FindApproxPath::CT_match_name_glob:
472  return out << "match_name_glob";
473 
474  case FindApproxPath::CT_match_exact_type:
475  return out << "match_exact_type";
476 
477  case FindApproxPath::CT_match_inexact_type:
478  return out << "match_inexact_type";
479 
480  case FindApproxPath::CT_match_tag:
481  return out << "match_tag";
482 
483  case FindApproxPath::CT_match_tag_value:
484  return out << "match_tag_value";
485 
486  case FindApproxPath::CT_match_one:
487  return out << "match_one";
488 
489  case FindApproxPath::CT_match_many:
490  return out << "match_many";
491 
492  case FindApproxPath::CT_match_pointer:
493  return out << "match_pointer";
494  };
495 
496  return out << "**invalid**";
497 };
498 
void add_match_pointer(PandaNode *pointer, int flags)
Adds a component that must match a particular node exactly, by pointer.
A basic node of the scene graph or data graph.
Definition: pandaNode.h:72
bool is_exact_type(TypeHandle handle) const
Returns true if the current object is the indicated type exactly.
Definition: typedObject.I:74
static TypeHandle none()
Returns a special zero-valued TypeHandle that is used to indicate no type.
Definition: typeHandle.I:274
TypeHandle find_type(const string &name) const
Looks for a previously-registered type of the given name.
bool add_flags(const string &str_flags)
Adds a sequence of control flags.
void add_match_one(int flags)
Adds a component that will match any node (but not a chain of many nodes).
void add_match_inexact_type(TypeHandle type, int flags)
Adds a component that must match the type of a node or be a base class of the node&#39;s type...
void add_match_exact_type(TypeHandle type, int flags)
Adds a component that must match the type of a node exactly, with no derived types matching...
void add_match_name_glob(const string &glob, int flags)
Adds a component that must match the name of a node using standard shell globbing rules...
void add_match_many(int flags)
Adds a component that will match a chain of zero or more consecutive nodes.
void add_match_name(const string &name, int flags)
Adds a component that must match the name of a node exactly.
void add_match_tag_value(const string &key, const string &value, int flags)
Adds a component that will match a node that has a tag with the indicated key.
static TypeRegistry * ptr()
Returns the pointer to the global TypeRegistry object.
void add_match_tag(const string &key, int flags)
Adds a component that will match a node that has a tag with the indicated key, no matter what the val...
bool add_string(const string &str_path)
Adds a sequence of components separated by slashes, followed optionally by a semicolon and a sequence...
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition: typedObject.I:63
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:85
bool add_component(string str_component)
Adds a single component to the path sequence, defined by a string as might appear between slashes in ...