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