Cleaned up whitespace, fix DOS line endings
[nbgit.git] / src / org / nbgit / DiskMapTurboProvider.java
blobb6603509d534d1698e14527a1807e1c36fee27cd
1 /*
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
6 * The contents of this file are subject to the terms of either the GNU
7 * General Public License Version 2 only ("GPL") or the Common
8 * Development and Distribution License("CDDL") (collectively, the
9 * "License"). You may not use this file except in compliance with the
10 * License. You can obtain a copy of the License at
11 * http://www.netbeans.org/cddl-gplv2.html
12 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
13 * specific language governing permissions and limitations under the
14 * License. When distributing the software, include this License Header
15 * Notice in each file and include the License file at
16 * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
17 * particular file as subject to the "Classpath" exception as provided
18 * by Sun in the GPL Version 2 section of the License file that
19 * accompanied this code. If applicable, add the following below the
20 * License Header, with the fields enclosed by brackets [] replaced by
21 * your own identifying information:
22 * "Portions Copyrighted [year] [name of copyright owner]"
24 * Contributor(s):
26 * The Original Software is NetBeans. The Initial Developer of the Original
27 * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
28 * Microsystems, Inc. All Rights Reserved.
29 * Portions Copyright 2008 Alexander Coles (Ikonoklastik Productions).
31 * If you wish your version of this file to be governed by only the CDDL
32 * or only the GPL Version 2, indicate your decision by adding
33 * "[Contributor] elects to include this software in this distribution
34 * under the [CDDL or GPL Version 2] license." If you do not indicate a
35 * single choice of license, a recipient has the option to distribute
36 * your version of this file under either the CDDL, the GPL Version 2 or
37 * to extend the choice of license to its licensees as provided above.
38 * However, if you add GPL Version 2 code and therefore, elected the GPL
39 * Version 2 license, then the option applies only if the new code is
40 * made subject to such option by the copyright holder.
42 package org.nbgit;
44 import java.io.BufferedInputStream;
45 import java.io.BufferedOutputStream;
46 import java.io.ByteArrayOutputStream;
47 import java.io.DataInputStream;
48 import java.io.DataOutputStream;
49 import java.io.EOFException;
50 import java.io.File;
51 import java.io.FileInputStream;
52 import java.io.FileOutputStream;
53 import java.io.IOException;
54 import java.io.InputStream;
55 import java.io.OutputStream;
56 import java.util.Collections;
57 import java.util.HashMap;
58 import java.util.Iterator;
59 import java.util.Map;
60 import java.util.Set;
61 import java.util.logging.Level;
62 import org.netbeans.modules.turbo.TurboProvider;
63 import org.openide.filesystems.FileUtil;
65 /**
66 * Storage of file attributes with shortcut to retrieve all stored values.
68 * @author Maros Sandor
70 class DiskMapTurboProvider implements TurboProvider {
72 static final String ATTR_STATUS_MAP = "git.STATUS_MAP"; // NOI18N
74 private static final int STATUS_VALUABLE = StatusInfo.STATUS_MANAGED & ~StatusInfo.STATUS_VERSIONED_UPTODATE;
75 private static final String CACHE_DIRECTORY = "gitcache"; // NOI18N
77 private File cacheStore;
78 private int storeSerial;
80 private int cachedStoreSerial = -1;
81 private Map<File, StatusInfo> cachedValues;
83 DiskMapTurboProvider() {
84 initCacheStore();
87 synchronized Map<File, StatusInfo> getAllModifiedValues() {
88 if (cachedStoreSerial != storeSerial || cachedValues == null) {
89 cachedValues = new HashMap<File, StatusInfo>();
90 File [] files = cacheStore.listFiles();
91 for (int i = 0; i < files.length; i++) {
92 File file = files[i];
93 if (file.getName().endsWith(".bin") == false) { // NOI18N
94 // on windows list returns already deleted .new files
95 continue;
97 DataInputStream dis = null;
98 try {
99 int retry = 0;
100 while (true) {
101 try {
102 dis = new DataInputStream(new BufferedInputStream(new FileInputStream(file)));
103 break;
104 } catch (IOException ioex) {
105 retry++;
106 if (retry > 7) {
107 throw ioex;
109 Thread.sleep(retry * 30);
113 for (;;) {
114 int pathLen = dis.readInt();
115 dis.readInt();
116 String path = readChars(dis, pathLen);
117 Map<File, StatusInfo> value = readValue(dis, path);
118 for (File f : value.keySet()) {
119 StatusInfo info = value.get(f);
120 if ((info.getStatus() & DiskMapTurboProvider.STATUS_VALUABLE) != 0) {
121 cachedValues.put(f, info);
125 } catch (EOFException e) {
126 // reached EOF, no entry for this key
127 } catch (Exception e) {
128 Git.LOG.log(Level.WARNING, null, e);
129 } finally {
130 if (dis != null) try { dis.close(); } catch (IOException e) {}
133 cachedStoreSerial = storeSerial;
134 cachedValues = Collections.unmodifiableMap(cachedValues);
136 return cachedValues;
139 public boolean recognizesAttribute(String name) {
140 return DiskMapTurboProvider.ATTR_STATUS_MAP.equals(name);
143 public boolean recognizesEntity(Object key) {
144 return key instanceof File;
147 public synchronized Object readEntry(Object key, String name, MemoryCache memoryCache) {
148 assert key instanceof File;
149 assert name != null;
151 boolean readFailed = false;
152 File dir = (File) key;
153 File store = getStore(dir);
154 if (!store.isFile()) {
155 return null;
158 String dirPath = dir.getAbsolutePath();
159 int dirPathLen = dirPath.length();
160 DataInputStream dis = null;
161 try {
163 int retry = 0;
164 while (true) {
165 try {
166 dis = new DataInputStream(new BufferedInputStream(new FileInputStream(store)));
167 break;
168 } catch (IOException ioex) {
169 retry++;
170 if (retry > 7) {
171 throw ioex;
173 Thread.sleep(retry * 30);
177 for (;;) {
178 int pathLen = dis.readInt();
179 int mapLen = dis.readInt();
180 if (pathLen != dirPathLen) {
181 skip(dis, pathLen * 2 + mapLen);
182 } else {
183 String path = readChars(dis, pathLen);
184 if (dirPath.equals(path)) {
185 return readValue(dis, path);
186 } else {
187 skip(dis, mapLen);
191 } catch (EOFException e) {
192 // reached EOF, no entry for this key
193 } catch (Exception e) {
194 Git.LOG.log(Level.INFO, null, e);
195 readFailed = true;
196 } finally {
197 if (dis != null) try { dis.close(); } catch (IOException e) {}
199 if (readFailed) store.delete();
200 return null;
203 public synchronized boolean writeEntry(Object key, String name, Object value) {
204 assert key instanceof File;
205 assert name != null;
207 if (value != null) {
208 if (!(value instanceof Map)) return false;
209 if (!isValuable(value)) value = null;
212 File dir = (File) key;
213 String dirPath = dir.getAbsolutePath();
214 int dirPathLen = dirPath.length();
215 File store = getStore(dir);
217 if (value == null && !store.exists()) return true;
219 File storeNew = new File(store.getParentFile(), store.getName() + ".new"); // NOI18N
221 DataOutputStream oos = null;
222 DataInputStream dis = null;
223 try {
224 oos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(storeNew)));
225 if (value != null) {
226 writeEntry(oos, dirPath, value);
228 if (store.exists()) {
229 int retry = 0;
230 while (true) {
231 try {
232 dis = new DataInputStream(new BufferedInputStream(new FileInputStream(store)));
233 break;
234 } catch (IOException ioex) {
235 retry++;
236 if (retry > 7) {
237 throw ioex;
239 Thread.sleep(retry * 30);
243 for (;;) {
244 int pathLen;
245 try {
246 pathLen = dis.readInt();
247 } catch (EOFException e) {
248 break;
250 int mapLen = dis.readInt();
251 if (pathLen == dirPathLen) {
252 String path = readChars(dis, pathLen);
253 if (dirPath.equals(path)) {
254 skip(dis, mapLen);
255 } else {
256 oos.writeInt(pathLen);
257 oos.writeInt(mapLen);
258 oos.writeChars(path);
259 DiskMapTurboProvider.copyStreams(oos, dis, mapLen);
261 } else {
262 oos.writeInt(pathLen);
263 oos.writeInt(mapLen);
264 DiskMapTurboProvider.copyStreams(oos, dis, mapLen + pathLen * 2);
268 } catch (Exception e) {
269 Git.LOG.log(Level.WARNING, "writeEntry(): Copy: {0} to: {1}", new Object[] {store.getAbsolutePath(), storeNew.getAbsolutePath()}); //NOI18N
270 return true;
271 } finally {
272 if (oos != null) try { oos.close(); } catch (IOException e) {}
273 if (dis != null) try { dis.close(); } catch (IOException e) {}
275 storeSerial++;
276 store.delete();
277 storeNew.renameTo(store);
278 return true;
281 private void skip(InputStream is, long len) throws IOException {
282 while (len > 0) {
283 long n = is.skip(len);
284 if (n < 0) throw new EOFException("Missing " + len + " bytes."); // NOI18N
285 len -= n;
289 private String readChars(DataInputStream dis, int len) throws IOException {
290 StringBuffer sb = new StringBuffer(len);
291 while (len-- > 0) {
292 sb.append(dis.readChar());
294 return sb.toString();
297 private Map<File, StatusInfo> readValue(DataInputStream dis, String dirPath) throws IOException {
298 Map<File, StatusInfo> map = new HashMap<File, StatusInfo>();
299 int len = dis.readInt();
300 while (len-- > 0) {
301 int nameLen = dis.readInt();
302 String name = readChars(dis, nameLen);
303 File file = new File(dirPath, name);
304 int status = dis.readInt();
305 StatusInfo info = new StatusInfo(status & 65535, status > 65535);
306 map.put(file, info);
308 return map;
311 private void writeEntry(DataOutputStream dos, String dirPath, Object value) throws IOException {
313 Map map = (Map) value;
314 Set set = map.keySet();
315 ByteArrayOutputStream baos = new ByteArrayOutputStream(set.size() * 50);
316 DataOutputStream temp = new DataOutputStream(baos);
318 temp.writeInt(set.size());
319 for (Iterator i = set.iterator(); i.hasNext();) {
320 File file = (File) i.next();
321 StatusInfo info = (StatusInfo) map.get(file);
322 temp.writeInt(file.getName().length());
323 temp.writeChars(file.getName());
324 temp.writeInt(info.getStatus() + (info.isDirectory() ? 65536 : 0));
326 temp.close();
327 byte [] valueBytes = baos.toByteArray();
329 dos.writeInt(dirPath.length());
330 dos.writeInt(valueBytes.length);
331 dos.writeChars(dirPath);
332 dos.write(valueBytes);
335 private boolean isValuable(Object value) {
336 Map map = (Map) value;
337 for (Iterator i = map.values().iterator(); i.hasNext();) {
338 StatusInfo info = (StatusInfo) i.next();
339 if ((info.getStatus() & DiskMapTurboProvider.STATUS_VALUABLE) != 0) return true;
341 return false;
344 private File getStore(File dir) {
345 String dirPath = dir.getAbsolutePath();
346 int dirHash = dirPath.hashCode();
347 return new File(cacheStore, Integer.toString(dirHash % 173 + 172) + ".bin"); // NOI18N
350 private void initCacheStore() {
351 String userDir = System.getProperty("netbeans.user"); // NOI18N
352 File cacheRoot;
353 if (userDir != null) {
354 cacheRoot = new File(new File(userDir, "var"), "cache"); // NOI18N
355 } else {
356 cacheRoot = FileUtil.toFile(FileUtil.getConfigRoot());
358 cacheStore = new File(cacheRoot, DiskMapTurboProvider.CACHE_DIRECTORY); // NOI18N
359 cacheStore.mkdirs();
362 private static void copyStreams(OutputStream out, InputStream in, int len) throws IOException {
363 byte [] buffer = new byte[4096];
364 for (;;) {
365 int n = (len <= 4096) ? len : 4096;
366 n = in.read(buffer, 0, n);
367 if (n < 0) throw new EOFException("Missing " + len + " bytes."); // NOI18N
368 out.write(buffer, 0, n);
369 if ((len -= n) == 0) break;
371 out.flush();