| /* |
| * Copyright (C) 2014 The Android Open Source Project |
| * |
| * 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.google.gct.idea.appengine.gradle.action; |
| |
| import com.android.tools.idea.gradle.parser.BuildFileKey; |
| import com.android.tools.idea.gradle.parser.BuildFileStatement; |
| import com.android.tools.idea.gradle.parser.Dependency; |
| import com.android.tools.idea.gradle.parser.GradleBuildFile; |
| import com.android.tools.idea.gradle.project.GradleProjectImporter; |
| import com.android.tools.idea.gradle.util.GradleUtil; |
| import com.android.tools.idea.templates.Parameter; |
| import com.android.tools.idea.templates.Template; |
| import com.android.tools.idea.templates.TemplateMetadata; |
| import com.android.tools.idea.templates.TemplateUtils; |
| |
| import com.google.gct.idea.appengine.util.AppEngineUtils; |
| import com.google.gct.idea.appengine.util.PsiUtils; |
| import com.google.gct.idea.appengine.wizard.AppEngineTemplates; |
| |
| import com.intellij.openapi.actionSystem.AnAction; |
| import com.intellij.openapi.actionSystem.AnActionEvent; |
| import com.intellij.openapi.actionSystem.LangDataKeys; |
| import com.intellij.openapi.application.ApplicationManager; |
| import com.intellij.openapi.command.WriteCommandAction; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.module.Module; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.ui.Messages; |
| import com.intellij.openapi.vfs.LocalFileSystem; |
| import com.intellij.openapi.vfs.VirtualFile; |
| import com.intellij.psi.PsiDirectory; |
| import com.intellij.psi.PsiJavaFile; |
| |
| import org.jetbrains.annotations.NonNls; |
| import org.jetbrains.annotations.NotNull; |
| |
| import java.io.File; |
| import java.io.FileNotFoundException; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| /** |
| * Action to generate an Endpoint class from a specified class. |
| */ |
| public class GenerateEndpointAction extends AnAction { |
| public static final String ENTITY_NAME = "entityName"; |
| public static final String ENTITY_TYPE = "entityType"; |
| public static final String ENDPOINT_TEMPLATE = "RudimentaryEndpoint"; |
| |
| private static final String ENDPOINT_CLASS_SUFFIX = "Endpoint.java"; |
| private static final String ERROR_MESSAGE_TITLE = "Failed to Generate Endpoint Class"; |
| private static final String DEFAULT_ERROR_MESSAGE = "Error occurred while generating Endpoint class"; |
| private static final String ENDPOINTS_DEPENDENCY = "com.google.appengine:appengine-endpoints:"; |
| private static final String ENDPOINTS_DEPS_DEPENDENCY = "com.google.appengine:appengine-endpoints-deps:"; |
| private static final Logger LOG = Logger.getInstance(GenerateEndpointAction.class); |
| |
| @Override |
| public void actionPerformed(AnActionEvent e) { |
| PsiJavaFile psiJavaFile = PsiUtils.getPsiJavaFileFromContext(e); |
| Project project = e.getProject(); |
| Module module = e.getData(LangDataKeys.MODULE); |
| |
| if (psiJavaFile == null || module == null || project == null) { |
| Messages.showErrorDialog(project, "Please select a Java file to create an Endpoint for.", ERROR_MESSAGE_TITLE); |
| return; |
| } |
| |
| try { |
| if (!AppEngineUtils.isAppEngineModule(project, module)) { |
| Messages.showErrorDialog(project, "Endpoints can only be generated for App Engine projects.", ERROR_MESSAGE_TITLE); |
| } |
| } |
| catch (FileNotFoundException error) { |
| LOG.error(ERROR_MESSAGE_TITLE, error); |
| Messages.showErrorDialog(project, DEFAULT_ERROR_MESSAGE, ERROR_MESSAGE_TITLE); |
| return; |
| } |
| |
| String packageName = psiJavaFile.getPackageName(); |
| PsiDirectory psiJavaFileContainingDirectory = psiJavaFile.getContainingDirectory(); |
| if (psiJavaFileContainingDirectory == null) { |
| Messages.showErrorDialog(project, DEFAULT_ERROR_MESSAGE, ERROR_MESSAGE_TITLE); |
| } |
| String directory = psiJavaFileContainingDirectory.getVirtualFile().getPath(); |
| |
| String classType = psiJavaFile.getName(); |
| int lastIndexOfDot = psiJavaFile.getName().lastIndexOf('.'); |
| if (lastIndexOfDot > 0) { |
| classType = psiJavaFile.getName().substring(0, psiJavaFile.getName().lastIndexOf('.')); |
| } |
| |
| doAction(project, module, packageName, directory, classType); |
| } |
| |
| /** |
| * Generates an endpoint class in the specified module and updates the gradle build file |
| * to include endpoint dependencies if they don't already exist. |
| */ |
| private void doAction(Project project, Module module, String packageName, String directory, @NonNls String classType) { |
| if (classType.isEmpty()) { |
| Messages.showErrorDialog(project, "Class object is required for Endpoint generation", ERROR_MESSAGE_TITLE); |
| return; |
| } |
| |
| // Check if there is a file with the same name as the file that will contain the endpoint class |
| String endpointFileName = directory +"/" + classType + ENDPOINT_CLASS_SUFFIX; |
| File temp = new File(endpointFileName); |
| if (temp.exists()) { |
| Messages.showErrorDialog(project, "\'" + temp.getName() + "\" already exists", ERROR_MESSAGE_TITLE); |
| return; |
| } |
| |
| AppEngineTemplates.TemplateInfo templateInfo = AppEngineTemplates.getAppEngineTemplate(ENDPOINT_TEMPLATE); |
| |
| if (templateInfo == null) { |
| Messages.showErrorDialog(project, DEFAULT_ERROR_MESSAGE, ERROR_MESSAGE_TITLE); |
| return; |
| } |
| |
| final Template template = Template.createFromPath(templateInfo.getFile()); |
| if (template == null) { |
| Messages.showErrorDialog(project, DEFAULT_ERROR_MESSAGE, ERROR_MESSAGE_TITLE); |
| return; |
| } |
| |
| final File projectRoot = new File(project.getBasePath()); |
| final File moduleRoot = new File(projectRoot, module.getName()); |
| |
| // Create the replacement map |
| final Map<String, Object> replacementMap = new HashMap<String, Object>(); |
| try { |
| replacementMap.put(TemplateMetadata.ATTR_PROJECT_OUT, moduleRoot.getCanonicalPath()); |
| } |
| catch (Exception e) { |
| Messages.showErrorDialog("Failed to resolve Module output destination : " + e.getMessage(), ERROR_MESSAGE_TITLE); |
| LOG.error(e); |
| return; |
| } |
| |
| String className = String.valueOf(Character.toLowerCase(classType.charAt(0))); |
| if (classType.length() > 1) { |
| className += classType.substring(1); |
| } |
| |
| replacementMap.put(ENTITY_NAME, className); |
| replacementMap.put(ENTITY_TYPE, classType); |
| replacementMap.put(TemplateMetadata.ATTR_SRC_DIR, directory); |
| replacementMap.put(TemplateMetadata.ATTR_PACKAGE_NAME, packageName); |
| |
| AppEngineTemplates.populateEndpointParameters(replacementMap, packageName); |
| |
| ApplicationManager.getApplication().runWriteAction(new Runnable() { |
| |
| @Override |
| public void run() { |
| template.render(projectRoot, moduleRoot, replacementMap); |
| } |
| }); |
| |
| // Add any missing Endpoint dependency to the build file and sync |
| updateBuildFile(project, module, template); |
| |
| // Open the new Endpoint class in the editor |
| VirtualFile endpointFile = LocalFileSystem.getInstance().findFileByPath(endpointFileName); |
| TemplateUtils.openEditor(project, endpointFile); |
| } |
| |
| /** |
| * Adds missing endpoint dependencies to gradle build file of the specified module. |
| */ |
| private void updateBuildFile(@NonNls Project project, @NotNull Module module, @NonNls Template template) { |
| final VirtualFile buildFile = GradleUtil.getGradleBuildFile(module); |
| if (buildFile == null) { |
| LOG.error("Cannot find gradle build file for module \"" + module.getName() + "\""); |
| return; |
| } |
| |
| Parameter appEngineVersionParam = template.getMetadata().getParameter("appEngineVersion"); |
| String appEngineVersion = (appEngineVersionParam == null) ? "+" : appEngineVersionParam.initial; |
| |
| final GradleBuildFile gradleBuildFile = new GradleBuildFile(buildFile, project); |
| Dependency endpointDependency = |
| new Dependency(Dependency.Scope.COMPILE, Dependency.Type.EXTERNAL, ENDPOINTS_DEPENDENCY + appEngineVersion); |
| Dependency endpointDepsDependency = |
| new Dependency(Dependency.Scope.COMPILE, Dependency.Type.EXTERNAL, ENDPOINTS_DEPS_DEPENDENCY + appEngineVersion); |
| List<Dependency> missingDependencies = new ArrayList<Dependency>(); |
| |
| // Check if the endpoint dependencies already exist in the gradle file |
| if (!gradleBuildFile.hasDependency(endpointDependency)) { |
| missingDependencies.add(endpointDependency); |
| } |
| |
| if (!gradleBuildFile.hasDependency(endpointDepsDependency)) { |
| missingDependencies.add(endpointDepsDependency); |
| } |
| |
| // Add the missing dependencies to the gradle build file |
| if (missingDependencies.size() > 0) { |
| final List<BuildFileStatement> currentDependencies = gradleBuildFile.getDependencies(); |
| currentDependencies.addAll(missingDependencies); |
| |
| WriteCommandAction.runWriteCommandAction(project, new Runnable() { |
| @Override |
| public void run() { |
| gradleBuildFile.setValue(BuildFileKey.DEPENDENCIES, currentDependencies); |
| } |
| }); |
| |
| GradleProjectImporter.getInstance().requestProjectSync(project, null); |
| } |
| } |
| } |