initial commit
This commit is contained in:
commit
4b60ece582
34
CMakeLists.txt
Normal file
34
CMakeLists.txt
Normal 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
34
README.md
Normal 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
40
corax/CMakeLists.txt
Normal 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
236
corax/corax.cpp
Normal 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
88
corax/corax.h
Normal 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
18
corax/main.cpp
Normal 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
61
corax/tools/audioid.cpp
Normal 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
28
corax/tools/audioid.h
Normal 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
35
corax/tools/audiotag.cpp
Normal 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
29
corax/tools/audiotag.h
Normal 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
309
corax/tools/parser.cpp
Normal 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
71
corax/tools/parser.h
Normal 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
15
lib/CMakeLists.txt
Normal 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)
|
17
lib/fontParser/CMakeLists.txt
Normal file
17
lib/fontParser/CMakeLists.txt
Normal 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
223
lib/fontParser/font.cpp
Normal 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
62
lib/fontParser/font.h
Normal 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
50
lib/fontParser/main.cpp
Normal 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;
|
||||
}
|
13
lib/fontParser/tables/CMakeLists.txt
Normal file
13
lib/fontParser/tables/CMakeLists.txt
Normal 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})
|
218
lib/fontParser/tables/cmap.cpp
Normal file
218
lib/fontParser/tables/cmap.cpp
Normal 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;
|
||||
}
|
66
lib/fontParser/tables/cmap.h
Normal file
66
lib/fontParser/tables/cmap.h
Normal 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
|
93
lib/fontParser/tables/head.cpp
Normal file
93
lib/fontParser/tables/head.cpp
Normal 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;
|
||||
}
|
28
lib/fontParser/tables/head.h
Normal file
28
lib/fontParser/tables/head.h
Normal 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
|
76
lib/fontParser/tables/hhea.cpp
Normal file
76
lib/fontParser/tables/hhea.cpp
Normal 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();
|
||||
}
|
27
lib/fontParser/tables/hhea.h
Normal file
27
lib/fontParser/tables/hhea.h
Normal 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
|
59
lib/fontParser/tables/hmtx.cpp
Normal file
59
lib/fontParser/tables/hmtx.cpp
Normal 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);
|
||||
}
|
29
lib/fontParser/tables/hmtx.h
Normal file
29
lib/fontParser/tables/hmtx.h
Normal 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
|
136
lib/fontParser/tables/name.cpp
Normal file
136
lib/fontParser/tables/name.cpp
Normal 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)
|
||||
{
|
||||
}
|
35
lib/fontParser/tables/name.h
Normal file
35
lib/fontParser/tables/name.h
Normal 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
|
56
lib/fontParser/tables/table.cpp
Normal file
56
lib/fontParser/tables/table.cpp
Normal 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;
|
||||
}
|
26
lib/fontParser/tables/table.h
Normal file
26
lib/fontParser/tables/table.h
Normal 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
15
lib/tools/CMakeLists.txt
Normal 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
115
lib/tools/file.cpp
Normal 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
33
lib/tools/file.h
Normal 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
22
lib/utils/CMakeLists.txt
Normal 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
9
lib/utils/defines.h
Normal 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
14
lib/utils/exception.cpp
Normal 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
22
lib/utils/exception.h
Normal 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
|
59
lib/utils/signalcatcher.cpp
Normal file
59
lib/utils/signalcatcher.cpp
Normal 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
33
lib/utils/signalcatcher.h
Normal 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
|
2
lib/wContainer/CMakeLists.txt
Normal file
2
lib/wContainer/CMakeLists.txt
Normal file
@ -0,0 +1,2 @@
|
||||
cmake_minimum_required(VERSION 2.8.12)
|
||||
|
136
lib/wContainer/order.h
Normal file
136
lib/wContainer/order.h
Normal 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
|
35
lib/wController/CMakeLists.txt
Normal file
35
lib/wController/CMakeLists.txt
Normal 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)
|
||||
|
87
lib/wController/attributes.cpp
Normal file
87
lib/wController/attributes.cpp
Normal 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();
|
||||
}
|
43
lib/wController/attributes.h
Normal file
43
lib/wController/attributes.h
Normal 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
|
220
lib/wController/catalogue.cpp
Normal file
220
lib/wController/catalogue.cpp
Normal 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"});
|
||||
}
|
63
lib/wController/catalogue.h
Normal file
63
lib/wController/catalogue.h
Normal 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
|
136
lib/wController/collection.cpp
Normal file
136
lib/wController/collection.cpp
Normal 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);
|
||||
}
|
52
lib/wController/collection.h
Normal file
52
lib/wController/collection.h
Normal 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
|
278
lib/wController/controller.cpp
Normal file
278
lib/wController/controller.cpp
Normal 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;
|
||||
}
|
82
lib/wController/controller.h
Normal file
82
lib/wController/controller.h
Normal 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
|
25
lib/wController/controllerstring.cpp
Normal file
25
lib/wController/controllerstring.cpp
Normal 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);
|
||||
}
|
33
lib/wController/controllerstring.h
Normal file
33
lib/wController/controllerstring.h
Normal 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
57
lib/wController/list.cpp
Normal 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
36
lib/wController/list.h
Normal 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
|
119
lib/wController/vocabulary.cpp
Normal file
119
lib/wController/vocabulary.cpp
Normal 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);
|
||||
}
|
49
lib/wController/vocabulary.h
Normal file
49
lib/wController/vocabulary.h
Normal 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
|
25
lib/wDatabase/CMakeLists.txt
Normal file
25
lib/wDatabase/CMakeLists.txt
Normal 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
230
lib/wDatabase/database.cpp
Normal 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
56
lib/wDatabase/database.h
Normal 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
1913
lib/wDatabase/lmdb++.h
Normal file
File diff suppressed because it is too large
Load Diff
277
lib/wDatabase/resourcecache.cpp
Normal file
277
lib/wDatabase/resourcecache.cpp
Normal 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();
|
||||
}
|
84
lib/wDatabase/resourcecache.h
Normal file
84
lib/wDatabase/resourcecache.h
Normal 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
|
27
lib/wDispatcher/CMakeLists.txt
Normal file
27
lib/wDispatcher/CMakeLists.txt
Normal 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)
|
11
lib/wDispatcher/defaulthandler.cpp
Normal file
11
lib/wDispatcher/defaulthandler.cpp
Normal file
@ -0,0 +1,11 @@
|
||||
#include "defaulthandler.h"
|
||||
|
||||
W::DefaultHandler::DefaultHandler()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
W::DefaultHandler::~DefaultHandler()
|
||||
{
|
||||
|
||||
}
|
18
lib/wDispatcher/defaulthandler.h
Normal file
18
lib/wDispatcher/defaulthandler.h
Normal 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
|
84
lib/wDispatcher/dispatcher.cpp
Normal file
84
lib/wDispatcher/dispatcher.cpp
Normal 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);
|
||||
}
|
48
lib/wDispatcher/dispatcher.h
Normal file
48
lib/wDispatcher/dispatcher.h
Normal 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
|
17
lib/wDispatcher/handler.cpp
Normal file
17
lib/wDispatcher/handler.cpp
Normal 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
56
lib/wDispatcher/handler.h
Normal 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
|
24
lib/wDispatcher/logger.cpp
Normal file
24
lib/wDispatcher/logger.cpp
Normal 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
21
lib/wDispatcher/logger.h
Normal 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
|
44
lib/wDispatcher/parentreporter.cpp
Normal file
44
lib/wDispatcher/parentreporter.cpp
Normal 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));
|
||||
}
|
||||
}
|
25
lib/wDispatcher/parentreporter.h
Normal file
25
lib/wDispatcher/parentreporter.h
Normal 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
36
lib/wModel/CMakeLists.txt
Normal 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
60
lib/wModel/attributes.cpp
Normal 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
32
lib/wModel/attributes.h
Normal 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
66
lib/wModel/catalogue.cpp
Normal 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
29
lib/wModel/catalogue.h
Normal 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
86
lib/wModel/file/file.cpp
Normal 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
44
lib/wModel/file/file.h
Normal 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
469
lib/wModel/icatalogue.cpp
Normal 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
316
lib/wModel/icatalogue.h
Normal 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
100
lib/wModel/list.cpp
Normal 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
41
lib/wModel/list.h
Normal 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
339
lib/wModel/model.cpp
Normal 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
92
lib/wModel/model.h
Normal 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
|
||||
|
77
lib/wModel/modelstring.cpp
Normal file
77
lib/wModel/modelstring.cpp
Normal 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
40
lib/wModel/modelstring.h
Normal 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
136
lib/wModel/vocabulary.cpp
Normal 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
45
lib/wModel/vocabulary.h
Normal 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
|
26
lib/wServerUtils/CMakeLists.txt
Normal file
26
lib/wServerUtils/CMakeLists.txt
Normal 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)
|
||||
|
85
lib/wServerUtils/commands.cpp
Normal file
85
lib/wServerUtils/commands.cpp
Normal 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;
|
||||
}
|
43
lib/wServerUtils/commands.h
Normal file
43
lib/wServerUtils/commands.h
Normal 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
|
124
lib/wServerUtils/connector.cpp
Normal file
124
lib/wServerUtils/connector.cpp
Normal 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);
|
||||
}
|
68
lib/wServerUtils/connector.h
Normal file
68
lib/wServerUtils/connector.h
Normal 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
|
27
lib/wSocket/CMakeLists.txt
Normal file
27
lib/wSocket/CMakeLists.txt
Normal 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
158
lib/wSocket/server.cpp
Normal 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
79
lib/wSocket/server.h
Normal 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
256
lib/wSocket/socket.cpp
Normal 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
108
lib/wSocket/socket.h
Normal 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
Loading…
Reference in New Issue
Block a user