/
zOMGdev
/
cppcms
Обзор
Документация
Войти
/
zOMGdev
/
cppcms
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
v2.0.0.beta1
src/url_mapper.cpp
594 строки
15 KB
Artyom Beilis
- Update copyright notice,
03 янв 2012, 17:58
03 янв 2012, 17:58
068d3f9
Код
Авторство
О чём код?
/////////////////////////////////////////////////////////////////////////////// // // Copyright (C) 2008-2012 Artyom Beilis (Tonkikh) <artyomtnk@yahoo.com> // // See accompanying file COPYING.TXT file for licensing details. // /////////////////////////////////////////////////////////////////////////////// #define CPPCMS_SOURCE #include <cppcms/url_mapper.h> #include <cppcms/application.h> #include <cppcms/service.h> #include <cppcms/cppcms_error.h> #include <cppcms/steal_buf.h> #include <booster/log.h> #include "cached_settings.h" #include <map> #include <stdlib.h> #include <iostream> namespace cppcms { template<typename Data,typename Entry> struct stream_it { Data const *self; Entry const *formatting; filters::streamable const * const *params; size_t params_no; std::map<string_key,std::string> const *data_helpers_default; std::map<string_key,std::string> const *data_helpers_override; void write(std::ostream &out) const { self->write(*formatting,params,params_no,*data_helpers_default,*data_helpers_override,out); } }; template<typename Data,typename Entry> std::ostream &operator<<(std::ostream &out,stream_it<Data,Entry> const &wr) { wr.write(out); return out; } struct url_mapper::data { data() : throws(true),parent(0),this_application(0) {} bool throws; std::string this_name; application *parent; application *this_application; struct entry { std::vector<std::string> parts; std::vector<int> indexes; std::vector<std::string> keys; application *child; entry() : child(0) {} }; template<typename Data,typename Entry> friend struct stream_it; typedef std::map<size_t,entry> by_size_type; typedef std::map<string_key,by_size_type> by_key_type; typedef std::map<string_key,std::string> helpers_type; by_key_type by_key; helpers_type helpers; std::string root; entry const &get_entry(string_key const &key,size_t params_no,string_key const &full_url) const { by_key_type::const_iterator kp = by_key.find(key); if(kp == by_key.end()) throw cppcms_error("url_mapper: key `" + key.str() + "' not found for " "url `" + full_url.str() + "'"); by_size_type::const_iterator sp = kp->second.find(params_no); if(sp == kp->second.end()) throw cppcms_error("url_mapper: invalid number of parameters for " + key.str() + "in url `" + full_url.str() + "'"); return sp->second; } url_mapper *is_app(string_key const &key) const { by_key_type::const_iterator kp = by_key.find(key); if(kp == by_key.end()) return 0; by_size_type::const_iterator sp = kp->second.begin(); if(sp==kp->second.end()) return 0; if(sp->second.child) return &sp->second.child->mapper(); return 0; } url_mapper &child(string_key const &name,string_key const &full_url) { entry const &fmt = get_entry(name,1,full_url); if(!fmt.child) { throw cppcms_error("url_mapper: the key " + name.str() + " is not child application key" " in url `" + full_url.str() + "'"); } return fmt.child->mapper(); } void write( entry const &formatting, filters::streamable const * const *params, size_t params_no, std::map<string_key,std::string> const &data_helpers_default, std::map<string_key,std::string> const &data_helpers_override, std::ostream &out) const { for(size_t i=0;i<formatting.parts.size();i++) { out << formatting.parts[i]; if( i < formatting.indexes.size() ) { if(formatting.indexes[i]==0) { string_key hkey = string_key::unowned(formatting.keys[i]); std::map<string_key,std::string>::const_iterator p; p = data_helpers_override.find(string_key::unowned(hkey)); if(p != data_helpers_override.end()) { out << p->second; } else { p = data_helpers_default.find(hkey); if(p!=data_helpers_default.end()) out << p->second; } } else { size_t index = formatting.indexes[i] - 1; if(index >= params_no) { throw cppcms_error("url_mapper: Index of parameter out of range"); } (*params[index])(out); } } } } void map( string_key const &key, string_key const &full_url, filters::streamable const * const *params, size_t params_no, std::map<string_key,std::string> const &data_helpers_default, std::map<string_key,std::string> const &data_helpers_override, std::ostream &out) const { entry const &formatting = get_entry(key,params_no,full_url); if(parent) { stream_it<data,entry> url = { this, &formatting, params, params_no, &data_helpers_default, &data_helpers_override }; filters::streamable stream_url(url); filters::streamable const *par_ptr=&stream_url; parent->mapper().d->map(this_name,full_url,&par_ptr,1, data_helpers_default, data_helpers_override,out); } else { out << root; write(formatting,params,params_no,data_helpers_default,data_helpers_override,out); } } }; void url_mapper::assign(std::string const &key,std::string const &url) { if( key.empty() || key.find('/') != std::string::npos || key.find(';') != std::string::npos || key.find(',') != std::string::npos || key ==".." || key == "." ) { throw cppcms_error("cppcms::url_mapper: key may not be '' , '.' or '..' and must not include '/' in it"); } real_assign(key,url,0); } void url_mapper::assign(std::string const &url) { real_assign(std::string(),url,0); } void url_mapper::real_assign(std::string const &key,std::string const &url,application *child) { 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++; } if(child && max_index!=1) { throw cppcms_error("cppcms::url_mapper the application mapping should use only 1 parameter"); } e.parts.push_back(std::string(prev,p)); e.child = child; if(child) { if(d->by_key.find(key)!=d->by_key.end()) throw cppcms_error( "cppcms::url_mapper: mounted application key `" + key + "' can't be shared with ordinary url key"); } else { data::by_key_type::iterator p = d->by_key.find(key); if(p!=d->by_key.end()) { data::by_size_type::const_iterator p2 = p->second.find(1); if(p2!=p->second.end() && p2->second.child) { throw cppcms_error( "cppcms::url_mapper: ordinary url key `"+key+ "can't be shared with mounted application key"); } } } d->by_key[key][max_index] = e; } void url_mapper::set_value(std::string const &key,std::string const &value) { root_mapper()->d->helpers[key]=value; } void url_mapper::clear_value(std::string const &key) { root_mapper()->d->helpers.erase(key); } url_mapper &url_mapper::child(std::string const &name) { return d->child(name,name); } url_mapper *url_mapper::root_mapper() { if(d->parent) return &d->parent->root()->mapper(); else return this; } void url_mapper::mount(std::string const &name,std::string const &url,application &app) { app.mapper().d->parent = d->this_application; app.mapper().d->this_name = name; real_assign(name,url,&app); // Copy all values to root most one std::map<string_key,std::string> &values = app.mapper().d->helpers; std::map<string_key,std::string>::const_iterator p; for(p=values.begin();p!=values.end();++p) { set_value(p->first,p->second); } values.clear(); } url_mapper::url_mapper(cppcms::application *my_app) : d(new url_mapper::data()) { d->this_application = my_app; d->throws = my_app->service().cached_settings().misc.invalid_url_throws; } url_mapper::~url_mapper() { } std::string url_mapper::root() { return d->root; } void url_mapper::root(std::string const &r) { d->root = r; } url_mapper &url_mapper::get_mapper_for_key(string_key const &key,string_key &real_key,std::vector<string_key> &keywords) { url_mapper *mapper = this; size_t pos = 0; if(key.empty()) { real_key.clear(); return *mapper; } if(key[0]=='/') { mapper = &mapper->topmost(); pos = 1; } for(;;) { size_t end = key.find('/',pos); if(end == std::string::npos) { size_t separator = key.find(';',pos); if(separator == std::string::npos) { real_key = key.unowned_substr(pos); } else { keywords.reserve(6); real_key = key.unowned_substr(pos,separator - pos); pos = separator + 1; for(;;){ end = key.find(',',pos); size_t size = end - pos; keywords.push_back(key.unowned_substr(pos,size)); if(end == std::string::npos) break; pos = end + 1; } } if(real_key == ".") real_key.clear(); else if(real_key == "..") { mapper = &mapper->parent(); real_key.clear(); } url_mapper *tmp = mapper->d->is_app(real_key); if(tmp) { // empty special key real_key.clear(); return *tmp; } return *mapper; } size_t chunk_size = end - pos; string_key subapp = key.unowned_substr(pos,chunk_size); if(subapp == ".") ; // Just continue where we are else if(subapp == "..") { mapper = &mapper->parent(); } else { mapper = &mapper->d->child(subapp,key); } pos = end + 1; } } void url_mapper::real_map( char const *ckey, filters::streamable const * const *params, size_t params_no, std::ostream &output) { util::steal_buffer<> temp_buf; if(!d->throws) temp_buf.steal(output); try { string_key key=string_key::unowned(ckey); string_key real_key; std::vector<string_key> direct; url_mapper &mp = get_mapper_for_key(key,real_key,direct); if(params_no < direct.size()) throw cppcms_error("url_mapper: number of keywords is larger then number of actual parameters"); std::map<string_key,std::string> mappings; if(direct.size() == 0) { mp.d->map(real_key,key,params,params_no,topmost().d->helpers,mappings,output); } else { size_t direct_size = direct.size(); filters::streamable const * const *key_params = params; params += direct_size; params_no -= direct_size; for(unsigned i=0;i<direct.size();i++) { std::ostringstream ss; ss.copyfmt(output); (*key_params[i])(ss); mappings[direct[i]]=ss.str(); } mp.d->map(real_key,key,params,params_no,topmost().d->helpers,mappings,output); } } catch(cppcms_error const &e) { if(d->throws) { throw; } BOOSTER_ERROR("cppcms") << e.what() <<'\n' << booster::trace(e); temp_buf.release(); output<<"/this_is_an_invalid_url_generated_by_url_mapper"; return; } if(!d->throws) { temp_buf.release(); output << temp_buf.c_str(); } } void url_mapper::map( std::ostream &out, std::string const &key) { map(out,key.c_str()); } void url_mapper::map( std::ostream &out, std::string const &key, filters::streamable const &p1) { map(out,key.c_str(),p1); } void url_mapper::map( std::ostream &out, std::string const &key, filters::streamable const &p1, filters::streamable const &p2) { map(out,key.c_str(),p1,p2); } void url_mapper::map( std::ostream &out, std::string const &key, filters::streamable const &p1, filters::streamable const &p2, filters::streamable const &p3) { map(out,key.c_str(),p1,p2,p3); } 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) { map(out,key.c_str(),p1,p2,p3,p4); } 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, filters::streamable const &p5) { map(out,key.c_str(),p1,p2,p3,p4,p5); } 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, filters::streamable const &p5, filters::streamable const &p6) { map(out,key.c_str(),p1,p2,p3,p4,p5,p6); } void url_mapper::map( std::ostream &out, char const *key) { real_map(key,0,0,out); } void url_mapper::map( std::ostream &out, char const *key, filters::streamable const &p1) { filters::streamable const *params[1]= { &p1 }; real_map(key,params,1,out); } void url_mapper::map( std::ostream &out, char const *key, filters::streamable const &p1, filters::streamable const &p2) { filters::streamable const *params[2] = { &p1,&p2 }; real_map(key,params,2,out); } void url_mapper::map( std::ostream &out, char const *key, filters::streamable const &p1, filters::streamable const &p2, filters::streamable const &p3) { filters::streamable const *params[3] = { &p1,&p2,&p3 }; real_map(key,params,3,out); } void url_mapper::map( std::ostream &out, char const *key, filters::streamable const &p1, filters::streamable const &p2, filters::streamable const &p3, filters::streamable const &p4) { filters::streamable const *params[4] = { &p1,&p2,&p3,&p4 }; real_map(key,params,4,out); } void url_mapper::map( std::ostream &out, char const *key, filters::streamable const &p1, filters::streamable const &p2, filters::streamable const &p3, filters::streamable const &p4, filters::streamable const &p5) { filters::streamable const *params[5] = { &p1,&p2,&p3,&p4,&p5 }; real_map(key,params,5,out); } void url_mapper::map( std::ostream &out, char const *key, filters::streamable const &p1, filters::streamable const &p2, filters::streamable const &p3, filters::streamable const &p4, filters::streamable const &p5, filters::streamable const &p6) { filters::streamable const *params[6] = { &p1,&p2,&p3,&p4,&p5,&p6 }; real_map(key,params,6,out); } url_mapper &url_mapper::parent() { if(d->parent) { return d->parent->mapper(); } throw cppcms_error("url_mapper: no parent found"); } url_mapper &url_mapper::topmost() { application *app = d->this_application; if(!app) return *this; while(app->mapper().d->parent) app = app->mapper().d->parent; return app->mapper(); } }