comparison hg4j/src/main/java/org/tmatesoft/hg/repo/HgRepository.java @ 213:6ec4af642ba8 gradle

Project uses Gradle for build - actual changes
author Alexander Kitaev <kitaev@gmail.com>
date Tue, 10 May 2011 10:52:53 +0200
parents
children
comparison
equal deleted inserted replaced
212:edb2e2829352 213:6ec4af642ba8
1 /*
2 * Copyright (c) 2010-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@hg4j.com
16 */
17 package org.tmatesoft.hg.repo;
18
19 import java.io.File;
20 import java.io.IOException;
21 import java.lang.ref.SoftReference;
22 import java.util.ArrayList;
23 import java.util.Collections;
24 import java.util.HashMap;
25 import java.util.List;
26
27 import org.tmatesoft.hg.internal.ConfigFile;
28 import org.tmatesoft.hg.internal.DataAccessProvider;
29 import org.tmatesoft.hg.internal.Filter;
30 import org.tmatesoft.hg.internal.RelativePathRewrite;
31 import org.tmatesoft.hg.internal.RequiresFile;
32 import org.tmatesoft.hg.internal.RevlogStream;
33 import org.tmatesoft.hg.util.FileIterator;
34 import org.tmatesoft.hg.util.FileWalker;
35 import org.tmatesoft.hg.util.Path;
36 import org.tmatesoft.hg.util.PathRewrite;
37
38
39
40 /**
41 * Shall be as state-less as possible, all the caching happens outside the repo, in commands/walkers
42 *
43 * @author Artem Tikhomirov
44 * @author TMate Software Ltd.
45 */
46 public final class HgRepository {
47
48 // if new constants added, consider fixing HgInternals#wrongLocalRevision
49 public static final int TIP = -3;
50 public static final int BAD_REVISION = Integer.MIN_VALUE;
51 public static final int WORKING_COPY = -2;
52
53 // temp aux marker method
54 public static IllegalStateException notImplemented() {
55 return new IllegalStateException("Not implemented");
56 }
57
58 private final File repoDir; // .hg folder
59 private final String repoLocation;
60 private final DataAccessProvider dataAccess;
61 private final PathRewrite normalizePath;
62 private final PathRewrite dataPathHelper;
63 private final PathRewrite repoPathHelper;
64
65 private HgChangelog changelog;
66 private HgManifest manifest;
67 private HgTags tags;
68 // XXX perhaps, shall enable caching explicitly
69 private final HashMap<Path, SoftReference<RevlogStream>> streamsCache = new HashMap<Path, SoftReference<RevlogStream>>();
70
71 private final org.tmatesoft.hg.internal.Internals impl = new org.tmatesoft.hg.internal.Internals();
72 private HgIgnore ignore;
73 private ConfigFile configFile;
74
75 HgRepository(String repositoryPath) {
76 repoDir = null;
77 repoLocation = repositoryPath;
78 dataAccess = null;
79 dataPathHelper = repoPathHelper = null;
80 normalizePath = null;
81 }
82
83 HgRepository(String repositoryPath, File repositoryRoot) {
84 assert ".hg".equals(repositoryRoot.getName()) && repositoryRoot.isDirectory();
85 assert repositoryPath != null;
86 assert repositoryRoot != null;
87 repoDir = repositoryRoot;
88 repoLocation = repositoryPath;
89 dataAccess = new DataAccessProvider();
90 final boolean runningOnWindows = System.getProperty("os.name").indexOf("Windows") != -1;
91 if (runningOnWindows) {
92 normalizePath = new PathRewrite() {
93
94 public String rewrite(String path) {
95 // TODO handle . and .. (although unlikely to face them from GUI client)
96 path = path.replace('\\', '/').replace("//", "/");
97 if (path.startsWith("/")) {
98 path = path.substring(1);
99 }
100 return path;
101 }
102 };
103 } else {
104 normalizePath = new PathRewrite.Empty(); // or strip leading slash, perhaps?
105 }
106 parseRequires();
107 dataPathHelper = impl.buildDataFilesHelper();
108 repoPathHelper = impl.buildRepositoryFilesHelper();
109 }
110
111 @Override
112 public String toString() {
113 return getClass().getSimpleName() + "[" + getLocation() + (isInvalid() ? "(BAD)" : "") + "]";
114 }
115
116 public String getLocation() {
117 return repoLocation;
118 }
119
120 public boolean isInvalid() {
121 return repoDir == null || !repoDir.exists() || !repoDir.isDirectory();
122 }
123
124 public HgChangelog getChangelog() {
125 if (this.changelog == null) {
126 String storagePath = repoPathHelper.rewrite("00changelog.i");
127 RevlogStream content = resolve(Path.create(storagePath), true);
128 this.changelog = new HgChangelog(this, content);
129 }
130 return this.changelog;
131 }
132
133 public HgManifest getManifest() {
134 if (this.manifest == null) {
135 RevlogStream content = resolve(Path.create(repoPathHelper.rewrite("00manifest.i")), true);
136 this.manifest = new HgManifest(this, content);
137 }
138 return this.manifest;
139 }
140
141 public final HgTags getTags() {
142 if (tags == null) {
143 tags = new HgTags();
144 try {
145 tags.readGlobal(new File(repoDir.getParentFile(), ".hgtags"));
146 tags.readLocal(new File(repoDir, "localtags"));
147 } catch (IOException ex) {
148 ex.printStackTrace(); // FIXME log or othewise report
149 }
150 }
151 return tags;
152 }
153
154 public HgDataFile getFileNode(String path) {
155 String nPath = normalizePath.rewrite(path);
156 String storagePath = dataPathHelper.rewrite(nPath);
157 RevlogStream content = resolve(Path.create(storagePath), false);
158 Path p = Path.create(nPath);
159 if (content == null) {
160 return new HgDataFile(this, p);
161 }
162 return new HgDataFile(this, p, content);
163 }
164
165 public HgDataFile getFileNode(Path path) {
166 String storagePath = dataPathHelper.rewrite(path.toString());
167 RevlogStream content = resolve(Path.create(storagePath), false);
168 // XXX no content when no file? or HgDataFile.exists() to detect that?
169 if (content == null) {
170 return new HgDataFile(this, path);
171 }
172 return new HgDataFile(this, path, content);
173 }
174
175 /* clients need to rewrite path from their FS to a repository-friendly paths, and, perhaps, vice versa*/
176 public PathRewrite getToRepoPathHelper() {
177 return normalizePath;
178 }
179
180 // local to hide use of io.File.
181 /*package-local*/ File getRepositoryRoot() {
182 return repoDir;
183 }
184
185 // XXX package-local, unless there are cases when required from outside (guess, working dir/revision walkers may hide dirstate access and no public visibility needed)
186 /*package-local*/ final HgDirstate loadDirstate() {
187 return new HgDirstate(getDataAccess(), new File(repoDir, "dirstate"));
188 }
189
190 // package-local, see comment for loadDirstate
191 /*package-local*/ final HgIgnore getIgnore() {
192 // TODO read config for additional locations
193 if (ignore == null) {
194 ignore = new HgIgnore();
195 try {
196 File ignoreFile = new File(repoDir.getParentFile(), ".hgignore");
197 ignore.read(ignoreFile);
198 } catch (IOException ex) {
199 ex.printStackTrace(); // log warn
200 }
201 }
202 return ignore;
203 }
204
205 /*package-local*/ DataAccessProvider getDataAccess() {
206 return dataAccess;
207 }
208
209 // FIXME not sure repository shall create walkers
210 /*package-local*/ FileIterator createWorkingDirWalker() {
211 File repoRoot = repoDir.getParentFile();
212 Path.Source pathSrc = new Path.SimpleSource(new PathRewrite.Composite(new RelativePathRewrite(repoRoot), getToRepoPathHelper()));
213 // Impl note: simple source is enough as files in the working dir are all unique
214 // even if they might get reused (i.e. after FileIterator#reset() and walking once again),
215 // path caching is better to be done in the code which knows that path are being reused
216 return new FileWalker(repoRoot, pathSrc);
217 }
218
219 /**
220 * Perhaps, should be separate interface, like ContentLookup
221 * path - repository storage path (i.e. one usually with .i or .d)
222 */
223 /*package-local*/ RevlogStream resolve(Path path, boolean shallFakeNonExistent) {
224 final SoftReference<RevlogStream> ref = streamsCache.get(path);
225 RevlogStream cached = ref == null ? null : ref.get();
226 if (cached != null) {
227 return cached;
228 }
229 File f = new File(repoDir, path.toString());
230 if (f.exists()) {
231 RevlogStream s = new RevlogStream(dataAccess, f);
232 streamsCache.put(path, new SoftReference<RevlogStream>(s));
233 return s;
234 } else {
235 if (shallFakeNonExistent) {
236 try {
237 File fake = File.createTempFile(f.getName(), null);
238 fake.deleteOnExit();
239 return new RevlogStream(dataAccess, fake);
240 } catch (IOException ex) {
241 ex.printStackTrace(); // FIXME report in debug
242 }
243 }
244 }
245 return null; // XXX empty stream instead?
246 }
247
248 // can't expose internal class, otherwise seems reasonable to have it in API
249 /*package-local*/ ConfigFile getConfigFile() {
250 if (configFile == null) {
251 configFile = impl.newConfigFile();
252 configFile.addLocation(new File(System.getProperty("user.home"), ".hgrc"));
253 // last one, overrides anything else
254 // <repo>/.hg/hgrc
255 configFile.addLocation(new File(getRepositoryRoot(), "hgrc"));
256 }
257 return configFile;
258 }
259
260 /*package-local*/ List<Filter> getFiltersFromRepoToWorkingDir(Path p) {
261 return instantiateFilters(p, new Filter.Options(Filter.Direction.FromRepo));
262 }
263
264 /*package-local*/ List<Filter> getFiltersFromWorkingDirToRepo(Path p) {
265 return instantiateFilters(p, new Filter.Options(Filter.Direction.ToRepo));
266 }
267
268 private List<Filter> instantiateFilters(Path p, Filter.Options opts) {
269 List<Filter.Factory> factories = impl.getFilters(this, getConfigFile());
270 if (factories.isEmpty()) {
271 return Collections.emptyList();
272 }
273 ArrayList<Filter> rv = new ArrayList<Filter>(factories.size());
274 for (Filter.Factory ff : factories) {
275 Filter f = ff.create(p, opts);
276 if (f != null) {
277 rv.add(f);
278 }
279 }
280 return rv;
281 }
282
283 private void parseRequires() {
284 new RequiresFile().parse(impl, new File(repoDir, "requires"));
285 }
286
287 }