public List<Sgroup> generate(final IAtomContainer mol) {

        // mark which atoms have already been abbreviated or are
        // part of an existing Sgroup
        Set<IAtom> usedAtoms = new HashSet<>();
        List<Sgroup> sgroups = mol.getProperty(CDKConstants.CTAB_SGROUPS);
        if (sgroups != null) {
            for (Sgroup sgroup : sgroups)
                usedAtoms.addAll(sgroup.getAtoms());
        }

        // disconnected abbreviations, salts, common reagents, large compounds
        if (usedAtoms.isEmpty()) {
            try {
                IAtomContainer copy = AtomContainerManipulator.copyAndSuppressedHydrogens(mol);
                String cansmi = usmigen.create(copy);
                String label = disconnectedAbbreviations.get(cansmi);

                if (label != null && !disabled.contains(label)) {
                    Sgroup sgroup = new Sgroup();
                    sgroup.setType(SgroupType.CtabAbbreviation);
                    sgroup.setSubscript(label);
                    for (IAtom atom : mol.atoms())
                        sgroup.addAtom(atom);
                    return Collections.singletonList(sgroup);
                } else if (cansmi.contains(".")) {
                    IAtomContainerSet parts = ConnectivityChecker.partitionIntoMolecules(mol);

                    // partiton in two two parts
                    Sgroup best = null;
                    for (int i = 0; i < parts.getAtomContainerCount(); i++) {
                        IAtomContainer a = parts.getAtomContainer(i);
                        IAtomContainer b = a.getBuilder().newAtomContainer();
                        for (int j = 0; j < parts.getAtomContainerCount(); j++)
                            if (j != i)
                                b.add(parts.getAtomContainer(j));
                        Sgroup sgroup1 = getAbbr(a);
                        Sgroup sgroup2 = getAbbr(b);
                        if (sgroup1 != null && sgroup2 != null) {
                            Sgroup combined = new Sgroup();
                            label = null;
                            for (IAtom atom : sgroup1.getAtoms())
                                combined.addAtom(atom);
                            for (IAtom atom : sgroup2.getAtoms())
                                combined.addAtom(atom);
                            if (sgroup1.getSubscript().length() > sgroup2.getSubscript().length())
                                combined.setSubscript(sgroup1.getSubscript() + INTERPUNCT + sgroup2.getSubscript());
                            else
                                combined.setSubscript(sgroup2.getSubscript() + INTERPUNCT + sgroup1.getSubscript());
                            combined.setType(SgroupType.CtabAbbreviation);
                            return Collections.singletonList(combined);
                        }
                        if (sgroup1 != null && (best == null || sgroup1.getAtoms().size() > best.getAtoms().size()))
                            best = sgroup1;
                        if (sgroup2 != null && (best == null || sgroup2.getAtoms().size() > best.getAtoms().size()))
                            best = sgroup2;
                    }

                    if (best != null)
                        return Collections.singletonList(best);
                }

            } catch (CDKException ignored) {
            }
        }

        final List<Sgroup> newSgroups = new ArrayList<>();
        List<IAtomContainer> fragments = generateFragments(mol);
        Multimap<IAtom, Sgroup> sgroupAdjs = ArrayListMultimap.create();

        for (IAtomContainer frag : fragments) {
            try {
                final String smi = usmigen.create(AtomContainerManipulator.copyAndSuppressedHydrogens(frag));
                final String label = connectedAbbreviations.get(smi);

                if (label == null || disabled.contains(label))
                    continue;

                boolean overlap = false;

                // note: first atom is '*'
                int numAtoms = frag.getAtomCount();
                int numBonds = frag.getBondCount();
                for (int i = 1; i < numAtoms; i++) {
                    if (usedAtoms.contains(frag.getAtom(i))) {
                        overlap = true;
                        break;
                    }
                }

                // overlaps with previous assignment
                if (overlap)
                    continue;

                // create new abbreviation SGroup
                Sgroup sgroup = new Sgroup();
                sgroup.setType(SgroupType.CtabAbbreviation);
                sgroup.setSubscript(label);

                IBond attachBond = frag.getBond(0).getProperty(CUT_BOND, IBond.class);
                IAtom attachAtom = null;
                sgroup.addBond(attachBond);
                for (int i = 1; i < numAtoms; i++) {
                    IAtom atom = frag.getAtom(i);
                    usedAtoms.add(atom);
                    sgroup.addAtom(atom);
                    if (attachBond.getBegin().equals(atom))
                        attachAtom = attachBond.getEnd();
                    else if (attachBond.getEnd().equals(atom))
                        attachAtom = attachBond.getBegin();
                }

                if (attachAtom != null)
                    sgroupAdjs.put(attachAtom, sgroup);
                newSgroups.add(sgroup);

             } catch (CDKException e) {
                // ignore
            }
        }

        if (!contractOnHetero)
            return newSgroups;

        // now collapse
        collapse:
        for (IAtom attach : mol.atoms()) {
            if (usedAtoms.contains(attach))
                continue;

            // skip charged or isotopic labelled, C or R/*, H, He
            if ((attach.getFormalCharge() != null && attach.getFormalCharge() != 0)
                || attach.getMassNumber() != null
                || attach.getAtomicNumber() == 6
                || attach.getAtomicNumber() < 2)
                continue;

            int hcount = attach.getImplicitHydrogenCount();
            Set<IAtom> xatoms   = new HashSet<>();
            Set<IBond> xbonds   = new HashSet<>();
            Set<IBond> newbonds = new HashSet<>();
            xatoms.add(attach);

            List<String> nbrSymbols = new ArrayList<>();
            Set<Sgroup> todelete = new HashSet<>();
            for (Sgroup sgroup : sgroupAdjs.get(attach)) {
                if (containsChargeChar(sgroup.getSubscript()))
                    continue;
                if (sgroup.getBonds().size() != 1)
                    continue;
                IBond xbond = sgroup.getBonds().iterator().next();
                xbonds.add(xbond);
                xatoms.addAll(sgroup.getAtoms());
                if (attach.getSymbol().length() == 1 &&
                    Character.isLowerCase(sgroup.getSubscript().charAt(0))) {
                    if (Elements.ofString(attach.getSymbol() + sgroup.getSubscript().charAt(0)) != Elements.Unknown)
                        continue collapse;
                }
                nbrSymbols.add(sgroup.getSubscript());
                todelete.add(sgroup);
            }
            int numSGrpNbrs = nbrSymbols.size();
            for (IBond bond : mol.getConnectedBondsList(attach)) {
                if (!xbonds.contains(bond)) {
                    IAtom nbr = bond.getOther(attach);
                    // contract terminal bonds
                    if (mol.getConnectedBondsCount(nbr) == 1) {
                        if (nbr.getMassNumber() != null ||
                            (nbr.getFormalCharge() != null && nbr.getFormalCharge() != 0)) {
                            newbonds.add(bond);
                        } else if (nbr.getAtomicNumber() == 1) {
                            hcount++;
                            xatoms.add(nbr);
                        } else if (nbr.getAtomicNumber() > 0){
                            nbrSymbols.add(newSymbol(nbr.getAtomicNumber(), nbr.getImplicitHydrogenCount(), false));
                            xatoms.add(nbr);
                        }
                    } else {
                        newbonds.add(bond);
                    }
                }
            }

            // reject if no symbols
            // reject if no bonds (<1), except if all symbols are identical... (HashSet.size==1)
            // reject if more that 2 bonds
            if (nbrSymbols.isEmpty() ||
                newbonds.size() < 1 && (new HashSet<>(nbrSymbols).size() != 1) ||
                newbonds.size() > 2)
                continue;

            // create the symbol
            StringBuilder sb = new StringBuilder();
            sb.append(newSymbol(attach.getAtomicNumber(), hcount, newbonds.size() == 0));
            String prev  = null;
            int    count = 0;
            Collections.sort(nbrSymbols, new Comparator<String>() {
                @Override
                public int compare(String o1, String o2) {
                    int cmp = Integer.compare(o1.length(), o2.length());
                    if (cmp != 0) return cmp;
                    return o1.compareTo(o2);
                }
            });
            for (String nbrSymbol : nbrSymbols) {
                if (nbrSymbol.equals(prev)) {
                    count++;
                } else {
                    boolean useParen = count == 0 || countUpper(prev) > 1 || (prev != null && nbrSymbol.startsWith(prev));
                    appendGroup(sb, prev, count, useParen);
                    prev = nbrSymbol;
                    count = 1;
                }
            }
            appendGroup(sb, prev, count, false);

            // remove existing
            newSgroups.removeAll(todelete);

            // create new
            Sgroup newSgroup = new Sgroup();
            newSgroup.setType(SgroupType.CtabAbbreviation);
            newSgroup.setSubscript(sb.toString());
            for (IBond bond : newbonds)
                newSgroup.addBond(bond);
            for (IAtom atom : xatoms)
                newSgroup.addAtom(atom);

            newSgroups.add(newSgroup);
            usedAtoms.addAll(xatoms);
        }

        return newSgroups;
    }
public List < Sgroup > generate ( final IAtomContainer mol ) { Set < IAtom > usedAtoms = new HashSet < > ( ) ; List < Sgroup > sgroups = mol . getProperty ( CDKConstants . CTAB_SGROUPS ) ; if ( sgroups != null ) { for ( Sgroup sgroup : sgroups ) usedAtoms . addAll ( sgroup . getAtoms ( ) ) ; } if ( usedAtoms . isEmpty ( ) ) { try { IAtomContainer copy = AtomContainerManipulator . copyAndSuppressedHydrogens ( mol ) ; String cansmi = usmigen . create ( copy ) ; String label = disconnectedAbbreviations . get ( cansmi ) ; if ( label != null && ! disabled . contains ( label ) ) { Sgroup sgroup = new Sgroup ( ) ; sgroup . setType ( SgroupType . CtabAbbreviation ) ; sgroup . setSubscript ( label ) ; for ( IAtom atom : mol . atoms ( ) ) sgroup . addAtom ( atom ) ; return Collections . singletonList ( sgroup ) ; } else if ( cansmi . contains ( "." ) ) { IAtomContainerSet parts = ConnectivityChecker . partitionIntoMolecules ( mol ) ; Sgroup best = null ; for ( int i = 0 ; i < parts . getAtomContainerCount ( ) ; i ++ ) { IAtomContainer a = parts . getAtomContainer ( i ) ; IAtomContainer b = a . getBuilder ( ) . newAtomContainer ( ) ; for ( int j = 0 ; j < parts . getAtomContainerCount ( ) ; j ++ ) if ( j != i ) b . add ( parts . getAtomContainer ( j ) ) ; Sgroup sgroup1 = getAbbr ( a ) ; Sgroup sgroup2 = getAbbr ( b ) ; if ( sgroup1 != null && sgroup2 != null ) { Sgroup combined = new Sgroup ( ) ; label = null ; for ( IAtom atom : sgroup1 . getAtoms ( ) ) combined . addAtom ( atom ) ; for ( IAtom atom : sgroup2 . getAtoms ( ) ) combined . addAtom ( atom ) ; if ( sgroup1 . getSubscript ( ) . length ( ) > sgroup2 . getSubscript ( ) . length ( ) ) combined . setSubscript ( sgroup1 . getSubscript ( ) + INTERPUNCT + sgroup2 . getSubscript ( ) ) ; else combined . setSubscript ( sgroup2 . getSubscript ( ) + INTERPUNCT + sgroup1 . getSubscript ( ) ) ; combined . setType ( SgroupType . CtabAbbreviation ) ; return Collections . singletonList ( combined ) ; } if ( sgroup1 != null && ( best == null || sgroup1 . getAtoms ( ) . size ( ) > best . getAtoms ( ) . size ( ) ) ) best = sgroup1 ; if ( sgroup2 != null && ( best == null || sgroup2 . getAtoms ( ) . size ( ) > best . getAtoms ( ) . size ( ) ) ) best = sgroup2 ; } if ( best != null ) return Collections . singletonList ( best ) ; } } catch ( CDKException ignored ) { } } final List < Sgroup > newSgroups = new ArrayList < > ( ) ; List < IAtomContainer > fragments = generateFragments ( mol ) ; Multimap < IAtom , Sgroup > sgroupAdjs = ArrayListMultimap . create ( ) ; for ( IAtomContainer frag : fragments ) { try { final String smi = usmigen . create ( AtomContainerManipulator . copyAndSuppressedHydrogens ( frag ) ) ; final String label = connectedAbbreviations . get ( smi ) ; if ( label == null || disabled . contains ( label ) ) continue ; boolean overlap = false ; int numAtoms = frag . getAtomCount ( ) ; int numBonds = frag . getBondCount ( ) ; for ( int i = 1 ; i < numAtoms ; i ++ ) { if ( usedAtoms . contains ( frag . getAtom ( i ) ) ) { overlap = true ; break ; } } if ( overlap ) continue ; Sgroup sgroup = new Sgroup ( ) ; sgroup . setType ( SgroupType . CtabAbbreviation ) ; sgroup . setSubscript ( label ) ; IBond attachBond = frag . getBond ( 0 ) . getProperty ( CUT_BOND , IBond . class ) ; IAtom attachAtom = null ; sgroup . addBond ( attachBond ) ; for ( int i = 1 ; i < numAtoms ; i ++ ) { IAtom atom = frag . getAtom ( i ) ; usedAtoms . add ( atom ) ; sgroup . addAtom ( atom ) ; if ( attachBond . getBegin ( ) . equals ( atom ) ) attachAtom = attachBond . getEnd ( ) ; else if ( attachBond . getEnd ( ) . equals ( atom ) ) attachAtom = attachBond . getBegin ( ) ; } if ( attachAtom != null ) sgroupAdjs . put ( attachAtom , sgroup ) ; newSgroups . add ( sgroup ) ; } catch ( CDKException e ) { } } if ( ! contractOnHetero ) return newSgroups ; collapse : for ( IAtom attach : mol . atoms ( ) ) { if ( usedAtoms . contains ( attach ) ) continue ; if ( ( attach . getFormalCharge ( ) != null && attach . getFormalCharge ( ) != 0 ) || attach . getMassNumber ( ) != null || attach . getAtomicNumber ( ) == 6 || attach . getAtomicNumber ( ) < 2 ) continue ; int hcount = attach . getImplicitHydrogenCount ( ) ; Set < IAtom > xatoms = new HashSet < > ( ) ; Set < IBond > xbonds = new HashSet < > ( ) ; Set < IBond > newbonds = new HashSet < > ( ) ; xatoms . add ( attach ) ; List < String > nbrSymbols = new ArrayList < > ( ) ; Set < Sgroup > todelete = new HashSet < > ( ) ; for ( Sgroup sgroup : sgroupAdjs . get ( attach ) ) { if ( containsChargeChar ( sgroup . getSubscript ( ) ) ) continue ; if ( sgroup . getBonds ( ) . size ( ) != 1 ) continue ; IBond xbond = sgroup . getBonds ( ) . iterator ( ) . next ( ) ; xbonds . add ( xbond ) ; xatoms . addAll ( sgroup . getAtoms ( ) ) ; if ( attach . getSymbol ( ) . length ( ) == 1 && Character . isLowerCase ( sgroup . getSubscript ( ) . charAt ( 0 ) ) ) { if ( Elements . ofString ( attach . getSymbol ( ) + sgroup . getSubscript ( ) . charAt ( 0 ) ) != Elements . Unknown ) continue collapse ; } nbrSymbols . add ( sgroup . getSubscript ( ) ) ; todelete . add ( sgroup ) ; } int numSGrpNbrs = nbrSymbols . size ( ) ; for ( IBond bond : mol . getConnectedBondsList ( attach ) ) { if ( ! xbonds . contains ( bond ) ) { IAtom nbr = bond . getOther ( attach ) ; if ( mol . getConnectedBondsCount ( nbr ) == 1 ) { if ( nbr . getMassNumber ( ) != null || ( nbr . getFormalCharge ( ) != null && nbr . getFormalCharge ( ) != 0 ) ) { newbonds . add ( bond ) ; } else if ( nbr . getAtomicNumber ( ) == 1 ) { hcount ++ ; xatoms . add ( nbr ) ; } else if ( nbr . getAtomicNumber ( ) > 0 ) { nbrSymbols . add ( newSymbol ( nbr . getAtomicNumber ( ) , nbr . getImplicitHydrogenCount ( ) , false ) ) ; xatoms . add ( nbr ) ; } } else { newbonds . add ( bond ) ; } } } if ( nbrSymbols . isEmpty ( ) || newbonds . size ( ) < 1 && ( new HashSet < > ( nbrSymbols ) . size ( ) != 1 ) || newbonds . size ( ) > 2 ) continue ; StringBuilder sb = new StringBuilder ( ) ; sb . append ( newSymbol ( attach . getAtomicNumber ( ) , hcount , newbonds . size ( ) == 0 ) ) ; String prev = null ; int count = 0 ; Collections . sort ( nbrSymbols , new Comparator < String > ( ) { @ Override public int compare ( String o1 , String o2 ) { int cmp = Integer . compare ( o1 . length ( ) , o2 . length ( ) ) ; if ( cmp != 0 ) return cmp ; return o1 . compareTo ( o2 ) ; } } ) ; for ( String nbrSymbol : nbrSymbols ) { if ( nbrSymbol . equals ( prev ) ) { count ++ ; } else { boolean useParen = count == 0 || countUpper ( prev ) > 1 || ( prev != null && nbrSymbol . startsWith ( prev ) ) ; appendGroup ( sb , prev , count , useParen ) ; prev = nbrSymbol ; count = 1 ; } } appendGroup ( sb , prev , count , false ) ; newSgroups . removeAll ( todelete ) ; Sgroup newSgroup = new Sgroup ( ) ; newSgroup . setType ( SgroupType . CtabAbbreviation ) ; newSgroup . setSubscript ( sb . toString ( ) ) ; for ( IBond bond : newbonds ) newSgroup . addBond ( bond ) ; for ( IAtom atom : xatoms ) newSgroup . addAtom ( atom ) ; newSgroups . add ( newSgroup ) ; usedAtoms . addAll ( xatoms ) ; } return newSgroups ; }
Find all enabled abbreviations in the provided molecule. They are not
added to the existing Sgroups and may need filtering.

@param mol molecule
@return list of new abbreviation Sgroups