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  */
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  */
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  */
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  */
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  */
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  */
322 add_match_exact_type(TypeHandle type, int flags) {
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  */
335 add_match_inexact_type(TypeHandle type, int flags) {
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  */
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  */
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  */
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  */
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  */
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 };
void add_match_one(int flags)
Adds a component that will match any node (but not a chain of many nodes).
bool add_flags(const std::string &str_flags)
Adds a sequence of control flags.
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_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.
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 ...
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...
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.
void add_match_many(int flags)
Adds a component that will match a chain of zero or more consecutive nodes.
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_name(const std::string &name, int flags)
Adds a component that must match the name of a node exactly.
void add_match_pointer(PandaNode *pointer, int flags)
Adds a component that must match a particular node exactly, by pointer.
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,...
A basic node of the scene graph or data graph.
Definition: pandaNode.h:65
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
static TypeRegistry * ptr()
Returns the pointer to the global TypeRegistry object.
Definition: typeRegistry.I:30
TypeHandle find_type(const std::string &name) const
Looks for a previously-registered type of the given name.
bool is_exact_type(TypeHandle handle) const
Returns true if the current object is the indicated type exactly.
Definition: typedObject.I:38
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition: typedObject.I:28
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.