From 68a391e7ad224c124355a902e5a9f728d8cfa3f8 Mon Sep 17 00:00:00 2001 From: Edouard DUPIN Date: Mon, 10 Sep 2018 21:42:09 +0200 Subject: [PATCH] [DEV] continue rework IO interface with abstract provider --- data/data/.file_hidden.txt | 1 + data/data/dir_A/file_A_1.txt | 1 + data/data/dir_B/dir_C/file_C_1.txt | 1 + data/data/dir_B/file_B_1.txt | 1 + data/data/dir_B/file_B_2.txt | 1 + data/data/file_1.txt | 1 + data/data/file_2.txt | 1 + data/data/file_3.txt | 1 + data/data_sample.zip | Bin 0 -> 2067 bytes etest/etest.cpp | 2 +- etk-core/String.hpp | 4 + etk-core/UString.hpp | 4 + etk-core/Vector.hpp | 2 +- etk/archive/Archive.cpp | 53 +++++- etk/archive/Archive.hpp | 13 ++ etk/archive/Zip.cpp | 44 +++++ etk/archive/Zip.hpp | 6 + etk/etk.cpp | 14 +- etk/fs/Path.hpp | 8 + etk/fs/fileSystem.cpp | 27 ++- etk/fs/fileSystem.hpp | 14 +- etk/uri/IoProvider.cpp | 0 etk/uri/IoProvider.hpp | 26 --- etk/uri/Query.cpp | 18 +- etk/uri/Query.hpp | 24 +++ etk/uri/Uri.cpp | 63 ++++++- etk/uri/Uri.hpp | 39 ++++- etk/uri/provider/Interface.hpp | 31 ++++ etk/uri/provider/ProviderFile.cpp | 55 +++++++ etk/uri/provider/ProviderFile.hpp | 27 +++ etk/uri/provider/ProviderFileZip.cpp | 62 +++++++ etk/uri/provider/ProviderFileZip.hpp | 32 ++++ etk/uri/provider/provider.cpp | 68 ++++++++ etk/uri/provider/provider.hpp | 65 ++++++++ lutin_etk-test.py | 5 + lutin_etk.py | 9 +- test/main.cpp | 6 +- test/testUri.cpp | 46 ++++++ test/testUriProvider.cpp | 238 +++++++++++++++++++++++++++ 39 files changed, 958 insertions(+), 55 deletions(-) create mode 100644 data/data/.file_hidden.txt create mode 100644 data/data/dir_A/file_A_1.txt create mode 100644 data/data/dir_B/dir_C/file_C_1.txt create mode 100644 data/data/dir_B/file_B_1.txt create mode 100644 data/data/dir_B/file_B_2.txt create mode 100644 data/data/file_1.txt create mode 100644 data/data/file_2.txt create mode 100644 data/data/file_3.txt create mode 100644 data/data_sample.zip delete mode 100644 etk/uri/IoProvider.cpp delete mode 100644 etk/uri/IoProvider.hpp create mode 100644 etk/uri/provider/Interface.hpp create mode 100644 etk/uri/provider/ProviderFile.cpp create mode 100644 etk/uri/provider/ProviderFile.hpp create mode 100644 etk/uri/provider/ProviderFileZip.cpp create mode 100644 etk/uri/provider/ProviderFileZip.hpp create mode 100644 etk/uri/provider/provider.cpp create mode 100644 etk/uri/provider/provider.hpp create mode 100644 test/testUriProvider.cpp diff --git a/data/data/.file_hidden.txt b/data/data/.file_hidden.txt new file mode 100644 index 0000000..ffb814e --- /dev/null +++ b/data/data/.file_hidden.txt @@ -0,0 +1 @@ +file_hidden.txt \ No newline at end of file diff --git a/data/data/dir_A/file_A_1.txt b/data/data/dir_A/file_A_1.txt new file mode 100644 index 0000000..604c0c2 --- /dev/null +++ b/data/data/dir_A/file_A_1.txt @@ -0,0 +1 @@ +file_A_1.txt \ No newline at end of file diff --git a/data/data/dir_B/dir_C/file_C_1.txt b/data/data/dir_B/dir_C/file_C_1.txt new file mode 100644 index 0000000..4baafc5 --- /dev/null +++ b/data/data/dir_B/dir_C/file_C_1.txt @@ -0,0 +1 @@ +file_C_1.txt \ No newline at end of file diff --git a/data/data/dir_B/file_B_1.txt b/data/data/dir_B/file_B_1.txt new file mode 100644 index 0000000..af2ff69 --- /dev/null +++ b/data/data/dir_B/file_B_1.txt @@ -0,0 +1 @@ +file_B_1.txt \ No newline at end of file diff --git a/data/data/dir_B/file_B_2.txt b/data/data/dir_B/file_B_2.txt new file mode 100644 index 0000000..e650576 --- /dev/null +++ b/data/data/dir_B/file_B_2.txt @@ -0,0 +1 @@ +file_B_2.txt \ No newline at end of file diff --git a/data/data/file_1.txt b/data/data/file_1.txt new file mode 100644 index 0000000..7ec3e9c --- /dev/null +++ b/data/data/file_1.txt @@ -0,0 +1 @@ +file_1.txt \ No newline at end of file diff --git a/data/data/file_2.txt b/data/data/file_2.txt new file mode 100644 index 0000000..e4678c3 --- /dev/null +++ b/data/data/file_2.txt @@ -0,0 +1 @@ +file_2.txt \ No newline at end of file diff --git a/data/data/file_3.txt b/data/data/file_3.txt new file mode 100644 index 0000000..2c0101a --- /dev/null +++ b/data/data/file_3.txt @@ -0,0 +1 @@ +file_3.txt \ No newline at end of file diff --git a/data/data_sample.zip b/data/data_sample.zip new file mode 100644 index 0000000000000000000000000000000000000000..9e54bdf40f8a6b310ab3d8c9461e92d0b52cf7d6 GIT binary patch literal 2067 zcma)-!An~~5XL7aky@1~D0-;&CfI{P>rs%rhzKGCwWl0{@FJ$Ds6;C0MG&lbDK+%c z(t`-%-ynF@KS9v5MJT1vW7~r>^WMIleY;5vYzSfAZ@>L^=6i+NWa=f*=f(2y9RCQv z((nrWrRq}l-C`RgH~!X&w6=HJA(47a0;GH_EJTmb;NwI0m%{|3l~Io`S60iVwR@bL&yr#4PQ z=%~dH-)@~gdRg|-6F!o@0~;voG<+zguv=b|m!G zD!yM{kGh}}Ti-F3vC_h9qGd2i9pxJHe*Pbz3x4J3XE^lfVCZ9_CCVQ80F^+<+5V(L zv6wIb5K4{F0BR4t)c09IRRkc=IsW>B*8pyVq>0sVjVLqHQV7JHQ%_ftUQVe+=8N_Nk^FBAh&pfFHY_V! etk::Archive::load(const etk::Path& _fileName) TK_ERROR("An error occured when load archive : " << _fileName); } } else { - TK_ERROR("Extention not managed " << _fileName << " Sopported extention : .zip"); + TK_ERROR("Extention not managed '" << _fileName << "' Supported extention : .zip"); + } + return output; +} +ememory::SharedPtr etk::Archive::load(const etk::Uri& _uri) { + ememory::SharedPtr output; + etk::String extention = _uri.getPath().getExtention().toLower(); + // select the corect Loader : + if( extention == "zip" + || extention == ".apk") { + output = ememory::makeShared(_uri); + if (output == null) { + TK_ERROR("An error occured when load archive : " << _uri); + } + } else { + TK_ERROR("Extention not managed '" << _uri << "' Supported extention : .zip"); } return output; } @@ -138,4 +153,38 @@ void etk::Archive::close(const etk::Path& _key) { } } -#endif \ No newline at end of file +etk::Vector etk::Archive::list(const etk::Path& _path) { + etk::Vector out; + etk::String base = _path.getString(); + for (auto& it: m_content) { + etk::String name = it.first.getString(); + if (name.size() < base.size()) { + continue; + } + if (etk::start_with(name, base) == true) { + // element or subElement... + if (it.first.getParent() == _path) { + out.pushBack(it.first); + continue; + } + etk::String back = name.extract(base.size()); + if ( back.size() == 0 + || back[0] != '/') { + continue; + } + back.erase(back.begin()); + size_t pos = back.find('/'); + if (pos == etk::String::npos) { + continue; + } + etk::String value = back.extract(0, pos); + etk::Path tmpppp = etk::Path(base) / value; + if (etk::isIn(tmpppp, out) == false) { + out.pushBack(tmpppp); + } + } + } + return out; +} + +#endif diff --git a/etk/archive/Archive.hpp b/etk/archive/Archive.hpp index c6d7c3e..2ed1a94 100644 --- a/etk/archive/Archive.hpp +++ b/etk/archive/Archive.hpp @@ -12,6 +12,7 @@ #include #include #include +#include namespace etk { /** @@ -174,12 +175,24 @@ namespace etk { * @return A pointer an the specified archive, the user might delete it. */ static ememory::SharedPtr load(const etk::Path& _fileName); + /** + * @brief Load an Achive with a specific name. + * @param[in] _uri Localisation of the file. + * @return A pointer an the specified archive, the user might delete it. + */ + static ememory::SharedPtr load(const etk::Uri& _uri); /** * @brief Load an Achive with a specific name in package mode ==> this mean the data is associated with the basic binary. * @param[in] _fileName File name of the specific archive. * @return A pointer an the specified archive, the user might delete it. */ static ememory::SharedPtr loadPackage(const etk::Path& _fileName); + /** + * @brief List the content of a specific path. + * @param[in] Path to parse. + * @return the full list of path in the _path. + */ + etk::Vector list(const etk::Path& _path); }; } #endif diff --git a/etk/archive/Zip.cpp b/etk/archive/Zip.cpp index 657589c..dc61de6 100644 --- a/etk/archive/Zip.cpp +++ b/etk/archive/Zip.cpp @@ -52,6 +52,50 @@ etk::archive::Zip::Zip(const etk::Path& _fileName, uint64_t _offset) : } } } +// TODO: Support the correct URI... ==> do a structure in unzip to manage the read and write of data with a callback... +etk::archive::Zip::Zip(const etk::Uri& _uri) : + etk::Archive(_uri.getPath()), + m_ctx(null) { + if (_uri.getScheme() != "") { + TK_ERROR("Can not read and uri that is not with a scheme != of RAW, we have " << _uri); + return; + } + /* Open the zip file */ + m_ctx = unzOpenOffset(m_fileName.getNative().c_str(), 0); + if(!m_ctx) { + TK_ERROR("Unable to open the zip file '" << m_fileName << "'"); + return; + } + /* Get info about the zip file */ + if(unzGetGlobalInfo(m_ctx, &m_info) != UNZ_OK) { + TK_ERROR("Unable to read the global info related to the '" << m_fileName << "' zip file"); + return; + } + + // Store all the file in the standard structure + for(uint32_t iii=0; iii(tmpFileInfo.uncompressed_size)); + } + /* Go the the next entry listed in the zip file. */ + if((iii+1) < m_info.number_entry) { + if (unzGoToNextFile(m_ctx) != UNZ_OK) { + TK_ERROR("Could not read next file from the zip file '" << m_fileName << "'"); + return; + } + } + } +} etk::archive::Zip::~Zip() { if (m_ctx!= null) { diff --git a/etk/archive/Zip.hpp b/etk/archive/Zip.hpp index 36fc12c..eb0efde 100644 --- a/etk/archive/Zip.hpp +++ b/etk/archive/Zip.hpp @@ -6,6 +6,7 @@ #pragma once #include +#include #ifdef ETK_BUILD_MINIZIP extern "C" { #include @@ -29,6 +30,11 @@ * @param[in] _offset Offset in the file where to start the parsing of the "zip" */ Zip(const etk::Path& _fileName, uint64_t _offset = 0LL); + /** + * @brief constructor of a zip file access + * @param[in] _uri URI of the file to parse (.zip / .apk) + */ + Zip(const etk::Uri& _uri); /** * @brief basic destructor */ diff --git a/etk/etk.cpp b/etk/etk.cpp index cb26a7f..55689c7 100644 --- a/etk/etk.cpp +++ b/etk/etk.cpp @@ -13,6 +13,8 @@ #include #include #include +#include +#include static int32_t nbTimeInit = 0; @@ -28,8 +30,9 @@ void etk::unInit() { nbTimeInit = 0; return; } - etk::theme::unInit(); TK_INFO("ETK system un-init (BEGIN)"); + etk::theme::unInit(); + etk::uri::provider::unInit(); ETK_MEM_SHOW_LOG(); TK_INFO("ETK system un-init (END)"); } @@ -46,13 +49,8 @@ void etk::init(int _argc, const char** _argv) { } else { TK_INFO("ETK system init (BEGIN) "); } - elog::init(_argc, _argv); - #if !defined(__TARGET_OS__Android) and !defined(__TARGET_OS__IOs) - // This action is done by the main wrapper... - if (_argc >= 1) { - etk::setArgZero(_argv[0]); - } - #endif + elog::init(_argc, _argv, etk::fs::getBinaryName()); + etk::uri::provider::init(); etk::theme::init(); for (int32_t iii=0; iii<_argc ; ++iii) { etk::String data = _argv[iii]; diff --git a/etk/fs/Path.hpp b/etk/fs/Path.hpp index a84e36d..5b8ae79 100644 --- a/etk/fs/Path.hpp +++ b/etk/fs/Path.hpp @@ -125,6 +125,14 @@ namespace etk { * @brief Clear data. */ void clear(); + /** + * @brief Check if the path ahave data + * @return true The path is empty + * @return famse The path have some element + */ + bool isEmpty() const { + return m_data.isEmpty(); + } /** * @brief Check if the 2 Path are identical. * @param[in] _obj Path to compare. diff --git a/etk/fs/fileSystem.cpp b/etk/fs/fileSystem.cpp index f596b52..86b9616 100644 --- a/etk/fs/fileSystem.cpp +++ b/etk/fs/fileSystem.cpp @@ -407,14 +407,33 @@ etk::Path etk::fs::getBinaryPath() { return out; } -etk::Path etk::fs::getBinaryName() { +etk::String etk::fs::getBinaryName() { return getBinaryPath().getFileName(); } -#if 0 + etk::Path etk::fs::getDataPath() { - + #if defined(__TARGET_OS__Web) + return "zz_generic_zz"; + #elif defined(__TARGET_OS__Android) + return "assets"; + #elif defined(__TARGET_OS__Linux) + etk::Path dataPath = etk::Path("/usr/share") / getBinaryName(); + etk::Path theoricInstalledName = etk::Path("/usr/bin") / getBinaryName(); + TK_DEBUG(" position : '" << getBinaryPath() << "' installed position : '" << theoricInstalledName << "'"); + if (getBinaryPath() != theoricInstalledName) { + dataPath = getBinaryPath().getParent() / ".." / "share" / getBinaryName(); + } + return dataPath; + #elif defined(__TARGET_OS__Windows) + return getBinaryPath().getParent() / "data"; + #elif defined(__TARGET_OS__MacOs) + return getBinaryPath().getParent() / ".." / "Resources" / getBinaryName(); + #elif defined(__TARGET_OS__IOs) + return getBinaryPath().getParent() / "share" / getBinaryName(); + #endif + return "NO_DATA_PATH"; } -#endif + uint64_t etk::fs::getCreateTime(const etk::Path& _path) { struct stat statProperty; if (-1 == stat(_path.getNative().c_str(), &statProperty)) { diff --git a/etk/fs/fileSystem.hpp b/etk/fs/fileSystem.hpp index 8d2cc11..0599234 100644 --- a/etk/fs/fileSystem.hpp +++ b/etk/fs/fileSystem.hpp @@ -212,19 +212,17 @@ namespace etk { * @brief Current binary name. * @return executable name. */ - etk::Path getBinaryName(); + etk::String getBinaryName(); /** * @brief Full banary name (with root path). * @return the binary absolute path. */ etk::Path getBinaryPath(); - #if 0 - /** - * @brief Get the data path of the application. - * @return the root path of the data for this application. - */ - etk::Path getDataPath(); - #endif + /** + * @brief Get the data path of the application. + * @return the root path of the data for this application. + */ + etk::Path getDataPath(); /** * @brief Get the creation time of the path. * @param[in] _path Path of the requested information. diff --git a/etk/uri/IoProvider.cpp b/etk/uri/IoProvider.cpp deleted file mode 100644 index e69de29..0000000 diff --git a/etk/uri/IoProvider.hpp b/etk/uri/IoProvider.hpp deleted file mode 100644 index 05797e7..0000000 --- a/etk/uri/IoProvider.hpp +++ /dev/null @@ -1,26 +0,0 @@ -/** @file - * @author Edouard DUPIN - * @copyright 2018, Edouard DUPIN, all right reserved - * @license MPL v2.0 (see license file) - */ -#pragma once - -#include -#include -#include -#include -#include -#include - - -namespace etk { - namespace uri { - class IoProviderInterface : public ememory::EnableSharedFromThis { - public: - - }; - void addProvider(const etk::String& _scheme, ememory::SharedPtr _interface); - void removeProvider(const etk::String& _scheme); - ememory::SharedPtr provideIO(const etk::uri::Uri& _uri); - } -} diff --git a/etk/uri/Query.cpp b/etk/uri/Query.cpp index bb01048..c303514 100644 --- a/etk/uri/Query.cpp +++ b/etk/uri/Query.cpp @@ -147,4 +147,20 @@ etk::Stream& etk::operator <<(etk::Stream& _os, const etk::uri::Query& _obj) { bool etk::uri::Query::isEmpty() const { return m_data.size() == 0; -} \ No newline at end of file +} + +bool etk::uri::Query::operator== (const etk::uri::Query& _obj) const { + return getEncoded() == _obj.getEncoded(); +} + +bool etk::uri::Query::operator!= (const etk::uri::Query& _obj) const { + return getEncoded() != _obj.getEncoded(); +} + +bool etk::uri::Query::operator< (const etk::uri::Query& _obj) const { + return getEncoded() < _obj.getEncoded(); +} + +bool etk::uri::Query::operator> (const etk::uri::Query& _obj) const { + return getEncoded() > _obj.getEncoded(); +} diff --git a/etk/uri/Query.hpp b/etk/uri/Query.hpp index 635e3c7..976fc63 100644 --- a/etk/uri/Query.hpp +++ b/etk/uri/Query.hpp @@ -78,6 +78,30 @@ namespace etk { * @return true The querry have no data. false Otherwise */ bool isEmpty() const; + /** + * @brief Check if the 2 Query are identical. + * @param[in] _obj Query to compare. + * @return true : same Query, false otherwise. + */ + bool operator== (const etk::uri::Query& _obj) const; + /** + * @brief Check if the 2 Query are different. + * @param[in] _obj Query to compare. + * @return false : same query, true otherwise. + */ + bool operator!= (const etk::uri::Query& _obj) const; + /** + * @brief check if this elemnt is greater than the other. + * @param[in] _obj Query to compare. + * @return true : less Query, false otherwise. + */ + bool operator< (const etk::uri::Query& _obj) const; + /** + * @brief Check if this elemnt is greater than the other. + * @param[in] _obj Query to compare. + * @return false : Greater Query, true otherwise. + */ + bool operator> (const etk::uri::Query& _obj) const; }; } //! @not_in_doc diff --git a/etk/uri/Uri.cpp b/etk/uri/Uri.cpp index 766c259..ce3fdd6 100644 --- a/etk/uri/Uri.cpp +++ b/etk/uri/Uri.cpp @@ -15,10 +15,19 @@ etk::Uri::Uri(const etk::String& _value) { set(_value); } +etk::Uri::Uri(const etk::Path& _value) { + m_path = _value; +} + etk::Uri::Uri(const char * _value) { set(_value); } +etk::Stream& etk::operator <<(etk::Stream& _os, const etk::Uri& _obj) { + _os << _obj.get(); + return _os; +} + void etk::Uri::set(const char * _value) { set(etk::String(_value)); } @@ -34,6 +43,58 @@ void etk::Uri::clear() { m_fragment.clear(); } +bool etk::Uri::isEmpty() const { + return m_scheme.isEmpty() == true + && m_user.isEmpty() == true + && m_password.isEmpty() == true + && m_server.isEmpty() == true + && m_port == 0 + && m_path.isEmpty() == true + && m_query.isEmpty() == true + && m_fragment.isEmpty() == true; +} +bool etk::Uri::operator== (const etk::Uri& _obj) const { + return m_scheme == _obj.m_scheme + && m_user == _obj.m_user + && m_password == _obj.m_password + && m_server == _obj.m_server + && m_port == _obj.m_port + && m_path == _obj.m_path + && m_query == _obj.m_query + && m_fragment == _obj.m_fragment; +} + +bool etk::Uri::operator!= (const etk::Uri& _obj) const { + return m_scheme != _obj.m_scheme + || m_user != _obj.m_user + || m_password != _obj.m_password + || m_server != _obj.m_server + || m_port != _obj.m_port + || m_path != _obj.m_path + || m_query != _obj.m_query + || m_fragment != _obj.m_fragment; +} +bool etk::Uri::operator< (const etk::Uri& _obj) const { + return m_scheme < _obj.m_scheme + && m_user < _obj.m_user + && m_password < _obj.m_password + && m_server < _obj.m_server + && m_port < _obj.m_port + && m_path < _obj.m_path + && m_query < _obj.m_query + && m_fragment < _obj.m_fragment; +} +bool etk::Uri::operator> (const etk::Uri& _obj) const { + return m_scheme > _obj.m_scheme + && m_user > _obj.m_user + && m_password > _obj.m_password + && m_server > _obj.m_server + && m_port > _obj.m_port + && m_path > _obj.m_path + && m_query > _obj.m_query + && m_fragment > _obj.m_fragment; +} + void etk::Uri::set(etk::String _value) { TK_VERBOSE("parse: '" << _value << "'"); size_t pos = _value.find("://"); @@ -92,7 +153,7 @@ void etk::Uri::set(etk::String _value) { TK_VERBOSE("find server / port : '" << m_server << "' / '" << m_port << "'"); } -etk::String etk::Uri::get() { +etk::String etk::Uri::get() const { etk::String out; if (m_scheme != "") { out += m_scheme; diff --git a/etk/uri/Uri.hpp b/etk/uri/Uri.hpp index 9341668..7f3378a 100644 --- a/etk/uri/Uri.hpp +++ b/etk/uri/Uri.hpp @@ -39,6 +39,11 @@ namespace etk { * @param[in] _value Element basic URI */ Uri(const etk::String& _value); + /** + * @brief Contructor with basic URI with a generic path. + * @param[in] _value generic path + */ + Uri(const etk::Path& _value); /** * @brief Contructor with basic URI. * @param[in] _value Element basic URI @@ -58,7 +63,7 @@ namespace etk { * @brief generate the URI string. * @return the uri correctly encoded */ - etk::String get(); + etk::String get() const; /** * @brief Get the scheme of the URI. * @return Scheme value. @@ -143,6 +148,38 @@ namespace etk { * @brief Clear the structure. */ void clear(); + /** + * @brief Check if the uri ahave data + * @return true The uri is empty + * @return famse The uri have some element + */ + bool isEmpty() const; + /** + * @brief Check if the 2 Uri are identical. + * @param[in] _obj Uri to compare. + * @return true : same Uri, false otherwise. + */ + bool operator== (const etk::Uri& _obj) const; + /** + * @brief Check if the 2 Uri are different. + * @param[in] _obj Uri to compare. + * @return false : same Uri, true otherwise. + */ + bool operator!= (const etk::Uri& _obj) const; + /** + * @brief check if this elemnt is greater than the other. + * @param[in] _obj Uri to compare. + * @return true : less Uri, false otherwise. + */ + bool operator< (const etk::Uri& _obj) const; + /** + * @brief Check if this elemnt is greater than the other. + * @param[in] _obj Uri to compare. + * @return false : Greater Uri, true otherwise. + */ + bool operator> (const etk::Uri& _obj) const; }; + //! @not_in_doc + etk::Stream& operator <<(etk::Stream& _os, const etk::Uri& _obj); } diff --git a/etk/uri/provider/Interface.hpp b/etk/uri/provider/Interface.hpp new file mode 100644 index 0000000..2f3d954 --- /dev/null +++ b/etk/uri/provider/Interface.hpp @@ -0,0 +1,31 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2018, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ +#pragma once + +#include +#include +#include +#include + + + +namespace etk { + namespace uri { + namespace provider { + class Interface : public ememory::EnableSharedFromThis { + public: + /** + * @brief Virtualize detructor: + */ + virtual ~Interface() = default; + public: + virtual ememory::SharedPtr create(const etk::Uri& _uri) = 0; + virtual bool exist(const etk::Uri& _uri) = 0; + virtual etk::Vector list(const etk::Uri& _uri) = 0; + }; + } + } +} diff --git a/etk/uri/provider/ProviderFile.cpp b/etk/uri/provider/ProviderFile.cpp new file mode 100644 index 0000000..7bcd3e6 --- /dev/null +++ b/etk/uri/provider/ProviderFile.cpp @@ -0,0 +1,55 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2018, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ +#include +#include +#include +#include + +etk::uri::provider::ProviderFile::ProviderFile() { + +} + +etk::uri::provider::ProviderFile::ProviderFile(const etk::Path& _offset) : + m_offset(_offset) { + +} + +ememory::SharedPtr etk::uri::provider::ProviderFile::create(const etk::Uri& _uri) { + if (m_offset.isEmpty() == true) { + return ememory::makeShared(_uri.getPath()); + } + return ememory::makeShared(m_offset / _uri.getPath()); +} + +bool etk::uri::provider::ProviderFile::exist(const etk::Uri& _uri) { + if (m_offset.isEmpty() == true) { + return etk::fs::exist(_uri.getPath()); + } + return etk::fs::exist(m_offset / _uri.getPath()); +} + +etk::Vector etk::uri::provider::ProviderFile::list(const etk::Uri& _uri) { + etk::Vector tmp; + etk::Vector out; + if (m_offset.isEmpty() == true) { + tmp = etk::fs::list(_uri.getPath()); + for (auto& elem: tmp) { + etk::Uri newUri = _uri; + newUri.setPath(elem); + out.pushBack(newUri); + } + return out; + } + TK_ERROR("list path: " << m_offset / _uri.getPath()); + tmp = etk::fs::list(m_offset / _uri.getPath()); + int32_t offset = m_offset.getString().size()+1; + for (auto& elem: tmp) { + etk::Uri newUri = _uri; + newUri.setPath(elem.getString().extract(offset)); + out.pushBack(newUri); + } + return out; +} diff --git a/etk/uri/provider/ProviderFile.hpp b/etk/uri/provider/ProviderFile.hpp new file mode 100644 index 0000000..6111d2b --- /dev/null +++ b/etk/uri/provider/ProviderFile.hpp @@ -0,0 +1,27 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2018, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ +#pragma once + +#include +#include + +namespace etk { + namespace uri { + namespace provider { + class ProviderFile : public etk::uri::provider::Interface { + protected: + etk::Path m_offset; + public: + ProviderFile(); + ProviderFile(const etk::Path& _offset); + public: + ememory::SharedPtr create(const etk::Uri& _uri) override; + bool exist(const etk::Uri& _uri) override; + etk::Vector list(const etk::Uri& _uri) override; + }; + } + } +} diff --git a/etk/uri/provider/ProviderFileZip.cpp b/etk/uri/provider/ProviderFileZip.cpp new file mode 100644 index 0000000..c6bc9ed --- /dev/null +++ b/etk/uri/provider/ProviderFileZip.cpp @@ -0,0 +1,62 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2018, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ +#include +#include +#include + +void etk::uri::provider::ProviderFileZip::loadZipFile(const etk::Uri& _zipFile) { + m_archive = etk::Archive::load(_zipFile); + TK_ASSERT(m_archive != null, "Error loading APK ... '" << _zipFile << "'"); + #ifdef DEBUG + //Just for debug, print APK contents + m_archive->display(); + #endif +} + +etk::uri::provider::ProviderFileZip::ProviderFileZip(const etk::Uri& _zipFile) { + loadZipFile(_zipFile); +} + +etk::uri::provider::ProviderFileZip::ProviderFileZip(const etk::Uri& _zipFile, const etk::Path& _offset) : + m_offset(_offset) { + loadZipFile(_zipFile); +} + +ememory::SharedPtr etk::uri::provider::ProviderFileZip::create(const etk::Uri& _uri) { + if (m_offset.isEmpty() == true) { + return ememory::makeShared(_uri.getPath(), m_archive); + } + return ememory::makeShared(m_offset / _uri.getPath(), m_archive); +} + +bool etk::uri::provider::ProviderFileZip::exist(const etk::Uri& _uri) { + if (m_offset.isEmpty() == true) { + return m_archive->exist(_uri.getPath()); + } + return m_archive->exist(m_offset / _uri.getPath()); +} + +etk::Vector etk::uri::provider::ProviderFileZip::list(const etk::Uri& _uri) { + etk::Vector tmp; + etk::Vector out; + if (m_offset.isEmpty() == true) { + tmp = m_archive->list(_uri.getPath()); + for (auto& elem: tmp) { + etk::Uri newUri = _uri; + newUri.setPath(elem); + out.pushBack(newUri); + } + return out; + } + tmp = m_archive->list(m_offset / _uri.getPath()); + int32_t offset = m_offset.getString().size()+1; + for (auto& elem: tmp) { + etk::Uri newUri = _uri; + newUri.setPath(elem.getString().extract(offset)); + out.pushBack(newUri); + } + return out; +} diff --git a/etk/uri/provider/ProviderFileZip.hpp b/etk/uri/provider/ProviderFileZip.hpp new file mode 100644 index 0000000..436fdf4 --- /dev/null +++ b/etk/uri/provider/ProviderFileZip.hpp @@ -0,0 +1,32 @@ + +/** @file + * @author Edouard DUPIN + * @copyright 2018, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ +#pragma once + +#include +#include +#include +#include + +namespace etk { + namespace uri { + namespace provider { + class ProviderFileZip : public etk::uri::provider::Interface { + protected: + ememory::SharedPtr m_archive; + etk::Path m_offset; + void loadZipFile(const etk::Uri& _zipFile); + public: + ProviderFileZip(const etk::Uri& _zipFile); + ProviderFileZip(const etk::Uri& _zipFile, const etk::Path& _offset); + public: + ememory::SharedPtr create(const etk::Uri& _uri) override; + bool exist(const etk::Uri& _uri) override; + etk::Vector list(const etk::Uri& _uri) override; + }; + } + } +} diff --git a/etk/uri/provider/provider.cpp b/etk/uri/provider/provider.cpp new file mode 100644 index 0000000..6d3916e --- /dev/null +++ b/etk/uri/provider/provider.cpp @@ -0,0 +1,68 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2018, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ +#include +#include +#include +#include + +static etk::Map>& getProviders() { + static etk::Map> g_data; + return g_data; +} + +void etk::uri::provider::add(const etk::String& _scheme, ememory::SharedPtr _interface) { + etk::String scheme = _scheme; + if (scheme.empty() == true) { + scheme = "RAW"; + } + getProviders().set(scheme, _interface); +} + +void etk::uri::provider::clear() { + getProviders().clear(); + etk::uri::provider::add("", ememory::makeShared()); +} + +void etk::uri::provider::remove(const etk::String& _scheme) { + getProviders().erase(_scheme); +} + +bool etk::uri::provider::exist(const etk::String& _scheme) { + etk::String scheme = _scheme; + if (scheme.empty() == true) { + scheme = "RAW"; + } + return getProviders().exist(scheme); +} + +ememory::SharedPtr etk::uri::provider::get(const etk::Uri& _uri) { + etk::String scheme = _uri.getScheme(); + if (scheme.empty() == true) { + scheme = "RAW"; + } + if (getProviders().exist(scheme) == false) { + return null; + } + return getProviders()[scheme]->create(_uri); +} +ememory::SharedPtr etk::uri::provider::getProvider(const etk::String& _scheme) { + etk::String scheme = _scheme; + if (scheme.empty() == true) { + scheme = "RAW"; + } + if (getProviders().exist(scheme) == false) { + return null; + } + return getProviders()[scheme]; +} + +void etk::uri::provider::init() { + etk::uri::provider::clear(); +} + +void etk::uri::provider::unInit() { + getProviders().clear(); +} diff --git a/etk/uri/provider/provider.hpp b/etk/uri/provider/provider.hpp new file mode 100644 index 0000000..8d80c08 --- /dev/null +++ b/etk/uri/provider/provider.hpp @@ -0,0 +1,65 @@ +/** @file + * @author Edouard DUPIN + * @copyright 2018, Edouard DUPIN, all right reserved + * @license MPL v2.0 (see license file) + */ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace etk { + namespace uri { + namespace provider { + /** + * @brief Add a IO interface provider ==> permit to have simple whangable backend interface. + * @param[in] _scheme URI scheme specific interface (like 'http' for 'http://' or 'RAW' for no scheme or "data" for 'data://') + * @param[in] _interface Provider interface + */ + void add(const etk::String& _scheme, ememory::SharedPtr _interface); + /** + * @brief Add a IO interface provider ==> permit to have simple whangable backend interface. + * @param[in] _scheme URI scheme specific interface + */ + void remove(const etk::String& _scheme); + /** + * @brief Check if a provider exist. + * @param[in] _scheme URI scheme specific interface + * @return true The provider is availlable + */ + bool exist(const etk::String& _scheme); + /** + * @brief Clear all providers + */ + void clear(); + /** + * @brief Get an IO interface with a specific URI + * @param[in] _uri Data interface requested + * @return The interface requested. + */ + ememory::SharedPtr get(const etk::Uri& _uri); + /** + * @brief Get an URI provider + * @param[in] _scheme type of provider + * @return The interface requested. + */ + ememory::SharedPtr getProvider(const etk::String& _scheme); + /** + * @brief Initialize uri provider + */ + void init(); + /** + * @brief Un-Initialize uri provider + */ + void unInit(); + } + } +} diff --git a/lutin_etk-test.py b/lutin_etk-test.py index 312c506..f3bacfd 100644 --- a/lutin_etk-test.py +++ b/lutin_etk-test.py @@ -33,6 +33,7 @@ def configure(target, my_module): 'test/testTheme.cpp', 'test/testUri.cpp', 'test/testQuery.cpp', + 'test/testUriProvider.cpp', ]) """ 'test/ConstructDestruct.cpp', @@ -63,5 +64,9 @@ def configure(target, my_module): 'test-debug' ]) my_module.copy_path('data/*') + my_module.copy_path('data/data/*', 'data') + my_module.copy_path('data/data/dir_A/*', 'data/dir_A') + my_module.copy_path('data/data/dir_B/*', 'data/dir_B') + my_module.copy_path('data/data/dir_B/dir_C/*', 'data/dir_B/dir_C') return True diff --git a/lutin_etk.py b/lutin_etk.py index d20caaf..66e3585 100644 --- a/lutin_etk.py +++ b/lutin_etk.py @@ -59,6 +59,9 @@ def configure(target, my_module): 'etk/archive/Zip.cpp', 'etk/uri/Uri.cpp', 'etk/uri/Query.cpp', + 'etk/uri/provider/provider.cpp', + 'etk/uri/provider/ProviderFile.cpp', + 'etk/uri/provider/ProviderFileZip.cpp', ]) my_module.add_header_file([ @@ -96,6 +99,10 @@ def configure(target, my_module): 'etk/FlatTree.hpp', 'etk/uri/Uri.hpp', 'etk/uri/Query.hpp', + 'etk/uri/provider/provider.hpp', + 'etk/uri/provider/Interface.hpp', + 'etk/uri/provider/ProviderFile.hpp', + 'etk/uri/provider/ProviderFileZip.hpp', ]) # build in C++ mode @@ -112,7 +119,7 @@ def configure(target, my_module): my_module.add_depend(['cxx']) # add some optionnal libraries - my_module.add_optionnal_depend('minizip', ["c++", "-DETK_BUILD_MINIZIP"]) + my_module.add_optionnal_depend('minizip', ["c++", "-DETK_BUILD_MINIZIP"], export=True) my_module.add_optionnal_depend('linearmath', ["c", "-DETK_BUILD_LINEARMATH"], export=True) if "Android" in target.get_type(): diff --git a/test/main.cpp b/test/main.cpp index de3cfbc..414189c 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -17,7 +17,11 @@ int main(int argc, const char *argv[]) { // init test engine: + etk::init(argc, argv); etest::init(argc, argv); TEST_INFO("TEST ETK"); - return RUN_ALL_TESTS(); + int out = RUN_ALL_TESTS(); + etest::unInit(); + etk::unInit(); + return out; } diff --git a/test/testUri.cpp b/test/testUri.cpp index e15c8b9..9be53a1 100644 --- a/test/testUri.cpp +++ b/test/testUri.cpp @@ -151,6 +151,20 @@ TEST(TestUri, base_9) { } TEST(TestUri, base_10) { + etk::String value = "__SCHEME__:////__PATH__?__QUERY__"; + etk::Uri uri(value); + EXPECT_EQ(uri.get(), value); + EXPECT_EQ(uri.getScheme(), "__SCHEME__"); + EXPECT_EQ(uri.getUser(), ""); + EXPECT_EQ(uri.getPassword(), ""); + EXPECT_EQ(uri.getServer(), ""); + EXPECT_EQ(uri.getPort(), 0); + EXPECT_EQ(uri.getPath(), etk::Path("/__PATH__")); + EXPECT_EQ(uri.getQuery().getEncoded(), "__QUERY__"); + EXPECT_EQ(uri.getFragment(), ""); +} + +TEST(TestUri, base_10_2) { etk::String value = "__SCHEME__:///__PATH__?__QUERY__"; etk::Uri uri(value); EXPECT_EQ(uri.get(), value); @@ -163,3 +177,35 @@ TEST(TestUri, base_10) { EXPECT_EQ(uri.getQuery().getEncoded(), "__QUERY__"); EXPECT_EQ(uri.getFragment(), ""); } + +TEST(TestUri, base_11_1) { + etk::String value = "//hello/txt"; + etk::Path valuePath = "/hello/txt"; + etk::Uri uri(valuePath); + EXPECT_EQ(uri.get(), value); + EXPECT_EQ(uri.getScheme(), ""); + EXPECT_EQ(uri.getUser(), ""); + EXPECT_EQ(uri.getPassword(), ""); + EXPECT_EQ(uri.getServer(), ""); + EXPECT_EQ(uri.getPort(), 0); + EXPECT_EQ(uri.getPath(), valuePath); + EXPECT_EQ(uri.getQuery().getEncoded(), ""); + EXPECT_EQ(uri.getFragment(), ""); +} + +TEST(TestUri, base_11_2) { + etk::String value = "/hello/txt"; + etk::Path valuePath = "hello/txt"; + etk::Uri uri(valuePath); + EXPECT_EQ(uri.get(), value); + EXPECT_EQ(uri.getScheme(), ""); + EXPECT_EQ(uri.getUser(), ""); + EXPECT_EQ(uri.getPassword(), ""); + EXPECT_EQ(uri.getServer(), ""); + EXPECT_EQ(uri.getPort(), 0); + EXPECT_EQ(uri.getPath(), valuePath); + EXPECT_EQ(uri.getQuery().getEncoded(), ""); + EXPECT_EQ(uri.getFragment(), ""); +} +// "audio://alsa/front?mode=I16" + diff --git a/test/testUriProvider.cpp b/test/testUriProvider.cpp new file mode 100644 index 0000000..5c15037 --- /dev/null +++ b/test/testUriProvider.cpp @@ -0,0 +1,238 @@ +/** + * @author Edouard DUPIN + * + * @copyright 2011, Edouard DUPIN, all right reserved + * + * @license MPL v2.0 (see license file) + */ + +#include +#include +#include +#include +#include +#include +#include + + + +TEST(TestUriProvider, defaultContructor) { + etk::uri::provider::clear(); + EXPECT_EQ(etk::uri::provider::exist(""), true); +} + +static int32_t s_plouf = 0; + +namespace { + class ProviderTest1 : public etk::uri::provider::Interface { + public: + ememory::SharedPtr create(const etk::Uri& _uri) override { + s_plouf = 5555; + return null; + } + bool exist(const etk::Uri& _uri) override { + return false; + } + etk::Vector list(const etk::Uri& _uri) override { + etk::Vector out; + return out; + } + }; + class ProviderTest2 : public etk::uri::provider::Interface { + public: + ememory::SharedPtr create(const etk::Uri& _uri) override { + s_plouf = 9999; + return null; + } + bool exist(const etk::Uri& _uri) override { + return false; + } + etk::Vector list(const etk::Uri& _uri) override { + etk::Vector out; + return out; + } + }; +} + + +TEST(TestUriProvider, checkPlouf) { + etk::uri::provider::clear(); + s_plouf = 0; + EXPECT_EQ(etk::uri::provider::exist(""), true); + EXPECT_EQ(etk::uri::provider::exist("RAW"), true); + EXPECT_EQ(etk::uri::provider::exist("PLOUF_1"), false); + EXPECT_EQ(etk::uri::provider::exist("PLOUF_2"), false); + etk::uri::provider::add("PLOUF_1", ememory::makeShared<::ProviderTest1>()); + EXPECT_EQ(etk::uri::provider::exist(""), true); + EXPECT_EQ(etk::uri::provider::exist("PLOUF_1"), true); + EXPECT_EQ(etk::uri::provider::exist("PLOUF_2"), false); + etk::uri::provider::add("PLOUF_2", ememory::makeShared<::ProviderTest2>()); + EXPECT_EQ(etk::uri::provider::exist(""), true); + EXPECT_EQ(etk::uri::provider::exist("PLOUF_1"), true); + EXPECT_EQ(etk::uri::provider::exist("PLOUF_2"), true); + EXPECT_EQ(s_plouf, 0); + etk::uri::provider::get("PLOUF_1:///qsdfqsdfqsdfqsdf.txt"); + EXPECT_EQ(s_plouf, 5555); + etk::uri::provider::get("PLOUF_2:///qsdfqsdfqsdfqsdf.txt"); + EXPECT_EQ(s_plouf, 9999); + etk::uri::provider::remove("PLOUF_1"); + EXPECT_EQ(etk::uri::provider::exist(""), true); + EXPECT_EQ(etk::uri::provider::exist("PLOUF_1"), false); + EXPECT_EQ(etk::uri::provider::exist("PLOUF_2"), true); + etk::uri::provider::remove("PLOUF_2"); + EXPECT_EQ(etk::uri::provider::exist(""), true); + EXPECT_EQ(etk::uri::provider::exist("PLOUF_1"), false); + EXPECT_EQ(etk::uri::provider::exist("PLOUF_2"), false); +} + +etk::Vector listDirect = { + "DATA:///filePresent.txt", + "DATA:///fileEmpty.txt", + "DATA:///data", + "DATA:///data_sample.zip", +}; + +/* This function takes last element as pivot, places + the pivot element at its correct position in sorted + array, and places all smaller (smaller than pivot) + to left of pivot and all greater elements to right + of pivot */ +int_t partition (etk::Vector& _data, int _low, int _high) { + int_t iii = (_low - 1); // Index of smaller element + for (int_t jjj = _low; jjj < _high; ++jjj) { + // If current element is smaller than or equal to pivot + if (_data[jjj] < _data[_high]) { + iii++; // increment index of smaller element + etk::swap(_data[iii], _data[jjj]); + } + } + etk::swap(_data[iii + 1], _data[_high]); + return (iii + 1); +} + +/* The main function that implements QuickSort + arr[] --> Array to be sorted, + low --> Starting index, + high --> Ending index */ +void quickSort(etk::Vector& _data, int _low, int _high) { + if (_low >= _high) { + return; + } + // pi is partitioning index, arr[p] is now at right place + int_t pi = partition(_data, _low, _high); + // Separately sort elements before partition and after partition + quickSort(_data, _low, pi - 1); + quickSort(_data, pi + 1, _high); +} + +TEST(TestUriProvider, checkDirectAccess) { + etk::uri::provider::clear(); + TEST_VERBOSE("data path: " << etk::fs::getDataPath()); + etk::uri::provider::add("DATA", ememory::makeShared(etk::fs::getDataPath())); + EXPECT_EQ(etk::uri::provider::exist("DATA"), true); + ememory::SharedPtr provider = etk::uri::provider::getProvider("DATA"); + EXPECT_NE(provider, null); + etk::Uri searchBase("DATA:///"); + auto elems = provider->list(searchBase); + TEST_WARNING("List DATA path: (A)"); + for (auto& it: elems) { + TEST_WARNING(" " << it); + } + elems.sort(0, elems.size(), [] (const etk::Uri& _left, const etk::Uri& _right) { + TEST_WARNING("compare " << _left << " " << (_left < _right?"<":">=") << " " << _right); + + return _left < _right; + }); + TEST_WARNING("List DATA path: (B)"); + for (auto& it: elems) { + TEST_WARNING(" " << it); + } + quickSort(elems, 0, elems.size()-1); + TEST_WARNING("List DATA path: (C)"); + for (auto& it: elems) { + TEST_WARNING(" " << it); + } + EXPECT_EQ(elems, listDirect); +} + +etk::Vector listDirect2 = { + "DATA:///data/.file_hidden.txt", + "DATA:///data/dir_A", + "DATA:///data/dir_B", + "DATA:///data/file_1.txt", + "DATA:///data/file_2.txt", + "DATA:///data/file_3.txt" +}; + +TEST(TestUriProvider, checkDirectAccess2) { + etk::uri::provider::clear(); + TEST_VERBOSE("data path: " << etk::fs::getDataPath()); + etk::uri::provider::add("DATA", ememory::makeShared(etk::fs::getDataPath())); + EXPECT_EQ(etk::uri::provider::exist("DATA"), true); + ememory::SharedPtr provider = etk::uri::provider::getProvider("DATA"); + EXPECT_NE(provider, null); + etk::Uri searchBase("DATA:///data"); + + auto elems = provider->list(searchBase); + TEST_WARNING("List DATA path: (A)"); + for (auto& it: elems) { + TEST_WARNING(" " << it); + } + elems.sort(0, elems.size(), [] (const etk::Uri& _left, const etk::Uri& _right) { + return _left < _right; + }); + TEST_WARNING("List DATA path: (B)"); + for (auto& it: elems) { + TEST_WARNING(" " << it); + } + EXPECT_EQ(elems, listDirect2); +} + +etk::Vector listZip = { + "DATA:///.file_hidden.txt", + "DATA:///dir_A", + "DATA:///dir_B", + "DATA:///file_1.txt", + "DATA:///file_2.txt", + "DATA:///file_3.txt" +}; + +TEST(TestUriProvider, checkZipAccess) { + etk::uri::provider::clear(); + TEST_VERBOSE("data path: " << etk::fs::getDataPath()); + etk::uri::provider::add("DATA", ememory::makeShared(etk::fs::getDataPath() / "data_sample.zip", "data")); + EXPECT_EQ(etk::uri::provider::exist("DATA"), true); + ememory::SharedPtr provider = etk::uri::provider::getProvider("DATA"); + EXPECT_NE(provider, null); + TEST_WARNING("List DATA path:"); + etk::Uri searchBase("DATA://"); + auto elems = provider->list(searchBase); + for (auto& it: elems) { + TEST_WARNING(" " << it); + } + EXPECT_EQ(elems, listZip); +} + + +etk::Vector listZip2 = { + "DATA:///dir_B/dir_C", + "DATA:///dir_B/file_B_1.txt", + "DATA:///dir_B/file_B_2.txt", +}; + +TEST(TestUriProvider, checkZipAccess2) { + etk::uri::provider::clear(); + TEST_VERBOSE("data path: " << etk::fs::getDataPath()); + etk::uri::provider::add("DATA", ememory::makeShared(etk::fs::getDataPath() / "data_sample.zip", "data")); + EXPECT_EQ(etk::uri::provider::exist("DATA"), true); + ememory::SharedPtr provider = etk::uri::provider::getProvider("DATA"); + EXPECT_NE(provider, null); + TEST_WARNING("List DATA path:"); + etk::Uri searchBase("DATA:///dir_B"); + auto elems = provider->list(searchBase); + for (auto& it: elems) { + TEST_WARNING(" " << it); + } + EXPECT_EQ(elems, listZip2); +} +