You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
arts/mcop/trader_impl.cpp

346 lines
7.8 KiB

/*
Copyright (C) 2000-2002 Stefan Westerfeld
stefan@space.twc.de
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "common.h"
#include "debug.h"
#include <iostream>
#include <fstream>
#include <algorithm>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include <cstring>
using namespace Arts;
using namespace std;
namespace Arts {
// TraderRestriction
struct TraderRestriction {
TraderRestriction(const string& key, const string& value)
:key(key), value(value) { }
string key;
string value;
};
// TraderHelper (internal, no public interface)
class TraderHelper {
private:
vector<class TraderOffer_impl *> allOffers;
static TraderHelper *_instance;
protected:
TraderHelper();
~TraderHelper();
void addDirectory(const string& directory, const string& iface = "",
map< pair<dev_t,ino_t>, bool > *dirsDone = 0);
public:
vector<TraderOffer> *doQuery(const vector<TraderRestriction>& query);
void load();
void unload();
static TraderHelper *the();
static void shutdown();
};
// TraderQuery
class TraderQuery_impl : virtual public TraderQuery_skel {
private:
vector<TraderRestriction> restrictions;
public:
void supports(const string& property, const string& value);
vector<TraderOffer> *query();
};
REGISTER_IMPLEMENTATION(TraderQuery_impl);
// TraderOffer
class TraderOffer_impl : virtual public TraderOffer_skel {
private:
string _interfaceName;
map<string, vector<string> > property;
public:
TraderOffer_impl(const string& interfaceName, const string& filename);
bool match(const vector<TraderRestriction>& restrictions);
// IDL
string interfaceName();
vector<string>* getProperty(const string& name);
};
}
// TraderQuery
//----------------------------------------------------------------------------
void TraderQuery_impl::supports(const string& property, const string& value)
{
restrictions.push_back(TraderRestriction(property,value));
}
vector<TraderOffer> *TraderQuery_impl::query()
{
return TraderHelper::the()->doQuery(restrictions);
}
// TraderOffer
//----------------------------------------------------------------------------
TraderOffer_impl::TraderOffer_impl(const string& interfaceName,
const string& filename)
:_interfaceName(interfaceName)
{
ifstream file(filename.c_str());
string line;
while(getline(file,line))
{
string key;
vector<string> values;
if(MCOPUtils::tokenize(line,key,values))
property[key] = values;
}
vector<string>& iname = property["InterfaceName"];
if(iname.empty())
iname.push_back(interfaceName);
}
bool TraderOffer_impl::match(const vector<TraderRestriction>& restrictions)
{
vector<TraderRestriction>::const_iterator i;
for(i = restrictions.begin(); i != restrictions.end();i++)
{
const TraderRestriction& res = *i;
const vector<string>& myvalues = property[res.key];
bool okay = false;
vector<string>::const_iterator offerIt = myvalues.begin();
while(!okay && offerIt != myvalues.end())
{
if(res.value == *offerIt)
okay = true;
else
offerIt++;
}
if(!okay) return false;
}
return true;
}
string TraderOffer_impl::interfaceName()
{
return _interfaceName;
}
vector<string>* TraderOffer_impl::getProperty(const string& name)
{
return new vector<string>(property[name]);
}
// TraderHelper
//----------------------------------------------------------------------------
TraderHelper::TraderHelper()
{
load();
}
TraderHelper::~TraderHelper()
{
unload();
}
void TraderHelper::load()
{
const vector<string> *path = MCOPUtils::traderPath();
vector<string>::const_iterator pi;
for(pi = path->begin(); pi != path->end(); pi++) addDirectory(*pi);
}
void TraderHelper::unload()
{
vector<TraderOffer_impl *>::iterator i;
for(i = allOffers.begin(); i != allOffers.end(); i++)
{
TraderOffer_impl *offer = *i;
offer->_release();
}
allOffers.clear();
}
void TraderHelper::addDirectory(const string& directory, const string& iface,
map< pair<dev_t,ino_t>, bool > *dirsDone)
{
bool created_dirs_done = false;
arts_debug("addDirectory(%s,%s)", directory.c_str(), iface.c_str());
if(!dirsDone)
{
/* map to prevent doing directories again due to symlinks */
dirsDone = new map< pair<dev_t,ino_t>, bool >;
struct stat st;
stat(directory.c_str(), &st);
(*dirsDone)[make_pair(st.st_dev, st.st_ino)] = true;
created_dirs_done = true;
}
DIR *dir = opendir(directory.c_str());
if(!dir) return;
struct dirent *de;
while((de = readdir(dir)) != 0)
{
string currentEntry = directory + "/" + de->d_name;
string currentIface = iface;
if(!iface.empty()) currentIface += "::";
currentIface += de->d_name;
struct stat st;
stat(currentEntry.c_str(),&st);
// recurse into subdirectories
if(S_ISDIR(st.st_mode))
{
bool& done = (*dirsDone)[make_pair(st.st_dev, st.st_ino)];
if(strcmp(de->d_name,".") && strcmp(de->d_name,"..") && !done)
{
done = true;
addDirectory(currentEntry,currentIface,dirsDone);
}
}
else if(S_ISREG(st.st_mode))
{
if(strlen(de->d_name) > 10 &&
strncmp(&de->d_name[strlen(de->d_name)-10],".mcopclass",10) == 0)
{
// cut .mcopclass for currentIface;
currentIface = currentIface.substr(0, currentIface.size()-10);
allOffers.push_back(
new TraderOffer_impl(currentIface,currentEntry) );
}
}
}
if (created_dirs_done)
delete dirsDone;
closedir(dir);
}
static inline int getOfferPreference(TraderOffer offer)
{
int result = 0;
vector<string> *p = offer.getProperty("Preference");
if(!p->empty())
result = atoi(p->front().c_str());
delete p;
return result;
}
static bool traderOfferCmp(TraderOffer offer1, TraderOffer offer2)
{
int p1 = getOfferPreference(offer1);
int p2 = getOfferPreference(offer2);
if(p1 > p2)
return true;
if(p1 < p2)
return false;
/* if the Preference= entry doesn't indicate which offer is better,
* sort the offers by name to make the trader results at least
* independant from the order they are read from the file system */
return offer1.interfaceName() < offer2.interfaceName();
}
vector<TraderOffer> *TraderHelper::doQuery(const
vector<TraderRestriction>& query)
{
vector<TraderOffer> *result = new vector<TraderOffer>;
vector<TraderOffer_impl *>::iterator i;
for(i = allOffers.begin(); i != allOffers.end(); i++)
{
TraderOffer_impl *offer = *i;
if(offer->match(query))
{
result->push_back(TraderOffer::_from_base(offer->_copy()));
}
}
/* sort trader query result by Preference= line for more than one result */
if(result->size() > 1)
sort(result->begin(), result->end(), traderOfferCmp);
return result;
}
TraderHelper *TraderHelper::_instance = 0;
TraderHelper *TraderHelper::the()
{
if(!_instance) _instance = new TraderHelper();
return _instance;
}
void TraderHelper::shutdown()
{
if(_instance)
{
delete _instance;
_instance = 0;
}
}
namespace Arts {
// trader shutdown
class TraderShutdown :public StartupClass
{
public:
void startup() { };
void shutdown() { TraderHelper::shutdown(); };
};
static TraderShutdown traderShutdown;
}
// Dispatcher function for reloading the trader data:
void Dispatcher::reloadTraderData()
{
TraderHelper::the()->unload();
TraderHelper::the()->load();
}