comparison OrthancServer/DicomProtocol/DicomUserConnection.cpp @ 763:b5e6d2823115

dynamic negotiation of storage sop classes
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 29 Apr 2014 17:15:59 +0200
parents b79eda29896d
children 0a2f8c707c78
comparison
equal deleted inserted replaced
760:b2a62f22fbe8 763:b5e6d2823115
58 #endif 58 #endif
59 59
60 60
61 static const char* DEFAULT_PREFERRED_TRANSFER_SYNTAX = UID_LittleEndianImplicitTransferSyntax; 61 static const char* DEFAULT_PREFERRED_TRANSFER_SYNTAX = UID_LittleEndianImplicitTransferSyntax;
62 62
63 /**
64 * "If we have more than 64 storage SOP classes, tools such as
65 * storescu will fail because they attempt to negotiate two
66 * presentation contexts for each SOP class, and there is a total
67 * limit of 128 contexts for one association."
68 **/
69 static const unsigned int MAXIMUM_STORAGE_SOP_CLASSES = 64;
70
71
63 namespace Orthanc 72 namespace Orthanc
64 { 73 {
65 struct DicomUserConnection::PImpl 74 struct DicomUserConnection::PImpl
66 { 75 {
67 // Connection state 76 // Connection state
101 { 110 {
102 pimpl_->CheckIsOpen(); 111 pimpl_->CheckIsOpen();
103 } 112 }
104 113
105 114
106 void DicomUserConnection::CopyParameters(const DicomUserConnection& other) 115 static void RegisterStorageSOPClass(T_ASC_Parameters* params,
107 { 116 unsigned int& presentationContextId,
108 Close(); 117 const std::string& sopClass,
109 localAet_ = other.localAet_; 118 const char* asPreferred[],
110 distantAet_ = other.distantAet_; 119 std::vector<const char*>& asFallback)
111 distantHost_ = other.distantHost_; 120 {
112 distantPort_ = other.distantPort_; 121 Check(ASC_addPresentationContext(params, presentationContextId,
113 manufacturer_ = other.manufacturer_; 122 sopClass.c_str(), asPreferred, 1));
114 preferredTransferSyntax_ = other.preferredTransferSyntax_; 123 presentationContextId += 2;
115 } 124
116 125 if (asFallback.size() > 0)
117 126 {
127 Check(ASC_addPresentationContext(params, presentationContextId,
128 sopClass.c_str(), &asFallback[0], asFallback.size()));
129 presentationContextId += 2;
130 }
131 }
132
133
118 void DicomUserConnection::SetupPresentationContexts(const std::string& preferredTransferSyntax) 134 void DicomUserConnection::SetupPresentationContexts(const std::string& preferredTransferSyntax)
119 { 135 {
120 // Fallback transfer syntaxes 136 // Flatten an array with the preferred transfer syntax
137 const char* asPreferred[1] = { preferredTransferSyntax.c_str() };
138
139 // Setup the fallback transfer syntaxes
121 std::set<std::string> fallbackSyntaxes; 140 std::set<std::string> fallbackSyntaxes;
122 fallbackSyntaxes.insert(UID_LittleEndianExplicitTransferSyntax); 141 fallbackSyntaxes.insert(UID_LittleEndianExplicitTransferSyntax);
123 fallbackSyntaxes.insert(UID_BigEndianExplicitTransferSyntax); 142 fallbackSyntaxes.insert(UID_BigEndianExplicitTransferSyntax);
124 fallbackSyntaxes.insert(UID_LittleEndianImplicitTransferSyntax); 143 fallbackSyntaxes.insert(UID_LittleEndianImplicitTransferSyntax);
125
126 // Transfer syntaxes for C-ECHO, C-FIND and C-MOVE
127 std::vector<std::string> transferSyntaxes;
128 transferSyntaxes.push_back(UID_VerificationSOPClass);
129 transferSyntaxes.push_back(UID_FINDPatientRootQueryRetrieveInformationModel);
130 transferSyntaxes.push_back(UID_FINDStudyRootQueryRetrieveInformationModel);
131 transferSyntaxes.push_back(UID_MOVEStudyRootQueryRetrieveInformationModel);
132
133 // TODO: Allow the set below to be configured
134 std::set<std::string> uselessSyntaxes;
135 uselessSyntaxes.insert(UID_BlendingSoftcopyPresentationStateStorage);
136 uselessSyntaxes.insert(UID_GrayscaleSoftcopyPresentationStateStorage);
137 uselessSyntaxes.insert(UID_ColorSoftcopyPresentationStateStorage);
138 uselessSyntaxes.insert(UID_PseudoColorSoftcopyPresentationStateStorage);
139
140 // Add the transfer syntaxes for C-STORE
141 for (int i = 0; i < numberOfDcmShortSCUStorageSOPClassUIDs - 1; i++)
142 {
143 // Test to make some room to allow the ECHO and FIND requests
144 if (uselessSyntaxes.find(dcmShortSCUStorageSOPClassUIDs[i]) == uselessSyntaxes.end())
145 {
146 transferSyntaxes.push_back(dcmShortSCUStorageSOPClassUIDs[i]);
147 }
148 }
149
150 // Flatten the fallback transfer syntaxes array
151 const char* asPreferred[1] = { preferredTransferSyntax.c_str() };
152
153 fallbackSyntaxes.erase(preferredTransferSyntax); 144 fallbackSyntaxes.erase(preferredTransferSyntax);
154 145
146 // Flatten an array with the fallback transfer syntaxes
155 std::vector<const char*> asFallback; 147 std::vector<const char*> asFallback;
156 asFallback.reserve(fallbackSyntaxes.size()); 148 asFallback.reserve(fallbackSyntaxes.size());
157 for (std::set<std::string>::const_iterator 149 for (std::set<std::string>::const_iterator
158 it = fallbackSyntaxes.begin(); it != fallbackSyntaxes.end(); ++it) 150 it = fallbackSyntaxes.begin(); it != fallbackSyntaxes.end(); ++it)
159 { 151 {
160 asFallback.push_back(it->c_str()); 152 asFallback.push_back(it->c_str());
161 } 153 }
162 154
155 CheckStorageSOPClassesInvariant();
163 unsigned int presentationContextId = 1; 156 unsigned int presentationContextId = 1;
164 for (size_t i = 0; i < transferSyntaxes.size(); i++) 157
165 { 158 for (std::list<std::string>::const_iterator it = reservedStorageSOPClasses_.begin();
166 Check(ASC_addPresentationContext(pimpl_->params_, presentationContextId, 159 it != reservedStorageSOPClasses_.end(); it++)
167 transferSyntaxes[i].c_str(), asPreferred, 1)); 160 {
168 presentationContextId += 2; 161 RegisterStorageSOPClass(pimpl_->params_, presentationContextId,
169 162 *it, asPreferred, asFallback);
170 if (asFallback.size() > 0) 163 }
171 { 164
172 Check(ASC_addPresentationContext(pimpl_->params_, presentationContextId, 165 for (std::set<std::string>::const_iterator it = storageSOPClasses_.begin();
173 transferSyntaxes[i].c_str(), &asFallback[0], asFallback.size())); 166 it != storageSOPClasses_.end(); it++)
174 presentationContextId += 2; 167 {
175 } 168 RegisterStorageSOPClass(pimpl_->params_, presentationContextId,
169 *it, asPreferred, asFallback);
170 }
171
172 for (std::set<std::string>::const_iterator it = defaultStorageSOPClasses_.begin();
173 it != defaultStorageSOPClasses_.end(); it++)
174 {
175 RegisterStorageSOPClass(pimpl_->params_, presentationContextId,
176 *it, asPreferred, asFallback);
176 } 177 }
177 } 178 }
178 179
179 180
180 static bool IsGenericTransferSyntax(const std::string& syntax) 181 static bool IsGenericTransferSyntax(const std::string& syntax)
190 CheckIsOpen(); 191 CheckIsOpen();
191 192
192 DcmFileFormat dcmff; 193 DcmFileFormat dcmff;
193 Check(dcmff.read(is, EXS_Unknown, EGL_noChange, DCM_MaxReadLength)); 194 Check(dcmff.read(is, EXS_Unknown, EGL_noChange, DCM_MaxReadLength));
194 195
196 // Determine the storage SOP class UID for this instance
197 static const DcmTagKey DCM_SOP_CLASS_UID(0x0008, 0x0016);
198 OFString sopClassUid;
199 if (dcmff.getDataset()->findAndGetOFString(DCM_SOP_CLASS_UID, sopClassUid).good())
200 {
201 connection.AddStorageSOPClass(sopClassUid.c_str());
202 }
203
195 // Determine whether a new presentation context must be 204 // Determine whether a new presentation context must be
196 // negociated, depending on the transfer syntax of this instance 205 // negotiated, depending on the transfer syntax of this instance
197 DcmXfer xfer(dcmff.getDataset()->getOriginalXfer()); 206 DcmXfer xfer(dcmff.getDataset()->getOriginalXfer());
198 const std::string syntax(xfer.getXferID()); 207 const std::string syntax(xfer.getXferID());
199 bool isGeneric = IsGenericTransferSyntax(syntax); 208 bool isGeneric = IsGenericTransferSyntax(syntax);
200 209
201 if (isGeneric ^ IsGenericTransferSyntax(connection.GetPreferredTransferSyntax())) 210 if (isGeneric ^ IsGenericTransferSyntax(connection.GetPreferredTransferSyntax()))
202 { 211 {
203 // Making a generic-to-specific or specific-to-generic change of 212 // Making a generic-to-specific or specific-to-generic change of
204 // the transfer syntax. Renegociate the connection. 213 // the transfer syntax. Renegotiate the connection.
205 LOG(INFO) << "Renegociating a C-Store association due to a change in the transfer syntax"; 214 LOG(INFO) << "Change in the transfer syntax: the C-Store associated must be renegotiated";
206 215
207 if (isGeneric) 216 if (isGeneric)
208 { 217 {
209 connection.ResetPreferredTransferSyntax(); 218 connection.ResetPreferredTransferSyntax();
210 } 219 }
211 else 220 else
212 { 221 {
213 connection.SetPreferredTransferSyntax(syntax); 222 connection.SetPreferredTransferSyntax(syntax);
214 } 223 }
215 224 }
225
226 if (!connection.IsOpen())
227 {
228 LOG(INFO) << "Renegotiating a C-Store association due to a change in the parameters";
216 connection.Open(); 229 connection.Open();
217 } 230 }
218 231
219 // Figure out which SOP class and SOP instance is encapsulated in the file 232 // Figure out which SOP class and SOP instance is encapsulated in the file
220 DIC_UI sopClass; 233 DIC_UI sopClass;
230 { 243 {
231 const char *modalityName = dcmSOPClassUIDToModality(sopClass); 244 const char *modalityName = dcmSOPClassUIDToModality(sopClass);
232 if (!modalityName) modalityName = dcmFindNameOfUID(sopClass); 245 if (!modalityName) modalityName = dcmFindNameOfUID(sopClass);
233 if (!modalityName) modalityName = "unknown SOP class"; 246 if (!modalityName) modalityName = "unknown SOP class";
234 throw OrthancException("DicomUserConnection: No presentation context for modality " + 247 throw OrthancException("DicomUserConnection: No presentation context for modality " +
235 std::string(modalityName)); 248 std::string(modalityName));
236 } 249 }
237 250
238 // Prepare the transmission of data 251 // Prepare the transmission of data
239 T_DIMSE_C_StoreRQ req; 252 T_DIMSE_C_StoreRQ req;
240 memset(&req, 0, sizeof(req)); 253 memset(&req, 0, sizeof(req));
286 299
287 const char* sopClass; 300 const char* sopClass;
288 std::auto_ptr<DcmDataset> dataset(ToDcmtkBridge::Convert(fields)); 301 std::auto_ptr<DcmDataset> dataset(ToDcmtkBridge::Convert(fields));
289 switch (model) 302 switch (model)
290 { 303 {
291 case FindRootModel_Patient: 304 case FindRootModel_Patient:
292 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0052), "PATIENT"); 305 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0052), "PATIENT");
293 sopClass = UID_FINDPatientRootQueryRetrieveInformationModel; 306 sopClass = UID_FINDPatientRootQueryRetrieveInformationModel;
294 307
295 // Accession number 308 // Accession number
296 if (!fields.HasTag(0x0008, 0x0050)) 309 if (!fields.HasTag(0x0008, 0x0050))
297 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0050), ""); 310 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0050), "");
298 311
299 // Patient ID 312 // Patient ID
300 if (!fields.HasTag(0x0010, 0x0020)) 313 if (!fields.HasTag(0x0010, 0x0020))
301 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0010, 0x0020), ""); 314 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0010, 0x0020), "");
302 315
303 break; 316 break;
304 317
305 case FindRootModel_Study: 318 case FindRootModel_Study:
306 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0052), "STUDY"); 319 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0052), "STUDY");
307 sopClass = UID_FINDStudyRootQueryRetrieveInformationModel; 320 sopClass = UID_FINDStudyRootQueryRetrieveInformationModel;
308 321
309 // Accession number 322 // Accession number
310 if (!fields.HasTag(0x0008, 0x0050)) 323 if (!fields.HasTag(0x0008, 0x0050))
311 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0050), ""); 324 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0050), "");
312 325
313 // Study instance UID 326 // Study instance UID
314 if (!fields.HasTag(0x0020, 0x000d)) 327 if (!fields.HasTag(0x0020, 0x000d))
315 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0020, 0x000d), ""); 328 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0020, 0x000d), "");
316 329
317 break; 330 break;
318 331
319 case FindRootModel_Series: 332 case FindRootModel_Series:
320 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0052), "SERIES"); 333 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0052), "SERIES");
321 sopClass = UID_FINDStudyRootQueryRetrieveInformationModel; 334 sopClass = UID_FINDStudyRootQueryRetrieveInformationModel;
322 335
323 // Accession number 336 // Accession number
324 if (!fields.HasTag(0x0008, 0x0050)) 337 if (!fields.HasTag(0x0008, 0x0050))
325 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0050), ""); 338 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0050), "");
326 339
327 // Study instance UID 340 // Study instance UID
328 if (!fields.HasTag(0x0020, 0x000d)) 341 if (!fields.HasTag(0x0020, 0x000d))
329 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0020, 0x000d), ""); 342 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0020, 0x000d), "");
330 343
331 // Series instance UID 344 // Series instance UID
332 if (!fields.HasTag(0x0020, 0x000e)) 345 if (!fields.HasTag(0x0020, 0x000e))
333 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0020, 0x000e), ""); 346 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0020, 0x000e), "");
334 347
335 break; 348 break;
336 349
337 case FindRootModel_Instance: 350 case FindRootModel_Instance:
338 if (manufacturer_ == ModalityManufacturer_ClearCanvas || 351 if (manufacturer_ == ModalityManufacturer_ClearCanvas ||
339 manufacturer_ == ModalityManufacturer_Dcm4Chee) 352 manufacturer_ == ModalityManufacturer_Dcm4Chee)
340 { 353 {
341 // This is a particular case for ClearCanvas, thanks to Peter Somlo <peter.somlo@gmail.com>. 354 // This is a particular case for ClearCanvas, thanks to Peter Somlo <peter.somlo@gmail.com>.
342 // https://groups.google.com/d/msg/orthanc-users/j-6C3MAVwiw/iolB9hclom8J 355 // https://groups.google.com/d/msg/orthanc-users/j-6C3MAVwiw/iolB9hclom8J
343 // http://www.clearcanvas.ca/Home/Community/OldForums/tabid/526/aff/11/aft/14670/afv/topic/Default.aspx 356 // http://www.clearcanvas.ca/Home/Community/OldForums/tabid/526/aff/11/aft/14670/afv/topic/Default.aspx
344 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0052), "IMAGE"); 357 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0052), "IMAGE");
345 } 358 }
346 else 359 else
347 { 360 {
348 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0052), "INSTANCE"); 361 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0052), "INSTANCE");
349 } 362 }
350 363
351 sopClass = UID_FINDStudyRootQueryRetrieveInformationModel; 364 sopClass = UID_FINDStudyRootQueryRetrieveInformationModel;
352 365
353 // Accession number 366 // Accession number
354 if (!fields.HasTag(0x0008, 0x0050)) 367 if (!fields.HasTag(0x0008, 0x0050))
355 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0050), ""); 368 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0050), "");
356 369
357 // Study instance UID 370 // Study instance UID
358 if (!fields.HasTag(0x0020, 0x000d)) 371 if (!fields.HasTag(0x0020, 0x000d))
359 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0020, 0x000d), ""); 372 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0020, 0x000d), "");
360 373
361 // Series instance UID 374 // Series instance UID
362 if (!fields.HasTag(0x0020, 0x000e)) 375 if (!fields.HasTag(0x0020, 0x000e))
363 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0020, 0x000e), ""); 376 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0020, 0x000e), "");
364 377
365 // SOP Instance UID 378 // SOP Instance UID
366 if (!fields.HasTag(0x0008, 0x0018)) 379 if (!fields.HasTag(0x0008, 0x0018))
367 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0018), ""); 380 DU_putStringDOElement(dataset.get(), DcmTagKey(0x0008, 0x0018), "");
368 381
369 break; 382 break;
370 383
371 default: 384 default:
372 throw OrthancException(ErrorCode_ParameterOutOfRange); 385 throw OrthancException(ErrorCode_ParameterOutOfRange);
373 } 386 }
374 387
375 // Figure out which of the accepted presentation contexts should be used 388 // Figure out which of the accepted presentation contexts should be used
376 int presID = ASC_findAcceptedPresentationContextID(pimpl_->assoc_, sopClass); 389 int presID = ASC_findAcceptedPresentationContextID(pimpl_->assoc_, sopClass);
377 if (presID == 0) 390 if (presID == 0)
499 512
500 Check(cond); 513 Check(cond);
501 } 514 }
502 515
503 516
517 void DicomUserConnection::ResetStorageSOPClasses()
518 {
519 CheckStorageSOPClassesInvariant();
520
521 storageSOPClasses_.clear();
522 defaultStorageSOPClasses_.clear();
523
524 // Copy the short list of storage SOP classes from DCMTK, making
525 // room for the 4 SOP classes reserved for C-ECHO, C-FIND, C-MOVE.
526
527 std::set<std::string> uncommon;
528 uncommon.insert(UID_BlendingSoftcopyPresentationStateStorage);
529 uncommon.insert(UID_GrayscaleSoftcopyPresentationStateStorage);
530 uncommon.insert(UID_ColorSoftcopyPresentationStateStorage);
531 uncommon.insert(UID_PseudoColorSoftcopyPresentationStateStorage);
532
533 // Add the storage syntaxes for C-STORE
534 for (int i = 0; i < numberOfDcmShortSCUStorageSOPClassUIDs - 1; i++)
535 {
536 if (uncommon.find(dcmShortSCUStorageSOPClassUIDs[i]) == uncommon.end())
537 {
538 defaultStorageSOPClasses_.insert(dcmShortSCUStorageSOPClassUIDs[i]);
539 }
540 }
541
542 CheckStorageSOPClassesInvariant();
543 }
544
545
504 DicomUserConnection::DicomUserConnection() : 546 DicomUserConnection::DicomUserConnection() :
505 pimpl_(new PImpl), 547 pimpl_(new PImpl),
506 preferredTransferSyntax_(DEFAULT_PREFERRED_TRANSFER_SYNTAX), 548 preferredTransferSyntax_(DEFAULT_PREFERRED_TRANSFER_SYNTAX),
507 localAet_("STORESCU"), 549 localAet_("STORESCU"),
508 distantAet_("ANY-SCP"), 550 distantAet_("ANY-SCP"),
512 manufacturer_ = ModalityManufacturer_Generic; 554 manufacturer_ = ModalityManufacturer_Generic;
513 555
514 pimpl_->net_ = NULL; 556 pimpl_->net_ = NULL;
515 pimpl_->params_ = NULL; 557 pimpl_->params_ = NULL;
516 pimpl_->assoc_ = NULL; 558 pimpl_->assoc_ = NULL;
559
560 // SOP classes for C-ECHO, C-FIND and C-MOVE
561 reservedStorageSOPClasses_.push_back(UID_VerificationSOPClass);
562 reservedStorageSOPClasses_.push_back(UID_FINDPatientRootQueryRetrieveInformationModel);
563 reservedStorageSOPClasses_.push_back(UID_FINDStudyRootQueryRetrieveInformationModel);
564 reservedStorageSOPClasses_.push_back(UID_MOVEStudyRootQueryRetrieveInformationModel);
565
566 ResetStorageSOPClasses();
517 } 567 }
518 568
519 DicomUserConnection::~DicomUserConnection() 569 DicomUserConnection::~DicomUserConnection()
520 { 570 {
521 Close(); 571 Close();
605 gethostname(localHost, HOST_NAME_MAX - 1); 655 gethostname(localHost, HOST_NAME_MAX - 1);
606 656
607 char distantHostAndPort[HOST_NAME_MAX]; 657 char distantHostAndPort[HOST_NAME_MAX];
608 658
609 #ifdef _MSC_VER 659 #ifdef _MSC_VER
610 _snprintf 660 _snprintf
611 #else 661 #else
612 snprintf 662 snprintf
613 #endif 663 #endif
614 (distantHostAndPort, HOST_NAME_MAX - 1, "%s:%d", distantHost_.c_str(), distantPort_); 664 (distantHostAndPort, HOST_NAME_MAX - 1, "%s:%d", distantHost_.c_str(), distantPort_);
615 665
616 Check(ASC_setPresentationAddresses(pimpl_->params_, localHost, distantHostAndPort)); 666 Check(ASC_setPresentationAddresses(pimpl_->params_, localHost, distantHostAndPort));
617 667
618 // Set various options 668 // Set various options
619 Check(ASC_setTransportLayerType(pimpl_->params_, /*opt_secureConnection*/ false)); 669 Check(ASC_setTransportLayerType(pimpl_->params_, /*opt_secureConnection*/ false));
740 void DicomUserConnection::SetConnectionTimeout(uint32_t seconds) 790 void DicomUserConnection::SetConnectionTimeout(uint32_t seconds)
741 { 791 {
742 dcmConnectionTimeout.set(seconds); 792 dcmConnectionTimeout.set(seconds);
743 } 793 }
744 794
795
796 void DicomUserConnection::CheckStorageSOPClassesInvariant() const
797 {
798 assert(storageSOPClasses_.size() +
799 defaultStorageSOPClasses_.size() +
800 reservedStorageSOPClasses_.size() <= MAXIMUM_STORAGE_SOP_CLASSES);
801 }
802
803 void DicomUserConnection::AddStorageSOPClass(const char* sop)
804 {
805 CheckStorageSOPClassesInvariant();
806
807 if (storageSOPClasses_.find(sop) != storageSOPClasses_.end())
808 {
809 // This storage SOP class is already explicitly registered. Do
810 // nothing.
811 return;
812 }
813
814 if (defaultStorageSOPClasses_.find(sop) != defaultStorageSOPClasses_.end())
815 {
816 // This storage SOP class is not explicitly registered, but is
817 // used by default. Just register it explicitly.
818 defaultStorageSOPClasses_.erase(sop);
819 storageSOPClasses_.insert(sop);
820
821 CheckStorageSOPClassesInvariant();
822 return;
823 }
824
825 // This storage SOP class is neither explicitly, nor implicitly
826 // registered. Close the connection and register it explicitly.
827
828 Close();
829
830 if (reservedStorageSOPClasses_.size() +
831 storageSOPClasses_.size() >= MAXIMUM_STORAGE_SOP_CLASSES) // (*)
832 {
833 // The maximum number of SOP classes is reached
834 ResetStorageSOPClasses();
835 defaultStorageSOPClasses_.erase(sop);
836 }
837 else if (reservedStorageSOPClasses_.size() + storageSOPClasses_.size() +
838 defaultStorageSOPClasses_.size() >= MAXIMUM_STORAGE_SOP_CLASSES)
839 {
840 // Make room in the default storage syntaxes
841 assert(defaultStorageSOPClasses_.size() > 0); // Necessarily true because condition (*) is false
842 defaultStorageSOPClasses_.erase(*defaultStorageSOPClasses_.rbegin());
843 }
844
845 // Explicitly register the new storage syntax
846 storageSOPClasses_.insert(sop);
847
848 CheckStorageSOPClassesInvariant();
849 }
850
745 } 851 }