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]"
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
;
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
;
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
;
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() {
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
++) {
94 if (file
.getName().endsWith(".bin") == false) { // NOI18N
95 // on windows list returns already deleted .new files
98 DataInputStream dis
= null;
103 dis
= new DataInputStream(new BufferedInputStream(new FileInputStream(file
)));
105 } catch (IOException ioex
) {
110 Thread
.sleep(retry
* 30);
115 int pathLen
= 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
);
132 if (dis
!= null) try { dis
.close(); } catch (IOException e
) {}
135 cachedStoreSerial
= storeSerial
;
136 cachedValues
= Collections
.unmodifiableMap(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
;
153 boolean readFailed
= false;
154 File dir
= (File
) key
;
155 File store
= getStore(dir
);
156 if (!store
.isFile()) {
160 String dirPath
= dir
.getAbsolutePath();
161 int dirPathLen
= dirPath
.length();
162 DataInputStream dis
= null;
168 dis
= new DataInputStream(new BufferedInputStream(new FileInputStream(store
)));
170 } catch (IOException ioex
) {
175 Thread
.sleep(retry
* 30);
180 int pathLen
= dis
.readInt();
181 int mapLen
= dis
.readInt();
182 if (pathLen
!= dirPathLen
) {
183 skip(dis
, pathLen
* 2 + mapLen
);
185 String path
= readChars(dis
, pathLen
);
186 if (dirPath
.equals(path
)) {
187 return readValue(dis
, path
);
193 } catch (EOFException e
) {
194 // reached EOF, no entry for this key
195 } catch (Exception e
) {
196 Git
.LOG
.log(Level
.INFO
, null, e
);
199 if (dis
!= null) try { dis
.close(); } catch (IOException e
) {}
201 if (readFailed
) store
.delete();
205 public synchronized boolean writeEntry(Object key
, String name
, Object value
) {
206 assert key
instanceof File
;
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;
226 oos
= new DataOutputStream(new BufferedOutputStream(new FileOutputStream(storeNew
)));
228 writeEntry(oos
, dirPath
, value
);
230 if (store
.exists()) {
234 dis
= new DataInputStream(new BufferedInputStream(new FileInputStream(store
)));
236 } catch (IOException ioex
) {
241 Thread
.sleep(retry
* 30);
248 pathLen
= dis
.readInt();
249 } catch (EOFException e
) {
252 int mapLen
= dis
.readInt();
253 if (pathLen
== dirPathLen
) {
254 String path
= readChars(dis
, pathLen
);
255 if (dirPath
.equals(path
)) {
258 oos
.writeInt(pathLen
);
259 oos
.writeInt(mapLen
);
260 oos
.writeChars(path
);
261 DiskMapTurboProvider
.copyStreams(oos
, dis
, mapLen
);
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
274 if (oos
!= null) try { oos
.close(); } catch (IOException e
) {}
275 if (dis
!= null) try { dis
.close(); } catch (IOException e
) {}
279 storeNew
.renameTo(store
);
283 private void skip(InputStream is
, long len
) throws IOException
{
285 long n
= is
.skip(len
);
286 if (n
< 0) throw new EOFException("Missing " + len
+ " bytes."); // NOI18N
291 private String
readChars(DataInputStream dis
, int len
) throws IOException
{
292 StringBuffer sb
= new StringBuffer(len
);
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();
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);
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));
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;
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
357 File cachedir
= FileUtil
.toFile(Repository
.getDefault().getDefaultFileSystem().getRoot());
358 cacheStore
= new File(cachedir
, DiskMapTurboProvider
.CACHE_DIRECTORY
); // NOI18N
363 private static void copyStreams(OutputStream out
, InputStream in
, int len
) throws IOException
{
364 byte [] buffer
= new byte[4096];
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;