initial commit

This commit is contained in:
Blue 2018-08-05 00:46:25 +03:00 committed by Юрий Губич
commit 4b60ece582
327 changed files with 28286 additions and 0 deletions

34
CMakeLists.txt Normal file
View File

@ -0,0 +1,34 @@
cmake_minimum_required(VERSION 2.8.12)
project(RadioW)
function(add_jslib file name path arch)
execute_process(COMMAND
node polymorph
${LIBJS_DIR}/${file}
${path}/lib/${file}
${name}
${arch}
${path}/lib
WORKING_DIRECTORY ${LIBJS_DIR})
endfunction(add_jslib)
include(CheckCXXCompilerFlag)
set(CMAKE_CXX_STANDARD 11)
include_directories(lib)
set(LIBJS_DIR ${CMAKE_SOURCE_DIR}/libjs)
set(ROBOUTE_DIR ${CMAKE_BINARY_DIR}/roboute)
set(CORAX_DIR ${CMAKE_BINARY_DIR}/corax)
set(MAGNUS_DIR ${CMAKE_BINARY_DIR}/magnus)
set(LORGAR_DIR ${MAGNUS_DIR}/public)
set(PERTURABO_DIR ${CMAKE_BINARY_DIR}/perturabo)
add_subdirectory(lib)
add_subdirectory(corax ${CORAX_DIR})
add_subdirectory(magnus ${MAGNUS_DIR})
add_subdirectory(lorgar ${LORGAR_DIR})
add_subdirectory(roboute ${ROBOUTE_DIR})
add_subdirectory(perturabo ${PERTURABO_DIR})
add_subdirectory(test)

34
README.md Normal file
View File

@ -0,0 +1,34 @@
# RadioW
## Dependencies
1. cmake >= 2.8.12
2. qt 5.*
1. qt(5)-base
2. qt(5)-websockets
3. nodejs
4. npm
5. libssh
6. lmdb
7. taglib
## Building
Attention! During the first build internet connection is mandatory. There are some nodejs dependencies, which npm is going to unstall during configuration.
1. Create a build directory and checkout there. For example if you are in project directory, and want to build in subdirectory run
```bash
mkdir build
cd build
```
2. Run cmake to configure the project, giving the path to project root directory. For example
```bash
cmake ../
```
3. Run make to build the project. For example
```bash
make
```

40
corax/CMakeLists.txt Normal file
View File

@ -0,0 +1,40 @@
cmake_minimum_required(VERSION 2.8.12)
project(corax)
find_package(Qt5Core REQUIRED)
find_package(Qt5Network REQUIRED)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOMOC ON)
set(HEADERS
corax.h
tools/parser.h
tools/audioid.h
tools/audiotag.h
)
set(SOURCES
main.cpp
corax.cpp
tools/parser.cpp
tools/audioid.cpp
tools/audiotag.cpp
)
add_executable(corax ${HEADERS} ${SOURCES})
target_link_libraries(corax Qt5::Core)
target_link_libraries(corax Qt5::Network)
target_link_libraries(corax wSocket)
target_link_libraries(corax wDispatcher)
target_link_libraries(corax utils)
target_link_libraries(corax wModel)
target_link_libraries(corax wController)
target_link_libraries(corax wServerUtils)
target_link_libraries(corax wDatabase)
target_link_libraries(corax tag)
target_link_libraries(corax tools)
install(TARGETS corax RUNTIME DESTINATION bin)

236
corax/corax.cpp Normal file
View File

@ -0,0 +1,236 @@
#include "corax.h"
#include <iostream>
using std::cout;
using std::endl;
Corax* Corax::corax = 0;
Corax::Corax(QObject *parent):
QObject(parent),
server(new W::Server(W::String(u"Corax"), this)),
logger(new W::Logger()),
parentReporter(new W::ParentReporter()),
attributes(new M::Attributes(W::Address({u"attributes"}))),
commands(new U::Commands(W::Address{u"management"})),
connector(0),
dispatcher(new W::Dispatcher()),
caches(),
parsers()
{
if (corax != 0)
{
throw SingletonError();
}
Corax::corax = this;
connector = new U::Connector(dispatcher, server, commands);
connector->addIgnoredNode(W::String(u"Lorgar"));
connector->addIgnoredNode(W::String(u"Roboute"));
connect(attributes, SIGNAL(serviceMessage(const QString&)), SLOT(onModelServiceMessage(const QString&)));
connect(commands, SIGNAL(serviceMessage(const QString&)), SLOT(onModelServiceMessage(const QString&)));
connect(connector, SIGNAL(serviceMessage(const QString&)), SLOT(onModelServiceMessage(const QString&)));
connect(connector, SIGNAL(nodeConnected(const W::String&)), SLOT(onNodeConnected(const W::String&)));
connect(connector, SIGNAL(nodeDisconnected(const W::String&)), SLOT(onNodeDisconnected(const W::String&)));
connect(server, SIGNAL(connectionCountChange(uint64_t)), SLOT(onConnectionCountChanged(uint64_t)));
dispatcher->registerDefaultHandler(parentReporter);
dispatcher->registerDefaultHandler(logger);
attributes->addAttribute(W::String(u"connectionsCount"), new M::String(W::String(u"0"), W::Address({u"attributes", u"connectionCount"})));
attributes->addAttribute(W::String(u"name"), new M::String(W::String(u"Corax"), W::Address({u"attributes", u"name"})));
attributes->addAttribute(W::String(u"version"), new M::String(W::String(u"0.0.2"), W::Address({u"attributes", u"version"})));
createCaches();
createHandlers();
}
Corax::~Corax()
{
std::map<W::String, Parser*>::iterator pbeg = parsers.begin();
std::map<W::String, Parser*>::iterator pend = parsers.end();
for (; pbeg != pend; ++pbeg) {
delete pbeg->second;
}
std::map<W::String, ResourceCache*>::iterator beg = caches.begin();
std::map<W::String, ResourceCache*>::iterator end = caches.end();
for (; beg != end; ++beg) {
delete beg->second;
}
delete connector;
dispatcher->unregisterDefaultHandler(logger);
delete commands;
delete attributes;
delete logger;
delete dispatcher;
Corax::corax = 0;
}
void Corax::onConnectionCountChanged(uint64_t count)
{
attributes->setAttribute(W::String(u"connectionsCount"), new W::String(std::to_string(count)));
}
void Corax::start()
{
std::map<W::String, ResourceCache*>::iterator beg = caches.begin();
std::map<W::String, ResourceCache*>::iterator end = caches.end();
cout << "Starting corax..." << endl;
server->listen(8080);
cout << "Registering models..." << endl;
attributes->registerModel(dispatcher, server);
commands->registerModel(dispatcher, server);
for (; beg != end; ++beg) {
beg->second->registerModel(dispatcher, server);
}
cout << "Opening caches..." << endl;
beg = caches.begin();
for (; beg != end; ++beg) {
beg->second->open();
}
commands->enableCommand(W::String(u"clearCache"), true);
cout << "Corax is ready" << endl;
}
void Corax::stop()
{
std::map<W::String, ResourceCache*>::iterator beg = caches.begin();
std::map<W::String, ResourceCache*>::iterator end = caches.end();
cout << "Stopping corax..." << endl;
commands->unregisterModel();
attributes->unregisterModel();
for (; beg != end; ++beg) {
beg->second->unregisterModel();
}
server->stop();
}
void Corax::onModelServiceMessage(const QString& msg)
{
cout << msg.toStdString() << endl;
}
void Corax::addCache(ResourceCache* cache)
{
attributes->addAttribute(cache->name, new M::String(W::String(u"0"), W::Address({u"attributes", cache->name})));
connect(cache, SIGNAL(serviceMessage(const QString&)), SLOT(onModelServiceMessage(const QString&)));
connect(cache, SIGNAL(countChange(uint64_t)), SLOT(onCacheCountChange(uint64_t)));
parentReporter->registerParent(cache->getAddress(), cache->subscribeMember);
caches.insert(std::make_pair(cache->name, cache));
}
void Corax::h_clearCache(const W::Event& ev)
{
const W::Vocabulary& vc = static_cast<const W::Vocabulary&>(ev.getData());
const W::String& name = static_cast<const W::String&>(vc.at(u"name"));
cout << "received command to clear cache " << name.toString() << endl;
std::map<W::String, ResourceCache*>::iterator itr = caches.find(name);
if (itr == caches.end()) {
cout << "cache " << name.toString() << " doesn't exist" << endl;
} else {
itr->second->clear();
}
}
void Corax::h_parseDirectory(const W::Event& ev)
{
const W::Vocabulary& vc = static_cast<const W::Vocabulary&>(ev.getData());
const W::String& path = static_cast<const W::String&>(vc.at(u"path"));
cout << "received command to parse directory " << path.toString() << endl;
std::map<W::String, Parser*>::const_iterator itr = parsers.find(path);
if (itr != parsers.end()) {
cout << "directory " << path.toString() << " is already being parsed" << endl;
} else {
const W::Socket& socket = connector->getNodeSocket(W::String(u"Perturabo"));
ResourceCache* music = caches.at(W::String(u"music"));
ResourceCache* images = caches.at(W::String(u"images"));
Parser* parser = new Parser(&socket, dispatcher, music, images);
parsers.insert(std::make_pair(path, parser));
connect(parser, SIGNAL(serviceMessage(const QString&)), SLOT(onModelServiceMessage(const QString&)));
connect(parser, SIGNAL(done(const W::String&)), SLOT(onParserDone(const W::String&)));
parser->parse(path);
}
}
void Corax::createCaches()
{
ResourceCache* music = new ResourceCache(W::String(u"music"));
ResourceCache* images = new ResourceCache(W::String(u"images"));
addCache(music);
addCache(images);
}
void Corax::createHandlers()
{
W::Handler* clearCache = W::Handler::create(W::Address({u"management", u"clearCache"}), this, &Corax::_h_clearCache);
W::Vocabulary clearArgs;
clearArgs.insert(u"name", W::Uint64(W::Object::string));
commands->addCommand(W::String(u"clearCache"), clearCache, clearArgs);
W::Handler* parseDirectory = W::Handler::create(W::Address({u"management", u"parseDirectory"}), this, &Corax::_h_parseDirectory);
W::Vocabulary parseArgs;
parseArgs.insert(u"path", W::Uint64(W::Object::string));
commands->addCommand(W::String(u"parseDirectory"), parseDirectory, parseArgs);
}
void Corax::onParserDone(const W::String& path)
{
std::map<W::String, Parser*>::const_iterator itr = parsers.find(path);
delete itr->second;
parsers.erase(itr);
}
void Corax::onCacheCountChange(uint64_t count)
{
ResourceCache* cache = static_cast<ResourceCache*>(sender());
attributes->setAttribute(cache->name, W::String(std::to_string(count)));
}
void Corax::onNodeConnected(const W::String& name)
{
cout << "connected node " << name.toString() << endl;
if (name == u"Perturabo") {
commands->enableCommand(W::String(u"parseDirectory"), true);
}
}
void Corax::onNodeDisconnected(const W::String& name)
{
cout << "disconnected node " << name.toString() << endl;
if (name == u"Perturabo") {
commands->enableCommand(W::String(u"parseDirectory"), false);
}
}

88
corax/corax.h Normal file
View File

@ -0,0 +1,88 @@
#ifndef CORAX_H
#define CORAX_H
#include <map>
#include <QtCore/QObject>
#include <QtCore/QString>
#include <wSocket/socket.h>
#include <wSocket/server.h>
#include <wType/string.h>
#include <wType/uint64.h>
#include <wType/event.h>
#include <wType/address.h>
#include <wType/vocabulary.h>
#include <wDispatcher/dispatcher.h>
#include <wDispatcher/logger.h>
#include <wDispatcher/parentreporter.h>
#include <wModel/modelstring.h>
#include <wModel/attributes.h>
#include <utils/exception.h>
#include <wServerUtils/commands.h>
#include <wServerUtils/connector.h>
#include <wDatabase/resourcecache.h>
#include "tools/parser.h"
class Corax: public QObject
{
Q_OBJECT
public:
Corax(QObject *parent = 0);
~Corax();
static Corax* corax;
private:
W::Server *server;
W::Logger *logger;
W::ParentReporter* parentReporter;
M::Attributes* attributes;
U::Commands* commands;
U::Connector* connector;
W::Dispatcher *dispatcher;
std::map<W::String, ResourceCache*> caches;
std::map<W::String, Parser*> parsers;
handler(clearCache);
handler(parseDirectory);
public slots:
void start();
void stop();
private slots:
void onModelServiceMessage(const QString& msg);
void onConnectionCountChanged(uint64_t count);
void onParserDone(const W::String& path);
void onCacheCountChange(uint64_t count);
void onNodeConnected(const W::String& name);
void onNodeDisconnected(const W::String& name);
private:
void addCache(ResourceCache* cache);
void createCaches();
void createHandlers();
private:
class SingletonError:
public Utils::Exception
{
public:
SingletonError():Exception(){}
std::string getMessage() const{return "Corax is a singleton, there was an attempt to construct it at the second time";}
};
};
#endif // CORAX_H

18
corax/main.cpp Normal file
View File

@ -0,0 +1,18 @@
#include <QtCore/QCoreApplication>
#include <QtCore/QTimer>
#include <utils/signalcatcher.h>
#include "corax.h"
int main(int argc, char **argv) {
QCoreApplication app(argc, argv);
W::SignalCatcher sc(&app);
Corax* corax = new Corax(&app);
QTimer::singleShot(0, corax, SLOT(start()));
QObject::connect(&app, SIGNAL(aboutToQuit()), corax, SLOT(stop()));
return app.exec();
}

61
corax/tools/audioid.cpp Normal file
View File

@ -0,0 +1,61 @@
#include "audioid.h"
AudioId::AudioId(const W::String& p_artist, const W::String& p_album, const W::String& p_name):
artist(p_artist),
album(p_album),
name(p_name)
{
}
AudioId::AudioId(const AudioId& other):
artist(other.artist),
album(other.album),
name(other.name)
{
}
bool AudioId::operator==(const AudioId& other) const
{
return name == other.name && album == other.album && artist == other.artist;
}
bool AudioId::operator!=(const AudioId& other) const
{
return operator==(other);
}
bool AudioId::operator>(const AudioId& other) const
{
if (name == other.name) {
if (album == other.album) {
return name > other.name;
} else {
return album > other.album;
}
} else {
return name > other.name;
}
}
bool AudioId::operator<(const AudioId& other) const
{
if (name == other.name) {
if (album == other.album) {
return name < other.name;
} else {
return album < other.album;
}
} else {
return name < other.name;
}
}
bool AudioId::operator>=(const AudioId& other) const
{
return !operator<(other);
}
bool AudioId::operator<=(const AudioId& other) const
{
return !operator>(other);
}

28
corax/tools/audioid.h Normal file
View File

@ -0,0 +1,28 @@
#ifndef AUDIOID_H
#define AUDIOID_H
/**
* @todo write docs
*/
#include <wType/string.h>
class AudioId
{
public:
AudioId(const W::String& p_artist, const W::String& p_album, const W::String& p_name);
AudioId(const AudioId& other);
bool operator==(const AudioId& other) const;
bool operator!=(const AudioId& other) const;
bool operator<(const AudioId& other) const;
bool operator>(const AudioId& other) const;
bool operator<=(const AudioId& other) const;
bool operator>=(const AudioId& other) const;
const W::String artist;
const W::String album;
const W::String name;
};
#endif // AUDIOID_H

35
corax/tools/audiotag.cpp Normal file
View File

@ -0,0 +1,35 @@
#include "audiotag.h"
AudioTag::AudioTag(const AudioTag& other):
fileRef(other.fileRef)
{
}
AudioTag::AudioTag(const T::File& file):
fileRef(file.getPath().toString().c_str())
{
}
AudioTag::~AudioTag()
{
}
W::String AudioTag::getTitle() const
{
return W::String(fileRef.tag()->title().to8Bit(true));
}
W::String AudioTag::getAlbum() const
{
return W::String(fileRef.tag()->album().to8Bit(true));
}
W::String AudioTag::getArtist() const
{
return W::String(fileRef.tag()->artist().to8Bit(true));
}
W::Uint64 AudioTag::getYear() const
{
return W::Uint64(fileRef.tag()->year());
}

29
corax/tools/audiotag.h Normal file
View File

@ -0,0 +1,29 @@
#ifndef AUDIOTAG_H
#define AUDIOTAG_H
#include <wType/string.h>
#include <wType/uint64.h>
#include <tools/file.h>
#include <taglib/fileref.h>
#include <taglib/tag.h>
class AudioTag
{
public:
AudioTag(const T::File& file);
AudioTag(const AudioTag& other);
~AudioTag();
W::String getTitle() const;
W::String getAlbum() const;
W::String getArtist() const;
W::Uint64 getYear() const;
private:
TagLib::FileRef fileRef;
};
#endif // AUDIOTAG_H

309
corax/tools/parser.cpp Normal file
View File

@ -0,0 +1,309 @@
#include "parser.h"
Parser::Parser(const W::Socket* p_socket, W::Dispatcher* p_dp, ResourceCache* p_audio, ResourceCache* p_images):
QObject(),
socket(p_socket),
dp(p_dp),
songs(W::Address({u"songs"})),
albums(W::Address({u"albums"})),
artists(W::Address({u"artists"})),
audio(p_audio),
images(p_images),
path(),
songsReady(false),
albumsReady(false),
artistsReady(false),
state(idle),
foundImages(),
foundAudios()
{
connect(&songs, SIGNAL(ready()), this, SLOT(onSongsReady()));
connect(&songs, SIGNAL(serviceMessage(const QString&)), this, SIGNAL(serviceMessage(const QString&)));
connect(&albums, SIGNAL(ready()), this, SLOT(onAlbumsReady()));
connect(&albums, SIGNAL(serviceMessage(const QString&)), this, SIGNAL(serviceMessage(const QString&)));
connect(&artists, SIGNAL(ready()), this, SLOT(onArtistsReady()));
connect(&artists, SIGNAL(serviceMessage(const QString&)), this, SIGNAL(serviceMessage(const QString&)));
songs.registerController(dp, socket);
albums.registerController(dp, socket);
artists.registerController(dp, socket);
}
Parser::~Parser()
{
}
void Parser::onSongsReady()
{
songsReady = true;
emit serviceMessage("Songs are ready");
checkState();
}
void Parser::onAlbumsReady()
{
albumsReady = true;
emit serviceMessage("Albums are ready");
checkState();
}
void Parser::onArtistsReady()
{
artistsReady = true;
emit serviceMessage("Artists are ready");
checkState();
}
void Parser::checkState()
{
switch (state) {
case idle:
break;
case waitingForCollections:
if (songsReady && albumsReady && artistsReady) {
state = parsingDirectory;
parseDirectory();
}
break;
case parsingDirectory:
parseDirectory();
break;
case updatingMusicDataBase:
if (songsReady && albumsReady && artistsReady) {
updateMusicDataBase();
}
break;
case updatingImageDataBase:
if (songsReady && albumsReady && artistsReady) {
updateImageDataBase();
}
break;
}
}
void Parser::parse(const W::String& p_path)
{
if (state != idle) {
emit serviceMessage("An attempt to make parsing while another isn't finished, quitting");
throw 15;
}
path = p_path;
if (!songs.isSubscribed()) {
songs.subscribe();
}
if (!albums.isSubscribed()) {
albums.subscribe();
}
if (!artists.isSubscribed()) {
artists.subscribe();
}
if (!songsReady || !albumsReady || !artistsReady) {
state = waitingForCollections;
} else {
state = parsingDirectory;
}
checkState();
}
void Parser::parseDirectory()
{
emit serviceMessage(QString("Starting to parse directory ") + path.toString().c_str());
std::list<T::File> *list = new std::list<T::File>();
bool success = T::File::readDirectoryRecursive(path, list);
if (success) {
emit serviceMessage("Successully recursively red the directory");
std::set<uint64_t> presentMusicId = audio->getAllIdentificators();
std::set<W::String> presentAudio;
std::set<uint64_t>::const_iterator pai(presentMusicId.begin()), pae(presentMusicId.end());
for (; pai != pae; ++pai) {
presentAudio.insert(audio->getPath(*pai));
}
std::set<uint64_t> presentImageId = images->getAllIdentificators();
std::set<W::String> presentImages;
std::set<uint64_t>::const_iterator pii(presentImageId.begin()), pie(presentImageId.end());
for (; pii != pie; ++pii) {
presentImages.insert(images->getPath(*pii));
}
std::list<T::File>::const_iterator itr = list->begin();
std::list<T::File>::const_iterator end = list->end();
for (; itr != end; ++itr) {
W::String path = itr->getPath();
emit serviceMessage(QString("Analysing ") + path.toString().c_str());
if (itr->suffix() == u"mp3") {
if (presentAudio.find(itr->getPath()) == presentAudio.end()) {
AudioTag tag(*itr);
uint64_t id = audio->addResource(itr->getPath());
AudioId aid(tag.getArtist(), tag.getAlbum(), tag.getTitle());
foundAudios.insert(std::make_pair(aid, id));
}
} else if (itr->suffix() == u"jpg") {
if (presentImages.find(itr->getPath()) == presentImages.end()) {
uint64_t id = images->addResource(itr->getPath());
foundImages.insert(std::make_pair(itr->parentDirectory(), id));
}
}
}
emit serviceMessage(QString("Found ") + std::to_string(foundAudios.size()).c_str() + " audio files");
emit serviceMessage(QString("Found ") + std::to_string(foundImages.size()).c_str() + " images");
state = updatingMusicDataBase;
updateMusicDataBase();
} else {
emit serviceMessage("Error parsing the directory");
}
delete list;
}
void Parser::updateMusicDataBase()
{
while (foundAudios.size() > 0) {
std::map<AudioId, uint64_t>::const_iterator itr = foundAudios.begin();
std::set<uint64_t> aids = artists.find(W::String(u"name"), itr->first.artist);
if (aids.size() == 0) {
W::Vocabulary art;
art.insert(u"name", itr->first.artist);
artists.addRemoteElement(art);
artistsReady = false;
emit serviceMessage(QString("Creating artist: ") + itr->first.artist.toString().c_str());
return;
}
uint64_t artistId = *(aids.begin());
uint64_t thumbId = 0; //TODO make some default picture for the case of not found images
std::set<uint64_t> alids = albums.find(W::String(u"name"), itr->first.album);
std::map<W::String, uint64_t>::const_iterator albImageItr = foundImages.find(itr->first.album);
if (albImageItr != foundImages.end()) {
thumbId = albImageItr->second;
}
uint64_t albumId = 0;
bool albumFound = false;
const C::Vocabulary* albCtrl = 0;
while (alids.size() > 0 && !albumFound) {
std::set<uint64_t>::const_iterator litr = alids.begin();
albumId = *litr;
alids.erase(litr);
albCtrl = &albums.get(albumId);
if (static_cast<const W::Uint64&>(albCtrl->at(u"artist")) == artistId) {
albumFound = true;
}
}
if (!albumFound) {
W::Vocabulary alb;
alb.insert(u"name", itr->first.album);
alb.insert(u"artist", W::Uint64(artistId));
if (thumbId != 0) {
alb.insert(u"image", W::Uint64(thumbId));
emit serviceMessage(QString("Found a cover for album: ") + itr->first.album.toString().c_str());
}
albums.addRemoteElement(alb);
albumsReady = false;
emit serviceMessage(QString("Creating album: ") + itr->first.album.toString().c_str());
return;
}
if (thumbId != 0 && (!albCtrl->has(u"image") || static_cast<const W::Uint64&>(albCtrl->at(u"image")) != thumbId)) {
W::Vocabulary alb;
alb.insert(u"image", W::Uint64(thumbId));
albums.updateRemoteElement(W::Uint64(albumId), alb);
emit serviceMessage(QString("Found a cover for album: ") + itr->first.album.toString().c_str());
foundImages.erase(albImageItr);
}
std::set<uint64_t> sids = songs.find(W::String(u"name"), itr->first.name);
uint64_t songId = 0;
bool songFound = false;
const C::Vocabulary* songCtrl = 0;
while (sids.size() > 0 && !songFound) {
std::set<uint64_t>::const_iterator sitr = sids.begin();
songId = *sitr;
sids.erase(sitr);
songCtrl = &songs.get(songId);
if (static_cast<const W::Uint64&>(songCtrl->at(u"album")) == albumId && static_cast<const W::Uint64&>(songCtrl->at(u"artist")) == artistId) {
songFound = true;
}
}
W::Vocabulary sng;
sng.insert(u"audio", W::Uint64(itr->second));
if (!songFound) {
sng.insert(u"name", itr->first.name);
sng.insert(u"album", W::Uint64(albumId));
sng.insert(u"artist", W::Uint64(artistId));
songs.addRemoteElement(sng);
songsReady = false;
emit serviceMessage(QString("Creating a song: ") + itr->first.name.toString().c_str());
} else if (!songCtrl->has(u"audio") || static_cast<const W::Uint64&>(songCtrl->at(u"audio")) != itr->second) {
emit serviceMessage(QString("Found missing media for a song: ") + itr->first.name.toString().c_str());
songs.updateRemoteElement(W::Uint64(songId), sng);
}
foundAudios.erase(itr);
if (!songFound) {
return;
}
}
emit serviceMessage("Audio parsing is complete");
state = updatingImageDataBase;
emit serviceMessage("Parsing images");
updateImageDataBase();
}
void Parser::updateImageDataBase()
{
while (foundImages.size() > 0) {
std::map<W::String, uint64_t>::const_iterator itr = foundImages.begin();
std::set<uint64_t> alids = albums.find(W::String(u"name"), itr->first);
if (alids.size() == 0) {
emit serviceMessage(QString("Image in the folder ") + itr->first.toString().c_str() + " doesn't belong to any albumm, skipping");
} else if (alids.size() > 1) {
emit serviceMessage(QString("Image in the folder ") + itr->first.toString().c_str() + " belongs to " + std::to_string(alids.size()).c_str() + " albums, skipping");
} else {
uint64_t albumId = *alids.begin();
const C::Vocabulary& ctrl = albums.get(albumId);
if (!ctrl.has(u"image") || static_cast<const W::Uint64&>(ctrl.at(u"image")) != itr->second) {
W::Vocabulary vc;
vc.insert(u"image", W::Uint64(itr->second));
emit serviceMessage(QString("Found missing cover for album: ") + itr->first.toString().c_str());
albums.updateRemoteElement(W::Uint64(albumId), vc);
}
}
foundImages.erase(itr);
}
emit serviceMessage("Parsing is complete");
state = idle;
emit done(path);
}

71
corax/tools/parser.h Normal file
View File

@ -0,0 +1,71 @@
#ifndef PARSER_H
#define PARSER_H
/**
* @todo write docs
*/
#include <map>
#include <QtCore/QObject>
#include <wType/string.h>
#include <wType/address.h>
#include <wSocket/socket.h>
#include <wDispatcher/dispatcher.h>
#include <wController/collection.h>
#include <wDatabase/resourcecache.h>
#include <tools/file.h>
#include "audiotag.h"
#include "audioid.h"
class Parser: public QObject
{
Q_OBJECT
public:
Parser(const W::Socket* p_socket, W::Dispatcher* p_dp, ResourceCache* p_audio, ResourceCache* p_images);
~Parser();
void parse(const W::String& p_path);
signals:
void serviceMessage(const QString& msg);
void done(const W::String& path);
private:
enum State {
idle,
waitingForCollections,
parsingDirectory,
updatingMusicDataBase,
updatingImageDataBase
};
const W::Socket* socket;
W::Dispatcher* dp;
C::Collection songs;
C::Collection albums;
C::Collection artists;
ResourceCache* audio;
ResourceCache* images;
W::String path;
bool songsReady;
bool albumsReady;
bool artistsReady;
State state;
std::map<W::String, uint64_t> foundImages;
std::map<AudioId, uint64_t> foundAudios;
void checkState();
void parseDirectory();
void updateMusicDataBase();
void updateImageDataBase();
private slots:
void onSongsReady();
void onAlbumsReady();
void onArtistsReady();
};
#endif // PARSER_H

15
lib/CMakeLists.txt Normal file
View File

@ -0,0 +1,15 @@
cmake_minimum_required(VERSION 2.8.12)
project(lib)
add_subdirectory(utils)
add_subdirectory(tools)
add_subdirectory(wSocket)
add_subdirectory(wType)
add_subdirectory(wDispatcher)
add_subdirectory(wContainer)
add_subdirectory(wSsh)
add_subdirectory(wModel)
add_subdirectory(wController)
add_subdirectory(wServerUtils)
add_subdirectory(fontParser)
add_subdirectory(wDatabase)

View File

@ -0,0 +1,17 @@
cmake_minimum_required(VERSION 2.6)
project(fontparser)
set(SOURCES
font.cpp
)
add_subdirectory(tables)
add_library(font STATIC ${SOURCES})
target_link_libraries(font tables)
add_executable(fontparser main.cpp)
target_link_libraries(fontparser font)
install(TARGETS fontparser RUNTIME DESTINATION bin)

223
lib/fontParser/font.cpp Normal file
View File

@ -0,0 +1,223 @@
#include "font.h"
#include <arpa/inet.h>
Font::Font(const std::string& p_path):
path(p_path),
tables(),
cmap(0),
hhea(0),
hmtx(0),
head(0),
name(0)
{
std::ifstream file(path, std::ios::in | std::ios::binary);
char * buffer;
buffer = new char[4];
file.read(buffer, 4);
uint32_t sfntVersion = ntohl(*((uint32_t*) buffer));
if (sfntVersion == 0x00010000) {
version = TrueTypeOutlines;
} else if (sfntVersion == 0x4f54544f) {
version = WithCFFData;
} else {
std::cout << "unsupported sfntVersion" << std::endl;
throw 1;
}
delete[] buffer;
buffer = new char[2];
file.read(buffer, 2);
numberTables = ntohs(*((uint16_t*) buffer));
file.read(buffer, 2);
searchRange = ntohs(*((uint16_t*) buffer));
file.read(buffer, 2);
entrySelector = ntohs(*((uint16_t*) buffer));
file.read(buffer, 2);
rangeShift = ntohs(*((uint16_t*) buffer));
for (int i = 0; i < numberTables; ++i) {
Table* t = Table::fromIfStream(file);
tables.insert(std::make_pair(t->tag, t));
}
file.close();
}
Font::~Font()
{
std::map<std::string, Table*>::const_iterator beg = tables.begin();
std::map<std::string, Table*>::const_iterator end = tables.end();
for (; beg != end; ++beg) {
delete beg->second;
}
}
bool Font::hasTable(const std::string& tag) const
{
std::map<std::string, Table*>::const_iterator itr = tables.find(tag);
return itr != tables.end();
}
std::list<std::string> Font::availableTables() const
{
std::list<std::string> res;
std::map<std::string, Table*>::const_iterator beg = tables.begin();
std::map<std::string, Table*>::const_iterator end = tables.end();
for (; beg != end; ++beg) {
res.push_back(beg->first);
}
return res;
}
std::map<uint32_t, uint32_t> Font::getCharCodeToCIDTable(uint32_t start, uint32_t end)
{
if (cmap == NULL) {
cmap = static_cast<Cmap*>(tables.at("cmap"));
cmap->read(path);
}
std::map<uint32_t, uint32_t> res;
for (uint32_t i = start; i <= end; ++i) {
res.insert(std::make_pair(i, cmap->getCID(i)));
}
return res;
}
std::map<uint32_t, Hmtx::HMetric> Font::getCharCodeMetrics(uint32_t start, uint32_t end)
{
std::map<uint32_t, uint32_t> CCtoCID = getCharCodeToCIDTable(start, end);
std::map<uint32_t, Hmtx::HMetric> res;
if (hmtx == NULL) {
hmtx = static_cast<Hmtx*>(tables.at("hmtx"));
if (hhea == NULL) {
hhea = static_cast<Hhea*>(tables.at("hhea"));
hhea->read(path);
}
hmtx->numOfLongHorMetrics = hhea->numOfLongHorMetrics;
hmtx->read(path);
}
std::map<uint32_t, uint32_t>::const_iterator itr = CCtoCID.begin();
std::map<uint32_t, uint32_t>::const_iterator mend = CCtoCID.end();
for (; itr != mend; ++itr) {
res.insert(std::make_pair(itr->first, hmtx->getMetric(itr->second)));
}
return res;
}
Table * Font::getTable(const std::string& tag)
{
std::map<std::string, Table*>::iterator itr = tables.find(tag);
return itr->second;
}
uint16_t Font::getUnitsPerEm()
{
if (head == NULL) {
head = static_cast<Head*>(tables.at("head"));
head->read(path);
}
return head->unitsPerEm;
}
int16_t Font::getAscent()
{
if (hhea == NULL) {
hhea = static_cast<Hhea*>(tables.at("hhea"));
hhea->read(path);
}
return hhea->ascent;
}
int16_t Font::getDescent()
{
if (hhea == NULL) {
hhea = static_cast<Hhea*>(tables.at("hhea"));
hhea->read(path);
}
return hhea->descent;
}
int16_t Font::getLineGap()
{
if (hhea == NULL) {
hhea = static_cast<Hhea*>(tables.at("hhea"));
hhea->read(path);
}
return hhea->lineGap;
}
std::string Font::getNameField(std::string key)
{
if (name == NULL) {
name = static_cast<Name*>(tables.at("name"));
name->read(path);
}
return name->getRecord(key);
}
int16_t Font::getCaretSlopeRise()
{
if (hhea == NULL) {
hhea = static_cast<Hhea*>(tables.at("hhea"));
hhea->read(path);
}
return hhea->caretSlopeRise;
}
int16_t Font::getCaretSlopeRun()
{
if (hhea == NULL) {
hhea = static_cast<Hhea*>(tables.at("hhea"));
hhea->read(path);
}
return hhea->caretSlopeRun;
}
int16_t Font::getXMax()
{
if (head == NULL) {
head = static_cast<Head*>(tables.at("head"));
head->read(path);
}
return head->xMax;
}
int16_t Font::getXMin()
{
if (head == NULL) {
head = static_cast<Head*>(tables.at("head"));
head->read(path);
}
return head->xMin;
}
int16_t Font::getYMax()
{
if (head == NULL) {
head = static_cast<Head*>(tables.at("head"));
head->read(path);
}
return head->yMax;
}
int16_t Font::getYMin()
{
if (head == NULL) {
head = static_cast<Head*>(tables.at("head"));
head->read(path);
}
return head->yMin;
}

62
lib/fontParser/font.h Normal file
View File

@ -0,0 +1,62 @@
#ifndef FILE_H
#define FILE_H
#include <fstream>
#include <iostream>
#include <string>
#include <stdint.h>
#include <map>
#include <list>
#include "tables/table.h"
#include "tables/cmap.h"
#include "tables/hhea.h"
#include "tables/hmtx.h"
#include "tables/head.h"
#include "tables/name.h"
class Font
{
public:
enum SfntVersion {
TrueTypeOutlines,
WithCFFData
};
Font(const std::string& p_path);
~Font();
bool hasTable(const std::string& tag) const;
Table* getTable(const std::string& tag);
std::list<std::string> availableTables() const;
std::map<uint32_t, uint32_t> getCharCodeToCIDTable(uint32_t start = 0, uint32_t end = 0xffff);
std::map<uint32_t, Hmtx::HMetric> getCharCodeMetrics(uint32_t start = 0, uint32_t end = 0xffff);
uint16_t getUnitsPerEm();
int16_t getAscent();
int16_t getDescent();
int16_t getLineGap();
int16_t getCaretSlopeRise();
int16_t getCaretSlopeRun();
int16_t getXMin();
int16_t getXMax();
int16_t getYMin();
int16_t getYMax();
std::string getNameField(std::string key);
SfntVersion version;
uint16_t numberTables;
uint16_t searchRange;
uint16_t entrySelector;
uint16_t rangeShift;
private:
const std::string path;
std::map<std::string, Table*> tables;
Cmap* cmap;
Hhea* hhea;
Hmtx* hmtx;
Head* head;
Name* name;
};
#endif // FILE_H

50
lib/fontParser/main.cpp Normal file
View File

@ -0,0 +1,50 @@
#include "font.h"
#include <iostream>
#include <list>
#include <string>
#include "tables/hmtx.h"
int main(int argc, char **argv) {
Font file(argv[1]);
std::map<uint32_t, Hmtx::HMetric> cidMap = file.getCharCodeMetrics(0, 0x4ff);
std::map<uint32_t, Hmtx::HMetric>::const_iterator itr = cidMap.begin();
std::map<uint32_t, Hmtx::HMetric>::const_iterator end = cidMap.end();
std::cout << "{\n";
std::cout << " \"ascent\": " << file.getAscent() << ",\n";
std::cout << " \"descent\": " << file.getDescent() << ",\n";
std::cout << " \"lineGap\": " << file.getLineGap() << ",\n";
std::cout << " \"caretSlopeRise\": " << file.getCaretSlopeRise() << ",\n";
std::cout << " \"caretSlopeRun\": " << file.getCaretSlopeRun() << ",\n";
std::cout << " \"unitsPerEm\": " << file.getUnitsPerEm() << ",\n";
std::cout << " \"fontFamily\": \"" << file.getNameField("fontFamily") << "\",\n";
std::cout << " \"postScriptName\": \"" << file.getNameField("postScriptName") << "\",\n";
std::cout << " \"boundingBox\": {\n";
std::cout << " \"xMin\": " << file.getXMin() << ",\n";
std::cout << " \"xMax\": " << file.getXMax() << ",\n";
std::cout << " \"yMin\": " << file.getYMin() << ",\n";
std::cout << " \"yMax\": " << file.getYMax() << "\n";
std::cout << " },\n";
std::cout << " \"advanceWidthArray\": [\n ";
int i = 0;
for (; itr != end; ++itr) {
if (i != 0) {
if (i == 16) {
std::cout << ",\n ";
i = 0;
} else {
std::cout << ", ";
}
}
std::cout << itr->second.advanceWidth;
++i;
}
std::cout << "\n ]\n";
std::cout << "}" << std::endl;
return 0;
}

View File

@ -0,0 +1,13 @@
cmake_minimum_required(VERSION 2.6)
project(tables)
set(SOURCES
table.cpp
cmap.cpp
hhea.cpp
hmtx.cpp
head.cpp
name.cpp
)
add_library(tables STATIC ${SOURCES})

View File

@ -0,0 +1,218 @@
#include "cmap.h"
#include <arpa/inet.h>
Cmap::Cmap(const std::string& p_tag, uint32_t p_checkSum, uint32_t p_offset, uint32_t p_length):
Table(p_tag, p_checkSum, p_offset, p_length),
initialized(false),
mt(0)
{
}
Cmap::~Cmap()
{
if (initialized) {
delete mt;
}
}
void Cmap::read(const std::string& path)
{
std::ifstream file(path, std::ios::in | std::ios::binary);
file.seekg(offset);
char * buffer;
buffer = new char[2];
file.read(buffer, 2);
version = ntohs(*((uint16_t*) buffer));
file.read(buffer, 2);
numberOfTables = ntohs(*((uint16_t*) buffer));
delete[] buffer;
buffer = new char[8];
std::list<Enc> encodings;
for (int i = 0; i < numberOfTables; ++i) {
file.read(buffer, 8);
char pb[2] = {buffer[0], buffer[1]};
char eb[2] = {buffer[2], buffer[3]};
char ob[4] = {buffer[4], buffer[5], buffer[6], buffer[7]};
uint16_t pid = ntohs(*((uint16_t*) pb));
uint16_t eid = ntohs(*((uint16_t*) eb));
uint16_t offset = ntohl(*((uint32_t*) ob));
//std::cout << "Found encoding platformId " << pid << ", encodingId " << eid << std::endl;
if (pid == 0 || (pid == 3 && eid == 1)) {
encodings.emplace_back(pid, eid, offset);
}
}
delete[] buffer;
std::list<Enc>::const_iterator itr = encodings.begin();
std::list<Enc>::const_iterator end = encodings.end();
for (; itr != end; ++itr) {
//std::cout << "Trying platformId " << itr->platformId << ", encodingId " << itr->encodingId << std::endl;
file.seekg(offset + itr->offset);
bool success = true;
MappingTable* table;
try {
table = MappingTable::fromIfStream(file);
} catch (int e) {
success = false;
}
if (success) {
initialized = true;
mt = table;
break;
}
}
file.close();
if (!initialized) {
//std::cout << "Error reading cmap: no supported encoding format" << std::endl;
throw 3;
}
}
uint32_t Cmap::getCID(uint32_t charCode) const
{
return this->mt->getCID(charCode);
}
MappingTable * MappingTable::fromIfStream(std::ifstream& file)
{
uint64_t position = file.tellg();
char * buffer;
buffer = new char[2];
file.read(buffer, 2);
uint16_t format = ntohs(*((uint16_t*) buffer));
MappingTable* table = NULL;
if (format >= 8) {
if (format != 14) {
file.read(buffer, 2); //padded .0 in stupid formats
}
delete[] buffer;
buffer = new char[4];
file.read(buffer, 4);
uint32_t length = ntohl(*((uint32_t*) buffer));
file.seekg(position);
buffer = new char[length];
file.read(buffer, length);
} else {
file.read(buffer, 2);
uint16_t length = ntohs(*((uint16_t*) buffer));
file.seekg(position);
buffer = new char[length];
file.read(buffer, length);
if (format == 4) {
table = new Format4(buffer, length);
}
}
delete[] buffer;
if (table == NULL) {
std::cout << "Unrecognized format " << format << std::endl;
throw 3;
}
return table;
}
MappingTable::MappingTable(uint16_t p_f):
format(p_f)
{
}
MappingTable::~MappingTable()
{
}
Format4::Format4(char * data, uint16_t length):
MappingTable(4),
charCodesEndCode(),
segments(0),
glyphIndexArray(0)
{
char sc[2] = {data[6], data[7]};
uint16_t segCount = ntohs(*((uint16_t*) sc)) / 2;
segments = new std::vector<SegParams>(segCount);
int endCodeShift = 14;
int startCodeShift = endCodeShift + segCount * 2 + 2;
int deltaShift = startCodeShift + segCount * 2;
int rangeShift = deltaShift + segCount * 2;
int giaShift = rangeShift + segCount * 2;
int giaLength = (length - giaShift) / 2;
glyphIndexArray = new std::vector<uint16_t>(giaLength);
// std::cout << "Segments: " << segCount << ", ";
// std::cout << "Glyphs: " << giaLength << "\n";
// std::cout << "******************************************" << "\n";
for (int i = 0; i < segCount; ++i) {
char cc[2] = {data[2 * i + endCodeShift], data[2 * i + endCodeShift + 1]};
char sc[2] = {data[2 * i + startCodeShift], data[2 * i + startCodeShift + 1]};
char dc[2] = {data[2 * i + deltaShift], data[2 * i + deltaShift + 1]};
char rc[2] = {data[2 * i + rangeShift], data[2 * i + rangeShift + 1]};
uint16_t endCharCode = ntohs(*((uint16_t*) cc));
uint16_t startCharCode = ntohs(*((uint16_t*) sc));
int16_t delta = ntohs(*((int16_t*) dc));
uint16_t range = ntohs(*((uint16_t*) rc));
SegParams& sp = segments->at(i);
sp.endCode = endCharCode;
sp.startCode = startCharCode;
sp.idDelta = delta;
sp.idRangeOffset = range;
charCodesEndCode.insert(std::make_pair(endCharCode, i));
// std::cout << "Segment " << i << ",\t";
// std::cout << "Start " << startCharCode << ",\t";
// std::cout << "End " << endCharCode << ",\t";
// std::cout << "Delta " << delta << ",\t";
// std::cout << "Range " << range << "\n";
}
// std::cout << "******************************************" << std::endl;;
for (int i = 0; i < giaLength; ++i) {
char cc[2] = {data[2 * i + giaShift], data[2 * i + giaShift + 1]};
uint16_t glyphIndex = ntohs(*((uint16_t*) cc));
glyphIndexArray->at(i) = glyphIndex;
}
}
Format4::~Format4()
{
delete segments;
delete glyphIndexArray;
}
uint32_t Format4::getCID(uint32_t charCode) const
{
uint16_t cid;
uint16_t c = charCode & 0xffff;
std::map<uint16_t, uint16_t>::const_iterator itr = charCodesEndCode.lower_bound(c);
uint16_t i = itr->second;
SegParams& seg = segments->at(i);
if (seg.startCode > c) {
return 0;
}
if (seg.idRangeOffset == 0) {
cid = c + seg.idDelta;
} else {
cid = i + seg.idRangeOffset - segments->size() + c - seg.startCode;
}
return cid;
}

View File

@ -0,0 +1,66 @@
#ifndef CMAP_H
#define CMAP_H
#include "table.h"
#include <list>
#include <map>
#include <vector>
struct Enc {
Enc(uint16_t pid, uint16_t eid, uint32_t off): platformId(pid), encodingId(eid), offset(off) {}
uint16_t platformId;
uint16_t encodingId;
uint32_t offset;
};
class MappingTable {
protected:
MappingTable(uint16_t p_f);
uint16_t format;
public:
static MappingTable* fromIfStream(std::ifstream& file);
virtual ~MappingTable();
virtual uint32_t getCID(uint32_t charCode) const = 0;
};
class Format4 : public MappingTable {
public:
Format4(char* data, uint16_t length);
~Format4();
uint32_t getCID(uint32_t charCode) const override;
private:
struct SegParams {
uint16_t endCode;
uint16_t startCode;
int16_t idDelta;
uint16_t idRangeOffset;
};
std::map<uint16_t, uint16_t> charCodesEndCode;
std::vector<SegParams>* segments;
std::vector<uint16_t>* glyphIndexArray;
};
class Cmap : public Table
{
public:
Cmap(const std::string& p_tag, uint32_t p_checkSum, uint32_t p_offset, uint32_t p_length);
~Cmap();
void read(const std::string & path) override;
uint32_t getCID(uint32_t charCode) const;
uint16_t version;
uint16_t numberOfTables;
private:
bool initialized;
MappingTable* mt;
};
#endif // CMAP_H

View File

@ -0,0 +1,93 @@
#include "head.h"
#include <arpa/inet.h>
Head::Head(const std::string& p_tag, uint32_t p_checkSum, uint32_t p_offset, uint32_t p_length):
Table(p_tag, p_checkSum, p_offset, p_length),
fontRevisionMajor(0),
fontRevisionMinor(0),
flags(0),
unitsPerEm(0),
xMin(0),
yMin(0),
xMax(0),
yMax(0),
macStyle(0),
lowestRecPPEM(0),
fontDirectionHint(0),
indexToLocFormat(0)
{
}
Head::~Head()
{
}
void Head::read(const std::string& path)
{
char * buffer;
buffer = new char[2];
std::ifstream file(path, std::ios::in | std::ios::binary);
file.seekg(offset);
file.read(buffer, 2); //version is not interesting, it is always 16.16 fixed point number equals to "1.0";
file.read(buffer, 2); //version is not interesting, it is always 16.16 fixed point number equals to "1.0";
file.read(buffer, 2);
fontRevisionMajor = ntohs(*((uint16_t*) buffer));
file.read(buffer, 2);
fontRevisionMinor = ntohs(*((uint16_t*) buffer));
delete[] buffer;
buffer = new char[4];
file.read(buffer, 4); //checkSumAdjustment - it's something fishy, no idea what to use it for;
file.read(buffer, 4); //magicNumber, always set to 0x5f0f3cf5;
delete[] buffer;
buffer = new char[2];
file.read(buffer, 2);
flags = ntohs(*((uint16_t*) buffer));
file.read(buffer, 2);
unitsPerEm = ntohs(*((uint16_t*) buffer));
file.read(buffer, 2); //creation date is a signed int64
file.read(buffer, 2);
file.read(buffer, 2);
file.read(buffer, 2);
file.read(buffer, 2); //last modification date is a signed int64
file.read(buffer, 2);
file.read(buffer, 2);
file.read(buffer, 2);
file.read(buffer, 2);
xMin = ntohs(*((int16_t*) buffer));
file.read(buffer, 2);
yMin = ntohs(*((int16_t*) buffer));
file.read(buffer, 2);
xMax = ntohs(*((int16_t*) buffer));
file.read(buffer, 2);
yMax = ntohs(*((int16_t*) buffer));
file.read(buffer, 2);
macStyle = ntohs(*((uint16_t*) buffer));
file.read(buffer, 2);
lowestRecPPEM = ntohs(*((uint16_t*) buffer));
file.read(buffer, 2);
fontDirectionHint = ntohs(*((int16_t*) buffer));
file.read(buffer, 2);
indexToLocFormat = ntohs(*((int16_t*) buffer));
//and there is stil uint16 glyph data format, but its always 0;
file.close();
delete[] buffer;
}

View File

@ -0,0 +1,28 @@
#ifndef HEAD_H
#define HEAD_H
#include "table.h"
class Head : public Table
{
public:
Head(const std::string& p_tag, uint32_t p_checkSum, uint32_t p_offset, uint32_t p_length);
~Head();
void read(const std::string & path) override;
uint16_t fontRevisionMajor;
uint16_t fontRevisionMinor;
uint16_t flags;
uint16_t unitsPerEm;
int16_t xMin;
int16_t yMin;
int16_t xMax;
int16_t yMax;
uint16_t macStyle;
uint16_t lowestRecPPEM;
int16_t fontDirectionHint;
int16_t indexToLocFormat;
};
#endif // HEAD_H

View File

@ -0,0 +1,76 @@
#include "hhea.h"
#include <arpa/inet.h>
Hhea::Hhea(const std::string& p_tag, uint32_t p_checkSum, uint32_t p_offset, uint32_t p_length):
Table(p_tag, p_checkSum, p_offset, p_length),
ascent(0),
descent(0),
lineGap(0),
advanceWidthMax(0),
minLeftSideBearing(0),
minRightSideBearing(0),
xMaxExtent(0),
caretSlopeRise(0),
caretSlopeRun(0),
caretOffset(0),
numOfLongHorMetrics(0)
{
}
Hhea::~Hhea()
{
}
void Hhea::read(const std::string& path)
{
char * buffer;
buffer = new char[2];
std::ifstream file(path, std::ios::in | std::ios::binary);
file.seekg(offset);
file.read(buffer, 2); //version is not interesting, it is always 16.16 fixed point number equals to "1.0";
file.read(buffer, 2); //version is not interesting, it is always 16.16 fixed point number equals to "1.0";
file.read(buffer, 2);
ascent = ntohs(*((int16_t*) buffer));
file.read(buffer, 2);
descent = ntohs(*((int16_t*) buffer));
file.read(buffer, 2);
lineGap = ntohs(*((int16_t*) buffer));
file.read(buffer, 2);
advanceWidthMax = ntohs(*((uint16_t*) buffer));
file.read(buffer, 2);
minLeftSideBearing = ntohs(*((int16_t*) buffer));
file.read(buffer, 2);
minRightSideBearing = ntohs(*((int16_t*) buffer));
file.read(buffer, 2);
xMaxExtent = ntohs(*((int16_t*) buffer));
file.read(buffer, 2);
caretSlopeRise = ntohs(*((int16_t*) buffer));
file.read(buffer, 2);
caretSlopeRun = ntohs(*((int16_t*) buffer));
file.read(buffer, 2);
caretOffset = ntohs(*((int16_t*) buffer));
file.read(buffer, 2); //reserved empty field, supposed to be 0;
file.read(buffer, 2); //reserved empty field, supposed to be 0;
file.read(buffer, 2); //reserved empty field, supposed to be 0;
file.read(buffer, 2); //reserved empty field, supposed to be 0;
file.read(buffer, 2); //metricDataFormat, it's supposed to be 0;
file.read(buffer, 2);
numOfLongHorMetrics = ntohs(*((uint16_t*) buffer));
delete[] buffer;
file.close();
}

View File

@ -0,0 +1,27 @@
#ifndef HHEA_H
#define HHEA_H
#include "table.h"
class Hhea : public Table
{
public:
Hhea(const std::string& p_tag, uint32_t p_checkSum, uint32_t p_offset, uint32_t p_length);
~Hhea();
void read(const std::string & path) override;
int16_t ascent;
int16_t descent;
int16_t lineGap;
uint16_t advanceWidthMax;
int16_t minLeftSideBearing;
int16_t minRightSideBearing;
int16_t xMaxExtent;
int16_t caretSlopeRise;
int16_t caretSlopeRun;
int16_t caretOffset;
uint16_t numOfLongHorMetrics;
};
#endif // HHEA_H

View File

@ -0,0 +1,59 @@
#include "hmtx.h"
#include <arpa/inet.h>
Hmtx::Hmtx(const std::string& p_tag, uint32_t p_checkSum, uint32_t p_offset, uint32_t p_length):
Table(p_tag, p_checkSum, p_offset, p_length),
numOfLongHorMetrics(0),
longHorMetric(0)
{
}
Hmtx::~Hmtx()
{
delete longHorMetric;
}
void Hmtx::read(const std::string& path)
{
if (numOfLongHorMetrics == 0) {
throw 1;
}
std::ifstream file(path, std::ios::in | std::ios::binary);
file.seekg(offset);
char * buffer;
buffer = new char[2];
longHorMetric = new std::vector<HMetric>(numOfLongHorMetrics);
for (int i = 0; i < numOfLongHorMetrics; ++i) {
HMetric& met = longHorMetric->at(i);
file.read(buffer, 2);
uint16_t aw = ntohs(*((uint16_t*) buffer));
file.read(buffer, 2);
int16_t lsb = ntohs(*((int16_t*) buffer));
met.advanceWidth = aw;
met.leftSideBearing = lsb;
}
file.close();
delete[] buffer;
}
Hmtx::HMetric::HMetric():
advanceWidth(0),
leftSideBearing(0)
{
}
Hmtx::HMetric Hmtx::getMetric(uint16_t cid) const
{
if (cid >= longHorMetric->size()) {
cid = longHorMetric->size() - 1;
}
return longHorMetric->at(cid);
}

View File

@ -0,0 +1,29 @@
#ifndef HMTX_H
#define HMTX_H
#include "table.h"
#include <vector>
class Hmtx : public Table
{
public:
Hmtx(const std::string& p_tag, uint32_t p_checkSum, uint32_t p_offset, uint32_t p_length);
~Hmtx();
uint16_t numOfLongHorMetrics;
struct HMetric {
HMetric();
uint16_t advanceWidth;
int16_t leftSideBearing;
};
void read(const std::string & path) override;
HMetric getMetric(uint16_t cid) const;
private:
std::vector<HMetric>* longHorMetric;
};
#endif // HMTX_H

View File

@ -0,0 +1,136 @@
#include "name.h"
#include <arpa/inet.h>
#include <list>
#include <set>
#include <codecvt>
#include <locale>
const std::map<std::string, uint16_t> Name::nameIds({
{ "copyright", 0 },
{ "fontFamily", 1 },
{ "fontSubfamily", 2 },
{ "uniqueSubfamilyId", 3 },
{ "fullFontName", 4 },
{ "nameTableVersion", 5 },
{ "postScriptName", 6 },
{ "trademarkNotice", 7 },
{ "manufacturerName", 8 },
{ "designerName", 9 },
{ "description", 10 },
{ "vendorURL", 11 },
{ "designerURL", 12 },
{ "licenseDescription", 13 },
{ "licenseURL", 14 },
{ "preferredFamily", 16 },
{ "preferredSubfamily", 17 },
{ "compatibleFull", 18 },
{ "sampleText", 19 },
{ "postScriptCID", 20 }
});
Name::Name(const std::string& p_tag, uint32_t p_checkSum, uint32_t p_offset, uint32_t p_length):
Table(p_tag, p_checkSum, p_offset, p_length),
names()
{
}
Name::~Name()
{
}
void Name::read(const std::string& path)
{
std::ifstream file(path, std::ios::in | std::ios::binary);
file.seekg(offset);
char * buffer;
buffer = new char[2];
file.read(buffer, 2); //format. it is always 0 or 1 for stupid microsoft langTags, but I don't cate, gonna use offset;
file.read(buffer, 2);
uint16_t count = ntohs(*((uint16_t*) buffer));
file.read(buffer, 2);
uint32_t storageOffset = offset + ntohs(*((uint16_t*) buffer));
std::list<NameRecord> list;
std::set<uint16_t> ids;
for (int i = 0; i < count; ++i) {
file.read(buffer, 2);
uint16_t pid = ntohs(*((uint16_t*) buffer));
file.read(buffer, 2);
uint16_t eid = ntohs(*((uint16_t*) buffer));
file.read(buffer, 2);
uint16_t lid = ntohs(*((uint16_t*) buffer));
file.read(buffer, 2);
uint16_t nid = ntohs(*((uint16_t*) buffer));
file.read(buffer, 2);
uint16_t length = ntohs(*((uint16_t*) buffer));
file.read(buffer, 2);
uint16_t nameOffset = ntohs(*((uint16_t*) buffer));
//std::cout << "Found pid " << pid << ", eid " << eid << ", nid " << nid << std::endl;
if (ids.find(nid) == ids.end()) {
if ((pid == 0 && (eid == 3 || eid == 4)) || (pid == 3 && eid == 1)) { //screw microsoft, screw apple;
list.emplace_back(pid, eid, lid, nid, length, nameOffset);
ids.insert(nid);
}
}
}
std::list<NameRecord>::const_iterator itr;
for (itr = list.begin(); itr != list.end(); ++itr) {
const NameRecord& nr = *itr;
file.seekg(storageOffset + nr.offset);
if ((nr.platformId == 0 && (nr.encodingId == 3 || nr.encodingId == 4)) || (nr.platformId == 3 && nr.encodingId == 1)) {
char16_t buf[nr.length / 2];
for (int i = 0; i < nr.length / 2; ++i) {
file.read(buffer, 2);
buf[i] = ntohs(*((char16_t*) buffer));
}
std::u16string string(buf, nr.length / 2);
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert;
names.insert(std::make_pair(nr.nameId, convert.to_bytes(string)));
}
}
delete[] buffer;
file.close();
}
std::string Name::getRecord(uint16_t id) const
{
std::string res("");
std::map<uint16_t, std::string>::const_iterator itr = names.find(id);
if (itr != names.end()) {
res = itr->second;
}
return res;
}
std::string Name::getRecord(const std::string& name) const
{
std::map<std::string, uint16_t>::const_iterator itr = nameIds.find(name);
if (itr == nameIds.end()) {
return "";
} else {
return getRecord(itr->second);
}
}
NameRecord::NameRecord(uint16_t pid, uint16_t eid, uint16_t lid, uint16_t nid, uint16_t p_l, uint16_t p_o):
platformId(pid),
encodingId(eid),
languageId(lid),
nameId(nid),
length(p_l),
offset(p_o)
{
}

View File

@ -0,0 +1,35 @@
#ifndef NAME_H
#define NAME_H
#include <map>
#include <string>
#include "table.h"
class Name : public Table
{
public:
Name(const std::string& p_tag, uint32_t p_checkSum, uint32_t p_offset, uint32_t p_length);
~Name();
void read(const std::string & path) override;
std::string getRecord(uint16_t id) const;
std::string getRecord(const std::string& name) const;
private:
std::map<uint16_t, std::string> names;
static const std::map<std::string, uint16_t> nameIds;
};
struct NameRecord {
NameRecord(uint16_t pid, uint16_t eid, uint16_t lid, uint16_t nid, uint16_t p_l, uint16_t p_o);
uint16_t platformId;
uint16_t encodingId;
uint16_t languageId;
uint16_t nameId;
uint16_t length;
uint16_t offset;
};
#endif // NAME_H

View File

@ -0,0 +1,56 @@
#include "table.h"
#include <arpa/inet.h>
#include "cmap.h"
#include "hhea.h"
#include "hmtx.h"
#include "head.h"
#include "name.h"
Table::Table(const std::string& p_tag, uint32_t p_checkSum, uint32_t p_offset, uint32_t p_length):
tag(p_tag),
checkSum(p_checkSum),
offset(p_offset),
length(p_length)
{
}
Table::~Table()
{
}
Table* Table::fromIfStream(std::ifstream& stream)
{
char * buffer;
buffer = new char[4];
stream.read(buffer, 4);
std::string tag(buffer, 4);
stream.read(buffer, 4);
uint32_t cs = ntohl(*((uint32_t*) buffer));
stream.read(buffer, 4);
uint32_t offset = ntohl(*((uint32_t*) buffer));
stream.read(buffer, 4);
uint32_t l = ntohl(*((uint32_t*) buffer));
if (tag == "cmap") {
return new Cmap(tag, cs, offset, l);
} else if (tag == "hhea") {
return new Hhea(tag, cs, offset, l);
} else if (tag == "hmtx") {
return new Hmtx(tag, cs, offset, l);
} else if (tag == "head") {
return new Head(tag, cs, offset, l);
} else if (tag == "name") {
return new Name(tag, cs, offset, l);
} else {
return new Table(tag, cs, offset, l);
}
}
void Table::read(const std::string& path)
{
std::cout << "table with type " << tag << " is not supported yet" << std::endl;
}

View File

@ -0,0 +1,26 @@
#ifndef TABLE_H
#define TABLE_H
#include <fstream>
#include <iostream>
#include <string>
#include <stdint.h>
class Table
{
public:
Table(const std::string& p_tag, uint32_t p_checkSum, uint32_t p_offset, uint32_t p_length);
virtual ~Table();
const std::string tag;
const uint32_t checkSum;
const uint32_t offset;
const uint32_t length;
static Table* fromIfStream(std::ifstream& stream);
virtual void read(const std::string& path);
};
#endif // TABLE_H

15
lib/tools/CMakeLists.txt Normal file
View File

@ -0,0 +1,15 @@
cmake_minimum_required(VERSION 2.8.12)
project(tools)
set(HEADERS
file.h
)
set(SOURCES
file.cpp
)
add_library(tools ${HEADERS} ${SOURCES})
target_link_libraries(tools wType)

115
lib/tools/file.cpp Normal file
View File

@ -0,0 +1,115 @@
#include "file.h"
#include <iostream>
T::File::File(const W::String& p_path):
path(p_path)
{
}
T::File::~File()
{
}
const W::String & T::File::getPath() const
{
return path;
}
W::String T::File::suffix() const
{
uint64_t dotPos = path.findLastOf(W::String(u"."));
if (dotPos > path.findLastOf(W::String(u"/"))) {
return path.substr(dotPos + 1);
} else {
return W::String(u"");
}
}
bool T::File::readDirectoryRecursive(const W::String& path, std::list<T::File>* result)
{
DIR *d;
struct dirent *dir;
d = opendir(path.toString().c_str());
bool success = false;
if (d) {
while ((dir = readdir(d)) != NULL) {
if (strcmp(dir->d_name, ".") == 0 || strcmp(dir->d_name, "..") == 0) {
continue;
}
W::String d_path = path + W::String(u"/") + W::String(std::string(dir->d_name));
struct stat st;
int err = lstat(d_path.toString().c_str(), &st);
if (err == 0) {
success = true;
switch (st.st_mode & S_IFMT) {
case S_IFDIR:
success = File::readDirectoryRecursive(d_path, result);
break;
case S_IFREG:
result->emplace_back(d_path);
break;
}
} else {
std::cout << "unable read description of file " << d_path.toString() << ". ";
switch (errno) {
case EACCES:
std::cout << "Search permission is denied for one of the directories in the path prefix of path";
break;
case EFAULT:
std::cout << "Bad address";
break;
case ELOOP:
std::cout << "Too many symbolic links encountered while traversing the path";
break;
case ENAMETOOLONG:
std::cout << "path is too long";
break;
case ENOENT:
std::cout << "A component of path does not exist, or path is an empty string";
break;
case ENOMEM:
std::cout << "Out of memory";
break;
case ENOTDIR:
std::cout << "A component of the path prefix of path is not a directory";
break;
case EOVERFLOW:
std::cout << "EOVERFLOW error";
break;
default:
std::cout << "undefined error";
}
std::cout << std::endl;
}
}
closedir(d);
} else {
std::cout << "unable to open a directory " << path.toString() << std::endl;
}
return success;
}
W::String T::File::parentDirectory() const
{
uint64_t lastSlashPos = path.findLastOf(W::String(u"/"));
W::String fPath = path.substr(0, lastSlashPos);
uint64_t pSpashPos = fPath.findLastOf(W::String(u"/"));
return fPath.substr(pSpashPos + 1);
}
W::String T::File::name() const
{
uint64_t slashPos = path.findLastOf(W::String(u"/"));
return path.substr(slashPos + 1);
}
W::String T::File::nameWithoutSuffix() const
{
W::String nws = name();
uint64_t dotPos = path.findLastOf(W::String(u"."));
return nws.substr(0, dotPos);
}

33
lib/tools/file.h Normal file
View File

@ -0,0 +1,33 @@
#ifndef TOOLS_FILE_H
#define TOOLS_FILE_H
#include <wType/string.h>
#include <list>
#include <dirent.h>
#include <string.h>
#include <sys/stat.h>
namespace T {
class File
{
public:
File(const W::String& p_path);
~File();
const W::String& getPath() const;
W::String suffix() const;
W::String nameWithoutSuffix() const;
W::String name() const;
W::String parentDirectory() const;
static bool readDirectoryRecursive(const W::String& path, std::list<File>* result);
private:
W::String path;
};
}
#endif // TOOLS_FILE_H

22
lib/utils/CMakeLists.txt Normal file
View File

@ -0,0 +1,22 @@
cmake_minimum_required(VERSION 2.8.12)
project(utils)
find_package(Qt5Core REQUIRED)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOMOC ON)
set(HEADERS
defines.h
exception.h
signalcatcher.h
)
set(SOURCES
exception.cpp
signalcatcher.cpp
)
add_library(utils ${HEADERS} ${SOURCES})
target_link_libraries(utils Qt5::Core)

9
lib/utils/defines.h Normal file
View File

@ -0,0 +1,9 @@
#ifndef DEFINES_UTILS_H
#define DEFINES_UTILS_H
#define handler(HANDLER) \
void _h_##HANDLER(const W::Event& ev) {h_##HANDLER(ev);}\
virtual void h_##HANDLER(const W::Event& ev);\
#endif

14
lib/utils/exception.cpp Normal file
View File

@ -0,0 +1,14 @@
#include "exception.h"
Utils::Exception::Exception()
{
}
Utils::Exception::~Exception()
{
}
const char* Utils::Exception::what() const noexcept( true )
{
return getMessage().c_str();
}

22
lib/utils/exception.h Normal file
View File

@ -0,0 +1,22 @@
#ifndef EXCEPTION_H
#define EXCEPTION_H
#include <stdexcept>
#include <string>
namespace Utils
{
class Exception:
public std::exception
{
public:
Exception();
virtual ~Exception();
virtual std::string getMessage() const = 0;
const char* what() const noexcept( true );
};
}
#endif // EXCEPTION_H

View File

@ -0,0 +1,59 @@
#include "signalcatcher.h"
#include <signal.h>
#include <sys/socket.h>
#include <unistd.h>
int W::SignalCatcher::sigintFd[2] = {0,0};
W::SignalCatcher::SignalCatcher(QCoreApplication *p_app, QObject *parent):
QObject(parent),
app(p_app)
{
if (::socketpair(AF_UNIX, SOCK_STREAM, 0, sigintFd))
{
qFatal("Couldn't create INT socketpair");
}
if (setup_unix_signal_handlers() != 0)
{
qFatal("Couldn't install unix handlers");
}
snInt = new QSocketNotifier(sigintFd[1], QSocketNotifier::Read, this);
connect(snInt, SIGNAL(activated(int)), this, SLOT(handleSigInt()));
}
W::SignalCatcher::~SignalCatcher()
{}
void W::SignalCatcher::handleSigInt()
{
snInt->setEnabled(false);
char tmp;
::read(sigintFd[1], &tmp, sizeof(tmp));
app->quit();
snInt->setEnabled(true);
}
void W::SignalCatcher::intSignalHandler(int unused)
{
char a = 1;
::write(sigintFd[0], &a, sizeof(a));
}
int W::SignalCatcher::setup_unix_signal_handlers()
{
struct sigaction s_int;
s_int.sa_handler = SignalCatcher::intSignalHandler;
sigemptyset(&s_int.sa_mask);
s_int.sa_flags = 0;
s_int.sa_flags |= SA_RESTART;
if (sigaction(SIGINT, &s_int, 0) > 0)
return 1;
return 0;
}

33
lib/utils/signalcatcher.h Normal file
View File

@ -0,0 +1,33 @@
#ifndef SIGNALCATCHER_H
#define SIGNALCATCHER_H
#include <QtCore/QCoreApplication>
#include <QtCore/QObject>
#include <QtCore/QSocketNotifier>
namespace W
{
class SignalCatcher: public QObject
{
Q_OBJECT
public:
SignalCatcher(QCoreApplication *p_app, QObject *parent = 0);
~SignalCatcher();
static void intSignalHandler(int unused);
public slots:
void handleSigInt();
private:
QCoreApplication *app;
static int sigintFd[2];
QSocketNotifier *snInt;
static int setup_unix_signal_handlers();
};
}
#endif // SIGNALCATCHER_H

View File

@ -0,0 +1,2 @@
cmake_minimum_required(VERSION 2.8.12)

136
lib/wContainer/order.h Normal file
View File

@ -0,0 +1,136 @@
#ifndef ORDER_H
#define ORDER_H
#include <map>
#include <list>
#include <utils/exception.h>
namespace W
{
template <typename data_type, typename comparator = std::less<data_type>>
class Order
{
class Duplicates:
public Utils::Exception
{
public:
Duplicates():Exception(){}
std::string getMessage() const{return "Inserting element duplicates existing";}
};
class NotFound:
public Utils::Exception
{
public:
NotFound():Exception(){}
std::string getMessage() const{return "Erasing element haven't been found";}
};
protected:
typedef std::list<data_type> List;
public:
typedef typename List::size_type size_type;
typedef typename List::const_iterator const_iterator;
typedef typename List::iterator iterator;
protected:
typedef std::map<data_type, const_iterator, comparator> Map;
typedef typename Map::const_iterator m_const_itr;
typedef typename Map::iterator m_itr;
public:
Order():
order(),
r_map()
{}
~Order() {};
size_type size() const {
return order.size();
}
void push_back(data_type element) {
m_const_itr m_itr = r_map.find(element);
if (m_itr != r_map.end()) {
throw Duplicates();
}
const_iterator itr = order.insert(order.end(), element);
r_map.insert(std::make_pair(element, itr));
}
void erase(data_type element) {
m_const_itr itr = r_map.find(element);
if (itr == r_map.end()) {
throw NotFound();
}
order.erase(itr->second);
r_map.erase(itr);
}
void clear() {
order.clear();
r_map.clear();
}
void insert(const_iterator pos, data_type element) {
m_const_itr m_itr = r_map.find(element);
if (m_itr != r_map.end()) {
throw Duplicates();
}
const_iterator itr = order.insert(pos, element);
r_map.insert(std::make_pair(element, itr));
}
void insert(iterator pos, data_type element) {
m_const_itr m_itr = r_map.find(element);
if (m_itr != r_map.end()) {
throw Duplicates();
}
const_iterator itr = order.insert(pos, element);
r_map.insert(std::make_pair(element, itr));
}
const_iterator find(data_type element) const {
m_const_itr itr = r_map.find(element);
if (itr == r_map.end()) {
return end();
} else {
return itr->second;
}
}
const_iterator begin() const {
return order.begin();
}
const_iterator end() const {
return order.end();
}
iterator begin() {
return order.begin();
}
iterator end() {
return order.end();
}
private:
List order;
Map r_map;
};
}
#endif // ORDER_H

View File

@ -0,0 +1,35 @@
cmake_minimum_required(VERSION 2.8.12)
project(controller)
find_package(Qt5Core REQUIRED)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOMOC ON)
set(HEADERS
controller.h
controllerstring.h
list.h
vocabulary.h
attributes.h
catalogue.h
collection.h
)
set(SOURCES
controller.cpp
controllerstring.cpp
list.cpp
vocabulary.cpp
attributes.cpp
catalogue.cpp
collection.cpp
)
add_library(wController STATIC ${HEADERS} ${SOURCES})
target_link_libraries(wController Qt5::Core)
target_link_libraries(wController wSocket)
target_link_libraries(wController wDispatcher)
target_link_libraries(wController wType)

View File

@ -0,0 +1,87 @@
#include "attributes.h"
uint64_t C::Attributes::counter = 0;
C::Attributes::Attributes(const W::Address& p_address, QObject* parent):
C::Vocabulary(p_address, W::Address({W::String(u"attributes") += counter++}), parent),
attributes(new Map()),
reversed(new RMap())
{
}
C::Attributes::~Attributes()
{
delete attributes;
delete reversed;
}
void C::Attributes::_newElement(const W::String& key, const W::Object& element)
{
const W::Vocabulary& evc = static_cast<const W::Vocabulary&>(element);
const W::Uint64& type = static_cast<const W::Uint64&>(evc.at(u"type"));
const W::Address& addr = static_cast<const W::Address&>(evc.at(u"address"));
C::Controller* child = C::Controller::createByType(type, addr);
attributes->insert(std::make_pair(key, child));
reversed->insert(std::make_pair(child, key));
addController(child);
connect(child, SIGNAL(modification(const W::Object&)), SLOT(onAttrModification(const W::Object&)));
C::Vocabulary::_newElement(key, element);
}
void C::Attributes::_removeElement(const W::String& key)
{
C::Vocabulary::_removeElement(key);
Map::iterator itr = attributes->find(key);
C::Controller* ctrl = itr->second;
ctrl->setProperty("name", QString::fromStdString(key.toString()));
RMap::iterator ritr = reversed->find(ctrl);
removeController(ctrl);
attributes->erase(itr);
reversed->erase(ritr);
delete ctrl;
}
void C::Attributes::_clear()
{
C::Vocabulary::_clear();
Map::iterator itr = attributes->begin();
Map::iterator end = attributes->end();
for (; itr != end; ++itr) {
removeController(itr->second);
delete itr->second;
}
attributes->clear();
reversed->clear();
}
void C::Attributes::onAttrModification(const W::Object& data)
{
C::Controller* ctrl = static_cast<C::Controller*>(sender());
RMap::iterator ritr = reversed->find(ctrl);
emit attributeChange(ritr->second, data);
}
void C::Attributes::unsubscribe()
{
C::Controller::unsubscribe();
_clear();
}
void C::Attributes::onSocketDisconnected()
{
C::Controller::onSocketDisconnected();
dropSubscribed();
_clear();
}

View File

@ -0,0 +1,43 @@
#ifndef ATTRIBUTES_H
#define ATTRIBUTES_H
#include "vocabulary.h"
#include <map>
#include <wType/uint64.h>
namespace C {
class Attributes : public C::Vocabulary
{
Q_OBJECT
public:
Attributes(const W::Address& p_address, QObject* parent = 0);
~Attributes();
void unsubscribe();
signals:
void attributeChange(const W::String& atteName, const W::Object& value);
protected:
void _newElement(const W::String & key, const W::Object & element) override;
void _removeElement(const W::String & key) override;
void _clear() override;
protected slots:
void onAttrModification(const W::Object& data);
void onSocketDisconnected() override;
private:
typedef std::map<W::String, C::Controller*> Map;
typedef std::map<C::Controller*, W::String> RMap;
static uint64_t counter;
Map* attributes;
RMap* reversed;
};
}
#endif // ATTRIBUTES_H

View File

@ -0,0 +1,220 @@
#include "catalogue.h"
uint64_t C::Catalogue::counter = 0;
C::Catalogue::Catalogue(const W::Address p_address, QObject* parent):
C::Controller(p_address, W::Address({W::String(u"catalogue") += counter++}), parent),
order(),
hasSorting(false),
hasFilter(false),
hasData(true),
sorting(0),
filter(0)
{
W::Handler* get = W::Handler::create(address + W::Address({u"get"}), this, &C::Catalogue::_h_get);
W::Handler* addElement = W::Handler::create(address + W::Address({u"addElement"}), this, &C::Catalogue::_h_addElement);
W::Handler* removeElement = W::Handler::create(address + W::Address({u"removeElement"}), this, &C::Catalogue::_h_removeElement);
W::Handler* moveElement = W::Handler::create(address + W::Address({u"moveElement"}), this, &C::Catalogue::_h_moveElement);
W::Handler* clear = W::Handler::create(address + W::Address({u"clear"}), this, &C::Catalogue::_h_clear);
addHandler(get);
addHandler(addElement);
addHandler(removeElement);
addHandler(moveElement);
addHandler(clear);
}
C::Catalogue::~Catalogue()
{
if (hasFilter) {
delete filter;
}
if (hasSorting) {
delete sorting;
}
}
void C::Catalogue::setSorting(const W::String& field, bool ascending)
{
if (!hasSorting) {
sorting = new W::Vocabulary();
hasSorting = true;
}
sorting->insert(u"field", field);
sorting->insert(u"ascending", W::Boolean(ascending));
if (hasData) {
clearCatalogue();
}
if (subscribed) {
getData();
}
}
void C::Catalogue::clearSorting()
{
if (hasSorting) {
delete sorting;
hasSorting = false;
if (hasData) {
clearCatalogue();
}
if (subscribed) {
getData();
}
}
}
void C::Catalogue::addElement(const W::Uint64& id, const W::Uint64& before)
{
if (before == 0) {
order.push_back(id);
} else {
W::Order<uint64_t>::const_iterator pos = order.find(before);
order.insert(pos, id);
}
emit addedElement(id, before);
}
void C::Catalogue::h_get(const W::Event& ev)
{
if (hasData) {
clearCatalogue();
}
const W::Vocabulary& vc = static_cast<const W::Vocabulary&>(ev.getData());
const W::Vector& ord = static_cast<const W::Vector&>(vc.at(u"data"));
W::Vector::size_type size = ord.length();
for (uint64_t i = 0; i < size; ++i) {
const W::Uint64& id = static_cast<const W::Uint64&>(ord.at(i));
addElement(id);
}
hasData = true;
emit data();
}
void C::Catalogue::h_addElement(const W::Event& ev)
{
const W::Vocabulary& vc = static_cast<const W::Vocabulary&>(ev.getData());
const W::Uint64& id = static_cast<const W::Uint64&>(vc.at(u"id"));
if (vc.has(u"before")) {
const W::Uint64& before = static_cast<const W::Uint64&>(vc.at(u"before"));
addElement(id, before);
} else {
addElement(id);
}
}
void C::Catalogue::clearCatalogue()
{
order.clear();
hasData = false;
emit clear();
}
void C::Catalogue::h_clear(const W::Event& ev)
{
clearCatalogue();
}
void C::Catalogue::h_removeElement(const W::Event& ev)
{
const W::Vocabulary& vc = static_cast<const W::Vocabulary&>(ev.getData());
const W::Uint64& id = static_cast<const W::Uint64&>(vc.at(u"id"));
removeElement(id);
}
void C::Catalogue::removeElement(const W::Uint64& id)
{
W::Order<uint64_t>::const_iterator pos = order.find(id);
if (pos == order.end()) {
emit serviceMessage(QString("Recieved event to remove element with id ") + id.toString().c_str() + " but element under such id isn't present in catalogue, skipping");
return;
}
order.erase(id);
uint64_t pid;
emit removedElement(pid);
}
W::Vocabulary * C::Catalogue::createSubscriptionVC() const
{
W::Vocabulary* vc = C::Controller::createSubscriptionVC();
if (hasSorting) {
vc->insert(u"sorting", sorting->copy());
}
if (hasFilter) {
vc->insert(u"filter", filter->copy());
}
return vc;
}
void C::Catalogue::h_moveElement(const W::Event& ev)
{
const W::Vocabulary& vc = static_cast<const W::Vocabulary&>(ev.getData());
const W::Uint64& id = static_cast<const W::Uint64&>(vc.at(u"id"));
W::Order<uint64_t>::const_iterator pos = order.find(id);
if (pos == order.end()) {
emit serviceMessage(QString("Recieved event to move element with id ") + id.toString().c_str() + " but element under such id isn't present in catalogue, skipping");
return;
}
order.erase(id);
if (vc.has(u"before")) {
const W::Uint64& before = static_cast<const W::Uint64&>(vc.at(u"before"));
W::Order<uint64_t>::const_iterator beforePosition = order.find(before);
if (beforePosition == order.end()) {
emit serviceMessage(QString("Recieved event to move element with id ") +
id.toString().c_str() + " before element with id " + before.toString().c_str() +
" but element under id " + before.toString().c_str() +
" isn't present in catalogue, inserting to the end");
order.push_back(id);
emit movedElement(id);
return;
}
order.insert(beforePosition, id);
emit movedElement(id, before);
} else {
order.push_back(id);
emit movedElement(id);
}
}
void C::Catalogue::getData()
{
W::Vocabulary* vc = new W::Vocabulary();
vc->insert(u"params", createSubscriptionVC());
send(vc, W::Address{u"get"});
}
void C::Catalogue::addRemoteElement(const W::Vocabulary& element) const
{
send(static_cast<W::Vocabulary*>(element.copy()), W::Address{u"add"});
}
void C::Catalogue::updateRemoteElement(const W::Uint64& id, const W::Vocabulary& newValue) const
{
W::Vocabulary* vc = new W::Vocabulary();
vc->insert(u"id", id);
vc->insert(u"value", newValue);
send(vc, W::Address{u"update"});
}

View File

@ -0,0 +1,63 @@
#ifndef CATALOGUE_H
#define CATALOGUE_H
/**
* @todo write docs
*/
#include "controller.h"
#include <wType/address.h>
#include <wType/string.h>
#include <wType/vocabulary.h>
#include <wType/boolean.h>
#include <wContainer/order.h>
namespace C {
class Catalogue : public Controller {
Q_OBJECT
public:
Catalogue(const W::Address p_address, QObject* parent);
~Catalogue();
void setSorting(const W::String& field, bool ascending = true);
void clearSorting();
void addRemoteElement(const W::Vocabulary& element) const;
void updateRemoteElement(const W::Uint64& id, const W::Vocabulary& newValue) const;
signals:
void addedElement(uint64_t id, uint64_t before = 0);
void movedElement(uint64_t id, uint64_t before = 0);
void removedElement(uint64_t id);
void clear();
void data();
protected:
handler(get)
handler(addElement)
handler(removeElement)
handler(moveElement)
handler(clear)
virtual void addElement(const W::Uint64& id, const W::Uint64& before = W::Uint64(0));
virtual void clearCatalogue();
virtual void removeElement(const W::Uint64& id);
virtual void getData();
W::Vocabulary* createSubscriptionVC() const override;
protected:
W::Order<uint64_t> order;
private:
bool hasSorting;
bool hasFilter;
bool hasData;
W::Vocabulary* sorting;
W::Vocabulary* filter;
static uint64_t counter;
};
}
#endif // CATALOGUE_H

View File

@ -0,0 +1,136 @@
#include "collection.h"
C::Collection::Collection(const W::Address p_address, QObject* parent):
C::Catalogue(p_address, parent),
elements(),
waitingElements(),
hasData(false)
{
}
C::Collection::~Collection()
{
}
void C::Collection::addChildVocabulary(const W::Uint64& id)
{
C::Vocabulary* ctrl = new C::Vocabulary(pairAddress + id);
elements.insert(std::make_pair(id, ctrl));
waitingElements.insert(ctrl);
addController(ctrl);
connect(ctrl, SIGNAL(data()), this, SLOT(onChildVCData()));
if (hasData) {
hasData = false;
}
}
void C::Collection::addElement(const W::Uint64& id, const W::Uint64& before)
{
C::Catalogue::addElement(id, before);
addChildVocabulary(id);
}
void C::Collection::clearCatalogue()
{
C::Catalogue::clearCatalogue();
std::set<C::Vocabulary*>::const_iterator itr = waitingElements.begin();
std::set<C::Vocabulary*>::const_iterator end = waitingElements.end();
for (; itr != end; ++itr) {
disconnect(*itr, SIGNAL(data()), this, SLOT(onChildVCData()));
}
elements.clear();
waitingElements.clear();
cleanChildren();
}
void C::Collection::removeElement(const W::Uint64& id)
{
C::Catalogue::removeElement(id);
Elements::const_iterator itr = elements.find(id);
C::Vocabulary* ctrl = itr->second;
removeController(ctrl);
elements.erase(itr);
if (!hasData) {
std::set<C::Vocabulary*>::const_iterator witr = waitingElements.find(ctrl);
if (witr != waitingElements.end()) {
disconnect(ctrl, SIGNAL(data()), this, SLOT(onChildVCData()));
waitingElements.erase(witr);
if (waitingElements.size() == 0) {
hasData = true;
emit ready();
}
}
}
delete ctrl;
}
void C::Collection::h_get(const W::Event& ev)
{
hasData = false;
C::Catalogue::h_get(ev);
if (waitingElements.size() == 0) {
hasData = true;
emit ready();
}
}
void C::Collection::h_clear(const W::Event& ev)
{
C::Catalogue::h_clear(ev);
if (!hasData) {
hasData = true;
emit ready();
}
}
void C::Collection::onChildVCData()
{
C::Vocabulary* child = static_cast<C::Vocabulary*>(sender());
std::set<C::Vocabulary*>::const_iterator itr = waitingElements.find(child);
waitingElements.erase(itr);
disconnect(child, SIGNAL(data()), this, SLOT(onChildVCData()));
if (waitingElements.size() == 0) {
hasData = true;
emit ready();
}
}
std::set<uint64_t> C::Collection::find(const W::String& field, const W::Object& value)
{
if (!hasData) {
emit serviceMessage(QString("An attempt to look for record where ") + field.toString().c_str() + " == " + value.toString().c_str() + " in " + address.toString().c_str() + " but controller has no data yet");
throw 2;
}
std::set<uint64_t> response;
Elements::const_iterator itr = elements.begin();
Elements::const_iterator end = elements.end();
for (; itr != end; ++itr) {
if (itr->second->at(field) == value) {
response.insert(itr->first);
}
}
return response;
}
const C::Vocabulary & C::Collection::get(uint64_t id) const
{
return *(elements.find(id)->second);
}

View File

@ -0,0 +1,52 @@
#ifndef COLLECTION_H
#define COLLECTION_H
#include "catalogue.h"
#include "vocabulary.h"
#include <wType/address.h>
#include <map>
#include <set>
namespace C {
/**
* @todo write docs
*/
class Collection : public C::Catalogue {
Q_OBJECT
public:
Collection(const W::Address p_address, QObject* parent = 0);
~Collection();
std::set<uint64_t> find(const W::String& field, const W::Object& value);
const C::Vocabulary& get(uint64_t id) const;
signals:
void ready(); //emits when every VC received their data;
protected:
void addElement(const W::Uint64 & id, const W::Uint64 & before) override;
void clearCatalogue() override;
void removeElement(const W::Uint64 & id) override;
void h_get(const W::Event & ev) override;
void h_clear(const W::Event & ev) override;
private:
typedef std::map<uint64_t, C::Vocabulary*> Elements;
Elements elements;
std::set<C::Vocabulary*> waitingElements;
bool hasData;
void addChildVocabulary(const W::Uint64& id);
private slots:
void onChildVCData();
};
}
#endif // COLLECTION_H

View File

@ -0,0 +1,278 @@
#include "controller.h"
#include "controllerstring.h"
#include "list.h"
#include "vocabulary.h"
#include "attributes.h"
#include "catalogue.h"
C::Controller::Controller(const W::Address& p_address, const W::Address& my_address, QObject* parent):
QObject(parent),
pairAddress(p_address),
address(my_address),
subscribed(false),
dispatcher(0),
socket(0),
registered(false),
controllers(new CList()),
handlers(new HList()),
properties(new W::Vector())
{
W::Handler* props = W::Handler::create(address + W::Address({u"properties"}), this, &C::Controller::_h_properties);
addHandler(props);
}
C::Controller::~Controller()
{
if (subscribed) {
unsubscribe();
}
if (registered) {
unregisterController();
}
CList::iterator itr = controllers->begin();
CList::iterator end = controllers->end();
for (; itr != end; ++itr) {
delete *itr;
}
HList::iterator hItr = handlers->begin();
HList::iterator hEnd = handlers->end();
for (; hItr != hEnd; ++hItr) {
delete *hItr;
}
delete controllers;
delete handlers;
delete properties;
}
void C::Controller::addController(C::Controller* ctrl)
{
controllers->push_back(ctrl);
connect(ctrl, SIGNAL(serviceMessage(const QString&)), SIGNAL(serviceMessage(const QString&)));
if (registered) {
ctrl->registerController(dispatcher, socket);
}
if (subscribed && !ctrl->subscribed) {
ctrl->subscribe();
}
}
void C::Controller::addHandler(W::Handler* handler)
{
handlers->push_back(handler);
if (registered) {
dispatcher->registerHandler(handler);
}
}
void C::Controller::removeHandler(W::Handler* handler)
{
handlers->erase(handler);
if (registered) {
dispatcher->unregisterHandler(handler);
}
}
void C::Controller::removeController(C::Controller* ctrl)
{
if (subscribed && !ctrl->subscribed) {
ctrl->unsubscribe();
}
if (registered) {
ctrl->unregisterController();
}
disconnect(ctrl, SIGNAL(serviceMessage(const QString&)), this, SIGNAL(serviceMessage(const QString&)));
controllers->erase(ctrl);
}
void C::Controller::h_properties(const W::Event& event)
{
delete properties;
const W::Vocabulary& vc = static_cast<const W::Vocabulary&>(event.getData());
properties = static_cast<W::Vector*>(vc.at(u"properties").copy());
//emit serviceMessage("successfully received properties");
}
void C::Controller::registerController(W::Dispatcher* dp, const W::Socket* sock)
{
if (registered) {
emit serviceMessage(QString("Controller ") + address.toString().c_str() + " is already registered");
throw 1;
} else {
dispatcher = dp;
socket = sock;
connect(socket, SIGNAL(disconnected()), this, SLOT(onSocketDisconnected()));
CList::iterator itr = controllers->begin();
CList::iterator end = controllers->end();
for (; itr != end; ++itr) {
C::Controller* ctrl = *itr;
ctrl->registerController(dispatcher, socket);
}
HList::iterator hItr = handlers->begin();
HList::iterator hEnd = handlers->end();
for (; hItr != hEnd; ++hItr) {
W::Handler* handler = *hItr;
dispatcher->registerHandler(handler);
}
registered = true;
}
}
void C::Controller::unregisterController()
{
if (!registered) {
emit serviceMessage(QString("Controller ") + address.toString().c_str() + " is already unregistered");
throw 2;
} else {
CList::iterator itr = controllers->begin();
CList::iterator end = controllers->end();
for (; itr != end; ++itr) {
Controller* ctrl = *itr;
ctrl->unregisterController();
}
HList::iterator hItr = handlers->begin();
HList::iterator hEnd = handlers->end();
for (; hItr != hEnd; ++hItr) {
W::Handler* handler = *hItr;
dispatcher->unregisterHandler(handler);
}
disconnect(socket, SIGNAL(disconnected()), this, SLOT(onSocketDisconnected()));
dispatcher = 0;
socket = 0;
registered = false;
}
}
void C::Controller::send(W::Vocabulary* vc, const W::Address& handlerAddress) const
{
if (!registered) {
emit serviceMessage(QString("An attempt to send event from model ") + address.toString().c_str() + " which was not registered");
throw 3;
}
vc->insert(u"source", address);
W::Event ev(pairAddress + handlerAddress, vc);
ev.setSenderId(socket->getId());
socket->send(ev);
}
void C::Controller::subscribe()
{
if (subscribed) {
emit serviceMessage(QString("An attempt to subscribe model ") + address.toString().c_str() + " which is already subscribed");
throw 3;
}
W::Vocabulary* vc = new W::Vocabulary();
vc->insert(u"params", createSubscriptionVC());
send(vc, W::Address{u"subscribe"});
CList::iterator itr = controllers->begin();
CList::iterator end = controllers->end();
for (; itr != end; ++itr) {
(*itr)->subscribe();
}
subscribed = true;
}
void C::Controller::unsubscribe()
{
if (!subscribed) {
emit serviceMessage(QString("An attempt to unsubscribe model ") + address.toString().c_str() + " which not subscribed");
throw 3;
}
send(new W::Vocabulary(), W::Address{u"unsubscribe"});
CList::iterator itr = controllers->begin();
CList::iterator end = controllers->end();
for (; itr != end; ++itr) {
(*itr)->unsubscribe();
}
subscribed = false;
}
void C::Controller::onSocketDisconnected()
{
subscribed = false;
}
C::Controller * C::Controller::createByType(int type, const W::Address& address, QObject* parent)
{
C::Controller* ptr;
switch (type) {
case string:
ptr = new C::String(address, parent);
break;
case list:
ptr = new C::List(address, parent);
break;
case vocabulary:
ptr = new C::Vocabulary(address, parent);
break;
case catalogue:
ptr = new C::Catalogue(address, parent);
break;
case attributes:
ptr = new C::Attributes(address, parent);
break;
default:
throw 1;
}
return ptr;
}
void C::Controller::dropSubscribed()
{
subscribed = false;
CList::iterator itr = controllers->begin();
CList::iterator end = controllers->end();
for (; itr != end; ++itr) {
(*itr)->dropSubscribed();
}
}
W::Vocabulary * C::Controller::createSubscriptionVC() const
{
return new W::Vocabulary();
}
void C::Controller::cleanChildren()
{
CList::const_iterator beg = controllers->begin();
CList::const_iterator end = controllers->end();
while (beg != end) {
C::Controller* ctrl = *beg;
removeController(ctrl);
delete ctrl;
beg = controllers->begin();
}
}
bool C::Controller::isSubscribed()
{
return subscribed;
}

View File

@ -0,0 +1,82 @@
#ifndef CONTROLLER_H
#define CONTROLLER_H
#include <utils/defines.h>
#include <QtCore/QObject>
#include <QtCore/QString>
#include <wType/address.h>
#include <wType/event.h>
#include <wType/vector.h>
#include <wType/vocabulary.h>
#include <wType/string.h>
#include <wDispatcher/dispatcher.h>
#include <wDispatcher/handler.h>
#include <wSocket/socket.h>
#include <wContainer/order.h>
namespace C {
class Controller : public QObject
{
Q_OBJECT
public:
enum ModelType {
string,
list,
vocabulary,
catalogue,
attributes = 50
};
Controller(const W::Address& p_address, const W::Address& my_address, QObject* parent = 0);
virtual ~Controller();
void addController(C::Controller* ctrl);
void addHandler(W::Handler* handler);
void registerController(W::Dispatcher* dp, const W::Socket* sock);
void unregisterController();
void subscribe();
void unsubscribe();
bool isSubscribed();
void removeHandler(W::Handler* handler);
void removeController(C::Controller* ctrl);
static C::Controller* createByType(int type, const W::Address& address, QObject* parent = 0);
signals:
void serviceMessage(const QString& msg) const;
void modification(const W::Object& data);
protected:
W::Address pairAddress;
W::Address address;
bool subscribed;
void send(W::Vocabulary* vc, const W::Address& handlerAddress) const;
handler(properties)
void dropSubscribed();
virtual W::Vocabulary* createSubscriptionVC() const;
void cleanChildren();
private:
typedef W::Order<W::Handler*> HList;
typedef W::Order<C::Controller*> CList;
W::Dispatcher* dispatcher;
const W::Socket* socket;
bool registered;
CList* controllers;
HList* handlers;
W::Vector* properties;
protected slots:
virtual void onSocketDisconnected();
};
}
#endif // CONTROLLER_H

View File

@ -0,0 +1,25 @@
#include "controllerstring.h"
uint64_t C::String::counter = 0;
C::String::String(const W::Address p_address, QObject* parent):
C::Controller(p_address, W::Address({W::String(u"string") += counter++}), parent),
data(u"")
{
W::Handler* get = W::Handler::create(address + W::Address({u"get"}), this, &C::String::_h_get);
addHandler(get);
}
C::String::~String()
{
}
void C::String::h_get(const W::Event& ev)
{
const W::Vocabulary& vc = static_cast<const W::Vocabulary&>(ev.getData());
data = static_cast<const W::String&>(vc.at(u"data"));
emit change(QString(data.toString().c_str()));
emit modification(data);
}

View File

@ -0,0 +1,33 @@
#ifndef CONTROLLER_STRING_H
#define CONTROLLER_STRING_H
#include "controller.h"
#include <wType/string.h>
#include <wType/vocabulary.h>
#include <wType/event.h>
#include <QtCore/QString>
namespace C {
class String : public C::Controller
{
Q_OBJECT
public:
String(const W::Address p_address, QObject* parent = 0);
~String();
signals:
void change(const QString& str);
protected:
W::String data;
handler(get)
private:
static uint64_t counter;
};
}
#endif // CONTROLLER_STRING_H

57
lib/wController/list.cpp Normal file
View File

@ -0,0 +1,57 @@
#include "list.h"
uint64_t C::List::counter = 0;
C::List::List(const W::Address p_address, QObject* parent):
C::Controller(p_address, W::Address({W::String(u"list") += counter++}), parent),
data(new W::Vector())
{
W::Handler* get = W::Handler::create(address + W::Address({u"get"}), this, &C::List::_h_get);
W::Handler* push = W::Handler::create(address + W::Address({u"push"}), this, &C::List::_h_push);
W::Handler* clear = W::Handler::create(address + W::Address({u"clear"}), this, &C::List::_h_clear);
addHandler(get);
addHandler(push);
addHandler(clear);
}
C::List::~List()
{
delete data;
}
void C::List::h_get(const W::Event& ev)
{
emit clear();
data->clear();
const W::Vocabulary& vc = static_cast<const W::Vocabulary&>(ev.getData());
const W::Vector& edata = static_cast<const W::Vector&>(vc.at(u"data"));
int size = edata.size();
for (int i = 0; i < size; ++i) {
data->push(edata.at(i));
emit newElement(edata.at(i));
}
emit modification(*data);
}
void C::List::h_push(const W::Event& ev)
{
const W::Vocabulary& vc = static_cast<const W::Vocabulary&>(ev.getData());
const W::Object& el = vc.at(u"data");
data->push(el);
emit newElement(el);
emit modification(*data);
}
void C::List::h_clear(const W::Event& ev)
{
emit clear();
data->clear();
emit modification(*data);
}

36
lib/wController/list.h Normal file
View File

@ -0,0 +1,36 @@
#ifndef CONTROLLER_LIST_H
#define CONTROLLER_LIST_H
#include "controller.h"
#include <wType/address.h>
#include <wType/object.h>
#include <wType/event.h>
#include <wType/vector.h>
namespace C {
class List : public C::Controller
{
Q_OBJECT
public:
List(const W::Address p_address, QObject* parent);
~List();
signals:
void clear();
void newElement(const W::Object& element);
protected:
W::Vector* data;
handler(get)
handler(push)
handler(clear)
private:
static uint64_t counter;
};
}
#endif // CONTROLLER_LIST_H

View File

@ -0,0 +1,119 @@
#include "vocabulary.h"
uint64_t C::Vocabulary::counter = 0;
C::Vocabulary::Vocabulary(const W::Address p_address, QObject* parent):
C::Controller(p_address, W::Address({W::String(u"vocabulary") += counter++}), parent),
p_data(new W::Vocabulary())
{
W::Handler* get = W::Handler::create(address + W::Address({u"get"}), this, &C::Vocabulary::_h_get);
W::Handler* change = W::Handler::create(address + W::Address({u"change"}), this, &C::Vocabulary::_h_change);
W::Handler* clear = W::Handler::create(address + W::Address({u"clear"}), this, &C::Vocabulary::_h_clear);
addHandler(get);
addHandler(change);
addHandler(clear);
}
C::Vocabulary::~Vocabulary()
{
delete p_data;
}
C::Vocabulary::Vocabulary(const W::Address p_address, const W::Address& my_address, QObject* parent):
C::Controller(p_address, my_address, parent),
p_data(new W::Vocabulary())
{
W::Handler* get = W::Handler::create(address + W::Address({u"get"}), this, &C::Vocabulary::_h_get);
W::Handler* change = W::Handler::create(address + W::Address({u"change"}), this, &C::Vocabulary::_h_change);
W::Handler* clear = W::Handler::create(address + W::Address({u"clear"}), this, &C::Vocabulary::_h_clear);
addHandler(get);
addHandler(change);
addHandler(clear);
}
void C::Vocabulary::h_get(const W::Event& ev)
{
_clear();
const W::Vocabulary& vc = static_cast<const W::Vocabulary&>(ev.getData());
const W::Vocabulary& e_data = static_cast<const W::Vocabulary&>(vc.at(u"data"));
W::Vector keys = e_data.keys();
int size = keys.length();
for (int i = 0; i < size; ++i) {
const W::String& key = static_cast<const W::String&>(keys.at(i));
_newElement(key, e_data.at(key));
}
emit modification(*p_data);
emit data();
}
void C::Vocabulary::h_change(const W::Event& ev)
{
const W::Vocabulary& vc = static_cast<const W::Vocabulary&>(ev.getData());
const W::Vector& erase = static_cast<const W::Vector&>(vc.at(u"erase"));
const W::Vocabulary& insert = static_cast<const W::Vocabulary&>(vc.at(u"insert"));
int eSize = erase.length();
for (int i = 0; i < eSize; ++i) {
const W::String& key = static_cast<const W::String&>(erase.at(i));
_removeElement(key);
}
W::Vector keys = insert.keys();
int iSize = keys.length();
for (int i = 0; i < iSize; ++i) {
const W::String& key = static_cast<const W::String&>(keys.at(i));
_newElement(key, insert.at(key));
}
emit modification(*p_data);
}
void C::Vocabulary::h_clear(const W::Event& ev)
{
_clear();
emit modification(*p_data);
}
void C::Vocabulary::_newElement(const W::String& key, const W::Object& element)
{
p_data->insert(key, element);
emit newElement(key, element);
}
void C::Vocabulary::_removeElement(const W::String& key)
{
emit removeElement(key);
p_data->erase(key);
}
void C::Vocabulary::_clear()
{
emit clear();
p_data->clear();
}
const W::Object & C::Vocabulary::at(const W::String& key) const
{
return p_data->at(key);
}
const W::Object & C::Vocabulary::at(const W::String::u16string& key) const
{
return p_data->at(key);
}
bool C::Vocabulary::has(const W::String& key) const
{
return p_data->has(key);
}
bool C::Vocabulary::has(const W::String::u16string& key) const
{
return p_data->has(key);
}

View File

@ -0,0 +1,49 @@
#ifndef CONTROLLER_VOCABULARY_H
#define CONTROLLER_VOCABULARY_H
#include "controller.h"
#include <wType/address.h>
#include <wType/object.h>
#include <wType/event.h>
#include <wType/vector.h>
#include <wType/vocabulary.h>
namespace C {
class Vocabulary : public C::Controller
{
Q_OBJECT
protected:
Vocabulary(const W::Address p_address, const W::Address& my_address, QObject* parent = 0); //for inheritors
public:
Vocabulary(const W::Address p_address, QObject* parent = 0);
~Vocabulary();
const W::Object& at(const W::String& key) const;
const W::Object& at(const W::String::u16string& key) const;
bool has(const W::String& key) const;
bool has(const W::String::u16string& key) const;
signals:
void clear();
void newElement(const W::String& key, const W::Object& element);
void removeElement(const W::String& key);
void data();
protected:
W::Vocabulary* p_data;
handler(get)
handler(change)
handler(clear)
virtual void _newElement(const W::String& key, const W::Object& element);
virtual void _removeElement(const W::String& key);
virtual void _clear();
private:
static uint64_t counter;
};
}
#endif // VOCABULARY_H

View File

@ -0,0 +1,25 @@
cmake_minimum_required(VERSION 2.8.12)
project(database)
find_package(Qt5Core REQUIRED)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOMOC ON)
set(HEADERS
database.h
resourcecache.h
)
set(SOURCES
database.cpp
resourcecache.cpp
)
add_library(wDatabase STATIC ${HEADERS} ${SOURCES})
target_link_libraries(wDatabase Qt5::Core)
target_link_libraries(wDatabase lmdb)
target_link_libraries(wDatabase wType)
target_link_libraries(wDatabase wModel)

230
lib/wDatabase/database.cpp Normal file
View File

@ -0,0 +1,230 @@
#include "database.h"
#include <sys/stat.h>
#include <sys/types.h>
#include <wType/bytearray.h>
#include <algorithm>
Database::Database(const W::String& dbName, QObject* parent):
M::ICatalogue(W::Address({dbName}), parent),
name(dbName),
opened(false),
environment(lmdb::env::create()),
dbi(0),
elements()
{
}
Database::~Database()
{
}
void Database::open()
{
if (!opened) {
checkDirAndOpenEnvironment();
index();
opened = true;
}
}
void Database::addIndex(const W::String& fieldName, W::Object::objectType fieldType)
{
ICatalogue::addIndex(fieldName, fieldType);
if (opened) {
lmdb::txn rtxn = lmdb::txn::begin(environment, nullptr, MDB_RDONLY);
lmdb::cursor cursor = lmdb::cursor::open(rtxn, dbi);
lmdb::val key;
lmdb::val value;
while (cursor.get(key, value, MDB_NEXT)) {
uint64_t iKey = *((uint64_t*) key.data());
W::ByteArray ba(value.size());
ba.fill(value.data(), value.size());
W::Vocabulary* wVal = static_cast<W::Vocabulary*>(W::Object::fromByteArray(ba));
IndexMap::const_iterator itr = indexes.find(fieldName);
itr->second->add(wVal->at(itr->first), iKey);
delete wVal;
}
cursor.close();
rtxn.abort();
}
}
uint64_t Database::addElement(const W::Vocabulary& record)
{
if (!opened) {
throw 6; //TODO
}
uint64_t id = ICatalogue::addElement(record);
elements.insert(id);
int size = record.size();
W::ByteArray ba(size + 1);
ba.push8(record.getType());
record.serialize(ba);
lmdb::val key((uint8_t*) &id, 8);
lmdb::val value(ba.getData(), ba.size());
lmdb::txn wTrans = lmdb::txn::begin(environment);
dbi.put(wTrans, key, value);
wTrans.commit();
return id;
}
void Database::checkDirAndOpenEnvironment()
{
int state1 = mkdir("./db", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
if (state1 != 0 && errno != EEXIST) {
emit serviceMessage("Failed to create a root database folder");
throw 1;
}
W::String path("./db/");
path += name;
int state2 = mkdir(path.toString().c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
if (state2 != 0 && errno != EEXIST) {
emit serviceMessage(QString("Failed to create ") + name.toString().c_str() + " database folder");
throw 1;
}
environment.set_mapsize(1UL * 1024UL * 1024UL * 1024UL);
environment.set_max_dbs(10);
environment.open(path.toString().c_str(), 0, 0664);
lmdb::txn wTrans = lmdb::txn::begin(environment);
dbi = lmdb::dbi::open(wTrans, "main", MDB_CREATE | MDB_INTEGERKEY);
wTrans.commit();
}
void Database::index()
{
lmdb::txn rtxn = lmdb::txn::begin(environment, nullptr, MDB_RDONLY);
lmdb::cursor cursor = lmdb::cursor::open(rtxn, dbi);
lmdb::val key;
lmdb::val value;
while (cursor.get(key, value, MDB_NEXT)) {
uint64_t iKey = *((uint64_t*) key.data());
W::ByteArray ba(value.size());
ba.fill(value.data(), value.size());
W::Vocabulary* wVal = static_cast<W::Vocabulary*>(W::Object::fromByteArray(ba));
ICatalogue::addElement(*wVal);
elements.insert(iKey);
delete wVal;
}
cursor.close();
rtxn.abort();
emit countChange(elements.size());
}
W::Vocabulary* Database::getElement(uint64_t id)
{
lmdb::txn rtxn = lmdb::txn::begin(environment, nullptr, MDB_RDONLY);
lmdb::val key((uint8_t*) &id, 8);
lmdb::val value;
if (dbi.get(rtxn, key, value)) {
W::ByteArray ba(value.size());
ba.fill(value.data(), value.size());
W::Vocabulary* wVal = static_cast<W::Vocabulary*>(W::Object::fromByteArray(ba));
rtxn.abort();
return wVal;
} else {
rtxn.abort();
throw 3;
}
}
std::set<uint64_t> Database::getAll() const
{
return elements;
}
void Database::removeElement(uint64_t id)
{
if (!opened) {
throw 6; //TODO
}
ICatalogue::removeElement(id);
lmdb::txn transaction = lmdb::txn::begin(environment);
lmdb::val key((uint8_t*) &id, 8);
dbi.del(transaction, key);
transaction.commit();
elements.erase(id);
}
void Database::clear()
{
if (!opened) {
throw 6; //TODO
}
M::ICatalogue::clear();
lmdb::txn transaction = lmdb::txn::begin(environment);
dbi.drop(transaction);
transaction.commit();
}
void Database::addModel(M::Model* model)
{
connect(model, SIGNAL(subscribersCountChange(uint64_t)), this, SLOT(onChildSubscribersCountChange(uint64_t)));
M::ICatalogue::addModel(model);
}
void Database::removeModel(M::Model* model)
{
disconnect(model, SIGNAL(subscribersCountChange(uint64_t)), this, SLOT(onChildSubscribersCountChange(uint64_t)));
M::ICatalogue::removeModel(model);
}
void Database::onChildSubscribersCountChange(uint64_t count)
{
if (count == 0) {
M::Model* model = static_cast<M::Model*>(sender());
removeModel(model);
emit serviceMessage(QString("Unregistered model ") + model->getAddress().toString().c_str() + " because there no subscribers left");
model->deleteLater();
}
}
void Database::modifyElement(uint64_t id, const W::Vocabulary& newValue)
{
if (!opened) {
throw 6; //TODO
}
int size = newValue.size();
W::ByteArray ba(size + 1);
ba.push8(newValue.getType());
newValue.serialize(ba);
lmdb::val key((uint8_t*) &id, 8);
lmdb::val value(ba.getData(), ba.size());
lmdb::txn wTrans = lmdb::txn::begin(environment);
dbi.put(wTrans, key, value);
wTrans.commit();
}
uint64_t Database::size() const
{
return elements.size();
}

56
lib/wDatabase/database.h Normal file
View File

@ -0,0 +1,56 @@
#ifndef DATABASE_H
#define DATABASE_H
#include "lmdb++.h"
#include <map>
#include <set>
#include <wModel/icatalogue.h>
#include <wType/string.h>
#include <wType/address.h>
class Database: public M::ICatalogue
{
Q_OBJECT
class AbstractIndex;
public:
Database(const W::String& dbName, QObject* parent = 0);
~Database();
void open();
uint64_t addElement(const W::Vocabulary& record) override;
W::Vocabulary* getElement(uint64_t id) override;
void removeElement(uint64_t id) override;
void clear() override;
void modifyElement(uint64_t id, const W::Vocabulary & newValue) override;
uint64_t size() const override;
void addIndex(const W::String& fieldName, W::Object::objectType fieldType) override;
void addModel(M::Model* model);
void removeModel(M::Model* model);
protected:
std::set<uint64_t> getAll() const override;
private:
void checkDirAndOpenEnvironment();
void index();
private slots:
void onChildSubscribersCountChange(uint64_t count);
public:
const W::String name;
private:
bool opened;
lmdb::env environment;
lmdb::dbi dbi;
std::set<uint64_t> elements;
};
#endif // DATABASE_H

1913
lib/wDatabase/lmdb++.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,277 @@
#include "resourcecache.h"
#include <sys/stat.h>
#include <sys/types.h>
#include <fstream>
ResourceCache::ResourceCache(const W::String& dbName, QObject* parent):
M::Model(W::Address{dbName}, parent),
subscribeMember(W::Handler::create(W::Address({}), this, &ResourceCache::_h_subscribeMember)),
name(dbName),
opened(false),
environment(lmdb::env::create()),
dbi(0),
elements(),
lastIndex(0)
{
W::Handler* get = W::Handler::create(address + W::Address({u"get"}), this, &ResourceCache::_h_get);
addHandler(get);
}
ResourceCache::~ResourceCache()
{
delete subscribeMember;
}
void ResourceCache::open()
{
if (!opened) {
checkDirAndOpenEnvironment();
lmdb::txn rtxn = lmdb::txn::begin(environment, nullptr, MDB_RDONLY);
lmdb::cursor cursor = lmdb::cursor::open(rtxn, dbi);
lmdb::val key;
lmdb::val value;
while (cursor.get(key, value, MDB_NEXT)) {
uint64_t iKey = *((uint64_t*) key.data());
elements.insert(iKey);
if (iKey > lastIndex) {
lastIndex = iKey;
}
}
cursor.close();
rtxn.abort();
opened = true;
emit countChange(elements.size());
}
}
void ResourceCache::checkDirAndOpenEnvironment()
{
int state1 = mkdir("./dbMedia", S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
if (state1 != 0 && errno != EEXIST) {
emit serviceMessage("Failed to create a root database folder");
throw 1;
}
W::String path("./dbMedia/");
path += name;
int state2 = mkdir(path.toString().c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
if (state2 != 0 && errno != EEXIST) {
emit serviceMessage(QString("Failed to create ") + name.toString().c_str() + " database folder");
throw 1;
}
environment.set_mapsize(1UL * 1024UL * 1024UL * 1024UL);
environment.set_max_dbs(10);
environment.open(path.toString().c_str(), 0, 0664);
lmdb::txn wTrans = lmdb::txn::begin(environment);
dbi = lmdb::dbi::open(wTrans, "main", MDB_CREATE | MDB_INTEGERKEY);
wTrans.commit();
}
uint64_t ResourceCache::addResource(const W::String& path)
{
uint64_t id = ++lastIndex;
elements.insert(id);
W::ByteArray ba(path.size() + 1);
ba.push8(path.getType());
path.serialize(ba);
lmdb::val key((uint8_t*) &id, 8);
lmdb::val value(ba.getData(), ba.size());
lmdb::txn wTrans = lmdb::txn::begin(environment);
dbi.put(wTrans, key, value);
wTrans.commit();
W::Vocabulary* vc = new W::Vocabulary();
vc->insert(u"id", new W::Uint64(id));
broadcast(vc, W::Address({u"addElement"}));
emit countChange(elements.size());
return id;
}
void ResourceCache::removeResource(uint64_t id)
{
if (!opened) {
throw ClosedDB("removeResource");
}
lmdb::txn transaction = lmdb::txn::begin(environment);
lmdb::val key((uint8_t*) &id, 8);
dbi.del(transaction, key);
transaction.commit();
elements.erase(id);
W::Vocabulary* vc = new W::Vocabulary();
vc->insert(u"id", new W::Uint64(id));
broadcast(vc, W::Address({u"removeElement"}));
emit countChange(elements.size());
//TODO not sure, may be it's better to also destroy resource model?
}
void ResourceCache::clear()
{
if (!opened) {
throw new ClosedDB("clear");
}
lmdb::txn transaction = lmdb::txn::begin(environment);
dbi.drop(transaction);
transaction.commit();
elements.clear();
lastIndex = 0;
W::Vocabulary* vc = new W::Vocabulary();
broadcast(vc, W::Address({u"clear"}));
emit countChange(elements.size());
}
void ResourceCache::h_get(const W::Event& ev)
{
W::Vector* order = new W::Vector();
std::set<uint64_t>::const_iterator itr = elements.begin();
std::set<uint64_t>::const_iterator end = elements.end();
for (; itr != end; ++itr) {
order->push(W::Uint64(*itr));
}
W::Vocabulary* rvc = new W::Vocabulary;
rvc->insert(u"data", order);
response(rvc, W::Address({u"get"}), ev);
}
M::Model::ModelType ResourceCache::getType() const
{
return type;
}
void ResourceCache::set(W::Object* value)
{
set(*value);
}
void ResourceCache::set(const W::Object& value)
{
throw 14; //what do you expect here? not implemented, and not sure it ever would be
}
void ResourceCache::h_subscribe(const W::Event& ev)
{
M::Model::h_subscribe(ev);
h_get(ev);
}
W::String * ResourceCache::getElement(uint64_t id) const
{
lmdb::txn rtxn = lmdb::txn::begin(environment, nullptr, MDB_RDONLY);
lmdb::val key((uint8_t*) &id, 8);
lmdb::val value;
if (lmdb::dbi_get(rtxn, dbi.handle(), key, value)) {
W::ByteArray ba(value.size());
ba.fill(value.data(), value.size());
W::String* wVal = static_cast<W::String*>(W::Object::fromByteArray(ba));
rtxn.abort();
return wVal;
} else {
rtxn.abort();
throw 3;
}
}
void ResourceCache::h_subscribeMember(const W::Event& ev)
{
const W::Address& addr = ev.getDestination();
W::Address lastHops = addr << address.length();
if (lastHops.length() == 2 && (lastHops.ends(W::Address{u"subscribe"})
|| lastHops.ends(W::Address{u"get"})
|| lastHops.ends(W::Address{u"getAdditional"}))
) {
W::String* record;
try {
record = getElement(lastHops.front().toUint64());
M::File* modelRecord = M::File::create(readFile(*record), address + lastHops >> 1);
delete record;
addModel(modelRecord);
passToHandler(ev);
} catch (int err) {
if (err == 3) {
emit serviceMessage(QString("An attempt to create and subscribe record model in resourcecache, but it is not found. Event: ") + ev.toString().c_str());
} else if (err == 10) {
serviceMessage(QString("Can't open file ") + record->toString().c_str());
delete record;
} else {
throw err;
}
} catch (const std::invalid_argument& err) {
emit serviceMessage(QString("Strange event in custom handler of resourcecache ") + ev.toString().c_str());
}
} else {
emit serviceMessage(QString("Strange event in custom handler of resourcecache ") + ev.toString().c_str());
}
}
W::Blob * ResourceCache::readFile(const W::String& path) const
{
std::ifstream file (path.toString(), std::ios::in|std::ios::binary|std::ios::ate);
if (file.is_open()) {
char * memblock;
uint32_t size;
size = file.tellg();
file.seekg(0, std::ios::beg);
memblock = new char[size];
file.read(memblock, size);
file.close();
return new W::Blob(size, memblock);
} else {
throw 10; //TODO
}
}
std::set<uint64_t> ResourceCache::getAllIdentificators() const
{
if (!opened) {
throw new ClosedDB("getAllIdentificators");
}
return elements;
}
W::String ResourceCache::getPath(uint64_t id) const
{
return *(getElement(id));
}
bool ResourceCache::has(uint64_t id) const
{
if (!opened) {
throw new ClosedDB("has");
}
return elements.find(id) != elements.end();
}

View File

@ -0,0 +1,84 @@
#ifndef RESOURCECACHE_H
#define RESOURCECACHE_H
#include <set>
#include <map>
#include "lmdb++.h"
#include <wModel/model.h>
#include <wModel/file/file.h>
#include <wType/address.h>
#include <wType/string.h>
#include <wType/bytearray.h>
#include <wType/uint64.h>
#include <wType/blob.h>
#include <utils/exception.h>
class ResourceCache : public M::Model
{
Q_OBJECT
public:
ResourceCache(const W::String& dbName, QObject* parent = 0);
~ResourceCache();
void open();
uint64_t addResource(const W::String& path);
void removeResource(uint64_t id);
void clear();
bool has(uint64_t id) const;
W::String getPath(uint64_t id) const;
std::set<uint64_t> getAllIdentificators() const;
W::Handler* subscribeMember;
handler(subscribeMember);
M::Model::ModelType getType() const override;
static const M::Model::ModelType type = resourceCache;
void set(const W::Object & value) override;
void set(W::Object * value) override;
signals:
void countChange(uint64_t count);
protected:
void h_subscribe(const W::Event & ev) override;
handler(get);
W::String* getElement(uint64_t id) const;
public:
const W::String name;
private:
bool opened;
lmdb::env environment;
lmdb::dbi dbi;
std::set<uint64_t> elements;
uint64_t lastIndex;
void checkDirAndOpenEnvironment();
W::Blob* readFile(const W::String& path) const;
class ClosedDB:
public Utils::Exception
{
public:
ClosedDB(const std::string& p_op):Exception(), operation(p_op){}
std::string getMessage() const {
std::string str = "An attempt to perform method ResourceCache::";
str += operation;
str += " but the database is not open";
return str;
}
private:
std::string operation;
};
};
#endif // RESOURCECACHE_H

View File

@ -0,0 +1,27 @@
cmake_minimum_required(VERSION 2.8.12)
project(wDispatcher)
find_package(Qt5Core REQUIRED)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOMOC ON)
set(HEADERS
dispatcher.h
handler.h
defaulthandler.h
logger.h
parentreporter.h
)
set(SOURCES
dispatcher.cpp
handler.cpp
defaulthandler.cpp
logger.cpp
parentreporter.cpp
)
add_library(wDispatcher ${HEADERS} ${SOURCES})
target_link_libraries(wDispatcher Qt5::Core)

View File

@ -0,0 +1,11 @@
#include "defaulthandler.h"
W::DefaultHandler::DefaultHandler()
{
}
W::DefaultHandler::~DefaultHandler()
{
}

View File

@ -0,0 +1,18 @@
#ifndef DEFAULTHANDLER_H
#define DEFAULTHANDLER_H
#include <wType/event.h>
namespace W
{
class DefaultHandler
{
public:
DefaultHandler();
virtual ~DefaultHandler();
virtual bool call(const W::Event& ev) const = 0;
};
}
#endif // DEFAULTHANDLER_H

View File

@ -0,0 +1,84 @@
#include "dispatcher.h"
W::Dispatcher::Dispatcher()
{}
W::Dispatcher::~Dispatcher()
{}
void W::Dispatcher::pass(const W::Event& ev) const
{
n_map::const_iterator itr;
itr = nodes.find(ev.getDestination());
if (itr != nodes.end())
{
W::Order<W::Handler*>::const_iterator beg = itr->second.begin();
W::Order<W::Handler*>::const_iterator end = itr->second.end();
std::list<W::Handler*> list(beg, end);
std::list<W::Handler*>::const_iterator itr = list.begin();
std::list<W::Handler*>::const_iterator tEnd = list.end();
for (; itr != tEnd; ++itr) {
(*itr)->pass(ev);
}
}
else
{
d_order::const_iterator itr = defaultHandlers.begin();
d_order::const_iterator end = defaultHandlers.end();
for (; itr != end; ++itr)
{
if ((*itr)->call(ev)){
break;
}
}
}
}
void W::Dispatcher::registerHandler(W::Handler* dp)
{
n_map::iterator itr = nodes.find(dp->getAddress());
if (itr == nodes.end()) {
W::Order<W::Handler*> ord;
itr = nodes.insert(std::make_pair(dp->getAddress(), ord)).first;
}
itr->second.push_back(dp);
}
void W::Dispatcher::unregisterHandler(W::Handler* dp)
{
n_map::iterator itr = nodes.find(dp->getAddress());
if (itr != nodes.end())
{
W::Order<W::Handler*>::const_iterator o_itr = itr->second.find(dp);
if (o_itr != itr->second.end()) {
itr->second.erase(dp);
if (itr->second.size() == 0) {
nodes.erase(itr);
}
}
else
{
throw 5;//TODO exception;
}
}
else
{
throw 5;//TODO exception;
}
}
void W::Dispatcher::registerDefaultHandler(W::DefaultHandler* dh)
{
defaultHandlers.push_back(dh);
}
void W::Dispatcher::unregisterDefaultHandler(W::DefaultHandler* dh)
{
defaultHandlers.erase(dh);
}

View File

@ -0,0 +1,48 @@
#ifndef DISPATCHER_H
#define DISPATCHER_H
#include <map>
#include <list>
#include <wType/string.h>
#include <wType/address.h>
#include <wType/event.h>
#include <QtCore/QObject>
#include "handler.h"
#include "defaulthandler.h"
#include <wContainer/order.h>
namespace W
{
class Dispatcher:
public QObject
{
Q_OBJECT
public:
Dispatcher();
~Dispatcher();
void registerHandler(W::Handler* dp);
void unregisterHandler(W::Handler* dp);
void registerDefaultHandler(W::DefaultHandler* dh);
void unregisterDefaultHandler(W::DefaultHandler* dh);
public slots:
void pass(const W::Event& ev) const;
protected:
typedef std::map<W::Address, W::Order<W::Handler*>> n_map;
typedef W::Order<W::DefaultHandler*> d_order;
n_map nodes;
d_order defaultHandlers;
};
}
#endif // DISPATCHER_H

View File

@ -0,0 +1,17 @@
#include "handler.h"
W::Handler::Handler(const W::Address& p_rel_addr):
address(p_rel_addr)
{
}
W::Handler::~Handler()
{
}
const W::Address& W::Handler::getAddress() const
{
return address;
}

56
lib/wDispatcher/handler.h Normal file
View File

@ -0,0 +1,56 @@
#ifndef HANDLER_H
#define HANDLER_H
#include "wType/address.h"
#include "wType/event.h"
namespace W
{
template<typename InstanceType, typename MethodType>
class ImplHandle;
class Handler
{
public:
Handler(const Address& p_rel_addr);
virtual ~Handler();
template<typename InstanceType, typename MethodType>
static Handler* create(const Address& addr, InstanceType* inst, MethodType mth)
{
return new ImplHandle<InstanceType, MethodType>(addr, inst, mth);
}
const W::Address& getAddress() const;
virtual void pass(const W::Event& ev) const = 0;
private:
W::Address address;
};
template<typename InstanceType, typename MethodType>
class ImplHandle: public Handler
{
public:
ImplHandle(const Address& p_rel_addr, InstanceType *p_inst, MethodType p_mth):
Handler(p_rel_addr),
inst(p_inst),
mth(p_mth)
{}
~ImplHandle() {}
void pass(const W::Event& ev) const
{
( ( *inst ).*mth )(ev);
}
private:
InstanceType* inst;
MethodType mth;
};
}
#endif // HANDLER_H

View File

@ -0,0 +1,24 @@
#include "logger.h"
#include "iostream"
W::Logger::Logger():
DefaultHandler()
{
}
W::Logger::~Logger()
{
}
bool W::Logger::call(const W::Event& ev) const
{
std::cout << "Event went to default handler.\n";
std::cout << "Destination: " << ev.getDestination().toString() << "\n";
std::cout << "Data: " << ev.getData().toString();
std::cout << std::endl;
return false;
}

21
lib/wDispatcher/logger.h Normal file
View File

@ -0,0 +1,21 @@
#ifndef LOGGER_H
#define LOGGER_H
#include "defaulthandler.h"
#include <wType/event.h>
namespace W
{
class Logger:
public DefaultHandler
{
public:
Logger();
~Logger();
bool call(const W::Event& ev) const;
};
}
#endif // LOGGER_H

View File

@ -0,0 +1,44 @@
#include "parentreporter.h"
W::ParentReporter::ParentReporter():
W::DefaultHandler(),
handlers()
{
}
W::ParentReporter::~ParentReporter()
{
}
bool W::ParentReporter::call(const W::Event& ev) const
{
const W::Address& addr = ev.getDestination();
std::map<int, W::Handler*> result;
Hmap::const_iterator itr = handlers.begin();
Hmap::const_iterator end = handlers.end();
for (; itr != end; ++itr) { //need to find the closest parent to the event destination
if (addr.begins(itr->first)) { //the closest parent has the longest address of those whose destinatiion begins with
result.insert(std::make_pair(itr->first.size(), itr->second));
}
}
if (result.size() > 0) {
std::map<int, W::Handler*>::const_iterator itr = result.end();
--itr;
itr->second->pass(ev); //need to report only to the closest parent
return true;
} else {
return false;
}
}
void W::ParentReporter::registerParent(const W::Address& address, W::Handler* handler)
{
Hmap::const_iterator itr = handlers.find(address);
if (itr != handlers.end()) {
throw 1;
} else {
handlers.insert(std::make_pair(address, handler));
}
}

View File

@ -0,0 +1,25 @@
#ifndef PARENTREPORTER_H
#define PARENTREPORTER_H
#include "defaulthandler.h"
#include "handler.h"
#include <map>
namespace W {
class ParentReporter : public DefaultHandler
{
public:
ParentReporter();
~ParentReporter();
bool call(const W::Event& ev) const;
void registerParent(const W::Address& address, W::Handler* handler);
private:
typedef std::map<W::Address, W::Handler*> Hmap;
Hmap handlers;
};
}
#endif // PARENTREPORTER_H

36
lib/wModel/CMakeLists.txt Normal file
View File

@ -0,0 +1,36 @@
cmake_minimum_required(VERSION 2.8.12)
project(model)
find_package(Qt5Core REQUIRED)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOMOC ON)
set(HEADERS
model.h
modelstring.h
list.h
vocabulary.h
attributes.h
icatalogue.h
catalogue.h
file/file.h
)
set(SOURCES
model.cpp
modelstring.cpp
list.cpp
vocabulary.cpp
attributes.cpp
icatalogue.cpp
catalogue.cpp
file/file.cpp
)
add_library(wModel STATIC ${HEADERS} ${SOURCES})
target_link_libraries(wModel Qt5::Core)
target_link_libraries(wModel wSocket)
target_link_libraries(wModel wDispatcher)
target_link_libraries(wModel wType)

60
lib/wModel/attributes.cpp Normal file
View File

@ -0,0 +1,60 @@
#include "attributes.h"
M::Attributes::Attributes(const W::Address p_address, QObject* parent):
M::Vocabulary(p_address, parent),
attributes(new Map())
{
}
M::Attributes::~Attributes()
{
delete attributes;
}
M::Model::ModelType M::Attributes::getType() const
{
return type;
}
void M::Attributes::addAttribute(const W::String& key, M::Model* model)
{
Map::const_iterator itr = attributes->find(key);
if (itr != attributes->end()) {
throw 1;
}
attributes->insert(std::make_pair(key, model));
addModel(model);
W::Vocabulary* vc = new W::Vocabulary();
vc->insert(u"name", key);
vc->insert(u"address", model->getAddress());
vc->insert(u"type", W::Uint64(model->getType()));
insert(key, vc);
}
void M::Attributes::removeAttribute(const W::String& key)
{
Map::const_iterator itr = attributes->find(key);
if (itr == attributes->end()) {
throw 1;
}
M::Model* model = itr->second;
attributes->erase(itr);
erase(key);
removeModel(model);
delete model;
}
void M::Attributes::setAttribute(const W::String& key, const W::Object& value)
{
Map::const_iterator itr = attributes->find(key);
itr->second->set(value);
}
void M::Attributes::setAttribute(const W::String& key, W::Object* value)
{
Map::const_iterator itr = attributes->find(key);
itr->second->set(value);
}

32
lib/wModel/attributes.h Normal file
View File

@ -0,0 +1,32 @@
#ifndef M_ATTRIBUTES_H
#define M_ATTRIBUTES_H
#include "vocabulary.h"
#include <wType/string.h>
#include <map>
namespace M {
class Attributes : public M::Vocabulary
{
public:
Attributes(const W::Address p_address, QObject* parent = 0);
~Attributes();
void addAttribute(const W::String& key, M::Model* model);
void removeAttribute(const W::String& key);
void setAttribute(const W::String& key, const W::Object& value);
void setAttribute(const W::String& key, W::Object* value);
M::Model::ModelType getType() const override;
static const M::Model::ModelType type = attributes;
private:
typedef std::map<W::String, M::Model*> Map;
Map* attributes;
};
}
#endif // ATTRIBUTES_H

66
lib/wModel/catalogue.cpp Normal file
View File

@ -0,0 +1,66 @@
#include "catalogue.h"
M::Catalogue::Catalogue(const W::Address p_address, QObject* parent):
ICatalogue(p_address, parent),
data()
{
}
M::Catalogue::~Catalogue()
{
}
uint64_t M::Catalogue::addElement(const W::Vocabulary& record)
{
uint64_t id = M::ICatalogue::addElement(record);
data.insert(std::make_pair(id, static_cast<W::Vocabulary*>(record.copy())));
return id;
}
void M::Catalogue::removeElement(uint64_t id)
{
M::ICatalogue::removeElement(id);
Data::const_iterator itr = data.find(id);
delete itr->second;
data.erase(itr);
}
void M::Catalogue::clear()
{
M::ICatalogue::clear();
data.clear();
}
W::Vocabulary * M::Catalogue::getElement(uint64_t id)
{
return static_cast<W::Vocabulary*>(data.at(id)->copy());
}
std::set<uint64_t> M::Catalogue::getAll() const
{
std::set<uint64_t> res;
Data::const_iterator itr = data.begin();
Data::const_iterator end = data.end();
for (; itr != end; ++itr) {
res.insert(itr->first);
}
return res;
}
void M::Catalogue::modifyElement(uint64_t id, const W::Vocabulary& newValue)
{
Data::iterator itr = data.find(id);
delete itr->second;
itr->second = static_cast<W::Vocabulary*>(newValue.copy());
}
uint64_t M::Catalogue::size() const
{
return data.size();
}

29
lib/wModel/catalogue.h Normal file
View File

@ -0,0 +1,29 @@
#ifndef CATALOGUE_H
#define CATALOGUE_H
#include "icatalogue.h"
namespace M {
class Catalogue : public ICatalogue {
public:
Catalogue(const W::Address p_address, QObject* parent = 0);
~Catalogue();
uint64_t addElement(const W::Vocabulary & record) override;
void removeElement(uint64_t id) override;
void clear() override;
W::Vocabulary* getElement(uint64_t id) override;
void modifyElement(uint64_t id, const W::Vocabulary & newValue) override;
uint64_t size() const override;
protected:
std::set<uint64_t> getAll() const override;
private:
typedef std::map<uint64_t, W::Vocabulary*> Data;
Data data;
};
}
#endif // CATALOGUE_H

86
lib/wModel/file/file.cpp Normal file
View File

@ -0,0 +1,86 @@
#include "file.h"
#include <iostream>
QMimeDatabase M::File::mimeDB;
M::File::File(W::Blob* p_file, const W::Address& addr, QObject* parent):
M::Model(addr, parent),
additional(),
file(p_file)
{
W::Handler* get = W::Handler::create(address + W::Address({u"get"}), this, &M::File::_h_get);
W::Handler* getAdditional = W::Handler::create(address + W::Address({u"getAdditional"}), this, &M::File::_h_getAdditional);
addHandler(get);
addHandler(getAdditional);
}
M::File::~File()
{
delete file;
}
M::Model::ModelType M::File::getType() const
{
return type;
}
void M::File::initAdditional(const W::String& p_mime)
{
additional.clear();
additional.insert(u"size", new W::Uint64(file->size()));
additional.insert(u"mimeType", p_mime);
}
void M::File::set(const W::Object& value)
{
set(value.copy());
}
void M::File::set(W::Object* value)
{
delete file;
file = static_cast<W::Blob*>(value);
QMimeType mt = mimeDB.mimeTypeForData(file->byteArray());
initAdditional(W::String(mt.name().toStdString()));
W::Vocabulary* vc = static_cast<W::Vocabulary*>(additional.copy());
broadcast(vc, W::Address({u"getAdditional"}));
}
void M::File::h_getAdditional(const W::Event& ev)
{
W::Vocabulary* vc = static_cast<W::Vocabulary*>(additional.copy());
response(vc, W::Address({u"getAdditional"}), ev);
}
void M::File::h_subscribe(const W::Event& ev)
{
M::Model::h_subscribe(ev);
h_getAdditional(ev);
}
void M::File::h_get(const W::Event& ev)
{
W::Vocabulary* vc = new W::Vocabulary();
vc->insert(u"additional", additional.copy());
vc->insert(u"data", file->copy());
response(vc, W::Address({u"get"}), ev);
}
M::File * M::File::create(W::Blob* blob, const W::Address& addr, QObject* parent)
{
M::File* out;
QMimeType mt = mimeDB.mimeTypeForData(blob->byteArray());
out = new File(blob, addr, parent);
out->initAdditional(W::String(mt.name().toStdString()));
return out;
}

44
lib/wModel/file/file.h Normal file
View File

@ -0,0 +1,44 @@
#ifndef FILE_H
#define FILE_H
/**
* @todo write docs
*/
#include <wModel/model.h>
#include <wType/blob.h>
#include <QtCore/QMimeDatabase>
namespace M {
class File: public Model {
protected:
File(W::Blob* p_file, const W::Address& addr, QObject* parent = 0);
public:
~File();
void set(const W::Object & value) override;
void set(W::Object * value) override;
M::Model::ModelType getType() const override;
static const M::Model::ModelType type = file;
static File* create(W::Blob* blob, const W::Address& addr, QObject* parent = 0);
protected:
virtual void initAdditional(const W::String& p_mime);
void h_subscribe(const W::Event & ev) override;
handler(get);
handler(getAdditional);
protected:
W::Vocabulary additional;
W::Blob* file;
static QMimeDatabase mimeDB;
};
}
#endif // FILE_H

469
lib/wModel/icatalogue.cpp Normal file
View File

@ -0,0 +1,469 @@
#include "icatalogue.h"
const std::set<uint64_t> M::ICatalogue::empty = std::set<uint64_t>();
M::ICatalogue::ICatalogue(const W::Address p_address, QObject* parent):
M::Model(p_address, parent),
subscribeMember(W::Handler::create(W::Address({}), this, &M::ICatalogue::_h_subscribeMember)),
indexes(),
lastIndex(0),
activeChildren()
{
W::Handler* get = W::Handler::create(address + W::Address({u"get"}), this, &M::ICatalogue::_h_get);
W::Handler* add = W::Handler::create(address + W::Address({u"add"}), this, &M::ICatalogue::_h_add);
W::Handler* update = W::Handler::create(address + W::Address({u"update"}), this, &M::ICatalogue::_h_update);
addHandler(get);
addHandler(add);
addHandler(update);
}
M::ICatalogue::~ICatalogue()
{
delete subscribeMember;
IndexMap::const_iterator itr = indexes.begin();
IndexMap::const_iterator end = indexes.end();
for (; itr != end; ++itr) {
delete itr->second;
}
}
void M::ICatalogue::clear()
{
lastIndex = 0;
IndexMap::const_iterator itr = indexes.begin();
IndexMap::const_iterator end = indexes.end();
for (; itr != end; ++itr) {
itr->second->clear();
}
if (registered) {
broadcast(new W::Vocabulary(), W::Address{u"clear"});
}
emit countChange(0);
}
void M::ICatalogue::addIndex(const W::String& fieldName, W::Object::objectType fieldType)
{
IndexMap::const_iterator itr = indexes.find(fieldName);
if (itr != indexes.end()) {
throw 2;
}
switch (fieldType) {
case W::Object::uint64:
indexes.insert(std::make_pair(fieldName, new Index<W::Uint64>()));
break;
case W::Object::string:
indexes.insert(std::make_pair(fieldName, new Index<W::String>()));
break;
default:
throw 3;
}
}
const std::set<uint64_t> & M::ICatalogue::find(const W::String& indexName, const W::Object& value) const
{
IndexMap::const_iterator itr = indexes.find(indexName);
if (itr == indexes.end()) {
throw 4;
}
return itr->second->find(value);
}
std::set<uint64_t> M::ICatalogue::find(const W::Vocabulary& value) const
{
W::Vector keys = value.keys();
int size = keys.length();
std::set<uint64_t> result;
bool first = true;
for (int i = 0; i < size; ++i) {
const W::String& key = static_cast<const W::String&>(keys.at(i));
IndexMap::const_iterator itr = indexes.find(key);
if (itr == indexes.end()) {
throw 4;
}
if (first) {
result = itr->second->find(value.at(key));
first = false;
} else {
std::set<uint64_t> copy = result;
result.clear();
const std::set<uint64_t>& current = itr->second->find(value.at(key));
std::set_intersection(copy.begin(), copy.end(), current.begin(), current.end(), std::inserter(result, result.end()));
}
if (result.empty()) {
break;
}
}
return result;
}
M::Model::ModelType M::ICatalogue::getType() const
{
return type;
}
uint64_t M::ICatalogue::addElement(const W::Vocabulary& record)
{
IndexMap::const_iterator itr = indexes.begin();
IndexMap::const_iterator end = indexes.end();
++lastIndex;
for (; itr != end; ++itr) {
itr->second->add(record.at(itr->first), lastIndex);
}
Map::const_iterator sItr = subscribers->begin();
Map::const_iterator sEnd = subscribers->end();
for (; sItr != sEnd; ++sItr) {
SMap::const_iterator oItr = sItr->second.begin();
SMap::const_iterator oEnd = sItr->second.end();
for (; oItr != oEnd; ++oItr) {
const W::Vocabulary& params = oItr->second;
if (params.has(u"filter")) {
processAddElement(lastIndex, record, oItr, sItr->first);
} else {
uint64_t bid = getInsertingNeighbour(oItr->second, record, lastIndex, getAll());
W::Address dest = oItr->first + W::Address({u"addElement"});
W::Vocabulary* vc = new W::Vocabulary();
vc->insert(u"id", new W::Uint64(lastIndex));
if (bid != 0) {
vc->insert(u"before", new W::Uint64(bid));
}
send(vc, dest, sItr->first);
}
}
}
emit countChange(size() + 1);
return lastIndex;
}
void M::ICatalogue::removeElement(uint64_t id)
{
IndexMap::const_iterator itr = indexes.begin();
IndexMap::const_iterator end = indexes.end();
W::Vocabulary* value = getElement(id);
Map::const_iterator sItr = subscribers->begin();
Map::const_iterator sEnd = subscribers->end();
for (; sItr != sEnd; ++sItr) {
SMap::const_iterator oItr = sItr->second.begin();
SMap::const_iterator oEnd = sItr->second.end();
for (; oItr != oEnd; ++oItr) {
const W::Vocabulary& params = oItr->second;
if (params.has(u"filter")) {
const W::Vocabulary& filter = static_cast<const W::Vocabulary&>(params.at(u"filter"));
std::set<uint64_t> set = find(filter);
std::set<uint64_t>::const_iterator idItr = set.find(id);
if (idItr != set.end()) {
W::Address dest = oItr->first + W::Address({u"removeElement"});
W::Vocabulary* vc = new W::Vocabulary();
vc->insert(u"id", new W::Uint64(id));
send(vc, dest, sItr->first);
}
} else {
W::Address dest = oItr->first + W::Address({u"removeElement"});
W::Vocabulary* vc = new W::Vocabulary();
vc->insert(u"id", new W::Uint64(id));
send(vc, dest, sItr->first);
}
}
}
for (; itr != end; ++itr) {
itr->second->remove(value->at(itr->first), id);
}
std::map<uint64_t, M::Vocabulary*>::const_iterator aItr = activeChildren.find(id);
if (aItr != activeChildren.end()) {
removeModel(aItr->second);
aItr->second->deleteLater();
activeChildren.erase(aItr);
}
emit countChange(size() - 1);
delete value;
}
void M::ICatalogue::h_get(const W::Event& ev)
{
const W::Vocabulary& vc = static_cast<const W::Vocabulary&>(ev.getData());
const W::Vocabulary& params = static_cast<const W::Vocabulary&>(vc.at(u"params"));
std::set<uint64_t> set;
if (params.has(u"filter")) {
const W::Vocabulary& filter = static_cast<const W::Vocabulary&>(params.at(u"filter"));
set = find(filter);
} else {
set = getAll();
}
W::Vocabulary* rvc = new W::Vocabulary;
if (params.has(u"sorting")) {
const W::Vocabulary& sorting = static_cast<const W::Vocabulary&>(params.at(u"sorting"));
const W::String& field = static_cast<const W::String&>(sorting.at(u"field"));
bool ascending = static_cast<const W::Boolean&>(sorting.at(u"ascending"));
rvc->insert(u"data", indexes.at(field)->sort(set, ascending));
} else {
W::Vector* order = new W::Vector();
std::set<uint64_t>::const_iterator itr = set.begin();
std::set<uint64_t>::const_iterator end = set.end();
for (; itr != end; ++itr) {
order->push(W::Uint64(*itr));
}
rvc->insert(u"data", order);
}
response(rvc, W::Address({u"get"}), ev);
}
void M::ICatalogue::h_subscribe(const W::Event& ev)
{
M::Model::h_subscribe(ev);
h_get(ev);
}
void M::ICatalogue::set(const W::Object& value)
{
throw 14; //what do you expect here? not implemented, and not sure it ever would be
}
void M::ICatalogue::set(W::Object* value)
{
set(*value);
}
void M::ICatalogue::h_add(const W::Event& ev)
{
addElement(static_cast<const W::Vocabulary&>(ev.getData()));
}
void M::ICatalogue::h_update(const W::Event& ev)
{
const W::Vocabulary& data = static_cast<const W::Vocabulary&>(ev.getData());
const W::Uint64& id = static_cast<const W::Uint64&>(data.at(u"id"));
const W::Vocabulary& newValue = static_cast<const W::Vocabulary&>(data.at(u"value"));
W::Vector affectedKeys = newValue.keys();
W::Vocabulary* oldValue = getElement(id);
W::Vocabulary* modifiedValue = W::Vocabulary::extend(*oldValue, newValue);
modifyElement(id, *modifiedValue);
std::map<uint64_t, M::Vocabulary*>::const_iterator itr = activeChildren.find(id);
if (itr != activeChildren.end()) {
itr->second->set(modifiedValue);
}
Map::const_iterator sItr = subscribers->begin();
Map::const_iterator sEnd = subscribers->end();
for (; sItr != sEnd; ++sItr) {
SMap::const_iterator oItr = sItr->second.begin();
SMap::const_iterator oEnd = sItr->second.end();
for (; oItr != oEnd; ++oItr) {
const W::Vocabulary& params = oItr->second;
if (params.has(u"filter")) {
const W::Vocabulary& filter = static_cast<const W::Vocabulary&>(params.at(u"filter"));
bool matched = match(*oldValue, filter);
bool matching = match(*modifiedValue, filter);
if (matched && !matching) {
W::Address dest = oItr->first + W::Address({u"removeElement"});
W::Vocabulary* vc = new W::Vocabulary();
vc->insert(u"id", new W::Uint64(id));
send(vc, dest, sItr->first);
} else if (!matched && matching) {
processAddElement(id, *modifiedValue, oItr, sItr->first);
} else if (matched && matching) {
std::set<uint64_t> set = find(filter);
uint64_t cbid = getInsertingNeighbour(params, *oldValue, id, set);
uint64_t bid = getInsertingNeighbour(params, *modifiedValue, id, set);
if (cbid != bid) {
W::Address dest = oItr->first + W::Address({u"moveElement"});
W::Vocabulary* vc = new W::Vocabulary();
vc->insert(u"id", new W::Uint64(id));
if (id != 0) {
vc->insert(u"before", new W::Uint64(bid));
}
send(vc, dest, sItr->first);
}
}
} else {
if (params.has(u"sorting")) {
std::set<uint64_t> set = getAll();
uint64_t cbid = getInsertingNeighbour(params, *oldValue, id, set);
uint64_t bid = getInsertingNeighbour(params, *modifiedValue, id, set);
if (cbid != bid) {
W::Address dest = oItr->first + W::Address({u"moveElement"});
W::Vocabulary* vc = new W::Vocabulary();
vc->insert(u"id", new W::Uint64(id));
if (id != 0) {
vc->insert(u"before", new W::Uint64(bid));
}
send(vc, dest, sItr->first);
}
}
}
}
}
}
void M::ICatalogue::h_subscribeMember(const W::Event& ev)
{
const W::Address& addr = ev.getDestination();
W::Address lastHops = addr << address.length();
if (lastHops.length() == 2 && (lastHops.ends(W::Address{u"subscribe"}) || lastHops.ends(W::Address{u"get"}))) {
W::Vocabulary* record;
try {
uint64_t id = lastHops.front().toUint64();
record = getElement(id);
if (lastHops.ends(W::Address{u"subscribe"})) {
M::Vocabulary* modelRecord = new M::Vocabulary(record, address + lastHops >> 1);
addModel(modelRecord);
activeChildren.insert(std::make_pair(id, modelRecord));
modelRecord->_h_subscribe(ev);
} else {
W::Vocabulary* vc = new W::Vocabulary;
vc->insert(u"data", record);
fakeResponse(vc, W::Address({u"get"}), addr >> 1, ev);
}
} catch(int err) {
if (err == 3) {
emit serviceMessage(QString("An attempt to create and subscribe record model in catalogue, but it is not found. Event: ") + ev.toString().c_str());
} else {
throw err;
}
} catch (const std::invalid_argument& err) {
emit serviceMessage(QString("Strange event in custom handler of catalogue ") + ev.toString().c_str());
}
} else {
emit serviceMessage(QString("Strange event in custom handler of catalogue ") + ev.toString().c_str());
}
}
bool M::ICatalogue::match(const W::Vocabulary& value, const W::Vocabulary& filter)
{
bool m = true;
W::Vector keys = filter.keys();
for (int i = 0; i < keys.length(); ++i) {
const W::String& key = static_cast<const W::String&>(keys.at(i));
if (filter.at(key) != value.at(key)) {
m = false;
break;
};
}
return m;
}
void M::ICatalogue::processAddElement(uint64_t id, const W::Vocabulary& value, SMap::const_iterator subscriberIterator, uint64_t socketId)
{
const W::Address& addr = subscriberIterator->first;
const W::Vocabulary& params = subscriberIterator->second;
const W::Vocabulary& filter = static_cast<const W::Vocabulary&>(params.at(u"filter"));
std::set<uint64_t> set = find(filter);
std::set<uint64_t>::const_iterator idItr = set.find(id);
if (idItr != set.end()) { //to make sure if subscriber cares
uint64_t bid = getInsertingNeighbour(params, value, id, set);
W::Address dest = addr + W::Address({u"addElement"});
W::Vocabulary* vc = new W::Vocabulary();
vc->insert(u"id", new W::Uint64(id));
if (id != 0) {
vc->insert(u"before", new W::Uint64(bid));
}
send(vc, dest, socketId);
}
}
uint64_t M::ICatalogue::getInsertingNeighbour(const W::Vocabulary& params, const W::Vocabulary& record, uint64_t id, const std::set<uint64_t>& allowed) const
{
uint64_t bid;
if (params.has(u"sorting")) {
if (allowed.empty()) {
bid = 0;
} else {
const W::Vocabulary& sorting = static_cast<const W::Vocabulary&>(params.at(u"sorting"));
const W::String& field = static_cast<const W::String&>(sorting.at(u"field"));
bool ascending = static_cast<const W::Boolean&>(sorting.at(u"ascending"));
uint64_t foundId = id;
do {
if (ascending) {
foundId = indexes.at(field)->getNext(foundId, record.at(field));
} else {
foundId = indexes.at(field)->getPrev(foundId, record.at(field));
}
} while (allowed.find(foundId) == allowed.end() || foundId != 0); //to make sure, that id folowing the inserting also present in the
bid = foundId; //subscribers filter result
}
} else {
std::set<uint64_t>::const_iterator idItr = allowed.find(id);
if (idItr == allowed.end()) {
bid = 0;
} else {
++idItr;
if (idItr == allowed.end()) {
bid = 0;
} else {
bid = *idItr;
}
}
}
return bid;
}
M::ICatalogue::AbstractIndex::TypeError::TypeError(const std::string& name, const std::string& method, W::Object::objectType myType, W::Object::objectType valueType):
Utils::Exception(),
name(name),
method(method),
myType(myType),
valueType(valueType)
{}
std::string M::ICatalogue::AbstractIndex::TypeError::getMessage() const
{
std::string msg = "An attempt to call Catalogue Index of ";
msg += name;
msg += " method \"";
msg += method;
msg += "\" with value type of ";
msg += W::Object::getTypeName(valueType);
msg += " but this index values supposed to have type ";
msg += W::Object::getTypeName(myType);
return msg;
}

316
lib/wModel/icatalogue.h Normal file
View File

@ -0,0 +1,316 @@
#ifndef ICATALOGUE_H
#define ICATALOGUE_H
#include "model.h"
#include <set>
#include <map>
#include <wModel/vocabulary.h>
#include <utils/exception.h>
namespace M {
class ICatalogue : public M::Model
{
Q_OBJECT
protected:
class AbstractIndex;
public:
ICatalogue(const W::Address p_address, QObject* parent = 0);
~ICatalogue();
virtual uint64_t addElement(const W::Vocabulary& record);
virtual void removeElement(uint64_t id);
virtual W::Vocabulary* getElement(uint64_t id) = 0;
virtual void modifyElement(uint64_t id, const W::Vocabulary& newValue) = 0;
virtual uint64_t size() const = 0;
virtual void clear();
virtual void addIndex(const W::String& fieldName, W::Object::objectType fieldType);
const std::set<uint64_t>& find(const W::String& indexName, const W::Object& value) const;
std::set<uint64_t> find(const W::Vocabulary& value) const;
M::Model::ModelType getType() const override;
static const M::Model::ModelType type = catalogue;
void set(const W::Object & value) override;
void set(W::Object * value) override;
W::Handler* subscribeMember;
handler(subscribeMember);
static bool match(const W::Vocabulary& value, const W::Vocabulary& filter);
static const std::set<uint64_t> empty;
signals:
void countChange(uint64_t count);
protected:
virtual std::set<uint64_t> getAll() const = 0;
void h_subscribe(const W::Event & ev) override;
handler(get);
handler(add);
handler(update);
typedef std::map<W::String, AbstractIndex*> IndexMap;
IndexMap indexes;
private:
uint64_t lastIndex;
std::map<uint64_t, M::Vocabulary*> activeChildren;
void processAddElement(uint64_t id, const W::Vocabulary& value, SMap::const_iterator subscriberIterator, uint64_t socketId);
uint64_t getInsertingNeighbour(const W::Vocabulary& params, const W::Vocabulary& record, uint64_t id, const std::set<uint64_t>& allowed = empty) const;
protected:
class AbstractIndex {
public:
AbstractIndex(W::Object::objectType vt): valueType(vt) {}
virtual ~AbstractIndex() {}
virtual const std::set<uint64_t>& find(const W::Object& value) const = 0;
virtual void add(const W::Object& value, uint64_t id) = 0;
virtual void remove(const W::Object & value, uint64_t id) = 0;
virtual void clear() = 0;
virtual W::Vector sort(const std::set<uint64_t>& set, bool ascending) = 0;
virtual uint64_t getNext(uint64_t id, const W::Object& value) = 0;
virtual uint64_t getPrev(uint64_t id, const W::Object& value) = 0;
W::Object::objectType valueType;
protected:
class TypeError : public Utils::Exception {
public:
TypeError(const std::string& name, const std::string& method, W::Object::objectType myType, W::Object::objectType valueType);
std::string getMessage() const;
private:
std::string name;
std::string method;
W::Object::objectType myType;
W::Object::objectType valueType;
};
};
template <class T>
class Index : public AbstractIndex {
public:
Index();
~Index();
const std::set<uint64_t>& find(const W::Object& value) const override;
void add(const W::Object & value, uint64_t id) override;
void remove(const W::Object & value, uint64_t id) override;
void clear() override;
W::Vector sort(const std::set<uint64_t> & set, bool ascending) override;
uint64_t getNext(uint64_t id, const W::Object& value) override;
uint64_t getPrev(uint64_t id, const W::Object& value) override;
private:
typedef std::map<T, std::set<uint64_t>> Map;
Map values;
};
};
template<class T>
ICatalogue::Index<T>::Index():
ICatalogue::AbstractIndex(T::type),
values()
{
}
template<class T>
ICatalogue::Index<T>::~Index()
{
}
template<class T>
const std::set<uint64_t> & ICatalogue::Index<T>::find(const W::Object& value) const
{
if (value.getType() != valueType) {
throw new TypeError("Unknown", "find", valueType, value.getType()); //todo replace that unknown stuff, find a way to provide index name
}
const T& val = static_cast<const T&>(value);
typename std::map<T, std::set<uint64_t>>::const_iterator itr = values.find(val);
if (itr == values.end()) {
return ICatalogue::empty;
} else {
return itr->second;
}
}
template<class T>
void ICatalogue::Index<T>::add(const W::Object& value, uint64_t id)
{
if (value.getType() != valueType) {
throw new TypeError("Unknown", "add", valueType, value.getType());
}
const T& val = static_cast<const T&>(value);
typename std::map<T, std::set<uint64_t>>::iterator itr = values.find(val);
if (itr == values.end()) {
itr = values.insert(std::make_pair(val, std::set<uint64_t>())).first;
}
itr->second.insert(id);
}
template<class T>
void ICatalogue::Index<T>::remove(const W::Object& value, uint64_t id)
{
if (value.getType() != valueType) {
throw new TypeError("Unknown", "remove", valueType, value.getType());
}
const T& val = static_cast<const T&>(value);
typename std::map<T, std::set<uint64_t>>::iterator itr = values.find(val);
if (itr != values.end()) {
std::set<uint64_t>& set = itr->second;
if (set.size() == 1) {
values.erase(itr);
} else {
std::set<uint64_t>::const_iterator hint = set.find(id);
set.erase(hint);
}
}
}
template<class T>
void ICatalogue::Index<T>::clear()
{
values.clear();
}
template<class T>
W::Vector ICatalogue::Index<T>::sort(const std::set<uint64_t> & set, bool ascending) //TODO this needs an optimization
{
W::Vector res;
std::set<uint64_t>::const_iterator sEnd = set.end();
uint64_t size = set.size();
if (size == 0) {
return res;
} else if (size == 1) {
res.push(W::Uint64(*(set.begin())));
return res;
}
if (ascending) {
typename std::map<T, std::set<uint64_t>>::const_iterator itr = values.begin();
typename std::map<T, std::set<uint64_t>>::const_iterator end = values.end();
for (; itr != end; ++itr) {
if (size == res.size()) {
break;
}
const std::set<uint64_t>& chunk = itr->second;
std::set<uint64_t>::const_iterator cItr = chunk.begin();
std::set<uint64_t>::const_iterator cEnd = chunk.end();
for (; cItr != cEnd; ++cItr) {
uint64_t id = *cItr;
if (set.find(id) != sEnd) {
res.push(W::Uint64(id));
}
}
}
} else {
typename std::map<T, std::set<uint64_t>>::reverse_iterator itr = values.rbegin();
typename std::map<T, std::set<uint64_t>>::reverse_iterator end = values.rend();
for (; itr != end; ++itr) {
if (size == res.size()) {
break;
}
const std::set<uint64_t>& chunk = itr->second;
std::set<uint64_t>::const_iterator cItr = chunk.begin();
std::set<uint64_t>::const_iterator cEnd = chunk.end();
for (; cItr != cEnd; ++cItr) {
uint64_t id = *cItr;
if (set.find(id) != sEnd) {
res.push(W::Uint64(id));
}
}
}
}
return res;
}
template<class T>
uint64_t ICatalogue::Index<T>::getNext(uint64_t id, const W::Object& value)
{
if (value.getType() != valueType) {
throw new TypeError("Unknown", "getNext", valueType, value.getType());
}
const T& val = static_cast<const T&>(value);
typename std::map<T, std::set<uint64_t>>::iterator itr = values.find(val);
if (itr == values.end()) {
throw 2; //this is not suppose to happen!
}
const std::set<uint64_t>& set = itr->second;
std::set<uint64_t>::const_iterator sItr = set.find(id);
if (sItr == set.end()) {
throw 2; //not suppose to happen!
}
++sItr;
if (sItr == set.end()) {
++itr;
bool found = false;
while (itr != values.end()) {
if (itr->second.size() != 0) {
sItr = set.begin();
found = true;
break;
}
++itr;
}
if (!found) {
return 0;
}
}
return *sItr;
}
template<class T>
uint64_t ICatalogue::Index<T>::getPrev(uint64_t id, const W::Object& value)
{
if (value.getType() != valueType) {
throw new TypeError("Unknown", "getPrev", valueType, value.getType());
}
const T& val = static_cast<const T&>(value);
typename std::map<T, std::set<uint64_t>>::iterator itr = values.find(val);
if (itr == values.end()) {
throw 2; //this is not suppose to happen!
}
const std::set<uint64_t>& set = itr->second;
std::set<uint64_t>::const_iterator sItr = set.find(id);
if (sItr == set.end()) {
throw 2; //not suppose to happen!
}
if (sItr == set.begin()) {
bool found = false;
while (itr != values.begin()) {
--itr;
if (itr->second.size() != 0) {
sItr = set.end();
--sItr;
break;
}
}
if (!found) {
return 0;
}
} else {
--sItr;
}
return *sItr;
}
}
#endif // ICATALOGUE_H

100
lib/wModel/list.cpp Normal file
View File

@ -0,0 +1,100 @@
#include "list.h"
M::List::List(const W::Address p_address, QObject* parent):
M::Model(p_address, parent),
data(new W::Vector())
{
W::Handler* get = W::Handler::create(address + W::Address({u"get"}), this, &M::List::_h_get);
addHandler(get);
}
M::List::~List()
{
delete data;
}
void M::List::h_subscribe(const W::Event& ev)
{
M::Model::h_subscribe(ev);
h_get(ev);
}
void M::List::h_get(const W::Event& ev)
{
W::Vocabulary* vc = new W::Vocabulary;
vc->insert(u"data", data->copy());
response(vc, W::Address({u"get"}), ev);
}
void M::List::push(const W::Object& obj)
{
data->push(obj);
if (registered) {
W::Vocabulary* vc = new W::Vocabulary();
vc->insert(u"data", obj);
broadcast(vc, W::Address{u"push"});
}
}
void M::List::push(W::Object* obj)
{
data->push(obj);
if (registered) {
W::Vocabulary* vc = new W::Vocabulary();
vc->insert(u"data", obj->copy());
broadcast(vc, W::Address{u"push"});
}
}
void M::List::clear()
{
data->clear();
if (registered) {
broadcast(new W::Vocabulary(), W::Address{u"clear"});
}
}
M::Model::ModelType M::List::getType() const
{
return type;
}
void M::List::set(const W::Object& value)
{
delete data;
data = static_cast<W::Vector*>(value.copy());
W::Vocabulary* vc = new W::Vocabulary;
vc->insert(u"data", data->copy());
broadcast(vc, W::Address({u"get"}));
}
void M::List::set(W::Object* value)
{
delete data;
data = static_cast<W::Vector*>(value);
W::Vocabulary* vc = new W::Vocabulary;
vc->insert(u"data", data->copy());
broadcast(vc, W::Address({u"get"}));
}
uint64_t M::List::size() const
{
return data->size();
}
const W::Object & M::List::at(uint64_t index) const
{
return data->at(index);
}

41
lib/wModel/list.h Normal file
View File

@ -0,0 +1,41 @@
#ifndef M_LIST_H
#define M_LIST_H
#include "model.h"
#include <wType/object.h>
#include <wType/address.h>
#include <wType/vector.h>
namespace M {
class List : public M::Model
{
public:
List(const W::Address p_address, QObject* parent = 0);
~List();
void push(const W::Object& obj);
void push(W::Object* obj);
void clear();
uint64_t size() const;
const W::Object& at(uint64_t index) const;
void set(const W::Object & value) override;
void set(W::Object * value) override;
M::Model::ModelType getType() const override;
static const M::Model::ModelType type = list;
protected:
void h_subscribe(const W::Event & ev) override;
handler(get);
private:
W::Vector* data;
};
}
#endif // M_LIST_H

339
lib/wModel/model.cpp Normal file
View File

@ -0,0 +1,339 @@
#include "model.h"
M::Model::Model(const W::Address p_address, QObject* parent):
QObject(parent),
address(p_address),
registered(false),
subscribers(new Map()),
dispatcher(0),
server(0),
subscribersCount(0),
handlers(new HList()),
properties(new W::Vector()),
models(new MList())
{
W::Handler* subscribe = W::Handler::create(address + W::Address({u"subscribe"}), this, &M::Model::_h_subscribe);
W::Handler* unsubscribe = W::Handler::create(address + W::Address({u"unsubscribe"}), this, &M::Model::_h_unsubscribe);
addHandler(subscribe);
addHandler(unsubscribe);
}
M::Model::~Model()
{
if (registered) {
unregisterModel();
}
MList::iterator itr = models->begin();
MList::iterator end = models->end();
for (; itr != end; ++itr) {
delete *itr;
}
HList::iterator hItr = handlers->begin();
HList::iterator hEnd = handlers->end();
for (; hItr != hEnd; ++hItr) {
delete *hItr;
}
delete subscribers;
delete properties;
delete handlers;
delete models;
}
void M::Model::addModel(M::Model* model)
{
models->push_back(model);
connect(model, SIGNAL(serviceMessage(const QString&)), SIGNAL(serviceMessage(const QString&)));
if (registered) {
model->registerModel(dispatcher, server);
}
}
void M::Model::addHandler(W::Handler* handler)
{
handlers->push_back(handler);
if (registered) {
dispatcher->registerHandler(handler);
}
}
void M::Model::addProperty(const W::String& value, const W::String& name)
{
W::Vocabulary vc;
vc.insert(u"key", name);
vc.insert(u"property", value);
properties->push(vc);
if (registered) {
W::Vocabulary* nvc = new W::Vocabulary;
nvc->insert(u"properties", *properties);
broadcast(nvc, W::Address({u"properties"}));
}
}
W::Address M::Model::getAddress() const
{
return address;
}
void M::Model::registerModel(W::Dispatcher* dp, W::Server* srv)
{
if (registered) {
emit serviceMessage(QString("Model ") + address.toString().c_str() + " is already registered");
throw 1;
} else {
dispatcher = dp;
server = srv;
MList::iterator itr = models->begin();
MList::iterator end = models->end();
for (; itr != end; ++itr) {
M::Model* model = *itr;
model->registerModel(dispatcher, server);
}
HList::iterator hItr = handlers->begin();
HList::iterator hEnd = handlers->end();
for (; hItr != hEnd; ++hItr) {
W::Handler* handler = *hItr;
dispatcher->registerHandler(handler);
}
registered = true;
}
}
void M::Model::unregisterModel()
{
if (!registered) {
emit serviceMessage(QString("Model ") + address.toString().c_str() + " is not registered");
throw 2;
} else {
MList::iterator itr = models->begin();
MList::iterator end = models->end();
for (; itr != end; ++itr) {
Model* model = *itr;
model->unregisterModel();
}
HList::iterator hItr = handlers->begin();
HList::iterator hEnd = handlers->end();
for (; hItr != hEnd; ++hItr) {
W::Handler* handler = *hItr;
dispatcher->unregisterHandler(handler);
}
Map::iterator sItr = subscribers->begin();
Map::iterator sEnd = subscribers->end();
for (; sItr != sEnd; ++sItr) {
const W::Socket& socket = server->getConnection(sItr->first);
disconnect(&socket, SIGNAL(disconnected()), this, SLOT(onSocketDisconnected()));
}
subscribers->clear();
subscribersCount = 0;
dispatcher = 0;
server = 0;
registered = false;
}
}
void M::Model::h_subscribe(const W::Event& ev)
{
uint64_t id = ev.getSenderId();
const W::Vocabulary& vc = static_cast<const W::Vocabulary&>(ev.getData());
const W::Address& source = static_cast<const W::Address&>(vc.at(u"source"));
W::Vocabulary params;
if (vc.has(u"params")) {
params = static_cast<const W::Vocabulary&>(vc.at(u"params"));
}
Map::iterator sItr = subscribers->find(id);
if (sItr == subscribers->end()) {
std::pair<Map::iterator, bool> pair = subscribers->emplace(std::make_pair(id, SMap()));
if (!pair.second) {
emit serviceMessage(QString("Model ") + address.toString().c_str() + ": something completely wrong happened");
throw 3;
}
const W::Socket& socket = server->getConnection(id);
connect(&socket, SIGNAL(disconnected()), this, SLOT(onSocketDisconnected()));
sItr = pair.first;
}
SMap::const_iterator oItr = sItr->second.find(source);
if (oItr != sItr->second.end()) {
emit serviceMessage(QString("Socket ") + id +
" subscriber " + source.toString().c_str() +
" is already subscribed to model " + source.toString().c_str());
throw 4;
}
sItr->second.insert(std::make_pair(source, params));
++subscribersCount;
W::Vocabulary* nvc = new W::Vocabulary();
nvc->insert(u"properties", *properties);
response(nvc, W::Address({u"properties"}), ev);
emit serviceMessage(QString("Model ") + address.toString().c_str() + ": now has " + std::to_string(subscribersCount).c_str() + " subscribers");
emit subscribersCountChange(subscribersCount);
}
void M::Model::onSocketDisconnected()
{
W::Socket* socket = static_cast<W::Socket*>(sender());
disconnect(socket, SIGNAL(disconnected()), this, SLOT(onSocketDisconnected()));
uint64_t id = socket->getId();
Map::iterator itr = subscribers->find(id);
if (itr == subscribers->end()) {
emit serviceMessage(QString("Model ") + address.toString().c_str() +
": socket disconnected have been handled for not subscribed id");
throw 5;
}
subscribersCount -= itr->second.size();
subscribers->erase(itr);
emit serviceMessage(QString("Model ") + address.toString().c_str() + ": now has " + std::to_string(subscribersCount).c_str() + " subscribers");
emit subscribersCountChange(subscribersCount);
}
void M::Model::h_unsubscribe(const W::Event& ev)
{
uint64_t id = ev.getSenderId();
const W::Vocabulary& vc = static_cast<const W::Vocabulary&>(ev.getData());
const W::Address& source = static_cast<const W::Address&>(vc.at(u"source"));
Map::iterator itr = subscribers->find(id);
if (itr == subscribers->end()) {
emit serviceMessage(QString("Socket ") + id +
" has no subscribed addresses to model " + source.toString().c_str());
throw 6;
}
SMap& smap = itr->second;
SMap::const_iterator sItr = smap.find(source);
if (sItr == smap.end()) {
emit serviceMessage(QString("Socket ") + id +
" subscriber " + source.toString().c_str() +
" is not subscribed to model " + source.toString().c_str());
throw 7;
}
smap.erase(sItr);
if (smap.size() == 0) {
const W::Socket& socket = server->getConnection(itr->first);
disconnect(&socket, SIGNAL(disconnected()), this, SLOT(onSocketDisconnected()));
subscribers->erase(itr);
}
--subscribersCount;
emit serviceMessage(QString("Model ") + address.toString().c_str() + ": now has " + std::to_string(subscribersCount).c_str() + " subscribers");
emit subscribersCountChange(subscribersCount);
}
void M::Model::send(W::Vocabulary* vc, const W::Address& destination, uint64_t connectionId)
{
if (!registered) {
emit serviceMessage(QString("An attempt to send event from model ") + address.toString().c_str() + " which was not registered");
throw 8;
}
W::Event ev(destination, vc);
ev.setSenderId(connectionId);
server->getConnection(connectionId).send(ev);
}
void M::Model::response(W::Vocabulary* vc, const W::Address& handlerAddress, const W::Event& src)
{
if (!registered) {
emit serviceMessage(QString("An attempt to send event from model ") + address.toString().c_str() + " which was not registered");
throw 8;
}
const W::Vocabulary& svc = static_cast<const W::Vocabulary&>(src.getData());
const W::Address& source = static_cast<const W::Address&>(svc.at(u"source"));
uint64_t id = src.getSenderId();
vc->insert(u"source", address);
W::Event ev(source + handlerAddress, vc);
ev.setSenderId(id);
server->getConnection(id).send(ev);
}
void M::Model::fakeResponse(W::Vocabulary* vc, const W::Address& handlerAddress, const W::Address& sourceAddress, const W::Event& src)
{
if (!registered) {
emit serviceMessage(QString("An attempt to send event from model ") + address.toString().c_str() + " which was not registered");
throw 8;
}
const W::Vocabulary& svc = static_cast<const W::Vocabulary&>(src.getData());
const W::Address& source = static_cast<const W::Address&>(svc.at(u"source"));
uint64_t id = src.getSenderId();
vc->insert(u"source", sourceAddress);
W::Event ev(source + handlerAddress, vc);
ev.setSenderId(id);
server->getConnection(id).send(ev);
}
void M::Model::broadcast(W::Vocabulary* vc, const W::Address& handlerAddress)
{
if (!registered) {
emit serviceMessage(QString("An attempt to send event from model ") + address.toString().c_str() + " which was not registered");
throw 8;
}
Map::const_iterator itr = subscribers->begin();
Map::const_iterator end = subscribers->end();
vc->insert(u"source", address);
for (;itr != end; ++itr) {
SMap::const_iterator oItr = itr->second.begin();
SMap::const_iterator oEnd = itr->second.end();
for (;oItr != oEnd; ++oItr) {
W::Event ev(oItr->first + handlerAddress, vc->copy());
ev.setSenderId(itr->first);
server->getConnection(itr->first).send(ev);
}
}
delete vc;
}
void M::Model::removeHandler(W::Handler* handler)
{
handlers->erase(handler);
if (registered) {
dispatcher->unregisterHandler(handler);
}
}
void M::Model::removeModel(M::Model* model)
{
models->erase(model);
if (registered) {
model->unregisterModel();
}
}
void M::Model::passToHandler(const W::Event& event) const
{
if (registered) {
dispatcher->pass(event);
} else {
emit serviceMessage(QString("An attempt to pass event to dispatcher from unregistered model\nModel address ") + address.toString().c_str());
}
}

92
lib/wModel/model.h Normal file
View File

@ -0,0 +1,92 @@
#ifndef W_MODEL_H
#define W_MODEL_H
#include <utils/defines.h>
#include <map>
#include <list>
#include <QtCore/QObject>
#include <QtCore/QString>
#include <wType/address.h>
#include <wType/vector.h>
#include <wType/event.h>
#include <wType/string.h>
#include <wSocket/socket.h>
#include <wSocket/server.h>
#include <wDispatcher/dispatcher.h>
#include <wDispatcher/handler.h>
#include <wContainer/order.h>
namespace M {
class Model : public QObject
{
Q_OBJECT
public:
enum ModelType {
string,
list,
vocabulary,
catalogue,
attributes = 50,
file,
resourceCache
};
Model(const W::Address p_address, QObject* parent = 0);
//i'm not sure about copy constructor, it just doesn't make sense, because the address is the parameter which is supposed to be unique
virtual ~Model();
virtual ModelType getType() const = 0;
virtual void set(W::Object* value) = 0;
virtual void set(const W::Object& value) = 0;
void addModel(M::Model* model);
void addHandler(W::Handler* handler);
void addProperty(const W::String& value, const W::String& name);
W::Address getAddress() const;
void registerModel(W::Dispatcher* dp, W::Server* srv);
void unregisterModel();
void removeHandler(W::Handler* handler);
void removeModel(M::Model* model);
void passToHandler(const W::Event& event) const;
signals:
void serviceMessage(const QString& msg) const;
void subscribersCountChange(uint64_t count) const;
protected:
typedef std::map<W::Address, W::Vocabulary> SMap;
typedef std::map<uint64_t, SMap> Map;
W::Address address;
bool registered;
Map* subscribers;
void send(W::Vocabulary* vc, const W::Address& destination, uint64_t connectionId);
void response(W::Vocabulary* vc, const W::Address& handlerAddress, const W::Event& src);
void fakeResponse(W::Vocabulary* vc, const W::Address& handlerAddress, const W::Address& sourceAddress, const W::Event& src);
void broadcast(W::Vocabulary* vc, const W::Address& handlerAddress);
handler(subscribe)
handler(unsubscribe)
private:
typedef W::Order<W::Handler*> HList;
typedef W::Order<M::Model*> MList;
W::Dispatcher* dispatcher;
W::Server* server;
uint64_t subscribersCount;
HList* handlers;
W::Vector* properties;
MList* models;
private slots:
void onSocketDisconnected();
};
}
#endif // W_MODEL_H

View File

@ -0,0 +1,77 @@
#include "modelstring.h"
M::String::String(const W::String& str, const W::Address& addr, QObject* parent):
M::Model(addr, parent),
data(new W::String(str))
{
addHandler(W::Handler::create(address + W::Address({u"get"}), this, &M::String::_h_get));
}
M::String::String(W::String* str, const W::Address& addr, QObject* parent):
M::Model(addr, parent),
data(str)
{
}
M::String::~String()
{
delete data;
}
void M::String::h_subscribe(const W::Event& ev)
{
M::Model::h_subscribe(ev);
h_get(ev);
}
void M::String::h_get(const W::Event& ev)
{
W::Vocabulary* vc = new W::Vocabulary;
vc->insert(u"data", *data);
response(vc, W::Address({u"get"}), ev);
}
void M::String::set(const W::String& str)
{
delete data;
data = static_cast<W::String*>(str.copy());
if (registered) {
W::Vocabulary* vc = new W::Vocabulary();
vc->insert(u"data", str);
broadcast(vc, W::Address{u"get"});
}
}
void M::String::set(W::String* str)
{
delete data;
data = str;
if (registered) {
W::Vocabulary* vc = new W::Vocabulary();
vc->insert(u"data", *str);
broadcast(vc, W::Address{u"get"});
}
}
void M::String::set(const W::Object& value)
{
set(static_cast<const W::String&>(value));
}
void M::String::set(W::Object* value)
{
set(static_cast<W::String*>(value));
}
M::Model::ModelType M::String::getType() const
{
return type;
}

40
lib/wModel/modelstring.h Normal file
View File

@ -0,0 +1,40 @@
#ifndef M_STRING_H
#define M_STRING_H
#include "model.h"
#include <QtCore/QObject>
#include <wType/string.h>
#include <wType/address.h>
namespace M {
class String: public Model
{
public:
String(const W::String& str, const W::Address& addr, QObject* parent = 0);
String(W::String* str, const W::Address& addr, QObject* parent = 0);
~String();
void set(const W::Object & value) override;
void set(W::Object * value) override;
void set(const W::String& str);
void set(W::String* str);
M::Model::ModelType getType() const override;
static const M::Model::ModelType type = M::Model::string;
protected:
void h_subscribe(const W::Event& ev);
handler(get)
private:
W::String* data;
};
}
#endif // M_STRING_H

136
lib/wModel/vocabulary.cpp Normal file
View File

@ -0,0 +1,136 @@
#include "vocabulary.h"
M::Vocabulary::Vocabulary(const W::Address p_address, QObject* parent):
M::Model(p_address, parent),
data(new W::Vocabulary())
{
W::Handler* get = W::Handler::create(address + W::Address({u"get"}), this, &M::Vocabulary::_h_get);
addHandler(get);
}
M::Vocabulary::Vocabulary(W::Vocabulary* p_data, const W::Address p_address, QObject* parent):
M::Model(p_address, parent),
data(p_data)
{
W::Handler* get = W::Handler::create(address + W::Address({u"get"}), this, &M::Vocabulary::_h_get);
addHandler(get);
}
M::Vocabulary::~Vocabulary()
{
delete data;
}
void M::Vocabulary::h_subscribe(const W::Event& ev)
{
M::Model::h_subscribe(ev);
h_get(ev);
}
void M::Vocabulary::h_get(const W::Event& ev)
{
W::Vocabulary* vc = new W::Vocabulary;
vc->insert(u"data", data->copy());
response(vc, W::Address({u"get"}), ev);
}
void M::Vocabulary::insert(const W::String& key, const W::Object& value)
{
if (registered) {
W::Vocabulary* vc = new W::Vocabulary();
W::Vocabulary* insert = new W::Vocabulary();
W::Vector* erase = new W::Vector();
if (data->has(key)) {
erase->push(key);
}
data->insert(key, value);
insert->insert(key, value);
vc->insert(u"insert", insert);
vc->insert(u"erase", erase);
broadcast(vc, W::Address{u"change"});
} else {
data->insert(key, value);
}
}
void M::Vocabulary::insert(const W::String& key, W::Object* value)
{
if (registered) {
W::Vocabulary* vc = new W::Vocabulary();
W::Vocabulary* insert = new W::Vocabulary();
W::Vector* erase = new W::Vector();
if (data->has(key)) {
erase->push(key);
}
data->insert(key, value);
insert->insert(key, value->copy());
vc->insert(u"insert", insert);
vc->insert(u"erase", erase);
broadcast(vc, W::Address{u"change"});
} else {
data->insert(key, value);
}
}
void M::Vocabulary::erase(const W::String& key)
{
data->erase(key);
if (registered) {
W::Vocabulary* vc = new W::Vocabulary();
W::Vocabulary* insert = new W::Vocabulary();
W::Vector* erase = new W::Vector();
erase->push(key);
vc->insert(u"insert", insert);
vc->insert(u"erase", erase);
broadcast(vc, W::Address{u"change"});
}
}
void M::Vocabulary::clear()
{
data->clear();
if (registered) {
broadcast(new W::Vocabulary(), W::Address{u"clear"});
}
}
M::Model::ModelType M::Vocabulary::getType() const
{
return type;
}
void M::Vocabulary::set(const W::Object& value)
{
delete data;
data = static_cast<W::Vocabulary*>(value.copy());
W::Vocabulary* vc = new W::Vocabulary;
vc->insert(u"data", data->copy());
broadcast(vc, W::Address({u"get"}));
}
void M::Vocabulary::set(W::Object* value)
{
delete data;
data = static_cast<W::Vocabulary*>(value);
W::Vocabulary* vc = new W::Vocabulary;
vc->insert(u"data", data->copy());
broadcast(vc, W::Address({u"get"}));
}

45
lib/wModel/vocabulary.h Normal file
View File

@ -0,0 +1,45 @@
#ifndef M_VOCABULARY_H
#define M_VOCABULARY_H
#include "model.h"
#include <wType/object.h>
#include <wType/address.h>
#include <wType/vocabulary.h>
#include <wType/vector.h>
namespace M {
class ICatalogue;
class Vocabulary : public M::Model
{
friend class ICatalogue;
public:
Vocabulary(const W::Address p_address, QObject* parent = 0);
Vocabulary(W::Vocabulary* p_data, const W::Address p_address, QObject* parent = 0);
~Vocabulary();
void insert(const W::String& key, const W::Object& value);
void insert(const W::String& key, W::Object* value);
void erase(const W::String& key);
void clear();
void set(const W::Object & value) override;
void set(W::Object* value) override;
M::Model::ModelType getType() const override;
static const M::Model::ModelType type = vocabulary;
protected:
void h_subscribe(const W::Event & ev) override;
handler(get);
private:
W::Vocabulary* data;
};
}
#endif // M_VOCABULARY_H

View File

@ -0,0 +1,26 @@
cmake_minimum_required(VERSION 2.8.12)
project(wServerUtils)
find_package(Qt5Core REQUIRED)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOMOC ON)
set(HEADERS
commands.h
connector.h
)
set(SOURCES
commands.cpp
connector.cpp
)
add_library(wServerUtils ${HEADERS} ${SOURCES})
target_link_libraries(wServerUtils Qt5::Core)
target_link_libraries(wServerUtils wType)
target_link_libraries(wServerUtils wModel)
target_link_libraries(wServerUtils wSocket)
target_link_libraries(wServerUtils wDispatcher)

View File

@ -0,0 +1,85 @@
#include "commands.h"
U::Commands::Commands(const W::Address& address, QObject* parent):
M::Vocabulary(address, parent),
commands(new Map())
{
}
U::Commands::~Commands()
{
Map::iterator beg = commands->begin();
Map::iterator end = commands->end();
for (; beg != end; ++beg) {
Command* cmd = beg->second;
if (cmd->enabled) {
removeHandler(cmd->handler);
}
delete cmd->handler;
delete cmd;
}
delete commands;
}
void U::Commands::addCommand(const W::String& key, W::Handler* handler, const W::Vocabulary& args)
{
Map::const_iterator itr = commands->find(key);
if (itr != commands->end()) {
throw 1;
}
Command* cmd = new Command{key, handler, args, false};
commands->insert(std::make_pair(cmd->name, cmd));
}
void U::Commands::enableCommand(const W::String& key, bool value)
{
Map::const_iterator itr = commands->find(key);
if (itr == commands->end()) {
throw 2;
}
Command* cmd = itr->second;
if (cmd->enabled != value) {
if (value) {
enableCommand(cmd);
} else {
disableCommand(cmd);
}
}
}
void U::Commands::enableCommand(U::Commands::Command* cmd)
{
addHandler(cmd->handler);
cmd->enabled = true;
W::Vocabulary* vc = new W::Vocabulary;
vc->insert(u"address", cmd->handler->getAddress());
vc->insert(u"arguments", cmd->arguments);
insert(cmd->name, vc);
}
void U::Commands::disableCommand(U::Commands::Command* cmd)
{
removeHandler(cmd->handler);
cmd->enabled = false;
erase(cmd->name);
}
void U::Commands::removeCommand(const W::String& key)
{
Map::const_iterator itr = commands->find(key);
if (itr == commands->end()) {
throw 2;
}
Command* cmd = itr->second;
if (cmd->enabled) {
disableCommand(cmd);
}
commands->erase(itr);
delete cmd->handler;
delete cmd;
}

View File

@ -0,0 +1,43 @@
#ifndef SERVERUTILS_COMMANDS_H
#define SERVERUTILS_COMMANDS_H
#include <map>
#include <wType/address.h>
#include <wType/vocabulary.h>
#include <wType/event.h>
#include <wType/string.h>
#include <wModel/vocabulary.h>
namespace U {
class Commands : public M::Vocabulary
{
struct Command;
typedef std::map<W::String, Command*> Map;
public:
Commands(const W::Address& address, QObject* parent = 0);
~Commands();
void addCommand(const W::String& key, W::Handler* handler, const W::Vocabulary& args);
void removeCommand(const W::String& key);
void enableCommand(const W::String& key, bool value);
private:
void enableCommand(Command* cmd);
void disableCommand(Command* cmd);
Map* commands;
struct Command {
W::String name;
W::Handler* handler;
W::Vocabulary arguments;
bool enabled;
};
};
}
#endif // SERVERUTILS_COMMANDS_H

View File

@ -0,0 +1,124 @@
#include "connector.h"
U::Connector::Connector(W::Dispatcher* dp, W::Server* srv, U::Commands* cmds, QObject* parent):
QObject(parent),
dispatcher(dp),
server(srv),
commands(cmds),
nodes(),
ignoredNodes()
{
connect(server, SIGNAL(newConnection(const W::Socket&)), SLOT(onNewConnection(const W::Socket&)));
connect(server, SIGNAL(closedConnection(const W::Socket&)), SLOT(onClosedConnection(const W::Socket&)));
W::String cn = W::String(u"connect");
W::Handler* ch = W::Handler::create(commands->getAddress() + W::Address({cn}), this, &U::Connector::_h_connect);
W::Vocabulary vc;
vc.insert(u"address", W::Uint64(W::Object::string));
vc.insert(u"port", W::Uint64(W::Object::uint64));
commands->addCommand(cn, ch, vc);
commands->enableCommand(cn, true);
}
U::Connector::~Connector()
{
commands->removeCommand(W::String(u"connect"));
Map::const_iterator itr = nodes.begin();
Map::const_iterator end = nodes.begin();
W::String dc = W::String(u"disconnect");
for (; itr != end; ++itr) {
commands->removeCommand(dc + itr->first);
}
}
void U::Connector::addIgnoredNode(const W::String& name)
{
ignoredNodes.insert(name);
}
void U::Connector::sendTo(const W::String& name, const W::Event& event)
{
Map::const_iterator itr = nodes.find(name);
if (itr != nodes.end()) {
throw new NodeAccessError(name);
} else {
server->getConnection(itr->second).send(event);
}
}
void U::Connector::onNewConnection(const W::Socket& socket)
{
W::String name = socket.getRemoteName();
std::set<W::String>::const_iterator ign = ignoredNodes.find(name);
if (ign == ignoredNodes.end()) {
Map::const_iterator itr = nodes.find(name);
if (itr == nodes.end()) {
if (server->getName() == name) {
emit serviceMessage("An attempt to connect node to itself, closing connection");
server->closeConnection(socket.getId());
} else {
W::String dc = W::String(u"disconnect");
W::String dn = dc + name;
W::Handler* dh = W::Handler::create(commands->getAddress() + W::Address({dc, name}), this, &U::Connector::_h_disconnect);
commands->addCommand(dn, dh, W::Vocabulary());
commands->enableCommand(dn, true);
nodes.insert(std::make_pair(name, socket.getId()));
emit serviceMessage(QString("New connection, id: ") + socket.getId().toString().c_str());
connect(&socket, SIGNAL(message(const W::Event&)), dispatcher, SLOT(pass(const W::Event&)));
emit nodeConnected(name);
}
} else {
emit serviceMessage(QString("Node ") + QString(name.toString().c_str()) + " tried to connect, but connection with that node is already open, closing new connection");
server->closeConnection(socket.getId());
}
} else {
emit serviceMessage(QString("New connection, id: ") + socket.getId().toString().c_str());
connect(&socket, SIGNAL(message(const W::Event&)), dispatcher, SLOT(pass(const W::Event&)));
}
}
void U::Connector::onClosedConnection(const W::Socket& socket)
{
emit serviceMessage(QString("Connection closed, id: ") + socket.getId().toString().c_str());
W::String name = socket.getRemoteName();
std::set<W::String>::const_iterator ign = ignoredNodes.find(name);
if (ign == ignoredNodes.end()) {
Map::const_iterator itr = nodes.find(name);
if (itr != nodes.end()) {
emit nodeDisconnected(name);
commands->removeCommand(W::String(u"disconnect") + name);
nodes.erase(itr);
}
}
}
void U::Connector::h_connect(const W::Event& ev)
{
const W::Vocabulary& vc = static_cast<const W::Vocabulary&>(ev.getData());
const W::String& addr = static_cast<const W::String&>(vc.at(u"address"));
const W::Uint64& port = static_cast<const W::Uint64&>(vc.at(u"port"));
server->openConnection(addr, port);
}
void U::Connector::h_disconnect(const W::Event& ev)
{
const W::Address& addr = static_cast<const W::Address&>(ev.getDestination());
const W::String& name = addr.back();
Map::const_iterator itr = nodes.find(name);
server->closeConnection(itr->second);
}
const W::Socket& U::Connector::getNodeSocket(const W::String& name)
{
Map::const_iterator itr = nodes.find(name);
if (itr == nodes.end()) {
throw new NodeAccessError(name);
}
return server->getConnection(itr->second);
}

View File

@ -0,0 +1,68 @@
#ifndef CONNECTOR_H
#define CONNECTOR_H
#include <QtCore/QObject>
#include <map>
#include <set>
#include <wDispatcher/dispatcher.h>
#include <wSocket/socket.h>
#include <wSocket/server.h>
#include <wType/string.h>
#include <wType/uint64.h>
#include <wType/event.h>
#include <utils/exception.h>
#include "commands.h"
namespace U {
class Connector : public QObject
{
Q_OBJECT
typedef std::map<W::String, uint64_t> Map;
public:
Connector(W::Dispatcher* dp, W::Server* srv, Commands* cmds, QObject* parent = 0);
~Connector();
void addIgnoredNode(const W::String& name);
void sendTo(const W::String& name, const W::Event& event);
const W::Socket& getNodeSocket(const W::String& name);
signals:
void serviceMessage(const QString& msg);
void nodeConnected(const W::String& name);
void nodeDisconnected(const W::String& name);
private:
W::Dispatcher* dispatcher;
W::Server* server;
U::Commands* commands;
Map nodes;
std::set<W::String> ignoredNodes;
protected:
handler(connect);
handler(disconnect);
private slots:
void onNewConnection(const W::Socket& socket);
void onClosedConnection(const W::Socket& socket);
public:
class NodeAccessError:
public Utils::Exception
{
W::String name;
public:
NodeAccessError(const W::String& p_name):Exception(), name(p_name){}
std::string getMessage() const{return std::string("An attempt to access non existing node ") + name.toString();}
};
};
}
#endif // CONNECTOR_H

View File

@ -0,0 +1,27 @@
cmake_minimum_required(VERSION 2.8.12)
project(wSocket)
find_package(Qt5Core REQUIRED)
find_package(Qt5Network REQUIRED)
find_package(Qt5WebSockets REQUIRED)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOMOC ON)
set(HEADERS
socket.h
server.h
)
set(SOURCES
socket.cpp
server.cpp
)
add_library(wSocket ${HEADERS} ${SOURCES})
target_link_libraries(wSocket Qt5::Core)
target_link_libraries(wSocket Qt5::Network)
target_link_libraries(wSocket Qt5::WebSockets)
target_link_libraries(wSocket wType)

158
lib/wSocket/server.cpp Normal file
View File

@ -0,0 +1,158 @@
#include "server.h"
#include <iostream>
using std::cout;
using std::endl;
W::Server::Server(const W::String& name, QObject* parent):
QObject(parent),
lastId(0),
pool(),
connections(),
server(0),
name(name)
{
server = new QWebSocketServer(name.toString().c_str(), QWebSocketServer::NonSecureMode, this);
connect(server, SIGNAL(newConnection()), SLOT(onNewConnection()));
connect(server, SIGNAL(serverError(QWebSocketProtocol::CloseCode)), SLOT(onServerError(QWebSocketProtocol::CloseCode)));
}
W::Server::~Server()
{
}
void W::Server::listen(uint16_t port)
{
if (server->listen(QHostAddress::Any, port)){
}
}
void W::Server::stop()
{
server->close();
lastId = 0;
pool.clear();
std::map<uint64_t, Socket*>::const_iterator it;
std::map<uint64_t, Socket*>::const_iterator end = connections.end();
for (it = connections.begin(); it != end; ++it) {
it->second->close();
}
}
const W::Socket& W::Server::getConnection(uint64_t p_id) const
{
std::map<uint64_t, Socket*>::const_iterator itr = connections.find(p_id);
if (itr == connections.end()) {
throw new SocketAccessError();
}
return *(itr->second);
}
uint64_t W::Server::getConnectionsCount() const
{
return connections.size();
}
void W::Server::onNewConnection()
{
QWebSocket *webSocket = server->nextPendingConnection();
Socket* wSocket = createSocket(webSocket);
wSocket->setRemoteId();
}
void W::Server::onSocketConnected() {
Socket* socket = static_cast<Socket*>(sender());
emit newConnection(*socket);
emit connectionCountChange(getConnectionsCount());
}
void W::Server::onSocketDisconnected() {
Socket* socket = static_cast<Socket*>(sender());
uint64_t socketId = socket->getId();
std::map<uint64_t, Socket*>::const_iterator it = connections.find(socketId);
connections.erase(it);
pool.insert(socketId);
emit closedConnection(*socket);
emit connectionCountChange(getConnectionsCount());
socket->deleteLater();
}
void W::Server::onServerError(QWebSocketProtocol::CloseCode code)
{
cout << "Server error: " << code << endl;
}
void W::Server::closeConnection(uint64_t p_id)
{
std::map<uint64_t, Socket*>::const_iterator itr = connections.find(p_id);
if (itr == connections.end()) {
throw new SocketAccessError();
}
itr->second->close();
}
W::Socket * W::Server::createSocket(QWebSocket* socket)
{
uint64_t connectionId;
if (pool.empty()) {
connectionId = ++lastId;
} else {
std::set<uint64_t>::const_iterator itr = pool.begin();
connectionId = *itr;
pool.erase(itr);
}
Socket *wSocket = new Socket(name, socket, connectionId, this);
connections[connectionId] = wSocket;
connect(wSocket, SIGNAL(connected()), SLOT(onSocketConnected()));
connect(wSocket, SIGNAL(disconnected()), SLOT(onSocketDisconnected()));
connect(wSocket, SIGNAL(negotiationId(uint64_t)), SLOT(onSocketNegotiationId(uint64_t)));
return wSocket;
}
void W::Server::openConnection(const W::String& addr, const W::Uint64& port)
{
QWebSocket *webSocket = new QWebSocket();
Socket* wSocket = createSocket(webSocket);
wSocket->open(addr, port);
}
void W::Server::onSocketNegotiationId(uint64_t p_id)
{
Socket* socket = static_cast<Socket*>(sender());
if (p_id == socket->id) {
socket->setRemoteName();
} else {
std::set<uint64_t>::const_iterator pItr = pool.lower_bound(p_id);
uint64_t newId;
if (pItr == pool.end()) {
newId = ++lastId;
} else {
newId = *pItr;
pool.erase(pItr);
}
std::map<uint64_t, Socket*>::const_iterator itr = connections.find(socket->id);
connections.erase(itr);
pool.insert(socket->id);
socket->id = Uint64(newId);
connections[newId] = socket;
socket->setRemoteId();
}
}
W::String W::Server::getName() const
{
return name;
}

79
lib/wSocket/server.h Normal file
View File

@ -0,0 +1,79 @@
#ifndef SERVER_H
#define SERVER_H
#include <QtCore/QObject>
#include <QtWebSockets/QWebSocketServer>
#include <QtNetwork/QHostAddress>
#include <wType/string.h>
#include <stdint.h>
#include <map>
#include <set>
#include "socket.h"
#include <utils/exception.h>
namespace W
{
class Server:
public QObject
{
Q_OBJECT
public:
explicit Server(const String& name, QObject *parent = 0);
~Server();
void listen(uint16_t port);
void stop();
const Socket& getConnection(uint64_t p_id) const;
uint64_t getConnectionsCount() const;
void closeConnection(uint64_t p_id);
void openConnection(const String& addr, const Uint64& port);
String getName() const;
private:
uint64_t lastId;
std::set<uint64_t> pool;
std::map<uint64_t, Socket*> connections;
QWebSocketServer* server;
String name;
Socket* createSocket(QWebSocket* socket);
signals:
void newConnection(const W::Socket&);
void closedConnection(const W::Socket&);
void connectionCountChange(uint64_t count);
private slots:
void onNewConnection();
void onServerError(QWebSocketProtocol::CloseCode code);
void onSocketConnected();
void onSocketDisconnected();
void onSocketNegotiationId(uint64_t p_id);
private:
class HandshakeNameError:
public Utils::Exception
{
public:
HandshakeNameError():Exception(){}
std::string getMessage() const{return "Name of connected socket haven't been found, but registering returned an error";}
};
class SocketAccessError:
public Utils::Exception
{
public:
SocketAccessError():Exception(){}
std::string getMessage() const{return "An attempt to access non existing socket";}
};
};
}
#endif // SERVER_H

256
lib/wSocket/socket.cpp Normal file
View File

@ -0,0 +1,256 @@
#include "socket.h"
#include <iostream>
using std::cout;
using std::endl;
W::Socket::Socket(const W::String& p_name, QObject* parent):
QObject(parent),
serverCreated(false),
state(disconnected_s),
dState(dSize),
socket(new QWebSocket()),
id(0),
name(p_name),
remoteName(),
helperBuffer(new W::ByteArray(4))
{
socket->setParent(this);
setHandlers();
}
W::Socket::Socket(const W::String& p_name, QWebSocket* p_socket, uint64_t p_id, QObject* parent):
QObject(parent),
serverCreated(true),
state(disconnected_s),
dState(dSize),
socket(p_socket),
id(p_id),
name(p_name),
remoteName(),
helperBuffer(new W::ByteArray(4))
{
socket->setParent(this);
setHandlers();
}
W::Socket::~Socket()
{
close();
delete helperBuffer;
}
void W::Socket::open(const W::String& addr, const W::Uint64& port)
{
if (state == disconnected_s) {
String::StdStr url_str("ws://" + addr.toString() + ":" + port.toString());
QUrl url(url_str.c_str());
remoteName = String();
state = connecting_s;
socket->open(url);
}
}
void W::Socket::close()
{
if (state != disconnected_s && state != disconnecting_s) {
state = disconnecting_s;
socket->close();
}
}
void W::Socket::send(const W::Event& ev) const
{
//std::cout << "Sending event: " << ev.toString() << std::endl;
W::Object::size_type size = ev.size();
ByteArray *wba = new ByteArray(size + 5);
wba->push32(size);
wba->push8(ev.getType());
ev.serialize(*wba);
QByteArray ba = QByteArray::fromRawData(wba->getData(), wba->size());
socket->sendBinaryMessage(ba);
delete wba;
}
W::Uint64 W::Socket::getId() const
{
return id;
}
W::String W::Socket::getRemoteName() const
{
return remoteName; //TODO may be throw the exception, when socket is not connected?
}
W::String W::Socket::getName() const
{
return name;
}
void W::Socket::setHandlers() {
connect(socket, SIGNAL(connected()), SLOT(onSocketConnected()));
connect(socket, SIGNAL(disconnected()), SLOT(onSocketDisconnected()));
connect(socket, SIGNAL(binaryMessageReceived(const QByteArray&)), SLOT(onBinaryMessageReceived(const QByteArray&)));
connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(onSocketError(QAbstractSocket::SocketError)));
}
void W::Socket::onSocketConnected()
{
dState = dSize;
delete helperBuffer;
helperBuffer = new W::ByteArray(4);
}
void W::Socket::onSocketDisconnected()
{
state = disconnected_s;
emit disconnected();
}
void W::Socket::onBinaryMessageReceived(const QByteArray& ba)
{
int i = 0;
while (i < ba.size()) {
switch (dState) {
case dSize:
i = helperBuffer->fill(ba.data(), ba.size(), i);
if (helperBuffer->filled()) {
int size = helperBuffer->pop32();
delete helperBuffer;
helperBuffer = new W::ByteArray(size + 1);
dState = dBody;
}
break;
case dBody:
i = helperBuffer->fill(ba.data(), ba.size(), i);
if (helperBuffer->filled()) {
Event* ev = static_cast<Event*>(W::Object::fromByteArray(*helperBuffer));
onEvent(ev);
delete ev;
delete helperBuffer;
helperBuffer = new W::ByteArray(4);
dState = dSize;
}
break;
}
}
}
void W::Socket::onEvent(W::Event* ev)
{
if (ev->isSystem()) {
const Vocabulary& vc = static_cast<const Vocabulary&>(ev->getData());
const String& command = static_cast<const String&>(vc.at(u"command"));
if (command == u"setId") {
if (serverCreated) {
if (state == connecting_s) {
emit negotiationId(static_cast<const Uint64&>(vc.at(u"id")));
} else {
throw ErrorIdSetting();
}
} else {
setId(static_cast<const Uint64&>(vc.at(u"id")));
setRemoteName();
}
} else if (command == u"setName") {
setName(static_cast<const String&>(vc.at(u"name")));
if (static_cast<const String&>(vc.at(u"yourName")) != name) {
setRemoteName();
}
emit connected();
}
} else {
emit message(*ev);
}
}
void W::Socket::setId(const W::Uint64& p_id)
{
if (state == connecting_s)
{
id = p_id;
}
else
{
throw ErrorIdSetting();
}
}
void W::Socket::setRemoteId()
{
if (state == disconnected_s) {
state = connecting_s;
}
String command(u"setId");
Vocabulary *vc = new Vocabulary();
vc->insert(u"command", command);
vc->insert(u"id", id);
Address addr;
Event ev(addr, vc, true);
ev.setSenderId(id);
send(ev);
}
void W::Socket::setRemoteName()
{
String command(u"setName");
Vocabulary *vc = new Vocabulary();
vc->insert(u"command", command);
vc->insert(u"name", name);
vc->insert(u"yourName", remoteName);
Address addr;
Event ev(addr, vc, true);
ev.setSenderId(id);
send(ev);
}
void W::Socket::setName(const W::String& p_name)
{
if (state == connecting_s)
{
remoteName = p_name;
state = connected_s;
}
else
{
throw ErrorNameSetting();
}
}
void W::Socket::cantDeliver(const W::Event& event) const
{
String command(u"cantDeliver");
Vocabulary *vc = new Vocabulary();
vc->insert(u"command", command);
vc->insert(u"event", event);
Address addr;
Event ev(addr, vc, true);
ev.setSenderId(id);
send(ev);
}
void W::Socket::onSocketError(QAbstractSocket::SocketError err)
{
if (state == connecting_s) {
state = disconnected_s;
}
//socket->close();
emit error(err, socket->errorString());
}

108
lib/wSocket/socket.h Normal file
View File

@ -0,0 +1,108 @@
#ifndef SOCKET_H
#define SOCKET_H
#include <QtCore/QObject>
#include <QtWebSockets/QWebSocket>
#include <QtCore/QByteArray>
#include <wType/string.h>
#include <wType/uint64.h>
#include <wType/bytearray.h>
#include <wType/event.h>
#include <wType/vocabulary.h>
#include <utils/exception.h>
namespace W
{
class Socket:
public QObject
{
Q_OBJECT
friend class Server;
enum State
{
disconnected_s,
disconnecting_s,
connecting_s,
connected_s
};
enum DeserializationState {
dSize,
dBody
};
public:
explicit Socket(const String& p_name, QObject* parent = 0);
~Socket();
void send(const Event& ev) const;
void open(const String& addr, const Uint64& port);
void close();
Uint64 getId() const;
String getRemoteName() const;
String getName() const;
typedef QAbstractSocket::SocketError SocketError;
private:
explicit Socket(const String& p_name, QWebSocket *p_socket, uint64_t p_id, QObject *parent = 0);
void setHandlers();
void setId(const Uint64& p_id);
void setRemoteId();
void setRemoteName();
void setName(const String& p_name);
bool serverCreated;
State state;
DeserializationState dState;
QWebSocket *socket;
Uint64 id;
String name;
String remoteName;
ByteArray* helperBuffer;
signals:
void connected();
void disconnected();
void negotiationId(uint64_t p_id);
void error(W::Socket::SocketError err, const QString& msg);
void message(const W::Event&);
void proxy(const W::Event&);
public slots:
void cantDeliver(const Event& event) const;
private slots:
void onSocketConnected();
void onSocketDisconnected();
void onSocketError(QAbstractSocket::SocketError err);
void onBinaryMessageReceived(const QByteArray& ba);
void onEvent(W::Event* ev);
private:
class ErrorIdSetting:
public Utils::Exception
{
public:
ErrorIdSetting():Exception(){}
std::string getMessage() const{return "An attempt to set id to the socket not in connecting state";}
};
class ErrorNameSetting:
public Utils::Exception
{
public:
ErrorNameSetting():Exception(){}
std::string getMessage() const{return "An attempt to set name to the socket not in connecting state";}
};
};
}
#endif // SOCKET_H

Some files were not shown because too many files have changed in this diff Show More