00001 // See ../license.txt for license information. 00002 // 00003 // jscript_object_navigator.cpp 00004 // 00005 // 8-Jul-2003 phamilton Created 00006 // 00007 00008 #define REFLECT_IN_LIBRARY_SOURCE 00009 00010 #include "jscript_object_navigator.hpp" 00011 #include <iostream> 00012 #include "../common/object.hpp" 00013 #include "../common/composition_object.hpp" 00014 #include "../common/outerable_object.hpp" 00015 #include "../common/comparable_object.hpp" 00016 #include "../common/nameable_object.hpp" 00017 #include "jscript_object_nav_errors.h" 00018 #include "object_visitor.hpp" 00019 #include "member_visitor.hpp" 00020 #include <boost/lexical_cast.hpp> 00021 #include <boost/bind.hpp> 00022 00023 using namespace ph::reflect; 00024 00025 object_navigator *jscript_object_navigator_factory::create(const ph::common::object_base *root) 00026 { 00027 // use jscript style navigation. 00028 return new jscript_object_navigator(root, _console, _debug); 00029 } 00030 00031 ph::common::object_base *jscript_object_navigator::navigate(const ph::common::object_base *obj, const std::string &path) 00032 /* 00033 Given the name of a sub-object, this function walks the 00034 object tree and retrieves the named object. 00035 00036 names are always relative to this object. 00037 00038 root() - means the root of the object tree. 00039 parent(n) - means the nth (where the n can be left out) parent of this object. 00040 00041 You can subscript a property if you know that the property is 00042 a vector property, 00043 */ 00044 { 00045 // start off with the path passed in. 00046 std::string descr = path; 00047 00048 // start off with the object passed in. 00049 const ph::common::object_base *o = obj; 00050 00051 // walk the dots in the string. 00052 int dot = find_span_quotes(descr, '.'); 00053 std::string part = ""; 00054 long parsed = dot; 00055 while (dot >= 0) 00056 { 00057 part = descr.substr(0, dot); 00058 descr = descr.substr(dot+1); 00059 00060 if (part.empty()) 00061 { 00062 error(e_empty_part, path.substr(parsed)); 00063 return 0; 00064 } 00065 00066 // get this piece. 00067 const ph::common::object_base *found_o = get_sub_object(o, part); 00068 00069 // if the obj is NULL, then just successfully return a NULL object. 00070 if (!found_o) 00071 return 0; 00072 00073 // next dot. 00074 o = found_o; 00075 parsed += dot; 00076 dot = find_span_quotes(descr, '.'); 00077 } 00078 00079 const ph::common::object_base *found_o = get_sub_object(o, descr); 00080 if (found_o) 00081 { 00082 // although we don't modify any of the objects we visit, we do 00083 // need to return the actual object at the end, so 00084 // we cast out const. 00085 return const_cast<ph::common::object_base *>(found_o); 00086 } 00087 return 0; 00088 } 00089 00090 static bool is_match_toggle_quotes(char i, char c, bool *inquotes) 00091 { 00092 if (i == '\'' || i == '"') 00093 *inquotes = !*inquotes; 00094 else if (!*inquotes && i == c) 00095 return true; 00096 return false; 00097 } 00098 00099 int jscript_object_navigator::find_span_quotes(const std::string &s, char c) 00100 /* 00101 A simple find that can span quotes. This allows "." and "(" to appear in quotes. 00102 */ 00103 { 00104 // a proper std::find_if would be nice here, but this will only 00105 // work efficiently if this function returns an iterator. 00106 bool inquotes = false; 00107 for (int i = 0; i < (int)s.size(); i++) 00108 if (is_match_toggle_quotes(s[i], c, &inquotes)) 00109 return i; 00110 00111 return -1; 00112 } 00113 00114 void jscript_object_navigator::error(const std::string &msg, const std::string &name) 00115 { 00116 if (!_silent) 00117 *_console << "Syntax Error. " + msg + " [" << name << "]" << std::endl; 00118 } 00119 00120 static std::string get_name(const ph::common::object_base *obj) 00121 { 00122 if (!obj->nameable()) 00123 return "object has no nameable interface."; 00124 return obj->nameable()->name(); 00125 } 00126 00127 const ph::common::object_base *jscript_object_navigator::skip_composite(const ph::common::object_base *obj) 00128 { 00129 // skip over composite 00130 if (obj->composition() && obj->composition()->composite()) 00131 return go_parent(obj); 00132 else 00133 return obj; 00134 } 00135 00136 const ph::common::object_base *jscript_object_navigator::go_parent(const ph::common::object_base *obj) 00137 { 00138 if (obj->outerable()) 00139 return obj->outerable()->outer(); 00140 error(e_internal_no_outer_interface, get_name(obj)); 00141 return 0; 00142 } 00143 00144 const ph::common::object_base *jscript_object_navigator::get_composite(const ph::common::object_base *obj, const std::string &name) 00145 { 00146 if (!obj->visitable()) 00147 { 00148 error(e_internal_no_reflect_interface, get_name(obj)); 00149 return 0; 00150 } 00151 00152 // the property may be a composite object reference (they don't subscript). 00153 get_composite_object_visitor v(name); 00154 obj->visitable()->accept(&v); 00155 return v.obj(); 00156 } 00157 00158 const ph::common::object_base *jscript_object_navigator::get_obj(const ph::common::object_base *obj, const std::string &name) 00159 { 00160 if (!obj->visitable()) 00161 { 00162 error(e_internal_no_reflect_interface, get_name(obj)); 00163 return 0; 00164 } 00165 00166 get_object_visitor v(name); 00167 obj->visitable()->accept(&v); 00168 return v.obj(); 00169 } 00170 00171 const ph::common::object_base *jscript_object_navigator::get_nth_obj(const ph::common::object_base *obj, int n) 00172 { 00173 if (!obj->visitable()) 00174 { 00175 error(e_internal_no_reflect_interface, get_name(obj)); 00176 return 0; 00177 } 00178 00179 if (_debug) 00180 *_console << ">> getting [" << n << "]." << std::endl; 00181 00182 get_nth_object_visitor v(n); 00183 obj->visitable()->accept(&v); 00184 return v.obj(); 00185 } 00186 00187 const ph::common::object_base *jscript_object_navigator::get_sub_object( 00188 const ph::common::object_base *parent, const std::string &path) 00189 { 00190 if (_debug) 00191 *_console << ">> searching [" << get_name(parent) << "] for [" << path << "]" << std::endl; 00192 int br = path.find('('); 00193 if (br >= 0 && path[path.length()-1] == ')') 00194 { 00195 // syntax was object(something) 00196 00197 std::string prop = path.substr(0, br); 00198 std::string subs = path.substr(br+1, path.length() - br - 2); 00199 00200 if (_debug) 00201 *_console << ">> " << prop << "(" << subs << ")" << std::endl; 00202 00203 // look at the special object references. 00204 if (subs.length() == 0) 00205 { 00206 // syntax was object() 00207 00208 if (prop == "root") 00209 { 00210 // root() 00211 if (_debug) 00212 *_console << ">> returning root" << std::endl; 00213 return _root; 00214 } 00215 else if (prop == "parent") 00216 { 00217 // parent() 00218 const ph::common::object_base *obj = go_parent(parent); 00219 if (!obj) 00220 { 00221 error(e_internal_no_outer, prop); 00222 return 0; 00223 } 00224 00225 // skip over composite 00226 obj = skip_composite(obj); 00227 00228 if (_debug) 00229 *_console << ">> returning [" << (obj ? get_name(obj) : "null") << "]" << std::endl; 00230 00231 return obj; 00232 } 00233 else 00234 { 00235 // see if the object to be found is a composite object, and if so return that. 00236 if (_debug) 00237 *_console << ">> " << prop << std::endl; 00238 00239 const ph::common::object_base *c = get_composite(parent, prop); 00240 if (!c) 00241 { 00242 error(e_nobuiltin_or_composite, prop); 00243 return 0; 00244 } 00245 00246 if (_debug) 00247 *_console << ">> returning [" << get_name(c) << "]" << std::endl; 00248 return c; 00249 } 00250 } 00251 else 00252 { 00253 // syntax was object(subscript) 00254 00255 if (prop == "parent") 00256 { 00257 // syntax was parent(subscript) 00258 if (_debug) 00259 *_console << ">> parent(subscript)." << std::endl; 00260 00261 // if the subscript has quotes around it, then use it as a string, 00262 // otherwise it's a number. 00263 if (subs[0] == '\'' && subs[subs.length()-1] == '\'') 00264 { 00265 std::string parentname = subs.substr(1, subs.length()-2); 00266 bool found = false; 00267 const ph::common::object_base *pn = go_parent(parent); 00268 while (pn && !found) 00269 { 00270 if (get_name(pn) == parentname) 00271 found = true; 00272 else 00273 pn = go_parent(pn); 00274 } 00275 if (!found) 00276 { 00277 error(e_no_parent, parentname); 00278 return 0; 00279 } 00280 00281 return pn; 00282 } 00283 else 00284 { 00285 int count = boost::lexical_cast<long>(subs); 00286 const ph::common::object_base *pn = go_parent(parent); 00287 if (!pn) 00288 { 00289 error(e_internal_no_outer, subs); 00290 return 0; 00291 } 00292 00293 // skip over composite 00294 pn = skip_composite(pn); 00295 while (pn && --count > 0) 00296 { 00297 pn = go_parent(pn); 00298 if (pn) 00299 pn = skip_composite(pn); 00300 } 00301 00302 if (!pn) 00303 { 00304 error(e_no_nth_parent, subs); 00305 return 0; 00306 } 00307 00308 return pn; 00309 } 00310 } 00311 else 00312 { 00313 // syntax was object(subscript) 00314 if (_debug) 00315 *_console << ">> object(subscript)." << std::endl; 00316 00317 const ph::common::object_base *c = get_composite(parent, prop); 00318 if (!c) 00319 { 00320 error(e_no_composite, prop); 00321 return 0; 00322 } 00323 00324 // if the subscript has quotes around it, then use it as a string, 00325 // otherwise it's a number. 00326 if (subs[0] == '\'' && subs[subs.length()-1] == '\'') 00327 { 00328 if (_debug) 00329 *_console << ">> object string subscript." << std::endl; 00330 00331 const ph::common::object_base *obj = get_obj(c, subs.substr(1, subs.length()-2)); 00332 if (!obj) 00333 { 00334 error(e_no_named_object, subs); 00335 return 0; 00336 } 00337 00338 if (_debug) 00339 *_console << ">> returning [" << get_name(obj) << "]" << std::endl; 00340 00341 return obj; 00342 } 00343 else 00344 { 00345 if (_debug) 00346 *_console << ">> object nth subscript." << std::endl; 00347 00348 // for the end user, we use 1 based subscripts. 00349 const ph::common::object_base *obj = get_nth_obj(c, boost::lexical_cast<long>(subs)-1); 00350 if (!obj) 00351 { 00352 error(e_no_nth_object, subs); 00353 return 0; 00354 } 00355 00356 if (_debug) 00357 *_console << ">> returning [" << get_name(obj) << "]" << std::endl; 00358 00359 if (obj->comparable() && obj->comparable()->equal(c)) 00360 *_console << ">> object returned was same as composite?." << std::endl; 00361 00362 return obj; 00363 } 00364 } 00365 } 00366 } 00367 else 00368 { 00369 // syntax was object.object. This will only work for singleton composites. 00370 if (_debug) 00371 *_console << ">> " << path << std::endl; 00372 00373 // the property may be a composite object reference (they don't subscript). 00374 const ph::common::object_base *c = get_composite(parent, path); 00375 if (!c) 00376 { 00377 error(e_no_composite, path); 00378 return 0; 00379 } 00380 00381 // if we have no composite interface, then it can't be a singleton composite. 00382 if (c->composition() && c->composition()->composite() && !c->composition()->composite()->singleton()) 00383 { 00384 error(e_not_singleton_composite, path); 00385 return 0; 00386 } 00387 00388 const ph::common::object_base *obj = get_nth_obj(c, 0); 00389 if (!obj) 00390 { 00391 error(e_empty_singleton_composite, path); 00392 return 0; 00393 } 00394 00395 if (_debug) 00396 *_console << ">> returning [" << get_name(obj) << "]" << std::endl; 00397 00398 return obj; 00399 } 00400 00401 error(e_internal_get_sub_object_reached_end, path); 00402 return 0; 00403 }