GitUri.java

package net.trajano.wagon.git.internal;

import static java.lang.String.format;

import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLDecoder;

import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;

/**
 * Git URI. This provides the logic to extract the components from a URI. The
 * URI string is in the form
 * <code>git:gitSpecificUri?branchName#relativeDirectory</code>.
 */
public class GitUri {
    /**
     * Branch name.
     */
    private final String branchName;

    /**
     * Repository URI.
     */
    private final String gitRepositoryUri;

    /**
     * Resource. This is the fragment part of the URI.
     */
    private final String resource;

    /**
     * Constructs the URI based on the parts.
     *
     * @param gitRepositoryUri
     *            git repository URL.
     * @param branchName
     *            branch name usually "gh-pages"
     * @param resource
     *            resource
     */
    public GitUri(final String gitRepositoryUri, final String branchName,
            final String resource) {
        this.gitRepositoryUri = gitRepositoryUri;
        this.branchName = branchName;
        this.resource = resource;
    }

    /**
     *
     * @param gitUri
     *            valid git URI (i.e. no git: schema).
     */
    private GitUri(final URI gitUri) {
        branchName = gitUri.getQuery();
        final String asciiUriString = gitUri.toASCIIString();
        gitRepositoryUri = asciiUriString.substring(0,
                asciiUriString.lastIndexOf('?'));
        resource = gitUri.getFragment();
    }

    @Override
    public boolean equals(final Object obj) {
        if (obj == null) {
            return false;
        }
        if (obj == this) {
            return true;
        }
        if (obj.getClass() != getClass()) {
            return false;
        }
        final GitUri x = (GitUri) obj;
        return new EqualsBuilder().append(branchName, x.branchName)
                .append(gitRepositoryUri, x.gitRepositoryUri)
                .append(resource, x.resource).build();
    }

    /**
     * Branch name.
     *
     * @return branch name
     */
    public String getBranchName() {
        return branchName;
    }

    /**
     * Git repository URI. This returns a string as that is what is being used
     * by the JGit API.
     *
     * @return Git repository URI
     */
    public String getGitRepositoryUri() {
        return gitRepositoryUri;
    }

    public String getResource() {
        return resource;
    }

    @Override
    public int hashCode() {
        return new HashCodeBuilder().append(resource).append(branchName)
                .append(gitRepositoryUri).build();
    }

    /**
     * Resolves a new {@link GitUri} with the fragment. The schema portion will
     * find the last occurrence of a URI path segment with a "?" character as
     * the Git URI with the branch name.
     *
     * @param fragment
     *            may contain escaped characters.
     * @return resolved {@link GitUri}
     */
    public GitUri resolve(final String fragment) {
        // TODO clean this up so it is less "hacky"
        final String decodedFragment;
        try {
            decodedFragment = URLDecoder.decode(fragment, "UTF-8");
        } catch (final UnsupportedEncodingException e) {
            // should not happen.
            throw new IllegalStateException(e);
        }
        final URI combined;
        if (resource != null) {
            combined = URI.create(gitRepositoryUri
                    + resource.replace("##", "#"));
        } else {
            combined = URI.create(gitRepositoryUri);
        }
        final String slashAppendedUri = combined.toASCIIString();
        final URI resolvedUri = URI.create(slashAppendedUri).resolve(
                decodedFragment.replace(" ", "%2520"));
        final StringBuilder resolved = new StringBuilder(
                resolvedUri.toASCIIString());

        final int lastQuestionMark = resolved.lastIndexOf("?");
        if (lastQuestionMark == -1) {
            return new GitUri(URI.create(format("%s?%s#%s", gitRepositoryUri,
                    branchName, decodedFragment.replace(" ", "%20"))));
        }
        final int lastRelevantSlash = resolved.indexOf("/", lastQuestionMark);
        final int lastRelevantHash = resolved.indexOf("#", lastQuestionMark);

        if (lastRelevantSlash > 0 && lastRelevantHash >= lastRelevantSlash) {
            resolved.insert(lastRelevantSlash, '#');
        }

        return new GitUri(URI.create(resolved.toString()));
    }

    @Override
    public String toString() {
        return new ToStringBuilder(this).append(gitRepositoryUri)
                .append(branchName).append(resource).build();
    }
}