Mercurial > hg > orthanc
comparison OrthancServer/Sources/DicomInstanceToStore.cpp @ 4044:d25f4c0fa160 framework
splitting code into OrthancFramework and OrthancServer
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Wed, 10 Jun 2020 20:30:34 +0200 |
parents | OrthancServer/DicomInstanceToStore.cpp@cc6ed76bba27 |
children | 05b8fd21089c |
comparison
equal
deleted
inserted
replaced
4043:6c6239aec462 | 4044:d25f4c0fa160 |
---|---|
1 /** | |
2 * Orthanc - A Lightweight, RESTful DICOM Store | |
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics | |
4 * Department, University Hospital of Liege, Belgium | |
5 * Copyright (C) 2017-2020 Osimis S.A., Belgium | |
6 * | |
7 * This program is free software: you can redistribute it and/or | |
8 * modify it under the terms of the GNU General Public License as | |
9 * published by the Free Software Foundation, either version 3 of the | |
10 * License, or (at your option) any later version. | |
11 * | |
12 * In addition, as a special exception, the copyright holders of this | |
13 * program give permission to link the code of its release with the | |
14 * OpenSSL project's "OpenSSL" library (or with modified versions of it | |
15 * that use the same license as the "OpenSSL" library), and distribute | |
16 * the linked executables. You must obey the GNU General Public License | |
17 * in all respects for all of the code used other than "OpenSSL". If you | |
18 * modify file(s) with this exception, you may extend this exception to | |
19 * your version of the file(s), but you are not obligated to do so. If | |
20 * you do not wish to do so, delete this exception statement from your | |
21 * version. If you delete this exception statement from all source files | |
22 * in the program, then also delete it here. | |
23 * | |
24 * This program is distributed in the hope that it will be useful, but | |
25 * WITHOUT ANY WARRANTY; without even the implied warranty of | |
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
27 * General Public License for more details. | |
28 * | |
29 * You should have received a copy of the GNU General Public License | |
30 * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
31 **/ | |
32 | |
33 | |
34 #include "PrecompiledHeadersServer.h" | |
35 #include "DicomInstanceToStore.h" | |
36 | |
37 #include "../Core/DicomParsing/FromDcmtkBridge.h" | |
38 #include "../Core/DicomParsing/ParsedDicomFile.h" | |
39 #include "../Core/Logging.h" | |
40 #include "../Core/OrthancException.h" | |
41 | |
42 #include <dcmtk/dcmdata/dcfilefo.h> | |
43 #include <dcmtk/dcmdata/dcdeftag.h> | |
44 | |
45 | |
46 namespace Orthanc | |
47 { | |
48 // Anonymous namespace to avoid clashes between compilation modules | |
49 namespace | |
50 { | |
51 template <typename T> | |
52 class SmartContainer | |
53 { | |
54 private: | |
55 T* content_; | |
56 bool toDelete_; | |
57 bool isReadOnly_; | |
58 | |
59 void Deallocate() | |
60 { | |
61 if (content_ && toDelete_) | |
62 { | |
63 delete content_; | |
64 toDelete_ = false; | |
65 content_ = NULL; | |
66 } | |
67 } | |
68 | |
69 public: | |
70 SmartContainer() : content_(NULL), toDelete_(false), isReadOnly_(true) | |
71 { | |
72 } | |
73 | |
74 ~SmartContainer() | |
75 { | |
76 Deallocate(); | |
77 } | |
78 | |
79 void Allocate() | |
80 { | |
81 Deallocate(); | |
82 content_ = new T; | |
83 toDelete_ = true; | |
84 isReadOnly_ = false; | |
85 } | |
86 | |
87 void TakeOwnership(T* content) | |
88 { | |
89 if (content == NULL) | |
90 { | |
91 throw OrthancException(ErrorCode_ParameterOutOfRange); | |
92 } | |
93 | |
94 Deallocate(); | |
95 content_ = content; | |
96 toDelete_ = true; | |
97 isReadOnly_ = false; | |
98 } | |
99 | |
100 void SetReference(T& content) // Read and write assign, without transfering ownership | |
101 { | |
102 Deallocate(); | |
103 content_ = &content; | |
104 toDelete_ = false; | |
105 isReadOnly_ = false; | |
106 } | |
107 | |
108 void SetConstReference(const T& content) // Read-only assign, without transfering ownership | |
109 { | |
110 Deallocate(); | |
111 content_ = &const_cast<T&>(content); | |
112 toDelete_ = false; | |
113 isReadOnly_ = true; | |
114 } | |
115 | |
116 bool HasContent() const | |
117 { | |
118 return content_ != NULL; | |
119 } | |
120 | |
121 T& GetContent() | |
122 { | |
123 if (content_ == NULL) | |
124 { | |
125 throw OrthancException(ErrorCode_BadSequenceOfCalls); | |
126 } | |
127 | |
128 if (isReadOnly_) | |
129 { | |
130 throw OrthancException(ErrorCode_ReadOnly); | |
131 } | |
132 | |
133 return *content_; | |
134 } | |
135 | |
136 const T& GetConstContent() const | |
137 { | |
138 if (content_ == NULL) | |
139 { | |
140 throw OrthancException(ErrorCode_BadSequenceOfCalls); | |
141 } | |
142 | |
143 return *content_; | |
144 } | |
145 }; | |
146 } | |
147 | |
148 | |
149 class DicomInstanceToStore::PImpl | |
150 { | |
151 public: | |
152 DicomInstanceOrigin origin_; | |
153 bool hasBuffer_; | |
154 std::unique_ptr<std::string> ownBuffer_; | |
155 const void* bufferData_; | |
156 size_t bufferSize_; | |
157 SmartContainer<ParsedDicomFile> parsed_; | |
158 SmartContainer<DicomMap> summary_; | |
159 SmartContainer<Json::Value> json_; | |
160 MetadataMap metadata_; | |
161 | |
162 PImpl() : | |
163 hasBuffer_(false), | |
164 bufferData_(NULL), | |
165 bufferSize_(0) | |
166 { | |
167 } | |
168 | |
169 private: | |
170 std::unique_ptr<DicomInstanceHasher> hasher_; | |
171 | |
172 void ParseDicomFile() | |
173 { | |
174 if (!parsed_.HasContent()) | |
175 { | |
176 if (!hasBuffer_) | |
177 { | |
178 throw OrthancException(ErrorCode_InternalError); | |
179 } | |
180 | |
181 if (ownBuffer_.get() != NULL) | |
182 { | |
183 parsed_.TakeOwnership(new ParsedDicomFile(*ownBuffer_)); | |
184 } | |
185 else | |
186 { | |
187 parsed_.TakeOwnership(new ParsedDicomFile(bufferData_, bufferSize_)); | |
188 } | |
189 } | |
190 } | |
191 | |
192 void ComputeMissingInformation() | |
193 { | |
194 if (hasBuffer_ && | |
195 summary_.HasContent() && | |
196 json_.HasContent()) | |
197 { | |
198 // Fine, everything is available | |
199 return; | |
200 } | |
201 | |
202 if (!hasBuffer_) | |
203 { | |
204 if (!parsed_.HasContent()) | |
205 { | |
206 if (!summary_.HasContent()) | |
207 { | |
208 throw OrthancException(ErrorCode_NotImplemented); | |
209 } | |
210 else | |
211 { | |
212 parsed_.TakeOwnership(new ParsedDicomFile(summary_.GetConstContent(), | |
213 GetDefaultDicomEncoding(), | |
214 false /* be strict */)); | |
215 } | |
216 } | |
217 | |
218 // Serialize the parsed DICOM file | |
219 ownBuffer_.reset(new std::string); | |
220 if (!FromDcmtkBridge::SaveToMemoryBuffer(*ownBuffer_, | |
221 *parsed_.GetContent().GetDcmtkObject().getDataset())) | |
222 { | |
223 throw OrthancException(ErrorCode_InternalError, | |
224 "Unable to serialize a DICOM file to a memory buffer"); | |
225 } | |
226 | |
227 hasBuffer_ = true; | |
228 } | |
229 | |
230 if (summary_.HasContent() && | |
231 json_.HasContent()) | |
232 { | |
233 return; | |
234 } | |
235 | |
236 // At this point, we know that the DICOM file is available as a | |
237 // memory buffer, but that its summary or its JSON version is | |
238 // missing | |
239 | |
240 ParseDicomFile(); | |
241 assert(parsed_.HasContent()); | |
242 | |
243 // At this point, we have parsed the DICOM file | |
244 | |
245 if (!summary_.HasContent()) | |
246 { | |
247 summary_.Allocate(); | |
248 FromDcmtkBridge::ExtractDicomSummary(summary_.GetContent(), | |
249 *parsed_.GetContent().GetDcmtkObject().getDataset()); | |
250 } | |
251 | |
252 if (!json_.HasContent()) | |
253 { | |
254 json_.Allocate(); | |
255 | |
256 std::set<DicomTag> ignoreTagLength; | |
257 FromDcmtkBridge::ExtractDicomAsJson(json_.GetContent(), | |
258 *parsed_.GetContent().GetDcmtkObject().getDataset(), | |
259 ignoreTagLength); | |
260 } | |
261 } | |
262 | |
263 | |
264 public: | |
265 void SetBuffer(const void* data, | |
266 size_t size) | |
267 { | |
268 ownBuffer_.reset(NULL); | |
269 bufferData_ = data; | |
270 bufferSize_ = size; | |
271 hasBuffer_ = true; | |
272 } | |
273 | |
274 const void* GetBufferData() | |
275 { | |
276 ComputeMissingInformation(); | |
277 | |
278 if (!hasBuffer_) | |
279 { | |
280 throw OrthancException(ErrorCode_InternalError); | |
281 } | |
282 | |
283 if (ownBuffer_.get() != NULL) | |
284 { | |
285 if (ownBuffer_->empty()) | |
286 { | |
287 return NULL; | |
288 } | |
289 else | |
290 { | |
291 return ownBuffer_->c_str(); | |
292 } | |
293 } | |
294 else | |
295 { | |
296 return bufferData_; | |
297 } | |
298 } | |
299 | |
300 | |
301 size_t GetBufferSize() | |
302 { | |
303 ComputeMissingInformation(); | |
304 | |
305 if (!hasBuffer_) | |
306 { | |
307 throw OrthancException(ErrorCode_InternalError); | |
308 } | |
309 | |
310 if (ownBuffer_.get() != NULL) | |
311 { | |
312 return ownBuffer_->size(); | |
313 } | |
314 else | |
315 { | |
316 return bufferSize_; | |
317 } | |
318 } | |
319 | |
320 | |
321 const DicomMap& GetSummary() | |
322 { | |
323 ComputeMissingInformation(); | |
324 | |
325 if (!summary_.HasContent()) | |
326 { | |
327 throw OrthancException(ErrorCode_InternalError); | |
328 } | |
329 | |
330 return summary_.GetConstContent(); | |
331 } | |
332 | |
333 | |
334 const Json::Value& GetJson() | |
335 { | |
336 ComputeMissingInformation(); | |
337 | |
338 if (!json_.HasContent()) | |
339 { | |
340 throw OrthancException(ErrorCode_InternalError); | |
341 } | |
342 | |
343 return json_.GetConstContent(); | |
344 } | |
345 | |
346 | |
347 DicomInstanceHasher& GetHasher() | |
348 { | |
349 if (hasher_.get() == NULL) | |
350 { | |
351 hasher_.reset(new DicomInstanceHasher(GetSummary())); | |
352 } | |
353 | |
354 if (hasher_.get() == NULL) | |
355 { | |
356 throw OrthancException(ErrorCode_InternalError); | |
357 } | |
358 | |
359 return *hasher_; | |
360 } | |
361 | |
362 | |
363 bool LookupTransferSyntax(std::string& result) | |
364 { | |
365 ComputeMissingInformation(); | |
366 | |
367 DicomMap header; | |
368 if (DicomMap::ParseDicomMetaInformation(header, GetBufferData(), GetBufferSize())) | |
369 { | |
370 const DicomValue* value = header.TestAndGetValue(DICOM_TAG_TRANSFER_SYNTAX_UID); | |
371 if (value != NULL && | |
372 !value->IsBinary() && | |
373 !value->IsNull()) | |
374 { | |
375 result = Toolbox::StripSpaces(value->GetContent()); | |
376 return true; | |
377 } | |
378 } | |
379 | |
380 return false; | |
381 } | |
382 | |
383 | |
384 ParsedDicomFile& GetParsedDicomFile() | |
385 { | |
386 ComputeMissingInformation(); | |
387 ParseDicomFile(); | |
388 | |
389 if (parsed_.HasContent()) | |
390 { | |
391 return parsed_.GetContent(); | |
392 } | |
393 else | |
394 { | |
395 throw OrthancException(ErrorCode_InternalError); | |
396 } | |
397 } | |
398 }; | |
399 | |
400 | |
401 DicomInstanceToStore::DicomInstanceToStore() : | |
402 pimpl_(new PImpl) | |
403 { | |
404 } | |
405 | |
406 | |
407 void DicomInstanceToStore::SetOrigin(const DicomInstanceOrigin& origin) | |
408 { | |
409 pimpl_->origin_ = origin; | |
410 } | |
411 | |
412 | |
413 const DicomInstanceOrigin& DicomInstanceToStore::GetOrigin() const | |
414 { | |
415 return pimpl_->origin_; | |
416 } | |
417 | |
418 | |
419 void DicomInstanceToStore::SetBuffer(const void* dicom, | |
420 size_t size) | |
421 { | |
422 pimpl_->SetBuffer(dicom, size); | |
423 } | |
424 | |
425 | |
426 void DicomInstanceToStore::SetParsedDicomFile(ParsedDicomFile& parsed) | |
427 { | |
428 pimpl_->parsed_.SetReference(parsed); | |
429 } | |
430 | |
431 | |
432 void DicomInstanceToStore::SetSummary(const DicomMap& summary) | |
433 { | |
434 pimpl_->summary_.SetConstReference(summary); | |
435 } | |
436 | |
437 | |
438 void DicomInstanceToStore::SetJson(const Json::Value& json) | |
439 { | |
440 pimpl_->json_.SetConstReference(json); | |
441 } | |
442 | |
443 | |
444 const DicomInstanceToStore::MetadataMap& DicomInstanceToStore::GetMetadata() const | |
445 { | |
446 return pimpl_->metadata_; | |
447 } | |
448 | |
449 | |
450 DicomInstanceToStore::MetadataMap& DicomInstanceToStore::GetMetadata() | |
451 { | |
452 return pimpl_->metadata_; | |
453 } | |
454 | |
455 | |
456 void DicomInstanceToStore::AddMetadata(ResourceType level, | |
457 MetadataType metadata, | |
458 const std::string& value) | |
459 { | |
460 pimpl_->metadata_[std::make_pair(level, metadata)] = value; | |
461 } | |
462 | |
463 | |
464 const void* DicomInstanceToStore::GetBufferData() const | |
465 { | |
466 return const_cast<PImpl&>(*pimpl_).GetBufferData(); | |
467 } | |
468 | |
469 | |
470 size_t DicomInstanceToStore::GetBufferSize() const | |
471 { | |
472 return const_cast<PImpl&>(*pimpl_).GetBufferSize(); | |
473 } | |
474 | |
475 | |
476 const DicomMap& DicomInstanceToStore::GetSummary() | |
477 { | |
478 return pimpl_->GetSummary(); | |
479 } | |
480 | |
481 | |
482 const Json::Value& DicomInstanceToStore::GetJson() const | |
483 { | |
484 return const_cast<PImpl&>(*pimpl_).GetJson(); | |
485 } | |
486 | |
487 | |
488 bool DicomInstanceToStore::LookupTransferSyntax(std::string& result) const | |
489 { | |
490 return const_cast<PImpl&>(*pimpl_).LookupTransferSyntax(result); | |
491 } | |
492 | |
493 | |
494 DicomInstanceHasher& DicomInstanceToStore::GetHasher() | |
495 { | |
496 return pimpl_->GetHasher(); | |
497 } | |
498 | |
499 bool DicomInstanceToStore::HasPixelData() const | |
500 { | |
501 return const_cast<PImpl&>(*pimpl_).GetParsedDicomFile().HasTag(DICOM_TAG_PIXEL_DATA); | |
502 } | |
503 | |
504 ParsedDicomFile& DicomInstanceToStore::GetParsedDicomFile() const | |
505 { | |
506 return const_cast<PImpl&>(*pimpl_).GetParsedDicomFile(); | |
507 } | |
508 } |