KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > editor > settings > storage > KeyBindingSettingsImpl


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.editor.settings.storage;
21
22 import java.beans.PropertyChangeEvent JavaDoc;
23 import java.beans.PropertyChangeListener JavaDoc;
24 import java.beans.PropertyChangeSupport JavaDoc;
25 import java.lang.ref.WeakReference JavaDoc;
26 import java.util.ArrayList JavaDoc;
27 import java.util.Collection JavaDoc;
28 import java.util.Collections JavaDoc;
29 import java.util.HashMap JavaDoc;
30 import java.util.HashSet JavaDoc;
31 import java.util.Iterator JavaDoc;
32 import java.util.List JavaDoc;
33 import java.util.Map JavaDoc;
34 import java.util.Set JavaDoc;
35 import java.util.WeakHashMap JavaDoc;
36 import java.util.logging.Level JavaDoc;
37 import java.util.logging.Logger JavaDoc;
38 import javax.swing.KeyStroke JavaDoc;
39 import org.netbeans.api.editor.mimelookup.MimePath;
40 import org.netbeans.api.editor.settings.KeyBindingSettings;
41 import org.netbeans.api.editor.settings.MultiKeyBinding;
42 import org.netbeans.modules.editor.settings.storage.api.EditorSettings;
43 import org.netbeans.modules.editor.settings.storage.api.KeyBindingSettingsFactory;
44 import org.openide.filesystems.FileAttributeEvent;
45 import org.openide.filesystems.FileChangeListener;
46 import org.openide.filesystems.FileEvent;
47 import org.openide.filesystems.FileObject;
48 import org.openide.filesystems.FileRenameEvent;
49 import org.openide.util.Utilities;
50
51 /**
52  * KeyBindings settings are represented by List of keybindings.
53  * The List contains the instances of {@link MultiKeyBinding}.
54  * <br>
55  * Instances of this class should be retrieved from the {@link org.netbeans.api.editor.mimelookup.MimeLookup}
56  * for a given mime-type.
57  * <br>
58  * <font color="red">This class must NOT be extended by any API clients</font>
59  *
60  * @author Jan Jancura
61  */

62 public final class KeyBindingSettingsImpl extends KeyBindingSettingsFactory {
63
64     private static final Logger JavaDoc LOG = Logger.getLogger(KeyBindingSettingsImpl.class.getName());
65     
66     private static final Map JavaDoc<MimePath, WeakReference JavaDoc<KeyBindingSettingsImpl>> INSTANCES =
67         new WeakHashMap JavaDoc<MimePath, WeakReference JavaDoc<KeyBindingSettingsImpl>>();
68     
69     public static synchronized KeyBindingSettingsImpl get(MimePath mimePath) {
70         WeakReference JavaDoc<KeyBindingSettingsImpl> reference = INSTANCES.get(mimePath);
71         KeyBindingSettingsImpl result = reference == null ? null : reference.get();
72         
73         if (result == null) {
74             result = new KeyBindingSettingsImpl(mimePath);
75             INSTANCES.put(mimePath, new WeakReference JavaDoc<KeyBindingSettingsImpl>(result));
76         }
77         
78         return result;
79     }
80     
81     private MimePath mimePath;
82     private PropertyChangeSupport JavaDoc pcs;
83     private Map JavaDoc<String JavaDoc, List JavaDoc<MultiKeyBinding>> keyMaps = new HashMap JavaDoc<String JavaDoc, List JavaDoc<MultiKeyBinding>>();
84     private KeyBindingSettingsFactory baseKBS;
85     private Listener JavaDoc listener;
86     
87     private String JavaDoc logActionName = null;
88     
89     /**
90      * Construction prohibited for API clients.
91      */

92     private KeyBindingSettingsImpl (MimePath mimePath) {
93         this.mimePath = mimePath;
94         pcs = new PropertyChangeSupport JavaDoc (this);
95         
96         // init logging
97
String JavaDoc myClassName = KeyBindingSettingsImpl.class.getName ();
98         String JavaDoc value = System.getProperty(myClassName);
99         if (value != null) {
100             if (!value.equals("true")) {
101                 logActionName = System.getProperty(myClassName);
102             }
103         } else if (mimePath.size() == 1) {
104             logActionName = System.getProperty(myClassName + '.' + mimePath.getMimeType(0));
105         }
106     }
107     
108     private boolean init = false;
109     private void init () {
110         if (init) return;
111         init = true;
112         if (mimePath.size() != 1 || !mimePath.getMimeType(0).equals("text/base")) { //NOI18N
113
baseKBS = EditorSettingsImpl.getInstance().getKeyBindingSettings(new String JavaDoc[] {"text/base"}); //NOI18N
114
}
115         listener = new Listener JavaDoc(this, baseKBS);
116     }
117     
118     /**
119      * Gets the keybindings list, where items are instances of {@link MultiKeyBinding}
120      *
121      * @return List of {@link MultiKeyBinding}
122      */

123     public List JavaDoc<MultiKeyBinding> getKeyBindings() {
124         return getKeyBindings(EditorSettingsImpl.getInstance().getCurrentKeyMapProfile());
125     }
126     
127     /**
128      * Gets the keybindings list, where items are instances of {@link MultiKeyBinding}
129      *
130      * @return List of {@link MultiKeyBinding}
131      */

132     public List JavaDoc<MultiKeyBinding> getKeyBindings(String JavaDoc profile) {
133         init ();
134         
135         // 1) get real profile
136
profile = EditorSettingsImpl.getInstance().getInternalKeymapProfile(profile);
137         
138         List JavaDoc<MultiKeyBinding> result = new ArrayList JavaDoc<MultiKeyBinding>();
139         if (!keyMaps.containsKey (profile)) {
140             synchronized (this) {
141
142                 // 2) load original profile for this mimeType
143
// Map (List (KeyStroke) > MultiKeyBinding)
144
Map JavaDoc<Collection JavaDoc<KeyStroke JavaDoc>, MultiKeyBinding> defaults =
145                     new HashMap JavaDoc<Collection JavaDoc<KeyStroke JavaDoc>, MultiKeyBinding>(getDefaults(profile));
146
147                 // 3) load & apply modifications to defaults
148
Object JavaDoc[] ret = KeyMapsStorage.loadKeyMaps(mimePath, profile, false);
149                 
150                 @SuppressWarnings JavaDoc("unchecked")
151                 Map JavaDoc<Collection JavaDoc<KeyStroke JavaDoc>, MultiKeyBinding> shortcuts = (Map JavaDoc<Collection JavaDoc<KeyStroke JavaDoc>, MultiKeyBinding>) ret[0];
152                 @SuppressWarnings JavaDoc("unchecked")
153                 Set JavaDoc<Collection JavaDoc<KeyStroke JavaDoc>> removedShortcuts = (Set JavaDoc<Collection JavaDoc<KeyStroke JavaDoc>>) ret[1];
154                 
155                 for(Collection JavaDoc<KeyStroke JavaDoc> s : removedShortcuts) {
156                     defaults.remove(s);
157                 }
158                 defaults.putAll(shortcuts);
159
160                 List JavaDoc<MultiKeyBinding> localShortcuts = new ArrayList JavaDoc<MultiKeyBinding>(defaults.values());
161                 keyMaps.put(profile, localShortcuts);
162                 result.addAll(localShortcuts);
163             }
164     } else {
165             result.addAll(keyMaps.get(profile));
166         }
167         
168         log ("getKeyBindings", result);
169
170         // 4) add global editor shortcuts
171
if (baseKBS != null) {
172             List JavaDoc<MultiKeyBinding> baseShortcuts = baseKBS.getKeyBindings(profile);
173             log ("", Collections.EMPTY_LIST);
174             result.addAll(baseShortcuts);
175         }
176         
177     return Collections.unmodifiableList(result);
178     }
179     
180     /**
181      * Returns default keybindings list for given keymap name, where items
182      * are instances of {@link MultiKeyBinding}.
183      *
184      * @return List of {@link MultiKeyBinding}
185      */

186     public List JavaDoc<MultiKeyBinding> getKeyBindingDefaults(String JavaDoc profile) {
187         // 1) get real profile
188
profile = EditorSettingsImpl.getInstance().getInternalKeymapProfile(profile);
189         return Collections.unmodifiableList(new ArrayList JavaDoc<MultiKeyBinding>(getDefaults(profile).values()));
190     }
191     
192     /**
193      * Gets the keybindings list, where items are instances of
194      * {@link MultiKeyBinding}.
195      *
196      * @return List of {@link MultiKeyBinding}
197      */

198     public synchronized void setKeyBindings (
199         String JavaDoc profile,
200         List JavaDoc<MultiKeyBinding> keyBindings
201     ) {
202         log ("setKeyBindings", keyBindings);
203
204         // 1) get real profile
205
profile = EditorSettingsImpl.getInstance().getInternalKeymapProfile(profile);
206         
207         init ();
208         if (keyBindings == null) {
209             // 1) delete user changes / user profile
210
keyMaps.remove (profile);
211             KeyMapsStorage.deleteProfile(mimePath, profile, false);
212             return;
213         }
214         keyMaps.put(profile, keyBindings);
215
216         // 1) convert keyBindings: List (MultiKeyBinding) to
217
// m: Map (List (KeyStroke) > MultiKeyBinding).
218
Map JavaDoc<Collection JavaDoc<KeyStroke JavaDoc>, MultiKeyBinding> m = new HashMap JavaDoc<Collection JavaDoc<KeyStroke JavaDoc>, MultiKeyBinding>();
219         for(MultiKeyBinding mkb : keyBindings) {
220             m.put(mkb.getKeyStrokeList(), mkb);
221         }
222
223         // 2) compute removed shortcuts & remove unchanged maappings from m
224
Map JavaDoc<Collection JavaDoc<KeyStroke JavaDoc>, MultiKeyBinding> defaults = getDefaults(profile);
225         Set JavaDoc<Collection JavaDoc<KeyStroke JavaDoc>> removed = new HashSet JavaDoc<Collection JavaDoc<KeyStroke JavaDoc>>();
226         
227         for(Collection JavaDoc<KeyStroke JavaDoc> shortcut : defaults.keySet()) {
228             MultiKeyBinding mkb2 = defaults.get(shortcut);
229             if (!m.containsKey (shortcut)) {
230                 removed.add (shortcut);
231             } else {
232                 MultiKeyBinding mkb1 = (MultiKeyBinding) m.get (shortcut);
233                 if (mkb1.getActionName ().equals (mkb2.getActionName ()))
234                     m.remove (shortcut);
235             }
236         }
237
238         log (" changed:", m.values ());
239         log (" removed:", removed);
240         log ("", Collections.EMPTY_LIST);
241
242         // 3) save diff & removed
243
listener.removeListeners ();
244         KeyMapsStorage.saveKeyMaps(mimePath, profile, false, m.values(), removed);
245         
246         listener.addListeners ();
247         pcs.firePropertyChange (null, null, null);
248     }
249     
250     /**
251      * PropertyChangeListener registration.
252      *
253      * @param l a PropertyChangeListener to be registerred
254      */

255     public void addPropertyChangeListener (PropertyChangeListener JavaDoc l) {
256         pcs.addPropertyChangeListener (l);
257     }
258     
259     /**
260      * PropertyChangeListener registration.
261      *
262      * @param l a PropertyChangeListener to be unregisterred
263      */

264     public void removePropertyChangeListener (PropertyChangeListener JavaDoc l) {
265         pcs.removePropertyChangeListener (l);
266     }
267     
268     // other methods ...........................................................
269

270     // Map (String (profile) > Map (String (shortcut) > MultiKeyBinding)).
271
private Map JavaDoc<String JavaDoc, Map JavaDoc<Collection JavaDoc<KeyStroke JavaDoc>, MultiKeyBinding>> defaults =
272         new HashMap JavaDoc<String JavaDoc, Map JavaDoc<Collection JavaDoc<KeyStroke JavaDoc>, MultiKeyBinding>>();
273     
274     /**
275      * Returns default shortcut set for given profile. Returns empty map for
276      * custom (user defined) profiles.
277      *
278      * @return Map (List (KeyStroke) > MultiKeyBinding)
279      */

280     private Map JavaDoc<Collection JavaDoc<KeyStroke JavaDoc>, MultiKeyBinding> getDefaults(String JavaDoc profile) {
281         if (!defaults.containsKey (profile)) {
282             Object JavaDoc [] ret = KeyMapsStorage.loadKeyMaps(mimePath, profile, true);
283             @SuppressWarnings JavaDoc("unchecked")
284             Map JavaDoc<Collection JavaDoc<KeyStroke JavaDoc>, MultiKeyBinding> keyMap =
285                 (Map JavaDoc<Collection JavaDoc<KeyStroke JavaDoc>, MultiKeyBinding>) ret[0];
286
287             defaults.put(profile, keyMap);
288         }
289         
290         return defaults.get (profile);
291     }
292
293     /**
294      * External change.
295      */

296     private void refresh () {
297         keyMaps = new HashMap JavaDoc<String JavaDoc, List JavaDoc<MultiKeyBinding>>();
298         log ("refresh", Collections.EMPTY_SET);
299         pcs.firePropertyChange (null, null, null);
300     }
301     
302     private void log (String JavaDoc text, Collection JavaDoc keymap) {
303         if (!LOG.isLoggable(Level.FINE)) {
304             return;
305         }
306         if (text.length() != 0) {
307             if (mimePath.size() == 1) {
308                 text += " " + mimePath.getMimeType(0);
309             }
310             text += " " + EditorSettingsImpl.getInstance().getCurrentKeyMapProfile();
311         }
312         if (keymap == null) {
313             LOG.fine(text + " : null");
314             return;
315         }
316         LOG.fine(text);
317         Iterator JavaDoc it = keymap.iterator ();
318         while (it.hasNext ()) {
319             Object JavaDoc mkb = it.next ();
320             if (logActionName == null || !(mkb instanceof MultiKeyBinding)) {
321                 LOG.fine(" " + mkb);
322             } else if (mkb instanceof MultiKeyBinding &&
323                 logActionName.equals(((MultiKeyBinding) mkb).getActionName ()))
324             {
325                 LOG.fine(" " + mkb);
326             }
327         }
328     }
329
330     public Object JavaDoc createInstanceForLookup() {
331         List JavaDoc<MultiKeyBinding> keyB = getKeyBindings();
332         return new Immutable(new ArrayList JavaDoc<MultiKeyBinding>(keyB));
333     }
334
335     
336     private static class Listener extends WeakReference JavaDoc<KeyBindingSettingsImpl>
337     implements FileChangeListener, PropertyChangeListener JavaDoc, Runnable JavaDoc {
338         
339         /** /Editor/mimetype/currentProfile/ folder*/
340         private FileObject fo;
341         private KeyBindingSettingsFactory baseKBS;
342         
343         Listener (
344             KeyBindingSettingsImpl kb,
345             KeyBindingSettingsFactory baseKBS
346         ) {
347             super(kb, Utilities.activeReferenceQueue());
348             this.baseKBS = baseKBS;
349             addListeners ();
350         }
351         
352         private KeyBindingSettingsImpl getSettings () {
353             KeyBindingSettingsImpl r = get ();
354             if (r != null) return r;
355             removeListeners ();
356             return null;
357         }
358         
359         private void addListeners () {
360             EditorSettingsImpl.getInstance().addPropertyChangeListener(
361                 EditorSettings.PROP_CURRENT_KEY_MAP_PROFILE,
362                 this
363             );
364             if (baseKBS != null)
365                 baseKBS.addPropertyChangeListener (this);
366             setFolderListener ();
367         }
368         
369         private void removeListeners () {
370             fo.removeFileChangeListener (this);
371             if (baseKBS != null)
372                 baseKBS.removePropertyChangeListener (this);
373             EditorSettingsImpl.getInstance().removePropertyChangeListener(
374                 EditorSettings.PROP_CURRENT_KEY_MAP_PROFILE,
375                 this
376             );
377         }
378         
379         public void propertyChange (PropertyChangeEvent JavaDoc evt) {
380             KeyBindingSettingsImpl r = getSettings ();
381             if (r == null) return;
382             if (EditorSettings.PROP_CURRENT_KEY_MAP_PROFILE.equals (
383                 evt.getPropertyName ()
384             ))
385                 setFolderListener ();
386             r.log ("refresh2", Collections.EMPTY_SET);
387             r.pcs.firePropertyChange (null, null, null);
388         }
389
390         public void run() {
391             removeListeners();
392         }
393         
394         public void fileDataCreated (FileEvent fe) {
395         }
396
397         public void fileChanged (FileEvent fe) {
398             KeyBindingSettingsImpl r = getSettings ();
399             if (r == null) return;
400             if (fe.getFile().getNameExt().equals(KeyMapsStorage.KEYBINDING_FILE_NAME)) {
401                 r.refresh();
402             }
403         }
404
405         public void fileDeleted (FileEvent fe) {
406             KeyBindingSettingsImpl r = getSettings ();
407             if (r == null) return;
408             if (fe.getFile().getNameExt().equals(KeyMapsStorage.KEYBINDING_FILE_NAME)) {
409                 r.refresh();
410             }
411         }
412         
413         public void fileFolderCreated(FileEvent fe) {
414         }
415
416         public void fileRenamed(FileRenameEvent fe) {
417         }
418
419         public void fileAttributeChanged(FileAttributeEvent fe) {
420         }
421
422         private void setFolderListener() {
423             if (fo != null) fo.removeFileChangeListener (this);
424             String JavaDoc profile = EditorSettingsImpl.getInstance().getCurrentKeyMapProfile();
425             if (profile.equals(EditorSettingsImpl.DEFAULT_PROFILE)) profile = null;
426
427             KeyBindingSettingsImpl kbsi = getSettings();
428             if (kbsi != null) {
429                 fo = Utils.createFileObject(kbsi.mimePath, profile, null);
430                 fo.addFileChangeListener (this);
431             }
432         }
433     }
434     
435     /* package */ static final class Immutable extends KeyBindingSettings {
436         private List JavaDoc<MultiKeyBinding> keyBindings;
437         
438         public Immutable(List JavaDoc<MultiKeyBinding> keyBindings) {
439             this.keyBindings = keyBindings;
440         }
441         
442         public List JavaDoc<MultiKeyBinding> getKeyBindings() {
443             return Collections.unmodifiableList(keyBindings);
444         }
445     }
446     
447 }
448
Popular Tags