CxSmilesGenerator { static String generate(CxSmilesState state, int opts, int[] components, final int[] ordering) { if (!SmiFlavor.isSet(opts, SmiFlavor.CxSmilesWithCoords)) return ""; final int[] invorder = inverse(ordering); StringBuilder sb = new StringBuilder(); sb.append(' '); sb.append('|'); final Comparator<Integer> invComp = new Comparator<Integer>() { @Override public int compare(Integer a, Integer b) { return Integer.compare(invorder[a], invorder[b]); } }; final Comparator<Integer> comp = new Comparator<Integer>() { @Override public int compare(Integer a, Integer b) { return Integer.compare(ordering[a], ordering[b]); } }; if (SmiFlavor.isSet(opts, SmiFlavor.CxFragmentGroup) && state.fragGroups != null && !state.fragGroups.isEmpty()) { int maxCompId = 0; for (int compId : components) { if (compId > maxCompId) maxCompId = compId; } final int[] compMap = new int[maxCompId + 1]; int compId = 1; for (int idx : invorder) { int component = components[idx]; if (compMap[component] == 0) compMap[component] = compId++; } for (int i = 0; i < compMap.length; i++) compMap[i]--; final Comparator<Integer> compComp = new Comparator<Integer>() { @Override public int compare(Integer a, Integer b) { return Integer.compare(compMap[a], compMap[b]); } }; List<List<Integer>> fragGroupCpy = new ArrayList<>(state.fragGroups); for (List<Integer> idxs : fragGroupCpy) Collections.sort(idxs, compComp); Collections.sort(fragGroupCpy, new Comparator<List<Integer>>() { @Override public int compare(List<Integer> a, List<Integer> b) { return CxSmilesGenerator.compare(compComp, a, b); } }); sb.append('f'); sb.append(':'); for (int i = 0; i < fragGroupCpy.size(); i++) { if (i > 0) sb.append(','); appendIntegers(compMap, '.', sb, fragGroupCpy.get(i)); } } if (SmiFlavor.isSet(opts, SmiFlavor.CxAtomLabel) && state.atomLabels != null && !state.atomLabels.isEmpty()) { if (sb.length() > 2) sb.append(','); sb.append('$'); int nonempty_cnt = 0; for (int idx : invorder) { String label = state.atomLabels.get(idx); if (label == null || label.isEmpty()) label = ""; else nonempty_cnt++; sb.append(encode_alias(label)); if (nonempty_cnt == state.atomLabels.size()) break; sb.append(";"); } sb.append('$'); } if (SmiFlavor.isSet(opts, SmiFlavor.CxAtomValue) && state.atomValues != null && !state.atomValues.isEmpty()) { if (sb.length() > 2) sb.append(','); sb.append("$_AV:"); int nonempty_cnt = 0; for (int idx : invorder) { String label = state.atomValues.get(idx); if (label == null || label.isEmpty()) label = ""; else nonempty_cnt++; sb.append(encode_alias(label)); if (nonempty_cnt == state.atomValues.size()) break; sb.append(";"); } sb.append('$'); } if (SmiFlavor.isSet(opts, SmiFlavor.CxCoordinates) && state.atomCoords != null && !state.atomCoords.isEmpty()) { DecimalFormat fmt = new DecimalFormat("#.##", DecimalFormatSymbols.getInstance(Locale.ROOT)); if (sb.length() > 2) sb.append(','); sb.append('('); for (int i = 0; i < ordering.length; i++) { double[] xyz = state.atomCoords.get(invorder[i]); if (i != 0) sb.append(';'); if (xyz[0] != 0) sb.append(fmt.format(xyz[0])); sb.append(','); if (xyz[1] != 0) sb.append(fmt.format(xyz[1])); sb.append(','); if (xyz[2] != 0) sb.append(fmt.format(xyz[2])); } sb.append(')'); } if (SmiFlavor.isSet(opts, SmiFlavor.CxMulticenter) && state.positionVar != null && !state.positionVar.isEmpty()) { if (sb.length() > 2) sb.append(','); sb.append('m'); sb.append(':'); List<Map.Entry<Integer, List<Integer>>> multicenters = new ArrayList<>(state.positionVar.entrySet()); multicenters.sort((a, b) -> comp.compare(a.getKey(), b.getKey())); for (int i = 0; i < multicenters.size(); i++) { if (i != 0) sb.append(','); Map.Entry<Integer, List<Integer>> e = multicenters.get(i); sb.append(ordering[e.getKey()]); sb.append(':'); List<Integer> vals = new ArrayList<>(e.getValue()); vals.sort(comp); appendIntegers(ordering, '.', sb, vals); } } if (SmiFlavor.isSet(opts, SmiFlavor.CxLigandOrder) && state.ligandOrdering != null && !state.ligandOrdering.isEmpty()) { if (sb.length() > 2) sb.append(','); sb.append("LO"); sb.append(':'); List<Map.Entry<Integer, List<Integer>>> ligandorderings = new ArrayList<>(state.ligandOrdering.entrySet()); ligandorderings.sort((a, b) -> comp.compare(a.getKey(), b.getKey())); for (int i = 0; i < ligandorderings.size(); i++) { if (i != 0) sb.append(','); Map.Entry<Integer, List<Integer>> e = ligandorderings.get(i); sb.append(ordering[e.getKey()]); sb.append(':'); appendIntegers(ordering, '.', sb, e.getValue()); } } int numSgroups = 0; if (SmiFlavor.isSet(opts, SmiFlavor.CxPolymer) && state.mysgroups != null && !state.mysgroups.isEmpty()) { List<CxPolymerSgroup> polysgroups = new ArrayList<>(); for (CxSgroup polysgroup : state.mysgroups) { if (polysgroup instanceof CxPolymerSgroup) { polysgroups.add((CxPolymerSgroup) polysgroup); Collections.sort(polysgroup.atoms, comp); } } Collections.sort(polysgroups, new Comparator<CxPolymerSgroup>() { @Override public int compare(CxPolymerSgroup a, CxPolymerSgroup b) { int cmp = 0; cmp = a.type.compareTo(b.type); if (cmp != 0) return cmp; cmp = CxSmilesGenerator.compare(comp, a.atoms, b.atoms); return cmp; } }); for (CxPolymerSgroup cxPolymerSgroup : polysgroups) { cxPolymerSgroup.id = numSgroups++; if (sb.length() > 2) sb.append(','); sb.append("Sg:"); sb.append(cxPolymerSgroup.type); sb.append(':'); appendIntegers(ordering, ',', sb, cxPolymerSgroup.atoms); sb.append(':'); if (cxPolymerSgroup.subscript != null) sb.append(cxPolymerSgroup.subscript); sb.append(':'); if (cxPolymerSgroup.supscript != null) sb.append(cxPolymerSgroup.supscript.toLowerCase(Locale.ROOT)); } } if (SmiFlavor.isSet(opts, SmiFlavor.CxDataSgroups) && state.mysgroups != null && !state.mysgroups.isEmpty()) { List<CxDataSgroup> datasgroups = new ArrayList<>(); for (CxSgroup datasgroup : state.mysgroups) { if (datasgroup instanceof CxDataSgroup) { datasgroups.add((CxDataSgroup)datasgroup); Collections.sort(datasgroup.atoms, comp); } } Collections.sort(datasgroups, new Comparator<CxDataSgroup>() { @Override public int compare(CxDataSgroup a, CxDataSgroup b) { int cmp = 0; cmp = a.field.compareTo(b.field); if (cmp != 0) return cmp; cmp = a.value.compareTo(b.value); if (cmp != 0) return cmp; cmp = CxSmilesGenerator.compare(comp, a.atoms, b.atoms); return cmp; } }); for (CxDataSgroup cxDataSgroup : datasgroups) { cxDataSgroup.id = numSgroups++; if (sb.length() > 2) sb.append(','); sb.append("SgD:"); appendIntegers(ordering, ',', sb, cxDataSgroup.atoms); sb.append(':'); if (cxDataSgroup.field != null) sb.append(cxDataSgroup.field); sb.append(':'); if (cxDataSgroup.value != null) sb.append(cxDataSgroup.value); sb.append(':'); if (cxDataSgroup.operator != null) sb.append(cxDataSgroup.operator); sb.append(':'); if (cxDataSgroup.unit != null) sb.append(cxDataSgroup.unit); } } if (numSgroups > 0) { boolean firstSgH = true; if (state.mysgroups != null) { state.mysgroups.sort(Comparator.comparingInt(o -> o.id)); for (CxSgroup sgroup : state.mysgroups) { if (sgroup.children.isEmpty()) continue; if (sb.length() > 2) sb.append(','); if (firstSgH) { sb.append("SgH:"); firstSgH = false; } sb.append(sgroup.id).append(':'); boolean first = true; List<CxSgroup> children = new ArrayList<>(sgroup.children); children.sort(Comparator.comparingInt(o -> o.id)); for (CxSgroup child : children) { if (child.id < 0) continue; if (!first) sb.append('.'); first = false; sb.append(child.id); } } } } if (SmiFlavor.isSet(opts, SmiFlavor.CxRadical) && state.atomRads != null && !state.atomRads.isEmpty()) { Map<CxSmilesState.Radical, List<Integer>> radinv = new TreeMap<>(); for (Map.Entry<Integer, CxSmilesState.Radical> e : state.atomRads.entrySet()) { List<Integer> idxs = radinv.get(e.getValue()); if (idxs == null) radinv.put(e.getValue(), idxs = new ArrayList<Integer>()); idxs.add(e.getKey()); } for (Map.Entry<CxSmilesState.Radical, List<Integer>> e : radinv.entrySet()) { if (sb.length() > 2) sb.append(','); sb.append('^'); sb.append(e.getKey().ordinal() + 1); sb.append(':'); Collections.sort(e.getValue(), comp); appendIntegers(ordering, ',', sb, e.getValue()); } } sb.append('|'); if (sb.length() <= 3) { return ""; } else { return sb.toString(); } }    }
@Test public void emptyCXSMILES() { CxSmilesState state = new CxSmilesState(); assertThat(CxSmilesGenerator.generate(state, SmiFlavor.CxSmiles, new int[0], new int[0]), is("")); }
@Test public void multicenter() { CxSmilesState state = new CxSmilesState(); state.positionVar = new HashMap<>(); state.positionVar.put(0, Arrays.asList(4, 5, 6, 7)); state.positionVar.put(2, Arrays.asList(4, 6, 5, 7)); assertThat(CxSmilesGenerator.generate(state, SmiFlavor.CxMulticenter, new int[0], new int[]{7, 6, 5, 4, 3, 2, 1, 0}), is(" |m:5:0.1.2.3,7:0.1.2.3|")); }
@Test public void coords2d() { CxSmilesState state = new CxSmilesState(); state.atomCoords = Arrays.asList(new double[]{0, 1.5, 0}, new double[]{0, 3, 0}, new double[]{1.5, 1.5, 0}); assertThat(CxSmilesGenerator.generate(state, SmiFlavor.CxCoordinates, new int[0], new int[]{1, 2, 0}), is(" |(1.5,1.5,;,1.5,;,3,)|")); }
@Test public void sgroups() { CxSmilesState state = new CxSmilesState(); state.mysgroups = new ArrayList<>(1); state.mysgroups.add(new CxSmilesState.CxPolymerSgroup("n", Arrays.asList(2,3), "n", "ht")); state.mysgroups.add(new CxSmilesState.CxPolymerSgroup("n", Arrays.asList(5), "m", "ht")); assertThat(CxSmilesGenerator.generate(state, SmiFlavor.CxPolymer, new int[0], new int[]{7, 6, 5, 4, 3, 2, 1, 0}), is(" |Sg:n:2:m:ht,Sg:n:4,5:n:ht|")); }
@Test public void radicals() { CxSmilesState state = new CxSmilesState(); state.atomRads = new HashMap<>(); state.atomRads.put(2, CxSmilesState.Radical.Monovalent); state.atomRads.put(6, CxSmilesState.Radical.Monovalent); state.atomRads.put(4, CxSmilesState.Radical.Divalent); assertThat(CxSmilesGenerator.generate(state, SmiFlavor.CxSmiles, new int[0], new int[]{7, 6, 5, 4, 3, 2, 1, 0}), is(" |^1:1,5,^2:3|")); }