blob: 910dddce6391e7e6839ea595c77018c46aa60335 [file] [log] [blame]
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.ide.fileTemplates.impl;
import com.intellij.CommonBundle;
import com.intellij.ide.IdeBundle;
import com.intellij.ide.fileTemplates.FileTemplate;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.ex.ProjectManagerEx;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.CharsetToolkit;
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.*;
import java.util.*;
/**
* @author Eugene Zhuravlev
* Date: 3/22/11
*/
class FTManager {
private static final Logger LOG = Logger.getInstance("#com.intellij.ide.fileTemplates.impl.FTManager");
public static final String TEMPLATES_DIR = "fileTemplates";
public static final String DEFAULT_TEMPLATE_EXTENSION = "ft";
public static final String TEMPLATE_EXTENSION_SUFFIX = "." + DEFAULT_TEMPLATE_EXTENSION;
public static final String CONTENT_ENCODING = CharsetToolkit.UTF8;
private static final String ENCODED_NAME_EXT_DELIMITER = "\u0F0Fext\u0F0F.";
private final String myName;
private final boolean myInternal;
private final String myTemplatesDir;
private final Map<String, FileTemplateBase> myTemplates = new HashMap<String, FileTemplateBase>();
private volatile List<FileTemplateBase> mySortedTemplates;
private final List<DefaultTemplate> myDefaultTemplates = new ArrayList<DefaultTemplate>();
FTManager(@NotNull @NonNls String name, @NotNull @NonNls String defaultTemplatesDirName) {
this(name, defaultTemplatesDirName, false);
}
FTManager(@NotNull @NonNls String name, @NotNull @NonNls String defaultTemplatesDirName, boolean internal) {
myName = name;
myInternal = internal;
myTemplatesDir = TEMPLATES_DIR + (defaultTemplatesDirName.equals(".") ? "" : File.separator + defaultTemplatesDirName);
}
public String getName() {
return myName;
}
@NotNull
public Collection<FileTemplateBase> getAllTemplates(boolean includeDisabled) {
List<FileTemplateBase> sorted = mySortedTemplates;
if (sorted == null) {
sorted = new ArrayList<FileTemplateBase>(myTemplates.values());
Collections.sort(sorted, new Comparator<FileTemplateBase>() {
@Override
public int compare(FileTemplateBase t1, FileTemplateBase t2) {
return t1.getName().compareToIgnoreCase(t2.getName());
}
});
mySortedTemplates = sorted;
}
if (includeDisabled) {
return Collections.unmodifiableCollection(sorted);
}
final List<FileTemplateBase> list = new ArrayList<FileTemplateBase>(sorted.size());
for (FileTemplateBase template : sorted) {
if (template instanceof BundledFileTemplate && !((BundledFileTemplate)template).isEnabled()) {
continue;
}
list.add(template);
}
return list;
}
/**
* @param templateQname
* @return template no matter enabled or disabled it is
*/
@Nullable
public FileTemplateBase getTemplate(@NotNull String templateQname) {
return myTemplates.get(templateQname);
}
/**
* Disabled templates are never returned
* @param templateName
* @return
*/
@Nullable
public FileTemplateBase findTemplateByName(@NotNull String templateName) {
final FileTemplateBase template = myTemplates.get(templateName);
if (template != null) {
final boolean isEnabled = !(template instanceof BundledFileTemplate) || ((BundledFileTemplate)template).isEnabled();
if (isEnabled) {
return template;
}
}
// templateName must be non-qualified name, since previous lookup found nothing
for (FileTemplateBase t : getAllTemplates(false)) {
final String qName = t.getQualifiedName();
if (qName.startsWith(templateName) && qName.length() > templateName.length()) {
String remainder = qName.substring(templateName.length());
if (remainder.startsWith(ENCODED_NAME_EXT_DELIMITER) || remainder.charAt(0) == '.') {
return t;
}
}
}
return null;
}
@NotNull
public FileTemplateBase addTemplate(String name, String extension) {
final String qName = FileTemplateBase.getQualifiedName(name, extension);
FileTemplateBase template = getTemplate(qName);
if (template == null) {
template = new CustomFileTemplate(name, extension);
myTemplates.put(qName, template);
mySortedTemplates = null;
}
else {
if (template instanceof BundledFileTemplate && !((BundledFileTemplate)template).isEnabled()) {
((BundledFileTemplate)template).setEnabled(true);
}
}
return template;
}
public void removeTemplate(@NotNull String qName) {
final FileTemplateBase template = myTemplates.get(qName);
if (template instanceof CustomFileTemplate) {
myTemplates.remove(qName);
mySortedTemplates = null;
}
else if (template instanceof BundledFileTemplate){
((BundledFileTemplate)template).setEnabled(false);
}
}
public void updateTemplates(@NotNull Collection<FileTemplate> newTemplates) {
final Set<String> toDisable = new HashSet<String>();
for (DefaultTemplate template : myDefaultTemplates) {
toDisable.add(template.getQualifiedName());
}
for (FileTemplate template : newTemplates) {
toDisable.remove(((FileTemplateBase)template).getQualifiedName());
}
myTemplates.clear();
mySortedTemplates = null;
for (DefaultTemplate template : myDefaultTemplates) {
final BundledFileTemplate bundled = createAndStoreBundledTemplate(template);
if (toDisable.contains(bundled.getQualifiedName())) {
bundled.setEnabled(false);
}
}
for (FileTemplate template : newTemplates) {
final FileTemplateBase _template = addTemplate(template.getName(), template.getExtension());
_template.setText(template.getText());
_template.setReformatCode(template.isReformatCode());
}
}
public void addDefaultTemplate(DefaultTemplate template) {
myDefaultTemplates.add(template);
createAndStoreBundledTemplate(template);
}
private BundledFileTemplate createAndStoreBundledTemplate(DefaultTemplate template) {
final BundledFileTemplate bundled = new BundledFileTemplate(template, myInternal);
final String qName = bundled.getQualifiedName();
final FileTemplateBase previous = myTemplates.put(qName, bundled);
mySortedTemplates = null;
LOG.assertTrue(previous == null, "Duplicate bundled template " + qName +
" [" + template.getTemplateURL() + ", " + previous + ']');
return bundled;
}
public void saveTemplates() {
final File configRoot = getConfigRoot(true);
final File[] files = configRoot.listFiles();
final Set<String> allNames = new HashSet<String>();
final Map<String, File> templatesOnDisk = files != null && files.length > 0? new HashMap<String, File>() : Collections.<String, File>emptyMap();
if (files != null) {
for (File file : files) {
if (!file.isDirectory()) {
final String name = file.getName();
templatesOnDisk.put(name, file);
allNames.add(name);
}
}
}
final Map<String, FileTemplateBase> templatesToSave = new HashMap<String, FileTemplateBase>();
for (FileTemplateBase template : getAllTemplates(true)) {
if (template instanceof BundledFileTemplate && !((BundledFileTemplate)template).isTextModified()) {
continue;
}
final String name = template.getQualifiedName();
templatesToSave.put(name, template);
allNames.add(name);
}
if (!allNames.isEmpty()) {
final String lineSeparator = CodeStyleSettingsManager.getSettings(ProjectManagerEx.getInstanceEx().getDefaultProject()).getLineSeparator();
for (String name : allNames) {
final File customizedTemplateFile = templatesOnDisk.get(name);
final FileTemplateBase templateToSave = templatesToSave.get(name);
if (customizedTemplateFile == null) {
// template was not saved before
try {
saveTemplate(configRoot, templateToSave, lineSeparator);
}
catch (IOException e) {
LOG.error("Unable to save template " + name, e);
}
}
else if (templateToSave == null) {
// template was removed
FileUtil.delete(customizedTemplateFile);
}
else {
// both customized content on disk and corresponding template are present
try {
final String diskText = StringUtil.convertLineSeparators(FileUtil.loadFile(customizedTemplateFile, CONTENT_ENCODING));
final String templateText = templateToSave.getText();
if (!diskText.equals(templateText)) {
// save only if texts differ to avoid unnecessary file touching
saveTemplate(configRoot, templateToSave, lineSeparator);
}
}
catch (IOException e) {
LOG.error("Unable to save template " + name, e);
}
}
}
}
}
/** Save template to file. If template is new, it is saved to specified directory. Otherwise it is saved to file from which it was read.
* If template was not modified, it is not saved.
* todo: review saving algorithm
*/
private static void saveTemplate(File parentDir, FileTemplateBase template, final String lineSeparator) throws IOException {
final File templateFile = new File(parentDir, encodeFileName(template.getName(), template.getExtension()));
FileOutputStream fileOutputStream;
try {
fileOutputStream = new FileOutputStream(templateFile);
}
catch (FileNotFoundException e) {
// try to recover from the situation 'file exists, but is a directory'
FileUtil.delete(templateFile);
fileOutputStream = new FileOutputStream(templateFile);
}
OutputStreamWriter outputStreamWriter;
try{
outputStreamWriter = new OutputStreamWriter(fileOutputStream, CONTENT_ENCODING);
}
catch (UnsupportedEncodingException e){
Messages.showMessageDialog(IdeBundle.message("error.unable.to.save.file.template.using.encoding", template.getName(),
CONTENT_ENCODING),
CommonBundle.getErrorTitle(), Messages.getErrorIcon());
outputStreamWriter = new OutputStreamWriter(fileOutputStream);
}
String content = template.getText();
if (!lineSeparator.equals("\n")){
content = StringUtil.convertLineSeparators(content, lineSeparator);
}
outputStreamWriter.write(content);
outputStreamWriter.close();
fileOutputStream.close();
}
public File getConfigRoot(boolean create) {
final File templatesPath = new File(PathManager.getConfigPath(), myTemplatesDir);
if (create) {
final boolean created = templatesPath.mkdirs();
if (!created && !templatesPath.exists()) {
LOG.info("Cannot create directory: " + templatesPath.getAbsolutePath());
}
}
return templatesPath;
}
@Override
public String toString() {
return myName + " file template manager";
}
public static String encodeFileName(String templateName, String extension) {
String nameExtDelimiter = extension.contains(".") ? ENCODED_NAME_EXT_DELIMITER : ".";
return templateName + nameExtDelimiter + extension;
}
public static Pair<String,String> decodeFileName(String fileName) {
String name = fileName;
String ext = "";
String nameExtDelimiter = fileName.contains(ENCODED_NAME_EXT_DELIMITER) ? ENCODED_NAME_EXT_DELIMITER : ".";
int extIndex = fileName.lastIndexOf(nameExtDelimiter);
if (extIndex >= 0) {
name = fileName.substring(0, extIndex);
ext = fileName.substring(extIndex + nameExtDelimiter.length());
}
return Pair.create(name, ext);
}
}