code: // Licensed to the Software Freedom Conservancy (SFC) under one
  // or more contributor license agreements.  See the NOTICE file
  // distributed with this work for additional information
  // regarding copyright ownership.  The SFC licenses this file
  // to you under the Apache License, Version 2.0 (the
  // "License"); you may not use this file except in compliance
  // with the License.  You may obtain a copy of the License at
  //
  //   http://www.apache.org/licenses/LICENSE-2.0
  //
  // Unless required by applicable law or agreed to in writing,
  // software distributed under the License is distributed on an
  // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  // KIND, either express or implied.  See the License for the
  // specific language governing permissions and limitations
  // under the License.
  
  package org.openqa.selenium.docker.internal;
  
  import com.google.common.annotations.VisibleForTesting;
  import com.google.common.collect.ImmutableMap;
  import java.util.Objects;
  import org.openqa.selenium.Beta;
  import org.openqa.selenium.docker.DockerException;
  import org.openqa.selenium.internal.Require;
  
  @Beta
  public class Reference {
    private static final String DEFAULT_DOMAIN = "docker.io";
    private static final String LEGACY_DEFAULT_DOMAIN = "index.docker.io";
    private static final String DEFAULT_REPO = "library";
    private static final String DEFAULT_TAG = "latest";
  
    private final String domain;
    private final String name;
    private final String tag;
    private final String digest;
  
    @VisibleForTesting
    Reference(String domain, String name, String tag, String digest) {
      this.domain = Require.nonNull("Domain", domain);
      this.name = Require.nonNull("Name", name);
      this.tag = tag;
      this.digest = digest;
    }
  
    // Logic taken from https://github.com/distribution/distribution/blob/main/reference/normalize.go
    public static Reference parse(String input) {
      Require.nonNull("Reference to parse", input);
  
      ImmutableMap<String, String> splitDockerDomain = splitDockerDomain(input);
      String domain = splitDockerDomain.get("domain");
      String remainder = splitDockerDomain.get("remainder");
  
      String name;
      String digest = null;
      String tag = DEFAULT_TAG;
  
      int digestSep = remainder.indexOf("@");
      int tagSep = remainder.indexOf(":");
      if (digestSep > -1 && tagSep > -1) {
        digest = remainder.substring(digestSep + 1);
        name = remainder.substring(0, digestSep);
        tag = null;
      } else if (tagSep > -1) {
        tag = remainder.substring(tagSep + 1);
        name = remainder.substring(0, tagSep);
      } else {
        name = remainder;
      }
  
      if (!name.toLowerCase().equals(name)) {
        throw new DockerException(
            String.format("Invalid reference format: repository name (%s) must be lowercase", name));
      }
  
      return new Reference(domain, name, tag, digest);
    }
  
    private static ImmutableMap<String, String> splitDockerDomain(String name) {
      String domain;
      String remainder;
      int domSep = name.indexOf("/");
      String possibleDomain = domSep == -1 ? "" : name.substring(0, domSep);
      if (domSep == -1
          || (!possibleDomain.contains(".")
              && !possibleDomain.contains(":")
              && !"localhost".equalsIgnoreCase(possibleDomain)
              && possibleDomain.toLowerCase().equals(possibleDomain))) {
        remainder = name;
        domain = DEFAULT_DOMAIN;
      } else {
        domain = possibleDomain;
        remainder = name.substring(domSep + 1);
      }
      if (LEGACY_DEFAULT_DOMAIN.equals(domain)) {
        domain = DEFAULT_DOMAIN;
      }
      if (DEFAULT_DOMAIN.equals(domain) && !remainder.contains("/")) {
        remainder = String.format("%s/%s", DEFAULT_REPO, remainder);
      }
      return ImmutableMap.of("domain", domain, "remainder", remainder);
    }
  
    public String getDomain() {
      return domain;
    }
  
    public String getName() {
      return name;
    }
  
    public String getTag() {
      return tag;
    }
  
    public String getDigest() {
      return digest;
    }
  
    public String getFamiliarName() {
      StringBuilder familiar = new StringBuilder();
  
      if (!DEFAULT_DOMAIN.equals(domain)) {
        familiar.append(domain).append("/");
      }
  
      if (name.contains(DEFAULT_REPO) && DEFAULT_DOMAIN.equals(domain)) {
        familiar.append(name.replace(DEFAULT_REPO + "/", ""));
      } else {
        familiar.append(name);
      }
  
      if (digest != null) {
        familiar.append("@").append(digest);
      } else if (tag != null) {
        familiar.append(":").append(tag);
      } else {
        throw new DockerException("Unable to form familiar name: " + this);
      }
  
      return familiar.toString();
    }
  
    @Override
    public String toString() {
      return "Reference{"
          + "domain='"
          + domain
          + '\''
          + ", name='"
          + name
          + '\''
          + ", tag='"
          + tag
          + '\''
          + ", digest='"
          + digest
          + '\''
          + '}';
    }
  
    @Override
    public boolean equals(Object o) {
      if (!(o instanceof Reference)) {
        return false;
      }
  
      Reference that = (Reference) o;
      return this.domain.equals(that.domain)
          && this.name.equals(that.name)
          && Objects.equals(tag, that.tag)
          && Objects.equals(digest, that.digest);
    }
  
    @Override
    public int hashCode() {
      return Objects.hash(domain, name, tag, digest);
    }
  }
  