Set module specification to 1.0.
[nbgit.git] / src / org / netbeans / modules / git / DiskMapTurboProvider.java
blob3307ba2716669cf97c63c9b7d23f7790352ca34a
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.netbeans.modules.git;
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;
64 import org.openide.filesystems.Repository;
66 /**
67 * Storage of file attributes with shortcut to retrieve all stored values.
69 * @author Maros Sandor
71 class DiskMapTurboProvider implements TurboProvider {
73 static final String ATTR_STATUS_MAP = "git.STATUS_MAP"; // NOI18N
75 private static final int STATUS_VALUABLE = FileInformation.STATUS_MANAGED & ~FileInformation.STATUS_VERSIONED_UPTODATE;
76 private static final String CACHE_DIRECTORY = "gitcache"; // NOI18N
78 private File cacheStore;
79 private int storeSerial;
81 private int cachedStoreSerial = -1;
82 private Map<File, FileInformation> cachedValues;
84 DiskMapTurboProvider() {
85 initCacheStore();
88 synchronized Map<File, FileInformation> getAllModifiedValues() {
89 if (cachedStoreSerial != storeSerial || cachedValues == null) {
90 cachedValues = new HashMap<File, FileInformation>();
91 File [] files = cacheStore.listFiles();
92 for (int i = 0; i < files.length; i++) {
93 File file = files[i];
94 if (file.getName().endsWith(".bin") == false) { // NOI18N
95 // on windows list returns already deleted .new files
96 continue;
98 DataInputStream dis = null;
99 try {
100 int retry = 0;
101 while (true) {
102 try {
103 dis = new DataInputStream(new BufferedInputStream(new FileInputStream(file)));
104 break;
105 } catch (IOException ioex) {
106 retry++;
107 if (retry > 7) {
108 throw ioex;
110 Thread.sleep(retry * 30);
114 for (;;) {
115 int pathLen = dis.readInt();
116 dis.readInt();
117 String path = readChars(dis, pathLen);
118 Map value = readValue(dis, path);
119 for (Iterator j = value.keySet().iterator(); j.hasNext();) {
120 File f = (File) j.next();
121 FileInformation info = (FileInformation) value.get(f);
122 if ((info.getStatus() & DiskMapTurboProvider.STATUS_VALUABLE) != 0) {
123 cachedValues.put(f, info);
127 } catch (EOFException e) {
128 // reached EOF, no entry for this key
129 } catch (Exception e) {
130 Git.LOG.log(Level.WARNING, null, e);
131 } finally {
132 if (dis != null) try { dis.close(); } catch (IOException e) {}
135 cachedStoreSerial = storeSerial;
136 cachedValues = Collections.unmodifiableMap(cachedValues);
138 return cachedValues;
141 public boolean recognizesAttribute(String name) {
142 return DiskMapTurboProvider.ATTR_STATUS_MAP.equals(name);
145 public boolean recognizesEntity(Object key) {
146 return key instanceof File;
149 public synchronized Object readEntry(Object key, String name, MemoryCache memoryCache) {
150 assert key instanceof File;
151 assert name != null;
153 boolean readFailed = false;
154 File dir = (File) key;
155 File store = getStore(dir);
156 if (!store.isFile()) {
157 return null;
160 String dirPath = dir.getAbsolutePath();
161 int dirPathLen = dirPath.length();
162 DataInputStream dis = null;
163 try {
165 int retry = 0;
166 while (true) {
167 try {
168 dis = new DataInputStream(new BufferedInputStream(new FileInputStream(store)));
169 break;
170 } catch (IOException ioex) {
171 retry++;
172 if (retry > 7) {
173 throw ioex;
175 Thread.sleep(retry * 30);
179 for (;;) {
180 int pathLen = dis.readInt();
181 int mapLen = dis.readInt();
182 if (pathLen != dirPathLen) {
183 skip(dis, pathLen * 2 + mapLen);
184 } else {
185 String path = readChars(dis, pathLen);
186 if (dirPath.equals(path)) {
187 return readValue(dis, path);
188 } else {
189 skip(dis, mapLen);
193 } catch (EOFException e) {
194 // reached EOF, no entry for this key
195 } catch (Exception e) {
196 Git.LOG.log(Level.INFO, null, e);
197 readFailed = true;
198 } finally {
199 if (dis != null) try { dis.close(); } catch (IOException e) {}
201 if (readFailed) store.delete();
202 return null;
205 public synchronized boolean writeEntry(Object key, String name, Object value) {
206 assert key instanceof File;
207 assert name != null;
209 if (value != null) {
210 if (!(value instanceof Map)) return false;
211 if (!isValuable(value)) value = null;
214 File dir = (File) key;
215 String dirPath = dir.getAbsolutePath();
216 int dirPathLen = dirPath.length();
217 File store = getStore(dir);
219 if (value == null && !store.exists()) return true;
221 File storeNew = new File(store.getParentFile(), store.getName() + ".new"); // NOI18N
223 DataOutputStream oos = null;
224 DataInputStream dis = null;
225 try {
226 oos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(storeNew)));
227 if (value != null) {
228 writeEntry(oos, dirPath, value);
230 if (store.exists()) {
231 int retry = 0;
232 while (true) {
233 try {
234 dis = new DataInputStream(new BufferedInputStream(new FileInputStream(store)));
235 break;
236 } catch (IOException ioex) {
237 retry++;
238 if (retry > 7) {
239 throw ioex;
241 Thread.sleep(retry * 30);
245 for (;;) {
246 int pathLen;
247 try {
248 pathLen = dis.readInt();
249 } catch (EOFException e) {
250 break;
252 int mapLen = dis.readInt();
253 if (pathLen == dirPathLen) {
254 String path = readChars(dis, pathLen);
255 if (dirPath.equals(path)) {
256 skip(dis, mapLen);
257 } else {
258 oos.writeInt(pathLen);
259 oos.writeInt(mapLen);
260 oos.writeChars(path);
261 DiskMapTurboProvider.copyStreams(oos, dis, mapLen);
263 } else {
264 oos.writeInt(pathLen);
265 oos.writeInt(mapLen);
266 DiskMapTurboProvider.copyStreams(oos, dis, mapLen + pathLen * 2);
270 } catch (Exception e) {
271 Git.LOG.log(Level.WARNING, "writeEntry(): Copy: {0} to: {1}", new Object[] {store.getAbsolutePath(), storeNew.getAbsolutePath()}); //NOI18N
272 return true;
273 } finally {
274 if (oos != null) try { oos.close(); } catch (IOException e) {}
275 if (dis != null) try { dis.close(); } catch (IOException e) {}
277 storeSerial++;
278 store.delete();
279 storeNew.renameTo(store);
280 return true;
283 private void skip(InputStream is, long len) throws IOException {
284 while (len > 0) {
285 long n = is.skip(len);
286 if (n < 0) throw new EOFException("Missing " + len + " bytes."); // NOI18N
287 len -= n;
291 private String readChars(DataInputStream dis, int len) throws IOException {
292 StringBuffer sb = new StringBuffer(len);
293 while (len-- > 0) {
294 sb.append(dis.readChar());
296 return sb.toString();
299 private Map<File, FileInformation> readValue(DataInputStream dis, String dirPath) throws IOException {
300 Map<File, FileInformation> map = new HashMap<File, FileInformation>();
301 int len = dis.readInt();
302 while (len-- > 0) {
303 int nameLen = dis.readInt();
304 String name = readChars(dis, nameLen);
305 File file = new File(dirPath, name);
306 int status = dis.readInt();
307 FileInformation info = new FileInformation(status & 65535, status > 65535);
308 map.put(file, info);
310 return map;
313 private void writeEntry(DataOutputStream dos, String dirPath, Object value) throws IOException {
315 Map map = (Map) value;
316 Set set = map.keySet();
317 ByteArrayOutputStream baos = new ByteArrayOutputStream(set.size() * 50);
318 DataOutputStream temp = new DataOutputStream(baos);
320 temp.writeInt(set.size());
321 for (Iterator i = set.iterator(); i.hasNext();) {
322 File file = (File) i.next();
323 FileInformation info = (FileInformation) map.get(file);
324 temp.writeInt(file.getName().length());
325 temp.writeChars(file.getName());
326 temp.writeInt(info.getStatus() + (info.isDirectory() ? 65536 : 0));
328 temp.close();
329 byte [] valueBytes = baos.toByteArray();
331 dos.writeInt(dirPath.length());
332 dos.writeInt(valueBytes.length);
333 dos.writeChars(dirPath);
334 dos.write(valueBytes);
337 private boolean isValuable(Object value) {
338 Map map = (Map) value;
339 for (Iterator i = map.values().iterator(); i.hasNext();) {
340 FileInformation info = (FileInformation) i.next();
341 if ((info.getStatus() & DiskMapTurboProvider.STATUS_VALUABLE) != 0) return true;
343 return false;
346 private File getStore(File dir) {
347 String dirPath = dir.getAbsolutePath();
348 int dirHash = dirPath.hashCode();
349 return new File(cacheStore, Integer.toString(dirHash % 173 + 172) + ".bin"); // NOI18N
352 private void initCacheStore() {
353 String userDir = System.getProperty("netbeans.user"); // NOI18N
354 if (userDir != null) {
355 cacheStore = new File(new File(new File (userDir, "var"), "cache"), DiskMapTurboProvider.CACHE_DIRECTORY); // NOI18N
356 } else {
357 File cachedir = FileUtil.toFile(Repository.getDefault().getDefaultFileSystem().getRoot());
358 cacheStore = new File(cachedir, DiskMapTurboProvider.CACHE_DIRECTORY); // NOI18N
360 cacheStore.mkdirs();
363 private static void copyStreams(OutputStream out, InputStream in, int len) throws IOException {
364 byte [] buffer = new byte[4096];
365 for (;;) {
366 int n = (len <= 4096) ? len : 4096;
367 n = in.read(buffer, 0, n);
368 if (n < 0) throw new EOFException("Missing " + len + " bytes."); // NOI18N
369 out.write(buffer, 0, n);
370 if ((len -= n) == 0) break;
372 out.flush();