blob: 7e236b2f328e97f70aab408bdf300ffa81744584 [file] [log] [blame]
/*
* 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);
return;
}
}
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);
}
}
}