in core/indigo-core/molecule/src/molecule_json_saver.cpp [683:1070]
void MoleculeJsonSaver::saveAtoms(BaseMolecule& mol, JsonWriter& writer)
{
QS_DEF(Array<char>, buf);
ArrayOutput out(buf);
for (auto i : mol.vertices())
{
buf.clear();
int anum = mol.getAtomNumber(i);
int isotope = mol.getAtomIsotope(i);
writer.StartObject();
if (mol.attachmentPointCount())
saveAttachmentPoint(mol, i, writer);
QS_DEF(Array<int>, rg_list);
int radical = 0;
int query_atom_type = QueryMolecule::QUERY_ATOM_UNKNOWN;
bool needCustomQuery = false;
std::map<int, std::unique_ptr<QueryMolecule::Atom>> query_atom_properties;
bool is_rSite = mol.isRSite(i);
if (is_rSite)
{
mol.getAllowedRGroups(i, rg_list);
writer.Key("type");
writer.String("rg-label");
writer.Key("$refs");
writer.StartArray();
for (int j = 0; j < rg_list.size(); ++j)
{
buf.clear();
out.printf("rg-%d", rg_list[j]);
buf.push(0);
writer.String(buf.ptr());
}
writer.EndArray();
}
else
{
bool is_qatom_list = false;
std::vector<std::unique_ptr<QueryMolecule::Atom>> atoms;
if (_pqmol)
{
query_atom_type = QueryMolecule::parseQueryAtomSmarts(*_pqmol, i, atoms, query_atom_properties);
needCustomQuery = query_atom_type == QueryMolecule::QUERY_ATOM_UNKNOWN;
if (query_atom_properties.count(QueryMolecule::ATOM_CHIRALITY) &&
(query_atom_properties[QueryMolecule::ATOM_CHIRALITY]->value_min != QueryMolecule::CHIRALITY_GENERAL ||
query_atom_properties[QueryMolecule::ATOM_CHIRALITY]->value_max & QueryMolecule::CHIRALITY_OR_UNSPECIFIED))
needCustomQuery = true;
}
if (mol.isPseudoAtom(i))
{
buf.readString(mol.getPseudoAtom(i), true);
}
else if (mol.isTemplateAtom(i))
{
buf.readString(mol.getTemplateAtom(i), true);
}
else if (anum != VALUE_UNKNOWN)
{
buf.readString(Element::toString(anum, isotope), true);
radical = mol.getAtomRadical(i);
}
else if (_pqmol)
{
if (query_atom_type != QueryMolecule::QUERY_ATOM_UNKNOWN)
{
if (query_atom_type == QueryMolecule::QUERY_ATOM_LIST || query_atom_type == QueryMolecule::QUERY_ATOM_NOTLIST)
{
is_qatom_list = true;
writer.Key("type");
writer.String("atom-list");
if (query_atom_type == QueryMolecule::QUERY_ATOM_NOTLIST)
{
writer.Key("notList");
writer.Bool(true);
}
writer.Key("elements");
writer.StartArray();
for (auto& atom : atoms)
if (atom->type == QueryMolecule::ATOM_NUMBER)
writer.String(Element::toString(atom->value_max));
else if (atom->type == QueryMolecule::ATOM_PSEUDO)
writer.String(atom->alias.ptr());
else
throw Error("Wrong atom type %d", atom->type);
writer.EndArray();
}
else if (query_atom_type == QueryMolecule::QUERY_ATOM_SINGLE)
{
anum = (*atoms.begin()).get()->value_max;
buf.readString(Element::toString(anum), true);
if (anum == ELEM_H && query_atom_properties.count(QueryMolecule::ATOM_ISOTOPE) > 0)
{
int h_isotope = query_atom_properties[QueryMolecule::ATOM_ISOTOPE]->value_min;
if (h_isotope == DEUTERIUM)
{
buf.clear();
buf.appendString("D", true);
}
else if (h_isotope == TRITIUM)
{
buf.clear();
buf.appendString("T", true);
}
}
}
else
QueryMolecule::getQueryAtomLabel(query_atom_type, buf);
}
else // query_atom_type == QueryMolecule::QUERY_ATOM_UNKNOWN
{
needCustomQuery = true;
}
}
if (needCustomQuery)
{
writer.Key("label");
writer.String("A"); // Set label any atom
}
else if (!is_qatom_list)
{
writer.Key("label");
writer.String(buf.ptr());
}
if (mol.isAlias(i))
{
writer.Key("alias");
writer.String(mol.getAlias(i));
}
}
if (mol.isAtomSelected(i))
{
writer.Key("selected");
writer.Bool(true);
}
Vec3f coord = mol.getAtomXyz(i);
writer.Key("location");
writer.StartArray();
writeFloat(writer, coord.x);
writeFloat(writer, coord.y);
writeFloat(writer, coord.z);
writer.EndArray();
int charge = mol.getAtomCharge(i);
int evalence = mol.getExplicitValence(i);
int mapping = mol.reaction_atom_mapping[i];
int inv_ret = mol.reaction_atom_inversion[i];
bool ecflag = mol.reaction_atom_exact_change[i];
int hcount = MoleculeSavers::getHCount(mol, i, anum, charge);
if (_pqmol && !is_rSite) // No custom query for RSite
{
std::map<int, const char*> qprops{{QueryMolecule::ATOM_SSSR_RINGS, "ringMembership"},
{QueryMolecule::ATOM_SMALLEST_RING_SIZE, "ringSize"},
{QueryMolecule::ATOM_CONNECTIVITY, "connectivity"}};
bool hasQueryProperties =
query_atom_properties.count(QueryMolecule::ATOM_AROMATICITY) > 0 || query_atom_properties.count(QueryMolecule::ATOM_CHIRALITY) > 0 ||
std::any_of(qprops.cbegin(), qprops.cend(), [&query_atom_properties](auto p) { return query_atom_properties.count(p.first) > 0; });
if (needCustomQuery || hasQueryProperties)
{
writer.Key("queryProperties");
writer.StartObject();
if (needCustomQuery)
{
QueryMolecule::Atom& atom = _pqmol->getAtom(i);
std::string customQuery = QueryMolecule::getSmartsAtomStr(&atom, _pqmol->original_format, false);
writer.Key("customQuery");
writer.String(customQuery.c_str());
}
else
{
int value = VALUE_UNKNOWN;
if (query_atom_properties.count(QueryMolecule::ATOM_AROMATICITY))
{
value = query_atom_properties[QueryMolecule::ATOM_AROMATICITY]->value_min;
writer.Key("aromaticity");
if (value == ATOM_AROMATIC)
writer.String(ATOM_AROMATIC_STR);
else if (value == ATOM_ALIPHATIC)
writer.String(ATOM_ALIPHATIC_STR);
else
throw "Wrong aromaticity value";
}
if (query_atom_properties.count(QueryMolecule::ATOM_CHIRALITY))
{
// This is CHIRALITY_GENERAL without CHIRALITY_OR_UNSPECIFIED
writer.Key("chirality");
value = query_atom_properties[QueryMolecule::ATOM_CHIRALITY]->value_max;
if (value == QueryMolecule::CHIRALITY_CLOCKWISE)
writer.String("clockwise");
else if (value == QueryMolecule::CHIRALITY_ANTICLOCKWISE)
writer.String("anticlockwise");
else
throw Error("Wrong chirality value %d", value);
}
for (auto p : qprops)
{
if (query_atom_properties.count(p.first) > 0)
{
writer.Key(p.second);
writer.Uint(query_atom_properties[p.first]->value_min);
}
}
// 2do add hirality
//*/
}
writer.EndObject();
}
int subst = 0, rbc = 0;
if (MoleculeSavers::getRingBondCountFlagValue(*_pqmol, i, rbc))
{
writer.Key("ringBondCount");
writer.Int(rbc);
}
if (MoleculeSavers::getSubstitutionCountFlagValue(*_pqmol, i, subst))
{
writer.Key("substitutionCount");
writer.Int(subst);
}
int unsat = 0;
if (_pqmol->getAtom(i).sureValue(QueryMolecule::ATOM_UNSATURATION, unsat))
{
writer.Key("unsaturatedAtom");
writer.Bool(true);
}
if (hcount == VALUE_UNKNOWN)
hcount = 0;
else
hcount++;
if (hcount > 0)
{
writer.Key("hCount");
writer.Int(hcount);
}
if (query_atom_type >= 0 && query_atom_properties.count(QueryMolecule::ATOM_IMPLICIT_H) > 0)
{
writer.Key("implicitHCount");
writer.Int(query_atom_properties[QueryMolecule::ATOM_IMPLICIT_H]->value_min);
}
}
else if (_pmol)
{
if (Molecule::shouldWriteHCount(mol.asMolecule(), i) && hcount > 0)
{
writer.Key("implicitHCount");
writer.Int(hcount);
}
}
if (mapping)
{
writer.Key("mapping");
writer.Int(mapping);
}
if ((mol.isQueryMolecule() && charge != CHARGE_UNKNOWN) || (!mol.isQueryMolecule() && charge != 0))
{
writer.Key("charge");
writer.Int(charge);
}
// int total_bond_count = 0;
if (evalence > 0)
{
writer.Key("explicitValence");
writer.Int(evalence);
}
if (radical > 0)
{
writer.Key("radical");
writer.Int(radical);
}
if (isotope > 0 && anum != ELEM_H)
{
writer.Key("isotope");
writer.Int(isotope);
}
if (inv_ret > 0)
{
writer.Key("invRet");
writer.Int(inv_ret);
}
if (ecflag)
{
writer.Key("exactChangeFlag");
writer.Bool(ecflag);
}
int enh_stereo_type = mol.stereocenters.getType(i);
if (enh_stereo_type > 1)
{
writer.Key("stereoLabel");
switch (enh_stereo_type)
{
case MoleculeStereocenters::ATOM_ABS:
writer.String("abs");
break;
case MoleculeStereocenters::ATOM_OR:
writer.String((std::string("or") + std::to_string(mol.stereocenters.getGroup(i))).c_str());
break;
case MoleculeStereocenters::ATOM_AND:
writer.String((std::string("&") + std::to_string(mol.stereocenters.getGroup(i))).c_str());
break;
default:
throw Error("Unknows enhanced stereo type %d", enh_stereo_type);
break;
}
}
auto cip = mol.getAtomCIP(i);
if (cip != CIPDesc::NONE)
{
auto cip_str = CIPToString(cip);
if (cip_str.size())
{
writer.Key("cip");
writer.String(cip_str.c_str());
}
}
if (mol.isRSite(i) && !_checkAttPointOrder(mol, i))
{
const Vertex& vertex = mol.getVertex(i);
writer.Key("attachmentOrder");
writer.StartArray();
for (int k = 0; k < vertex.degree(); k++)
{
writer.StartObject();
writer.Key("attachmentAtom");
writer.Int(mol.getRSiteAttachmentPointByOrder(i, k));
writer.Key("attachmentId");
writer.Int(k);
writer.EndObject();
}
writer.EndArray();
}
if (mol.isTemplateAtom(i))
{
auto pclass = mol.getTemplateAtomClass(i);
if (pclass && strlen(pclass))
{
writer.Key("class");
writer.String(pclass);
}
auto seqid = mol.getTemplateAtomSeqid(i);
if (seqid != VALUE_UNKNOWN)
{
writer.Key("seqid");
writer.Int(seqid);
}
if (mol.template_attachment_points.size())
{
if (mol.getTemplateAtomAttachmentPointsCount(i))
{
writer.Key("attOrder");
writer.StartArray();
for (int j = mol.template_attachment_points.begin(); j != mol.template_attachment_points.end(); j = mol.template_attachment_points.next(j))
{
BaseMolecule::TemplateAttPoint& ap = mol.template_attachment_points.at(j);
if (ap.ap_occur_idx == i)
{
writer.StartObject();
writer.Key("index");
writer.Int(ap.ap_aidx);
writer.Key("id");
writer.String(ap.ap_id.ptr());
writer.EndObject();
}
}
writer.EndArray();
}
}
}
writer.EndObject();
}
}