From 32263e0eddc1f4c24edb798ca96fd73fc90341a0 Mon Sep 17 00:00:00 2001 From: Yaroslav Pankratyev Date: Wed, 26 Jul 2017 18:50:46 +0300 Subject: [PATCH 01/27] Move component-related actions to separate package, minor improvements --- plugins/devkit/resources/META-INF/plugin.xml | 8 ++-- ...erateClassAndPatchPluginXmlActionBase.java | 15 +------- ...onentClassAndPatchPluginXmlActionBase.java | 37 +++++++++++++++++++ ...enerateComponentExternalizationAction.java | 9 ++--- .../NewApplicationComponentAction.java | 14 ++----- .../NewModuleComponentAction.java | 14 ++----- .../NewProjectComponentAction.java | 14 ++----- 7 files changed, 57 insertions(+), 54 deletions(-) create mode 100644 plugins/devkit/src/actions/component/GenerateComponentClassAndPatchPluginXmlActionBase.java rename plugins/devkit/src/actions/{ => component}/GenerateComponentExternalizationAction.java (96%) rename plugins/devkit/src/actions/{ => component}/NewApplicationComponentAction.java (88%) rename plugins/devkit/src/actions/{ => component}/NewModuleComponentAction.java (88%) rename plugins/devkit/src/actions/{ => component}/NewProjectComponentAction.java (88%) diff --git a/plugins/devkit/resources/META-INF/plugin.xml b/plugins/devkit/resources/META-INF/plugin.xml index aff372b04dee..592f0ab9f5d8 100644 --- a/plugins/devkit/resources/META-INF/plugin.xml +++ b/plugins/devkit/resources/META-INF/plugin.xml @@ -223,17 +223,17 @@ + class="org.jetbrains.idea.devkit.actions.component.NewApplicationComponentAction"/> + class="org.jetbrains.idea.devkit.actions.component.NewProjectComponentAction"/> + class="org.jetbrains.idea.devkit.actions.component.NewModuleComponentAction"/> - diff --git a/plugins/devkit/src/actions/GenerateClassAndPatchPluginXmlActionBase.java b/plugins/devkit/src/actions/GenerateClassAndPatchPluginXmlActionBase.java index 322316ab8851..f90df234cb3c 100644 --- a/plugins/devkit/src/actions/GenerateClassAndPatchPluginXmlActionBase.java +++ b/plugins/devkit/src/actions/GenerateClassAndPatchPluginXmlActionBase.java @@ -17,19 +17,12 @@ package org.jetbrains.idea.devkit.actions; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; -import com.intellij.psi.PsiClass; import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiElement; -import com.intellij.psi.xml.XmlFile; -import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.Nullable; -import org.jetbrains.idea.devkit.util.ComponentType; -import javax.swing.*; +import javax.swing.Icon; -/** - * @author max - */ public abstract class GenerateClassAndPatchPluginXmlActionBase extends GeneratePluginClassAction { public GenerateClassAndPatchPluginXmlActionBase(String text, String description, @Nullable Icon icon) { super(text, description, icon); @@ -43,10 +36,4 @@ public abstract class GenerateClassAndPatchPluginXmlActionBase extends GenerateP Messages.showInputDialog(project, getClassNamePrompt(), getClassNamePromptTitle(), Messages.getQuestionIcon(), "", validator); return validator.getCreatedElements(); } - - protected abstract ComponentType getComponentType(); - - public void patchPluginXml(XmlFile pluginXml, PsiClass klass) throws IncorrectOperationException { - getComponentType().patchPluginXml(pluginXml, klass); - } } diff --git a/plugins/devkit/src/actions/component/GenerateComponentClassAndPatchPluginXmlActionBase.java b/plugins/devkit/src/actions/component/GenerateComponentClassAndPatchPluginXmlActionBase.java new file mode 100644 index 000000000000..cffeb09ce034 --- /dev/null +++ b/plugins/devkit/src/actions/component/GenerateComponentClassAndPatchPluginXmlActionBase.java @@ -0,0 +1,37 @@ +/* + * Copyright 2000-2017 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.jetbrains.idea.devkit.actions.component; + +import com.intellij.psi.PsiClass; +import com.intellij.psi.xml.XmlFile; +import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.devkit.actions.GenerateClassAndPatchPluginXmlActionBase; +import org.jetbrains.idea.devkit.util.ComponentType; + +import javax.swing.Icon; + +public abstract class GenerateComponentClassAndPatchPluginXmlActionBase extends GenerateClassAndPatchPluginXmlActionBase { + public GenerateComponentClassAndPatchPluginXmlActionBase(String text, String description, @Nullable Icon icon) { + super(text, description, icon); + } + + protected abstract ComponentType getComponentType(); + + public void patchPluginXml(XmlFile pluginXml, PsiClass klass) throws IncorrectOperationException { + getComponentType().patchPluginXml(pluginXml, klass); + } +} diff --git a/plugins/devkit/src/actions/GenerateComponentExternalizationAction.java b/plugins/devkit/src/actions/component/GenerateComponentExternalizationAction.java similarity index 96% rename from plugins/devkit/src/actions/GenerateComponentExternalizationAction.java rename to plugins/devkit/src/actions/component/GenerateComponentExternalizationAction.java index 481a3559cc17..a94a797e9118 100644 --- a/plugins/devkit/src/actions/GenerateComponentExternalizationAction.java +++ b/plugins/devkit/src/actions/component/GenerateComponentExternalizationAction.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.idea.devkit.actions; +package org.jetbrains.idea.devkit.actions.component; import com.intellij.codeInsight.actions.CodeInsightEditorAction; import com.intellij.openapi.actionSystem.*; @@ -33,11 +33,8 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.devkit.DevKitBundle; -/** - * @author max - */ public class GenerateComponentExternalizationAction extends AnAction { - private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.devkit.actions.GenerateComponentExternalizationAction"); + private static final Logger LOG = Logger.getInstance("#" + GenerateComponentExternalizationAction.class.getCanonicalName()); @NonNls private final static String BASE_COMPONENT = "com.intellij.openapi.components.BaseComponent"; @NonNls private final static String PERSISTENCE_STATE_COMPONENT = "com.intellij.openapi.components.PersistentStateComponent"; diff --git a/plugins/devkit/src/actions/NewApplicationComponentAction.java b/plugins/devkit/src/actions/component/NewApplicationComponentAction.java similarity index 88% rename from plugins/devkit/src/actions/NewApplicationComponentAction.java rename to plugins/devkit/src/actions/component/NewApplicationComponentAction.java index 8cc6988c3def..f57aab56c886 100644 --- a/plugins/devkit/src/actions/NewApplicationComponentAction.java +++ b/plugins/devkit/src/actions/component/NewApplicationComponentAction.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -13,19 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.idea.devkit.actions; +package org.jetbrains.idea.devkit.actions.component; import com.intellij.psi.PsiDirectory; -import org.jetbrains.idea.devkit.util.ComponentType; import org.jetbrains.idea.devkit.DevKitBundle; +import org.jetbrains.idea.devkit.util.ComponentType; -/** - * @author max - */ -public class NewApplicationComponentAction extends GenerateClassAndPatchPluginXmlActionBase { - /** - *. - */ +public class NewApplicationComponentAction extends GenerateComponentClassAndPatchPluginXmlActionBase { public NewApplicationComponentAction() { super(DevKitBundle.message("new.menu.application.component.text"), DevKitBundle.message("new.menu.application.component.description"), null); diff --git a/plugins/devkit/src/actions/NewModuleComponentAction.java b/plugins/devkit/src/actions/component/NewModuleComponentAction.java similarity index 88% rename from plugins/devkit/src/actions/NewModuleComponentAction.java rename to plugins/devkit/src/actions/component/NewModuleComponentAction.java index 7a999c39e36d..88b6ce53d34a 100644 --- a/plugins/devkit/src/actions/NewModuleComponentAction.java +++ b/plugins/devkit/src/actions/component/NewModuleComponentAction.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -13,19 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.idea.devkit.actions; +package org.jetbrains.idea.devkit.actions.component; import com.intellij.psi.PsiDirectory; -import org.jetbrains.idea.devkit.util.ComponentType; import org.jetbrains.idea.devkit.DevKitBundle; +import org.jetbrains.idea.devkit.util.ComponentType; -/** - * @author max - */ -public class NewModuleComponentAction extends GenerateClassAndPatchPluginXmlActionBase { - /** - *. - */ +public class NewModuleComponentAction extends GenerateComponentClassAndPatchPluginXmlActionBase { public NewModuleComponentAction() { super(DevKitBundle.message("new.menu.module.component.text"), DevKitBundle.message("new.menu.module.component.description"), null); diff --git a/plugins/devkit/src/actions/NewProjectComponentAction.java b/plugins/devkit/src/actions/component/NewProjectComponentAction.java similarity index 88% rename from plugins/devkit/src/actions/NewProjectComponentAction.java rename to plugins/devkit/src/actions/component/NewProjectComponentAction.java index d97b4fc554f7..8acab90dc60f 100644 --- a/plugins/devkit/src/actions/NewProjectComponentAction.java +++ b/plugins/devkit/src/actions/component/NewProjectComponentAction.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2017 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. @@ -13,19 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.idea.devkit.actions; +package org.jetbrains.idea.devkit.actions.component; import com.intellij.psi.PsiDirectory; -import org.jetbrains.idea.devkit.util.ComponentType; import org.jetbrains.idea.devkit.DevKitBundle; +import org.jetbrains.idea.devkit.util.ComponentType; -/** - * @author max - */ -public class NewProjectComponentAction extends GenerateClassAndPatchPluginXmlActionBase { - /** - *. - */ +public class NewProjectComponentAction extends GenerateComponentClassAndPatchPluginXmlActionBase { public NewProjectComponentAction() { super(DevKitBundle.message("new.menu.project.component.text"), DevKitBundle.message("new.menu.project.component.description"), null); From ee3dbf755ec9c0387a3257984efb047bbc770c2a Mon Sep 17 00:00:00 2001 From: Yaroslav Pankratyev Date: Wed, 26 Jul 2017 18:54:35 +0300 Subject: [PATCH 02/27] Add Override annotations to component-related actions --- .../GenerateComponentClassAndPatchPluginXmlActionBase.java | 1 + .../component/GenerateComponentExternalizationAction.java | 2 ++ .../actions/component/NewApplicationComponentAction.java | 7 +++++++ .../src/actions/component/NewModuleComponentAction.java | 7 +++++++ .../src/actions/component/NewProjectComponentAction.java | 7 +++++++ 5 files changed, 24 insertions(+) diff --git a/plugins/devkit/src/actions/component/GenerateComponentClassAndPatchPluginXmlActionBase.java b/plugins/devkit/src/actions/component/GenerateComponentClassAndPatchPluginXmlActionBase.java index cffeb09ce034..d78aa4bb1a1c 100644 --- a/plugins/devkit/src/actions/component/GenerateComponentClassAndPatchPluginXmlActionBase.java +++ b/plugins/devkit/src/actions/component/GenerateComponentClassAndPatchPluginXmlActionBase.java @@ -31,6 +31,7 @@ public abstract class GenerateComponentClassAndPatchPluginXmlActionBase extends protected abstract ComponentType getComponentType(); + @Override public void patchPluginXml(XmlFile pluginXml, PsiClass klass) throws IncorrectOperationException { getComponentType().patchPluginXml(pluginXml, klass); } diff --git a/plugins/devkit/src/actions/component/GenerateComponentExternalizationAction.java b/plugins/devkit/src/actions/component/GenerateComponentExternalizationAction.java index a94a797e9118..065eb745aa87 100644 --- a/plugins/devkit/src/actions/component/GenerateComponentExternalizationAction.java +++ b/plugins/devkit/src/actions/component/GenerateComponentExternalizationAction.java @@ -47,6 +47,7 @@ public class GenerateComponentExternalizationAction extends AnAction { super.beforeActionPerformedUpdate(e); } + @Override public void actionPerformed(AnActionEvent e) { final PsiClass target = getComponentInContext(e.getDataContext()); assert target != null; @@ -123,6 +124,7 @@ public class GenerateComponentExternalizationAction extends AnAction { return contextClass; } + @Override public void update(AnActionEvent e) { super.update(e); final PsiClass target = getComponentInContext(e.getDataContext()); diff --git a/plugins/devkit/src/actions/component/NewApplicationComponentAction.java b/plugins/devkit/src/actions/component/NewApplicationComponentAction.java index f57aab56c886..0c1930c0a897 100644 --- a/plugins/devkit/src/actions/component/NewApplicationComponentAction.java +++ b/plugins/devkit/src/actions/component/NewApplicationComponentAction.java @@ -25,30 +25,37 @@ public class NewApplicationComponentAction extends GenerateComponentClassAndPatc DevKitBundle.message("new.menu.application.component.description"), null); } + @Override protected ComponentType getComponentType() { return ComponentType.APPLICATION; } + @Override protected String getErrorTitle() { return DevKitBundle.message("new.application.component.error"); } + @Override protected String getCommandName() { return DevKitBundle.message("new.application.component.command"); } + @Override protected String getClassNamePromptTitle() { return DevKitBundle.message("new.application.component.prompt.title"); } + @Override protected String getClassTemplateName() { return "ApplicationComponent.java"; } + @Override protected String getClassNamePrompt() { return DevKitBundle.message("new.application.component.prompt"); } + @Override protected String getActionName(PsiDirectory directory, String newName) { return DevKitBundle.message("new.application.component.action.name", directory, newName); } diff --git a/plugins/devkit/src/actions/component/NewModuleComponentAction.java b/plugins/devkit/src/actions/component/NewModuleComponentAction.java index 88b6ce53d34a..53d83fea6c72 100644 --- a/plugins/devkit/src/actions/component/NewModuleComponentAction.java +++ b/plugins/devkit/src/actions/component/NewModuleComponentAction.java @@ -25,30 +25,37 @@ public class NewModuleComponentAction extends GenerateComponentClassAndPatchPlug DevKitBundle.message("new.menu.module.component.description"), null); } + @Override protected ComponentType getComponentType() { return ComponentType.MODULE; } + @Override protected String getErrorTitle() { return DevKitBundle.message("new.module.component.error"); } + @Override protected String getCommandName() { return DevKitBundle.message("new.module.component.command"); } + @Override protected String getClassNamePromptTitle() { return DevKitBundle.message("new.module.component.prompt.title"); } + @Override protected String getClassTemplateName() { return "ModuleComponent.java"; } + @Override protected String getClassNamePrompt() { return DevKitBundle.message("new.module.component.prompt"); } + @Override protected String getActionName(PsiDirectory directory, String newName) { return DevKitBundle.message("new.module.component.action.name", directory, newName); } diff --git a/plugins/devkit/src/actions/component/NewProjectComponentAction.java b/plugins/devkit/src/actions/component/NewProjectComponentAction.java index 8acab90dc60f..2c895926a033 100644 --- a/plugins/devkit/src/actions/component/NewProjectComponentAction.java +++ b/plugins/devkit/src/actions/component/NewProjectComponentAction.java @@ -25,30 +25,37 @@ public class NewProjectComponentAction extends GenerateComponentClassAndPatchPlu DevKitBundle.message("new.menu.project.component.description"), null); } + @Override protected ComponentType getComponentType() { return ComponentType.PROJECT; } + @Override protected String getErrorTitle() { return DevKitBundle.message("new.project.component.error"); } + @Override protected String getCommandName() { return DevKitBundle.message("new.project.component.command"); } + @Override protected String getClassNamePromptTitle() { return DevKitBundle.message("new.project.component.prompt.title"); } + @Override protected String getClassTemplateName() { return "ProjectComponent.java"; } + @Override protected String getClassNamePrompt() { return DevKitBundle.message("new.project.component.prompt"); } + @Override protected String getActionName(PsiDirectory directory, String newName) { return DevKitBundle.message("new.project.component.action.name", directory, newName); } From 733ab9ebc9ae6786986922d3ae69c5394e6bed79 Mon Sep 17 00:00:00 2001 From: Yaroslav Pankratyev Date: Wed, 26 Jul 2017 23:19:58 +0300 Subject: [PATCH 03/27] Actions for creating application/project service --- plugins/devkit/resources/META-INF/plugin.xml | 6 ++ .../j2ee/ApplicationService.java.ft | 10 +++ .../j2ee/ApplicationService.java.html | 11 ++++ .../fileTemplates/j2ee/ProjectService.java.ft | 12 ++++ .../j2ee/ProjectService.java.html | 11 ++++ .../idea/devkit/DevKitBundle.properties | 35 +++++------ ...rviceClassAndPatchPluginXmlActionBase.java | 56 +++++++++++++++++ .../service/NewApplicationServiceAction.java | 61 +++++++++++++++++++ .../service/NewProjectServiceAction.java | 61 +++++++++++++++++++ 9 files changed, 243 insertions(+), 20 deletions(-) create mode 100644 plugins/devkit/resources/fileTemplates/j2ee/ApplicationService.java.ft create mode 100644 plugins/devkit/resources/fileTemplates/j2ee/ApplicationService.java.html create mode 100644 plugins/devkit/resources/fileTemplates/j2ee/ProjectService.java.ft create mode 100644 plugins/devkit/resources/fileTemplates/j2ee/ProjectService.java.html create mode 100644 plugins/devkit/src/actions/service/GenerateServiceClassAndPatchPluginXmlActionBase.java create mode 100644 plugins/devkit/src/actions/service/NewApplicationServiceAction.java create mode 100644 plugins/devkit/src/actions/service/NewProjectServiceAction.java diff --git a/plugins/devkit/resources/META-INF/plugin.xml b/plugins/devkit/resources/META-INF/plugin.xml index 592f0ab9f5d8..de11f690e201 100644 --- a/plugins/devkit/resources/META-INF/plugin.xml +++ b/plugins/devkit/resources/META-INF/plugin.xml @@ -222,6 +222,12 @@ class="com.intellij.ide.actions.NonTrivialActionGroup"> + + + + + + + + + +
This is a built-in template used each time you create + a new IntelliJ Platform application-level service. +
+ + \ No newline at end of file diff --git a/plugins/devkit/resources/fileTemplates/j2ee/ProjectService.java.ft b/plugins/devkit/resources/fileTemplates/j2ee/ProjectService.java.ft new file mode 100644 index 000000000000..c84da0dc8860 --- /dev/null +++ b/plugins/devkit/resources/fileTemplates/j2ee/ProjectService.java.ft @@ -0,0 +1,12 @@ +package ${PACKAGE_NAME}; + +import com.intellij.openapi.components.ServiceManager; +import com.intellij.openapi.project.Project; +import org.jetbrains.annotations.NotNull; + +#parse("File Header.java") +public class ${NAME} { + public static ${NAME} getInstance(@NotNull Project project) { + return ServiceManager.getService(project, ${NAME}.class); + } +} diff --git a/plugins/devkit/resources/fileTemplates/j2ee/ProjectService.java.html b/plugins/devkit/resources/fileTemplates/j2ee/ProjectService.java.html new file mode 100644 index 000000000000..08c20e5ee4df --- /dev/null +++ b/plugins/devkit/resources/fileTemplates/j2ee/ProjectService.java.html @@ -0,0 +1,11 @@ + + + + + + +
This is a built-in template used each time you create + a new IntelliJ Platform project-level service. +
+ + \ No newline at end of file diff --git a/plugins/devkit/resources/org/jetbrains/idea/devkit/DevKitBundle.properties b/plugins/devkit/resources/org/jetbrains/idea/devkit/DevKitBundle.properties index bcb11d87e0ef..23cd2eb251a2 100644 --- a/plugins/devkit/resources/org/jetbrains/idea/devkit/DevKitBundle.properties +++ b/plugins/devkit/resources/org/jetbrains/idea/devkit/DevKitBundle.properties @@ -42,30 +42,17 @@ error.occurred=Error Occurred sdk.no.specified=No SDK specified for plugin module ''{0}'' sdk.type.incorrect=Wrong SDK type for plugin module ''{0}''. sdk.type.incorrect.common=Wrong SDK type for plugin module -incorrect.dependency.non-plugin-module=The non-plugin module ''{0}'' cannot depend on plugin module ''{1}''. -incorrect.dependency.not-declared=The plugin module ''{0}'' doesn''t declare the dependency on ''{1}'' in its plugin.xml. error.file.not.found=File not Found error.file.not.found.message=File ''{0}'' not found error.no.plugin.xml=No plugin.xml file found error.plugin.xml.readonly=The plugin.xml file is read-only #run configurations -run.configuration.classpath.from.module.choose=Use Classpath and JDK from Module: run.configuration.no.module.specified=No plugin module specified for configuration run.configuration.title=Plugin run.configuration.type.description=Plugin Sandbox Environment -idea.log.tab.title=IDEA LOG - #Misc info.message=Info -create.smth=Create {0} -show.smth=&Show {0} -presentable.plugin.module.name=Plugin Module ''{0}'' - -action.MakeJarAction.text=Prepare To Deploy -action.MakeAllJarsAction.text=Prepare All Plugins To Deploy - -dont.add.idea.libs.to.classpath=IDE-related libraries ({0}) must not be added to the module classpath. Please add them to the IntelliJ Platform Plugin SDK instead. new.action.id=&Action ID: new.action.description=&Description: new.action.class.name=&Class Name: @@ -112,6 +99,21 @@ new.project.component.prompt.title=New Project Component new.project.component.prompt=Enter new project component name: new.project.component.action.name=Creating new project component: {0}.{1} +new.menu.application.service.text=Application Service +new.menu.application.service.description=Create New Application Service +new.application.service.error=Cannot create application service +new.application.service.command=Create Application Service +new.application.service.prompt.title=New Application Service +new.application.service.prompt=Enter new application service name: +new.application.service.action.name=Creating new application service: {0}.{1} +new.menu.project.service.text=Project Service +new.menu.project.service.description=Create New Project Service +new.project.service.error=Cannot create project service +new.project.service.command=Create Project Service +new.project.service.prompt.title=New Project Service +new.project.service.prompt=Enter new project service name: +new.project.service.action.name=Creating new project service: {0}.{1} + select.plugin.modules.to.patch=Select Plugin Modules to Patch keyword.extend=extend @@ -126,7 +128,6 @@ inspections.registration.problems.option.check.plugin.xml=Check Plugin Descripto inspections.registration.problems.option.check.java.actions=Check Java Actions inspections.registration.problems.option.check.java.code=Check Java Code inspections.registration.problems.quickfix.read-only=Class ''{0}'' is read-only -inspections.registration.problems.quickfix.make.public=Make {0} public inspections.registration.problems.quickfix.create.constructor=Create no-argument constructor inspections.registration.problems.incompatible.message=According to its registration in plugin.xml, the class should {0} ''{1}'' @@ -135,7 +136,6 @@ inspections.registration.problems.missing.noarg.ctor=Action class must have a no inspections.registration.problems.missing.implementation.class=Missing implementation-class inspections.registration.problems.cannot.resolve.class=Cannot resolve {0} class -inspections.registration.problems.component.should.implement=Component class must implement ''{0}'' inspections.registration.problems.component.incompatible.interface=Component class is not assignable to its interface-class ''{0}'' inspections.registration.problems.component.duplicate.interface=Multiple components with the same interface-class are not allowed inspections.registration.problems.action.incompatible.class=Action class must extend ''{0}'' @@ -150,17 +150,12 @@ inspections.component.not.registered.quickfix.error=Cannot Register {0} inspections.component.postfix.template.not.found.description.name=Postfix template Description Checker -InspectionUseGrayColor=Use Gray ant.build.jar.comment=Build archive for plugin ''{0}'' ant.build.jar.description=Build plugin archive for module ''{0}'' -project.title=Plugin Project no.java.sdk.for.idea.sdk.found=No Java SDK of appropriate version found. In addition to the IntelliJ Platform Plugin SDK, you need to define a JDK with the same Java version ({0}). no.idea.sdk.version.found=Failed to detect JDK version required for IntelliJ Platform Plugin SDK. -group.PluginDeployActions.text=Plugin Deployment Actions - error.cannot.resolve.plugin=Cannot resolve plugin {0} create.description.file=Create description file {0} select.target.location.of.description=Select target location of {0} -serialization.only.member.used.explicitly=Serialization-only member used explicitly implemented.at.runtime.dom=DOM Element implemented at runtime implemented.at.runtime.jamElement=JAM Element implemented at runtime diff --git a/plugins/devkit/src/actions/service/GenerateServiceClassAndPatchPluginXmlActionBase.java b/plugins/devkit/src/actions/service/GenerateServiceClassAndPatchPluginXmlActionBase.java new file mode 100644 index 000000000000..0d9a2d94f505 --- /dev/null +++ b/plugins/devkit/src/actions/service/GenerateServiceClassAndPatchPluginXmlActionBase.java @@ -0,0 +1,56 @@ +/* + * Copyright 2000-2017 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.jetbrains.idea.devkit.actions.service; + +import com.intellij.psi.*; +import com.intellij.psi.xml.XmlDocument; +import com.intellij.psi.xml.XmlFile; +import com.intellij.psi.xml.XmlTag; +import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.devkit.actions.GenerateClassAndPatchPluginXmlActionBase; + +import javax.swing.*; + +public abstract class GenerateServiceClassAndPatchPluginXmlActionBase extends GenerateClassAndPatchPluginXmlActionBase { + public GenerateServiceClassAndPatchPluginXmlActionBase(String text, String description, @Nullable Icon icon) { + super(text, description, icon); + } + + @Override + public void patchPluginXml(XmlFile pluginXml, PsiClass klass) throws IncorrectOperationException { + XmlDocument document = pluginXml.getDocument(); + if (document == null) { + // shouldn't happen (actions won't be visible when there's no plugin.xml) + return; + } + + XmlTag rootTag = document.getRootTag(); + if (rootTag != null && "idea-plugin".equals(rootTag.getName())) { + XmlTag extensions = rootTag.findFirstSubTag("extensions"); + if (extensions == null || !extensions.isPhysical()) { + extensions = (XmlTag)rootTag.add(rootTag.createChildTag("extensions", rootTag.getNamespace(), null, false)); + extensions.setAttribute("defaultExtensionNs", "com.intellij"); + } + + XmlTag service = (XmlTag)extensions.add(extensions.createChildTag(getTagName(), extensions.getNamespace(), null, false)); + service.setAttribute("serviceInterface", klass.getQualifiedName()); + service.setAttribute("serviceImplementation", klass.getQualifiedName()); + } + } + + protected abstract String getTagName(); +} diff --git a/plugins/devkit/src/actions/service/NewApplicationServiceAction.java b/plugins/devkit/src/actions/service/NewApplicationServiceAction.java new file mode 100644 index 000000000000..93af8bf7c2a9 --- /dev/null +++ b/plugins/devkit/src/actions/service/NewApplicationServiceAction.java @@ -0,0 +1,61 @@ +/* + * Copyright 2000-2017 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.jetbrains.idea.devkit.actions.service; + +import com.intellij.psi.PsiDirectory; +import org.jetbrains.idea.devkit.DevKitBundle; + +public class NewApplicationServiceAction extends GenerateServiceClassAndPatchPluginXmlActionBase { + public NewApplicationServiceAction() { + super(DevKitBundle.message("new.menu.application.service.text"), + DevKitBundle.message("new.menu.application.service.description"), null); + } + + @Override + protected String getClassNamePrompt() { + return DevKitBundle.message("new.application.service.prompt"); + } + + @Override + protected String getClassNamePromptTitle() { + return DevKitBundle.message("new.application.service.prompt.title"); + } + + @Override + protected String getClassTemplateName() { + return "ApplicationService.java"; + } + + @Override + protected String getErrorTitle() { + return DevKitBundle.message("new.application.service.error"); + } + + @Override + protected String getCommandName() { + return DevKitBundle.message("new.application.service.command"); + } + + @Override + protected String getActionName(PsiDirectory directory, String newName) { + return DevKitBundle.message("new.application.service.action.name", directory, newName); + } + + @Override + protected String getTagName() { + return "applicationService"; + } +} diff --git a/plugins/devkit/src/actions/service/NewProjectServiceAction.java b/plugins/devkit/src/actions/service/NewProjectServiceAction.java new file mode 100644 index 000000000000..4eb3a1c02d18 --- /dev/null +++ b/plugins/devkit/src/actions/service/NewProjectServiceAction.java @@ -0,0 +1,61 @@ +/* + * Copyright 2000-2017 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.jetbrains.idea.devkit.actions.service; + +import com.intellij.psi.PsiDirectory; +import org.jetbrains.idea.devkit.DevKitBundle; + +public class NewProjectServiceAction extends GenerateServiceClassAndPatchPluginXmlActionBase { + public NewProjectServiceAction() { + super(DevKitBundle.message("new.menu.project.service.text"), + DevKitBundle.message("new.menu.project.service.description"), null); + } + + @Override + protected String getClassNamePrompt() { + return DevKitBundle.message("new.project.service.prompt"); + } + + @Override + protected String getClassNamePromptTitle() { + return DevKitBundle.message("new.project.service.prompt.title"); + } + + @Override + protected String getClassTemplateName() { + return "ProjectService.java"; + } + + @Override + protected String getErrorTitle() { + return DevKitBundle.message("new.project.service.error"); + } + + @Override + protected String getCommandName() { + return DevKitBundle.message("new.project.service.command"); + } + + @Override + protected String getActionName(PsiDirectory directory, String newName) { + return DevKitBundle.message("new.project.service.action.name", directory, newName); + } + + @Override + protected String getTagName() { + return "projectService"; + } +} From d1b5377f83b6ee59bd00391473e7bd9f2053ee18 Mon Sep 17 00:00:00 2001 From: Yaroslav Pankratyev Date: Thu, 27 Jul 2017 12:25:18 +0300 Subject: [PATCH 04/27] Action for creating module service, typo fix --- plugins/devkit/resources/META-INF/plugin.xml | 4 +- .../fileTemplates/j2ee/ModuleService.java.ft | 12 ++++ .../j2ee/ModuleService.java.html | 11 ++++ .../idea/devkit/DevKitBundle.properties | 7 +++ .../service/NewModuleServiceAction.java | 61 +++++++++++++++++++ 5 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 plugins/devkit/resources/fileTemplates/j2ee/ModuleService.java.ft create mode 100644 plugins/devkit/resources/fileTemplates/j2ee/ModuleService.java.html create mode 100644 plugins/devkit/src/actions/service/NewModuleServiceAction.java diff --git a/plugins/devkit/resources/META-INF/plugin.xml b/plugins/devkit/resources/META-INF/plugin.xml index de11f690e201..e99e66196dee 100644 --- a/plugins/devkit/resources/META-INF/plugin.xml +++ b/plugins/devkit/resources/META-INF/plugin.xml @@ -224,8 +224,10 @@ - + + + + + + +
This is a built-in template used each time you create + a new IntelliJ Platform module-level service. +
+ + \ No newline at end of file diff --git a/plugins/devkit/resources/org/jetbrains/idea/devkit/DevKitBundle.properties b/plugins/devkit/resources/org/jetbrains/idea/devkit/DevKitBundle.properties index 23cd2eb251a2..24b8a29aec0a 100644 --- a/plugins/devkit/resources/org/jetbrains/idea/devkit/DevKitBundle.properties +++ b/plugins/devkit/resources/org/jetbrains/idea/devkit/DevKitBundle.properties @@ -113,6 +113,13 @@ new.project.service.command=Create Project Service new.project.service.prompt.title=New Project Service new.project.service.prompt=Enter new project service name: new.project.service.action.name=Creating new project service: {0}.{1} +new.menu.module.service.text=Module Service +new.menu.module.service.description=Create New Module Service +new.module.service.error=Cannot create module service +new.module.service.command=Create Module Service +new.module.service.prompt.title=New Module Service +new.module.service.prompt=Enter new module service name: +new.module.service.action.name=Creating new module service: {0}.{1} select.plugin.modules.to.patch=Select Plugin Modules to Patch diff --git a/plugins/devkit/src/actions/service/NewModuleServiceAction.java b/plugins/devkit/src/actions/service/NewModuleServiceAction.java new file mode 100644 index 000000000000..255ff8aa0727 --- /dev/null +++ b/plugins/devkit/src/actions/service/NewModuleServiceAction.java @@ -0,0 +1,61 @@ +/* + * Copyright 2000-2017 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.jetbrains.idea.devkit.actions.service; + +import com.intellij.psi.PsiDirectory; +import org.jetbrains.idea.devkit.DevKitBundle; + +public class NewModuleServiceAction extends GenerateServiceClassAndPatchPluginXmlActionBase { + public NewModuleServiceAction() { + super(DevKitBundle.message("new.menu.module.service.text"), + DevKitBundle.message("new.menu.module.service.description"), null); + } + + @Override + protected String getClassNamePrompt() { + return DevKitBundle.message("new.module.service.prompt"); + } + + @Override + protected String getClassNamePromptTitle() { + return DevKitBundle.message("new.module.service.prompt.title"); + } + + @Override + protected String getClassTemplateName() { + return "ModuleService.java"; + } + + @Override + protected String getErrorTitle() { + return DevKitBundle.message("new.module.service.error"); + } + + @Override + protected String getCommandName() { + return DevKitBundle.message("new.module.service.command"); + } + + @Override + protected String getActionName(PsiDirectory directory, String newName) { + return DevKitBundle.message("new.module.service.action.name", directory, newName); + } + + @Override + protected String getTagName() { + return "moduleService"; + } +} From ec6449084276f3e4d25bfd2fd060ab50724227b5 Mon Sep 17 00:00:00 2001 From: Yaroslav Pankratyev Date: Thu, 27 Jul 2017 12:44:02 +0300 Subject: [PATCH 05/27] Remove actions for creating components --- plugins/devkit/resources/META-INF/plugin.xml | 11 +--- ...enerateComponentExternalizationAction.java | 2 +- ...onentClassAndPatchPluginXmlActionBase.java | 38 ------------ .../NewApplicationComponentAction.java | 62 ------------------- .../component/NewModuleComponentAction.java | 62 ------------------- .../component/NewProjectComponentAction.java | 62 ------------------- 6 files changed, 2 insertions(+), 235 deletions(-) rename plugins/devkit/src/actions/{component => }/GenerateComponentExternalizationAction.java (99%) delete mode 100644 plugins/devkit/src/actions/component/GenerateComponentClassAndPatchPluginXmlActionBase.java delete mode 100644 plugins/devkit/src/actions/component/NewApplicationComponentAction.java delete mode 100644 plugins/devkit/src/actions/component/NewModuleComponentAction.java delete mode 100644 plugins/devkit/src/actions/component/NewProjectComponentAction.java diff --git a/plugins/devkit/resources/META-INF/plugin.xml b/plugins/devkit/resources/META-INF/plugin.xml index e99e66196dee..637ca4ac05b5 100644 --- a/plugins/devkit/resources/META-INF/plugin.xml +++ b/plugins/devkit/resources/META-INF/plugin.xml @@ -228,20 +228,11 @@ class="org.jetbrains.idea.devkit.actions.service.NewProjectServiceAction"/> - - - - - - - diff --git a/plugins/devkit/src/actions/component/GenerateComponentExternalizationAction.java b/plugins/devkit/src/actions/GenerateComponentExternalizationAction.java similarity index 99% rename from plugins/devkit/src/actions/component/GenerateComponentExternalizationAction.java rename to plugins/devkit/src/actions/GenerateComponentExternalizationAction.java index 065eb745aa87..7291f7196646 100644 --- a/plugins/devkit/src/actions/component/GenerateComponentExternalizationAction.java +++ b/plugins/devkit/src/actions/GenerateComponentExternalizationAction.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.idea.devkit.actions.component; +package org.jetbrains.idea.devkit.actions; import com.intellij.codeInsight.actions.CodeInsightEditorAction; import com.intellij.openapi.actionSystem.*; diff --git a/plugins/devkit/src/actions/component/GenerateComponentClassAndPatchPluginXmlActionBase.java b/plugins/devkit/src/actions/component/GenerateComponentClassAndPatchPluginXmlActionBase.java deleted file mode 100644 index d78aa4bb1a1c..000000000000 --- a/plugins/devkit/src/actions/component/GenerateComponentClassAndPatchPluginXmlActionBase.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2000-2017 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.jetbrains.idea.devkit.actions.component; - -import com.intellij.psi.PsiClass; -import com.intellij.psi.xml.XmlFile; -import com.intellij.util.IncorrectOperationException; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.idea.devkit.actions.GenerateClassAndPatchPluginXmlActionBase; -import org.jetbrains.idea.devkit.util.ComponentType; - -import javax.swing.Icon; - -public abstract class GenerateComponentClassAndPatchPluginXmlActionBase extends GenerateClassAndPatchPluginXmlActionBase { - public GenerateComponentClassAndPatchPluginXmlActionBase(String text, String description, @Nullable Icon icon) { - super(text, description, icon); - } - - protected abstract ComponentType getComponentType(); - - @Override - public void patchPluginXml(XmlFile pluginXml, PsiClass klass) throws IncorrectOperationException { - getComponentType().patchPluginXml(pluginXml, klass); - } -} diff --git a/plugins/devkit/src/actions/component/NewApplicationComponentAction.java b/plugins/devkit/src/actions/component/NewApplicationComponentAction.java deleted file mode 100644 index 0c1930c0a897..000000000000 --- a/plugins/devkit/src/actions/component/NewApplicationComponentAction.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2000-2017 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.jetbrains.idea.devkit.actions.component; - -import com.intellij.psi.PsiDirectory; -import org.jetbrains.idea.devkit.DevKitBundle; -import org.jetbrains.idea.devkit.util.ComponentType; - -public class NewApplicationComponentAction extends GenerateComponentClassAndPatchPluginXmlActionBase { - public NewApplicationComponentAction() { - super(DevKitBundle.message("new.menu.application.component.text"), - DevKitBundle.message("new.menu.application.component.description"), null); - } - - @Override - protected ComponentType getComponentType() { - return ComponentType.APPLICATION; - } - - @Override - protected String getErrorTitle() { - return DevKitBundle.message("new.application.component.error"); - } - - @Override - protected String getCommandName() { - return DevKitBundle.message("new.application.component.command"); - } - - @Override - protected String getClassNamePromptTitle() { - return DevKitBundle.message("new.application.component.prompt.title"); - } - - @Override - protected String getClassTemplateName() { - return "ApplicationComponent.java"; - } - - @Override - protected String getClassNamePrompt() { - return DevKitBundle.message("new.application.component.prompt"); - } - - @Override - protected String getActionName(PsiDirectory directory, String newName) { - return DevKitBundle.message("new.application.component.action.name", directory, newName); - } -} diff --git a/plugins/devkit/src/actions/component/NewModuleComponentAction.java b/plugins/devkit/src/actions/component/NewModuleComponentAction.java deleted file mode 100644 index 53d83fea6c72..000000000000 --- a/plugins/devkit/src/actions/component/NewModuleComponentAction.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2000-2017 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.jetbrains.idea.devkit.actions.component; - -import com.intellij.psi.PsiDirectory; -import org.jetbrains.idea.devkit.DevKitBundle; -import org.jetbrains.idea.devkit.util.ComponentType; - -public class NewModuleComponentAction extends GenerateComponentClassAndPatchPluginXmlActionBase { - public NewModuleComponentAction() { - super(DevKitBundle.message("new.menu.module.component.text"), - DevKitBundle.message("new.menu.module.component.description"), null); - } - - @Override - protected ComponentType getComponentType() { - return ComponentType.MODULE; - } - - @Override - protected String getErrorTitle() { - return DevKitBundle.message("new.module.component.error"); - } - - @Override - protected String getCommandName() { - return DevKitBundle.message("new.module.component.command"); - } - - @Override - protected String getClassNamePromptTitle() { - return DevKitBundle.message("new.module.component.prompt.title"); - } - - @Override - protected String getClassTemplateName() { - return "ModuleComponent.java"; - } - - @Override - protected String getClassNamePrompt() { - return DevKitBundle.message("new.module.component.prompt"); - } - - @Override - protected String getActionName(PsiDirectory directory, String newName) { - return DevKitBundle.message("new.module.component.action.name", directory, newName); - } -} diff --git a/plugins/devkit/src/actions/component/NewProjectComponentAction.java b/plugins/devkit/src/actions/component/NewProjectComponentAction.java deleted file mode 100644 index 2c895926a033..000000000000 --- a/plugins/devkit/src/actions/component/NewProjectComponentAction.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2000-2017 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.jetbrains.idea.devkit.actions.component; - -import com.intellij.psi.PsiDirectory; -import org.jetbrains.idea.devkit.DevKitBundle; -import org.jetbrains.idea.devkit.util.ComponentType; - -public class NewProjectComponentAction extends GenerateComponentClassAndPatchPluginXmlActionBase { - public NewProjectComponentAction() { - super(DevKitBundle.message("new.menu.project.component.text"), - DevKitBundle.message("new.menu.project.component.description"), null); - } - - @Override - protected ComponentType getComponentType() { - return ComponentType.PROJECT; - } - - @Override - protected String getErrorTitle() { - return DevKitBundle.message("new.project.component.error"); - } - - @Override - protected String getCommandName() { - return DevKitBundle.message("new.project.component.command"); - } - - @Override - protected String getClassNamePromptTitle() { - return DevKitBundle.message("new.project.component.prompt.title"); - } - - @Override - protected String getClassTemplateName() { - return "ProjectComponent.java"; - } - - @Override - protected String getClassNamePrompt() { - return DevKitBundle.message("new.project.component.prompt"); - } - - @Override - protected String getActionName(PsiDirectory directory, String newName) { - return DevKitBundle.message("new.project.component.action.name", directory, newName); - } -} From 937da4d4627056bd00532bccad480a5aa1b4a986 Mon Sep 17 00:00:00 2001 From: Yaroslav Pankratyev Date: Fri, 28 Jul 2017 12:16:35 +0300 Subject: [PATCH 06/27] Delete component templates --- .../j2ee/ApplicationComponent.java.ft | 26 ------------- .../j2ee/ApplicationComponent.java.html | 11 ------ .../j2ee/ModuleComponent.java.ft | 33 ----------------- .../j2ee/ModuleComponent.java.html | 11 ------ .../j2ee/ProjectComponent.java.ft | 37 ------------------- .../j2ee/ProjectComponent.java.html | 11 ------ 6 files changed, 129 deletions(-) delete mode 100644 plugins/devkit/resources/fileTemplates/j2ee/ApplicationComponent.java.ft delete mode 100644 plugins/devkit/resources/fileTemplates/j2ee/ApplicationComponent.java.html delete mode 100644 plugins/devkit/resources/fileTemplates/j2ee/ModuleComponent.java.ft delete mode 100644 plugins/devkit/resources/fileTemplates/j2ee/ModuleComponent.java.html delete mode 100644 plugins/devkit/resources/fileTemplates/j2ee/ProjectComponent.java.ft delete mode 100644 plugins/devkit/resources/fileTemplates/j2ee/ProjectComponent.java.html diff --git a/plugins/devkit/resources/fileTemplates/j2ee/ApplicationComponent.java.ft b/plugins/devkit/resources/fileTemplates/j2ee/ApplicationComponent.java.ft deleted file mode 100644 index 55c8d555e965..000000000000 --- a/plugins/devkit/resources/fileTemplates/j2ee/ApplicationComponent.java.ft +++ /dev/null @@ -1,26 +0,0 @@ -package ${PACKAGE_NAME}; - -import com.intellij.openapi.components.ApplicationComponent; -import org.jetbrains.annotations.NotNull; - -#parse("File Header.java") -public class ${NAME} implements ApplicationComponent { - public ${NAME}() { - } - - @Override - public void initComponent() { - // TODO: insert component initialization logic here - } - - @Override - public void disposeComponent() { - // TODO: insert component disposal logic here - } - - @Override - @NotNull - public String getComponentName() { - return "${NAME}"; - } -} diff --git a/plugins/devkit/resources/fileTemplates/j2ee/ApplicationComponent.java.html b/plugins/devkit/resources/fileTemplates/j2ee/ApplicationComponent.java.html deleted file mode 100644 index bdb4e6f18604..000000000000 --- a/plugins/devkit/resources/fileTemplates/j2ee/ApplicationComponent.java.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - -
This is a built-in template used each time you create - a new IntelliJ Platform application component. -
- - \ No newline at end of file diff --git a/plugins/devkit/resources/fileTemplates/j2ee/ModuleComponent.java.ft b/plugins/devkit/resources/fileTemplates/j2ee/ModuleComponent.java.ft deleted file mode 100644 index c82902992e11..000000000000 --- a/plugins/devkit/resources/fileTemplates/j2ee/ModuleComponent.java.ft +++ /dev/null @@ -1,33 +0,0 @@ -package ${PACKAGE_NAME}; - -import com.intellij.openapi.module.ModuleComponent; -import com.intellij.openapi.module.Module; -import org.jetbrains.annotations.NotNull; - -#parse("File Header.java") -public class ${NAME} implements ModuleComponent { - public ${NAME}(Module module) { - } - - @Override - public void initComponent() { - // TODO: insert component initialization logic here - } - - @Override - public void disposeComponent() { - // TODO: insert component disposal logic here - } - - @Override - @NotNull - public String getComponentName() { - return "${NAME}"; - } - - @Override - public void moduleAdded() { - // Invoked when the module corresponding to this component instance has been completely - // loaded and added to the project. - } -} diff --git a/plugins/devkit/resources/fileTemplates/j2ee/ModuleComponent.java.html b/plugins/devkit/resources/fileTemplates/j2ee/ModuleComponent.java.html deleted file mode 100644 index 851b80a35df1..000000000000 --- a/plugins/devkit/resources/fileTemplates/j2ee/ModuleComponent.java.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - -
This is a built-in template used each time you create - a new IntelliJ Platform module component. -
- - \ No newline at end of file diff --git a/plugins/devkit/resources/fileTemplates/j2ee/ProjectComponent.java.ft b/plugins/devkit/resources/fileTemplates/j2ee/ProjectComponent.java.ft deleted file mode 100644 index c894cbae60a7..000000000000 --- a/plugins/devkit/resources/fileTemplates/j2ee/ProjectComponent.java.ft +++ /dev/null @@ -1,37 +0,0 @@ -package ${PACKAGE_NAME}; - -import com.intellij.openapi.components.ProjectComponent; -import com.intellij.openapi.project.Project; -import org.jetbrains.annotations.NotNull; - -#parse("File Header.java") -public class ${NAME} implements ProjectComponent { - public ${NAME}(Project project) { - } - - @Override - public void initComponent() { - // TODO: insert component initialization logic here - } - - @Override - public void disposeComponent() { - // TODO: insert component disposal logic here - } - - @Override - @NotNull - public String getComponentName() { - return "${NAME}"; - } - - @Override - public void projectOpened() { - // called when project is opened - } - - @Override - public void projectClosed() { - // called when project is being closed - } -} diff --git a/plugins/devkit/resources/fileTemplates/j2ee/ProjectComponent.java.html b/plugins/devkit/resources/fileTemplates/j2ee/ProjectComponent.java.html deleted file mode 100644 index 4b1452f22fa9..000000000000 --- a/plugins/devkit/resources/fileTemplates/j2ee/ProjectComponent.java.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - -
This is a built-in template used each time you create - a new IntelliJ Platform project component. -
- - \ No newline at end of file From 64561528051af4b9917592371ed883fc17f8ffb0 Mon Sep 17 00:00:00 2001 From: Yaroslav Pankratyev Date: Mon, 31 Jul 2017 16:38:27 +0300 Subject: [PATCH 07/27] Working prototype of new service actions with separated interface and implementation --- ...ava.ft => ApplicationServiceClass.java.ft} | 0 .../j2ee/ApplicationServiceClass.java.html | 11 + .../j2ee/ApplicationServiceInterface.java.ft | 10 + ... => ApplicationServiceInterface.java.html} | 2 +- ...ice.java.ft => ModuleServiceClass.java.ft} | 0 .../j2ee/ModuleServiceClass.java.html | 11 + .../j2ee/ModuleServiceInterface.java.ft | 12 + ....html => ModuleServiceInterface.java.html} | 2 +- ...ce.java.ft => ProjectServiceClass.java.ft} | 0 .../j2ee/ProjectServiceClass.java.html | 11 + .../j2ee/ProjectServiceInterface.java.ft | 12 + ...html => ProjectServiceInterface.java.html} | 2 +- .../idea/devkit/DevKitBundle.properties | 45 +-- .../devkit/src/actions/DevkitActionsUtil.java | 125 +++++++++ .../actions/GeneratePluginClassAction.java | 95 +------ ...rviceClassAndPatchPluginXmlActionBase.java | 262 +++++++++++++++++- .../service/NewApplicationServiceAction.java | 48 ++-- .../service/NewModuleServiceAction.java | 48 ++-- .../service/NewProjectServiceAction.java | 48 ++-- .../src/actions/service/NewServiceDialog.form | 58 ++++ plugins/devkit/src/util/DescriptorUtil.java | 42 +-- 21 files changed, 597 insertions(+), 247 deletions(-) rename plugins/devkit/resources/fileTemplates/j2ee/{ApplicationService.java.ft => ApplicationServiceClass.java.ft} (100%) create mode 100644 plugins/devkit/resources/fileTemplates/j2ee/ApplicationServiceClass.java.html create mode 100644 plugins/devkit/resources/fileTemplates/j2ee/ApplicationServiceInterface.java.ft rename plugins/devkit/resources/fileTemplates/j2ee/{ProjectService.java.html => ApplicationServiceInterface.java.html} (78%) rename plugins/devkit/resources/fileTemplates/j2ee/{ModuleService.java.ft => ModuleServiceClass.java.ft} (100%) create mode 100644 plugins/devkit/resources/fileTemplates/j2ee/ModuleServiceClass.java.html create mode 100644 plugins/devkit/resources/fileTemplates/j2ee/ModuleServiceInterface.java.ft rename plugins/devkit/resources/fileTemplates/j2ee/{ApplicationService.java.html => ModuleServiceInterface.java.html} (79%) rename plugins/devkit/resources/fileTemplates/j2ee/{ProjectService.java.ft => ProjectServiceClass.java.ft} (100%) create mode 100644 plugins/devkit/resources/fileTemplates/j2ee/ProjectServiceClass.java.html create mode 100644 plugins/devkit/resources/fileTemplates/j2ee/ProjectServiceInterface.java.ft rename plugins/devkit/resources/fileTemplates/j2ee/{ModuleService.java.html => ProjectServiceInterface.java.html} (78%) create mode 100644 plugins/devkit/src/actions/DevkitActionsUtil.java create mode 100644 plugins/devkit/src/actions/service/NewServiceDialog.form diff --git a/plugins/devkit/resources/fileTemplates/j2ee/ApplicationService.java.ft b/plugins/devkit/resources/fileTemplates/j2ee/ApplicationServiceClass.java.ft similarity index 100% rename from plugins/devkit/resources/fileTemplates/j2ee/ApplicationService.java.ft rename to plugins/devkit/resources/fileTemplates/j2ee/ApplicationServiceClass.java.ft diff --git a/plugins/devkit/resources/fileTemplates/j2ee/ApplicationServiceClass.java.html b/plugins/devkit/resources/fileTemplates/j2ee/ApplicationServiceClass.java.html new file mode 100644 index 000000000000..ffad651eb417 --- /dev/null +++ b/plugins/devkit/resources/fileTemplates/j2ee/ApplicationServiceClass.java.html @@ -0,0 +1,11 @@ + + + + + + +
This is a built-in template used each time you create + a new IntelliJ Platform application-level service class (without a separated service interface). +
+ + \ No newline at end of file diff --git a/plugins/devkit/resources/fileTemplates/j2ee/ApplicationServiceInterface.java.ft b/plugins/devkit/resources/fileTemplates/j2ee/ApplicationServiceInterface.java.ft new file mode 100644 index 000000000000..1f215b44299b --- /dev/null +++ b/plugins/devkit/resources/fileTemplates/j2ee/ApplicationServiceInterface.java.ft @@ -0,0 +1,10 @@ +package ${PACKAGE_NAME}; + +import com.intellij.openapi.components.ServiceManager; + +#parse("File Header.java") +public interface ${NAME} { + static ${NAME} getInstance() { + return ServiceManager.getService(${NAME}.class); + } +} diff --git a/plugins/devkit/resources/fileTemplates/j2ee/ProjectService.java.html b/plugins/devkit/resources/fileTemplates/j2ee/ApplicationServiceInterface.java.html similarity index 78% rename from plugins/devkit/resources/fileTemplates/j2ee/ProjectService.java.html rename to plugins/devkit/resources/fileTemplates/j2ee/ApplicationServiceInterface.java.html index 08c20e5ee4df..99788adf1c40 100644 --- a/plugins/devkit/resources/fileTemplates/j2ee/ProjectService.java.html +++ b/plugins/devkit/resources/fileTemplates/j2ee/ApplicationServiceInterface.java.html @@ -3,7 +3,7 @@
This is a built-in template used each time you create - a new IntelliJ Platform project-level service. + a new IntelliJ Platform application-level service interface.
diff --git a/plugins/devkit/resources/fileTemplates/j2ee/ModuleService.java.ft b/plugins/devkit/resources/fileTemplates/j2ee/ModuleServiceClass.java.ft similarity index 100% rename from plugins/devkit/resources/fileTemplates/j2ee/ModuleService.java.ft rename to plugins/devkit/resources/fileTemplates/j2ee/ModuleServiceClass.java.ft diff --git a/plugins/devkit/resources/fileTemplates/j2ee/ModuleServiceClass.java.html b/plugins/devkit/resources/fileTemplates/j2ee/ModuleServiceClass.java.html new file mode 100644 index 000000000000..b57d160f2c42 --- /dev/null +++ b/plugins/devkit/resources/fileTemplates/j2ee/ModuleServiceClass.java.html @@ -0,0 +1,11 @@ + + + + + + +
This is a built-in template used each time you create + a new IntelliJ Platform module-level service class (without a separated service interface). +
+ + \ No newline at end of file diff --git a/plugins/devkit/resources/fileTemplates/j2ee/ModuleServiceInterface.java.ft b/plugins/devkit/resources/fileTemplates/j2ee/ModuleServiceInterface.java.ft new file mode 100644 index 000000000000..2b702ec47610 --- /dev/null +++ b/plugins/devkit/resources/fileTemplates/j2ee/ModuleServiceInterface.java.ft @@ -0,0 +1,12 @@ +package ${PACKAGE_NAME}; + +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleServiceManager; +import org.jetbrains.annotations.NotNull; + +#parse("File Header.java") +public interface ${NAME} { + static ${NAME} getInstance(@NotNull Module module) { + return ModuleServiceManager.getService(module, ${NAME}.class); + } +} diff --git a/plugins/devkit/resources/fileTemplates/j2ee/ApplicationService.java.html b/plugins/devkit/resources/fileTemplates/j2ee/ModuleServiceInterface.java.html similarity index 79% rename from plugins/devkit/resources/fileTemplates/j2ee/ApplicationService.java.html rename to plugins/devkit/resources/fileTemplates/j2ee/ModuleServiceInterface.java.html index 5a27684631df..8e656c2a8a8d 100644 --- a/plugins/devkit/resources/fileTemplates/j2ee/ApplicationService.java.html +++ b/plugins/devkit/resources/fileTemplates/j2ee/ModuleServiceInterface.java.html @@ -3,7 +3,7 @@
This is a built-in template used each time you create - a new IntelliJ Platform application-level service. + a new IntelliJ Platform module-level service interface.
diff --git a/plugins/devkit/resources/fileTemplates/j2ee/ProjectService.java.ft b/plugins/devkit/resources/fileTemplates/j2ee/ProjectServiceClass.java.ft similarity index 100% rename from plugins/devkit/resources/fileTemplates/j2ee/ProjectService.java.ft rename to plugins/devkit/resources/fileTemplates/j2ee/ProjectServiceClass.java.ft diff --git a/plugins/devkit/resources/fileTemplates/j2ee/ProjectServiceClass.java.html b/plugins/devkit/resources/fileTemplates/j2ee/ProjectServiceClass.java.html new file mode 100644 index 000000000000..38247e5efaa1 --- /dev/null +++ b/plugins/devkit/resources/fileTemplates/j2ee/ProjectServiceClass.java.html @@ -0,0 +1,11 @@ + + + + + + +
This is a built-in template used each time you create + a new IntelliJ Platform project-level service class (without a separated service interface). +
+ + \ No newline at end of file diff --git a/plugins/devkit/resources/fileTemplates/j2ee/ProjectServiceInterface.java.ft b/plugins/devkit/resources/fileTemplates/j2ee/ProjectServiceInterface.java.ft new file mode 100644 index 000000000000..9d945b83275d --- /dev/null +++ b/plugins/devkit/resources/fileTemplates/j2ee/ProjectServiceInterface.java.ft @@ -0,0 +1,12 @@ +package ${PACKAGE_NAME}; + +import com.intellij.openapi.components.ServiceManager; +import com.intellij.openapi.project.Project; +import org.jetbrains.annotations.NotNull; + +#parse("File Header.java") +public interface ${NAME} { + static ${NAME} getInstance(@NotNull Project project) { + return ServiceManager.getService(project, ${NAME}.class); + } +} diff --git a/plugins/devkit/resources/fileTemplates/j2ee/ModuleService.java.html b/plugins/devkit/resources/fileTemplates/j2ee/ProjectServiceInterface.java.html similarity index 78% rename from plugins/devkit/resources/fileTemplates/j2ee/ModuleService.java.html rename to plugins/devkit/resources/fileTemplates/j2ee/ProjectServiceInterface.java.html index 1d124016e2d7..e9f5e2ac5f6f 100644 --- a/plugins/devkit/resources/fileTemplates/j2ee/ModuleService.java.html +++ b/plugins/devkit/resources/fileTemplates/j2ee/ProjectServiceInterface.java.html @@ -3,7 +3,7 @@
This is a built-in template used each time you create - a new IntelliJ Platform module-level service. + a new IntelliJ Platform project-level service interface.
diff --git a/plugins/devkit/resources/org/jetbrains/idea/devkit/DevKitBundle.properties b/plugins/devkit/resources/org/jetbrains/idea/devkit/DevKitBundle.properties index 24b8a29aec0a..2966b7e0bdc6 100644 --- a/plugins/devkit/resources/org/jetbrains/idea/devkit/DevKitBundle.properties +++ b/plugins/devkit/resources/org/jetbrains/idea/devkit/DevKitBundle.properties @@ -45,12 +45,14 @@ sdk.type.incorrect.common=Wrong SDK type for plugin module error.file.not.found=File not Found error.file.not.found.message=File ''{0}'' not found error.no.plugin.xml=No plugin.xml file found -error.plugin.xml.readonly=The plugin.xml file is read-only +error.plugin.xml.readonly=Read-only plugin.xml files cannot be processed: {0} +error.cannot.create.service.class=Cannot create service class #run configurations run.configuration.no.module.specified=No plugin module specified for configuration run.configuration.title=Plugin run.configuration.type.description=Plugin Sandbox Environment + #Misc info.message=Info new.action.id=&Action ID: @@ -71,55 +73,36 @@ new.action.keyboard.second=Second: new.action.keyboard.clear=X new.action.keyboard.clear.tooltip=Clear shortcut command.implement.externalizable=Implement Externalizable + new.menu.action.text=Action new.menu.action.description=Create New Action new.action.error=Cannot create action new.action.command=Create Action new.action.action.name=Creating new action: {0}.{1} new.action.dialog.title=New Action + new.menu.application.component.text=Application Component -new.menu.application.component.description=Create New Application Component new.application.component.error=Cannot create application component -new.application.component.command=Create Application Component -new.application.component.prompt.title=New Application Component -new.application.component.prompt=Enter new application component name: -new.application.component.action.name=Creating new application component: {0}.{1} new.menu.module.component.text=Module Component -new.menu.module.component.description=Create New Module Component -new.module.component.error=Cannot create module component -new.module.component.command=Create Module Component -new.module.component.prompt.title=New Module Component -new.module.component.prompt=Enter new module component name: -new.module.component.action.name=Creating new module component: {0}.{1} new.menu.project.component.text=Project Component -new.menu.project.component.description=Create New Project Component new.project.component.error=Cannot create project component new.project.component.command=Create Project Component -new.project.component.prompt.title=New Project Component -new.project.component.prompt=Enter new project component name: -new.project.component.action.name=Creating new project component: {0}.{1} + +new.service.class.action.name=Creating new service class: {0} +new.service.dialog.interface=Service &interface: +new.service.dialog.implementation=Service i&mplementation: +new.service.dialog.class=Service &class: +new.service.dialog.separate=&Separate interface from implementation new.menu.application.service.text=Application Service new.menu.application.service.description=Create New Application Service -new.application.service.error=Cannot create application service -new.application.service.command=Create Application Service -new.application.service.prompt.title=New Application Service -new.application.service.prompt=Enter new application service name: -new.application.service.action.name=Creating new application service: {0}.{1} +new.application.service.dialog.title=Create Application Service new.menu.project.service.text=Project Service new.menu.project.service.description=Create New Project Service -new.project.service.error=Cannot create project service -new.project.service.command=Create Project Service -new.project.service.prompt.title=New Project Service -new.project.service.prompt=Enter new project service name: -new.project.service.action.name=Creating new project service: {0}.{1} +new.project.service.dialog.title=Create Project Service new.menu.module.service.text=Module Service new.menu.module.service.description=Create New Module Service -new.module.service.error=Cannot create module service -new.module.service.command=Create Module Service -new.module.service.prompt.title=New Module Service -new.module.service.prompt=Enter new module service name: -new.module.service.action.name=Creating new module service: {0}.{1} +new.module.service.dialog.title=Create New Module Service select.plugin.modules.to.patch=Select Plugin Modules to Patch diff --git a/plugins/devkit/src/actions/DevkitActionsUtil.java b/plugins/devkit/src/actions/DevkitActionsUtil.java new file mode 100644 index 000000000000..8a6325d3cee4 --- /dev/null +++ b/plugins/devkit/src/actions/DevkitActionsUtil.java @@ -0,0 +1,125 @@ +/* + * Copyright 2000-2017 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.jetbrains.idea.devkit.actions; + +import com.intellij.ide.actions.CreateFileAction; +import com.intellij.openapi.actionSystem.Presentation; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.roots.OrderEntry; +import com.intellij.openapi.roots.ProjectFileIndex; +import com.intellij.openapi.roots.ProjectRootManager; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.JavaDirectoryService; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiDirectory; +import com.intellij.psi.xml.XmlFile; +import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.devkit.DevKitBundle; +import org.jetbrains.idea.devkit.module.PluginModuleType; +import org.jetbrains.idea.devkit.util.ChooseModulesDialog; + +import java.util.*; + +public final class DevkitActionsUtil { + private DevkitActionsUtil() { + } + + // length == 1 is important to make MyInputValidator close the dialog when + // module selection is canceled. That's some weird interface actually... + public static final PsiClass[] CANCELED = new PsiClass[1]; + + + public static PsiClass[] createSinglePluginClass(String name, String classTemplateName, PsiDirectory directory, + Set pluginXmlsToPatch, Presentation templatePresentation) { + Project project = directory.getProject(); + Module module = getModule(directory); + + if (module != null) { + addPluginModule(module, pluginXmlsToPatch); + + if (pluginXmlsToPatch.isEmpty()) { + List candidateModules = PluginModuleType.getCandidateModules(module); + Iterator it = candidateModules.iterator(); + while (it.hasNext()) { + Module m = it.next(); + if (PluginModuleType.getPluginXml(m) == null) it.remove(); + } + + if (candidateModules.size() == 1) { + addPluginModule(candidateModules.get(0), pluginXmlsToPatch); + } + else { + ChooseModulesDialog dialog = new ChooseModulesDialog(project, candidateModules, templatePresentation.getDescription()); + if (!dialog.showAndGet()) { + // create() should return CANCELED now + return CANCELED; + } + else { + List modules = dialog.getSelectedModules(); + for (Module m : modules) { + addPluginModule(m, pluginXmlsToPatch); + } + } + } + } + } + + if (pluginXmlsToPatch.size() == 0) { + throw new IncorrectOperationException(DevKitBundle.message("error.no.plugin.xml")); + } + + if (name.contains(".")) { + String[] names = name.split("\\."); + for (int i = 0; i < names.length - 1; i++) { + directory = CreateFileAction.findOrCreateSubdirectory(directory, names[i]); + } + name = names[names.length - 1]; + } + + PsiClass klass = JavaDirectoryService.getInstance().createClass(directory, name, classTemplateName); + return new PsiClass[] {klass}; + } + + @Nullable + private static Module getModule(PsiDirectory dir) { + Project project = dir.getProject(); + final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(project).getFileIndex(); + + final VirtualFile vFile = dir.getVirtualFile(); + if (fileIndex.isInLibrarySource(vFile) || fileIndex.isInLibraryClasses(vFile)) { + final List orderEntries = fileIndex.getOrderEntriesForFile(vFile); + if (orderEntries.isEmpty()) { + return null; + } + Set modules = new HashSet<>(); + for (OrderEntry orderEntry : orderEntries) { + modules.add(orderEntry.getOwnerModule()); + } + final Module[] candidates = modules.toArray(new Module[modules.size()]); + Arrays.sort(candidates, ModuleManager.getInstance(project).moduleDependencyComparator()); + return candidates[0]; + } + return fileIndex.getModuleForFile(vFile); + } + + private static void addPluginModule(Module module, Set pluginXmlsToPatch) { + final XmlFile pluginXml = PluginModuleType.getPluginXml(module); + if (pluginXml != null) pluginXmlsToPatch.add(pluginXml); + } +} diff --git a/plugins/devkit/src/actions/GeneratePluginClassAction.java b/plugins/devkit/src/actions/GeneratePluginClassAction.java index 94686bd7e91f..5a87a1d4c072 100644 --- a/plugins/devkit/src/actions/GeneratePluginClassAction.java +++ b/plugins/devkit/src/actions/GeneratePluginClassAction.java @@ -21,41 +21,28 @@ import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.LangDataKeys; import com.intellij.openapi.actionSystem.Presentation; import com.intellij.openapi.module.Module; -import com.intellij.openapi.module.ModuleManager; import com.intellij.openapi.project.Project; -import com.intellij.openapi.roots.OrderEntry; import com.intellij.openapi.roots.ProjectFileIndex; import com.intellij.openapi.roots.ProjectRootManager; -import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.JavaDirectoryService; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiElement; import com.intellij.psi.xml.XmlFile; -import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.idea.devkit.DevKitBundle; -import org.jetbrains.idea.devkit.module.PluginModuleType; -import org.jetbrains.idea.devkit.util.ChooseModulesDialog; import org.jetbrains.idea.devkit.util.DescriptorUtil; import org.jetbrains.idea.devkit.util.PsiUtil; import org.jetbrains.jps.model.java.JavaModuleSourceRootTypes; import javax.swing.*; -import java.util.*; +import java.util.HashSet; +import java.util.Set; -/** - * @author yole - */ public abstract class GeneratePluginClassAction extends CreateElementActionBase implements DescriptorUtil.Patcher { protected final Set myFilesToPatch = new HashSet<>(); - // length == 1 is important to make MyInputValidator close the dialog when - // module selection is canceled. That's some weird interface actually... - private static final PsiElement[] CANCELED = new PsiElement[1]; - public GeneratePluginClassAction(String text, String description, @Nullable Icon icon) { super(text, description, icon); } @@ -64,7 +51,7 @@ public abstract class GeneratePluginClassAction extends CreateElementActionBase protected final PsiElement[] invokeDialog(Project project, PsiDirectory directory) { try { final PsiElement[] psiElements = invokeDialogImpl(project, directory); - return psiElements == CANCELED ? PsiElement.EMPTY_ARRAY : psiElements; + return psiElements == DevkitActionsUtil.CANCELED ? PsiElement.EMPTY_ARRAY : psiElements; } finally { myFilesToPatch.clear(); @@ -73,11 +60,6 @@ public abstract class GeneratePluginClassAction extends CreateElementActionBase protected abstract PsiElement[] invokeDialogImpl(Project project, PsiDirectory directory); - private void addPluginModule(Module module) { - final XmlFile pluginXml = PluginModuleType.getPluginXml(module); - if (pluginXml != null) myFilesToPatch.add(pluginXml); - } - public void update(final AnActionEvent e) { super.update(e); @@ -105,28 +87,6 @@ public abstract class GeneratePluginClassAction extends CreateElementActionBase } } - @Nullable - protected static Module getModule(PsiDirectory dir) { - Project project = dir.getProject(); - final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(project).getFileIndex(); - - final VirtualFile vFile = dir.getVirtualFile(); - if (fileIndex.isInLibrarySource(vFile) || fileIndex.isInLibraryClasses(vFile)) { - final List orderEntries = fileIndex.getOrderEntriesForFile(vFile); - if (orderEntries.isEmpty()) { - return null; - } - Set modules = new HashSet<>(); - for (OrderEntry orderEntry : orderEntries) { - modules.add(orderEntry.getOwnerModule()); - } - final Module[] candidates = modules.toArray(new Module[modules.size()]); - Arrays.sort(candidates, ModuleManager.getInstance(project).moduleDependencyComparator()); - return candidates[0]; - } - return fileIndex.getModuleForFile(vFile); - } - @Override public boolean startInWriteAction() { return false; @@ -134,52 +94,11 @@ public abstract class GeneratePluginClassAction extends CreateElementActionBase @NotNull protected PsiElement[] create(String newName, PsiDirectory directory) throws Exception { - final Project project = directory.getProject(); - final Module module = getModule(directory); + PsiClass[] createdClass = + DevkitActionsUtil.createSinglePluginClass(newName, getClassTemplateName(), directory, myFilesToPatch, getTemplatePresentation()); - if (module != null) { - addPluginModule(module); - - if (myFilesToPatch.isEmpty()) { - final List candidateModules = PluginModuleType.getCandidateModules(module); - final Iterator it = candidateModules.iterator(); - while (it.hasNext()) { - Module m = it.next(); - if (PluginModuleType.getPluginXml(m) == null) it.remove(); - } - - if (candidateModules.size() == 1) { - addPluginModule(candidateModules.get(0)); - } - else { - final ChooseModulesDialog dialog = new ChooseModulesDialog(project, candidateModules, getTemplatePresentation().getDescription()); - if (!dialog.showAndGet()) { - // create() should return CANCELED now - return CANCELED; - } - else { - final List modules = dialog.getSelectedModules(); - for (Module m : modules) { - addPluginModule(m); - } - } - } - } - } - - if (myFilesToPatch.size() == 0) { - throw new IncorrectOperationException(DevKitBundle.message("error.no.plugin.xml")); - } - if (myFilesToPatch.size() == 0) { - // user canceled module selection - return CANCELED; - } - - final PsiClass klass = JavaDirectoryService.getInstance().createClass(directory, newName, getClassTemplateName()); - - DescriptorUtil.patchPluginXml(this, klass, myFilesToPatch.toArray(new XmlFile[myFilesToPatch.size()])); - - return new PsiElement[]{klass}; + DescriptorUtil.patchPluginXml(this, createdClass[0], myFilesToPatch.toArray(new XmlFile[myFilesToPatch.size()])); + return createdClass; } @NonNls diff --git a/plugins/devkit/src/actions/service/GenerateServiceClassAndPatchPluginXmlActionBase.java b/plugins/devkit/src/actions/service/GenerateServiceClassAndPatchPluginXmlActionBase.java index 0d9a2d94f505..8a42c5615630 100644 --- a/plugins/devkit/src/actions/service/GenerateServiceClassAndPatchPluginXmlActionBase.java +++ b/plugins/devkit/src/actions/service/GenerateServiceClassAndPatchPluginXmlActionBase.java @@ -15,23 +15,56 @@ */ package org.jetbrains.idea.devkit.actions.service; +import com.intellij.ide.IdeView; +import com.intellij.ide.actions.CreateInDirectoryActionBase; +import com.intellij.ide.actions.ElementCreator; +import com.intellij.ide.fileTemplates.JavaTemplateUtil; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.LangDataKeys; +import com.intellij.openapi.application.WriteAction; +import com.intellij.openapi.application.WriteActionAware; +import com.intellij.openapi.command.CommandProcessor; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.DialogWrapper; import com.intellij.psi.*; import com.intellij.psi.xml.XmlDocument; import com.intellij.psi.xml.XmlFile; import com.intellij.psi.xml.XmlTag; -import com.intellij.util.IncorrectOperationException; +import com.intellij.ui.DocumentAdapter; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.idea.devkit.actions.GenerateClassAndPatchPluginXmlActionBase; +import org.jetbrains.idea.devkit.DevKitBundle; +import org.jetbrains.idea.devkit.actions.DevkitActionsUtil; +import org.jetbrains.idea.devkit.util.DescriptorUtil; import javax.swing.*; +import javax.swing.event.DocumentEvent; +import java.util.HashSet; +import java.util.Set; -public abstract class GenerateServiceClassAndPatchPluginXmlActionBase extends GenerateClassAndPatchPluginXmlActionBase { - public GenerateServiceClassAndPatchPluginXmlActionBase(String text, String description, @Nullable Icon icon) { - super(text, description, icon); +public abstract class GenerateServiceClassAndPatchPluginXmlActionBase extends CreateInDirectoryActionBase implements WriteActionAware { + private final Set myFilesToPatch = new HashSet<>(); + + public GenerateServiceClassAndPatchPluginXmlActionBase(String text, String description) { + super(text, description, null); } @Override - public void patchPluginXml(XmlFile pluginXml, PsiClass klass) throws IncorrectOperationException { + public boolean startInWriteAction() { + return false; + } + + + private void patchPluginXmls(@NotNull XmlFile[] pluginXmls, @Nullable PsiClass serviceInterface, @NotNull PsiClass serviceImplementation) { + DescriptorUtil.checkPluginXmlsWritable(serviceImplementation.getProject(), pluginXmls); + WriteAction.run(() -> CommandProcessor.getInstance().runUndoTransparentAction(() -> { + for (XmlFile pluginXml : pluginXmls) { + patchPluginXml(pluginXml, serviceInterface, serviceImplementation); + } + })); + } + + private void patchPluginXml(XmlFile pluginXml, @Nullable PsiClass serviceInterface, @NotNull PsiClass serviceImplementation) { XmlDocument document = pluginXml.getDocument(); if (document == null) { // shouldn't happen (actions won't be visible when there's no plugin.xml) @@ -42,15 +75,224 @@ public abstract class GenerateServiceClassAndPatchPluginXmlActionBase extends Ge if (rootTag != null && "idea-plugin".equals(rootTag.getName())) { XmlTag extensions = rootTag.findFirstSubTag("extensions"); if (extensions == null || !extensions.isPhysical()) { - extensions = (XmlTag)rootTag.add(rootTag.createChildTag("extensions", rootTag.getNamespace(), null, false)); + extensions = rootTag.addSubTag(rootTag.createChildTag("extensions", rootTag.getNamespace(), null, false), false); extensions.setAttribute("defaultExtensionNs", "com.intellij"); } - XmlTag service = (XmlTag)extensions.add(extensions.createChildTag(getTagName(), extensions.getNamespace(), null, false)); - service.setAttribute("serviceInterface", klass.getQualifiedName()); - service.setAttribute("serviceImplementation", klass.getQualifiedName()); + XmlTag serviceTag = extensions.createChildTag(getTagName(), rootTag.getNamespace(), null, false); + if (serviceInterface != null) { + serviceTag.setAttribute("serviceInterface", serviceInterface.getQualifiedName()); + } + serviceTag.setAttribute("serviceImplementation", serviceImplementation.getQualifiedName()); + extensions.addSubTag(serviceTag, false); + } + } + + @Override + public final void actionPerformed(final AnActionEvent e) { + IdeView view = e.getData(LangDataKeys.IDE_VIEW); + if (view == null) { + return; + } + + Project project = e.getProject(); + + PsiDirectory dir = view.getOrChooseDirectory(); + if (dir == null) return; + + String errorTitle = DevKitBundle.message("error.cannot.create.service.class"); + MyServiceClassCreator onlyImplementationElementCreator = new MyServiceClassCreator( + project, errorTitle, dir, getOnlyImplementationTemplateName()); + MyServiceClassCreator implementationElementCreator = new MyServiceClassCreator( + project, errorTitle, dir, JavaTemplateUtil.INTERNAL_CLASS_TEMPLATE_NAME); + MyServiceClassCreator interfaceElementCreator = new MyServiceClassCreator(project, errorTitle, dir, getInterfaceTemplateName()); + + invokeDialog(project, onlyImplementationElementCreator, implementationElementCreator, interfaceElementCreator); + PsiElement[] createdElements = handleCreatedElements(onlyImplementationElementCreator, + implementationElementCreator, interfaceElementCreator); + for (PsiElement createdElement : createdElements) { + view.selectElement(createdElement); + } + } + + private void invokeDialog(Project project, MyServiceClassCreator onlyImplementationElementCreator, + MyServiceClassCreator implementationElementCreator, + MyServiceClassCreator interfaceElementCreator) { + DialogWrapper dialog = new NewServiceDialog(project, onlyImplementationElementCreator, + interfaceElementCreator, implementationElementCreator); + dialog.show(); + } + + @SuppressWarnings("ConstantConditions") //noinspection ConstantConditions - anonymous classes are not possible here, so no NPE + @NotNull + private PsiElement[] handleCreatedElements(MyServiceClassCreator onlyImplementationElementCreator, + MyServiceClassCreator implementationElementCreator, + MyServiceClassCreator interfaceElementCreator) { + XmlFile[] pluginXmls = myFilesToPatch.toArray(new XmlFile[myFilesToPatch.size()]); + PsiClass createdImplementation = implementationElementCreator.getCreatedClass(); + PsiClass createdInterface = interfaceElementCreator.getCreatedClass(); + boolean separatedInterface = createdImplementation != null && createdInterface != null; + if (separatedInterface) { + WriteAction.run(() -> CommandProcessor.getInstance().runUndoTransparentAction(() -> { + JavaPsiFacade facade = JavaPsiFacade.getInstance(createdImplementation.getProject()); + PsiElementFactory factory = facade.getElementFactory(); + PsiJavaCodeReferenceElement interfaceReference = + factory.createReferenceElementByFQClassName(createdInterface.getQualifiedName(), createdImplementation.getResolveScope()); + createdImplementation.getImplementsList().add(interfaceReference); + })); + + patchPluginXmls(pluginXmls, createdInterface, createdImplementation); + return new PsiElement[]{createdInterface, createdImplementation}; + } + else { + PsiClass createdOnlyImplementation = onlyImplementationElementCreator.getCreatedClass(); + if (createdOnlyImplementation != null) { + patchPluginXmls(pluginXmls, null, createdOnlyImplementation); + return new PsiElement[]{createdOnlyImplementation}; + } + return PsiElement.EMPTY_ARRAY; } } protected abstract String getTagName(); + + protected abstract String getOnlyImplementationTemplateName(); + protected abstract String getInterfaceTemplateName(); + + protected abstract String getDialogTitle(); + + + private class NewServiceDialog extends DialogWrapper { + private final MyServiceClassCreator myOnlyImplementationElementCreator; + private final MyServiceClassCreator myInterfaceElementCreator; + private final MyServiceClassCreator myImplementationElementCreator; + + private JPanel myTopPanel; + + private JTextField myServiceNameTextField; + private JCheckBox mySeparateServiceInterfaceCheckbox; + private JTextField myServiceImplementationTextField; + private JLabel myServiceNameLabel; + + private boolean myAdjusting = false; + private boolean myNeedAdjust = true; + + NewServiceDialog(@Nullable Project project, + MyServiceClassCreator onlyImplementationElementCreator, + MyServiceClassCreator interfaceElementCreator, + MyServiceClassCreator implementationElementCreator) { + super(project); + + setOKActionEnabled(false); + setTitle(getDialogTitle()); + + myOnlyImplementationElementCreator = onlyImplementationElementCreator; + myInterfaceElementCreator = interfaceElementCreator; + myImplementationElementCreator = implementationElementCreator; + + mySeparateServiceInterfaceCheckbox.addActionListener(e -> { + if (mySeparateServiceInterfaceCheckbox.isSelected()) { + myServiceImplementationTextField.setEnabled(true); + myServiceNameLabel.setText(DevKitBundle.message("new.service.dialog.interface")); + } else { + myServiceImplementationTextField.setEnabled(false); + myServiceNameLabel.setText(DevKitBundle.message("new.service.dialog.class")); + } + adjustServiceImplementationTextField(); + }); + + myServiceNameTextField.getDocument().addDocumentListener(new DocumentAdapter() { + @Override + protected void textChanged(DocumentEvent e) { + setOKActionEnabled(myServiceNameTextField.getText().length() > 0); + adjustServiceImplementationTextField(); + } + }); + myServiceImplementationTextField.getDocument().addDocumentListener(new DocumentAdapter() { + @Override + protected void textChanged(DocumentEvent e) { + if (!myAdjusting) { + myNeedAdjust = false; + } + } + }); + + init(); + } + + private void adjustServiceImplementationTextField() { + if (!mySeparateServiceInterfaceCheckbox.isSelected()) { + myAdjusting = true; + myServiceImplementationTextField.setText(""); + myAdjusting = false; + } else if (myNeedAdjust) { + myAdjusting = true; + myServiceImplementationTextField.setText("impl." + myServiceNameTextField.getText() + "Impl"); + myAdjusting = false; + } + } + + @Override + protected void doOKAction() { + if (mySeparateServiceInterfaceCheckbox.isSelected()) { + // separated interface and implementation + String serviceInterface = myServiceNameTextField.getText().trim(); + String serviceImplementation = myServiceImplementationTextField.getText().trim(); + if (myImplementationElementCreator.canClose(serviceImplementation) && myInterfaceElementCreator.canClose(serviceInterface)) { + close(OK_EXIT_CODE); + } + } else { + // only implementation + String serviceImplementation = myServiceNameTextField.getText().trim(); + if (myOnlyImplementationElementCreator.canClose(serviceImplementation)) { + close(OK_EXIT_CODE); + } + } + } + + @Nullable + @Override + protected JComponent createCenterPanel() { + return myTopPanel; + } + } + + private class MyServiceClassCreator extends ElementCreator { + private final PsiDirectory myDirectory; + private final String myClassTemplateName; + + private PsiClass myCreatedClass = null; + + MyServiceClassCreator(Project project, String errorTitle, PsiDirectory directory, String classTemplateName) { + super(project, errorTitle); + myDirectory = directory; + myClassTemplateName = classTemplateName; + } + + @NotNull + protected PsiElement[] create(String newName) throws Exception { + return DevkitActionsUtil.createSinglePluginClass( + newName, myClassTemplateName, myDirectory, myFilesToPatch, getTemplatePresentation()); + } + + @Override + protected String getActionName(String newName) { + return DevKitBundle.message("new.service.class.action.name", newName); + } + + public boolean canClose(String inputString) { + PsiElement[] createdElements = tryCreate(inputString); + if (createdElements.length > 0) { + myCreatedClass = (PsiClass)createdElements[0]; // cast is safe since create() returns array of single PsiClass + return true; + } else { + return false; + } + } + + @Nullable + public PsiClass getCreatedClass() { + return myCreatedClass; + } + } } diff --git a/plugins/devkit/src/actions/service/NewApplicationServiceAction.java b/plugins/devkit/src/actions/service/NewApplicationServiceAction.java index 93af8bf7c2a9..c42f6c1d0015 100644 --- a/plugins/devkit/src/actions/service/NewApplicationServiceAction.java +++ b/plugins/devkit/src/actions/service/NewApplicationServiceAction.java @@ -15,47 +15,31 @@ */ package org.jetbrains.idea.devkit.actions.service; -import com.intellij.psi.PsiDirectory; import org.jetbrains.idea.devkit.DevKitBundle; public class NewApplicationServiceAction extends GenerateServiceClassAndPatchPluginXmlActionBase { public NewApplicationServiceAction() { super(DevKitBundle.message("new.menu.application.service.text"), - DevKitBundle.message("new.menu.application.service.description"), null); - } - - @Override - protected String getClassNamePrompt() { - return DevKitBundle.message("new.application.service.prompt"); - } - - @Override - protected String getClassNamePromptTitle() { - return DevKitBundle.message("new.application.service.prompt.title"); - } - - @Override - protected String getClassTemplateName() { - return "ApplicationService.java"; - } - - @Override - protected String getErrorTitle() { - return DevKitBundle.message("new.application.service.error"); - } - - @Override - protected String getCommandName() { - return DevKitBundle.message("new.application.service.command"); - } - - @Override - protected String getActionName(PsiDirectory directory, String newName) { - return DevKitBundle.message("new.application.service.action.name", directory, newName); + DevKitBundle.message("new.menu.application.service.description")); } @Override protected String getTagName() { return "applicationService"; } + + @Override + protected String getOnlyImplementationTemplateName() { + return "ApplicationServiceClass.java"; + } + + @Override + protected String getInterfaceTemplateName() { + return "ApplicationServiceInterface.java"; + } + + @Override + protected String getDialogTitle() { + return DevKitBundle.message("new.application.service.dialog.title"); + } } diff --git a/plugins/devkit/src/actions/service/NewModuleServiceAction.java b/plugins/devkit/src/actions/service/NewModuleServiceAction.java index 255ff8aa0727..b2dfffba584c 100644 --- a/plugins/devkit/src/actions/service/NewModuleServiceAction.java +++ b/plugins/devkit/src/actions/service/NewModuleServiceAction.java @@ -15,47 +15,31 @@ */ package org.jetbrains.idea.devkit.actions.service; -import com.intellij.psi.PsiDirectory; import org.jetbrains.idea.devkit.DevKitBundle; public class NewModuleServiceAction extends GenerateServiceClassAndPatchPluginXmlActionBase { public NewModuleServiceAction() { super(DevKitBundle.message("new.menu.module.service.text"), - DevKitBundle.message("new.menu.module.service.description"), null); - } - - @Override - protected String getClassNamePrompt() { - return DevKitBundle.message("new.module.service.prompt"); - } - - @Override - protected String getClassNamePromptTitle() { - return DevKitBundle.message("new.module.service.prompt.title"); - } - - @Override - protected String getClassTemplateName() { - return "ModuleService.java"; - } - - @Override - protected String getErrorTitle() { - return DevKitBundle.message("new.module.service.error"); - } - - @Override - protected String getCommandName() { - return DevKitBundle.message("new.module.service.command"); - } - - @Override - protected String getActionName(PsiDirectory directory, String newName) { - return DevKitBundle.message("new.module.service.action.name", directory, newName); + DevKitBundle.message("new.menu.module.service.description")); } @Override protected String getTagName() { return "moduleService"; } + + @Override + protected String getOnlyImplementationTemplateName() { + return "ModuleServiceClass.java"; + } + + @Override + protected String getInterfaceTemplateName() { + return "ModuleServiceInterface.java"; + } + + @Override + protected String getDialogTitle() { + return DevKitBundle.message("new.module.service.dialog.title"); + } } diff --git a/plugins/devkit/src/actions/service/NewProjectServiceAction.java b/plugins/devkit/src/actions/service/NewProjectServiceAction.java index 4eb3a1c02d18..d0ffd885698d 100644 --- a/plugins/devkit/src/actions/service/NewProjectServiceAction.java +++ b/plugins/devkit/src/actions/service/NewProjectServiceAction.java @@ -15,47 +15,31 @@ */ package org.jetbrains.idea.devkit.actions.service; -import com.intellij.psi.PsiDirectory; import org.jetbrains.idea.devkit.DevKitBundle; public class NewProjectServiceAction extends GenerateServiceClassAndPatchPluginXmlActionBase { public NewProjectServiceAction() { super(DevKitBundle.message("new.menu.project.service.text"), - DevKitBundle.message("new.menu.project.service.description"), null); - } - - @Override - protected String getClassNamePrompt() { - return DevKitBundle.message("new.project.service.prompt"); - } - - @Override - protected String getClassNamePromptTitle() { - return DevKitBundle.message("new.project.service.prompt.title"); - } - - @Override - protected String getClassTemplateName() { - return "ProjectService.java"; - } - - @Override - protected String getErrorTitle() { - return DevKitBundle.message("new.project.service.error"); - } - - @Override - protected String getCommandName() { - return DevKitBundle.message("new.project.service.command"); - } - - @Override - protected String getActionName(PsiDirectory directory, String newName) { - return DevKitBundle.message("new.project.service.action.name", directory, newName); + DevKitBundle.message("new.menu.project.service.description")); } @Override protected String getTagName() { return "projectService"; } + + @Override + protected String getOnlyImplementationTemplateName() { + return "ProjectServiceClass.java"; + } + + @Override + protected String getInterfaceTemplateName() { + return "ProjectServiceInterface.java"; + } + + @Override + protected String getDialogTitle() { + return DevKitBundle.message("new.project.service.dialog.title"); + } } diff --git a/plugins/devkit/src/actions/service/NewServiceDialog.form b/plugins/devkit/src/actions/service/NewServiceDialog.form new file mode 100644 index 000000000000..0fa5b25141a3 --- /dev/null +++ b/plugins/devkit/src/actions/service/NewServiceDialog.form @@ -0,0 +1,58 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/plugins/devkit/src/util/DescriptorUtil.java b/plugins/devkit/src/util/DescriptorUtil.java index 57b8b2e876ea..5b75ad03a987 100644 --- a/plugins/devkit/src/util/DescriptorUtil.java +++ b/plugins/devkit/src/util/DescriptorUtil.java @@ -17,6 +17,7 @@ package org.jetbrains.idea.devkit.util; import com.intellij.openapi.application.WriteAction; import com.intellij.openapi.module.Module; +import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.ReadonlyStatusHandler; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiClass; @@ -33,10 +34,17 @@ import org.jetbrains.idea.devkit.DevKitBundle; import org.jetbrains.idea.devkit.dom.IdeaPlugin; import org.jetbrains.idea.devkit.module.PluginModuleType; -/** - * @author swr - */ -public class DescriptorUtil { +import java.util.Arrays; +import java.util.stream.Stream; + +public final class DescriptorUtil { + private DescriptorUtil() { + } + + public interface Patcher { + void patchPluginXml(XmlFile pluginXml, PsiClass klass) throws IncorrectOperationException; + } + public static void processComponents(XmlTag root, ComponentType.Processor processor) { final ComponentType[] types = ComponentType.values(); @@ -52,22 +60,8 @@ public class DescriptorUtil { } } - public interface Patcher { - void patchPluginXml(XmlFile pluginXml, PsiClass klass) throws IncorrectOperationException; - } - public static void patchPluginXml(Patcher patcher, PsiClass klass, XmlFile... pluginXmls) throws IncorrectOperationException { - final VirtualFile[] files = new VirtualFile[pluginXmls.length]; - int i = 0; - for (XmlFile pluginXml : pluginXmls) { - files[i++] = pluginXml.getVirtualFile(); - } - - final ReadonlyStatusHandler readonlyStatusHandler = ReadonlyStatusHandler.getInstance(klass.getProject()); - final ReadonlyStatusHandler.OperationStatus status = readonlyStatusHandler.ensureFilesWritable(files); - if (status.hasReadonlyFiles()) { - throw new IncorrectOperationException(DevKitBundle.message("error.plugin.xml.readonly")); - } + checkPluginXmlsWritable(klass.getProject(), pluginXmls); WriteAction.run((ThrowableRunnable)() -> { for (XmlFile pluginXml : pluginXmls) { @@ -76,6 +70,16 @@ public class DescriptorUtil { }); } + public static void checkPluginXmlsWritable(Project project, XmlFile... pluginXmls) { + VirtualFile[] files = Stream.of(pluginXmls).map(PsiFile::getVirtualFile).toArray(VirtualFile[]::new); + + final ReadonlyStatusHandler readonlyStatusHandler = ReadonlyStatusHandler.getInstance(project); + final ReadonlyStatusHandler.OperationStatus status = readonlyStatusHandler.ensureFilesWritable(files); + if (status.hasReadonlyFiles()) { + throw new IncorrectOperationException(DevKitBundle.message("error.plugin.xml.readonly", Arrays.toString(status.getReadonlyFiles()))); + } + } + @Nullable public static String getPluginId(Module plugin) { assert PluginModuleType.isOfType(plugin); From ad3e65327031755a7aa9875ce947479bdc8a4344 Mon Sep 17 00:00:00 2001 From: Yaroslav Pankratyev Date: Tue, 1 Aug 2017 11:49:04 +0300 Subject: [PATCH 08/27] Fix formatting of new service class and plugin.xml, refactoring --- .../idea/devkit/DevKitBundle.properties | 3 + ...rviceClassAndPatchPluginXmlActionBase.java | 62 +++++++++++-------- 2 files changed, 38 insertions(+), 27 deletions(-) diff --git a/plugins/devkit/resources/org/jetbrains/idea/devkit/DevKitBundle.properties b/plugins/devkit/resources/org/jetbrains/idea/devkit/DevKitBundle.properties index 2966b7e0bdc6..5a87b5edc30c 100644 --- a/plugins/devkit/resources/org/jetbrains/idea/devkit/DevKitBundle.properties +++ b/plugins/devkit/resources/org/jetbrains/idea/devkit/DevKitBundle.properties @@ -46,6 +46,7 @@ error.file.not.found=File not Found error.file.not.found.message=File ''{0}'' not found error.no.plugin.xml=No plugin.xml file found error.plugin.xml.readonly=Read-only plugin.xml files cannot be processed: {0} +error.cannot.process.plugin.xml=Cannot process plugin descriptor file: {0} error.cannot.create.service.class=Cannot create service class #run configurations @@ -89,6 +90,8 @@ new.project.component.error=Cannot create project component new.project.component.command=Create Project Component new.service.class.action.name=Creating new service class: {0} +new.service.adding.interface.to.class=Making service class implement service interface +new.service.patch.plugin.xml.action.name=Registering service in plugin descriptor new.service.dialog.interface=Service &interface: new.service.dialog.implementation=Service i&mplementation: new.service.dialog.class=Service &class: diff --git a/plugins/devkit/src/actions/service/GenerateServiceClassAndPatchPluginXmlActionBase.java b/plugins/devkit/src/actions/service/GenerateServiceClassAndPatchPluginXmlActionBase.java index 8a42c5615630..fa2936e8907f 100644 --- a/plugins/devkit/src/actions/service/GenerateServiceClassAndPatchPluginXmlActionBase.java +++ b/plugins/devkit/src/actions/service/GenerateServiceClassAndPatchPluginXmlActionBase.java @@ -21,20 +21,24 @@ import com.intellij.ide.actions.ElementCreator; import com.intellij.ide.fileTemplates.JavaTemplateUtil; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.LangDataKeys; -import com.intellij.openapi.application.WriteAction; import com.intellij.openapi.application.WriteActionAware; -import com.intellij.openapi.command.CommandProcessor; +import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.psi.*; -import com.intellij.psi.xml.XmlDocument; import com.intellij.psi.xml.XmlFile; import com.intellij.psi.xml.XmlTag; import com.intellij.ui.DocumentAdapter; +import com.intellij.util.IncorrectOperationException; +import com.intellij.util.xml.DomFileElement; +import com.intellij.util.xml.DomManager; +import com.intellij.xml.util.IncludedXmlTag; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.devkit.DevKitBundle; import org.jetbrains.idea.devkit.actions.DevkitActionsUtil; +import org.jetbrains.idea.devkit.dom.Extensions; +import org.jetbrains.idea.devkit.dom.IdeaPlugin; import org.jetbrains.idea.devkit.util.DescriptorUtil; import javax.swing.*; @@ -55,37 +59,35 @@ public abstract class GenerateServiceClassAndPatchPluginXmlActionBase extends Cr } - private void patchPluginXmls(@NotNull XmlFile[] pluginXmls, @Nullable PsiClass serviceInterface, @NotNull PsiClass serviceImplementation) { + private void patchPluginXmls(@NotNull XmlFile[] pluginXmls, @Nullable PsiClass serviceInterface, + @NotNull PsiClass serviceImplementation) { DescriptorUtil.checkPluginXmlsWritable(serviceImplementation.getProject(), pluginXmls); - WriteAction.run(() -> CommandProcessor.getInstance().runUndoTransparentAction(() -> { + WriteCommandAction.runWriteCommandAction(serviceImplementation.getProject(), + DevKitBundle.message("new.service.patch.plugin.xml.action.name"), null, () -> { for (XmlFile pluginXml : pluginXmls) { patchPluginXml(pluginXml, serviceInterface, serviceImplementation); } - })); + }); } private void patchPluginXml(XmlFile pluginXml, @Nullable PsiClass serviceInterface, @NotNull PsiClass serviceImplementation) { - XmlDocument document = pluginXml.getDocument(); - if (document == null) { - // shouldn't happen (actions won't be visible when there's no plugin.xml) - return; + DomFileElement fileElement = DomManager.getDomManager(pluginXml.getProject()).getFileElement(pluginXml, IdeaPlugin.class); + if (fileElement == null) { + throw new IncorrectOperationException(DevKitBundle.message("error.cannot.process.plugin.xml", pluginXml)); } - XmlTag rootTag = document.getRootTag(); - if (rootTag != null && "idea-plugin".equals(rootTag.getName())) { - XmlTag extensions = rootTag.findFirstSubTag("extensions"); - if (extensions == null || !extensions.isPhysical()) { - extensions = rootTag.addSubTag(rootTag.createChildTag("extensions", rootTag.getNamespace(), null, false), false); - extensions.setAttribute("defaultExtensionNs", "com.intellij"); - } + IdeaPlugin ideaPlugin = fileElement.getRootElement(); + Extensions targetExtensions = ideaPlugin.getExtensions().stream() + .filter(extensions -> !(extensions instanceof IncludedXmlTag)) + .filter(extensions -> Extensions.DEFAULT_PREFIX.equals(extensions.getDefaultExtensionNs().getStringValue())) + .findAny() + .orElseGet(() -> ideaPlugin.addExtensions()); - XmlTag serviceTag = extensions.createChildTag(getTagName(), rootTag.getNamespace(), null, false); - if (serviceInterface != null) { - serviceTag.setAttribute("serviceInterface", serviceInterface.getQualifiedName()); - } - serviceTag.setAttribute("serviceImplementation", serviceImplementation.getQualifiedName()); - extensions.addSubTag(serviceTag, false); + XmlTag serviceTag = targetExtensions.addExtension(Extensions.DEFAULT_PREFIX + "." + getTagName()).getXmlTag(); + if (serviceInterface != null) { + serviceTag.setAttribute("serviceInterface", serviceInterface.getQualifiedName()); } + serviceTag.setAttribute("serviceImplementation", serviceImplementation.getQualifiedName()); } @Override @@ -133,14 +135,14 @@ public abstract class GenerateServiceClassAndPatchPluginXmlActionBase extends Cr PsiClass createdInterface = interfaceElementCreator.getCreatedClass(); boolean separatedInterface = createdImplementation != null && createdInterface != null; if (separatedInterface) { - WriteAction.run(() -> CommandProcessor.getInstance().runUndoTransparentAction(() -> { - JavaPsiFacade facade = JavaPsiFacade.getInstance(createdImplementation.getProject()); + Project project = createdImplementation.getProject(); + WriteCommandAction.runWriteCommandAction(project, DevKitBundle.message("new.service.adding.interface.to.class"), null, () -> { + JavaPsiFacade facade = JavaPsiFacade.getInstance(project); PsiElementFactory factory = facade.getElementFactory(); PsiJavaCodeReferenceElement interfaceReference = factory.createReferenceElementByFQClassName(createdInterface.getQualifiedName(), createdImplementation.getResolveScope()); createdImplementation.getImplementsList().add(interfaceReference); - })); - + }); patchPluginXmls(pluginXmls, createdInterface, createdImplementation); return new PsiElement[]{createdInterface, createdImplementation}; } @@ -232,6 +234,12 @@ public abstract class GenerateServiceClassAndPatchPluginXmlActionBase extends Cr } } + @Nullable + @Override + public JComponent getPreferredFocusedComponent() { + return myServiceNameTextField; + } + @Override protected void doOKAction() { if (mySeparateServiceInterfaceCheckbox.isSelected()) { From c38a4b5e8dfc3b96751746af3a20bd97341b243d Mon Sep 17 00:00:00 2001 From: Yaroslav Pankratyev Date: Tue, 1 Aug 2017 14:23:42 +0300 Subject: [PATCH 09/27] Fix undoing service creating --- ...rviceClassAndPatchPluginXmlActionBase.java | 32 +++++++++++++++++-- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/plugins/devkit/src/actions/service/GenerateServiceClassAndPatchPluginXmlActionBase.java b/plugins/devkit/src/actions/service/GenerateServiceClassAndPatchPluginXmlActionBase.java index fa2936e8907f..34d003a0d3d9 100644 --- a/plugins/devkit/src/actions/service/GenerateServiceClassAndPatchPluginXmlActionBase.java +++ b/plugins/devkit/src/actions/service/GenerateServiceClassAndPatchPluginXmlActionBase.java @@ -21,6 +21,8 @@ import com.intellij.ide.actions.ElementCreator; import com.intellij.ide.fileTemplates.JavaTemplateUtil; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.LangDataKeys; +import com.intellij.openapi.application.Result; +import com.intellij.openapi.application.RunResult; import com.intellij.openapi.application.WriteActionAware; import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.project.Project; @@ -62,8 +64,8 @@ public abstract class GenerateServiceClassAndPatchPluginXmlActionBase extends Cr private void patchPluginXmls(@NotNull XmlFile[] pluginXmls, @Nullable PsiClass serviceInterface, @NotNull PsiClass serviceImplementation) { DescriptorUtil.checkPluginXmlsWritable(serviceImplementation.getProject(), pluginXmls); - WriteCommandAction.runWriteCommandAction(serviceImplementation.getProject(), - DevKitBundle.message("new.service.patch.plugin.xml.action.name"), null, () -> { + executeGlobalUndoWriteCommandAction(serviceImplementation.getProject(), + DevKitBundle.message("new.service.patch.plugin.xml.action.name"), () -> { for (XmlFile pluginXml : pluginXmls) { patchPluginXml(pluginXml, serviceInterface, serviceImplementation); } @@ -136,13 +138,14 @@ public abstract class GenerateServiceClassAndPatchPluginXmlActionBase extends Cr boolean separatedInterface = createdImplementation != null && createdInterface != null; if (separatedInterface) { Project project = createdImplementation.getProject(); - WriteCommandAction.runWriteCommandAction(project, DevKitBundle.message("new.service.adding.interface.to.class"), null, () -> { + executeGlobalUndoWriteCommandAction(project, DevKitBundle.message("new.service.adding.interface.to.class"), () -> { JavaPsiFacade facade = JavaPsiFacade.getInstance(project); PsiElementFactory factory = facade.getElementFactory(); PsiJavaCodeReferenceElement interfaceReference = factory.createReferenceElementByFQClassName(createdInterface.getQualifiedName(), createdImplementation.getResolveScope()); createdImplementation.getImplementsList().add(interfaceReference); }); + patchPluginXmls(pluginXmls, createdInterface, createdImplementation); return new PsiElement[]{createdInterface, createdImplementation}; } @@ -156,6 +159,29 @@ public abstract class GenerateServiceClassAndPatchPluginXmlActionBase extends Cr } } + private static void executeGlobalUndoWriteCommandAction(Project project, String commandName, Runnable runnable) { + RunResult result = new WriteCommandAction(project, commandName) { + @Override + protected void run(@NotNull Result result) throws Throwable { + runnable.run(); + } + + @Override + protected boolean isGlobalUndoAction() { + return true; + } + }.execute(); + + if (result.hasException()) { + Throwable e = result.getThrowable(); + if (e instanceof RuntimeException) { + throw (RuntimeException)e; + } else { + throw new RuntimeException(e); + } + } + } + protected abstract String getTagName(); protected abstract String getOnlyImplementationTemplateName(); From 3c9186016e63ee6d1bd60cf61dd7d27493b6d591 Mon Sep 17 00:00:00 2001 From: Yaroslav Pankratyev Date: Tue, 1 Aug 2017 16:57:11 +0300 Subject: [PATCH 10/27] Better undo support for create service actions --- .../intellij/ide/actions/ElementCreator.java | 7 +- .../idea/devkit/DevKitBundle.properties | 6 +- .../devkit/src/actions/DevkitActionsUtil.java | 2 + ...rviceClassAndPatchPluginXmlActionBase.java | 242 ++++++++++-------- 4 files changed, 140 insertions(+), 117 deletions(-) diff --git a/platform/lang-api/src/com/intellij/ide/actions/ElementCreator.java b/platform/lang-api/src/com/intellij/ide/actions/ElementCreator.java index 5d1ac159d889..995dcbf0b838 100644 --- a/platform/lang-api/src/com/intellij/ide/actions/ElementCreator.java +++ b/platform/lang-api/src/com/intellij/ide/actions/ElementCreator.java @@ -99,10 +99,15 @@ public abstract class ElementCreator implements WriteActionAware { private void handleException(Exception t) { LOG.info(t); + String errorMessage = getErrorMessage(t); + Messages.showMessageDialog(myProject, errorMessage, myErrorTitle, Messages.getErrorIcon()); + } + + public static String getErrorMessage(Throwable t) { String errorMessage = CreateElementActionBase.filterMessage(t.getMessage()); if (errorMessage == null || errorMessage.length() == 0) { errorMessage = t.toString(); } - Messages.showMessageDialog(myProject, errorMessage, myErrorTitle, Messages.getErrorIcon()); + return errorMessage; } } diff --git a/plugins/devkit/resources/org/jetbrains/idea/devkit/DevKitBundle.properties b/plugins/devkit/resources/org/jetbrains/idea/devkit/DevKitBundle.properties index 5a87b5edc30c..4ca2e046b1ef 100644 --- a/plugins/devkit/resources/org/jetbrains/idea/devkit/DevKitBundle.properties +++ b/plugins/devkit/resources/org/jetbrains/idea/devkit/DevKitBundle.properties @@ -47,7 +47,7 @@ error.file.not.found.message=File ''{0}'' not found error.no.plugin.xml=No plugin.xml file found error.plugin.xml.readonly=Read-only plugin.xml files cannot be processed: {0} error.cannot.process.plugin.xml=Cannot process plugin descriptor file: {0} -error.cannot.create.service.class=Cannot create service class +error.cannot.create.service.class=Cannot Create Service Class #run configurations run.configuration.no.module.specified=No plugin module specified for configuration @@ -89,9 +89,7 @@ new.menu.project.component.text=Project Component new.project.component.error=Cannot create project component new.project.component.command=Create Project Component -new.service.class.action.name=Creating new service class: {0} -new.service.adding.interface.to.class=Making service class implement service interface -new.service.patch.plugin.xml.action.name=Registering service in plugin descriptor +new.service.class.action.name=Creating new service new.service.dialog.interface=Service &interface: new.service.dialog.implementation=Service i&mplementation: new.service.dialog.class=Service &class: diff --git a/plugins/devkit/src/actions/DevkitActionsUtil.java b/plugins/devkit/src/actions/DevkitActionsUtil.java index 8a6325d3cee4..0b0c645011f0 100644 --- a/plugins/devkit/src/actions/DevkitActionsUtil.java +++ b/plugins/devkit/src/actions/DevkitActionsUtil.java @@ -29,6 +29,7 @@ import com.intellij.psi.PsiClass; import com.intellij.psi.PsiDirectory; import com.intellij.psi.xml.XmlFile; import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.devkit.DevKitBundle; import org.jetbrains.idea.devkit.module.PluginModuleType; @@ -45,6 +46,7 @@ public final class DevkitActionsUtil { public static final PsiClass[] CANCELED = new PsiClass[1]; + @NotNull public static PsiClass[] createSinglePluginClass(String name, String classTemplateName, PsiDirectory directory, Set pluginXmlsToPatch, Presentation templatePresentation) { Project project = directory.getProject(); diff --git a/plugins/devkit/src/actions/service/GenerateServiceClassAndPatchPluginXmlActionBase.java b/plugins/devkit/src/actions/service/GenerateServiceClassAndPatchPluginXmlActionBase.java index 34d003a0d3d9..6046d03b41c8 100644 --- a/plugins/devkit/src/actions/service/GenerateServiceClassAndPatchPluginXmlActionBase.java +++ b/plugins/devkit/src/actions/service/GenerateServiceClassAndPatchPluginXmlActionBase.java @@ -15,6 +15,8 @@ */ package org.jetbrains.idea.devkit.actions.service; +import com.intellij.CommonBundle; +import com.intellij.ide.IdeBundle; import com.intellij.ide.IdeView; import com.intellij.ide.actions.CreateInDirectoryActionBase; import com.intellij.ide.actions.ElementCreator; @@ -24,9 +26,13 @@ import com.intellij.openapi.actionSystem.LangDataKeys; import com.intellij.openapi.application.Result; import com.intellij.openapi.application.RunResult; import com.intellij.openapi.application.WriteActionAware; +import com.intellij.openapi.command.UndoConfirmationPolicy; import com.intellij.openapi.command.WriteCommandAction; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; import com.intellij.psi.xml.XmlFile; import com.intellij.psi.xml.XmlTag; @@ -47,6 +53,7 @@ import javax.swing.*; import javax.swing.event.DocumentEvent; import java.util.HashSet; import java.util.Set; +import java.util.concurrent.Callable; public abstract class GenerateServiceClassAndPatchPluginXmlActionBase extends CreateInDirectoryActionBase implements WriteActionAware { private final Set myFilesToPatch = new HashSet<>(); @@ -64,12 +71,9 @@ public abstract class GenerateServiceClassAndPatchPluginXmlActionBase extends Cr private void patchPluginXmls(@NotNull XmlFile[] pluginXmls, @Nullable PsiClass serviceInterface, @NotNull PsiClass serviceImplementation) { DescriptorUtil.checkPluginXmlsWritable(serviceImplementation.getProject(), pluginXmls); - executeGlobalUndoWriteCommandAction(serviceImplementation.getProject(), - DevKitBundle.message("new.service.patch.plugin.xml.action.name"), () -> { - for (XmlFile pluginXml : pluginXmls) { - patchPluginXml(pluginXml, serviceInterface, serviceImplementation); - } - }); + for (XmlFile pluginXml : pluginXmls) { + patchPluginXml(pluginXml, serviceInterface, serviceImplementation); + } } private void patchPluginXml(XmlFile pluginXml, @Nullable PsiClass serviceInterface, @NotNull PsiClass serviceImplementation) { @@ -104,82 +108,22 @@ public abstract class GenerateServiceClassAndPatchPluginXmlActionBase extends Cr PsiDirectory dir = view.getOrChooseDirectory(); if (dir == null) return; - String errorTitle = DevKitBundle.message("error.cannot.create.service.class"); - MyServiceClassCreator onlyImplementationElementCreator = new MyServiceClassCreator( - project, errorTitle, dir, getOnlyImplementationTemplateName()); - MyServiceClassCreator implementationElementCreator = new MyServiceClassCreator( - project, errorTitle, dir, JavaTemplateUtil.INTERNAL_CLASS_TEMPLATE_NAME); - MyServiceClassCreator interfaceElementCreator = new MyServiceClassCreator(project, errorTitle, dir, getInterfaceTemplateName()); + ServiceCreator serviceCreator = new ServiceCreator(dir, getInterfaceTemplateName(), getOnlyImplementationTemplateName()); + PsiClass[] createdClasses = invokeDialog(project, serviceCreator); + if (createdClasses == null) { + return; + } - invokeDialog(project, onlyImplementationElementCreator, implementationElementCreator, interfaceElementCreator); - PsiElement[] createdElements = handleCreatedElements(onlyImplementationElementCreator, - implementationElementCreator, interfaceElementCreator); - for (PsiElement createdElement : createdElements) { - view.selectElement(createdElement); + for (PsiClass createdClass : createdClasses) { + view.selectElement(createdClass); } } - private void invokeDialog(Project project, MyServiceClassCreator onlyImplementationElementCreator, - MyServiceClassCreator implementationElementCreator, - MyServiceClassCreator interfaceElementCreator) { - DialogWrapper dialog = new NewServiceDialog(project, onlyImplementationElementCreator, - interfaceElementCreator, implementationElementCreator); + @Nullable + private PsiClass[] invokeDialog(Project project, ServiceCreator serviceCreator) { + DialogWrapper dialog = new NewServiceDialog(project, serviceCreator); dialog.show(); - } - - @SuppressWarnings("ConstantConditions") //noinspection ConstantConditions - anonymous classes are not possible here, so no NPE - @NotNull - private PsiElement[] handleCreatedElements(MyServiceClassCreator onlyImplementationElementCreator, - MyServiceClassCreator implementationElementCreator, - MyServiceClassCreator interfaceElementCreator) { - XmlFile[] pluginXmls = myFilesToPatch.toArray(new XmlFile[myFilesToPatch.size()]); - PsiClass createdImplementation = implementationElementCreator.getCreatedClass(); - PsiClass createdInterface = interfaceElementCreator.getCreatedClass(); - boolean separatedInterface = createdImplementation != null && createdInterface != null; - if (separatedInterface) { - Project project = createdImplementation.getProject(); - executeGlobalUndoWriteCommandAction(project, DevKitBundle.message("new.service.adding.interface.to.class"), () -> { - JavaPsiFacade facade = JavaPsiFacade.getInstance(project); - PsiElementFactory factory = facade.getElementFactory(); - PsiJavaCodeReferenceElement interfaceReference = - factory.createReferenceElementByFQClassName(createdInterface.getQualifiedName(), createdImplementation.getResolveScope()); - createdImplementation.getImplementsList().add(interfaceReference); - }); - - patchPluginXmls(pluginXmls, createdInterface, createdImplementation); - return new PsiElement[]{createdInterface, createdImplementation}; - } - else { - PsiClass createdOnlyImplementation = onlyImplementationElementCreator.getCreatedClass(); - if (createdOnlyImplementation != null) { - patchPluginXmls(pluginXmls, null, createdOnlyImplementation); - return new PsiElement[]{createdOnlyImplementation}; - } - return PsiElement.EMPTY_ARRAY; - } - } - - private static void executeGlobalUndoWriteCommandAction(Project project, String commandName, Runnable runnable) { - RunResult result = new WriteCommandAction(project, commandName) { - @Override - protected void run(@NotNull Result result) throws Throwable { - runnable.run(); - } - - @Override - protected boolean isGlobalUndoAction() { - return true; - } - }.execute(); - - if (result.hasException()) { - Throwable e = result.getThrowable(); - if (e instanceof RuntimeException) { - throw (RuntimeException)e; - } else { - throw new RuntimeException(e); - } - } + return serviceCreator.getCreatedClasses(); } protected abstract String getTagName(); @@ -191,9 +135,7 @@ public abstract class GenerateServiceClassAndPatchPluginXmlActionBase extends Cr private class NewServiceDialog extends DialogWrapper { - private final MyServiceClassCreator myOnlyImplementationElementCreator; - private final MyServiceClassCreator myInterfaceElementCreator; - private final MyServiceClassCreator myImplementationElementCreator; + private final ServiceCreator myServiceCreator; private JPanel myTopPanel; @@ -205,18 +147,13 @@ public abstract class GenerateServiceClassAndPatchPluginXmlActionBase extends Cr private boolean myAdjusting = false; private boolean myNeedAdjust = true; - NewServiceDialog(@Nullable Project project, - MyServiceClassCreator onlyImplementationElementCreator, - MyServiceClassCreator interfaceElementCreator, - MyServiceClassCreator implementationElementCreator) { + NewServiceDialog(@Nullable Project project, ServiceCreator serviceCreator) { super(project); setOKActionEnabled(false); setTitle(getDialogTitle()); - myOnlyImplementationElementCreator = onlyImplementationElementCreator; - myInterfaceElementCreator = interfaceElementCreator; - myImplementationElementCreator = implementationElementCreator; + myServiceCreator = serviceCreator; mySeparateServiceInterfaceCheckbox.addActionListener(e -> { if (mySeparateServiceInterfaceCheckbox.isSelected()) { @@ -272,18 +209,30 @@ public abstract class GenerateServiceClassAndPatchPluginXmlActionBase extends Cr // separated interface and implementation String serviceInterface = myServiceNameTextField.getText().trim(); String serviceImplementation = myServiceImplementationTextField.getText().trim(); - if (myImplementationElementCreator.canClose(serviceImplementation) && myInterfaceElementCreator.canClose(serviceInterface)) { + + if (checkInput(serviceInterface) && checkInput(serviceImplementation) && + myServiceCreator.createInterfaceAndImplementation(serviceInterface, serviceImplementation)) { close(OK_EXIT_CODE); } } else { // only implementation - String serviceImplementation = myServiceNameTextField.getText().trim(); - if (myOnlyImplementationElementCreator.canClose(serviceImplementation)) { + String serviceOnlyImplementation = myServiceNameTextField.getText().trim(); + + if (checkInput(serviceOnlyImplementation) && myServiceCreator.createOnlyImplementation(serviceOnlyImplementation)) { close(OK_EXIT_CODE); } } } + private boolean checkInput(String input) { + if (StringUtil.isEmpty(input)) { + Messages.showMessageDialog(getContentPane(), IdeBundle.message("error.name.should.be.specified"), + CommonBundle.getErrorTitle(), Messages.getErrorIcon()); + return false; + } + return true; + } + @Nullable @Override protected JComponent createCenterPanel() { @@ -291,42 +240,111 @@ public abstract class GenerateServiceClassAndPatchPluginXmlActionBase extends Cr } } - private class MyServiceClassCreator extends ElementCreator { + + private class ServiceCreator { + private final Logger LOG = Logger.getInstance("#" + ServiceCreator.class.getCanonicalName()); + private final PsiDirectory myDirectory; - private final String myClassTemplateName; + private final String myServiceInterfaceTemplateName; + private final String myServiceOnlyImplementationTemplateName; - private PsiClass myCreatedClass = null; + private PsiClass[] createdClasses = null; - MyServiceClassCreator(Project project, String errorTitle, PsiDirectory directory, String classTemplateName) { - super(project, errorTitle); + ServiceCreator(PsiDirectory directory, + String serviceInterfaceTemplateName, + String serviceOnlyImplementationTemplateName) { myDirectory = directory; - myClassTemplateName = classTemplateName; + this.myServiceInterfaceTemplateName = serviceInterfaceTemplateName; + this.myServiceOnlyImplementationTemplateName = serviceOnlyImplementationTemplateName; } - @NotNull - protected PsiElement[] create(String newName) throws Exception { - return DevkitActionsUtil.createSinglePluginClass( - newName, myClassTemplateName, myDirectory, myFilesToPatch, getTemplatePresentation()); + PsiClass[] getCreatedClasses() { + return createdClasses; } - @Override - protected String getActionName(String newName) { - return DevKitBundle.message("new.service.class.action.name", newName); - } + /** + * @return whether the service was created (which indicates whether the create service dialog can be closed). + */ + @SuppressWarnings("ConstantConditions") // no NPE here since created classes are not anonymous + boolean createInterfaceAndImplementation(String interfaceName, String implementationName) { + return doCreateService(() -> { + PsiClass createdInterface = DevkitActionsUtil.createSinglePluginClass( + interfaceName, myServiceInterfaceTemplateName, myDirectory, myFilesToPatch, getTemplatePresentation())[0]; + if (createdInterface == null) { + return false; // canceled + } - public boolean canClose(String inputString) { - PsiElement[] createdElements = tryCreate(inputString); - if (createdElements.length > 0) { - myCreatedClass = (PsiClass)createdElements[0]; // cast is safe since create() returns array of single PsiClass + PsiClass createdImplementation = DevkitActionsUtil.createSinglePluginClass( + implementationName, JavaTemplateUtil.INTERNAL_CLASS_TEMPLATE_NAME, myDirectory, myFilesToPatch, getTemplatePresentation())[0]; + if (createdImplementation == null) { + // unlikely to happen since plugin.xml to patch is already defined after service interface is created + return false; + } + + JavaPsiFacade facade = JavaPsiFacade.getInstance(myDirectory.getProject()); + PsiElementFactory factory = facade.getElementFactory(); + PsiJavaCodeReferenceElement interfaceReference = + factory.createReferenceElementByFQClassName(createdInterface.getQualifiedName(), createdImplementation.getResolveScope()); + createdImplementation.getImplementsList().add(interfaceReference); + + handleCreatedElements(createdInterface, createdImplementation); + createdClasses = new PsiClass[]{createdInterface, createdImplementation}; return true; - } else { + }); + } + + /** + * @return whether the service was created (which indicates whether the create service dialog can be closed). + */ + boolean createOnlyImplementation(String onlyImplementationName) { + return doCreateService(() -> { + PsiClass createOnlyImplementation = DevkitActionsUtil.createSinglePluginClass( + onlyImplementationName, myServiceOnlyImplementationTemplateName, myDirectory, myFilesToPatch, getTemplatePresentation())[0]; + if (createOnlyImplementation == null) { + return false; // canceled + } + + handleCreatedElements(null, createOnlyImplementation); + createdClasses = new PsiClass[]{createOnlyImplementation}; + return true; + }); + } + + private boolean doCreateService(Callable action) { + RunResult result = new WriteCommandAction(getProject(), DevKitBundle.message("new.service.class.action.name")) { + @Override + protected void run(@NotNull Result result) throws Throwable { + result.setResult(action.call()); + } + + @Override + protected UndoConfirmationPolicy getUndoConfirmationPolicy() { + return UndoConfirmationPolicy.REQUEST_CONFIRMATION; + } + }.execute(); + + if (result.hasException()) { + handleException(result.getThrowable()); return false; } + + return result.getResultObject(); } - @Nullable - public PsiClass getCreatedClass() { - return myCreatedClass; + private void handleCreatedElements(@Nullable PsiClass createdInterface, @NotNull PsiClass createdImplementation) { + XmlFile[] pluginXmls = myFilesToPatch.toArray(new XmlFile[myFilesToPatch.size()]); + patchPluginXmls(pluginXmls, createdInterface, createdImplementation); + } + + private void handleException(Throwable t) { + LOG.info(t); + String errorMessage = ElementCreator.getErrorMessage(t); + Messages.showMessageDialog( + getProject(), errorMessage, DevKitBundle.message("error.cannot.create.service.class"), Messages.getErrorIcon()); + } + + private Project getProject() { + return myDirectory.getProject(); } } } From a8b5c4ef62535c898ad60404df6ada63cc5b8326 Mon Sep 17 00:00:00 2001 From: Yaroslav Pankratyev Date: Tue, 1 Aug 2017 18:42:17 +0300 Subject: [PATCH 11/27] Rename GenerateServiceClassAndPatchPluginXmlActionBase to NewServiceActionBase --- .../src/actions/service/NewApplicationServiceAction.java | 2 +- .../devkit/src/actions/service/NewModuleServiceAction.java | 2 +- .../src/actions/service/NewProjectServiceAction.java | 2 +- ...hPluginXmlActionBase.java => NewServiceActionBase.java} | 7 +++++-- plugins/devkit/src/actions/service/NewServiceDialog.form | 2 +- 5 files changed, 9 insertions(+), 6 deletions(-) rename plugins/devkit/src/actions/service/{GenerateServiceClassAndPatchPluginXmlActionBase.java => NewServiceActionBase.java} (97%) diff --git a/plugins/devkit/src/actions/service/NewApplicationServiceAction.java b/plugins/devkit/src/actions/service/NewApplicationServiceAction.java index c42f6c1d0015..fdd4a08efc87 100644 --- a/plugins/devkit/src/actions/service/NewApplicationServiceAction.java +++ b/plugins/devkit/src/actions/service/NewApplicationServiceAction.java @@ -17,7 +17,7 @@ package org.jetbrains.idea.devkit.actions.service; import org.jetbrains.idea.devkit.DevKitBundle; -public class NewApplicationServiceAction extends GenerateServiceClassAndPatchPluginXmlActionBase { +public class NewApplicationServiceAction extends NewServiceActionBase { public NewApplicationServiceAction() { super(DevKitBundle.message("new.menu.application.service.text"), DevKitBundle.message("new.menu.application.service.description")); diff --git a/plugins/devkit/src/actions/service/NewModuleServiceAction.java b/plugins/devkit/src/actions/service/NewModuleServiceAction.java index b2dfffba584c..b4b11a4d9ddc 100644 --- a/plugins/devkit/src/actions/service/NewModuleServiceAction.java +++ b/plugins/devkit/src/actions/service/NewModuleServiceAction.java @@ -17,7 +17,7 @@ package org.jetbrains.idea.devkit.actions.service; import org.jetbrains.idea.devkit.DevKitBundle; -public class NewModuleServiceAction extends GenerateServiceClassAndPatchPluginXmlActionBase { +public class NewModuleServiceAction extends NewServiceActionBase { public NewModuleServiceAction() { super(DevKitBundle.message("new.menu.module.service.text"), DevKitBundle.message("new.menu.module.service.description")); diff --git a/plugins/devkit/src/actions/service/NewProjectServiceAction.java b/plugins/devkit/src/actions/service/NewProjectServiceAction.java index d0ffd885698d..8c2ed2503cde 100644 --- a/plugins/devkit/src/actions/service/NewProjectServiceAction.java +++ b/plugins/devkit/src/actions/service/NewProjectServiceAction.java @@ -17,7 +17,7 @@ package org.jetbrains.idea.devkit.actions.service; import org.jetbrains.idea.devkit.DevKitBundle; -public class NewProjectServiceAction extends GenerateServiceClassAndPatchPluginXmlActionBase { +public class NewProjectServiceAction extends NewServiceActionBase { public NewProjectServiceAction() { super(DevKitBundle.message("new.menu.project.service.text"), DevKitBundle.message("new.menu.project.service.description")); diff --git a/plugins/devkit/src/actions/service/GenerateServiceClassAndPatchPluginXmlActionBase.java b/plugins/devkit/src/actions/service/NewServiceActionBase.java similarity index 97% rename from plugins/devkit/src/actions/service/GenerateServiceClassAndPatchPluginXmlActionBase.java rename to plugins/devkit/src/actions/service/NewServiceActionBase.java index 6046d03b41c8..885f46904388 100644 --- a/plugins/devkit/src/actions/service/GenerateServiceClassAndPatchPluginXmlActionBase.java +++ b/plugins/devkit/src/actions/service/NewServiceActionBase.java @@ -55,10 +55,13 @@ import java.util.HashSet; import java.util.Set; import java.util.concurrent.Callable; -public abstract class GenerateServiceClassAndPatchPluginXmlActionBase extends CreateInDirectoryActionBase implements WriteActionAware { +/** + * An base class for actions generating service classes (implementation and optionally interface) and registering new service in plugin.xml. + */ +public abstract class NewServiceActionBase extends CreateInDirectoryActionBase implements WriteActionAware { private final Set myFilesToPatch = new HashSet<>(); - public GenerateServiceClassAndPatchPluginXmlActionBase(String text, String description) { + public NewServiceActionBase(String text, String description) { super(text, description, null); } diff --git a/plugins/devkit/src/actions/service/NewServiceDialog.form b/plugins/devkit/src/actions/service/NewServiceDialog.form index 0fa5b25141a3..1eabd371b6cf 100644 --- a/plugins/devkit/src/actions/service/NewServiceDialog.form +++ b/plugins/devkit/src/actions/service/NewServiceDialog.form @@ -1,5 +1,5 @@ -
+ From c777ba9a8a29fd42fbf80e1b4ae242258e167a12 Mon Sep 17 00:00:00 2001 From: Yaroslav Pankratyev Date: Tue, 1 Aug 2017 18:45:42 +0300 Subject: [PATCH 12/27] Register service class/interface templates --- plugins/devkit/src/DevKitFileTemplatesFactory.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/plugins/devkit/src/DevKitFileTemplatesFactory.java b/plugins/devkit/src/DevKitFileTemplatesFactory.java index b31e5432bd90..310fc3e92ec5 100644 --- a/plugins/devkit/src/DevKitFileTemplatesFactory.java +++ b/plugins/devkit/src/DevKitFileTemplatesFactory.java @@ -27,9 +27,12 @@ public class DevKitFileTemplatesFactory implements FileTemplateGroupDescriptorFa FileTemplateGroupDescriptor descriptor = new FileTemplateGroupDescriptor(DevKitBundle.message("plugin.descriptor"), AllIcons.Nodes.Plugin); descriptor.addTemplate(new FileTemplateDescriptor("plugin.xml", StdFileTypes.XML.getIcon())); - descriptor.addTemplate(new FileTemplateDescriptor("ProjectComponent.java", StdFileTypes.JAVA.getIcon())); - descriptor.addTemplate(new FileTemplateDescriptor("ApplicationComponent.java", StdFileTypes.JAVA.getIcon())); - descriptor.addTemplate(new FileTemplateDescriptor("ModuleComponent.java", StdFileTypes.JAVA.getIcon())); + descriptor.addTemplate(new FileTemplateDescriptor("ProjectServiceClass.java", StdFileTypes.JAVA.getIcon())); + descriptor.addTemplate(new FileTemplateDescriptor("ProjectServiceInterface.java", StdFileTypes.JAVA.getIcon())); + descriptor.addTemplate(new FileTemplateDescriptor("ApplicationServiceClass.java", StdFileTypes.JAVA.getIcon())); + descriptor.addTemplate(new FileTemplateDescriptor("ApplicationServiceInterface.java", StdFileTypes.JAVA.getIcon())); + descriptor.addTemplate(new FileTemplateDescriptor("ModuleServiceClass.java", StdFileTypes.JAVA.getIcon())); + descriptor.addTemplate(new FileTemplateDescriptor("ModuleServiceInterface.java", StdFileTypes.JAVA.getIcon())); descriptor.addTemplate(new FileTemplateDescriptor("Action.java", StdFileTypes.JAVA.getIcon())); descriptor.addTemplate(new FileTemplateDescriptor("InspectionDescription.html", StdFileTypes.HTML.getIcon())); return descriptor; From d280307ab4309f969543930ce5256621ff38ef89 Mon Sep 17 00:00:00 2001 From: Yaroslav Pankratyev Date: Wed, 2 Aug 2017 16:51:23 +0300 Subject: [PATCH 13/27] Allow selecting only one plugin.xml to register service in; refactoring --- .../idea/devkit/DevKitBundle.properties | 1 + .../devkit/src/actions/DevkitActionsUtil.java | 63 ++++-- ...erateClassAndPatchPluginXmlActionBase.java | 39 ---- .../actions/GeneratePluginClassAction.java | 106 --------- .../devkit/src/actions/NewActionAction.java | 97 +++++++-- .../actions/service/NewServiceActionBase.java | 115 ++++------ .../quickfix/AbstractRegisterFix.java | 4 +- .../devkit/src/util/ChooseModulesDialog.java | 205 ------------------ .../choose/ChooseModulesDialogBase.java | 78 +++++++ .../choose/ChooseModulesDialogUtil.java | 91 ++++++++ .../choose/ChooseMultipleModulesDialog.java | 102 +++++++++ .../choose/ChooseSingleModuleDialog.java | 83 +++++++ 12 files changed, 531 insertions(+), 453 deletions(-) delete mode 100644 plugins/devkit/src/actions/GenerateClassAndPatchPluginXmlActionBase.java delete mode 100644 plugins/devkit/src/actions/GeneratePluginClassAction.java delete mode 100644 plugins/devkit/src/util/ChooseModulesDialog.java create mode 100644 plugins/devkit/src/util/module/choose/ChooseModulesDialogBase.java create mode 100644 plugins/devkit/src/util/module/choose/ChooseModulesDialogUtil.java create mode 100644 plugins/devkit/src/util/module/choose/ChooseMultipleModulesDialog.java create mode 100644 plugins/devkit/src/util/module/choose/ChooseSingleModuleDialog.java diff --git a/plugins/devkit/resources/org/jetbrains/idea/devkit/DevKitBundle.properties b/plugins/devkit/resources/org/jetbrains/idea/devkit/DevKitBundle.properties index 4ca2e046b1ef..13e4804b5d3a 100644 --- a/plugins/devkit/resources/org/jetbrains/idea/devkit/DevKitBundle.properties +++ b/plugins/devkit/resources/org/jetbrains/idea/devkit/DevKitBundle.properties @@ -105,6 +105,7 @@ new.menu.module.service.text=Module Service new.menu.module.service.description=Create New Module Service new.module.service.dialog.title=Create New Module Service +select.plugin.module.to.patch=Select Plugin Module to Patch select.plugin.modules.to.patch=Select Plugin Modules to Patch keyword.extend=extend diff --git a/plugins/devkit/src/actions/DevkitActionsUtil.java b/plugins/devkit/src/actions/DevkitActionsUtil.java index 0b0c645011f0..6ee894ffcc49 100644 --- a/plugins/devkit/src/actions/DevkitActionsUtil.java +++ b/plugins/devkit/src/actions/DevkitActionsUtil.java @@ -29,11 +29,11 @@ import com.intellij.psi.PsiClass; import com.intellij.psi.PsiDirectory; import com.intellij.psi.xml.XmlFile; import com.intellij.util.IncorrectOperationException; -import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.devkit.DevKitBundle; import org.jetbrains.idea.devkit.module.PluginModuleType; -import org.jetbrains.idea.devkit.util.ChooseModulesDialog; +import org.jetbrains.idea.devkit.util.module.choose.ChooseMultipleModulesDialog; +import org.jetbrains.idea.devkit.util.module.choose.ChooseSingleModuleDialog; import java.util.*; @@ -46,12 +46,30 @@ public final class DevkitActionsUtil { public static final PsiClass[] CANCELED = new PsiClass[1]; - @NotNull - public static PsiClass[] createSinglePluginClass(String name, String classTemplateName, PsiDirectory directory, - Set pluginXmlsToPatch, Presentation templatePresentation) { + /** + * @return selected plugin descriptor or null if canceled. + * @throws IncorrectOperationException if no plugin descriptors found. + */ + @Nullable + public static XmlFile showChooseModuleDialog(PsiDirectory directory, Presentation templatePresentation) { + XmlFile[] result = doShowChooseModulesDialog(false, directory, templatePresentation); + return result == null ? null : result[0]; + } + + /** + * @return selected plugin descriptors (never an empty array) or null if canceled. + * @throws IncorrectOperationException if no plugin descriptors found. + */ + @Nullable + public static XmlFile[] showChooseModulesDialog(PsiDirectory directory, Presentation templatePresentation) { + return doShowChooseModulesDialog(true, directory, templatePresentation); + } + + private static XmlFile[] doShowChooseModulesDialog(boolean multiple, PsiDirectory directory, Presentation templatePresentation) { Project project = directory.getProject(); Module module = getModule(directory); + Set pluginXmlsToPatch = new HashSet<>(); if (module != null) { addPluginModule(module, pluginXmlsToPatch); @@ -65,27 +83,35 @@ public final class DevkitActionsUtil { if (candidateModules.size() == 1) { addPluginModule(candidateModules.get(0), pluginXmlsToPatch); - } - else { - ChooseModulesDialog dialog = new ChooseModulesDialog(project, candidateModules, templatePresentation.getDescription()); - if (!dialog.showAndGet()) { - // create() should return CANCELED now - return CANCELED; - } - else { - List modules = dialog.getSelectedModules(); - for (Module m : modules) { - addPluginModule(m, pluginXmlsToPatch); + } else { + if (multiple) { + ChooseMultipleModulesDialog dialog = + new ChooseMultipleModulesDialog(project, candidateModules, templatePresentation.getDescription()); + if (!dialog.showAndGet()) { + return null; // canceled } + List modules = dialog.getSelectedModules(); + modules.forEach(m -> addPluginModule(m, pluginXmlsToPatch)); + } else { + ChooseSingleModuleDialog dialog = + new ChooseSingleModuleDialog(project, candidateModules, templatePresentation.getDescription()); + if (!dialog.showAndGet()) { + return null; // canceled + } + addPluginModule(dialog.getSelectedModule(), pluginXmlsToPatch); } } } } - if (pluginXmlsToPatch.size() == 0) { + if (pluginXmlsToPatch.isEmpty()) { throw new IncorrectOperationException(DevKitBundle.message("error.no.plugin.xml")); } + return pluginXmlsToPatch.toArray(new XmlFile[pluginXmlsToPatch.size()]); + } + + public static PsiClass createSingleClass(String name, String classTemplateName, PsiDirectory directory) { if (name.contains(".")) { String[] names = name.split("\\."); for (int i = 0; i < names.length - 1; i++) { @@ -94,8 +120,7 @@ public final class DevkitActionsUtil { name = names[names.length - 1]; } - PsiClass klass = JavaDirectoryService.getInstance().createClass(directory, name, classTemplateName); - return new PsiClass[] {klass}; + return JavaDirectoryService.getInstance().createClass(directory, name, classTemplateName); } @Nullable diff --git a/plugins/devkit/src/actions/GenerateClassAndPatchPluginXmlActionBase.java b/plugins/devkit/src/actions/GenerateClassAndPatchPluginXmlActionBase.java deleted file mode 100644 index f90df234cb3c..000000000000 --- a/plugins/devkit/src/actions/GenerateClassAndPatchPluginXmlActionBase.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2000-2012 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.jetbrains.idea.devkit.actions; - -import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.Messages; -import com.intellij.psi.PsiDirectory; -import com.intellij.psi.PsiElement; -import org.jetbrains.annotations.Nullable; - -import javax.swing.Icon; - -public abstract class GenerateClassAndPatchPluginXmlActionBase extends GeneratePluginClassAction { - public GenerateClassAndPatchPluginXmlActionBase(String text, String description, @Nullable Icon icon) { - super(text, description, icon); - } - - protected abstract String getClassNamePrompt(); - protected abstract String getClassNamePromptTitle(); - - protected PsiElement[] invokeDialogImpl(Project project, PsiDirectory directory) { - MyInputValidator validator = new MyInputValidator(project, directory); - Messages.showInputDialog(project, getClassNamePrompt(), getClassNamePromptTitle(), Messages.getQuestionIcon(), "", validator); - return validator.getCreatedElements(); - } -} diff --git a/plugins/devkit/src/actions/GeneratePluginClassAction.java b/plugins/devkit/src/actions/GeneratePluginClassAction.java deleted file mode 100644 index 5a87a1d4c072..000000000000 --- a/plugins/devkit/src/actions/GeneratePluginClassAction.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright 2000-2017 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.jetbrains.idea.devkit.actions; - -import com.intellij.ide.IdeView; -import com.intellij.ide.actions.CreateElementActionBase; -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.actionSystem.LangDataKeys; -import com.intellij.openapi.actionSystem.Presentation; -import com.intellij.openapi.module.Module; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.roots.ProjectFileIndex; -import com.intellij.openapi.roots.ProjectRootManager; -import com.intellij.psi.JavaDirectoryService; -import com.intellij.psi.PsiClass; -import com.intellij.psi.PsiDirectory; -import com.intellij.psi.PsiElement; -import com.intellij.psi.xml.XmlFile; -import org.jetbrains.annotations.NonNls; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.idea.devkit.util.DescriptorUtil; -import org.jetbrains.idea.devkit.util.PsiUtil; -import org.jetbrains.jps.model.java.JavaModuleSourceRootTypes; - -import javax.swing.*; -import java.util.HashSet; -import java.util.Set; - -public abstract class GeneratePluginClassAction extends CreateElementActionBase implements DescriptorUtil.Patcher { - protected final Set myFilesToPatch = new HashSet<>(); - - public GeneratePluginClassAction(String text, String description, @Nullable Icon icon) { - super(text, description, icon); - } - - @NotNull - protected final PsiElement[] invokeDialog(Project project, PsiDirectory directory) { - try { - final PsiElement[] psiElements = invokeDialogImpl(project, directory); - return psiElements == DevkitActionsUtil.CANCELED ? PsiElement.EMPTY_ARRAY : psiElements; - } - finally { - myFilesToPatch.clear(); - } - } - - protected abstract PsiElement[] invokeDialogImpl(Project project, PsiDirectory directory); - - public void update(final AnActionEvent e) { - super.update(e); - - final Presentation presentation = e.getPresentation(); - if (presentation.isEnabled()) { - final Project project = e.getProject(); - final Module module = e.getData(LangDataKeys.MODULE); - if (project != null && module != null && - PsiUtil.isPluginModule(module)) { - final IdeView view = e.getData(LangDataKeys.IDE_VIEW); - if (view != null) { - // from com.intellij.ide.actions.CreateClassAction.update() - ProjectFileIndex projectFileIndex = ProjectRootManager.getInstance(project).getFileIndex(); - PsiDirectory[] dirs = view.getDirectories(); - for (PsiDirectory dir : dirs) { - if (projectFileIndex.isUnderSourceRootOfType(dir.getVirtualFile(), JavaModuleSourceRootTypes.SOURCES) && - JavaDirectoryService.getInstance().getPackage(dir) != null) { - return; - } - } - } - } - - presentation.setEnabledAndVisible(false); - } - } - - @Override - public boolean startInWriteAction() { - return false; - } - - @NotNull - protected PsiElement[] create(String newName, PsiDirectory directory) throws Exception { - PsiClass[] createdClass = - DevkitActionsUtil.createSinglePluginClass(newName, getClassTemplateName(), directory, myFilesToPatch, getTemplatePresentation()); - - DescriptorUtil.patchPluginXml(this, createdClass[0], myFilesToPatch.toArray(new XmlFile[myFilesToPatch.size()])); - return createdClass; - } - - @NonNls - protected abstract String getClassTemplateName(); -} diff --git a/plugins/devkit/src/actions/NewActionAction.java b/plugins/devkit/src/actions/NewActionAction.java index 3beb14958419..386cedde0252 100644 --- a/plugins/devkit/src/actions/NewActionAction.java +++ b/plugins/devkit/src/actions/NewActionAction.java @@ -15,56 +15,123 @@ */ package org.jetbrains.idea.devkit.actions; +import com.intellij.ide.IdeView; +import com.intellij.ide.actions.CreateElementActionBase; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.LangDataKeys; +import com.intellij.openapi.actionSystem.Presentation; +import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; +import com.intellij.openapi.roots.ProjectFileIndex; +import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.psi.JavaDirectoryService; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiElement; import com.intellij.psi.xml.XmlFile; import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.NotNull; import org.jetbrains.idea.devkit.DevKitBundle; import org.jetbrains.idea.devkit.util.ActionType; +import org.jetbrains.idea.devkit.util.DescriptorUtil; +import org.jetbrains.idea.devkit.util.PsiUtil; +import org.jetbrains.jps.model.java.JavaModuleSourceRootTypes; -/** - * @author yole - */ -public class NewActionAction extends GeneratePluginClassAction { +public class NewActionAction extends CreateElementActionBase implements DescriptorUtil.Patcher { private NewActionDialog myDialog; + private XmlFile[] pluginDescriptorsToPatch; public NewActionAction() { super(DevKitBundle.message("new.menu.action.text"), DevKitBundle.message("new.menu.action.description"), null); } - protected PsiElement[] invokeDialogImpl(Project project, PsiDirectory directory) { + @NotNull + @Override + protected final PsiElement[] invokeDialog(Project project, PsiDirectory directory) { + final PsiElement[] psiElements = doInvokeDialog(project, directory); + return psiElements == DevkitActionsUtil.CANCELED ? PsiElement.EMPTY_ARRAY : psiElements; + } + + private PsiElement[] doInvokeDialog(Project project, PsiDirectory directory) { myDialog = new NewActionDialog(project); - myDialog.show(); - if (myDialog.getExitCode() == DialogWrapper.OK_EXIT_CODE) { - final MyInputValidator validator = new MyInputValidator(project, directory); - // this actually runs the action to create the class from template - validator.canClose(myDialog.getActionName()); + try { + myDialog.show(); + if (myDialog.getExitCode() == DialogWrapper.OK_EXIT_CODE) { + pluginDescriptorsToPatch = DevkitActionsUtil.showChooseModulesDialog(directory, getTemplatePresentation()); + if (pluginDescriptorsToPatch != null) { + assert pluginDescriptorsToPatch.length > 0; + MyInputValidator validator = new MyInputValidator(project, directory); + // this actually runs the action to create the class from template + validator.canClose(myDialog.getActionName()); + return validator.getCreatedElements(); + } + } + return PsiElement.EMPTY_ARRAY; + } finally { myDialog = null; - return validator.getCreatedElements(); + pluginDescriptorsToPatch = null; } - myDialog = null; - return PsiElement.EMPTY_ARRAY; } - protected String getClassTemplateName() { - return "Action.java"; + @Override + public void update(final AnActionEvent e) { + super.update(e); + + final Presentation presentation = e.getPresentation(); + if (presentation.isEnabled()) { + final Project project = e.getProject(); + final Module module = e.getData(LangDataKeys.MODULE); + if (project != null && module != null && + PsiUtil.isPluginModule(module)) { + final IdeView view = e.getData(LangDataKeys.IDE_VIEW); + if (view != null) { + // from com.intellij.ide.actions.CreateClassAction.update() + ProjectFileIndex projectFileIndex = ProjectRootManager.getInstance(project).getFileIndex(); + PsiDirectory[] dirs = view.getDirectories(); + for (PsiDirectory dir : dirs) { + if (projectFileIndex.isUnderSourceRootOfType(dir.getVirtualFile(), JavaModuleSourceRootTypes.SOURCES) && + JavaDirectoryService.getInstance().getPackage(dir) != null) { + return; + } + } + } + } + + presentation.setEnabledAndVisible(false); + } } + @Override + public boolean startInWriteAction() { + return false; + } + + @NotNull + @Override + protected PsiElement[] create(String newName, PsiDirectory directory) throws Exception { + PsiClass createdClass = DevkitActionsUtil.createSingleClass(newName, "Action.java", directory); + DescriptorUtil.patchPluginXml(this, createdClass, pluginDescriptorsToPatch); + return new PsiElement[]{createdClass}; + } + + + @Override public void patchPluginXml(final XmlFile pluginXml, final PsiClass klass) throws IncorrectOperationException { ActionType.ACTION.patchPluginXml(pluginXml, klass, myDialog); } + @Override protected String getErrorTitle() { return DevKitBundle.message("new.action.error"); } + @Override protected String getCommandName() { return DevKitBundle.message("new.action.command"); } + @Override protected String getActionName(PsiDirectory directory, String newName) { return DevKitBundle.message("new.action.action.name", directory, newName); } diff --git a/plugins/devkit/src/actions/service/NewServiceActionBase.java b/plugins/devkit/src/actions/service/NewServiceActionBase.java index 885f46904388..8131d932c91c 100644 --- a/plugins/devkit/src/actions/service/NewServiceActionBase.java +++ b/plugins/devkit/src/actions/service/NewServiceActionBase.java @@ -51,16 +51,12 @@ import org.jetbrains.idea.devkit.util.DescriptorUtil; import javax.swing.*; import javax.swing.event.DocumentEvent; -import java.util.HashSet; -import java.util.Set; import java.util.concurrent.Callable; /** * An base class for actions generating service classes (implementation and optionally interface) and registering new service in plugin.xml. */ public abstract class NewServiceActionBase extends CreateInDirectoryActionBase implements WriteActionAware { - private final Set myFilesToPatch = new HashSet<>(); - public NewServiceActionBase(String text, String description) { super(text, description, null); } @@ -70,35 +66,6 @@ public abstract class NewServiceActionBase extends CreateInDirectoryActionBase i return false; } - - private void patchPluginXmls(@NotNull XmlFile[] pluginXmls, @Nullable PsiClass serviceInterface, - @NotNull PsiClass serviceImplementation) { - DescriptorUtil.checkPluginXmlsWritable(serviceImplementation.getProject(), pluginXmls); - for (XmlFile pluginXml : pluginXmls) { - patchPluginXml(pluginXml, serviceInterface, serviceImplementation); - } - } - - private void patchPluginXml(XmlFile pluginXml, @Nullable PsiClass serviceInterface, @NotNull PsiClass serviceImplementation) { - DomFileElement fileElement = DomManager.getDomManager(pluginXml.getProject()).getFileElement(pluginXml, IdeaPlugin.class); - if (fileElement == null) { - throw new IncorrectOperationException(DevKitBundle.message("error.cannot.process.plugin.xml", pluginXml)); - } - - IdeaPlugin ideaPlugin = fileElement.getRootElement(); - Extensions targetExtensions = ideaPlugin.getExtensions().stream() - .filter(extensions -> !(extensions instanceof IncludedXmlTag)) - .filter(extensions -> Extensions.DEFAULT_PREFIX.equals(extensions.getDefaultExtensionNs().getStringValue())) - .findAny() - .orElseGet(() -> ideaPlugin.addExtensions()); - - XmlTag serviceTag = targetExtensions.addExtension(Extensions.DEFAULT_PREFIX + "." + getTagName()).getXmlTag(); - if (serviceInterface != null) { - serviceTag.setAttribute("serviceInterface", serviceInterface.getQualifiedName()); - } - serviceTag.setAttribute("serviceImplementation", serviceImplementation.getQualifiedName()); - } - @Override public final void actionPerformed(final AnActionEvent e) { IdeView view = e.getData(LangDataKeys.IDE_VIEW); @@ -112,7 +79,7 @@ public abstract class NewServiceActionBase extends CreateInDirectoryActionBase i if (dir == null) return; ServiceCreator serviceCreator = new ServiceCreator(dir, getInterfaceTemplateName(), getOnlyImplementationTemplateName()); - PsiClass[] createdClasses = invokeDialog(project, serviceCreator); + PsiClass[] createdClasses = invokeDialog(project, serviceCreator, dir); if (createdClasses == null) { return; } @@ -123,8 +90,8 @@ public abstract class NewServiceActionBase extends CreateInDirectoryActionBase i } @Nullable - private PsiClass[] invokeDialog(Project project, ServiceCreator serviceCreator) { - DialogWrapper dialog = new NewServiceDialog(project, serviceCreator); + private PsiClass[] invokeDialog(Project project, ServiceCreator serviceCreator, PsiDirectory dir) { + DialogWrapper dialog = new NewServiceDialog(project, serviceCreator, dir); dialog.show(); return serviceCreator.getCreatedClasses(); } @@ -139,6 +106,7 @@ public abstract class NewServiceActionBase extends CreateInDirectoryActionBase i private class NewServiceDialog extends DialogWrapper { private final ServiceCreator myServiceCreator; + private final PsiDirectory myDirectory; private JPanel myTopPanel; @@ -150,13 +118,14 @@ public abstract class NewServiceActionBase extends CreateInDirectoryActionBase i private boolean myAdjusting = false; private boolean myNeedAdjust = true; - NewServiceDialog(@Nullable Project project, ServiceCreator serviceCreator) { + NewServiceDialog(@Nullable Project project, ServiceCreator serviceCreator, PsiDirectory directory) { super(project); setOKActionEnabled(false); setTitle(getDialogTitle()); myServiceCreator = serviceCreator; + myDirectory = directory; mySeparateServiceInterfaceCheckbox.addActionListener(e -> { if (mySeparateServiceInterfaceCheckbox.isSelected()) { @@ -208,20 +177,26 @@ public abstract class NewServiceActionBase extends CreateInDirectoryActionBase i @Override protected void doOKAction() { + XmlFile pluginDescriptorToPatch = DevkitActionsUtil.showChooseModuleDialog(myDirectory, getTemplatePresentation()); + if (pluginDescriptorToPatch == null) { + return; // canceled + } + if (mySeparateServiceInterfaceCheckbox.isSelected()) { // separated interface and implementation String serviceInterface = myServiceNameTextField.getText().trim(); String serviceImplementation = myServiceImplementationTextField.getText().trim(); if (checkInput(serviceInterface) && checkInput(serviceImplementation) && - myServiceCreator.createInterfaceAndImplementation(serviceInterface, serviceImplementation)) { + myServiceCreator.createInterfaceAndImplementation(serviceInterface, serviceImplementation, pluginDescriptorToPatch)) { close(OK_EXIT_CODE); } } else { // only implementation String serviceOnlyImplementation = myServiceNameTextField.getText().trim(); - if (checkInput(serviceOnlyImplementation) && myServiceCreator.createOnlyImplementation(serviceOnlyImplementation)) { + if (checkInput(serviceOnlyImplementation) && + myServiceCreator.createOnlyImplementation(serviceOnlyImplementation, pluginDescriptorToPatch)) { close(OK_EXIT_CODE); } } @@ -253,9 +228,7 @@ public abstract class NewServiceActionBase extends CreateInDirectoryActionBase i private PsiClass[] createdClasses = null; - ServiceCreator(PsiDirectory directory, - String serviceInterfaceTemplateName, - String serviceOnlyImplementationTemplateName) { + ServiceCreator(PsiDirectory directory, String serviceInterfaceTemplateName, String serviceOnlyImplementationTemplateName) { myDirectory = directory; this.myServiceInterfaceTemplateName = serviceInterfaceTemplateName; this.myServiceOnlyImplementationTemplateName = serviceOnlyImplementationTemplateName; @@ -269,28 +242,21 @@ public abstract class NewServiceActionBase extends CreateInDirectoryActionBase i * @return whether the service was created (which indicates whether the create service dialog can be closed). */ @SuppressWarnings("ConstantConditions") // no NPE here since created classes are not anonymous - boolean createInterfaceAndImplementation(String interfaceName, String implementationName) { + boolean createInterfaceAndImplementation(String interfaceName, String implementationName, XmlFile pluginXml) { return doCreateService(() -> { - PsiClass createdInterface = DevkitActionsUtil.createSinglePluginClass( - interfaceName, myServiceInterfaceTemplateName, myDirectory, myFilesToPatch, getTemplatePresentation())[0]; - if (createdInterface == null) { - return false; // canceled - } - - PsiClass createdImplementation = DevkitActionsUtil.createSinglePluginClass( - implementationName, JavaTemplateUtil.INTERNAL_CLASS_TEMPLATE_NAME, myDirectory, myFilesToPatch, getTemplatePresentation())[0]; - if (createdImplementation == null) { - // unlikely to happen since plugin.xml to patch is already defined after service interface is created - return false; - } + PsiClass createdInterface = DevkitActionsUtil.createSingleClass(interfaceName, myServiceInterfaceTemplateName, myDirectory); + PsiClass createdImplementation = DevkitActionsUtil.createSingleClass( + implementationName, JavaTemplateUtil.INTERNAL_CLASS_TEMPLATE_NAME, myDirectory); + // make service implementation implement service interface JavaPsiFacade facade = JavaPsiFacade.getInstance(myDirectory.getProject()); PsiElementFactory factory = facade.getElementFactory(); PsiJavaCodeReferenceElement interfaceReference = factory.createReferenceElementByFQClassName(createdInterface.getQualifiedName(), createdImplementation.getResolveScope()); createdImplementation.getImplementsList().add(interfaceReference); - handleCreatedElements(createdInterface, createdImplementation); + patchPluginXml(createdInterface, createdImplementation, pluginXml); + createdClasses = new PsiClass[]{createdInterface, createdImplementation}; return true; }); @@ -299,16 +265,14 @@ public abstract class NewServiceActionBase extends CreateInDirectoryActionBase i /** * @return whether the service was created (which indicates whether the create service dialog can be closed). */ - boolean createOnlyImplementation(String onlyImplementationName) { + boolean createOnlyImplementation(String onlyImplementationName, XmlFile pluginXml) { return doCreateService(() -> { - PsiClass createOnlyImplementation = DevkitActionsUtil.createSinglePluginClass( - onlyImplementationName, myServiceOnlyImplementationTemplateName, myDirectory, myFilesToPatch, getTemplatePresentation())[0]; - if (createOnlyImplementation == null) { - return false; // canceled - } + PsiClass createdOnlyImplementation = DevkitActionsUtil.createSingleClass( + onlyImplementationName, myServiceOnlyImplementationTemplateName, myDirectory); - handleCreatedElements(null, createOnlyImplementation); - createdClasses = new PsiClass[]{createOnlyImplementation}; + patchPluginXml(null, createdOnlyImplementation, pluginXml); + + createdClasses = new PsiClass[]{createdOnlyImplementation}; return true; }); } @@ -334,9 +298,26 @@ public abstract class NewServiceActionBase extends CreateInDirectoryActionBase i return result.getResultObject(); } - private void handleCreatedElements(@Nullable PsiClass createdInterface, @NotNull PsiClass createdImplementation) { - XmlFile[] pluginXmls = myFilesToPatch.toArray(new XmlFile[myFilesToPatch.size()]); - patchPluginXmls(pluginXmls, createdInterface, createdImplementation); + private void patchPluginXml(@Nullable PsiClass createdInterface, @NotNull PsiClass createdImplementation, XmlFile pluginXml) { + DescriptorUtil.checkPluginXmlsWritable(createdImplementation.getProject(), pluginXml); + + DomFileElement fileElement = DomManager.getDomManager(pluginXml.getProject()).getFileElement(pluginXml, IdeaPlugin.class); + if (fileElement == null) { + throw new IncorrectOperationException(DevKitBundle.message("error.cannot.process.plugin.xml", pluginXml)); + } + + IdeaPlugin ideaPlugin = fileElement.getRootElement(); + Extensions targetExtensions = ideaPlugin.getExtensions().stream() + .filter(extensions -> !(extensions instanceof IncludedXmlTag)) + .filter(extensions -> Extensions.DEFAULT_PREFIX.equals(extensions.getDefaultExtensionNs().getStringValue())) + .findAny() + .orElseGet(() -> ideaPlugin.addExtensions()); + + XmlTag serviceTag = targetExtensions.addExtension(Extensions.DEFAULT_PREFIX + "." + getTagName()).getXmlTag(); + if (createdInterface != null) { + serviceTag.setAttribute("serviceInterface", createdInterface.getQualifiedName()); + } + serviceTag.setAttribute("serviceImplementation", createdImplementation.getQualifiedName()); } private void handleException(Throwable t) { diff --git a/plugins/devkit/src/inspections/quickfix/AbstractRegisterFix.java b/plugins/devkit/src/inspections/quickfix/AbstractRegisterFix.java index daaf9c66f00b..7ae518b80de9 100644 --- a/plugins/devkit/src/inspections/quickfix/AbstractRegisterFix.java +++ b/plugins/devkit/src/inspections/quickfix/AbstractRegisterFix.java @@ -34,8 +34,8 @@ import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.idea.devkit.DevKitBundle; import org.jetbrains.idea.devkit.module.PluginModuleType; -import org.jetbrains.idea.devkit.util.ChooseModulesDialog; import org.jetbrains.idea.devkit.util.DescriptorUtil; +import org.jetbrains.idea.devkit.util.module.choose.ChooseMultipleModulesDialog; import java.util.List; @@ -93,7 +93,7 @@ abstract class AbstractRegisterFix implements LocalQuickFix, DescriptorUtil.Patc else { List modules = PluginModuleType.getCandidateModules(module); if (modules.size() > 1) { - ChooseModulesDialog dialog = new ChooseModulesDialog(project, modules, getName()); + ChooseMultipleModulesDialog dialog = new ChooseMultipleModulesDialog(project, modules, getName()); if (!dialog.showAndGet()) { return; } diff --git a/plugins/devkit/src/util/ChooseModulesDialog.java b/plugins/devkit/src/util/ChooseModulesDialog.java deleted file mode 100644 index 5d6dfbbe8ca5..000000000000 --- a/plugins/devkit/src/util/ChooseModulesDialog.java +++ /dev/null @@ -1,205 +0,0 @@ -/* - * Copyright 2000-2015 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.jetbrains.idea.devkit.util; - -import com.intellij.openapi.module.Module; -import com.intellij.openapi.module.ModuleType; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.DialogWrapper; -import com.intellij.openapi.ui.Messages; -import com.intellij.openapi.ui.MultiLineLabelUI; -import com.intellij.openapi.vfs.VfsUtilCore; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.xml.XmlFile; -import com.intellij.ui.ColoredListCellRenderer; -import com.intellij.ui.ScrollPaneFactory; -import com.intellij.ui.SimpleTextAttributes; -import com.intellij.ui.TableUtil; -import com.intellij.ui.components.JBList; -import com.intellij.ui.table.JBTable; -import com.intellij.util.ui.JBUI; -import com.intellij.util.ui.components.BorderLayoutPanel; -import org.jetbrains.annotations.NonNls; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.idea.devkit.DevKitBundle; -import org.jetbrains.idea.devkit.module.PluginModuleType; - -import javax.swing.*; -import javax.swing.event.TableModelEvent; -import javax.swing.event.TableModelListener; -import javax.swing.table.AbstractTableModel; -import javax.swing.table.TableCellRenderer; -import java.awt.*; -import java.awt.event.KeyAdapter; -import java.awt.event.KeyEvent; -import java.io.File; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Iterator; -import java.util.List; - -/** - * @author swr - */ -public class ChooseModulesDialog extends DialogWrapper { - private final Icon myIcon; - private final String myMessage; - private final JTable myView; - private final List myCandidateModules; - private final boolean[] myStates; - - public ChooseModulesDialog(final Project project, List candidateModules, @NonNls String title) { - this ( project, candidateModules, title, DevKitBundle.message("select.plugin.modules.to.patch")); - } - - public ChooseModulesDialog(final Project project, List candidateModules, @NonNls String title, final String message) { - super(project, false); - setTitle(title); - - myCandidateModules = candidateModules; - myIcon = Messages.getQuestionIcon(); - myMessage = message; - myView = new JBTable(new AbstractTableModel() { - public int getRowCount() { - return myCandidateModules.size(); - } - - public int getColumnCount() { - return 2; - } - - public boolean isCellEditable(int rowIndex, int columnIndex) { - return columnIndex == 0; - } - - public void setValueAt(Object aValue, int rowIndex, int columnIndex) { - myStates[rowIndex] = (Boolean)aValue; - fireTableCellUpdated(rowIndex, columnIndex); - } - - public Class getColumnClass(int columnIndex) { - return columnIndex == 0 ? Boolean.class : Module.class; - } - - public Object getValueAt(int rowIndex, int columnIndex) { - return columnIndex == 0 ? myStates[rowIndex] : myCandidateModules.get(rowIndex); - } - }); - - myView.setShowGrid(false); - myView.setTableHeader(null); - myView.setIntercellSpacing(JBUI.emptySize()); - TableUtil.setupCheckboxColumn(myView, 0); - myView.getModel().addTableModelListener(new TableModelListener() { - public void tableChanged(TableModelEvent e) { - getOKAction().setEnabled(getSelectedModules().size() > 0); - } - }); - myView.addKeyListener(new KeyAdapter() { - public void keyTyped(KeyEvent e) { - if (e.getKeyCode() == KeyEvent.VK_ENTER - || e.getKeyChar() == '\n') { - doOKAction(); - } - } - }); - myView.setDefaultRenderer(Module.class, new MyTableCellRenderer(project)); - - myStates = new boolean[candidateModules.size()]; - Arrays.fill(myStates, true); - - init(); - } - - protected JComponent createNorthPanel() { - BorderLayoutPanel panel = JBUI.Panels.simplePanel(15, 10); - if (myIcon != null) { - JLabel iconLabel = new JLabel(myIcon); - panel.addToLeft(JBUI.Panels.simplePanel().addToTop(iconLabel)); - } - - BorderLayoutPanel messagePanel = JBUI.Panels.simplePanel(); - if (myMessage != null) { - JLabel textLabel = new JLabel(myMessage); - textLabel.setBorder(JBUI.Borders.emptyBottom(5)); - textLabel.setUI(new MultiLineLabelUI()); - messagePanel.addToTop(textLabel); - } - panel.add(messagePanel, BorderLayout.CENTER); - - final JScrollPane jScrollPane = ScrollPaneFactory.createScrollPane(); - jScrollPane.setViewportView(myView); - jScrollPane.setPreferredSize(JBUI.size(300, 80)); - panel.addToBottom(jScrollPane); - return panel; - } - - public JComponent getPreferredFocusedComponent() { - return myView; - } - - - protected JComponent createCenterPanel() { - return null; - } - - public List getSelectedModules() { - final ArrayList list = new ArrayList<>(myCandidateModules); - final Iterator modules = list.iterator(); - for (boolean b : myStates) { - modules.next(); - if (!b) { - modules.remove(); - } - } - return list; - } - - private static class MyTableCellRenderer implements TableCellRenderer { - private final JList myList; - private final Project myProject; - private final ColoredListCellRenderer myCellRenderer; - - public MyTableCellRenderer(Project project) { - myProject = project; - myList = new JBList(); - myCellRenderer = new ColoredListCellRenderer() { - protected void customizeCellRenderer(@NotNull JList list, Object value, int index, boolean selected, boolean hasFocus) { - final Module module = ((Module)value); - setIcon(ModuleType.get(module).getIcon()); - append(module.getName(), SimpleTextAttributes.REGULAR_ATTRIBUTES); - - final XmlFile pluginXml = PluginModuleType.getPluginXml(module); - assert pluginXml != null; - - final VirtualFile virtualFile = pluginXml.getVirtualFile(); - assert virtualFile != null; - final VirtualFile projectPath = myProject.getBaseDir(); - assert projectPath != null; - if (VfsUtilCore.isAncestor(projectPath, virtualFile, false)) { - append(" (" + VfsUtilCore.getRelativePath(virtualFile, projectPath, File.separatorChar) + ")", SimpleTextAttributes.GRAYED_ATTRIBUTES); - } else { - append(" (" + virtualFile.getPresentableUrl() + ")", SimpleTextAttributes.GRAYED_ATTRIBUTES); - } - } - }; - } - - public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { - return myCellRenderer.getListCellRendererComponent(myList, value, row, isSelected, hasFocus); - } - } -} diff --git a/plugins/devkit/src/util/module/choose/ChooseModulesDialogBase.java b/plugins/devkit/src/util/module/choose/ChooseModulesDialogBase.java new file mode 100644 index 000000000000..f5ab40eb4ddd --- /dev/null +++ b/plugins/devkit/src/util/module/choose/ChooseModulesDialogBase.java @@ -0,0 +1,78 @@ +/* + * Copyright 2000-2017 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.jetbrains.idea.devkit.util.module.choose; + +import com.intellij.openapi.module.Module; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.ui.MultiLineLabelUI; +import com.intellij.ui.ScrollPaneFactory; +import com.intellij.util.ui.JBUI; +import com.intellij.util.ui.components.BorderLayoutPanel; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.awt.*; +import java.util.List; + +abstract class ChooseModulesDialogBase extends DialogWrapper { + private final List myCandidateModules; + private final Icon myIcon; + private final String myMessage; + + protected ChooseModulesDialogBase(Project project, List candidateModules, String title, String message) { + super(project, false); + setTitle(title); + + myCandidateModules = candidateModules; + myIcon = Messages.getQuestionIcon(); + myMessage = message; + } + + @Nullable + @Override + protected JComponent createCenterPanel() { + return null; + } + + @Nullable + @Override + protected JComponent createNorthPanel() { + BorderLayoutPanel panel = JBUI.Panels.simplePanel(15, 10); + JLabel iconLabel = new JLabel(myIcon); + panel.addToLeft(JBUI.Panels.simplePanel().addToTop(iconLabel)); + + BorderLayoutPanel messagePanel = JBUI.Panels.simplePanel(); + JLabel textLabel = new JLabel(myMessage); + textLabel.setBorder(JBUI.Borders.emptyBottom(5)); + textLabel.setUI(new MultiLineLabelUI()); + messagePanel.addToTop(textLabel); + panel.add(messagePanel, BorderLayout.CENTER); + + final JScrollPane jScrollPane = ScrollPaneFactory.createScrollPane(); + jScrollPane.setViewportView(getTable()); + jScrollPane.setPreferredSize(JBUI.size(300, 80)); + panel.addToBottom(jScrollPane); + return panel; + } + + protected List getCandidateModules() { + return myCandidateModules; + } + + protected abstract JTable getTable(); +} diff --git a/plugins/devkit/src/util/module/choose/ChooseModulesDialogUtil.java b/plugins/devkit/src/util/module/choose/ChooseModulesDialogUtil.java new file mode 100644 index 000000000000..a69ecb76fd1a --- /dev/null +++ b/plugins/devkit/src/util/module/choose/ChooseModulesDialogUtil.java @@ -0,0 +1,91 @@ +/* + * Copyright 2000-2017 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.jetbrains.idea.devkit.util.module.choose; + +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleType; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VfsUtilCore; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.xml.XmlFile; +import com.intellij.ui.ColoredListCellRenderer; +import com.intellij.ui.SimpleTextAttributes; +import com.intellij.ui.components.JBList; +import com.intellij.util.ui.JBUI; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.idea.devkit.module.PluginModuleType; + +import javax.swing.*; +import javax.swing.table.TableCellRenderer; +import java.awt.*; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.io.File; + +class ChooseModulesDialogUtil { + private ChooseModulesDialogUtil() { + } + + static void setupTable(@NotNull JTable table, @NotNull Project project, Runnable onEnter) { + table.setShowGrid(false); + table.setTableHeader(null); + table.setIntercellSpacing(JBUI.emptySize()); + table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + table.setDefaultRenderer(Module.class, new MyTableCellRenderer(project)); + table.addKeyListener(new KeyAdapter() { + public void keyTyped(KeyEvent e) { + if (e.getKeyCode() == KeyEvent.VK_ENTER || e.getKeyChar() == '\n') { + onEnter.run(); + } + } + }); + } + + private static class MyTableCellRenderer implements TableCellRenderer { + private final JList myList; + private final Project myProject; + private final ColoredListCellRenderer myCellRenderer; + + public MyTableCellRenderer(Project project) { + myProject = project; + myList = new JBList(); + myCellRenderer = new ColoredListCellRenderer() { + protected void customizeCellRenderer(@NotNull JList list, Object value, int index, boolean selected, boolean hasFocus) { + final Module module = ((Module)value); + setIcon(ModuleType.get(module).getIcon()); + append(module.getName(), SimpleTextAttributes.REGULAR_ATTRIBUTES); + + final XmlFile pluginXml = PluginModuleType.getPluginXml(module); + assert pluginXml != null; + + final VirtualFile virtualFile = pluginXml.getVirtualFile(); + assert virtualFile != null; + final VirtualFile projectPath = myProject.getBaseDir(); + assert projectPath != null; + if (VfsUtilCore.isAncestor(projectPath, virtualFile, false)) { + append(" (" + VfsUtilCore.getRelativePath(virtualFile, projectPath, File.separatorChar) + ")", SimpleTextAttributes.GRAYED_ATTRIBUTES); + } else { + append(" (" + virtualFile.getPresentableUrl() + ")", SimpleTextAttributes.GRAYED_ATTRIBUTES); + } + } + }; + } + + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { + return myCellRenderer.getListCellRendererComponent(myList, value, row, isSelected, hasFocus); + } + } +} diff --git a/plugins/devkit/src/util/module/choose/ChooseMultipleModulesDialog.java b/plugins/devkit/src/util/module/choose/ChooseMultipleModulesDialog.java new file mode 100644 index 000000000000..3464d2b61ae4 --- /dev/null +++ b/plugins/devkit/src/util/module/choose/ChooseMultipleModulesDialog.java @@ -0,0 +1,102 @@ +/* + * Copyright 2000-2017 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.jetbrains.idea.devkit.util.module.choose; + +import com.intellij.openapi.module.Module; +import com.intellij.openapi.project.Project; +import com.intellij.ui.TableUtil; +import com.intellij.ui.table.JBTable; +import org.jetbrains.idea.devkit.DevKitBundle; + +import javax.swing.*; +import javax.swing.event.TableModelEvent; +import javax.swing.event.TableModelListener; +import javax.swing.table.AbstractTableModel; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; + +public class ChooseMultipleModulesDialog extends ChooseModulesDialogBase { + private final JTable myTable; + private final boolean[] myStates; + + public ChooseMultipleModulesDialog(Project project, List candidateModules, String title) { + super(project, candidateModules, title, DevKitBundle.message("select.plugin.modules.to.patch")); + myTable = new JBTable(new AbstractTableModel() { + public int getRowCount() { + return getCandidateModules().size(); + } + + public int getColumnCount() { + return 2; + } + + public boolean isCellEditable(int rowIndex, int columnIndex) { + return columnIndex == 0; + } + + public void setValueAt(Object aValue, int rowIndex, int columnIndex) { + myStates[rowIndex] = (Boolean)aValue; + fireTableCellUpdated(rowIndex, columnIndex); + } + + public Class getColumnClass(int columnIndex) { + return columnIndex == 0 ? Boolean.class : Module.class; + } + + public Object getValueAt(int rowIndex, int columnIndex) { + return columnIndex == 0 ? myStates[rowIndex] : getCandidateModules().get(rowIndex); + } + }); + + ChooseModulesDialogUtil.setupTable(myTable, project, this::doOKAction); + TableUtil.setupCheckboxColumn(myTable, 0); + + myTable.getModel().addTableModelListener(new TableModelListener() { + public void tableChanged(TableModelEvent e) { + getOKAction().setEnabled(getSelectedModules().size() > 0); + } + }); + + myStates = new boolean[candidateModules.size()]; + Arrays.fill(myStates, true); + + init(); + } + + @Override + protected JTable getTable() { + return myTable; + } + + @Override + public JComponent getPreferredFocusedComponent() { + return myTable; + } + + public List getSelectedModules() { + final ArrayList list = new ArrayList<>(getCandidateModules()); + final Iterator modules = list.iterator(); + for (boolean b : myStates) { + modules.next(); + if (!b) { + modules.remove(); + } + } + return list; + } +} diff --git a/plugins/devkit/src/util/module/choose/ChooseSingleModuleDialog.java b/plugins/devkit/src/util/module/choose/ChooseSingleModuleDialog.java new file mode 100644 index 000000000000..76bcc35c31cd --- /dev/null +++ b/plugins/devkit/src/util/module/choose/ChooseSingleModuleDialog.java @@ -0,0 +1,83 @@ +/* + * Copyright 2000-2017 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.jetbrains.idea.devkit.util.module.choose; + +import com.intellij.openapi.module.Module; +import com.intellij.openapi.project.Project; +import com.intellij.ui.table.JBTable; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.devkit.DevKitBundle; + +import javax.swing.*; +import javax.swing.event.TableModelEvent; +import javax.swing.event.TableModelListener; +import javax.swing.table.AbstractTableModel; +import java.util.List; + +public class ChooseSingleModuleDialog extends ChooseModulesDialogBase { + private final JTable myTable; + + public ChooseSingleModuleDialog(Project project, List candidateModules, String title) { + super(project, candidateModules, title, DevKitBundle.message("select.plugin.module.to.patch")); + + myTable = new JBTable(new AbstractTableModel() { + public int getRowCount() { + return getCandidateModules().size(); + } + + public int getColumnCount() { + return 1; + } + + public Class getColumnClass(int columnIndex) { + return Module.class; + } + + public Object getValueAt(int rowIndex, int columnIndex) { + return getCandidateModules().get(rowIndex); + } + }); + + ChooseModulesDialogUtil.setupTable(myTable, project, this::doOKAction); + + myTable.getModel().addTableModelListener(new TableModelListener() { + public void tableChanged(TableModelEvent e) { + getOKAction().setEnabled(myTable.getSelectedRowCount() > 0); + } + }); + + init(); + } + + @Override + protected JTable getTable() { + return myTable; + } + + @Override + public JComponent getPreferredFocusedComponent() { + return myTable; + } + + @Nullable + public Module getSelectedModule() { + int selectedRow = myTable.getSelectedRow(); + if (selectedRow < 0) { + return null; + } + return (Module)myTable.getModel().getValueAt(selectedRow, 0); + } +} From 4676af3d5ff0e4afe3bdfcd9fc3996629eebcda6 Mon Sep 17 00:00:00 2001 From: Yaroslav Pankratyev Date: Wed, 2 Aug 2017 17:21:40 +0300 Subject: [PATCH 14/27] Cleanup unused properties from DevKit bundle; minor style fixes --- .../idea/devkit/DevKitBundle.properties | 3 -- .../devkit/src/actions/DevkitActionsUtil.java | 10 +++---- .../devkit/src/actions/NewActionAction.java | 14 +++++----- .../actions/service/NewServiceActionBase.java | 8 +++--- .../choose/ChooseModulesDialogUtil.java | 28 +++++++++---------- 5 files changed, 30 insertions(+), 33 deletions(-) diff --git a/plugins/devkit/resources/org/jetbrains/idea/devkit/DevKitBundle.properties b/plugins/devkit/resources/org/jetbrains/idea/devkit/DevKitBundle.properties index 13e4804b5d3a..7eea94f39395 100644 --- a/plugins/devkit/resources/org/jetbrains/idea/devkit/DevKitBundle.properties +++ b/plugins/devkit/resources/org/jetbrains/idea/devkit/DevKitBundle.properties @@ -83,11 +83,8 @@ new.action.action.name=Creating new action: {0}.{1} new.action.dialog.title=New Action new.menu.application.component.text=Application Component -new.application.component.error=Cannot create application component new.menu.module.component.text=Module Component new.menu.project.component.text=Project Component -new.project.component.error=Cannot create project component -new.project.component.command=Create Project Component new.service.class.action.name=Creating new service new.service.dialog.interface=Service &interface: diff --git a/plugins/devkit/src/actions/DevkitActionsUtil.java b/plugins/devkit/src/actions/DevkitActionsUtil.java index 6ee894ffcc49..d755a75cd6cb 100644 --- a/plugins/devkit/src/actions/DevkitActionsUtil.java +++ b/plugins/devkit/src/actions/DevkitActionsUtil.java @@ -126,11 +126,11 @@ public final class DevkitActionsUtil { @Nullable private static Module getModule(PsiDirectory dir) { Project project = dir.getProject(); - final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(project).getFileIndex(); + ProjectFileIndex fileIndex = ProjectRootManager.getInstance(project).getFileIndex(); - final VirtualFile vFile = dir.getVirtualFile(); + VirtualFile vFile = dir.getVirtualFile(); if (fileIndex.isInLibrarySource(vFile) || fileIndex.isInLibraryClasses(vFile)) { - final List orderEntries = fileIndex.getOrderEntriesForFile(vFile); + List orderEntries = fileIndex.getOrderEntriesForFile(vFile); if (orderEntries.isEmpty()) { return null; } @@ -138,7 +138,7 @@ public final class DevkitActionsUtil { for (OrderEntry orderEntry : orderEntries) { modules.add(orderEntry.getOwnerModule()); } - final Module[] candidates = modules.toArray(new Module[modules.size()]); + Module[] candidates = modules.toArray(new Module[modules.size()]); Arrays.sort(candidates, ModuleManager.getInstance(project).moduleDependencyComparator()); return candidates[0]; } @@ -146,7 +146,7 @@ public final class DevkitActionsUtil { } private static void addPluginModule(Module module, Set pluginXmlsToPatch) { - final XmlFile pluginXml = PluginModuleType.getPluginXml(module); + XmlFile pluginXml = PluginModuleType.getPluginXml(module); if (pluginXml != null) pluginXmlsToPatch.add(pluginXml); } } diff --git a/plugins/devkit/src/actions/NewActionAction.java b/plugins/devkit/src/actions/NewActionAction.java index 386cedde0252..bd0d4c663c5c 100644 --- a/plugins/devkit/src/actions/NewActionAction.java +++ b/plugins/devkit/src/actions/NewActionAction.java @@ -49,7 +49,7 @@ public class NewActionAction extends CreateElementActionBase implements Descript @NotNull @Override protected final PsiElement[] invokeDialog(Project project, PsiDirectory directory) { - final PsiElement[] psiElements = doInvokeDialog(project, directory); + PsiElement[] psiElements = doInvokeDialog(project, directory); return psiElements == DevkitActionsUtil.CANCELED ? PsiElement.EMPTY_ARRAY : psiElements; } @@ -75,16 +75,16 @@ public class NewActionAction extends CreateElementActionBase implements Descript } @Override - public void update(final AnActionEvent e) { + public void update(AnActionEvent e) { super.update(e); - final Presentation presentation = e.getPresentation(); + Presentation presentation = e.getPresentation(); if (presentation.isEnabled()) { - final Project project = e.getProject(); - final Module module = e.getData(LangDataKeys.MODULE); + Project project = e.getProject(); + Module module = e.getData(LangDataKeys.MODULE); if (project != null && module != null && PsiUtil.isPluginModule(module)) { - final IdeView view = e.getData(LangDataKeys.IDE_VIEW); + IdeView view = e.getData(LangDataKeys.IDE_VIEW); if (view != null) { // from com.intellij.ide.actions.CreateClassAction.update() ProjectFileIndex projectFileIndex = ProjectRootManager.getInstance(project).getFileIndex(); @@ -117,7 +117,7 @@ public class NewActionAction extends CreateElementActionBase implements Descript @Override - public void patchPluginXml(final XmlFile pluginXml, final PsiClass klass) throws IncorrectOperationException { + public void patchPluginXml(XmlFile pluginXml, PsiClass klass) throws IncorrectOperationException { ActionType.ACTION.patchPluginXml(pluginXml, klass, myDialog); } diff --git a/plugins/devkit/src/actions/service/NewServiceActionBase.java b/plugins/devkit/src/actions/service/NewServiceActionBase.java index 8131d932c91c..79afdb2599fb 100644 --- a/plugins/devkit/src/actions/service/NewServiceActionBase.java +++ b/plugins/devkit/src/actions/service/NewServiceActionBase.java @@ -67,7 +67,7 @@ public abstract class NewServiceActionBase extends CreateInDirectoryActionBase i } @Override - public final void actionPerformed(final AnActionEvent e) { + public final void actionPerformed(AnActionEvent e) { IdeView view = e.getData(LangDataKeys.IDE_VIEW); if (view == null) { return; @@ -249,7 +249,7 @@ public abstract class NewServiceActionBase extends CreateInDirectoryActionBase i implementationName, JavaTemplateUtil.INTERNAL_CLASS_TEMPLATE_NAME, myDirectory); // make service implementation implement service interface - JavaPsiFacade facade = JavaPsiFacade.getInstance(myDirectory.getProject()); + JavaPsiFacade facade = JavaPsiFacade.getInstance(getProject()); PsiElementFactory factory = facade.getElementFactory(); PsiJavaCodeReferenceElement interfaceReference = factory.createReferenceElementByFQClassName(createdInterface.getQualifiedName(), createdImplementation.getResolveScope()); @@ -299,9 +299,9 @@ public abstract class NewServiceActionBase extends CreateInDirectoryActionBase i } private void patchPluginXml(@Nullable PsiClass createdInterface, @NotNull PsiClass createdImplementation, XmlFile pluginXml) { - DescriptorUtil.checkPluginXmlsWritable(createdImplementation.getProject(), pluginXml); + DescriptorUtil.checkPluginXmlsWritable(getProject(), pluginXml); - DomFileElement fileElement = DomManager.getDomManager(pluginXml.getProject()).getFileElement(pluginXml, IdeaPlugin.class); + DomFileElement fileElement = DomManager.getDomManager(getProject()).getFileElement(pluginXml, IdeaPlugin.class); if (fileElement == null) { throw new IncorrectOperationException(DevKitBundle.message("error.cannot.process.plugin.xml", pluginXml)); } diff --git a/plugins/devkit/src/util/module/choose/ChooseModulesDialogUtil.java b/plugins/devkit/src/util/module/choose/ChooseModulesDialogUtil.java index a69ecb76fd1a..02b9f0b48322 100644 --- a/plugins/devkit/src/util/module/choose/ChooseModulesDialogUtil.java +++ b/plugins/devkit/src/util/module/choose/ChooseModulesDialogUtil.java @@ -35,7 +35,7 @@ import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.io.File; -class ChooseModulesDialogUtil { +final class ChooseModulesDialogUtil { private ChooseModulesDialogUtil() { } @@ -55,28 +55,28 @@ class ChooseModulesDialogUtil { } private static class MyTableCellRenderer implements TableCellRenderer { - private final JList myList; private final Project myProject; - private final ColoredListCellRenderer myCellRenderer; + private final JList myList; + private final ColoredListCellRenderer myCellRenderer; public MyTableCellRenderer(Project project) { myProject = project; - myList = new JBList(); - myCellRenderer = new ColoredListCellRenderer() { - protected void customizeCellRenderer(@NotNull JList list, Object value, int index, boolean selected, boolean hasFocus) { - final Module module = ((Module)value); - setIcon(ModuleType.get(module).getIcon()); - append(module.getName(), SimpleTextAttributes.REGULAR_ATTRIBUTES); + myList = new JBList<>(); + myCellRenderer = new ColoredListCellRenderer() { + protected void customizeCellRenderer(@NotNull JList list, Module value, int index, boolean selected, boolean hasFocus) { + setIcon(ModuleType.get(value).getIcon()); + append(value.getName(), SimpleTextAttributes.REGULAR_ATTRIBUTES); - final XmlFile pluginXml = PluginModuleType.getPluginXml(module); + XmlFile pluginXml = PluginModuleType.getPluginXml(value); assert pluginXml != null; - final VirtualFile virtualFile = pluginXml.getVirtualFile(); + VirtualFile virtualFile = pluginXml.getVirtualFile(); assert virtualFile != null; - final VirtualFile projectPath = myProject.getBaseDir(); + VirtualFile projectPath = myProject.getBaseDir(); assert projectPath != null; if (VfsUtilCore.isAncestor(projectPath, virtualFile, false)) { - append(" (" + VfsUtilCore.getRelativePath(virtualFile, projectPath, File.separatorChar) + ")", SimpleTextAttributes.GRAYED_ATTRIBUTES); + append(" (" + VfsUtilCore.getRelativePath(virtualFile, projectPath, File.separatorChar) + ")", + SimpleTextAttributes.GRAYED_ATTRIBUTES); } else { append(" (" + virtualFile.getPresentableUrl() + ")", SimpleTextAttributes.GRAYED_ATTRIBUTES); } @@ -85,7 +85,7 @@ class ChooseModulesDialogUtil { } public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { - return myCellRenderer.getListCellRendererComponent(myList, value, row, isSelected, hasFocus); + return myCellRenderer.getListCellRendererComponent(myList, (Module)value, row, isSelected, hasFocus); } } } From 4b781bfa89dcbec6bf93916c38bb2ff033e6c6ad Mon Sep 17 00:00:00 2001 From: Yaroslav Pankratyev Date: Thu, 3 Aug 2017 16:47:28 +0300 Subject: [PATCH 15/27] ServiceCreator test --- .../actions/service/NewServiceActionBase.java | 13 +- .../actions/newService/META-INF/plugin.xml | 7 + .../actions/service/ServiceCreatorTest.java | 152 ++++++++++++++++++ 3 files changed, 168 insertions(+), 4 deletions(-) create mode 100644 plugins/devkit/testData/actions/newService/META-INF/plugin.xml create mode 100644 plugins/devkit/testSources/actions/service/ServiceCreatorTest.java diff --git a/plugins/devkit/src/actions/service/NewServiceActionBase.java b/plugins/devkit/src/actions/service/NewServiceActionBase.java index 79afdb2599fb..fa2fbf4c8055 100644 --- a/plugins/devkit/src/actions/service/NewServiceActionBase.java +++ b/plugins/devkit/src/actions/service/NewServiceActionBase.java @@ -78,7 +78,7 @@ public abstract class NewServiceActionBase extends CreateInDirectoryActionBase i PsiDirectory dir = view.getOrChooseDirectory(); if (dir == null) return; - ServiceCreator serviceCreator = new ServiceCreator(dir, getInterfaceTemplateName(), getOnlyImplementationTemplateName()); + ServiceCreator serviceCreator = new ServiceCreator(dir, getInterfaceTemplateName(), getOnlyImplementationTemplateName(), getTagName()); PsiClass[] createdClasses = invokeDialog(project, serviceCreator, dir); if (createdClasses == null) { return; @@ -219,19 +219,24 @@ public abstract class NewServiceActionBase extends CreateInDirectoryActionBase i } - private class ServiceCreator { + static class ServiceCreator { // not private for testing purpose only private final Logger LOG = Logger.getInstance("#" + ServiceCreator.class.getCanonicalName()); private final PsiDirectory myDirectory; private final String myServiceInterfaceTemplateName; private final String myServiceOnlyImplementationTemplateName; + private final String myTagName; private PsiClass[] createdClasses = null; - ServiceCreator(PsiDirectory directory, String serviceInterfaceTemplateName, String serviceOnlyImplementationTemplateName) { + ServiceCreator(PsiDirectory directory, + String serviceInterfaceTemplateName, + String serviceOnlyImplementationTemplateName, + String tagName) { myDirectory = directory; this.myServiceInterfaceTemplateName = serviceInterfaceTemplateName; this.myServiceOnlyImplementationTemplateName = serviceOnlyImplementationTemplateName; + this.myTagName = tagName; } PsiClass[] getCreatedClasses() { @@ -313,7 +318,7 @@ public abstract class NewServiceActionBase extends CreateInDirectoryActionBase i .findAny() .orElseGet(() -> ideaPlugin.addExtensions()); - XmlTag serviceTag = targetExtensions.addExtension(Extensions.DEFAULT_PREFIX + "." + getTagName()).getXmlTag(); + XmlTag serviceTag = targetExtensions.addExtension(Extensions.DEFAULT_PREFIX + "." + myTagName).getXmlTag(); if (createdInterface != null) { serviceTag.setAttribute("serviceInterface", createdInterface.getQualifiedName()); } diff --git a/plugins/devkit/testData/actions/newService/META-INF/plugin.xml b/plugins/devkit/testData/actions/newService/META-INF/plugin.xml new file mode 100644 index 000000000000..1da555c15e7b --- /dev/null +++ b/plugins/devkit/testData/actions/newService/META-INF/plugin.xml @@ -0,0 +1,7 @@ + + test.plugin + Test Plugin + 1.0 + + + \ No newline at end of file diff --git a/plugins/devkit/testSources/actions/service/ServiceCreatorTest.java b/plugins/devkit/testSources/actions/service/ServiceCreatorTest.java new file mode 100644 index 000000000000..3b9f3be4833b --- /dev/null +++ b/plugins/devkit/testSources/actions/service/ServiceCreatorTest.java @@ -0,0 +1,152 @@ +/* + * Copyright 2000-2017 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.jetbrains.idea.devkit.actions.service; + +import com.intellij.openapi.application.PluginPathManager; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiDirectory; +import com.intellij.psi.PsiJavaCodeReferenceElement; +import com.intellij.psi.PsiReferenceList; +import com.intellij.psi.xml.XmlFile; +import com.intellij.psi.xml.XmlTag; +import com.intellij.testFramework.TestDataPath; +import com.intellij.testFramework.fixtures.JavaCodeInsightFixtureTestCase; +import com.intellij.util.xml.DomFileElement; +import com.intellij.util.xml.DomManager; +import org.jetbrains.idea.devkit.dom.Extensions; +import org.jetbrains.idea.devkit.dom.IdeaPlugin; +import org.jetbrains.idea.devkit.module.PluginModuleType; + +import java.util.List; + +@TestDataPath("$CONTENT_ROOT/testData/actions/newService/") +public class ServiceCreatorTest extends JavaCodeInsightFixtureTestCase { + @Override + protected String getBasePath() { + return PluginPathManager.getPluginHomePathRelative("devkit") + "/testData/actions/newService"; + } + + + public void testCreateApplicationServiceInterfaceAndImplementation() { + doTestCreateInterfaceAndImplementation("my.plugin.ApplicationServiceInterface", "my.plugin.impl.ApplicationServiceImpl", + "ApplicationServiceInterface.java", "applicationService"); + } + + public void testCreateProjectServiceInterfaceAndImplementation() { + doTestCreateInterfaceAndImplementation("my.plugin.ProjectServiceInterface", "my.plugin.impl.ProjectServiceImpl", + "ProjectServiceInterface.java", "projectService"); + } + + public void testCreateModuleServiceInterfaceAndImplementation() { + doTestCreateInterfaceAndImplementation("my.plugin.ModuleServiceInterface", "my.plugin.impl.ModuleServiceImpl", + "ModuleServiceInterface.java", "moduleService"); + } + + public void testCreateApplicationServiceOnlyImplementation() { + doTestCreateOnlyImplementation("my.plugin.ApplicationServiceClass", "ApplicationServiceClass.java", "applicationService"); + } + + public void testCreateProjectServiceOnlyImplementation() { + doTestCreateOnlyImplementation("my.plugin.ProjectServiceClass", "ProjectServiceClass.java", "projectService"); + } + + public void testCreateModuleServiceOnlyImplementation() { + doTestCreateOnlyImplementation("my.plugin.ModuleServiceClass", "ModuleServiceClass.java", "moduleService"); + } + + + private void doTestCreateInterfaceAndImplementation(String interfaceFqName, String implementationFqName, + String interfaceTemplate, String tagName) { + VirtualFile copied = myFixture.copyDirectoryToProject("", ""); + PsiDirectory dir = myFixture.getPsiManager().findDirectory(copied); + XmlFile pluginXml = PluginModuleType.getPluginXml(myFixture.getModule()); + + NewServiceActionBase.ServiceCreator creator = new NewServiceActionBase.ServiceCreator(dir, interfaceTemplate, null, tagName); + boolean created = creator.createInterfaceAndImplementation(interfaceFqName, implementationFqName, pluginXml); + assertTrue(created); + + PsiClass[] createdClasses = creator.getCreatedClasses(); + assertNotNull(createdClasses); + assertTrue(createdClasses.length == 2); + + PsiClass createdInterface = createdClasses[0]; + PsiClass createdImplementation = createdClasses[1]; + + assertEquals(interfaceFqName.substring(interfaceFqName.lastIndexOf(".") + 1), createdInterface.getName()); + assertEquals(implementationFqName.substring(implementationFqName.lastIndexOf(".") + 1), createdImplementation.getName()); + + PsiReferenceList implementsList = createdImplementation.getImplementsList(); + assertNotNull(implementsList); + PsiJavaCodeReferenceElement[] elements = implementsList.getReferenceElements(); + assertNotNull(elements); + PsiJavaCodeReferenceElement element = elements[0]; + assertEquals(interfaceFqName, element.getQualifiedName()); + + DomFileElement fileElement = DomManager.getDomManager(getProject()).getFileElement(pluginXml, IdeaPlugin.class); + assertNotNull(fileElement); + IdeaPlugin ideaPlugin = fileElement.getRootElement(); + List extensionsList = ideaPlugin.getExtensions(); + assertNotNull(extensionsList); + assertEquals(1, extensionsList.size()); + + XmlTag extensions = extensionsList.get(0).getXmlTag(); + assertNotNull(extensions); + XmlTag[] extensionTags = extensions.getSubTags(); + assertNotNull(extensionTags); + assertEquals(1, extensionTags.length); + + XmlTag serviceTag = extensionTags[0]; + assertEquals(tagName, serviceTag.getName()); + assertEquals(interfaceFqName, serviceTag.getAttributeValue("serviceInterface")); + assertEquals(implementationFqName, serviceTag.getAttributeValue("serviceImplementation")); + } + + private void doTestCreateOnlyImplementation(String implementationFqName, String classTemplate, String tagName) { + VirtualFile copied = myFixture.copyDirectoryToProject("", ""); + PsiDirectory dir = myFixture.getPsiManager().findDirectory(copied); + XmlFile pluginXml = PluginModuleType.getPluginXml(myFixture.getModule()); + + NewServiceActionBase.ServiceCreator creator = new NewServiceActionBase.ServiceCreator(dir, null, classTemplate, tagName); + boolean created = creator.createOnlyImplementation(implementationFqName, pluginXml); + assertTrue(created); + + PsiClass[] createdClasses = creator.getCreatedClasses(); + assertNotNull(createdClasses); + assertTrue(createdClasses.length == 1); + + PsiClass createdImplementation = createdClasses[0]; + assertEquals(implementationFqName.substring(implementationFqName.lastIndexOf(".") + 1), createdImplementation.getName()); + + DomFileElement fileElement = DomManager.getDomManager(getProject()).getFileElement(pluginXml, IdeaPlugin.class); + assertNotNull(fileElement); + IdeaPlugin ideaPlugin = fileElement.getRootElement(); + List extensionsList = ideaPlugin.getExtensions(); + assertNotNull(extensionsList); + assertEquals(1, extensionsList.size()); + + XmlTag extensions = extensionsList.get(0).getXmlTag(); + assertNotNull(extensions); + XmlTag[] extensionTags = extensions.getSubTags(); + assertNotNull(extensionTags); + assertEquals(1, extensionTags.length); + + XmlTag serviceTag = extensionTags[0]; + assertEquals(tagName, serviceTag.getName()); + assertNull(serviceTag.getAttributeValue("serviceInterface")); + assertEquals(implementationFqName, serviceTag.getAttributeValue("serviceImplementation")); + } +} From 1b91233891dcfc7c68d5003a99c9ac4a76b801c9 Mon Sep 17 00:00:00 2001 From: Yaroslav Pankratyev Date: Thu, 3 Aug 2017 17:02:41 +0300 Subject: [PATCH 16/27] Move CANCELED constant from DevkitActionsUtil to NewActionAction --- plugins/devkit/src/actions/DevkitActionsUtil.java | 4 ---- plugins/devkit/src/actions/NewActionAction.java | 6 +++++- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/devkit/src/actions/DevkitActionsUtil.java b/plugins/devkit/src/actions/DevkitActionsUtil.java index d755a75cd6cb..bab66228ee33 100644 --- a/plugins/devkit/src/actions/DevkitActionsUtil.java +++ b/plugins/devkit/src/actions/DevkitActionsUtil.java @@ -41,10 +41,6 @@ public final class DevkitActionsUtil { private DevkitActionsUtil() { } - // length == 1 is important to make MyInputValidator close the dialog when - // module selection is canceled. That's some weird interface actually... - public static final PsiClass[] CANCELED = new PsiClass[1]; - /** * @return selected plugin descriptor or null if canceled. diff --git a/plugins/devkit/src/actions/NewActionAction.java b/plugins/devkit/src/actions/NewActionAction.java index bd0d4c663c5c..ad044888e217 100644 --- a/plugins/devkit/src/actions/NewActionAction.java +++ b/plugins/devkit/src/actions/NewActionAction.java @@ -39,6 +39,10 @@ import org.jetbrains.idea.devkit.util.PsiUtil; import org.jetbrains.jps.model.java.JavaModuleSourceRootTypes; public class NewActionAction extends CreateElementActionBase implements DescriptorUtil.Patcher { + // length == 1 is important to make MyInputValidator close the dialog when + // module selection is canceled. That's some weird interface actually... + private static final PsiClass[] CANCELED = new PsiClass[1]; + private NewActionDialog myDialog; private XmlFile[] pluginDescriptorsToPatch; @@ -50,7 +54,7 @@ public class NewActionAction extends CreateElementActionBase implements Descript @Override protected final PsiElement[] invokeDialog(Project project, PsiDirectory directory) { PsiElement[] psiElements = doInvokeDialog(project, directory); - return psiElements == DevkitActionsUtil.CANCELED ? PsiElement.EMPTY_ARRAY : psiElements; + return psiElements == CANCELED ? PsiElement.EMPTY_ARRAY : psiElements; } private PsiElement[] doInvokeDialog(Project project, PsiDirectory directory) { From ba731f3f115788b1d150d045f1e87568bb7aef80 Mon Sep 17 00:00:00 2001 From: Yaroslav Pankratyev Date: Sun, 6 Aug 2017 17:08:32 +0700 Subject: [PATCH 17/27] Fix assertions in ServiceCreatorTest --- .../testSources/actions/service/ServiceCreatorTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/devkit/testSources/actions/service/ServiceCreatorTest.java b/plugins/devkit/testSources/actions/service/ServiceCreatorTest.java index 3b9f3be4833b..7faa64333ba9 100644 --- a/plugins/devkit/testSources/actions/service/ServiceCreatorTest.java +++ b/plugins/devkit/testSources/actions/service/ServiceCreatorTest.java @@ -81,7 +81,7 @@ public class ServiceCreatorTest extends JavaCodeInsightFixtureTestCase { PsiClass[] createdClasses = creator.getCreatedClasses(); assertNotNull(createdClasses); - assertTrue(createdClasses.length == 2); + assertSize(2, createdClasses); PsiClass createdInterface = createdClasses[0]; PsiClass createdImplementation = createdClasses[1]; @@ -107,7 +107,7 @@ public class ServiceCreatorTest extends JavaCodeInsightFixtureTestCase { assertNotNull(extensions); XmlTag[] extensionTags = extensions.getSubTags(); assertNotNull(extensionTags); - assertEquals(1, extensionTags.length); + assertSize(1, extensionTags); XmlTag serviceTag = extensionTags[0]; assertEquals(tagName, serviceTag.getName()); @@ -126,7 +126,7 @@ public class ServiceCreatorTest extends JavaCodeInsightFixtureTestCase { PsiClass[] createdClasses = creator.getCreatedClasses(); assertNotNull(createdClasses); - assertTrue(createdClasses.length == 1); + assertSize(1, createdClasses); PsiClass createdImplementation = createdClasses[0]; assertEquals(implementationFqName.substring(implementationFqName.lastIndexOf(".") + 1), createdImplementation.getName()); @@ -142,7 +142,7 @@ public class ServiceCreatorTest extends JavaCodeInsightFixtureTestCase { assertNotNull(extensions); XmlTag[] extensionTags = extensions.getSubTags(); assertNotNull(extensionTags); - assertEquals(1, extensionTags.length); + assertSize(1, extensionTags); XmlTag serviceTag = extensionTags[0]; assertEquals(tagName, serviceTag.getName()); From 62f6398c8d7534dc8c095871ae3abeef0e9e8322 Mon Sep 17 00:00:00 2001 From: Yaroslav Pankratyev Date: Wed, 9 Aug 2017 21:35:16 +0700 Subject: [PATCH 18/27] Refactor NewActionAction update/isAvailable; better Logger usage --- .../JavaCreateTemplateInPackageAction.java | 4 ++ .../CreateTemplateInPackageAction.java | 10 ++++- ...enerateComponentExternalizationAction.java | 2 +- .../devkit/src/actions/NewActionAction.java | 44 ++++++------------- .../actions/service/NewServiceActionBase.java | 2 +- 5 files changed, 28 insertions(+), 34 deletions(-) diff --git a/java/java-impl/src/com/intellij/ide/actions/JavaCreateTemplateInPackageAction.java b/java/java-impl/src/com/intellij/ide/actions/JavaCreateTemplateInPackageAction.java index 1859532ea8d7..acfd3507dafa 100644 --- a/java/java-impl/src/com/intellij/ide/actions/JavaCreateTemplateInPackageAction.java +++ b/java/java-impl/src/com/intellij/ide/actions/JavaCreateTemplateInPackageAction.java @@ -38,6 +38,10 @@ public abstract class JavaCreateTemplateInPackageAction ex @Override protected boolean checkPackageExists(PsiDirectory directory) { + return doCheckPackageExists(directory); + } + + public static boolean doCheckPackageExists(PsiDirectory directory) { PsiPackage pkg = JavaDirectoryService.getInstance().getPackage(directory); if (pkg == null) { return false; diff --git a/platform/lang-impl/src/com/intellij/ide/actions/CreateTemplateInPackageAction.java b/platform/lang-impl/src/com/intellij/ide/actions/CreateTemplateInPackageAction.java index 2d32ca5521ec..32b68252ceec 100644 --- a/platform/lang-impl/src/com/intellij/ide/actions/CreateTemplateInPackageAction.java +++ b/platform/lang-impl/src/com/intellij/ide/actions/CreateTemplateInPackageAction.java @@ -34,6 +34,7 @@ import org.jetbrains.jps.model.module.JpsModuleSourceRootType; import javax.swing.*; import java.util.Set; +import java.util.function.Function; /** * @author peter @@ -58,19 +59,24 @@ public abstract class CreateTemplateInPackageAction extend @Override protected boolean isAvailable(final DataContext dataContext) { + return isAvailable(dataContext, mySourceRootTypes, this::checkPackageExists); + } + + public static boolean isAvailable(DataContext dataContext, Set> sourceRootTypes, + Function checkPackageExists) { final Project project = CommonDataKeys.PROJECT.getData(dataContext); final IdeView view = LangDataKeys.IDE_VIEW.getData(dataContext); if (project == null || view == null || view.getDirectories().length == 0) { return false; } - if (mySourceRootTypes == null) { + if (sourceRootTypes == null) { return true; } ProjectFileIndex projectFileIndex = ProjectRootManager.getInstance(project).getFileIndex(); for (PsiDirectory dir : view.getDirectories()) { - if (projectFileIndex.isUnderSourceRootOfType(dir.getVirtualFile(), mySourceRootTypes) && checkPackageExists(dir)) { + if (projectFileIndex.isUnderSourceRootOfType(dir.getVirtualFile(), sourceRootTypes) && checkPackageExists.apply(dir)) { return true; } } diff --git a/plugins/devkit/src/actions/GenerateComponentExternalizationAction.java b/plugins/devkit/src/actions/GenerateComponentExternalizationAction.java index 7291f7196646..9c69bcf134dd 100644 --- a/plugins/devkit/src/actions/GenerateComponentExternalizationAction.java +++ b/plugins/devkit/src/actions/GenerateComponentExternalizationAction.java @@ -34,7 +34,7 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.devkit.DevKitBundle; public class GenerateComponentExternalizationAction extends AnAction { - private static final Logger LOG = Logger.getInstance("#" + GenerateComponentExternalizationAction.class.getCanonicalName()); + private static final Logger LOG = Logger.getInstance(GenerateComponentExternalizationAction.class); @NonNls private final static String BASE_COMPONENT = "com.intellij.openapi.components.BaseComponent"; @NonNls private final static String PERSISTENCE_STATE_COMPONENT = "com.intellij.openapi.components.PersistentStateComponent"; diff --git a/plugins/devkit/src/actions/NewActionAction.java b/plugins/devkit/src/actions/NewActionAction.java index ad044888e217..8d7cb030f61c 100644 --- a/plugins/devkit/src/actions/NewActionAction.java +++ b/plugins/devkit/src/actions/NewActionAction.java @@ -15,17 +15,14 @@ */ package org.jetbrains.idea.devkit.actions; -import com.intellij.ide.IdeView; import com.intellij.ide.actions.CreateElementActionBase; -import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.ide.actions.CreateTemplateInPackageAction; +import com.intellij.ide.actions.JavaCreateTemplateInPackageAction; +import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.actionSystem.LangDataKeys; -import com.intellij.openapi.actionSystem.Presentation; import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; -import com.intellij.openapi.roots.ProjectFileIndex; -import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.ui.DialogWrapper; -import com.intellij.psi.JavaDirectoryService; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiElement; @@ -79,31 +76,18 @@ public class NewActionAction extends CreateElementActionBase implements Descript } @Override - public void update(AnActionEvent e) { - super.update(e); - - Presentation presentation = e.getPresentation(); - if (presentation.isEnabled()) { - Project project = e.getProject(); - Module module = e.getData(LangDataKeys.MODULE); - if (project != null && module != null && - PsiUtil.isPluginModule(module)) { - IdeView view = e.getData(LangDataKeys.IDE_VIEW); - if (view != null) { - // from com.intellij.ide.actions.CreateClassAction.update() - ProjectFileIndex projectFileIndex = ProjectRootManager.getInstance(project).getFileIndex(); - PsiDirectory[] dirs = view.getDirectories(); - for (PsiDirectory dir : dirs) { - if (projectFileIndex.isUnderSourceRootOfType(dir.getVirtualFile(), JavaModuleSourceRootTypes.SOURCES) && - JavaDirectoryService.getInstance().getPackage(dir) != null) { - return; - } - } - } - } - - presentation.setEnabledAndVisible(false); + protected boolean isAvailable(DataContext dataContext) { + if (!super.isAvailable(dataContext)) { + return false; } + + Module module = dataContext.getData(LangDataKeys.MODULE); + if (module == null || !PsiUtil.isPluginModule(module)) { + return false; + } + + return CreateTemplateInPackageAction.isAvailable(dataContext, JavaModuleSourceRootTypes.SOURCES, + JavaCreateTemplateInPackageAction::doCheckPackageExists); } @Override diff --git a/plugins/devkit/src/actions/service/NewServiceActionBase.java b/plugins/devkit/src/actions/service/NewServiceActionBase.java index fa2fbf4c8055..519a2730e117 100644 --- a/plugins/devkit/src/actions/service/NewServiceActionBase.java +++ b/plugins/devkit/src/actions/service/NewServiceActionBase.java @@ -220,7 +220,7 @@ public abstract class NewServiceActionBase extends CreateInDirectoryActionBase i static class ServiceCreator { // not private for testing purpose only - private final Logger LOG = Logger.getInstance("#" + ServiceCreator.class.getCanonicalName()); + private final Logger LOG = Logger.getInstance(ServiceCreator.class); private final PsiDirectory myDirectory; private final String myServiceInterfaceTemplateName; From 00099c8783f5bcdb73ad1a733f8e97d229647500 Mon Sep 17 00:00:00 2001 From: Yaroslav Pankratyev Date: Wed, 9 Aug 2017 23:37:02 +0700 Subject: [PATCH 19/27] Remove support for patching multiple plugin descriptors --- .../ui/configuration/ChooseModulesDialog.java | 2 +- .../idea/devkit/DevKitBundle.properties | 3 +- .../devkit/src/actions/DevkitActionsUtil.java | 91 ++++++---------- .../devkit/src/actions/NewActionAction.java | 11 +- .../actions/service/NewServiceActionBase.java | 2 +- .../quickfix/AbstractRegisterFix.java | 28 ++--- plugins/devkit/src/util/DescriptorUtil.java | 22 ++-- .../choose/ChooseModulesDialogBase.java | 78 -------------- .../choose/ChooseModulesDialogUtil.java | 91 ---------------- .../choose/ChooseMultipleModulesDialog.java | 102 ------------------ .../choose/ChooseSingleModuleDialog.java | 83 -------------- 11 files changed, 55 insertions(+), 458 deletions(-) delete mode 100644 plugins/devkit/src/util/module/choose/ChooseModulesDialogBase.java delete mode 100644 plugins/devkit/src/util/module/choose/ChooseModulesDialogUtil.java delete mode 100644 plugins/devkit/src/util/module/choose/ChooseMultipleModulesDialog.java delete mode 100644 plugins/devkit/src/util/module/choose/ChooseSingleModuleDialog.java diff --git a/platform/lang-impl/src/com/intellij/openapi/roots/ui/configuration/ChooseModulesDialog.java b/platform/lang-impl/src/com/intellij/openapi/roots/ui/configuration/ChooseModulesDialog.java index dbb09dbc643b..fd1945f97732 100644 --- a/platform/lang-impl/src/com/intellij/openapi/roots/ui/configuration/ChooseModulesDialog.java +++ b/platform/lang-impl/src/com/intellij/openapi/roots/ui/configuration/ChooseModulesDialog.java @@ -36,7 +36,7 @@ public class ChooseModulesDialog extends ChooseElementsDialog { super(parent, items, title, description, true); } - public ChooseModulesDialog(final Project project, final List items, final String title, final String description) { + public ChooseModulesDialog(Project project, List items, String title, @Nullable String description) { super(project, items, title, description, true); } diff --git a/plugins/devkit/resources/org/jetbrains/idea/devkit/DevKitBundle.properties b/plugins/devkit/resources/org/jetbrains/idea/devkit/DevKitBundle.properties index 7eea94f39395..705d5a48bcc3 100644 --- a/plugins/devkit/resources/org/jetbrains/idea/devkit/DevKitBundle.properties +++ b/plugins/devkit/resources/org/jetbrains/idea/devkit/DevKitBundle.properties @@ -45,7 +45,7 @@ sdk.type.incorrect.common=Wrong SDK type for plugin module error.file.not.found=File not Found error.file.not.found.message=File ''{0}'' not found error.no.plugin.xml=No plugin.xml file found -error.plugin.xml.readonly=Read-only plugin.xml files cannot be processed: {0} +error.plugin.xml.readonly=Read-only plugin.xml file cannot be processed: {0} error.cannot.process.plugin.xml=Cannot process plugin descriptor file: {0} error.cannot.create.service.class=Cannot Create Service Class @@ -103,7 +103,6 @@ new.menu.module.service.description=Create New Module Service new.module.service.dialog.title=Create New Module Service select.plugin.module.to.patch=Select Plugin Module to Patch -select.plugin.modules.to.patch=Select Plugin Modules to Patch keyword.extend=extend keyword.implement=implement diff --git a/plugins/devkit/src/actions/DevkitActionsUtil.java b/plugins/devkit/src/actions/DevkitActionsUtil.java index bab66228ee33..b36dd8251dc4 100644 --- a/plugins/devkit/src/actions/DevkitActionsUtil.java +++ b/plugins/devkit/src/actions/DevkitActionsUtil.java @@ -16,13 +16,13 @@ package org.jetbrains.idea.devkit.actions; import com.intellij.ide.actions.CreateFileAction; -import com.intellij.openapi.actionSystem.Presentation; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.OrderEntry; import com.intellij.openapi.roots.ProjectFileIndex; import com.intellij.openapi.roots.ProjectRootManager; +import com.intellij.openapi.roots.ui.configuration.ChooseModulesDialog; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.JavaDirectoryService; import com.intellij.psi.PsiClass; @@ -32,8 +32,6 @@ import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.devkit.DevKitBundle; import org.jetbrains.idea.devkit.module.PluginModuleType; -import org.jetbrains.idea.devkit.util.module.choose.ChooseMultipleModulesDialog; -import org.jetbrains.idea.devkit.util.module.choose.ChooseSingleModuleDialog; import java.util.*; @@ -43,68 +41,50 @@ public final class DevkitActionsUtil { /** - * @return selected plugin descriptor or null if canceled. + * @return plugin descriptor for current module (if it's a plugin module) or plugin descriptor selected in dialog or null if cancelled. * @throws IncorrectOperationException if no plugin descriptors found. */ @Nullable - public static XmlFile showChooseModuleDialog(PsiDirectory directory, Presentation templatePresentation) { - XmlFile[] result = doShowChooseModulesDialog(false, directory, templatePresentation); - return result == null ? null : result[0]; - } - - /** - * @return selected plugin descriptors (never an empty array) or null if canceled. - * @throws IncorrectOperationException if no plugin descriptors found. - */ - @Nullable - public static XmlFile[] showChooseModulesDialog(PsiDirectory directory, Presentation templatePresentation) { - return doShowChooseModulesDialog(true, directory, templatePresentation); - } - - private static XmlFile[] doShowChooseModulesDialog(boolean multiple, PsiDirectory directory, Presentation templatePresentation) { + public static XmlFile choosePluginModuleDescriptor(PsiDirectory directory) { Project project = directory.getProject(); Module module = getModule(directory); - Set pluginXmlsToPatch = new HashSet<>(); + XmlFile currentModulePluginXml = PluginModuleType.getPluginXml(module); + if (currentModulePluginXml != null) { + return currentModulePluginXml; + } + if (module != null) { - addPluginModule(module, pluginXmlsToPatch); + List candidateModules = PluginModuleType.getCandidateModules(module); + Iterator it = candidateModules.iterator(); + while (it.hasNext()) { + Module m = it.next(); + if (PluginModuleType.getPluginXml(m) == null) it.remove(); + } - if (pluginXmlsToPatch.isEmpty()) { - List candidateModules = PluginModuleType.getCandidateModules(module); - Iterator it = candidateModules.iterator(); - while (it.hasNext()) { - Module m = it.next(); - if (PluginModuleType.getPluginXml(m) == null) it.remove(); - } + if (candidateModules.size() == 1) { + return PluginModuleType.getPluginXml(candidateModules.get(0)); + } - if (candidateModules.size() == 1) { - addPluginModule(candidateModules.get(0), pluginXmlsToPatch); - } else { - if (multiple) { - ChooseMultipleModulesDialog dialog = - new ChooseMultipleModulesDialog(project, candidateModules, templatePresentation.getDescription()); - if (!dialog.showAndGet()) { - return null; // canceled - } - List modules = dialog.getSelectedModules(); - modules.forEach(m -> addPluginModule(m, pluginXmlsToPatch)); - } else { - ChooseSingleModuleDialog dialog = - new ChooseSingleModuleDialog(project, candidateModules, templatePresentation.getDescription()); - if (!dialog.showAndGet()) { - return null; // canceled - } - addPluginModule(dialog.getSelectedModule(), pluginXmlsToPatch); - } - } + //TODO plugin.xml paths in dialog + ChooseModulesDialog chooseModulesDialog = new ChooseModulesDialog(project, candidateModules, + DevKitBundle.message("select.plugin.module.to.patch"), null); + chooseModulesDialog.setSingleSelectionMode(); + chooseModulesDialog.show(); + + List selectedModules = chooseModulesDialog.getChosenElements(); + if (selectedModules.isEmpty()) { + return null; // cancelled + } + + assert selectedModules.size() == 1; + XmlFile pluginXml = PluginModuleType.getPluginXml(selectedModules.get(0)); + if (pluginXml != null) { + return pluginXml; } } - if (pluginXmlsToPatch.isEmpty()) { - throw new IncorrectOperationException(DevKitBundle.message("error.no.plugin.xml")); - } - - return pluginXmlsToPatch.toArray(new XmlFile[pluginXmlsToPatch.size()]); + throw new IncorrectOperationException(DevKitBundle.message("error.no.plugin.xml")); } public static PsiClass createSingleClass(String name, String classTemplateName, PsiDirectory directory) { @@ -140,9 +120,4 @@ public final class DevkitActionsUtil { } return fileIndex.getModuleForFile(vFile); } - - private static void addPluginModule(Module module, Set pluginXmlsToPatch) { - XmlFile pluginXml = PluginModuleType.getPluginXml(module); - if (pluginXml != null) pluginXmlsToPatch.add(pluginXml); - } } diff --git a/plugins/devkit/src/actions/NewActionAction.java b/plugins/devkit/src/actions/NewActionAction.java index 8d7cb030f61c..62fbb7b130cd 100644 --- a/plugins/devkit/src/actions/NewActionAction.java +++ b/plugins/devkit/src/actions/NewActionAction.java @@ -41,7 +41,7 @@ public class NewActionAction extends CreateElementActionBase implements Descript private static final PsiClass[] CANCELED = new PsiClass[1]; private NewActionDialog myDialog; - private XmlFile[] pluginDescriptorsToPatch; + private XmlFile pluginDescriptorToPatch; public NewActionAction() { super(DevKitBundle.message("new.menu.action.text"), DevKitBundle.message("new.menu.action.description"), null); @@ -59,9 +59,8 @@ public class NewActionAction extends CreateElementActionBase implements Descript try { myDialog.show(); if (myDialog.getExitCode() == DialogWrapper.OK_EXIT_CODE) { - pluginDescriptorsToPatch = DevkitActionsUtil.showChooseModulesDialog(directory, getTemplatePresentation()); - if (pluginDescriptorsToPatch != null) { - assert pluginDescriptorsToPatch.length > 0; + pluginDescriptorToPatch = DevkitActionsUtil.choosePluginModuleDescriptor(directory); + if (pluginDescriptorToPatch != null) { MyInputValidator validator = new MyInputValidator(project, directory); // this actually runs the action to create the class from template validator.canClose(myDialog.getActionName()); @@ -71,7 +70,7 @@ public class NewActionAction extends CreateElementActionBase implements Descript return PsiElement.EMPTY_ARRAY; } finally { myDialog = null; - pluginDescriptorsToPatch = null; + pluginDescriptorToPatch = null; } } @@ -99,7 +98,7 @@ public class NewActionAction extends CreateElementActionBase implements Descript @Override protected PsiElement[] create(String newName, PsiDirectory directory) throws Exception { PsiClass createdClass = DevkitActionsUtil.createSingleClass(newName, "Action.java", directory); - DescriptorUtil.patchPluginXml(this, createdClass, pluginDescriptorsToPatch); + DescriptorUtil.patchPluginXml(this, createdClass, pluginDescriptorToPatch); return new PsiElement[]{createdClass}; } diff --git a/plugins/devkit/src/actions/service/NewServiceActionBase.java b/plugins/devkit/src/actions/service/NewServiceActionBase.java index 519a2730e117..6940502362e2 100644 --- a/plugins/devkit/src/actions/service/NewServiceActionBase.java +++ b/plugins/devkit/src/actions/service/NewServiceActionBase.java @@ -177,7 +177,7 @@ public abstract class NewServiceActionBase extends CreateInDirectoryActionBase i @Override protected void doOKAction() { - XmlFile pluginDescriptorToPatch = DevkitActionsUtil.showChooseModuleDialog(myDirectory, getTemplatePresentation()); + XmlFile pluginDescriptorToPatch = DevkitActionsUtil.choosePluginModuleDescriptor(myDirectory); if (pluginDescriptorToPatch == null) { return; // canceled } diff --git a/plugins/devkit/src/inspections/quickfix/AbstractRegisterFix.java b/plugins/devkit/src/inspections/quickfix/AbstractRegisterFix.java index 7ae518b80de9..99110d1addb1 100644 --- a/plugins/devkit/src/inspections/quickfix/AbstractRegisterFix.java +++ b/plugins/devkit/src/inspections/quickfix/AbstractRegisterFix.java @@ -33,11 +33,9 @@ import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.idea.devkit.DevKitBundle; +import org.jetbrains.idea.devkit.actions.DevkitActionsUtil; import org.jetbrains.idea.devkit.module.PluginModuleType; import org.jetbrains.idea.devkit.util.DescriptorUtil; -import org.jetbrains.idea.devkit.util.module.choose.ChooseMultipleModulesDialog; - -import java.util.List; abstract class AbstractRegisterFix implements LocalQuickFix, DescriptorUtil.Patcher { protected final SmartPsiElementPointer myPointer; @@ -87,33 +85,21 @@ abstract class AbstractRegisterFix implements LocalQuickFix, DescriptorUtil.Patc Runnable command = () -> { try { XmlFile pluginXml = PluginModuleType.getPluginXml(module); + if (pluginXml == null) { + pluginXml = DevkitActionsUtil.choosePluginModuleDescriptor(psiFile.getContainingDirectory()); + } + if (pluginXml != null) { DescriptorUtil.patchPluginXml(this, element, pluginXml); } - else { - List modules = PluginModuleType.getCandidateModules(module); - if (modules.size() > 1) { - ChooseMultipleModulesDialog dialog = new ChooseMultipleModulesDialog(project, modules, getName()); - if (!dialog.showAndGet()) { - return; - } - modules = dialog.getSelectedModules(); - } - XmlFile[] pluginXmls = new XmlFile[modules.size()]; - for (int i = 0; i < pluginXmls.length; i++) { - pluginXmls[i] = PluginModuleType.getPluginXml(modules.get(i)); - } - - DescriptorUtil.patchPluginXml(this, element, pluginXmls); - } CommandProcessor.getInstance().markCurrentCommandAsGlobal(project); - } - catch (IncorrectOperationException e) { + } catch (IncorrectOperationException e) { Messages.showMessageDialog(project, filterMessage(e.getMessage()), DevKitBundle.message("inspections.component.not.registered.quickfix.error", getType()), Messages.getErrorIcon()); } }; + CommandProcessor.getInstance().executeCommand(project, command, getName(), null); } } diff --git a/plugins/devkit/src/util/DescriptorUtil.java b/plugins/devkit/src/util/DescriptorUtil.java index 5b75ad03a987..b915bfb4d64f 100644 --- a/plugins/devkit/src/util/DescriptorUtil.java +++ b/plugins/devkit/src/util/DescriptorUtil.java @@ -34,9 +34,6 @@ import org.jetbrains.idea.devkit.DevKitBundle; import org.jetbrains.idea.devkit.dom.IdeaPlugin; import org.jetbrains.idea.devkit.module.PluginModuleType; -import java.util.Arrays; -import java.util.stream.Stream; - public final class DescriptorUtil { private DescriptorUtil() { } @@ -60,23 +57,18 @@ public final class DescriptorUtil { } } - public static void patchPluginXml(Patcher patcher, PsiClass klass, XmlFile... pluginXmls) throws IncorrectOperationException { - checkPluginXmlsWritable(klass.getProject(), pluginXmls); - - WriteAction.run((ThrowableRunnable)() -> { - for (XmlFile pluginXml : pluginXmls) { - patcher.patchPluginXml(pluginXml, klass); - } - }); + public static void patchPluginXml(Patcher patcher, PsiClass klass, XmlFile pluginXml) throws IncorrectOperationException { + checkPluginXmlsWritable(klass.getProject(), pluginXml); + WriteAction.run((ThrowableRunnable)() -> patcher.patchPluginXml(pluginXml, klass)); } - public static void checkPluginXmlsWritable(Project project, XmlFile... pluginXmls) { - VirtualFile[] files = Stream.of(pluginXmls).map(PsiFile::getVirtualFile).toArray(VirtualFile[]::new); + public static void checkPluginXmlsWritable(Project project, XmlFile pluginXml) { + VirtualFile file = pluginXml.getVirtualFile(); final ReadonlyStatusHandler readonlyStatusHandler = ReadonlyStatusHandler.getInstance(project); - final ReadonlyStatusHandler.OperationStatus status = readonlyStatusHandler.ensureFilesWritable(files); + final ReadonlyStatusHandler.OperationStatus status = readonlyStatusHandler.ensureFilesWritable(file); if (status.hasReadonlyFiles()) { - throw new IncorrectOperationException(DevKitBundle.message("error.plugin.xml.readonly", Arrays.toString(status.getReadonlyFiles()))); + throw new IncorrectOperationException(DevKitBundle.message("error.plugin.xml.readonly", status.getReadonlyFiles()[0])); } } diff --git a/plugins/devkit/src/util/module/choose/ChooseModulesDialogBase.java b/plugins/devkit/src/util/module/choose/ChooseModulesDialogBase.java deleted file mode 100644 index f5ab40eb4ddd..000000000000 --- a/plugins/devkit/src/util/module/choose/ChooseModulesDialogBase.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2000-2017 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.jetbrains.idea.devkit.util.module.choose; - -import com.intellij.openapi.module.Module; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.DialogWrapper; -import com.intellij.openapi.ui.Messages; -import com.intellij.openapi.ui.MultiLineLabelUI; -import com.intellij.ui.ScrollPaneFactory; -import com.intellij.util.ui.JBUI; -import com.intellij.util.ui.components.BorderLayoutPanel; -import org.jetbrains.annotations.Nullable; - -import javax.swing.*; -import java.awt.*; -import java.util.List; - -abstract class ChooseModulesDialogBase extends DialogWrapper { - private final List myCandidateModules; - private final Icon myIcon; - private final String myMessage; - - protected ChooseModulesDialogBase(Project project, List candidateModules, String title, String message) { - super(project, false); - setTitle(title); - - myCandidateModules = candidateModules; - myIcon = Messages.getQuestionIcon(); - myMessage = message; - } - - @Nullable - @Override - protected JComponent createCenterPanel() { - return null; - } - - @Nullable - @Override - protected JComponent createNorthPanel() { - BorderLayoutPanel panel = JBUI.Panels.simplePanel(15, 10); - JLabel iconLabel = new JLabel(myIcon); - panel.addToLeft(JBUI.Panels.simplePanel().addToTop(iconLabel)); - - BorderLayoutPanel messagePanel = JBUI.Panels.simplePanel(); - JLabel textLabel = new JLabel(myMessage); - textLabel.setBorder(JBUI.Borders.emptyBottom(5)); - textLabel.setUI(new MultiLineLabelUI()); - messagePanel.addToTop(textLabel); - panel.add(messagePanel, BorderLayout.CENTER); - - final JScrollPane jScrollPane = ScrollPaneFactory.createScrollPane(); - jScrollPane.setViewportView(getTable()); - jScrollPane.setPreferredSize(JBUI.size(300, 80)); - panel.addToBottom(jScrollPane); - return panel; - } - - protected List getCandidateModules() { - return myCandidateModules; - } - - protected abstract JTable getTable(); -} diff --git a/plugins/devkit/src/util/module/choose/ChooseModulesDialogUtil.java b/plugins/devkit/src/util/module/choose/ChooseModulesDialogUtil.java deleted file mode 100644 index 02b9f0b48322..000000000000 --- a/plugins/devkit/src/util/module/choose/ChooseModulesDialogUtil.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright 2000-2017 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.jetbrains.idea.devkit.util.module.choose; - -import com.intellij.openapi.module.Module; -import com.intellij.openapi.module.ModuleType; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.vfs.VfsUtilCore; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.xml.XmlFile; -import com.intellij.ui.ColoredListCellRenderer; -import com.intellij.ui.SimpleTextAttributes; -import com.intellij.ui.components.JBList; -import com.intellij.util.ui.JBUI; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.idea.devkit.module.PluginModuleType; - -import javax.swing.*; -import javax.swing.table.TableCellRenderer; -import java.awt.*; -import java.awt.event.KeyAdapter; -import java.awt.event.KeyEvent; -import java.io.File; - -final class ChooseModulesDialogUtil { - private ChooseModulesDialogUtil() { - } - - static void setupTable(@NotNull JTable table, @NotNull Project project, Runnable onEnter) { - table.setShowGrid(false); - table.setTableHeader(null); - table.setIntercellSpacing(JBUI.emptySize()); - table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - table.setDefaultRenderer(Module.class, new MyTableCellRenderer(project)); - table.addKeyListener(new KeyAdapter() { - public void keyTyped(KeyEvent e) { - if (e.getKeyCode() == KeyEvent.VK_ENTER || e.getKeyChar() == '\n') { - onEnter.run(); - } - } - }); - } - - private static class MyTableCellRenderer implements TableCellRenderer { - private final Project myProject; - private final JList myList; - private final ColoredListCellRenderer myCellRenderer; - - public MyTableCellRenderer(Project project) { - myProject = project; - myList = new JBList<>(); - myCellRenderer = new ColoredListCellRenderer() { - protected void customizeCellRenderer(@NotNull JList list, Module value, int index, boolean selected, boolean hasFocus) { - setIcon(ModuleType.get(value).getIcon()); - append(value.getName(), SimpleTextAttributes.REGULAR_ATTRIBUTES); - - XmlFile pluginXml = PluginModuleType.getPluginXml(value); - assert pluginXml != null; - - VirtualFile virtualFile = pluginXml.getVirtualFile(); - assert virtualFile != null; - VirtualFile projectPath = myProject.getBaseDir(); - assert projectPath != null; - if (VfsUtilCore.isAncestor(projectPath, virtualFile, false)) { - append(" (" + VfsUtilCore.getRelativePath(virtualFile, projectPath, File.separatorChar) + ")", - SimpleTextAttributes.GRAYED_ATTRIBUTES); - } else { - append(" (" + virtualFile.getPresentableUrl() + ")", SimpleTextAttributes.GRAYED_ATTRIBUTES); - } - } - }; - } - - public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { - return myCellRenderer.getListCellRendererComponent(myList, (Module)value, row, isSelected, hasFocus); - } - } -} diff --git a/plugins/devkit/src/util/module/choose/ChooseMultipleModulesDialog.java b/plugins/devkit/src/util/module/choose/ChooseMultipleModulesDialog.java deleted file mode 100644 index 3464d2b61ae4..000000000000 --- a/plugins/devkit/src/util/module/choose/ChooseMultipleModulesDialog.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright 2000-2017 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.jetbrains.idea.devkit.util.module.choose; - -import com.intellij.openapi.module.Module; -import com.intellij.openapi.project.Project; -import com.intellij.ui.TableUtil; -import com.intellij.ui.table.JBTable; -import org.jetbrains.idea.devkit.DevKitBundle; - -import javax.swing.*; -import javax.swing.event.TableModelEvent; -import javax.swing.event.TableModelListener; -import javax.swing.table.AbstractTableModel; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Iterator; -import java.util.List; - -public class ChooseMultipleModulesDialog extends ChooseModulesDialogBase { - private final JTable myTable; - private final boolean[] myStates; - - public ChooseMultipleModulesDialog(Project project, List candidateModules, String title) { - super(project, candidateModules, title, DevKitBundle.message("select.plugin.modules.to.patch")); - myTable = new JBTable(new AbstractTableModel() { - public int getRowCount() { - return getCandidateModules().size(); - } - - public int getColumnCount() { - return 2; - } - - public boolean isCellEditable(int rowIndex, int columnIndex) { - return columnIndex == 0; - } - - public void setValueAt(Object aValue, int rowIndex, int columnIndex) { - myStates[rowIndex] = (Boolean)aValue; - fireTableCellUpdated(rowIndex, columnIndex); - } - - public Class getColumnClass(int columnIndex) { - return columnIndex == 0 ? Boolean.class : Module.class; - } - - public Object getValueAt(int rowIndex, int columnIndex) { - return columnIndex == 0 ? myStates[rowIndex] : getCandidateModules().get(rowIndex); - } - }); - - ChooseModulesDialogUtil.setupTable(myTable, project, this::doOKAction); - TableUtil.setupCheckboxColumn(myTable, 0); - - myTable.getModel().addTableModelListener(new TableModelListener() { - public void tableChanged(TableModelEvent e) { - getOKAction().setEnabled(getSelectedModules().size() > 0); - } - }); - - myStates = new boolean[candidateModules.size()]; - Arrays.fill(myStates, true); - - init(); - } - - @Override - protected JTable getTable() { - return myTable; - } - - @Override - public JComponent getPreferredFocusedComponent() { - return myTable; - } - - public List getSelectedModules() { - final ArrayList list = new ArrayList<>(getCandidateModules()); - final Iterator modules = list.iterator(); - for (boolean b : myStates) { - modules.next(); - if (!b) { - modules.remove(); - } - } - return list; - } -} diff --git a/plugins/devkit/src/util/module/choose/ChooseSingleModuleDialog.java b/plugins/devkit/src/util/module/choose/ChooseSingleModuleDialog.java deleted file mode 100644 index 76bcc35c31cd..000000000000 --- a/plugins/devkit/src/util/module/choose/ChooseSingleModuleDialog.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2000-2017 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.jetbrains.idea.devkit.util.module.choose; - -import com.intellij.openapi.module.Module; -import com.intellij.openapi.project.Project; -import com.intellij.ui.table.JBTable; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.idea.devkit.DevKitBundle; - -import javax.swing.*; -import javax.swing.event.TableModelEvent; -import javax.swing.event.TableModelListener; -import javax.swing.table.AbstractTableModel; -import java.util.List; - -public class ChooseSingleModuleDialog extends ChooseModulesDialogBase { - private final JTable myTable; - - public ChooseSingleModuleDialog(Project project, List candidateModules, String title) { - super(project, candidateModules, title, DevKitBundle.message("select.plugin.module.to.patch")); - - myTable = new JBTable(new AbstractTableModel() { - public int getRowCount() { - return getCandidateModules().size(); - } - - public int getColumnCount() { - return 1; - } - - public Class getColumnClass(int columnIndex) { - return Module.class; - } - - public Object getValueAt(int rowIndex, int columnIndex) { - return getCandidateModules().get(rowIndex); - } - }); - - ChooseModulesDialogUtil.setupTable(myTable, project, this::doOKAction); - - myTable.getModel().addTableModelListener(new TableModelListener() { - public void tableChanged(TableModelEvent e) { - getOKAction().setEnabled(myTable.getSelectedRowCount() > 0); - } - }); - - init(); - } - - @Override - protected JTable getTable() { - return myTable; - } - - @Override - public JComponent getPreferredFocusedComponent() { - return myTable; - } - - @Nullable - public Module getSelectedModule() { - int selectedRow = myTable.getSelectedRow(); - if (selectedRow < 0) { - return null; - } - return (Module)myTable.getModel().getValueAt(selectedRow, 0); - } -} From 2eb13a615fb3e19a1e92b91b109f3d0c7f778de0 Mon Sep 17 00:00:00 2001 From: Yaroslav Pankratyev Date: Fri, 11 Aug 2017 14:57:16 +0700 Subject: [PATCH 20/27] Refactor MultiStateElementsChooser.MyElementColumnCellRenderer and related; support displaying item location in ChooseElementsDialog; use default ChoosePluginModuleDialog in DevKit --- .../importProject/ProjectLayoutPanel.java | 7 -- .../src/ChooseComponentsToExportDialog.java | 12 ---- .../ide/util/ChooseElementsDialog.java | 12 +++- .../ide/util/MultiStateElementsChooser.java | 66 +++++++++++-------- .../devkit/src/actions/DevkitActionsUtil.java | 29 +++++++- 5 files changed, 74 insertions(+), 52 deletions(-) diff --git a/java/idea-ui/src/com/intellij/ide/util/importProject/ProjectLayoutPanel.java b/java/idea-ui/src/com/intellij/ide/util/importProject/ProjectLayoutPanel.java index 34cd2e3b6f8a..01edd36d772b 100644 --- a/java/idea-ui/src/com/intellij/ide/util/importProject/ProjectLayoutPanel.java +++ b/java/idea-ui/src/com/intellij/ide/util/importProject/ProjectLayoutPanel.java @@ -459,9 +459,6 @@ abstract class ProjectLayoutPanel extends JPanel { public Icon getIcon() { return getElementIcon(file); } - public Color getColor() { - return null; - } }); } myChooser.selectElements(ContainerUtil.createMaybeSingletonList(ContainerUtil.getFirstItem(files))); @@ -527,9 +524,5 @@ abstract class ProjectLayoutPanel extends JPanel { public Icon getIcon() { return getElementIcon(myEntry); } - - public Color getColor() { - return null; - } } } diff --git a/platform/configuration-store-impl/src/ChooseComponentsToExportDialog.java b/platform/configuration-store-impl/src/ChooseComponentsToExportDialog.java index 221ee9942929..49d9d2c0e29c 100644 --- a/platform/configuration-store-impl/src/ChooseComponentsToExportDialog.java +++ b/platform/configuration-store-impl/src/ChooseComponentsToExportDialog.java @@ -237,18 +237,6 @@ public class ChooseComponentsToExportDialog extends DialogWrapper { private static class ComponentElementProperties implements ElementsChooser.ElementProperties { private final Set items = new THashSet<>(); - @Override - @Nullable - public Icon getIcon() { - return null; - } - - @Override - @Nullable - public Color getColor() { - return null; - } - public String toString() { Set names = new LinkedHashSet<>(); for (ExportableItem component : items) { diff --git a/platform/platform-impl/src/com/intellij/ide/util/ChooseElementsDialog.java b/platform/platform-impl/src/com/intellij/ide/util/ChooseElementsDialog.java index 3e00db61cd28..d1d1b5391664 100644 --- a/platform/platform-impl/src/com/intellij/ide/util/ChooseElementsDialog.java +++ b/platform/platform-impl/src/com/intellij/ide/util/ChooseElementsDialog.java @@ -100,6 +100,10 @@ public abstract class ChooseElementsDialog extends DialogWrapper { @Nullable protected abstract Icon getItemIcon(T item); + protected String getItemLocation(T item) { + return null; // default implementation + } + @NotNull public List getChosenElements() { return isOK() ? myChooser.getSelectedElements() : Collections.emptyList(); @@ -132,12 +136,16 @@ public abstract class ChooseElementsDialog extends DialogWrapper { private ElementsChooser.ElementProperties createElementProperties(final T item) { return new ElementsChooser.ElementProperties() { + @Override + @Nullable public Icon getIcon() { return getItemIcon(item); } - public Color getColor() { - return null; + @Override + @Nullable + public String getLocation() { + return getItemLocation(item); } }; } diff --git a/platform/platform-impl/src/com/intellij/ide/util/MultiStateElementsChooser.java b/platform/platform-impl/src/com/intellij/ide/util/MultiStateElementsChooser.java index 0d8de2e29548..83d8885c9b47 100644 --- a/platform/platform-impl/src/com/intellij/ide/util/MultiStateElementsChooser.java +++ b/platform/platform-impl/src/com/intellij/ide/util/MultiStateElementsChooser.java @@ -15,6 +15,7 @@ */ package com.intellij.ide.util; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.wm.IdeFocusManager; import com.intellij.ui.*; import com.intellij.ui.table.JBTable; @@ -317,9 +318,17 @@ public class MultiStateElementsChooser extends JPanel implements Component public interface ElementProperties { @Nullable - Icon getIcon(); + default Icon getIcon() { + return null; + } @Nullable - Color getColor(); + default Color getColor() { + return null; + } + @Nullable + default String getLocation() { + return null; + } } public void addElement(T element, final S markState, ElementProperties elementProperties) { @@ -643,37 +652,36 @@ public class MultiStateElementsChooser extends JPanel implements Component return null; } - private class MyElementColumnCellRenderer extends DefaultTableCellRenderer { + + private class MyElementColumnCellRenderer extends ColoredTableCellRenderer { @Override - public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { - final Color color = UIUtil.getTableFocusCellBackground(); - Component component; - T t = (T)value; - try { - UIManager.put(UIUtil.TABLE_FOCUS_CELL_BACKGROUND_PROPERTY, table.getSelectionBackground()); - component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); - setText(t != null ? getItemText(t) : ""); - if (component instanceof JLabel) { - ((JLabel)component).setBorder(noFocusBorder); + protected void customizeCellRenderer(JTable table, @Nullable Object value, boolean selected, boolean hasFocus, int row, int column) { + @SuppressWarnings("unchecked") T item = (T)value; + String text = item == null ? "" : getItemText(item); + append(text); + + ElementProperties properties = myElementToPropertiesMap.get(item); + + if (properties != null) { + String location = properties.getLocation(); + if (StringUtil.isNotEmpty(location)) { + append(" (" + location + ")", SimpleTextAttributes.GRAYED_ATTRIBUTES); } } - finally { - UIManager.put(UIUtil.TABLE_FOCUS_CELL_BACKGROUND_PROPERTY, color); + + setTransparentIconBackground(true); + Icon icon = properties != null ? properties.getIcon() : item != null ? getItemIcon(item) : null; + if (icon != null) { + setIcon(icon); } - final MyTableModel model = (MyTableModel)table.getModel(); - component.setEnabled(isSelected || (MultiStateElementsChooser.this.isEnabled() && - (!myColorUnmarkedElements || myMarkStateDescriptor.isMarked(model.getElementMarkState(row))))); - final ElementProperties properties = myElementToPropertiesMap.get(t); - if (component instanceof JLabel) { - final Icon icon = properties != null ? properties.getIcon() : t != null ? getItemIcon(t) : null; - JLabel label = (JLabel)component; - label.setIcon(icon); - label.setDisabledIcon(icon); - } - component.setForeground(properties != null && properties.getColor() != null ? - properties.getColor() : - isSelected ? table.getSelectionForeground() : table.getForeground()); - return component; + + setForeground(properties != null && properties.getColor() != null ? + properties.getColor() : + selected ? table.getSelectionForeground() : table.getForeground()); + + @SuppressWarnings("unchecked") MyTableModel model = (MyTableModel)table.getModel(); + setEnabled(selected || (MultiStateElementsChooser.this.isEnabled() && + (!myColorUnmarkedElements || myMarkStateDescriptor.isMarked(model.getElementMarkState(row))))); } } diff --git a/plugins/devkit/src/actions/DevkitActionsUtil.java b/plugins/devkit/src/actions/DevkitActionsUtil.java index b36dd8251dc4..46b1385b1cb5 100644 --- a/plugins/devkit/src/actions/DevkitActionsUtil.java +++ b/plugins/devkit/src/actions/DevkitActionsUtil.java @@ -23,6 +23,7 @@ import com.intellij.openapi.roots.OrderEntry; import com.intellij.openapi.roots.ProjectFileIndex; import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.roots.ui.configuration.ChooseModulesDialog; +import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.JavaDirectoryService; import com.intellij.psi.PsiClass; @@ -33,6 +34,7 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.devkit.DevKitBundle; import org.jetbrains.idea.devkit.module.PluginModuleType; +import java.io.File; import java.util.*; public final class DevkitActionsUtil { @@ -66,8 +68,7 @@ public final class DevkitActionsUtil { return PluginModuleType.getPluginXml(candidateModules.get(0)); } - //TODO plugin.xml paths in dialog - ChooseModulesDialog chooseModulesDialog = new ChooseModulesDialog(project, candidateModules, + ChoosePluginModuleDialog chooseModulesDialog = new ChoosePluginModuleDialog(project, candidateModules, DevKitBundle.message("select.plugin.module.to.patch"), null); chooseModulesDialog.setSingleSelectionMode(); chooseModulesDialog.show(); @@ -120,4 +121,28 @@ public final class DevkitActionsUtil { } return fileIndex.getModuleForFile(vFile); } + + + private static class ChoosePluginModuleDialog extends ChooseModulesDialog { + public ChoosePluginModuleDialog(Project project, List items, String title, @Nullable String description) { + super(project, items, title, description); + } + + @Override + protected String getItemLocation(Module item) { + XmlFile pluginXml = PluginModuleType.getPluginXml(item); + assert pluginXml != null; + + VirtualFile virtualFile = pluginXml.getVirtualFile(); + VirtualFile projectPath = item.getProject().getBaseDir(); + assert virtualFile != null; + assert projectPath != null; + + if (VfsUtilCore.isAncestor(projectPath, virtualFile, false)) { + return VfsUtilCore.getRelativePath(virtualFile, projectPath, File.separatorChar); + } else { + return virtualFile.getPresentableUrl(); + } + } + } } From 7da8e487f13571d40f747cf599a82e28686b9b89 Mon Sep 17 00:00:00 2001 From: Yaroslav Pankratyev Date: Tue, 15 Aug 2017 17:21:11 +0700 Subject: [PATCH 21/27] Handle absent plugin.xml on module choose --- plugins/devkit/src/actions/DevkitActionsUtil.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/plugins/devkit/src/actions/DevkitActionsUtil.java b/plugins/devkit/src/actions/DevkitActionsUtil.java index 46b1385b1cb5..80cb26f79d5b 100644 --- a/plugins/devkit/src/actions/DevkitActionsUtil.java +++ b/plugins/devkit/src/actions/DevkitActionsUtil.java @@ -15,6 +15,7 @@ */ package org.jetbrains.idea.devkit.actions; +import com.intellij.CommonBundle; import com.intellij.ide.actions.CreateFileAction; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleManager; @@ -23,6 +24,7 @@ import com.intellij.openapi.roots.OrderEntry; import com.intellij.openapi.roots.ProjectFileIndex; import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.roots.ui.configuration.ChooseModulesDialog; +import com.intellij.openapi.ui.Messages; import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.JavaDirectoryService; @@ -85,7 +87,9 @@ public final class DevkitActionsUtil { } } - throw new IncorrectOperationException(DevKitBundle.message("error.no.plugin.xml")); + Messages.showMessageDialog(project, DevKitBundle.message("error.no.plugin.xml"), + CommonBundle.getErrorTitle(), Messages.getErrorIcon()); + return null; } public static PsiClass createSingleClass(String name, String classTemplateName, PsiDirectory directory) { @@ -131,7 +135,9 @@ public final class DevkitActionsUtil { @Override protected String getItemLocation(Module item) { XmlFile pluginXml = PluginModuleType.getPluginXml(item); - assert pluginXml != null; + if (pluginXml == null) { + return null; + } VirtualFile virtualFile = pluginXml.getVirtualFile(); VirtualFile projectPath = item.getProject().getBaseDir(); @@ -140,9 +146,8 @@ public final class DevkitActionsUtil { if (VfsUtilCore.isAncestor(projectPath, virtualFile, false)) { return VfsUtilCore.getRelativePath(virtualFile, projectPath, File.separatorChar); - } else { - return virtualFile.getPresentableUrl(); } + return virtualFile.getPresentableUrl(); } } } From 26efdb4b21fc3fdd56eaf8f90fb827c4d62f050a Mon Sep 17 00:00:00 2001 From: Yaroslav Pankratyev Date: Wed, 16 Aug 2017 17:50:00 +0700 Subject: [PATCH 22/27] Javadoc for ChooseElementsDialog.getItemLocation() --- .../src/com/intellij/ide/util/ChooseElementsDialog.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/platform/platform-impl/src/com/intellij/ide/util/ChooseElementsDialog.java b/platform/platform-impl/src/com/intellij/ide/util/ChooseElementsDialog.java index d1d1b5391664..8b798f7f6801 100644 --- a/platform/platform-impl/src/com/intellij/ide/util/ChooseElementsDialog.java +++ b/platform/platform-impl/src/com/intellij/ide/util/ChooseElementsDialog.java @@ -100,6 +100,10 @@ public abstract class ChooseElementsDialog extends DialogWrapper { @Nullable protected abstract Icon getItemIcon(T item); + /** + * Override this method and return non-null value to specify location of {@code item}. + * It will be shown as grayed text next to the {@link #getItemText(T) item text}. + */ protected String getItemLocation(T item) { return null; // default implementation } From fa593c5ff37c0e8479722391968a76bddef617e0 Mon Sep 17 00:00:00 2001 From: Yaroslav Pankratyev Date: Thu, 17 Aug 2017 17:05:23 +0700 Subject: [PATCH 23/27] Restore implicitly used properties in DevKit bundle --- .../org/jetbrains/idea/devkit/DevKitBundle.properties | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/plugins/devkit/resources/org/jetbrains/idea/devkit/DevKitBundle.properties b/plugins/devkit/resources/org/jetbrains/idea/devkit/DevKitBundle.properties index 705d5a48bcc3..bd779f7644dc 100644 --- a/plugins/devkit/resources/org/jetbrains/idea/devkit/DevKitBundle.properties +++ b/plugins/devkit/resources/org/jetbrains/idea/devkit/DevKitBundle.properties @@ -4,6 +4,9 @@ module.description=Plugin modules are used for developing plugins for Intelli They provide IntelliJ Platform Plugin SDK and Run Configuration for running and debugging plugins inside an IDE. plugin.descriptor=IntelliJ Platform Plugin Descriptor +# suppress inspection "UnusedProperty" +group.PluginDeployActions.text=Plugin Deployment Actions + #Module Editor - Deployment deployment.title=Plugin Deployment deployment.cleanup=Clean Up {0} Directory @@ -19,6 +22,10 @@ vm.parameters=&VM Options program.parameters=&Program Arguments #Prepare for deployment action +# suppress inspection "UnusedProperty" +action.MakeJarAction.text=Prepare To Deploy +# suppress inspection "UnusedProperty" +action.MakeAllJarsAction.text=Prepare All Plugins To Deploy select.plugin.modules.title=Select modules select.plugin.modules.description=Select modules to be prepared for deployment prepare.for.deployment.common=Preparing For Deployment From cc13955876be6cd1f3f60d32d0e32687173dd67b Mon Sep 17 00:00:00 2001 From: Yaroslav Pankratyev Date: Tue, 22 Aug 2017 17:41:30 +0700 Subject: [PATCH 24/27] Handle null plugin.xml or project basedir VirtualFile in ChoosePluginModuleDialog --- .../devkit/src/actions/DevkitActionsUtil.java | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/plugins/devkit/src/actions/DevkitActionsUtil.java b/plugins/devkit/src/actions/DevkitActionsUtil.java index 80cb26f79d5b..5ccd8213c91d 100644 --- a/plugins/devkit/src/actions/DevkitActionsUtil.java +++ b/plugins/devkit/src/actions/DevkitActionsUtil.java @@ -17,6 +17,7 @@ package org.jetbrains.idea.devkit.actions; import com.intellij.CommonBundle; import com.intellij.ide.actions.CreateFileAction; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleManager; import com.intellij.openapi.project.Project; @@ -40,6 +41,8 @@ import java.io.File; import java.util.*; public final class DevkitActionsUtil { + private static final Logger LOG = Logger.getInstance(DevkitActionsUtil.class); + private DevkitActionsUtil() { } @@ -141,8 +144,18 @@ public final class DevkitActionsUtil { VirtualFile virtualFile = pluginXml.getVirtualFile(); VirtualFile projectPath = item.getProject().getBaseDir(); - assert virtualFile != null; - assert projectPath != null; + + boolean shouldReturnNull = false; + if (virtualFile == null) { + LOG.warn("Unexpected null plugin.xml VirtualFile for module: " + item); + shouldReturnNull = true; + } + if (projectPath == null) { + LOG.warn("Unexpected null project basedir VirtualFile for module: " + item); + shouldReturnNull = true; + } + if (shouldReturnNull) return null; + if (VfsUtilCore.isAncestor(projectPath, virtualFile, false)) { return VfsUtilCore.getRelativePath(virtualFile, projectPath, File.separatorChar); From 6c2febc37effb4709f8e22571f5a0a45a0e0f112 Mon Sep 17 00:00:00 2001 From: Yaroslav Pankratyev Date: Thu, 24 Aug 2017 15:31:08 +0700 Subject: [PATCH 25/27] Service implementation templates, constructors in project/module service templates --- .../ApplicationServiceImplementation.java.ft | 8 ++++ ...ApplicationServiceImplementation.java.html | 26 ++++++++++ .../j2ee/ModuleServiceClass.java.ft | 3 ++ .../j2ee/ModuleServiceImplementation.java.ft | 10 ++++ .../ModuleServiceImplementation.java.html | 26 ++++++++++ .../j2ee/ProjectServiceClass.java.ft | 3 ++ .../j2ee/ProjectServiceImplementation.java.ft | 10 ++++ .../ProjectServiceImplementation.java.html | 26 ++++++++++ .../src/DevKitFileTemplatesFactory.java | 3 ++ .../devkit/src/actions/DevkitActionsUtil.java | 8 +++- .../devkit/src/actions/NewActionAction.java | 2 +- .../service/NewApplicationServiceAction.java | 5 ++ .../service/NewModuleServiceAction.java | 5 ++ .../service/NewProjectServiceAction.java | 5 ++ .../actions/service/NewServiceActionBase.java | 48 ++++++++++++------- .../actions/service/ServiceCreatorTest.java | 14 +++--- 16 files changed, 175 insertions(+), 27 deletions(-) create mode 100644 plugins/devkit/resources/fileTemplates/j2ee/ApplicationServiceImplementation.java.ft create mode 100644 plugins/devkit/resources/fileTemplates/j2ee/ApplicationServiceImplementation.java.html create mode 100644 plugins/devkit/resources/fileTemplates/j2ee/ModuleServiceImplementation.java.ft create mode 100644 plugins/devkit/resources/fileTemplates/j2ee/ModuleServiceImplementation.java.html create mode 100644 plugins/devkit/resources/fileTemplates/j2ee/ProjectServiceImplementation.java.ft create mode 100644 plugins/devkit/resources/fileTemplates/j2ee/ProjectServiceImplementation.java.html diff --git a/plugins/devkit/resources/fileTemplates/j2ee/ApplicationServiceImplementation.java.ft b/plugins/devkit/resources/fileTemplates/j2ee/ApplicationServiceImplementation.java.ft new file mode 100644 index 000000000000..1f643380176d --- /dev/null +++ b/plugins/devkit/resources/fileTemplates/j2ee/ApplicationServiceImplementation.java.ft @@ -0,0 +1,8 @@ +#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME};#end +#if(${INTERFACE_PACKAGE_NAME} && ${INTERFACE_PACKAGE_NAME} != "") +import ${INTERFACE_PACKAGE_NAME}.${INTERFACE_NAME};#end + +#parse("File Header.java") +public class ${NAME} implements ${INTERFACE_NAME} { + +} diff --git a/plugins/devkit/resources/fileTemplates/j2ee/ApplicationServiceImplementation.java.html b/plugins/devkit/resources/fileTemplates/j2ee/ApplicationServiceImplementation.java.html new file mode 100644 index 000000000000..d75d13fa6d94 --- /dev/null +++ b/plugins/devkit/resources/fileTemplates/j2ee/ApplicationServiceImplementation.java.html @@ -0,0 +1,26 @@ + + + + + + +
This is a built-in template used each time you create + a new IntelliJ Platform application-level service interface implementation. +
+ + + + + + + + + + + + + + +
Predefined variables will take the following values:
${INTERFACE_NAME} service interface short name.
${INTERFACE_PACKAGE_NAME} service interface package or an empty string if interface is placed in the same package that implementation.
+ + \ No newline at end of file diff --git a/plugins/devkit/resources/fileTemplates/j2ee/ModuleServiceClass.java.ft b/plugins/devkit/resources/fileTemplates/j2ee/ModuleServiceClass.java.ft index 4883ed3f87ee..57a642afe7ff 100644 --- a/plugins/devkit/resources/fileTemplates/j2ee/ModuleServiceClass.java.ft +++ b/plugins/devkit/resources/fileTemplates/j2ee/ModuleServiceClass.java.ft @@ -6,6 +6,9 @@ import org.jetbrains.annotations.NotNull; #parse("File Header.java") public class ${NAME} { + public ${NAME}(Module module) { + } + public static ${NAME} getInstance(@NotNull Module module) { return ModuleServiceManager.getService(module, ${NAME}.class); } diff --git a/plugins/devkit/resources/fileTemplates/j2ee/ModuleServiceImplementation.java.ft b/plugins/devkit/resources/fileTemplates/j2ee/ModuleServiceImplementation.java.ft new file mode 100644 index 000000000000..bbae6db44471 --- /dev/null +++ b/plugins/devkit/resources/fileTemplates/j2ee/ModuleServiceImplementation.java.ft @@ -0,0 +1,10 @@ +#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME};#end +#if(${INTERFACE_PACKAGE_NAME} && ${INTERFACE_PACKAGE_NAME} != "") +import ${INTERFACE_PACKAGE_NAME}.${INTERFACE_NAME};#end +import com.intellij.openapi.module.Module; + +#parse("File Header.java") +public class ${NAME} implements ${INTERFACE_NAME} { + public ${NAME}(Module project) { + } +} diff --git a/plugins/devkit/resources/fileTemplates/j2ee/ModuleServiceImplementation.java.html b/plugins/devkit/resources/fileTemplates/j2ee/ModuleServiceImplementation.java.html new file mode 100644 index 000000000000..f75d0e67825f --- /dev/null +++ b/plugins/devkit/resources/fileTemplates/j2ee/ModuleServiceImplementation.java.html @@ -0,0 +1,26 @@ + + + + + + +
This is a built-in template used each time you create + a new IntelliJ Platform module-level service interface implementation. +
+ + + + + + + + + + + + + + +
Predefined variables will take the following values:
${INTERFACE_NAME} service interface short name.
${INTERFACE_PACKAGE_NAME} service interface package or an empty string if interface is placed in the same package that implementation.
+ + \ No newline at end of file diff --git a/plugins/devkit/resources/fileTemplates/j2ee/ProjectServiceClass.java.ft b/plugins/devkit/resources/fileTemplates/j2ee/ProjectServiceClass.java.ft index c84da0dc8860..147e64e3ad62 100644 --- a/plugins/devkit/resources/fileTemplates/j2ee/ProjectServiceClass.java.ft +++ b/plugins/devkit/resources/fileTemplates/j2ee/ProjectServiceClass.java.ft @@ -6,6 +6,9 @@ import org.jetbrains.annotations.NotNull; #parse("File Header.java") public class ${NAME} { + public ${NAME}(Project project) { + } + public static ${NAME} getInstance(@NotNull Project project) { return ServiceManager.getService(project, ${NAME}.class); } diff --git a/plugins/devkit/resources/fileTemplates/j2ee/ProjectServiceImplementation.java.ft b/plugins/devkit/resources/fileTemplates/j2ee/ProjectServiceImplementation.java.ft new file mode 100644 index 000000000000..29230c8fb895 --- /dev/null +++ b/plugins/devkit/resources/fileTemplates/j2ee/ProjectServiceImplementation.java.ft @@ -0,0 +1,10 @@ +#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME};#end +#if(${INTERFACE_PACKAGE_NAME} && ${INTERFACE_PACKAGE_NAME} != "") +import ${INTERFACE_PACKAGE_NAME}.${INTERFACE_NAME};#end +import com.intellij.openapi.project.Project; + +#parse("File Header.java") +public class ${NAME} implements ${INTERFACE_NAME} { + public ${NAME}(Project project) { + } +} diff --git a/plugins/devkit/resources/fileTemplates/j2ee/ProjectServiceImplementation.java.html b/plugins/devkit/resources/fileTemplates/j2ee/ProjectServiceImplementation.java.html new file mode 100644 index 000000000000..741c8099afde --- /dev/null +++ b/plugins/devkit/resources/fileTemplates/j2ee/ProjectServiceImplementation.java.html @@ -0,0 +1,26 @@ + + + + + + +
This is a built-in template used each time you create + a new IntelliJ Platform project-level service interface implementation. +
+ + + + + + + + + + + + + + +
Predefined variables will take the following values:
${INTERFACE_NAME} service interface short name.
${INTERFACE_PACKAGE_NAME} service interface package or an empty string if interface is placed in the same package that implementation.
+ + \ No newline at end of file diff --git a/plugins/devkit/src/DevKitFileTemplatesFactory.java b/plugins/devkit/src/DevKitFileTemplatesFactory.java index 310fc3e92ec5..a8bf2da2c093 100644 --- a/plugins/devkit/src/DevKitFileTemplatesFactory.java +++ b/plugins/devkit/src/DevKitFileTemplatesFactory.java @@ -29,10 +29,13 @@ public class DevKitFileTemplatesFactory implements FileTemplateGroupDescriptorFa descriptor.addTemplate(new FileTemplateDescriptor("plugin.xml", StdFileTypes.XML.getIcon())); descriptor.addTemplate(new FileTemplateDescriptor("ProjectServiceClass.java", StdFileTypes.JAVA.getIcon())); descriptor.addTemplate(new FileTemplateDescriptor("ProjectServiceInterface.java", StdFileTypes.JAVA.getIcon())); + descriptor.addTemplate(new FileTemplateDescriptor("ProjectServiceImplementation.java", StdFileTypes.JAVA.getIcon())); descriptor.addTemplate(new FileTemplateDescriptor("ApplicationServiceClass.java", StdFileTypes.JAVA.getIcon())); descriptor.addTemplate(new FileTemplateDescriptor("ApplicationServiceInterface.java", StdFileTypes.JAVA.getIcon())); + descriptor.addTemplate(new FileTemplateDescriptor("ApplicationServiceImplementation.java", StdFileTypes.JAVA.getIcon())); descriptor.addTemplate(new FileTemplateDescriptor("ModuleServiceClass.java", StdFileTypes.JAVA.getIcon())); descriptor.addTemplate(new FileTemplateDescriptor("ModuleServiceInterface.java", StdFileTypes.JAVA.getIcon())); + descriptor.addTemplate(new FileTemplateDescriptor("ModuleServiceImplementation.java", StdFileTypes.JAVA.getIcon())); descriptor.addTemplate(new FileTemplateDescriptor("Action.java", StdFileTypes.JAVA.getIcon())); descriptor.addTemplate(new FileTemplateDescriptor("InspectionDescription.html", StdFileTypes.HTML.getIcon())); return descriptor; diff --git a/plugins/devkit/src/actions/DevkitActionsUtil.java b/plugins/devkit/src/actions/DevkitActionsUtil.java index 5ccd8213c91d..d561241e14ba 100644 --- a/plugins/devkit/src/actions/DevkitActionsUtil.java +++ b/plugins/devkit/src/actions/DevkitActionsUtil.java @@ -95,7 +95,8 @@ public final class DevkitActionsUtil { return null; } - public static PsiClass createSingleClass(String name, String classTemplateName, PsiDirectory directory) { + public static PsiClass createSingleClass(String name, String classTemplateName, PsiDirectory directory, + @Nullable Map properties) { if (name.contains(".")) { String[] names = name.split("\\."); for (int i = 0; i < names.length - 1; i++) { @@ -104,7 +105,10 @@ public final class DevkitActionsUtil { name = names[names.length - 1]; } - return JavaDirectoryService.getInstance().createClass(directory, name, classTemplateName); + if (properties == null) { + properties = Collections.emptyMap(); + } + return JavaDirectoryService.getInstance().createClass(directory, name, classTemplateName, false, properties); } @Nullable diff --git a/plugins/devkit/src/actions/NewActionAction.java b/plugins/devkit/src/actions/NewActionAction.java index 62fbb7b130cd..ea56efdda248 100644 --- a/plugins/devkit/src/actions/NewActionAction.java +++ b/plugins/devkit/src/actions/NewActionAction.java @@ -97,7 +97,7 @@ public class NewActionAction extends CreateElementActionBase implements Descript @NotNull @Override protected PsiElement[] create(String newName, PsiDirectory directory) throws Exception { - PsiClass createdClass = DevkitActionsUtil.createSingleClass(newName, "Action.java", directory); + PsiClass createdClass = DevkitActionsUtil.createSingleClass(newName, "Action.java", directory, null); DescriptorUtil.patchPluginXml(this, createdClass, pluginDescriptorToPatch); return new PsiElement[]{createdClass}; } diff --git a/plugins/devkit/src/actions/service/NewApplicationServiceAction.java b/plugins/devkit/src/actions/service/NewApplicationServiceAction.java index fdd4a08efc87..8bbeb5815c3f 100644 --- a/plugins/devkit/src/actions/service/NewApplicationServiceAction.java +++ b/plugins/devkit/src/actions/service/NewApplicationServiceAction.java @@ -38,6 +38,11 @@ public class NewApplicationServiceAction extends NewServiceActionBase { return "ApplicationServiceInterface.java"; } + @Override + protected String getImplementationTemplateName() { + return "ApplicationServiceImplementation.java"; + } + @Override protected String getDialogTitle() { return DevKitBundle.message("new.application.service.dialog.title"); diff --git a/plugins/devkit/src/actions/service/NewModuleServiceAction.java b/plugins/devkit/src/actions/service/NewModuleServiceAction.java index b4b11a4d9ddc..5f25fd643ff3 100644 --- a/plugins/devkit/src/actions/service/NewModuleServiceAction.java +++ b/plugins/devkit/src/actions/service/NewModuleServiceAction.java @@ -38,6 +38,11 @@ public class NewModuleServiceAction extends NewServiceActionBase { return "ModuleServiceInterface.java"; } + @Override + protected String getImplementationTemplateName() { + return "ModuleServiceImplementation.java"; + } + @Override protected String getDialogTitle() { return DevKitBundle.message("new.module.service.dialog.title"); diff --git a/plugins/devkit/src/actions/service/NewProjectServiceAction.java b/plugins/devkit/src/actions/service/NewProjectServiceAction.java index 8c2ed2503cde..268a72994931 100644 --- a/plugins/devkit/src/actions/service/NewProjectServiceAction.java +++ b/plugins/devkit/src/actions/service/NewProjectServiceAction.java @@ -38,6 +38,11 @@ public class NewProjectServiceAction extends NewServiceActionBase { return "ProjectServiceInterface.java"; } + @Override + protected String getImplementationTemplateName() { + return "ProjectServiceImplementation.java"; + } + @Override protected String getDialogTitle() { return DevKitBundle.message("new.project.service.dialog.title"); diff --git a/plugins/devkit/src/actions/service/NewServiceActionBase.java b/plugins/devkit/src/actions/service/NewServiceActionBase.java index 6940502362e2..83cdea182743 100644 --- a/plugins/devkit/src/actions/service/NewServiceActionBase.java +++ b/plugins/devkit/src/actions/service/NewServiceActionBase.java @@ -20,7 +20,6 @@ import com.intellij.ide.IdeBundle; import com.intellij.ide.IdeView; import com.intellij.ide.actions.CreateInDirectoryActionBase; import com.intellij.ide.actions.ElementCreator; -import com.intellij.ide.fileTemplates.JavaTemplateUtil; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.LangDataKeys; import com.intellij.openapi.application.Result; @@ -33,11 +32,13 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.psi.*; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiDirectory; import com.intellij.psi.xml.XmlFile; import com.intellij.psi.xml.XmlTag; import com.intellij.ui.DocumentAdapter; import com.intellij.util.IncorrectOperationException; +import com.intellij.util.containers.ContainerUtil; import com.intellij.util.xml.DomFileElement; import com.intellij.util.xml.DomManager; import com.intellij.xml.util.IncludedXmlTag; @@ -78,7 +79,8 @@ public abstract class NewServiceActionBase extends CreateInDirectoryActionBase i PsiDirectory dir = view.getOrChooseDirectory(); if (dir == null) return; - ServiceCreator serviceCreator = new ServiceCreator(dir, getInterfaceTemplateName(), getOnlyImplementationTemplateName(), getTagName()); + ServiceCreator serviceCreator = new ServiceCreator(dir, getInterfaceTemplateName(), getImplementationTemplateName(), + getOnlyImplementationTemplateName(), getTagName()); PsiClass[] createdClasses = invokeDialog(project, serviceCreator, dir); if (createdClasses == null) { return; @@ -100,6 +102,7 @@ public abstract class NewServiceActionBase extends CreateInDirectoryActionBase i protected abstract String getOnlyImplementationTemplateName(); protected abstract String getInterfaceTemplateName(); + protected abstract String getImplementationTemplateName(); protected abstract String getDialogTitle(); @@ -220,10 +223,13 @@ public abstract class NewServiceActionBase extends CreateInDirectoryActionBase i static class ServiceCreator { // not private for testing purpose only - private final Logger LOG = Logger.getInstance(ServiceCreator.class); + private static final Logger LOG = Logger.getInstance(ServiceCreator.class); + private static final String INTERFACE_NAME_PROPERTY = "INTERFACE_NAME"; + private static final String INTERFACE_PACKAGE_PROPERTY = "INTERFACE_PACKAGE_NAME"; private final PsiDirectory myDirectory; private final String myServiceInterfaceTemplateName; + private final String myServiceImplementationTemplateName; private final String myServiceOnlyImplementationTemplateName; private final String myTagName; @@ -231,12 +237,14 @@ public abstract class NewServiceActionBase extends CreateInDirectoryActionBase i ServiceCreator(PsiDirectory directory, String serviceInterfaceTemplateName, + String serviceImplementationTemplateName, String serviceOnlyImplementationTemplateName, String tagName) { myDirectory = directory; - this.myServiceInterfaceTemplateName = serviceInterfaceTemplateName; - this.myServiceOnlyImplementationTemplateName = serviceOnlyImplementationTemplateName; - this.myTagName = tagName; + myServiceInterfaceTemplateName = serviceInterfaceTemplateName; + myServiceImplementationTemplateName = serviceImplementationTemplateName; + myServiceOnlyImplementationTemplateName = serviceOnlyImplementationTemplateName; + myTagName = tagName; } PsiClass[] getCreatedClasses() { @@ -246,19 +254,23 @@ public abstract class NewServiceActionBase extends CreateInDirectoryActionBase i /** * @return whether the service was created (which indicates whether the create service dialog can be closed). */ - @SuppressWarnings("ConstantConditions") // no NPE here since created classes are not anonymous boolean createInterfaceAndImplementation(String interfaceName, String implementationName, XmlFile pluginXml) { return doCreateService(() -> { - PsiClass createdInterface = DevkitActionsUtil.createSingleClass(interfaceName, myServiceInterfaceTemplateName, myDirectory); - PsiClass createdImplementation = DevkitActionsUtil.createSingleClass( - implementationName, JavaTemplateUtil.INTERNAL_CLASS_TEMPLATE_NAME, myDirectory); + PsiClass createdInterface = DevkitActionsUtil.createSingleClass(interfaceName, myServiceInterfaceTemplateName, myDirectory, null); - // make service implementation implement service interface - JavaPsiFacade facade = JavaPsiFacade.getInstance(getProject()); - PsiElementFactory factory = facade.getElementFactory(); - PsiJavaCodeReferenceElement interfaceReference = - factory.createReferenceElementByFQClassName(createdInterface.getQualifiedName(), createdImplementation.getResolveScope()); - createdImplementation.getImplementsList().add(interfaceReference); + String interfaceShortName = createdInterface.getName(); + String implementationDirRelativePackage = StringUtil.getPackageName(implementationName); + String interfacePackage; + if (implementationDirRelativePackage.isEmpty()) { + interfacePackage = ""; // interface and implementation are placed in the same package; there shouldn't be an import statement + } else { + //noinspection ConstantConditions + interfacePackage = StringUtil.getPackageName(createdInterface.getQualifiedName()); + } + + PsiClass createdImplementation = DevkitActionsUtil.createSingleClass( + implementationName, myServiceImplementationTemplateName, myDirectory, + ContainerUtil.stringMap(INTERFACE_NAME_PROPERTY, interfaceShortName, INTERFACE_PACKAGE_PROPERTY, interfacePackage)); patchPluginXml(createdInterface, createdImplementation, pluginXml); @@ -273,7 +285,7 @@ public abstract class NewServiceActionBase extends CreateInDirectoryActionBase i boolean createOnlyImplementation(String onlyImplementationName, XmlFile pluginXml) { return doCreateService(() -> { PsiClass createdOnlyImplementation = DevkitActionsUtil.createSingleClass( - onlyImplementationName, myServiceOnlyImplementationTemplateName, myDirectory); + onlyImplementationName, myServiceOnlyImplementationTemplateName, myDirectory, null); patchPluginXml(null, createdOnlyImplementation, pluginXml); diff --git a/plugins/devkit/testSources/actions/service/ServiceCreatorTest.java b/plugins/devkit/testSources/actions/service/ServiceCreatorTest.java index 7faa64333ba9..f21c1f8d1bb1 100644 --- a/plugins/devkit/testSources/actions/service/ServiceCreatorTest.java +++ b/plugins/devkit/testSources/actions/service/ServiceCreatorTest.java @@ -43,17 +43,18 @@ public class ServiceCreatorTest extends JavaCodeInsightFixtureTestCase { public void testCreateApplicationServiceInterfaceAndImplementation() { doTestCreateInterfaceAndImplementation("my.plugin.ApplicationServiceInterface", "my.plugin.impl.ApplicationServiceImpl", - "ApplicationServiceInterface.java", "applicationService"); + "ApplicationServiceInterface.java", "ApplicationServiceImplementation.java", + "applicationService"); } public void testCreateProjectServiceInterfaceAndImplementation() { doTestCreateInterfaceAndImplementation("my.plugin.ProjectServiceInterface", "my.plugin.impl.ProjectServiceImpl", - "ProjectServiceInterface.java", "projectService"); + "ProjectServiceInterface.java", "ProjectServiceImplementation.java", "projectService"); } public void testCreateModuleServiceInterfaceAndImplementation() { doTestCreateInterfaceAndImplementation("my.plugin.ModuleServiceInterface", "my.plugin.impl.ModuleServiceImpl", - "ModuleServiceInterface.java", "moduleService"); + "ModuleServiceInterface.java", "ModuleServiceImplementation.java", "moduleService"); } public void testCreateApplicationServiceOnlyImplementation() { @@ -70,12 +71,13 @@ public class ServiceCreatorTest extends JavaCodeInsightFixtureTestCase { private void doTestCreateInterfaceAndImplementation(String interfaceFqName, String implementationFqName, - String interfaceTemplate, String tagName) { + String interfaceTemplate, String implementationTemplate, String tagName) { VirtualFile copied = myFixture.copyDirectoryToProject("", ""); PsiDirectory dir = myFixture.getPsiManager().findDirectory(copied); XmlFile pluginXml = PluginModuleType.getPluginXml(myFixture.getModule()); - NewServiceActionBase.ServiceCreator creator = new NewServiceActionBase.ServiceCreator(dir, interfaceTemplate, null, tagName); + NewServiceActionBase.ServiceCreator creator = new NewServiceActionBase.ServiceCreator( + dir, interfaceTemplate, implementationTemplate, null, tagName); boolean created = creator.createInterfaceAndImplementation(interfaceFqName, implementationFqName, pluginXml); assertTrue(created); @@ -120,7 +122,7 @@ public class ServiceCreatorTest extends JavaCodeInsightFixtureTestCase { PsiDirectory dir = myFixture.getPsiManager().findDirectory(copied); XmlFile pluginXml = PluginModuleType.getPluginXml(myFixture.getModule()); - NewServiceActionBase.ServiceCreator creator = new NewServiceActionBase.ServiceCreator(dir, null, classTemplate, tagName); + NewServiceActionBase.ServiceCreator creator = new NewServiceActionBase.ServiceCreator(dir, null, null, classTemplate, tagName); boolean created = creator.createOnlyImplementation(implementationFqName, pluginXml); assertTrue(created); From 6d867c3e4dc06e3a14eb1ff8b76172812042b323 Mon Sep 17 00:00:00 2001 From: Yaroslav Pankratyev Date: Mon, 28 Aug 2017 11:13:29 +0700 Subject: [PATCH 26/27] Fix service template descriptions; make NewServiceActionBase package-private --- .../j2ee/ApplicationServiceImplementation.java.html | 2 +- .../fileTemplates/j2ee/ModuleServiceImplementation.java.html | 2 +- .../fileTemplates/j2ee/ProjectServiceImplementation.java.html | 2 +- plugins/devkit/src/actions/service/NewServiceActionBase.java | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/devkit/resources/fileTemplates/j2ee/ApplicationServiceImplementation.java.html b/plugins/devkit/resources/fileTemplates/j2ee/ApplicationServiceImplementation.java.html index d75d13fa6d94..9619304526c7 100644 --- a/plugins/devkit/resources/fileTemplates/j2ee/ApplicationServiceImplementation.java.html +++ b/plugins/devkit/resources/fileTemplates/j2ee/ApplicationServiceImplementation.java.html @@ -19,7 +19,7 @@ ${INTERFACE_PACKAGE_NAME}   - service interface package or an empty string if interface is placed in the same package that implementation. + service interface package or an empty string if interface is placed in the same package as implementation. diff --git a/plugins/devkit/resources/fileTemplates/j2ee/ModuleServiceImplementation.java.html b/plugins/devkit/resources/fileTemplates/j2ee/ModuleServiceImplementation.java.html index f75d0e67825f..3c01a48f4f36 100644 --- a/plugins/devkit/resources/fileTemplates/j2ee/ModuleServiceImplementation.java.html +++ b/plugins/devkit/resources/fileTemplates/j2ee/ModuleServiceImplementation.java.html @@ -19,7 +19,7 @@ ${INTERFACE_PACKAGE_NAME}   - service interface package or an empty string if interface is placed in the same package that implementation. + service interface package or an empty string if interface is placed in the same package as implementation. diff --git a/plugins/devkit/resources/fileTemplates/j2ee/ProjectServiceImplementation.java.html b/plugins/devkit/resources/fileTemplates/j2ee/ProjectServiceImplementation.java.html index 741c8099afde..afb242e07ed3 100644 --- a/plugins/devkit/resources/fileTemplates/j2ee/ProjectServiceImplementation.java.html +++ b/plugins/devkit/resources/fileTemplates/j2ee/ProjectServiceImplementation.java.html @@ -19,7 +19,7 @@ ${INTERFACE_PACKAGE_NAME}   - service interface package or an empty string if interface is placed in the same package that implementation. + service interface package or an empty string if interface is placed in the same package as implementation. diff --git a/plugins/devkit/src/actions/service/NewServiceActionBase.java b/plugins/devkit/src/actions/service/NewServiceActionBase.java index 83cdea182743..eb50569ed84b 100644 --- a/plugins/devkit/src/actions/service/NewServiceActionBase.java +++ b/plugins/devkit/src/actions/service/NewServiceActionBase.java @@ -57,8 +57,8 @@ import java.util.concurrent.Callable; /** * An base class for actions generating service classes (implementation and optionally interface) and registering new service in plugin.xml. */ -public abstract class NewServiceActionBase extends CreateInDirectoryActionBase implements WriteActionAware { - public NewServiceActionBase(String text, String description) { +abstract class NewServiceActionBase extends CreateInDirectoryActionBase implements WriteActionAware { + NewServiceActionBase(String text, String description) { super(text, description, null); } From ff0bb42b46ba8a0c12bf954a5d3d20e05c8c8e21 Mon Sep 17 00:00:00 2001 From: Yaroslav Pankratyev Date: Tue, 29 Aug 2017 15:54:11 +0700 Subject: [PATCH 27/27] Overloaded DevkitActionsUtil.createSingleClass w/o properties --- plugins/devkit/src/actions/DevkitActionsUtil.java | 10 ++++++---- plugins/devkit/src/actions/NewActionAction.java | 2 +- .../src/actions/service/NewServiceActionBase.java | 4 ++-- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/plugins/devkit/src/actions/DevkitActionsUtil.java b/plugins/devkit/src/actions/DevkitActionsUtil.java index d561241e14ba..d469344f68d7 100644 --- a/plugins/devkit/src/actions/DevkitActionsUtil.java +++ b/plugins/devkit/src/actions/DevkitActionsUtil.java @@ -33,6 +33,7 @@ import com.intellij.psi.PsiClass; import com.intellij.psi.PsiDirectory; import com.intellij.psi.xml.XmlFile; import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.devkit.DevKitBundle; import org.jetbrains.idea.devkit.module.PluginModuleType; @@ -95,8 +96,12 @@ public final class DevkitActionsUtil { return null; } + public static PsiClass createSingleClass(String name, String classTemplateName, PsiDirectory directory) { + return createSingleClass(name, classTemplateName, directory, Collections.emptyMap()); + } + public static PsiClass createSingleClass(String name, String classTemplateName, PsiDirectory directory, - @Nullable Map properties) { + @NotNull Map properties) { if (name.contains(".")) { String[] names = name.split("\\."); for (int i = 0; i < names.length - 1; i++) { @@ -105,9 +110,6 @@ public final class DevkitActionsUtil { name = names[names.length - 1]; } - if (properties == null) { - properties = Collections.emptyMap(); - } return JavaDirectoryService.getInstance().createClass(directory, name, classTemplateName, false, properties); } diff --git a/plugins/devkit/src/actions/NewActionAction.java b/plugins/devkit/src/actions/NewActionAction.java index ea56efdda248..62fbb7b130cd 100644 --- a/plugins/devkit/src/actions/NewActionAction.java +++ b/plugins/devkit/src/actions/NewActionAction.java @@ -97,7 +97,7 @@ public class NewActionAction extends CreateElementActionBase implements Descript @NotNull @Override protected PsiElement[] create(String newName, PsiDirectory directory) throws Exception { - PsiClass createdClass = DevkitActionsUtil.createSingleClass(newName, "Action.java", directory, null); + PsiClass createdClass = DevkitActionsUtil.createSingleClass(newName, "Action.java", directory); DescriptorUtil.patchPluginXml(this, createdClass, pluginDescriptorToPatch); return new PsiElement[]{createdClass}; } diff --git a/plugins/devkit/src/actions/service/NewServiceActionBase.java b/plugins/devkit/src/actions/service/NewServiceActionBase.java index eb50569ed84b..c5c3feb90f5b 100644 --- a/plugins/devkit/src/actions/service/NewServiceActionBase.java +++ b/plugins/devkit/src/actions/service/NewServiceActionBase.java @@ -256,7 +256,7 @@ abstract class NewServiceActionBase extends CreateInDirectoryActionBase implemen */ boolean createInterfaceAndImplementation(String interfaceName, String implementationName, XmlFile pluginXml) { return doCreateService(() -> { - PsiClass createdInterface = DevkitActionsUtil.createSingleClass(interfaceName, myServiceInterfaceTemplateName, myDirectory, null); + PsiClass createdInterface = DevkitActionsUtil.createSingleClass(interfaceName, myServiceInterfaceTemplateName, myDirectory); String interfaceShortName = createdInterface.getName(); String implementationDirRelativePackage = StringUtil.getPackageName(implementationName); @@ -285,7 +285,7 @@ abstract class NewServiceActionBase extends CreateInDirectoryActionBase implemen boolean createOnlyImplementation(String onlyImplementationName, XmlFile pluginXml) { return doCreateService(() -> { PsiClass createdOnlyImplementation = DevkitActionsUtil.createSingleClass( - onlyImplementationName, myServiceOnlyImplementationTemplateName, myDirectory, null); + onlyImplementationName, myServiceOnlyImplementationTemplateName, myDirectory); patchPluginXml(null, createdOnlyImplementation, pluginXml);