KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > openfile > RecentFiles


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.modules.openfile;
21         
22 import java.beans.PropertyChangeEvent JavaDoc;
23 import java.util.prefs.BackingStoreException JavaDoc;
24 import org.netbeans.modules.openfile.RecentFiles.HistoryItem;
25 import org.openide.loaders.DataObject;
26 import org.openide.windows.CloneableTopComponent;
27 import org.openide.windows.TopComponent;
28 import java.beans.PropertyChangeListener JavaDoc;
29 import java.lang.NumberFormatException JavaDoc;
30 import java.net.MalformedURLException JavaDoc;
31 import java.net.URL JavaDoc;
32 import java.util.ArrayList JavaDoc;
33 import java.util.Collections JavaDoc;
34 import java.util.List JavaDoc;
35 import java.util.logging.Level JavaDoc;
36 import java.util.logging.Logger JavaDoc;
37 import java.util.prefs.Preferences JavaDoc;
38 import org.openide.filesystems.FileObject;
39 import org.openide.filesystems.URLMapper;
40 import org.openide.util.NbPreferences;
41 import org.openide.windows.WindowManager;
42
43 /**
44  * Manages prioritized set of recently closed files.
45  *
46  * @author Dafe Simonek
47  */

48 public final class RecentFiles {
49
50     /** List of recently closed files */
51     private static List JavaDoc<HistoryItem> history = new ArrayList JavaDoc<HistoryItem>();
52     
53     /** Preferences node for storing history info */
54     private static Preferences JavaDoc prefs;
55     
56     private static final Object JavaDoc HISTORY_LOCK = new Object JavaDoc();
57     
58     /** Name of preferences node where we persist history */
59     private static final String JavaDoc PREFS_NODE = "RecentFilesHistory";
60
61     /** Separator to encode file path and time into one string in preferences */
62     private static final String JavaDoc SEPARATOR = "; time=";
63
64     /** Boundary for items count in history */
65     private static final int MAX_HISTORY_ITEMS = 20;
66     
67     private RecentFiles () {
68     }
69
70     /** Starts to listen for recently closed files */
71     public static void init () {
72         WindowManager.getDefault().invokeWhenUIReady(new Runnable JavaDoc() {
73             public void run() {
74                 List JavaDoc<HistoryItem> loaded = load();
75                 synchronized (HISTORY_LOCK) {
76                     history.addAll(0, loaded);
77                 }
78                 TopComponent.getRegistry().addPropertyChangeListener(new WindowRegistryL());
79             }
80         });
81     }
82
83     /** Returns read-only list of recently closed files */
84     public static List JavaDoc<HistoryItem> getRecentFiles () {
85         synchronized (HISTORY_LOCK) {
86             checkHistory();
87             return Collections.unmodifiableList(history);
88         }
89     }
90
91     /** Loads list of recent files stored in previous system sessions.
92      * @return list of stored recent files
93      */

94     static List JavaDoc<HistoryItem> load () {
95         String JavaDoc[] keys;
96         Preferences JavaDoc prefs = getPrefs();
97         try {
98             keys = prefs.keys();
99         }
100         catch (BackingStoreException JavaDoc ex) {
101             Logger.getLogger(RecentFiles.class.getName()).log(Level.WARNING, ex.getMessage(), ex);
102             return Collections.emptyList();
103         }
104         List JavaDoc<HistoryItem> result = new ArrayList JavaDoc<HistoryItem>(keys.length + 10);
105         HistoryItem hItem;
106         for (String JavaDoc curKey : keys) {
107             hItem = decode(prefs.get(curKey, null));
108             if (hItem != null) {
109                 result.add(hItem);
110             } else {
111                 // decode failed, so clear crippled item
112
prefs.remove(curKey);
113             }
114         }
115         Collections.sort(result);
116         return result;
117     }
118     
119     private static HistoryItem decode (String JavaDoc value) {
120         int sepIndex = value.lastIndexOf(SEPARATOR);
121         if (sepIndex <= 0) {
122             return null;
123         }
124         URL JavaDoc url = null;
125         try {
126             url = new URL JavaDoc(value.substring(0, sepIndex));
127         } catch (MalformedURLException JavaDoc ex) {
128             // url corrupted, skip
129
Logger.getLogger(RecentFiles.class.getName()).log(Level.INFO, ex.getMessage(), ex);
130             return null;
131         }
132         FileObject fo = URLMapper.findFileObject(url);
133         if (fo == null) {
134             return null;
135         }
136         long time = 0;
137         try {
138             time = Long.decode(value.substring(sepIndex + SEPARATOR.length()));
139         } catch (NumberFormatException JavaDoc ex) {
140             // stored data corrupted, skip
141
Logger.getLogger(RecentFiles.class.getName()).log(Level.INFO, ex.getMessage(), ex);
142             return null;
143         }
144         return new HistoryItem(fo, time);
145     }
146
147     static void storeRemoved (HistoryItem hItem) {
148         String JavaDoc stringURL = null;
149         URL JavaDoc url = URLMapper.findURL(hItem.getFile(), URLMapper.EXTERNAL);
150         if (url == null) {
151             // not possible to store
152
Logger.getLogger(RecentFiles.class.getName()).log(Level.INFO,
153                     "storeRemoved: URL can't be found for FileObject " + hItem.getFile()); // NOI18N
154
return;
155         }
156         stringURL = url.toExternalForm();
157         getPrefs().remove(trimToKeySize(stringURL));
158     }
159     
160     static void storeAdded (HistoryItem hItem) {
161         String JavaDoc stringURL = null;
162         URL JavaDoc url = URLMapper.findURL(hItem.getFile(), URLMapper.EXTERNAL);
163         if (url == null) {
164             // not possible to store
165
Logger.getLogger(RecentFiles.class.getName()).log(Level.INFO,
166                     "storeAdded: URL can't be found for FileObject " + hItem.getFile()); // NOI18N
167
return;
168         }
169         stringURL = url.toExternalForm();
170         String JavaDoc value = stringURL + SEPARATOR + String.valueOf(hItem.getTime());
171         getPrefs().put(trimToKeySize(stringURL), value);
172     }
173     
174     private static String JavaDoc trimToKeySize (String JavaDoc path) {
175         int length = path.length();
176         if (length > Preferences.MAX_KEY_LENGTH) {
177             path = path.substring(length - Preferences.MAX_KEY_LENGTH, length);
178         }
179         return path;
180     }
181     
182    static Preferences JavaDoc getPrefs () {
183         if (prefs == null) {
184             prefs = NbPreferences.forModule(RecentFiles.class).node(PREFS_NODE);
185         }
186         return prefs;
187     }
188     
189     /** Adds file represented by given TopComponent to the list,
190      * if conditions are met.
191      */

192     private static void addFile (TopComponent tc) {
193         if (tc instanceof CloneableTopComponent) {
194             FileObject fo = obtainFileObject(tc);
195             if (fo != null) {
196                 boolean added = false;
197                 synchronized (HISTORY_LOCK) {
198                     // avoid duplicates
199
HistoryItem hItem = findHistoryItem(fo);
200                     if (hItem == null) {
201                         hItem = new HistoryItem(fo, System.currentTimeMillis());
202                         history.add(0, hItem);
203                         storeAdded(hItem);
204                         added = true;
205                         // keep manageable size of history
206
// remove the oldest item if needed
207
if (history.size() > MAX_HISTORY_ITEMS) {
208                             HistoryItem oldest = history.get(history.size() - 1);
209                             history.remove(oldest);
210                             storeRemoved(oldest);
211                         }
212                     }
213                 }
214             }
215         }
216     }
217
218     /** Removes file represented by given TopComponent from the list */
219     private static void removeFile (TopComponent tc) {
220         if (tc instanceof CloneableTopComponent) {
221             FileObject fo = obtainFileObject(tc);
222             if (fo != null) {
223                 boolean removed = false;
224                 synchronized (HISTORY_LOCK) {
225                     HistoryItem hItem = findHistoryItem(fo);
226                     if (hItem != null) {
227                         history.remove(hItem);
228                         storeRemoved(hItem);
229                         removed = true;
230                     }
231                 }
232             }
233         }
234     }
235     
236     private static FileObject obtainFileObject (TopComponent tc) {
237         DataObject dObj = tc.getLookup().lookup(DataObject.class);
238         return dObj != null ? dObj.getPrimaryFile() : null;
239     }
240     
241     private static HistoryItem findHistoryItem (FileObject fo) {
242         for (HistoryItem hItem : history) {
243             if (fo.equals(hItem.getFile())) {
244                 return hItem;
245             }
246         }
247         return null;
248     }
249     
250     /** Checks recent files history and removes non-valid entries */
251     private static void checkHistory () {
252         // note, code optimized for the frequent case that there are no invalid entries
253
List JavaDoc<HistoryItem> invalidEntries = new ArrayList JavaDoc<HistoryItem>(3);
254         for (HistoryItem historyItem : history) {
255             if (!historyItem.getFile().isValid()) {
256                 invalidEntries.add(historyItem);
257             }
258         }
259         for (HistoryItem historyItem : invalidEntries) {
260             history.remove(historyItem);
261         }
262     }
263
264     /** One item of the recently closed files history
265      * Comparable by the time field, ascending from most recent to older items.
266      */

267     public static final class HistoryItem<T extends HistoryItem> implements Comparable JavaDoc<T> {
268         
269         private long time;
270         private FileObject file;
271         
272         HistoryItem (FileObject file, long time) {
273             this.file = file;
274             this.time = time;
275         }
276         
277         public FileObject getFile () {
278             return file;
279         }
280         
281         public long getTime () {
282             return time;
283         }
284
285         public int compareTo(T other) {
286             long diff = time - other.getTime();
287             return diff < 0 ? 1 : diff > 0 ? -1 : 0;
288         }
289         
290     }
291     
292     /** Receives info about opened and closed TopComponents from window system.
293      */

294     private static class WindowRegistryL implements PropertyChangeListener JavaDoc {
295         
296         public void propertyChange(PropertyChangeEvent JavaDoc evt) {
297             if (TopComponent.Registry.PROP_TC_CLOSED.equals(evt.getPropertyName())) {
298                 addFile((TopComponent) evt.getNewValue());
299             }
300             if (TopComponent.Registry.PROP_TC_OPENED.equals(evt.getPropertyName())) {
301                 removeFile((TopComponent) evt.getNewValue());
302             }
303         }
304     
305     }
306     
307 }
308
Popular Tags