Java tutorial
/* * Copyright 2000-2013 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 org.moe.designer.fileTemplates; import org.moe.designer.ixml.IXmlFileImpl; import org.moe.designer.ixml.IXmlFileType; import com.intellij.ide.IdeBundle; import com.intellij.ide.fileTemplates.*; import com.intellij.ide.fileTemplates.impl.CustomFileTemplate; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.fileTypes.FileTypeManager; import com.intellij.openapi.fileTypes.FileTypes; import com.intellij.openapi.fileTypes.StdFileTypes; import com.intellij.openapi.fileTypes.ex.FileTypeManagerEx; import com.intellij.openapi.project.Project; 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.*; 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; import org.apache.velocity.exception.ResourceNotFoundException; import org.apache.velocity.exception.VelocityException; import org.apache.velocity.runtime.RuntimeConstants; import org.apache.velocity.runtime.RuntimeServices; import org.apache.velocity.runtime.RuntimeSingleton; import org.apache.velocity.runtime.log.LogSystem; import org.apache.velocity.runtime.parser.ParseException; import org.apache.velocity.runtime.parser.Token; import org.apache.velocity.runtime.parser.node.*; import org.apache.velocity.runtime.resource.Resource; import org.apache.velocity.runtime.resource.loader.ResourceLoader; import org.apache.velocity.util.StringUtils; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.io.*; import java.util.*; /** * @author MYakovlev */ public class IXMLFileTemplateUtil { 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"; private IXMLFileTemplateUtil() { } static { try { LogSystem emptyLogSystem = new LogSystem() { @Override public void init(RuntimeServices runtimeServices) throws Exception { } @Override public void logVelocityMessage(int i, String s) { //todo[myakovlev] log somethere? } }; Velocity.setProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM, emptyLogSystem); Velocity.setProperty(RuntimeConstants.INPUT_ENCODING, FileTemplate.ourEncoding); Velocity.setProperty(RuntimeConstants.PARSER_POOL_SIZE, 3); Velocity.setProperty(RuntimeConstants.RESOURCE_LOADER, "includes"); Velocity.setProperty("includes.resource.loader.instance", new ResourceLoader() { @Override public void init(ExtendedProperties configuration) { } @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); } final String text = include.getText(); try { return new ByteArrayInputStream(text.getBytes(FileTemplate.ourEncoding)); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } @Override public boolean isSourceModified(Resource resource) { return true; } @Override public long getLastModified(Resource resource) { return 0L; } }); Velocity.init(); } catch (Exception e) { LOG.error("Unable to init Velocity", e); } } 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, project); } public static String[] calculateAttributes(String templateContent, Map<String, Object> properties, boolean includeDummies, Project project) throws ParseException { return calculateAttributes(templateContent, properties.keySet(), includeDummies, project); } 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>(), 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, 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, project); if (apacheChild instanceof ASTReference) { ASTReference apacheReference = (ASTReference) apacheChild; String s = apacheReference.literal(); s = referenceToAttribute(s, includeDummies); if (s != null && s.length() > 0 && !propertiesNames.contains(s)) { referenced.add(s); } } else if (apacheChild instanceof ASTSetDirective) { ASTReference lhs = (ASTReference) apacheChild.jjtGetChild(0); String attr = referenceToAttribute(lhs.literal(), false); if (attr != null) { defined.add(attr); } } else if (apacheChild instanceof ASTDirective && "parse".equals(((ASTDirective) apacheChild).getDirectiveName()) && apacheChild.jjtGetNumChildren() == 1) { Node literal = apacheChild.jjtGetChild(0); if (literal instanceof ASTStringLiteral && literal.jjtGetNumChildren() == 0) { Token firstToken = literal.getFirstToken(); if (firstToken != null) { String s = StringUtil.unquoteString(firstToken.toString()); 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, project); } } } } } } /** * Removes each two leading '\', removes leading $, removes {} * Examples: * $qqq -> qqq * \$qqq -> qqq if dummy attributes are collected too, null otherwise * \\$qqq -> qqq * ${qqq} -> qqq */ @Nullable private static String referenceToAttribute(String attrib, boolean includeDummies) { while (attrib.startsWith("\\\\")) { attrib = attrib.substring(2); } if (attrib.startsWith("\\$")) { if (includeDummies) { attrib = attrib.substring(1); } else return null; } if (!StringUtil.startsWithChar(attrib, '$')) { return null; } attrib = attrib.substring(1); if (StringUtil.startsWithChar(attrib, '{')) { String cleanAttribute = null; for (int i = 1; i < attrib.length(); i++) { char currChar = attrib.charAt(i); if (currChar == '{' || currChar == '.') { // Invalid match cleanAttribute = null; break; } else if (currChar == '}') { // Valid match cleanAttribute = attrib.substring(1, i); break; } } attrib = cleanAttribute; } else { for (int i = 0; i < attrib.length(); i++) { char currChar = attrib.charAt(i); if (currChar == '{' || currChar == '}' || currChar == '.') { attrib = attrib.substring(0, i); break; } } } return attrib; } public static String mergeTemplate(Map attributes, String content, boolean useSystemLineSeparators) throws IOException { VelocityContext context = createVelocityContext(); for (final Object o : attributes.keySet()) { String name = (String) o; context.put(name, attributes.get(name)); } return mergeTemplate(content, context, useSystemLineSeparators); } private static VelocityContext createVelocityContext() { VelocityContext context = new VelocityContext(); context.put("StringUtils", StringUtils.class); return context; } public static String mergeTemplate(Properties attributes, String content, boolean useSystemLineSeparators) throws IOException { VelocityContext context = createVelocityContext(); Enumeration<?> names = attributes.propertyNames(); while (names.hasMoreElements()) { String name = (String) names.nextElement(); context.put(name, attributes.getProperty(name)); } return mergeTemplate(content, context, useSystemLineSeparators); } private static String mergeTemplate(String templateContent, final VelocityContext context, boolean useSystemLineSeparators) throws IOException { final StringWriter stringWriter = new StringWriter(); try { 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()) { LOG.error(e); } LOG.info("Error evaluating template:\n" + templateContent, e); ApplicationManager.getApplication().invokeLater(new Runnable() { @Override public void run() { Messages.showErrorDialog(IdeBundle.message("error.parsing.file.template", e.getMessage()), IdeBundle.message("title.velocity.error")); } }); } final String result = stringWriter.toString(); if (useSystemLineSeparators) { final String newSeparator = CodeStyleSettingsManager .getSettings(ProjectManagerEx.getInstanceEx().getDefaultProject()).getLineSeparator(); if (!"\n".equals(newSeparator)) { return StringUtil.convertLineSeparators(result, newSeparator); } } return result; } public static PsiElement createFromTemplate(@NotNull final FileTemplate template, @NonNls @Nullable final String fileName, @Nullable Properties props, @NotNull final PsiDirectory directory) throws Exception { Map<String, Object> map; if (props != null) { map = new HashMap<String, Object>(); putAll(map, props); } else { map = null; } return createFromTemplate(template, fileName, map, directory, null); } public static PsiElement createFromTemplate(@NotNull final FileTemplate template, @NonNls @Nullable String fileName, @Nullable Properties props, @NotNull final PsiDirectory directory, @Nullable ClassLoader classLoader) throws Exception { Map<String, Object> map; if (props != null) { map = new HashMap<String, Object>(); putAll(map, props); } else { map = null; } return createFromTemplate(template, fileName, map, directory, classLoader); } public static PsiElement createFromTemplate(@NotNull final FileTemplate template, @NonNls @Nullable String fileName, @Nullable Map<String, Object> propsMap, @NotNull final PsiDirectory directory, @Nullable ClassLoader classLoader) throws Exception { @NotNull final Project project = directory.getProject(); if (propsMap == null) { Properties p = FileTemplateManager.getInstance(project).getDefaultProperties(); propsMap = new HashMap<String, Object>(); putAll(propsMap, p); } FileTemplateManager.getInstance(project).addRecentName(template.getName()); Properties p = new Properties(); fillDefaultProperties(p, directory); putAll(propsMap, p); final CreateFromTemplateHandler handler = findHandler(template); if (fileName != null && propsMap.get(FileTemplate.ATTRIBUTE_NAME) == null) { propsMap.put(FileTemplate.ATTRIBUTE_NAME, fileName); } else if (fileName == null && handler.isNameRequired()) { fileName = (String) propsMap.get(FileTemplate.ATTRIBUTE_NAME); if (fileName == null) { throw new Exception("File name must be specified"); } } //Set escaped references to dummy values to remove leading "\" (if not already explicitly set) String[] dummyRefs = calculateAttributes(template.getText(), propsMap, true, directory.getProject()); for (String dummyRef : dummyRefs) { propsMap.put(dummyRef, ""); } handler.prepareProperties(propsMap); final Map<String, Object> props_ = propsMap; final String fileName_ = fileName; String mergedText = ClassLoaderUtil.runWithClassLoader( classLoader != null ? classLoader : FileTemplateUtil.class.getClassLoader(), new ThrowableComputable<String, IOException>() { @Override public String compute() throws IOException { return template.getText(props_); } }); final String templateText = StringUtil.convertLineSeparators(mergedText); // directory.createFile(fileName + ".ixml"); PsiFile myFile = PsiFileFactory.getInstance(directory.getProject()).createFileFromText(fileName + ".ixml", IXmlFileType.INSTANCE, templateText); directory.add(myFile); // File theDir = new File(directory.getText()); // File combo = new File(theDir,fileName+".ixml"); // PrintWriter writer = new PrintWriter(combo.getP ath(),"UTF8"); // PsiManager.getInstance(directory.getProject()).findFile() // writer.println(templateText); // writer.close(); // directory.add(myFile); // directory. return directory.findFile(fileName + ".ixml"); // //// IXmlFileImpl myFile = new IXmlFileImpl() // final Exception[] commandException = new Exception[1]; // final PsiElement[] result = new PsiElement[1]; // CommandProcessor.getInstance().executeCommand(project, new Runnable(){ // @Override // public void run(){ // ApplicationManager.getApplication().runWriteAction(new Runnable(){ // @Override // public void run(){ // try{ // result[0] = handler.createFromTemplate(project, directory, fileName_, template, templateText, props_); // } // catch (Exception ex){ // commandException[0] = ex; // } // } // }); // } // }, template.isTemplateOfType(StdFileTypes.JAVA) && !"package-info".equals(template.getName()) // ? IdeBundle.message("command.create.class.from.template") // : IdeBundle.message("command.create.file.from.template"), null); // if(commandException[0] != null){ // throw commandException[0]; // } // return result[0]; } public static CreateFromTemplateHandler findHandler(final FileTemplate template) { for (CreateFromTemplateHandler handler : Extensions.getExtensions(CreateFromTemplateHandler.EP_NAME)) { if (handler.handlesTemplate(template)) { return handler; } } return ourDefaultCreateFromTemplateHandler; } public static void fillDefaultProperties(final Properties props, final PsiDirectory directory) { final DefaultTemplatePropertiesProvider[] providers = Extensions .getExtensions(DefaultTemplatePropertiesProvider.EP_NAME); for (DefaultTemplatePropertiesProvider provider : providers) { provider.fillProperties(directory, props); } } public static String indent(String methodText, Project project, FileType fileType) { int indent = CodeStyleSettingsManager.getSettings(project).getIndentSize(fileType); return methodText.replaceAll("\n", "\n" + StringUtil.repeatSymbol(' ', indent)); } public static boolean canCreateFromTemplate(PsiDirectory[] dirs, FileTemplate template) { FileType fileType = FileTypeManagerEx.getInstanceEx().getFileTypeByExtension(template.getExtension()); if (fileType.equals(FileTypes.UNKNOWN)) return false; CreateFromTemplateHandler handler = findHandler(template); return handler.canCreate(dirs); } @Nullable public static Icon getIcon(@NotNull FileTemplate fileTemplate) { return FileTypeManager.getInstance().getFileTypeByExtension(fileTemplate.getExtension()).getIcon(); } public static void putAll(final Map<String, Object> props, final Properties p) { for (Enumeration<?> e = p.propertyNames(); e.hasMoreElements();) { String s = (String) e.nextElement(); props.put(s, p.getProperty(s)); } } @NotNull public static FileTemplate createTemplate(@NotNull String prefName, @NotNull String extension, @NotNull String content, FileTemplate[] templates) { final Set<String> names = new HashSet<String>(); for (FileTemplate template : templates) { names.add(template.getName()); } String name = prefName; int i = 0; while (names.contains(name)) { name = prefName + " (" + ++i + ")"; } final FileTemplate newTemplate = new CustomFileTemplate(name, extension); newTemplate.setText(content); return newTemplate; } }