From d51ce75a5ff80e2870f6e0f3abd103a8aac90f6c Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Thu, 10 Apr 2014 19:14:45 +0200 Subject: [PATCH 01/21] IDEA-51509 (IG: "String concatenation in i18n context" could suggest annotation quick fix for more variables) --- .../StringConcatenationInspectionBase.java | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/internationalization/StringConcatenationInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/internationalization/StringConcatenationInspectionBase.java index df3b8dcb9b87..5d6bc9059e23 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/internationalization/StringConcatenationInspectionBase.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/internationalization/StringConcatenationInspectionBase.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2013 Dave Griffith, Bas Leijdekkers + * Copyright 2003-2014 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. @@ -30,6 +30,7 @@ import com.siyeh.ig.DelegatingFix; import com.siyeh.ig.InspectionGadgetsFix; import com.siyeh.ig.psiutils.ExpressionUtils; import com.siyeh.ig.psiutils.MethodUtils; +import com.siyeh.ig.psiutils.ParenthesesUtils; import com.siyeh.ig.psiutils.TypeUtils; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -79,7 +80,7 @@ public class StringConcatenationInspectionBase extends BaseInspection { protected InspectionGadgetsFix[] buildFixes(Object... infos) { final PsiPolyadicExpression polyadicExpression = (PsiPolyadicExpression)infos[0]; final Collection result = new ArrayList(); - final PsiElement parent = polyadicExpression.getParent(); + final PsiElement parent = ParenthesesUtils.getParentSkipParentheses(polyadicExpression); if (parent instanceof PsiVariable) { final PsiVariable variable = (PsiVariable)parent; final InspectionGadgetsFix fix = createAddAnnotationFix(variable); @@ -98,6 +99,22 @@ public class StringConcatenationInspectionBase extends BaseInspection { } } } + else if (parent instanceof PsiExpressionList) { + final PsiElement grandParent = parent.getParent(); + if (grandParent instanceof PsiMethodCallExpression) { + final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)grandParent; + final PsiReferenceExpression methodExpression = methodCallExpression.getMethodExpression(); + final PsiExpression qualifierExpression = methodExpression.getQualifierExpression(); + if (qualifierExpression instanceof PsiReferenceExpression) { + final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)qualifierExpression; + final PsiElement target = referenceExpression.resolve(); + if (target instanceof PsiModifierListOwner) { + final PsiModifierListOwner modifierListOwner = (PsiModifierListOwner)target; + result.add(createAddAnnotationFix(modifierListOwner)); + } + } + } + } final PsiExpression[] operands = polyadicExpression.getOperands(); for (PsiExpression operand : operands) { final PsiModifierListOwner element1 = getAnnotatableElement(operand); From 7a23ecf64a7d147a78790b1fe392537c52ee6079 Mon Sep 17 00:00:00 2001 From: Michael Golubev Date: Thu, 10 Apr 2014 19:19:16 +0200 Subject: [PATCH 02/21] IDEA-123825 - Git Clouds: detect Git remotes --- .../impl/src/com/intellij/remoteServer/util/CloudNotifier.java | 3 ++- .../util/importProject/CloudGitChooseAccountStep.java | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/platform/remote-servers/impl/src/com/intellij/remoteServer/util/CloudNotifier.java b/platform/remote-servers/impl/src/com/intellij/remoteServer/util/CloudNotifier.java index 029e47c14311..39be9a8f54b5 100644 --- a/platform/remote-servers/impl/src/com/intellij/remoteServer/util/CloudNotifier.java +++ b/platform/remote-servers/impl/src/com/intellij/remoteServer/util/CloudNotifier.java @@ -36,9 +36,10 @@ public class CloudNotifier { showMessage(message, messageType, null); } - public void showMessage(String message, MessageType messageType, @Nullable NotificationListener listener) { + public Notification showMessage(String message, MessageType messageType, @Nullable NotificationListener listener) { NotificationGroup notificationGroup = NotificationGroup.balloonGroup(myNotificationDisplayId); Notification notification = notificationGroup.createNotification("", message, messageType.toNotificationType(), listener); notification.notify(null); + return notification; } } diff --git a/plugins/git4idea/remote-servers-git/src/com/intellij/remoteServer/util/importProject/CloudGitChooseAccountStep.java b/plugins/git4idea/remote-servers-git/src/com/intellij/remoteServer/util/importProject/CloudGitChooseAccountStep.java index 46179e224b7d..f6f5e39f3f0b 100644 --- a/plugins/git4idea/remote-servers-git/src/com/intellij/remoteServer/util/importProject/CloudGitChooseAccountStep.java +++ b/plugins/git4idea/remote-servers-git/src/com/intellij/remoteServer/util/importProject/CloudGitChooseAccountStep.java @@ -116,7 +116,7 @@ public class CloudGitChooseAccountStep Date: Thu, 10 Apr 2014 21:43:18 +0400 Subject: [PATCH 03/21] Storyboard format --- .../src/com/intellij/openapi/util/text/StringUtil.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/platform/util/src/com/intellij/openapi/util/text/StringUtil.java b/platform/util/src/com/intellij/openapi/util/text/StringUtil.java index 8ea9e5846940..01270d0e4eee 100644 --- a/platform/util/src/com/intellij/openapi/util/text/StringUtil.java +++ b/platform/util/src/com/intellij/openapi/util/text/StringUtil.java @@ -1699,6 +1699,13 @@ public class StringUtil extends StringUtilRt { return -1; } + @Nullable + public static String substringBefore(@NotNull String text, @NotNull String subString) { + int i = text.indexOf(subString); + if (i == -1) return null; + return text.substring(0, i); + } + @Nullable public static String substringAfter(@NotNull String text, @NotNull String subString) { int i = text.indexOf(subString); From d35ae288433b20856773c1067499d6fdd25f4849 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Thu, 10 Apr 2014 19:47:40 +0200 Subject: [PATCH 04/21] IDEA-122914 (unclosed ZipFile: JarFile usages) --- .../libraries/JarVersionDetectionUtil.java | 88 ++++++------------- .../openapi/projectRoots/JdkUtil.java | 60 ++++++------- .../documentation/AbstractExternalFilter.java | 25 +++--- .../ide/browsers/BrowserLauncherAppless.java | 77 +++++++++------- .../com/intellij/openapi/util/io/JarUtil.java | 79 +++++++++++++++++ .../lang/ant/config/impl/AntInstallation.java | 28 +----- .../appengine/sdk/impl/AppEngineSdkImpl.java | 9 +- .../testng/util/TestNGUtil.java | 47 ++++------ 8 files changed, 211 insertions(+), 202 deletions(-) diff --git a/java/openapi/src/com/intellij/openapi/roots/libraries/JarVersionDetectionUtil.java b/java/openapi/src/com/intellij/openapi/roots/libraries/JarVersionDetectionUtil.java index 2e5472ee3443..903ad9168dd5 100644 --- a/java/openapi/src/com/intellij/openapi/roots/libraries/JarVersionDetectionUtil.java +++ b/java/openapi/src/com/intellij/openapi/roots/libraries/JarVersionDetectionUtil.java @@ -13,14 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.intellij.openapi.roots.libraries; -import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.module.Module; +import com.intellij.openapi.projectRoots.JdkUtil; import com.intellij.openapi.roots.LibraryOrderEntry; import com.intellij.openapi.roots.ModuleRootManager; import com.intellij.openapi.roots.OrderEntry; +import com.intellij.openapi.util.io.JarUtil; import com.intellij.openapi.vfs.JarFileSystem; import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.NotNull; @@ -35,47 +35,32 @@ import java.util.jar.JarFile; import java.util.jar.Manifest; public class JarVersionDetectionUtil { - private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.roots.libraries.JarVersionDetectionUtil"); - - private JarVersionDetectionUtil() { - } + private JarVersionDetectionUtil() { } @Nullable - public static String detectJarVersion(@NotNull final String detectionClass, @NotNull Module module) { - try { - return detectJarVersion(getDetectionJar(detectionClass, module)); - } - catch (IOException e) { - return null; - } - } - - @Nullable - public static String detectJarVersion(@NotNull String detectionClass, @NotNull List files) { - final VirtualFile jar = LibrariesHelper.getInstance().findRootByClass(files, detectionClass); - if (jar != null && jar.getFileSystem() instanceof JarFileSystem) { - final VirtualFile manifestFile = jar.findFileByRelativePath(JarFile.MANIFEST_NAME); - if (manifestFile != null) { - try { - final InputStream input = manifestFile.getInputStream(); - try { - return new Manifest(input).getMainAttributes().getValue(Attributes.Name.IMPLEMENTATION_VERSION); - } - finally { - input.close(); - } - } - catch (IOException e) { - LOG.debug(e); - return null; + public static String detectJarVersion(@NotNull String detectionClass, @NotNull Module module) { + for (OrderEntry library : ModuleRootManager.getInstance(module).getOrderEntries()) { + if (library instanceof LibraryOrderEntry) { + VirtualFile jar = LibrariesHelper.getInstance().findJarByClass(((LibraryOrderEntry)library).getLibrary(), detectionClass); + if (jar != null && jar.getFileSystem() instanceof JarFileSystem) { + return JdkUtil.getJarMainAttribute(jar, Attributes.Name.IMPLEMENTATION_VERSION); } } } + return null; } @Nullable - public static String detectJarVersion(com.intellij.openapi.vfs.JarFile zipFile) { + public static String detectJarVersion(@NotNull String detectionClass, @NotNull List files) { + VirtualFile jarRoot = LibrariesHelper.getInstance().findRootByClass(files, detectionClass); + return jarRoot != null && jarRoot.getFileSystem() instanceof JarFileSystem ? + JdkUtil.getJarMainAttribute(jarRoot, Attributes.Name.IMPLEMENTATION_VERSION) : null; + } + + /** @deprecated use {@link JarUtil#getJarAttribute(File, Attributes.Name)} (to remove in IDEA 15) */ + @SuppressWarnings("UnusedDeclaration") + public static String detectJarVersion(@Nullable com.intellij.openapi.vfs.JarFile zipFile) { if (zipFile == null) { return null; } @@ -94,40 +79,17 @@ public class JarVersionDetectionUtil { } } - @Nullable - private static com.intellij.openapi.vfs.JarFile getDetectionJar(final String detectionClass, Module module) throws IOException { - for (OrderEntry library : ModuleRootManager.getInstance(module).getOrderEntries()) { - if (library instanceof LibraryOrderEntry) { - VirtualFile file = LibrariesHelper.getInstance().findJarByClass(((LibraryOrderEntry)library).getLibrary(), detectionClass); - if (file != null && file.getFileSystem() instanceof JarFileSystem) { - return JarFileSystem.getInstance().getJarFile(file); - } - } - } - return null; - } - - private static String getJarAttribute(@NotNull File jar, @NotNull String attributeName, @Nullable String entryName) throws IOException { - JarFile runJar = new JarFile(jar); - try { - Attributes attributes = entryName == null ? runJar.getManifest().getMainAttributes() : runJar.getManifest().getAttributes(entryName); - return attributes.getValue(attributeName); - } - finally { - runJar.close(); - } - } - public static String getBundleVersion(@NotNull File jar) throws IOException { - return getJarAttribute(jar, "Bundle-Version", null); + return JarUtil.getJarAttribute(jar, new Attributes.Name("Bundle-Version")); } public static String getImplementationVersion(@NotNull File jar) throws IOException { - return getJarAttributeVersion(jar, Attributes.Name.IMPLEMENTATION_VERSION, null); + return JarUtil.getJarAttribute(jar, Attributes.Name.IMPLEMENTATION_VERSION); } - public static String getJarAttributeVersion(@NotNull File jar, @NotNull Attributes.Name attributeName, @Nullable String entryName) throws IOException { - return getJarAttribute(jar, attributeName.toString(), entryName); + /** @deprecated use {@link JarUtil#getJarAttribute(File, Attributes.Name)} (to remove in IDEA 15) */ + @SuppressWarnings("UnusedDeclaration") + public static String getJarAttributeVersion(@NotNull File jar, @NotNull Attributes.Name attribute, @Nullable String entryName) { + return entryName != null ? JarUtil.getJarAttribute(jar, entryName, attribute) : JarUtil.getJarAttribute(jar, attribute); } } - diff --git a/platform/lang-api/src/com/intellij/openapi/projectRoots/JdkUtil.java b/platform/lang-api/src/com/intellij/openapi/projectRoots/JdkUtil.java index 6eaa636943be..f75fbc679afb 100644 --- a/platform/lang-api/src/com/intellij/openapi/projectRoots/JdkUtil.java +++ b/platform/lang-api/src/com/intellij/openapi/projectRoots/JdkUtil.java @@ -50,18 +50,16 @@ public class JdkUtil { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.projectRoots.JdkUtil"); private static final String WRAPPER_CLASS = "com.intellij.rt.execution.CommandLineWrapper"; - private JdkUtil() { - } + private JdkUtil() { } /** * @return the specified attribute of the JDK (examines rt.jar) or null if cannot determine the value */ @Nullable - public static String getJdkMainAttribute(@NotNull Sdk jdk, Attributes.Name attributeName) { - final VirtualFile homeDirectory = jdk.getHomeDirectory(); - if (homeDirectory == null) { - return null; - } + public static String getJdkMainAttribute(@NotNull Sdk jdk, Attributes.Name attribute) { + VirtualFile homeDirectory = jdk.getHomeDirectory(); + if (homeDirectory == null) return null; + VirtualFile rtJar = homeDirectory.findFileByRelativePath("jre/lib/rt.jar"); if (rtJar == null) { rtJar = homeDirectory.findFileByRelativePath("lib/rt.jar"); @@ -72,6 +70,7 @@ public class JdkUtil { if (rtJar == null) { rtJar = homeDirectory.findFileByRelativePath("../Classes/classes.jar"); // for mac } + if (rtJar == null) { String versionString = jdk.getVersionString(); if (versionString != null) { @@ -81,34 +80,29 @@ public class JdkUtil { } return versionString; } - VirtualFile rtJarFileContent = JarFileSystem.getInstance().findFileByPath(rtJar.getPath() + JarFileSystem.JAR_SEPARATOR); - if (rtJarFileContent == null) { - return null; - } - com.intellij.openapi.vfs.JarFile manifestJarFile; - try { - manifestJarFile = JarFileSystem.getInstance().getJarFile(rtJarFileContent); - } - catch (IOException e) { - return null; - } - if (manifestJarFile == null) { - return null; - } - try { - com.intellij.openapi.vfs.JarFile.JarEntry entry = manifestJarFile.getEntry(JarFile.MANIFEST_NAME); - if (entry == null) { - return null; + + VirtualFile jarRoot = JarFileSystem.getInstance().getJarRootForLocalFile(rtJar); + return jarRoot != null ? getJarMainAttribute(jarRoot, attribute) : null; + } + + @Nullable + public static String getJarMainAttribute(@NotNull VirtualFile jarRoot, Attributes.Name attribute) { + VirtualFile manifestFile = jarRoot.findFileByRelativePath(JarFile.MANIFEST_NAME); + if (manifestFile != null) { + try { + InputStream stream = manifestFile.getInputStream(); + try { + return new Manifest(stream).getMainAttributes().getValue(attribute); + } + finally { + stream.close(); + } + } + catch (IOException e) { + LOG.debug(e); } - InputStream is = manifestJarFile.getInputStream(entry); - Manifest manifest = new Manifest(is); - is.close(); - Attributes attributes = manifest.getMainAttributes(); - return attributes.getValue(attributeName); - } - catch (IOException e) { - // nothing } + return null; } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/documentation/AbstractExternalFilter.java b/platform/lang-impl/src/com/intellij/codeInsight/documentation/AbstractExternalFilter.java index 42f34f09f903..d8d9b7cbbe50 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/documentation/AbstractExternalFilter.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/documentation/AbstractExternalFilter.java @@ -31,7 +31,6 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.*; import com.intellij.psi.PsiElement; import com.intellij.util.SystemProperties; -import com.intellij.util.io.URLUtil; import com.intellij.util.io.UrlConnectionUtil; import com.intellij.util.net.HttpConfigurable; import org.jetbrains.annotations.NonNls; @@ -42,17 +41,15 @@ import java.io.*; import java.net.URL; import java.net.URLConnection; import java.util.concurrent.Future; +import java.util.jar.JarFile; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.zip.ZipEntry; /** - * Created by IntelliJ IDEA. - * User: db - * Date: May 2, 2003 - * Time: 8:35:34 PM - * To change this template use Options | File Templates. + * @author db + * @since May 2, 2003 */ - public abstract class AbstractExternalFilter { private static final boolean EXTRACT_IMAGES_FROM_JARS = SystemProperties.getBooleanProperty("extract.doc.images", true); @@ -156,11 +153,10 @@ public abstract class AbstractExternalFilter { boolean referenceUnpackedImage = true; if (!unpackedImage.isFile()) { referenceUnpackedImage = false; - JarFileSystem jarFileSystem = JarFileSystem.getInstance(); try { - JarFile jarFile = jarFileSystem.getJarFile(jarFileSystem.findFileByPath(jarPath + URLUtil.JAR_SEPARATOR)); - if (jarFile != null) { - JarFile.JarEntry entry = jarFile.getEntry(imgPath); + JarFile jarFile = new JarFile(jarPath); + try { + ZipEntry entry = jarFile.getEntry(imgPath); if (entry != null) { FileUtilRt.createIfNotExists(unpackedImage); FileOutputStream fOut = new FileOutputStream(unpackedImage); @@ -176,9 +172,12 @@ public abstract class AbstractExternalFilter { } unpackedImage.deleteOnExit(); } + finally { + jarFile.close(); + } } catch (IOException e) { - // Do nothing + LOG.debug(e); } } if (referenceUnpackedImage) { @@ -385,7 +384,7 @@ public abstract class AbstractExternalFilter { if (useDt) { boolean skip = false; - + do { if (StringUtil.toUpperCase(read).contains(H2) && !read.toUpperCase().contains("H2")) { // read=class name in

data.append(H2); diff --git a/platform/platform-api/src/com/intellij/ide/browsers/BrowserLauncherAppless.java b/platform/platform-api/src/com/intellij/ide/browsers/BrowserLauncherAppless.java index a5f4bafab614..309ac01ac03b 100644 --- a/platform/platform-api/src/com/intellij/ide/browsers/BrowserLauncherAppless.java +++ b/platform/platform-api/src/com/intellij/ide/browsers/BrowserLauncherAppless.java @@ -31,6 +31,7 @@ import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.io.FileUtil; @@ -40,6 +41,8 @@ import com.intellij.openapi.vfs.*; import com.intellij.ui.GuiUtils; import com.intellij.util.ArrayUtil; import com.intellij.util.PathUtil; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.io.URLUtil; import com.intellij.util.io.ZipUtil; import com.intellij.util.ui.OptionsDialog; import org.jetbrains.annotations.Contract; @@ -54,8 +57,10 @@ import java.io.IOException; import java.io.InputStream; import java.lang.reflect.InvocationTargetException; import java.net.URI; -import java.util.*; +import java.util.Arrays; +import java.util.Collections; import java.util.List; +import java.util.Set; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; @@ -186,21 +191,17 @@ public class BrowserLauncherAppless extends BrowserLauncher { url = url.substring(0, sharpPos); } - VirtualFile file = VirtualFileManager.getInstance().findFileByUrl(url); - if (file == null || !(file.getFileSystem() instanceof JarFileSystem)) return null; + Pair pair = URLUtil.splitJarUrl(url); + if (pair == null) return null; - JarFileSystem jarFileSystem = (JarFileSystem)file.getFileSystem(); - VirtualFile jarVirtualFile = jarFileSystem.getVirtualFileForJar(file); - if (jarVirtualFile == null) return null; + File jarFile = new File(FileUtil.toSystemDependentName(pair.first)); + if (!jarFile.canRead()) return null; - String targetFilePath = file.getPath(); - String targetFileRelativePath = StringUtil.substringAfter(targetFilePath, JarFileSystem.JAR_SEPARATOR); - LOG.assertTrue(targetFileRelativePath != null); + String jarUrl = StandardFileSystems.FILE_PROTOCOL_PREFIX + FileUtil.toSystemIndependentName(jarFile.getPath()); + String jarLocationHash = jarFile.getName() + "." + Integer.toHexString(jarUrl.hashCode()); + final File outputDir = new File(getExtractedFilesDir(), jarLocationHash); - String jarVirtualFileLocationHash = jarVirtualFile.getName() + Integer.toHexString(jarVirtualFile.getUrl().hashCode()); - final File outputDir = new File(getExtractedFilesDir(), jarVirtualFileLocationHash); - - final String currentTimestamp = String.valueOf(new File(jarVirtualFile.getPath()).lastModified()); + final String currentTimestamp = String.valueOf(new File(jarFile.getPath()).lastModified()); final File timestampFile = new File(outputDir, ".idea.timestamp"); String previousTimestamp = null; @@ -239,21 +240,21 @@ public class BrowserLauncherAppless extends BrowserLauncher { return null; } - @SuppressWarnings("ConstantConditions") - final ZipFile zipFile = jarFileSystem.getJarFile(file).getZipFile(); - if (zipFile == null) { - return null; - } - ZipEntry entry = zipFile.getEntry(targetFileRelativePath); - if (entry == null) { - return null; - } - InputStream is = zipFile.getInputStream(entry); + boolean closeZip = true; + final ZipFile zipFile = new ZipFile(jarFile); try { + ZipEntry entry = zipFile.getEntry(pair.second); + if (entry == null) { + return null; + } + InputStream is = zipFile.getInputStream(entry); ZipUtil.extractEntry(entry, is, outputDir); + closeZip = false; } finally { - is.close(); + if (closeZip) { + zipFile.close(); + } } ApplicationManager.getApplication().invokeLater(new Runnable() { @@ -266,8 +267,7 @@ public class BrowserLauncherAppless extends BrowserLauncher { final int[] counter = new int[]{0}; class MyFilter implements FilenameFilter { - private final Set myImportantDirs = new HashSet( - Arrays.asList(outputDir, new File(outputDir, "resources"))); + private final Set myImportantDirs = ContainerUtil.newHashSet(outputDir, new File(outputDir, "resources")); private final boolean myImportantOnly; private MyFilter(boolean importantOnly) { @@ -287,19 +287,23 @@ public class BrowserLauncherAppless extends BrowserLauncher { } try { - ZipUtil.extract(zipFile, outputDir, new MyFilter(true)); - ZipUtil.extract(zipFile, outputDir, new MyFilter(false)); - FileUtil.writeToFile(timestampFile, currentTimestamp.getBytes()); - } - catch (IOException ignore) { + try { + ZipUtil.extract(zipFile, outputDir, new MyFilter(true)); + ZipUtil.extract(zipFile, outputDir, new MyFilter(false)); + FileUtil.writeToFile(timestampFile, currentTimestamp); + } + finally { + zipFile.close(); + } } + catch (IOException ignore) { } } }.queue(); } }); } - return VfsUtilCore.pathToUrl(FileUtil.toSystemIndependentName(new File(outputDir, targetFileRelativePath).getPath())) + anchor; + return VfsUtilCore.pathToUrl(FileUtil.toSystemIndependentName(new File(outputDir, pair.second).getPath())) + anchor; } catch (IOException e) { LOG.warn(e); @@ -421,6 +425,15 @@ public class BrowserLauncherAppless extends BrowserLauncher { @Nullable final Project project, String[] additionalParameters) { GeneralCommandLine commandLine = new GeneralCommandLine(command); + + if (url != null && url.startsWith("jar:")) { + String files = extractFiles(url); + if (files == null) { + return false; + } + url = files; + } + if (url != null) { commandLine.addParameter(url); } diff --git a/platform/util/src/com/intellij/openapi/util/io/JarUtil.java b/platform/util/src/com/intellij/openapi/util/io/JarUtil.java index 2af33337d552..a119e00707bb 100644 --- a/platform/util/src/com/intellij/openapi/util/io/JarUtil.java +++ b/platform/util/src/com/intellij/openapi/util/io/JarUtil.java @@ -15,13 +15,22 @@ */ package com.intellij.openapi.util.io; +import com.intellij.openapi.diagnostic.Logger; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.io.File; import java.io.IOException; +import java.util.Properties; +import java.util.jar.Attributes; import java.util.jar.JarFile; +import java.util.jar.Manifest; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; public class JarUtil { + private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.util.io.JarUtil"); + /** * Returns true if the given .jar file exists and contains the given class. */ @@ -56,4 +65,74 @@ public class JarUtil { return false; } + + /** + * Returns attribute value from a manifest main section, + * or null if missing or a file does not contain a manifest. + */ + @Nullable + public static String getJarAttribute(@NotNull File file, @NotNull Attributes.Name attribute) { + return getJarAttributeImpl(file, null, attribute); + } + + /** + * Returns attribute value from a given manifest section, + * or null if missing or a file does not contain a manifest. + */ + @Nullable + public static String getJarAttribute(@NotNull File file, @NotNull String entryName, @NotNull Attributes.Name attribute) { + return getJarAttributeImpl(file, entryName, attribute); + } + + private static String getJarAttributeImpl(@NotNull File file, @Nullable String entryName, @NotNull Attributes.Name attribute) { + if (file.canRead()) { + try { + JarFile jarFile = new JarFile(file); + try { + Manifest manifest = jarFile.getManifest(); + if (manifest != null) { + Attributes attributes = entryName != null ? manifest.getAttributes(entryName) : manifest.getMainAttributes(); + return attributes.getValue(attribute); + } + } + finally { + jarFile.close(); + } + } + catch (IOException e) { + LOG.debug(e); + } + } + + return null; + } + + /** + * Loads archive entry as Java properties. + * Returns loaded instance, or null if requested entry is missed or invalid. + */ + @Nullable + public static Properties loadProperties(@NotNull File file, @NotNull String entryName) { + if (file.canRead()) { + try { + ZipFile zipFile = new ZipFile(file); + try { + ZipEntry entry = zipFile.getEntry(entryName); + if (entry != null) { + Properties properties = new Properties(); + properties.load(zipFile.getInputStream(entry)); + return properties; + } + } + finally { + zipFile.close(); + } + } + catch (IOException e) { + LOG.debug(e); + } + } + + return null; + } } diff --git a/plugins/ant/src/com/intellij/lang/ant/config/impl/AntInstallation.java b/plugins/ant/src/com/intellij/lang/ant/config/impl/AntInstallation.java index f07283b52347..12c8d141b79e 100644 --- a/plugins/ant/src/com/intellij/lang/ant/config/impl/AntInstallation.java +++ b/plugins/ant/src/com/intellij/lang/ant/config/impl/AntInstallation.java @@ -19,16 +19,14 @@ import com.intellij.lang.ant.AntBundle; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.InvalidDataException; import com.intellij.openapi.util.WriteExternalException; +import com.intellij.openapi.util.io.JarUtil; import com.intellij.util.config.*; import com.intellij.util.containers.Convertor; -import com.intellij.util.lang.UrlClassLoader; import org.jdom.Element; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import java.io.File; -import java.io.IOException; -import java.io.InputStream; import java.net.MalformedURLException; import java.util.Comparator; import java.util.Properties; @@ -162,28 +160,10 @@ public class AntInstallation { } private static Properties loadProperties(File antJar) throws MalformedURLException, ConfigurationException { - Properties properties = new Properties(); - InputStream stream = null; - try { - stream = UrlClassLoader.build().urls(antJar.toURI().toURL()).allowUnescaped().noPreload().get().getResourceAsStream(VERSION_RESOURCE); - properties.load(stream); - } - catch (MalformedURLException e) { - throw e; - } - catch (IOException e) { + Properties properties = JarUtil.loadProperties(antJar, VERSION_RESOURCE); + if (properties == null) { throw new ConfigurationException(AntBundle.message("cant.read.from.ant.jar.error.message", antJar.getAbsolutePath())); } - finally { - if (stream != null) { - try { - stream.close(); - } - catch (IOException e) { - LOG.error(e); - } - } - } return properties; } @@ -204,7 +184,7 @@ public class AntInstallation { private static void registerProperties(ExternalizablePropertyContainer container) { container.registerProperty((StringProperty)NAME); container.registerProperty(HOME_DIR); - container.registerProperty(CLASS_PATH, "classpathItem", SinglePathEntry.EXTERNALIZER); + container.registerProperty(CLASS_PATH, "classpathItem", AntClasspathEntry.EXTERNALIZER); container.registerProperty((StringProperty)VERSION); } } diff --git a/plugins/google-app-engine/source/com/intellij/appengine/sdk/impl/AppEngineSdkImpl.java b/plugins/google-app-engine/source/com/intellij/appengine/sdk/impl/AppEngineSdkImpl.java index c8aeced42b29..76bdc96b1cd1 100644 --- a/plugins/google-app-engine/source/com/intellij/appengine/sdk/impl/AppEngineSdkImpl.java +++ b/plugins/google-app-engine/source/com/intellij/appengine/sdk/impl/AppEngineSdkImpl.java @@ -19,9 +19,9 @@ import com.intellij.appengine.sdk.AppEngineSdk; import com.intellij.appengine.util.AppEngineUtil; import com.intellij.execution.configurations.ParametersList; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.roots.libraries.JarVersionDetectionUtil; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.io.JarUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VfsUtilCore; @@ -140,12 +140,7 @@ public class AppEngineSdkImpl implements AppEngineSdk { @Override @Nullable public String getVersion() { - try { - return JarVersionDetectionUtil.getJarAttributeVersion(getToolsApiJarFile(), Attributes.Name.SPECIFICATION_VERSION, "com/google/appengine/tools/info/"); - } - catch (IOException e) { - return null; - } + return JarUtil.getJarAttribute(getToolsApiJarFile(), "com/google/appengine/tools/info/", Attributes.Name.SPECIFICATION_VERSION); } private File getCachedWhiteListFile() { diff --git a/plugins/testng/src/com/theoryinpractice/testng/util/TestNGUtil.java b/plugins/testng/src/com/theoryinpractice/testng/util/TestNGUtil.java index 2f7791165397..c707a5e86e4e 100644 --- a/plugins/testng/src/com/theoryinpractice/testng/util/TestNGUtil.java +++ b/plugins/testng/src/com/theoryinpractice/testng/util/TestNGUtil.java @@ -24,10 +24,12 @@ import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ModuleRootModificationUtil; -import com.intellij.openapi.roots.libraries.JarVersionDetectionUtil; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.Comparing; -import com.intellij.openapi.vfs.*; +import com.intellij.openapi.util.io.JarUtil; +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.psi.javadoc.PsiDocComment; import com.intellij.psi.javadoc.PsiDocTag; @@ -48,47 +50,31 @@ import org.testng.TestNG; import org.testng.annotations.*; import java.io.File; -import java.io.IOException; import java.util.*; +import java.util.jar.Attributes; import java.util.regex.Matcher; import java.util.regex.Pattern; /** - * @author Hani Suleiman Date: Jul 20, 2005 Time: 1:37:36 PM + * @author Hani Suleiman + * @since Jul 20, 2005 */ -public class TestNGUtil -{ +public class TestNGUtil { private static final Logger LOGGER = Logger.getInstance("TestNG Runner"); + public static final String TESTNG_GROUP_NAME = "TestNG"; public static boolean hasDocTagsSupport = hasDocTagsSupport(); private static boolean hasDocTagsSupport() { - final String testngJarPath = PathUtil.getJarPathForClass(Test.class); - if (testngJarPath != null) { - final VirtualFile testngjar = LocalFileSystem.getInstance().findFileByPath(testngJarPath); - if (testngjar != null ) { - try { - final VirtualFile jarRoot = JarFileSystem.getInstance().getJarRootForLocalFile(testngjar); - if (jarRoot != null) { - final JarFile zipFile = JarFileSystem.getInstance().getJarFile(jarRoot); - final String version = JarVersionDetectionUtil.detectJarVersion(zipFile); - if (version != null && version.compareTo("5.12") > 0) { - return false; - } - } - } - catch (IOException e) { - return true; - } - } - } - return true; + String testngJarPath = PathUtil.getJarPathForClass(Test.class); + String version = JarUtil.getJarAttribute(new File(testngJarPath), Attributes.Name.IMPLEMENTATION_VERSION); + return version == null || StringUtil.compareVersionNumbers(version, "5.12") <= 0; } public static final String TEST_ANNOTATION_FQN = Test.class.getName(); public static final String FACTORY_ANNOTATION_FQN = Factory.class.getName(); - public static final String[] CONFIG_ANNOTATIONS_FQN = { + @SuppressWarnings("deprecation") public static final String[] CONFIG_ANNOTATIONS_FQN = { Configuration.class.getName(), Factory.class.getName(), ObjectFactory.class.getName(), @@ -119,9 +105,10 @@ public class TestNGUtil "testng.after-suite", "testng.after-test" }; - static final List junitAnnotions = + + private static final List JUNIT_ANNOTATIONS = Arrays.asList("org.junit.Test", "org.junit.Before", "org.junit.BeforeClass", "org.junit.After", "org.junit.AfterClass"); - private static final Logger LOG = Logger.getInstance("#" + TestNGUtil.class.getName()); + @NonNls private static final String SUITE_TAG_NAME = "suite"; @@ -501,7 +488,7 @@ public class TestNGUtil } public static boolean containsJunitAnnotions(PsiMethod method) { - return method != null && AnnotationUtil.isAnnotated(method, junitAnnotions); + return method != null && AnnotationUtil.isAnnotated(method, JUNIT_ANNOTATIONS); } public static boolean inheritsJUnitTestCase(PsiClass psiClass) { From f6154f9eeb828d0916bb837af4cc387f98ba3933 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Thu, 10 Apr 2014 19:50:01 +0200 Subject: [PATCH 05/21] Cleanup (test) --- .../builder/AbstractModelBuilderTest.java | 40 ++++++++----------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/plugins/gradle/tooling-extension/testSources/org/jetbrains/plugins/gradle/tooling/builder/AbstractModelBuilderTest.java b/plugins/gradle/tooling-extension/testSources/org/jetbrains/plugins/gradle/tooling/builder/AbstractModelBuilderTest.java index f3fb09de4eb7..fed32c092caf 100644 --- a/plugins/gradle/tooling-extension/testSources/org/jetbrains/plugins/gradle/tooling/builder/AbstractModelBuilderTest.java +++ b/plugins/gradle/tooling-extension/testSources/org/jetbrains/plugins/gradle/tooling/builder/AbstractModelBuilderTest.java @@ -32,6 +32,7 @@ import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import java.io.File; +import java.io.IOException; import java.util.Arrays; import java.util.Collection; import java.util.Set; @@ -53,11 +54,7 @@ public abstract class AbstractModelBuilderTest { public static final String GRADLE_v1_11 = "1.11"; public static final String GRADLE_v1_12 = "1.12-20140327133732+0000"; - public static final Pattern TEST_METHOD_NAME_PATTERN; - - static { - TEST_METHOD_NAME_PATTERN = Pattern.compile("(.*)\\[(\\d*)\\]"); - } + public static final Pattern TEST_METHOD_NAME_PATTERN = Pattern.compile("(.*)\\[(\\d*)\\]"); private static File ourTempDir; @@ -74,11 +71,12 @@ public abstract class AbstractModelBuilderTest { @Parameterized.Parameters public static Collection data() { - Object[][] data = new Object[][]{ + Object[][] data = { {AbstractModelBuilderTest.GRADLE_v1_9}, {AbstractModelBuilderTest.GRADLE_v1_10}, {AbstractModelBuilderTest.GRADLE_v1_11}, - {AbstractModelBuilderTest.GRADLE_v1_12}}; + {AbstractModelBuilderTest.GRADLE_v1_12} + }; return Arrays.asList(data); } @@ -94,29 +92,23 @@ public abstract class AbstractModelBuilderTest { } testDir = new File(ourTempDir, methodName); - testDir.mkdirs(); + FileUtil.ensureExists(testDir); FileUtil.writeToFile( new File(testDir, GradleConstants.DEFAULT_SCRIPT_NAME), - FileUtil.loadTextAndClose(getClass().getResourceAsStream( - String.format("/%s/%s", methodName, GradleConstants.DEFAULT_SCRIPT_NAME)) - ) + FileUtil.loadTextAndClose(getClass().getResourceAsStream("/" + methodName + "/" + GradleConstants.DEFAULT_SCRIPT_NAME)) ); FileUtil.writeToFile( new File(testDir, GradleConstants.SETTINGS_FILE_NAME), - FileUtil.loadTextAndClose(getClass().getResourceAsStream( - String.format("/%s/%s", methodName, GradleConstants.SETTINGS_FILE_NAME)) - ) + FileUtil.loadTextAndClose(getClass().getResourceAsStream("/" + methodName + "/" + GradleConstants.SETTINGS_FILE_NAME)) ); GradleConnector connector = GradleConnector.newConnector(); - - DefaultGradleConnector gradleConnector = (DefaultGradleConnector)connector; - gradleConnector.useGradleVersion(gradleVersion); - gradleConnector.forProjectDirectory(testDir); - gradleConnector.daemonMaxIdleTime(1, TimeUnit.SECONDS); - ProjectConnection connection = gradleConnector.connect(); + connector.useGradleVersion(gradleVersion); + connector.forProjectDirectory(testDir); + ((DefaultGradleConnector)connector).daemonMaxIdleTime(1, TimeUnit.SECONDS); + ProjectConnection connection = connector.connect(); final ProjectImportAction projectImportAction = new ProjectImportAction(false); projectImportAction.addExtraProjectModelClasses(getModels()); @@ -130,16 +122,18 @@ public abstract class AbstractModelBuilderTest { @After public void tearDown() throws Exception { - FileUtil.delete(testDir); + if (testDir != null) { + FileUtil.delete(testDir); + } } protected abstract Set getModels(); - private static void ensureTempDirCreated() { + private static void ensureTempDirCreated() throws IOException { if (ourTempDir != null) return; ourTempDir = new File(FileUtil.getTempDirectory(), "gradleTests"); FileUtil.delete(ourTempDir); - ourTempDir.mkdirs(); + FileUtil.ensureExists(ourTempDir); } } From c5de91af6034fa3d6b371153ae0e9ec090b08a55 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Thu, 10 Apr 2014 20:03:55 +0200 Subject: [PATCH 06/21] tests: common TeamCity detector --- .../src/com/intellij/testFramework/UsefulTestCase.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/platform/testFramework/src/com/intellij/testFramework/UsefulTestCase.java b/platform/testFramework/src/com/intellij/testFramework/UsefulTestCase.java index 40ecf18868c3..3a040f55e1b3 100644 --- a/platform/testFramework/src/com/intellij/testFramework/UsefulTestCase.java +++ b/platform/testFramework/src/com/intellij/testFramework/UsefulTestCase.java @@ -75,6 +75,8 @@ import java.util.regex.Pattern; */ @SuppressWarnings("UseOfSystemOutOrSystemErr") public abstract class UsefulTestCase extends TestCase { + public static final boolean IS_UNDER_TEAMCITY = System.getenv("TEAMCITY_VERSION") != null; + public static final String IDEA_MARKER_CLASS = "com.intellij.openapi.components.impl.stores.IdeaProjectStoreImpl"; public static final String TEMP_DIR_MARKER = "unitTest_"; @@ -643,7 +645,7 @@ public abstract class UsefulTestCase extends TestCase { String actualText = StringUtil.convertLineSeparators(actual.trim()); Assert.assertEquals(expectedText, actualText); } - + public static void assertExists(File file){ assertTrue("File should exists " + file, file.exists()); } From 0b4a7dfb2be42ae8bf3271b97444a447183be53b Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Thu, 10 Apr 2014 16:25:12 +0200 Subject: [PATCH 07/21] setup javadoc url for javafx for java 7 as it was distributed without sources (IDEA-123290) --- .../com/intellij/openapi/projectRoots/impl/JavaSdkImpl.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/java/java-impl/src/com/intellij/openapi/projectRoots/impl/JavaSdkImpl.java b/java/java-impl/src/com/intellij/openapi/projectRoots/impl/JavaSdkImpl.java index e334754923da..2aa495c42694 100644 --- a/java/java-impl/src/com/intellij/openapi/projectRoots/impl/JavaSdkImpl.java +++ b/java/java-impl/src/com/intellij/openapi/projectRoots/impl/JavaSdkImpl.java @@ -366,6 +366,10 @@ public class JavaSdkImpl extends JavaSdk { sdkModificator.addRoot(VirtualFileManager.getInstance().findFileByUrl(url), JavadocOrderRootType.getInstance()); } } + } else { + if (getVersion(sdk) == JavaSdkVersion.JDK_1_7) { + sdkModificator.addRoot(VirtualFileManager.getInstance().findFileByUrl("http://docs.oracle.com/javafx/2/api/"), JavadocOrderRootType.getInstance()); + } } attachJdkAnnotations(sdkModificator); sdkModificator.commitChanges(); From b8249580db57fcd3d4c1c45da3bfc0fe1e3f9ac0 Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Thu, 10 Apr 2014 18:10:10 +0200 Subject: [PATCH 08/21] infer nullity: preview with ability to exclude some usages(IDEA-123110), remember "annotate local" checkbox --- .../inferNullity/AnnotateTask.java | 11 +- .../InferNullityAnnotationsAction.java | 130 ++++++++++++++++-- .../inferNullity/NullityInferrer.java | 85 +++++++----- .../refactoring/BaseRefactoringProcessor.java | 53 +------ .../com/intellij/usageView/UsageViewUtil.java | 16 +++ .../usages/UsageInfoSearcherAdapter.java | 65 +++++++++ 6 files changed, 262 insertions(+), 98 deletions(-) create mode 100644 platform/usageView/src/com/intellij/usages/UsageInfoSearcherAdapter.java diff --git a/java/java-impl/src/com/intellij/codeInspection/inferNullity/AnnotateTask.java b/java/java-impl/src/com/intellij/codeInspection/inferNullity/AnnotateTask.java index 3cc23f4140ff..a7fd8ff16632 100644 --- a/java/java-impl/src/com/intellij/codeInspection/inferNullity/AnnotateTask.java +++ b/java/java-impl/src/com/intellij/codeInspection/inferNullity/AnnotateTask.java @@ -18,23 +18,24 @@ package com.intellij.codeInspection.inferNullity; import com.intellij.codeInsight.NullableNotNullManager; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.project.Project; +import com.intellij.usageView.UsageInfo; import com.intellij.util.SequentialModalProgressTask; import com.intellij.util.SequentialTask; class AnnotateTask implements SequentialTask { private final Project myProject; - private final NullityInferrer myInferrer; + private UsageInfo[] myInfos; private final SequentialModalProgressTask myTask; private int myCount = 0; private final int myTotal; private final NullableNotNullManager myNotNullManager; - public AnnotateTask(Project project, NullityInferrer inferrer, SequentialModalProgressTask progressTask) { + public AnnotateTask(Project project, SequentialModalProgressTask progressTask, UsageInfo[] infos) { myProject = project; + myInfos = infos; myNotNullManager = NullableNotNullManager.getInstance(myProject); - myInferrer = inferrer; myTask = progressTask; - myTotal = myInferrer.getCount(); + myTotal = infos.length; } @Override @@ -53,7 +54,7 @@ class AnnotateTask implements SequentialTask { indicator.setFraction(((double)myCount) / myTotal); } - myInferrer.apply(myCount++, myProject, myNotNullManager); + NullityInferrer.apply(myProject, myNotNullManager, myInfos[myCount++]); return isDone(); } diff --git a/java/java-impl/src/com/intellij/codeInspection/inferNullity/InferNullityAnnotationsAction.java b/java/java-impl/src/com/intellij/codeInspection/inferNullity/InferNullityAnnotationsAction.java index 7affcf4c8b04..c3d61021c0d0 100644 --- a/java/java-impl/src/com/intellij/codeInspection/inferNullity/InferNullityAnnotationsAction.java +++ b/java/java-impl/src/com/intellij/codeInspection/inferNullity/InferNullityAnnotationsAction.java @@ -25,13 +25,14 @@ import com.intellij.codeInsight.daemon.impl.quickfix.LocateLibraryDialog; import com.intellij.codeInsight.daemon.impl.quickfix.OrderEntryFix; import com.intellij.history.LocalHistory; import com.intellij.history.LocalHistoryAction; +import com.intellij.ide.util.PropertiesComponent; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.PathManager; import com.intellij.openapi.application.Result; import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.editor.Document; import com.intellij.openapi.module.Module; -import com.intellij.openapi.module.ModuleUtil; +import com.intellij.openapi.module.ModuleUtilCore; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; @@ -41,24 +42,35 @@ import com.intellij.openapi.roots.libraries.Library; import com.intellij.openapi.roots.libraries.LibraryUtil; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.ui.VerticalFlowLayout; +import com.intellij.openapi.util.Computable; +import com.intellij.openapi.util.Factory; +import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.*; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.util.PsiUtil; +import com.intellij.refactoring.RefactoringBundle; import com.intellij.ui.TitledSeparator; +import com.intellij.usageView.UsageInfo; +import com.intellij.usageView.UsageViewUtil; +import com.intellij.usages.*; import com.intellij.util.Function; +import com.intellij.util.Processor; import com.intellij.util.SequentialModalProgressTask; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import javax.swing.*; +import java.util.ArrayList; import java.util.HashSet; +import java.util.List; import java.util.Set; public class InferNullityAnnotationsAction extends BaseAnalysisAction { @NonNls private static final String INFER_NULLITY_ANNOTATIONS = "Infer Nullity Annotations"; + @NonNls private static final String ANNOTATE_LOCAL_VARIABLES = "annotate.local.variables"; private JCheckBox myAnnotateLocalVariablesCb; public InferNullityAnnotationsAction() { @@ -67,6 +79,9 @@ public class InferNullityAnnotationsAction extends BaseAnalysisAction { @Override protected void analyze(@NotNull final Project project, @NotNull final AnalysisScope scope) { + + PropertiesComponent.getInstance().setValue(ANNOTATE_LOCAL_VARIABLES, String.valueOf(myAnnotateLocalVariablesCb.isSelected())); + final ProgressManager progressManager = ProgressManager.getInstance(); final int totalFiles = scope.getFileCount(); @@ -91,7 +106,7 @@ public class InferNullityAnnotationsAction extends BaseAnalysisAction { } progressIndicator.setFraction(((double)myFileCount) / totalFiles); } - final Module module = ModuleUtil.findModuleForPsiElement(file); + final Module module = ModuleUtilCore.findModuleForPsiElement(file); if (module != null && !processed.contains(module)) { processed.add(module); if (JavaPsiFacade.getInstance(project) @@ -173,12 +188,32 @@ public class InferNullityAnnotationsAction extends BaseAnalysisAction { return; } if (scope.checkScopeWritable(project)) return; + PsiDocumentManager.getInstance(project).commitAllDocuments(); + final UsageInfo[] usageInfos = findUsages(project, scope); + if (usageInfos == null) return; + + if (usageInfos.length < 5) { + SwingUtilities.invokeLater(applyRunnable(project, new Computable() { + @Override + public UsageInfo[] compute() { + return usageInfos; + } + })); + } + else { + showUsageView(project, usageInfos, scope); + } + } + + private UsageInfo[] findUsages(final Project project, + final AnalysisScope scope) { final NullityInferrer inferrer = new NullityInferrer(myAnnotateLocalVariablesCb.isSelected(), project); final PsiManager psiManager = PsiManager.getInstance(project); - if (!progressManager.runProcessWithProgressSynchronously(new Runnable() { + final Runnable searchForUsages = new Runnable() { @Override public void run() { + final int totalFiles = scope.getFileCount(); scope.accept(new PsiElementVisitor() { int myFileCount = 0; @@ -191,9 +226,7 @@ public class InferNullityAnnotationsAction extends BaseAnalysisAction { if (document == null || virtualFile.getFileType().isBinary()) return; //do not inspect binary files final ProgressIndicator progressIndicator = ProgressManager.getInstance().getProgressIndicator(); if (progressIndicator != null) { - if (virtualFile != null) { - progressIndicator.setText2(ProjectUtil.calcRelativeToProjectPath(virtualFile, project)); - } + progressIndicator.setText2(ProjectUtil.calcRelativeToProjectPath(virtualFile, project)); progressIndicator.setFraction(((double)myFileCount) / totalFiles); } if (file instanceof PsiJavaFile) { @@ -202,11 +235,22 @@ public class InferNullityAnnotationsAction extends BaseAnalysisAction { } }); } - }, INFER_NULLITY_ANNOTATIONS, true, project)) { - return; + }; + if (ApplicationManager.getApplication().isDispatchThread()) { + if (!ProgressManager.getInstance().runProcessWithProgressSynchronously(searchForUsages, INFER_NULLITY_ANNOTATIONS, true, project)) { + return null; + } + } else { + searchForUsages.run(); } - final Runnable applyRunnable = new Runnable() { + final List usages = new ArrayList(); + inferrer.collect(usages); + return usages.toArray(new UsageInfo[usages.size()]); + } + + private static Runnable applyRunnable(final Project project, final Computable computable) { + return new Runnable() { @Override public void run() { final LocalHistoryAction action = LocalHistory.getInstance().startAction(INFER_NULLITY_ANNOTATIONS); @@ -214,11 +258,14 @@ public class InferNullityAnnotationsAction extends BaseAnalysisAction { new WriteCommandAction(project, INFER_NULLITY_ANNOTATIONS) { @Override protected void run(Result result) throws Throwable { - if (!inferrer.nothingFoundMessage(project)) { + final UsageInfo[] infos = computable.compute(); + if (infos.length > 0) { final SequentialModalProgressTask progressTask = new SequentialModalProgressTask(project, INFER_NULLITY_ANNOTATIONS, false); progressTask.setMinIterationTime(200); - progressTask.setTask(new AnnotateTask(project, inferrer, progressTask)); + progressTask.setTask(new AnnotateTask(project, progressTask, infos)); ProgressManager.getInstance().run(progressTask); + } else { + NullityInferrer.nothingFoundMessage(project); } } }.execute(); @@ -228,7 +275,6 @@ public class InferNullityAnnotationsAction extends BaseAnalysisAction { } } }; - SwingUtilities.invokeLater(applyRunnable); } private void restartAnalysis(final Project project, final AnalysisScope scope) { @@ -241,11 +287,69 @@ public class InferNullityAnnotationsAction extends BaseAnalysisAction { } + private void showUsageView(final Project project, final UsageInfo[] usageInfos, AnalysisScope scope) { + final UsageTarget[] targets = UsageTarget.EMPTY_ARRAY; + final Ref convertUsagesRef = new Ref(); + if (!ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable() { + @Override + public void run() { + ApplicationManager.getApplication().runReadAction(new Runnable() { + @Override + public void run() { + convertUsagesRef.set(UsageInfo2UsageAdapter.convert(usageInfos)); + } + }); + } + }, "Preprocess usages", true, project)) return; + + if (convertUsagesRef.isNull()) return; + final Usage[] usages = convertUsagesRef.get(); + + final UsageViewPresentation presentation = new UsageViewPresentation(); + presentation.setTabText("Infer Nullity Preview"); + presentation.setShowReadOnlyStatusAsRed(true); + presentation.setShowCancelButton(true); + presentation.setUsagesString(RefactoringBundle.message("usageView.usagesText")); + + final UsageView usageView = UsageViewManager.getInstance(project).showUsages(targets, usages, presentation, rerunFactory(project, scope)); + + final Runnable refactoringRunnable = applyRunnable(project, new Computable() { + @Override + public UsageInfo[] compute() { + final Set infos = UsageViewUtil.getNotExcludedUsageInfos(usageView); + return infos.toArray(new UsageInfo[infos.size()]); + } + }); + + String canNotMakeString = "Cannot perform operation.\nThere were changes in code after usages have been found.\nPlease perform operation search again."; + + usageView.addPerformOperationAction(refactoringRunnable, INFER_NULLITY_ANNOTATIONS, canNotMakeString, INFER_NULLITY_ANNOTATIONS, false); + } + + private Factory rerunFactory(final Project project, final AnalysisScope scope) { + return new Factory() { + @Override + public UsageSearcher create() { + return new UsageInfoSearcherAdapter() { + @Override + protected UsageInfo[] findUsages() { + return InferNullityAnnotationsAction.this.findUsages(project, scope); + } + + @Override + public void generate(@NotNull Processor processor) { + processUsages(processor, project); + } + }; + } + }; + } + @Override protected JComponent getAdditionalActionSettings(Project project, BaseAnalysisActionDialog dialog) { final JPanel panel = new JPanel(new VerticalFlowLayout()); panel.add(new TitledSeparator()); - myAnnotateLocalVariablesCb = new JCheckBox("Annotate local variables", false); + myAnnotateLocalVariablesCb = new JCheckBox("Annotate local variables", PropertiesComponent.getInstance().getBoolean(ANNOTATE_LOCAL_VARIABLES, false)); panel.add(myAnnotateLocalVariablesCb); return panel; } diff --git a/java/java-impl/src/com/intellij/codeInspection/inferNullity/NullityInferrer.java b/java/java-impl/src/com/intellij/codeInspection/inferNullity/NullityInferrer.java index 649634903574..7ce5e3f36183 100644 --- a/java/java-impl/src/com/intellij/codeInspection/inferNullity/NullityInferrer.java +++ b/java/java-impl/src/com/intellij/codeInspection/inferNullity/NullityInferrer.java @@ -17,7 +17,6 @@ package com.intellij.codeInspection.inferNullity; import com.intellij.codeInsight.NullableNotNullManager; import com.intellij.codeInsight.intention.AddAnnotationFix; -import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; @@ -27,6 +26,7 @@ import com.intellij.psi.search.searches.OverridingMethodsSearch; import com.intellij.psi.search.searches.ReferencesSearch; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; +import com.intellij.usageView.UsageInfo; import com.intellij.util.ArrayUtil; import com.intellij.util.Query; import org.jetbrains.annotations.NotNull; @@ -145,49 +145,40 @@ public class NullityInferrer { public void apply(final Project project) { final NullableNotNullManager manager = NullableNotNullManager.getInstance(project); for (SmartPsiElementPointer pointer : myNullableSet) { - annotateNullable(project, manager, pointer); + annotateNullable(project, manager, pointer.getElement()); } for (SmartPsiElementPointer pointer : myNotNullSet) { - annotateNotNull(project, manager, pointer); + annotateNotNull(project, manager, pointer.getElement()); } - nothingFoundMessage(project); - } - - public boolean nothingFoundMessage(final Project project) { if (myNullableSet.isEmpty() && myNotNullSet.isEmpty()) { - if (ApplicationManager.getApplication().isUnitTestMode()) { - throw new RuntimeException(NOTHING_FOUND_TO_INFER); - } - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - Messages.showInfoMessage(project, "No places found to infer @Nullable/@NotNull", "Infer Nullity Results"); - } - }); - return true; + throw new RuntimeException(NOTHING_FOUND_TO_INFER); } - return false; } - private void annotateNotNull(Project project, - NullableNotNullManager manager, - SmartPsiElementPointer pointer) { - final PsiModifierListOwner element = pointer.getElement(); + public static void nothingFoundMessage(final Project project) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + Messages.showInfoMessage(project, "No places found to infer @Nullable/@NotNull", "Infer Nullity Results"); + } + }); + } + + private static void annotateNotNull(Project project, + NullableNotNullManager manager, + final PsiModifierListOwner element) { if (element != null) { - if (shouldIgnore(element)) return; if (element instanceof PsiField && ((PsiField)element).hasInitializer() && element.hasModifierProperty(PsiModifier.FINAL)) return; invoke(project, element, manager.getDefaultNotNull(), manager.getDefaultNullable()); } } - private void annotateNullable(Project project, - NullableNotNullManager manager, - SmartPsiElementPointer pointer) { - final PsiModifierListOwner element = pointer.getElement(); + private static void annotateNullable(Project project, + NullableNotNullManager manager, + final PsiModifierListOwner element) { if (element != null) { - if (shouldIgnore(element)) return; invoke(project, element, manager.getDefaultNullable(), manager.getDefaultNotNull()); } } @@ -207,12 +198,11 @@ public class NullityInferrer { return myNotNullSet.size() + myNullableSet.size(); } - public void apply(int i, Project project, NullableNotNullManager manager) { - if (i < myNullableSet.size()) { - annotateNullable(project, manager, myNullableSet.get(i)); - } else { - i -= myNullableSet.size(); - annotateNotNull(project, manager, myNotNullSet.get(i)); + public static void apply(Project project, NullableNotNullManager manager, UsageInfo info) { + if (info instanceof NullableUsageInfo) { + annotateNullable(project, manager, (PsiModifierListOwner)info.getElement()); + } else if (info instanceof NotNullUsageInfo) { + annotateNotNull(project, manager, (PsiModifierListOwner)info.getElement()); } } @@ -243,6 +233,33 @@ public class NullityInferrer { numAnnotationsAdded++; } + private static class NullableUsageInfo extends UsageInfo { + public NullableUsageInfo(@NotNull PsiElement element) { + super(element); + } + } + + private static class NotNullUsageInfo extends UsageInfo { + private NotNullUsageInfo(@NotNull PsiElement element) { + super(element); + } + } + + public void collect(List usages) { + collect(usages, true); + collect(usages, false); + } + + private void collect(List usages, boolean nullable) { + final List> set = nullable ? myNullableSet : myNotNullSet; + for (SmartPsiElementPointer elementPointer : set) { + final PsiModifierListOwner element = elementPointer.getElement(); + if (element != null && !shouldIgnore(element)) { + usages.add(nullable ? new NullableUsageInfo(element) : new NotNullUsageInfo(element)); + } + } + } + private class ExpressionIsNeverNullVisitor extends JavaElementVisitor { private boolean neverNull = true; diff --git a/platform/lang-impl/src/com/intellij/refactoring/BaseRefactoringProcessor.java b/platform/lang-impl/src/com/intellij/refactoring/BaseRefactoringProcessor.java index 2532a9207298..27f65c87ccc6 100644 --- a/platform/lang-impl/src/com/intellij/refactoring/BaseRefactoringProcessor.java +++ b/platform/lang-impl/src/com/intellij/refactoring/BaseRefactoringProcessor.java @@ -237,7 +237,7 @@ public abstract class BaseRefactoringProcessor implements Runnable { Factory factory = new Factory() { @Override public UsageSearcher create() { - return new UsageSearcher() { + return new UsageInfoSearcherAdapter() { @Override public void generate(@NotNull final Processor processor) { ApplicationManager.getApplication().runReadAction(new Runnable() { @@ -249,38 +249,12 @@ public abstract class BaseRefactoringProcessor implements Runnable { refreshElements(elements); } }); - final Ref refUsages = new Ref(); - final Ref dumbModeOccurred = new Ref(); - ApplicationManager.getApplication().runReadAction(new Runnable() { - @Override - public void run() { - try { - refUsages.set(findUsages()); - } - catch (IndexNotReadyException e) { - dumbModeOccurred.set(true); - } - } - }); - if (!dumbModeOccurred.isNull()) { - DumbService.getInstance(myProject).showDumbModeNotification("Usage search is not available until indices are ready"); - return; - } - final Usage[] usages = ApplicationManager.getApplication().runReadAction(new Computable() { - @Override - public Usage[] compute() { - return UsageInfo2UsageAdapter.convert(refUsages.get()); - } - }); + processUsages(processor, myProject); + } - for (final Usage usage : usages) { - ApplicationManager.getApplication().runReadAction(new Runnable() { - @Override - public void run() { - processor.process(usage); - } - }); - } + @Override + protected UsageInfo[] findUsages() { + return BaseRefactoringProcessor.this.findUsages(); } }; } @@ -418,7 +392,7 @@ public abstract class BaseRefactoringProcessor implements Runnable { final Runnable refactoringRunnable = new Runnable() { @Override public void run() { - Set usagesToRefactor = getUsageInfosToRefactor(usageView); + Set usagesToRefactor = UsageViewUtil.getNotExcludedUsageInfos(usageView); final UsageInfo[] infos = usagesToRefactor.toArray(new UsageInfo[usagesToRefactor.size()]); if (ensureElementsWritable(infos, viewDescriptor)) { execute(infos); @@ -436,19 +410,6 @@ public abstract class BaseRefactoringProcessor implements Runnable { RefactoringBundle.message("usageView.doAction"), false); } - private static Set getUsageInfosToRefactor(final UsageView usageView) { - Set excludedUsages = usageView.getExcludedUsages(); - - Set usageInfos = new LinkedHashSet(); - for (Usage usage : usageView.getUsages()) { - if (usage instanceof UsageInfo2UsageAdapter && !excludedUsages.contains(usage)) { - UsageInfo usageInfo = ((UsageInfo2UsageAdapter)usage).getUsageInfo(); - usageInfos.add(usageInfo); - } - } - return usageInfos; - } - private void doRefactoring(@NotNull final Collection usageInfoSet) { for (Iterator iterator = usageInfoSet.iterator(); iterator.hasNext();) { UsageInfo usageInfo = iterator.next(); diff --git a/platform/lang-impl/src/com/intellij/usageView/UsageViewUtil.java b/platform/lang-impl/src/com/intellij/usageView/UsageViewUtil.java index 82b4ffec1ebe..ca230c790e65 100644 --- a/platform/lang-impl/src/com/intellij/usageView/UsageViewUtil.java +++ b/platform/lang-impl/src/com/intellij/usageView/UsageViewUtil.java @@ -30,6 +30,9 @@ import com.intellij.psi.PsiFile; import com.intellij.psi.PsiReference; import com.intellij.refactoring.util.MoveRenameUsageInfo; import com.intellij.refactoring.util.NonCodeUsageInfo; +import com.intellij.usages.Usage; +import com.intellij.usages.UsageInfo2UsageAdapter; +import com.intellij.usages.UsageView; import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; @@ -163,4 +166,17 @@ public class UsageViewUtil { Project project = info.getProject(); FileEditorManager.getInstance(project).openTextEditor(new OpenFileDescriptor(project, file, offset), requestFocus); } + + public static Set getNotExcludedUsageInfos(final UsageView usageView) { + Set excludedUsages = usageView.getExcludedUsages(); + + Set usageInfos = new LinkedHashSet(); + for (Usage usage : usageView.getUsages()) { + if (usage instanceof UsageInfo2UsageAdapter && !excludedUsages.contains(usage)) { + UsageInfo usageInfo = ((UsageInfo2UsageAdapter)usage).getUsageInfo(); + usageInfos.add(usageInfo); + } + } + return usageInfos; + } } diff --git a/platform/usageView/src/com/intellij/usages/UsageInfoSearcherAdapter.java b/platform/usageView/src/com/intellij/usages/UsageInfoSearcherAdapter.java new file mode 100644 index 000000000000..d448cbde9b46 --- /dev/null +++ b/platform/usageView/src/com/intellij/usages/UsageInfoSearcherAdapter.java @@ -0,0 +1,65 @@ +/* + * Copyright 2000-2014 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.usages; + +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.project.DumbService; +import com.intellij.openapi.project.IndexNotReadyException; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Computable; +import com.intellij.openapi.util.Ref; +import com.intellij.usageView.UsageInfo; +import com.intellij.util.Processor; +import org.jetbrains.annotations.NotNull; + +public abstract class UsageInfoSearcherAdapter implements UsageSearcher { + protected void processUsages(final @NotNull Processor processor, Project project) { + final Ref refUsages = new Ref(); + final Ref dumbModeOccurred = new Ref(); + ApplicationManager.getApplication().runReadAction(new Runnable() { + @Override + public void run() { + try { + refUsages.set(findUsages()); + } + catch (IndexNotReadyException e) { + dumbModeOccurred.set(true); + } + } + }); + if (!dumbModeOccurred.isNull()) { + DumbService.getInstance(project).showDumbModeNotification("Usage search is not available until indices are ready"); + return; + } + final Usage[] usages = ApplicationManager.getApplication().runReadAction(new Computable() { + @Override + public Usage[] compute() { + return UsageInfo2UsageAdapter.convert(refUsages.get()); + } + }); + + for (final Usage usage : usages) { + ApplicationManager.getApplication().runReadAction(new Runnable() { + @Override + public void run() { + processor.process(usage); + } + }); + } + } + + protected abstract UsageInfo[] findUsages(); +} From 02724fc1db27fc0980f9e0229d470b4a577bc990 Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Thu, 10 Apr 2014 19:07:06 +0200 Subject: [PATCH 09/21] try to start javafx without main (IDEA-101939) --- .../rt/execution/application/AppMain.java | 29 +++++++++++++++- .../src/META-INF/common-javaFX-plugin.xml | 1 + .../JavaFXApplicationMainMethodProvider.java | 34 +++++++++++++++++++ 3 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 plugins/javaFX/src/org/jetbrains/plugins/javaFX/JavaFXApplicationMainMethodProvider.java diff --git a/java/java-runtime/src/com/intellij/rt/execution/application/AppMain.java b/java/java-runtime/src/com/intellij/rt/execution/application/AppMain.java index 1319956e38cc..3673a930d742 100644 --- a/java/java-runtime/src/com/intellij/rt/execution/application/AppMain.java +++ b/java/java-runtime/src/com/intellij/rt/execution/application/AppMain.java @@ -33,6 +33,9 @@ public class AppMain { private static final String PROPERTY_PORT_NUMBER = "idea.launcher.port"; private static final String PROPERTY_BINPATH = "idea.launcher.bin.path"; + private static final String JAVAFX_LAUNCHER = "com.sun.javafx.application.LauncherImpl"; + private static final String LAUNCH_APPLICATION_METHOD_NAME = "launchApplication"; + private static native void triggerControlBreak(); static { @@ -110,7 +113,18 @@ public class AppMain { for (int j = 1; j < args.length; j++) { parms[j - 1] = args[j]; } - Method m = Class.forName(mainClass).getMethod("main", new Class[]{parms.getClass()}); + final Class appClass = Class.forName(mainClass); + Method m; + try { + m = appClass.getMethod("main", new Class[]{parms.getClass()}); + } + catch (NoSuchMethodException e) { + if (!startJavaFXApplication(parms, appClass)) { + throw e; + } + return; + } + if (!Modifier.isStatic(m.getModifiers())) { System.err.println("main method should be static"); return; @@ -123,6 +137,19 @@ public class AppMain { } } + private static boolean startJavaFXApplication(String[] parms, Class appClass) throws NoSuchMethodException { + try { + //check in launch method for application class in the stack trace leads to this hack here + final Method launchApplication = Class.forName(JAVAFX_LAUNCHER).getMethod(LAUNCH_APPLICATION_METHOD_NAME, + new Class[]{appClass.getClass(), parms.getClass()}); + launchApplication.invoke(null, new Object[] {appClass, parms}); + return true; + } + catch (Throwable e) { + return false; + } + } + private static void ensureAccess(Object reflectionObject) { // need to call setAccessible here in order to be able to launch package-local classes // calling setAccessible() via reflection because the method is missing from java version 1.1.x diff --git a/plugins/javaFX/src/META-INF/common-javaFX-plugin.xml b/plugins/javaFX/src/META-INF/common-javaFX-plugin.xml index cdc1397b5aac..c03a5f4bff9c 100644 --- a/plugins/javaFX/src/META-INF/common-javaFX-plugin.xml +++ b/plugins/javaFX/src/META-INF/common-javaFX-plugin.xml @@ -52,6 +52,7 @@ + diff --git a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/JavaFXApplicationMainMethodProvider.java b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/JavaFXApplicationMainMethodProvider.java new file mode 100644 index 000000000000..aa767cc12921 --- /dev/null +++ b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/JavaFXApplicationMainMethodProvider.java @@ -0,0 +1,34 @@ +package org.jetbrains.plugins.javaFX; + +import com.intellij.codeInsight.runner.JavaMainMethodProvider; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiMethod; +import com.intellij.psi.util.InheritanceUtil; +import com.intellij.psi.util.PsiMethodUtil; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.plugins.javaFX.fxml.JavaFxCommonClassNames; + +public class JavaFXApplicationMainMethodProvider implements JavaMainMethodProvider { + @NonNls public static final String LAUNCH_MAIN = "launch"; + + @Override + public boolean isApplicable(PsiClass clazz) { + return InheritanceUtil.isInheritor(clazz, true, JavaFxCommonClassNames.JAVAFX_APPLICATION_APPLICATION); + } + + @Override + public boolean hasMainMethod(PsiClass clazz) { + return InheritanceUtil.isInheritor(clazz, true, JavaFxCommonClassNames.JAVAFX_APPLICATION_APPLICATION); + } + + @Override + public PsiMethod findMainInClass(PsiClass clazz) { + final PsiMethod[] launches = clazz.findMethodsByName(LAUNCH_MAIN, true); + for (PsiMethod launchMethod : launches) { + if (PsiMethodUtil.isMainMethod(launchMethod)) { + return launchMethod; + } + } + return null; + } +} From 925a1869d25c29f72bd0bf3204ebb11964ebb57b Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Thu, 10 Apr 2014 19:57:31 +0200 Subject: [PATCH 10/21] copyright: allow to skip files with existing copyrights (IDEA-121912) --- .../actions/AbstractFileProcessor.java | 6 ++--- .../actions/UpdateCopyrightAction.java | 23 ++++++++++++++++++- .../actions/UpdateCopyrightProcessor.java | 8 +++++-- .../copyright/psi/UpdatePsiFileCopyright.java | 12 ++++++---- 4 files changed, 39 insertions(+), 10 deletions(-) diff --git a/plugins/copyright/src/com/maddyhome/idea/copyright/actions/AbstractFileProcessor.java b/plugins/copyright/src/com/maddyhome/idea/copyright/actions/AbstractFileProcessor.java index 21bf3e4d0454..504f2b2bd167 100644 --- a/plugins/copyright/src/com/maddyhome/idea/copyright/actions/AbstractFileProcessor.java +++ b/plugins/copyright/src/com/maddyhome/idea/copyright/actions/AbstractFileProcessor.java @@ -53,7 +53,7 @@ public abstract class AbstractFileProcessor { private final String message; private final String title; - protected abstract Runnable preprocessFile(PsiFile psifile) throws IncorrectOperationException; + protected abstract Runnable preprocessFile(PsiFile file, boolean allowReplacement) throws IncorrectOperationException; protected AbstractFileProcessor(Project project, String title, String message) { myProject = project; @@ -124,7 +124,7 @@ public abstract class AbstractFileProcessor { @Override public void run() { try { - resultRunnable[0] = preprocessFile(file); + resultRunnable[0] = preprocessFile(file, true); } catch (IncorrectOperationException incorrectoperationexception) { logger.error(incorrectoperationexception); @@ -168,7 +168,7 @@ public abstract class AbstractFileProcessor { if (pfile.isWritable()) { try { - runnables[i] = preprocessFile(pfile); + runnables[i] = preprocessFile(pfile, true); } catch (IncorrectOperationException incorrectoperationexception) { logger.error(incorrectoperationexception); diff --git a/plugins/copyright/src/com/maddyhome/idea/copyright/actions/UpdateCopyrightAction.java b/plugins/copyright/src/com/maddyhome/idea/copyright/actions/UpdateCopyrightAction.java index f9645e1043c9..eb879a1e52e9 100644 --- a/plugins/copyright/src/com/maddyhome/idea/copyright/actions/UpdateCopyrightAction.java +++ b/plugins/copyright/src/com/maddyhome/idea/copyright/actions/UpdateCopyrightAction.java @@ -18,6 +18,8 @@ package com.maddyhome.idea.copyright.actions; import com.intellij.analysis.AnalysisScope; import com.intellij.analysis.BaseAnalysisAction; +import com.intellij.analysis.BaseAnalysisActionDialog; +import com.intellij.ide.util.PropertiesComponent; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.command.CommandProcessor; @@ -27,19 +29,26 @@ import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.VerticalFlowLayout; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; +import com.intellij.ui.TitledSeparator; import com.intellij.util.SequentialModalProgressTask; import com.intellij.util.SequentialTask; import com.maddyhome.idea.copyright.CopyrightManager; import com.maddyhome.idea.copyright.pattern.FileUtil; import com.maddyhome.idea.copyright.util.FileTypeUtil; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import javax.swing.*; import java.util.ArrayList; import java.util.List; public class UpdateCopyrightAction extends BaseAnalysisAction { + public static final String UPDATE_EXISTING_COPYRIGHTS = "update.existing.copyrights"; + private JCheckBox myUpdateExistingCopyrightsCb; + protected UpdateCopyrightAction() { super(UpdateCopyrightProcessor.TITLE, UpdateCopyrightProcessor.TITLE); } @@ -107,8 +116,20 @@ public class UpdateCopyrightAction extends BaseAnalysisAction { return true; } + @Nullable + @Override + protected JComponent getAdditionalActionSettings(Project project, BaseAnalysisActionDialog dialog) { + final JPanel panel = new JPanel(new VerticalFlowLayout()); + panel.add(new TitledSeparator()); + myUpdateExistingCopyrightsCb = new JCheckBox("Update existing copyrights", + PropertiesComponent.getInstance().getBoolean(UPDATE_EXISTING_COPYRIGHTS, true)); + panel.add(myUpdateExistingCopyrightsCb); + return panel; + } + @Override protected void analyze(@NotNull final Project project, @NotNull final AnalysisScope scope) { + PropertiesComponent.getInstance().setValue(UPDATE_EXISTING_COPYRIGHTS, String.valueOf(myUpdateExistingCopyrightsCb.isSelected())); if (scope.checkScopeWritable(project)) return; final List preparations = new ArrayList(); Task.Backgroundable task = new Task.Backgroundable(project, "Prepare Copyright...", true) { @@ -120,7 +141,7 @@ public class UpdateCopyrightAction extends BaseAnalysisAction { if (indicator.isCanceled()) { return; } - preparations.add(new UpdateCopyrightProcessor(project, ModuleUtilCore.findModuleForPsiElement(file), file).preprocessFile(file)); + preparations.add(new UpdateCopyrightProcessor(project, ModuleUtilCore.findModuleForPsiElement(file), file).preprocessFile(file, myUpdateExistingCopyrightsCb.isSelected())); } }); } diff --git a/plugins/copyright/src/com/maddyhome/idea/copyright/actions/UpdateCopyrightProcessor.java b/plugins/copyright/src/com/maddyhome/idea/copyright/actions/UpdateCopyrightProcessor.java index dceefc6fd6e7..c9d7e4871dc3 100644 --- a/plugins/copyright/src/com/maddyhome/idea/copyright/actions/UpdateCopyrightProcessor.java +++ b/plugins/copyright/src/com/maddyhome/idea/copyright/actions/UpdateCopyrightProcessor.java @@ -31,6 +31,7 @@ import com.maddyhome.idea.copyright.CopyrightManager; import com.maddyhome.idea.copyright.CopyrightProfile; import com.maddyhome.idea.copyright.psi.UpdateCopyright; import com.maddyhome.idea.copyright.psi.UpdateCopyrightFactory; +import com.maddyhome.idea.copyright.psi.UpdatePsiFileCopyright; import com.maddyhome.idea.copyright.util.FileTypeUtil; public class UpdateCopyrightProcessor extends AbstractFileProcessor @@ -62,7 +63,7 @@ public class UpdateCopyrightProcessor extends AbstractFileProcessor setup(project, module); } - protected Runnable preprocessFile(final PsiFile file) throws IncorrectOperationException + protected Runnable preprocessFile(final PsiFile file, final boolean allowReplacement) throws IncorrectOperationException { VirtualFile vfile = file.getVirtualFile(); if (vfile == null) return EmptyRunnable.getInstance(); @@ -92,8 +93,11 @@ public class UpdateCopyrightProcessor extends AbstractFileProcessor { try { + if (update instanceof UpdatePsiFileCopyright) { + ((UpdatePsiFileCopyright)update).complete(allowReplacement); + } else { update.complete(); - + } } catch (Exception e) { diff --git a/plugins/copyright/src/com/maddyhome/idea/copyright/psi/UpdatePsiFileCopyright.java b/plugins/copyright/src/com/maddyhome/idea/copyright/psi/UpdatePsiFileCopyright.java index fa7f3467c856..d34152f870cd 100644 --- a/plugins/copyright/src/com/maddyhome/idea/copyright/psi/UpdatePsiFileCopyright.java +++ b/plugins/copyright/src/com/maddyhome/idea/copyright/psi/UpdatePsiFileCopyright.java @@ -72,13 +72,17 @@ public abstract class UpdatePsiFileCopyright extends AbstractUpdateCopyright { @Override public void complete() throws Exception { + complete(true); + } + + public void complete(boolean allowReplacement) throws Exception { if (file == null) { logger.info("No file for root: " + getRoot()); return; } if (accept()) { - processActions(); + processActions(allowReplacement); } } @@ -315,7 +319,7 @@ public abstract class UpdatePsiFileCopyright extends AbstractUpdateCopyright { return element == null ? null : element.getNextSibling(); } - protected void processActions() throws IncorrectOperationException { + protected void processActions(final boolean allowReplacement) throws IncorrectOperationException { new WriteCommandAction.Simple(file.getProject(), "Update copyright") { @Override protected void run() throws Throwable { @@ -334,10 +338,10 @@ public abstract class UpdatePsiFileCopyright extends AbstractUpdateCopyright { } break; case CommentAction.ACTION_REPLACE: - doc.replaceString(start, end, getCommentText("", "")); + if (allowReplacement) doc.replaceString(start, end, getCommentText("", "")); break; case CommentAction.ACTION_DELETE: - doc.deleteString(start, end); + if (allowReplacement) doc.deleteString(start, end); break; } } From 025232d50c3ad3422c83bfb758d058dede148191 Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Thu, 10 Apr 2014 20:07:11 +0200 Subject: [PATCH 11/21] IDEA-123818 Inspection "JUnit TestCase with non-trivial constructors" should not report error when used in a Parameterized test --- .../com/siyeh/ig/junit/TestCaseWithConstructorInspection.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/TestCaseWithConstructorInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/TestCaseWithConstructorInspection.java index 3b8529a67fc2..96221113f78c 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/TestCaseWithConstructorInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/junit/TestCaseWithConstructorInspection.java @@ -15,6 +15,7 @@ */ package com.siyeh.ig.junit; +import com.intellij.codeInsight.AnnotationUtil; import com.intellij.psi.*; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspection; @@ -65,7 +66,7 @@ public class TestCaseWithConstructorInspection extends BaseInspection { return; } final PsiClass aClass = method.getContainingClass(); - if (!TestUtils.isJUnitTestClass(aClass)) { + if (aClass == null || !TestUtils.isJUnitTestClass(aClass) || AnnotationUtil.isAnnotated(aClass, "org.junit.runner.RunWith", true)) { return; } final PsiCodeBlock body = method.getBody(); From e1aa83075bae3d43f0d46663813d323f07769990 Mon Sep 17 00:00:00 2001 From: Anton Makeev Date: Thu, 10 Apr 2014 20:08:16 +0200 Subject: [PATCH 12/21] Platform: not-null SimpleProgramParameters.getEnv --- .../intellij/execution/util/JavaParametersUtil.java | 6 ++---- .../configurations/SimpleProgramParameters.java | 8 +++----- .../execution/util/ProgramParametersConfigurator.java | 11 +++++------ .../plugins/groovy/griffon/GriffonFramework.java | 9 +-------- .../jetbrains/plugins/groovy/mvc/MvcFramework.java | 8 +------- .../plugins/groovy/mvc/MvcRunConfiguration.java | 8 ++------ .../idea/maven/execution/MavenJUnitPatcher.java | 2 +- 7 files changed, 15 insertions(+), 37 deletions(-) diff --git a/java/execution/impl/src/com/intellij/execution/util/JavaParametersUtil.java b/java/execution/impl/src/com/intellij/execution/util/JavaParametersUtil.java index 4433413eaf35..8abf465104dd 100644 --- a/java/execution/impl/src/com/intellij/execution/util/JavaParametersUtil.java +++ b/java/execution/impl/src/com/intellij/execution/util/JavaParametersUtil.java @@ -56,10 +56,8 @@ public class JavaParametersUtil { if (vmParameters != null) { vmParameters = ProgramParametersUtil.expandPath(vmParameters, module, project); - if (parameters.getEnv() != null) { - for (Map.Entry each : parameters.getEnv().entrySet()) { - vmParameters = StringUtil.replace(vmParameters, "$" + each.getKey() + "$", each.getValue(), false); //replace env usages - } + for (Map.Entry each : parameters.getEnv().entrySet()) { + vmParameters = StringUtil.replace(vmParameters, "$" + each.getKey() + "$", each.getValue(), false); //replace env usages } } diff --git a/platform/lang-api/src/com/intellij/execution/configurations/SimpleProgramParameters.java b/platform/lang-api/src/com/intellij/execution/configurations/SimpleProgramParameters.java index 28c34514d349..15bd92cfcf8c 100644 --- a/platform/lang-api/src/com/intellij/execution/configurations/SimpleProgramParameters.java +++ b/platform/lang-api/src/com/intellij/execution/configurations/SimpleProgramParameters.java @@ -18,6 +18,7 @@ package com.intellij.execution.configurations; import com.intellij.execution.configuration.EnvironmentVariablesComponent; import gnu.trove.THashMap; +import org.jetbrains.annotations.NotNull; import java.io.File; import java.util.HashMap; @@ -26,7 +27,7 @@ import java.util.Map; public class SimpleProgramParameters { private final ParametersList myProgramParameters = new ParametersList(); private String myWorkingDirectory; - private Map myEnv; + private Map myEnv = new THashMap(); private boolean myPassParentEnvs = true; public String getWorkingDirectory() { @@ -45,15 +46,12 @@ public class SimpleProgramParameters { return myProgramParameters; } + @NotNull public Map getEnv() { return myEnv; } public String addEnv(String name, String value) { - if (myEnv == null) { - myEnv = new THashMap(); - } - return myEnv.put(name, value); } diff --git a/platform/lang-impl/src/com/intellij/execution/util/ProgramParametersConfigurator.java b/platform/lang-impl/src/com/intellij/execution/util/ProgramParametersConfigurator.java index af2ea11eaea4..1a11305ec0a1 100644 --- a/platform/lang-impl/src/com/intellij/execution/util/ProgramParametersConfigurator.java +++ b/platform/lang-impl/src/com/intellij/execution/util/ProgramParametersConfigurator.java @@ -43,13 +43,12 @@ public class ProgramParametersConfigurator { parameters.setWorkingDirectory(getWorkingDir(configuration, project, module)); parameters.setupEnvs(configuration.getEnvs(), configuration.isPassParentEnvs()); - if (parameters.getEnv() != null) { - Map expanded = new HashMap(); - for (Map.Entry each : parameters.getEnv().entrySet()) { - expanded.put(each.getKey(), expandPath(each.getValue(), module, project)); - } - parameters.setEnv(expanded); + + Map expanded = new HashMap(); + for (Map.Entry each : parameters.getEnv().entrySet()) { + expanded.put(each.getKey(), expandPath(each.getValue(), module, project)); } + parameters.setEnv(expanded); } @Nullable diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/griffon/GriffonFramework.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/griffon/GriffonFramework.java index faa49555b9c1..f03e3b3ed3cb 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/griffon/GriffonFramework.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/griffon/GriffonFramework.java @@ -55,9 +55,7 @@ import org.jetbrains.plugins.groovy.mvc.*; import javax.swing.*; import java.io.File; import java.io.IOException; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -238,12 +236,7 @@ public class GriffonFramework extends MvcFramework { return params; } - Map env = params.getEnv(); - if (env == null) { - env = new HashMap(); - params.setEnv(env); - } - env.put(getSdkHomePropertyName(), FileUtil.toSystemDependentName(sdkRoot.getPath())); + params.addEnv(getSdkHomePropertyName(), FileUtil.toSystemDependentName(sdkRoot.getPath())); final VirtualFile lib = sdkRoot.findChild("lib"); if (lib != null) { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcFramework.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcFramework.java index 3617ad1fec79..c554c7c24549 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcFramework.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcFramework.java @@ -425,13 +425,7 @@ public abstract class MvcFramework { if (sdk != null && sdk.getSdkType() instanceof JavaSdkType) { String path = StringUtil.trimEnd(sdk.getHomePath(), File.separator); if (StringUtil.isNotEmpty(path)) { - Map env = params.getEnv(); - if (env == null) { - env = new HashMap(); - params.setEnv(env); - } - - env.put("JAVA_HOME", FileUtil.toSystemDependentName(path)); + params.addEnv("JAVA_HOME", FileUtil.toSystemDependentName(path)); } } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcRunConfiguration.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcRunConfiguration.java index ccc3a35918eb..7a87dd140b2e 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcRunConfiguration.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcRunConfiguration.java @@ -254,12 +254,8 @@ public abstract class MvcRunConfiguration extends ModuleBasedConfiguration envVars = new HashMap(envs); - - Map oldEnv = params.getEnv(); - if (oldEnv != null) { - envVars.putAll(oldEnv); - } - + envVars.putAll(params.getEnv()); + params.setupEnvs(envVars, passParentEnv); MvcFramework.addJavaHome(params, myModule); diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/execution/MavenJUnitPatcher.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/execution/MavenJUnitPatcher.java index 8435f6f327d3..cb612cebdc1c 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/execution/MavenJUnitPatcher.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/execution/MavenJUnitPatcher.java @@ -85,7 +85,7 @@ public class MavenJUnitPatcher extends JUnitPatcher { for (Element element : environmentVariables.getChildren()) { String variableName = element.getName(); - if (javaParameters.getEnv() == null || !javaParameters.getEnv().containsKey(variableName)) { + if (!javaParameters.getEnv().containsKey(variableName)) { String value = resolveSurefireProperties(element.getValue()); if (isResolved(value)) { javaParameters.addEnv(variableName, value); From ee8955d38dc5d3cbe89d0851524a973167eed58a Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Thu, 10 Apr 2014 20:59:43 +0200 Subject: [PATCH 13/21] rerun failed tests: include all parameterized if one have failed (IDEA-121214) --- .../src/com/intellij/execution/junit/TestMethods.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/plugins/junit/src/com/intellij/execution/junit/TestMethods.java b/plugins/junit/src/com/intellij/execution/junit/TestMethods.java index bb847fbaec35..a526abb6a7ea 100644 --- a/plugins/junit/src/com/intellij/execution/junit/TestMethods.java +++ b/plugins/junit/src/com/intellij/execution/junit/TestMethods.java @@ -21,6 +21,7 @@ import com.intellij.execution.ExecutionException; import com.intellij.execution.JavaExecutionUtil; import com.intellij.execution.Location; import com.intellij.execution.configurations.RunConfigurationModule; +import com.intellij.execution.junit2.PsiMemberParameterizedLocation; import com.intellij.execution.junit2.TestProxy; import com.intellij.execution.junit2.info.MethodLocation; import com.intellij.execution.junit2.info.TestInfo; @@ -72,6 +73,13 @@ public class TestMethods extends TestMethod { final GlobalSearchScope searchScope = myConfiguration.getConfigurationModule().getSearchScope(); for (AbstractTestProxy failedTest : myFailedTests) { Location location = failedTest.getLocation(project, searchScope); + if (location instanceof PsiMemberParameterizedLocation) { + final PsiElement element = location.getPsiElement(); + if (element instanceof PsiMethod) { + location = MethodLocation.elementInClass(((PsiMethod)element), + ((PsiMemberParameterizedLocation)location).getContainingClass()); + } + } if (!(location instanceof MethodLocation)) continue; PsiElement psiElement = location.getPsiElement(); LOG.assertTrue(psiElement instanceof PsiMethod); From 18bec53fedc357c62980b667706aa4a61f565c2f Mon Sep 17 00:00:00 2001 From: "Gregory.Shrago" Date: Thu, 10 Apr 2014 23:44:43 +0400 Subject: [PATCH 14/21] User/password dialog --- .../ui/PasswordPromptComponent.java | 111 ++++++++++++++ .../ui/PasswordSafePromptDialog.form | 135 +++++++++++++----- .../ui/PasswordSafePromptDialog.java | 86 +++++------ 3 files changed, 248 insertions(+), 84 deletions(-) create mode 100644 platform/platform-impl/src/com/intellij/ide/passwordSafe/ui/PasswordPromptComponent.java diff --git a/platform/platform-impl/src/com/intellij/ide/passwordSafe/ui/PasswordPromptComponent.java b/platform/platform-impl/src/com/intellij/ide/passwordSafe/ui/PasswordPromptComponent.java new file mode 100644 index 000000000000..a9a0a7b66dd3 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/ide/passwordSafe/ui/PasswordPromptComponent.java @@ -0,0 +1,111 @@ +/* + * Copyright 2000-2014 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.ide.passwordSafe.ui; + +import com.intellij.ide.passwordSafe.config.PasswordSafeSettings; +import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.util.ui.UIUtil; + +import javax.swing.*; + +/** + * @author gregsh + */ +public class PasswordPromptComponent { + private JPanel myRootPanel; + private JPanel myUserPanel; + private JPanel myPasswordPanel; + private JPasswordField myPasswordField; + private JCheckBox myRememberCheckBox; + private JLabel myMessageLabel; + private JLabel myPasswordLabel; + private JLabel myUserLabel; + private JTextField myUserTextField; + private JLabel myIconLabel; + + public PasswordPromptComponent(PasswordSafeSettings.ProviderType type, + String message, + boolean showUserName, + String passwordPrompt, + String rememberPrompt) { + myIconLabel.setText(""); + myIconLabel.setIcon(Messages.getWarningIcon()); + myMessageLabel.setFont(UIUtil.getLabelFont(UIUtil.FontSize.SMALL)); + myMessageLabel.setText(message); + setTargetProviderType(type); + setUserInputVisible(showUserName); + if (passwordPrompt != null) myPasswordLabel.setText(passwordPrompt); + if (rememberPrompt != null) myRememberCheckBox.setText(rememberPrompt); + } + + public JComponent getComponent() { + return myRootPanel; + } + + public JComponent getPreferredFocusedComponent() { + return myUserTextField.isShowing() && StringUtil.isEmpty(myUserTextField.getText()) ? + myUserTextField : myPasswordField; + } + + private void setUserInputVisible(boolean visible) { + UIUtil.setEnabled(myUserPanel, visible, true); + myUserPanel.setVisible(visible); + } + + private void setTargetProviderType(PasswordSafeSettings.ProviderType type) { + switch (type) { + case MASTER_PASSWORD: + myRememberCheckBox.setEnabled(true); + myRememberCheckBox.setSelected(true); + myRememberCheckBox.setToolTipText("The password will be stored between application sessions."); + break; + case MEMORY_ONLY: + myRememberCheckBox.setEnabled(true); + myRememberCheckBox.setSelected(true); + myRememberCheckBox.setToolTipText("The password will be stored only during this application session."); + break; + case DO_NOT_STORE: + myRememberCheckBox.setVisible(false); + myRememberCheckBox.setEnabled(false); + myRememberCheckBox.setSelected(false); + myRememberCheckBox.setToolTipText("The password storing is disabled."); + break; + default: + throw new AssertionError("Unknown policy type: " + type); + } + } + + public String getUserName() { + return myUserTextField.getText(); + } + + public void setUserName(String text) { + myUserTextField.setText(text); + } + + public char[] getPassword() { + return myPasswordField.getPassword(); + } + + public void setPassword(String text) { + myPasswordField.setText(text); + } + + public boolean isRememberChecked() { + return myRememberCheckBox.isSelected(); + } +} diff --git a/platform/platform-impl/src/com/intellij/ide/passwordSafe/ui/PasswordSafePromptDialog.form b/platform/platform-impl/src/com/intellij/ide/passwordSafe/ui/PasswordSafePromptDialog.form index 90997c8942b3..1e4e36458644 100644 --- a/platform/platform-impl/src/com/intellij/ide/passwordSafe/ui/PasswordSafePromptDialog.form +++ b/platform/platform-impl/src/com/intellij/ide/passwordSafe/ui/PasswordSafePromptDialog.form @@ -1,54 +1,119 @@ -
- + + - + - + + + - + + - - - - - - - - - - - - - - - - - - - - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - - - - - - + diff --git a/platform/platform-impl/src/com/intellij/ide/passwordSafe/ui/PasswordSafePromptDialog.java b/platform/platform-impl/src/com/intellij/ide/passwordSafe/ui/PasswordSafePromptDialog.java index 2d218189ea16..dfe478b11f31 100644 --- a/platform/platform-impl/src/com/intellij/ide/passwordSafe/ui/PasswordSafePromptDialog.java +++ b/platform/platform-impl/src/com/intellij/ide/passwordSafe/ui/PasswordSafePromptDialog.java @@ -25,12 +25,14 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.Ref; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; -import java.util.concurrent.atomic.AtomicReference; /** * The generic password dialog. Use it to ask a password from user with option to remember it. @@ -38,54 +40,46 @@ import java.util.concurrent.atomic.AtomicReference; public class PasswordSafePromptDialog extends DialogWrapper { private static final Logger LOG = Logger.getInstance(PasswordSafePromptDialog.class.getName()); - private JPasswordField myPasswordField; - private JCheckBox myRememberPasswordCheckBox; - private JPanel myRootPanel; - private JLabel myMessageLabel; - private JLabel myPromptLabel; - + private final PasswordPromptComponent myComponent; /** * The private constructor. Note that it does not do init on dialog. * * @param project the project - * @param passwordSafe the passwordSafe instance * @param title the dialog title * @param message the message on the dialog + * @param type */ - private PasswordSafePromptDialog(@Nullable Project project, @NotNull PasswordSafeImpl passwordSafe, String title, String message) { + private PasswordSafePromptDialog(@Nullable Project project, @NotNull String title, @NotNull PasswordPromptComponent component) { super(project, true); setTitle(title); - myMessageLabel.setText(message); - switch (passwordSafe.getSettings().getProviderType()) { - case MASTER_PASSWORD: - myRememberPasswordCheckBox.setEnabled(true); - myRememberPasswordCheckBox.setSelected(true); - myRememberPasswordCheckBox.setToolTipText("The password will be stored between application sessions."); - break; - case MEMORY_ONLY: - myRememberPasswordCheckBox.setEnabled(true); - myRememberPasswordCheckBox.setSelected(true); - myRememberPasswordCheckBox.setToolTipText("The password will be stored only during this application session."); - break; - case DO_NOT_STORE: - myRememberPasswordCheckBox.setEnabled(false); - myRememberPasswordCheckBox.setSelected(false); - myRememberPasswordCheckBox.setToolTipText("The password storing is disabled."); - break; - default: - LOG.error("Unknown policy type: " + passwordSafe.getSettings().getProviderType()); - } + myComponent = component; + setResizable(false); + init(); + } + + public PasswordPromptComponent getComponent() { + return myComponent; } @Override protected JComponent createCenterPanel() { - return myRootPanel; + return myComponent.getComponent(); } @Override public JComponent getPreferredFocusedComponent() { - return myPasswordField; + return myComponent.getPreferredFocusedComponent(); + } + + @Nullable + public static Pair showUserPasswordDialog(Project project, String title, String message, String defUser) { + PasswordPromptComponent component = new PasswordPromptComponent(PasswordSafeSettings.ProviderType.DO_NOT_STORE, message, true, null, null); + component.setUserName(StringUtil.notNullize(defUser)); + if (new PasswordSafePromptDialog(project, title, component).showAndGet()) { + return Pair.create(component.getUserName(), new String(component.getPassword())); + } + return null; } /** @@ -206,28 +200,22 @@ public class PasswordSafePromptDialog extends DialogWrapper { LOG.debug("Failed to retrieve or reset password", ex); } } - final AtomicReference pw = new AtomicReference(null); + final Ref ref = Ref.create(); ApplicationManager.getApplication().invokeAndWait(new Runnable() { public void run() { - final PasswordSafePromptDialog d = new PasswordSafePromptDialog(project, ps, title, message); - if (promptLabel != null) { - d.myPromptLabel.setText(promptLabel); - } - if (checkboxLabel != null) { - d.myRememberPasswordCheckBox.setText(checkboxLabel); - } - d.init(); + PasswordSafeSettings.ProviderType type = ps.getSettings().getProviderType(); + final PasswordPromptComponent component = new PasswordPromptComponent(type, message, false, promptLabel, checkboxLabel); + PasswordSafePromptDialog d = new PasswordSafePromptDialog(project, title, component); + d.setErrorText(error); - d.show(); - if (d.isOK()) { - String p = new String(d.myPasswordField.getPassword()); - pw.set(p); + if (d.showAndGet()) { + ref.set(new String(component.getPassword())); try { - if (d.myRememberPasswordCheckBox.isSelected()) { - ps.storePassword(project, requestor, key, p); + if (component.isRememberChecked()) { + ps.storePassword(project, requestor, key, ref.get()); } - else if (!ps.getSettings().getProviderType().equals(PasswordSafeSettings.ProviderType.DO_NOT_STORE)) { - ps.getMemoryProvider().storePassword(project, requestor, key, p); + else if (!type.equals(PasswordSafeSettings.ProviderType.DO_NOT_STORE)) { + ps.getMemoryProvider().storePassword(project, requestor, key, ref.get()); } } catch (PasswordSafeException e) { @@ -239,7 +227,7 @@ public class PasswordSafePromptDialog extends DialogWrapper { } } }, modalityState == null ? ModalityState.defaultModalityState() : modalityState); - return pw.get(); + return ref.get(); } } From 17ae7cb87f56c43edee12a95d79f5feb1fd5310d Mon Sep 17 00:00:00 2001 From: "Gregory.Shrago" Date: Thu, 10 Apr 2014 23:51:35 +0400 Subject: [PATCH 15/21] renamed --- ...PasswordSafePromptDialog.form => PasswordPromptComponent.form} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename platform/platform-impl/src/com/intellij/ide/passwordSafe/ui/{PasswordSafePromptDialog.form => PasswordPromptComponent.form} (100%) diff --git a/platform/platform-impl/src/com/intellij/ide/passwordSafe/ui/PasswordSafePromptDialog.form b/platform/platform-impl/src/com/intellij/ide/passwordSafe/ui/PasswordPromptComponent.form similarity index 100% rename from platform/platform-impl/src/com/intellij/ide/passwordSafe/ui/PasswordSafePromptDialog.form rename to platform/platform-impl/src/com/intellij/ide/passwordSafe/ui/PasswordPromptComponent.form From ea048db8a35bbe2d9645211b44efc5dfaeed9f8d Mon Sep 17 00:00:00 2001 From: peter Date: Thu, 10 Apr 2014 21:50:29 +0200 Subject: [PATCH 16/21] move more logic from CacheUpdateSession to CacheUpdateRunner --- .../openapi/project/CacheUpdateRunner.java | 15 +++++++++++++-- .../openapi/project/CacheUpdateSession.java | 13 +++++++------ 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/project/CacheUpdateRunner.java b/platform/platform-impl/src/com/intellij/openapi/project/CacheUpdateRunner.java index 5059b26b9688..156dca9294a6 100644 --- a/platform/platform-impl/src/com/intellij/openapi/project/CacheUpdateRunner.java +++ b/platform/platform-impl/src/com/intellij/openapi/project/CacheUpdateRunner.java @@ -27,6 +27,7 @@ import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.progress.util.ProgressIndicatorBase; import com.intellij.openapi.progress.util.ProgressWrapper; +import com.intellij.openapi.util.Key; import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.Consumer; @@ -40,6 +41,7 @@ import java.util.concurrent.atomic.AtomicBoolean; public class CacheUpdateRunner { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.project.CacheUpdateRunner"); + private static final Key FAILED_TO_INDEX = Key.create("FAILED_TO_INDEX"); private static final int PROC_COUNT = Runtime.getRuntime().availableProcessors(); private final Project myProject; private final Collection myUpdaters; @@ -251,9 +253,18 @@ public class CacheUpdateRunner { myInnerIndicator.checkCanceled(); if (!myProject.isDisposed()) { final VirtualFile file = fileContent.getVirtualFile(); - if (file.isValid()) { + try { myProgressUpdater.consume(file); - myProcessor.consume(fileContent); + if (file.isValid() && !file.isDirectory() && !Boolean.TRUE.equals(file.getUserData(FAILED_TO_INDEX))) { + myProcessor.consume(fileContent); + } + } + catch (ProcessCanceledException e) { + throw e; + } + catch (Throwable e) { + LOG.error("Error while indexing " + file.getPresentableUrl() + "\n" + "To reindex this file IDEA has to be restarted", e); + file.putUserData(FAILED_TO_INDEX, Boolean.TRUE); } } } diff --git a/platform/platform-impl/src/com/intellij/openapi/project/CacheUpdateSession.java b/platform/platform-impl/src/com/intellij/openapi/project/CacheUpdateSession.java index 9feceed488a9..288b409deecf 100644 --- a/platform/platform-impl/src/com/intellij/openapi/project/CacheUpdateSession.java +++ b/platform/platform-impl/src/com/intellij/openapi/project/CacheUpdateSession.java @@ -21,7 +21,6 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.util.Condition; -import com.intellij.openapi.util.Key; import com.intellij.openapi.util.Pair; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.containers.ContainerUtil; @@ -33,7 +32,6 @@ import java.util.*; public class CacheUpdateSession { private static final Logger LOG = Logger.getInstance("#" + CacheUpdateSession.class.getName()); - private static final Key FAILED_TO_INDEX = Key.create(CacheUpdateSession.class.getSimpleName() + ".FAILED_TO_INDEX"); private final List myFilesToUpdate; private final int myJobsToDo; private final List>> myUpdatersWithFiles = new ArrayList>>(); @@ -94,13 +92,14 @@ public class CacheUpdateSession { VirtualFile file = content.getVirtualFile(); boolean isValid = file.isValid() && !file.isDirectory(); + Throwable exception = null; while (true) { Pair> pair = getPair(file); - if (pair == null) return; + if (pair == null) break; CacheUpdater eachUpdater = pair.getFirst(); Collection eachFiles = pair.getSecond(); try { - if (isValid && !Boolean.TRUE.equals(file.getUserData(FAILED_TO_INDEX))) { + if (isValid && exception == null) { eachUpdater.processFile(content); } } @@ -108,11 +107,13 @@ public class CacheUpdateSession { throw e; } catch (Throwable e) { - LOG.error("Error while indexing " + file.getPresentableUrl() + "\n" + "To reindex this file IDEA has to be restarted", e); - file.putUserData(FAILED_TO_INDEX, Boolean.TRUE); + exception = e; } removeFile(file, eachUpdater, eachFiles); } + if (exception instanceof RuntimeException) throw (RuntimeException)exception; + if (exception instanceof Error) throw (Error)exception; + if (exception != null) throw new RuntimeException(exception); } private synchronized void removeFile(@NotNull VirtualFile file, @NotNull CacheUpdater eachUpdater, @NotNull Collection eachFiles) { From 542bb74d272b05c9ea24ed5c760651432c565012 Mon Sep 17 00:00:00 2001 From: peter Date: Thu, 10 Apr 2014 22:10:46 +0200 Subject: [PATCH 17/21] run pushers in background --- .../util/indexing/UnindexedFilesUpdater.java | 3 + .../impl/PushedFilePropertiesUpdater.java | 137 +++++++++++++----- 2 files changed, 104 insertions(+), 36 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/util/indexing/UnindexedFilesUpdater.java b/platform/lang-impl/src/com/intellij/util/indexing/UnindexedFilesUpdater.java index fc628b5bd82b..c044a8ea9a00 100644 --- a/platform/lang-impl/src/com/intellij/util/indexing/UnindexedFilesUpdater.java +++ b/platform/lang-impl/src/com/intellij/util/indexing/UnindexedFilesUpdater.java @@ -26,6 +26,7 @@ import com.intellij.openapi.project.CacheUpdateRunner; import com.intellij.openapi.project.DumbModeTask; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.CollectingContentIterator; +import com.intellij.openapi.roots.impl.PushedFilePropertiesUpdater; import com.intellij.openapi.startup.StartupManager; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.Consumer; @@ -50,6 +51,8 @@ public class UnindexedFilesUpdater extends DumbModeTask { } private void updateUnindexedFiles(ProgressIndicator indicator) { + PushedFilePropertiesUpdater.getInstance(myProject).performPushTasks(indicator); + indicator.setIndeterminate(true); indicator.setText(IdeBundle.message("progress.indexing.scanning")); diff --git a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/PushedFilePropertiesUpdater.java b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/PushedFilePropertiesUpdater.java index 1663a0ef327c..2478fef835fc 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/PushedFilePropertiesUpdater.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/PushedFilePropertiesUpdater.java @@ -20,6 +20,7 @@ package com.intellij.openapi.roots.impl; import com.intellij.ProjectTopics; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.extensions.ExtensionException; import com.intellij.openapi.extensions.Extensions; @@ -27,10 +28,14 @@ import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleManager; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.project.DumbModeTask; +import com.intellij.openapi.project.DumbService; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.*; import com.intellij.openapi.startup.StartupManager; +import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.Condition; +import com.intellij.openapi.util.EmptyRunnable; import com.intellij.openapi.vfs.*; import com.intellij.openapi.vfs.impl.BulkVirtualFileListenerAdapter; import com.intellij.util.containers.ContainerUtil; @@ -39,6 +44,8 @@ import com.intellij.util.messages.MessageBusConnection; import org.jetbrains.annotations.NotNull; import java.io.IOException; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; public class PushedFilePropertiesUpdater { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.roots.impl.PushedFilePropertiesUpdater"); @@ -46,6 +53,7 @@ public class PushedFilePropertiesUpdater { private final Project myProject; private final FilePropertyPusher[] myPushers; private final FilePropertyPusher[] myFilePushers; + private final Queue myTasks = new ConcurrentLinkedQueue(); public static PushedFilePropertiesUpdater getInstance(Project project) { return project.getComponent(PushedFilePropertiesUpdater.class); @@ -84,17 +92,19 @@ public class PushedFilePropertiesUpdater { public void fileCreated(@NotNull final VirtualFileEvent event) { final VirtualFile file = event.getFile(); final FilePropertyPusher[] pushers = file.isDirectory() ? myPushers : myFilePushers; - pushRecursively(file, project, pushers); + pushRecursively(file, pushers); } @Override public void fileMoved(@NotNull final VirtualFileMoveEvent event) { final VirtualFile file = event.getFile(); final FilePropertyPusher[] pushers = file.isDirectory() ? myPushers : myFilePushers; + if (pushers.length == 0) return; for (FilePropertyPusher pusher : pushers) { file.putUserData(pusher.getFileDataKey(), null); } - pushRecursively(file, project, pushers); + // push synchronously to avoid entering dumb mode in the middle of a meaningful write action + doPushRecursively(file, pushers, ProjectRootManager.getInstance(myProject).getFileIndex()); } })); for (final FilePropertyPusher pusher : myPushers) { @@ -106,7 +116,7 @@ public class PushedFilePropertiesUpdater { @Override public void pushRecursively(VirtualFile file, Project project) { - PushedFilePropertiesUpdater.this.pushRecursively(file, project, pusher); + PushedFilePropertiesUpdater.this.pushRecursively(file, pusher); } }); } @@ -114,9 +124,19 @@ public class PushedFilePropertiesUpdater { }); } - public void pushRecursively(final VirtualFile dir, final Project project, final FilePropertyPusher... pushers) { + private void pushRecursively(final VirtualFile dir, final FilePropertyPusher... pushers) { if (pushers.length == 0) return; - ProjectRootManager.getInstance(project).getFileIndex().iterateContentUnderDirectory(dir, new ContentIterator() { + final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(myProject).getFileIndex(); + queueTask(new DumbModeTask() { + @Override + public void performInDumbMode(@NotNull final ProgressIndicator indicator) { + doPushRecursively(dir, pushers, fileIndex); + } + }); + } + + private void doPushRecursively(VirtualFile dir, final FilePropertyPusher[] pushers, ProjectFileIndex fileIndex) { + fileIndex.iterateContentUnderDirectory(dir, new ContentIterator() { @Override public boolean processFile(final VirtualFile fileOrDir) { applyPushersToFile(fileOrDir, pushers, null); @@ -125,6 +145,26 @@ public class PushedFilePropertiesUpdater { }); } + private void queueTask(DumbModeTask task) { + myTasks.offer(task); + DumbService.getInstance(myProject).queueTask(new DumbModeTask() { + @Override + public void performInDumbMode(@NotNull ProgressIndicator indicator) { + performPushTasks(indicator); + } + }); + } + + public void performPushTasks(ProgressIndicator indicator) { + while (true) { + DumbModeTask task = myTasks.poll(); + if (task == null) { + break; + } + task.performInDumbMode(indicator); + } + } + private static T findPusherValuesUpwards(Project project, VirtualFile dir, FilePropertyPusher pusher, T moduleValue) { final T value = pusher.getImmediateValue(project, dir); if (value != null) return value; @@ -147,44 +187,69 @@ public class PushedFilePropertiesUpdater { } public void pushAll(final FilePropertyPusher... pushers) { - ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator(); - if (indicator != null) { - indicator.pushState(); - indicator.setText("Initializing file system cache..."); - } - Module[] modules = ModuleManager.getInstance(myProject).getModules(); - for (int i1 = 0; i1 < modules.length; i1++) { - if (indicator != null) { - indicator.setFraction((double) i1 / modules.length); + queueTask(new DumbModeTask() { + @Override + public void performInDumbMode(@NotNull ProgressIndicator indicator) { + doPushAll(pushers); } - Module module = modules[i1]; - final Object[] moduleValues = new Object[pushers.length]; - for (int i = 0; i < moduleValues.length; i++) { - moduleValues[i] = pushers[i].getImmediateValue(module); - } - final ModuleRootManager rootManager = ModuleRootManager.getInstance(module); - final ModuleFileIndex index = rootManager.getFileIndex(); - for (VirtualFile root : rootManager.getContentRoots()) { - index.iterateContentUnderDirectory(root, new ContentIterator() { - @Override - public boolean processFile(final VirtualFile fileOrDir) { - applyPushersToFile(fileOrDir, pushers, moduleValues); - return true; - } - }); - } - } - if (indicator != null) { - indicator.popState(); - } - + }); } - private void applyPushersToFile(VirtualFile fileOrDir, FilePropertyPusher[] pushers, Object[] moduleValues) { + private void doPushAll(final FilePropertyPusher[] pushers) { + Module[] modules = ApplicationManager.getApplication().runReadAction(new Computable() { + @Override + public Module[] compute() { + return ModuleManager.getInstance(myProject).getModules(); + } + }); + + for (final Module module : modules) { + Runnable iteration = ApplicationManager.getApplication().runReadAction(new Computable() { + @Override + public Runnable compute() { + if (module.isDisposed()) return EmptyRunnable.INSTANCE; + ProgressManager.checkCanceled(); + + final Object[] moduleValues = new Object[pushers.length]; + for (int i = 0; i < moduleValues.length; i++) { + moduleValues[i] = pushers[i].getImmediateValue(module); + } + + final ModuleFileIndex fileIndex = ModuleRootManager.getInstance(module).getFileIndex(); + return new Runnable() { + @Override + public void run() { + fileIndex.iterateContent(new ContentIterator() { + @Override + public boolean processFile(final VirtualFile fileOrDir) { + applyPushersToFile(fileOrDir, pushers, moduleValues); + return true; + } + }); + } + }; + } + }); + iteration.run(); + } + } + + private void applyPushersToFile(final VirtualFile fileOrDir, final FilePropertyPusher[] pushers, final Object[] moduleValues) { + ApplicationManager.getApplication().runReadAction(new Runnable() { + @Override + public void run() { + ProgressManager.checkCanceled(); + if (!fileOrDir.isValid()) return; + doApplyPushersToFile(fileOrDir, pushers, moduleValues); + } + }); + } + private void doApplyPushersToFile(VirtualFile fileOrDir, FilePropertyPusher[] pushers, Object[] moduleValues) { FilePropertyPusher pusher = null; try { final boolean isDir = fileOrDir.isDirectory(); for (int i = 0, pushersLength = pushers.length; i < pushersLength; i++) { + //noinspection unchecked pusher = pushers[i]; if (!isDir && (pusher.pushDirectoriesOnly() || !pusher.acceptsFile(fileOrDir))) continue; else if (isDir && !pusher.acceptsDirectory(fileOrDir, myProject)) continue; From c8e27bf3d361fcd49e2e1819ca37fd304d980b57 Mon Sep 17 00:00:00 2001 From: "Maxim.Mossienko" Date: Thu, 10 Apr 2014 23:15:04 +0200 Subject: [PATCH 18/21] ability to store PHM in more compact format --- .../intellij/util/io/PersistentMapTest.java | 22 ++++++++++++++--- .../intellij/util/io/PersistentHashMap.java | 24 ++++++++++++++++--- 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/platform/platform-tests/testSrc/com/intellij/util/io/PersistentMapTest.java b/platform/platform-tests/testSrc/com/intellij/util/io/PersistentMapTest.java index 957e9abd4453..4dcd4da5aa87 100644 --- a/platform/platform-tests/testSrc/com/intellij/util/io/PersistentMapTest.java +++ b/platform/platform-tests/testSrc/com/intellij/util/io/PersistentMapTest.java @@ -440,14 +440,25 @@ public class PersistentMapTest extends TestCase { TIntIntHashMap checkMap = new TIntIntHashMap(size); Random r = new Random(1); while(size != checkMap.size()) { - checkMap.put(r.nextInt(), size == 0 ? 0 : r.nextInt()); + if (checkMap.size() == 0) { + checkMap.put(r.nextInt(), 0); + checkMap.put(r.nextInt(), 0); + checkMap.put(0, r.nextInt()); + } else { + checkMap.put(r.nextInt(), r.nextInt()); + } } long started = System.currentTimeMillis(); PersistentHashMap map = null; try { - map = new PersistentHashMap(file, EnumeratorIntegerDescriptor.INSTANCE, EnumeratorIntegerDescriptor.INSTANCE); + map = new PersistentHashMap(file, EnumeratorIntegerDescriptor.INSTANCE, EnumeratorIntegerDescriptor.INSTANCE) { + @Override + protected boolean wantCompactIntegralValues() { + return true; + } + }; final PersistentHashMap mapFinal = map; boolean result = checkMap.forEachEntry(new TIntIntProcedure() { @@ -467,7 +478,12 @@ public class PersistentMapTest extends TestCase { map.close(); System.out.println("Done:"+(System.currentTimeMillis() - started)); started = System.currentTimeMillis(); - map = new PersistentHashMap(file, EnumeratorIntegerDescriptor.INSTANCE, EnumeratorIntegerDescriptor.INSTANCE); + map = new PersistentHashMap(file, EnumeratorIntegerDescriptor.INSTANCE, EnumeratorIntegerDescriptor.INSTANCE) { + @Override + protected boolean wantCompactIntegralValues() { + return true; + } + }; final PersistentHashMap mapFinal2 = map; result = checkMap.forEachEntry(new TIntIntProcedure() { @Override diff --git a/platform/util/src/com/intellij/util/io/PersistentHashMap.java b/platform/util/src/com/intellij/util/io/PersistentHashMap.java index 7defd2974edf..00a10d958bbf 100644 --- a/platform/util/src/com/intellij/util/io/PersistentHashMap.java +++ b/platform/util/src/com/intellij/util/io/PersistentHashMap.java @@ -62,6 +62,7 @@ public class PersistentHashMap extends PersistentEnumeratorDelegate< private final int myParentValueRefOffset; @NotNull private final byte[] myRecordBuffer; @NotNull private final byte[] mySmallRecordBuffer; + private final boolean myIntMapping; private final boolean myCanReEnumerate; private int myLargeIndexWatermarkId; // starting with this id we store offset in adjacent file in long format private boolean myIntAddressForNewRecord; @@ -112,6 +113,8 @@ public class PersistentHashMap extends PersistentEnumeratorDelegate< myAppendCache = createAppendCache(keyDescriptor); final PersistentEnumeratorBase.RecordBufferHandler recordHandler = myEnumerator.getRecordHandler(); myParentValueRefOffset = recordHandler.getRecordBuffer(myEnumerator).length; + myIntMapping = valueExternalizer instanceof IntInlineKeyDescriptor && wantCompactIntegralValues(); + myRecordBuffer = new byte[myParentValueRefOffset + 8]; mySmallRecordBuffer = new byte[myParentValueRefOffset + 4]; @@ -180,6 +183,10 @@ public class PersistentHashMap extends PersistentEnumeratorDelegate< } } + protected boolean wantCompactIntegralValues() { + return false; + } + private SLRUCache createAppendCache(final KeyDescriptor keyDescriptor) { final EqualityPolicy hashingStrategy = new EqualityPolicy() { @Override @@ -306,14 +313,18 @@ public class PersistentHashMap extends PersistentEnumeratorDelegate< myEnumerator.markDirty(true); myAppendCache.remove(key); + final int id = enumerate(key); + if (myIntMapping) { + myEnumerator.myStorage.putInt(id + myParentValueRefOffset, (Integer)value); + return; + } + final BufferExposingByteArrayOutputStream bytes = new BufferExposingByteArrayOutputStream(); AppendStream appenderStream = ourFlyweightAppenderStream.getValue(); appenderStream.setOut(bytes); myValueExternalizer.save(appenderStream, value); appenderStream.setOut(null); - final int id = enumerate(key); - long oldheader = readValueId(id); if (oldheader != NULL_ADDR) { myLiveAndGarbageKeysCounter++; @@ -357,6 +368,7 @@ public class PersistentHashMap extends PersistentEnumeratorDelegate< }; protected void doAppendData(Key key, @NotNull ValueDataAppender appender) throws IOException { + assert !myIntMapping; myEnumerator.markDirty(true); AppendStream appenderStream = ourFlyweightAppenderStream.getValue(); @@ -413,11 +425,15 @@ public class PersistentHashMap extends PersistentEnumeratorDelegate< if (id == PersistentEnumerator.NULL_ID) { return null; } + + if (myIntMapping) { + return (Value)(Integer)myEnumerator.myStorage.getInt(id + myParentValueRefOffset); + } + final long oldHeader = readValueId(id); if (oldHeader == PersistentEnumerator.NULL_ID) { return null; } - PersistentHashMapValueStorage.ReadResult readResult = myValueStorage.readBytes(oldHeader); if (readResult.offset != oldHeader) { myEnumerator.markDirty(true); @@ -454,6 +470,7 @@ public class PersistentHashMap extends PersistentEnumeratorDelegate< if (id == PersistentEnumerator.NULL_ID) { return false; } + if(myIntMapping) return true; return readValueId(id) != NULL_ADDR; } finally { @@ -475,6 +492,7 @@ public class PersistentHashMap extends PersistentEnumeratorDelegate< if (id == PersistentEnumerator.NULL_ID) { return; } + assert !myIntMapping; // removal isn't supported myEnumerator.markDirty(true); final long record = readValueId(id); From 458ffce3e6a4d825f7f65e9c15e8e400c8e3f88d Mon Sep 17 00:00:00 2001 From: Max Medvedev Date: Thu, 10 Apr 2014 18:40:10 +0400 Subject: [PATCH 19/21] known recursion manager prototype --- plugins/groovy/src/META-INF/plugin.xml | 3 + .../lang/psi/impl/GroovyPsiManager.java | 6 +- .../blocks/GrClosableBlockImpl.java | 9 ++- .../statements/params/GrParameterImpl.java | 9 ++- .../lang/resolve/KnownRecursionManager.java | 77 +++++++++++++++++++ 5 files changed, 101 insertions(+), 3 deletions(-) create mode 100644 plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/KnownRecursionManager.java diff --git a/plugins/groovy/src/META-INF/plugin.xml b/plugins/groovy/src/META-INF/plugin.xml index dbf606cb014a..0677ce7e72cf 100644 --- a/plugins/groovy/src/META-INF/plugin.xml +++ b/plugins/groovy/src/META-INF/plugin.xml @@ -540,6 +540,9 @@ + + diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GroovyPsiManager.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GroovyPsiManager.java index bb8e0c33488e..8804c7459000 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GroovyPsiManager.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GroovyPsiManager.java @@ -209,11 +209,15 @@ public class GroovyPsiManager { } } if (!type.isValid()) { - LOG.error("Type is invalid: " + type + "; element: " + element + " of class " + element.getClass()); + error(element, type); } return UNKNOWN_TYPE == type ? null : type; } + private static void error(PsiElement element, PsiType type) { + LOG.error("Type is invalid: " + type + "; element: " + element + " of class " + element.getClass()); + } + @Nullable public GrTypeDefinition getArrayClass(@NotNull PsiType type) { final String typeText = type.getCanonicalText(); diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/blocks/GrClosableBlockImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/blocks/GrClosableBlockImpl.java index dac07251e6c7..d77f5ec59d5c 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/blocks/GrClosableBlockImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/blocks/GrClosableBlockImpl.java @@ -17,6 +17,7 @@ package org.jetbrains.plugins.groovy.lang.psi.impl.statements.blocks; import com.intellij.lang.ASTNode; +import com.intellij.openapi.util.Computable; import com.intellij.psi.*; import com.intellij.psi.scope.PsiScopeProcessor; import com.intellij.psi.tree.IElementType; @@ -49,6 +50,7 @@ import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUt import org.jetbrains.plugins.groovy.lang.psi.impl.statements.params.GrParameterListImpl; import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.ClosureSyntheticParameter; import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrLightVariable; +import org.jetbrains.plugins.groovy.lang.resolve.KnownRecursionManager; import org.jetbrains.plugins.groovy.lang.resolve.MethodTypeInferencer; import org.jetbrains.plugins.groovy.lang.resolve.processors.ClassHint; import org.jetbrains.plugins.groovy.lang.resolve.processors.ResolverProcessor; @@ -335,7 +337,12 @@ public class GrClosableBlockImpl extends GrBlockImpl implements GrClosableBlock @Nullable public PsiType getReturnType() { - return TypeInferenceHelper.getCurrentContext().getExpressionType(this, ourTypesCalculator); + return KnownRecursionManager.getInstance().run(this, new Computable() { + @Override + public PsiType compute() { + return TypeInferenceHelper.getCurrentContext().getExpressionType(GrClosableBlockImpl.this, ourTypesCalculator); + } + }, getAllParameters()); } @Override diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/params/GrParameterImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/params/GrParameterImpl.java index f74185bbd5af..274b013371b9 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/params/GrParameterImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/params/GrParameterImpl.java @@ -17,6 +17,7 @@ package org.jetbrains.plugins.groovy.lang.psi.impl.statements.params; import com.intellij.lang.ASTNode; +import com.intellij.openapi.util.Computable; import com.intellij.psi.*; import com.intellij.psi.codeStyle.JavaCodeStyleManager; import com.intellij.psi.search.LocalSearchScope; @@ -48,6 +49,7 @@ import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUt import org.jetbrains.plugins.groovy.lang.psi.stubs.GrParameterStub; import org.jetbrains.plugins.groovy.lang.psi.typeEnhancers.ClosureParameterEnhancer; import org.jetbrains.plugins.groovy.lang.psi.typeEnhancers.GrVariableEnhancer; +import org.jetbrains.plugins.groovy.lang.resolve.KnownRecursionManager; /** * @author: Dmitry.Krasilschikov @@ -100,7 +102,12 @@ public class GrParameterImpl extends GrVariableBaseImpl impleme return TypesUtil.createTypeByFQClassName(CommonClassNames.JAVA_LANG_THROWABLE, this); } - return GrVariableEnhancer.getEnhancedType(this); + return KnownRecursionManager.getInstance().run(this, new Computable() { + @Override + public PsiType compute() { + return GrVariableEnhancer.getEnhancedType(GrParameterImpl.this); + } + }, getDeclarationScope()); } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/KnownRecursionManager.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/KnownRecursionManager.java new file mode 100644 index 000000000000..6542a315864a --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/KnownRecursionManager.java @@ -0,0 +1,77 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.plugins.groovy.lang.resolve; + +import com.intellij.openapi.components.ServiceManager; +import com.intellij.openapi.util.Computable; +import com.intellij.util.containers.ContainerUtil; + +import java.util.Set; + +/** + * Created by Max Medvedev on 10/04/14 + */ +public class KnownRecursionManager { + private final ThreadLocal myThreads = new ThreadLocal() { + @Override + protected ThreadInfo initialValue() { + return new ThreadInfo(); + } + }; + + public T run(Object key, Computable computable, Object... stopAt) { + try { + if (!startInference(key, stopAt)) { + return null; + } + return computable.compute(); + } + finally { + try { + finishInference(key); + } + catch (Throwable e) { + //noinspection ThrowFromFinallyBlock + throw new RuntimeException("exception in finishInference", e); + } + } + } + + private boolean startInference(Object key, Object[] stopAt) { + ThreadInfo info = myThreads.get(); + + for (Object o : stopAt) { + if (info.myObjects.contains(o)) return false; + } + if (!info.myObjects.add(key)) return false; + + return true; + } + + private void finishInference(Object key) { + ThreadInfo info = myThreads.get(); + + info.myObjects.remove(key); + } + + public static KnownRecursionManager getInstance() { + return ServiceManager.getService(KnownRecursionManager.class); + } + + private static class ThreadInfo { + private final Set myObjects = ContainerUtil.newLinkedHashSet(); + } +} From 397567d2c29fbe2fce13ac8e2da2029358ed3cd9 Mon Sep 17 00:00:00 2001 From: Max Medvedev Date: Fri, 11 Apr 2014 09:54:26 +0400 Subject: [PATCH 20/21] tuple and map types improved. Used VolatileNotNullLazyValue instead of AtomicNotNullLazyValue to avoid deadlock Got rid of GrTupleTypeWithLazyValue. All the functionality is moved to GrTupleType --- ...ypeImpl.java => GrImmediateTupleType.java} | 19 +++---- .../groovy/lang/psi/impl/GrMapType.java | 4 +- .../lang/psi/impl/GrMapTypeFromNamedArgs.java | 6 +- .../groovy/lang/psi/impl/GrTupleType.java | 27 ++++++++- .../impl/GrTupleTypeWithLazyComponents.java | 55 ------------------- .../psi/impl/auxiliary/GrListOrMapImpl.java | 5 +- .../statements/expressions/TypesUtil.java | 5 +- .../expressions/path/GrIndexPropertyImpl.java | 4 +- 8 files changed, 44 insertions(+), 81 deletions(-) rename plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/{GrTupleTypeImpl.java => GrImmediateTupleType.java} (65%) delete mode 100644 plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrTupleTypeWithLazyComponents.java diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrTupleTypeImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrImmediateTupleType.java similarity index 65% rename from plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrTupleTypeImpl.java rename to plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrImmediateTupleType.java index 0b7ffcd8f53c..fb20b7508f7c 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrTupleTypeImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrImmediateTupleType.java @@ -16,9 +16,7 @@ package org.jetbrains.plugins.groovy.lang.psi.impl; -import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.JavaPsiFacade; -import com.intellij.psi.PsiClassType; import com.intellij.psi.PsiType; import com.intellij.psi.search.GlobalSearchScope; import org.jetbrains.annotations.NotNull; @@ -26,15 +24,11 @@ import org.jetbrains.annotations.NotNull; /** * @author ven */ -public class GrTupleTypeImpl extends GrTupleType { +public class GrImmediateTupleType extends GrTupleType { private final PsiType[] myComponentTypes; - public GrTupleTypeImpl(PsiType[] componentTypes, JavaPsiFacade facade, GlobalSearchScope scope) { - this(componentTypes, facade, scope, LanguageLevel.JDK_1_5); - } - - public GrTupleTypeImpl(PsiType[] componentTypes, JavaPsiFacade facade, GlobalSearchScope scope, LanguageLevel languageLevel) { - super(scope, facade, languageLevel); + public GrImmediateTupleType(@NotNull PsiType[] componentTypes, @NotNull JavaPsiFacade facade, @NotNull GlobalSearchScope scope) { + super(scope, facade); myComponentTypes = componentTypes; } @@ -46,12 +40,13 @@ public class GrTupleTypeImpl extends GrTupleType { return true; } - @Override @NotNull - public PsiClassType setLanguageLevel(@NotNull final LanguageLevel languageLevel) { - return new GrTupleTypeImpl(myComponentTypes, myFacade, myScope, languageLevel); + @Override + protected PsiType[] inferComponents() { + return myComponentTypes; } + @NotNull @Override public PsiType[] getComponentTypes() { return myComponentTypes; diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrMapType.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrMapType.java index f4a43045041b..1f422261542e 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrMapType.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrMapType.java @@ -16,8 +16,8 @@ package org.jetbrains.plugins.groovy.lang.psi.impl; -import com.intellij.openapi.util.AtomicNotNullLazyValue; import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.VolatileNotNullLazyValue; import com.intellij.openapi.util.text.StringUtil; import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.JavaPsiFacade; @@ -41,7 +41,7 @@ import static org.jetbrains.plugins.groovy.lang.psi.util.GroovyCommonClassNames. public abstract class GrMapType extends GrLiteralClassType { private final String myJavaClassName; - private final AtomicNotNullLazyValue myParameters = new AtomicNotNullLazyValue() { + private final VolatileNotNullLazyValue myParameters = new VolatileNotNullLazyValue() { @NotNull @Override protected PsiType[] compute() { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrMapTypeFromNamedArgs.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrMapTypeFromNamedArgs.java index 16e94872a70d..8224f0a4dfe3 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrMapTypeFromNamedArgs.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrMapTypeFromNamedArgs.java @@ -15,8 +15,8 @@ */ package org.jetbrains.plugins.groovy.lang.psi.impl; -import com.intellij.openapi.util.AtomicNotNullLazyValue; import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.VolatileNotNullLazyValue; import com.intellij.psi.CommonClassNames; import com.intellij.psi.JavaPsiFacade; import com.intellij.psi.PsiElement; @@ -43,7 +43,7 @@ public class GrMapTypeFromNamedArgs extends GrMapType { private final Map myStringEntries; private final List> myOtherEntries; - private final AtomicNotNullLazyValue>> myTypesOfOtherEntries = new AtomicNotNullLazyValue>>() { + private final VolatileNotNullLazyValue>> myTypesOfOtherEntries = new VolatileNotNullLazyValue>>() { @NotNull @Override protected List> compute() { @@ -56,7 +56,7 @@ public class GrMapTypeFromNamedArgs extends GrMapType { } }; - private final AtomicNotNullLazyValue> myTypesOfStringEntries = new AtomicNotNullLazyValue>() { + private final VolatileNotNullLazyValue> myTypesOfStringEntries = new VolatileNotNullLazyValue>() { @NotNull @Override protected Map compute() { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrTupleType.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrTupleType.java index dde223d036a2..1faa47d9ed1e 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrTupleType.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrTupleType.java @@ -16,12 +16,13 @@ package org.jetbrains.plugins.groovy.lang.psi.impl; -import com.intellij.openapi.util.AtomicNotNullLazyValue; import com.intellij.openapi.util.Comparing; +import com.intellij.openapi.util.VolatileNotNullLazyValue; import com.intellij.openapi.util.text.StringUtil; import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.CommonClassNames; import com.intellij.psi.JavaPsiFacade; +import com.intellij.psi.PsiClassType; import com.intellij.psi.PsiType; import com.intellij.psi.search.GlobalSearchScope; import org.jetbrains.annotations.NotNull; @@ -31,7 +32,7 @@ import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUt * @author ven */ public abstract class GrTupleType extends GrLiteralClassType { - private final AtomicNotNullLazyValue myParameters = new AtomicNotNullLazyValue() { + private final VolatileNotNullLazyValue myParameters = new VolatileNotNullLazyValue() { @NotNull @Override protected PsiType[] compute() { @@ -43,6 +44,14 @@ public abstract class GrTupleType extends GrLiteralClassType { } }; + private final VolatileNotNullLazyValue myComponents = new VolatileNotNullLazyValue() { + @NotNull + @Override + protected PsiType[] compute() { + return inferComponents(); + } + }; + public GrTupleType(@NotNull GlobalSearchScope scope, @NotNull JavaPsiFacade facade) { this(scope, facade, LanguageLevel.JDK_1_5); } @@ -122,5 +131,17 @@ public abstract class GrTupleType extends GrLiteralClassType { return super.isAssignableFrom(type); } - public abstract PsiType[] getComponentTypes(); + @NotNull + public PsiType[] getComponentTypes() { + return myComponents.getValue(); + } + + @NotNull + protected abstract PsiType[] inferComponents(); + + @NotNull + @Override + public PsiClassType setLanguageLevel(@NotNull LanguageLevel languageLevel) { + return new GrImmediateTupleType(getComponentTypes(), myFacade, getResolveScope()); + } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrTupleTypeWithLazyComponents.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrTupleTypeWithLazyComponents.java deleted file mode 100644 index b2171ea2556d..000000000000 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrTupleTypeWithLazyComponents.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.plugins.groovy.lang.psi.impl; - -import com.intellij.openapi.util.AtomicNotNullLazyValue; -import com.intellij.pom.java.LanguageLevel; -import com.intellij.psi.JavaPsiFacade; -import com.intellij.psi.PsiClassType; -import com.intellij.psi.PsiType; -import com.intellij.psi.search.GlobalSearchScope; -import org.jetbrains.annotations.NotNull; - -/** - * Created by Max Medvedev on 06/04/14 - */ -public abstract class GrTupleTypeWithLazyComponents extends GrTupleType { - private final AtomicNotNullLazyValue myComponents = new AtomicNotNullLazyValue() { - @NotNull - @Override - protected PsiType[] compute() { - return inferComponents(); - } - }; - - protected abstract PsiType[] inferComponents(); - - public GrTupleTypeWithLazyComponents(@NotNull GlobalSearchScope scope, @NotNull JavaPsiFacade facade) { - super(scope, facade); - } - - @Override - public PsiType[] getComponentTypes() { - return myComponents.getValue(); - } - - @NotNull - @Override - public PsiClassType setLanguageLevel(@NotNull LanguageLevel languageLevel) { - return new GrTupleTypeImpl(getComponentTypes(), myFacade, getResolveScope(), languageLevel); - } - -} diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/auxiliary/GrListOrMapImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/auxiliary/GrListOrMapImpl.java index 79a502c1bd52..e330f21cae75 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/auxiliary/GrListOrMapImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/auxiliary/GrListOrMapImpl.java @@ -37,7 +37,7 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpres import org.jetbrains.plugins.groovy.lang.psi.api.types.GrTypeElement; import org.jetbrains.plugins.groovy.lang.psi.dataFlow.types.TypeInferenceHelper; import org.jetbrains.plugins.groovy.lang.psi.impl.GrMapType; -import org.jetbrains.plugins.groovy.lang.psi.impl.GrTupleTypeWithLazyComponents; +import org.jetbrains.plugins.groovy.lang.psi.impl.GrTupleType; import org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil; import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.GrExpressionImpl; import org.jetbrains.plugins.groovy.lang.psi.util.GroovyCommonClassNames; @@ -211,7 +211,8 @@ public class GrListOrMapImpl extends GrExpressionImpl implements GrListOrMap { } } - return new GrTupleTypeWithLazyComponents(scope, facade) { + return new GrTupleType(scope, facade) { + @NotNull @Override protected PsiType[] inferComponents() { return ContainerUtil.map(initializers, new Function() { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/TypesUtil.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/TypesUtil.java index d3c78c896e07..d9a5e7720964 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/TypesUtil.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/TypesUtil.java @@ -519,7 +519,7 @@ public class TypesUtil { components3[i] = getLeastUpperBound(c1, c2, manager); } } - return new GrTupleTypeImpl(components3, JavaPsiFacade.getInstance(manager.getProject()), tuple1.getScope().intersectWith(tuple2.getResolveScope())); + return new GrImmediateTupleType(components3, JavaPsiFacade.getInstance(manager.getProject()), tuple1.getScope().intersectWith(tuple2.getResolveScope())); } else if (checkEmptyListAndList(type1, type2)) { return genNewListBy(type2, manager); @@ -841,7 +841,8 @@ public class TypesUtil { } public static PsiType getTupleByAnnotationArrayInitializer(final GrAnnotationArrayInitializer value) { - return new GrTupleTypeWithLazyComponents(value.getResolveScope(), JavaPsiFacade.getInstance(value.getProject())) { + return new GrTupleType(value.getResolveScope(), JavaPsiFacade.getInstance(value.getProject())) { + @NotNull @Override protected PsiType[] inferComponents() { final GrAnnotationMemberValue[] initializers = value.getInitializers(); diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/path/GrIndexPropertyImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/path/GrIndexPropertyImpl.java index cdd41885bba9..4e867cedeaf1 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/path/GrIndexPropertyImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/path/GrIndexPropertyImpl.java @@ -37,8 +37,8 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrRefere import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrIndexProperty; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrGdkMethod; import org.jetbrains.plugins.groovy.lang.psi.dataFlow.types.TypeInferenceHelper; +import org.jetbrains.plugins.groovy.lang.psi.impl.GrImmediateTupleType; import org.jetbrains.plugins.groovy.lang.psi.impl.GrTupleType; -import org.jetbrains.plugins.groovy.lang.psi.impl.GrTupleTypeImpl; import org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil; import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.GrExpressionImpl; import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil; @@ -218,7 +218,7 @@ public class GrIndexPropertyImpl extends GrExpressionImpl implements GrIndexProp } if (candidates.length != 1) { - final GrTupleType tupleType = new GrTupleTypeImpl(argTypes, JavaPsiFacade.getInstance(getProject()), resolveScope); + final GrTupleType tupleType = new GrImmediateTupleType(argTypes, JavaPsiFacade.getInstance(getProject()), resolveScope); final GroovyResolveResult[] tupleCandidates = ResolveUtil.getMethodCandidates(thisType, name, invoked, tupleType); if (incompleteCode) { candidates = ArrayUtil.mergeArrays(candidates, tupleCandidates, new ArrayFactory() { From 09215e7c03180bdc3dd6021d54f094bf5f942f7d Mon Sep 17 00:00:00 2001 From: Dmitry Batrak Date: Fri, 11 Apr 2014 11:56:40 +0400 Subject: [PATCH 21/21] another attempt to reimplement IDEA-53663 previous attempts: 05a1415, 7261323 Current change is supposed to make Ctrl-Click not sensitive to 'micro-drags', i.e. mouse drags that don't actually introduce any selection --- .../src/com/intellij/ide/IdeEventQueue.java | 21 ++++++++- .../openapi/editor/impl/EditorImpl.java | 38 +++++++++++++--- .../keymap/impl/IdeMouseEventDispatcher.java | 44 +++++++++++-------- .../impl/IdeMouseEventDispatcherTest.java | 15 ++++--- 4 files changed, 88 insertions(+), 30 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/ide/IdeEventQueue.java b/platform/platform-impl/src/com/intellij/ide/IdeEventQueue.java index f41f4878eafb..207f30c8e925 100644 --- a/platform/platform-impl/src/com/intellij/ide/IdeEventQueue.java +++ b/platform/platform-impl/src/com/intellij/ide/IdeEventQueue.java @@ -833,8 +833,20 @@ public class IdeEventQueue extends EventQueue { return myKeyEventDispatcher; } + /** + * Same as {@link #blockNextEvents(java.awt.event.MouseEvent, com.intellij.ide.IdeEventQueue.BlockMode)} with blockMode equal + * to COMPLETE. + */ public void blockNextEvents(final MouseEvent e) { - myMouseEventDispatcher.blockNextEvents(e); + blockNextEvents(e, BlockMode.COMPLETE); + } + + /** + * When blockMode is COMPLETE, blocks following related mouse events completely, when blockMode is + * ACTIONS only blocks performing actions bound to corresponding mouse shortcuts. + */ + public void blockNextEvents(final MouseEvent e, BlockMode blockMode) { + myMouseEventDispatcher.blockNextEvents(e, blockMode); } public boolean isSuspendMode() { @@ -976,4 +988,11 @@ public class IdeEventQueue extends EventQueue { myFrequentEventDetector.eventHappened(); super.postEvent(theEvent); } + + /** + * @see com.intellij.ide.IdeEventQueue#blockNextEvents(java.awt.event.MouseEvent, com.intellij.ide.IdeEventQueue.BlockMode) + */ + public enum BlockMode { + COMPLETE, ACTIONS + } } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java index 85a187e3fe28..2b1da0c2b150 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java @@ -306,6 +306,8 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi private VisualPosition myTargetMultiSelectionPosition; private boolean myMultiSelectionInProgress; private boolean myLastPressCreatedCaret; + // Set when the selection (normal or block one) initiated by mouse drag becomes noticeable (at least one character is selected). + // Reset on mouse press event. private boolean myCurrentDragIsSubstantial; private CaretImpl myPrimaryCaret; @@ -4204,12 +4206,11 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi if (isColumnMode() || e.isAltDown()) { if (myCaretModel.supportsMultipleCarets()) { if (myLastMousePressedLocation != null && (myCurrentDragIsSubstantial || !newLogicalCaret.equals(myLastMousePressedLocation))) { - selectionModel.setBlockSelection(myLastMousePressedLocation, newLogicalCaret); - myCurrentDragIsSubstantial = true; + setBlockSelectionAndBlockActions(e, myLastMousePressedLocation, newLogicalCaret); } } else { final LogicalPosition blockStart = selectionModel.hasBlockSelection() ? selectionModel.getBlockStart() : oldLogicalCaret; - selectionModel.setBlockSelection(blockStart, getCaretModel().getLogicalPosition()); + setBlockSelectionAndBlockActions(e, blockStart, getCaretModel().getLogicalPosition()); } } else { @@ -4270,10 +4271,10 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi } } if (oldVisLeadSelectionStart != null) { - selectionModel.setSelection(oldVisLeadSelectionStart, oldSelectionStart, newVisualCaret, newCaretOffset); + setSelectionAndBlockActions(e, oldVisLeadSelectionStart, oldSelectionStart, newVisualCaret, newCaretOffset); } else { - selectionModel.setSelection(oldSelectionStart, newCaretOffset); + setSelectionAndBlockActions(e, oldSelectionStart, newCaretOffset); } cancelAutoResetForMouseSelectionState(); } @@ -4297,9 +4298,36 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi } else { myScrollingTimer.start(dx, dy); + onSubstantialDrag(e); } } + private void setSelectionAndBlockActions(@NotNull MouseEvent mouseDragEvent, int startOffset, int endOffset) { + mySelectionModel.setSelection(startOffset, endOffset); + if (myCurrentDragIsSubstantial || startOffset != endOffset) { + onSubstantialDrag(mouseDragEvent); + } + } + + private void setSelectionAndBlockActions(@NotNull MouseEvent mouseDragEvent, VisualPosition startPosition, int startOffset, VisualPosition endPosition, int endOffset) { + mySelectionModel.setSelection(startPosition, startOffset, endPosition, endOffset); + if (myCurrentDragIsSubstantial || startOffset != endOffset || !Comparing.equal(startPosition, endPosition)) { + onSubstantialDrag(mouseDragEvent); + } + } + + private void setBlockSelectionAndBlockActions(@NotNull MouseEvent mouseDragEvent, @NotNull LogicalPosition startPosition, @NotNull LogicalPosition endPosition) { + mySelectionModel.setBlockSelection(startPosition, endPosition); + if (myCurrentDragIsSubstantial || !startPosition.equals(endPosition)) { + onSubstantialDrag(mouseDragEvent); + } + } + + private void onSubstantialDrag(@NotNull MouseEvent mouseDragEvent) { + IdeEventQueue.getInstance().blockNextEvents(mouseDragEvent, IdeEventQueue.BlockMode.ACTIONS); + myCurrentDragIsSubstantial = true; + } + private static class RepaintCursorCommand implements Runnable { private long mySleepTime = 500; private boolean myIsBlinkCaret = true; diff --git a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/IdeMouseEventDispatcher.java b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/IdeMouseEventDispatcher.java index 94f72f6da840..6c78ec12f9df 100644 --- a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/IdeMouseEventDispatcher.java +++ b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/IdeMouseEventDispatcher.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2014 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. @@ -17,6 +17,7 @@ package com.intellij.openapi.keymap.impl; import com.intellij.featureStatistics.FeatureUsageTracker; import com.intellij.ide.DataManager; +import com.intellij.ide.IdeEventQueue; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.actionSystem.ex.ActionManagerEx; import com.intellij.openapi.actionSystem.ex.ActionUtil; @@ -55,9 +56,8 @@ import static java.awt.event.MouseEvent.*; public final class IdeMouseEventDispatcher { private final PresentationFactory myPresentationFactory = new PresentationFactory(); private final ArrayList myActions = new ArrayList(1); - private final Map myRootPane2BlockedId = new HashMap(); + private final Map myRootPane2BlockedId = new HashMap(); private int myLastHorScrolledComponentHash = 0; - private MouseEvent myPreviousMouseEvent; // Don't compare MouseEvent ids. Swing has wrong sequence of events: first is mouse_clicked(500) // then mouse_pressed(501), mouse_released(502) etc. Here, mouse events sorted so we can compare @@ -122,9 +122,6 @@ public final class IdeMouseEventDispatcher { * to normal event dispatching. */ public boolean dispatchMouseEvent(MouseEvent e) { - MouseEvent previousEvent = myPreviousMouseEvent; - myPreviousMouseEvent = e; - Component c = e.getComponent(); //frame activation by mouse click @@ -160,17 +157,18 @@ public final class IdeMouseEventDispatcher { ignore = true; } - if (e.getID() == MOUSE_RELEASED && previousEvent != null && previousEvent.getID() == MOUSE_DRAGGED) { - ignore = true; // we don't want to process action bindings on mouse release at the end of drag operation - } - - final JRootPane root = findRoot(e); + final JRootPane root = findRoot(e); if (root != null) { - final Integer lastId = myRootPane2BlockedId.get(root); - if (lastId != null) { - if (SWING_EVENTS_PRIORITY.indexOf(lastId) < SWING_EVENTS_PRIORITY.indexOf(e.getID())) { - myRootPane2BlockedId.put(root, e.getID()); - return true; + BlockState blockState = myRootPane2BlockedId.get(root); + if (blockState != null) { + if (SWING_EVENTS_PRIORITY.indexOf(blockState.currentEventId) < SWING_EVENTS_PRIORITY.indexOf(e.getID())) { + blockState.currentEventId = e.getID(); + if (blockState.blockMode == IdeEventQueue.BlockMode.COMPLETE) { + return true; + } + else { + ignore = true; + } } else { myRootPane2BlockedId.remove(root); } @@ -322,11 +320,11 @@ public final class IdeMouseEventDispatcher { return c != null && "y.view.Graph2DView".equals(c.getClass().getName()); } - public void blockNextEvents(final MouseEvent e) { + public void blockNextEvents(final MouseEvent e, IdeEventQueue.BlockMode blockMode) { final JRootPane root = findRoot(e); if (root == null) return; - myRootPane2BlockedId.put(root, e.getID()); + myRootPane2BlockedId.put(root, new BlockState(e.getID(), blockMode)); } @Nullable @@ -346,4 +344,14 @@ public final class IdeMouseEventDispatcher { return root; } + + private static class BlockState { + private int currentEventId; + private final IdeEventQueue.BlockMode blockMode; + + private BlockState(int id, IdeEventQueue.BlockMode mode) { + currentEventId = id; + blockMode = mode; + } + } } diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/keymap/impl/IdeMouseEventDispatcherTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/keymap/impl/IdeMouseEventDispatcherTest.java index f411c4be6456..352fa5aeb5bd 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/keymap/impl/IdeMouseEventDispatcherTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/keymap/impl/IdeMouseEventDispatcherTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2014 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. @@ -15,6 +15,7 @@ */ package com.intellij.openapi.keymap.impl; +import com.intellij.ide.IdeEventQueue; import com.intellij.openapi.actionSystem.ActionManager; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; @@ -24,7 +25,6 @@ import com.intellij.openapi.keymap.ex.KeymapManagerEx; import com.intellij.testFramework.LightPlatformTestCase; import javax.swing.*; -import java.awt.*; import java.awt.event.MouseEvent; public class IdeMouseEventDispatcherTest extends LightPlatformTestCase { @@ -34,7 +34,7 @@ public class IdeMouseEventDispatcherTest extends LightPlatformTestCase { private KeymapImpl keymap; private Keymap mySavedKeymap; - private Component myEventSource; + private JFrame myEventSource; private int myActionExecutionCount; public void setUp() throws Exception { @@ -49,12 +49,13 @@ public class IdeMouseEventDispatcherTest extends LightPlatformTestCase { mySavedKeymap = KeymapManagerEx.getInstanceEx().getActiveKeymap(); KeymapManagerEx.getInstanceEx().setActiveKeymap(keymap); - myEventSource = new JPanel(); + myEventSource = new JFrame(); myEventSource.setSize(1,1); } @Override public void tearDown() throws Exception { + myEventSource.dispose(); KeymapManagerEx.getInstanceEx().getSchemesManager().removeScheme(keymap); KeymapManagerEx.getInstanceEx().setActiveKeymap(mySavedKeymap); ActionManager.getInstance().unregisterAction(OUR_TEST_ACTION); @@ -70,11 +71,13 @@ public class IdeMouseEventDispatcherTest extends LightPlatformTestCase { assertEquals(1, myActionExecutionCount); } - public void testActionSuppressionAfterDrag() throws Exception { + public void testActionBlocking() throws Exception { IdeMouseEventDispatcher dispatcher = new IdeMouseEventDispatcher(); assertFalse(dispatcher.dispatchMouseEvent(new MouseEvent(myEventSource, MouseEvent.MOUSE_PRESSED, 0, 0, 0, 0, 1, false, MouseEvent.BUTTON2))); - assertFalse(dispatcher.dispatchMouseEvent(new MouseEvent(myEventSource, MouseEvent.MOUSE_DRAGGED, 0, 0, 0, 0, 0, false, MouseEvent.BUTTON2))); + MouseEvent dragEvent = new MouseEvent(myEventSource, MouseEvent.MOUSE_DRAGGED, 0, 0, 0, 0, 0, false, MouseEvent.BUTTON2); + assertFalse(dispatcher.dispatchMouseEvent(dragEvent)); + dispatcher.blockNextEvents(dragEvent, IdeEventQueue.BlockMode.ACTIONS); assertFalse(dispatcher.dispatchMouseEvent(new MouseEvent(myEventSource, MouseEvent.MOUSE_RELEASED, 0, 0, 0, 0, 1, false, MouseEvent.BUTTON2))); assertEquals(0, myActionExecutionCount); }