test_class.file: storage/smiles/src/test/java/org/openscience/cdk/smiles/CxSmilesGeneratorTest.java
test_class.identifier: CxSmilesGeneratorTest
test_case.body: @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_case.class_method_signature: CxSmilesGeneratorTest.sgroups()
test_case.full_signature: @Test public void sgroups()
test_case.identifier: sgroups
test_case.invocations: add asList add asList assertThat generate is
test_case.modifiers: @Test public
test_case.parameters: ()
test_case.return: void
test_case.signature: void sgroups()
focal_class.file: storage/smiles/src/main/java/org/openscience/cdk/smiles/CxSmilesGenerator.java
focal_class.identifier: CxSmilesGenerator
focal_class.class_method_signature: CxSmilesGenerator.inverse(int[] perm)
focal_class.full_signature: private static int[] inverse(int[] perm)
focal_class.identifier: inverse
focal_class.modifiers: private static
focal_class.parameters: (int[] perm)
focal_class.return: int[]
focal_class.signature: int[] inverse(int[] perm)
focal_class.class_method_signature: CxSmilesGenerator.encode_alias(String label)
focal_class.full_signature: private static String encode_alias(String label)
focal_class.identifier: encode_alias
focal_class.modifiers: private static
focal_class.parameters: (String label)
focal_class.return: String
focal_class.signature: String encode_alias(String label)
focal_class.class_method_signature: CxSmilesGenerator.compare(Comparator<Integer> comp, List<Integer> a, List<Integer> b)
focal_class.full_signature: private static int compare(Comparator<Integer> comp, List<Integer> a, List<Integer> b)
focal_class.identifier: compare
focal_class.modifiers: private static
focal_class.parameters: (Comparator<Integer> comp, List<Integer> a, List<Integer> b)
focal_class.return: int
focal_class.signature: int compare(Comparator<Integer> comp, List<Integer> a, List<Integer> b)
focal_class.class_method_signature: CxSmilesGenerator.generate(CxSmilesState state, int opts, int[] components, final int[] ordering)
focal_class.full_signature: static String generate(CxSmilesState state, int opts, int[] components, final int[] ordering)
focal_class.identifier: generate
focal_class.modifiers: static
focal_class.parameters: (CxSmilesState state, int opts, int[] components, final int[] ordering)
focal_class.return: String
focal_class.signature: String generate(CxSmilesState state, int opts, int[] components, final int[] ordering)
focal_class.class_method_signature: CxSmilesGenerator.appendIntegers(int[] invorder, char sep, StringBuilder sb, List<Integer> vals)
focal_class.full_signature: private static void appendIntegers(int[] invorder, char sep, StringBuilder sb, List<Integer> vals)
focal_class.identifier: appendIntegers
focal_class.modifiers: private static
focal_class.parameters: (int[] invorder, char sep, StringBuilder sb, List<Integer> vals)
focal_class.return: void
focal_class.signature: void appendIntegers(int[] invorder, char sep, StringBuilder sb, List<Integer> vals)
focal_method.body: 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]);
              }
          };
  
          // Fragment Grouping
          if (SmiFlavor.isSet(opts, SmiFlavor.CxFragmentGroup) &&
              state.fragGroups != null && !state.fragGroups.isEmpty()) {
  
              int maxCompId = 0;
              for (int compId : components) {
                  if (compId > maxCompId)
                      maxCompId = compId;
              }
  
              // get the output component order
              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++;
              }
              // index vs number, we need to output index
              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);
                  }
              });
  
              // C1=CC=CC=C1.C1=CC=CC=C1.[OH-].[Na+]>> |f:0.1,2.3,c:0,2,4,6,8,10|
              sb.append('f');
              sb.append(':');
              for (int i = 0; i < fragGroupCpy.size(); i++) {
                  if (i > 0) sb.append(',');
                  appendIntegers(compMap, '.', sb, fragGroupCpy.get(i));
              }
          }
  
          // Atom Labels
          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));
                  // don't need to write anymore more ';'
                  if (nonempty_cnt == state.atomLabels.size())
                      break;
                  sb.append(";");
              }
              sb.append('$');
          }
  
          // Atom Values
          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));
                  // don't need to write anymore more ';'
                  if (nonempty_cnt == state.atomValues.size())
                      break;
                  sb.append(";");
              }
              sb.append('$');
          }
  
          // 2D/3D Coordinates
          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(')');
          }
  
          // Multicenter/Positional variation bonds
          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());
  
              // consistent output order
              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());
  
              // consistent output order
              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;
  
          // *CCO* |$_AP1;;;;_AP2$,Sg:n:1,2,3::ht|
          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);
                  // fmt (t/f/n) + coords?
              }
          }
  
          // hierarchy information
          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);
                      }
                  }
              }
          }
  
          // [C]1[CH][CH]CCC1 |^1:1,2,^3:0|
          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();
          }
      }
focal_method.class_method_signature: CxSmilesGenerator.generate(CxSmilesState state, int opts, int[] components, final int[] ordering)
focal_method.full_signature: static String generate(CxSmilesState state, int opts, int[] components, final int[] ordering)
focal_method.identifier: generate
focal_method.invocations: isSet inverse append append compare compare isSet isEmpty compare sort sort compare append append size append appendIntegers get isSet isEmpty length append append get isEmpty append encode_alias size append append isSet isEmpty length append append get isEmpty append encode_alias size append append isSet isEmpty getInstance length append append get append append format append append format append append format append isSet isEmpty length append append append entrySet sort compare getKey getKey size append get append getKey append getValue sort appendIntegers isSet isEmpty length append append append entrySet sort compare getKey getKey size append get append getKey append appendIntegers getValue isSet isEmpty add sort sort compareTo compare length append append append append appendIntegers append append append append toLowerCase isSet isEmpty add sort sort compareTo compareTo compare length append append appendIntegers append append append append append append append append sort comparingInt isEmpty length append append append append sort comparingInt append append isSet isEmpty entrySet get getValue put getValue add getKey entrySet length append append append ordinal getKey append sort getValue appendIntegers getValue append length toString
focal_method.modifiers: static
focal_method.parameters: (CxSmilesState state, int opts, int[] components, final int[] ordering)
focal_method.return: String
focal_method.signature: String generate(CxSmilesState state, int opts, int[] components, final int[] ordering)