code: package org.rug.web.helpers;
  
  import org.eclipse.jgit.api.Git;
  import org.eclipse.jgit.api.errors.GitAPIException;
  import org.eclipse.jgit.transport.CredentialsProvider;
  import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
  import org.rug.web.credentials.Credentials;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  import java.io.File;
  import java.io.IOException;
  import java.nio.file.Path;
  import java.nio.file.Paths;
  
  /**
   * Helper class that will be used to clone locally a remote Git repository.
   */
  public class RemoteProjectFetcher {
  
      private final static Logger logger = LoggerFactory.getLogger(RemoteProjectFetcher.class);
      private final Path destination;
  
      public RemoteProjectFetcher(Path destination){
          this.destination = destination;
      }
  
      /**
       * Helper class that will take a path as an argument, and will return a Path object
       * which holds the location of the project to be analysed.
       */
      public Path fetchProject(String linkOrName, Credentials credentials) throws IOException, GitAPIException {
          if (this.isValidGitLink(linkOrName)) {
              var name = getProjectName(linkOrName);
              var path = Paths.get(destination.toAbsolutePath().toString(), name);
              try {
                  CredentialsProvider credentialsProvider = getCredentialsProvider(credentials);
                  if (!this.checkIfAlreadyCloned(path.toFile())) {
                      // The project is not present locally, clone it from the remote repository.
                      var git = Git.cloneRepository()
                              .setURI(linkOrName)
                              .setCredentialsProvider(credentialsProvider)
                              .setRemote("origin")
                              .setDirectory(path.toFile()).call();
                      git.close();
                  } else {
                      // The project has been cloned before, update the branch to the latest version.
                      var git = Git.open(path.toFile());
                      var result = git.pull()
                              .setRemoteBranchName("HEAD")
                              .setCredentialsProvider(credentialsProvider)
                              .setRemote("origin")
                              .call();
                      if (!result.isSuccessful()) {
                          logger.warn(String.format(
                                  "Was not able to pull the latest changes from the repository %s",
                                  name
                          ));
                      }
                  }
                  return path;
              } catch (IOException | GitAPIException e) {
                  e.printStackTrace();
                  logger.error(String.format(
                          "Was not able to access the given repository %s",
                          name
                  ));
                  throw e;
              }
          }
          return Paths.get(destination.toAbsolutePath().toString(), linkOrName);
      }
  
      private CredentialsProvider getCredentialsProvider(Credentials credentials) {
          if (credentials.getAuthToken() != null && !credentials.getAuthToken().isEmpty()) {
              return new UsernamePasswordCredentialsProvider(credentials.getAuthToken(), "");
          }
          if(credentials.getUsername() != null && credentials.getPassword() != null){
              return new UsernamePasswordCredentialsProvider(credentials.getUsername(), credentials.getPassword());
          }
          return CredentialsProvider.getDefault();
      }
  
      /**
       * Will check if a folder with the same name exists a the given location.
       *
       * @param file The folder location
       * @return boolean
       */
      public boolean checkIfAlreadyCloned(File file) {
          var list = file.list();
          return file.exists() && file.isDirectory() && list != null && list.length > 0;
      }
  
      /**
       * Will return the name of a project extracted from the path given as input.
       * - If the path is a Git link, it will look between the last "/" and "." symbols.
       * - If the path is just a name, it will return the same string.
       *
       * @param linkOrName
       * @return
       */
      public String getProjectName(String linkOrName){
          var slashIndex = linkOrName.lastIndexOf("/");
          if (linkOrName.endsWith("/")) {
              linkOrName = linkOrName.substring(0, slashIndex);
              slashIndex = linkOrName.lastIndexOf("/");
          }
          if (linkOrName.endsWith(".git")){
              linkOrName = linkOrName.substring(0, linkOrName.lastIndexOf("."));
          }
          return slashIndex == -1 ? linkOrName : linkOrName.substring(slashIndex + 1);
      }
  
      public boolean isValidGitLink(String link) {
          return link.matches("^http(s)?:.*");
      }
  
  }
  