1 /* 2 Copyright (C) 2010-2014 Kristian Duske 3 4 This file is part of TrenchBroom. 5 6 TrenchBroom is free software: you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation, either version 3 of the License, or 9 (at your option) any later version. 10 11 TrenchBroom is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with TrenchBroom. If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 #ifndef TrenchBroom_StringMap 21 #define TrenchBroom_StringMap 22 23 #include "CollectionUtils.h" 24 #include "Exceptions.h" 25 #include "StringUtils.h" 26 27 #include <cassert> 28 #include <map> 29 #include <set> 30 #include <vector> 31 32 namespace TrenchBroom { 33 template <typename V> 34 class StringMapValueContainer { 35 public: 36 typedef std::vector<V> ValueContainer; 37 typedef std::vector<V> QueryResult; 38 insertValue(ValueContainer & values,const V & value)39 static void insertValue(ValueContainer& values, const V& value) { 40 values.push_back(value); 41 } 42 removeValue(ValueContainer & values,const V & value)43 static void removeValue(ValueContainer& values, const V& value) { 44 typename ValueContainer::iterator it = std::find(values.begin(), values.end(), value); 45 if (it == values.end()) 46 throw Exception("Cannot remove value (does not belong to this node)"); 47 values.erase(it); 48 } 49 getValues(const ValueContainer & values,QueryResult & result)50 static void getValues(const ValueContainer& values, QueryResult& result) { 51 VectorUtils::append(result, values); 52 } 53 }; 54 55 template <typename V> 56 class StringMultiMapValueContainer { 57 public: 58 typedef std::map<V, size_t> ValueContainer; 59 typedef std::set<V> QueryResult; 60 insertValue(ValueContainer & values,const V & value)61 static void insertValue(ValueContainer& values, const V& value) { 62 typename ValueContainer::iterator it = MapUtils::findOrInsert(values, value, 0u); 63 ++it->second; 64 } 65 removeValue(ValueContainer & values,const V & value)66 static void removeValue(ValueContainer& values, const V& value) { 67 typename ValueContainer::iterator it = values.find(value); 68 if (it == values.end()) 69 throw Exception("Cannot remove value from string map."); 70 if (it->second == 1) 71 values.erase(it); 72 else 73 --it->second; 74 } 75 getValues(const ValueContainer & values,QueryResult & result)76 static void getValues(const ValueContainer& values, QueryResult& result) { 77 typename ValueContainer::const_iterator it, end; 78 for (it = values.begin(), end = values.end(); it != end; ++it) 79 result.insert(it->first); 80 } 81 }; 82 83 template <typename V, typename P> 84 class StringMap { 85 public: 86 typedef typename P::QueryResult QueryResult; 87 private: 88 class Node { 89 private: 90 typedef std::set<Node> NodeSet; 91 typedef typename P::ValueContainer ValueContainer; 92 93 // The key is declared mutable because we must change it in splitNode and mergeNode, but the resulting new key 94 // will still compare equal to the old key. 95 mutable String m_key; 96 mutable ValueContainer m_values; 97 mutable NodeSet m_children; 98 public: Node(const String & key)99 explicit Node(const String& key) : 100 m_key(key) {} 101 102 bool operator<(const Node& rhs) const { 103 const size_t firstDiff = StringUtils::findFirstDifference(m_key, rhs.m_key); 104 if (firstDiff == 0) 105 return m_key[0] < rhs.m_key[0]; 106 // both keys share a common prefix and are thus treated as the same 107 return false; 108 } 109 110 /* 111 Possible cases for insertion: 112 index: 01234567 | | #m_key: 6 113 m_key: target | ^ | #key | conditions | todo 114 =================|===|======|=========================|====== 115 case: key: | | | | 116 1: targetli | 6 | 8 | ^ < #key AND ^ = #m_key | try insert in all children, if none match, create child 'li' and insert there; 117 ^ | | | | 118 2: target | 6 | 6 | ^ = #key AND ^ = #m_key | insert here; return true; 119 ^ | | | | 120 3: tarus | 3 | 5 | ^ < #key AND ^ < #m_key | split this node in 'tar' and 'get'; create child 'us' and insert there; 121 ^ | | | | 122 4: tar | 3 | 3 | ^ = #key AND ^ < #m_key | split this node in 'tar' and 'get'; insert here; return true; 123 ^ | | | | 124 5: blah | 0 | 4 | ^ = 0 | return false; 125 ^ | | | | 126 ================================================================================== 127 ^ indicates where key and m_key first differ 128 */ insert(const String & key,const V & value)129 void insert(const String& key, const V& value) const { 130 const size_t firstDiff = StringUtils::findFirstDifference(key, m_key); 131 if (firstDiff == 0 && !m_key.empty()) 132 // no common prefix 133 return; 134 if (firstDiff < key.size()) { 135 if (firstDiff < m_key.size()) { 136 // key and m_key share a common prefix, split this node and insert again 137 splitNode(firstDiff); 138 insert(key, value); 139 } else if (firstDiff == m_key.size()) { 140 // m_key is a prefix of key, find or create a child that shares a common prefix with remainder, and insert there, and insert here 141 const String remainder = key.substr(firstDiff); 142 const Node& child = findOrCreateChild(remainder); 143 child.insert(remainder, value); 144 } 145 } else if (firstDiff == key.size()) { 146 if (firstDiff < m_key.size()) { 147 // key is prefix of m_key, split this node and insert here 148 splitNode(firstDiff); 149 insertValue(value); 150 } else if (firstDiff == m_key.size()) { 151 // keys are equal, insert here 152 insertValue(value); 153 } 154 } 155 } 156 remove(const String & key,const V & value)157 bool remove(const String& key, const V& value) const { 158 const size_t firstDiff = StringUtils::findFirstDifference(key, m_key); 159 if (m_key.size() <= key.size() && firstDiff == m_key.size()) { 160 // this node's key is a prefix of the given key 161 if (firstDiff < key.size()) { 162 // the given key is longer than this node's key, so we must continue at the appropriate child node 163 const String remainder(key.substr(firstDiff)); 164 const Node query(remainder); 165 typename NodeSet::iterator it = m_children.find(query); 166 assert(it != m_children.end()); 167 const Node& child = *it; 168 if (child.remove(remainder, value)) 169 m_children.erase(it); 170 } else { 171 removeValue(value); 172 } 173 174 if (!m_key.empty() && m_values.empty() && m_children.size() == 1) 175 mergeNode(); 176 } 177 return !m_key.empty() && m_values.empty() && m_children.empty(); 178 } 179 queryExact(const String & key,QueryResult & result)180 void queryExact(const String& key, QueryResult& result) const { 181 const size_t firstDiff = StringUtils::findFirstDifference(key, m_key); 182 if (firstDiff == 0 && !m_key.empty()) 183 // no common prefix 184 return; 185 if (firstDiff == key.size() && firstDiff <= m_key.size()) { 186 // this node represents the given (remaining) prefix 187 if (firstDiff == m_key.size()) 188 getValues(result); 189 } else if (firstDiff < key.size() && firstDiff == m_key.size()) { 190 // this node is only a partial match, try to find a child to continue searching 191 const String remainder(key.substr(firstDiff)); 192 const Node query(remainder); 193 typename NodeSet::iterator it = m_children.find(query); 194 if (it != m_children.end()) { 195 const Node& child = *it; 196 child.queryExact(remainder, result); 197 } 198 } 199 } 200 queryPrefix(const String & prefix,QueryResult & result)201 void queryPrefix(const String& prefix, QueryResult& result) const { 202 const size_t firstDiff = StringUtils::findFirstDifference(prefix, m_key); 203 if (firstDiff == 0 && !m_key.empty()) 204 // no common prefix 205 return; 206 if (firstDiff == prefix.size() && firstDiff <= m_key.size()) { 207 // the given prefix is a prefix of this node's key, collect all values in the subtree starting at 208 // this node 209 collectValues(result); 210 } else if (firstDiff < prefix.size() && firstDiff == m_key.size()) { 211 // this node is only a partial match, try to find a child to continue searching 212 const String remainder(prefix.substr(firstDiff)); 213 const Node query(remainder); 214 typename NodeSet::iterator it = m_children.find(query); 215 if (it != m_children.end()) { 216 const Node& child = *it; 217 child.queryPrefix(remainder, result); 218 } 219 } 220 } 221 collectValues(QueryResult & result)222 void collectValues(QueryResult& result) const { 223 getValues(result); 224 typename NodeSet::const_iterator it, end; 225 for (it = m_children.begin(), end = m_children.end(); it != end; ++it) { 226 const Node& child = *it; 227 child.collectValues(result); 228 } 229 } 230 queryNumbered(const String & prefix,QueryResult & result)231 void queryNumbered(const String& prefix, QueryResult& result) const { 232 const size_t firstDiff = StringUtils::findFirstDifference(prefix, m_key); 233 if (firstDiff == 0 && !m_key.empty()) 234 // no common prefix 235 return; 236 if (firstDiff == prefix.size() && firstDiff <= m_key.size()) { 237 // the given prefix is a prefix of this node's key 238 // if the remainder of this node's key is a number, add this node's values and continue searching 239 // the entire subtree starting at this node 240 const String remainder(m_key.substr(firstDiff)); 241 if (StringUtils::isNumber(remainder)) { 242 getValues(result); 243 typename NodeSet::const_iterator it, end; 244 for (it = m_children.begin(), end = m_children.end(); it != end; ++it) { 245 const Node& child = *it; 246 child.collectIfNumbered(result); 247 } 248 } 249 } else if (firstDiff < prefix.size() && firstDiff == m_key.size()) { 250 // this node is only a partial match, try to find a child to continue searching 251 const String remainder(prefix.substr(firstDiff)); 252 const Node query(remainder); 253 typename NodeSet::iterator it = m_children.find(query); 254 if (it != m_children.end()) { 255 const Node& child = *it; 256 child.queryNumbered(remainder, result); 257 } 258 } 259 } 260 collectIfNumbered(QueryResult & result)261 void collectIfNumbered(QueryResult& result) const { 262 if (StringUtils::isNumber(m_key)) { 263 getValues(result); 264 typename NodeSet::const_iterator it, end; 265 for (it = m_children.begin(), end = m_children.end(); it != end; ++it) { 266 const Node& child = *it; 267 child.collectIfNumbered(result); 268 } 269 } 270 } 271 private: insertValue(const V & value)272 void insertValue(const V& value) const { 273 P::insertValue(m_values, value); 274 } 275 removeValue(const V & value)276 void removeValue(const V& value) const { 277 P::removeValue(m_values, value); 278 } 279 findOrCreateChild(const String & key)280 const Node& findOrCreateChild(const String& key) const { 281 std::pair<typename NodeSet::iterator, bool> result = m_children.insert(Node(key)); 282 typename NodeSet::iterator it = result.first; 283 return *it; 284 } 285 splitNode(const size_t index)286 void splitNode(const size_t index) const { 287 using std::swap; 288 289 assert(m_key.size() > 1); 290 assert(index < m_key.size()); 291 292 const String newKey = m_key.substr(0, index); 293 const String remainder = m_key.substr(index); 294 295 // We want to avoid copying the children of this node to the new child, therefore we swap with an empty 296 // node set. Afterwards this node's children are empty. 297 NodeSet newChildren; 298 swap(newChildren, m_children); 299 300 const Node& newChild = findOrCreateChild(remainder); 301 swap(newChild.m_children, newChildren); 302 swap(newChild.m_values, m_values); 303 304 m_key = newKey; 305 } 306 mergeNode()307 void mergeNode() const { 308 using std::swap; 309 310 assert(m_children.size() == 1); 311 assert(m_values.empty()); 312 313 NodeSet oldChildren; 314 swap(oldChildren, m_children); 315 316 const Node& child = *oldChildren.begin(); 317 swap(m_children, child.m_children); 318 swap(m_values, child.m_values); 319 320 m_key += child.m_key; 321 } 322 getValues(QueryResult & result)323 void getValues(QueryResult& result) const { 324 P::getValues(m_values, result); 325 } 326 }; 327 328 Node* m_root; 329 public: StringMap()330 StringMap() : 331 m_root(new Node("")) {} 332 ~StringMap()333 ~StringMap() { 334 delete m_root; 335 m_root = NULL; 336 } 337 insert(const String & key,const V & value)338 void insert(const String& key, const V& value) { 339 assert(m_root != NULL); 340 m_root->insert(key, value); 341 } 342 remove(const String & key,const V & value)343 void remove(const String& key, const V& value) { 344 assert(m_root != NULL); 345 m_root->remove(key, value); 346 } 347 clear()348 void clear() { 349 delete m_root; 350 m_root = new Node(""); 351 } 352 queryPrefixMatches(const String & prefix)353 QueryResult queryPrefixMatches(const String& prefix) const { 354 assert(m_root != NULL); 355 QueryResult result; 356 m_root->queryPrefix(prefix, result); 357 return result; 358 } 359 queryNumberedMatches(const String & prefix)360 QueryResult queryNumberedMatches(const String& prefix) const { 361 assert(m_root != NULL); 362 QueryResult result; 363 m_root->queryNumbered(prefix, result); 364 return result; 365 } 366 queryExactMatches(const String & prefix)367 QueryResult queryExactMatches(const String& prefix) const { 368 assert(m_root != NULL); 369 QueryResult result; 370 m_root->queryExact(prefix, result); 371 return result; 372 } 373 private: 374 StringMap(const StringMap& other); 375 StringMap& operator=(const StringMap& other); 376 }; 377 } 378 379 #endif /* defined(TrenchBroom_StringMap) */ 380