1 19 20 package org.netbeans.modules.openfile; 21 22 import java.beans.PropertyChangeEvent ; 23 import java.util.prefs.BackingStoreException ; 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 ; 29 import java.lang.NumberFormatException ; 30 import java.net.MalformedURLException ; 31 import java.net.URL ; 32 import java.util.ArrayList ; 33 import java.util.Collections ; 34 import java.util.List ; 35 import java.util.logging.Level ; 36 import java.util.logging.Logger ; 37 import java.util.prefs.Preferences ; 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 48 public final class RecentFiles { 49 50 51 private static List <HistoryItem> history = new ArrayList <HistoryItem>(); 52 53 54 private static Preferences prefs; 55 56 private static final Object HISTORY_LOCK = new Object (); 57 58 59 private static final String PREFS_NODE = "RecentFilesHistory"; 60 61 62 private static final String SEPARATOR = "; time="; 63 64 65 private static final int MAX_HISTORY_ITEMS = 20; 66 67 private RecentFiles () { 68 } 69 70 71 public static void init () { 72 WindowManager.getDefault().invokeWhenUIReady(new Runnable () { 73 public void run() { 74 List <HistoryItem> loaded = load(); 75 synchronized (HISTORY_LOCK) { 76 history.addAll(0, loaded); 77 } 78 TopComponent.getRegistry().addPropertyChangeListener(new WindowRegistryL()); 79 } 80 }); 81 } 82 83 84 public static List <HistoryItem> getRecentFiles () { 85 synchronized (HISTORY_LOCK) { 86 checkHistory(); 87 return Collections.unmodifiableList(history); 88 } 89 } 90 91 94 static List <HistoryItem> load () { 95 String [] keys; 96 Preferences prefs = getPrefs(); 97 try { 98 keys = prefs.keys(); 99 } 100 catch (BackingStoreException ex) { 101 Logger.getLogger(RecentFiles.class.getName()).log(Level.WARNING, ex.getMessage(), ex); 102 return Collections.emptyList(); 103 } 104 List <HistoryItem> result = new ArrayList <HistoryItem>(keys.length + 10); 105 HistoryItem hItem; 106 for (String curKey : keys) { 107 hItem = decode(prefs.get(curKey, null)); 108 if (hItem != null) { 109 result.add(hItem); 110 } else { 111 prefs.remove(curKey); 113 } 114 } 115 Collections.sort(result); 116 return result; 117 } 118 119 private static HistoryItem decode (String value) { 120 int sepIndex = value.lastIndexOf(SEPARATOR); 121 if (sepIndex <= 0) { 122 return null; 123 } 124 URL url = null; 125 try { 126 url = new URL (value.substring(0, sepIndex)); 127 } catch (MalformedURLException ex) { 128 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 ex) { 140 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 stringURL = null; 149 URL url = URLMapper.findURL(hItem.getFile(), URLMapper.EXTERNAL); 150 if (url == null) { 151 Logger.getLogger(RecentFiles.class.getName()).log(Level.INFO, 153 "storeRemoved: URL can't be found for FileObject " + hItem.getFile()); return; 155 } 156 stringURL = url.toExternalForm(); 157 getPrefs().remove(trimToKeySize(stringURL)); 158 } 159 160 static void storeAdded (HistoryItem hItem) { 161 String stringURL = null; 162 URL url = URLMapper.findURL(hItem.getFile(), URLMapper.EXTERNAL); 163 if (url == null) { 164 Logger.getLogger(RecentFiles.class.getName()).log(Level.INFO, 166 "storeAdded: URL can't be found for FileObject " + hItem.getFile()); return; 168 } 169 stringURL = url.toExternalForm(); 170 String value = stringURL + SEPARATOR + String.valueOf(hItem.getTime()); 171 getPrefs().put(trimToKeySize(stringURL), value); 172 } 173 174 private static String trimToKeySize (String 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 getPrefs () { 183 if (prefs == null) { 184 prefs = NbPreferences.forModule(RecentFiles.class).node(PREFS_NODE); 185 } 186 return prefs; 187 } 188 189 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 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 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 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 251 private static void checkHistory () { 252 List <HistoryItem> invalidEntries = new ArrayList <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 267 public static final class HistoryItem<T extends HistoryItem> implements Comparable <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 294 private static class WindowRegistryL implements PropertyChangeListener { 295 296 public void propertyChange(PropertyChangeEvent 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 |