KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > editor > mimelookup > impl > SwitchLookup


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.mimelookup.impl;
21
22 import java.beans.PropertyChangeEvent JavaDoc;
23 import java.beans.PropertyChangeListener JavaDoc;
24 import java.util.ArrayList JavaDoc;
25 import java.util.HashMap JavaDoc;
26 import java.util.HashSet JavaDoc;
27 import java.util.Iterator JavaDoc;
28 import java.util.List JavaDoc;
29 import java.util.Set JavaDoc;
30 import java.util.logging.Logger JavaDoc;
31 import org.netbeans.api.editor.mimelookup.MimePath;
32 import org.netbeans.spi.editor.mimelookup.InstanceProvider;
33 import org.openide.util.Lookup;
34 import org.openide.util.WeakListeners;
35 import org.openide.util.lookup.ProxyLookup;
36
37 /**
38  *
39  * @author vita
40  */

41 public class SwitchLookup extends Lookup {
42
43     private static final Logger JavaDoc LOG = Logger.getLogger(SwitchLookup.class.getName());
44     
45     /* package */ static final String JavaDoc ROOT_FOLDER = "Editors"; //NOI18N
46

47     private MimePath mimePath;
48
49     private final String JavaDoc LOCK = new String JavaDoc("SwitchLookup.LOCK"); //NOI18N
50

51     private MappingListener listener;
52     
53     private HashMap JavaDoc classLookups = new HashMap JavaDoc();
54     private HashMap JavaDoc pathsLookups = new HashMap JavaDoc();
55
56     private HashMap JavaDoc classInfos = new HashMap JavaDoc();
57     private HashMap JavaDoc pathsToClasses = new HashMap JavaDoc();
58     
59     /** Creates a new instance of SwitchLookup */
60     public SwitchLookup(MimePath mimePath) {
61         super();
62         
63         this.mimePath = mimePath;
64         
65         this.listener = new MappingListener();
66         ClassInfoStorage.getInstance().addPropertyChangeListener(
67             WeakListeners.propertyChange(listener, ClassInfoStorage.getInstance()));
68     }
69
70     public Lookup.Result lookup(Lookup.Template template) {
71         return findLookup(template.getType()).lookup(template);
72     }
73
74     public Object JavaDoc lookup(Class JavaDoc clazz) {
75         return findLookup(clazz).lookup(clazz);
76     }
77
78     private Lookup findLookup(Class JavaDoc clazz) {
79         synchronized (LOCK) {
80             String JavaDoc className = clazz.getName();
81             Lookup lookup = (Lookup) classLookups.get(className);
82             if (lookup == null) {
83                 // Get the the class info and remember it
84
ClassInfoStorage.Info classInfo = ClassInfoStorage.getInstance().getInfo(className);
85                 classInfos.put(className, classInfo);
86                 
87                 // Create lookup
88
Lookup innerLookup = createLookup(classInfo);
89                 lookup = new UpdatableProxyLookup(new Lookup [] { innerLookup });
90                 
91                 classLookups.put(className, lookup);
92             }
93
94             return lookup;
95         }
96     }
97
98     private Lookup createLookup(ClassInfoStorage.Info classInfo) {
99         List JavaDoc paths = computePaths(mimePath, ROOT_FOLDER, classInfo.getExtraPath());
100         Lookup lookup;
101         
102         if (classInfo.getInstanceProviderClass() != null) {
103             // Get a lookup for the new instance provider
104
lookup = getLookupForProvider(classInfo.getClassName(), paths, classInfo.getInstanceProvider());
105         } else {
106             // Add the className to the list of users of the new paths
107
Set JavaDoc pathsUsers = (Set JavaDoc) pathsToClasses.get(paths);
108             if (pathsUsers == null) {
109                 pathsUsers = new HashSet JavaDoc();
110                 pathsToClasses.put(paths, pathsUsers);
111             }
112             pathsUsers.add(classInfo.getClassName());
113
114             // Get a lookup for the new paths
115
lookup = getLookupForPaths(paths);
116         }
117         
118         return lookup;
119     }
120     
121     private Lookup getLookupForPaths(List JavaDoc paths) {
122         Lookup lookup = (Lookup) pathsLookups.get(paths);
123         if (lookup == null) {
124             lookup = new FolderPathLookup((String JavaDoc []) paths.toArray(new String JavaDoc[paths.size()]));
125             pathsLookups.put(paths, lookup);
126         }
127         
128         return lookup;
129     }
130
131     private Lookup getLookupForProvider(String JavaDoc className, List JavaDoc paths, InstanceProvider instanceProvider) {
132         return new InstanceProviderLookup((String JavaDoc [])paths.toArray(new String JavaDoc[paths.size()]), instanceProvider);
133     }
134     
135     private void rebuildLookup(String JavaDoc className) {
136         synchronized (LOCK) {
137             UpdatableProxyLookup classLookup = (UpdatableProxyLookup) classLookups.get(className);
138             if (classLookup == null) {
139                 // no lookup for the class, nothing to do
140
return;
141             }
142
143             ClassInfoStorage.Info currentClassInfo = (ClassInfoStorage.Info) classInfos.get(className);
144             ClassInfoStorage.Info classInfo = ClassInfoStorage.getInstance().getInfo(className);
145             
146             if (currentClassInfo.equals(classInfo)) {
147                 // bogus change event, the class information hasn't changed, nothing to do
148
return;
149             }
150
151             if (currentClassInfo.getInstanceProviderClass() == null) {
152                 List JavaDoc currentPaths = computePaths(mimePath, ROOT_FOLDER, currentClassInfo.getExtraPath());
153
154                 // Remove the className from the list of users of the current paths
155
Set JavaDoc currentPathsUsers = (Set JavaDoc) pathsToClasses.get(currentPaths);
156                 currentPathsUsers.remove(className);
157
158                 if (currentPathsUsers.isEmpty()) {
159                     pathsToClasses.remove(currentPaths);
160                     pathsLookups.remove(currentPaths);
161                 }
162             }
163
164             // Remember the new class info
165
classInfos.put(className, classInfo);
166     
167             // Update the classLookup
168
Lookup innerLookup = createLookup(classInfo);
169             classLookup.setLookupsEx(new Lookup [] { innerLookup });
170         }
171     }
172     
173     // XXX: This is currently called from editor/settings/storage (SettingsProvider)
174
// via reflection. We will eventually make it friend API. In the meantime just
175
// make sure that any changes here still work for e/s/s module.
176

177     /* package */ static List JavaDoc computePaths(MimePath mimePath, String JavaDoc prefixPath, String JavaDoc suffixPath) {
178         ArrayList JavaDoc arrays = new ArrayList JavaDoc(mimePath.size());
179         String JavaDoc innerMimeType = null;
180
181         if (mimePath.size() > 1) {
182             innerMimeType = mimePath.getMimeType(mimePath.size() - 1);
183         }
184         
185         for (int i = mimePath.size(); i >= 0 ; i--) {
186             MimePath currentPath = mimePath.getPrefix(i);
187
188             // Skip the top level mime type if it's the same as the inner mime type
189
// to avoid duplicities.
190
if (currentPath.size() != 1 || innerMimeType == null ||
191                 !currentPath.getMimeType(0).equals(innerMimeType)
192             ) {
193                 // Add the current mime path
194
arrays.add(split(currentPath));
195             }
196
197             // For compound mime types fork the existing paths and add their
198
// variant for the generic part of the mime type as well.
199
// E.g. text/x-ant+xml adds both text/x-ant+xml and text/xml
200
if (currentPath.size() > 0) {
201                 String JavaDoc mimeType = currentPath.getMimeType(currentPath.size() - 1);
202                 String JavaDoc genericMimeType = getGenericPartOfCompoundMimeType(mimeType);
203
204                 if (genericMimeType != null) {
205                     List JavaDoc genericPaths = forkPaths(arrays, genericMimeType, i - 1);
206                     arrays.addAll(genericPaths);
207                 }
208             }
209         }
210
211         // Add the inner type on a prominent position
212
if (innerMimeType != null) {
213             arrays.add(1, new String JavaDoc [] { innerMimeType });
214             
215             String JavaDoc genericInnerMimeType = getGenericPartOfCompoundMimeType(innerMimeType);
216             if (genericInnerMimeType != null) {
217                 arrays.add(2, new String JavaDoc [] { genericInnerMimeType });
218             }
219         }
220         
221         ArrayList JavaDoc paths = new ArrayList JavaDoc(arrays.size());
222
223         for (Iterator JavaDoc i = arrays.iterator(); i.hasNext(); ) {
224             String JavaDoc [] path = (String JavaDoc []) i.next();
225             StringBuffer JavaDoc sb = new StringBuffer JavaDoc(10 * path.length + 20);
226
227             if (prefixPath != null && prefixPath.length() > 0) {
228                 sb.append(prefixPath);
229             }
230             for (int ii = 0; ii < path.length; ii++) {
231                 if (path[ii].length() > 0) {
232                     if (sb.length() > 0) {
233                         sb.append('/'); //NOI18N
234
}
235                     sb.append(path[ii]);
236                 }
237             }
238             if (suffixPath != null && suffixPath.length() > 0) {
239                 if (sb.length() > 0) {
240                     sb.append('/'); //NOI18N
241
}
242                 sb.append(suffixPath);
243             }
244
245             paths.add(sb.toString());
246         }
247
248         return paths;
249     }
250
251     // See http://tools.ietf.org/html/rfc4288#section-4.2 for the structure of
252
// mime type strings.
253
// package private just for tests
254
/* package */ static String JavaDoc getGenericPartOfCompoundMimeType(String JavaDoc mimeType) {
255         int plusIdx = mimeType.lastIndexOf('+'); //NOI18N
256
if (plusIdx != -1 && plusIdx < mimeType.length() - 1) {
257             int slashIdx = mimeType.indexOf('/'); //NOI18N
258
String JavaDoc prefix = mimeType.substring(0, slashIdx + 1);
259             String JavaDoc suffix = mimeType.substring(plusIdx + 1);
260
261             // fix for #61245
262
if (suffix.equals("xml")) { //NOI18N
263
prefix = "text/"; //NOI18N
264
}
265
266             return prefix + suffix;
267         } else {
268             return null;
269         }
270     }
271
272     private static String JavaDoc [] split(MimePath mimePath) {
273         String JavaDoc [] array = new String JavaDoc[mimePath.size()];
274         
275         for (int i = 0; i < mimePath.size(); i++) {
276             array[i] = mimePath.getMimeType(i);
277         }
278         
279         return array;
280     }
281     
282     // Remember the paths list contains string arrays such as { 'text/x-jsp', 'text/x-ant+xml', 'text/x-java' },
283
// the elementIdx points to the 'text/x-ant+xml' part and genericMimeType is 'text/xml'.
284
private static List JavaDoc forkPaths(List JavaDoc paths, String JavaDoc genericMimeType, int elementIdx) {
285         ArrayList JavaDoc forkedPaths = new ArrayList JavaDoc(paths.size());
286         
287         for (Iterator JavaDoc i = paths.iterator(); i.hasNext(); ) {
288             String JavaDoc [] path = (String JavaDoc []) i.next();
289             String JavaDoc [] forkedPath = new String JavaDoc [path.length];
290             
291             for (int ii = 0; ii < path.length; ii++) {
292                 if (ii != elementIdx) {
293                     forkedPath[ii] = path[ii];
294                 } else {
295                     forkedPath[ii] = genericMimeType;
296                 }
297             }
298             
299             forkedPaths.add(forkedPath);
300         }
301         
302         return forkedPaths;
303     }
304     
305     /**
306      * An ordinary <code>ProxyLookup</code> except that it exposes the
307      * <code>setLookupEx</code> method.
308      */

309     private static final class UpdatableProxyLookup extends ProxyLookup {
310         public UpdatableProxyLookup() {
311             super();
312         }
313         
314         public UpdatableProxyLookup(Lookup [] lookups) {
315             super(lookups);
316         }
317         
318         public void setLookupsEx(Lookup [] lookups) {
319             setLookups(lookups);
320         }
321     } // End of UpdatableProxyLookup class
322

323     private final class MappingListener implements PropertyChangeListener JavaDoc {
324         public void propertyChange(PropertyChangeEvent JavaDoc evt) {
325             Set JavaDoc classNames = (Set JavaDoc)evt.getNewValue();
326             
327             for (Iterator JavaDoc i = classNames.iterator(); i.hasNext(); ) {
328                 String JavaDoc className = (String JavaDoc) i.next();
329                 rebuildLookup(className);
330             }
331         }
332     } // End of MappingListsner class
333

334 }
335
Popular Tags