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