Main Page | Namespace List | Class Hierarchy | Alphabetical List | Data Structures | Directories | File List | Namespace Members | Data Fields | Globals

jscript_object_navigator.cpp

Go to the documentation of this file.
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 }

Generated on Wed Apr 5 22:03:24 2006 for cppxmlobj by  doxygen 1.4.3