Main Page | Modules | Namespace List | Class Hierarchy | Alphabetical List | Compound List | File List | Namespace Members | Compound Members | File Members | Related Pages | Examples

vos/metaobjects/misc/search.cc

Go to the documentation of this file.
00001 /* $Id: search.cc,v 1.25 2003/08/06 08:04:13 tetron Exp $ */
00002 
00003 
00004 /* This file was generated by otd2cpp.pl
00005     Copyright (C) 2002 Peter Amstutz <tetron@interreality.org>
00006 
00007     This library is free software; you can redistribute it and/or
00008     modify it under the terms of the GNU Lesser General Public
00009     License as published by the Free Software Foundation; either
00010     version 2 of the License, or (at your option) any later version.
00011 
00012     This library is distributed in the hope that it will be useful,
00013     but WITHOUT ANY WARRANTY; without even the implied warranty of
00014     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015     Lesser General Public License for more details.
00016 
00017     You should have received a copy of the GNU Lesser General Public
00018     License along with this library; if not, write to the Free Software
00019     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
00020 
00021 */
00022 
00023 /** @file search.cc Code: a MetaObject for Search object types. */
00024 
00025 #include <vos/corelibs/vos/vos.hh>
00026 
00027 #include <vos/metaobjects/property/property.hh>
00028 #include "search.hh"
00029 
00030 #include <fstream>
00031 
00032 /* Constructor */
00033 Search::Search(MetaObject* s) : MetaObject(s)
00034 {
00035     accessControl = &NoPropertyAccessControl::static_;
00036 }
00037 
00038 /* Destructor */
00039 Search::~Search() {
00040 }
00041 
00042 /* Get/Set default AC */
00043 void Search::setPropertyAccessControl(PropertyAccessControl* ac) {
00044     accessControl = ac;
00045 }
00046 PropertyAccessControl* Search::getPropertyAccessControl() {
00047     return accessControl;
00048 }
00049 
00050 LocalSearch::LocalSearch(MetaObject* s) : Search(s), MetaObject(s)
00051 {
00052     addMessageHandler<LocalSearch>("misc:search", this, &LocalSearch::handleSearch);
00053 }
00054 
00055 LocalSearch::~LocalSearch() {
00056     //acccessControl = &NoPropertyAccessControl::static_;
00057 }
00058 
00059 /* Initialize required subobjects: */
00060 
00061 void LocalSearch::initialize(PropertyAccessControl* ac) {
00062     accessControl = ac;
00063     initialize();
00064 }
00065 
00066 void LocalSearch::initialize() {
00067 }
00068 
00069 RemoteSearch::RemoteSearch(MetaObject* s) : Search(s), MetaObject(s)
00070 {
00071     addUpdateHandler<RemoteSearch>("misc:search-done", this, &RemoteSearch::handleSearchDone);
00072 }
00073 
00074 RemoteSearch::~RemoteSearch() {
00075 }
00076 
00077 /* Return type string ("search:search") */
00078 
00079 const string Search::getType() {
00080     return string("misc:search");
00081 }
00082 
00083 /* Register extenders with libvos MetaFactory */
00084 void Search::registerExtenders() {
00085     static bool done = false;
00086     if(! done) {
00087     LocalSite::addLocalObjectExtension(typeid(LocalSearch).name(), &LocalSearch::new_LocalSearch);
00088     LocalSite::addLocalObjectExtension(typeid(Search).name(), &LocalSearch::new_LocalSearch);
00089     LocalSite::addLocalObjectExtension("misc:search", &LocalSearch::new_LocalSearch);
00090     RemoteSite::addRemoteObjectExtension(typeid(RemoteSearch).name(), &RemoteSearch::new_RemoteSearch);
00091     RemoteSite::addRemoteObjectExtension(typeid(Search).name(), &RemoteSearch::new_RemoteSearch);
00092     RemoteSite::addRemoteObjectExtension("misc:search", &RemoteSearch::new_RemoteSearch);
00093     done = true;
00094     }
00095 }
00096 
00097 
00098 /* Process remote update messages */
00099 void RemoteSearch::sendUpdateMessage(Message* m) {
00100 
00101     /* let superclass have message... */
00102     Search::sendUpdateMessage(m);
00103 
00104     bool mark = false;
00105     for(set<string>::iterator i = searches.begin(); i != searches.end(); i++) {
00106         if(m->getNonce() == (string("acquire-") + (*i))) {
00107             mark = true;
00108             break;
00109         }
00110     }
00111 
00112     if(mark) {
00113         try {
00114             if(m->getMethod() == "core:insert-child-update"
00115                || m->getMethod() == "core:set-child-update") {
00116                 try {
00117                     for(int i=0; i < m->getNumFields(); ++i) {
00118                         const Message::Field& name = m->getField(i);
00119                         if(name.key != "name") continue;
00120                         try {
00121                             vRef<Vobject> v = findObject(name.value);
00122                             v->addFlag("from-search");
00123                         } catch(NoSuchObjectError) { }
00124                     }
00125                 } catch(Message::NoSuchFieldError) { }
00126             }
00127         } catch(NoSuchObjectError) {
00128         } catch(NoSuchSiteError) {
00129         } catch(URL::BadURLError) {
00130         }
00131     }
00132 }
00133 
00134 /* Local Actuators: */
00135 
00136 /* Do search. */
00137 
00138 deque<LocalSearch::PatternRule*> LocalSearch::makeRules(Message* rules)
00139 {
00140     deque<PatternRule*> prules;
00141 
00142     int rc = 0, i = 0;
00143     for(; i < rules->getNumFields() && rules->getField(i).key != "typerule"; i++);
00144     for(; i < rules->getNumFields(); i+=3) {
00145         rc++;
00146 
00147         const Message::Field& typerule = rules->getField(i);
00148         const Message::Field& childrule = rules->getField(i+1);
00149         const Message::Field& action = rules->getField(i+2);
00150 
00151         if(typerule.key != "typerule") {
00152             LOG("search", 2, "missing type rule parmeter");
00153             return prules;
00154         }
00155         if(childrule.key != "childrule") {
00156             LOG("search", 2, "missing child rule parmeter");
00157             return prules;
00158         }
00159         if(action.key != "action") {
00160             LOG("search", 2, "missing action parmeter");
00161             return prules;
00162         }
00163 
00164         PatternRule* currule = new PatternRule;
00165 
00166     currule->typePatternEmpty = (typerule.value.size() == 0);   
00167         LOG("search", 4, "type pattern: " << typerule.value);
00168         regcomp(&currule->typepattern, typerule.value.c_str(), REG_EXTENDED | REG_NOSUB);
00169 
00170         if(! (childrule.value.size() == 0
00171               || childrule.value[0] == '/'
00172               || (childrule.value[0] == '!' && childrule.value[1] == '/')))
00173         {
00174             LOG("search", 2, "child rule doesn't start with '/' and isn't blank");
00175             return prules;
00176         }
00177 
00178         unsigned int n = 1, c = 1;
00179         if(childrule.value.size() > 0) {
00180             if(childrule.value[0] == '!') {
00181                 currule->negateChild = true;
00182                 n++;
00183                 c++;
00184             } else currule->negateChild = false;
00185 
00186             vector<regex_t> childpatsplit;
00187             for(; n < childrule.value.size(); n++) {
00188                 if(childrule.value[n] == '/') {
00189                     LOG("search", 4, "child pattern: " << childrule.value.substr(c, n-c));
00190 
00191                     regex_t aregexp;
00192                     regcomp(&aregexp, childrule.value.substr(c, n-c).c_str(), REG_EXTENDED | REG_NOSUB);
00193 
00194                     currule->childpattern.push_back(aregexp);
00195                     c = n+1;
00196                 }
00197             }
00198         }
00199         if(n == childrule.value.size()) {
00200             LOG("search", 4, "child pattern: " << childrule.value.substr(c, n-c));
00201 
00202             regex_t aregexp;
00203             regcomp(&aregexp, childrule.value.substr(c, n-c).c_str(), REG_EXTENDED | REG_NOSUB);
00204 
00205             currule->childpattern.push_back(aregexp);
00206         }
00207 
00208         for(unsigned int n = 0, c = 0; n < action.value.size(); n++) {
00209             while(n < action.value.size() && isspace(action.value[n])) { n++; c++; }
00210             while(n < action.value.size() && action.value[n] != ',') n++;
00211             LOG("search", 4, "action: '" << action.value.substr(c, n-c) << "'");
00212             currule->actions.push_back(action.value.substr(c, n-c));
00213             c = n+1;
00214         }
00215         prules.push_back(currule);
00216     }
00217     return prules;
00218 }
00219 
00220 
00221 bool LocalSearch::doActions(Vobject* currentobject,
00222                             const deque<PatternRule*>& rules,
00223                             const PatternRule& thisrule,
00224                             RemoteCOD* cod,
00225                             list<Vobject*>& touchedObjects)
00226 {
00227     if(currentobject->isRemote()) return false;
00228     if(cod->getVobject(currentobject->getName())) return true;
00229 
00230     LOG("searched", 4, "doActions looking at " << currentobject->getURL().getString());
00231 
00232     vRef<LocalSite> ls = cod->getLocalSite();
00233     vRef<RemoteSite> rs = cod->getRemoteSite();
00234 
00235     char nonce[32];
00236     snprintf(nonce, sizeof(nonce), "%i", (int)rand());
00237     CatchUpdatesFilter cu(nonce);
00238     bool wantIt = false;
00239     bool dorecurse = false;
00240     bool ret = true;
00241 
00242     for(vector<string>::const_iterator act = thisrule.actions.begin();
00243         act != thisrule.actions.end(); act++)
00244     {
00245         if((*act) == "recurse") {
00246             dorecurse = true;
00247         } else if((*act) == "acquire") {
00248             wantIt = true;
00249         } else if((*act) == "stop") {
00250             ret = false;
00251             break;
00252         } else {
00253             try {
00254                 vRef<MessageBlock> mb = rs->getMessageBlock(*act);
00255                 /*vRef<MessageContext> mc = new MessageContext();
00256 
00257                 mc->setParameter("to", currentobject->getURL().getString());
00258                 mc->setParameter("from", rs->getURL().getString());
00259                 mc->setParameter("nonce", nonce);*/
00260 
00261                 rs->insertMessageFilter(-1, &cu);
00262 
00263                 for(int i = 0; i < mb->numMessages(); i++) {
00264                     try {
00265                         vRef<Message> msg = new Message(*mb->getMessage(i));
00266                         //msg->setMessageContext(&mc);
00267                         msg->setSourceSite(&rs);
00268 
00269                         //if(msg->getTo() == "")
00270                         msg->setTo(currentobject->getURL().getString());
00271                         //if(msg->getFrom() == "")
00272                         msg->setFrom(rs->getURL().getString());
00273                         msg->setNonce(nonce);
00274 
00275                         LOG("search", 4, "to " << currentobject->getURL().getString()
00276                             << " sending " << msg->getFormattedString());
00277 
00278                         currentobject->sendMessage(&msg);
00279                     } catch(runtime_error e) {
00280                         LOG("remotesite", 2, "Error applying rule: " << e.what());
00281                     } catch(...) {
00282                     }
00283                 }
00284 
00285                 rs->removeMessageFilter(&cu);
00286             } catch(Site::NoSuchMessageBlockError) {
00287                 LOG("search", 2, "No such message block defined \"" << *act << "\"");
00288             }
00289         }
00290     }
00291 
00292     if(wantIt) {
00293         LOG("search", 4, "Adding " << currentobject->getURL().getString() << " to the COD");
00294         cod->addVobject(currentobject, currentobject->getTypes(), cu.mb, cu.savechildren);
00295     } else {
00296         rs->sendMessage(cu.mb);
00297     }
00298 
00299     if(dorecurse) search(currentobject, rules, cod, touchedObjects);
00300 
00301     return ret;
00302 }
00303 
00304 bool LocalSearch::actOnChildren(Vobject* currentobject,
00305                                 const deque<PatternRule*>& rules,
00306                                 const PatternRule& thisrule,
00307                                 int patternpart,
00308                                 RemoteCOD* cod,
00309                                 list<Vobject*>& touchedObjects)
00310 {
00311     bool ret = true;
00312     const Vobject::ChildList& cl = currentobject->getChildren();
00313 
00314     LOG("searched", 4, "actOnChildren looking at " << currentobject->getURL().getString());
00315 
00316     for(Vobject::ChildList::const_iterator i = cl.begin(); i != cl.end(); i++) {
00317         if((*i)->child->isLocal()) {
00318             LOG("search", 4, "looking at " << (*i)->contextual_name);
00319             int e = regexec(&thisrule.childpattern[patternpart], (*i)->contextual_name.c_str(), 0, 0, 0);
00320             if(thisrule.negateChild) e = ((e == 0) ? REG_NOMATCH : 0);
00321             calledregex++;
00322             if(e == 0) {
00323                 LOG("search", 4, "matched");
00324                 if(thisrule.childpattern.size()-1 == (unsigned int)patternpart) {
00325                     ret &= doActions((*i)->child, rules, thisrule, cod, touchedObjects);
00326                 } else actOnChildren((*i)->child, rules, thisrule, patternpart+1, cod, touchedObjects);
00327             } else {
00328                 if(e != REG_NOMATCH) {
00329                     char errbuf[256];
00330                     regerror(e, &thisrule.childpattern[patternpart], errbuf,  sizeof(errbuf));
00331                     LOG("search", 2, "oops bad regex " << errbuf);
00332                     return false;
00333                 }
00334             }
00335         }
00336     }
00337     return ret;
00338 }
00339 
00340 void LocalSearch::search(Vobject* currentobject,
00341                          const deque<PatternRule*>& rules,
00342                          RemoteCOD* cod,
00343                          list<Vobject*>& touchedObjects)
00344 {
00345     if(currentobject->isRemote()) return;
00346 
00347     if(!currentobject->checkFlag("searched")) {
00348         LOG("searched", 4, "search looking at " << currentobject->getURL().getString());
00349 
00350         currentobject->addFlag("searched");
00351         currentobject->acquire(); // released by: handleSearch
00352         touchedObjects.push_back(currentobject);
00353 
00354         for(deque<PatternRule*>::const_iterator currule = rules.begin(); currule != rules.end(); currule++) {
00355             bool doRule = false;
00356             const Vobject::TypeSet& ts = currentobject->getTypes();
00357             if((*currule)->typePatternEmpty && ts.size() == 0) doRule = true;
00358             for(Vobject::TypeSet::const_iterator tsi = ts.begin(); tsi != ts.end(); tsi++) {
00359                 int e = regexec(&(*currule)->typepattern, (*tsi).c_str(), 0, 0, 0);
00360                 calledregex++;
00361                 if(e == 0) {
00362                     LOG("search", 4, "matched " << (*tsi));
00363                     doRule = true;
00364                     break;
00365                 } else {
00366                     if(e != REG_NOMATCH) {
00367                         char errbuf[256];
00368                         regerror(e, &(*currule)->typepattern, errbuf,  sizeof(errbuf));
00369                         LOG("search", 2, "oops bad regex " << errbuf);
00370                         return;
00371                     }
00372                 }
00373             }
00374             if(doRule) {
00375                 if((*currule)->childpattern.size() > 0) {
00376                     if(! actOnChildren(currentobject, rules, (**currule), 0, cod, touchedObjects)) return;
00377                 } else {
00378                     if(! doActions(currentobject, rules, (**currule), cod, touchedObjects)) return;
00379                 }
00380             }
00381         }
00382     }
00383 }
00384 
00385 
00386 /* Message Handlers:  */
00387 
00388 void LocalSearch::handleSearch(Message* m) {
00389     deque<PatternRule*> rules = makeRules(m);
00390 
00391     for(deque<PatternRule*>::iterator i = rules.begin(); i != rules.end(); i++) {
00392         for(vector<string>::iterator n = (*i)->actions.begin();  n != (*i)->actions.end(); n++)
00393         {
00394             LOG("search", 4, "ok: " << *n);
00395         }
00396         LOG("search", 4, "---");
00397     }
00398 
00399 
00400     vRef<LocalSite> mysite = dynamic_cast<LocalSite&>(getSite());
00401 
00402     vRef<Message> reply;
00403     try {
00404         reply = new Message();
00405         LocalVobject::initReply(this, &reply, m, "misc:search-done");
00406     } catch(Message::NoSuchFieldError e) {
00407             /* A field wasn't found */
00408     }
00409 
00410     double selectwait = mysite->getTimeoutOnSelect();
00411     mysite->setTimeoutOnSelect(0);
00412 
00413     vRef<RemoteSite> sourcesite = dynamic_cast<RemoteSite*>(m->getSourceSite());
00414 
00415 #ifdef USE_STRSTREAM
00416     strstream of;
00417 #else
00418     stringstream of;
00419 #endif
00420     RemoteCOD rc(of, *mysite, *sourcesite);
00421 
00422     LOG("search", 3, "starting search...");
00423 
00424     double d = getRealTime();
00425     calledregex = 0;
00426     for(int i = 0; i < m->getNumFields(); i++){
00427         const Message::Field& f = m->getField(i);
00428         if(f.key == "start") {
00429             try {
00430                 list<Vobject*> touchedObjects;
00431                 vRef<Vobject> v = findObject(f.value);
00432                 search(&v, rules, &rc, touchedObjects);
00433 
00434                 for(list<Vobject*>::iterator li = touchedObjects.begin(); li != touchedObjects.end(); li++) {
00435                     (*li)->removeFlag("searched");
00436                     (*li)->release();
00437                 }
00438             } catch(NoSuchObjectError) {
00439             } catch(runtime_error x) {
00440                 LOG("search", 1, "Got error doing search " << x.what());
00441                 // send error reply
00442             }
00443         }
00444     }
00445     LOG("search", 3, "search done, took " << getRealTime() - d << " sec with " << calledregex << " regexes");
00446 
00447     d = getRealTime();
00448     LOG("search", 3, "starting COD  write");
00449 
00450     unsigned char* data;
00451     unsigned int size;
00452     rc.writeCOD(&data, &size);
00453 
00454     LOG("search", 3, "did write COD, took " << getRealTime() - d << " sec");
00455 
00456     for(unsigned int i = 0; i < rules.size(); i++) {
00457         delete rules[i];
00458     }
00459     rules.resize(0);
00460 
00461     mysite->flushIncomingBuffers();
00462     mysite->setTimeoutOnSelect(selectwait);
00463 
00464     try {
00465         vRef<Vobject> from = Vobject::findObjectFromRoot(m->getFrom());
00466         reply->insertField(-1, "cod", string((char*)data, size), true);
00467         from->sendMessage(&reply);
00468     } catch(...) {
00469     }
00470 }
00471 
00472 /* Remote Actuators: */
00473 
00474 /* Do search. */
00475 string RemoteSearch::doSearch(const deque<string>& objects, const deque<string>& rules, SearchListener* listener) {
00476     string n = doSearch(objects, rules, true);
00477     RefCounted* r = dynamic_cast<RefCounted*>(listener);
00478     if(r) r->acquire();
00479     listeners[n] = listener;
00480     return n;
00481 }
00482 
00483 string RemoteSearch::doSearch(const deque<string>& objects, const deque<string>& rules, bool async, RemoteCOD** cod) {
00484     /* Create a new message and send it to the local object, and wait for reply. */
00485 
00486     vRef<Message> m = new Message();
00487     vRef<LocalSite> ls = RemoteVobject::initFields(this, &m, "misc:search", true);
00488 
00489     for(deque<string>::const_iterator i = objects.begin(); i != objects.end(); i++) {
00490         m->insertField(-1, "start", *i);
00491     }
00492     for(deque<string>::const_iterator i = rules.begin(); i != rules.end(); i++) {
00493         m->insertField(-1, "typerule", *i);
00494         i++;
00495         m->insertField(-1, "childrule", *i);
00496         i++;
00497         m->insertField(-1, "action", *i);
00498     }
00499 
00500     searches.insert(m->getNonce());
00501 
00502     sendMessage(&m);
00503     vRef<RemoteSite> site = dynamic_cast<RemoteSite&>(getSite());
00504     if(! async) {
00505         vRef<Message> reply = ls->waitFor(m->getNonce(), &site, 3600);
00506             try {
00507                 LOG("search", 4,"starting to unpack COD");
00508                 double d = getRealTime();
00509 
00510                 const Message::Field& f = reply->getField("cod");
00511 #if 0
00512 #ifdef USE_STRSTREAM
00513                 strstream input((char*)(f.value.c_str()), f.value.length());
00514                 // cast from (const char*) should be OK, since we are
00515                 // only going to read from this string, not write to it.
00516 #else
00517                 stringstream input(f.value, ios::in | ios::bin);
00518 #endif
00519 #endif
00520                 /* for testing only
00521                    fstream outtest("out.cod", ios::out | ios::bin);
00522                    outtest.write(f.value.c_str(), f.value.size());
00523                    outtest.close();
00524                 */
00525                 vRef<RemoteSite> rs = dynamic_cast<RemoteSite*>(reply->getSourceSite());
00526                 vRef<LocalSite> ls = rs->getLocalPeer();
00527 
00528                 RemoteCOD* rc = new RemoteCOD(*ls, *rs);
00529                 LOG("search", 4, "x4");
00530                 rc->readCOD((unsigned char*)(f.value.c_str()), f.value.length());
00531                 LOG("search", 4, "x5");
00532 
00533                 if(cod) {
00534                     *cod = rc;
00535                 } else delete rc;
00536                 LOG("search", 3, "unpacking done, took " << getRealTime() - d << " sec");
00537             } catch(Message::NoSuchFieldError) {
00538                 LOG("search", 2, "Received search-done message but could not find the COD field");
00539             } catch(COD::eof) {
00540                 LOG("search", 2, "COD ended prematurely");
00541             }
00542     }
00543     return m->getNonce();
00544 }
00545 
00546 
00547 /* Generators for factory */
00548 
00549 MetaObject* LocalSearch::new_LocalSearch(MetaObject *s, const string& type) {
00550     LocalSearch* o = new LocalSearch(s);
00551     return o;
00552 }
00553 
00554 MetaObject* RemoteSearch::new_RemoteSearch(MetaObject *s, const string& type) {
00555     return new RemoteSearch(s);
00556 }
00557 
00558 /* Handle search-done. */
00559 
00560 void RemoteSearch::handleSearchDone(Message* m) {
00561     string n = m->getNonce();
00562     if(listeners.find(n) != listeners.end()) {
00563         try {
00564             const Message::Field& f = m->getField("cod");
00565 #if 0
00566 #ifdef USE_STRSTREAM
00567             strstream input((char*) f.value.c_str(), f.value.length());
00568             // cast from (const char*) should be OK, since we are
00569             // only going to read from this string, not write to it.
00570 #else
00571             stringstream input(f.value);
00572 #endif
00573 #endif
00574             vRef<RemoteSite> rs = dynamic_cast<RemoteSite*>(m->getSourceSite());
00575             vRef<LocalSite> ls = rs->getLocalPeer();
00576 
00577             RemoteCOD cod(*ls, *rs);
00578             cod.readCOD((unsigned char*)(f.value.c_str()), f.value.length());
00579         } catch(Message::NoSuchFieldError) {
00580             LOG("search", 2, "Received search-done message but could not find the COD field");
00581         }
00582 
00583         listeners[n]->notifySearchDone();
00584         RefCounted* r = dynamic_cast<RefCounted*>(listeners[n]);
00585         if(r) r->release();
00586         listeners.erase(n);
00587     }
00588 }
00589 

Generated on Tue Aug 12 03:55:45 2003 for Interreality Project - VOS by doxygen 1.3.2