comparison src/org/tmatesoft/hg/repo/ext/MqManager.java @ 464:1a3c18d57a8e smartgit3

MqManager evolution: same PatchRecord instances, list patch queues, detect active queue
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Thu, 21 Jun 2012 20:27:58 +0200
parents a0507a9f3da0
children 2078692eeb58
comparison
equal deleted inserted replaced
463:a0507a9f3da0 464:1a3c18d57a8e
18 18
19 import java.io.BufferedReader; 19 import java.io.BufferedReader;
20 import java.io.File; 20 import java.io.File;
21 import java.io.FileReader; 21 import java.io.FileReader;
22 import java.io.IOException; 22 import java.io.IOException;
23 import java.util.ArrayList;
24 import java.util.Collection;
23 import java.util.Collections; 25 import java.util.Collections;
26 import java.util.HashMap;
24 import java.util.LinkedList; 27 import java.util.LinkedList;
25 import java.util.List; 28 import java.util.List;
29 import java.util.Map;
26 30
27 import org.tmatesoft.hg.core.HgInvalidControlFileException; 31 import org.tmatesoft.hg.core.HgInvalidControlFileException;
28 import org.tmatesoft.hg.core.HgInvalidFileException; 32 import org.tmatesoft.hg.core.HgInvalidFileException;
29 import org.tmatesoft.hg.core.Nodeid; 33 import org.tmatesoft.hg.core.Nodeid;
30 import org.tmatesoft.hg.repo.HgInternals; 34 import org.tmatesoft.hg.repo.HgInternals;
42 public class MqManager { 46 public class MqManager {
43 47
44 private final HgRepository repo; 48 private final HgRepository repo;
45 private List<PatchRecord> applied = Collections.emptyList(); 49 private List<PatchRecord> applied = Collections.emptyList();
46 private List<PatchRecord> allKnown = Collections.emptyList(); 50 private List<PatchRecord> allKnown = Collections.emptyList();
51 private List<String> queueNames = Collections.emptyList();
52 private String activeQueue = "patches";
47 53
48 public MqManager(HgRepository hgRepo) { 54 public MqManager(HgRepository hgRepo) {
49 repo = hgRepo; 55 repo = hgRepo;
50 } 56 }
51 57
52 /** 58 /**
53 * Updates manager with up-to-date state of the mercurial queues. 59 * Updates manager with up-to-date state of the mercurial queues.
54 */ 60 */
55 public void refresh() throws HgInvalidControlFileException { 61 public void refresh() throws HgInvalidControlFileException {
62 applied = allKnown = Collections.emptyList();
63 queueNames = Collections.emptyList();
56 File repoDir = HgInternals.getRepositoryDir(repo); 64 File repoDir = HgInternals.getRepositoryDir(repo);
57 final LogFacility log = HgInternals.getContext(repo).getLog(); 65 final LogFacility log = HgInternals.getContext(repo).getLog();
58 final File fileStatus = new File(repoDir, "patches/status"); 66 final File fileStatus = new File(repoDir, "patches/status");
59 final File fileSeries = new File(repoDir, "patches/series"); 67 final File fileSeries = new File(repoDir, "patches/series");
60 try { 68 try {
69 File queues = new File(repoDir, "patches.queues");
70 if (queues.isFile()) {
71 LineReader lr = new LineReader(queues, log).trimLines(true).skipEmpty(true);
72 lr.read(new SimpleLineCollector(), queueNames = new LinkedList<String>());
73 }
74 File activeQueueFile = new File(repoDir, "patches.queue");
75 ArrayList<String> contents = new ArrayList<String>();
76 if (activeQueueFile.isFile()) {
77 new LineReader(activeQueueFile, log).read(new SimpleLineCollector(), contents);
78 if (contents.isEmpty()) {
79 log.warn(getClass(), "File %s with active queue name is empty", activeQueueFile.getName());
80 activeQueue = "patches";
81 } else {
82 activeQueue = contents.get(0);
83 }
84 } else {
85 activeQueue = "patches";
86 }
61 if (fileStatus.isFile()) { 87 if (fileStatus.isFile()) {
62 new LineReader(fileStatus, log).read(new LineConsumer<List<PatchRecord>>() { 88 new LineReader(fileStatus, log).read(new LineConsumer<List<PatchRecord>>() {
63 89
64 public boolean consume(String line, List<PatchRecord> result) throws IOException { 90 public boolean consume(String line, List<PatchRecord> result) throws IOException {
65 int sep = line.indexOf(':'); 91 int sep = line.indexOf(':');
73 return true; 99 return true;
74 } 100 }
75 }, applied = new LinkedList<PatchRecord>()); 101 }, applied = new LinkedList<PatchRecord>());
76 } 102 }
77 if (fileSeries.isFile()) { 103 if (fileSeries.isFile()) {
78 new LineReader(fileSeries, log).read(new LineConsumer<List<PatchRecord>>() { 104 final Map<String,PatchRecord> name2patch = new HashMap<String, PatchRecord>();
79 105 for (PatchRecord pr : applied) {
80 public boolean consume(String line, List<PatchRecord> result) throws IOException { 106 name2patch.put(pr.getName(), pr);
81 result.add(new PatchRecord(null, line, Path.create(".hg/patches/" + line))); 107 }
82 return true; 108 LinkedList<String> knownPatchNames = new LinkedList<String>();
83 } 109 new LineReader(fileSeries, log).read(new SimpleLineCollector(), knownPatchNames);
84 }, allKnown = new LinkedList<PatchRecord>()); 110 // XXX read other queues?
111 allKnown = new ArrayList<PatchRecord>(knownPatchNames.size());
112 for (String name : knownPatchNames) {
113 PatchRecord pr = name2patch.get(name);
114 if (pr == null) {
115 pr = new PatchRecord(null, name, Path.create(".hg/patches/" + name));
116 }
117 allKnown.add(pr);
118 }
85 } 119 }
86 } catch (HgInvalidFileException ex) { 120 } catch (HgInvalidFileException ex) {
87 HgInvalidControlFileException th = new HgInvalidControlFileException(ex.getMessage(), ex.getCause(), ex.getFile()); 121 HgInvalidControlFileException th = new HgInvalidControlFileException(ex.getMessage(), ex.getCause(), ex.getFile());
88 th.setStackTrace(ex.getStackTrace()); 122 th.setStackTrace(ex.getStackTrace());
89 throw th; 123 throw th;
90 } 124 }
91 } 125 }
126
127 static class SimpleLineCollector implements LineConsumer<Collection<String>> {
128
129 public boolean consume(String line, Collection<String> result) throws IOException {
130 result.add(line);
131 return true;
132 }
133 }
134
135 /**
136 * Number of patches not yet applied
137 * @return positive value when there are
138 */
139 public int getQueueSize() {
140 return getAllKnownPatches().size() - getAppliedPatches().size();
141 }
92 142
93 /** 143 /**
94 * Subset of the patches from the queue that were already applied to the repository 144 * Subset of the patches from the queue that were already applied to the repository
145 * <p>Analog of 'hg qapplied'
95 * 146 *
96 * <p>Clients shall call {@link #refresh()} prior to first use 147 * <p>Clients shall call {@link #refresh()} prior to first use
97 * @return collection of records in no particular order, may be empty if none applied 148 * @return collection of records in no particular order, may be empty if none applied
98 */ 149 */
99 public List<PatchRecord> getAppliedPatches() { 150 public List<PatchRecord> getAppliedPatches() {
108 */ 159 */
109 public List<PatchRecord> getAllKnownPatches() { 160 public List<PatchRecord> getAllKnownPatches() {
110 return Collections.unmodifiableList(allKnown); 161 return Collections.unmodifiableList(allKnown);
111 } 162 }
112 163
164 /**
165 * Name of the patch queue <code>hg qqueue --active</code> which is active now.
166 * @return patch queue name
167 */
168 public String getActiveQueueName() {
169 return activeQueue;
170 }
171
172 /**
173 * Patch queues known in the repository, <code>hg qqueue -l</code> analog.
174 * There's at least one patch queue (default one names 'patches'). Only one patch queue at a time is active.
175 *
176 * @return names of patch queues
177 */
178 public List<String> getQueueNames() {
179 return Collections.unmodifiableList(queueNames);
180 }
181
113 public class PatchRecord { 182 public class PatchRecord {
114 private final Nodeid nodeid; 183 private final Nodeid nodeid;
115 private final String name; 184 private final String name;
116 private final Path location; 185 private final Path location;
186
187 // hashCode/equals might be useful if cons becomes public
117 188
118 PatchRecord(Nodeid revision, String name, Path diffLocation) { 189 PatchRecord(Nodeid revision, String name, Path diffLocation) {
119 nodeid = revision; 190 nodeid = revision;
120 this.name = name; 191 this.name = name;
121 this.location = diffLocation; 192 this.location = diffLocation;
160 231
161 class LineReader { 232 class LineReader {
162 233
163 private final File file; 234 private final File file;
164 private final LogFacility log; 235 private final LogFacility log;
236 private boolean trimLines = true;
237 private boolean skipEmpty = true;
238 private String ignoreThatStars = null;
165 239
166 LineReader(File f, LogFacility logFacility) { 240 LineReader(File f, LogFacility logFacility) {
167 file = f; 241 file = f;
168 log = logFacility; 242 log = logFacility;
243 }
244
245 /**
246 * default: <code>true</code>
247 * <code>false</code> to return line as is
248 */
249 LineReader trimLines(boolean trim) {
250 trimLines = trim;
251 return this;
252 }
253
254 /**
255 * default: <code>true</code>
256 * <code>false</code> to pass empty lines to consumer
257 */
258 LineReader skipEmpty(boolean skip) {
259 skipEmpty = skip;
260 return this;
261 }
262
263 /**
264 * default: doesn't skip any line.
265 * set e.g. to "#" or "//" to skip lines that start with such prefix
266 */
267 LineReader ignoreLineComments(String lineStart) {
268 ignoreThatStars = lineStart;
269 return this;
169 } 270 }
170 271
171 <T> void read(LineConsumer<T> consumer, T paramObj) throws HgInvalidFileException { 272 <T> void read(LineConsumer<T> consumer, T paramObj) throws HgInvalidFileException {
172 BufferedReader statusFileReader = null; 273 BufferedReader statusFileReader = null;
173 try { 274 try {
174 // consumer.begin(file, paramObj); 275 // consumer.begin(file, paramObj);
175 statusFileReader = new BufferedReader(new FileReader(file)); 276 statusFileReader = new BufferedReader(new FileReader(file));
176 String line; 277 String line;
177 boolean ok = true; 278 boolean ok = true;
178 while (ok && (line = statusFileReader.readLine()) != null) { 279 while (ok && (line = statusFileReader.readLine()) != null) {
179 line = line.trim(); 280 if (trimLines) {
180 if (line.length() > 0) { 281 line = line.trim();
282 }
283 if (ignoreThatStars != null && line.startsWith(ignoreThatStars)) {
284 continue;
285 }
286 if (!skipEmpty || line.length() > 0) {
181 ok = consumer.consume(line, paramObj); 287 ok = consumer.consume(line, paramObj);
182 } 288 }
183 } 289 }
184 } catch (IOException ex) { 290 } catch (IOException ex) {
185 throw new HgInvalidFileException(ex.getMessage(), ex, file); 291 throw new HgInvalidFileException(ex.getMessage(), ex, file);