file templates: project-aware velocity context

This commit is contained in:
Dmitry Avdeev
2014-12-17 17:59:44 +03:00
parent 85d59c84e1
commit 0ec276863c
7 changed files with 60 additions and 29 deletions
@@ -36,7 +36,7 @@ public class FileTemplatesTest extends IdeaTestCase {
final File customInclude = new File(testsDir, customIncludeFileName);
final String includeText = FileUtil.loadFile(customInclude, FileTemplate.ourEncoding);
final FileTemplateManager templateManager = FileTemplateManager.getDefaultInstance();
final FileTemplateManager templateManager = FileTemplateManager.getInstance(getProject());
final ArrayList<FileTemplate> originalIncludes = new ArrayList<FileTemplate>(Arrays.asList(templateManager.getAllPatterns()));
try {
// configure custom include
@@ -67,9 +67,10 @@ public class FileTemplatesTest extends IdeaTestCase {
EncodingAwareProperties properties = new EncodingAwareProperties();
properties.load(propFile, FileTemplate.ourEncoding);
properties.put(FileTemplateManager.PROJECT_NAME_VARIABLE, getProject().getName())
System.out.println(resultFile.getName());
doTestTemplate(inputText, properties, outputText, resultFile.getParent());
doTestTemplate(inputText, properties, outputText);
}
}
finally {
@@ -77,19 +78,19 @@ public class FileTemplatesTest extends IdeaTestCase {
}
}
private static void doTestTemplate(String inputString, Properties properties, String expected, String dir) throws Exception {
private void doTestTemplate(String inputString, Properties properties, String expected) throws Exception {
inputString = StringUtil.convertLineSeparators(inputString);
expected = StringUtil.convertLineSeparators(expected);
final String result = FileTemplateUtil.mergeTemplate(properties, inputString, false);
assertEquals(expected, result);
List attrs = Arrays.asList(FileTemplateUtil.calculateAttributes(inputString, new Properties(), false));
assertTrue(properties.size() <= attrs.size());
List attrs = Arrays.asList(FileTemplateUtil.calculateAttributes(inputString, new Properties(), false, getProject()));
assertTrue(properties.size() - 1 <= attrs.size());
Enumeration e = properties.propertyNames();
while (e.hasMoreElements()) {
String s = (String)e.nextElement();
assertTrue("Attribute '" + s + "' not found in properties", attrs.contains(s));
assertTrue("Attribute '" + s + "' not found in properties", attrs.contains(s) || FileTemplateManager.PROJECT_NAME_VARIABLE.equals(s));
}
}
@@ -102,7 +103,7 @@ public class FileTemplatesTest extends IdeaTestCase {
FileTemplate template = addTestTemplate("myclass", '${ABC} ${DEF} ${NAME}')
Properties properties = new Properties()
properties.NAME = 'zzz'
assert template.getUnsetAttributes(properties) as Set == ['ABC', 'DEF'] as Set
assert template.getUnsetAttributes(properties, project) as Set == ['ABC', 'DEF'] as Set
}
public void "test collect undefined attribute names from included templates"() {
@@ -112,7 +113,7 @@ public class FileTemplatesTest extends IdeaTestCase {
FileTemplate template = addTestTemplate("myclass", '#parse("included.java") ${DEF} ${NAME}')
Properties properties = new Properties()
properties.NAME = 'zzz'
assert template.getUnsetAttributes(properties) as Set == ['ABC', 'DEF'] as Set
assert template.getUnsetAttributes(properties, project) as Set == ['ABC', 'DEF'] as Set
}
public void testDefaultPackage() throws Exception {
@@ -17,6 +17,7 @@
package com.intellij.ide.fileTemplates;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.CharsetToolkit;
import org.apache.velocity.runtime.parser.ParseException;
import org.jetbrains.annotations.NotNull;
@@ -49,8 +50,6 @@ public interface FileTemplate extends Cloneable {
String ATTRIBUTE_NAME = "NAME";
String ATTRIBUTE_FILE_NAME = "FILE_NAME";
@NotNull String[] getUnsetAttributes(@NotNull Properties properties) throws ParseException;
/** Name without extension */
@NotNull String getName();
@@ -83,4 +82,6 @@ public interface FileTemplate extends Cloneable {
void setReformatCode(boolean reformat);
FileTemplate clone();
@NotNull String[] getUnsetAttributes(@NotNull Properties properties, Project project) throws ParseException;
}
@@ -50,6 +50,8 @@ public abstract class FileTemplateManager{
public static final String CODE_TEMPLATES_CATEGORY = "Code";
public static final String J2EE_TEMPLATES_CATEGORY = "J2EE";
public static final String PROJECT_NAME_VARIABLE = "PROJECT_NAME";
public static FileTemplateManager getInstance(@NotNull Project project){
return ServiceManager.getService(project, FileTemplateManager.class);
}
@@ -89,7 +91,7 @@ public abstract class FileTemplateManager{
@NotNull
public Properties getDefaultProperties(@NotNull Project project) {
Properties properties = getDefaultProperties();
properties.setProperty("PROJECT_NAME", project.getName());
properties.setProperty(PROJECT_NAME_VARIABLE, project.getName());
return properties;
}
@@ -31,12 +31,14 @@ import com.intellij.openapi.project.ProjectManager;
import com.intellij.openapi.project.ex.ProjectManagerEx;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.ClassLoaderUtil;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.ThrowableComputable;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiElement;
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
import com.intellij.util.ArrayUtil;
import com.intellij.util.containers.ContainerUtil;
import org.apache.commons.collections.ExtendedProperties;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
@@ -66,6 +68,7 @@ import java.util.*;
public class FileTemplateUtil{
private static final Logger LOG = Logger.getInstance("#com.intellij.ide.fileTemplates.FileTemplateUtil");
private static final CreateFromTemplateHandler ourDefaultCreateFromTemplateHandler = new DefaultCreateFromTemplateHandler();
private static final ThreadLocal<FileTemplateManager> ourTemplateManager = new ThreadLocal<FileTemplateManager>();
@NonNls public static final String INTERNAL_PACKAGE_INFO_TEMPLATE_NAME = "package-info";
@@ -74,8 +77,6 @@ public class FileTemplateUtil{
static {
try{
final FileTemplateManager templateManager = FileTemplateManager.getInstance(ProjectManager.getInstance().getDefaultProject());
LogSystem emptyLogSystem = new LogSystem() {
@Override
public void init(RuntimeServices runtimeServices) throws Exception {
@@ -97,6 +98,8 @@ public class FileTemplateUtil{
@Override
public InputStream getResourceStream(String resourceName) throws ResourceNotFoundException {
FileTemplateManager templateManager = ourTemplateManager.get();
if (templateManager == null) templateManager = FileTemplateManager.getDefaultInstance();
final FileTemplate include = templateManager.getPattern(resourceName);
if (include == null) {
throw new ResourceNotFoundException("Template not found: " + resourceName);
@@ -127,36 +130,42 @@ public class FileTemplateUtil{
}
}
public static String[] calculateAttributes(String templateContent, Properties properties, boolean includeDummies) throws ParseException {
public static String[] calculateAttributes(String templateContent, Properties properties, boolean includeDummies, Project project) throws ParseException {
Set<String> propertiesNames = new HashSet<String>();
for (Enumeration e = properties.propertyNames(); e.hasMoreElements(); ) {
propertiesNames.add((String)e.nextElement());
}
return calculateAttributes(templateContent, propertiesNames, includeDummies);
return calculateAttributes(templateContent, propertiesNames, includeDummies, project);
}
public static String[] calculateAttributes(String templateContent, Map<String, Object> properties, boolean includeDummies) throws ParseException {
return calculateAttributes(templateContent, properties.keySet(), includeDummies);
public static String[] calculateAttributes(String templateContent, Map<String, Object> properties, boolean includeDummies, Project project) throws ParseException {
return calculateAttributes(templateContent, properties.keySet(), includeDummies, project);
}
public static String[] calculateAttributes(String templateContent, Set<String> propertiesNames, boolean includeDummies) throws ParseException {
private static String[] calculateAttributes(String templateContent, Set<String> propertiesNames, boolean includeDummies, Project project) throws ParseException {
final Set<String> unsetAttributes = new LinkedHashSet<String>();
final Set<String> definedAttributes = new HashSet<String>();
//noinspection HardCodedStringLiteral
SimpleNode template = RuntimeSingleton.parse(new StringReader(templateContent), "MyTemplate");
collectAttributes(unsetAttributes, definedAttributes, template, propertiesNames, includeDummies, new HashSet<String>());
collectAttributes(unsetAttributes, definedAttributes, template, propertiesNames, includeDummies, new HashSet<String>(), project);
for (String definedAttribute : definedAttributes) {
unsetAttributes.remove(definedAttribute);
}
return ArrayUtil.toStringArray(unsetAttributes);
}
private static void collectAttributes(Set<String> referenced, Set<String> defined, Node apacheNode, final Set<String> propertiesNames, final boolean includeDummies, Set<String> visitedIncludes)
private static void collectAttributes(Set<String> referenced,
Set<String> defined,
Node apacheNode,
final Set<String> propertiesNames,
final boolean includeDummies,
Set<String> visitedIncludes,
Project project)
throws ParseException {
int childCount = apacheNode.jjtGetNumChildren();
for(int i = 0; i < childCount; i++){
Node apacheChild = apacheNode.jjtGetChild(i);
collectAttributes(referenced, defined, apacheChild, propertiesNames, includeDummies, visitedIncludes);
collectAttributes(referenced, defined, apacheChild, propertiesNames, includeDummies, visitedIncludes, project);
if (apacheChild instanceof ASTReference){
ASTReference apacheReference = (ASTReference)apacheChild;
String s = apacheReference.literal();
@@ -178,10 +187,10 @@ public class FileTemplateUtil{
Token firstToken = literal.getFirstToken();
if (firstToken != null) {
String s = StringUtil.unquoteString(firstToken.toString());
final FileTemplate includedTemplate = FileTemplateManager.getDefaultInstance().getTemplate(s);
final FileTemplate includedTemplate = FileTemplateManager.getInstance(project).getTemplate(s);
if (includedTemplate != null && visitedIncludes.add(s)) {
SimpleNode template = RuntimeSingleton.parse(new StringReader(includedTemplate.getText()), "MyTemplate");
collectAttributes(referenced, defined, template, propertiesNames, includeDummies, visitedIncludes);
collectAttributes(referenced, defined, template, propertiesNames, includeDummies, visitedIncludes, project);
}
}
}
@@ -270,7 +279,24 @@ public class FileTemplateUtil{
private static String mergeTemplate(String templateContent, final VelocityContext context, boolean useSystemLineSeparators) throws IOException {
final StringWriter stringWriter = new StringWriter();
try {
Velocity.evaluate(context, stringWriter, "", templateContent);
Project project = null;
final Object projectName = context.get(FileTemplateManager.PROJECT_NAME_VARIABLE);
if (projectName instanceof String) {
Project[] projects = ProjectManager.getInstance().getOpenProjects();
project = ContainerUtil.find(projects, new Condition<Project>() {
@Override
public boolean value(Project project) {
return projectName.equals(project.getName());
}
});
}
try {
ourTemplateManager.set(project == null ? FileTemplateManager.getDefaultInstance() : FileTemplateManager.getInstance(project));
Velocity.evaluate(context, stringWriter, "", templateContent);
}
finally {
ourTemplateManager.set(null);
}
}
catch (final VelocityException e) {
if (ApplicationManager.getApplication().isUnitTestMode()) {
@@ -356,7 +382,7 @@ public class FileTemplateUtil{
}
//Set escaped references to dummy values to remove leading "\" (if not already explicitly set)
String[] dummyRefs = calculateAttributes(template.getText(), propsMap, true);
String[] dummyRefs = calculateAttributes(template.getText(), propsMap, true, directory.getProject());
for (String dummyRef : dummyRefs) {
propsMap.put(dummyRef, "");
}
@@ -19,6 +19,7 @@ import com.intellij.ide.fileTemplates.FileTemplate;
import com.intellij.ide.fileTemplates.FileTemplateUtil;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.fileTypes.ex.FileTypeManagerEx;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import org.apache.velocity.runtime.parser.ParseException;
import org.jetbrains.annotations.NotNull;
@@ -94,8 +95,8 @@ public abstract class FileTemplateBase implements FileTemplate {
@Override
@NotNull
public final String[] getUnsetAttributes(@NotNull Properties properties) throws ParseException {
return FileTemplateUtil.calculateAttributes(getText(), properties, false);
public final String[] getUnsetAttributes(@NotNull Properties properties, Project project) throws ParseException {
return FileTemplateUtil.calculateAttributes(getText(), properties, false, project);
}
@Override
@@ -210,7 +210,7 @@ public class FileTemplateManagerImpl extends FileTemplateManager implements Pers
props.setProperty("DS", "$"); // Dollar sign, strongly needed for PHP, JS, etc. See WI-8979
props.setProperty("PROJECT_NAME", myProject.getName());
props.setProperty(PROJECT_NAME_VARIABLE, myProject.getName());
return props;
}
@@ -72,7 +72,7 @@ public class CreateFromTemplateDialog extends DialogWrapper {
String[] unsetAttributes = null;
try {
unsetAttributes = myTemplate.getUnsetAttributes(myDefaultProperties);
unsetAttributes = myTemplate.getUnsetAttributes(myDefaultProperties, project);
}
catch (ParseException e) {
showErrorDialog(e);