@@ -236,6 +236,7 @@ set(CPPCMS_SOURCES | |||||
src/applications_pool.cpp | src/applications_pool.cpp | ||||
src/application.cpp | src/application.cpp | ||||
src/url_dispatcher.cpp | src/url_dispatcher.cpp | ||||
src/url_mapper.cpp | |||||
src/http_cookie.cpp | src/http_cookie.cpp | ||||
src/http_file.cpp | src/http_file.cpp | ||||
src/http_content_type.cpp | src/http_content_type.cpp | ||||
@@ -467,6 +468,7 @@ set(ALL_TESTS | |||||
serialization_test | serialization_test | ||||
status_test | status_test | ||||
xss_test | xss_test | ||||
url_mapper_test | |||||
) | ) | ||||
foreach(TEST ${ALL_TESTS}) | foreach(TEST ${ALL_TESTS}) | ||||
add_executable(${TEST} tests/${TEST}.cpp) | add_executable(${TEST} tests/${TEST}.cpp) | ||||
@@ -544,6 +546,7 @@ add_test(content_type_parser_test content_type_parser_test) | |||||
add_test(cache_backend_test cache_backend_test) | add_test(cache_backend_test cache_backend_test) | ||||
add_test(serialization_test serialization_test) | add_test(serialization_test serialization_test) | ||||
add_test(xss_test xss_test) | add_test(xss_test xss_test) | ||||
add_test(url_mapper_test url_mapper_test) | |||||
add_test(status_test | add_test(status_test | ||||
status_test "-c" "${CNF}/status_test.js" | status_test "-c" "${CNF}/status_test.js" | ||||
@@ -80,7 +80,7 @@ class html_type: | |||||
class view_block: | class view_block: | ||||
pattern=r'^<%\s*view\s+(\w+)\s+uses\s+(:?:?\w+(::\w+)?)(\s+extends\s+(:?:?\w+(::\w+)?))?\s*%>$' | |||||
pattern=r'^<%\s*view\s+(\w+)\s+uses\s+(:?:?\w+(::\w+)*)(\s+extends\s+(:?:?\w+(::\w+)?))?\s*%>$' | |||||
type='view' | type='view' | ||||
topmost = 0 | topmost = 0 | ||||
def declare(self): | def declare(self): | ||||
@@ -539,6 +539,18 @@ class gettext_block: | |||||
else: | else: | ||||
output( "out()<<cppcms::locale::format(cppcms::locale::translate(%s)) %% (%s);" % (s , ') % ('.join(params))) | output( "out()<<cppcms::locale::format(cppcms::locale::translate(%s)) %% (%s);" % (s , ') % ('.join(params))) | ||||
class url_block: | |||||
pattern=r'^<%\s*url\s*('+str_match+')\s*(using(.*))?\s*%>$' | |||||
def use(self,m): | |||||
s=m.group(1) | |||||
params=[] | |||||
if m.group(3): | |||||
params=make_format_params(m.group(4)) | |||||
if not params: | |||||
output( "out()<<content.url(%s);" % s) | |||||
else: | |||||
output( "out()<<content.url(%s, %s);" % (s , ', '.join(params))) | |||||
class include_block: | class include_block: | ||||
pattern=r'^<%\s*include\s+([a-zA_Z]\w*(::\w+)?)\s*\(\s*(.*)\)\s*%>$'; | pattern=r'^<%\s*include\s+([a-zA_Z]\w*(::\w+)?)\s*\(\s*(.*)\)\s*%>$'; | ||||
@@ -648,6 +660,7 @@ def main(): | |||||
skin_block(), view_block(), if_block(), template_block(), end_block(), else_block(), \ | skin_block(), view_block(), if_block(), template_block(), end_block(), else_block(), \ | ||||
cpp_include_block(), \ | cpp_include_block(), \ | ||||
gettext_block(),ngettext_block(), \ | gettext_block(),ngettext_block(), \ | ||||
url_block(), \ | |||||
foreach_block(), item_block(), empty_block(),separator_block(), \ | foreach_block(), item_block(), empty_block(),separator_block(), \ | ||||
include_block(), \ | include_block(), \ | ||||
html_type(), form_block(), \ | html_type(), form_block(), \ | ||||
@@ -41,6 +41,7 @@ namespace cppcms { | |||||
class service; | class service; | ||||
class url_dispatcher; | class url_dispatcher; | ||||
class url_mapper; | |||||
class applications_pool; | class applications_pool; | ||||
class application; | class application; | ||||
class base_content; | class base_content; | ||||
@@ -55,6 +56,9 @@ namespace cppcms { | |||||
namespace json { | namespace json { | ||||
class value; | class value; | ||||
} | } | ||||
namespace filters { | |||||
class streamable; | |||||
} | |||||
/// | /// | ||||
@@ -124,12 +128,23 @@ namespace cppcms { | |||||
/// | /// | ||||
/// Get a dispatched class -- class that responsible on mapping between URLs and a member | /// Get a dispatched class -- class that responsible on mapping between URLs and a member | ||||
/// functions of application class. This member function is application specific and not | |||||
/// functions of application class. | |||||
/// | |||||
/// This member function is application specific and not | |||||
/// Connection specific. | /// Connection specific. | ||||
/// | /// | ||||
url_dispatcher &dispatcher(); | url_dispatcher &dispatcher(); | ||||
/// | /// | ||||
/// Get a url_mapper class -- class that responsible on mapping between real objects and | |||||
/// urls displayer on the page. | |||||
/// | |||||
/// This member function is application specific and not | |||||
/// Connection specific. | |||||
/// | |||||
url_mapper &mapper(); | |||||
/// | |||||
/// Get a cache_interface instance. Same as context().cache(); | /// Get a cache_interface instance. Same as context().cache(); | ||||
/// | /// | ||||
cache_interface &cache(); | cache_interface &cache(); | ||||
@@ -170,6 +185,25 @@ namespace cppcms { | |||||
/// | /// | ||||
void render(std::string skin,std::string template_name,std::ostream &out,base_content &content); | void render(std::string skin,std::string template_name,std::ostream &out,base_content &content); | ||||
/// | |||||
/// Render a template \a template_name of default skin using this application as content. | |||||
/// | |||||
/// Side effect requires: output stream for response class, causes all updated session | |||||
/// data be saved and all headers be written. You can't change headers after calling this function. | |||||
/// | |||||
void render(std::string template_name); | |||||
/// | |||||
/// Render a template \a template_name of \a skin skin using this application as content | |||||
/// | |||||
/// Side effect requires: output stream for response class, causes all updated session | |||||
/// data be saved and all headers be written. You can't change headers after calling this function. | |||||
/// | |||||
void render(std::string skin,std::string template_name); | |||||
/// | /// | ||||
/// Register an application \a app as child. Ownership of app is not transfered to parent, however | /// Register an application \a app as child. Ownership of app is not transfered to parent, however | ||||
/// it would shared it's parent reference count. | /// it would shared it's parent reference count. | ||||
@@ -274,7 +308,101 @@ namespace cppcms { | |||||
/// | /// | ||||
virtual void clear(); | virtual void clear(); | ||||
/// | |||||
/// Translate a message in current locale for given \a message in \a context | |||||
/// | |||||
std::string translate(char const *context,char const *message); | |||||
/// | |||||
/// Translate a message in current locale for given \a message | |||||
/// | |||||
std::string translate(char const *message); | |||||
/// | |||||
/// Translate a message in current locale for given \a single and \a plural form for number \a n in \a context. | |||||
/// | |||||
std::string translate(char const *context,char const *single,char const *plural,int n); | |||||
/// | |||||
/// Translate a message in current locale for given \a single and \a plural form for number \a n | |||||
/// | |||||
std::string translate(char const *single,char const *plural,int n); | |||||
/// | |||||
/// Map url-key \a key to actual URL, with parameters | |||||
/// | |||||
/// Key format is (/|(../)+)?real_key, where real key is the value that is defined | |||||
/// in the mapper. | |||||
/// | |||||
/// - If the ket does not start with "/" or "../" then current dispatcher is used | |||||
/// - If it starts with / then the actual mapper being used is root()->mapper() | |||||
/// - If it starts with ../ then the actual mapper being used is parent()->mapper() | |||||
/// - If it starts with ../../ then the actual mapper being used is parent()->parent()->mapper() and so on | |||||
/// | |||||
std::string url(std::string const &key); | |||||
/// | |||||
/// Map url-key \a key to actual URL, with parameter p1 | |||||
/// | |||||
/// Key format is (/|(../)+)?real_key, where real key is the value that is defined | |||||
/// in the mapper. | |||||
/// | |||||
/// - If the ket does not start with "/" or "../" then current dispatcher is used | |||||
/// - If it starts with / then the actual mapper being used is root()->mapper() | |||||
/// - If it starts with ../ then the actual mapper being used is parent()->mapper() | |||||
/// - If it starts with ../../ then the actual mapper being used is parent()->parent()->mapper() and so on | |||||
/// | |||||
std::string url(std::string const &key, | |||||
filters::streamable const &p1); | |||||
/// | |||||
/// Map url-key \a key to actual URL, with parameters p1, p2 | |||||
/// | |||||
/// Key format is (/|(../)+)?real_key, where real key is the value that is defined | |||||
/// in the mapper. | |||||
/// | |||||
/// - If the ket does not start with "/" or "../" then current dispatcher is used | |||||
/// - If it starts with / then the actual mapper being used is root()->mapper() | |||||
/// - If it starts with ../ then the actual mapper being used is parent()->mapper() | |||||
/// - If it starts with ../../ then the actual mapper being used is parent()->parent()->mapper() and so on | |||||
/// | |||||
std::string url(std::string const &key, | |||||
filters::streamable const &p1, | |||||
filters::streamable const &p2); | |||||
/// | |||||
/// Map url-key \a key to actual URL, with parameters p1, p2, p3 | |||||
/// | |||||
/// Key format is (/|(../)+)?real_key, where real key is the value that is defined | |||||
/// in the mapper. | |||||
/// | |||||
/// - If the ket does not start with "/" or "../" then current dispatcher is used | |||||
/// - If it starts with / then the actual mapper being used is root()->mapper() | |||||
/// - If it starts with ../ then the actual mapper being used is parent()->mapper() | |||||
/// - If it starts with ../../ then the actual mapper being used is parent()->parent()->mapper() and so on | |||||
/// | |||||
std::string url(std::string const &key, | |||||
filters::streamable const &p1, | |||||
filters::streamable const &p2, | |||||
filters::streamable const &p3); | |||||
/// | |||||
/// Map url-key \a key to actual URL, with parameters p1, p2, p3, p4 | |||||
/// | |||||
/// Key format is (/|(../)+)?real_key, where real key is the value that is defined | |||||
/// in the mapper. | |||||
/// | |||||
/// - If the ket does not start with "/" or "../" then current dispatcher is used | |||||
/// - If it starts with / then the actual mapper being used is root()->mapper() | |||||
/// - If it starts with ../ then the actual mapper being used is parent()->mapper() | |||||
/// - If it starts with ../../ then the actual mapper being used is parent()->parent()->mapper() and so on | |||||
/// | |||||
std::string url(std::string const &key, | |||||
filters::streamable const &p1, | |||||
filters::streamable const &p2, | |||||
filters::streamable const &p3, | |||||
filters::streamable const &p4); | |||||
private: | private: | ||||
url_mapper &get_mapper_for_key(std::string const &key,std::string &real_key); | |||||
void recycle(); | void recycle(); | ||||
void parent(application *parent); | void parent(application *parent); | ||||
@@ -0,0 +1,80 @@ | |||||
/////////////////////////////////////////////////////////////////////////////// | |||||
// | |||||
// Copyright (C) 2008-2010 Artyom Beilis (Tonkikh) <artyomtnk@yahoo.com> | |||||
// | |||||
// This program is free software: you can redistribute it and/or modify | |||||
// it under the terms of the GNU Lesser General Public License as published by | |||||
// the Free Software Foundation, either version 3 of the License, or | |||||
// (at your option) any later version. | |||||
// | |||||
// This program is distributed in the hope that it will be useful, | |||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
// GNU Lesser General Public License for more details. | |||||
// | |||||
// You should have received a copy of the GNU Lesser General Public License | |||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. | |||||
// | |||||
/////////////////////////////////////////////////////////////////////////////// | |||||
#ifndef CPPCMS_URL_MAPPER_H | |||||
#define CPPCMS_URL_MAPPER_H | |||||
#include <cppcms/defs.h> | |||||
#include <booster/noncopyable.h> | |||||
#include <booster/hold_ptr.h> | |||||
#include <cppcms/filters.h> | |||||
#include <string> | |||||
#include <vector> | |||||
namespace cppcms { | |||||
/// | |||||
/// \brief class for mapping URLs - oposite of dispatch | |||||
/// | |||||
class CPPCMS_API url_mapper : public booster::noncopyable { | |||||
public: | |||||
url_mapper(); | |||||
~url_mapper(); | |||||
std::string root(); | |||||
void root(std::string const &r); | |||||
void assign(std::string const &key,std::string const &url); | |||||
void set_value(std::string const &key,std::string const &value); | |||||
void clear_value(std::string const &key); | |||||
void map( std::ostream &out, | |||||
std::string const &key); | |||||
void map( std::ostream &out, | |||||
std::string const &key, | |||||
filters::streamable const &p1); | |||||
void map( std::ostream &out, | |||||
std::string const &key, | |||||
filters::streamable const &p1, | |||||
filters::streamable const &p2); | |||||
void map( std::ostream &out, | |||||
std::string const &key, | |||||
filters::streamable const &p1, | |||||
filters::streamable const &p2, | |||||
filters::streamable const &p3); | |||||
void map( std::ostream &out, | |||||
std::string const &key, | |||||
filters::streamable const &p1, | |||||
filters::streamable const &p2, | |||||
filters::streamable const &p3, | |||||
filters::streamable const &p4); | |||||
private: | |||||
std::string real_map(std::string const &key,std::vector<std::string> const ¶ms); | |||||
struct data; | |||||
booster::hold_ptr<data> d; | |||||
}; | |||||
}; | |||||
#endif |
@@ -16,3 +16,4 @@ | |||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. | // along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
// | // | ||||
/////////////////////////////////////////////////////////////////////////////// | /////////////////////////////////////////////////////////////////////////////// | ||||
@@ -21,14 +21,19 @@ | |||||
#include <cppcms/application.h> | #include <cppcms/application.h> | ||||
#include <cppcms/http_context.h> | #include <cppcms/http_context.h> | ||||
#include <cppcms/service.h> | #include <cppcms/service.h> | ||||
#include <cppcms/filters.h> | |||||
#include <cppcms/cppcms_error.h> | #include <cppcms/cppcms_error.h> | ||||
#include <cppcms/url_dispatcher.h> | #include <cppcms/url_dispatcher.h> | ||||
#include <cppcms/url_mapper.h> | |||||
#include <cppcms/applications_pool.h> | #include <cppcms/applications_pool.h> | ||||
#include <cppcms/http_response.h> | #include <cppcms/http_response.h> | ||||
#include <cppcms/views_pool.h> | #include <cppcms/views_pool.h> | ||||
#include <set> | #include <set> | ||||
#include <vector> | #include <vector> | ||||
#include <sstream> | |||||
#include <booster/locale/message.h> | |||||
#include <cppcms/config.h> | #include <cppcms/config.h> | ||||
#ifdef CPPCMS_USE_EXTERNAL_BOOST | #ifdef CPPCMS_USE_EXTERNAL_BOOST | ||||
@@ -50,6 +55,7 @@ struct application::_data { | |||||
booster::shared_ptr<http::context> conn; | booster::shared_ptr<http::context> conn; | ||||
int pool_id; | int pool_id; | ||||
url_dispatcher url; | url_dispatcher url; | ||||
url_mapper url_map; | |||||
std::vector<application *> managed_children; | std::vector<application *> managed_children; | ||||
}; | }; | ||||
@@ -92,6 +98,11 @@ url_dispatcher &application::dispatcher() | |||||
return d->url; | return d->url; | ||||
} | } | ||||
url_mapper &application::mapper() | |||||
{ | |||||
return d->url_map; | |||||
} | |||||
booster::shared_ptr<http::context> application::get_context() | booster::shared_ptr<http::context> application::get_context() | ||||
{ | { | ||||
return root()->d->conn; | return root()->d->conn; | ||||
@@ -196,6 +207,29 @@ void application::render(std::string skin,std::string template_name,base_content | |||||
service().views_pool().render(skin,template_name,response().out(),content); | service().views_pool().render(skin,template_name,response().out(),content); | ||||
} | } | ||||
void application::render(std::string template_name) | |||||
{ | |||||
base_content *cnt = dynamic_cast<base_content *>(this); | |||||
if(!cnt) { | |||||
throw cppcms_error( "Can't use application::render(std::string) when the application " | |||||
"is not derived from base_content"); | |||||
} | |||||
render(template_name,*cnt); | |||||
} | |||||
void application::render(std::string skin,std::string template_name) | |||||
{ | |||||
base_content *cnt = dynamic_cast<base_content *>(this); | |||||
if(!cnt) { | |||||
throw cppcms_error( "Can't use application::render(std::string,std::string) when the application " | |||||
"is not derived from base_content"); | |||||
} | |||||
render(skin,template_name,*cnt); | |||||
} | |||||
void application::render(std::string template_name,std::ostream &out,base_content &content) | void application::render(std::string template_name,std::ostream &out,base_content &content) | ||||
{ | { | ||||
service().views_pool().render(context().skin(),template_name,out,content); | service().views_pool().render(context().skin(),template_name,out,content); | ||||
@@ -222,6 +256,103 @@ void application::recycle() | |||||
assign_context(booster::shared_ptr<http::context>()); | assign_context(booster::shared_ptr<http::context>()); | ||||
} | } | ||||
std::string application::translate(char const *ctx,char const *message) | |||||
{ | |||||
return booster::locale::translate(ctx,message).str<char>(context().locale()); | |||||
} | |||||
std::string application::translate(char const *message) | |||||
{ | |||||
return booster::locale::translate(message).str<char>(context().locale()); | |||||
} | |||||
std::string application::translate(char const *ctx,char const *single,char const *plural,int n) | |||||
{ | |||||
return booster::locale::translate(ctx,single,plural,n).str<char>(context().locale()); | |||||
} | |||||
std::string application::translate(char const *single,char const *plural,int n) | |||||
{ | |||||
return booster::locale::translate(single,plural,n).str<char>(context().locale()); | |||||
} | |||||
url_mapper &application::get_mapper_for_key(std::string const &key,std::string &real_key) | |||||
{ | |||||
if(!key.empty() && key[0]=='/') { | |||||
real_key = key.substr(1); | |||||
return root()->mapper(); | |||||
} | |||||
unsigned index = 0; | |||||
application *app = this; | |||||
while(key.size() >= index+3 && memcmp(key.c_str()+index,"../",3)==0) { | |||||
index+=3; | |||||
app = app->parent(); | |||||
} | |||||
real_key = key.substr(index); | |||||
return app->mapper(); | |||||
} | |||||
std::string application::url(std::string const &key) | |||||
{ | |||||
std::ostringstream ss; | |||||
ss.imbue(context().locale()); | |||||
std::string real_key; | |||||
url_mapper &mp = get_mapper_for_key(key,real_key); | |||||
mp.map(ss,real_key); | |||||
return ss.str(); | |||||
} | |||||
std::string application::url( std::string const &key, | |||||
filters::streamable const &p1) | |||||
{ | |||||
std::ostringstream ss; | |||||
ss.imbue(context().locale()); | |||||
std::string real_key; | |||||
url_mapper &mp = get_mapper_for_key(key,real_key); | |||||
mp.map(ss,real_key,p1); | |||||
return ss.str(); | |||||
} | |||||
std::string application::url( std::string const &key, | |||||
filters::streamable const &p1, | |||||
filters::streamable const &p2) | |||||
{ | |||||
std::ostringstream ss; | |||||
ss.imbue(context().locale()); | |||||
std::string real_key; | |||||
url_mapper &mp = get_mapper_for_key(key,real_key); | |||||
mp.map(ss,real_key,p1,p2); | |||||
return ss.str(); | |||||
} | |||||
std::string application::url( std::string const &key, | |||||
filters::streamable const &p1, | |||||
filters::streamable const &p2, | |||||
filters::streamable const &p3) | |||||
{ | |||||
std::ostringstream ss; | |||||
ss.imbue(context().locale()); | |||||
std::string real_key; | |||||
url_mapper &mp = get_mapper_for_key(key,real_key); | |||||
mp.map(ss,real_key,p1,p2,p3); | |||||
return ss.str(); | |||||
} | |||||
std::string application::url( std::string const &key, | |||||
filters::streamable const &p1, | |||||
filters::streamable const &p2, | |||||
filters::streamable const &p3, | |||||
filters::streamable const &p4) | |||||
{ | |||||
std::ostringstream ss; | |||||
ss.imbue(context().locale()); | |||||
std::string real_key; | |||||
url_mapper &mp = get_mapper_for_key(key,real_key); | |||||
mp.map(ss,real_key,p1,p2,p3,p4); | |||||
return ss.str(); | |||||
} | |||||
} // cppcms | } // cppcms | ||||
@@ -0,0 +1,226 @@ | |||||
/////////////////////////////////////////////////////////////////////////////// | |||||
// | |||||
// Copyright (C) 2008-2010 Artyom Beilis (Tonkikh) <artyomtnk@yahoo.com> | |||||
// | |||||
// This program is free software: you can redistribute it and/or modify | |||||
// it under the terms of the GNU Lesser General Public License as published by | |||||
// the Free Software Foundation, either version 3 of the License, or | |||||
// (at your option) any later version. | |||||
// | |||||
// This program is distributed in the hope that it will be useful, | |||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
// GNU Lesser General Public License for more details. | |||||
// | |||||
// You should have received a copy of the GNU Lesser General Public License | |||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. | |||||
// | |||||
/////////////////////////////////////////////////////////////////////////////// | |||||
#define CPPCMS_SOURCE | |||||
#include <cppcms/url_mapper.h> | |||||
#include <cppcms/cppcms_error.h> | |||||
#include <map> | |||||
#include <stdlib.h> | |||||
namespace cppcms { | |||||
struct url_mapper::data | |||||
{ | |||||
struct entry { | |||||
std::vector<std::string> parts; | |||||
std::vector<int> indexes; | |||||
std::vector<std::string> keys; | |||||
}; | |||||
typedef std::map<size_t,entry> by_size_type; | |||||
typedef std::map<std::string,by_size_type> by_key_type; | |||||
typedef std::map<std::string,std::string> helpers_type; | |||||
by_key_type by_key; | |||||
helpers_type helpers; | |||||
std::string root; | |||||
bool map( std::string const key, | |||||
std::vector<std::string> const ¶ms, | |||||
std::string &output) const | |||||
{ | |||||
by_key_type::const_iterator kp = by_key.find(key); | |||||
if(kp == by_key.end()) | |||||
return false; | |||||
by_size_type::const_iterator sp = kp->second.find(params.size()); | |||||
if(sp == kp->second.end()) | |||||
return false; | |||||
output.clear(); | |||||
output+=root; | |||||
entry const &formatting = sp->second; | |||||
for(size_t i=0;i<formatting.parts.size();i++) { | |||||
output += formatting.parts[i]; | |||||
if( i < formatting.indexes.size() ) { | |||||
if(formatting.indexes[i]==0) { | |||||
std::string const &hkey = formatting.keys[i]; | |||||
std::map<std::string,std::string>::const_iterator p = helpers.find(hkey); | |||||
if(p != helpers.end()) { | |||||
output += p->second; | |||||
} | |||||
} | |||||
else { | |||||
output+=params.at(formatting.indexes[i] - 1); | |||||
} | |||||
} | |||||
} | |||||
return true; | |||||
} | |||||
}; | |||||
void url_mapper::assign(std::string const &key,std::string const &url) | |||||
{ | |||||
data::entry e; | |||||
std::string::const_iterator prev = url.begin(), p = url.begin(); | |||||
int max_index = 0; | |||||
while(p!=url.end()) { | |||||
if(*p=='{') { | |||||
e.parts.push_back(std::string(prev,p)); | |||||
prev = p; | |||||
while(p!=url.end()) { | |||||
if(*p=='}') { | |||||
std::string const hkey(prev+1,p); | |||||
prev = p+1; | |||||
if(hkey.size()==0) { | |||||
throw cppcms_error("cppcms::url_mapper: empty index between {}"); | |||||
} | |||||
bool all_digits = true; | |||||
for(unsigned i=0;all_digits && i<hkey.size();i++) { | |||||
if(hkey[i] < '0' || '9' <hkey[i]) | |||||
all_digits = false; | |||||
} | |||||
if(!all_digits) { | |||||
e.indexes.push_back(0); | |||||
e.keys.push_back(hkey); | |||||
} | |||||
else { | |||||
int index = atoi(hkey.c_str()); | |||||
if(index == 0) | |||||
throw cppcms_error("cppcms::url_mapper: index 0 is invalid"); | |||||
max_index = std::max(index,max_index); | |||||
e.indexes.push_back(index); | |||||
e.keys.resize(e.keys.size()+1); | |||||
} | |||||
break; | |||||
} | |||||
else | |||||
p++; | |||||
} | |||||
if(p==url.end()) | |||||
throw cppcms_error("cppcms::url_mapper: '{' in url without '}'"); | |||||
p++; | |||||
} | |||||
else if(*p=='}') { | |||||
throw cppcms_error("cppcms::url_mapper: '}' in url without '{'"); | |||||
} | |||||
else | |||||
p++; | |||||
} | |||||
e.parts.push_back(std::string(prev,p)); | |||||
d->by_key[key][max_index] = e; | |||||
} | |||||
void url_mapper::set_value(std::string const &key,std::string const &value) | |||||
{ | |||||
d->helpers[key]=value; | |||||
} | |||||
void url_mapper::clear_value(std::string const &key) | |||||
{ | |||||
d->helpers.erase(key); | |||||
} | |||||
url_mapper::url_mapper() : d(new url_mapper::data()) | |||||
{ | |||||
} | |||||
url_mapper::~url_mapper() | |||||
{ | |||||
} | |||||
std::string url_mapper::root() | |||||
{ | |||||
return d->root; | |||||
} | |||||
void url_mapper::root(std::string const &r) | |||||
{ | |||||
d->root = r; | |||||
} | |||||
std::string url_mapper::real_map(std::string const &key,std::vector<std::string> const ¶ms) | |||||
{ | |||||
std::string result; | |||||
if(!d->map(key,params,result)) { | |||||
throw cppcms_error("cppcms::url_mapper:invalid key `" + key + "' given"); | |||||
} | |||||
return result; | |||||
} | |||||
void url_mapper::map( std::ostream &out, | |||||
std::string const &key) | |||||
{ | |||||
std::vector<std::string> params; | |||||
out << real_map(key,params); | |||||
} | |||||
void url_mapper::map( std::ostream &out, | |||||
std::string const &key, | |||||
filters::streamable const &p1) | |||||
{ | |||||
std::vector<std::string> params(1); | |||||
params[0] = p1.get(out); | |||||
out << real_map(key,params); | |||||
} | |||||
void url_mapper::map( std::ostream &out, | |||||
std::string const &key, | |||||
filters::streamable const &p1, | |||||
filters::streamable const &p2) | |||||
{ | |||||
std::vector<std::string> params(2); | |||||
params[0] = p1.get(out); | |||||
params[1] = p2.get(out); | |||||
out << real_map(key,params); | |||||
} | |||||
void url_mapper::map( std::ostream &out, | |||||
std::string const &key, | |||||
filters::streamable const &p1, | |||||
filters::streamable const &p2, | |||||
filters::streamable const &p3) | |||||
{ | |||||
std::vector<std::string> params(3); | |||||
params[0] = p1.get(out); | |||||
params[1] = p2.get(out); | |||||
params[2] = p3.get(out); | |||||
out << real_map(key,params); | |||||
} | |||||
void url_mapper::map( std::ostream &out, | |||||
std::string const &key, | |||||
filters::streamable const &p1, | |||||
filters::streamable const &p2, | |||||
filters::streamable const &p3, | |||||
filters::streamable const &p4) | |||||
{ | |||||
std::vector<std::string> params(4); | |||||
params[0] = p1.get(out); | |||||
params[1] = p2.get(out); | |||||
params[2] = p3.get(out); | |||||
params[3] = p4.get(out); | |||||
out << real_map(key,params); | |||||
} | |||||
} |
@@ -0,0 +1,126 @@ | |||||
/////////////////////////////////////////////////////////////////////////////// | |||||
// | |||||
// Copyright (C) 2008-2010 Artyom Beilis (Tonkikh) <artyomtnk@yahoo.com> | |||||
// | |||||
// This program is free software: you can redistribute it and/or modify | |||||
// it under the terms of the GNU Lesser General Public License as published by | |||||
// the Free Software Foundation, either version 3 of the License, or | |||||
// (at your option) any later version. | |||||
// | |||||
// This program is distributed in the hope that it will be useful, | |||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
// GNU Lesser General Public License for more details. | |||||
// | |||||
// You should have received a copy of the GNU Lesser General Public License | |||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. | |||||
// | |||||
/////////////////////////////////////////////////////////////////////////////// | |||||
#include <cppcms/url_mapper.h> | |||||
#include <cppcms/cppcms_error.h> | |||||
#include <sstream> | |||||
#include "test.h" | |||||
std::string value(std::ostringstream &s) | |||||
{ | |||||
std::string res = s.str(); | |||||
s.str(""); | |||||
return res; | |||||
} | |||||
int main() | |||||
{ | |||||
try { | |||||
cppcms::url_mapper m; | |||||
m.assign("foo","/foo"); | |||||
m.assign("foo","/foo/{1}"); | |||||
m.assign("foo","/foo/{1}/{2}"); | |||||
m.assign("foo","/foo/{1}/{2}/{3}"); | |||||
m.assign("foo","/foo/{1}/{2}/{3}/{4}"); | |||||
m.assign("bar","/bar/{lang}/{1}"); | |||||
m.assign("bar","/bar/{2}/{1}"); | |||||
m.assign("test1","{1}x"); | |||||
m.assign("test2","x{1}"); | |||||
m.set_value("lang","en"); | |||||
m.root("test.com"); | |||||
TEST(m.root()=="test.com"); | |||||
std::ostringstream ss; | |||||
m.map(ss,"foo"); | |||||
TEST(value(ss) == "test.com/foo"); | |||||
m.map(ss,"foo",1); | |||||
TEST(value(ss) == "test.com/foo/1"); | |||||
m.map(ss,"foo",1,2); | |||||
TEST(value(ss) == "test.com/foo/1/2"); | |||||
m.map(ss,"foo",1,2,3); | |||||
TEST(value(ss) == "test.com/foo/1/2/3"); | |||||
m.map(ss,"foo",1,2,3,4); | |||||
TEST(value(ss) == "test.com/foo/1/2/3/4"); | |||||
m.map(ss,"foo",1,"a","b",5); | |||||
TEST(value(ss) == "test.com/foo/1/a/b/5"); | |||||
m.map(ss,"bar",1); | |||||
TEST(value(ss) == "test.com/bar/en/1"); | |||||
m.map(ss,"bar",1,"ru"); | |||||
TEST(value(ss) == "test.com/bar/ru/1"); | |||||
m.root(""); | |||||
m.map(ss,"test1",10); | |||||
TEST(value(ss) == "10x"); | |||||
m.map(ss,"test2",10); | |||||
TEST(value(ss) == "x10"); | |||||
try { | |||||
m.assign("x","a{"); | |||||
TEST(0); | |||||
} | |||||
catch(cppcms::cppcms_error const &e) {} | |||||
catch(...) { TEST(0); } | |||||
try { | |||||
m.assign("x","a}"); | |||||
TEST(0); | |||||
} | |||||
catch(cppcms::cppcms_error const &e) {} | |||||
catch(...) { TEST(0); } | |||||
try { | |||||
m.assign("x","a{0}"); | |||||
TEST(0); | |||||
} | |||||
catch(cppcms::cppcms_error const &e) {} | |||||
catch(...) { TEST(0); } | |||||
try { | |||||
m.map(ss,"undefined"); | |||||
TEST(0); | |||||
} | |||||
catch(cppcms::cppcms_error const &e) {} | |||||
catch(...) { TEST(0); } | |||||
try { | |||||
m.map(ss,"undefined"); | |||||
TEST(0); | |||||
} | |||||
catch(cppcms::cppcms_error const &e) {} | |||||
catch(...) { TEST(0); } | |||||
try { | |||||
m.map(ss,"test1",1,2); | |||||
TEST(0); | |||||
} | |||||
catch(cppcms::cppcms_error const &e) {} | |||||
catch(...) { TEST(0); } | |||||
} | |||||
catch(std::exception const &e) { | |||||
std::cerr << "Fail" << e.what() << std::endl; | |||||
return 1; | |||||
} | |||||
std::cout << "ok" << std::endl; | |||||
return 0; | |||||
} | |||||