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 }