/**
   * Convert a {@code stringKey} produced using {@link StateNamespace#stringKey} on one of the
   * namespaces produced by this class into the original {@link StateNamespace}.
   */
  public static <W extends BoundedWindow> StateNamespace fromString(
      String stringKey, Coder<W> windowCoder) {
    if (!stringKey.startsWith("/") || !stringKey.endsWith("/")) {
      throw new RuntimeException("Invalid namespace string: '" + stringKey + "'");
    }

    if (GlobalNamespace.GLOBAL_STRING.equals(stringKey)) {
      return global();
    }

    List<String> parts = SLASH_SPLITTER.splitToList(stringKey);
    if (parts.size() != 3 && parts.size() != 4) {
      throw new RuntimeException("Invalid namespace string: '" + stringKey + "'");
    }
    // Ends should be empty (we start and end with /)
    if (!parts.get(0).isEmpty() || !parts.get(parts.size() - 1).isEmpty()) {
      throw new RuntimeException("Invalid namespace string: '" + stringKey + "'");
    }

    try {
      W window = CoderUtils.decodeFromBase64(windowCoder, parts.get(1));
      if (parts.size() > 3) {
        int index = Integer.parseInt(parts.get(2), WindowAndTriggerNamespace.TRIGGER_RADIX);
        return windowAndTrigger(windowCoder, window, index);
      } else {
        return window(windowCoder, window);
      }
    } catch (Exception e) {
      throw new RuntimeException("Invalid namespace string: '" + stringKey + "'", e);
    }
  }
 @Override
 public String stringKey() {
   try {
     return String.format(WINDOW_FORMAT, CoderUtils.encodeToBase64(windowCoder, window));
   } catch (CoderException e) {
     throw new RuntimeException("Unable to generate string key from window " + window, e);
   }
 }
 @Override
 public String stringKey() {
   try {
     return String.format(
         WINDOW_AND_TRIGGER_FORMAT,
         CoderUtils.encodeToBase64(windowCoder, window),
         // Use base 36 so that can address 36 triggers in a single byte and still be human
         // readable.
         Integer.toString(triggerIndex, TRIGGER_RADIX).toUpperCase());
   } catch (CoderException e) {
     throw new RuntimeException("Unable to generate string key from window " + window, e);
   }
 }
 @Override
 public void appendTo(Appendable sb) throws IOException {
   sb.append('/').append(CoderUtils.encodeToBase64(windowCoder, window));
   sb.append('/').append(Integer.toString(triggerIndex, TRIGGER_RADIX).toUpperCase());
   sb.append('/');
 }
 @Override
 public void appendTo(Appendable sb) throws IOException {
   sb.append('/').append(CoderUtils.encodeToBase64(windowCoder, window)).append('/');
 }