1 19 20 package org.netbeans.lib.editor.codetemplates; 21 22 import java.lang.ref.WeakReference ; 23 import java.util.ArrayList ; 24 import java.util.Collection ; 25 import java.util.Collections ; 26 import java.util.HashMap ; 27 import java.util.Iterator ; 28 import java.util.List ; 29 import java.util.Map ; 30 import javax.swing.event.ChangeEvent ; 31 import javax.swing.event.ChangeListener ; 32 import javax.swing.event.EventListenerList ; 33 import javax.swing.text.Document ; 34 import javax.swing.text.JTextComponent ; 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 56 public final class CodeTemplateManagerOperation 57 implements LookupListener, Runnable , SettingsChangeListener { 58 59 private static Map mime2operation = new HashMap (8); 60 61 public static synchronized CodeTemplateManager getManager(Document doc) { 62 return get(doc).getManager(); 63 } 64 65 public static synchronized CodeTemplateManagerOperation get(Document doc) { 66 String mimeType = (String )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 ref = (WeakReference )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 (operation)); 84 } 85 86 doc.putProperty(CodeTemplateManagerOperation.class, operation); 87 } 88 89 return operation; 90 } 91 92 private static boolean mimeTypesEqual(String mimeType1, String 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 mimeType; 101 102 private Lookup.Result descriptions; 103 104 private Collection processorFactories; 105 106 private Collection filterFactories; 107 108 private Map abbrev2template; 109 110 private List sortedTemplatesByAbbrev; 111 112 private List unmodSortedTemplatesByAbbrev; 113 114 private List sortedTemplatesByParametrizedText; 115 116 private List selectionTemplates; 117 118 private EventListenerList listenerList = new EventListenerList (); 119 120 private boolean settingsListeningInitialized; 121 122 private CodeTemplateManagerOperation(String mimeType) { 123 this.mimeType = mimeType; 124 125 CodeTemplateManager.class.getName(); 127 128 this.manager = CodeTemplateApiPackageAccessor.get().createCodeTemplateManager(this); 129 130 RequestProcessor.getDefault().post(this); 132 } 133 134 public CodeTemplateManager getManager() { 135 assert (manager != null); 136 return manager; 137 } 138 139 public String getMimeType() { 140 return mimeType; 141 } 142 143 public Collection getCodeTemplates() { 144 return unmodSortedTemplatesByAbbrev; 145 } 146 147 public Collection findSelectionTemplates() { 148 return selectionTemplates; 149 } 150 151 public CodeTemplate findByAbbreviation(String abbreviation) { 152 return (CodeTemplate)abbrev2template.get(abbreviation); 153 } 154 155 public Collection findByParametrizedText(String prefix, boolean ignoreCase) { 156 List result = new ArrayList (); 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 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) { break; 182 } else if (mp == MATCH_IGNORE_CASE) { if (ignoreCase) { result.add(t); 185 } 186 } else { 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) { break; 198 } else if (mp == MATCH_IGNORE_CASE) { if (ignoreCase) { result.add(t); 201 } 202 } else { result.add(t); 204 } 205 i++; 206 } 207 208 return result; 209 } 210 211 public Collection getTemplateFilters(JTextComponent component, int offset) { 212 List result = new ArrayList (); 213 for (Iterator 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 component) { 221 CodeTemplateInsertHandler handler = new CodeTemplateInsertHandler( 222 codeTemplate, component, processorFactories); 223 handler.processTemplate(); 224 } 225 226 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 text, CharSequence prefix) { 238 boolean matchCase = true; 239 int prefixLength = prefix.length(); 240 if (prefixLength > text.length()) { 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) { return matchCase ? MATCH : MATCH_IGNORE_CASE; 256 } else { return MATCH_NO; 258 } 259 } 260 261 private static int compareTextIgnoreCase(CharSequence text1, CharSequence 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 listener) { 280 synchronized (listenerList) { 281 if (descriptions != null) { listener.stateChanged(new ChangeEvent (this)); 283 } else { listenerList.add(ChangeListener .class, listener); 285 } 286 } 287 } 288 289 public void waitLoaded() { 290 synchronized (listenerList) { 291 if (!isLoaded()) { 292 try { 293 listenerList.wait(); 294 } catch (InterruptedException e) { 295 } 296 } 297 } 298 } 299 300 private void fireStateChanged(ChangeEvent evt) { 301 Object [] listeners; 302 synchronized (listenerList) { 303 listeners = listenerList.getListenerList(); 304 } 305 for (int i = 0; i < listeners.length; i += 2) { 306 if (ChangeListener .class == listeners[i]) { 307 ((ChangeListener )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 320 result = lookup.lookup( 321 new Lookup.Template(CodeTemplateFilter.Factory.class)); 322 323 filterFactories = result.allInstances(); 324 326 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 (manager)); 339 listenerList.notifyAll(); 341 } 342 } 343 344 private Collection updateDescriptionInstances(Collection descriptionsInstances) { 345 descriptionsInstances = new ArrayList (); 346 347 Lookup lookup = MimeLookup.getLookup(MimePath.parse(mimeType)); 348 BaseOptions baseOptions = (BaseOptions) lookup.lookup(BaseOptions.class); 349 if (baseOptions != null) { 350 Map abbrevMap = baseOptions.getAbbrevMap(); 351 if (abbrevMap != null) { 352 for (Iterator entryIt = abbrevMap.entrySet().iterator(); entryIt.hasNext();) { 353 Map.Entry entry = (Map.Entry )entryIt.next(); 354 String abbreviation = (String )entry.getKey(); 355 String abbrevText = (String )entry.getValue(); 356 357 String parametrizedText = abbrevText.replaceAll( 358 "([^|]+)[|]([^|]+)", "$1\\${cursor}$2"); parametrizedText.replaceAll("[|][|]", "[|]"); 361 String desc = abbrevText; 362 int nlInd = abbrevText.indexOf('\n'); 363 if (nlInd != -1) { 364 desc = abbrevText.substring(0, nlInd) + "..."; } 366 StringBuffer htmlText = new StringBuffer (); 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 if (!settingsListeningInitialized) { 381 settingsListeningInitialized = true; 382 Settings.addSettingsChangeListener(this); 383 } 384 } 385 return descriptionsInstances; 386 } 387 388 private void rebuildCodeTemplates() { 389 Collection descriptionsInstances = descriptions.allInstances(); 390 descriptionsInstances = updateDescriptionInstances(descriptionsInstances); 391 List codeTemplates = new ArrayList (descriptionsInstances.size()); 392 selectionTemplates = new ArrayList (descriptionsInstances.size()); 393 CodeTemplateApiPackageAccessor api = CodeTemplateApiPackageAccessor.get(); 394 for (Iterator 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) { selectionTemplates.add(ct); 402 } 403 } 404 405 refreshMaps(codeTemplates); 406 } 407 408 private void refreshMaps(List codeTemplates) { 409 abbrev2template = new HashMap (codeTemplates.size()); 410 sortedTemplatesByAbbrev = new ArrayList (codeTemplates.size()); 411 unmodSortedTemplatesByAbbrev = Collections.unmodifiableList(sortedTemplatesByAbbrev); 412 sortedTemplatesByParametrizedText = new ArrayList (codeTemplates.size()); 413 for (Iterator it = codeTemplates.iterator(); it.hasNext();) { 415 CodeTemplate template = (CodeTemplate)it.next(); 416 String abbreviation = template.getAbbreviation(); 417 abbrev2template.put(abbreviation, template); 418 sortedTemplatesByAbbrev.add(template); 419 sortedTemplatesByParametrizedText.add(template); 420 } 421 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 |