in core/indigo-core/molecule/src/molfile_loader.cpp [2365:3142]
void MolfileLoader::_readCtab3000()
{
QS_DEF(Array<char>, str);
_scanner.readLine(str, true);
if (strncmp(str.ptr(), "M V30 BEGIN CTAB", 17) != 0)
throw Error("error reading CTAB block header");
str.clear_resize(14);
_scanner.read(14, str.ptr());
if (strncmp(str.ptr(), "M V30 COUNTS ", 14) != 0)
throw Error("error reading COUNTS line");
int i, nsgroups, n3d, chiral_int;
_scanner.readLine(str, true);
if (sscanf(str.ptr(), "%d %d %d %d %d", &_atoms_num, &_bonds_num, &nsgroups, &n3d, &chiral_int) < 5)
throw Error("error parsing COUNTS line");
_chiral = (chiral_int != 0);
if (ignore_no_chiral_flag)
_chiral = true;
_init();
bool atom_block_exists = true;
bool bond_block_exists = true;
_scanner.readLine(str, true);
if (strncmp(str.ptr(), "M V30 BEGIN ATOM", 14) != 0)
{
if (_atoms_num > 0)
throw Error("Error reading ATOM block header");
atom_block_exists = false;
}
else
{
for (i = 0; i < _atoms_num; i++)
{
_readMultiString(str);
BufferScanner strscan(str.ptr());
int& atom_type = _atom_types.push();
_hcount.push(0);
atom_type = _ATOM_ELEMENT;
int isotope = 0;
int label = 0;
std::unique_ptr<QueryMolecule::Atom> query_atom;
strscan.readInt1(); // atom index -- ignored
QS_DEF(Array<char>, buf);
strscan.readWord(buf, " [");
char stopchar = strscan.readChar();
if (stopchar == '[')
{
if (_qmol == 0)
throw Error("atom list is allowed only for queries");
if (buf[0] == 0)
atom_type = _ATOM_LIST;
else if (strcmp(buf.ptr(), "NOT") == 0)
atom_type = _ATOM_NOTLIST;
else
throw Error("bad word: %s", buf.ptr());
bool was_a = false, was_q = false, was_x = false, was_m = false;
while (1)
{
strscan.readWord(buf, ",]");
stopchar = strscan.readChar();
if (was_a)
throw Error("'A' inside atom list, if present, must be single");
if (was_q)
throw Error("'Q' inside atom list, if present, must be single");
if (was_x)
throw Error("'X' inside atom list, if present, must be single");
if (was_m)
throw Error("'M' inside atom list, if present, must be single");
if (buf.size() == 2 && buf[0] == 'A')
{
was_a = true;
atom_type = _ATOM_A;
}
else if (buf.size() == 3 && buf[0] == 'A' && buf[1] == 'H')
{
was_a = true;
atom_type = _ATOM_AH;
}
else if (buf.size() == 2 && buf[0] == 'Q')
{
was_q = true;
atom_type = _ATOM_Q;
}
else if (buf.size() == 3 && buf[0] == 'Q' && buf[1] == 'H')
{
was_a = true;
atom_type = _ATOM_QH;
}
else if (buf.size() == 2 && buf[0] == 'X')
{
was_q = true;
atom_type = _ATOM_X;
}
else if (buf.size() == 3 && buf[0] == 'X' && buf[1] == 'H')
{
was_a = true;
atom_type = _ATOM_XH;
}
else if (buf.size() == 2 && buf[0] == 'M')
{
was_q = true;
atom_type = _ATOM_M;
}
else if (buf.size() == 3 && buf[0] == 'M' && buf[1] == 'H')
{
was_a = true;
atom_type = _ATOM_MH;
}
else
{
_appendQueryAtom(buf.ptr(), query_atom);
}
if (stopchar == ']')
break;
}
}
else
{
label = Element::fromString2(buf.ptr());
long long cur_pos = strscan.tell();
QS_DEF(ReusableObjArray<Array<char>>, strs);
strs.clear();
strs.push().readString("CLASS", false);
strs.push().readString("SEQID", false);
auto fw_res = strscan.findWord(strs);
strscan.seek(cur_pos, SEEK_SET);
if (fw_res != -1)
atom_type = _ATOM_TEMPLATE;
else if (buf.size() == 2 && buf[0] == 'D')
{
label = ELEM_H;
isotope = 2;
}
else if (buf.size() == 2 && buf[0] == 'T')
{
label = ELEM_H;
isotope = 3;
}
else if (buf.size() == 2 && buf[0] == 'Q')
{
if (_qmol == 0)
throw Error("'Q' atom is allowed only for queries");
atom_type = _ATOM_Q;
}
else if (buf.size() == 3 && buf[0] == 'Q' && buf[1] == 'H')
{
if (_qmol == 0)
throw Error("'QH' atom is allowed only for queries");
atom_type = _ATOM_QH;
}
else if (buf.size() == 2 && buf[0] == 'A')
{
if (_qmol == 0)
throw Error("'A' atom is allowed only for queries");
atom_type = _ATOM_A;
}
else if (buf.size() == 3 && buf[0] == 'A' && buf[1] == 'H')
{
if (_qmol == 0)
throw Error("'AH' atom is allowed only for queries");
atom_type = _ATOM_AH;
}
else if (buf.size() == 2 && buf[0] == 'X' && !treat_x_as_pseudoatom)
{
if (_qmol == 0)
throw Error("'X' atom is allowed only for queries");
atom_type = _ATOM_X;
}
else if (buf.size() == 3 && buf[0] == 'X' && buf[1] == 'H' && !treat_x_as_pseudoatom)
{
if (_qmol == 0)
throw Error("'XH' atom is allowed only for queries");
atom_type = _ATOM_XH;
}
else if (buf.size() == 2 && buf[0] == 'M')
{
if (_qmol == 0)
throw Error("'M' atom is allowed only for queries");
atom_type = _ATOM_M;
}
else if (buf.size() == 3 && buf[0] == 'M' && buf[1] == 'H')
{
if (_qmol == 0)
throw Error("'MH' atom is allowed only for queries");
atom_type = _ATOM_MH;
}
else if (buf.size() == 3 && buf[0] == 'R' && buf[1] == '#')
{
atom_type = _ATOM_R;
label = ELEM_RSITE;
}
else if (label == -1)
atom_type = _ATOM_PSEUDO;
}
strscan.skipSpace();
float x = strscan.readFloat();
strscan.skipSpace();
float y = strscan.readFloat();
strscan.skipSpace();
float z = strscan.readFloat();
strscan.skipSpace();
int aamap = strscan.readInt1();
if (_mol != 0)
{
if (atom_type == _ATOM_TEMPLATE)
{
_preparePseudoAtomLabel(buf);
_mol->addTemplateAtom(buf.ptr());
}
else
{
_mol->addAtom(label);
if (atom_type == _ATOM_PSEUDO)
{
_preparePseudoAtomLabel(buf);
_mol->setPseudoAtom(i, buf.ptr());
}
}
}
else
{
if (atom_type == _ATOM_LIST)
_qmol->addAtom(query_atom.release());
else if (atom_type == _ATOM_NOTLIST)
_qmol->addAtom(QueryMolecule::Atom::nicht(query_atom.release()));
else if (atom_type == _ATOM_ELEMENT)
_qmol->addAtom(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, label));
else if (atom_type == _ATOM_PSEUDO)
_qmol->addAtom(new QueryMolecule::Atom(QueryMolecule::ATOM_PSEUDO, buf.ptr()));
else if (atom_type == _ATOM_TEMPLATE)
_qmol->addTemplateAtom(buf.ptr());
else if (atom_type == _ATOM_A)
_qmol->addAtom(QueryMolecule::Atom::nicht(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_H)));
else if (atom_type == _ATOM_AH)
{
std::unique_ptr<QueryMolecule::Atom> atom = std::make_unique<QueryMolecule::Atom>();
atom->type = QueryMolecule::OP_NONE;
_qmol->addAtom(atom.release());
}
else if (atom_type == _ATOM_X)
{
std::unique_ptr<QueryMolecule::Atom> atom = std::make_unique<QueryMolecule::Atom>();
atom->type = QueryMolecule::OP_OR;
atom->children.add(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_F));
atom->children.add(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_Cl));
atom->children.add(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_Br));
atom->children.add(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_I));
atom->children.add(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_At));
_qmol->addAtom(atom.release());
}
else if (atom_type == _ATOM_XH)
{
std::unique_ptr<QueryMolecule::Atom> atom = std::make_unique<QueryMolecule::Atom>();
atom->type = QueryMolecule::OP_OR;
atom->children.add(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_F));
atom->children.add(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_Cl));
atom->children.add(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_Br));
atom->children.add(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_I));
atom->children.add(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_At));
atom->children.add(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_H));
_qmol->addAtom(atom.release());
}
else if (atom_type == _ATOM_QH)
_qmol->addAtom(QueryMolecule::Atom::nicht(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_C)));
else if (atom_type == _ATOM_Q)
_qmol->addAtom(QueryMolecule::Atom::und(QueryMolecule::Atom::nicht(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_H)),
QueryMolecule::Atom::nicht(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_C))));
else if (atom_type == _ATOM_MH)
{
std::unique_ptr<QueryMolecule::Atom> atom = std::make_unique<QueryMolecule::Atom>();
atom->type = QueryMolecule::OP_AND;
atom->children.add(QueryMolecule::Atom::nicht(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_C)));
atom->children.add(QueryMolecule::Atom::nicht(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_N)));
atom->children.add(QueryMolecule::Atom::nicht(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_O)));
atom->children.add(QueryMolecule::Atom::nicht(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_F)));
atom->children.add(QueryMolecule::Atom::nicht(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_P)));
atom->children.add(QueryMolecule::Atom::nicht(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_S)));
atom->children.add(QueryMolecule::Atom::nicht(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_Cl)));
atom->children.add(QueryMolecule::Atom::nicht(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_Se)));
atom->children.add(QueryMolecule::Atom::nicht(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_Br)));
atom->children.add(QueryMolecule::Atom::nicht(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_I)));
atom->children.add(QueryMolecule::Atom::nicht(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_At)));
atom->children.add(QueryMolecule::Atom::nicht(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_He)));
atom->children.add(QueryMolecule::Atom::nicht(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_Ne)));
atom->children.add(QueryMolecule::Atom::nicht(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_Ar)));
atom->children.add(QueryMolecule::Atom::nicht(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_Kr)));
atom->children.add(QueryMolecule::Atom::nicht(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_Xe)));
atom->children.add(QueryMolecule::Atom::nicht(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_Rn)));
_qmol->addAtom(atom.release());
}
else if (atom_type == _ATOM_M)
{
std::unique_ptr<QueryMolecule::Atom> atom = std::make_unique<QueryMolecule::Atom>();
atom->type = QueryMolecule::OP_AND;
atom->children.add(QueryMolecule::Atom::nicht(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_C)));
atom->children.add(QueryMolecule::Atom::nicht(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_N)));
atom->children.add(QueryMolecule::Atom::nicht(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_O)));
atom->children.add(QueryMolecule::Atom::nicht(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_F)));
atom->children.add(QueryMolecule::Atom::nicht(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_P)));
atom->children.add(QueryMolecule::Atom::nicht(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_S)));
atom->children.add(QueryMolecule::Atom::nicht(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_Cl)));
atom->children.add(QueryMolecule::Atom::nicht(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_Se)));
atom->children.add(QueryMolecule::Atom::nicht(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_Br)));
atom->children.add(QueryMolecule::Atom::nicht(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_I)));
atom->children.add(QueryMolecule::Atom::nicht(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_At)));
atom->children.add(QueryMolecule::Atom::nicht(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_He)));
atom->children.add(QueryMolecule::Atom::nicht(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_Ne)));
atom->children.add(QueryMolecule::Atom::nicht(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_Ar)));
atom->children.add(QueryMolecule::Atom::nicht(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_Kr)));
atom->children.add(QueryMolecule::Atom::nicht(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_Xe)));
atom->children.add(QueryMolecule::Atom::nicht(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_Rn)));
atom->children.add(QueryMolecule::Atom::nicht(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_H)));
_qmol->addAtom(atom.release());
}
else // _ATOM_R
_qmol->addAtom(new QueryMolecule::Atom(QueryMolecule::ATOM_RSITE, 0));
}
// int hcount = 0;
int irflag = 0;
int ecflag = 0;
int radical = 0;
// read remaining atom properties
while (true)
{
strscan.skipSpace();
if (strscan.isEOF())
break;
QS_DEF(Array<char>, prop_arr);
strscan.readWord(prop_arr, "=");
strscan.skip(1);
const char* prop = prop_arr.ptr();
if (strcmp(prop, "CHG") == 0)
{
int charge = strscan.readInt1();
if (_mol != 0)
_mol->setAtomCharge_Silent(i, charge);
else
{
_qmol->resetAtom(i, QueryMolecule::Atom::und(_qmol->releaseAtom(i), new QueryMolecule::Atom(QueryMolecule::ATOM_CHARGE, charge)));
}
}
else if (strcmp(prop, "RAD") == 0)
{
radical = strscan.readInt1();
if (_qmol != 0)
{
_qmol->resetAtom(i, QueryMolecule::Atom::und(_qmol->releaseAtom(i), new QueryMolecule::Atom(QueryMolecule::ATOM_RADICAL, radical)));
}
}
else if (strcmp(prop, "CFG") == 0)
{
strscan.readInt1();
// int cfg = strscan.readInt1();
// if (cfg == 3)
// _stereocenter_types[idx] = MoleculeStereocenters::ATOM4;
}
else if (strcmp(prop, "MASS") == 0)
{
isotope = strscan.readInt1();
}
else if (strcmp(prop, "VAL") == 0)
{
int valence = strscan.readInt1();
if (valence == -1)
valence = 0;
_bmol->setExplicitValence(i, valence);
}
else if (strcmp(prop, "HCOUNT") == 0)
{
int hcount = strscan.readInt1();
if (_qmol == 0)
{
if (!ignore_noncritical_query_features)
throw Error("H count is allowed only for queries");
}
if (hcount == -1)
_hcount[i] = 1;
else if (hcount > 0)
_hcount[i] = hcount + 1; // to comply to the code in _postLoad()
else
throw Error("invalid HCOUNT value: %d", hcount);
}
else if (strcmp(prop, "STBOX") == 0)
_stereo_care_atoms[i] = strscan.readInt1();
else if (strcmp(prop, "INVRET") == 0)
irflag = strscan.readInt1();
else if (strcmp(prop, "EXACHG") == 0)
ecflag = strscan.readInt1();
else if (strcmp(prop, "SUBST") == 0)
{
if (_qmol == 0)
throw Error("substitution count is allowed only for queries");
int subst = strscan.readInt1();
if (subst != 0)
{
if (subst == -1)
_qmol->resetAtom(i, QueryMolecule::Atom::und(_qmol->releaseAtom(i), new QueryMolecule::Atom(QueryMolecule::ATOM_SUBSTITUENTS, 0)));
else if (subst == -2)
{
_qmol->resetAtom(
i, QueryMolecule::Atom::und(_qmol->releaseAtom(i),
new QueryMolecule::Atom(QueryMolecule::ATOM_SUBSTITUENTS_AS_DRAWN, _qmol->getVertex(i).degree())));
}
else if (subst > 0)
_qmol->resetAtom(i, QueryMolecule::Atom::und(_qmol->releaseAtom(i), new QueryMolecule::Atom(QueryMolecule::ATOM_SUBSTITUENTS, subst,
(subst < 6 ? subst : 100))));
else
throw Error("invalid SUBST value: %d", subst);
}
}
else if (strcmp(prop, "UNSAT") == 0)
{
if (_qmol == 0)
{
if (!ignore_noncritical_query_features)
throw Error("unsaturation flag is allowed only for queries");
}
else
{
bool unsat = (strscan.readInt1() > 0);
if (unsat)
_qmol->resetAtom(i, QueryMolecule::Atom::und(_qmol->releaseAtom(i), new QueryMolecule::Atom(QueryMolecule::ATOM_UNSATURATION, 0)));
}
}
else if (strcmp(prop, "RBCNT") == 0)
{
if (_qmol == 0)
{
if (!ignore_noncritical_query_features)
throw Error("ring bond count is allowed only for queries");
}
else
{
int rb = strscan.readInt1();
if (rb != 0)
{
if (rb == -1)
rb = 0;
else if (rb == -2)
{
int rbonds = 0;
const Vertex& vertex = _qmol->getVertex(i);
for (int k = vertex.neiBegin(); k != vertex.neiEnd(); k = vertex.neiNext(k))
if (_qmol->getEdgeTopology(vertex.neiEdge(k)) == TOPOLOGY_RING)
rbonds++;
_qmol->resetAtom(i, QueryMolecule::Atom::und(_qmol->releaseAtom(i),
new QueryMolecule::Atom(QueryMolecule::ATOM_RING_BONDS_AS_DRAWN, rbonds)));
}
else if (rb > 1)
_qmol->resetAtom(i, QueryMolecule::Atom::und(_qmol->releaseAtom(i),
new QueryMolecule::Atom(QueryMolecule::ATOM_RING_BONDS, rb, (rb < 4 ? rb : 100))));
else
throw Error("invalid RBCNT value: %d", rb);
}
}
}
else if (strcmp(prop, "RGROUPS") == 0)
{
int n_rg;
strscan.skip(1); // skip '('
n_rg = strscan.readInt1();
while (n_rg-- > 0)
_bmol->allowRGroupOnRSite(i, strscan.readInt1());
}
else if (strcmp(prop, "ATTCHPT") == 0)
{
int att_type = strscan.readInt1();
if (att_type == -1)
att_type = 3;
for (int att_idx = 0; (1 << att_idx) <= att_type; att_idx++)
if (att_type & (1 << att_idx))
_bmol->addAttachmentPoint(att_idx + 1, i);
}
else if (strcmp(prop, "ATTCHORD") == 0)
{
int n_items, nei_idx, att_type;
QS_DEF(Array<char>, att_id);
strscan.skip(1); // skip '('
n_items = strscan.readInt1() / 2;
while (n_items-- > 0)
{
nei_idx = strscan.readInt1();
if (atom_type == _ATOM_R)
{
att_type = strscan.readInt1();
_bmol->setRSiteAttachmentOrder(i, nei_idx - 1, att_type - 1);
}
else
{
strscan.readWord(att_id, " )");
att_id.push(0);
_bmol->setTemplateAtomAttachmentOrder(i, nei_idx - 1, att_id.ptr());
strscan.skip(1); // skip stop character
}
}
}
else if (strcmp(prop, "CLASS") == 0)
{
QS_DEF(Array<char>, temp_class);
strscan.readWord(temp_class, 0);
temp_class.push(0);
_bmol->setTemplateAtomClass(i, temp_class.ptr());
}
else if (strcmp(prop, "SEQID") == 0)
{
int seq_id = strscan.readInt1();
_bmol->setTemplateAtomSeqid(i, seq_id);
}
else if (strcmp(prop, "SEQNAME") == 0)
{
QS_DEF(Array<char>, seq_name);
strscan.readWord(seq_name, 0);
seq_name.push(0);
}
else
{
throw Error("unsupported property of CTAB3000: %s", prop);
}
}
if (isotope != 0)
{
if (_mol != 0)
_mol->setAtomIsotope(i, isotope);
else
_qmol->resetAtom(i, QueryMolecule::Atom::und(_qmol->releaseAtom(i), new QueryMolecule::Atom(QueryMolecule::ATOM_ISOTOPE, isotope)));
}
if (_mol != 0)
_mol->setAtomRadical(i, radical);
_bmol->reaction_atom_inversion[i] = irflag;
_bmol->reaction_atom_exact_change[i] = ecflag;
_bmol->reaction_atom_mapping[i] = aamap;
_bmol->setAtomXyz(i, x, y, z);
}
_scanner.readLine(str, true);
if (strncmp(str.ptr(), "M V30 END ATOM", 15) != 0)
throw Error("Error reading ATOM block footer");
}
if (atom_block_exists)
_scanner.readLine(str, true);
if (strncmp(str.ptr(), "M V30 BEGIN BOND", 17) != 0)
{
if (_bonds_num > 0)
throw Error("Error reading BOND block header");
bond_block_exists = false;
}
else
{
for (i = 0; i < _bonds_num; i++)
{
int reacting_center = 0;
_readMultiString(str);
BufferScanner strscan(str.ptr());
strscan.readInt1(); // bond index -- ignored
int order = strscan.readInt1();
int beg = strscan.readInt1() - 1;
int end = strscan.readInt1() - 1;
if (_mol != 0)
{
if (order == BOND_SINGLE || order == BOND_DOUBLE || order == BOND_TRIPLE || order == BOND_AROMATIC || order == _BOND_COORDINATION ||
order == _BOND_HYDROGEN)
_mol->addBond_Silent(beg, end, order);
else if (order == _BOND_COORDINATION || order == _BOND_HYDROGEN)
_mol->addBond_Silent(beg, end, BOND_ZERO);
else if (order == _BOND_SINGLE_OR_DOUBLE)
throw Error("'single or double' bonds are allowed only for queries");
else if (order == _BOND_SINGLE_OR_AROMATIC)
throw Error("'single or aromatic' bonds are allowed only for queries");
else if (order == _BOND_DOUBLE_OR_AROMATIC)
throw Error("'double or aromatic' bonds are allowed only for queries");
else if (order == _BOND_ANY)
throw Error("'any' bonds are allowed only for queries");
else
throw Error("unknown bond type: %d", order);
}
else
{
_qmol->addBond(beg, end, QueryMolecule::createQueryMoleculeBond(order, 0, 0));
}
while (true)
{
strscan.skipSpace();
if (strscan.isEOF())
break;
QS_DEF(Array<char>, prop);
strscan.readWord(prop, "=");
strscan.skip(1);
int n;
if (strcmp(prop.ptr(), "CFG") == 0)
{
n = strscan.readInt1();
if (n == 1)
_bmol->setBondDirection(i, BOND_UP);
else if (n == 3)
_bmol->setBondDirection(i, BOND_DOWN);
else if (n == 2)
{
int bond_order = _bmol->getBondOrder(i);
if (bond_order == BOND_SINGLE)
_bmol->setBondDirection(i, BOND_EITHER);
else if (bond_order == BOND_DOUBLE)
_ignore_cistrans[i] = 1;
else
throw Error("unknown bond CFG=%d for the bond order %d", n, bond_order);
}
else
throw Error("unknown bond CFG=%d", n);
}
else if (strcmp(prop.ptr(), "STBOX") == 0)
{
if (_qmol == 0)
if (!ignore_noncritical_query_features)
throw Error("stereo care box is allowed only for queries");
if ((strscan.readInt1() != 0))
_stereo_care_bonds[i] = 1;
}
else if (strcmp(prop.ptr(), "TOPO") == 0)
{
if (_qmol == 0)
{
if (!ignore_noncritical_query_features)
throw Error("bond topology setting is allowed only for queries");
}
else
{
int topo = strscan.readInt1();
_qmol->resetBond(
i, QueryMolecule::Bond::und(_qmol->releaseBond(i),
new QueryMolecule::Bond(QueryMolecule::BOND_TOPOLOGY, topo == 1 ? TOPOLOGY_RING : TOPOLOGY_CHAIN)));
}
}
else if (strcmp(prop.ptr(), "RXCTR") == 0)
reacting_center = strscan.readInt1();
else if (strcmp(prop.ptr(), "ENDPTS") == 0)
{
strscan.skip(1); // (
n = strscan.readInt1();
while (n-- > 0)
{
strscan.readInt();
strscan.skipSpace();
}
strscan.skip(1); // )
}
else if (strcmp(prop.ptr(), "ATTACH") == 0)
{
while (!strscan.isEOF())
{
char c = strscan.readChar();
if (c == ' ')
break;
}
}
else if (strcmp(prop.ptr(), "DISP") == 0)
{
while (!strscan.isEOF())
{
char c = strscan.readChar();
if (c == ' ')
break;
}
}
else
{
throw Error("unsupported property of CTAB3000 (in BOND block): %s", prop.ptr());
}
}
_bmol->reaction_bond_reacting_center[i] = reacting_center;
}
_scanner.readLine(str, true);
if (strncmp(str.ptr(), "M V30 END BOND", 15) != 0)
throw Error("Error reading BOND block footer");
_scanner.readLine(str, true);
}
// Read collections and sgroups
// There is no predefined order: sgroups may appear before collection
bool collection_parsed = false, sgroups_parsed = false;
while (strncmp(str.ptr(), "M V30 END CTAB", 15) != 0)
{
if (strncmp(str.ptr(), "M V30 BEGIN COLLECTION", 23) == 0)
{
if (collection_parsed)
throw Error("COLLECTION block has already been parsed");
_readCollectionBlock3000();
collection_parsed = true;
}
else if (strncmp(str.ptr(), "M V30 BEGIN SGROUP", 19) == 0)
{
if (sgroups_parsed)
throw Error("SGROUP block has already been parsed");
_readSGroupsBlock3000();
sgroups_parsed = true;
}
else if (strncmp(str.ptr(), "M V30 LINKNODE", 15) == 0)
throw Error("link nodes are not supported yet (%s)", str.ptr());
else
throw Error("error reading CTAB block footer: %s", str.ptr());
_scanner.readLine(str, true);
}
}