$search
00001 #include "typename.hh" 00002 #include <boost/static_assert.hpp> 00003 #include <utilmm/stringtools.hh> 00004 #include <iostream> 00005 00006 using namespace std; 00007 using namespace boost; 00008 using namespace Typelib; 00009 using namespace utilmm; 00010 00011 namespace 00012 { 00013 typedef std::string::value_type NamespaceMarkType; 00014 static const NamespaceMarkType NamespaceMark = '/'; 00015 static const char* NamespaceMarkString = "/"; 00016 00017 struct NameSeparator : public char_separator<NamespaceMarkType> 00018 { 00019 NameSeparator() 00020 : char_separator<NamespaceMarkType>(NamespaceMarkString, "", boost::keep_empty_tokens) {} 00021 }; 00022 typedef boost::tokenizer<NameSeparator> NameTokenizer; 00023 00024 // helper function which checks that \c identifier does contain 00025 // only valid characters. Note that identifier is supposed 00026 // to be a normalized name, so that it cannot be empty 00027 bool isValidIdentifier(const string& identifier) 00028 { 00029 if (identifier.empty()) return false; 00030 00031 const int length(identifier.length()); 00032 for (int i = 0; i < length; ++i) 00033 { 00034 string::value_type c(identifier[i]); 00035 if (c != '_' && c != ' ' && !isalnum(c) && c != '<' && c != '>') 00036 return false; 00037 } 00038 00039 return true; 00040 } 00041 } 00042 00043 namespace Typelib 00044 { 00045 bool isAbsoluteName(const string& identifier) 00046 { 00047 if (identifier.empty()) return false; 00048 return identifier[0] == NamespaceMark; 00049 } 00050 00051 static bool isValidTypeBasename(std::string s, bool absolute, bool accept_integers) 00052 { 00053 if (accept_integers && s.find_first_not_of("0123456789") == string::npos) 00054 return true; 00055 00056 int pos = 0; 00057 if (absolute && s[0] != '/') 00058 return false; 00059 00060 if (s[0] == '/') 00061 ++pos; 00062 00063 size_t modifiers = s.find_first_of("*["); 00064 if (modifiers != string::npos) 00065 return isValidIdentifier(string(s, pos, modifiers - pos)); 00066 else 00067 return isValidIdentifier(string(s, pos)); 00068 00069 } 00070 00071 std::list<std::string> splitTypename(std::string const& name) 00072 { 00073 unsigned int start_pos = 0; 00074 if (name[0] == '/') 00075 start_pos++; 00076 00077 int template_level = 0; 00078 std::list<std::string> result; 00079 for (unsigned int i = start_pos; i < name.length(); ++i) 00080 { 00081 if (name[i] == '/') 00082 { 00083 if (template_level == 0) 00084 { 00085 result.push_back(string(name, start_pos, i - start_pos)); 00086 start_pos = i + 1; 00087 } 00088 } 00089 else if (name[i] == '<') 00090 template_level++; 00091 else if (name[i] == '>') 00092 template_level--; 00093 } 00094 if (start_pos < name.length()) 00095 result.push_back(string(name, start_pos, name.length() - start_pos)); 00096 00097 return result; 00098 } 00099 00100 static pair<bool, int> isValidTypename(std::string const& s, int pos, bool absolute, bool accept_integers) 00101 { 00102 unsigned int start_pos = pos; 00103 if (s[pos] == '/') 00104 pos++; 00105 00106 for (unsigned int i = pos; i < s.length(); ++i) 00107 { 00108 if (s[i] == '/') 00109 { 00110 if (!isValidTypeBasename(string(s, start_pos, i - start_pos), absolute, accept_integers)) 00111 return make_pair(false, i); 00112 start_pos = i; 00113 } 00114 else if (s[i] == '<') 00115 { 00116 if (!isValidTypeBasename(string(s, start_pos, i - start_pos), absolute, accept_integers)) 00117 return make_pair(false, i); 00118 00119 while (s[i] != '>') 00120 { 00121 pair<bool, int> valid_arg = isValidTypename(s, i + 1, true, true); 00122 i = valid_arg.second; 00123 00124 if (!valid_arg.first) 00125 return valid_arg; 00126 else if (i == s.length() || (s[i] != ',' && s[i] != '>')) 00127 return make_pair(false, i); 00128 } 00129 00130 start_pos = i + 1; 00131 if (i + 1 < s.length()) 00132 { 00133 if (s[i + 1] == '/') 00134 i++; 00135 else if (s[i + 1] != '>' && s[i + 1] != '[') 00136 return make_pair(false, i + 1); 00137 } 00138 } 00139 else if (s[i] == '[') 00140 { 00141 // start_pos == i if we are parsing an array of templates 00142 if (start_pos != i && !isValidTypeBasename(string(s, start_pos, i - start_pos), absolute, accept_integers)) 00143 return make_pair(false, i); 00144 00145 if (i + 1 == s.length()) 00146 return make_pair(false, i); 00147 00148 start_pos = i + 1; 00149 } 00150 else if (s[i] == ']') 00151 { 00152 if (start_pos == i) 00153 return make_pair(false, start_pos); 00154 if (string(s, start_pos, i - start_pos).find_first_not_of("0123456789") != string::npos) 00155 return make_pair(false, start_pos); 00156 00157 start_pos = i + 1; 00158 if (i + 1 < s.length()) 00159 { 00160 if (s[i + 1] == '/') 00161 i++; 00162 else if (s[i + 1] != '>' && s[i + 1] != '[') 00163 return make_pair(false, i + 1); 00164 } 00165 } 00166 else if (s[i] == '>' || s[i] == ',') 00167 { 00168 if (!isValidTypeBasename(string(s, start_pos, i - start_pos), true, true)) 00169 return make_pair(false, i); 00170 return make_pair(true, i); 00171 } 00172 } 00173 00174 if (start_pos != s.length()) 00175 return make_pair(isValidTypeBasename(string(s, start_pos, s.length() - start_pos), absolute, accept_integers), s.length()); 00176 else return make_pair(true, s.length()); 00177 } 00178 00179 bool isValidTypename(const std::string& name, bool absolute) 00180 { 00181 if (name.empty()) 00182 return false; 00183 return isValidTypename(name, 0, absolute, false).first; 00184 } 00185 00186 bool isValidNamespace(const string& name, bool absolute) 00187 { 00188 if (name.empty()) return false; 00189 if (absolute && name[0] != NamespaceMark) return false; 00190 00191 NameTokenizer tokenizer(name); 00192 00193 NameTokenizer::const_iterator it = tokenizer.begin(); 00194 for (; it != tokenizer.end(); ++it) 00195 { 00196 if (!isValidIdentifier(*it)) 00197 return false; 00198 } 00199 00200 return true; 00201 } 00202 00203 bool isInNamespace(const string& type, const std::string& nspace, bool recursive) 00204 { 00205 std::string normalized_nspace( getNormalizedNamespace(nspace) ); 00206 const int length (normalized_nspace.length()); 00207 00208 bool begins_with = string(type, 0, length) == normalized_nspace; 00209 if (!begins_with) return false; 00210 if (recursive) return true; 00211 std::string remainder(type, length, string::npos); 00212 return splitTypename(remainder).size() == 1; 00213 } 00214 00215 std::string getNormalizedNamespace(const std::string& name) 00216 { 00217 if (name.empty()) return NamespaceMarkString; 00218 if (name[name.size() - 1] != NamespaceMark) return name + NamespaceMark; 00219 return name; 00220 } 00221 00222 std::string getTypename(const std::string& name) 00223 { 00224 list<string> split = splitTypename(name); 00225 if (split.empty()) 00226 return std::string(); 00227 return split.back(); 00228 } 00229 00230 std::string getRelativeName(std::string const& name, std::string const& ns) 00231 { 00232 size_t size = ns.length(); 00233 if (*ns.rbegin() != '/') 00234 size += 1; 00235 return std::string(name, size, string::npos); 00236 } 00237 00238 std::string getMinimalPathTo(std::string const& full_name, std::string const& ns) 00239 { 00240 string type_ns = getNamespace(full_name); 00241 if (isInNamespace(full_name, ns, true)) 00242 return getRelativeName(getNamespace(full_name), ns); 00243 else if (ns.find(type_ns) != string::npos || ns.find(full_name) != string::npos) // need an absolute path 00244 return type_ns; 00245 00246 list<string> tok1(splitTypename(type_ns)); 00247 list<string> tok2(splitTypename(ns)); 00248 list<string>::const_iterator it1 = tok1.begin(); 00249 list<string>::const_iterator it2 = tok2.begin(); 00250 std::vector<std::string> common_tokens; 00251 00252 // Filter out the common NS parts 00253 std::string ns1, ns2; 00254 for (; it1 != tok1.end() && it2 != tok2.end(); ++it1, ++it2) 00255 { 00256 ns1 = *it1; 00257 ns2 = *it2; 00258 int value = ns1.compare(ns2); 00259 if (value) break; 00260 common_tokens.push_back(ns1); 00261 } 00262 00263 if (common_tokens.empty()) 00264 return type_ns; 00265 00266 // Build the remainder of both namespaces, and verify that the remainder 00267 // of the type is unambiguous. If it is, go back in it1 until it is not. 00268 // We already checked the case where the full path is ambiguous 00269 std::string result = *it1; 00270 list<string>::const_iterator remainder_it = it1; 00271 for (++remainder_it; remainder_it != tok1.end(); ++remainder_it) 00272 result += Typelib::NamespaceMarkString + *remainder_it; 00273 00274 while (!common_tokens.empty() && ns.find(result) != string::npos) 00275 { 00276 result = common_tokens.back() + Typelib::NamespaceMarkString + result; 00277 common_tokens.pop_back(); 00278 } 00279 if (common_tokens.empty()) 00280 return type_ns; 00281 else if (result.empty()) 00282 return result; 00283 else 00284 return result + Typelib::NamespaceMarkString; 00285 } 00286 00287 std::string getNamespace(const std::string& name) 00288 { 00289 list<string> split = splitTypename(name); 00290 if (split.empty()) 00291 return "/"; 00292 split.pop_back(); 00293 00294 std::string result; 00295 for (list<string>::const_iterator it = split.begin(); it != split.end(); ++it) 00296 result += "/" + *it; 00297 00298 result += "/"; 00299 return result; 00300 } 00301 }; 00302