private List<IBond> makeIonicBonds(final List<IAtomContainer> frags) {
        assert frags.size() > 1;

        // merge duplicates together, e.g. [H-].[H-].[H-].[Na+].[Na+].[Na+]
        // would be two needsMerge fragments. We currently only do single
        // atoms but in theory could also do larger ones
        final List<IAtomContainer> mergedFrags = mergeAtomicIons(frags);
        final List<IAtomContainer> posFrags = new ArrayList<>();
        final List<IAtomContainer> negFrags = new ArrayList<>();

        int chgSum = 0;
        for (IAtomContainer frag : mergedFrags) {
            int chg = 0;
            for (final IAtom atom : frag.atoms())
                chg += nullAsZero(atom.getFormalCharge());
            chgSum += chg;
            frag.setProperty(FRAGMENT_CHARGE, chg);
            if (chg < 0)
                negFrags.add(frag);
            else if (chg > 0)
                posFrags.add(frag);
        }

        // non-neutral or we only have one needsMerge fragment?
        if (chgSum != 0 || mergedFrags.size() == 1)
            return Collections.emptyList();

        List<IAtom> cations = new ArrayList<>();
        List<IAtom> anions = new ArrayList<>();

        // trivial case
        if (posFrags.size() == 1 && negFrags.size() == 1) {
            cations.addAll(selectIons(posFrags.get(0), +1));
            anions.addAll(selectIons(negFrags.get(0), -1));
        } else {

            // sort hi->lo fragment charge, if same charge then we put smaller
            // fragments (bond count) before in cations and after in anions
            Comparator<IAtomContainer> comparator = new Comparator<IAtomContainer>() {
                @Override
                public int compare(IAtomContainer a, IAtomContainer b) {
                    int qA = a.getProperty(FRAGMENT_CHARGE);
                    int qB = b.getProperty(FRAGMENT_CHARGE);
                    int cmp = Integer.compare(Math.abs(qA), Math.abs(qB));
                    if (cmp != 0) return cmp;
                    int sign = Integer.signum(qA);
                    return Integer.compare(sign * a.getBondCount(), sign * b.getBondCount());
                }
            };

            // greedy selection
            Collections.sort(posFrags, comparator);
            Collections.sort(negFrags, comparator);

            for (IAtomContainer posFrag : posFrags)
                cations.addAll(selectIons(posFrag, +1));
            for (IAtomContainer negFrag : negFrags)
                anions.addAll(selectIons(negFrag, -1));
        }

        if (cations.size() != anions.size() && cations.isEmpty())
            return Collections.emptyList();

        final IChemObjectBuilder bldr = frags.get(0).getBuilder();

        // make the bonds
        final List<IBond> ionicBonds = new ArrayList<>(cations.size());
        for (int i = 0; i < cations.size(); i++) {
            final IAtom beg = cations.get(i);
            final IAtom end = anions.get(i);

            boolean unique = true;
            for (IBond bond : ionicBonds)
                if (bond.getBegin().equals(beg) && bond.getEnd().equals(end) ||
                    bond.getEnd().equals(beg) && bond.getBegin().equals(end))
                    unique = false;

            if (unique)
                ionicBonds.add(bldr.newInstance(IBond.class, beg, end));
        }

        // we could merge the fragments here using union-find structures
        // but it's much simpler (and probably more efficient) to return
        // the new bonds and re-fragment the molecule with these bonds added.

        return ionicBonds;
    }
private List < IBond > makeIonicBonds ( final List < IAtomContainer > frags ) { assert frags . size ( ) > 1 ; final List < IAtomContainer > mergedFrags = mergeAtomicIons ( frags ) ; final List < IAtomContainer > posFrags = new ArrayList < > ( ) ; final List < IAtomContainer > negFrags = new ArrayList < > ( ) ; int chgSum = 0 ; for ( IAtomContainer frag : mergedFrags ) { int chg = 0 ; for ( final IAtom atom : frag . atoms ( ) ) chg += nullAsZero ( atom . getFormalCharge ( ) ) ; chgSum += chg ; frag . setProperty ( FRAGMENT_CHARGE , chg ) ; if ( chg < 0 ) negFrags . add ( frag ) ; else if ( chg > 0 ) posFrags . add ( frag ) ; } if ( chgSum != 0 || mergedFrags . size ( ) == 1 ) return Collections . emptyList ( ) ; List < IAtom > cations = new ArrayList < > ( ) ; List < IAtom > anions = new ArrayList < > ( ) ; if ( posFrags . size ( ) == 1 && negFrags . size ( ) == 1 ) { cations . addAll ( selectIons ( posFrags . get ( 0 ) , + 1 ) ) ; anions . addAll ( selectIons ( negFrags . get ( 0 ) , - 1 ) ) ; } else { Comparator < IAtomContainer > comparator = new Comparator < IAtomContainer > ( ) { @ Override public int compare ( IAtomContainer a , IAtomContainer b ) { int qA = a . getProperty ( FRAGMENT_CHARGE ) ; int qB = b . getProperty ( FRAGMENT_CHARGE ) ; int cmp = Integer . compare ( Math . abs ( qA ) , Math . abs ( qB ) ) ; if ( cmp != 0 ) return cmp ; int sign = Integer . signum ( qA ) ; return Integer . compare ( sign * a . getBondCount ( ) , sign * b . getBondCount ( ) ) ; } } ; Collections . sort ( posFrags , comparator ) ; Collections . sort ( negFrags , comparator ) ; for ( IAtomContainer posFrag : posFrags ) cations . addAll ( selectIons ( posFrag , + 1 ) ) ; for ( IAtomContainer negFrag : negFrags ) anions . addAll ( selectIons ( negFrag , - 1 ) ) ; } if ( cations . size ( ) != anions . size ( ) && cations . isEmpty ( ) ) return Collections . emptyList ( ) ; final IChemObjectBuilder bldr = frags . get ( 0 ) . getBuilder ( ) ; final List < IBond > ionicBonds = new ArrayList < > ( cations . size ( ) ) ; for ( int i = 0 ; i < cations . size ( ) ; i ++ ) { final IAtom beg = cations . get ( i ) ; final IAtom end = anions . get ( i ) ; boolean unique = true ; for ( IBond bond : ionicBonds ) if ( bond . getBegin ( ) . equals ( beg ) && bond . getEnd ( ) . equals ( end ) || bond . getEnd ( ) . equals ( beg ) && bond . getBegin ( ) . equals ( end ) ) unique = false ; if ( unique ) ionicBonds . add ( bldr . newInstance ( IBond . class , beg , end ) ) ; } return ionicBonds ; }
Alternative method name "Humpty Dumpty" (a la. R Sayle).

(Re)bonding of ionic fragments for improved layout. This method takes a list
of two or more fragments and creates zero or more bonds (return value) that
should be temporarily used for layout generation. In general this problem is
difficult but since molecules will be laid out in a grid by default - any
positioning is an improvement. Heuristics could be added if bad (re)bonds
are seen.

@param frags connected fragments
@return ionic bonds to make