Mercurial > hg > orthanc
comparison OrthancServer/DicomInstanceToStore.cpp @ 2894:a27b0e9a3fd9 db-changes
refactoring DicomInstanceToStore
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Thu, 18 Oct 2018 10:35:09 +0200 |
parents | c691fcf66071 |
children | e5e3253a1164 |
comparison
equal
deleted
inserted
replaced
2893:1723cbba55c7 | 2894:a27b0e9a3fd9 |
---|---|
33 | 33 |
34 #include "PrecompiledHeadersServer.h" | 34 #include "PrecompiledHeadersServer.h" |
35 #include "DicomInstanceToStore.h" | 35 #include "DicomInstanceToStore.h" |
36 | 36 |
37 #include "../Core/DicomParsing/FromDcmtkBridge.h" | 37 #include "../Core/DicomParsing/FromDcmtkBridge.h" |
38 #include "../Core/DicomParsing/ParsedDicomFile.h" | |
38 #include "../Core/Logging.h" | 39 #include "../Core/Logging.h" |
40 #include "../Core/OrthancException.h" | |
39 | 41 |
40 #include <dcmtk/dcmdata/dcfilefo.h> | 42 #include <dcmtk/dcmdata/dcfilefo.h> |
41 #include <dcmtk/dcmdata/dcdeftag.h> | 43 #include <dcmtk/dcmdata/dcdeftag.h> |
42 | 44 |
43 | 45 |
44 namespace Orthanc | 46 namespace Orthanc |
45 { | 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 struct DicomInstanceToStore::PImpl | |
150 { | |
151 DicomInstanceOrigin origin_; | |
152 SmartContainer<std::string> buffer_; | |
153 SmartContainer<ParsedDicomFile> parsed_; | |
154 SmartContainer<DicomMap> summary_; | |
155 SmartContainer<Json::Value> json_; | |
156 MetadataMap metadata_; | |
157 | |
158 void ComputeMissingInformation() | |
159 { | |
160 if (buffer_.HasContent() && | |
161 summary_.HasContent() && | |
162 json_.HasContent()) | |
163 { | |
164 // Fine, everything is available | |
165 return; | |
166 } | |
167 | |
168 if (!buffer_.HasContent()) | |
169 { | |
170 if (!parsed_.HasContent()) | |
171 { | |
172 if (!summary_.HasContent()) | |
173 { | |
174 throw OrthancException(ErrorCode_NotImplemented); | |
175 } | |
176 else | |
177 { | |
178 parsed_.TakeOwnership(new ParsedDicomFile(summary_.GetConstContent())); | |
179 } | |
180 } | |
181 | |
182 // Serialize the parsed DICOM file | |
183 buffer_.Allocate(); | |
184 if (!FromDcmtkBridge::SaveToMemoryBuffer(buffer_.GetContent(), | |
185 *parsed_.GetContent().GetDcmtkObject().getDataset())) | |
186 { | |
187 LOG(ERROR) << "Unable to serialize a DICOM file to a memory buffer"; | |
188 throw OrthancException(ErrorCode_InternalError); | |
189 } | |
190 } | |
191 | |
192 if (summary_.HasContent() && | |
193 json_.HasContent()) | |
194 { | |
195 return; | |
196 } | |
197 | |
198 // At this point, we know that the DICOM file is available as a | |
199 // memory buffer, but that its summary or its JSON version is | |
200 // missing | |
201 | |
202 if (!parsed_.HasContent()) | |
203 { | |
204 parsed_.TakeOwnership(new ParsedDicomFile(buffer_.GetConstContent())); | |
205 } | |
206 | |
207 // At this point, we have parsed the DICOM file | |
208 | |
209 if (!summary_.HasContent()) | |
210 { | |
211 summary_.Allocate(); | |
212 FromDcmtkBridge::ExtractDicomSummary(summary_.GetContent(), | |
213 *parsed_.GetContent().GetDcmtkObject().getDataset()); | |
214 } | |
215 | |
216 if (!json_.HasContent()) | |
217 { | |
218 json_.Allocate(); | |
219 | |
220 std::set<DicomTag> ignoreTagLength; | |
221 FromDcmtkBridge::ExtractDicomAsJson(json_.GetContent(), | |
222 *parsed_.GetContent().GetDcmtkObject().getDataset(), | |
223 ignoreTagLength); | |
224 } | |
225 } | |
226 | |
227 | |
228 const char* GetBufferData() | |
229 { | |
230 ComputeMissingInformation(); | |
231 | |
232 if (!buffer_.HasContent()) | |
233 { | |
234 throw OrthancException(ErrorCode_InternalError); | |
235 } | |
236 | |
237 if (buffer_.GetConstContent().size() == 0) | |
238 { | |
239 return NULL; | |
240 } | |
241 else | |
242 { | |
243 return buffer_.GetConstContent().c_str(); | |
244 } | |
245 } | |
246 | |
247 | |
248 size_t GetBufferSize() | |
249 { | |
250 ComputeMissingInformation(); | |
251 | |
252 if (!buffer_.HasContent()) | |
253 { | |
254 throw OrthancException(ErrorCode_InternalError); | |
255 } | |
256 | |
257 return buffer_.GetConstContent().size(); | |
258 } | |
259 | |
260 | |
261 const DicomMap& GetSummary() | |
262 { | |
263 ComputeMissingInformation(); | |
264 | |
265 if (!summary_.HasContent()) | |
266 { | |
267 throw OrthancException(ErrorCode_InternalError); | |
268 } | |
269 | |
270 return summary_.GetConstContent(); | |
271 } | |
272 | |
273 | |
274 const Json::Value& GetJson() | |
275 { | |
276 ComputeMissingInformation(); | |
277 | |
278 if (!json_.HasContent()) | |
279 { | |
280 throw OrthancException(ErrorCode_InternalError); | |
281 } | |
282 | |
283 return json_.GetConstContent(); | |
284 } | |
285 | |
286 | |
287 bool LookupTransferSyntax(std::string& result) | |
288 { | |
289 ComputeMissingInformation(); | |
290 | |
291 DicomMap header; | |
292 if (DicomMap::ParseDicomMetaInformation(header, GetBufferData(), GetBufferSize())) | |
293 { | |
294 const DicomValue* value = header.TestAndGetValue(DICOM_TAG_TRANSFER_SYNTAX_UID); | |
295 if (value != NULL && | |
296 !value->IsBinary() && | |
297 !value->IsNull()) | |
298 { | |
299 result = Toolbox::StripSpaces(value->GetContent()); | |
300 return true; | |
301 } | |
302 } | |
303 | |
304 return false; | |
305 } | |
306 }; | |
307 | |
308 | |
309 DicomInstanceToStore::DicomInstanceToStore() : | |
310 pimpl_(new PImpl) | |
311 { | |
312 } | |
313 | |
314 | |
315 void DicomInstanceToStore::SetOrigin(const DicomInstanceOrigin& origin) | |
316 { | |
317 pimpl_->origin_ = origin; | |
318 } | |
319 | |
320 | |
321 const DicomInstanceOrigin& DicomInstanceToStore::GetOrigin() const | |
322 { | |
323 return pimpl_->origin_; | |
324 } | |
325 | |
326 | |
327 void DicomInstanceToStore::SetBuffer(const std::string& dicom) | |
328 { | |
329 pimpl_->buffer_.SetConstReference(dicom); | |
330 } | |
331 | |
332 | |
333 void DicomInstanceToStore::SetParsedDicomFile(ParsedDicomFile& parsed) | |
334 { | |
335 pimpl_->parsed_.SetReference(parsed); | |
336 } | |
337 | |
338 | |
339 void DicomInstanceToStore::SetSummary(const DicomMap& summary) | |
340 { | |
341 pimpl_->summary_.SetConstReference(summary); | |
342 } | |
343 | |
344 | |
345 void DicomInstanceToStore::SetJson(const Json::Value& json) | |
346 { | |
347 pimpl_->json_.SetConstReference(json); | |
348 } | |
349 | |
350 | |
351 const DicomInstanceToStore::MetadataMap& DicomInstanceToStore::GetMetadata() const | |
352 { | |
353 return pimpl_->metadata_; | |
354 } | |
355 | |
356 | |
357 DicomInstanceToStore::MetadataMap& DicomInstanceToStore::GetMetadata() | |
358 { | |
359 return pimpl_->metadata_; | |
360 } | |
361 | |
362 | |
46 void DicomInstanceToStore::AddMetadata(ResourceType level, | 363 void DicomInstanceToStore::AddMetadata(ResourceType level, |
47 MetadataType metadata, | 364 MetadataType metadata, |
48 const std::string& value) | 365 const std::string& value) |
49 { | 366 { |
50 metadata_[std::make_pair(level, metadata)] = value; | 367 pimpl_->metadata_[std::make_pair(level, metadata)] = value; |
51 } | 368 } |
52 | |
53 | |
54 void DicomInstanceToStore::ComputeMissingInformation() | |
55 { | |
56 if (buffer_.HasContent() && | |
57 summary_.HasContent() && | |
58 json_.HasContent()) | |
59 { | |
60 // Fine, everything is available | |
61 return; | |
62 } | |
63 | |
64 if (!buffer_.HasContent()) | |
65 { | |
66 if (!parsed_.HasContent()) | |
67 { | |
68 if (!summary_.HasContent()) | |
69 { | |
70 throw OrthancException(ErrorCode_NotImplemented); | |
71 } | |
72 else | |
73 { | |
74 parsed_.TakeOwnership(new ParsedDicomFile(summary_.GetConstContent())); | |
75 } | |
76 } | |
77 | |
78 // Serialize the parsed DICOM file | |
79 buffer_.Allocate(); | |
80 if (!FromDcmtkBridge::SaveToMemoryBuffer(buffer_.GetContent(), | |
81 *parsed_.GetContent().GetDcmtkObject().getDataset())) | |
82 { | |
83 LOG(ERROR) << "Unable to serialize a DICOM file to a memory buffer"; | |
84 throw OrthancException(ErrorCode_InternalError); | |
85 } | |
86 } | |
87 | |
88 if (summary_.HasContent() && | |
89 json_.HasContent()) | |
90 { | |
91 return; | |
92 } | |
93 | |
94 // At this point, we know that the DICOM file is available as a | |
95 // memory buffer, but that its summary or its JSON version is | |
96 // missing | |
97 | |
98 if (!parsed_.HasContent()) | |
99 { | |
100 parsed_.TakeOwnership(new ParsedDicomFile(buffer_.GetConstContent())); | |
101 } | |
102 | |
103 // At this point, we have parsed the DICOM file | |
104 | |
105 if (!summary_.HasContent()) | |
106 { | |
107 summary_.Allocate(); | |
108 FromDcmtkBridge::ExtractDicomSummary(summary_.GetContent(), | |
109 *parsed_.GetContent().GetDcmtkObject().getDataset()); | |
110 } | |
111 | |
112 if (!json_.HasContent()) | |
113 { | |
114 json_.Allocate(); | |
115 | |
116 std::set<DicomTag> ignoreTagLength; | |
117 FromDcmtkBridge::ExtractDicomAsJson(json_.GetContent(), | |
118 *parsed_.GetContent().GetDcmtkObject().getDataset(), | |
119 ignoreTagLength); | |
120 } | |
121 } | |
122 | |
123 | 369 |
124 | 370 |
125 const char* DicomInstanceToStore::GetBufferData() | 371 const char* DicomInstanceToStore::GetBufferData() |
126 { | 372 { |
127 ComputeMissingInformation(); | 373 return pimpl_->GetBufferData(); |
128 | |
129 if (!buffer_.HasContent()) | |
130 { | |
131 throw OrthancException(ErrorCode_InternalError); | |
132 } | |
133 | |
134 if (buffer_.GetConstContent().size() == 0) | |
135 { | |
136 return NULL; | |
137 } | |
138 else | |
139 { | |
140 return buffer_.GetConstContent().c_str(); | |
141 } | |
142 } | 374 } |
143 | 375 |
144 | 376 |
145 size_t DicomInstanceToStore::GetBufferSize() | 377 size_t DicomInstanceToStore::GetBufferSize() |
146 { | 378 { |
147 ComputeMissingInformation(); | 379 return pimpl_->GetBufferSize(); |
148 | |
149 if (!buffer_.HasContent()) | |
150 { | |
151 throw OrthancException(ErrorCode_InternalError); | |
152 } | |
153 | |
154 return buffer_.GetConstContent().size(); | |
155 } | 380 } |
156 | 381 |
157 | 382 |
158 const DicomMap& DicomInstanceToStore::GetSummary() | 383 const DicomMap& DicomInstanceToStore::GetSummary() |
159 { | 384 { |
160 ComputeMissingInformation(); | 385 return pimpl_->GetSummary(); |
161 | |
162 if (!summary_.HasContent()) | |
163 { | |
164 throw OrthancException(ErrorCode_InternalError); | |
165 } | |
166 | |
167 return summary_.GetConstContent(); | |
168 } | 386 } |
169 | 387 |
170 | 388 |
171 const Json::Value& DicomInstanceToStore::GetJson() | 389 const Json::Value& DicomInstanceToStore::GetJson() |
172 { | 390 { |
173 ComputeMissingInformation(); | 391 return pimpl_->GetJson(); |
174 | |
175 if (!json_.HasContent()) | |
176 { | |
177 throw OrthancException(ErrorCode_InternalError); | |
178 } | |
179 | |
180 return json_.GetConstContent(); | |
181 } | 392 } |
182 | 393 |
183 | 394 |
184 bool DicomInstanceToStore::LookupTransferSyntax(std::string& result) | 395 bool DicomInstanceToStore::LookupTransferSyntax(std::string& result) |
185 { | 396 { |
186 ComputeMissingInformation(); | 397 return pimpl_->LookupTransferSyntax(result); |
187 | |
188 DicomMap header; | |
189 if (DicomMap::ParseDicomMetaInformation(header, GetBufferData(), GetBufferSize())) | |
190 { | |
191 const DicomValue* value = header.TestAndGetValue(DICOM_TAG_TRANSFER_SYNTAX_UID); | |
192 if (value != NULL && | |
193 !value->IsBinary() && | |
194 !value->IsNull()) | |
195 { | |
196 result = Toolbox::StripSpaces(value->GetContent()); | |
197 return true; | |
198 } | |
199 } | |
200 | |
201 return false; | |
202 } | 398 } |
203 } | 399 } |