/*******************************************************************************
* Copyright (c) 2010, 2012 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
*******************************************************************************/
package org.eclipse.egit.ui.internal.repository.tree;
import java.io.File;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.PlatformObject;
import org.eclipse.egit.ui.internal.CommonUtils;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
/**
* A node in the Git Repositories view tree
*
* @param <T>
* the type
*/
public abstract class RepositoryTreeNode<T> extends PlatformObject implements Comparable<RepositoryTreeNode> {
private Repository myRepository;
private T myObject;
private final RepositoryTreeNodeType myType;
private final RepositoryTreeNode myParent;
/**
* Constructs a node
*
* @param parent
* the parent (may be null)
* @param type
* the type
* @param repository
* the {@link Repository}
* @param treeObject
* an object (depending on the type)
*/
public RepositoryTreeNode(RepositoryTreeNode parent,
RepositoryTreeNodeType type, Repository repository, T treeObject) {
myParent = parent;
myRepository = repository;
myType = type;
myObject = treeObject;
}
/**
* @return the parent, or null
*/
public RepositoryTreeNode getParent() {
return myParent;
}
/**
* @return the type
*/
public RepositoryTreeNodeType getType() {
return myType;
}
/**
* @return the repository
*/
public Repository getRepository() {
return myRepository;
}
/**
* @return the path of the file, folder or repository
*/
public IPath getPath() {
Repository repository = getRepository();
if (repository == null) {
return null;
}
return new Path(getRepository().getWorkTree().getAbsolutePath());
}
/**
* Removes the references to the repository and to the object. <strong>Call
* only after this node has been removed from the view!</strong>
* <p>
* The CommonViewer framework keeps on to a hard reference to the last
* selection, even if that no longer will appear in the view. Moreover, the
* WorkbenchSourceProvider may also hold such a reference to the
* RepositoryNode(s). This will preclude for some time the garbage
* collection and eventual removal of the Repository instance
* (RepositoryCache relies on WeakReference semantics). Therefore, this
* operation provides a means to clear the reference to the Repository in a
* now otherwise unreferenced RepositoryNode.
*/
public void clear() {
myRepository = null;
myObject = null;
}
/**
* Depending on the node type, the returned type is:
*
* <table border=1>
* <th>Type</th>
* <th>Object type</th>
* <tr>
* <td>{@link RepositoryTreeNodeType#BRANCHES}</td>
* <td>{@link String}</td>
* </tr>
* <tr>
* <td>{@link RepositoryTreeNodeType#BRANCHHIERARCHY}</td>
* <td>{@link IPath}</td>
* </tr>
* <tr>
* <td>{@link RepositoryTreeNodeType#LOCAL}</td>
* <td>{@link String}</td>
* </tr>
* <tr>
* <td>{@link RepositoryTreeNodeType#REMOTETRACKING}</td>
* <td>{@link String}</td>
* </tr>
* <tr>
* <td>{@link RepositoryTreeNodeType#TAGS}</td>
* <td>{@link String}</td>
* </tr>
* <tr>
* <td>{@link RepositoryTreeNodeType#REMOTE}</td>
* <td>{@link String}</td>
* </tr>
* <tr>
* <td>{@link RepositoryTreeNodeType#REMOTES}</td>
* <td>{@link String}</td>
* </tr>
* <tr>
* <td>{@link RepositoryTreeNodeType#REPO}</td>
* <td>{@link Repository}</td>
* </tr>
* </table>
*
* @return the type-specific object
*/
public T getObject() {
return myObject;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
switch (myType) {
case REPO:
// fall through
case REMOTES:
// fall through
case LOCAL:
// fall through
case REMOTETRACKING:
// fall through
case BRANCHES:
// fall through
case ADDITIONALREFS:
// fall through
case SUBMODULES:
// fall through
case STASH:
// fall through
case WORKINGDIR:
result = prime
* result
+ ((myObject == null) ? 0 : ((Repository) myObject)
.getDirectory().hashCode());
break;
case REF:
// fall through
case TAG:
// fall through
case ADDITIONALREF:
result = prime
* result
+ ((myObject == null) ? 0 : ((Ref) myObject).getName()
.hashCode());
break;
case FILE:
// fall through
case FOLDER:
result = prime
* result
+ ((myObject == null) ? 0 : ((File) myObject).getPath()
.hashCode());
break;
case TAGS:
// fall through
case REMOTE:
// fall through
case PUSH:
// fall through
case FETCH:
// fall through
case BRANCHHIERARCHY:
// fall through
case STASHED_COMMIT:
// fall through
case ERROR:
result = prime * result
+ ((myObject == null) ? 0 : myObject.hashCode());
}
result = prime * result
+ ((myParent == null) ? 0 : myParent.hashCode());
result = prime
* result
+ ((myRepository == null) ? 0 : myRepository.getDirectory()
.hashCode());
result = prime * result + myType.hashCode();
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
RepositoryTreeNode other = (RepositoryTreeNode) obj;
if (myType == null) {
if (other.myType != null)
return false;
} else if (!myType.equals(other.myType))
return false;
if (myParent == null) {
if (other.myParent != null)
return false;
} else if (!myParent.equals(other.myParent))
return false;
if (myRepository == null) {
if (other.myRepository != null) {
return false;
}
} else {
if (other.myRepository == null) {
return false;
}
if (!myRepository.getDirectory()
.equals(other.myRepository.getDirectory())) {
return false;
}
}
if (myObject == null) {
if (other.myObject != null) {
return false;
}
} else {
if (other.myObject == null) {
return false;
}
if (!checkObjectsEqual(other.myObject)) {
return false;
}
}
return true;
}
@Override
public int compareTo(RepositoryTreeNode otherNode) {
int typeDiff = this.myType.ordinal() - otherNode.getType().ordinal();
if (typeDiff != 0)
return typeDiff;
// we only implement this for sorting, so we only have to
// implement this for nodes that can be on the same level
// i.e. siblings to each other
switch (myType) {
case BRANCHES:
// fall through
case LOCAL:
// fall through
case REMOTETRACKING:
// fall through
case BRANCHHIERARCHY:
return CommonUtils.STRING_ASCENDING_COMPARATOR.compare(
myObject.toString(), otherNode.getObject().toString());
case REMOTES:
// fall through
case ADDITIONALREFS:
// fall through
case TAGS:
// fall through
case ERROR:
// fall through
case SUBMODULES:
// fall through
case STASH:
// fall through
case WORKINGDIR:
return 0;
case FETCH:
// fall through
case PUSH:
// fall through
case REMOTE:
return CommonUtils.STRING_ASCENDING_COMPARATOR
.compare((String) myObject, (String) otherNode.getObject());
case FILE:
// fall through
case FOLDER:
return CommonUtils.STRING_ASCENDING_COMPARATOR
.compare(((File) myObject).getName(),
((File) otherNode.getObject()).getName());
case STASHED_COMMIT:
// ok for positive indexes < ~2 billion
return ((StashedCommitNode) this).getIndex()
- ((StashedCommitNode) otherNode).getIndex();
case TAG:
// fall through
case ADDITIONALREF:
// fall through
case REF:
return CommonUtils.REF_ASCENDING_COMPARATOR.compare((Ref) myObject,
(Ref) otherNode.getObject());
case REPO:
int nameCompare = CommonUtils.STRING_ASCENDING_COMPARATOR.compare(
getDirectoryContainingRepo((Repository) myObject).getName(),
getDirectoryContainingRepo(
(Repository) otherNode.getObject())
.getName());
if (nameCompare != 0)
return nameCompare;
// if the name is not unique, let's look at the whole path
return CommonUtils.STRING_ASCENDING_COMPARATOR.compare(
getDirectoryContainingRepo((Repository) myObject)
.getParentFile().getPath(),
getDirectoryContainingRepo(
(Repository) otherNode.getObject()).getParentFile()
.getPath());
}
return 0;
}
private File getDirectoryContainingRepo(Repository repo) {
if (!repo.isBare())
return repo.getDirectory().getParentFile();
else
return repo.getDirectory();
}
private boolean checkObjectsEqual(Object otherObject) {
switch (myType) {
case REPO:
// fall through
case REMOTES:
// fall through
case BRANCHES:
// fall through
case LOCAL:
// fall through
case REMOTETRACKING:
// fall through
case ADDITIONALREFS:
// fall through
case SUBMODULES:
// fall through
case STASH:
// fall through
case WORKINGDIR:
return ((Repository) myObject).getDirectory().equals(
((Repository) otherObject).getDirectory());
case REF:
// fall through
case TAG:
// fall through
case ADDITIONALREF:
return ((Ref) myObject).getName().equals(
((Ref) otherObject).getName());
case FOLDER:
// fall through
case FILE:
return ((File) myObject).getPath().equals(
((File) otherObject).getPath());
case ERROR:
// fall through
case REMOTE:
// fall through
case FETCH:
// fall through
case PUSH:
// fall through
case BRANCHHIERARCHY:
// fall through
case STASHED_COMMIT:
// fall through
case TAGS:
return myObject.equals(otherObject);
}
return false;
}
@Override
public Object getAdapter(Class adapter) {
if (Repository.class == adapter && myRepository != null) {
return myRepository;
}
if (myObject != null) {
if (adapter.isInstance(myObject)) {
return myObject;
}
}
return super.getAdapter(adapter);
}
@Override
public String toString() {
return "RepositoryNode[" + myType + ", " + myObject.toString() + "]"; //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$
}
}