void QueryMolecule::writeSmartsAtom()

in core/indigo-core/molecule/src/query_molecule.cpp [611:842]


void QueryMolecule::writeSmartsAtom(Output& output, Atom* atom, int aam, int chirality, int depth, bool has_or_parent, bool has_not_parent, int original_format)
{
    int i;
    bool brackets_used = false;

    if (depth == 0) // "organic" subset can be used without [], but CNOPS need explicit aromatic to use letter
        if (!isOrganicSubset(atom) || isAromaticByCaseAtom(atom))
        {
            bool atom_by_case = false;
            bool aromaticity = false;
            if (atom->type == OP_AND && atom->children.size() == 2)
            {
                atom_by_case = isAromaticByCaseAtom(atom->child(0)) || isAromaticByCaseAtom(atom->child(1));
                aromaticity = (atom->child(0)->type == ATOM_AROMATICITY || atom->child(1)->type == ATOM_AROMATICITY);
            }
            if (!atom_by_case || !aromaticity)
            {
                output.writeChar('[');
                brackets_used = true;
            }
        }

    switch (atom->type)
    {
    case OP_NOT: {
        if (isNotAtom(*atom, ELEM_H))
        {
            output.printf("*");
            break;
        }
        output.writeChar('!');
        writeSmartsAtom(output, atom->child(0), aam, chirality, depth + 1, has_or_parent, true, original_format);
        break;
    }
    case OP_AND: {
        bool has_number = false;
        bool has_aromatic = false;
        bool aromatic = false;
        char atom_name[10];
        long long cur_pos = output.tell();
        for (i = 0; i < atom->children.size(); i++)
        {
            if (isAromaticByCaseAtom(atom->children[i]))
            {
                has_number = true;
                strncpy(atom_name, Element::toString(atom->child(i)->value_max), sizeof(atom_name));
            }
            if (atom->children[i]->type == ATOM_AROMATICITY)
            {
                has_aromatic = true;
                aromatic = atom->child(i)->value_min == ATOM_AROMATIC;
            }
        }
        if (has_aromatic && has_number)
        { // Convert a & #6 -> c,  A & #6 -> C
            if (aromatic)
                atom_name[0] = static_cast<char>(tolower(atom_name[0]));
            output.printf("%s", atom_name);
        }
        for (i = 0; i < atom->children.size(); i++)
        {
            if (has_aromatic && has_number && (atom->children[i]->type == ATOM_AROMATICITY || atom->children[i]->type == ATOM_NUMBER))
            {
                continue;
            }
            if (atom->children[i]->type == ATOM_RADICAL || atom->children[i]->type == ATOM_VALENCE)
            {
                continue;
            }

            if (output.tell() > cur_pos)
            {
                output.writeChar(has_or_parent ? '&' : ';');
                cur_pos = output.tell();
            }
            writeSmartsAtom(output, atom->child(i), aam, chirality, depth + 1, has_or_parent, has_not_parent, original_format);
        }
        break;
    }
    case OP_OR: {
        for (i = 0; i < atom->children.size(); i++)
        {
            if (atom->children[i]->type == QueryMolecule::ATOM_RADICAL || atom->children[i]->type == QueryMolecule::ATOM_VALENCE)
            {
                continue;
            }

            if (i > 0)
                output.printf(has_not_parent ? "!" : ",");
            writeSmartsAtom(output, atom->child(i), aam, chirality, depth + 1, true, has_not_parent, original_format);
        }
        break;
    }
    case ATOM_ISOTOPE:
        output.printf("%d", atom->value_max);
        break;
    case ATOM_NUMBER: {
        if (isAromaticByCaseAtom(atom))
            output.printf("#%d", atom->value_max);
        else
            output.printf("%s", Element::toString(atom->value_max));
        switch (original_format)
        {
        case SMARTS:
        case KET:
            // SMARTS and ket save chirality in ATOM_CHIRALITY for query molecule
            break;
        default:
            if (chirality == CHIRALITY_ANTICLOCKWISE)
                output.printf("@");
            else if (chirality == CHIRALITY_CLOCKWISE)
                output.printf("@@");
            break;
        }

        if (aam > 0)
            output.printf(":%d", aam);

        break;
    }
    case ATOM_CHARGE: {
        int charge = atom->value_max;

        if (charge > 1)
            output.printf("+%d", charge);
        else if (charge < -1)
            output.printf("-%d", -charge);
        else if (charge == 1)
            output.printf("+");
        else if (charge == -1)
            output.printf("-");
        else
            output.printf("+0");
        break;
    }
    case ATOM_FRAGMENT: {
        if (atom->fragment->fragment_smarts.ptr() == 0)
            throw Error("fragment_smarts has unexpectedly gone");
        output.printf("$(%s)", atom->fragment->fragment_smarts.ptr());
        break;
    }
    case ATOM_AROMATICITY: {
        if (atom->value_min == ATOM_AROMATIC)
            output.printf("a");
        else
            output.printf("A");
        break;
    }
    case OP_NONE:
        output.writeChar('*');
        break;
    case ATOM_TOTAL_H: {
        _write_num(output, 'H', atom->value_min);
        break;
    }

    case ATOM_SSSR_RINGS: {
        _write_num_if_set(output, 'R', atom->value_min, atom->value_max);
        break;
    }

    case ATOM_RING_BONDS_AS_DRAWN: {
        output.printf("x0"); // exact value should be writed in extended part
        break;
    }

    case ATOM_RING_BONDS: {
        _write_num_if_set(output, 'x', atom->value_min, atom->value_max);
        break;
    }

    case ATOM_IMPLICIT_H: {
        _write_num_if_set(output, 'h', atom->value_min, atom->value_max);
        break;
    }

    case ATOM_UNSATURATION: {
        output.printf("$([*,#1]=,#,:[*,#1])");
        break;
    }

    case ATOM_SMALLEST_RING_SIZE: {
        _write_num_if_set(output, 'r', atom->value_min, atom->value_max);
        break;
    }

    case ATOM_SUBSTITUENTS: {
        output.printf("D%d", atom->value_min);
        break;
    }

    case ATOM_SUBSTITUENTS_AS_DRAWN: {
        output.printf("D%d", atom->value_min);
        break;
    }

    case ATOM_PSEUDO: {
        // output.writeString(atom->alias.ptr());
        output.writeChar('*');
        break;
    }
    case ATOM_TEMPLATE: {
        output.writeString(atom->alias.ptr());
        break;
    }

    case ATOM_CONNECTIVITY: {
        output.printf("X%d", atom->value_min);
        break;
    }

    case ATOM_TOTAL_BOND_ORDER: {
        _write_num(output, 'v', atom->value_min);
        break;
    }

    case ATOM_CHIRALITY: {
        _getAtomChiralityDescription(atom, output);
        break;
    }
    case ATOM_RSITE:
        output.printf("*:%d", atom->value_min);
        break;
    default: {
        throw Error("Unknown atom attribute %d", atom->type);
        break;
    }
    }

    if (brackets_used)
        output.writeChar(']');
}