From d3fe205507a3295f26fdff51f238dda72ce1c864 Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Wed, 7 Oct 2015 08:40:47 +0200 Subject: [PATCH 01/22] IDEA-CR-5425 correct javadoc --- .../intellij/openapi/components/PersistentStateComponent.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/platform/core-api/src/com/intellij/openapi/components/PersistentStateComponent.java b/platform/core-api/src/com/intellij/openapi/components/PersistentStateComponent.java index 64d3cfaf8a32..df8eed4d1255 100644 --- a/platform/core-api/src/com/intellij/openapi/components/PersistentStateComponent.java +++ b/platform/core-api/src/com/intellij/openapi/components/PersistentStateComponent.java @@ -24,7 +24,8 @@ import org.jetbrains.annotations.Nullable; * See IntelliJ Platform SDK DevGuide * for detailed description. * - * Implementation must be thread-safe - a component should expect these methods to be called at any moment of its lifecycle. + * In general, implementation should be thread-safe, because "loadState" is called from the same thread where component is initialized. + * If component used only from one thread (e.g. EDT), thread-safe implementation is not required. */ public interface PersistentStateComponent { /** From 6e8c1cc180daf435db380bc7cfc3eb5f53f58324 Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Wed, 7 Oct 2015 08:44:31 +0200 Subject: [PATCH 02/22] cleanup --- platform/built-in-server/testSrc/RestApiTest.kt | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/platform/built-in-server/testSrc/RestApiTest.kt b/platform/built-in-server/testSrc/RestApiTest.kt index f399816c1be9..5116c6425b47 100644 --- a/platform/built-in-server/testSrc/RestApiTest.kt +++ b/platform/built-in-server/testSrc/RestApiTest.kt @@ -1,13 +1,10 @@ package org.jetbrains.ide import com.google.gson.stream.JsonWriter -import com.intellij.openapi.vfs.CharsetToolkit import io.netty.handler.codec.http.HttpResponseStatus import org.assertj.core.api.Assertions.assertThat import org.jetbrains.ide.TestManager.TestDescriptor import org.junit.Test -import java.io.BufferedOutputStream -import java.io.OutputStreamWriter import java.net.HttpURLConnection import java.net.URL @@ -72,13 +69,13 @@ private class RestApiTest : BuiltInServerTestCase() { connection = URL("$serviceUrl").openConnection() as HttpURLConnection connection.requestMethod = "POST" connection.doOutput = true - val writer = JsonWriter(OutputStreamWriter(BufferedOutputStream(connection.outputStream), CharsetToolkit.UTF8_CHARSET)) - writer.beginObject() - writer.name("file").value(manager.filePath) - writer.name("line").value(line) - writer.name("column").value(column) - writer.endObject() - writer.close() + JsonWriter(connection.outputStream.bufferedWriter()).use { + it.beginObject() + it.name("file").value(manager.filePath) + it.name("line").value(line) + it.name("column").value(column) + it.endObject() + } assertThat(HttpResponseStatus.valueOf(connection.responseCode)).isEqualTo(expectedStatus) } } From 5b4e765f0562be52b71c2350e2849301e219c717 Mon Sep 17 00:00:00 2001 From: nik Date: Wed, 7 Oct 2015 10:37:24 +0300 Subject: [PATCH 03/22] splitted javadoc generator configuration and run profile to separate classes --- .../javadoc/JavaDocInfoGenerator.java | 4 +- .../intellij/javadoc/JavadocConfigurable.java | 14 +- .../javadoc/JavadocConfiguration.java | 364 +---------------- .../javadoc/JavadocGenerationManager.java | 7 +- .../javadoc/JavadocGeneratorRunProfile.java | 373 ++++++++++++++++++ .../actions/GenerateJavadocAction.java | 2 +- 6 files changed, 399 insertions(+), 365 deletions(-) create mode 100644 java/java-impl/src/com/intellij/javadoc/JavadocGeneratorRunProfile.java diff --git a/java/java-impl/src/com/intellij/codeInsight/javadoc/JavaDocInfoGenerator.java b/java/java-impl/src/com/intellij/codeInsight/javadoc/JavaDocInfoGenerator.java index 905bba345c72..bd39b5f96f47 100644 --- a/java/java-impl/src/com/intellij/codeInsight/javadoc/JavaDocInfoGenerator.java +++ b/java/java-impl/src/com/intellij/codeInsight/javadoc/JavaDocInfoGenerator.java @@ -21,7 +21,7 @@ import com.intellij.codeInsight.ExternalAnnotationsManager; import com.intellij.codeInsight.InferredAnnotationsManager; import com.intellij.codeInsight.documentation.DocumentationManagerProtocol; import com.intellij.codeInsight.documentation.DocumentationManagerUtil; -import com.intellij.javadoc.JavadocConfiguration; +import com.intellij.javadoc.JavadocGeneratorRunProfile; import com.intellij.lang.ASTNode; import com.intellij.lang.LangBundle; import com.intellij.openapi.diagnostic.Logger; @@ -233,7 +233,7 @@ public class JavaDocInfoGenerator { myProject = project; myElement = element; - Sdk jdk = JavadocConfiguration.getSdk(myProject); + Sdk jdk = JavadocGeneratorRunProfile.getSdk(myProject); mySdkVersion = jdk == null ? null : JavaSdk.getInstance().getVersion(jdk); } diff --git a/java/java-impl/src/com/intellij/javadoc/JavadocConfigurable.java b/java/java-impl/src/com/intellij/javadoc/JavadocConfigurable.java index d0d6ad8ff11b..0f51d477408c 100644 --- a/java/java-impl/src/com/intellij/javadoc/JavadocConfigurable.java +++ b/java/java-impl/src/com/intellij/javadoc/JavadocConfigurable.java @@ -16,6 +16,9 @@ package com.intellij.javadoc; import com.intellij.openapi.options.Configurable; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.roots.JavadocOrderRootType; import com.intellij.psi.PsiKeyword; import javax.swing.*; @@ -24,14 +27,21 @@ import java.io.File; public final class JavadocConfigurable implements Configurable { private JavadocGenerationPanel myPanel; private final JavadocConfiguration myConfiguration; + private final Project myProject; - public JavadocConfigurable(JavadocConfiguration configuration) { + public JavadocConfigurable(JavadocConfiguration configuration, Project project) { myConfiguration = configuration; + myProject = project; + } + + public static boolean sdkHasJavadocUrls(Project project) { + Sdk sdk = JavadocGeneratorRunProfile.getSdk(project); + return sdk != null && sdk.getRootProvider().getFiles(JavadocOrderRootType.getInstance()).length > 0; } public JComponent createComponent() { myPanel = new JavadocGenerationPanel(); - myPanel.myLinkToJdkDocs.setEnabled(myConfiguration.sdkHasJavadocUrls()); + myPanel.myLinkToJdkDocs.setEnabled(sdkHasJavadocUrls(myProject)); return myPanel.myPanel; } diff --git a/java/java-impl/src/com/intellij/javadoc/JavadocConfiguration.java b/java/java-impl/src/com/intellij/javadoc/JavadocConfiguration.java index ae35f7e616d5..55e392b9434b 100644 --- a/java/java-impl/src/com/intellij/javadoc/JavadocConfiguration.java +++ b/java/java-impl/src/com/intellij/javadoc/JavadocConfiguration.java @@ -15,57 +15,18 @@ */ package com.intellij.javadoc; -import com.intellij.analysis.AnalysisScope; -import com.intellij.execution.CantRunException; -import com.intellij.execution.ExecutionException; -import com.intellij.execution.Executor; -import com.intellij.execution.configurations.*; -import com.intellij.execution.filters.RegexpFilter; -import com.intellij.execution.process.OSProcessHandler; -import com.intellij.execution.process.ProcessAdapter; -import com.intellij.execution.process.ProcessEvent; -import com.intellij.execution.process.ProcessTerminatedListener; -import com.intellij.execution.runners.ExecutionEnvironment; -import com.intellij.ide.BrowserUtil; -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.module.Module; -import com.intellij.openapi.module.ModuleUtilCore; -import com.intellij.openapi.progress.ProgressManager; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.projectRoots.JavaSdk; -import com.intellij.openapi.projectRoots.JavaSdkType; -import com.intellij.openapi.projectRoots.JavaSdkVersion; -import com.intellij.openapi.projectRoots.Sdk; -import com.intellij.openapi.projectRoots.ex.PathUtilEx; -import com.intellij.openapi.roots.*; -import com.intellij.openapi.util.*; -import com.intellij.openapi.util.io.FileUtil; -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.openapi.vfs.VfsUtil; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.*; -import com.intellij.util.PathsList; -import com.intellij.util.containers.HashSet; +import com.intellij.openapi.util.DefaultJDOMExternalizer; +import com.intellij.openapi.util.InvalidDataException; +import com.intellij.openapi.util.JDOMExternalizable; +import com.intellij.openapi.util.WriteExternalException; +import com.intellij.psi.PsiKeyword; import org.jdom.Element; -import org.jetbrains.annotations.NonNls; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.jps.model.java.JavaModuleSourceRootTypes; - -import javax.swing.*; -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.io.PrintWriter; -import java.util.Collection; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Set; /** * @author Eugene Zhuravlev * Date: Apr 24, 2004 */ -public class JavadocConfiguration implements ModuleRunProfile, JDOMExternalizable{ +public class JavadocConfiguration implements JDOMExternalizable { public String OUTPUT_DIRECTORY; public String OPTION_SCOPE = PsiKeyword.PROTECTED; public boolean OPTION_HIERARCHY = true; @@ -81,46 +42,10 @@ public class JavadocConfiguration implements ModuleRunProfile, JDOMExternalizabl public String HEAP_SIZE; public String LOCALE; public boolean OPEN_IN_BROWSER = true; - - private final Project myProject; - private AnalysisScope myGenerationScope; - private static final Logger LOGGER = Logger.getInstance("#" + JavadocConfiguration.class.getName()); public boolean OPTION_INCLUDE_LIBS = false; public boolean OPTION_LINK_TO_JDK_DOCS = false; - public void setGenerationScope(AnalysisScope generationScope) { - myGenerationScope = generationScope; - } - - public JavadocConfiguration(Project project) { - myProject = project; - } - - public RunProfileState getState(@NotNull final Executor executor, @NotNull final ExecutionEnvironment env) throws ExecutionException { - return new MyJavaCommandLineState(myProject, myGenerationScope, env); - } - - public String getName() { - return JavadocBundle.message("javadoc.settings.title"); - } - - public void checkConfiguration() throws RuntimeConfigurationException { - if (myGenerationScope == null) { - throw new RuntimeConfigurationError(JavadocBundle.message("javadoc.settings.not.specified")); - } - } - - public JavadocConfigurable createConfigurable() { - return new JavadocConfigurable(this); - } - - public Icon getIcon() { - return null; - } - - @NotNull - public Module[] getModules() { - return Module.EMPTY_ARRAY; + public JavadocConfiguration() { } public void readExternal(Element element) throws InvalidDataException { @@ -130,278 +55,5 @@ public class JavadocConfiguration implements ModuleRunProfile, JDOMExternalizabl public void writeExternal(Element element) throws WriteExternalException { DefaultJDOMExternalizer.writeExternal(this, element); } - - public boolean sdkHasJavadocUrls() { - Sdk sdk = getSdk(myProject); - return sdk != null && sdk.getRootProvider().getFiles(JavadocOrderRootType.getInstance()).length > 0; - } - - public static Sdk getSdk(@NotNull Project project) { - return PathUtilEx.getAnyJdk(project); - } - - private class MyJavaCommandLineState extends CommandLineState { - private final AnalysisScope myGenerationOptions; - private final Project myProject; - @NonNls private static final String INDEX_HTML = "index.html"; - - public MyJavaCommandLineState(Project project, AnalysisScope generationOptions, ExecutionEnvironment env) { - super(env); - myGenerationOptions = generationOptions; - myProject = project; - addConsoleFilters(new RegexpFilter(project, "$FILE_PATH$:$LINE$:[^\\^]+\\^"), - new RegexpFilter(project, "$FILE_PATH$:$LINE$: warning - .+$")); - } - - protected GeneralCommandLine createCommandLine() throws ExecutionException { - final GeneralCommandLine cmdLine = new GeneralCommandLine(); - final Sdk jdk = getSdk(myProject); - setupExeParams(jdk, cmdLine); - setupProgramParameters(jdk, cmdLine); - return cmdLine; - } - - private void setupExeParams(final Sdk jdk, GeneralCommandLine cmdLine) throws ExecutionException { - final String jdkPath = jdk != null && jdk.getSdkType() instanceof JavaSdkType ? ((JavaSdkType)jdk.getSdkType()).getBinPath(jdk) : null; - if (jdkPath == null) { - throw new CantRunException(JavadocBundle.message("javadoc.generate.no.jdk.path")); - } - JavaSdkVersion version = JavaSdk.getInstance().getVersion(jdk); - if (HEAP_SIZE != null && HEAP_SIZE.trim().length() != 0) { - if (version == null || version.isAtLeast(JavaSdkVersion.JDK_1_2)) { - cmdLine.getParametersList().prepend("-J-Xmx" + HEAP_SIZE + "m"); - } - else { - cmdLine.getParametersList().prepend("-J-mx" + HEAP_SIZE + "m"); - } - } - cmdLine.setWorkDirectory((File)null); - @NonNls final String javadocExecutableName = File.separator + (SystemInfo.isWindows ? "javadoc.exe" : "javadoc"); - @NonNls String exePath = jdkPath.replace('/', File.separatorChar) + javadocExecutableName; - if (new File(exePath).exists()) { - cmdLine.setExePath(exePath); - } else { //try to use wrapper jdk - exePath = new File(jdkPath).getParent().replace('/', File.separatorChar) + javadocExecutableName; - if (!new File(exePath).exists()){ - final File parent = new File(System.getProperty("java.home")).getParentFile(); //try system jre - exePath = parent.getPath() + File.separator + "bin" + javadocExecutableName; - if (!new File(exePath).exists()){ - throw new CantRunException(JavadocBundle.message("javadoc.generate.no.jdk.path")); - } - } - cmdLine.setExePath(exePath); - } - } - - private void setupProgramParameters(final Sdk jdk, final GeneralCommandLine cmdLine) throws CantRunException { - @NonNls final ParametersList parameters = cmdLine.getParametersList(); - - if (LOCALE != null && LOCALE.length() > 0) { - parameters.add("-locale"); - parameters.add(LOCALE); - } - - if (OPTION_SCOPE != null) { - parameters.add("-" + OPTION_SCOPE); - } - - if (!OPTION_HIERARCHY) { - parameters.add("-notree"); - } - - if (!OPTION_NAVIGATOR) { - parameters.add("-nonavbar"); - } - - if (!OPTION_INDEX) { - parameters.add("-noindex"); - } - else if (OPTION_SEPARATE_INDEX) { - parameters.add("-splitindex"); - } - - if (OPTION_DOCUMENT_TAG_USE) { - parameters.add("-use"); - } - - if (OPTION_DOCUMENT_TAG_AUTHOR) { - parameters.add("-author"); - } - - if (OPTION_DOCUMENT_TAG_VERSION) { - parameters.add("-version"); - } - - if (!OPTION_DOCUMENT_TAG_DEPRECATED) { - parameters.add("-nodeprecated"); - } - else if (!OPTION_DEPRECATED_LIST) { - parameters.add("-nodeprecatedlist"); - } - - parameters.addParametersString(OTHER_OPTIONS); - - final Set modules = new LinkedHashSet(); - try { - final File sourcePathTempFile = FileUtil.createTempFile("javadoc", "args.txt", true); - parameters.add("@" + sourcePathTempFile.getCanonicalPath()); - final PrintWriter writer = new PrintWriter(new FileWriter(sourcePathTempFile)); - try { - final Collection packages = new HashSet(); - final Collection sources = new HashSet(); - final Runnable findRunnable = new Runnable() { - public void run() { - final int scopeType = myGenerationOptions.getScopeType(); - final boolean usePackageNotation = scopeType == AnalysisScope.MODULE || - scopeType == AnalysisScope.MODULES || - scopeType == AnalysisScope.PROJECT || - scopeType == AnalysisScope.DIRECTORY; - myGenerationOptions.accept(new MyContentIterator(myProject, packages, sources, modules, usePackageNotation)); - } - }; - if (!ProgressManager.getInstance().runProcessWithProgressSynchronously(findRunnable, "Search for sources to generate javadoc in...", true, myProject)) { - return; - } - if (packages.size() + sources.size() == 0) { - throw new CantRunException(JavadocBundle.message("javadoc.generate.no.classes.in.selected.packages.error")); - } - for (String aPackage : packages) { - writer.println(aPackage); - } - //http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#runningjavadoc - for (String source : sources) { - writer.println(StringUtil.wrapWithDoubleQuote(source)); - } - writer.println("-sourcepath"); - OrderEnumerator enumerator = OrderEnumerator.orderEntries(myProject); - if (!OPTION_INCLUDE_LIBS) { - enumerator = enumerator.withoutSdk().withoutLibraries(); - } - final PathsList pathsList = enumerator.getSourcePathsList(); - final List files = pathsList.getRootDirs(); - final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(myProject).getFileIndex(); - final StringBuilder sourcePath = new StringBuilder(); - boolean start = true; - for (VirtualFile file : files) { - if (!myGenerationOptions.isIncludeTestSource() && fileIndex.isInTestSourceContent(file)) continue; - if (start) { - start = false; - } - else { - sourcePath.append(File.pathSeparator); - } - sourcePath.append(file.getPath()); - } - writer.println(StringUtil.wrapWithDoubleQuote(sourcePath.toString())); - } - finally { - writer.close(); - } - } - catch (IOException e) { - LOGGER.error(e); - } - - if (OPTION_LINK_TO_JDK_DOCS) { - VirtualFile[] docUrls = jdk.getRootProvider().getFiles(JavadocOrderRootType.getInstance()); - for (VirtualFile docUrl : docUrls) { - parameters.add("-link"); - parameters.add(VfsUtil.toUri(docUrl).toString()); - } - } - - final PathsList classPath; - final OrderEnumerator orderEnumerator = ProjectRootManager.getInstance(myProject).orderEntries(modules); - if (jdk.getSdkType() instanceof JavaSdk) { - classPath = orderEnumerator.withoutSdk().withoutModuleSourceEntries().getPathsList(); - } - else { - //libraries are included into jdk - classPath = orderEnumerator.withoutModuleSourceEntries().getPathsList(); - } - final String classPathString = classPath.getPathsString(); - if (classPathString.length() > 0) { - parameters.add("-classpath"); - parameters.add(classPathString); - } - - if (OUTPUT_DIRECTORY != null) { - parameters.add("-d"); - parameters.add(OUTPUT_DIRECTORY.replace('/', File.separatorChar)); - } - - } - - @NotNull - protected OSProcessHandler startProcess() throws ExecutionException { - final OSProcessHandler handler = JavaCommandLineStateUtil.startProcess(createCommandLine()); - ProcessTerminatedListener.attach(handler, myProject, JavadocBundle.message("javadoc.generate.exited")); - handler.addProcessListener(new ProcessAdapter() { - public void processTerminated(ProcessEvent event) { - if (OPEN_IN_BROWSER) { - File url = new File(OUTPUT_DIRECTORY, INDEX_HTML); - if (url.exists() && event.getExitCode() == 0) { - BrowserUtil.browse(url); - } - } - } - }); - return handler; - } - } - - private static class MyContentIterator extends PsiRecursiveElementWalkingVisitor { - private final PsiManager myPsiManager; - private final Collection myPackages; - private final Collection mySourceFiles; - private final Set myModules; - private final boolean myUsePackageNotation; - - public MyContentIterator(Project project, - Collection packages, - Collection sources, - Set modules, - boolean canUsePackageNotation) { - myModules = modules; - myUsePackageNotation = canUsePackageNotation; - myPsiManager = PsiManager.getInstance(project); - myPackages = packages; - mySourceFiles = sources; - } - - @Override - public void visitFile(PsiFile file) { - final VirtualFile fileOrDir = file.getVirtualFile(); - if (fileOrDir == null) return; - if (!fileOrDir.isInLocalFileSystem()) return; - final Module module = ModuleUtilCore.findModuleForFile(fileOrDir, myPsiManager.getProject()); - if (module != null) { - myModules.add(module); - } - if (file instanceof PsiJavaFile) { - final PsiJavaFile javaFile = (PsiJavaFile)file; - final String packageName = javaFile.getPackageName(); - if (containsPackagePrefix(module, packageName) || (packageName.length() == 0 && !(javaFile instanceof ServerPageFile)) || !myUsePackageNotation) { - mySourceFiles.add(FileUtil.toSystemIndependentName(fileOrDir.getPath())); - } - else { - myPackages.add(packageName); - } - } - } - - private static boolean containsPackagePrefix(Module module, String packageFQName) { - if (module == null) return false; - for (ContentEntry contentEntry : ModuleRootManager.getInstance(module).getContentEntries()) { - for (SourceFolder sourceFolder : contentEntry.getSourceFolders(JavaModuleSourceRootTypes.SOURCES)) { - final String packagePrefix = sourceFolder.getPackagePrefix(); - final int prefixLength = packagePrefix.length(); - if (prefixLength > 0 && packageFQName.startsWith(packagePrefix)) { - return true; - } - } - } - return false; - } - } } + diff --git a/java/java-impl/src/com/intellij/javadoc/JavadocGenerationManager.java b/java/java-impl/src/com/intellij/javadoc/JavadocGenerationManager.java index 3589918fe4a1..b1a5ef4984ef 100644 --- a/java/java-impl/src/com/intellij/javadoc/JavadocGenerationManager.java +++ b/java/java-impl/src/com/intellij/javadoc/JavadocGenerationManager.java @@ -20,7 +20,6 @@ import com.intellij.analysis.AnalysisScope; import com.intellij.execution.ExecutionException; import com.intellij.execution.executors.DefaultRunExecutor; import com.intellij.execution.runners.ExecutionEnvironmentBuilder; -import com.intellij.execution.runners.ExecutionUtil; import com.intellij.execution.util.ExecutionErrorDialog; import com.intellij.openapi.components.*; import com.intellij.openapi.diagnostic.Logger; @@ -48,7 +47,7 @@ public final class JavadocGenerationManager implements PersistentStateComponent< JavadocGenerationManager(Project project) { myProject = project; - myConfiguration = new JavadocConfiguration(project); + myConfiguration = new JavadocConfiguration(); } @Override @@ -78,9 +77,9 @@ public final class JavadocGenerationManager implements PersistentStateComponent< } public void generateJavadoc(AnalysisScope scope) { - myConfiguration.setGenerationScope(scope); try { - ExecutionEnvironmentBuilder.create(myProject, DefaultRunExecutor.getRunExecutorInstance(), myConfiguration).buildAndExecute(); + JavadocGeneratorRunProfile profile = new JavadocGeneratorRunProfile(myProject, scope, myConfiguration); + ExecutionEnvironmentBuilder.create(myProject, DefaultRunExecutor.getRunExecutorInstance(), profile).buildAndExecute(); } catch (ExecutionException e) { ExecutionErrorDialog.show(e, CommonBundle.getErrorTitle(), myProject); diff --git a/java/java-impl/src/com/intellij/javadoc/JavadocGeneratorRunProfile.java b/java/java-impl/src/com/intellij/javadoc/JavadocGeneratorRunProfile.java new file mode 100644 index 000000000000..b18a5b38ef6f --- /dev/null +++ b/java/java-impl/src/com/intellij/javadoc/JavadocGeneratorRunProfile.java @@ -0,0 +1,373 @@ +/* + * 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 com.intellij.javadoc; + +import com.intellij.analysis.AnalysisScope; +import com.intellij.execution.CantRunException; +import com.intellij.execution.ExecutionException; +import com.intellij.execution.Executor; +import com.intellij.execution.configurations.*; +import com.intellij.execution.filters.RegexpFilter; +import com.intellij.execution.process.OSProcessHandler; +import com.intellij.execution.process.ProcessAdapter; +import com.intellij.execution.process.ProcessEvent; +import com.intellij.execution.process.ProcessTerminatedListener; +import com.intellij.execution.runners.ExecutionEnvironment; +import com.intellij.ide.BrowserUtil; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleUtilCore; +import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.projectRoots.JavaSdk; +import com.intellij.openapi.projectRoots.JavaSdkType; +import com.intellij.openapi.projectRoots.JavaSdkVersion; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.projectRoots.ex.PathUtilEx; +import com.intellij.openapi.roots.*; +import com.intellij.openapi.util.SystemInfo; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.VfsUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.*; +import com.intellij.util.PathsList; +import com.intellij.util.containers.HashSet; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jps.model.java.JavaModuleSourceRootTypes; + +import javax.swing.*; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.Collection; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +/** + * @author nik + */ +public class JavadocGeneratorRunProfile implements ModuleRunProfile { + private static final Logger LOGGER = Logger.getInstance("#" + JavadocConfiguration.class.getName()); + private final Project myProject; + private final AnalysisScope myGenerationScope; + private final JavadocConfiguration myConfiguration; + + public JavadocGeneratorRunProfile(Project project, AnalysisScope generationScope, JavadocConfiguration configuration) { + myProject = project; + myGenerationScope = generationScope; + myConfiguration = configuration; + } + + public static Sdk getSdk(@NotNull Project project) { + return PathUtilEx.getAnyJdk(project); + } + + public RunProfileState getState(@NotNull final Executor executor, @NotNull final ExecutionEnvironment env) throws ExecutionException { + return new MyJavaCommandLineState(myConfiguration, myProject, myGenerationScope, env); + } + + public String getName() { + return JavadocBundle.message("javadoc.settings.title"); + } + + public Icon getIcon() { + return null; + } + + @NotNull + public Module[] getModules() { + return Module.EMPTY_ARRAY; + } + + private static class MyJavaCommandLineState extends CommandLineState { + private final AnalysisScope myGenerationOptions; + private final Project myProject; + @NonNls private static final String INDEX_HTML = "index.html"; + private JavadocConfiguration myConfiguration; + + public MyJavaCommandLineState(final JavadocConfiguration configuration, + Project project, + AnalysisScope generationOptions, + ExecutionEnvironment env) { + super(env); + myGenerationOptions = generationOptions; + myProject = project; + addConsoleFilters(new RegexpFilter(project, "$FILE_PATH$:$LINE$:[^\\^]+\\^"), + new RegexpFilter(project, "$FILE_PATH$:$LINE$: warning - .+$")); + this.myConfiguration = configuration; + } + + protected GeneralCommandLine createCommandLine() throws ExecutionException { + final GeneralCommandLine cmdLine = new GeneralCommandLine(); + final Sdk jdk = getSdk(myProject); + setupExeParams(jdk, cmdLine); + setupProgramParameters(jdk, cmdLine); + return cmdLine; + } + + private void setupExeParams(final Sdk jdk, GeneralCommandLine cmdLine) throws ExecutionException { + final String jdkPath = + jdk != null && jdk.getSdkType() instanceof JavaSdkType ? ((JavaSdkType)jdk.getSdkType()).getBinPath(jdk) : null; + if (jdkPath == null) { + throw new CantRunException(JavadocBundle.message("javadoc.generate.no.jdk.path")); + } + JavaSdkVersion version = JavaSdk.getInstance().getVersion(jdk); + if (myConfiguration.HEAP_SIZE != null && myConfiguration.HEAP_SIZE.trim().length() != 0) { + if (version == null || version.isAtLeast(JavaSdkVersion.JDK_1_2)) { + cmdLine.getParametersList().prepend("-J-Xmx" + myConfiguration.HEAP_SIZE + "m"); + } + else { + cmdLine.getParametersList().prepend("-J-mx" + myConfiguration.HEAP_SIZE + "m"); + } + } + cmdLine.setWorkDirectory((File)null); + @NonNls final String javadocExecutableName = File.separator + (SystemInfo.isWindows ? "javadoc.exe" : "javadoc"); + @NonNls String exePath = jdkPath.replace('/', File.separatorChar) + javadocExecutableName; + if (new File(exePath).exists()) { + cmdLine.setExePath(exePath); + } + else { //try to use wrapper jdk + exePath = new File(jdkPath).getParent().replace('/', File.separatorChar) + javadocExecutableName; + if (!new File(exePath).exists()) { + final File parent = new File(System.getProperty("java.home")).getParentFile(); //try system jre + exePath = parent.getPath() + File.separator + "bin" + javadocExecutableName; + if (!new File(exePath).exists()) { + throw new CantRunException(JavadocBundle.message("javadoc.generate.no.jdk.path")); + } + } + cmdLine.setExePath(exePath); + } + } + + private void setupProgramParameters(final Sdk jdk, final GeneralCommandLine cmdLine) throws CantRunException { + @NonNls final ParametersList parameters = cmdLine.getParametersList(); + + if (myConfiguration.LOCALE != null && myConfiguration.LOCALE.length() > 0) { + parameters.add("-locale"); + parameters.add(myConfiguration.LOCALE); + } + + if (myConfiguration.OPTION_SCOPE != null) { + parameters.add("-" + myConfiguration.OPTION_SCOPE); + } + + if (!myConfiguration.OPTION_HIERARCHY) { + parameters.add("-notree"); + } + + if (!myConfiguration.OPTION_NAVIGATOR) { + parameters.add("-nonavbar"); + } + + if (!myConfiguration.OPTION_INDEX) { + parameters.add("-noindex"); + } + else if (myConfiguration.OPTION_SEPARATE_INDEX) { + parameters.add("-splitindex"); + } + + if (myConfiguration.OPTION_DOCUMENT_TAG_USE) { + parameters.add("-use"); + } + + if (myConfiguration.OPTION_DOCUMENT_TAG_AUTHOR) { + parameters.add("-author"); + } + + if (myConfiguration.OPTION_DOCUMENT_TAG_VERSION) { + parameters.add("-version"); + } + + if (!myConfiguration.OPTION_DOCUMENT_TAG_DEPRECATED) { + parameters.add("-nodeprecated"); + } + else if (!myConfiguration.OPTION_DEPRECATED_LIST) { + parameters.add("-nodeprecatedlist"); + } + + parameters.addParametersString(myConfiguration.OTHER_OPTIONS); + + final Set modules = new LinkedHashSet(); + try { + final File sourcePathTempFile = FileUtil.createTempFile("javadoc", "args.txt", true); + parameters.add("@" + sourcePathTempFile.getCanonicalPath()); + final PrintWriter writer = new PrintWriter(new FileWriter(sourcePathTempFile)); + try { + final Collection packages = new HashSet(); + final Collection sources = new HashSet(); + final Runnable findRunnable = new Runnable() { + public void run() { + final int scopeType = myGenerationOptions.getScopeType(); + final boolean usePackageNotation = scopeType == AnalysisScope.MODULE || + scopeType == AnalysisScope.MODULES || + scopeType == AnalysisScope.PROJECT || + scopeType == AnalysisScope.DIRECTORY; + myGenerationOptions.accept(new MyContentIterator(myProject, packages, sources, modules, usePackageNotation)); + } + }; + if (!ProgressManager + .getInstance() + .runProcessWithProgressSynchronously(findRunnable, "Search for sources to generate javadoc in...", true, myProject)) { + return; + } + if (packages.size() + sources.size() == 0) { + throw new CantRunException(JavadocBundle.message("javadoc.generate.no.classes.in.selected.packages.error")); + } + for (String aPackage : packages) { + writer.println(aPackage); + } + //http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javadoc.html#runningjavadoc + for (String source : sources) { + writer.println(StringUtil.wrapWithDoubleQuote(source)); + } + writer.println("-sourcepath"); + OrderEnumerator enumerator = OrderEnumerator.orderEntries(myProject); + if (!myConfiguration.OPTION_INCLUDE_LIBS) { + enumerator = enumerator.withoutSdk().withoutLibraries(); + } + final PathsList pathsList = enumerator.getSourcePathsList(); + final List files = pathsList.getRootDirs(); + final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(myProject).getFileIndex(); + final StringBuilder sourcePath = new StringBuilder(); + boolean start = true; + for (VirtualFile file : files) { + if (!myGenerationOptions.isIncludeTestSource() && fileIndex.isInTestSourceContent(file)) continue; + if (start) { + start = false; + } + else { + sourcePath.append(File.pathSeparator); + } + sourcePath.append(file.getPath()); + } + writer.println(StringUtil.wrapWithDoubleQuote(sourcePath.toString())); + } + finally { + writer.close(); + } + } + catch (IOException e) { + LOGGER.error(e); + } + + if (myConfiguration.OPTION_LINK_TO_JDK_DOCS) { + VirtualFile[] docUrls = jdk.getRootProvider().getFiles(JavadocOrderRootType.getInstance()); + for (VirtualFile docUrl : docUrls) { + parameters.add("-link"); + parameters.add(VfsUtil.toUri(docUrl).toString()); + } + } + + final PathsList classPath; + final OrderEnumerator orderEnumerator = ProjectRootManager.getInstance(myProject).orderEntries(modules); + if (jdk.getSdkType() instanceof JavaSdk) { + classPath = orderEnumerator.withoutSdk().withoutModuleSourceEntries().getPathsList(); + } + else { + //libraries are included into jdk + classPath = orderEnumerator.withoutModuleSourceEntries().getPathsList(); + } + final String classPathString = classPath.getPathsString(); + if (classPathString.length() > 0) { + parameters.add("-classpath"); + parameters.add(classPathString); + } + + if (myConfiguration.OUTPUT_DIRECTORY != null) { + parameters.add("-d"); + parameters.add(myConfiguration.OUTPUT_DIRECTORY.replace('/', File.separatorChar)); + } + } + + @NotNull + protected OSProcessHandler startProcess() throws ExecutionException { + final OSProcessHandler handler = JavaCommandLineStateUtil.startProcess(createCommandLine()); + ProcessTerminatedListener.attach(handler, myProject, JavadocBundle.message("javadoc.generate.exited")); + handler.addProcessListener(new ProcessAdapter() { + public void processTerminated(ProcessEvent event) { + if (myConfiguration.OPEN_IN_BROWSER) { + File url = new File(myConfiguration.OUTPUT_DIRECTORY, INDEX_HTML); + if (url.exists() && event.getExitCode() == 0) { + BrowserUtil.browse(url); + } + } + } + }); + return handler; + } + } + + private static class MyContentIterator extends PsiRecursiveElementWalkingVisitor { + private final PsiManager myPsiManager; + private final Collection myPackages; + private final Collection mySourceFiles; + private final Set myModules; + private final boolean myUsePackageNotation; + + public MyContentIterator(Project project, + Collection packages, + Collection sources, + Set modules, + boolean canUsePackageNotation) { + myModules = modules; + myUsePackageNotation = canUsePackageNotation; + myPsiManager = PsiManager.getInstance(project); + myPackages = packages; + mySourceFiles = sources; + } + + @Override + public void visitFile(PsiFile file) { + final VirtualFile fileOrDir = file.getVirtualFile(); + if (fileOrDir == null) return; + if (!fileOrDir.isInLocalFileSystem()) return; + final Module module = ModuleUtilCore.findModuleForFile(fileOrDir, myPsiManager.getProject()); + if (module != null) { + myModules.add(module); + } + if (file instanceof PsiJavaFile) { + final PsiJavaFile javaFile = (PsiJavaFile)file; + final String packageName = javaFile.getPackageName(); + if (containsPackagePrefix(module, packageName) || + (packageName.length() == 0 && !(javaFile instanceof ServerPageFile)) || + !myUsePackageNotation) { + mySourceFiles.add(FileUtil.toSystemIndependentName(fileOrDir.getPath())); + } + else { + myPackages.add(packageName); + } + } + } + + private static boolean containsPackagePrefix(Module module, String packageFQName) { + if (module == null) return false; + for (ContentEntry contentEntry : ModuleRootManager.getInstance(module).getContentEntries()) { + for (SourceFolder sourceFolder : contentEntry.getSourceFolders(JavaModuleSourceRootTypes.SOURCES)) { + final String packagePrefix = sourceFolder.getPackagePrefix(); + final int prefixLength = packagePrefix.length(); + if (prefixLength > 0 && packageFQName.startsWith(packagePrefix)) { + return true; + } + } + } + return false; + } + } +} diff --git a/java/java-impl/src/com/intellij/javadoc/actions/GenerateJavadocAction.java b/java/java-impl/src/com/intellij/javadoc/actions/GenerateJavadocAction.java index 068f527c0170..9f097a117c4b 100644 --- a/java/java-impl/src/com/intellij/javadoc/actions/GenerateJavadocAction.java +++ b/java/java-impl/src/com/intellij/javadoc/actions/GenerateJavadocAction.java @@ -44,7 +44,7 @@ public final class GenerateJavadocAction extends BaseAnalysisAction{ @Override protected JComponent getAdditionalActionSettings(Project project, final BaseAnalysisActionDialog dialog) { - myConfigurable = JavadocGenerationManager.getInstance(project).getConfiguration().createConfigurable(); + myConfigurable = new JavadocConfigurable(JavadocGenerationManager.getInstance(project).getConfiguration(), project); final JComponent component = myConfigurable.createComponent(); myConfigurable.reset(); myConfigurable.getOutputDirField().getDocument().addDocumentListener(new DocumentAdapter() { From f065882dff8ffa717691046ffe522499840097ce Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Wed, 7 Oct 2015 09:57:05 +0200 Subject: [PATCH 04/22] CR-IC-7402 IDEA-146068 --- build/scripts/layouts.gant | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/build/scripts/layouts.gant b/build/scripts/layouts.gant index e866606c5852..c32d10a8ced1 100644 --- a/build/scripts/layouts.gant +++ b/build/scripts/layouts.gant @@ -664,8 +664,9 @@ def setPluginVersionAndSince(String moduleName, String version) { if (file.isFile()) { def text = file.text if (!text.contains("")) { - def nextMajorVersion = Integer.parseInt(version.substring(0, version.indexOf('.'))) + 1 - file.text = text.replace("", "\n ${version}\n \n") + def dotIndex = version.indexOf('.') + def untilBuild = dotIndex > 0 ? (Integer.parseInt(version.substring(0, dotIndex)) + 1).toString() + ".0" : version + file.text = text.replace("", "\n ${version}\n \n") } } } From 3b98c2e48ac7d853b57e72101fb5b78589eaf123 Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Tue, 6 Oct 2015 22:52:24 +0200 Subject: [PATCH 05/22] make IG test light --- .../IfMayBeConditional.java | 6 ++--- .../if_may_be_conditional/expected.xml | 24 ------------------- .../IfMayBeConditionalInspectionTest.java | 20 +++++++++++----- 3 files changed, 17 insertions(+), 33 deletions(-) delete mode 100644 plugins/InspectionGadgets/test/com/siyeh/igtest/controlflow/if_may_be_conditional/expected.xml diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/controlflow/if_may_be_conditional/IfMayBeConditional.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/controlflow/if_may_be_conditional/IfMayBeConditional.java index 1109243d24a2..39e8e05d1768 100644 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/controlflow/if_may_be_conditional/IfMayBeConditional.java +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/controlflow/if_may_be_conditional/IfMayBeConditional.java @@ -9,11 +9,11 @@ public class IfMayBeConditional { void foo2(int a, int b) { int c = 0; - if (a < b) { c += a - b; } else { c += b; } + if (a < b) { c += a - b; } else { c += b; } } void foo3(int i, StringBuilder sb) { - if (i == 0) { + if (i == 0) { sb.append("type.getConstructor()", 0, 1); } else { @@ -22,7 +22,7 @@ public class IfMayBeConditional { } int foo4(int a, int b) { - if (a < b) return a; + if (a < b) return a; else return b; } diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/controlflow/if_may_be_conditional/expected.xml b/plugins/InspectionGadgets/test/com/siyeh/igtest/controlflow/if_may_be_conditional/expected.xml deleted file mode 100644 index 3ecb05a6e414..000000000000 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/controlflow/if_may_be_conditional/expected.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - IfMayBeConditional.java - 12 - If statement could be replaced with simple conditional expression - <code>if</code> could be replaced with simple conditional expression #loc - - - - IfMayBeConditional.java - 16 - 'if' statement could be replaced with simple conditional expression - <code>if</code> could be replaced with simple conditional expression #loc - - - - IfMayBeConditional.java - 25 - 'if' statement could be replaced with conditional expression - <code>if</code> could be replaced with conditional expression #loc - - \ No newline at end of file diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/controlflow/IfMayBeConditionalInspectionTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/controlflow/IfMayBeConditionalInspectionTest.java index 2a3ff34b7677..1f5b254068a5 100644 --- a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/controlflow/IfMayBeConditionalInspectionTest.java +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/controlflow/IfMayBeConditionalInspectionTest.java @@ -1,12 +1,20 @@ package com.siyeh.ig.controlflow; -import com.siyeh.ig.IGInspectionTestCase; +import com.intellij.codeInspection.InspectionProfileEntry; +import com.siyeh.ig.LightInspectionTestCase; +import org.jetbrains.annotations.Nullable; -public class IfMayBeConditionalInspectionTest extends IGInspectionTestCase { +public class IfMayBeConditionalInspectionTest extends LightInspectionTestCase { - public void test() throws Exception { - final IfMayBeConditionalInspection tool = new IfMayBeConditionalInspection(); - tool.reportMethodCalls = true; - doTest("com/siyeh/igtest/controlflow/if_may_be_conditional", tool); + public void testIfMayBeConditional() throws Exception { + doTest(); + } + + @Nullable + @Override + protected InspectionProfileEntry getInspection() { + final IfMayBeConditionalInspection inspection = new IfMayBeConditionalInspection(); + inspection.reportMethodCalls = true; + return inspection; } } \ No newline at end of file From cd73aa3441e919d9f31c4a6f2b6ec8d3c191a905 Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Wed, 7 Oct 2015 10:08:23 +0200 Subject: [PATCH 06/22] IDEA-145984 (Replace with conditional exception gives confusing result on conditional with many if / else if / else branches) --- .../controlflow/IfMayBeConditionalInspection.java | 5 ++++- .../if_may_be_conditional/IfMayBeConditional.java | 13 +++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/controlflow/IfMayBeConditionalInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/controlflow/IfMayBeConditionalInspection.java index f75617403e5c..04724eb4d929 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/controlflow/IfMayBeConditionalInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/controlflow/IfMayBeConditionalInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2012 Bas Leijdekkers + * Copyright 2008-2015 Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -190,6 +190,9 @@ public class IfMayBeConditionalInspection extends BaseInspection { @Override public void visitIfStatement(PsiIfStatement statement) { super.visitIfStatement(statement); + if (ControlFlowUtils.isElseIf(statement)) { + return; + } final PsiStatement thenBranch = statement.getThenBranch(); final PsiStatement elseBranch = statement.getElseBranch(); final PsiStatement thenStatement = ControlFlowUtils.stripBraces(thenBranch); diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/controlflow/if_may_be_conditional/IfMayBeConditional.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/controlflow/if_may_be_conditional/IfMayBeConditional.java index 39e8e05d1768..4f8524d2600a 100644 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/controlflow/if_may_be_conditional/IfMayBeConditional.java +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/controlflow/if_may_be_conditional/IfMayBeConditional.java @@ -42,4 +42,17 @@ public class IfMayBeConditional { i = b < c ? b : c; } } + + void largeIf(boolean a, boolean b, boolean c) { + final String value; + if (a) { + value = "a"; + } else if (b) { + value = "b"; + } else if (c) { + value = "c"; + } else { + value = "d"; + } + } } From c697d69d0a510b2c79cf926a2cfa488b16ebff65 Mon Sep 17 00:00:00 2001 From: nik Date: Wed, 7 Oct 2015 11:03:52 +0300 Subject: [PATCH 07/22] javadoc configuration: don't save default value of newly added option --- .../src/com/intellij/javadoc/JavadocConfiguration.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/java/java-impl/src/com/intellij/javadoc/JavadocConfiguration.java b/java/java-impl/src/com/intellij/javadoc/JavadocConfiguration.java index 55e392b9434b..5ac31e88f656 100644 --- a/java/java-impl/src/com/intellij/javadoc/JavadocConfiguration.java +++ b/java/java-impl/src/com/intellij/javadoc/JavadocConfiguration.java @@ -21,6 +21,9 @@ import com.intellij.openapi.util.JDOMExternalizable; import com.intellij.openapi.util.WriteExternalException; import com.intellij.psi.PsiKeyword; import org.jdom.Element; +import org.jetbrains.annotations.NotNull; + +import java.lang.reflect.Field; /** * @author Eugene Zhuravlev @@ -53,7 +56,12 @@ public class JavadocConfiguration implements JDOMExternalizable { } public void writeExternal(Element element) throws WriteExternalException { - DefaultJDOMExternalizer.writeExternal(this, element); + DefaultJDOMExternalizer.writeExternal(this, element, new DefaultJDOMExternalizer.JDOMFilter() { + @Override + public boolean isAccept(@NotNull Field field) { + return !field.getName().equals("OPTION_LINK_TO_JDK_DOCS") || OPTION_LINK_TO_JDK_DOCS; + } + }); } } From 970b75864da583fea696011ef01fd89c68d75647 Mon Sep 17 00:00:00 2001 From: nik Date: Wed, 7 Oct 2015 11:12:07 +0300 Subject: [PATCH 08/22] PluginProjectWizardTest fixed (now plugin.xml is created in 'resources/META-INF' directory) --- plugins/devkit/testSources/PluginProjectWizardTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/devkit/testSources/PluginProjectWizardTest.java b/plugins/devkit/testSources/PluginProjectWizardTest.java index 99ba3f4cbb9c..af96140257ce 100644 --- a/plugins/devkit/testSources/PluginProjectWizardTest.java +++ b/plugins/devkit/testSources/PluginProjectWizardTest.java @@ -36,7 +36,7 @@ public class PluginProjectWizardTest extends NewProjectWizardTestCase { createSdk("devkit", IdeaJdk.getInstance()); Project project = createProjectFromTemplate(PluginModuleType.getInstance().getName(), null, null); VirtualFile baseDir = project.getBaseDir(); - VirtualFile virtualFile = VfsUtilCore.findRelativeFile("META-INF/plugin.xml", baseDir); + VirtualFile virtualFile = VfsUtilCore.findRelativeFile("resources/META-INF/plugin.xml", baseDir); assertNotNull(virtualFile); RunnerAndConfigurationSettings configuration = RunManager.getInstance(project).getSelectedConfiguration(); From c4b0a9df56fa23310f3e14bc97be4dee56291c4a Mon Sep 17 00:00:00 2001 From: nik Date: Wed, 7 Oct 2015 12:46:23 +0300 Subject: [PATCH 09/22] PsiUtil: marked old 'isCompileTimeConstant' method as deprecated so we would be able to remove it later --- .../daemon/impl/analysis/GenericsHighlightUtil.java | 2 +- .../daemon/impl/analysis/HighlightClassUtil.java | 2 +- java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java | 9 ++++++--- .../siyeh/ig/abstraction/MagicNumberInspectionBase.java | 2 +- .../siyeh/ig/performance/FieldMayBeStaticInspection.java | 4 ++-- 5 files changed, 11 insertions(+), 8 deletions(-) diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/GenericsHighlightUtil.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/GenericsHighlightUtil.java index f563f316422b..5448aff23bb9 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/GenericsHighlightUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/GenericsHighlightUtil.java @@ -740,7 +740,7 @@ public class GenericsHighlightUtil { if (type instanceof PsiClassType && ((PsiClassType)type).resolve() == aClass) return null; } - if (PsiUtil.isCompileTimeConstant(field)) return null; + if (PsiUtil.isCompileTimeConstant((PsiVariable)field)) return null; String description = JavaErrorMessages.message( "illegal.to.access.static.member.from.enum.constructor.or.instance.initializer", diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightClassUtil.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightClassUtil.java index 830678b58b1e..0dbd868f5144 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightClassUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightClassUtil.java @@ -292,7 +292,7 @@ public class HighlightClassUtil { } PsiField field = (PsiField)keyword.getParent().getParent(); - if (PsiUtilCore.hasErrorElementChild(field) || PsiUtil.isCompileTimeConstant(field)) { + if (PsiUtilCore.hasErrorElementChild(field) || PsiUtil.isCompileTimeConstant((PsiVariable)field)) { return null; } diff --git a/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java b/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java index 306c43e5b15c..54cade32e37a 100644 --- a/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java +++ b/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java @@ -636,13 +636,16 @@ public final class PsiUtil extends PsiUtilCore { return containingClass1 == null && containingClass2 == null; } + /** + * @deprecated use more generic {@link #isCompileTimeConstant(PsiVariable)} instead + */ public static boolean isCompileTimeConstant(@NotNull final PsiField field) { return isCompileTimeConstant((PsiVariable)field); } - /** - * JLS 15.28 - */ + /** + * JLS 15.28 + */ public static boolean isCompileTimeConstant(@NotNull final PsiVariable field) { return field.hasModifierProperty(PsiModifier.FINAL) && (TypeConversionUtil.isPrimitiveAndNotNull(field.getType()) || field.getType().equalsToText(JAVA_LANG_STRING)) diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/abstraction/MagicNumberInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/abstraction/MagicNumberInspectionBase.java index a664d01a92dc..222c5715c602 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/abstraction/MagicNumberInspectionBase.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/abstraction/MagicNumberInspectionBase.java @@ -93,7 +93,7 @@ public class MagicNumberInspectionBase extends BaseInspection { return; } final PsiField field = PsiTreeUtil.getParentOfType(expression, PsiField.class); - if (field != null && PsiUtil.isCompileTimeConstant(field)) { + if (field != null && PsiUtil.isCompileTimeConstant((PsiVariable)field)) { return; } final PsiElement parent = expression.getParent(); diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/performance/FieldMayBeStaticInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/performance/FieldMayBeStaticInspection.java index 40b06c127967..fcdcdbfa22fe 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/performance/FieldMayBeStaticInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/performance/FieldMayBeStaticInspection.java @@ -77,11 +77,11 @@ public class FieldMayBeStaticInspection extends BaseInspection { if (containingClass != null && !containingClass.hasModifierProperty(PsiModifier.STATIC) && containingClass.getContainingClass() != null - && !PsiUtil.isCompileTimeConstant(field)) { + && !PsiUtil.isCompileTimeConstant((PsiVariable)field)) { // inner class cannot have static declarations return; } - if (containingClass instanceof PsiAnonymousClass && !PsiUtil.isCompileTimeConstant(field)) { + if (containingClass instanceof PsiAnonymousClass && !PsiUtil.isCompileTimeConstant((PsiVariable)field)) { return; } if (!canBeStatic(initializer)) { From c659618f9fe41493bc1fde670fb862f9eeb5ee2e Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Fri, 2 Oct 2015 18:38:54 +0300 Subject: [PATCH 10/22] guava type migration: limit() and skip() does not receive lambda parameter --- .../guava/GuavaFluentIterableConversionRule.java | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/guava/GuavaFluentIterableConversionRule.java b/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/guava/GuavaFluentIterableConversionRule.java index 3a75c0202116..ef0ba1d21089 100644 --- a/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/guava/GuavaFluentIterableConversionRule.java +++ b/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/guava/GuavaFluentIterableConversionRule.java @@ -75,8 +75,8 @@ public class GuavaFluentIterableConversionRule extends BaseGuavaTypeConversionRu new TypeConversionDescriptorFactory("$it$.contains($o$)", "$it$.anyMatch(e -> e != null && e.equals($o$))", false)); DESCRIPTORS_MAP.put("from", new TypeConversionDescriptorFactory("FluentIterable.from($it$)", "$it$.stream()", false, true)); DESCRIPTORS_MAP.put("isEmpty", new TypeConversionDescriptorFactory("$q$.isEmpty()", "$q$.findAny().isPresent()", false)); - DESCRIPTORS_MAP.put("skip", new TypeConversionDescriptorFactory("$q$.skip($p$)", "$q$.skip($p$)", false, true)); - DESCRIPTORS_MAP.put("limit", new TypeConversionDescriptorFactory("$q$.limit($p$)", "$q$.limit($p$)", false, true)); + DESCRIPTORS_MAP.put("skip", new TypeConversionDescriptorFactory("$q$.skip($p$)", "$q$.skip($p$)", false, false)); + DESCRIPTORS_MAP.put("limit", new TypeConversionDescriptorFactory("$q$.limit($p$)", "$q$.limit($p$)", false, false)); DESCRIPTORS_MAP.put("first", new TypeConversionDescriptorFactory("$q$.first()", "$q$.findFirst()", false)); DESCRIPTORS_MAP.put("transform", new TypeConversionDescriptorFactory("$q$.transform($params$)", "$q$.map($params$)", true, true)); //TODO support @@ -184,15 +184,21 @@ public class GuavaFluentIterableConversionRule extends BaseGuavaTypeConversionRu public PsiExpression replace(PsiExpression expression) throws IncorrectOperationException { PsiMethodCallExpression toReturn = null; PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression) expression; + final SmartPointerManager smartPointerManager = SmartPointerManager.getInstance(expression.getProject()); + for (TypeConversionDescriptor descriptor : myMethodDescriptors) { + final PsiExpression oldQualifier = methodCallExpression.getMethodExpression().getQualifierExpression(); + final SmartPsiElementPointer qualifierRef = oldQualifier == null + ? null + : smartPointerManager.createSmartPsiElementPointer(oldQualifier); final PsiMethodCallExpression replaced = (PsiMethodCallExpression)descriptor.replace(methodCallExpression); if (toReturn == null) { toReturn = replaced; } - final PsiExpression qualifier = replaced.getMethodExpression().getQualifierExpression(); - if (qualifier instanceof PsiMethodCallExpression) { - methodCallExpression = (PsiMethodCallExpression)qualifier; + final PsiExpression newQualifier = qualifierRef == null ? null : qualifierRef.getElement(); + if (newQualifier instanceof PsiMethodCallExpression) { + methodCallExpression = (PsiMethodCallExpression)newQualifier; } else { return toReturn; } From cdf73572f20e8f77e68ba9de2f4d2e70483d7a5e Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Wed, 7 Oct 2015 12:55:29 +0300 Subject: [PATCH 11/22] guava type migration: supports migration of FluentIterable/Optional methodChains which are started with FluentIterable.from --- .../TypeMigrationReplacementUtil.java | 15 +-- .../src/META-INF/TypeMigration.xml | 1 + .../inspections/GuavaInspection.java | 115 +++++++++++++++++- .../guava/BaseGuavaTypeConversionRule.java | 18 +-- .../GuavaFluentIterableConversionRule.java | 59 ++++++--- .../guava/GuavaOptionalConversionRule.java | 21 ++-- .../guava/GuavaSupplierConversionRule.java | 46 +++++++ ...daParametersTypeConversionDescription.java | 15 ++- .../inspections/GuavaInspectionTest.java | 10 +- .../fluentIterableChainWithOptional.java | 10 ++ ...fluentIterableChainWithOptional_after.java | 10 ++ .../fluentIterableChainWithoutVariable.java | 25 ++++ ...entIterableChainWithoutVariable_after.java | 27 ++++ 13 files changed, 322 insertions(+), 50 deletions(-) create mode 100644 java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/guava/GuavaSupplierConversionRule.java create mode 100644 java/typeMigration/testData/inspections/guava/fluentIterableChainWithOptional.java create mode 100644 java/typeMigration/testData/inspections/guava/fluentIterableChainWithOptional_after.java create mode 100644 java/typeMigration/testData/inspections/guava/fluentIterableChainWithoutVariable.java create mode 100644 java/typeMigration/testData/inspections/guava/fluentIterableChainWithoutVariable_after.java diff --git a/java/java-impl/src/com/intellij/refactoring/typeMigration/TypeMigrationReplacementUtil.java b/java/java-impl/src/com/intellij/refactoring/typeMigration/TypeMigrationReplacementUtil.java index 0611af494c47..15d7b115352e 100644 --- a/java/java-impl/src/com/intellij/refactoring/typeMigration/TypeMigrationReplacementUtil.java +++ b/java/java-impl/src/com/intellij/refactoring/typeMigration/TypeMigrationReplacementUtil.java @@ -39,17 +39,17 @@ public class TypeMigrationReplacementUtil { private TypeMigrationReplacementUtil() { } - public static void replaceExpression(PsiExpression expression, final Project project, Object conversion) { + public static PsiElement replaceExpression(PsiExpression expression, final Project project, Object conversion) { if (conversion instanceof TypeConversionDescriptorBase) { try { - ((TypeConversionDescriptorBase)conversion).replace(expression); + return ((TypeConversionDescriptorBase)conversion).replace(expression); } catch (IncorrectOperationException e) { LOG.error(e); } } else if (conversion instanceof String) { String replacement = (String)conversion; try { - expression.replace( + return expression.replace( JavaPsiFacade.getInstance(project).getElementFactory().createExpressionFromText(replacement, expression)); } catch (IncorrectOperationException e) { @@ -66,7 +66,7 @@ public class TypeMigrationReplacementUtil { if (conversion instanceof PsiMethod) { if (resolved instanceof PsiMethod) { try { - expression.replace( + return expression.replace( JavaPsiFacade.getInstance(project).getElementFactory().createExpressionFromText(newref, expression)); } catch (IncorrectOperationException e) { @@ -75,7 +75,7 @@ public class TypeMigrationReplacementUtil { } else { try { - expression.replace(JavaPsiFacade.getInstance(project).getElementFactory().createExpressionFromText( + return expression.replace(JavaPsiFacade.getInstance(project).getElementFactory().createExpressionFromText( newref + "()", expression)); } catch (IncorrectOperationException e) { @@ -86,7 +86,7 @@ public class TypeMigrationReplacementUtil { else if (conversion instanceof PsiField) { if (resolved instanceof PsiField) { try { - expression.replace( + return expression.replace( JavaPsiFacade.getInstance(project).getElementFactory().createExpressionFromText(newref, expression)); } catch (IncorrectOperationException e) { @@ -98,7 +98,7 @@ public class TypeMigrationReplacementUtil { if (parent instanceof PsiMethodCallExpression) { try { - parent.replace( + return parent.replace( JavaPsiFacade.getInstance(project).getElementFactory().createExpressionFromText(newref, expression)); } catch (IncorrectOperationException e) { @@ -108,6 +108,7 @@ public class TypeMigrationReplacementUtil { } } } + return expression; } static void migratePsiMemberType(final PsiElement element, final Project project, PsiType migratedType) { diff --git a/java/typeMigration/src/META-INF/TypeMigration.xml b/java/typeMigration/src/META-INF/TypeMigration.xml index 9a0c0e187dfa..082c3ba802d5 100644 --- a/java/typeMigration/src/META-INF/TypeMigration.xml +++ b/java/typeMigration/src/META-INF/TypeMigration.xml @@ -12,6 +12,7 @@ + com.intellij.refactoring.typeMigration.intentions.ConvertFieldToAtomicIntention Java/Concurrency diff --git a/java/typeMigration/src/com/intellij/refactoring/typeMigration/inspections/GuavaInspection.java b/java/typeMigration/src/com/intellij/refactoring/typeMigration/inspections/GuavaInspection.java index 7a31db0f2d95..914a17cb6bf0 100644 --- a/java/typeMigration/src/com/intellij/refactoring/typeMigration/inspections/GuavaInspection.java +++ b/java/typeMigration/src/com/intellij/refactoring/typeMigration/inspections/GuavaInspection.java @@ -15,21 +15,31 @@ */ package com.intellij.refactoring.typeMigration.inspections; +import com.intellij.codeInsight.FileModificationService; import com.intellij.codeInsight.daemon.impl.quickfix.VariableTypeFix; import com.intellij.codeInspection.*; +import com.intellij.openapi.command.undo.UndoUtil; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.AtomicNotNullLazyValue; import com.intellij.psi.*; +import com.intellij.psi.codeStyle.JavaCodeStyleManager; import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; -import com.intellij.refactoring.typeMigration.TypeMigrationVariableTypeFixProvider; +import com.intellij.refactoring.typeMigration.*; import com.intellij.refactoring.typeMigration.rules.TypeConversionRule; import com.intellij.refactoring.typeMigration.rules.guava.BaseGuavaTypeConversionRule; +import com.intellij.refactoring.typeMigration.rules.guava.GuavaFluentIterableConversionRule; import com.intellij.refactoring.typeMigration.rules.guava.GuavaFunctionConversionRule; +import com.intellij.refactoring.typeMigration.rules.guava.GuavaOptionalConversionRule; +import com.intellij.util.IncorrectOperationException; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.hash.HashMap; +import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; @@ -43,6 +53,7 @@ public class GuavaInspection extends BaseJavaLocalInspectionTool { private final static Logger LOG = Logger.getInstance(GuavaInspection.class); private final static String PROBLEM_DESCRIPTION_FOR_VARIABLE = "Guava's functional primitives can be replaced by Java API"; + private final static String PROBLEM_DESCRIPTION_FOR_METHOD_CHAIN = "Guava's FluentIterable method chain can be replaced by Java API"; @NotNull @Override @@ -93,6 +104,54 @@ public class GuavaInspection extends BaseJavaLocalInspectionTool { } } + @Override + public void visitMethodCallExpression(PsiMethodCallExpression expression) { + if (!isFluentIterableFromCall(expression)) return; + + final PsiMethodCallExpression chain = findGuavaMethodChain(expression); + + PsiClassType initialType = (PsiClassType)expression.getType(); + LOG.assertTrue(initialType != null); + PsiClass resolvedClass = initialType.resolve(); + PsiClass target; + if (resolvedClass == null || (target = myGuavaClassConversions.getValue().get(resolvedClass.getQualifiedName())) == null) { + return; + } + PsiClassType targetType = addTypeParameters(initialType, initialType.resolveGenerics(), target); + + holder.registerProblem(chain, PROBLEM_DESCRIPTION_FOR_METHOD_CHAIN, new MigrateFluentIterableChainQuickFix(chain, initialType, targetType)); + } + + private boolean isFluentIterableFromCall(PsiMethodCallExpression expression) { + PsiMethod method = expression.resolveMethod(); + if (method == null || !"from".equals(method.getName())) { + return false; + } + PsiClass aClass = method.getContainingClass(); + return aClass != null && GuavaFluentIterableConversionRule.FLUENT_ITERABLE.equals(aClass.getQualifiedName()); + } + + private PsiMethodCallExpression findGuavaMethodChain(PsiMethodCallExpression expression) { + PsiMethodCallExpression chain = expression; + while (true) { + final PsiMethodCallExpression current = PsiTreeUtil.getParentOfType(chain, PsiMethodCallExpression.class); + if (current != null && current.getMethodExpression().getQualifierExpression() == chain) { + final PsiMethod method = current.resolveMethod(); + if (method == null) { + return chain; + } + final PsiClass containingClass = method.getContainingClass(); + if (containingClass == null || ! (GuavaFluentIterableConversionRule.FLUENT_ITERABLE.equals(containingClass.getQualifiedName()) + || GuavaOptionalConversionRule.GUAVA_OPTIONAL.equals(containingClass.getQualifiedName()))) { + return chain; + } + } else { + return chain; + } + chain = current; + } + } + private PsiClassType addTypeParameters(PsiType currentType, PsiClassType.ClassResolveResult currentTypeResolveResult, PsiClass targetClass) { final Map substitutionMap = currentTypeResolveResult.getSubstitutor().getSubstitutionMap(); final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(holder.getProject()); @@ -110,4 +169,58 @@ public class GuavaInspection extends BaseJavaLocalInspectionTool { } }; } + + public static class MigrateFluentIterableChainQuickFix extends LocalQuickFixAndIntentionActionOnPsiElement { + private final PsiClassType myInitialType; + private final PsiClassType myTargetType; + + private MigrateFluentIterableChainQuickFix(@Nullable PsiElement element, PsiClassType initialType, PsiClassType targetType) { + super(element); + myInitialType = initialType; + myTargetType = targetType; + } + + @Override + public void invoke(@NotNull Project project, + @NotNull PsiFile file, + @Nullable("is null when called from inspection") Editor editor, + @NotNull PsiElement startElement, + @NotNull PsiElement endElement) { + + if (!FileModificationService.getInstance().prepareFileForWrite(file)) return; + try { + final PsiMethodCallExpression expr = (PsiMethodCallExpression)startElement; + final TypeMigrationRules rules = new TypeMigrationRules(myInitialType); + rules.setMigrationRootType(myTargetType); + rules.setBoundScope(GlobalSearchScope.fileScope(file)); + final TypeConversionDescriptorBase conversion = + rules.findConversion(myInitialType, myTargetType, expr.resolveMethod(), expr, new TypeMigrationLabeler(rules)); + LOG.assertTrue(conversion != null); + final PsiElement replacedExpression = TypeMigrationReplacementUtil.replaceExpression(expr, project, conversion); + JavaCodeStyleManager.getInstance(project).shortenClassReferences(replacedExpression); + UndoUtil.markPsiFileForUndo(file); + } + catch (IncorrectOperationException e) { + LOG.error(e); + } + } + + @Override + public boolean startInWriteAction() { + return true; + } + + @NotNull + @Override + public String getText() { + return getFamilyName(); + } + + @Nls + @NotNull + @Override + public String getFamilyName() { + return "Replace FluentIterable method chain by Java API"; + } + } } \ No newline at end of file diff --git a/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/guava/BaseGuavaTypeConversionRule.java b/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/guava/BaseGuavaTypeConversionRule.java index 8daec6de5c7d..8984ccc0f4a4 100644 --- a/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/guava/BaseGuavaTypeConversionRule.java +++ b/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/guava/BaseGuavaTypeConversionRule.java @@ -45,10 +45,10 @@ public abstract class BaseGuavaTypeConversionRule extends TypeConversionRule { protected void fillSimpleDescriptors(Map descriptorsMap) {}; @Nullable - protected TypeConversionDescriptorBase findConversionForMethod(@NotNull PsiType from, - @NotNull PsiType to, + protected TypeConversionDescriptorBase findConversionForMethod(PsiType from, + PsiType to, @NotNull PsiMethod method, - String methodName, + @NotNull String methodName, PsiExpression context, TypeMigrationLabeler labeler) { return null; @@ -62,12 +62,12 @@ public abstract class BaseGuavaTypeConversionRule extends TypeConversionRule { @Nullable @Override - public final TypeConversionDescriptorBase findConversion(PsiType from, - PsiType to, - PsiMember member, - PsiExpression context, - TypeMigrationLabeler labeler) { - if (!canConvert(from, to)) { + public final TypeConversionDescriptorBase findConversion(@Nullable PsiType from, + @Nullable PsiType to, + PsiMember member, + PsiExpression context, + TypeMigrationLabeler labeler) { + if (from != null && to != null && !canConvert(from, to)) { return null; } if (member instanceof PsiMethod) { diff --git a/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/guava/GuavaFluentIterableConversionRule.java b/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/guava/GuavaFluentIterableConversionRule.java index ef0ba1d21089..386c6ebcfe6b 100644 --- a/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/guava/GuavaFluentIterableConversionRule.java +++ b/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/guava/GuavaFluentIterableConversionRule.java @@ -16,10 +16,12 @@ package com.intellij.refactoring.typeMigration.rules.guava; import com.intellij.codeInspection.java18StreamApi.StreamApiConstants; +import com.intellij.openapi.util.NotNullLazyValue; import com.intellij.psi.*; import com.intellij.refactoring.typeMigration.TypeConversionDescriptor; import com.intellij.refactoring.typeMigration.TypeConversionDescriptorBase; import com.intellij.refactoring.typeMigration.TypeMigrationLabeler; +import com.intellij.refactoring.typeMigration.rules.TypeConversionRule; import com.intellij.util.IncorrectOperationException; import com.intellij.util.SmartList; import com.intellij.util.containers.hash.HashMap; @@ -27,7 +29,6 @@ import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.Collections; import java.util.List; import java.util.Map; @@ -75,8 +76,8 @@ public class GuavaFluentIterableConversionRule extends BaseGuavaTypeConversionRu new TypeConversionDescriptorFactory("$it$.contains($o$)", "$it$.anyMatch(e -> e != null && e.equals($o$))", false)); DESCRIPTORS_MAP.put("from", new TypeConversionDescriptorFactory("FluentIterable.from($it$)", "$it$.stream()", false, true)); DESCRIPTORS_MAP.put("isEmpty", new TypeConversionDescriptorFactory("$q$.isEmpty()", "$q$.findAny().isPresent()", false)); - DESCRIPTORS_MAP.put("skip", new TypeConversionDescriptorFactory("$q$.skip($p$)", "$q$.skip($p$)", false, false)); - DESCRIPTORS_MAP.put("limit", new TypeConversionDescriptorFactory("$q$.limit($p$)", "$q$.limit($p$)", false, false)); + DESCRIPTORS_MAP.put("skip", new TypeConversionDescriptorFactory("$q$.skip($p$)", "$q$.skip($p$)", false, true)); + DESCRIPTORS_MAP.put("limit", new TypeConversionDescriptorFactory("$q$.limit($p$)", "$q$.limit($p$)", false, true)); DESCRIPTORS_MAP.put("first", new TypeConversionDescriptorFactory("$q$.first()", "$q$.findFirst()", false)); DESCRIPTORS_MAP.put("transform", new TypeConversionDescriptorFactory("$q$.transform($params$)", "$q$.map($params$)", true, true)); //TODO support @@ -106,11 +107,11 @@ public class GuavaFluentIterableConversionRule extends BaseGuavaTypeConversionRu protected TypeConversionDescriptorBase findConversionForMethod(@NotNull PsiType from, @NotNull PsiType to, @NotNull PsiMethod method, - String methodName, + @NotNull String methodName, PsiExpression context, TypeMigrationLabeler labeler) { if (context instanceof PsiMethodCallExpression) { - return buildCompoundDescriptor((PsiMethodCallExpression)context, to); + return buildCompoundDescriptor((PsiMethodCallExpression)context, to, labeler); } final TypeConversionDescriptorFactory base = DESCRIPTORS_MAP.get(methodName); if (base != null) { @@ -126,8 +127,23 @@ public class GuavaFluentIterableConversionRule extends BaseGuavaTypeConversionRu } @Nullable - private GuavaChainedConversionDescriptor buildCompoundDescriptor(PsiMethodCallExpression expression, PsiType to) { - List methodDescriptors = new SmartList(); + private static GuavaChainedConversionDescriptor buildCompoundDescriptor(PsiMethodCallExpression expression, + PsiType to, + TypeMigrationLabeler labeler) { + List methodDescriptors = new SmartList(); + + NotNullLazyValue optionalDescriptor = new NotNullLazyValue() { + @NotNull + @Override + protected TypeConversionRule compute() { + for (TypeConversionRule rule : TypeConversionRule.EP_NAME.getExtensions()) { + if (rule instanceof GuavaOptionalConversionRule) { + return rule; + } + } + throw new RuntimeException("GuavaOptionalConversionRule extension is not found"); + } + }; PsiMethodCallExpression current = expression; while (true) { @@ -137,15 +153,26 @@ public class GuavaFluentIterableConversionRule extends BaseGuavaTypeConversionRu } final String methodName = method.getName(); final PsiClass containingClass = method.getContainingClass(); - //TODO for optional too - if (containingClass == null || !FLUENT_ITERABLE.equals(containingClass.getQualifiedName())) { + if (containingClass == null) { break; } - final TypeConversionDescriptorFactory descriptorFactory = DESCRIPTORS_MAP.get(methodName); - if (descriptorFactory == null) { - return null; + TypeConversionDescriptorBase descriptor; + if (FLUENT_ITERABLE.equals(containingClass.getQualifiedName())) { + final TypeConversionDescriptorFactory descriptorFactory = DESCRIPTORS_MAP.get(methodName); + if (descriptorFactory == null) { + return null; + } + descriptor = descriptorFactory.create(); } - methodDescriptors.add(descriptorFactory.create()); + else if (GuavaOptionalConversionRule.GUAVA_OPTIONAL.equals(containingClass.getQualifiedName())) { + descriptor = optionalDescriptor.getValue().findConversion(null, null, method, current.getMethodExpression(), labeler); + if (descriptor == null) { + return null; + } + } else { + break; + } + methodDescriptors.add(descriptor); final PsiExpression qualifier = current.getMethodExpression().getQualifierExpression(); if (qualifier instanceof PsiMethodCallExpression) { current = (PsiMethodCallExpression)qualifier; @@ -172,10 +199,10 @@ public class GuavaFluentIterableConversionRule extends BaseGuavaTypeConversionRu } private static class GuavaChainedConversionDescriptor extends TypeConversionDescriptorBase { - private final List myMethodDescriptors; + private final List myMethodDescriptors; private final PsiType myToType; - private GuavaChainedConversionDescriptor(List descriptors, PsiType to) { + private GuavaChainedConversionDescriptor(List descriptors, PsiType to) { myMethodDescriptors = descriptors; myToType = to; } @@ -186,7 +213,7 @@ public class GuavaFluentIterableConversionRule extends BaseGuavaTypeConversionRu PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression) expression; final SmartPointerManager smartPointerManager = SmartPointerManager.getInstance(expression.getProject()); - for (TypeConversionDescriptor descriptor : myMethodDescriptors) { + for (TypeConversionDescriptorBase descriptor : myMethodDescriptors) { final PsiExpression oldQualifier = methodCallExpression.getMethodExpression().getQualifierExpression(); final SmartPsiElementPointer qualifierRef = oldQualifier == null ? null diff --git a/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/guava/GuavaOptionalConversionRule.java b/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/guava/GuavaOptionalConversionRule.java index cc8968f8f51e..9363797e757d 100644 --- a/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/guava/GuavaOptionalConversionRule.java +++ b/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/guava/GuavaOptionalConversionRule.java @@ -36,10 +36,10 @@ public class GuavaOptionalConversionRule extends BaseGuavaTypeConversionRule { @Nullable @Override - protected TypeConversionDescriptorBase findConversionForMethod(@NotNull PsiType from, - @NotNull PsiType to, + protected TypeConversionDescriptorBase findConversionForMethod(@Nullable PsiType from, + @Nullable PsiType to, @NotNull PsiMethod method, - String methodName, + @NotNull String methodName, PsiExpression context, TypeMigrationLabeler labeler) { if ("or".equals(methodName)) { @@ -53,9 +53,7 @@ public class GuavaOptionalConversionRule extends BaseGuavaTypeConversionRule { if (aClass != null) { final String qName = aClass.getQualifiedName(); String pattern = - GUAVA_OPTIONAL.equals(qName) ? "java.util.Optional.ofNullable($expr$.get())" : "com.google.common.bas.Supplier".equals(qName) - ? "java.util.Optional.ofNullable($expr$)" - : "java.util.Optional.ofNullable($expr$)"; + GUAVA_OPTIONAL.equals(qName) ? "java.util.Optional.ofNullable($expr$.get())" : "java.util.Optional.ofNullable($expr$)"; return new TypeConversionDescriptor("$expr$", pattern); } return null; @@ -73,10 +71,15 @@ public class GuavaOptionalConversionRule extends BaseGuavaTypeConversionRule { if (aClass != null) { final String qName = aClass.getQualifiedName(); if (GUAVA_OPTIONAL.equals(qName)) { - return new TypeConversionDescriptor("$val$.or($other$)", "java.util.Optional.ofNullable($val$.orElseGet($other$::get))", to); + TypeConversionDescriptor descriptor = + new TypeConversionDescriptor("$val$.or($other$)", "java.util.Optional.ofNullable($val$.orElseGet($other$::get))"); + if (to != null) { + descriptor.withConversionType(to); + } + return descriptor; } - String pattern = "com.google.common.bas.Supplier".equals(qName) ? "$val$.orElseGet($other$::get)" : "$val$.orElse($other$)"; - return new TypeConversionDescriptor("$val$.or($other$)", pattern); + String pattern = GuavaSupplierConversionRule.GUAVA_SUPPLIER.equals(qName) ? "$val$.orElseGet($other$)" : "$val$.orElse($other$)"; + return new LambdaParametersTypeConversionDescription("$val$.or($other$)", pattern); } return null; } diff --git a/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/guava/GuavaSupplierConversionRule.java b/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/guava/GuavaSupplierConversionRule.java new file mode 100644 index 000000000000..dc9df00e3250 --- /dev/null +++ b/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/guava/GuavaSupplierConversionRule.java @@ -0,0 +1,46 @@ +/* + * 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 com.intellij.refactoring.typeMigration.rules.guava; + +import com.intellij.refactoring.typeMigration.TypeConversionDescriptor; +import com.intellij.refactoring.typeMigration.TypeConversionDescriptorBase; +import org.jetbrains.annotations.NotNull; + +import java.util.Map; + +/** + * @author Dmitry Batkovich + */ +public class GuavaSupplierConversionRule extends BaseGuavaTypeConversionRule { + public final static String GUAVA_SUPPLIER = "com.google.common.base.Supplier"; + + @Override + protected void fillSimpleDescriptors(Map descriptorsMap) { + descriptorsMap.put("get", new TypeConversionDescriptor("$val$.get()", "$val$.get()")); + } + + @NotNull + @Override + public String ruleFromClass() { + return GUAVA_SUPPLIER; + } + + @NotNull + @Override + public String ruleToClass() { + return "java.util.function.Supplier"; + } +} diff --git a/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/guava/LambdaParametersTypeConversionDescription.java b/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/guava/LambdaParametersTypeConversionDescription.java index c70598eeb77d..c64472df85a3 100644 --- a/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/guava/LambdaParametersTypeConversionDescription.java +++ b/java/typeMigration/src/com/intellij/refactoring/typeMigration/rules/guava/LambdaParametersTypeConversionDescription.java @@ -27,10 +27,6 @@ import org.jetbrains.annotations.NonNls; public class LambdaParametersTypeConversionDescription extends TypeConversionDescriptor { private static final Logger LOG = Logger.getInstance(LambdaParametersTypeConversionDescription.class); - public LambdaParametersTypeConversionDescription(@NonNls String stringToReplace, @NonNls String replaceByString, PsiType conversionType) { - super(stringToReplace, replaceByString, conversionType); - } - public LambdaParametersTypeConversionDescription(@NonNls String stringToReplace, @NonNls String replaceByString) { super(stringToReplace, replaceByString); } @@ -52,7 +48,16 @@ public class LambdaParametersTypeConversionDescription extends TypeConversionDes } private static PsiExpression addApplyReference(final PsiExpression expression) { - return (PsiExpression)expression.replace(JavaPsiFacade.getElementFactory(expression.getProject()).createExpressionFromText(expression.getText() + "::apply", null)); + boolean isSupplier = false; + PsiType type = expression.getType(); + if (type instanceof PsiClassType) { + PsiClass resolvedClass = ((PsiClassType)type).resolve(); + if (resolvedClass != null && GuavaSupplierConversionRule.GUAVA_SUPPLIER.equals(resolvedClass.getQualifiedName())) { + isSupplier = true; + } + } + return (PsiExpression)expression.replace( + JavaPsiFacade.getElementFactory(expression.getProject()).createExpressionFromText(expression.getText() + "::" + (isSupplier ? "get" : "apply"), null)); } public static PsiExpression convertParameter(PsiExpression expression) { diff --git a/java/typeMigration/test/com/intellij/codeInsight/inspections/GuavaInspectionTest.java b/java/typeMigration/test/com/intellij/codeInsight/inspections/GuavaInspectionTest.java index 8afd272fb25a..886a3c356356 100644 --- a/java/typeMigration/test/com/intellij/codeInsight/inspections/GuavaInspectionTest.java +++ b/java/typeMigration/test/com/intellij/codeInsight/inspections/GuavaInspectionTest.java @@ -63,7 +63,7 @@ public class GuavaInspectionTest extends JavaCodeInsightFixtureTestCase { doTest(); } - public void _testFluentIterableChainWithoutVariable() { + public void testFluentIterableChainWithoutVariable() { doTest(); } @@ -71,19 +71,23 @@ public class GuavaInspectionTest extends JavaCodeInsightFixtureTestCase { doTest(); } + public void testFluentIterableChainWithOptional() { + doTest(); + } + private void doTest() { myFixture.configureByFile(getTestName(true) + ".java"); myFixture.enableInspections(new GuavaInspection()); boolean actionFound = false; myFixture.doHighlighting(); for (IntentionAction action : myFixture.getAvailableIntentions()) { - if (action instanceof VariableTypeFix) { + if (action instanceof VariableTypeFix || action instanceof GuavaInspection.MigrateFluentIterableChainQuickFix) { myFixture.launchAction(action); actionFound = true; break; } } - assertTrue(actionFound); + assertTrue("Quick fix isn't found", actionFound); myFixture.checkResultByFile(getTestName(true) + "_after.java"); } } diff --git a/java/typeMigration/testData/inspections/guava/fluentIterableChainWithOptional.java b/java/typeMigration/testData/inspections/guava/fluentIterableChainWithOptional.java new file mode 100644 index 000000000000..5bc29c381651 --- /dev/null +++ b/java/typeMigration/testData/inspections/guava/fluentIterableChainWithOptional.java @@ -0,0 +1,10 @@ +import java.util.*; +import com.google.common.collect.FluentIterable; + +class A { + void m1() { + ArrayList strings = new ArrayList(); + String str = FluentIterable.from(strings).transform(s -> s + s).limit(10).firstMatch(s -> s.isEmpty()).orNull(); + System.out.println("s: " + str); + } +} \ No newline at end of file diff --git a/java/typeMigration/testData/inspections/guava/fluentIterableChainWithOptional_after.java b/java/typeMigration/testData/inspections/guava/fluentIterableChainWithOptional_after.java new file mode 100644 index 000000000000..e55604989080 --- /dev/null +++ b/java/typeMigration/testData/inspections/guava/fluentIterableChainWithOptional_after.java @@ -0,0 +1,10 @@ +import java.util.*; +import com.google.common.collect.FluentIterable; + +class A { + void m1() { + ArrayList strings = new ArrayList(); + String str = strings.stream().map(s -> s + s).limit(10).filter(s -> s.isEmpty()).findFirst().orElse(null); + System.out.println("s: " + str); + } +} \ No newline at end of file diff --git a/java/typeMigration/testData/inspections/guava/fluentIterableChainWithoutVariable.java b/java/typeMigration/testData/inspections/guava/fluentIterableChainWithoutVariable.java new file mode 100644 index 000000000000..a66738c7885d --- /dev/null +++ b/java/typeMigration/testData/inspections/guava/fluentIterableChainWithoutVariable.java @@ -0,0 +1,25 @@ +/* + * 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. + */ +import java.util.*; +import com.google.common.collect.FluentIterable; + +class A { + int m1() { + ArrayList strings = new ArrayList(); + int size = FluentIterable.from(strings).transform(s -> s + s).limit(10).size(); + return size + } +} \ No newline at end of file diff --git a/java/typeMigration/testData/inspections/guava/fluentIterableChainWithoutVariable_after.java b/java/typeMigration/testData/inspections/guava/fluentIterableChainWithoutVariable_after.java new file mode 100644 index 000000000000..fd41604f6a9f --- /dev/null +++ b/java/typeMigration/testData/inspections/guava/fluentIterableChainWithoutVariable_after.java @@ -0,0 +1,27 @@ +/* + * 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. + */ +import java.util.*; +import java.util.stream.Collectors; + +import com.google.common.collect.FluentIterable; + +class A { + int m1() { + ArrayList strings = new ArrayList(); + int size = strings.stream().map(s -> s + s).limit(10).collect(Collectors.toList()).size(); + return size + } +} \ No newline at end of file From 413295c558eaca643a931657adb71e3945957387 Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Wed, 7 Oct 2015 11:31:00 +0200 Subject: [PATCH 12/22] IG: remove unused variable --- .../IteratorHasNextCallsIteratorNextInspection.java | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/IteratorHasNextCallsIteratorNextInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/IteratorHasNextCallsIteratorNextInspection.java index ea55c8778593..ba62d217d304 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/IteratorHasNextCallsIteratorNextInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/IteratorHasNextCallsIteratorNextInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2007 Dave Griffith, Bas Leijdekkers + * Copyright 2003-2015 Dave Griffith, Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,7 +23,6 @@ import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; import com.siyeh.ig.psiutils.IteratorUtils; import com.siyeh.ig.psiutils.MethodUtils; -import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; public class IteratorHasNextCallsIteratorNextInspection @@ -48,15 +47,12 @@ public class IteratorHasNextCallsIteratorNextInspection return new IteratorHasNextCallsIteratorNext(); } - private static class IteratorHasNextCallsIteratorNext - extends BaseInspectionVisitor { + private static class IteratorHasNextCallsIteratorNext extends BaseInspectionVisitor { @Override public void visitMethod(@NotNull PsiMethod method) { // note: no call to super - @NonNls final String name = method.getName(); - if (!MethodUtils.methodMatches(method, CommonClassNames.JAVA_UTIL_ITERATOR, null, - HardcodedMethodConstants.HAS_NEXT)) { + if (!MethodUtils.methodMatches(method, CommonClassNames.JAVA_UTIL_ITERATOR, null, HardcodedMethodConstants.HAS_NEXT)) { return; } if (!IteratorUtils.containsCallToIteratorNext(method, null, true)) { From be00c872f5735af81a9e007d7c65c4231c6b9f3c Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Wed, 7 Oct 2015 11:58:00 +0200 Subject: [PATCH 13/22] IDEA-145834 (IteratorNextCanNotThrowNoSuchElementException inspection: false positive) --- .../com/siyeh/ig/psiutils/IteratorUtils.java | 7 +- ...asNextCallsIteratorNextInspectionTest.java | 70 +++++++++++++++++++ ...wNoSuchElementExceptionInspectionTest.java | 66 +++++++++++++++++ 3 files changed, 139 insertions(+), 4 deletions(-) create mode 100644 plugins/InspectionGadgets/testsrc/com/siyeh/ig/bugs/IteratorHasNextCallsIteratorNextInspectionTest.java create mode 100644 plugins/InspectionGadgets/testsrc/com/siyeh/ig/bugs/IteratorNextDoesNotThrowNoSuchElementExceptionInspectionTest.java diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/IteratorUtils.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/IteratorUtils.java index 4f4cb93b62e2..f2e1e9ab5e15 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/IteratorUtils.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/IteratorUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2011 Bas Leijdekkers + * Copyright 2003-2015 Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -96,9 +96,8 @@ public class IteratorUtils { } } else { - if (!MethodCallUtils.isCallToMethod(expression, - CommonClassNames.JAVA_UTIL_ITERATOR, null, - HardcodedMethodConstants.NEXT)) { + if (!MethodCallUtils.isCallToMethod(expression, CommonClassNames.JAVA_UTIL_ITERATOR, null, HardcodedMethodConstants.NEXT) + && !MethodCallUtils.isCallToMethod(expression, "java.util.ListIterator", null, "previous")) { return; } } diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/bugs/IteratorHasNextCallsIteratorNextInspectionTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/bugs/IteratorHasNextCallsIteratorNextInspectionTest.java new file mode 100644 index 000000000000..bd09bdcc14bb --- /dev/null +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/bugs/IteratorHasNextCallsIteratorNextInspectionTest.java @@ -0,0 +1,70 @@ +/* + * 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 com.siyeh.ig.bugs; + +import com.intellij.codeInspection.InspectionProfileEntry; +import com.siyeh.ig.LightInspectionTestCase; +import org.jetbrains.annotations.Nullable; + +/** + * @author Bas Leijdekkers + */ +public class IteratorHasNextCallsIteratorNextInspectionTest extends LightInspectionTestCase { + + public void testHasNextCallsNext() { + doTest("import java.util.*;" + + "class MyIterator implements Iterator {" + + " private Iterator iterator;" + + " public MyIterator(Iterator iterator) {" + + " this.iterator = iterator;" + + " }" + + " public boolean /*'Iterator.hasNext()' contains call to 'next()'*/hasNext/**/() {" + + " return next() != null;" + + " }" + + " public T next() {" + + " return iterator.next();" + + " }" + + " public void remove() {" + + " iterator.remove();" + + " }" + + "}"); + } + + public void testHasNextCallsPrevious() { + doTest("import java.util.*;" + + "abstract class MyIterator implements ListIterator {" + + " private ListIterator iterator;" + + " public MyIterator(ListIterator iterator) {" + + " this.iterator = iterator;" + + " }" + + " public boolean /*'Iterator.hasNext()' contains call to 'next()'*/hasNext/**/() {" + + " return previous() != null;" + + " }" + + " public T next() {" + + " return iterator.next();" + + " }" + + " public T previous() {" + + " return iterator.previous();" + + " }" + + "}"); + } + + @Nullable + @Override + protected InspectionProfileEntry getInspection() { + return new IteratorHasNextCallsIteratorNextInspection(); + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/bugs/IteratorNextDoesNotThrowNoSuchElementExceptionInspectionTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/bugs/IteratorNextDoesNotThrowNoSuchElementExceptionInspectionTest.java new file mode 100644 index 000000000000..f5fad269994d --- /dev/null +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/bugs/IteratorNextDoesNotThrowNoSuchElementExceptionInspectionTest.java @@ -0,0 +1,66 @@ +/* + * 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 com.siyeh.ig.bugs; + +import com.intellij.codeInspection.InspectionProfileEntry; +import com.siyeh.ig.LightInspectionTestCase; +import junit.framework.TestCase; +import org.jetbrains.annotations.Nullable; + +/** + * @author Bas Leijdekkers + */ +public class IteratorNextDoesNotThrowNoSuchElementExceptionInspectionTest extends LightInspectionTestCase { + + public void testPrevious() { + doTest("import java.util.*;" + + "class ReverseListIterator implements Iterator {" + + " private ListIterator iterator;" + + " public ReverseListIterator(List list) {" + + " this.iterator = list.listIterator(list.size());" + + " }" + + " public boolean hasNext() {" + + " return iterator.hasPrevious();" + + " }" + + " public T next() {" + + " return iterator.previous();" + + " }" + + " public void remove() {" + + " iterator.remove();" + + " }" + + "}"); + } + + public void testEmpty() { + doTest("import java.util.*;" + + "class EmptyIterator implements Iterator {" + + " public boolean hasNext() {" + + " return false;" + + " }" + + " public T /*'Iterator.next()' which can't throw 'NoSuchElementException'*/next/**/() {" + + " return null;" + + " }" + + " public void remove() {" + + " }" + + "}"); + } + + @Nullable + @Override + protected InspectionProfileEntry getInspection() { + return new IteratorNextDoesNotThrowNoSuchElementExceptionInspection(); + } +} \ No newline at end of file From 95623cf89c62e057fa59b7b5ece78157920e91f1 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Wed, 7 Oct 2015 12:05:39 +0200 Subject: [PATCH 14/22] [tests] test data updated (IDEA-CR-4070) --- .../util/testSrc/com/intellij/openapi/util/BuildNumberTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/util/testSrc/com/intellij/openapi/util/BuildNumberTest.java b/platform/util/testSrc/com/intellij/openapi/util/BuildNumberTest.java index f2e3e0bebfaa..c5690842a29d 100644 --- a/platform/util/testSrc/com/intellij/openapi/util/BuildNumberTest.java +++ b/platform/util/testSrc/com/intellij/openapi/util/BuildNumberTest.java @@ -41,7 +41,7 @@ public class BuildNumberTest { assertTrue(BuildNumber.fromString("90.SNAPSHOT").compareTo(BuildNumber.fromString("90.12345")) > 0); assertTrue(BuildNumber.fromString("IU-90.SNAPSHOT").compareTo(BuildNumber.fromString("RM-90.12345")) > 0); assertTrue(BuildNumber.fromString("IU-90.SNAPSHOT").compareTo(BuildNumber.fromString("RM-100.12345")) < 0); - assertTrue(BuildNumber.fromString("IU-90.SNAPSHOT").compareTo(BuildNumber.fromString("RM-100.SNAPSHOT")) == 0); + assertTrue(BuildNumber.fromString("IU-90.SNAPSHOT").compareTo(BuildNumber.fromString("RM-100.SNAPSHOT")) < 0); assertTrue(BuildNumber.fromString("IU-90.SNAPSHOT").compareTo(BuildNumber.fromString("RM-90.SNAPSHOT")) == 0); } } From ee9afc87e5774231c43a9dbf0390e35ef111bb75 Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Tue, 6 Oct 2015 15:55:13 +0300 Subject: [PATCH 15/22] do not hardcode to BoundedTaskExecutor private implementation details --- .../jetbrains/jps/cmdline/BuildSession.java | 32 +++++++++---------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/jps/jps-builders/src/org/jetbrains/jps/cmdline/BuildSession.java b/jps/jps-builders/src/org/jetbrains/jps/cmdline/BuildSession.java index b0470155ff36..c4fa71e86864 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/cmdline/BuildSession.java +++ b/jps/jps-builders/src/org/jetbrains/jps/cmdline/BuildSession.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * 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. @@ -22,6 +22,7 @@ import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream; import com.intellij.openapi.util.io.FileSystemUtil; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.util.concurrency.Semaphore; import com.intellij.util.concurrency.SequentialTaskExecutor; import com.intellij.util.io.DataOutputStream; import io.netty.channel.Channel; @@ -49,7 +50,6 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import java.util.concurrent.atomic.AtomicBoolean; import static org.jetbrains.jps.api.CmdlineRemoteProto.Message.ControllerMessage.ParametersMessage.TargetTypeBuildScope; @@ -64,7 +64,7 @@ final class BuildSession implements Runnable, CanceledStatus { private final Channel myChannel; @Nullable private final PreloadedData myPreloadedData; - private volatile boolean myCanceled = false; + private volatile boolean myCanceled; private final String myProjectPath; @Nullable private CmdlineRemoteProto.Message.ControllerMessage.FSEvent myInitialFSDelta; @@ -345,7 +345,7 @@ final class BuildSession implements Runnable, CanceledStatus { } public void processFSEvent(final CmdlineRemoteProto.Message.ControllerMessage.FSEvent event) { - myEventsProcessor.submit(new Runnable() { + myEventsProcessor.execute(new Runnable() { @Override public void run() { try { @@ -646,23 +646,21 @@ final class BuildSession implements Runnable, CanceledStatus { } private static class EventsProcessor extends SequentialTaskExecutor { - private final AtomicBoolean myProcessingEnabled = new AtomicBoolean(false); + private final Semaphore myProcessingEnabled = new Semaphore(); - EventsProcessor() { + private EventsProcessor() { super(SharedThreadPool.getInstance()); + myProcessingEnabled.down(); + execute(new Runnable() { + @Override + public void run() { + myProcessingEnabled.waitFor(); + } + }); } - public void startProcessing() { - if (!myProcessingEnabled.getAndSet(true)) { - super.processQueue(); - } - } - - @Override - protected void processQueue() { - if (myProcessingEnabled.get()) { - super.processQueue(); - } + private void startProcessing() { + myProcessingEnabled.up(); } } From 112cbf74e6e3af6575483cb41f24317c1efbe8b6 Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Tue, 6 Oct 2015 19:11:30 +0300 Subject: [PATCH 16/22] toString --- platform/configuration-store-impl/src/StateMap.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/platform/configuration-store-impl/src/StateMap.kt b/platform/configuration-store-impl/src/StateMap.kt index 1ea0b98b8097..403e0f4ef31c 100644 --- a/platform/configuration-store-impl/src/StateMap.kt +++ b/platform/configuration-store-impl/src/StateMap.kt @@ -32,6 +32,9 @@ import java.util.* import java.util.concurrent.atomic.AtomicReferenceArray class StateMap private constructor(private val names: Array, private val states: AtomicReferenceArray) { + override fun toString(): String = + if (this == EMPTY) "EMPTY" else states.toString(); + companion object { private val XML_FORMAT = Format.getRawFormat().setTextMode(Format.TextMode.TRIM).setOmitEncoding(true).setOmitDeclaration(true) From 51df65fe7a7a8e62bee64d1982984a9a758901f7 Mon Sep 17 00:00:00 2001 From: Denis Fokin Date: Wed, 7 Oct 2015 12:15:59 +0300 Subject: [PATCH 17/22] NPE --- .../src/com/intellij/ui/messages/SheetController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/platform-impl/src/com/intellij/ui/messages/SheetController.java b/platform/platform-impl/src/com/intellij/ui/messages/SheetController.java index 7c3240ac26db..3511604c6631 100755 --- a/platform/platform-impl/src/com/intellij/ui/messages/SheetController.java +++ b/platform/platform-impl/src/com/intellij/ui/messages/SheetController.java @@ -367,7 +367,7 @@ public class SheetController { } private static String handleBreaks(final String message) { - return message.replaceAll("(\r\n|\n)", "
"); + return message == null ? "" : message.replaceAll("(\r\n|\n)", "
"); } private void shiftButtonsToTheBottom(int shiftDistance) { From 95398701ed835ce8944fefbf8736ec3788383d94 Mon Sep 17 00:00:00 2001 From: Andrey Vokin Date: Wed, 7 Oct 2015 13:36:44 +0300 Subject: [PATCH 18/22] Ruby. Injection in x command strings implemented (%x{injection}) --- .../codeInsight/CodeInsightUtilCore.java | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/platform/core-impl/src/com/intellij/codeInsight/CodeInsightUtilCore.java b/platform/core-impl/src/com/intellij/codeInsight/CodeInsightUtilCore.java index fef3b5703a43..2181e43f96f6 100644 --- a/platform/core-impl/src/com/intellij/codeInsight/CodeInsightUtilCore.java +++ b/platform/core-impl/src/com/intellij/codeInsight/CodeInsightUtilCore.java @@ -28,6 +28,9 @@ import com.intellij.util.ReflectionUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Arrays; +import java.util.List; + public abstract class CodeInsightUtilCore extends FileModificationService { public static T findElementInRange(@NotNull PsiFile file, int startOffset, @@ -78,7 +81,12 @@ public abstract class CodeInsightUtilCore extends FileModificationService { } public static boolean parseStringCharacters(@NotNull String chars, @NotNull StringBuilder outChars, @Nullable int[] sourceOffsets) { + return parseStringCharacters(chars, outChars, sourceOffsets, '"', '\''); + } + + public static boolean parseStringCharacters(@NotNull String chars, @NotNull StringBuilder outChars, @Nullable int[] sourceOffsets, Character... endChars) { assert sourceOffsets == null || sourceOffsets.length == chars.length()+1; + List endCharList = Arrays.asList(endChars); if (chars.indexOf('\\') < 0) { outChars.append(chars); if (sourceOffsets != null) { @@ -123,14 +131,6 @@ public abstract class CodeInsightUtilCore extends FileModificationService { outChars.append('\r'); break; - case'"': - outChars.append('"'); - break; - - case'\'': - outChars.append('\''); - break; - case'\\': outChars.append('\\'); break; @@ -193,7 +193,11 @@ public abstract class CodeInsightUtilCore extends FileModificationService { break; default: - return false; + if (endCharList.contains(c)) { + outChars.append(c); + } else { + return false; + } } if (sourceOffsets != null) { sourceOffsets[outChars.length()-outOffset] = index; From f40eedf08fc5ffa5288084210f40fd6cb6f846c7 Mon Sep 17 00:00:00 2001 From: Alexander Zolotov Date: Tue, 6 Oct 2015 17:26:51 +0300 Subject: [PATCH 19/22] Add Microsoft Edge icon --- platform/icons/src/xml/browsers/edge16.png | Bin 0 -> 668 bytes platform/icons/src/xml/browsers/edge16@2x.png | Bin 0 -> 1541 bytes .../util/src/com/intellij/icons/AllIcons.java | 1 + 3 files changed, 1 insertion(+) create mode 100644 platform/icons/src/xml/browsers/edge16.png create mode 100644 platform/icons/src/xml/browsers/edge16@2x.png diff --git a/platform/icons/src/xml/browsers/edge16.png b/platform/icons/src/xml/browsers/edge16.png new file mode 100644 index 0000000000000000000000000000000000000000..80ea90842c974d109665dd338f2774792794a379 GIT binary patch literal 668 zcmV;N0%QG&P)6uwmvT#BHeZroLi&AoH;z{f%bb?qZ!ML}G+5sJljW|F2gHW4XS+_-eDP(g5K zGBY>U)LOOp=)#4+pe~A7Tf}M|zcWdi>to@8!yL|>$9KLn<9U#5c6_bZl!R14AoW4Y$l?k-hpCmom(H*2jN^{xte1=U4CoRBd;5ZqN!^$ zj$DTT_bmU!1_RZeHbkxlnj|{1u+nAN-&{whhRzl8Q4^d$p{HY;(4*7^PD&mlc<9!{ zcyv6F>V#9SPbP#q1U2XraKT$ug|S zjRCfkLsNG3<>eyBe7gLOiq8eSy0wj~XxFU1I2m04p$mkB9t<(wS}lDj`eZ2ZVz;bs z{C&815fLT8^lb#bf_`bNQGLLA9|4&NuT(l)vA+O*(T?~^3lCEO0000_Z+3zEqQYXE&+s+MlQeEk48&#fOThvC^2`&F)^$IrrY3JCkgF6bBA7bAQe` z_xyb4+(e?$lNGBwJBQck-?qN|HMYy{w_U#1a`|VL$ESdbNtYEZFZem>@f^J0usrry zo6m1DIIZd&7HbkKc{+zf!InF^5twE`(6_dq6K&a(A~kqII6IFB5GX7^5O#W8qy~=D zoE+ymsqYHC>7NTl_)p59B1rIkz{yTrx~LMS zztQAQ<_l>~^05|1r>~+V?DUwhca96Ye@u{4G)L={B4`<2L>EbG1fMuv``0e4gb=HU z;7R(y6T}?+3JhkIVButr0;5yp3R}7Y=Gb|(49=0IrLhm#I$yN}-r*|7tdA3{M&@rq#uq>>zG zRS3>|BUUg?4@^GB2uIVQ5_W$czw>An$75G66-Y^!KW_Q_JPC7IT!|v_cG=M_-G{HJ z^U?|=eYOqFYJ=aM3|Zg|FnlDT=B?+08$iK5tzNE4Z^rzl{f0Shk3Vhq=d_zbcYs1T zhD3bhhp~PBcKhzHl^@vw1Rg!t>ahob@n5(m5;q3eG-Zb`)=cq~sa*vSN(zJ-0@=}) zB`X|^n69-axQ=`;PySrQtR8;`gA9YIwV&UDW1F)1^%&8bP^fyZCu{lq1+_;+GlQ2> z0|gwLgm}Br5W*f-LDFps2aTUZv}@n}4OoVMxLhQKz`UCf5@Vn ze7+Bay=taMs~fb#!r$>Ew8`b~q5r{$4$ZlvsVL=BE&U@~&`P$?dhp~bh>f(A)vj^}UGyNg*7igt2vC(ohD}Cgnqj z(mQ2FjMsq#5l^Dy5(H}$7DR=I|8tm*wimn-tAJt2#7U`TB#J9inV_s7*(5a{$buyO zUTZ*Q!UJ&of`zClo4*{vM* zdI?hWONTFr&zbq^@Fxz%qv$=b69brF$7S;rO$4QN2aBuEDyv{5p3Og#&p!Yp9% zET~9qXQu$LHkf;KEkLH4%<;I4(2Dcg@JLDBBXPj&0eO`g6_<_Ky}V^*?%lr@ddx>9 z4a=vsWl^|FGyeuPPg Date: Wed, 7 Oct 2015 14:10:09 +0300 Subject: [PATCH 20/22] removed references to non-existing test source root from Maven modules --- plugins/maven/maven3-server-common/maven3-server-common.iml | 1 - plugins/maven/maven30-server-impl/maven30-server-impl.iml | 1 - plugins/maven/maven32-server-impl/maven32-server-impl.iml | 1 - 3 files changed, 3 deletions(-) diff --git a/plugins/maven/maven3-server-common/maven3-server-common.iml b/plugins/maven/maven3-server-common/maven3-server-common.iml index c48f45961ae9..585533b9a91d 100644 --- a/plugins/maven/maven3-server-common/maven3-server-common.iml +++ b/plugins/maven/maven3-server-common/maven3-server-common.iml @@ -4,7 +4,6 @@ - diff --git a/plugins/maven/maven30-server-impl/maven30-server-impl.iml b/plugins/maven/maven30-server-impl/maven30-server-impl.iml index 953e3dc5d7c4..61fd91b52dfa 100644 --- a/plugins/maven/maven30-server-impl/maven30-server-impl.iml +++ b/plugins/maven/maven30-server-impl/maven30-server-impl.iml @@ -4,7 +4,6 @@ - diff --git a/plugins/maven/maven32-server-impl/maven32-server-impl.iml b/plugins/maven/maven32-server-impl/maven32-server-impl.iml index b002fe636334..7d11ef040a60 100644 --- a/plugins/maven/maven32-server-impl/maven32-server-impl.iml +++ b/plugins/maven/maven32-server-impl/maven32-server-impl.iml @@ -4,7 +4,6 @@ - From a864ef84a5e746ce783be5d4575ae5eba482bda7 Mon Sep 17 00:00:00 2001 From: Dennis Ushakov Date: Wed, 7 Oct 2015 13:08:31 +0300 Subject: [PATCH 21/22] remove doctype patcher to prevent default project initialization --- platform/platform-resources/src/META-INF/XmlPlugin.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/platform/platform-resources/src/META-INF/XmlPlugin.xml b/platform/platform-resources/src/META-INF/XmlPlugin.xml index c33f9ef75e3d..b7ce21f6e5cf 100644 --- a/platform/platform-resources/src/META-INF/XmlPlugin.xml +++ b/platform/platform-resources/src/META-INF/XmlPlugin.xml @@ -11,9 +11,9 @@ - - com.intellij.javaee.DefaultHtmlDoctypeInitialConfigurator - + + + com.intellij.codeInsight.editorActions.XmlTagNameSynchronizer From 1611901ef6b17ac70bd24b10ccc2058c4e167fe6 Mon Sep 17 00:00:00 2001 From: Dmitry Batrak Date: Wed, 7 Oct 2015 14:20:04 +0300 Subject: [PATCH 22/22] IDEA-145646 Weird undo behavior with 'introduce field' postfix template --- .../introduceField/IntroduceFieldDialog.java | 31 ++++++++++++++----- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/java/java-impl/src/com/intellij/refactoring/introduceField/IntroduceFieldDialog.java b/java/java-impl/src/com/intellij/refactoring/introduceField/IntroduceFieldDialog.java index 6f1cd002c9c5..e71e6c3317ae 100644 --- a/java/java-impl/src/com/intellij/refactoring/introduceField/IntroduceFieldDialog.java +++ b/java/java-impl/src/com/intellij/refactoring/introduceField/IntroduceFieldDialog.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * 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. @@ -16,6 +16,7 @@ package com.intellij.refactoring.introduceField; import com.intellij.codeInsight.completion.JavaCompletionUtil; +import com.intellij.openapi.editor.Editor; import com.intellij.openapi.help.HelpManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; @@ -32,6 +33,8 @@ import com.intellij.refactoring.ui.*; import com.intellij.refactoring.util.CommonRefactoringUtil; import com.intellij.refactoring.util.RefactoringMessageUtil; import com.intellij.util.ArrayUtil; +import com.intellij.util.ui.update.Activatable; +import com.intellij.util.ui.update.UiNotifyConnector; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -147,7 +150,7 @@ class IntroduceFieldDialog extends DialogWrapper { gbConstraints.gridx = 0; gbConstraints.gridy = 0; - JLabel type = new JLabel(getTypeLabel()); + final JLabel type = new JLabel(getTypeLabel()); panel.add(type, gbConstraints); @@ -163,7 +166,7 @@ class IntroduceFieldDialog extends DialogWrapper { gbConstraints.weighty = 1; gbConstraints.gridx = 0; gbConstraints.gridy = 1; - JLabel namePrompt = new JLabel(RefactoringBundle.message("name.prompt")); + final JLabel namePrompt = new JLabel(RefactoringBundle.message("name.prompt")); panel.add(namePrompt, gbConstraints); gbConstraints.insets = new Insets(4, 0, 4, 4); @@ -180,10 +183,24 @@ class IntroduceFieldDialog extends DialogWrapper { }); namePrompt.setLabelFor(myNameField.getFocusableComponent()); - myNameSuggestionsManager = new NameSuggestionsManager(myTypeSelector, myNameField, - createGenerator(myWillBeDeclaredStatic, myLocalVariable, myInitializerExpression, myIsInvokedOnDeclaration, myEnteredName, - myParentClass, myProject)); - myNameSuggestionsManager.setLabelsFor(type, namePrompt); + // We delay initialization of name field till dialog is shown, so that it will be executed in a different command and won't + // be tied to any document changes performed in current command (and won't prevent undo for them later) + new UiNotifyConnector.Once(panel, new Activatable.Adapter() { + @Override + public void showNotify() { + myNameSuggestionsManager = new NameSuggestionsManager(myTypeSelector, myNameField, + createGenerator(myWillBeDeclaredStatic, myLocalVariable, + myInitializerExpression, myIsInvokedOnDeclaration, + myEnteredName, myParentClass, myProject)); + myNameSuggestionsManager.setLabelsFor(type, namePrompt); + + Editor editor = myNameField.getEditor(); + if (editor != null) { + editor.getSelectionModel().setSelection(0, editor.getDocument().getTextLength()); + } + } + }); + return panel; }