comparison src/org/tmatesoft/hg/core/RepositoryTreeWalker.java @ 64:19e9e220bf68

Convenient commands constitute hi-level API. org.tmatesoft namespace, GPL2 statement
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Fri, 21 Jan 2011 05:56:43 +0100
parents
children 64bddc2dcc0e
comparison
equal deleted inserted replaced
63:a47530a2ea12 64:19e9e220bf68
1 /*
2 * Copyright (c) 2011 TMate Software Ltd
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 2 of the License.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * For information on how to redistribute this software under
14 * the terms of a license other than GNU General Public License
15 * contact TMate Software at support@svnkit.com
16 */
17 package org.tmatesoft.hg.core;
18
19 import static com.tmate.hgkit.ll.HgRepository.TIP;
20
21 import java.util.ConcurrentModificationException;
22 import java.util.LinkedHashMap;
23 import java.util.LinkedList;
24 import java.util.List;
25
26 import org.tmatesoft.hg.core.LogCommand.FileRevision;
27 import org.tmatesoft.hg.util.PathPool;
28
29 import com.tmate.hgkit.ll.HgManifest;
30 import com.tmate.hgkit.ll.HgRepository;
31 import com.tmate.hgkit.ll.Nodeid;
32
33 /**
34 *
35 * @author Artem Tikhomirov
36 * @author TMate Software Ltd.
37 */
38 public class RepositoryTreeWalker {
39
40 private final HgRepository repo;
41 private Path.Matcher matcher;
42 private int startRev = 0, endRev = TIP;
43 private Handler visitor;
44 private boolean needDirs = false;
45
46 private final Mediator mediator = new Mediator();
47
48 public RepositoryTreeWalker(HgRepository hgRepo) {
49 this.repo = hgRepo;
50 }
51
52 public RepositoryTreeWalker range(int rev1, int rev2) {
53 // if manifest range is different from that of changelog, need conversion utils (external?)
54 throw HgRepository.notImplemented();
55 }
56
57 public RepositoryTreeWalker dirs(boolean include) {
58 // XXX whether directories with directories only are include or not
59 // now lists only directories with files
60 needDirs = include;
61 return this;
62 }
63
64 /**
65 * Limit manifest walk to a subset of files.
66 * @param pathMatcher - filter, pass <code>null</code> to clear.
67 * @return <code>this</code> instance for convenience
68 */
69 public RepositoryTreeWalker match(Path.Matcher pathMatcher) {
70 matcher = pathMatcher;
71 return this;
72 }
73
74 public void walk(Handler handler) {
75 if (handler == null) {
76 throw new IllegalArgumentException();
77 }
78 if (visitor != null) {
79 throw new ConcurrentModificationException();
80 }
81 try {
82 visitor = handler;
83 mediator.start();
84 repo.getManifest().walk(startRev, endRev, mediator);
85 } finally {
86 visitor = null;
87 mediator.done();
88 }
89 }
90
91 /**
92 * Callback to walk file/directory tree of a revision
93 */
94 public interface Handler {
95 void begin(Nodeid manifestRevision);
96 void dir(Path p); // optionally invoked (if walker was configured to spit out directories) prior to any files from this dir and subdirs
97 void file(FileRevision fileRevision); // XXX allow to check p is invalid (df.exists())
98 void end(Nodeid manifestRevision);
99 }
100
101 // I'd rather let RepositoryTreeWalker implement HgManifest.Inspector directly, but this pollutes API alot
102 private class Mediator implements HgManifest.Inspector {
103 private PathPool pathPool;
104 private List<FileRevision> manifestContent;
105 private Nodeid manifestNodeid;
106
107 public void start() {
108 pathPool = new PathPool(repo.getPathHelper());
109 }
110
111 public void done() {
112 manifestContent = null;
113 pathPool = null;
114 }
115
116 public boolean begin(int revision, Nodeid nid) {
117 if (needDirs && manifestContent == null) {
118 manifestContent = new LinkedList<FileRevision>();
119 }
120 visitor.begin(manifestNodeid = nid);
121 return true;
122 }
123 public boolean end(int revision) {
124 if (needDirs) {
125 LinkedHashMap<Path, LinkedList<FileRevision>> breakDown = new LinkedHashMap<Path, LinkedList<FileRevision>>();
126 for (FileRevision fr : manifestContent) {
127 Path filePath = fr.getPath();
128 Path dirPath = pathPool.parent(filePath);
129 LinkedList<FileRevision> revs = breakDown.get(dirPath);
130 if (revs == null) {
131 revs = new LinkedList<FileRevision>();
132 breakDown.put(dirPath, revs);
133 }
134 revs.addLast(fr);
135 }
136 for (Path dir : breakDown.keySet()) {
137 visitor.dir(dir);
138 for (FileRevision fr : breakDown.get(dir)) {
139 visitor.file(fr);
140 }
141 }
142 manifestContent.clear();
143 }
144 visitor.end(manifestNodeid);
145 manifestNodeid = null;
146 return true;
147 }
148 public boolean next(Nodeid nid, String fname, String flags) {
149 Path p = pathPool.path(fname);
150 if (matcher != null && !matcher.accept(p)) {
151 return true;
152 }
153 FileRevision fr = new FileRevision(repo, nid, p);
154 if (needDirs) {
155 manifestContent.add(fr);
156 } else {
157 visitor.file(fr);
158 }
159 return true;
160 }
161 }
162 }