Skip to content

Commit

Permalink
Merge pull request #305 from sbmlteam/issue-296-setannotation
Browse files Browse the repository at this point in the history
Issue 296 setannotation
  • Loading branch information
skeating committed Mar 22, 2023
2 parents 9d0c8a2 + e0dc07a commit 3b1758a
Show file tree
Hide file tree
Showing 15 changed files with 623 additions and 34 deletions.
1 change: 1 addition & 0 deletions examples/c++/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ foreach(example
translateL3Math
unsetAnnotation
unsetNotes
setAnnotation
validateSBML

)
Expand Down
115 changes: 115 additions & 0 deletions examples/c++/setAnnotation.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/**
* Example, reading an SBML file containing an element with provided
* meta id, reading a raw annoation from a file, and adding it to the
* element.
*
*/

#include <iostream>
#include <sbml/SBMLTypes.h>

using namespace std;
LIBSBML_CPP_NAMESPACE_USE

XMLNode* parseAnnotation(const std::string& annotationFile)
{
// read file into memory
FILE* file = fopen(annotationFile.c_str(), "r");
fseek(file, 0, SEEK_END);
long length = ftell(file);

fseek(file, 0, SEEK_SET);
char* data = new char[length + 1];

// fill data with zeros
memset(data, 0, length + 1);

fread(data, 1, length, file);
fclose(file);
data[length] = '\0';

// parse the string
XMLNode* xmlnode = XMLNode::convertStringToXMLNode(data);
delete[] data;
return xmlnode;
}

bool setAnnotation(const std::string& sbmlFile, const std::string& metaId,
const std::string& annotationFile, const std::string& outputFile)
{
SBMLDocument *document = readSBML(sbmlFile.c_str());
if (document->getNumErrors(LIBSBML_SEV_ERROR) > 0)
{
cerr << "Encountered the following SBML errors:" << std::endl;
document->printErrors();
return false;
}

SBase* element = document->getElementByMetaId(metaId);
if (!element)
{
cerr << "No element with meta id " << metaId << " found." << std::endl;
return false;
}

XMLNode* annotation = parseAnnotation(annotationFile);
if (!annotation)
{
cerr << "the annotation could not be parsed from file: " << annotationFile << "." << std::endl;
return false;
}
std::string annotationString = annotation->toXMLString();
element->setAnnotation(annotation);

// at this point we'd expect the annotation to be set precisely to what it was
// in the file.


std::string resultingAnnotation = element->getAnnotationString();

if (annotationString != resultingAnnotation)
{
cerr << "Annotation was not set correctly." << std::endl;
return false;
}

// write document to output file
writeSBMLToFile(document, outputFile.c_str());

delete document;

return true;
}

int main(int argc, char* argv[])
{
std::string sbmlFile;
std::string metaId;
std::string annotationFile;
std::string outputFile;

if (argc != 5)
{
cout << endl
<< " usage: setAnnotaion <input-filename> <element-meta-id> <annotation-file> <output-file>" << endl
<< " Adds controlled vocabulary term to a species" << endl
<< endl;
return 1;


sbmlFile = "bm190.xml";
annotationFile = "annotation.xml";
metaId = "metaid_0000036";
outputFile = "bm190_out.xml";

}
else
{
sbmlFile = argv[1];
metaId = argv[2];
annotationFile = argv[3];
outputFile = argv[4];
}

setAnnotation(sbmlFile, metaId, annotationFile, outputFile);
}
59 changes: 43 additions & 16 deletions src/sbml/SBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1341,13 +1341,36 @@ SBase::setAnnotation (const XMLNode* annotation)
mCVTerms = NULL;
}

unsigned int level = getLevel();
bool hasNestedTerms = false;
bool validNestedTerms = true;
if (level < 2 ||
(level == 2 && getVersion() < 5))
{
validNestedTerms = false;
}

if(mAnnotation != NULL
&& RDFAnnotationParser::hasCVTermRDFAnnotation(mAnnotation))
{
// parse mAnnotation (if any) and set mCVTerms
mCVTerms = new List();
RDFAnnotationParser::parseRDFAnnotation(mAnnotation, mCVTerms);
// look at cvterms to see if we have a nested term
for (unsigned int cv = 0; cv < mCVTerms->getSize(); cv++)
{
CVTerm * term = (CVTerm *)(mCVTerms->get(cv));
if (term->getNumNestedCVTerms() > 0)
{
hasNestedTerms = true;
/* this essentially tells the code that rewrites the annotation to
* reconstruct the node and should leave out the nested bit
* if it not allowed
*/
term->setHasBeenModifiedFlag();
term->setCapturedInStoredAnnotation(!validNestedTerms);
}
}
mCVTermsChanged = true;
}

Expand Down Expand Up @@ -4670,9 +4693,10 @@ SBase::readAnnotation (XMLInputStream& stream)
const string& name = stream.peek().getName();

unsigned int level = getLevel();
unsigned int version = getVersion();

if (name == "annotation"
|| (level == 1 && getVersion() == 1 && name == "annotations"))
|| (level == 1 && version == 1 && name == "annotations"))
{
// If this is a level 1 document then annotations are not allowed on
// the sbml container
Expand Down Expand Up @@ -4702,15 +4726,15 @@ SBase::readAnnotation (XMLInputStream& stream)
break;
}
msg += "has multiple <annotation> children.";
if (getLevel() < 3)
if (level < 3)
{
logError(NotSchemaConformant, getLevel(), getVersion(),
logError(NotSchemaConformant, level, version,
"Only one <annotation> element is permitted inside a "
"particular containing element. " + msg);
}
else
{
logError(MultipleAnnotations, getLevel(), getVersion(), msg);
logError(MultipleAnnotations, level, version, msg);
}
}

Expand All @@ -4725,7 +4749,7 @@ SBase::readAnnotation (XMLInputStream& stream)
}
mCVTerms = new List();
/* might have model history on sbase objects */
if (getLevel() > 2 && getTypeCode()!= SBML_MODEL)
if (level > 2 && getTypeCode()!= SBML_MODEL)
{
delete mHistory;
if (RDFAnnotationParser::hasHistoryRDFAnnotation(mAnnotation))
Expand All @@ -4734,7 +4758,7 @@ SBase::readAnnotation (XMLInputStream& stream)
getMetaId().c_str(), &(stream), this);
if (mHistory != NULL && mHistory->hasRequiredAttributes() == false)
{
logError(RDFNotCompleteModelHistory, getLevel(), getVersion(),
logError(RDFNotCompleteModelHistory, level, version,
"An invalid ModelHistory element has been stored.");
}
setModelHistory(mHistory);
Expand All @@ -4750,6 +4774,13 @@ SBase::readAnnotation (XMLInputStream& stream)
getMetaId().c_str(), &(stream));

bool hasNestedTerms = false;
bool validNestedTerms = true;
if (level < 2 ||
(level == 2 && version < 5))
{
validNestedTerms = false;
}

// look at cvterms to see if we have a nested term
for (unsigned int cv = 0; cv < mCVTerms->getSize(); cv++)
{
Expand All @@ -4762,19 +4793,14 @@ SBase::readAnnotation (XMLInputStream& stream)
* if it not allowed
*/
term->setHasBeenModifiedFlag();
term->setCapturedInStoredAnnotation(!validNestedTerms);
}
}

if (hasNestedTerms == true)
if (hasNestedTerms == true && validNestedTerms == false)
{
unsigned int version = getVersion();
if (level < 2 ||
(level == 2 && version < 5) ||
(level == 3) )
{
logError(NestedAnnotationNotAllowed, level, version,
"The nested annotation has been stored but will not be written out.");
}
logError(NestedAnnotationNotAllowed, level, version,
"The nested annotation has been stored but not saved as a CVTerm.");
}

}
Expand Down Expand Up @@ -5905,7 +5931,8 @@ SBase::syncAnnotation ()
{
for (unsigned int i = 0; i < getNumCVTerms(); i++)
{
if (getCVTerm(i)->hasBeenModified() == true)
if (getCVTerm(i)->hasBeenModified() == true &&
getCVTerm(i)->getCapturedInStoredAnnotation() == false)
{
mCVTermsChanged = true;
break;
Expand Down
16 changes: 16 additions & 0 deletions src/sbml/annotation/CVTerm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ LIBSBML_CPP_NAMESPACE_BEGIN
CVTerm::CVTerm(QualifierType_t type) :
mHasBeenModified (false)
, mNestedCVTerms (NULL)
, mCapturedInStoredAnnotation (false)

{
mResources = new XMLAttributes();
Expand All @@ -87,6 +88,7 @@ CVTerm::CVTerm(QualifierType_t type) :
CVTerm::CVTerm(const XMLNode node) :
mHasBeenModified (false)
, mNestedCVTerms(NULL)
, mCapturedInStoredAnnotation(false)
{
const string& name = node.getName();
const string& prefix = node.getPrefix();
Expand Down Expand Up @@ -170,6 +172,7 @@ CVTerm::CVTerm(const CVTerm& orig)
mBiolQualifier = orig.mBiolQualifier;
mResources = new XMLAttributes(*orig.mResources);
mHasBeenModified = orig.mHasBeenModified;
mCapturedInStoredAnnotation = orig.mCapturedInStoredAnnotation;

if(orig.mNestedCVTerms != NULL)
{
Expand Down Expand Up @@ -204,6 +207,7 @@ CVTerm::operator=(const CVTerm& rhs)
mResources=new XMLAttributes(*rhs.mResources);

mHasBeenModified = rhs.mHasBeenModified;
mCapturedInStoredAnnotation = rhs.mCapturedInStoredAnnotation;

if(this->mNestedCVTerms != NULL)
{
Expand Down Expand Up @@ -581,6 +585,18 @@ CVTerm::setHasBeenModifiedFlag()
mHasBeenModified = true;
}

void
CVTerm::setCapturedInStoredAnnotation(bool captured)
{
mCapturedInStoredAnnotation = captured;
}

bool
CVTerm::getCapturedInStoredAnnotation()
{
return mCapturedInStoredAnnotation;
}

unsigned int
CVTerm::getNumNestedCVTerms() const
{
Expand Down
9 changes: 9 additions & 0 deletions src/sbml/annotation/CVTerm.h
Original file line number Diff line number Diff line change
Expand Up @@ -1279,7 +1279,16 @@ class LIBSBML_EXTERN CVTerm

void setHasBeenModifiedFlag();

// when technically invalid nested cvterms have been
// used people have complained about getting two description tags
// marking this true avoids writing out twice
bool mCapturedInStoredAnnotation;

void setCapturedInStoredAnnotation(bool captured);
bool getCapturedInStoredAnnotation();

friend class SBase;
friend class RDFAnnotationParser;
/** @endcond */
};

Expand Down
48 changes: 39 additions & 9 deletions src/sbml/annotation/RDFAnnotationParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -516,20 +516,35 @@ RDFAnnotationParser::parseCVTerms(const SBase * object)
return NULL;
}

unsigned int n = 0;
// see how many cvterms are captured in an additional
// rdf annotation that will also be output as it contains
// nested terms that will not be output
// this avoids 2 descr elements
for (unsigned int i = 0; i < object->getNumCVTerms(); i++)
{
if (static_cast <CVTerm *>(object->getCVTerms()->get(i))->getCapturedInStoredAnnotation())
{
n++;
}
}
if (n != object->getNumCVTerms())
{
XMLNode *CVTerms = createRDFDescriptionWithCVTerms(object);

XMLNode *CVTerms = createRDFDescriptionWithCVTerms(object);

XMLNode * RDF = createRDFAnnotation(object->getLevel(), object->getVersion());
RDF->addChild(*CVTerms);
XMLNode * RDF = createRDFAnnotation(object->getLevel(), object->getVersion());
RDF->addChild(*CVTerms);

delete CVTerms;
delete CVTerms;

XMLNode *ann = createAnnotation();
ann->addChild(*RDF);
XMLNode *ann = createAnnotation();
ann->addChild(*RDF);

delete RDF;
delete RDF;

return ann;
return ann;
}
return NULL;
}


Expand All @@ -544,6 +559,21 @@ RDFAnnotationParser::createRDFDescriptionWithCVTerms(const SBase * object)
{
return NULL;
}
else
{
unsigned int n = 0;
for (unsigned int i = 0; i < object->getNumCVTerms(); i++)
{
if (static_cast <CVTerm *>(object->getCVTerms()->get(i))->getCapturedInStoredAnnotation())
{
n++;
}
}
if (n == object->getNumCVTerms())
{
return NULL;
}
}

XMLNode *description = createRDFDescription(object);

Expand Down
Loading

0 comments on commit 3b1758a

Please sign in to comment.