comparison src/org/tmatesoft/hg/core/HgFileInformer.java @ 361:8099939af5fa

Utilize status object to supply more information about manifest check for specific file
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Fri, 09 Dec 2011 01:13:53 +0100
parents 5f9073eabf06
children 2fadf8695f8a
comparison
equal deleted inserted replaced
360:150500515714 361:8099939af5fa
19 import org.tmatesoft.hg.internal.ManifestRevision; 19 import org.tmatesoft.hg.internal.ManifestRevision;
20 import org.tmatesoft.hg.repo.HgDataFile; 20 import org.tmatesoft.hg.repo.HgDataFile;
21 import org.tmatesoft.hg.repo.HgInternals; 21 import org.tmatesoft.hg.repo.HgInternals;
22 import org.tmatesoft.hg.repo.HgRepository; 22 import org.tmatesoft.hg.repo.HgRepository;
23 import org.tmatesoft.hg.util.Path; 23 import org.tmatesoft.hg.util.Path;
24 import org.tmatesoft.hg.util.Status;
24 25
25 /** 26 /**
26 * Primary purpose is to provide information about file revisions at specific changeset. Multiple {@link #check(Path)} calls 27 * Primary purpose is to provide information about file revisions at specific changeset. Multiple {@link #check(Path)} calls
27 * are possible once {@link #changeset(Nodeid)} (and optionally, {@link #followRenames(boolean)}) were set. 28 * are possible once {@link #changeset(Nodeid)} (and optionally, {@link #followRenames(boolean)}) were set.
28 * 29 *
35 * ... 36 * ...
36 * } 37 * }
37 * </pre></code> 38 * </pre></code>
38 * 39 *
39 * FIXME need better name. It's more about manifest of specific changeset, rather than informing (about) files 40 * FIXME need better name. It's more about manifest of specific changeset, rather than informing (about) files
41 * TODO may add #manifest(Nodeid) to select manifest according to its revision (not only changeset revision as it's now)
40 * 42 *
41 * @author Artem Tikhomirov 43 * @author Artem Tikhomirov
42 * @author TMate Software Ltd. 44 * @author TMate Software Ltd.
43 */ 45 */
44 public class HgFileInformer { 46 public class HgFileInformer {
46 private final HgRepository repo; 48 private final HgRepository repo;
47 private boolean followRenames; 49 private boolean followRenames;
48 private Nodeid cset; 50 private Nodeid cset;
49 private ManifestRevision cachedManifest; 51 private ManifestRevision cachedManifest;
50 private HgFileRevision fileRevision; 52 private HgFileRevision fileRevision;
51 private boolean checked, renamed; 53 private boolean renamed;
54 private Status checkResult;
52 55
53 public HgFileInformer(HgRepository hgRepo) { 56 public HgFileInformer(HgRepository hgRepo) {
54 repo = hgRepo; 57 repo = hgRepo;
55 } 58 }
56 59
81 fileRevision = null; 84 fileRevision = null;
82 return this; 85 return this;
83 } 86 }
84 87
85 /** 88 /**
86 * Find file (or its origin, if {@link #followRenames(boolean)} was set to <code>true</code>) among files known at specified {@link #changeset(Nodeid)}. 89 * Shortcut to perform {@link #check(Path)} and {@link #exists()}. Result of the check may be accessed via {@link #getCheckStatus()}.
87 * 90 *
88 * @param file name of the file in question 91 * @param file name of the file in question
89 * @return <code>true</code> if file is known at the selected changeset. 92 * @return <code>true</code> if file is known at the selected changeset.
90 * @throws IllegalArgumentException if {@link #changeset(Nodeid)} not specified or file argument is bad. 93 * @throws IllegalArgumentException if {@link #changeset(Nodeid)} not specified or file argument is bad.
91 */ 94 * @throws HgInvalidControlFileException if access to revlog index/data entry failed
92 public boolean check(Path file) throws HgInvalidControlFileException { // XXX IStatus instead of boolean? If status, shall it handle exceptions as well? 95 */
96 public boolean checkExists(Path file) throws HgInvalidControlFileException {
97 check(file);
98 if (!checkResult.isOk() && checkResult.getException() instanceof HgInvalidControlFileException) {
99 throw (HgInvalidControlFileException) checkResult.getException();
100 }
101 return checkResult.isOk() && exists();
102 }
103
104 /**
105 * Find file (or its origin, if {@link #followRenames(boolean)} was set to <code>true</code>) among files known at specified {@link #changeset(Nodeid)}.
106 *
107 * @param file name of the file in question
108 * @return status object that describes outcome, {@link Status#isOk() Ok} status indicates successful completion of the operation, but doesn't imply
109 * file existence, use {@link #exists()} for that purpose. Message of the status may provide further hints on what exactly had happened.
110 * @throws IllegalArgumentException if {@link #changeset(Nodeid)} not specified or file argument is bad.
111 */
112 public Status check(Path file) {
93 fileRevision = null; 113 fileRevision = null;
94 checked = false; 114 checkResult = null;
95 renamed = false; 115 renamed = false;
96 if (cset == null || file == null || file.isDirectory()) { 116 if (cset == null || file == null || file.isDirectory()) {
97 throw new IllegalArgumentException(); 117 throw new IllegalArgumentException();
98 } 118 }
99 HgDataFile dataFile = repo.getFileNode(file); 119 HgDataFile dataFile = repo.getFileNode(file);
100 if (!dataFile.exists()) { 120 if (!dataFile.exists()) {
101 return false; 121 checkResult = new Status(Status.Kind.OK, String.format("File named %s is not known in the repository", file));
102 } 122 return checkResult;
103 if (cachedManifest == null) { 123 }
104 int csetRev = repo.getChangelog().getLocalRevision(cset); 124 Nodeid toExtract = null;
105 cachedManifest = new ManifestRevision(null, null); // XXX how about context and cached manifest revisions
106 repo.getManifest().walk(csetRev, csetRev, cachedManifest);
107 // cachedManifest shall be meaningful - changelog.getLocalRevision above ensures we've got version that exists.
108 }
109 Nodeid toExtract = cachedManifest.nodeid(file);
110 try { 125 try {
126 if (cachedManifest == null) {
127 int csetRev = repo.getChangelog().getLocalRevision(cset);
128 cachedManifest = new ManifestRevision(null, null); // XXX how about context and cached manifest revisions
129 repo.getManifest().walk(csetRev, csetRev, cachedManifest);
130 // cachedManifest shall be meaningful - changelog.getLocalRevision above ensures we've got version that exists.
131 }
132 toExtract = cachedManifest.nodeid(file);
111 if (toExtract == null && followRenames) { 133 if (toExtract == null && followRenames) {
112 while (toExtract == null && dataFile.isCopy()) { 134 while (toExtract == null && dataFile.isCopy()) {
113 renamed = true; 135 renamed = true;
114 file = dataFile.getCopySourceName(); 136 file = dataFile.getCopySourceName();
115 dataFile = repo.getFileNode(file); 137 dataFile = repo.getFileNode(file);
116 toExtract = cachedManifest.nodeid(file); 138 toExtract = cachedManifest.nodeid(file);
117 } 139 }
118 } 140 }
141 } catch (HgInvalidControlFileException ex) {
142 checkResult = new Status(Status.Kind.ERROR, "", ex);
143 return checkResult;
119 } catch (HgDataStreamException ex) { 144 } catch (HgDataStreamException ex) {
120 HgInternals.getContext(repo).getLog().warn(getClass(), ex, "Follow copy/rename failed"); 145 checkResult = new Status(Status.Kind.ERROR, "Follow copy/rename failed", ex);
121 // ignore now, however if there's IStatus retval, might report error with reasonable explanation. 146 HgInternals.getContext(repo).getLog().warn(getClass(), ex, checkResult.getMessage());
122 // Perhaps, may add a String reason() method with such info? 147 return checkResult;
123 } 148 }
124 checked = true;
125 if (toExtract != null) { 149 if (toExtract != null) {
126 fileRevision = new HgFileRevision(repo, toExtract, dataFile.getPath()); 150 fileRevision = new HgFileRevision(repo, toExtract, dataFile.getPath());
127 return true; 151 checkResult = new Status(Status.Kind.OK, String.format("File %s, revision %s found at changeset %s", dataFile.getPath(), toExtract.shortNotation(), cset.shortNotation()));
128 } // else String.format("File %s nor its origins were not known at repository %s revision", file, cset.shortNotation()) 152 return checkResult;
129 return false; 153 }
154 checkResult = new Status(Status.Kind.OK, String.format("File %s nor its origins were not known at repository %s revision", file, cset.shortNotation()));
155 return checkResult;
156 }
157
158 /**
159 * Re-get latest check status object
160 */
161 public Status getCheckStatus() {
162 assertCheckRan();
163 return checkResult;
130 } 164 }
131 165
132 /** 166 /**
133 * @return result of the last {@link #check(Path)} call. 167 * @return result of the last {@link #check(Path)} call.
134 */ 168 */
172 assertCheckRan(); 206 assertCheckRan();
173 return fileRevision.getRevision(); 207 return fileRevision.getRevision();
174 } 208 }
175 209
176 private void assertCheckRan() { 210 private void assertCheckRan() {
177 if (!checked) { 211 if (checkResult == null) {
178 throw new HgBadStateException("Shall invoke #check(Path) first"); 212 throw new HgBadStateException("Shall invoke #check(Path) first");
179 } 213 }
180 } 214 }
181 } 215 }