PortableNames.java

package org.hammer.audio.experiment.document;

import java.nio.file.Path;

/** Strict validators for portable asset references and output basenames. */
@SuppressWarnings("PMD.LiteralsFirstInComparisons")
public final class PortableNames {

  private PortableNames() {
    // utility class
  }

  /** Require a normalized relative path without traversal or platform-specific separators. */
  public static String requireRelativePath(String value) {
    String checked = ExperimentDocument.requireNonBlank(value, "relativePath");
    if (checked.indexOf('\\') >= 0 || checked.startsWith("/") || checked.contains(":")) {
      throw new IllegalArgumentException("relativePath must be platform-neutral and relative");
    }
    Path path = Path.of(checked).normalize();
    if (path.isAbsolute()
        || path.getNameCount() == 0
        || path.startsWith("..")
        || !path.toString().replace('\\', '/').equals(checked)) {
      throw new IllegalArgumentException("relativePath contains traversal or is not normalized");
    }
    return checked;
  }

  /** Require a single portable filename component without path separators or traversal. */
  public static String requireBaseName(String value) {
    String checked = ExperimentDocument.requireNonBlank(value, "baseName");
    Path fileName = Path.of(checked).getFileName();
    if (checked.equals(".")
        || checked.equals("..")
        || checked.indexOf('/') >= 0
        || checked.indexOf('\\') >= 0
        || checked.indexOf(':') >= 0
        || fileName == null
        || !fileName.toString().equals(checked)) {
      throw new IllegalArgumentException("baseName must be one portable filename component");
    }
    return checked;
  }
}