KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > lib > editor > codetemplates > CodeTemplateManagerOperation


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.lib.editor.codetemplates;
21
22 import java.lang.ref.WeakReference JavaDoc;
23 import java.util.ArrayList JavaDoc;
24 import java.util.Collection JavaDoc;
25 import java.util.Collections JavaDoc;
26 import java.util.HashMap JavaDoc;
27 import java.util.Iterator JavaDoc;
28 import java.util.List JavaDoc;
29 import java.util.Map JavaDoc;
30 import javax.swing.event.ChangeEvent JavaDoc;
31 import javax.swing.event.ChangeListener JavaDoc;
32 import javax.swing.event.EventListenerList JavaDoc;
33 import javax.swing.text.Document JavaDoc;
34 import javax.swing.text.JTextComponent JavaDoc;
35 import org.netbeans.api.editor.mimelookup.MimeLookup;
36 import org.netbeans.api.editor.mimelookup.MimePath;
37 import org.netbeans.api.editor.settings.CodeTemplateDescription;
38 import org.netbeans.editor.Settings;
39 import org.netbeans.editor.SettingsChangeEvent;
40 import org.netbeans.editor.SettingsChangeListener;
41 import org.netbeans.lib.editor.codetemplates.api.CodeTemplate;
42 import org.netbeans.lib.editor.codetemplates.api.CodeTemplateManager;
43 import org.netbeans.lib.editor.codetemplates.spi.*;
44 import org.netbeans.modules.editor.options.BaseOptions;
45 import org.openide.util.Lookup;
46 import org.openide.util.LookupEvent;
47 import org.openide.util.LookupListener;
48 import org.openide.util.RequestProcessor;
49
50 /**
51  * Code template allows the client to paste itself into the given
52  * text component.
53  *
54  * @author Miloslav Metelka
55  */

56 public final class CodeTemplateManagerOperation
57 implements LookupListener, Runnable JavaDoc, SettingsChangeListener {
58     
59     private static Map JavaDoc mime2operation = new HashMap JavaDoc(8);
60     
61     public static synchronized CodeTemplateManager getManager(Document JavaDoc doc) {
62         return get(doc).getManager();
63     }
64
65     public static synchronized CodeTemplateManagerOperation get(Document JavaDoc doc) {
66         String JavaDoc mimeType = (String JavaDoc)doc.getProperty("mimeType");
67         CodeTemplateManagerOperation operation = (CodeTemplateManagerOperation)
68                 doc.getProperty(CodeTemplateManagerOperation.class);
69         boolean mimesEqual = (operation != null) && mimeTypesEqual(mimeType,
70                 operation.getMimeType());
71
72         if (!mimesEqual) {
73             WeakReference JavaDoc ref = (WeakReference JavaDoc)mime2operation.get(mimeType);
74             if (ref != null) {
75                 operation = (CodeTemplateManagerOperation)ref.get();
76             } else {
77                 operation = null;
78             }
79             if (operation == null) {
80                 operation = new CodeTemplateManagerOperation(mimeType);
81                 CodeTemplateApiPackageAccessor.get().createCodeTemplateManager(operation);
82
83                 mime2operation.put(mimeType, new WeakReference JavaDoc(operation));
84             }
85
86             doc.putProperty(CodeTemplateManagerOperation.class, operation);
87         }
88         
89         return operation;
90     }
91     
92     private static boolean mimeTypesEqual(String JavaDoc mimeType1, String JavaDoc mimeType2) {
93         return (mimeType1 == null && mimeType2 == null)
94             || (mimeType1 != null && mimeType1.equals(mimeType2));
95     }
96
97
98     private final CodeTemplateManager manager;
99
100     private final String JavaDoc mimeType;
101
102     private Lookup.Result descriptions;
103     
104     private Collection JavaDoc/*<CodeTemplateProcessorFactory>*/ processorFactories;
105     
106     private Collection JavaDoc/*<CodeTemplateFilter.Factory>*/ filterFactories;
107     
108     private Map JavaDoc abbrev2template;
109     
110     private List JavaDoc sortedTemplatesByAbbrev;
111     
112     private List JavaDoc unmodSortedTemplatesByAbbrev;
113     
114     private List JavaDoc sortedTemplatesByParametrizedText;
115     
116     private List JavaDoc selectionTemplates;
117     
118     private EventListenerList JavaDoc listenerList = new EventListenerList JavaDoc();
119     
120     private boolean settingsListeningInitialized;
121     
122     private CodeTemplateManagerOperation(String JavaDoc mimeType) {
123         this.mimeType = mimeType;
124         
125         // Ensure the API package accessor gets initialized
126
CodeTemplateManager.class.getName();
127
128         this.manager = CodeTemplateApiPackageAccessor.get().createCodeTemplateManager(this);
129         
130         // Compute descriptions asynchronously
131
RequestProcessor.getDefault().post(this);
132     }
133     
134     public CodeTemplateManager getManager() {
135         assert (manager != null);
136         return manager;
137     }
138     
139     public String JavaDoc getMimeType() {
140         return mimeType;
141     }
142     
143     public Collection JavaDoc getCodeTemplates() {
144         return unmodSortedTemplatesByAbbrev;
145     }
146     
147     public Collection JavaDoc findSelectionTemplates() {
148         return selectionTemplates;
149     }
150     
151     public CodeTemplate findByAbbreviation(String JavaDoc abbreviation) {
152         return (CodeTemplate)abbrev2template.get(abbreviation);
153     }
154     
155     public Collection JavaDoc findByParametrizedText(String JavaDoc prefix, boolean ignoreCase) {
156         List JavaDoc result = new ArrayList JavaDoc();
157         
158         int low = 0;
159     int high = sortedTemplatesByParametrizedText.size() - 1;
160     while (low <= high) {
161         int mid = (low + high) >> 1;
162         CodeTemplate t = (CodeTemplate)sortedTemplatesByParametrizedText.get(mid);
163         int cmp = compareTextIgnoreCase(t.getParametrizedText(), prefix);
164
165         if (cmp < 0) {
166         low = mid + 1;
167             } else if (cmp > 0) {
168         high = mid - 1;
169             } else {
170                 low = mid;
171         break;
172             }
173     }
174         
175         // Go back whether prefix matches the name
176
int i = low - 1;
177         while (i >= 0) {
178             CodeTemplate t = (CodeTemplate)sortedTemplatesByParametrizedText.get(i);
179             int mp = matchPrefix(t.getParametrizedText(), prefix);
180             if (mp == MATCH_NO) { // not matched
181
break;
182             } else if (mp == MATCH_IGNORE_CASE) { // matched when ignoring case
183
if (ignoreCase) { // do not add if exact match required
184
result.add(t);
185                 }
186             } else { // matched exactly
187
result.add(t);
188             }
189             i--;
190         }
191         
192         i = low;
193         while (i < sortedTemplatesByParametrizedText.size()) {
194             CodeTemplate t = (CodeTemplate)sortedTemplatesByParametrizedText.get(i);
195             int mp = matchPrefix(t.getParametrizedText(), prefix);
196             if (mp == MATCH_NO) { // not matched
197
break;
198             } else if (mp == MATCH_IGNORE_CASE) { // matched when ignoring case
199
if (ignoreCase) { // do not add if exact match required
200
result.add(t);
201                 }
202             } else { // matched exactly
203
result.add(t);
204             }
205             i++;
206         }
207         
208         return result;
209     }
210     
211     public Collection JavaDoc/*<CodeTemplateFilter>*/ getTemplateFilters(JTextComponent JavaDoc component, int offset) {
212         List JavaDoc/*<CodeTemplateFilter>*/ result = new ArrayList JavaDoc/*<CodeTemplateFilter>*/();
213         for (Iterator JavaDoc it = filterFactories.iterator(); it.hasNext();) {
214             CodeTemplateFilter.Factory factory = (CodeTemplateFilter.Factory)it.next();
215             result.add(factory.createFilter(component, offset));
216         }
217         return result;
218     }
219
220     public void insert(CodeTemplate codeTemplate, JTextComponent JavaDoc component) {
221         CodeTemplateInsertHandler handler = new CodeTemplateInsertHandler(
222                 codeTemplate, component, processorFactories);
223         handler.processTemplate();
224     }
225     
226     /**
227      * Match text against the given prefix.
228      *
229      * @param text text to be compared with the prefix.
230      * @param prefix text to be matched as a prefix of the text parameter.
231      * @return one of <code>MATCH_NO</code>, <code>MATCH_IGNORE_CASE</code>
232      * or <code>MATCH</code>
233      */

234     private static final int MATCH_NO = 0;
235     private static final int MATCH_IGNORE_CASE = 1;
236     private static final int MATCH = 2;
237     private static int matchPrefix(CharSequence JavaDoc text, CharSequence JavaDoc prefix) {
238         boolean matchCase = true;
239         int prefixLength = prefix.length();
240         if (prefixLength > text.length()) { // prefix longer than text
241
return MATCH_NO;
242         }
243         int i;
244         for (i = 0; i < prefixLength; i++) {
245             char ch1 = text.charAt(i);
246             char ch2 = prefix.charAt(i);
247             if (ch1 != ch2) {
248                 matchCase = false;
249                 if (Character.toLowerCase(ch1) != Character.toLowerCase(ch2)) {
250                     break;
251                 }
252             }
253         }
254         if (i == prefixLength) { // compared all
255
return matchCase ? MATCH : MATCH_IGNORE_CASE;
256         } else { // not compared all => not matched
257
return MATCH_NO;
258         }
259     }
260     
261     private static int compareTextIgnoreCase(CharSequence JavaDoc text1, CharSequence JavaDoc text2) {
262         int len = Math.min(text1.length(), text2.length());
263         for (int i = 0; i < len; i++) {
264             char ch1 = Character.toLowerCase(text1.charAt(i));
265             char ch2 = Character.toLowerCase(text2.charAt(i));
266             if (ch1 != ch2) {
267                 return ch1 - ch2;
268             }
269         }
270         return text1.length() - text2.length();
271     }
272     
273     public boolean isLoaded() {
274         synchronized (listenerList) {
275             return (descriptions != null);
276         }
277     }
278     
279     public void registerLoadedListener(ChangeListener JavaDoc listener) {
280         synchronized (listenerList) {
281             if (descriptions != null) { // already loaded
282
listener.stateChanged(new ChangeEvent JavaDoc(this));
283             } else { // not yet loaded
284
listenerList.add(ChangeListener JavaDoc.class, listener);
285             }
286         }
287     }
288     
289     public void waitLoaded() {
290         synchronized (listenerList) {
291             if (!isLoaded()) {
292                 try {
293                     listenerList.wait();
294                 } catch (InterruptedException JavaDoc e) {
295                 }
296             }
297         }
298     }
299     
300     private void fireStateChanged(ChangeEvent JavaDoc evt) {
301         Object JavaDoc[] listeners;
302         synchronized (listenerList) {
303             listeners = listenerList.getListenerList();
304         }
305         for (int i = 0; i < listeners.length; i += 2) {
306             if (ChangeListener JavaDoc.class == listeners[i]) {
307                 ((ChangeListener JavaDoc)listeners[i + 1]).stateChanged(evt);
308             }
309         }
310     }
311     
312     public void run() {
313         Lookup lookup = MimeLookup.getLookup(MimePath.parse(getMimeType()));
314         Lookup.Result result = lookup.lookup(
315                 new Lookup.Template(CodeTemplateProcessorFactory.class));
316         
317         processorFactories = result.allInstances();
318         // [TODO] listen for changes
319

320         result = lookup.lookup(
321                 new Lookup.Template(CodeTemplateFilter.Factory.class));
322         
323         filterFactories = result.allInstances();
324         // [TODO] listen for changes
325

326         // [TODO] take from settings
327
setDescriptions(Lookup.EMPTY.lookup(new Lookup.Template(CodeTemplateDescription.class)));
328     }
329     
330     public void settingsChange(SettingsChangeEvent evt) {
331         rebuildCodeTemplates();
332     }
333     
334     void setDescriptions(Lookup.Result descriptions) {
335         synchronized (listenerList) {
336             this.descriptions = descriptions;
337             rebuildCodeTemplates();
338             fireStateChanged(new ChangeEvent JavaDoc(manager));
339             // Notify loading finished
340
listenerList.notifyAll();
341         }
342     }
343     
344     private Collection JavaDoc updateDescriptionInstances(Collection JavaDoc descriptionsInstances) {
345         descriptionsInstances = new ArrayList JavaDoc();
346         
347         Lookup lookup = MimeLookup.getLookup(MimePath.parse(mimeType));
348         BaseOptions baseOptions = (BaseOptions) lookup.lookup(BaseOptions.class);
349         if (baseOptions != null) {
350             Map JavaDoc abbrevMap = baseOptions.getAbbrevMap();
351             if (abbrevMap != null) {
352                 for (Iterator JavaDoc entryIt = abbrevMap.entrySet().iterator(); entryIt.hasNext();) {
353                     Map.Entry JavaDoc entry = (Map.Entry JavaDoc)entryIt.next();
354                     String JavaDoc abbreviation = (String JavaDoc)entry.getKey();
355                     String JavaDoc abbrevText = (String JavaDoc)entry.getValue();
356
357                     String JavaDoc parametrizedText = abbrevText.replaceAll(
358                             "([^|]+)[|]([^|]+)", "$1\\${cursor}$2"); // NOI18N
359
parametrizedText.replaceAll("[|][|]", "[|]"); // NOI18N
360

361                     String JavaDoc desc = abbrevText;
362                     int nlInd = abbrevText.indexOf('\n');
363                     if (nlInd != -1) {
364                         desc = abbrevText.substring(0, nlInd) + "..."; // NOI18N
365
}
366                     StringBuffer JavaDoc htmlText = new StringBuffer JavaDoc();
367                     ParametrizedTextParser parser = new ParametrizedTextParser(null, desc);
368                     parser.parse();
369                     parser.appendHtmlText(htmlText);
370                     desc = htmlText.toString();
371
372                     CodeTemplateDescription ctd = new CodeTemplateDescription(
373                             abbreviation, desc, parametrizedText, null);
374                     descriptionsInstances.add(ctd);
375
376                 }
377             }
378
379             // Start listening on 'abbrevMap' changes
380
if (!settingsListeningInitialized) {
381                 settingsListeningInitialized = true;
382                 Settings.addSettingsChangeListener(this);
383             }
384         }
385         return descriptionsInstances;
386     }
387     
388     private void rebuildCodeTemplates() {
389         Collection JavaDoc descriptionsInstances = descriptions.allInstances();
390         descriptionsInstances = updateDescriptionInstances(descriptionsInstances);
391         List JavaDoc/*<CodeTemplate>*/ codeTemplates = new ArrayList JavaDoc(descriptionsInstances.size());
392         selectionTemplates = new ArrayList JavaDoc(descriptionsInstances.size());
393         CodeTemplateApiPackageAccessor api = CodeTemplateApiPackageAccessor.get();
394         // Construct template instances
395
for (Iterator JavaDoc it = descriptionsInstances.iterator(); it.hasNext();) {
396             CodeTemplateDescription description = (CodeTemplateDescription)it.next();
397             CodeTemplate ct = api.createCodeTemplate(this, description.getAbbreviation(),
398                     description.getDescription(), description.getParametrizedText());
399             codeTemplates.add(ct);
400             if (description.getParametrizedText().toLowerCase().indexOf("${selection") > -1) { //NOI18N
401
selectionTemplates.add(ct);
402             }
403         }
404         
405         refreshMaps(codeTemplates);
406     }
407     
408     private void refreshMaps(List JavaDoc/*<CodeTemplate>*/ codeTemplates) {
409         abbrev2template = new HashMap JavaDoc(codeTemplates.size());
410         sortedTemplatesByAbbrev = new ArrayList JavaDoc(codeTemplates.size());
411         unmodSortedTemplatesByAbbrev = Collections.unmodifiableList(sortedTemplatesByAbbrev);
412         sortedTemplatesByParametrizedText = new ArrayList JavaDoc(codeTemplates.size());
413         // Construct template instances and store them in map and sorted list
414
for (Iterator JavaDoc it = codeTemplates.iterator(); it.hasNext();) {
415             CodeTemplate template = (CodeTemplate)it.next();
416             String JavaDoc abbreviation = template.getAbbreviation();
417             abbrev2template.put(abbreviation, template);
418             sortedTemplatesByAbbrev.add(template);
419             sortedTemplatesByParametrizedText.add(template);
420         }
421         // Sort the templates in case insensitive order
422
Collections.sort(sortedTemplatesByAbbrev,
423                 CodeTemplateComparator.BY_ABBREVIATION_IGNORE_CASE);
424
425         Collections.sort(sortedTemplatesByParametrizedText,
426                 CodeTemplateComparator.BY_PARAMETRIZED_TEXT_IGNORE_CASE);
427     }
428
429     public void resultChanged(LookupEvent ev) {
430         rebuildCodeTemplates();
431     }
432     
433     public void testInstallProcessorFactory(CodeTemplateProcessorFactory factory) {
434         processorFactories = Collections.singletonList(factory);
435     }
436     
437 }
438
Popular Tags