PlotWalk.java

  1. /*
  2.  * Copyright (C) 2008-2018, Robin Rosenberg <robin.rosenberg@dewire.com>
  3.  * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> and others
  4.  *
  5.  * This program and the accompanying materials are made available under the
  6.  * terms of the Eclipse Distribution License v. 1.0 which is available at
  7.  * https://www.eclipse.org/org/documents/edl-v10.php.
  8.  *
  9.  * SPDX-License-Identifier: BSD-3-Clause
  10.  */

  11. package org.eclipse.jgit.revplot;

  12. import static org.eclipse.jgit.lib.Constants.R_HEADS;
  13. import static org.eclipse.jgit.lib.Constants.R_REMOTES;
  14. import static org.eclipse.jgit.lib.Constants.R_TAGS;

  15. import java.io.IOException;
  16. import java.util.Arrays;
  17. import java.util.Collection;
  18. import java.util.Collections;
  19. import java.util.Comparator;
  20. import java.util.HashMap;
  21. import java.util.HashSet;
  22. import java.util.Map;
  23. import java.util.Set;

  24. import org.eclipse.jgit.errors.IncorrectObjectTypeException;
  25. import org.eclipse.jgit.errors.MissingObjectException;
  26. import org.eclipse.jgit.internal.JGitText;
  27. import org.eclipse.jgit.lib.AnyObjectId;
  28. import org.eclipse.jgit.lib.PersonIdent;
  29. import org.eclipse.jgit.lib.Ref;
  30. import org.eclipse.jgit.lib.Repository;
  31. import org.eclipse.jgit.revwalk.RevCommit;
  32. import org.eclipse.jgit.revwalk.RevObject;
  33. import org.eclipse.jgit.revwalk.RevSort;
  34. import org.eclipse.jgit.revwalk.RevTag;
  35. import org.eclipse.jgit.revwalk.RevWalk;

  36. /**
  37.  * Specialized RevWalk for visualization of a commit graph.
  38.  */
  39. public class PlotWalk extends RevWalk {

  40.     private Map<AnyObjectId, Set<Ref>> additionalRefMap;

  41.     private Map<AnyObjectId, Set<Ref>> reverseRefMap;

  42.     private Repository repository;

  43.     /** {@inheritDoc} */
  44.     @Override
  45.     public void dispose() {
  46.         super.dispose();
  47.         if (reverseRefMap != null) {
  48.             reverseRefMap.clear();
  49.             reverseRefMap = null;
  50.         }
  51.         if (additionalRefMap != null) {
  52.             additionalRefMap.clear();
  53.             additionalRefMap = null;
  54.         }
  55.         repository = null;
  56.     }

  57.     /**
  58.      * Create a new revision walker for a given repository.
  59.      *
  60.      * @param repo
  61.      *            the repository the walker will obtain data from.
  62.      */
  63.     public PlotWalk(Repository repo) {
  64.         super(repo);
  65.         super.sort(RevSort.TOPO, true);
  66.         additionalRefMap = new HashMap<>();
  67.         repository = repo;
  68.     }

  69.     /**
  70.      * Add additional refs to the walk
  71.      *
  72.      * @param refs
  73.      *            additional refs
  74.      * @throws java.io.IOException
  75.      */
  76.     public void addAdditionalRefs(Iterable<Ref> refs) throws IOException {
  77.         for (Ref ref : refs) {
  78.             Set<Ref> set = additionalRefMap.get(ref.getObjectId());
  79.             if (set == null)
  80.                 set = Collections.singleton(ref);
  81.             else {
  82.                 set = new HashSet<>(set);
  83.                 set.add(ref);
  84.             }
  85.             additionalRefMap.put(ref.getObjectId(), set);
  86.         }
  87.     }

  88.     /** {@inheritDoc} */
  89.     @Override
  90.     public void sort(RevSort s, boolean use) {
  91.         if (s == RevSort.TOPO && !use)
  92.             throw new IllegalArgumentException(JGitText.get().topologicalSortRequired);
  93.         super.sort(s, use);
  94.     }

  95.     /** {@inheritDoc} */
  96.     @Override
  97.     protected RevCommit createCommit(AnyObjectId id) {
  98.         return new PlotCommit(id);
  99.     }

  100.     /** {@inheritDoc} */
  101.     @Override
  102.     public RevCommit next() throws MissingObjectException,
  103.             IncorrectObjectTypeException, IOException {
  104.         PlotCommit<?> pc = (PlotCommit) super.next();
  105.         if (pc != null)
  106.             pc.refs = getRefs(pc);
  107.         return pc;
  108.     }

  109.     private Ref[] getRefs(AnyObjectId commitId) {
  110.         if (reverseRefMap == null) {
  111.             reverseRefMap = repository.getAllRefsByPeeledObjectId();
  112.             for (Map.Entry<AnyObjectId, Set<Ref>> entry : additionalRefMap
  113.                     .entrySet()) {
  114.                 Set<Ref> set = reverseRefMap.get(entry.getKey());
  115.                 Set<Ref> additional = entry.getValue();
  116.                 if (set != null) {
  117.                     if (additional.size() == 1) {
  118.                         // It's an unmodifiable singleton set...
  119.                         additional = new HashSet<>(additional);
  120.                     }
  121.                     additional.addAll(set);
  122.                 }
  123.                 reverseRefMap.put(entry.getKey(), additional);
  124.             }
  125.             additionalRefMap.clear();
  126.             additionalRefMap = null;
  127.         }
  128.         Collection<Ref> list = reverseRefMap.get(commitId);
  129.         if (list == null) {
  130.             return PlotCommit.NO_REFS;
  131.         }
  132.         Ref[] tags = list.toArray(new Ref[0]);
  133.         Arrays.sort(tags, new PlotRefComparator());
  134.         return tags;
  135.     }

  136.     class PlotRefComparator implements Comparator<Ref> {
  137.         @Override
  138.         public int compare(Ref o1, Ref o2) {
  139.             try {
  140.                 RevObject obj1 = parseAny(o1.getObjectId());
  141.                 RevObject obj2 = parseAny(o2.getObjectId());
  142.                 long t1 = timeof(obj1);
  143.                 long t2 = timeof(obj2);
  144.                 if (t1 > t2)
  145.                     return -1;
  146.                 if (t1 < t2)
  147.                     return 1;
  148.             } catch (IOException e) {
  149.                 // ignore
  150.             }

  151.             int cmp = kind(o1) - kind(o2);
  152.             if (cmp == 0)
  153.                 cmp = o1.getName().compareTo(o2.getName());
  154.             return cmp;
  155.         }

  156.         long timeof(RevObject o) {
  157.             if (o instanceof RevCommit)
  158.                 return ((RevCommit) o).getCommitTime();
  159.             if (o instanceof RevTag) {
  160.                 RevTag tag = (RevTag) o;
  161.                 try {
  162.                     parseBody(tag);
  163.                 } catch (IOException e) {
  164.                     return 0;
  165.                 }
  166.                 PersonIdent who = tag.getTaggerIdent();
  167.                 return who != null ? who.getWhen().getTime() : 0;
  168.             }
  169.             return 0;
  170.         }

  171.         int kind(Ref r) {
  172.             if (r.getName().startsWith(R_TAGS))
  173.                 return 0;
  174.             if (r.getName().startsWith(R_HEADS))
  175.                 return 1;
  176.             if (r.getName().startsWith(R_REMOTES))
  177.                 return 2;
  178.             return 3;
  179.         }
  180.     }
  181. }