1 package com
.intellij
.codeInsight
.template
.impl
;
3 import com
.intellij
.codeInsight
.CodeInsightBundle
;
4 import com
.intellij
.codeInsight
.template
.Template
;
5 import com
.intellij
.openapi
.application
.PathManager
;
6 import com
.intellij
.openapi
.application
.ex
.DecodeDefaultsUtil
;
7 import com
.intellij
.openapi
.components
.*;
8 import com
.intellij
.openapi
.diagnostic
.Logger
;
9 import com
.intellij
.openapi
.extensions
.Extensions
;
10 import com
.intellij
.openapi
.options
.SchemeProcessor
;
11 import com
.intellij
.openapi
.options
.SchemesManager
;
12 import com
.intellij
.openapi
.options
.SchemesManagerFactory
;
13 import com
.intellij
.openapi
.util
.DefaultJDOMExternalizer
;
14 import com
.intellij
.openapi
.util
.InvalidDataException
;
15 import com
.intellij
.openapi
.util
.JDOMUtil
;
16 import com
.intellij
.openapi
.util
.WriteExternalException
;
17 import org
.jdom
.Document
;
18 import org
.jdom
.Element
;
19 import org
.jdom
.JDOMException
;
20 import org
.jetbrains
.annotations
.NonNls
;
21 import org
.jetbrains
.annotations
.NotNull
;
22 import org
.jetbrains
.annotations
.Nullable
;
25 import java
.io
.IOException
;
26 import java
.io
.InputStream
;
31 name
="TemplateSettings",
35 file
= "$APP_CONFIG$/other.xml"
38 public class TemplateSettings
implements PersistentStateComponent
<Element
>, ExportableComponent
{
40 private static final Logger LOG
= Logger
.getInstance("#com.intellij.codeInsight.template.impl.TemplateSettings");
42 public @NonNls static final String USER_GROUP_NAME
= "user";
43 private @NonNls static final String TEMPLATE_SET
= "templateSet";
44 private @NonNls static final String GROUP
= "group";
45 private @NonNls static final String TEMPLATE
= "template";
47 private @NonNls static final String DELETED_TEMPLATES
= "deleted_templates";
48 private List
<String
> myDeletedTemplates
= new ArrayList
<String
>();
50 public static final char SPACE_CHAR
= ' ';
51 public static final char TAB_CHAR
= '\t';
52 public static final char ENTER_CHAR
= '\n';
53 public static final char DEFAULT_CHAR
= 'D';
55 private static final @NonNls String SPACE
= "SPACE";
56 private static final @NonNls String TAB
= "TAB";
57 private static final @NonNls String ENTER
= "ENTER";
59 private static final @NonNls String NAME
= "name";
60 private static final @NonNls String VALUE
= "value";
61 private static final @NonNls String DESCRIPTION
= "description";
62 private static final @NonNls String SHORTCUT
= "shortcut";
64 private static final @NonNls String VARIABLE
= "variable";
65 private static final @NonNls String EXPRESSION
= "expression";
66 private static final @NonNls String DEFAULT_VALUE
= "defaultValue";
67 private static final @NonNls String ALWAYS_STOP_AT
= "alwaysStopAt";
69 private static final @NonNls String CONTEXT
= "context";
70 private static final @NonNls String TO_REFORMAT
= "toReformat";
71 private static final @NonNls String TO_SHORTEN_FQ_NAMES
= "toShortenFQNames";
73 private static final @NonNls String DEFAULT_SHORTCUT
= "defaultShortcut";
74 private static final @NonNls String DEACTIVATED
= "deactivated";
76 @NonNls private static final String RESOURCE_BUNDLE
= "resource-bundle";
77 @NonNls private static final String KEY
= "key";
78 @NonNls private static final String ID
= "id";
80 private static final @NonNls String TEMPLATES_CONFIG_FOLDER
= "templates";
82 private final Map
<String
,Template
> myTemplates
= new LinkedHashMap
<String
,Template
>();
83 private final Map
<String
,Template
> myTemplatesById
= new LinkedHashMap
<String
,Template
>();
84 private final Map
<String
,TemplateImpl
> myDefaultTemplates
= new LinkedHashMap
<String
, TemplateImpl
>();
86 private int myMaxKeyLength
= 0;
87 private char myDefaultShortcutChar
= TAB_CHAR
;
88 private String myLastSelectedTemplateKey
;
90 public static final String XML_EXTENSION
= ".xml";
91 private final SchemesManager
<TemplateGroup
, TemplateGroup
> mySchemesManager
;
92 private final SchemeProcessor
<TemplateGroup
> myProcessor
;
93 private static final String FILE_SPEC
= "$ROOT_CONFIG$/templates";
95 public TemplateSettings(SchemesManagerFactory schemesManagerFactory
) {
98 myProcessor
= new SchemeProcessor
<TemplateGroup
>() {
99 public TemplateGroup
readScheme(final Document schemeContent
)
100 throws InvalidDataException
, IOException
, JDOMException
{
101 return readTemplateFile(schemeContent
, schemeContent
.getRootElement().getAttributeValue("group"), false, false);
105 public boolean shouldBeSaved(final TemplateGroup template
) {
106 for (TemplateImpl t
: template
.getTemplates()) {
107 if (!t
.equals(myDefaultTemplates
.get(t
.getKey()))) {
114 public Document
writeScheme(final TemplateGroup template
) throws WriteExternalException
{
115 Element templateSetElement
= new Element(TEMPLATE_SET
);
116 templateSetElement
.setAttribute(GROUP
, template
.getName());
118 for (TemplateImpl t
: template
.getTemplates()) {
119 if (!t
.equals(myDefaultTemplates
.get(t
.getKey()))) {
120 saveTemplate(t
, templateSetElement
);
124 return new Document(templateSetElement
);
127 public void showReadErrorMessage(final Exception e
, final String schemeName
, final String filePath
) {
131 public void showWriteErrorMessage(final Exception e
, final String schemeName
, final String filePath
) {
135 public void initScheme(final TemplateGroup scheme
) {
136 Collection
<TemplateImpl
> templates
= scheme
.getTemplates();
138 for (TemplateImpl template
: templates
) {
139 addTemplate(template
);
144 mySchemesManager
= schemesManagerFactory
.createSchemesManager(FILE_SPEC
, myProcessor
, RoamingType
.PER_USER
);
150 public File
[] getExportFiles() {
151 return new File
[]{getTemplateDirectory(true),PathManager
.getDefaultOptionsFile()};
155 public String
getPresentableName() {
156 return CodeInsightBundle
.message("templates.export.display.name");
159 public static TemplateSettings
getInstance() {
160 return ServiceManager
.getService(TemplateSettings
.class);
163 public void loadState(Element parentNode
) {
164 Element element
= parentNode
.getChild(DEFAULT_SHORTCUT
);
165 if (element
!= null) {
166 String shortcut
= element
.getAttributeValue(SHORTCUT
);
167 if (TAB
.equals(shortcut
)) {
168 myDefaultShortcutChar
= TAB_CHAR
;
169 } else if (ENTER
.equals(shortcut
)) {
170 myDefaultShortcutChar
= ENTER_CHAR
;
172 myDefaultShortcutChar
= SPACE_CHAR
;
176 Element deleted
= parentNode
.getChild(DELETED_TEMPLATES
);
177 if (deleted
!= null) {
178 List children
= deleted
.getChildren();
179 for (final Object aChildren
: children
) {
180 Element child
= (Element
)aChildren
;
181 myDeletedTemplates
.add(child
.getAttributeValue(NAME
));
185 for (String name
: myDeletedTemplates
) {
186 Template toDelete
= myTemplates
.get(name
);
187 if (toDelete
!= null) {
188 removeTemplate(toDelete
);
192 //TODO lesya reload schemes
195 public Element
getState() {
196 Element parentNode
= new Element("TemplateSettings");
197 Element element
= new Element(DEFAULT_SHORTCUT
);
198 if (myDefaultShortcutChar
== TAB_CHAR
) {
199 element
.setAttribute(SHORTCUT
, TAB
);
200 } else if (myDefaultShortcutChar
== ENTER_CHAR
) {
201 element
.setAttribute(SHORTCUT
, ENTER
);
203 element
.setAttribute(SHORTCUT
, SPACE
);
205 parentNode
.addContent(element
);
207 if (myDeletedTemplates
.size() > 0) {
208 Element deleted
= new Element(DELETED_TEMPLATES
);
209 for (final String myDeletedTemplate
: myDeletedTemplates
) {
210 Element template
= new Element(TEMPLATE
);
211 template
.setAttribute(NAME
, myDeletedTemplate
);
212 deleted
.addContent(template
);
215 parentNode
.addContent(deleted
);
220 public String
getLastSelectedTemplateKey() {
221 return myLastSelectedTemplateKey
;
224 public void setLastSelectedTemplateKey(String key
) {
225 myLastSelectedTemplateKey
= key
;
228 public TemplateImpl
[] getTemplates() {
229 return myTemplates
.values().toArray(new TemplateImpl
[myTemplates
.size()]);
232 public char getDefaultShortcutChar() {
233 return myDefaultShortcutChar
;
236 public void setDefaultShortcutChar(char defaultShortcutChar
) {
237 myDefaultShortcutChar
= defaultShortcutChar
;
240 public TemplateImpl
getTemplate(@NonNls String key
) {
241 return (TemplateImpl
) myTemplates
.get(key
);
244 public Template
getTemplateById(@NonNls String id
) {
245 return myTemplatesById
.get(id
);
248 public int getMaxKeyLength() {
249 return myMaxKeyLength
;
252 public void addTemplate(Template template
) {
253 clearPreviouslyRegistered(template
);
254 addTemplateImpl(template
);
257 private void clearPreviouslyRegistered(final Template template
) {
258 TemplateImpl existing
= getTemplate(template
.getKey());
259 if (existing
!= null) {
260 LOG
.info("Template with key " + template
.getKey() + " and id " + template
.getId() + " already registered");
261 TemplateGroup group
= mySchemesManager
.findSchemeByName(existing
.getGroupName());
263 group
.removeTemplate(existing
);
264 if (group
.isEmpty()) {
265 mySchemesManager
.removeScheme(group
);
268 myTemplatesById
.remove(template
.getId());
269 myTemplates
.remove(template
.getKey());
273 private void addTemplateImpl(Template template
) {
274 if (!myTemplates
.containsKey(template
.getKey()) && !myTemplatesById
.containsKey(template
.getId())) {
275 myTemplates
.put(template
.getKey(), template
);
277 final String id
= template
.getId();
279 myTemplatesById
.put(id
, template
);
281 myMaxKeyLength
= Math
.max(myMaxKeyLength
, template
.getKey().length());
283 myDeletedTemplates
.remove(template
.getKey());
287 public void removeTemplate(Template template
) {
288 myTemplates
.remove(template
.getKey());
289 myTemplatesById
.remove(template
.getId());
291 TemplateImpl templImpl
= (TemplateImpl
)template
;
292 String groupName
= templImpl
.getGroupName();
293 TemplateGroup group
= mySchemesManager
.findSchemeByName(groupName
);
296 group
.removeTemplate((TemplateImpl
)template
);
297 if (group
.isEmpty()) {
298 mySchemesManager
.removeScheme(group
);
304 private TemplateImpl
addTemplate(String key
, String string
, String group
, String description
, String shortcut
, boolean isDefault
,
306 TemplateImpl template
= new TemplateImpl(key
, string
, group
);
308 template
.setDescription(description
);
309 if (TAB
.equals(shortcut
)) {
310 template
.setShortcutChar(TAB_CHAR
);
311 } else if (ENTER
.equals(shortcut
)) {
312 template
.setShortcutChar(ENTER_CHAR
);
313 } else if (SPACE
.equals(shortcut
)) {
314 template
.setShortcutChar(SPACE_CHAR
);
316 template
.setShortcutChar(DEFAULT_CHAR
);
319 myDefaultTemplates
.put(key
, template
);
325 private static File
getTemplateDirectory(boolean toCreate
) {
326 String directoryPath
= PathManager
.getConfigPath() + File
.separator
+ TEMPLATES_CONFIG_FOLDER
;
327 File directory
= new File(directoryPath
);
328 if (!directory
.exists()) {
332 if (!directory
.mkdir()) {
333 if (LOG
.isDebugEnabled()) {
334 LOG
.debug("cannot create directory: " + directory
.getAbsolutePath());
342 private void loadTemplates() {
344 mySchemesManager
.loadSchemes();
348 for(DefaultLiveTemplatesProvider provider
: Extensions
.getExtensions(DefaultLiveTemplatesProvider
.EP_NAME
)) {
349 for (String defTemplate
: provider
.getDefaultLiveTemplateFiles()) {
350 String templateName
= getDefaultTemplateName(defTemplate
);
351 InputStream inputStream
= DecodeDefaultsUtil
.getDefaultsInputStream(provider
, defTemplate
);
352 if (inputStream
!= null) {
353 readDefTemplateFile(inputStream
, templateName
);
357 } catch (Exception e
) {
362 public static String
getDefaultTemplateName(String defTemplate
) {
363 return defTemplate
.substring(defTemplate
.lastIndexOf("/") + 1);
366 public void readDefTemplateFile(InputStream inputStream
, String defGroupName
) throws JDOMException
, InvalidDataException
, IOException
{
367 readTemplateFile(JDOMUtil
.loadDocument(inputStream
), defGroupName
, true, true);
371 public TemplateGroup
readTemplateFile(Document document
, @NonNls String defGroupName
, boolean isDefault
, boolean registerTemplate
) throws InvalidDataException
{
372 if (document
== null) {
373 throw new InvalidDataException();
375 Element root
= document
.getRootElement();
376 if (root
== null || !TEMPLATE_SET
.equals(root
.getName())) {
377 throw new InvalidDataException();
380 String groupName
= root
.getAttributeValue(GROUP
);
381 if (groupName
== null || groupName
.length() == 0) groupName
= defGroupName
;
383 TemplateGroup result
= new TemplateGroup(groupName
);
385 Map
<String
, TemplateImpl
> created
= new LinkedHashMap
<String
, TemplateImpl
>();
387 for (final Object o1
: root
.getChildren(TEMPLATE
)) {
388 Element element
= (Element
)o1
;
390 String name
= element
.getAttributeValue(NAME
);
391 String value
= element
.getAttributeValue(VALUE
);
393 String resourceBundle
= element
.getAttributeValue(RESOURCE_BUNDLE
);
394 String key
= element
.getAttributeValue(KEY
);
395 String id
= element
.getAttributeValue(ID
);
396 if (resourceBundle
!= null && key
!= null) {
397 ResourceBundle bundle
= ResourceBundle
.getBundle(resourceBundle
);
398 description
= bundle
.getString(key
);
401 description
= element
.getAttributeValue(DESCRIPTION
);
403 String shortcut
= element
.getAttributeValue(SHORTCUT
);
404 if (isDefault
&& (myDeletedTemplates
.contains(name
) || myTemplates
.containsKey(name
))) continue;
405 TemplateImpl template
= addTemplate(name
, value
, groupName
, description
, shortcut
, isDefault
, id
);
406 template
.setToReformat(Boolean
.parseBoolean(element
.getAttributeValue(TO_REFORMAT
)));
407 template
.setToShortenLongNames(Boolean
.parseBoolean(element
.getAttributeValue(TO_SHORTEN_FQ_NAMES
)));
408 template
.setDeactivated(Boolean
.parseBoolean(element
.getAttributeValue(DEACTIVATED
)));
411 for (final Object o
: element
.getChildren(VARIABLE
)) {
412 Element e
= (Element
)o
;
413 String variableName
= e
.getAttributeValue(NAME
);
414 String expression
= e
.getAttributeValue(EXPRESSION
);
415 String defaultValue
= e
.getAttributeValue(DEFAULT_VALUE
);
416 boolean isAlwaysStopAt
= Boolean
.parseBoolean(e
.getAttributeValue(ALWAYS_STOP_AT
));
417 template
.addVariable(variableName
, expression
, defaultValue
, isAlwaysStopAt
);
420 Element context
= element
.getChild(CONTEXT
);
421 if (context
!= null) {
422 DefaultJDOMExternalizer
.readExternal(template
.getTemplateContext(), context
);
425 created
.put(template
.getKey(), template
);
431 if (registerTemplate
) {
432 TemplateGroup existingScheme
= mySchemesManager
.findSchemeByName(result
.getName());
433 if (existingScheme
!= null) {
434 result
= existingScheme
;
438 for (TemplateImpl template
: created
.values()) {
439 if (registerTemplate
) {
440 addTemplate(template
);
443 result
.addTemplate(template
);
446 if (registerTemplate
) {
447 TemplateGroup existingScheme
= mySchemesManager
.findSchemeByName(result
.getName());
448 if (existingScheme
== null && !result
.isEmpty()) {
449 mySchemesManager
.addNewScheme(result
, false);
453 return result
.isEmpty() ?
null : result
;
457 private static void saveTemplate(TemplateImpl template
, Element templateSetElement
) {
458 Element element
= new Element(TEMPLATE
);
459 final String id
= template
.getId();
461 element
.setAttribute(ID
, id
);
463 element
.setAttribute(NAME
, template
.getKey());
464 element
.setAttribute(VALUE
, template
.getString());
465 if (template
.getShortcutChar() == TAB_CHAR
) {
466 element
.setAttribute(SHORTCUT
, TAB
);
467 } else if (template
.getShortcutChar() == ENTER_CHAR
) {
468 element
.setAttribute(SHORTCUT
, ENTER
);
469 } else if (template
.getShortcutChar() == SPACE_CHAR
) {
470 element
.setAttribute(SHORTCUT
, SPACE
);
472 if (template
.getDescription() != null) {
473 element
.setAttribute(DESCRIPTION
, template
.getDescription());
475 element
.setAttribute(TO_REFORMAT
, Boolean
.toString(template
.isToReformat()));
476 element
.setAttribute(TO_SHORTEN_FQ_NAMES
, Boolean
.toString(template
.isToShortenLongNames()));
477 if (template
.isDeactivated()) {
478 element
.setAttribute(DEACTIVATED
, Boolean
.toString(true));
481 for (int i
= 0; i
< template
.getVariableCount(); i
++) {
482 Element variableElement
= new Element(VARIABLE
);
483 variableElement
.setAttribute(NAME
, template
.getVariableNameAt(i
));
484 variableElement
.setAttribute(EXPRESSION
, template
.getExpressionStringAt(i
));
485 variableElement
.setAttribute(DEFAULT_VALUE
, template
.getDefaultValueStringAt(i
));
486 variableElement
.setAttribute(ALWAYS_STOP_AT
, Boolean
.toString(template
.isAlwaysStopAt(i
)));
487 element
.addContent(variableElement
);
491 Element contextElement
= new Element(CONTEXT
);
492 DefaultJDOMExternalizer
.writeExternal(template
.getTemplateContext(), contextElement
);
493 element
.addContent(contextElement
);
494 } catch (WriteExternalException e
) {
496 templateSetElement
.addContent(element
);
499 public void setTemplates(List
<TemplateGroup
> newGroups
) {
501 myTemplatesById
.clear();
502 myDeletedTemplates
.clear();
503 for (TemplateImpl template
: myDefaultTemplates
.values()) {
504 myDeletedTemplates
.add(template
.getKey());
506 mySchemesManager
.clearAllSchemes();
508 for (TemplateGroup group
: newGroups
) {
509 if (!group
.isEmpty()) {
510 mySchemesManager
.addNewScheme(group
, true);
511 for (TemplateImpl template
: group
.getTemplates()) {
512 addTemplate(template
);
518 public SchemesManager
<TemplateGroup
,TemplateGroup
> getSchemesManager() {
519 return mySchemesManager
;
522 public List
<TemplateGroup
> getTemplateGroups() {
523 return mySchemesManager
.getAllSchemes();