Package org.eclipse.egit.ui.internal.decorators

Source Code of org.eclipse.egit.ui.internal.decorators.GitDocument

/*******************************************************************************
* Copyright (C) 2008, 2009 Robin Rosenberg <robin.rosenberg@dewire.com>
*
* 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
*******************************************************************************/
package org.eclipse.egit.ui.internal.decorators;

import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.WeakHashMap;

import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.egit.core.GitProvider;
import org.eclipse.egit.core.internal.CompareCoreUtils;
import org.eclipse.egit.core.project.RepositoryMapping;
import org.eclipse.egit.ui.Activator;
import org.eclipse.egit.ui.internal.UIText;
import org.eclipse.egit.ui.internal.trace.GitTraceLocation;
import org.eclipse.jface.text.Document;
import org.eclipse.jgit.diff.DiffConfig;
import org.eclipse.jgit.diff.DiffConfig.RenameDetectionType;
import org.eclipse.jgit.diff.DiffEntry;
import org.eclipse.jgit.diff.RenameDetector;
import org.eclipse.jgit.dircache.DirCacheIterator;
import org.eclipse.jgit.events.ListenerHandle;
import org.eclipse.jgit.events.RefsChangedEvent;
import org.eclipse.jgit.events.RefsChangedListener;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectLoader;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevTree;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.treewalk.CanonicalTreeParser;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.osgi.util.NLS;
import org.eclipse.team.core.RepositoryProvider;

class GitDocument extends Document implements RefsChangedListener {
  private final IResource resource;

  private ObjectId lastCommit;

  private ObjectId lastTree;

  private ObjectId lastBlob;

  private ListenerHandle myRefsChangedHandle;

  // Job that reloads the document when something has changed
  private Job reloadJob;

  private boolean disposed;

  static Map<GitDocument, Repository> doc2repo = new WeakHashMap<GitDocument, Repository>();

  static GitDocument create(final IResource resource) throws IOException {
    if (GitTraceLocation.QUICKDIFF.isActive())
      GitTraceLocation.getTrace().trace(
          GitTraceLocation.QUICKDIFF.getLocation(),
          "(GitDocument) create: " + resource); //$NON-NLS-1$
    GitDocument ret = null;
    if (RepositoryProvider.getProvider(resource.getProject()) instanceof GitProvider) {
      ret = new GitDocument(resource);
      ret.populate();
      final Repository repository = ret.getRepository();
      if (repository != null)
        ret.myRefsChangedHandle = repository.getListenerList()
            .addRefsChangedListener(ret);
    }
    return ret;
  }

  private GitDocument(IResource resource) {
    this.resource = resource;
    synchronized (doc2repo) {
      doc2repo.put(this, getRepository());
    }
  }

  private void setResolved(final AnyObjectId commit, final AnyObjectId tree,
      final AnyObjectId blob, final String value) {
    lastCommit = commit != null ? commit.copy() : null;
    lastTree = tree != null ? tree.copy() : null;
    lastBlob = blob != null ? blob.copy() : null;
    set(value);
    if (blob != null)
      if (GitTraceLocation.QUICKDIFF.isActive())
        GitTraceLocation
            .getTrace()
            .trace(
                GitTraceLocation.QUICKDIFF.getLocation(),
                "(GitDocument) resolved " + resource + " to " + lastBlob + " in " + lastCommit + "/" + lastTree); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
      else if (GitTraceLocation.QUICKDIFF.isActive())
        GitTraceLocation.getTrace().trace(
            GitTraceLocation.QUICKDIFF.getLocation(),
            "(GitDocument) unresolved " + resource); //$NON-NLS-1$
  }

  void populate() throws IOException {
    if (GitTraceLocation.QUICKDIFF.isActive())
      GitTraceLocation.getTrace().traceEntry(
          GitTraceLocation.QUICKDIFF.getLocation(), resource);

    // Do not populate if already disposed
    if (disposed)
      return;

    TreeWalk tw = null;
    RevWalk rw = null;
    try {
      RepositoryMapping mapping = RepositoryMapping.getMapping(resource);
      if (mapping == null) {
        setResolved(null, null, null, ""); //$NON-NLS-1$
        return;
      }
      final String gitPath = mapping.getRepoRelativePath(resource);
      if (gitPath == null) {
        setResolved(null, null, null, ""); //$NON-NLS-1$
        return;
      }
      final Repository repository = mapping.getRepository();
      String baseline = GitQuickDiffProvider.baseline.get(repository);
      if (baseline == null)
        baseline = Constants.HEAD;
      ObjectId commitId = repository.resolve(baseline);
      if (commitId != null) {
        if (commitId.equals(lastCommit)) {
          if (GitTraceLocation.QUICKDIFF.isActive())
            GitTraceLocation.getTrace().trace(
                GitTraceLocation.QUICKDIFF.getLocation(),
                "(GitDocument) already resolved"); //$NON-NLS-1$
          return;
        }
      } else {
        if (repository.getRef(Constants.HEAD) == null) {
          // Complain only if not an unborn branch
          String msg = NLS.bind(UIText.GitDocument_errorResolveQuickdiff,
              new Object[] { baseline, resource, repository });
          Activator.logError(msg, new Throwable());
        }
        setResolved(null, null, null, ""); //$NON-NLS-1$
        return;
      }
      rw = new RevWalk(repository);
      RevCommit baselineCommit;
      ObjectReader reader = null;
      String oldPath = gitPath;

      try {
        reader = repository.newObjectReader();
        baselineCommit = rw.parseCommit(commitId);
        DiffConfig diffConfig = repository.getConfig().get(
            DiffConfig.KEY);
        if (diffConfig.getRenameDetectionType() != RenameDetectionType.FALSE) {
          TreeWalk walk = new TreeWalk(repository);
          CanonicalTreeParser baseLineIterator = new CanonicalTreeParser();
          baseLineIterator.reset(reader, baselineCommit.getTree());
          walk.addTree(baseLineIterator);
          walk.addTree(new DirCacheIterator(repository.readDirCache()));
          List<DiffEntry> diffs = DiffEntry.scan(walk, true);
          RenameDetector renameDetector = new RenameDetector(
              repository);
          renameDetector.addAll(diffs);
          List<DiffEntry> renames = renameDetector.compute();
          for (DiffEntry e : renames) {
            if (e.getNewPath().equals(gitPath)) {
              oldPath = e.getOldPath();
              break;
            }
          }
        }
      } catch (IOException err) {
        String msg = NLS
            .bind(UIText.GitDocument_errorLoadCommit, new Object[] {
                commitId, baseline, resource, repository });
        Activator.logError(msg, err);
        setResolved(null, null, null, ""); //$NON-NLS-1$
        return;
      } finally {
        if (reader != null)
          reader.release();
        rw.dispose();
      }
      RevTree treeId = baselineCommit.getTree();
      if (treeId.equals(lastTree)) {
        if (GitTraceLocation.QUICKDIFF.isActive())
          GitTraceLocation.getTrace().trace(
              GitTraceLocation.QUICKDIFF.getLocation(),
              "(GitDocument) already resolved"); //$NON-NLS-1$
        return;
      }

      tw = TreeWalk.forPath(repository, oldPath, treeId);
      if (tw == null) {
        if (GitTraceLocation.QUICKDIFF.isActive())
          GitTraceLocation
              .getTrace()
              .trace(
                  GitTraceLocation.QUICKDIFF.getLocation(),
                  "(GitDocument) resource " + resource + " not found in " + treeId + " in " + repository + ", baseline=" + baseline); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
        setResolved(null, null, null, ""); //$NON-NLS-1$
        return;
      }
      ObjectId id = tw.getObjectId(0);
      if (id.equals(ObjectId.zeroId())) {
        setResolved(null, null, null, ""); //$NON-NLS-1$
        String msg = NLS
            .bind(UIText.GitDocument_errorLoadTree, new Object[] {
                treeId.getName(), baseline, resource, repository });
        Activator.logError(msg, new Throwable());
        setResolved(null, null, null, ""); //$NON-NLS-1$
        return;
      }
      if (!id.equals(lastBlob)) {
        if (GitTraceLocation.QUICKDIFF.isActive())
          GitTraceLocation.getTrace().trace(
              GitTraceLocation.QUICKDIFF.getLocation(),
              "(GitDocument) compareTo: " + baseline); //$NON-NLS-1$
        ObjectLoader loader = repository.open(id, Constants.OBJ_BLOB);
        byte[] bytes = loader.getBytes();
        String charset;
        charset = CompareCoreUtils.getResourceEncoding(resource);
        // Finally we could consider validating the content with respect
        // to the content. We don't do that here.
        String s = new String(bytes, charset);
        setResolved(commitId, treeId, id, s);
        if (GitTraceLocation.QUICKDIFF.isActive())
          GitTraceLocation
              .getTrace()
              .trace(GitTraceLocation.QUICKDIFF.getLocation(),
                  "(GitDocument) has reference doc, size=" + s.length() + " bytes"); //$NON-NLS-1$ //$NON-NLS-2$
      } else {
        if (GitTraceLocation.QUICKDIFF.isActive())
          GitTraceLocation.getTrace().trace(
              GitTraceLocation.QUICKDIFF.getLocation(),
              "(GitDocument) already resolved"); //$NON-NLS-1$
      }
    } finally {
      if (tw != null)
        tw.release();
      if (rw != null)
        rw.release();
      if (GitTraceLocation.QUICKDIFF.isActive())
        GitTraceLocation.getTrace().traceExit(
            GitTraceLocation.QUICKDIFF.getLocation());
    }

  }

  void dispose() {
    if (GitTraceLocation.QUICKDIFF.isActive())
      GitTraceLocation.getTrace().trace(
          GitTraceLocation.QUICKDIFF.getLocation(),
          "(GitDocument) dispose: " + resource); //$NON-NLS-1$
    synchronized (doc2repo) {
      doc2repo.remove(this);
    }
    if (myRefsChangedHandle != null) {
      myRefsChangedHandle.remove();
      myRefsChangedHandle = null;
    }
    cancelReloadJob();
    disposed = true;
  }

  public void onRefsChanged(final RefsChangedEvent event) {
    cancelReloadJob();

    reloadJob = new Job(UIText.GitDocument_ReloadJobName) {
      @Override
      protected IStatus run(IProgressMonitor monitor) {
        try {
          populate();
          return Status.OK_STATUS;
        } catch (IOException e) {
          return Activator.createErrorStatus(
              UIText.GitDocument_ReloadJobError, e);
        }
      }
    };
    reloadJob.schedule();
  }

  private void cancelReloadJob() {
    if (reloadJob != null && reloadJob.getState() != Job.NONE)
      reloadJob.cancel();
  }

  private Repository getRepository() {
    RepositoryMapping mapping = RepositoryMapping.getMapping(resource);
    return (mapping != null) ? mapping.getRepository() : null;
  }

  /**
   * A change occurred to a repository. Update any GitDocument instances
   * referring to such repositories.
   *
   * @param repository
   *            Repository which changed
   * @throws IOException
   */
  static void refreshRelevant(final Repository repository) throws IOException {
    final Entry[] docs;
    synchronized (doc2repo) {
      docs = doc2repo.entrySet().toArray(new Entry[doc2repo.size()]);
    }
    for (Entry doc : docs)
      if (doc.getValue() == repository)
        ((GitDocument) doc.getKey()).populate();
  }
}
TOP

Related Classes of org.eclipse.egit.ui.internal.decorators.GitDocument

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.