void CmlSaver::_addMoleculeElement()

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);
        }
    }
}