class ImageReference {

  public static ImageReference parse(String reference) throws InvalidImageReferenceException {
    if (reference.equals("scratch")) {
      return ImageReference.scratch();
    }

    Matcher matcher = REFERENCE_PATTERN.matcher(reference);

    if (!matcher.find() || matcher.groupCount() < 4) {
      throw new InvalidImageReferenceException(reference);
    }

    String registry = matcher.group(1);
    String repository = matcher.group(2);
    String tag = matcher.group(3);
    String digest = matcher.group(4);

    // If no registry was matched, use Docker Hub by default.
    if (Strings.isNullOrEmpty(registry)) {
      registry = DOCKER_HUB_REGISTRY;
    }

    if (Strings.isNullOrEmpty(repository)) {
      throw new InvalidImageReferenceException(reference);
    }
    /*
     * If a registry was matched but it does not contain any dots or colons, it should actually be
     * part of the repository unless it is "localhost".
     *
     * See https://github.com/docker/distribution/blob/245ca4659e09e9745f3cc1217bf56e946509220c/reference/normalize.go#L62
     */
    if (!registry.contains(".") && !registry.contains(":") && !"localhost".equals(registry)) {
      repository = registry + "/" + repository;
      registry = DOCKER_HUB_REGISTRY;
    }

    /*
     * For Docker Hub, if the repository is only one component, then it should be prefixed with
     * 'library/'.
     *
     * See https://docs.docker.com/engine/reference/commandline/pull/#pull-an-image-from-docker-hub
     */
    if (DOCKER_HUB_REGISTRY.equals(registry) && repository.indexOf('/') < 0) {
      repository = LIBRARY_REPOSITORY_PREFIX + repository;
    }

    if (Strings.isNullOrEmpty(tag) && Strings.isNullOrEmpty(digest)) {
      tag = DEFAULT_TAG;
    }
    if (Strings.isNullOrEmpty(tag)) {
      tag = null;
    }
    if (Strings.isNullOrEmpty(digest)) {
      digest = null;
    }

    return new ImageReference(registry, repository, tag, digest);
  }

  private  ImageReference(
      String registry, String repository, @Nullable String tag, @Nullable String digest);

}