Mercurial > hg > orthanc
annotate Core/RestApi/RestApiHierarchy.cpp @ 1035:39537e2fe637
back to mainline
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Thu, 10 Jul 2014 16:22:46 +0200 |
parents | f1ff2a2f06cd |
children | 0332e6e8c679 |
rev | line source |
---|---|
969 | 1 /** |
2 * Orthanc - A Lightweight, RESTful DICOM Store | |
3 * Copyright (C) 2012-2014 Medical Physics Department, CHU of Liege, | |
4 * Belgium | |
5 * | |
6 * This program is free software: you can redistribute it and/or | |
7 * modify it under the terms of the GNU General Public License as | |
8 * published by the Free Software Foundation, either version 3 of the | |
9 * License, or (at your option) any later version. | |
10 * | |
11 * In addition, as a special exception, the copyright holders of this | |
12 * program give permission to link the code of its release with the | |
13 * OpenSSL project's "OpenSSL" library (or with modified versions of it | |
14 * that use the same license as the "OpenSSL" library), and distribute | |
15 * the linked executables. You must obey the GNU General Public License | |
16 * in all respects for all of the code used other than "OpenSSL". If you | |
17 * modify file(s) with this exception, you may extend this exception to | |
18 * your version of the file(s), but you are not obligated to do so. If | |
19 * you do not wish to do so, delete this exception statement from your | |
20 * version. If you delete this exception statement from all source files | |
21 * in the program, then also delete it here. | |
22 * | |
23 * This program is distributed in the hope that it will be useful, but | |
24 * WITHOUT ANY WARRANTY; without even the implied warranty of | |
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
26 * General Public License for more details. | |
27 * | |
28 * You should have received a copy of the GNU General Public License | |
29 * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
30 **/ | |
31 | |
32 | |
33 #include "RestApiHierarchy.h" | |
34 | |
975 | 35 #include "../OrthancException.h" |
36 | |
969 | 37 #include <cassert> |
38 | |
39 namespace Orthanc | |
40 { | |
978 | 41 RestApiHierarchy::Resource::Resource() : |
972 | 42 getHandler_(NULL), |
43 postHandler_(NULL), | |
44 putHandler_(NULL), | |
45 deleteHandler_(NULL) | |
46 { | |
47 } | |
48 | |
49 | |
978 | 50 bool RestApiHierarchy::Resource::HasHandler(HttpMethod method) const |
972 | 51 { |
52 switch (method) | |
53 { | |
54 case HttpMethod_Get: | |
55 return getHandler_ != NULL; | |
56 | |
57 case HttpMethod_Post: | |
58 return postHandler_ != NULL; | |
59 | |
60 case HttpMethod_Put: | |
61 return putHandler_ != NULL; | |
62 | |
63 case HttpMethod_Delete: | |
64 return deleteHandler_ != NULL; | |
65 | |
66 default: | |
67 throw OrthancException(ErrorCode_ParameterOutOfRange); | |
68 } | |
69 } | |
70 | |
71 | |
978 | 72 bool RestApiHierarchy::Resource::IsEmpty() const |
969 | 73 { |
972 | 74 return (getHandler_ == NULL && |
75 postHandler_ == NULL && | |
76 putHandler_ == NULL && | |
77 deleteHandler_ == NULL); | |
78 } | |
79 | |
80 | |
969 | 81 RestApiHierarchy& RestApiHierarchy::AddChild(Children& children, |
82 const std::string& name) | |
83 { | |
84 Children::iterator it = children.find(name); | |
85 | |
86 if (it == children.end()) | |
87 { | |
88 // Create new child | |
89 RestApiHierarchy *child = new RestApiHierarchy; | |
90 children[name] = child; | |
91 return *child; | |
92 } | |
93 else | |
94 { | |
95 return *it->second; | |
96 } | |
97 } | |
98 | |
99 | |
978 | 100 |
101 bool RestApiHierarchy::Resource::Handle(RestApiGetCall& call) const | |
102 { | |
103 if (getHandler_ != NULL) | |
104 { | |
105 getHandler_(call); | |
106 return true; | |
107 } | |
108 else | |
109 { | |
110 return false; | |
111 } | |
112 } | |
113 | |
114 | |
115 bool RestApiHierarchy::Resource::Handle(RestApiPutCall& call) const | |
116 { | |
117 if (putHandler_ != NULL) | |
118 { | |
119 putHandler_(call); | |
120 return true; | |
121 } | |
122 else | |
123 { | |
124 return false; | |
125 } | |
126 } | |
127 | |
128 | |
129 bool RestApiHierarchy::Resource::Handle(RestApiPostCall& call) const | |
130 { | |
131 if (postHandler_ != NULL) | |
132 { | |
133 postHandler_(call); | |
134 return true; | |
135 } | |
136 else | |
137 { | |
138 return false; | |
139 } | |
140 } | |
141 | |
142 | |
143 bool RestApiHierarchy::Resource::Handle(RestApiDeleteCall& call) const | |
144 { | |
145 if (deleteHandler_ != NULL) | |
146 { | |
147 deleteHandler_(call); | |
148 return true; | |
149 } | |
150 else | |
151 { | |
152 return false; | |
153 } | |
154 } | |
155 | |
156 | |
157 | |
969 | 158 void RestApiHierarchy::DeleteChildren(Children& children) |
159 { | |
160 for (Children::iterator it = children.begin(); | |
161 it != children.end(); it++) | |
162 { | |
163 delete it->second; | |
164 } | |
165 } | |
166 | |
167 | |
168 template <typename Handler> | |
169 void RestApiHierarchy::RegisterInternal(const RestApiPath& path, | |
170 Handler handler, | |
171 size_t level) | |
172 { | |
173 if (path.GetLevelCount() == level) | |
174 { | |
175 if (path.IsUniversalTrailing()) | |
176 { | |
177 universalHandlers_.Register(handler); | |
178 } | |
179 else | |
180 { | |
181 handlers_.Register(handler); | |
182 } | |
183 } | |
184 else | |
185 { | |
186 RestApiHierarchy* child; | |
187 if (path.IsWildcardLevel(level)) | |
188 { | |
189 child = &AddChild(wildcardChildren_, path.GetWildcardName(level)); | |
190 } | |
191 else | |
192 { | |
193 child = &AddChild(children_, path.GetLevelName(level)); | |
194 } | |
195 | |
196 child->RegisterInternal(path, handler, level + 1); | |
197 } | |
198 } | |
199 | |
200 | |
978 | 201 bool RestApiHierarchy::LookupResource(HttpHandler::Arguments& components, |
969 | 202 const UriComponents& uri, |
978 | 203 IVisitor& visitor, |
204 size_t level) | |
969 | 205 { |
980
f1ff2a2f06cd
use RestApiHierarchy inside RestApi
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
978
diff
changeset
|
206 if (uri.size() != 0 && |
f1ff2a2f06cd
use RestApiHierarchy inside RestApi
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
978
diff
changeset
|
207 level > uri.size()) |
f1ff2a2f06cd
use RestApiHierarchy inside RestApi
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
978
diff
changeset
|
208 { |
f1ff2a2f06cd
use RestApiHierarchy inside RestApi
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
978
diff
changeset
|
209 return false; |
f1ff2a2f06cd
use RestApiHierarchy inside RestApi
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
978
diff
changeset
|
210 } |
f1ff2a2f06cd
use RestApiHierarchy inside RestApi
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
978
diff
changeset
|
211 |
969 | 212 UriComponents trailing; |
213 | |
214 // Look for an exact match on the resource of interest | |
980
f1ff2a2f06cd
use RestApiHierarchy inside RestApi
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
978
diff
changeset
|
215 if (uri.size() == 0 || |
f1ff2a2f06cd
use RestApiHierarchy inside RestApi
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
978
diff
changeset
|
216 level == uri.size()) |
969 | 217 { |
218 if (!handlers_.IsEmpty() && | |
978 | 219 visitor.Visit(handlers_, uri, components, trailing)) |
969 | 220 { |
221 return true; | |
222 } | |
223 } | |
224 | |
225 | |
980
f1ff2a2f06cd
use RestApiHierarchy inside RestApi
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
978
diff
changeset
|
226 if (level < uri.size()) // A recursive call is possible |
969 | 227 { |
980
f1ff2a2f06cd
use RestApiHierarchy inside RestApi
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
978
diff
changeset
|
228 // Try and go down in the hierarchy, using an exact match for the child |
f1ff2a2f06cd
use RestApiHierarchy inside RestApi
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
978
diff
changeset
|
229 Children::const_iterator child = children_.find(uri[level]); |
f1ff2a2f06cd
use RestApiHierarchy inside RestApi
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
978
diff
changeset
|
230 if (child != children_.end()) |
969 | 231 { |
980
f1ff2a2f06cd
use RestApiHierarchy inside RestApi
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
978
diff
changeset
|
232 if (child->second->LookupResource(components, uri, visitor, level + 1)) |
f1ff2a2f06cd
use RestApiHierarchy inside RestApi
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
978
diff
changeset
|
233 { |
f1ff2a2f06cd
use RestApiHierarchy inside RestApi
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
978
diff
changeset
|
234 return true; |
f1ff2a2f06cd
use RestApiHierarchy inside RestApi
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
978
diff
changeset
|
235 } |
969 | 236 } |
237 | |
980
f1ff2a2f06cd
use RestApiHierarchy inside RestApi
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
978
diff
changeset
|
238 // Try and go down in the hierarchy, using wildcard rules for children |
f1ff2a2f06cd
use RestApiHierarchy inside RestApi
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
978
diff
changeset
|
239 for (child = wildcardChildren_.begin(); |
f1ff2a2f06cd
use RestApiHierarchy inside RestApi
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
978
diff
changeset
|
240 child != wildcardChildren_.end(); child++) |
f1ff2a2f06cd
use RestApiHierarchy inside RestApi
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
978
diff
changeset
|
241 { |
f1ff2a2f06cd
use RestApiHierarchy inside RestApi
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
978
diff
changeset
|
242 HttpHandler::Arguments subComponents = components; |
f1ff2a2f06cd
use RestApiHierarchy inside RestApi
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
978
diff
changeset
|
243 subComponents[child->first] = uri[level]; |
969 | 244 |
980
f1ff2a2f06cd
use RestApiHierarchy inside RestApi
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
978
diff
changeset
|
245 if (child->second->LookupResource(subComponents, uri, visitor, level + 1)) |
f1ff2a2f06cd
use RestApiHierarchy inside RestApi
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
978
diff
changeset
|
246 { |
f1ff2a2f06cd
use RestApiHierarchy inside RestApi
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
978
diff
changeset
|
247 return true; |
f1ff2a2f06cd
use RestApiHierarchy inside RestApi
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
978
diff
changeset
|
248 } |
f1ff2a2f06cd
use RestApiHierarchy inside RestApi
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
978
diff
changeset
|
249 } |
969 | 250 } |
251 | |
252 | |
253 // As a last resort, call the universal handlers, if any | |
254 if (!universalHandlers_.IsEmpty()) | |
255 { | |
256 trailing.resize(uri.size() - level); | |
257 size_t pos = 0; | |
258 for (size_t i = level; i < uri.size(); i++, pos++) | |
259 { | |
260 trailing[pos] = uri[i]; | |
261 } | |
262 | |
263 assert(pos == trailing.size()); | |
264 | |
978 | 265 if (visitor.Visit(universalHandlers_, uri, components, trailing)) |
969 | 266 { |
267 return true; | |
268 } | |
269 } | |
270 | |
271 return false; | |
272 } | |
273 | |
274 | |
978 | 275 bool RestApiHierarchy::CanGenerateDirectory() const |
276 { | |
277 return (!handlers_.HasHandler(HttpMethod_Get) && | |
278 universalHandlers_.IsEmpty() && | |
279 wildcardChildren_.size() == 0); | |
280 } | |
281 | |
282 | |
969 | 283 bool RestApiHierarchy::GetDirectory(Json::Value& result, |
284 const UriComponents& uri, | |
285 size_t level) | |
286 { | |
287 if (uri.size() == level) | |
288 { | |
978 | 289 if (CanGenerateDirectory()) |
969 | 290 { |
291 result = Json::arrayValue; | |
292 | |
293 for (Children::const_iterator it = children_.begin(); | |
294 it != children_.end(); it++) | |
295 { | |
296 result.append(it->first); | |
297 } | |
298 | |
299 return true; | |
300 } | |
301 else | |
302 { | |
303 return false; | |
304 } | |
305 } | |
306 | |
307 Children::const_iterator child = children_.find(uri[level]); | |
308 if (child != children_.end()) | |
309 { | |
310 if (child->second->GetDirectory(result, uri, level + 1)) | |
311 { | |
312 return true; | |
313 } | |
314 } | |
315 | |
316 for (child = wildcardChildren_.begin(); | |
317 child != wildcardChildren_.end(); child++) | |
318 { | |
319 if (child->second->GetDirectory(result, uri, level + 1)) | |
320 { | |
321 return true; | |
322 } | |
323 } | |
324 | |
325 return false; | |
326 } | |
327 | |
328 | |
329 RestApiHierarchy::~RestApiHierarchy() | |
330 { | |
331 DeleteChildren(children_); | |
332 DeleteChildren(wildcardChildren_); | |
333 } | |
334 | |
970 | 335 void RestApiHierarchy::Register(const std::string& uri, |
974 | 336 RestApiGetCall::Handler handler) |
969 | 337 { |
970 | 338 RestApiPath path(uri); |
969 | 339 RegisterInternal(path, handler, 0); |
340 } | |
341 | |
970 | 342 void RestApiHierarchy::Register(const std::string& uri, |
974 | 343 RestApiPutCall::Handler handler) |
969 | 344 { |
970 | 345 RestApiPath path(uri); |
969 | 346 RegisterInternal(path, handler, 0); |
347 } | |
348 | |
970 | 349 void RestApiHierarchy::Register(const std::string& uri, |
974 | 350 RestApiPostCall::Handler handler) |
969 | 351 { |
970 | 352 RestApiPath path(uri); |
969 | 353 RegisterInternal(path, handler, 0); |
354 } | |
355 | |
970 | 356 void RestApiHierarchy::Register(const std::string& uri, |
974 | 357 RestApiDeleteCall::Handler handler) |
969 | 358 { |
970 | 359 RestApiPath path(uri); |
969 | 360 RegisterInternal(path, handler, 0); |
361 } | |
362 | |
363 void RestApiHierarchy::CreateSiteMap(Json::Value& target) const | |
364 { | |
980
f1ff2a2f06cd
use RestApiHierarchy inside RestApi
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
978
diff
changeset
|
365 target = Json::objectValue; |
f1ff2a2f06cd
use RestApiHierarchy inside RestApi
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
978
diff
changeset
|
366 |
f1ff2a2f06cd
use RestApiHierarchy inside RestApi
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
978
diff
changeset
|
367 /*std::string s = " "; |
972 | 368 if (handlers_.HasHandler(HttpMethod_Get)) |
969 | 369 { |
980
f1ff2a2f06cd
use RestApiHierarchy inside RestApi
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
978
diff
changeset
|
370 s += "GET "; |
969 | 371 } |
372 | |
972 | 373 if (handlers_.HasHandler(HttpMethod_Post)) |
969 | 374 { |
980
f1ff2a2f06cd
use RestApiHierarchy inside RestApi
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
978
diff
changeset
|
375 s += "POST "; |
969 | 376 } |
377 | |
972 | 378 if (handlers_.HasHandler(HttpMethod_Put)) |
969 | 379 { |
980
f1ff2a2f06cd
use RestApiHierarchy inside RestApi
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
978
diff
changeset
|
380 s += "PUT "; |
969 | 381 } |
382 | |
972 | 383 if (handlers_.HasHandler(HttpMethod_Delete)) |
969 | 384 { |
980
f1ff2a2f06cd
use RestApiHierarchy inside RestApi
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
978
diff
changeset
|
385 s += "DELETE "; |
969 | 386 } |
387 | |
978 | 388 target = s;*/ |
980
f1ff2a2f06cd
use RestApiHierarchy inside RestApi
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
978
diff
changeset
|
389 |
f1ff2a2f06cd
use RestApiHierarchy inside RestApi
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
978
diff
changeset
|
390 for (Children::const_iterator it = children_.begin(); |
f1ff2a2f06cd
use RestApiHierarchy inside RestApi
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
978
diff
changeset
|
391 it != children_.end(); it++) |
969 | 392 { |
980
f1ff2a2f06cd
use RestApiHierarchy inside RestApi
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
978
diff
changeset
|
393 it->second->CreateSiteMap(target[it->first]); |
969 | 394 } |
395 | |
980
f1ff2a2f06cd
use RestApiHierarchy inside RestApi
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
978
diff
changeset
|
396 for (Children::const_iterator it = wildcardChildren_.begin(); |
f1ff2a2f06cd
use RestApiHierarchy inside RestApi
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
978
diff
changeset
|
397 it != wildcardChildren_.end(); it++) |
f1ff2a2f06cd
use RestApiHierarchy inside RestApi
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
978
diff
changeset
|
398 { |
f1ff2a2f06cd
use RestApiHierarchy inside RestApi
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
978
diff
changeset
|
399 it->second->CreateSiteMap(target["<" + it->first + ">"]); |
f1ff2a2f06cd
use RestApiHierarchy inside RestApi
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
978
diff
changeset
|
400 } |
969 | 401 } |
402 | |
978 | 403 |
404 bool RestApiHierarchy::LookupResource(const UriComponents& uri, | |
405 IVisitor& visitor) | |
969 | 406 { |
975 | 407 HttpHandler::Arguments components; |
978 | 408 return LookupResource(components, uri, visitor, 0); |
969 | 409 } |
410 | |
978 | 411 |
412 | |
413 namespace | |
414 { | |
415 // Anonymous namespace to avoid clashes between compilation modules | |
416 | |
417 class AcceptedMethodsVisitor : public RestApiHierarchy::IVisitor | |
418 { | |
419 private: | |
420 std::set<HttpMethod>& methods_; | |
421 | |
422 public: | |
423 AcceptedMethodsVisitor(std::set<HttpMethod>& methods) : methods_(methods) | |
424 { | |
425 } | |
426 | |
427 virtual bool Visit(const RestApiHierarchy::Resource& resource, | |
428 const UriComponents& uri, | |
429 const HttpHandler::Arguments& components, | |
430 const UriComponents& trailing) | |
431 { | |
432 if (trailing.size() == 0) // Ignore universal handlers | |
433 { | |
434 if (resource.HasHandler(HttpMethod_Get)) | |
435 { | |
436 methods_.insert(HttpMethod_Get); | |
437 } | |
438 | |
439 if (resource.HasHandler(HttpMethod_Post)) | |
440 { | |
441 methods_.insert(HttpMethod_Post); | |
442 } | |
443 | |
444 if (resource.HasHandler(HttpMethod_Put)) | |
445 { | |
446 methods_.insert(HttpMethod_Put); | |
447 } | |
448 | |
449 if (resource.HasHandler(HttpMethod_Delete)) | |
450 { | |
451 methods_.insert(HttpMethod_Delete); | |
452 } | |
453 } | |
454 | |
455 return false; // Continue to check all the possible ways to access this URI | |
456 } | |
457 }; | |
458 } | |
459 | |
460 void RestApiHierarchy::GetAcceptedMethods(std::set<HttpMethod>& methods, | |
461 const UriComponents& uri) | |
969 | 462 { |
975 | 463 HttpHandler::Arguments components; |
978 | 464 AcceptedMethodsVisitor visitor(methods); |
465 LookupResource(components, uri, visitor, 0); | |
969 | 466 |
978 | 467 Json::Value d; |
468 if (GetDirectory(d, uri)) | |
469 { | |
470 methods.insert(HttpMethod_Get); | |
471 } | |
472 } | |
969 | 473 } |