Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue 296 setannotation #305

Merged
merged 14 commits into from
Mar 22, 2023
Merged
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