1 package com
.intellij
.ide
.fileTemplates
.impl
;
3 import com
.intellij
.ide
.IdeBundle
;
4 import com
.intellij
.ide
.fileTemplates
.FileTemplate
;
5 import com
.intellij
.ide
.fileTemplates
.FileTemplateManager
;
6 import com
.intellij
.ide
.fileTemplates
.InternalTemplateBean
;
7 import com
.intellij
.openapi
.application
.ApplicationManager
;
8 import com
.intellij
.openapi
.application
.ApplicationNamesInfo
;
9 import com
.intellij
.openapi
.application
.PathManager
;
10 import com
.intellij
.openapi
.components
.ExportableComponent
;
11 import com
.intellij
.openapi
.components
.ServiceManager
;
12 import com
.intellij
.openapi
.diagnostic
.Logger
;
13 import com
.intellij
.openapi
.extensions
.Extensions
;
14 import com
.intellij
.openapi
.extensions
.PluginDescriptor
;
15 import com
.intellij
.openapi
.fileTypes
.ex
.FileTypeManagerEx
;
16 import com
.intellij
.openapi
.util
.*;
17 import com
.intellij
.openapi
.util
.text
.StringUtil
;
18 import com
.intellij
.openapi
.vfs
.VfsUtil
;
19 import com
.intellij
.openapi
.vfs
.VirtualFile
;
20 import com
.intellij
.openapi
.vfs
.VirtualFileManager
;
21 import com
.intellij
.openapi
.vfs
.newvfs
.BulkFileListener
;
22 import com
.intellij
.openapi
.vfs
.newvfs
.events
.VFileEvent
;
23 import com
.intellij
.util
.ArrayUtil
;
24 import com
.intellij
.util
.Function
;
25 import com
.intellij
.util
.SystemProperties
;
26 import com
.intellij
.util
.messages
.MessageBus
;
27 import gnu
.trove
.THashSet
;
28 import org
.jdom
.Element
;
29 import org
.jetbrains
.annotations
.NonNls
;
30 import org
.jetbrains
.annotations
.NotNull
;
33 import java
.io
.IOException
;
35 import java
.text
.DateFormat
;
36 import java
.text
.MessageFormat
;
44 * locking policy: if the class needs to take a read or write action, the LOCK lock must be taken
45 * _inside_, not outside of the read action
47 public class FileTemplateManagerImpl
extends FileTemplateManager
implements ExportableComponent
, JDOMExternalizable
{
48 private static final Logger LOG
= Logger
.getInstance("#com.intellij.ide.fileTemplates.impl.FileTemplateManagerImpl");
49 @NonNls private static final String DEFAULT_TEMPLATE_EXTENSION
= "ft";
50 @NonNls private static final String TEMPLATES_DIR
= "fileTemplates";
51 @NonNls private static final String DEFAULT_TEMPLATES_TOP_DIR
= TEMPLATES_DIR
;
52 @NonNls private static final String INTERNAL_DIR
= "internal";
53 @NonNls private static final String INCLUDES_DIR
= "includes";
54 @NonNls private static final String CODETEMPLATES_DIR
= "code";
55 @NonNls private static final String J2EE_TEMPLATES_DIR
= "j2ee";
57 private final String myName
;
58 @NonNls private final String myDefaultTemplatesDir
;
59 @NonNls private final String myTemplatesDir
;
60 private MyTemplates myTemplates
;
61 private final RecentTemplatesManager myRecentList
= new RecentTemplatesManager();
62 private volatile boolean myLoaded
= false;
63 private final FileTemplateManagerImpl myInternalTemplatesManager
;
64 private final FileTemplateManagerImpl myPatternsManager
;
65 private final FileTemplateManagerImpl myCodeTemplatesManager
;
66 private final FileTemplateManagerImpl myJ2eeTemplatesManager
;
67 private final MyDeletedTemplatesManager myDeletedTemplatesManager
= new MyDeletedTemplatesManager();
68 private VirtualFile myDefaultDescription
;
70 private static VirtualFile
[] ourTopDirs
;
71 private final FileTypeManagerEx myTypeManager
;
72 @NonNls private static final String ELEMENT_DELETED_TEMPLATES
= "deleted_templates";
73 @NonNls private static final String ELEMENT_DELETED_INCLUDES
= "deleted_includes";
74 @NonNls private static final String ELEMENT_RECENT_TEMPLATES
= "recent_templates";
75 @NonNls private static final String ELEMENT_TEMPLATES
= "templates";
76 @NonNls private static final String ELEMENT_INTERNAL_TEMPLATE
= "internal_template";
77 @NonNls private static final String ELEMENT_TEMPLATE
= "template";
78 @NonNls private static final String ATTRIBUTE_NAME
= "name";
79 @NonNls private static final String ATTRIBUTE_REFORMAT
= "reformat";
81 private final Object LOCK
= new Object();
82 private static final Object TOP_DIRS_LOCK
= new Object();
84 private final FileTemplateManagerImpl
[] myChildren
;
85 public static FileTemplateManagerImpl
getInstance() {
86 return (FileTemplateManagerImpl
)ServiceManager
.getService(FileTemplateManager
.class);
89 public FileTemplateManagerImpl(@NotNull FileTypeManagerEx typeManager
, @NotNull MessageBus bus
) {
90 this("Default", ".", typeManager
,
91 new FileTemplateManagerImpl("Internal", INTERNAL_DIR
, typeManager
, null, null, null, null),
92 new FileTemplateManagerImpl("Includes", INCLUDES_DIR
, typeManager
, null, null, null, null),
93 new FileTemplateManagerImpl("Code", CODETEMPLATES_DIR
, typeManager
, null, null, null, null),
94 new FileTemplateManagerImpl("J2EE", J2EE_TEMPLATES_DIR
, typeManager
, null, null, null, null));
96 bus
.connect().subscribe(VirtualFileManager
.VFS_CHANGES
, new BulkFileListener() {
97 public void before(final List
<?
extends VFileEvent
> events
) {
100 public void after(final List
<?
extends VFileEvent
> events
) {
106 private FileTemplateManagerImpl(@NotNull @NonNls String name
,
107 @NotNull @NonNls String defaultTemplatesDirName
,
108 @NotNull FileTypeManagerEx fileTypeManagerEx
,
109 FileTemplateManagerImpl internalTemplatesManager
,
110 FileTemplateManagerImpl patternsManager
,
111 FileTemplateManagerImpl codeTemplatesManager
,
112 FileTemplateManagerImpl j2eeTemplatesManager
) {
114 myDefaultTemplatesDir
= defaultTemplatesDirName
;
115 myTemplatesDir
= TEMPLATES_DIR
+ (defaultTemplatesDirName
.equals(".") ?
"" : File
.separator
+ defaultTemplatesDirName
);
116 myTypeManager
= fileTypeManagerEx
;
117 myInternalTemplatesManager
= internalTemplatesManager
;
118 myPatternsManager
= patternsManager
;
119 myCodeTemplatesManager
= codeTemplatesManager
;
120 myJ2eeTemplatesManager
= j2eeTemplatesManager
;
121 myChildren
= internalTemplatesManager
== null ?
new FileTemplateManagerImpl
[0] : new FileTemplateManagerImpl
[]{internalTemplatesManager
,patternsManager
,codeTemplatesManager
,j2eeTemplatesManager
};
125 public File
[] getExportFiles() {
126 return new File
[]{getParentDirectory(false), PathManager
.getDefaultOptionsFile()};
130 public String
getPresentableName() {
131 return IdeBundle
.message("item.file.templates");
135 public FileTemplate
[] getAllTemplates() {
136 ensureTemplatesAreLoaded();
137 synchronized (LOCK
) {
138 return myTemplates
.getAllTemplates();
142 public FileTemplate
getTemplate(@NotNull @NonNls String templateName
) {
143 ensureTemplatesAreLoaded();
144 synchronized (LOCK
) {
145 return myTemplates
.findByName(templateName
);
150 public FileTemplate
addTemplate(@NotNull @NonNls String name
, @NotNull @NonNls String extension
) {
152 ensureTemplatesAreLoaded();
153 synchronized (LOCK
) {
155 LOG
.assertTrue(name
.length() > 0);
156 if (myTemplates
.findByName(name
) != null) {
157 LOG
.error("Duplicate template " + name
);
160 FileTemplate fileTemplate
= new FileTemplateImpl("", name
, extension
);
161 myTemplates
.addTemplate(fileTemplate
);
166 public void removeTemplate(@NotNull FileTemplate template
, boolean fromDiskOnly
) {
167 ensureTemplatesAreLoaded();
168 synchronized (LOCK
) {
169 myTemplates
.removeTemplate(template
);
171 ((FileTemplateImpl
)template
).removeFromDisk();
173 catch (Exception e
) {
174 LOG
.error("Unable to remove template", e
);
178 myDeletedTemplatesManager
.addName(template
.getName() + "." + template
.getExtension() + "." + DEFAULT_TEMPLATE_EXTENSION
);
185 public void removeInternal(@NotNull FileTemplate template
) {
186 LOG
.assertTrue(myInternalTemplatesManager
!= null);
187 myInternalTemplatesManager
.removeTemplate(template
, true);
189 public FileTemplate
addInternal(@NotNull @NonNls String name
, @NotNull @NonNls String extension
) {
190 return myInternalTemplatesManager
.addTemplate(name
, extension
);
194 public Properties
getDefaultProperties() {
195 @NonNls Properties props
= new Properties();
197 Date date
= new Date();
198 props
.setProperty("DATE", DateFormat
.getDateInstance().format(date
));
199 props
.setProperty("TIME", DateFormat
.getTimeInstance().format(date
));
200 Calendar calendar
= Calendar
.getInstance();
201 props
.setProperty("YEAR", Integer
.toString(calendar
.get(Calendar
.YEAR
)));
202 props
.setProperty("MONTH", getCalendarValue(calendar
, Calendar
.MONTH
));
203 props
.setProperty("DAY", getCalendarValue(calendar
, Calendar
.DAY_OF_MONTH
));
204 props
.setProperty("HOUR", getCalendarValue(calendar
, Calendar
.HOUR_OF_DAY
));
205 props
.setProperty("MINUTE", getCalendarValue(calendar
, Calendar
.MINUTE
));
207 props
.setProperty("USER", SystemProperties
.getUserName());
212 private static String
getCalendarValue(final Calendar calendar
, final int field
) {
213 int val
= calendar
.get(field
);
214 if (field
== Calendar
.MONTH
) val
++;
215 final String result
= Integer
.toString(val
);
216 if (result
.length() == 1) {
222 private File
getParentDirectory(boolean create
) {
223 File configPath
= new File(PathManager
.getConfigPath());
224 File templatesPath
= new File(configPath
, myTemplatesDir
);
225 if (!templatesPath
.exists()) {
227 final boolean created
= templatesPath
.mkdirs();
229 LOG
.error("Cannot create directory: " + templatesPath
.getAbsolutePath());
233 return templatesPath
;
236 private void ensureTemplatesAreLoaded() {
240 ApplicationManager
.getApplication().runReadAction(new Runnable() {
242 synchronized (LOCK
) {
253 private void loadTemplates() {
254 Collection
<VirtualFile
> defaultTemplates
= getDefaultTemplates();
255 for (VirtualFile file
: defaultTemplates
) {
256 //noinspection HardCodedStringLiteral
257 if (file
.getName().equals("default.html")) {
258 myDefaultDescription
= file
; //todo[myakovlev]
262 File templateDir
= getParentDirectory(false);
263 File
[] files
= templateDir
.listFiles();
268 if (myTemplates
== null) {
269 myTemplates
= new MyTemplates();
271 List
<FileTemplate
> existingTemplates
= new ArrayList
<FileTemplate
>();
272 // Read user-defined templates
273 for (File file
: files
) {
274 if (file
.isDirectory()) {
277 String name
= file
.getName();
278 String extension
= myTypeManager
.getExtension(name
);
279 name
= name
.substring(0, name
.length() - extension
.length() - 1);
280 if (file
.isHidden() || name
.length() == 0) {
283 FileTemplate existing
= myTemplates
.findByName(name
);
284 if (existing
== null || existing
.isDefault()) {
285 if (existing
!= null) {
286 myTemplates
.removeTemplate(existing
);
288 FileTemplateImpl fileTemplate
= new FileTemplateImpl(file
, name
, extension
, false);
289 //fileTemplate.setDescription(myDefaultDescription); default description will be shown
290 myTemplates
.addTemplate(fileTemplate
);
291 existingTemplates
.add(fileTemplate
);
294 // it is a user-defined template, revalidate it
295 LOG
.assertTrue(!((FileTemplateImpl
)existing
).isModified());
296 ((FileTemplateImpl
)existing
).invalidate();
297 existingTemplates
.add(existing
);
300 LOG
.debug("FileTemplateManagerImpl.loadTemplates() reading default templates...");
301 // Read default templates
302 for (VirtualFile file
: defaultTemplates
) {
303 String name
= file
.getName(); //name.extension.ft , e.g. "NewClass.java.ft"
304 @NonNls String extension
= myTypeManager
.getExtension(name
);
305 name
= name
.substring(0, name
.length() - extension
.length() - 1); //name="NewClass.java" extension="ft"
306 if (extension
.equals("html")) {
309 if (!extension
.equals(DEFAULT_TEMPLATE_EXTENSION
)) {
310 LOG
.error(file
.toString() + " should have *." + DEFAULT_TEMPLATE_EXTENSION
+ " extension!");
312 extension
= myTypeManager
.getExtension(name
);
313 name
= name
.substring(0, name
.length() - extension
.length() - 1); //name="NewClass" extension="java"
314 FileTemplate aTemplate
= myTemplates
.findByName(name
);
315 if (aTemplate
== null) {
316 FileTemplate fileTemplate
= new FileTemplateImpl(file
, name
, extension
);
317 myTemplates
.addTemplate(fileTemplate
);
318 aTemplate
= fileTemplate
;
320 VirtualFile description
= getDescriptionForTemplate(file
);
321 if (description
!= null) {
322 ((FileTemplateImpl
)aTemplate
).setDescription(description
);
325 ((FileTemplateImpl)aTemplate).setDescription(myDefaultDescription);
328 FileTemplate
[] allTemplates
= myTemplates
.getAllTemplates();
329 for (FileTemplate template
: allTemplates
) {
330 FileTemplateImpl templateImpl
= (FileTemplateImpl
)template
;
331 if (!templateImpl
.isDefault()) {
332 if (!existingTemplates
.contains(templateImpl
)) {
333 if (!templateImpl
.isNew()) {
334 myTemplates
.removeTemplate(templateImpl
);
335 templateImpl
.removeFromDisk();
343 private void saveTemplates() {
345 if (myTemplates
!= null) {
346 for (FileTemplate template
: myTemplates
.getAllTemplates()) {
347 FileTemplateImpl templateImpl
= (FileTemplateImpl
)template
;
348 if (templateImpl
.isModified()) {
349 templateImpl
.writeExternal(getParentDirectory(true));
353 for (FileTemplateManagerImpl child
: myChildren
) {
354 child
.saveTemplates();
357 catch (IOException e
) {
358 LOG
.error("Unable to save templates", e
);
363 public Collection
<String
> getRecentNames() {
364 ensureTemplatesAreLoaded();
365 synchronized (LOCK
) {
366 validateRecentNames();
367 return myRecentList
.getRecentNames(RECENT_TEMPLATES_SIZE
);
371 public void addRecentName(@NotNull @NonNls String name
) {
372 synchronized (LOCK
) {
373 myRecentList
.addName(name
);
377 public void readExternal(Element element
) throws InvalidDataException
{
378 Element deletedTemplatesElement
= element
.getChild(ELEMENT_DELETED_TEMPLATES
);
379 if (deletedTemplatesElement
!= null) {
380 myDeletedTemplatesManager
.readExternal(deletedTemplatesElement
);
383 Element deletedIncludesElement
= element
.getChild(ELEMENT_DELETED_INCLUDES
);
384 if (deletedIncludesElement
!= null) {
385 myPatternsManager
.myDeletedTemplatesManager
.readExternal(deletedIncludesElement
);
388 Element recentElement
= element
.getChild(ELEMENT_RECENT_TEMPLATES
);
389 if (recentElement
!= null) {
390 myRecentList
.readExternal(recentElement
);
393 Element templatesElement
= element
.getChild(ELEMENT_TEMPLATES
);
394 if (templatesElement
!= null) {
396 FileTemplate
[] internals
= getInternalTemplates();
397 List children
= templatesElement
.getChildren();
398 for (final Object aChildren
: children
) {
399 Element child
= (Element
)aChildren
;
400 String name
= child
.getAttributeValue(ATTRIBUTE_NAME
);
401 boolean reformat
= Boolean
.TRUE
.toString().equals(child
.getAttributeValue(ATTRIBUTE_REFORMAT
));
402 if (child
.getName().equals(ELEMENT_INTERNAL_TEMPLATE
)) {
403 for (FileTemplate internal
: internals
) {
404 if (name
.equals(internal
.getName())) internal
.setAdjust(reformat
);
407 else if (child
.getName().equals(ELEMENT_TEMPLATE
)) {
408 FileTemplate template
= getTemplate(name
);
409 if (template
!= null) {
410 template
.setAdjust(reformat
);
417 public void writeExternal(Element element
) throws WriteExternalException
{
419 validateRecentNames();
421 Element deletedTemplatesElement
= new Element(ELEMENT_DELETED_TEMPLATES
);
422 element
.addContent(deletedTemplatesElement
);
423 myDeletedTemplatesManager
.writeExternal(deletedTemplatesElement
);
425 Element deletedIncludesElement
= new Element(ELEMENT_DELETED_INCLUDES
);
426 element
.addContent(deletedIncludesElement
);
427 myPatternsManager
.myDeletedTemplatesManager
.writeExternal(deletedIncludesElement
);
429 Element recentElement
= new Element(ELEMENT_RECENT_TEMPLATES
);
430 element
.addContent(recentElement
);
431 myRecentList
.writeExternal(recentElement
);
433 Element templatesElement
= new Element(ELEMENT_TEMPLATES
);
434 element
.addContent(templatesElement
);
436 FileTemplate
[] internals
= getInternalTemplates();
437 for (FileTemplate internal
: internals
) {
438 templatesElement
.addContent(createElement(internal
, true));
441 FileTemplate
[] allTemplates
= getAllTemplates();
442 for (FileTemplate fileTemplate
: allTemplates
) {
443 templatesElement
.addContent(createElement(fileTemplate
, false));
447 private static Element
createElement(FileTemplate template
, boolean isInternal
) {
448 Element templateElement
= new Element(isInternal ? ELEMENT_INTERNAL_TEMPLATE
: ELEMENT_TEMPLATE
);
449 templateElement
.setAttribute(ATTRIBUTE_NAME
, template
.getName());
450 templateElement
.setAttribute(ATTRIBUTE_REFORMAT
, Boolean
.toString(template
.isAdjust()));
451 return templateElement
;
454 private void validateRecentNames() {
455 if (myTemplates
!= null) {
456 List
<String
> allNames
= new ArrayList
<String
>(myTemplates
.size());
457 FileTemplate
[] allTemplates
= myTemplates
.getAllTemplates();
458 for (FileTemplate fileTemplate
: allTemplates
) {
459 allNames
.add(fileTemplate
.getName());
461 myRecentList
.validateNames(allNames
);
465 private void invalidate() {
466 synchronized (LOCK
) {
469 if (myTemplates
!= null) {
470 FileTemplate
[] allTemplates
= myTemplates
.getAllTemplates();
471 for (FileTemplate template
: allTemplates
) {
472 ((FileTemplateImpl
)template
).invalidate();
478 public void saveAll() {
479 synchronized (LOCK
) {
485 public FileTemplate
[] getInternalTemplates() {
486 InternalTemplateBean
[] internalTemplateBeans
= Extensions
.getExtensions(InternalTemplateBean
.EP_NAME
);
487 FileTemplate
[] result
= new FileTemplate
[internalTemplateBeans
.length
];
488 for(int i
=0; i
<internalTemplateBeans
.length
; i
++) {
489 result
[i
] = getInternalTemplate(internalTemplateBeans
[i
].name
);
494 public FileTemplate
getInternalTemplate(@NotNull @NonNls String templateName
) {
495 synchronized (LOCK
) {
496 LOG
.assertTrue(myInternalTemplatesManager
!= null);
497 //noinspection HardCodedStringLiteral
499 FileTemplateImpl template
= (FileTemplateImpl
)myInternalTemplatesManager
.getTemplate(templateName
);
501 if (template
== null) {
502 if (ApplicationManager
.getApplication().isUnitTestMode()) {
503 String text
= getTestClassTemplateText(templateName
);
504 template
= new FileTemplateImpl(normalizeText(text
), templateName
+ "ForTest", "java");
505 template
.setInternal(true);
509 template
= (FileTemplateImpl
)getTemplate(templateName
);
512 if (template
== null) {
513 template
= (FileTemplateImpl
)getJ2eeTemplate(templateName
); // Hack to be able to register class templates from the plugin.
514 if (template
!= null) {
515 template
.setAdjust(true);
518 String text
= normalizeText(getDefaultClassTemplateText(templateName
));
520 template
= (FileTemplateImpl
)myInternalTemplatesManager
.addTemplate(templateName
, "java");
521 template
.setText(text
);
525 template
.setInternal(true);
530 private static String
normalizeText(String text
) {
531 text
= StringUtil
.convertLineSeparators(text
);
532 text
= StringUtil
.replace(text
, "$NAME$", "${NAME}");
533 text
= StringUtil
.replace(text
, "$PACKAGE_NAME$", "${PACKAGE_NAME}");
534 text
= StringUtil
.replace(text
, "$DATE$", "${DATE}");
535 text
= StringUtil
.replace(text
, "$TIME$", "${TIME}");
536 text
= StringUtil
.replace(text
, "$USER$", "${USER}");
541 private String
getTestClassTemplateText(@NotNull @NonNls String templateName
) {
542 return "package $PACKAGE_NAME$;\npublic " + internalTemplateToSubject(templateName
) + " $NAME$ { }";
546 public String
internalTemplateToSubject(@NotNull @NonNls String templateName
) {
547 //noinspection HardCodedStringLiteral
548 for(InternalTemplateBean bean
: Extensions
.getExtensions(InternalTemplateBean
.EP_NAME
)) {
549 if (bean
.name
.equals(templateName
) && bean
.subject
!= null) {
553 return templateName
.toLowerCase();
557 public String
localizeInternalTemplateName(@NotNull final FileTemplate template
) {
558 return template
.getName();
562 private String
getDefaultClassTemplateText(@NotNull @NonNls String templateName
) {
563 return IdeBundle
.message("template.default.class.comment", ApplicationNamesInfo
.getInstance().getFullProductName()) +
564 "package $PACKAGE_NAME$;\n" + "public " + internalTemplateToSubject(templateName
) + " $NAME$ { }";
567 public FileTemplate
getCodeTemplate(@NotNull @NonNls String templateName
) {
568 return getTemplateFromManager(templateName
, myCodeTemplatesManager
);
571 public FileTemplate
getJ2eeTemplate(@NotNull @NonNls String templateName
) {
572 return getTemplateFromManager(templateName
, myJ2eeTemplatesManager
);
575 private FileTemplate
getTemplateFromManager(@NotNull @NonNls String templateName
, @NotNull FileTemplateManagerImpl templatesManager
) {
576 String name
= templateName
;
577 String extension
= myTypeManager
.getExtension(name
);
578 if (extension
.length() > 0) {
579 name
= name
.substring(0, name
.length() - extension
.length() - 1);
581 FileTemplate template
= templatesManager
.getTemplate(name
);
582 if (template
!= null) {
583 if (extension
.equals(template
.getExtension())) {
588 if (ApplicationManager
.getApplication().isUnitTestMode() && templateName
.endsWith("ForTest")) return null;
590 String message
= templateNotFound(templateName
, templatesManager
);
596 private static String
templateNotFound(String templateName
, FileTemplateManagerImpl templatesManager
) {
597 Collection
<VirtualFile
> defaultTemplates
= templatesManager
.getDefaultTemplates();
598 @NonNls String message
=
599 "Unable to find template '" + templateName
+ "' in " + templatesManager
+ " in '"+templatesManager
.myDefaultTemplatesDir
+"'" +
601 "Default templates are: ";
602 message
+= StringUtil
.join(defaultTemplates
, new Function
<VirtualFile
, String
>() {
603 public String
fun(VirtualFile virtualFile
) {
604 return virtualFile
.getPresentableUrl();
611 @SuppressWarnings({"HardCodedStringLiteral"})
612 private VirtualFile
getDescriptionForTemplate(VirtualFile vfile
) {
614 VirtualFile parent
= vfile
.getParent();
615 assert parent
!= null;
616 String name
= vfile
.getName(); //name.extension.ft , f.e. "NewClass.java.ft"
617 String extension
= myTypeManager
.getExtension(name
);
618 if (extension
.equals(DEFAULT_TEMPLATE_EXTENSION
)) {
619 name
= name
.substring(0, name
.length() - extension
.length() - 1); //name="NewClass.java" extension="ft"
621 Locale locale
= Locale
.getDefault();
622 String descName
= MessageFormat
.format("{0}_{1}_{2}.html", name
, locale
.getLanguage(), locale
.getCountry());
623 VirtualFile descFile
= parent
.findChild(descName
);
624 if (descFile
!= null && descFile
.isValid()) {
628 descName
= MessageFormat
.format("{0}_{1}.html", name
, locale
.getLanguage());
629 descFile
= parent
.findChild(descName
);
630 if (descFile
!= null && descFile
.isValid()) {
634 descFile
= parent
.findChild(name
+ ".html");
635 if (descFile
!= null && descFile
.isValid()) {
643 private static List
<VirtualFile
> listDir(VirtualFile vfile
) {
644 List
<VirtualFile
> result
= new ArrayList
<VirtualFile
>();
645 if (vfile
!= null && vfile
.isDirectory()) {
646 VirtualFile
[] children
= vfile
.getChildren();
647 for (VirtualFile child
: children
) {
648 if (!child
.isDirectory()) {
656 private void removeDeletedTemplates(Set
<VirtualFile
> files
) {
657 Iterator
<VirtualFile
> iterator
= files
.iterator();
658 while (iterator
.hasNext()) {
659 VirtualFile file
= iterator
.next();
660 String nameWithExtension
= file
.getName();
661 if (myDeletedTemplatesManager
.contains(nameWithExtension
)) {
667 private static VirtualFile
getDefaultFromManager(@NotNull @NonNls String name
,
668 @NotNull @NonNls String extension
,
669 @NotNull FileTemplateManagerImpl manager
) {
670 Collection
<VirtualFile
> files
= manager
.getDefaultTemplates();
671 for (VirtualFile file
: files
) {
672 if (DEFAULT_TEMPLATE_EXTENSION
.equals(file
.getExtension())) {
673 String fullName
= file
.getNameWithoutExtension(); //Strip .ft
674 if (fullName
.equals(name
+ "." + extension
)) return file
;
680 public VirtualFile
getDefaultTemplate(@NotNull @NonNls String name
, @NotNull @NonNls String extension
) {
682 if ((result
= getDefaultFromManager(name
, extension
, this)) != null) return result
;
683 for (FileTemplateManagerImpl child
: myChildren
) {
684 if ((result
= getDefaultFromManager(name
, extension
, child
)) != null) return result
;
690 public FileTemplate
getDefaultTemplate(@NotNull @NonNls String name
) {
691 @NonNls String extension
= myTypeManager
.getExtension(name
);
692 String nameWithoutExtension
= StringUtil
.trimEnd(name
, "." + extension
);
693 if (extension
.length() == 0) {
696 VirtualFile file
= getDefaultTemplate(nameWithoutExtension
, extension
);
699 for (FileTemplateManagerImpl child
: ArrayUtil
.append(myChildren
,this)) {
700 message
+= templateNotFound(name
, child
) + "\n";
705 return new FileTemplateImpl(file
, nameWithoutExtension
, extension
);
709 private Collection
<VirtualFile
> getDefaultTemplates() {
710 LOG
.assertTrue(!StringUtil
.isEmpty(myDefaultTemplatesDir
), myDefaultTemplatesDir
);
711 VirtualFile
[] topDirs
= getTopTemplatesDir();
712 if (LOG
.isDebugEnabled()) {
713 @NonNls String message
= "Top dirs found: ";
714 for (int i
= 0; i
< topDirs
.length
; i
++) {
715 VirtualFile topDir
= topDirs
[i
];
716 message
+= (i
> 0 ?
", " : "") + topDir
.getPresentableUrl();
720 Set
<VirtualFile
> templatesList
= new THashSet
<VirtualFile
>();
721 for (VirtualFile topDir
: topDirs
) {
722 VirtualFile parentDir
= myDefaultTemplatesDir
.equals(".") ? topDir
: topDir
.findChild(myDefaultTemplatesDir
);
723 if (parentDir
!= null) {
724 templatesList
.addAll(listDir(parentDir
));
727 removeDeletedTemplates(templatesList
);
729 return templatesList
;
732 private static void refreshTopDirs() {
733 synchronized (TOP_DIRS_LOCK
) {
734 if (ourTopDirs
!= null) {
735 for (VirtualFile dir
: ourTopDirs
) {
746 private static VirtualFile
[] getTopTemplatesDir() {
747 synchronized (TOP_DIRS_LOCK
) {
748 if (ourTopDirs
!= null) {
752 Set
<VirtualFile
> dirList
= new THashSet
<VirtualFile
>();
754 appendDefaultTemplatesDirFromClassloader(FileTemplateManagerImpl
.class.getClassLoader(), dirList
);
755 PluginDescriptor
[] plugins
= ApplicationManager
.getApplication().getPlugins();
756 for (PluginDescriptor plugin
: plugins
) {
757 appendDefaultTemplatesDirFromClassloader(plugin
.getPluginClassLoader(), dirList
);
760 ourTopDirs
= dirList
.toArray(new VirtualFile
[dirList
.size()]);
761 for (VirtualFile topDir
: ourTopDirs
) {
762 topDir
.refresh(true,true);
768 private static void appendDefaultTemplatesDirFromClassloader(ClassLoader classLoader
, Set
<VirtualFile
> dirList
) {
770 Enumeration systemResources
= classLoader
.getResources(DEFAULT_TEMPLATES_TOP_DIR
);
771 if (systemResources
!= null && systemResources
.hasMoreElements()) {
772 Set
<URL
> urls
= new HashSet
<URL
>();
773 while (systemResources
.hasMoreElements()) {
774 URL nextURL
= (URL
)systemResources
.nextElement();
775 if (!urls
.contains(nextURL
)) {
777 VirtualFile dir
= VfsUtil
.findFileByURL(nextURL
);
779 LOG
.error("Cannot find file by URL: " + nextURL
);
782 if (LOG
.isDebugEnabled()) {
783 LOG
.debug("Top directory: " + dir
.getPresentableUrl());
791 catch (IOException e
) {
797 public FileTemplate
[] getAllPatterns() {
798 return myPatternsManager
.getAllTemplates();
801 public FileTemplate
getPattern(@NotNull @NonNls String name
) {
802 return myPatternsManager
.getTemplate(name
);
805 public FileTemplate
addPattern(@NotNull @NonNls String name
, @NotNull @NonNls String extension
) {
806 LOG
.assertTrue(myPatternsManager
!= null);
807 return myPatternsManager
.addTemplate(name
, extension
);
810 public void removePattern(@NotNull FileTemplate template
, boolean fromDiskOnly
) {
811 LOG
.assertTrue(myPatternsManager
!= null);
812 myPatternsManager
.removeTemplate(template
, fromDiskOnly
);
816 public FileTemplate
[] getAllCodeTemplates() {
817 LOG
.assertTrue(myCodeTemplatesManager
!= null);
818 return myCodeTemplatesManager
.getAllTemplates();
822 public FileTemplate
[] getAllJ2eeTemplates() {
823 LOG
.assertTrue(myJ2eeTemplatesManager
!= null);
824 return myJ2eeTemplatesManager
.getAllTemplates();
828 public FileTemplate
addCodeTemplate(@NotNull @NonNls String name
, @NotNull @NonNls String extension
) {
829 LOG
.assertTrue(myCodeTemplatesManager
!= null);
830 return myCodeTemplatesManager
.addTemplate(name
, extension
);
834 public FileTemplate
addJ2eeTemplate(@NotNull @NonNls String name
, @NotNull @NonNls String extension
) {
835 LOG
.assertTrue(myJ2eeTemplatesManager
!= null);
836 return myJ2eeTemplatesManager
.addTemplate(name
, extension
);
839 public void removeCodeTemplate(@NotNull FileTemplate template
, boolean fromDiskOnly
) {
840 LOG
.assertTrue(myCodeTemplatesManager
!= null);
841 myCodeTemplatesManager
.removeTemplate(template
, fromDiskOnly
);
844 public void removeJ2eeTemplate(@NotNull FileTemplate template
, boolean fromDiskOnly
) {
845 LOG
.assertTrue(myJ2eeTemplatesManager
!= null);
846 myJ2eeTemplatesManager
.removeTemplate(template
, fromDiskOnly
);
849 public VirtualFile
getDefaultTemplateDescription() {
850 return myDefaultDescription
;
853 public VirtualFile
getDefaultIncludeDescription() {
854 return myPatternsManager
.myDefaultDescription
;
857 private static class MyTemplates
{
858 private final List
<FileTemplate
> myTemplatesList
= new ArrayList
<FileTemplate
>();
861 return myTemplatesList
.size();
864 public void removeTemplate(FileTemplate template
) {
865 myTemplatesList
.remove(template
);
869 public FileTemplate
[] getAllTemplates() {
870 return myTemplatesList
.toArray(new FileTemplate
[myTemplatesList
.size()]);
873 public FileTemplate
findByName(@NotNull @NonNls String name
) {
874 for (FileTemplate template
: myTemplatesList
) {
875 if (template
.getName().equals(name
)) {
882 public void addTemplate(@NotNull FileTemplate newTemplate
) {
883 String newName
= newTemplate
.getName();
885 for (FileTemplate template
: myTemplatesList
) {
886 if (template
== newTemplate
) {
889 if (template
.getName().compareToIgnoreCase(newName
) > 0) {
890 myTemplatesList
.add(myTemplatesList
.indexOf(template
), newTemplate
);
894 myTemplatesList
.add(newTemplate
);
898 private static class MyDeletedTemplatesManager
implements JDOMExternalizable
{
899 public JDOMExternalizableStringList DELETED_DEFAULT_TEMPLATES
= new JDOMExternalizableStringList();
901 public void addName(@NotNull @NonNls String nameWithExtension
) {
902 DELETED_DEFAULT_TEMPLATES
.remove(nameWithExtension
);
903 DELETED_DEFAULT_TEMPLATES
.add(nameWithExtension
);
906 public boolean contains(@NotNull @NonNls String nameWithExtension
) {
907 return DELETED_DEFAULT_TEMPLATES
.contains(nameWithExtension
);
910 public void readExternal(Element element
) throws InvalidDataException
{
911 DefaultJDOMExternalizer
.readExternal(this, element
);
914 public void writeExternal(Element element
) throws WriteExternalException
{
915 DefaultJDOMExternalizer
.writeExternal(this, element
);
919 private static class RecentTemplatesManager
implements JDOMExternalizable
{
920 public JDOMExternalizableStringList RECENT_TEMPLATES
= new JDOMExternalizableStringList();
922 public void addName(@NotNull @NonNls String name
) {
923 RECENT_TEMPLATES
.remove(name
);
924 RECENT_TEMPLATES
.add(name
);
928 public Collection
<String
> getRecentNames(int max
) {
929 int size
= RECENT_TEMPLATES
.size();
930 int resultSize
= Math
.min(max
, size
);
931 return RECENT_TEMPLATES
.subList(size
- resultSize
, size
);
934 public void validateNames(List
<String
> validNames
) {
935 RECENT_TEMPLATES
.retainAll(validNames
);
938 public void readExternal(Element element
) throws InvalidDataException
{
939 DefaultJDOMExternalizer
.readExternal(this, element
);
942 public void writeExternal(Element element
) throws WriteExternalException
{
943 DefaultJDOMExternalizer
.writeExternal(this, element
);
950 public String
toString() {
951 return myName
+ " file template manager";