1 /**
2 * @file CompBase.cpp
3 * @brief Implementation of CompBase, the base class of extension
4 * entities plugged in SBase derived classes in the SBML Core package.
5 * @author Lucian Smith
6 *
7 * <!--------------------------------------------------------------------------
8 * This file is part of libSBML. Please visit http://sbml.org for more
9 * information about SBML, and the latest version of libSBML.
10 *
11 * Copyright (C) 2020 jointly by the following organizations:
12 * 1. California Institute of Technology, Pasadena, CA, USA
13 * 2. University of Heidelberg, Heidelberg, Germany
14 * 3. University College London, London, UK
15 *
16 * Copyright (C) 2019 jointly by the following organizations:
17 * 1. California Institute of Technology, Pasadena, CA, USA
18 * 2. University of Heidelberg, Heidelberg, Germany
19 *
20 * Copyright (C) 2013-2018 jointly by the following organizations:
21 * 1. California Institute of Technology, Pasadena, CA, USA
22 * 2. EMBL European Bioinformatics Institute (EMBL-EBI), Hinxton, UK
23 * 3. University of Heidelberg, Heidelberg, Germany
24 *
25 * Copyright 2011-2012 jointly by the following organizations:
26 * 1. California Institute of Technology, Pasadena, CA, USA
27 * 2. EMBL European Bioinformatics Institute (EMBL-EBI), Hinxton, UK
28 *
29 * This library is free software; you can redistribute it and/or modify it
30 * under the terms of the GNU Lesser General Public License as published by
31 * the Free Software Foundation. A copy of the license agreement is provided
32 * in the file named "LICENSE.txt" included with this software distribution
33 * and also available online as http://sbml.org/software/libsbml/license.html
34 * ------------------------------------------------------------------------ -->
35 */
36
37 #include <sbml/packages/comp/sbml/CompBase.h>
38 #include <sbml/packages/comp/extension/CompModelPlugin.h>
39 #include <sbml/packages/comp/sbml/Port.h>
40 #include <sbml/Model.h>
41 #include <sbml/extension/SBMLExtensionRegistry.h>
42 #include <sbml/packages/comp/validator/CompSBMLError.h>
43
44 #ifdef __cplusplus
45
46 #include <sstream>
47 #include <iostream>
48
49 using namespace std;
50
51 LIBSBML_CPP_NAMESPACE_BEGIN
52 #ifdef __cplusplus
53
CompBase(unsigned int level,unsigned int version,unsigned int pkgVersion)54 CompBase::CompBase (unsigned int level, unsigned int version, unsigned int pkgVersion)
55 : SBase (level,version)
56 {
57 // set an SBMLNamespaces derived object (CompPkgNamespaces) of this package.
58 CompPkgNamespaces* cpn = new CompPkgNamespaces(level,version,pkgVersion);
59 setSBMLNamespacesAndOwn(cpn);
60
61 // connect child elements to this element.
62 connectToChild();
63
64 mSBMLExt = SBMLExtensionRegistry::getInstance().getExtension(mSBMLNamespaces->getURI());
65 }
66
67
CompBase(CompPkgNamespaces * compns)68 CompBase::CompBase(CompPkgNamespaces* compns)
69 : SBase(compns)
70 , mSBMLExt(SBMLExtensionRegistry::getInstance().getExtension(compns->getURI()))
71 {
72 // set the element namespace of this object
73 setElementNamespace(compns->getURI());
74
75 // connect child elements to this element.
76 connectToChild();
77 }
78
79
CompBase(const CompBase & source)80 CompBase::CompBase(const CompBase& source)
81 : SBase (source)
82 , mSBMLExt(NULL)
83 {
84 if (source.mSBMLExt != NULL) {
85 mSBMLExt = source.mSBMLExt->clone();
86 }
87 // connect child elements to this element.
88 connectToChild();
89
90 // load package extensions bound with this object (if any)
91 loadPlugins(mSBMLNamespaces);
92 }
93
operator =(const CompBase & source)94 CompBase& CompBase::operator=(const CompBase& source)
95 {
96 if(&source!=this)
97 {
98 SBase::operator=(source);
99 if (source.mSBMLExt != NULL) {
100 mSBMLExt = source.mSBMLExt->clone();
101 }
102
103 // connect child elements to this element.
104 connectToChild();
105
106 // load package extensions bound with this object (if any)
107 loadPlugins(mSBMLNamespaces);
108 }
109
110 return *this;
111 }
112
113
~CompBase()114 CompBase::~CompBase ()
115 {
116 delete mSBMLExt;
117 }
118
119
120 /*
121 * Returns the XML namespace (URI) of the package extension
122 * of this object.
123 */
124 const std::string&
getPackageURI() const125 CompBase::getPackageURI() const
126 {
127 return mURI;
128 }
129
130 /*
131 * Returns the package name of this object.
132 */
133 const std::string&
getPackageName() const134 CompBase::getPackageName() const
135 {
136 return mSBMLExt->getName();
137 }
138
139 /*
140 * Returns the package version of this plugin object.
141 */
142 unsigned int
getPackageVersion() const143 CompBase::getPackageVersion() const
144 {
145 return mSBMLExt->getPackageVersion(mURI);
146 }
147
148 Model*
getParentModel(SBase * child)149 CompBase::getParentModel(SBase* child)
150 {
151 SBase* parent = child->getParentSBMLObject();
152 while (parent != NULL && parent->getTypeCode() != SBML_DOCUMENT) {
153 if ((parent->getTypeCode() == SBML_MODEL) ||
154 (parent->getTypeCode() == SBML_COMP_MODELDEFINITION)) {
155 return static_cast<Model*>(parent);
156 }
157 if (parent->getTypeCode() == SBML_COMP_EXTERNALMODELDEFINITION) return NULL;
158 parent = parent->getParentSBMLObject();
159 }
160 return NULL;
161 }
162
163 void
readAttributes(const XMLAttributes & attributes,const ExpectedAttributes & expectedAttributes,bool hasCompIdName,bool idRequired,CompSBMLErrorCode_t errcode)164 CompBase::readAttributes (const XMLAttributes& attributes,
165 const ExpectedAttributes& expectedAttributes,
166 bool hasCompIdName, bool idRequired, CompSBMLErrorCode_t errcode)
167 {
168 SBase::readAttributes(attributes,expectedAttributes);
169
170 //const unsigned int sbmlLevel = getLevel ();
171 //const unsigned int sbmlVersion = getVersion();
172 //const unsigned int pkgVersion = getPackageVersion();
173
174 std::string element = getElementName();
175
176 //
177 // check that all attributes of this plugin object are expected
178 //
179 for (int i = 0; i < attributes.getLength(); i++)
180 {
181 std::string name = attributes.getName(i);
182 std::string uri = attributes.getURI(i);
183
184 if (uri != mURI) continue;
185
186 if (!expectedAttributes.hasAttribute(name))
187 {
188 logUnknownAttribute(name, element);
189 }
190 }
191
192 SBMLErrorLog* log = getErrorLog();
193 const unsigned int sbmlLevel = getLevel ();
194 const unsigned int sbmlVersion = getVersion();
195 const unsigned int pkgVersion = getPackageVersion();
196
197 string compid = attributes.getValue("id", mURI);
198 string coreid = attributes.getValue("id", "");
199 string compname = attributes.getValue("name", mURI);
200 string corename = attributes.getValue("name", "");
201
202 if (hasCompIdName)
203 {
204 XMLTriple tripleId("id", mURI, getPrefix());
205 bool assigned = attributes.readInto(tripleId, mId);
206
207 if (!assigned && idRequired && coreid.empty())
208 {
209 string message = "Comp attribute 'comp:id' is missing.";
210 getErrorLog()->logPackageError("comp", errcode,
211 pkgVersion, sbmlLevel, sbmlVersion, message, getLine(), getColumn());
212 }
213 else if (!assigned && !coreid.empty())
214 {
215 string details = "The <comp:";
216 details += getElementName() + "> element with the 'id' with value '"
217 + coreid + "' must use 'comp:id' instead.";
218 log->logPackageError("comp", errcode,
219 pkgVersion, sbmlLevel, sbmlVersion, details, getLine(), getColumn());
220 }
221 else if (!coreid.empty())
222 {
223 //Both core and comp id's
224 unsigned int severity = LIBSBML_SEV_ERROR;
225 if (coreid == compid) {
226 severity = LIBSBML_SEV_WARNING;
227 }
228 string details = "The <comp:";
229 details += getElementName() + "> element with the 'id' with value '"
230 + coreid + "' and the 'comp:id' with value '"
231 + compid + "' must only use the 'comp:id' attribute.";
232 log->logPackageError("comp", errcode,
233 pkgVersion, sbmlLevel, sbmlVersion, details, getLine(), getColumn(), severity);
234 //log->getError(log->getNumErrors() - 1)->setSeverity(severity);
235 }
236
237 if (assigned && !SyntaxChecker::isValidSBMLSId(mId)) {
238 logInvalidId("comp:id", mId);
239 }
240
241 XMLTriple tripleName("name", mURI, getPrefix());
242 assigned = attributes.readInto(tripleName, mName);
243 if (!assigned && !corename.empty())
244 {
245 string details = "The <comp:";
246 details += getElementName() + "> element with the 'name' with value '"
247 + corename + "' must use 'comp:name' instead.";
248 log->logPackageError("comp", errcode,
249 pkgVersion, sbmlLevel, sbmlVersion, details, getLine(), getColumn());
250 }
251 else if (!corename.empty())
252 {
253 //Both core and comp name's
254 unsigned int severity = LIBSBML_SEV_ERROR;
255 if (corename == compname) {
256 severity = LIBSBML_SEV_WARNING;
257 }
258 string details = "The <comp:";
259 details += getElementName() + "> element with the 'name' with value '"
260 + corename + "' and the 'comp:name' with value '"
261 + compname + "' must only use the 'comp:name' attribute.";
262 log->logPackageError("comp", errcode,
263 pkgVersion, sbmlLevel, sbmlVersion, details, getLine(), getColumn(), severity);
264 }
265 if (assigned && mName.empty()) {
266 logInvalidId("comp:name", mName);
267 }
268 }
269 else
270 {
271 if (!compid.empty())
272 {
273 string details = "The <comp:";
274 details += getElementName() + "> element with the 'comp:id' with value '"
275 + compid;
276 if (!coreid.empty()) {
277 details += "' and the 'id' with value '" + coreid;
278 }
279 details += "' must not use the 'comp:id' attribute.";
280 log->logPackageError("comp", errcode,
281 pkgVersion, sbmlLevel, sbmlVersion, details, getLine(), getColumn());
282 }
283 if (!compname.empty())
284 {
285 string details = "The <comp:";
286 details += getElementName() + "> element with the 'comp:name' with value '"
287 + compname;
288 if (!corename.empty()) {
289 details += "' and the 'name' with value '" + corename;
290 }
291 details += "' must not use the 'comp:name' attribute.";
292 log->logPackageError("comp", errcode,
293 pkgVersion, sbmlLevel, sbmlVersion, details, getLine(), getColumn());
294 }
295 }
296 }
297
298 void
writeAttributes(XMLOutputStream & stream) const299 CompBase::writeAttributes (XMLOutputStream& stream) const
300 {
301 SBase::writeAttributes(stream);
302 //
303 // If some other package extends this one, we'll need to write its attributes
304 //
305 writeExtensionAttributes(stream);
306 }
307
308 /*
309 * Helper to log a common type of error.
310 */
311 void
logUnknownElement(const std::string & element)312 CompBase::logUnknownElement(const std::string &element)
313 {
314 std::ostringstream msg;
315
316 msg << "Element '" << element << "' is not part of the definition of "
317 << "SBML Level " << getLevel() << " Version " << getVersion()
318 << " Package \"" << getPrefix() << "\" Version "
319 << getPackageVersion() << ".";
320
321 SBMLErrorLog* errlog = getErrorLog();
322 if (errlog)
323 {
324 errlog->logError(UnrecognizedElement, getLevel(), getVersion(), msg.str());
325 }
326 }
327
328
329 /*
330 * Helper to log a common type of error.
331 */
332 void
logUnknownAttribute(const std::string & attribute,const std::string & element)333 CompBase::logUnknownAttribute(const std::string &attribute,
334 const std::string& element)
335 {
336 std::ostringstream msg;
337
338 msg << "Attribute '" << attribute << "' is not part of the "
339 << "definition of an SBML Level " << getLevel()
340 << " Version " << getVersion() << " Package \""
341 << getPrefix() << "\" Version " << getPackageVersion()
342 << " on " << element << " element.";
343
344 SBMLErrorLog* errlog = getErrorLog();
345 if (errlog != NULL)
346 {
347 if (element == "port")
348 {
349 errlog->logPackageError(getPackageName(), CompPortAllowedAttributes,
350 getPackageVersion(), getLevel(), getVersion(), msg.str(),
351 getLine(), getColumn());
352 }
353 else
354 {
355 errlog->logError(NotSchemaConformant, getLevel(), getVersion(), msg.str(),
356 getLine(), getColumn());
357 }
358 }
359 }
360
361
362 /*
363 * Helper to log a common type of error.
364 */
365 void
logEmptyString(const std::string & attribute,const std::string & element)366 CompBase::logEmptyString(const std::string &attribute,
367 const std::string& element)
368 {
369
370 std::ostringstream msg;
371
372 msg << "Attribute '" << attribute << "' on an "
373 << element << " of package \"" << getPrefix()
374 << "\" version " << getPackageVersion() << " must not be an empty string.";
375
376 SBMLErrorLog* errlog = getErrorLog();
377 if (errlog)
378 {
379 errlog->logError(NotSchemaConformant, getLevel(), getVersion(), msg.str(),
380 getLine(), getColumn());
381 }
382 }
383
384
385 void
logInvalidId(const std::string & attribute,const std::string & wrongattribute,const std::string object)386 CompBase::logInvalidId(const std::string& attribute,
387 const std::string& wrongattribute,
388 const std::string object)
389 {
390
391 std::ostringstream msg;
392
393 if (attribute == "comp:metaIdRef")
394 {
395 msg << "Setting the attribute '" << attribute << "' of a <"
396 << getElementName() << "> in the " << getPackageName()
397 << " package (version " << getPackageVersion() << ") to '" << wrongattribute
398 << "' is illegal: the string is not a well-formed XML ID.";
399 }
400 else
401 {
402 msg << "Setting the attribute '" << attribute << "' of a <"
403 << getElementName() << "> in the " << getPackageName()
404 << " package (version " << getPackageVersion() << ") to '" << wrongattribute
405 << "' is illegal: the string is not a well-formed SId.";
406 }
407
408 SBMLErrorLog* errlog = getErrorLog();
409
410 if (errlog != NULL)
411 {
412 // so here we have different errors depending on
413 // what the attribute actually is
414 if (attribute == "comp:deletion")
415 {
416 errlog->logPackageError(getPackageName(), CompInvalidDeletionSyntax,
417 getPackageVersion(), getLevel(), getVersion(), msg.str(),
418 getLine(), getColumn());
419 }
420 else if (attribute == "comp:conversionFactor")
421 {
422 errlog->logPackageError(getPackageName(), CompInvalidConversionFactorSyntax,
423 getPackageVersion(), getLevel(), getVersion(), msg.str(),
424 getLine(), getColumn());
425 }
426 else if (attribute == "comp:submodelRef")
427 {
428 errlog->logPackageError(getPackageName(), CompInvalidSubmodelRefSyntax,
429 getPackageVersion(), getLevel(), getVersion(), msg.str(),
430 getLine(), getColumn());
431 }
432 else if (attribute == "comp:modelRef")
433 {
434 if (object == "Submodel")
435 {
436 errlog->logPackageError(getPackageName(), CompModReferenceSyntax,
437 getPackageVersion(), getLevel(), getVersion(), msg.str(),
438 getLine(), getColumn());
439 }
440 else
441 {
442 errlog->logPackageError(getPackageName(), CompInvalidModelRefSyntax,
443 getPackageVersion(), getLevel(), getVersion(), msg.str(),
444 getLine(), getColumn());
445 }
446 }
447 else if (attribute == "comp:metaIdRef")
448 {
449 errlog->logPackageError(getPackageName(), CompInvalidMetaIdRefSyntax,
450 getPackageVersion(), getLevel(), getVersion(), msg.str(),
451 getLine(), getColumn());
452 }
453 else if (attribute == "comp:idRef")
454 {
455 errlog->logPackageError(getPackageName(), CompInvalidIdRefSyntax,
456 getPackageVersion(), getLevel(), getVersion(), msg.str(),
457 getLine(), getColumn());
458 }
459 else if (attribute == "comp:portRef")
460 {
461 errlog->logPackageError(getPackageName(), CompInvalidPortRefSyntax,
462 getPackageVersion(), getLevel(), getVersion(), msg.str(),
463 getLine(), getColumn());
464 }
465 else if (attribute == "comp:unitRef")
466 {
467 errlog->logPackageError(getPackageName(), CompInvalidUnitRefSyntax,
468 getPackageVersion(), getLevel(), getVersion(), msg.str(),
469 getLine(), getColumn());
470 }
471 else if (attribute == "comp:timeConversionFactor")
472 {
473 errlog->logPackageError(getPackageName(), CompInvalidTimeConvFactorSyntax,
474 getPackageVersion(), getLevel(), getVersion(), msg.str(),
475 getLine(), getColumn());
476 }
477 else if (attribute == "comp:extentConversionFactor")
478 {
479 errlog->logPackageError(getPackageName(), CompInvalidExtentConvFactorSyntax,
480 getPackageVersion(), getLevel(), getVersion(), msg.str(),
481 getLine(), getColumn());
482 }
483 else
484 {
485 errlog->logPackageError(getPackageName(), CompInvalidSIdSyntax,
486 getPackageVersion(), getLevel(), getVersion(), msg.str(),
487 getLine(), getColumn());
488 }
489 }
490 }
491
492 void
logMissingAttribute(const std::string & attribute,const std::string & element)493 CompBase::logMissingAttribute(const std::string& attribute,
494 const std::string& element)
495 {
496
497 std::ostringstream msg;
498
499 msg << "The required attribute '" << attribute << "' of a <"
500 << getElementName() << "> in the " << getPackageName()
501 << " package (version " << getPackageVersion() << ") is missing.";
502
503 SBMLErrorLog* errlog = getErrorLog();
504
505 if (errlog != NULL)
506 {
507 // so here we have different errors depending on
508 // what the attribute actually is
509 if (element == "<Port>")
510 {
511 errlog->logPackageError(getPackageName(), CompPortAllowedAttributes,
512 getPackageVersion(), getLevel(), getVersion(), msg.str(),
513 getLine(), getColumn());
514 }
515 else if (element == "<ExternalModelDefinition>")
516 {
517 errlog->logPackageError(getPackageName(), CompExtModDefAllowedAttributes,
518 getPackageVersion(), getLevel(), getVersion(), msg.str(),
519 getLine(), getColumn());
520 }
521 else if (element == "<Deletion>")
522 {
523 errlog->logPackageError(getPackageName(), CompDeletionAllowedAttributes,
524 getPackageVersion(), getLevel(), getVersion(), msg.str(),
525 getLine(), getColumn());
526 }
527 }
528 }
529
530
531 bool
hasValidLevelVersionNamespaceCombination()532 CompBase::hasValidLevelVersionNamespaceCombination()
533 {
534 //Here is where we could combine all namespace checking into one place, like is done for SBase.
535 // But for now, we just need to check to see if the single legal URI is present.
536
537 XMLNamespaces *xmlns = getNamespaces();
538 if (xmlns == NULL) return false;
539 if (xmlns->hasURI("http://www.sbml.org/sbml/level3/version1/comp/version1")) return true;
540 return false;
541 }
542
543
removeFromParentAndPorts(SBase * todelete,set<SBase * > * removed)544 int CompBase::removeFromParentAndPorts(SBase* todelete, set<SBase*>* removed)
545 {
546 //First remove from ports:
547 Model* parent = static_cast<Model*>(todelete->getAncestorOfType(SBML_COMP_MODELDEFINITION, "comp"));
548 if (parent==NULL) {
549 parent = static_cast<Model*>(todelete->getAncestorOfType(SBML_MODEL));
550 }
551 while (parent != NULL) {
552 CompModelPlugin* cmp = static_cast<CompModelPlugin*>(parent->getPlugin("comp"));
553 if (cmp==NULL) {
554 parent = NULL;
555 continue;
556 }
557 for (unsigned long p=0; p<cmp->getNumPorts();) {
558 Port* port = cmp->getPort((unsigned int)p);
559 if (port->getReferencedElement() == todelete) {
560 if (removed) {
561 removed->insert(port);
562 }
563 port->removeFromParentAndDelete();
564 }
565 else {
566 p++;
567 }
568 }
569 Model* tempparent = static_cast<Model*>(parent->getAncestorOfType(SBML_COMP_MODELDEFINITION, "comp"));
570 if (tempparent==NULL) {
571 parent = static_cast<Model*>(parent->getAncestorOfType(SBML_MODEL));
572 }
573 else parent = tempparent;
574 }
575 //And secondly, remove from parent
576 if (removed) {
577 removed->insert(todelete);
578 }
579 return todelete->removeFromParentAndDelete();
580 }
581
582
583 //Deprecated function
removeFromParentAndPorts(SBase * todelete)584 int CompBase::removeFromParentAndPorts(SBase* todelete)
585 {
586 //First remove from ports:
587 Model* parent = static_cast<Model*>(todelete->getAncestorOfType(SBML_COMP_MODELDEFINITION, "comp"));
588 if (parent==NULL) {
589 parent = static_cast<Model*>(todelete->getAncestorOfType(SBML_MODEL));
590 }
591 while (parent != NULL) {
592 CompModelPlugin* cmp = static_cast<CompModelPlugin*>(parent->getPlugin("comp"));
593 if (cmp==NULL) {
594 parent = NULL;
595 continue;
596 }
597 CompModelPlugin* basecmp = cmp;
598 SBase* base = parent->getParentSBMLObject();
599 while (base != NULL && base->getTypeCode() != SBML_DOCUMENT) {
600 if (base->getTypeCode() == SBML_COMP_MODELDEFINITION ||
601 base->getTypeCode() == SBML_MODEL)
602 {
603 CompModelPlugin* testcmp = static_cast<CompModelPlugin*>(base->getPlugin("comp"));
604 if (testcmp != NULL) {
605 basecmp = testcmp;
606 }
607 }
608 base = base->getParentSBMLObject();
609 }
610 for (unsigned long p=0; p<cmp->getNumPorts();) {
611 Port* port = cmp->getPort((unsigned int)p);
612 if (port->getReferencedElement() == todelete) {
613 set<SBase*>* removed = basecmp->getRemovedSet();
614 set<SBase*> toremove;
615 toremove.insert(port);
616 basecmp->removeCollectedElements(removed, &toremove);
617 }
618 else {
619 p++;
620 }
621 }
622 Model* tempparent = static_cast<Model*>(parent->getAncestorOfType(SBML_COMP_MODELDEFINITION, "comp"));
623 if (tempparent==NULL) {
624 parent = static_cast<Model*>(parent->getAncestorOfType(SBML_MODEL));
625 }
626 else parent = tempparent;
627 }
628 //And secondly, remove from parent
629 return todelete->removeFromParentAndDelete();
630 }
631
632 #endif /* __cplusplus */
633
634 LIBSBML_CPP_NAMESPACE_END
635
636 #endif /* __cplusplus */
637