a1f72df86d463ebd0f9fb66ab90a227561e08836
[idea/community.git] / platform / lang-impl / src / com / intellij / codeInsight / template / impl / TemplateSettings.java
1 /*
2  * Copyright 2000-2009 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package com.intellij.codeInsight.template.impl;
18
19 import com.intellij.codeInsight.CodeInsightBundle;
20 import com.intellij.codeInsight.template.Template;
21 import com.intellij.openapi.application.PathManager;
22 import com.intellij.openapi.application.ex.DecodeDefaultsUtil;
23 import com.intellij.openapi.components.*;
24 import com.intellij.openapi.diagnostic.Logger;
25 import com.intellij.openapi.extensions.Extensions;
26 import com.intellij.openapi.options.Scheme;
27 import com.intellij.openapi.options.SchemeProcessor;
28 import com.intellij.openapi.options.SchemesManager;
29 import com.intellij.openapi.options.SchemesManagerFactory;
30 import com.intellij.openapi.util.InvalidDataException;
31 import com.intellij.openapi.util.JDOMUtil;
32 import com.intellij.openapi.util.WriteExternalException;
33 import com.intellij.util.containers.MultiMap;
34 import org.jdom.Document;
35 import org.jdom.Element;
36 import org.jdom.JDOMException;
37 import org.jetbrains.annotations.NonNls;
38 import org.jetbrains.annotations.NotNull;
39 import org.jetbrains.annotations.Nullable;
40
41 import java.io.File;
42 import java.io.IOException;
43 import java.io.InputStream;
44 import java.util.*;
45
46
47 @State(
48   name="TemplateSettings",
49   storages= {
50     @Storage(
51       id="other",
52       file = "$APP_CONFIG$/other.xml"
53     )}
54 )
55 public class TemplateSettings implements PersistentStateComponent<Element>, ExportableComponent {
56
57   private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.template.impl.TemplateSettings");
58
59   public  @NonNls static final String USER_GROUP_NAME = "user";
60   private @NonNls static final String TEMPLATE_SET = "templateSet";
61   private @NonNls static final String GROUP = "group";
62   private @NonNls static final String TEMPLATE = "template";
63
64   private @NonNls static final String DELETED_TEMPLATES = "deleted_templates";
65   private final List<TemplateKey> myDeletedTemplates = new ArrayList<TemplateKey>();
66
67   public static final char SPACE_CHAR = ' ';
68   public static final char TAB_CHAR = '\t';
69   public static final char ENTER_CHAR = '\n';
70   public static final char DEFAULT_CHAR = 'D';
71
72   private static final @NonNls String SPACE = "SPACE";
73   private static final @NonNls String TAB = "TAB";
74   private static final @NonNls String ENTER = "ENTER";
75
76   private static final @NonNls String NAME = "name";
77   private static final @NonNls String VALUE = "value";
78   private static final @NonNls String DESCRIPTION = "description";
79   private static final @NonNls String SHORTCUT = "shortcut";
80
81   private static final @NonNls String VARIABLE = "variable";
82   private static final @NonNls String EXPRESSION = "expression";
83   private static final @NonNls String DEFAULT_VALUE = "defaultValue";
84   private static final @NonNls String ALWAYS_STOP_AT = "alwaysStopAt";
85
86   private static final @NonNls String CONTEXT = "context";
87   private static final @NonNls String TO_REFORMAT = "toReformat";
88   private static final @NonNls String TO_SHORTEN_FQ_NAMES = "toShortenFQNames";
89
90   private static final @NonNls String DEFAULT_SHORTCUT = "defaultShortcut";
91   private static final @NonNls String DEACTIVATED = "deactivated";
92
93   @NonNls private static final String RESOURCE_BUNDLE = "resource-bundle";
94   @NonNls private static final String KEY = "key";
95   @NonNls private static final String ID = "id";
96
97   private static final @NonNls String TEMPLATES_CONFIG_FOLDER = "templates";
98
99   private final List<TemplateImpl> myAllTemplates = new ArrayList<TemplateImpl>();
100   private final MultiMap<String,TemplateImpl> myTemplates = new MultiMap<String,TemplateImpl>();
101   private final Map<String,Template> myTemplatesById = new LinkedHashMap<String,Template>();
102   private final Map<String,TemplateImpl> myDefaultTemplates = new LinkedHashMap<String, TemplateImpl>();
103
104   private int myMaxKeyLength = 0;
105   private char myDefaultShortcutChar = TAB_CHAR;
106   private String myLastSelectedTemplateKey;
107   @NonNls
108   public static final String XML_EXTENSION = ".xml";
109   private final SchemesManager<TemplateGroup, TemplateGroup> mySchemesManager;
110   private final SchemeProcessor<TemplateGroup> myProcessor;
111   private static final String FILE_SPEC = "$ROOT_CONFIG$/templates";
112
113   private static class TemplateKey {
114     final String groupName;
115     final String key;
116
117     private TemplateKey(String groupName, String key) {
118       this.groupName = groupName;
119       this.key = key;
120     }
121
122     public static TemplateKey keyOf(TemplateImpl template) {
123       return new TemplateKey(template.getGroupName(), template.getKey());
124     }
125
126     public boolean equals(Object o) {
127       if (this == o) return true;
128       if (o == null || getClass() != o.getClass()) return false;
129
130       TemplateKey that = (TemplateKey)o;
131
132       if (groupName != null ? !groupName.equals(that.groupName) : that.groupName != null) return false;
133       if (key != null ? !key.equals(that.key) : that.key != null) return false;
134
135       return true;
136     }
137
138     public int hashCode() {
139       int result = groupName != null ? groupName.hashCode() : 0;
140       result = 31 * result + (key != null ? key.hashCode() : 0);
141       return result;
142     }
143   }
144
145   public TemplateSettings(SchemesManagerFactory schemesManagerFactory) {
146
147
148     myProcessor = new SchemeProcessor<TemplateGroup>() {
149       public TemplateGroup readScheme(final Document schemeContent)
150           throws InvalidDataException, IOException, JDOMException {
151         return readTemplateFile(schemeContent, schemeContent.getRootElement().getAttributeValue("group"), false, false,
152                                 getClass().getClassLoader());
153       }
154
155
156       public boolean shouldBeSaved(final TemplateGroup template) {
157         for (TemplateImpl t : template.getElements()) {
158           if (differsFromDefault(t)) {
159             return true;
160           }
161         }
162         return false;
163       }
164
165       public Document writeScheme(final TemplateGroup template) throws WriteExternalException {
166         Element templateSetElement = new Element(TEMPLATE_SET);
167         templateSetElement.setAttribute(GROUP, template.getName());
168
169         for (TemplateImpl t : template.getElements()) {
170           if (differsFromDefault(t)) {
171             saveTemplate(t, templateSetElement);
172           }
173         }
174
175         return new Document(templateSetElement);
176       }
177
178       public void initScheme(final TemplateGroup scheme) {
179         Collection<TemplateImpl> templates = scheme.getElements();
180
181         for (TemplateImpl template : templates) {
182           addTemplateImpl(template);
183         }
184       }
185
186       public void onSchemeAdded(final TemplateGroup scheme) {
187         for (TemplateImpl template : scheme.getElements()) {
188           addTemplateImpl(template);
189         }
190       }
191
192       public void onSchemeDeleted(final TemplateGroup scheme) {
193         for (TemplateImpl template : scheme.getElements()) {
194           removeTemplate(template);
195         }
196       }
197
198       public void onCurrentSchemeChanged(final Scheme newCurrentScheme) {
199
200       }
201     };
202
203     mySchemesManager = schemesManagerFactory.createSchemesManager(FILE_SPEC, myProcessor, RoamingType.PER_USER);
204
205     loadTemplates();
206   }
207
208   private boolean differsFromDefault(TemplateImpl t) {
209     TemplateImpl def = myDefaultTemplates.get(t.getKey());
210     if (def == null) return true;
211     return !t.equals(def) || !t.contextsEqual(def);
212   }
213
214   @NotNull
215   public File[] getExportFiles() {
216     return new File[]{getTemplateDirectory(true),PathManager.getDefaultOptionsFile()};
217   }
218
219   @NotNull
220   public String getPresentableName() {
221     return CodeInsightBundle.message("templates.export.display.name");
222   }
223
224   public static TemplateSettings getInstance() {
225     return ServiceManager.getService(TemplateSettings.class);
226   }
227
228   public void loadState(Element parentNode) {
229     Element element = parentNode.getChild(DEFAULT_SHORTCUT);
230     if (element != null) {
231       String shortcut = element.getAttributeValue(SHORTCUT);
232       if (TAB.equals(shortcut)) {
233         myDefaultShortcutChar = TAB_CHAR;
234       } else if (ENTER.equals(shortcut)) {
235         myDefaultShortcutChar = ENTER_CHAR;
236       } else {
237         myDefaultShortcutChar = SPACE_CHAR;
238       }
239     }
240
241     Element deleted = parentNode.getChild(DELETED_TEMPLATES);
242     if (deleted != null) {
243       List children = deleted.getChildren();
244       for (final Object aChildren : children) {
245         Element child = (Element)aChildren;
246         myDeletedTemplates.add(new TemplateKey(child.getAttributeValue(GROUP), child.getAttributeValue(NAME)));
247       }
248     }
249
250     for (TemplateKey templateKey : myDeletedTemplates) {
251       if (templateKey.groupName == null) {
252         final Collection<TemplateImpl> templates = myTemplates.get(templateKey.key);
253         for (TemplateImpl template : templates) {
254           removeTemplate(template);
255         }
256       }
257       else {
258         final TemplateImpl toDelete = getTemplate(templateKey.key, templateKey.groupName);
259         if (toDelete != null) {
260           removeTemplate(toDelete);
261         }
262       }
263     }
264
265     //TODO lesya reload schemes
266   }
267
268   public Element getState()  {
269     Element parentNode = new Element("TemplateSettings");
270     Element element = new Element(DEFAULT_SHORTCUT);
271     if (myDefaultShortcutChar == TAB_CHAR) {
272       element.setAttribute(SHORTCUT, TAB);
273     } else if (myDefaultShortcutChar == ENTER_CHAR) {
274       element.setAttribute(SHORTCUT, ENTER);
275     } else {
276       element.setAttribute(SHORTCUT, SPACE);
277     }
278     parentNode.addContent(element);
279
280     if (myDeletedTemplates.size() > 0) {
281       Element deleted = new Element(DELETED_TEMPLATES);
282       for (final TemplateKey deletedTemplate : myDeletedTemplates) {
283         if (deletedTemplate.key != null) {
284           Element template = new Element(TEMPLATE);
285           template.setAttribute(NAME, deletedTemplate.key);
286           if (deletedTemplate.groupName != null) {
287             template.setAttribute(GROUP, deletedTemplate.groupName);
288           }
289           deleted.addContent(template);
290         }
291       }
292       parentNode.addContent(deleted);
293     }
294     return parentNode;
295   }
296
297   public String getLastSelectedTemplateKey() {
298     return myLastSelectedTemplateKey;
299   }
300
301   public void setLastSelectedTemplateKey(String key) {
302     myLastSelectedTemplateKey = key;
303   }
304
305   public TemplateImpl[] getTemplates() {
306     return myAllTemplates.toArray(new TemplateImpl[myAllTemplates.size()]);
307   }
308
309   public char getDefaultShortcutChar() {
310     return myDefaultShortcutChar;
311   }
312
313   public void setDefaultShortcutChar(char defaultShortcutChar) {
314     myDefaultShortcutChar = defaultShortcutChar;
315   }
316
317   public Collection<TemplateImpl> getTemplates(@NonNls String key) {
318     return myTemplates.get(key);
319   }
320
321   public TemplateImpl getTemplate(@NonNls String key, String group) {
322     final Collection<TemplateImpl> templates = myTemplates.get(key);
323     for (TemplateImpl template : templates) {
324       if (template.getGroupName().equals(group)) {
325         return template;
326       }
327     }
328     return null;
329   }
330
331   public Template getTemplateById(@NonNls String id) {
332     return myTemplatesById.get(id);
333   }
334
335   public int getMaxKeyLength() {
336     return myMaxKeyLength;
337   }
338
339   public void addTemplate(Template template) {
340     clearPreviouslyRegistered(template);
341     addTemplateImpl(template);
342
343     TemplateImpl templateImpl = (TemplateImpl)template;
344     String groupName = templateImpl.getGroupName();
345     TemplateGroup group = mySchemesManager.findSchemeByName(groupName);
346     if (group == null) {
347       group = new TemplateGroup(groupName);
348       mySchemesManager.addNewScheme(group, true);
349     }
350     group.addElement(templateImpl);
351   }
352
353   private void clearPreviouslyRegistered(final Template template) {
354     TemplateImpl existing = getTemplate(template.getKey(), ((TemplateImpl) template).getGroupName());
355     if (existing != null) {
356       LOG.info("Template with key " + template.getKey() + " and id " + template.getId() + " already registered");
357       TemplateGroup group = mySchemesManager.findSchemeByName(existing.getGroupName());
358       if (group != null) {
359         group.removeElement(existing);
360         if (group.isEmpty()) {
361           mySchemesManager.removeScheme(group);
362         }
363       }
364       myTemplates.removeValue(template.getKey(), existing);
365     }
366   }
367
368   private void addTemplateImpl(Template template) {
369     final TemplateImpl templateImpl = (TemplateImpl)template;
370     if (getTemplate(templateImpl.getKey(), templateImpl.getGroupName()) == null) {
371       myTemplates.putValue(template.getKey(), templateImpl);
372       myAllTemplates.add(templateImpl);
373     }
374
375     myMaxKeyLength = Math.max(myMaxKeyLength, template.getKey().length());
376     myDeletedTemplates.remove(TemplateKey.keyOf((TemplateImpl)template));
377
378   }
379
380   private void addTemplateById(Template template) {
381     if (!myTemplatesById.containsKey(template.getId())) {
382       final String id = template.getId();
383       if (id != null) {
384         myTemplatesById.put(id, template);
385       }
386     }
387   }
388
389   public void removeTemplate(Template template) {
390     myTemplates.removeValue(template.getKey(), (TemplateImpl )template);
391
392     TemplateImpl templateImpl = (TemplateImpl)template;
393     myAllTemplates.remove(templateImpl);
394     String groupName = templateImpl.getGroupName();
395     TemplateGroup group = mySchemesManager.findSchemeByName(groupName);
396
397     if (group != null) {
398       group.removeElement((TemplateImpl)template);
399       if (group.isEmpty()) {
400         mySchemesManager.removeScheme(group);
401       }
402
403     }
404   }
405
406   private TemplateImpl addTemplate(String key, String string, String group, String description, String shortcut, boolean isDefault,
407                                    final String id) {
408     TemplateImpl template = new TemplateImpl(key, string, group);
409     template.setId(id);
410     template.setDescription(description);
411     if (TAB.equals(shortcut)) {
412       template.setShortcutChar(TAB_CHAR);
413     } else if (ENTER.equals(shortcut)) {
414       template.setShortcutChar(ENTER_CHAR);
415     } else if (SPACE.equals(shortcut)) {
416       template.setShortcutChar(SPACE_CHAR);
417     } else {
418       template.setShortcutChar(DEFAULT_CHAR);
419     }
420     if (isDefault) {
421       myDefaultTemplates.put(key, template);
422     }
423     return template;
424   }
425
426   @Nullable
427   private static File getTemplateDirectory(boolean toCreate) {
428     String directoryPath = PathManager.getConfigPath() + File.separator + TEMPLATES_CONFIG_FOLDER;
429     File directory = new File(directoryPath);
430     if (!directory.exists()) {
431       if (!toCreate) {
432         return null;
433       }
434       if (!directory.mkdir()) {
435         if (LOG.isDebugEnabled()) {
436           LOG.debug("cannot create directory: " + directory.getAbsolutePath());
437         }
438         return null;
439       }
440     }
441     return directory;
442   }
443
444   private void loadTemplates() {
445
446     Collection<TemplateGroup> loaded = mySchemesManager.loadSchemes();
447     for (TemplateGroup group : loaded) {
448       Collection<TemplateImpl> templates = group.getElements();
449
450       for (TemplateImpl template : templates) {
451         addTemplateImpl(template);
452       }
453
454     }
455
456
457     try {
458       for(DefaultLiveTemplatesProvider provider: Extensions.getExtensions(DefaultLiveTemplatesProvider.EP_NAME)) {
459         for (String defTemplate : provider.getDefaultLiveTemplateFiles()) {
460           String templateName = getDefaultTemplateName(defTemplate);
461           InputStream inputStream = DecodeDefaultsUtil.getDefaultsInputStream(provider, defTemplate);
462           if (inputStream != null) {
463             readDefTemplateFile(inputStream, templateName, provider.getClass().getClassLoader());
464           }
465         }
466       }
467     } catch (Exception e) {
468       LOG.error(e);
469     }
470   }
471
472   public static String getDefaultTemplateName(String defTemplate) {
473     return defTemplate.substring(defTemplate.lastIndexOf("/") + 1);
474   }
475
476   public void readDefTemplateFile(InputStream inputStream, String defGroupName) throws JDOMException, InvalidDataException, IOException {
477     readDefTemplateFile(inputStream, defGroupName, getClass().getClassLoader());
478   }
479
480   public void readDefTemplateFile(InputStream inputStream, String defGroupName, ClassLoader classLoader) throws JDOMException, InvalidDataException, IOException {
481     readTemplateFile(JDOMUtil.loadDocument(inputStream), defGroupName, true, true, classLoader);
482   }
483
484   @Nullable
485   public TemplateGroup readTemplateFile(Document document, @NonNls String defGroupName, boolean isDefault, boolean registerTemplate) throws InvalidDataException {
486     return readTemplateFile(document, defGroupName, isDefault, registerTemplate, getClass().getClassLoader()  );
487   }
488
489   @Nullable
490   public TemplateGroup readTemplateFile(Document document, @NonNls String defGroupName, boolean isDefault, boolean registerTemplate, ClassLoader classLoader) throws InvalidDataException {
491     if (document == null) {
492       throw new InvalidDataException();
493     }
494     Element root = document.getRootElement();
495     if (root == null || !TEMPLATE_SET.equals(root.getName())) {
496       throw new InvalidDataException();
497     }
498
499     String groupName = root.getAttributeValue(GROUP);
500     if (groupName == null || groupName.length() == 0) groupName = defGroupName;
501
502     TemplateGroup result = new TemplateGroup(groupName);
503
504     Map<String, TemplateImpl> created = new LinkedHashMap<String,  TemplateImpl>();
505
506     for (final Object o1 : root.getChildren(TEMPLATE)) {
507       Element element = (Element)o1;
508
509       TemplateImpl template = readTemplateFromElement(isDefault, groupName, element, classLoader);
510       boolean doNotRegister = isDefault && (myDeletedTemplates.contains(TemplateKey.keyOf(template)) || myTemplatesById.containsKey(template.getId()));
511
512       if(!doNotRegister) {
513         created.put(template.getKey(), template);
514       }
515     }
516
517     if (registerTemplate) {
518       TemplateGroup existingScheme = mySchemesManager.findSchemeByName(result.getName());
519       if (existingScheme != null) {
520         result = existingScheme;
521       }
522     }
523
524     for (TemplateImpl template : created.values()) {
525       if (registerTemplate) {
526         clearPreviouslyRegistered(template);
527         addTemplateImpl(template);
528       }
529
530       result.addElement(template);
531     }
532
533     if (registerTemplate) {
534       TemplateGroup existingScheme = mySchemesManager.findSchemeByName(result.getName());
535       if (existingScheme == null && !result.isEmpty()) {
536         mySchemesManager.addNewScheme(result, false);
537       }
538     }
539
540     return result.isEmpty() ? null : result;
541
542   }
543
544   private TemplateImpl readTemplateFromElement(final boolean isDefault,
545                                                final String groupName,
546                                                final Element element,
547                                                ClassLoader classLoader) throws InvalidDataException {
548     String name = element.getAttributeValue(NAME);
549     String value = element.getAttributeValue(VALUE);
550     String description;
551     String resourceBundle = element.getAttributeValue(RESOURCE_BUNDLE);
552     String key = element.getAttributeValue(KEY);
553     String id = element.getAttributeValue(ID);
554     if (resourceBundle != null && key != null) {
555       if (classLoader == null) {
556         classLoader = getClass().getClassLoader();
557       }
558       ResourceBundle bundle = ResourceBundle.getBundle(resourceBundle, Locale.getDefault(), classLoader);
559       description = bundle.getString(key);
560     }
561     else {
562       description = element.getAttributeValue(DESCRIPTION);
563     }
564     String shortcut = element.getAttributeValue(SHORTCUT);
565     TemplateImpl template = addTemplate(name, value, groupName, description, shortcut, isDefault, id);
566
567     template.setToReformat(Boolean.parseBoolean(element.getAttributeValue(TO_REFORMAT)));
568     template.setToShortenLongNames(Boolean.parseBoolean(element.getAttributeValue(TO_SHORTEN_FQ_NAMES)));
569     template.setDeactivated(Boolean.parseBoolean(element.getAttributeValue(DEACTIVATED)));
570
571
572     for (final Object o : element.getChildren(VARIABLE)) {
573       Element e = (Element)o;
574       String variableName = e.getAttributeValue(NAME);
575       String expression = e.getAttributeValue(EXPRESSION);
576       String defaultValue = e.getAttributeValue(DEFAULT_VALUE);
577       boolean isAlwaysStopAt = Boolean.parseBoolean(e.getAttributeValue(ALWAYS_STOP_AT));
578       template.addVariable(variableName, expression, defaultValue, isAlwaysStopAt);
579     }
580
581     Element context = element.getChild(CONTEXT);
582     if (context != null) {
583       template.getTemplateContext().readExternal(context);
584     }
585
586     return template;
587   }
588
589   public void readHiddenTemplateFile(Document document) throws InvalidDataException {
590     if (document == null) {
591       throw new InvalidDataException();
592     }
593     Element root = document.getRootElement();
594     if (root == null || !TEMPLATE_SET.equals(root.getName())) {
595       throw new InvalidDataException();
596     }
597
598     for (final Object o1 : root.getChildren(TEMPLATE)) {
599
600       addTemplateById(readTemplateFromElement(false, null, (Element)o1, getClass().getClassLoader()));
601     }
602
603
604   }
605
606   private static void saveTemplate(TemplateImpl template, Element templateSetElement) {
607     Element element = new Element(TEMPLATE);
608     final String id = template.getId();
609     if (id != null) {
610       element.setAttribute(ID, id);
611     }
612     element.setAttribute(NAME, template.getKey());
613     element.setAttribute(VALUE, template.getString());
614     if (template.getShortcutChar() == TAB_CHAR) {
615       element.setAttribute(SHORTCUT, TAB);
616     } else if (template.getShortcutChar() == ENTER_CHAR) {
617       element.setAttribute(SHORTCUT, ENTER);
618     } else if (template.getShortcutChar() == SPACE_CHAR) {
619       element.setAttribute(SHORTCUT, SPACE);
620     }
621     if (template.getDescription() != null) {
622       element.setAttribute(DESCRIPTION, template.getDescription());
623     }
624     element.setAttribute(TO_REFORMAT, Boolean.toString(template.isToReformat()));
625     element.setAttribute(TO_SHORTEN_FQ_NAMES, Boolean.toString(template.isToShortenLongNames()));
626     if (template.isDeactivated()) {
627       element.setAttribute(DEACTIVATED, Boolean.toString(true));
628     }
629
630     for (int i = 0; i < template.getVariableCount(); i++) {
631       Element variableElement = new Element(VARIABLE);
632       variableElement.setAttribute(NAME, template.getVariableNameAt(i));
633       variableElement.setAttribute(EXPRESSION, template.getExpressionStringAt(i));
634       variableElement.setAttribute(DEFAULT_VALUE, template.getDefaultValueStringAt(i));
635       variableElement.setAttribute(ALWAYS_STOP_AT, Boolean.toString(template.isAlwaysStopAt(i)));
636       element.addContent(variableElement);
637     }
638
639     try {
640       Element contextElement = new Element(CONTEXT);
641       template.getTemplateContext().writeExternal(contextElement);
642       element.addContent(contextElement);
643     } catch (WriteExternalException e) {
644     }
645     templateSetElement.addContent(element);
646   }
647
648   public void setTemplates(List<TemplateGroup> newGroups) {
649     myTemplates.clear();
650     myAllTemplates.clear();
651     myDeletedTemplates.clear();
652     for (TemplateImpl template : myDefaultTemplates.values()) {
653       myDeletedTemplates.add(TemplateKey.keyOf(template));
654     }
655     mySchemesManager.clearAllSchemes();
656     myMaxKeyLength = 0;
657     for (TemplateGroup group : newGroups) {
658       if (!group.isEmpty()) {
659         mySchemesManager.addNewScheme(group, true);
660         for (TemplateImpl template : group.getElements()) {
661           clearPreviouslyRegistered(template);
662           addTemplateImpl(template);
663         }
664       }
665     }
666   }
667
668   public SchemesManager<TemplateGroup,TemplateGroup> getSchemesManager() {
669     return mySchemesManager;
670   }
671
672   public List<TemplateGroup> getTemplateGroups() {
673     return mySchemesManager.getAllSchemes();
674   }
675
676   public List<TemplateImpl> collectMatchingCandidates(String key, char shortcutChar, boolean hasArgument) {
677     final Collection<TemplateImpl> templates = getTemplates(key);
678     List<TemplateImpl> candidates = new ArrayList<TemplateImpl>();
679     for (TemplateImpl template : templates) {
680       if (template.isDeactivated()) {
681         continue;
682       }
683       if (getShortcutChar(template) != shortcutChar) {
684         continue;
685       }
686       if (template.isSelectionTemplate()) {
687         continue;
688       }
689       if (hasArgument && !template.hasArgument()) {
690         continue;
691       }
692       candidates.add(template);
693     }
694     return candidates;
695   }
696
697   private char getShortcutChar(TemplateImpl template) {
698     char c = template.getShortcutChar();
699     if (c == DEFAULT_CHAR) {
700       return getDefaultShortcutChar();
701     }
702     else {
703       return c;
704     }
705   }
706 }