test fix
[fedora-idea.git] / platform / lang-impl / src / com / intellij / ide / fileTemplates / impl / FileTemplateManagerImpl.java
blobf5e2478efdf55424503b616504a881b31a6755c6
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;
32 import java.io.File;
33 import java.io.IOException;
34 import java.net.URL;
35 import java.text.DateFormat;
36 import java.text.MessageFormat;
37 import java.util.*;
39 /**
40 * @author MYakovlev
41 * Date: Jul 24
42 * @author 2002
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) {
101 refreshTopDirs();
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) {
113 myName = name;
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};
124 @NotNull
125 public File[] getExportFiles() {
126 return new File[]{getParentDirectory(false), PathManager.getDefaultOptionsFile()};
129 @NotNull
130 public String getPresentableName() {
131 return IdeBundle.message("item.file.templates");
134 @NotNull
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);
149 @NotNull
150 public FileTemplate addTemplate(@NotNull @NonNls String name, @NotNull @NonNls String extension) {
151 invalidate();
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);
162 return fileTemplate;
166 public void removeTemplate(@NotNull FileTemplate template, boolean fromDiskOnly) {
167 ensureTemplatesAreLoaded();
168 synchronized (LOCK) {
169 myTemplates.removeTemplate(template);
170 try {
171 ((FileTemplateImpl)template).removeFromDisk();
173 catch (Exception e) {
174 LOG.error("Unable to remove template", e);
177 if (!fromDiskOnly) {
178 myDeletedTemplatesManager.addName(template.getName() + "." + template.getExtension() + "." + DEFAULT_TEMPLATE_EXTENSION);
181 invalidate();
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);
193 @NotNull
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());
209 return props;
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) {
217 return "0" + result;
219 return result;
222 private File getParentDirectory(boolean create) {
223 File configPath = new File(PathManager.getConfigPath());
224 File templatesPath = new File(configPath, myTemplatesDir);
225 if (!templatesPath.exists()) {
226 if (create) {
227 final boolean created = templatesPath.mkdirs();
228 if (!created) {
229 LOG.error("Cannot create directory: " + templatesPath.getAbsolutePath());
233 return templatesPath;
236 private void ensureTemplatesAreLoaded() {
237 if (myLoaded) {
238 return;
240 ApplicationManager.getApplication().runReadAction(new Runnable() {
241 public void run() {
242 synchronized (LOCK) {
243 if (myLoaded) {
244 return;
246 loadTemplates();
248 myLoaded = true;
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();
264 if (files == null) {
265 files = new File[0];
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()) {
275 continue;
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) {
281 continue;
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);
293 else {
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")) {
307 continue;
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);
324 /*else{
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() {
344 try {
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);
362 @NotNull
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) {
395 invalidate();
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 {
418 saveTemplates();
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);
435 invalidate();
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) {
467 saveAll();
468 myLoaded = false;
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) {
480 saveTemplates();
484 @NotNull
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);
491 return result;
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);
506 return template;
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);
517 else {
518 String text = normalizeText(getDefaultClassTemplateText(templateName));
520 template = (FileTemplateImpl)myInternalTemplatesManager.addTemplate(templateName, "java");
521 template.setText(text);
525 template.setInternal(true);
526 return template;
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}");
537 return text;
540 @NonNls
541 private String getTestClassTemplateText(@NotNull @NonNls String templateName) {
542 return "package $PACKAGE_NAME$;\npublic " + internalTemplateToSubject(templateName) + " $NAME$ { }";
545 @NotNull
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) {
550 return bean.subject;
553 return templateName.toLowerCase();
556 @NotNull
557 public String localizeInternalTemplateName(@NotNull final FileTemplate template) {
558 return template.getName();
561 @NonNls
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())) {
584 return template;
587 else {
588 if (ApplicationManager.getApplication().isUnitTestMode() && templateName.endsWith("ForTest")) return null;
590 String message = templateNotFound(templateName, templatesManager);
591 LOG.error(message);
593 return null;
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+"'" +
600 "\n" +
601 "Default templates are: ";
602 message += StringUtil.join(defaultTemplates, new Function<VirtualFile, String>() {
603 public String fun(VirtualFile virtualFile) {
604 return virtualFile.getPresentableUrl();
606 }, ",");
607 return message;
611 @SuppressWarnings({"HardCodedStringLiteral"})
612 private VirtualFile getDescriptionForTemplate(VirtualFile vfile) {
613 if (vfile != null) {
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()) {
625 return descFile;
628 descName = MessageFormat.format("{0}_{1}.html", name, locale.getLanguage());
629 descFile = parent.findChild(descName);
630 if (descFile != null && descFile.isValid()) {
631 return descFile;
634 descFile = parent.findChild(name + ".html");
635 if (descFile != null && descFile.isValid()) {
636 return descFile;
640 return null;
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()) {
649 result.add(child);
653 return result;
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)) {
662 iterator.remove();
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;
677 return null;
680 public VirtualFile getDefaultTemplate(@NotNull @NonNls String name, @NotNull @NonNls String extension) {
681 VirtualFile result;
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;
686 return null;
689 @NotNull
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) {
694 extension = "java";
696 VirtualFile file = getDefaultTemplate(nameWithoutExtension, extension);
697 if (file == null) {
698 String message = "";
699 for (FileTemplateManagerImpl child : ArrayUtil.append(myChildren,this)) {
700 message += templateNotFound(name, child) + "\n";
702 LOG.error(message);
703 return null;
705 return new FileTemplateImpl(file, nameWithoutExtension, extension);
708 @NotNull
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();
718 LOG.debug(message);
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) {
736 if (!dir.exists()) {
737 ourTopDirs = null;
738 break;
745 @NotNull
746 private static VirtualFile[] getTopTemplatesDir() {
747 synchronized (TOP_DIRS_LOCK) {
748 if (ourTopDirs != null) {
749 return ourTopDirs;
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);
764 return ourTopDirs;
768 private static void appendDefaultTemplatesDirFromClassloader(ClassLoader classLoader, Set<VirtualFile> dirList) {
769 try {
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)) {
776 urls.add(nextURL);
777 VirtualFile dir = VfsUtil.findFileByURL(nextURL);
778 if (dir == null) {
779 LOG.error("Cannot find file by URL: " + nextURL);
781 else {
782 if (LOG.isDebugEnabled()) {
783 LOG.debug("Top directory: " + dir.getPresentableUrl());
785 dirList.add(dir);
791 catch (IOException e) {
792 LOG.error(e);
796 @NotNull
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);
815 @NotNull
816 public FileTemplate[] getAllCodeTemplates() {
817 LOG.assertTrue(myCodeTemplatesManager != null);
818 return myCodeTemplatesManager.getAllTemplates();
821 @NotNull
822 public FileTemplate[] getAllJ2eeTemplates() {
823 LOG.assertTrue(myJ2eeTemplatesManager != null);
824 return myJ2eeTemplatesManager.getAllTemplates();
827 @NotNull
828 public FileTemplate addCodeTemplate(@NotNull @NonNls String name, @NotNull @NonNls String extension) {
829 LOG.assertTrue(myCodeTemplatesManager != null);
830 return myCodeTemplatesManager.addTemplate(name, extension);
833 @NotNull
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>();
860 public int size() {
861 return myTemplatesList.size();
864 public void removeTemplate(FileTemplate template) {
865 myTemplatesList.remove(template);
868 @NotNull
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)) {
876 return template;
879 return null;
882 public void addTemplate(@NotNull FileTemplate newTemplate) {
883 String newName = newTemplate.getName();
885 for (FileTemplate template : myTemplatesList) {
886 if (template == newTemplate) {
887 return;
889 if (template.getName().compareToIgnoreCase(newName) > 0) {
890 myTemplatesList.add(myTemplatesList.indexOf(template), newTemplate);
891 return;
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);
927 @NotNull
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);
948 @NonNls
949 @Override
950 public String toString() {
951 return myName + " file template manager";