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.
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
;
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() {
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
++) {
93 if (file
.getName().endsWith(".bin") == false) { // NOI18N
94 // on windows list returns already deleted .new files
97 DataInputStream dis
= null;
102 dis
= new DataInputStream(new BufferedInputStream(new FileInputStream(file
)));
104 } catch (IOException ioex
) {
109 Thread
.sleep(retry
* 30);
114 int pathLen
= 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
);
130 if (dis
!= null) try { dis
.close(); } catch (IOException e
) {}
133 cachedStoreSerial
= storeSerial
;
134 cachedValues
= Collections
.unmodifiableMap(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
;
151 boolean readFailed
= false;
152 File dir
= (File
) key
;
153 File store
= getStore(dir
);
154 if (!store
.isFile()) {
158 String dirPath
= dir
.getAbsolutePath();
159 int dirPathLen
= dirPath
.length();
160 DataInputStream dis
= null;
166 dis
= new DataInputStream(new BufferedInputStream(new FileInputStream(store
)));
168 } catch (IOException ioex
) {
173 Thread
.sleep(retry
* 30);
178 int pathLen
= dis
.readInt();
179 int mapLen
= dis
.readInt();
180 if (pathLen
!= dirPathLen
) {
181 skip(dis
, pathLen
* 2 + mapLen
);
183 String path
= readChars(dis
, pathLen
);
184 if (dirPath
.equals(path
)) {
185 return readValue(dis
, path
);
191 } catch (EOFException e
) {
192 // reached EOF, no entry for this key
193 } catch (Exception e
) {
194 Git
.LOG
.log(Level
.INFO
, null, e
);
197 if (dis
!= null) try { dis
.close(); } catch (IOException e
) {}
199 if (readFailed
) store
.delete();
203 public synchronized boolean writeEntry(Object key
, String name
, Object value
) {
204 assert key
instanceof File
;
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;
224 oos
= new DataOutputStream(new BufferedOutputStream(new FileOutputStream(storeNew
)));
226 writeEntry(oos
, dirPath
, value
);
228 if (store
.exists()) {
232 dis
= new DataInputStream(new BufferedInputStream(new FileInputStream(store
)));
234 } catch (IOException ioex
) {
239 Thread
.sleep(retry
* 30);
246 pathLen
= dis
.readInt();
247 } catch (EOFException e
) {
250 int mapLen
= dis
.readInt();
251 if (pathLen
== dirPathLen
) {
252 String path
= readChars(dis
, pathLen
);
253 if (dirPath
.equals(path
)) {
256 oos
.writeInt(pathLen
);
257 oos
.writeInt(mapLen
);
258 oos
.writeChars(path
);
259 DiskMapTurboProvider
.copyStreams(oos
, dis
, mapLen
);
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
272 if (oos
!= null) try { oos
.close(); } catch (IOException e
) {}
273 if (dis
!= null) try { dis
.close(); } catch (IOException e
) {}
277 storeNew
.renameTo(store
);
281 private void skip(InputStream is
, long len
) throws IOException
{
283 long n
= is
.skip(len
);
284 if (n
< 0) throw new EOFException("Missing " + len
+ " bytes."); // NOI18N
289 private String
readChars(DataInputStream dis
, int len
) throws IOException
{
290 StringBuffer sb
= new StringBuffer(len
);
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();
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);
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));
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;
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
353 if (userDir
!= null) {
354 cacheRoot
= new File(new File(userDir
, "var"), "cache"); // NOI18N
356 cacheRoot
= FileUtil
.toFile(FileUtil
.getConfigRoot());
358 cacheStore
= new File(cacheRoot
, DiskMapTurboProvider
.CACHE_DIRECTORY
); // NOI18N
362 private static void copyStreams(OutputStream out
, InputStream in
, int len
) throws IOException
{
363 byte [] buffer
= new byte[4096];
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;