class Cake {

  @SuppressWarnings("unchecked")
  public <T> T get(String... key) {
    List<String> keys =
        ((key == null) ? new ArrayList<String>() : Arrays.asList(key))
            .stream()
                .flatMap(
                    k ->
                        Arrays.stream(
                            k == null ? new String[] {null} : StringUtils.split(k, Cake.SEPARATOR)))
                .collect(Collectors.toList());

    if (keys.isEmpty()) {
      throw new IllegalArgumentException("cannot get value for empty key");
    }

    keys.forEach(Cake::validateKey);
    String fullKey = StringUtils.join(keys, Cake.SEPARATOR);

    // search within current namespace
    String searchKey = getPrefixWithSeparator(prefixStack) + fullKey;
    if (entries.containsKey(searchKey)) {
      return (T) entries.get(searchKey);
    }

    // search within each ancestor namespace up to the root
    LinkedList<String> namespaces = new LinkedList<>(prefixStack);
    while (!namespaces.isEmpty()) {
      namespaces.removeLast();
      searchKey = getPrefixWithSeparator(namespaces) + fullKey;
      if (entries.containsKey(searchKey)) {
        return (T) entries.get(searchKey);
      }
    }

    // search within any other namespace (if unambiguous)
    List<String> candidates =
        entries.keySet().stream()
            .filter(
                k -> {
                  String[] subkeys = StringUtils.split(k, SEPARATOR);
                  if (keys.size() > subkeys.length) {
                    return false;
                  }
                  for (int i = 0; i < keys.size(); i++) {
                    if (!keys.get(keys.size() - 1 - i).equals(subkeys[subkeys.length - 1 - i])) {
                      return false;
                    }
                  }
                  return true;
                })
            .collect(Collectors.toList());

    if (candidates.size() == 1) {
      return (T) entries.get(candidates.get(0));
    } else if (candidates.isEmpty()) {
      throw new RuntimeException(
          "cake does not contain key '" + fullKey + "; current keys: " + this.entries.keySet());
    } else {
      throw new RuntimeException(
          "cannot retrieve ambiguous key '" + fullKey + "'; candidates: " + candidates);
    }
  }

  public  Cake();
  public  Cake(Cake other);

}