Package org.eclipse.egit.ui.internal.repository

Source Code of org.eclipse.egit.ui.internal.repository.RepositoriesViewContentProvider

/*******************************************************************************
* Copyright (c) 2010, 2013 SAP AG and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
*    Mathias Kinzler (SAP AG) - initial implementation
*    Laurent Goubet <laurent.goubet@obeo.fr - 404121
*******************************************************************************/
package org.eclipse.egit.ui.internal.repository;

import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.WeakHashMap;

import org.eclipse.core.commands.IStateListener;
import org.eclipse.core.commands.State;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.egit.core.RepositoryCache;
import org.eclipse.egit.core.RepositoryUtil;
import org.eclipse.egit.ui.Activator;
import org.eclipse.egit.ui.internal.CommonUtils;
import org.eclipse.egit.ui.internal.UIText;
import org.eclipse.egit.ui.internal.repository.tree.AdditionalRefNode;
import org.eclipse.egit.ui.internal.repository.tree.AdditionalRefsNode;
import org.eclipse.egit.ui.internal.repository.tree.BranchHierarchyNode;
import org.eclipse.egit.ui.internal.repository.tree.BranchesNode;
import org.eclipse.egit.ui.internal.repository.tree.StashedCommitNode;
import org.eclipse.egit.ui.internal.repository.tree.ErrorNode;
import org.eclipse.egit.ui.internal.repository.tree.FetchNode;
import org.eclipse.egit.ui.internal.repository.tree.FileNode;
import org.eclipse.egit.ui.internal.repository.tree.FolderNode;
import org.eclipse.egit.ui.internal.repository.tree.LocalNode;
import org.eclipse.egit.ui.internal.repository.tree.PushNode;
import org.eclipse.egit.ui.internal.repository.tree.RefNode;
import org.eclipse.egit.ui.internal.repository.tree.RemoteNode;
import org.eclipse.egit.ui.internal.repository.tree.RemoteTrackingNode;
import org.eclipse.egit.ui.internal.repository.tree.RemotesNode;
import org.eclipse.egit.ui.internal.repository.tree.RepositoryNode;
import org.eclipse.egit.ui.internal.repository.tree.RepositoryTreeNode;
import org.eclipse.egit.ui.internal.repository.tree.StashNode;
import org.eclipse.egit.ui.internal.repository.tree.SubmodulesNode;
import org.eclipse.egit.ui.internal.repository.tree.TagNode;
import org.eclipse.egit.ui.internal.repository.tree.TagsNode;
import org.eclipse.egit.ui.internal.repository.tree.WorkingDirNode;
import org.eclipse.egit.ui.internal.repository.tree.command.ToggleBranchHierarchyCommand;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.events.ListenerHandle;
import org.eclipse.jgit.events.RefsChangedEvent;
import org.eclipse.jgit.events.RefsChangedListener;
import org.eclipse.jgit.lib.ConfigConstants;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefDatabase;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevObject;
import org.eclipse.jgit.revwalk.RevTag;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.submodule.SubmoduleWalk;
import org.eclipse.jgit.transport.RemoteConfig;
import org.eclipse.jgit.transport.URIish;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.commands.ICommandService;

/**
* Content Provider for the Git Repositories View
*/
public class RepositoriesViewContentProvider implements ITreeContentProvider,
    IStateListener {
  private final RepositoryCache repositoryCache = org.eclipse.egit.core.Activator
      .getDefault().getRepositoryCache();

  private final State commandState;

  private boolean branchHierarchyMode = false;

  private Map<Repository, Map<String, Ref>> branchRefs = new WeakHashMap<Repository, Map<String, Ref>>();

  private Map<Repository, ListenerHandle> refsChangedListeners = new WeakHashMap<Repository, ListenerHandle>();

  /**
   * Constructs this instance
   */
  public RepositoriesViewContentProvider() {
    ICommandService srv = CommonUtils.getService(PlatformUI.getWorkbench(), ICommandService.class);
    commandState = srv.getCommand(
        ToggleBranchHierarchyCommand.ID)
        .getState(ToggleBranchHierarchyCommand.TOGGLE_STATE);
    commandState.addListener(this);
    try {
      this.branchHierarchyMode = ((Boolean) commandState.getValue())
          .booleanValue();
    } catch (Exception e) {
      Activator.handleError(e.getMessage(), e, false);
    }
  }

  @SuppressWarnings("unchecked")
  public Object[] getElements(Object inputElement) {

    List<RepositoryTreeNode> nodes = new ArrayList<RepositoryTreeNode>();
    List<String> directories = new ArrayList<String>();
    RepositoryUtil repositoryUtil = Activator.getDefault()
        .getRepositoryUtil();

    if (inputElement instanceof Collection) {
      for (Iterator it = ((Collection) inputElement).iterator(); it
          .hasNext();) {
        Object next = it.next();
        if (next instanceof RepositoryTreeNode)
          nodes.add((RepositoryTreeNode) next);
        else if (next instanceof String)
          directories.add((String) next);
      }
    } else if (inputElement instanceof IWorkspaceRoot) {
      directories.addAll(repositoryUtil.getConfiguredRepositories());
    }

    for (String directory : directories) {
      try {
        File gitDir = new File(directory);
        if (gitDir.exists()) {
          RepositoryNode rNode = new RepositoryNode(null,
              repositoryCache.lookupRepository(gitDir));
          nodes.add(rNode);
        } else
          repositoryUtil.removeDir(gitDir);
      } catch (IOException e) {
        // ignore for now
      }
    }

    Collections.sort(nodes);
    return nodes.toArray();
  }

  public void dispose() {
    commandState.removeListener(this);
    for (ListenerHandle handle : refsChangedListeners.values())
      handle.remove();
    refsChangedListeners.clear();
  }

  public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
    // nothing
  }

  public Object[] getChildren(Object parentElement) {

    RepositoryTreeNode node = (RepositoryTreeNode) parentElement;
    Repository repo = node.getRepository();

    switch (node.getType()) {

    case BRANCHES: {
      List<RepositoryTreeNode> nodes = new ArrayList<RepositoryTreeNode>();
      nodes.add(new LocalNode(node, repo));
      nodes.add(new RemoteTrackingNode(node, repo));
      return nodes.toArray();
    }

    case LOCAL: {
      if (branchHierarchyMode) {
        BranchHierarchyNode hierNode = new BranchHierarchyNode(node,
            repo, new Path(Constants.R_HEADS));
        List<RepositoryTreeNode> children = new ArrayList<RepositoryTreeNode>();
        try {
          for (IPath path : hierNode.getChildPaths()) {
            children.add(new BranchHierarchyNode(node, node
                .getRepository(), path));
          }
          for (Ref ref : hierNode.getChildRefs()) {
            children.add(new RefNode(node, node.getRepository(),
                ref));
          }
        } catch (Exception e) {
          return handleException(e, node);
        }
        return children.toArray();
      } else {
        List<RepositoryTreeNode<Ref>> refs = new ArrayList<RepositoryTreeNode<Ref>>();
        try {
          for (Entry<String, Ref> refEntry : getRefs(repo, Constants.R_HEADS).entrySet()) {
            if (!refEntry.getValue().isSymbolic())
              refs.add(new RefNode(node, repo, refEntry
                  .getValue()));
          }
        } catch (Exception e) {
          return handleException(e, node);
        }
        return refs.toArray();
      }
    }

    case REMOTETRACKING: {
      if (branchHierarchyMode) {
        BranchHierarchyNode hierNode = new BranchHierarchyNode(node,
            repo, new Path(Constants.R_REMOTES));
        List<RepositoryTreeNode> children = new ArrayList<RepositoryTreeNode>();
        try {
          for (IPath path : hierNode.getChildPaths()) {
            children.add(new BranchHierarchyNode(node, node
                .getRepository(), path));
          }
          for (Ref ref : hierNode.getChildRefs()) {
            children.add(new RefNode(node, node.getRepository(),
                ref));
          }
        } catch (Exception e) {
          return handleException(e, node);
        }
        return children.toArray();
      } else {
        List<RepositoryTreeNode<Ref>> refs = new ArrayList<RepositoryTreeNode<Ref>>();
        try {
          for (Entry<String, Ref> refEntry : getRefs(repo, Constants.R_REMOTES).entrySet()) {
            if (!refEntry.getValue().isSymbolic())
              refs.add(new RefNode(node, repo, refEntry
                  .getValue()));
          }
        } catch (Exception e) {
          return handleException(e, node);
        }

        return refs.toArray();
      }
    }

    case BRANCHHIERARCHY: {
      BranchHierarchyNode hierNode = (BranchHierarchyNode) node;
      List<RepositoryTreeNode> children = new ArrayList<RepositoryTreeNode>();
      try {
        for (IPath path : hierNode.getChildPaths()) {
          children.add(new BranchHierarchyNode(node, node
              .getRepository(), path));
        }
        for (Ref ref : hierNode.getChildRefs()) {
          children.add(new RefNode(node, node.getRepository(), ref));
        }
      } catch (IOException e) {
        return handleException(e, node);
      }
      return children.toArray();
    }

    case TAGS: {
      return getTagsChildren(node, repo);
    }

    case ADDITIONALREFS: {
      List<RepositoryTreeNode<Ref>> refs = new ArrayList<RepositoryTreeNode<Ref>>();
      try {
        for (Entry<String, Ref> refEntry : getRefs(repo, RefDatabase.ALL).entrySet()) {
          String name=refEntry.getKey();
          if (!(name.startsWith(Constants.R_HEADS) || name.startsWith(Constants.R_TAGS)|| name.startsWith(Constants.R_REMOTES)))
            refs.add(new AdditionalRefNode(node, repo, refEntry
                .getValue()));
        }
        for (Ref r : repo.getRefDatabase().getAdditionalRefs())
          refs.add(new AdditionalRefNode(node, repo, r));
      } catch (Exception e) {
        return handleException(e, node);
      }
      return refs.toArray();
    }

    case REMOTES: {
      List<RepositoryTreeNode<String>> remotes = new ArrayList<RepositoryTreeNode<String>>();

      Repository rep = node.getRepository();

      Set<String> configNames = rep.getConfig().getSubsections(
          RepositoriesView.REMOTE);

      for (String configName : configNames) {
        remotes.add(new RemoteNode(node, repo, configName));
      }

      return remotes.toArray();
    }

    case REPO: {

      List<RepositoryTreeNode<? extends Object>> nodeList = new ArrayList<RepositoryTreeNode<? extends Object>>();
      nodeList.add(new BranchesNode(node, repo));
      nodeList.add(new TagsNode(node, repo));
      nodeList.add(new AdditionalRefsNode(node, repo));
      final boolean bare = repo.isBare();
      if (!bare)
        nodeList.add(new WorkingDirNode(node, repo));
      nodeList.add(new RemotesNode(node, repo));
      if(!bare && hasStashedCommits(repo))
        nodeList.add(new StashNode(node, repo));
      if (!bare && hasConfiguredSubmodules(repo))
        nodeList.add(new SubmodulesNode(node, repo));

      return nodeList.toArray();
    }

    case WORKINGDIR: {
      List<RepositoryTreeNode<File>> children = new ArrayList<RepositoryTreeNode<File>>();

      if (node.getRepository().isBare())
        return children.toArray();
      File workingDir = repo.getWorkTree();
      if (workingDir == null || !workingDir.exists())
        return children.toArray();

      File[] childFiles = workingDir.listFiles();
      Arrays.sort(childFiles, new Comparator<File>() {
        public int compare(File o1, File o2) {
          if (o1.isDirectory()) {
            if (o2.isDirectory()) {
              return o1.compareTo(o2);
            }
            return -1;
          } else if (o2.isDirectory()) {
            return 1;
          }
          return o1.compareTo(o2);
        }
      });
      for (File file : childFiles) {
        if (file.isDirectory()) {
          children.add(new FolderNode(node, repo, file));
        } else {
          children.add(new FileNode(node, repo, file));
        }
      }

      return children.toArray();
    }

    case FOLDER: {
      List<RepositoryTreeNode<File>> children = new ArrayList<RepositoryTreeNode<File>>();

      File parent = ((File) node.getObject());

      File[] childFiles = parent.listFiles();
      if (childFiles == null)
        return children.toArray();

      Arrays.sort(childFiles, new Comparator<File>() {
        public int compare(File o1, File o2) {
          if (o1.isDirectory()) {
            if (o2.isDirectory()) {
              return o1.compareTo(o2);
            }
            return -1;
          } else if (o2.isDirectory()) {
            return 1;
          }
          return o1.compareTo(o2);
        }
      });
      for (File file : childFiles) {
        if (file.isDirectory()) {
          children.add(new FolderNode(node, repo, file));
        } else {
          children.add(new FileNode(node, repo, file));
        }
      }

      return children.toArray();
    }

    case REMOTE: {

      List<RepositoryTreeNode<String>> children = new ArrayList<RepositoryTreeNode<String>>();

      String remoteName = (String) node.getObject();
      RemoteConfig rc;
      try {
        rc = new RemoteConfig(node.getRepository().getConfig(),
            remoteName);
      } catch (URISyntaxException e) {
        return handleException(e, node);
      }

      if (!rc.getURIs().isEmpty())
        children.add(new FetchNode(node, node.getRepository(), rc
            .getURIs().get(0).toPrivateString()));

      int uriCount = rc.getPushURIs().size();
      if (uriCount == 0 && !rc.getURIs().isEmpty())
        uriCount++;

      // show push if either a fetch or push URI is specified and
      // at least one push specification
      if (uriCount > 0) {
        URIish firstUri;
        if (!rc.getPushURIs().isEmpty())
          firstUri = rc.getPushURIs().get(0);
        else
          firstUri = rc.getURIs().get(0);

        if (uriCount == 1)
          children.add(new PushNode(node, node.getRepository(),
              firstUri.toPrivateString()));
        else
          children.add(new PushNode(node, node.getRepository(),
              firstUri.toPrivateString() + "...")); //$NON-NLS-1$
      }
      return children.toArray();

    }

    case SUBMODULES:
      List<RepositoryNode> children = new ArrayList<RepositoryNode>();
      try {
        SubmoduleWalk walk = SubmoduleWalk.forIndex(node
            .getRepository());
        while (walk.next()) {
          Repository subRepo = walk.getRepository();
          if (subRepo != null) {
            Repository cachedRepo = null;
            try {
              cachedRepo = repositoryCache
                .lookupRepository(subRepo.getDirectory());
            } finally {
              subRepo.close();
            }
            if (cachedRepo != null)
              children.add(new RepositoryNode(node, cachedRepo));
          }
        }
      } catch (IOException e) {
        handleException(e, node);
      }
      return children.toArray();
    case STASH:
      List<StashedCommitNode> stashNodes = new ArrayList<StashedCommitNode>();
      int index = 0;
      try {
        for (RevCommit commit : Git.wrap(repo).stashList().call())
          stashNodes.add(new StashedCommitNode(node, repo, index++,
              commit));
      } catch (Exception e) {
        handleException(e, node);
      }
      return stashNodes.toArray();
    case FILE:
      // fall through
    case REF:
      // fall through
    case PUSH:
      // fall through
    case TAG:
      // fall through
    case FETCH:
      // fall through
    case ERROR:
      // fall through
    case STASHED_COMMIT:
      // fall through
    case ADDITIONALREF:
      return null;

    }

    return null;

  }

  private Object[] getTagsChildren(RepositoryTreeNode parentNode,
      Repository repo) {
    List<RepositoryTreeNode<Ref>> nodes = new ArrayList<RepositoryTreeNode<Ref>>();

    RevWalk walk = new RevWalk(repo);
    walk.setRetainBody(true);
    try {
      Map<String, Ref> tagRefs = getRefs(repo, Constants.R_TAGS);
      for (Ref tagRef : tagRefs.values()) {
        ObjectId objectId = tagRef.getLeaf().getObjectId();
        RevObject revObject = walk.parseAny(objectId);
        RevObject peeledObject = walk.peel(revObject);
        TagNode tagNode = createTagNode(parentNode, repo, tagRef,
            revObject, peeledObject);
        nodes.add(tagNode);
      }
    } catch (IOException e) {
      return handleException(e, parentNode);
    } finally {
      walk.release();
    }

    return nodes.toArray();
  }

  private TagNode createTagNode(RepositoryTreeNode parentNode,
      Repository repo, Ref ref, RevObject revObject,
      RevObject peeledObject) {
    boolean annotated = (revObject instanceof RevTag);
    if (peeledObject instanceof RevCommit) {
      RevCommit commit = (RevCommit) peeledObject;
      String id = commit.getId().name();
      String message = commit.getShortMessage();
      return new TagNode(parentNode, repo, ref, annotated, id, message);
    } else {
      return new TagNode(parentNode, repo, ref, annotated, "", ""); //$NON-NLS-1$ //$NON-NLS-2$
    }
  }

  private Object[] handleException(Exception e, RepositoryTreeNode parentNode) {
    Activator.handleError(e.getMessage(), e, false);
    // add a node indicating that there was an Exception
    String message = e.getMessage();
    if (message == null)
      return new Object[] { new ErrorNode(parentNode, parentNode
          .getRepository(),
          UIText.RepositoriesViewContentProvider_ExceptionNodeText) };
    else
      return new Object[] { new ErrorNode(parentNode, parentNode
          .getRepository(), message) };
  }

  public Object getParent(Object element) {
    if (element instanceof RepositoryTreeNode)
      return ((RepositoryTreeNode) element).getParent();
    return null;
  }

  public boolean hasChildren(Object element) {
    // for some of the nodes we can optimize this call
    RepositoryTreeNode node = (RepositoryTreeNode) element;
    Repository repo = node.getRepository();
    switch (node.getType()) {
    case BRANCHES:
      return true;
    case REPO:
      return true;
    case ADDITIONALREFS:
      return true;
    case SUBMODULES:
      return true;
    case TAGS:
      try {
        return !getRefs(repo, Constants.R_TAGS).isEmpty();
      } catch (IOException e) {
        return true;
      }
    case WORKINGDIR:
      if (node.getRepository().isBare())
        return false;
      File workingDir = repo.getWorkTree();
      if (workingDir == null || !workingDir.exists())
        return false;
      return workingDir.listFiles().length > 0;
    default:
      Object[] children = getChildren(element);
      return children != null && children.length > 0;
    }
  }

  public void handleStateChange(State state, Object oldValue) {
    try {
      this.branchHierarchyMode = ((Boolean) state.getValue())
          .booleanValue();
    } catch (Exception e) {
      Activator.handleError(e.getMessage(), e, false);
    }
  }

  private synchronized Map<String, Ref> getRefs(final Repository repo, final String prefix) throws IOException {
    Map<String, Ref> allRefs = branchRefs.get(repo);
    if (allRefs == null) {
      allRefs = repo.getRefDatabase().getRefs(RefDatabase.ALL);
      branchRefs.put(repo, allRefs);
      if (refsChangedListeners.get(repo) == null) {
        RefsChangedListener listener = new RefsChangedListener() {
          public void onRefsChanged(RefsChangedEvent event) {
            synchronized (RepositoriesViewContentProvider.this) {
              branchRefs.remove(repo);
            }
          }
        };
        refsChangedListeners.put(repo, repo.getListenerList()
            .addRefsChangedListener(listener));
      }
    }
    if (prefix.equals(RefDatabase.ALL))
      return allRefs;

    Map<String, Ref> filtered = new HashMap<String, Ref>();
    for (Map.Entry<String, Ref> entry : allRefs.entrySet()) {
      if (entry.getKey().startsWith(prefix))
        filtered.put(entry.getKey(), entry.getValue());
    }
    return filtered;
  }

  /**
   * Does the repository have any submodule configurations?
   * <p>
   * This method checks for a '.gitmodules' file at the root of the working
   * directory or any 'submodule' sections in the repository's config file
   *
   * @param repository
   * @return true if submodules, false otherwise
   */
  private boolean hasConfiguredSubmodules(final Repository repository) {
    if (new File(repository.getWorkTree(), Constants.DOT_GIT_MODULES)
        .isFile())
      return true;
    return !repository.getConfig()
        .getSubsections(ConfigConstants.CONFIG_SUBMODULE_SECTION)
        .isEmpty();
  }

  /**
   * Does the repository have any stashed commits?
   * <p>
   * This method checks for a {@link Constants#R_STASH} ref in the given
   * repository
   *
   * @param repository
   * @return true if stashed commits, false otherwise
   */
  private boolean hasStashedCommits(final Repository repository) {
    try {
      return repository.getRef(Constants.R_STASH) != null;
    } catch (IOException e) {
      return false;
    }
  }
}
TOP

Related Classes of org.eclipse.egit.ui.internal.repository.RepositoriesViewContentProvider

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.