in core/indigo-core/molecule/src/cml_saver.cpp [96:574]
void CmlSaver::_addMoleculeElement(XMLElement* elem, BaseMolecule& mol, bool query)
{
int i;
BaseMolecule* _mol = &mol;
QueryMolecule* qmol = 0;
if (query)
qmol = (QueryMolecule*)(&mol);
XMLElement* molecule = _doc->NewElement("molecule");
if (elem == 0)
_doc->LinkEndChild(molecule);
else
elem->LinkEndChild(molecule);
if (_mol->name.ptr() != 0)
{
if (strchr(_mol->name.ptr(), '\"') != NULL)
throw Error("can not save molecule with '\"' in title");
molecule->SetAttribute("title", _mol->name.ptr());
}
bool have_xyz = BaseMolecule::hasCoord(*_mol);
bool have_z = BaseMolecule::hasZCoord(*_mol);
if (_mol->vertexCount() > 0)
{
XMLElement* atomarray = _doc->NewElement("atomArray");
molecule->LinkEndChild(atomarray);
for (i = _mol->vertexBegin(); i != _mol->vertexEnd(); i = _mol->vertexNext(i))
{
int atom_number = _mol->getAtomNumber(i);
const char* atom_str = nullptr;
if (_mol->isRSite(i))
atom_str = "R";
else if (_mol->isPseudoAtom(i))
atom_str = _mol->getPseudoAtom(i);
// else if (_mol->isTemplateAtom(i))
// atom_str = _mol->getTemplateAtom(i);
else if (atom_number > 0)
atom_str = Element::toString(atom_number);
else if (qmol != 0)
{
QS_DEF(Array<int>, list);
int query_atom_type;
if ((query_atom_type = QueryMolecule::parseQueryAtom(*qmol, i, list)) != -1)
{
if (query_atom_type == QueryMolecule::QUERY_ATOM_LIST || query_atom_type == QueryMolecule::QUERY_ATOM_NOTLIST)
{
atom_str = Element::toString(list[0]);
}
if (QueryMolecule::queryAtomIsSpecial(*qmol, i) || query_atom_type == QueryMolecule::QUERY_ATOM_A)
{
atom_str = Element::toString(ELEM_C);
}
}
}
if (atom_str == nullptr)
{
throw Error("internal error: atom element was not set");
}
XMLElement* atom = _doc->NewElement("atom");
atomarray->LinkEndChild(atom);
QS_DEF(Array<char>, buf);
ArrayOutput out(buf);
out.printf("a%d", i);
buf.push(0);
atom->SetAttribute("id", buf.ptr());
atom->SetAttribute("elementType", atom_str);
if (qmol != 0)
{
QS_DEF(Array<int>, list);
int query_atom_type;
if ((query_atom_type = QueryMolecule::parseQueryAtom(*qmol, i, list)) != -1)
{
if (query_atom_type == QueryMolecule::QUERY_ATOM_AH)
atom->SetAttribute("mrvPseudo", "AH");
else if (query_atom_type == QueryMolecule::QUERY_ATOM_QH)
atom->SetAttribute("mrvPseudo", "QH");
else if (query_atom_type == QueryMolecule::QUERY_ATOM_X)
atom->SetAttribute("mrvPseudo", "X");
else if (query_atom_type == QueryMolecule::QUERY_ATOM_XH)
atom->SetAttribute("mrvPseudo", "XH");
else if (query_atom_type == QueryMolecule::QUERY_ATOM_M)
atom->SetAttribute("mrvPseudo", "M");
else if (query_atom_type == QueryMolecule::QUERY_ATOM_MH)
atom->SetAttribute("mrvPseudo", "MH");
}
}
if (_mol->getAtomIsotope(i) > 0)
{
atom->SetAttribute("isotope", _mol->getAtomIsotope(i));
// for inchi-1 program which ignores "isotope" property (version 1.03)
atom->SetAttribute("isotopeNumber", _mol->getAtomIsotope(i));
}
int charge = _mol->getAtomCharge(i);
if ((mol.isQueryMolecule() && charge != CHARGE_UNKNOWN) || (!mol.isQueryMolecule() && charge != 0))
atom->SetAttribute("formalCharge", _mol->getAtomCharge(i));
if (!_mol->isRSite(i) && !_mol->isPseudoAtom(i))
{
if (_mol->getAtomRadical_NoThrow(i, 0) > 0)
{
atom->SetAttribute("spinMultiplicity", _mol->getAtomRadical(i));
if (_mol->getAtomRadical_NoThrow(i, 0) == 1)
atom->SetAttribute("radical", "divalent1");
else if (_mol->getAtomRadical_NoThrow(i, 0) == 2)
atom->SetAttribute("radical", "monovalent");
else if (_mol->getAtomRadical_NoThrow(i, 0) == 3)
atom->SetAttribute("radical", "divalent3");
}
if (_mol->getExplicitValence(i) > 0)
atom->SetAttribute("mrvValence", _mol->getExplicitValence(i));
if (qmol == 0)
{
if (Molecule::shouldWriteHCount(mol.asMolecule(), i))
{
int hcount;
try
{
hcount = _mol->getAtomTotalH(i);
}
catch (Exception&)
{
hcount = -1;
}
if (hcount >= 0)
atom->SetAttribute("hydrogenCount", hcount);
}
}
if (mol.isAlias(i))
{
atom->SetAttribute("mrvAlias", mol.getAlias(i));
}
}
if (_mol->isRSite(i))
{
QS_DEF(Array<int>, rg_refs);
_mol->getAllowedRGroups(i, rg_refs);
QS_DEF(Array<char>, rbuf);
ArrayOutput rout(rbuf);
if (rg_refs.size() == 1)
{
rout.printf("%d", rg_refs[0]);
rbuf.push(0);
atom->SetAttribute("rgroupRef", rbuf.ptr());
}
}
if (qmol != 0)
{
QS_DEF(Array<char>, qbuf);
ArrayOutput qout(qbuf);
QS_DEF(Array<int>, list);
int query_atom_type;
if ((query_atom_type = QueryMolecule::parseQueryAtom(*qmol, i, list)) != -1)
{
if (query_atom_type == QueryMolecule::QUERY_ATOM_LIST || query_atom_type == QueryMolecule::QUERY_ATOM_NOTLIST)
{
int k;
qout.writeString("L");
for (k = 0; k < list.size(); k++)
{
if (query_atom_type == QueryMolecule::QUERY_ATOM_NOTLIST)
qout.writeString("!");
else
qout.writeString(",");
qout.writeString(Element::toString(list[k]));
}
qout.writeString(": ");
}
else if (query_atom_type == QueryMolecule::QUERY_ATOM_A)
qout.writeString("A");
else if (query_atom_type == QueryMolecule::QUERY_ATOM_Q)
qout.writeString("Q");
}
int rbc;
if (_getRingBondCountFlagValue(*qmol, i, rbc))
{
if (rbc > 0)
qout.printf("rb%d;", rbc);
else if (rbc == -2)
qout.printf("rb*;");
else if (rbc == -1)
qout.printf("rb0;");
}
int subst;
if (_getSubstitutionCountFlagValue(*qmol, i, subst))
{
if (subst > 0)
qout.printf("s%d;", subst);
else if (subst == -2)
qout.printf("s*;");
else if (subst == -1)
qout.printf("s0;");
}
int unsat;
if (qmol->getAtom(i).sureValue(QueryMolecule::ATOM_UNSATURATION, unsat))
qout.printf("u1;");
int hcount = MoleculeSavers::getHCount(mol, i, atom_number, charge);
if (hcount > 0)
qout.printf("H%d", hcount);
else if (hcount == 0)
qout.printf("H0");
if (qbuf.size() > 0)
{
qbuf.push(0);
atom->SetAttribute("mrvQueryProps", qbuf.ptr());
}
}
if (_mol->attachmentPointCount() > 0)
{
int val = 0;
for (int idx = 1; idx <= _mol->attachmentPointCount(); idx++)
{
for (int j = 0; _mol->getAttachmentPoint(idx, j) != -1; j++)
{
if (_mol->getAttachmentPoint(idx, j) == i)
{
val |= 1 << (idx - 1);
break;
}
}
}
if (val > 0)
{
if (val == 3)
atom->SetAttribute("attachmentPoint", "both");
else
atom->SetAttribute("attachmentPoint", val);
}
}
if (have_xyz)
{
Vec3f& pos = _mol->getAtomXyz(i);
if (have_z)
{
if (fabsf(pos.x) < FLT_MIN)
pos.x = 0.f;
if (fabsf(pos.y) < FLT_MIN)
pos.y = 0.f;
if (fabsf(pos.z) < FLT_MIN)
pos.z = 0.f;
atom->SetAttribute("x3", pos.x);
atom->SetAttribute("y3", pos.y);
atom->SetAttribute("z3", pos.z);
}
else
{
if (fabsf(pos.x) < FLT_MIN)
pos.x = 0.f;
if (fabsf(pos.y) < FLT_MIN)
pos.y = 0.f;
atom->SetAttribute("x2", pos.x);
atom->SetAttribute("y2", pos.y);
}
}
if (_mol->stereocenters.getType(i) > MoleculeStereocenters::ATOM_ANY && _mol->stereocenters.isTetrahydral(i))
{
XMLElement* atomparity = _doc->NewElement("atomParity");
atom->LinkEndChild(atomparity);
QS_DEF(Array<char>, sbuf);
ArrayOutput sout(sbuf);
const int* pyramid = _mol->stereocenters.getPyramid(i);
if (pyramid[2] == -1)
{
// The atomRefs4 attribute in the atomParity element specifies
// the four atoms involved in defining the stereochemistry.
// These atoms are typically ordered in a sequence
// that represents a directed path from the stereocenter to the fourth atom
// with the reference atom (the atom to which the stereochemistry is referenced) being included twice.
const Vertex& v = _mol->getVertex(i);
int j = v.neiVertex(i);
sout.printf("a%d a%d a%d a%d", pyramid[0], pyramid[1], i, j);
}
else if (pyramid[3] == -1)
sout.printf("a%d a%d a%d a%d", pyramid[0], pyramid[1], pyramid[2], i);
else
sout.printf("a%d a%d a%d a%d", pyramid[0], pyramid[1], pyramid[2], pyramid[3]);
sbuf.push(0);
atomparity->SetAttribute("atomRefs4", sbuf.ptr());
atomparity->LinkEndChild(_doc->NewText("1"));
}
if (_mol->reaction_atom_mapping[i] > 0)
{
atom->SetAttribute("mrvMap", _mol->reaction_atom_mapping[i]);
}
if (_mol->reaction_atom_inversion[i] > 0)
{
if (_mol->reaction_atom_inversion[i] == 1)
atom->SetAttribute("reactionStereo", "Inv");
else if (_mol->reaction_atom_inversion[i] == 2)
atom->SetAttribute("reactionStereo", "Ret");
}
if (_mol->reaction_atom_exact_change[i] > 0)
{
atom->SetAttribute("exactChange", "1");
}
}
int latest_ind = i;
if (_mol->attachmentPointCount() > 0)
{
for (i = _mol->vertexBegin(); i != _mol->vertexEnd(); i = _mol->vertexNext(i))
{
int val = 0;
for (int idx = 1; idx <= _mol->attachmentPointCount(); idx++)
{
for (int j = 0; _mol->getAttachmentPoint(idx, j) != -1; j++)
{
if (_mol->getAttachmentPoint(idx, j) == i)
{
val |= 1 << (idx - 1);
break;
}
}
}
if (val > 0)
{
XMLElement* atom = _doc->NewElement("atom");
atomarray->LinkEndChild(atom);
QS_DEF(Array<char>, buf);
ArrayOutput out(buf);
out.printf("a%d", latest_ind++);
buf.push(0);
atom->SetAttribute("id", buf.ptr());
atom->SetAttribute("elementType", "*");
}
}
}
}
if (_mol->edgeCount() > 0)
{
XMLElement* bondarray = _doc->NewElement("bondArray");
molecule->LinkEndChild(bondarray);
for (i = _mol->edgeBegin(); i != _mol->edgeEnd(); i = _mol->edgeNext(i))
{
const Edge& edge = _mol->getEdge(i);
XMLElement* bond = _doc->NewElement("bond");
bondarray->LinkEndChild(bond);
QS_DEF(Array<char>, buf);
ArrayOutput out(buf);
out.printf("a%d a%d", edge.beg, edge.end);
buf.push(0);
bond->SetAttribute("atomRefs2", buf.ptr());
int order = _mol->getBondOrder(i);
if (order == BOND_SINGLE || order == BOND_DOUBLE || order == BOND_TRIPLE)
bond->SetAttribute("order", order);
else if (order == BOND_AROMATIC)
bond->SetAttribute("order", "A");
if (qmol != 0)
{
if (order < 0)
bond->SetAttribute("order", 1);
int qb = QueryMolecule::getQueryBondType(qmol->getBond(i));
if (qb == _BOND_SINGLE_OR_DOUBLE)
bond->SetAttribute("queryType", "SD");
else if (qb == _BOND_SINGLE_OR_AROMATIC)
bond->SetAttribute("queryType", "SA");
else if (qb == _BOND_DOUBLE_OR_AROMATIC)
bond->SetAttribute("queryType", "DA");
else if (qb == _BOND_ANY)
bond->SetAttribute("queryType", "Any");
int indigo_topology = -1;
if (qmol != 0)
qmol->getBond(i).sureValue(QueryMolecule::BOND_TOPOLOGY, indigo_topology);
if (indigo_topology == TOPOLOGY_RING)
bond->SetAttribute("topology", "ring");
else if (indigo_topology == TOPOLOGY_CHAIN)
bond->SetAttribute("topology", "chain");
}
int dir = _mol->getBondDirection(i);
int parity = _mol->cis_trans.getParity(i);
if (dir == BOND_UP || dir == BOND_DOWN)
{
XMLElement* bondstereo = _doc->NewElement("bondStereo");
bond->LinkEndChild(bondstereo);
bondstereo->LinkEndChild(_doc->NewText((dir == BOND_UP) ? "W" : "H"));
}
else if (parity != 0)
{
XMLElement* bondstereo = _doc->NewElement("bondStereo");
bond->LinkEndChild(bondstereo);
QS_DEF(Array<char>, pbuf);
ArrayOutput pout(pbuf);
const int* subst = _mol->cis_trans.getSubstituents(i);
pout.printf("a%d a%d a%d a%d", subst[0], edge.beg, edge.end, subst[2]);
pbuf.push(0);
bondstereo->SetAttribute("atomRefs4", pbuf.ptr());
bondstereo->LinkEndChild(_doc->NewText((parity == MoleculeCisTrans::CIS) ? "C" : "T"));
}
else if (_mol->cis_trans.isIgnored(i))
{
XMLElement* bondstereo = _doc->NewElement("bondStereo");
bond->LinkEndChild(bondstereo);
bondstereo->SetAttribute("convention", "MDL");
bondstereo->SetAttribute("conventionValue", "3");
}
if (_mol->reaction_bond_reacting_center[i] != 0)
{
bond->SetAttribute("mrvReactingCenter", _mol->reaction_bond_reacting_center[i]);
}
}
}
if (_mol->countSGroups() > 0)
{
for (i = _mol->sgroups.begin(); i != _mol->sgroups.end(); i = _mol->sgroups.next(i))
{
SGroup& sgroup = _mol->sgroups.getSGroup(i);
if (sgroup.parent_group == 0)
_addSgroupElement(molecule, *_mol, sgroup);
}
}
}