Mercurial > jhg
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 } |
