From 323727f189734651de49cc8737bfaa0cdf8b64e4 Mon Sep 17 00:00:00 2001 From: Eugene Kudelevsky Date: Mon, 11 Apr 2011 18:47:47 +0400 Subject: [PATCH 01/30] IDEA-67886 IDEA-67758 map help ids --- .../jetbrains/android/actions/NewAndroidComponentDialog.java | 5 +++++ .../android/compiler/AndroidDexCompilerSettingsFactory.java | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/plugins/android/src/org/jetbrains/android/actions/NewAndroidComponentDialog.java b/plugins/android/src/org/jetbrains/android/actions/NewAndroidComponentDialog.java index aa3cbd09b151..701629831d0c 100644 --- a/plugins/android/src/org/jetbrains/android/actions/NewAndroidComponentDialog.java +++ b/plugins/android/src/org/jetbrains/android/actions/NewAndroidComponentDialog.java @@ -216,6 +216,11 @@ public class NewAndroidComponentDialog extends DialogWrapper { return myNameField; } + @Override + protected String getHelpId() { + return "reference.new.android.component"; + } + @Override protected JComponent createCenterPanel() { return myPanel; diff --git a/plugins/android/src/org/jetbrains/android/compiler/AndroidDexCompilerSettingsFactory.java b/plugins/android/src/org/jetbrains/android/compiler/AndroidDexCompilerSettingsFactory.java index d3acbcb3eaf9..4afcfc11a93c 100644 --- a/plugins/android/src/org/jetbrains/android/compiler/AndroidDexCompilerSettingsFactory.java +++ b/plugins/android/src/org/jetbrains/android/compiler/AndroidDexCompilerSettingsFactory.java @@ -63,7 +63,7 @@ public class AndroidDexCompilerSettingsFactory implements CompilerSettingsFactor @Override public String getHelpTopic() { - return null; + return "settings.android.dx.compiler"; } @Override From 1c2b3236d2e17555422e0d50436296cbddf0ab76 Mon Sep 17 00:00:00 2001 From: Eugene Kudelevsky Date: Mon, 11 Apr 2011 18:59:18 +0400 Subject: [PATCH 02/30] fix manageSpaceActivity attribute --- .../org/jetbrains/android/dom/manifest/Application.java | 2 ++ .../android/testData/dom/manifest/ManageSpaceActivity.xml | 7 +++++++ .../testData/dom/manifest/ManageSpaceActivity_after.xml | 7 +++++++ .../org/jetbrains/android/dom/AndroidManifestDomTest.java | 5 +++++ 4 files changed, 21 insertions(+) create mode 100644 plugins/android/testData/dom/manifest/ManageSpaceActivity.xml create mode 100644 plugins/android/testData/dom/manifest/ManageSpaceActivity_after.xml diff --git a/plugins/android/src/org/jetbrains/android/dom/manifest/Application.java b/plugins/android/src/org/jetbrains/android/dom/manifest/Application.java index c0c00735cf2b..299637052793 100644 --- a/plugins/android/src/org/jetbrains/android/dom/manifest/Application.java +++ b/plugins/android/src/org/jetbrains/android/dom/manifest/Application.java @@ -16,6 +16,7 @@ package org.jetbrains.android.dom.manifest; import com.intellij.psi.PsiClass; +import com.intellij.util.xml.Attribute; import com.intellij.util.xml.Convert; import com.intellij.util.xml.ExtendClass; import org.jetbrains.android.dom.AndroidAttributeValue; @@ -53,6 +54,7 @@ public interface Application extends ManifestElement { @Convert(PackageClassConverter.class) @ExtendClass("android.app.Activity") + @Attribute("manageSpaceActivity") AndroidAttributeValue getManageSpaceActivity(); @Convert(PackageClassConverter.class) diff --git a/plugins/android/testData/dom/manifest/ManageSpaceActivity.xml b/plugins/android/testData/dom/manifest/ManageSpaceActivity.xml new file mode 100644 index 000000000000..484ed7c56704 --- /dev/null +++ b/plugins/android/testData/dom/manifest/ManageSpaceActivity.xml @@ -0,0 +1,7 @@ + + + + + + diff --git a/plugins/android/testData/dom/manifest/ManageSpaceActivity_after.xml b/plugins/android/testData/dom/manifest/ManageSpaceActivity_after.xml new file mode 100644 index 000000000000..7dbec8354eec --- /dev/null +++ b/plugins/android/testData/dom/manifest/ManageSpaceActivity_after.xml @@ -0,0 +1,7 @@ + + + + + + diff --git a/plugins/android/testSrc/org/jetbrains/android/dom/AndroidManifestDomTest.java b/plugins/android/testSrc/org/jetbrains/android/dom/AndroidManifestDomTest.java index ee773714e680..ddd54fe706e5 100644 --- a/plugins/android/testSrc/org/jetbrains/android/dom/AndroidManifestDomTest.java +++ b/plugins/android/testSrc/org/jetbrains/android/dom/AndroidManifestDomTest.java @@ -150,6 +150,11 @@ public class AndroidManifestDomTest extends AndroidDomTest { doTestCompletion(); } + public void testManageSpaceActivity() throws Throwable { + copyFileToProject("MyActivity.java", "src/p1/p2/MyActivity.java"); + doTestCompletion(); + } + private void doTestCompletion() throws Throwable { toTestCompletion(getTestName(false) + ".xml", getTestName(false) + "_after.xml"); } From 9ab67a792f708b8424ad8ab609f6c9aad22c4dbf Mon Sep 17 00:00:00 2001 From: Eugene Kudelevsky Date: Mon, 11 Apr 2011 19:36:44 +0400 Subject: [PATCH 03/30] try to suggest Android SDK path, if ANDROID_HOME is not set --- .../android/maven/AndroidFacetImporter.java | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/plugins/android/src/org/jetbrains/android/maven/AndroidFacetImporter.java b/plugins/android/src/org/jetbrains/android/maven/AndroidFacetImporter.java index 8600fb1f9340..7a85ef242625 100644 --- a/plugins/android/src/org/jetbrains/android/maven/AndroidFacetImporter.java +++ b/plugins/android/src/org/jetbrains/android/maven/AndroidFacetImporter.java @@ -18,7 +18,9 @@ package org.jetbrains.android.maven; import com.android.sdklib.IAndroidTarget; import com.intellij.facet.FacetType; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.module.Module; +import com.intellij.openapi.projectRoots.ProjectJdkTable; import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.roots.ModifiableRootModel; import com.intellij.openapi.roots.ModuleRootManager; @@ -34,6 +36,7 @@ import org.jetbrains.android.facet.AndroidFacetConfiguration; import org.jetbrains.android.facet.AndroidFacetType; import org.jetbrains.android.facet.AndroidRootUtil; import org.jetbrains.android.sdk.AndroidSdk; +import org.jetbrains.android.sdk.AndroidSdkType; import org.jetbrains.android.sdk.AndroidSdkUtils; import org.jetbrains.android.sdk.EmptySdkLog; import org.jetbrains.android.util.AndroidUtils; @@ -55,6 +58,8 @@ import java.util.Map; * @author Eugene.Kudelevsky */ public class AndroidFacetImporter extends FacetImporter { + private static final Logger LOG = Logger.getInstance("#org.jetbrains.android.maven.AndroidFacetImporter"); + public AndroidFacetImporter() { super("com.jayway.maven.plugins.android.generation2", "maven-android-plugin", FacetType.findInstance(AndroidFacetType.class), "Android"); } @@ -116,6 +121,13 @@ public class AndroidFacetImporter extends FacetImporter androidSdks = ProjectJdkTable.getInstance().getSdksOfType(AndroidSdkType.getInstance()); + for (Sdk androidSdk : androidSdks) { + final VirtualFile sdkHome = androidSdk.getHomeDirectory(); + + if (sdkHome != null && sdkHome.exists() && sdkHome.isValid() && sdkHome.isDirectory()) { + return sdkHome.getPath(); + } + } + + return null; + } + private void configurePaths(AndroidFacet facet, MavenProject project) { Module module = facet.getModule(); String moduleDirPath = AndroidRootUtil.getModuleDirPath(module); From 0cbf29c51a5335e2bc6921268df1839078b49872 Mon Sep 17 00:00:00 2001 From: Eugene Kudelevsky Date: Tue, 12 Apr 2011 19:44:20 +0400 Subject: [PATCH 04/30] fix exception when manifest is out of module --- .../dom/converters/ConstantFieldConverter.java | 8 ++++++-- .../dom/converters/PackageClassConverter.java | 13 ++++++++++--- .../dom/manifest/ManifestDomFileDescription.java | 4 +++- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/plugins/android/src/org/jetbrains/android/dom/converters/ConstantFieldConverter.java b/plugins/android/src/org/jetbrains/android/dom/converters/ConstantFieldConverter.java index 8dae75193224..911bc8d77ee3 100644 --- a/plugins/android/src/org/jetbrains/android/dom/converters/ConstantFieldConverter.java +++ b/plugins/android/src/org/jetbrains/android/dom/converters/ConstantFieldConverter.java @@ -15,6 +15,7 @@ */ package org.jetbrains.android.dom.converters; +import com.intellij.openapi.module.Module; import com.intellij.psi.*; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.util.xml.ConvertContext; @@ -41,8 +42,11 @@ public class ConstantFieldConverter extends ResolvingConverter { LookupClass lookupClass = element.getAnnotation(LookupClass.class); LookupPrefix lookupPrefix = element.getAnnotation(LookupPrefix.class); if (lookupClass != null && lookupPrefix != null) { - PsiClass psiClass = JavaPsiFacade.getInstance(context.getPsiManager().getProject()).findClass(lookupClass.value(), - GlobalSearchScope.allScope(context.getModule().getProject())); + final Module module = context.getModule(); + final GlobalSearchScope scope = module != null ? + GlobalSearchScope.allScope(module.getProject()) : + context.getInvocationElement().getResolveScope(); + PsiClass psiClass = JavaPsiFacade.getInstance(context.getPsiManager().getProject()).findClass(lookupClass.value(), scope); if (psiClass != null) { PsiField[] psiFields = psiClass.getFields(); for(PsiField field: psiFields) { diff --git a/plugins/android/src/org/jetbrains/android/dom/converters/PackageClassConverter.java b/plugins/android/src/org/jetbrains/android/dom/converters/PackageClassConverter.java index 61bac36b8730..1455df54150b 100644 --- a/plugins/android/src/org/jetbrains/android/dom/converters/PackageClassConverter.java +++ b/plugins/android/src/org/jetbrains/android/dom/converters/PackageClassConverter.java @@ -77,7 +77,10 @@ public class PackageClassConverter extends ResolvingConverter implemen className = packageName + "." + s; } JavaPsiFacade facade = JavaPsiFacade.getInstance(context.getPsiManager().getProject()); - GlobalSearchScope scope = GlobalSearchScope.moduleWithDependenciesAndLibrariesScope(context.getModule()); + final Module module = context.getModule(); + GlobalSearchScope scope = module != null + ? GlobalSearchScope.moduleWithDependenciesAndLibrariesScope(module) + : context.getInvocationElement().getResolveScope(); PsiClass psiClass = facade.findClass(className, scope); if (psiClass == null) { psiClass = facade.findClass(s, scope); @@ -264,7 +267,9 @@ public class PackageClassConverter extends ResolvingConverter implemen if (!myStartsWithPoint) { final PsiElement element = myIsPackage ? facade.findPackage(value) : - facade.findClass(value, myModule.getModuleWithDependenciesScope()); + facade.findClass(value, myModule != null + ? myModule.getModuleWithDependenciesScope() + : myElement.getResolveScope()); if (element != null) { return element; @@ -275,7 +280,9 @@ public class PackageClassConverter extends ResolvingConverter implemen if (relativeName != null) { return myIsPackage ? facade.findPackage(relativeName) : - facade.findClass(relativeName, myModule.getModuleWithDependenciesScope()); + facade.findClass(relativeName, myModule != null + ? myModule.getModuleWithDependenciesScope() + : myElement.getResolveScope()); } return null; } diff --git a/plugins/android/src/org/jetbrains/android/dom/manifest/ManifestDomFileDescription.java b/plugins/android/src/org/jetbrains/android/dom/manifest/ManifestDomFileDescription.java index 03461b9631e4..acea0d6bb5f5 100644 --- a/plugins/android/src/org/jetbrains/android/dom/manifest/ManifestDomFileDescription.java +++ b/plugins/android/src/org/jetbrains/android/dom/manifest/ManifestDomFileDescription.java @@ -17,6 +17,7 @@ package org.jetbrains.android.dom.manifest; import com.android.sdklib.SdkConstants; import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleUtil; import com.intellij.psi.xml.XmlFile; import com.intellij.util.xml.DomFileDescription; import org.jetbrains.android.facet.AndroidFacet; @@ -40,7 +41,8 @@ public class ManifestDomFileDescription extends DomFileDescription { if (!file.getName().equals(SdkConstants.FN_ANDROID_MANIFEST_XML)) { return false; } - return AndroidFacet.getInstance(file) != null; + final Module module = ModuleUtil.findModuleForPsiElement(file); + return module == null || AndroidFacet.getInstance(module) != null; } protected void initializeFileDescription() { From efa71142dbbd1d19551dc61b6b8d63341ee18c93 Mon Sep 17 00:00:00 2001 From: Eugene Kudelevsky Date: Tue, 12 Apr 2011 19:44:57 +0400 Subject: [PATCH 05/30] do not check debuggable option anymore --- .../run/AndroidRunConfigurationBase.java | 44 +------------------ 1 file changed, 2 insertions(+), 42 deletions(-) diff --git a/plugins/android/src/org/jetbrains/android/run/AndroidRunConfigurationBase.java b/plugins/android/src/org/jetbrains/android/run/AndroidRunConfigurationBase.java index f761e33f4230..764438e8314a 100644 --- a/plugins/android/src/org/jetbrains/android/run/AndroidRunConfigurationBase.java +++ b/plugins/android/src/org/jetbrains/android/run/AndroidRunConfigurationBase.java @@ -27,7 +27,6 @@ import com.intellij.execution.executors.DefaultDebugExecutor; import com.intellij.execution.runners.ExecutionEnvironment; import com.intellij.execution.ui.ConsoleView; import com.intellij.ide.util.PropertiesComponent; -import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleManager; import com.intellij.openapi.project.Project; @@ -44,10 +43,8 @@ import com.intellij.psi.xml.XmlAttributeValue; import com.intellij.util.PsiNavigateUtil; import com.intellij.util.containers.HashMap; import com.intellij.util.xml.GenericAttributeValue; -import com.intellij.util.xml.converters.values.BooleanValueConverter; import org.jdom.Element; import org.jetbrains.android.actions.AndroidEnableDdmsAction; -import org.jetbrains.android.dom.manifest.Application; import org.jetbrains.android.dom.manifest.Manifest; import org.jetbrains.android.facet.AndroidFacet; import org.jetbrains.android.facet.AndroidFacetConfiguration; @@ -171,14 +168,14 @@ public abstract class AndroidRunConfigurationBase extends ModuleBasedConfigurati return true; } - private static boolean containsRealDevice(@NotNull IDevice[] devices) { + /*private static boolean containsRealDevice(@NotNull IDevice[] devices) { for (IDevice device : devices) { if (!device.isEmulator()) { return true; } } return false; - } + }*/ public RunProfileState getState(@NotNull final Executor executor, @NotNull ExecutionEnvironment env) throws ExecutionException { final Module module = getConfigurationModule().getModule(); @@ -207,11 +204,6 @@ public abstract class AndroidRunConfigurationBase extends ModuleBasedConfigurati if (!activateDdmsIfNeccessary(facet)) { return null; } - if (!CHOOSE_DEVICE_MANUALLY && PREFERRED_AVD.length() == 0) { - if (!checkDebuggableOption(facet)) { - return null; - } - } } String aPackage = getPackageName(facet); @@ -224,11 +216,6 @@ public abstract class AndroidRunConfigurationBase extends ModuleBasedConfigurati if (CHOOSE_DEVICE_MANUALLY) { IDevice[] devices = chooseDevicesManually(facet); if (devices.length > 0) { - if (debug && containsRealDevice(devices)) { - if (!checkDebuggableOption(facet)) { - return null; - } - } targetDevices = devices; PropertiesComponent.getInstance(getProject()).setValue(ANDROID_TARGET_DEVICES_PROPERTY, toString(targetDevices)); } @@ -249,33 +236,6 @@ public abstract class AndroidRunConfigurationBase extends ModuleBasedConfigurati return null; } - private static boolean checkDebuggableOption(@NotNull AndroidFacet facet) { - Manifest manifest = facet.getManifest(); - // validated in checkConfiguration() - assert manifest != null; - final Application application = manifest.getApplication(); - if (application != null) { - String debuggable = application.getDebuggable().getValue(); - BooleanValueConverter booleanValueConverter = BooleanValueConverter.getInstance(true); - if (debuggable == null || !booleanValueConverter.isTrue(debuggable)) { - Project project = facet.getModule().getProject(); - int result = Messages.showYesNoCancelDialog(project, AndroidBundle.message("android.manifest.debuggable.attribute.not.true.warning"), - CommonBundle.getWarningTitle(), - Messages.getWarningIcon()); - if (result == 0) { - ApplicationManager.getApplication().runWriteAction(new Runnable() { - @Override - public void run() { - application.getDebuggable().setValue("true"); - } - }); - } - return result != 2; - } - } - return true; - } - private static boolean activateDdmsIfNeccessary(@NotNull AndroidFacet facet) { final Project project = facet.getModule().getProject(); final boolean ddmsEnabled = AndroidEnableDdmsAction.isDdmsEnabled(); From 818c97551cd51e657111852985f669c3bb9e0bb1 Mon Sep 17 00:00:00 2001 From: Eugene Kudelevsky Date: Tue, 12 Apr 2011 20:07:37 +0400 Subject: [PATCH 06/30] IDEA-64014 support for debug builds --- .../actions/ExportSignedPackageAction.java | 59 ++------- .../android/compiler/AndroidAptCompiler.java | 13 +- .../android/compiler/AndroidCompileUtil.java | 43 ++++++- .../compiler/AndroidPackagingCompiler.java | 83 +++++++++--- .../AndroidResourcesPackagingCompiler.java | 119 ++++++++++++++++-- .../compiler/ResourcesValidityState.java | 2 +- .../compiler/tools/AndroidApkBuilder.java | 20 +-- .../android/exportSignedPackage/ApkStep.java | 32 +++-- .../exportSignedPackage/CheckModulePanel.java | 14 +-- 9 files changed, 262 insertions(+), 123 deletions(-) diff --git a/plugins/android/src/org/jetbrains/android/actions/ExportSignedPackageAction.java b/plugins/android/src/org/jetbrains/android/actions/ExportSignedPackageAction.java index 41a929ec24b0..33b1181cff0b 100644 --- a/plugins/android/src/org/jetbrains/android/actions/ExportSignedPackageAction.java +++ b/plugins/android/src/org/jetbrains/android/actions/ExportSignedPackageAction.java @@ -21,14 +21,8 @@ import com.intellij.facet.ProjectFacetManager; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.DataKeys; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.compiler.CompileContext; -import com.intellij.openapi.compiler.CompileScope; -import com.intellij.openapi.compiler.CompileStatusNotification; -import com.intellij.openapi.compiler.CompilerManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; -import com.intellij.openapi.ui.Messages; import org.jetbrains.android.exportSignedPackage.CheckModulePanel; import org.jetbrains.android.exportSignedPackage.ExportSignedPackageWizard; import org.jetbrains.android.facet.AndroidFacet; @@ -51,55 +45,18 @@ public class ExportSignedPackageAction extends AnAction { e.getPresentation().setEnabled(project != null && ProjectFacetManager.getInstance(project).getFacets(AndroidFacet.ID).size() > 0); } - private static void makeProjectIfNeccessaryAndRun(final Project project, final Runnable afterAction) { - final CompilerManager manager = CompilerManager.getInstance(project); - final CompileScope compileScope = manager.createProjectCompileScope(project); - if (!manager.isUpToDate(compileScope)) { - ApplicationManager.getApplication().invokeLater(new Runnable() { - public void run() { - final int result = Messages.showYesNoDialog(project, AndroidBundle.message("android.export.signed.package.make.question"), - AndroidBundle.message("android.export.signed.package.action.text"), - Messages.getQuestionIcon()); - if (result == 0) { - manager.make(compileScope, new CompileStatusNotification() { - public void finished(boolean aborted, int errors, int warnings, CompileContext compileContext) { - if (!aborted && errors == 0) { - afterAction.run(); - } - } - }); - } - else { - afterAction.run(); - } - } - }); - } - else { - ApplicationManager.getApplication().invokeLater(afterAction); - } - } - @Override public void actionPerformed(AnActionEvent e) { final Project project = e.getData(DataKeys.PROJECT); assert project != null; - final Runnable exportRunnable = new Runnable() { - public void run() { - List facets = ProjectFacetManager.getInstance(project).getFacets(AndroidFacet.ID); - assert facets.size() > 0; - if (facets.size() == 1) { - if (!checkFacet(facets.get(0))) return; - } - ExportSignedPackageWizard wizard = new ExportSignedPackageWizard(project, facets); - wizard.show(); - } - }; - ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { - public void run() { - makeProjectIfNeccessaryAndRun(project, exportRunnable); - } - }); + + List facets = ProjectFacetManager.getInstance(project).getFacets(AndroidFacet.ID); + assert facets.size() > 0; + if (facets.size() == 1) { + if (!checkFacet(facets.get(0))) return; + } + ExportSignedPackageWizard wizard = new ExportSignedPackageWizard(project, facets); + wizard.show(); } private static boolean checkFacet(final AndroidFacet facet) { diff --git a/plugins/android/src/org/jetbrains/android/compiler/AndroidAptCompiler.java b/plugins/android/src/org/jetbrains/android/compiler/AndroidAptCompiler.java index 193c9f9c0f2a..7dec4b76a687 100644 --- a/plugins/android/src/org/jetbrains/android/compiler/AndroidAptCompiler.java +++ b/plugins/android/src/org/jetbrains/android/compiler/AndroidAptCompiler.java @@ -114,7 +114,7 @@ public class AndroidAptCompiler implements SourceGeneratingCompiler { try { Map> messages = AndroidApt - .compile(aptItem.myAndroidTarget, aptItem.myManifestPath, aptItem.mySourceRootPath, aptItem.myResourcesPaths, + .compile(aptItem.myAndroidTarget, aptItem.myManifestFile.getPath(), aptItem.mySourceRootPath, aptItem.myResourcesPaths, aptItem.myAssetsPath, aptItem.myCustomPackage ? aptItem.myPackage : null ); AndroidCompileUtil.addMessages(context, messages); @@ -169,7 +169,7 @@ public class AndroidAptCompiler implements SourceGeneratingCompiler { final static class AptGenerationItem implements GenerationItem { final Module myModule; - final String myManifestPath; + final VirtualFile myManifestFile; final String[] myResourcesPaths; final String myAssetsPath; final String mySourceRootPath; @@ -179,7 +179,7 @@ public class AndroidAptCompiler implements SourceGeneratingCompiler { final boolean myCustomPackage; private AptGenerationItem(@NotNull Module module, - @NotNull String manifestPath, + @NotNull VirtualFile manifestFile, @NotNull String[] resourcesPaths, @Nullable String assetsPath, @NotNull String sourceRootPath, @@ -187,7 +187,7 @@ public class AndroidAptCompiler implements SourceGeneratingCompiler { @NotNull String aPackage, boolean customPackage) { myModule = module; - myManifestPath = manifestPath; + myManifestFile = manifestFile; myResourcesPaths = resourcesPaths; myAssetsPath = assetsPath; mySourceRootPath = sourceRootPath; @@ -290,12 +290,11 @@ public class AndroidAptCompiler implements SourceGeneratingCompiler { AndroidCompileUtil.createSourceRootIfNotExist(sourceRootPath, module); String assetsDirPath = assetsDir != null ? assetsDir.getPath() : null; - String manifestPath = manifestFile.getPath(); - items.add(new AptGenerationItem(module, manifestPath, resPaths, assetsDirPath, sourceRootPath, target, + items.add(new AptGenerationItem(module, manifestFile, resPaths, assetsDirPath, sourceRootPath, target, packageName, false)); for (String libPackage : AndroidUtils.getDepLibsPackages(module)) { - items.add(new AptGenerationItem(module, manifestPath, resPaths, assetsDirPath, sourceRootPath, target, + items.add(new AptGenerationItem(module, manifestFile, resPaths, assetsDirPath, sourceRootPath, target, libPackage, true)); } } diff --git a/plugins/android/src/org/jetbrains/android/compiler/AndroidCompileUtil.java b/plugins/android/src/org/jetbrains/android/compiler/AndroidCompileUtil.java index fd3e4cecca9f..45a7c88c4e2a 100644 --- a/plugins/android/src/org/jetbrains/android/compiler/AndroidCompileUtil.java +++ b/plugins/android/src/org/jetbrains/android/compiler/AndroidCompileUtil.java @@ -34,11 +34,9 @@ import com.intellij.openapi.module.ModuleManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.*; import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.util.Key; import com.intellij.openapi.util.io.FileUtil; -import com.intellij.openapi.vfs.LocalFileSystem; -import com.intellij.openapi.vfs.ReadonlyStatusHandler; -import com.intellij.openapi.vfs.VfsUtil; -import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.*; import com.intellij.psi.JavaPsiFacade; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiFile; @@ -64,10 +62,17 @@ public class AndroidCompileUtil { private static final Pattern ourMessagePattern = Pattern.compile("(.+):(\\d+):.+"); + private static final Key RELEASE_BUILD_KEY = new Key("RELEASE_BUILD_KEY"); + private AndroidCompileUtil() { } static void addMessages(final CompileContext context, final Map> messages) { + addMessages(context, messages, null); + } + + static void addMessages(final CompileContext context, final Map> messages, + @Nullable final Map presentableFilesMap) { ApplicationManager.getApplication().runReadAction(new Runnable() { public void run() { if (context.getProject().isDisposed()) return; @@ -80,7 +85,7 @@ public class AndroidCompileUtil { if (matcher.matches()) { String fileName = matcher.group(1); if (new File(fileName).exists()) { - url = "file://" + fileName; + url = getPresentableFile("file://" + fileName, presentableFilesMap); line = Integer.parseInt(matcher.group(2)); } } @@ -91,6 +96,25 @@ public class AndroidCompileUtil { }); } + @NotNull + private static String getPresentableFile(@NotNull String url, @Nullable Map presentableFilesMap) { + final VirtualFile file = VirtualFileManager.getInstance().findFileByUrl(url); + if (file == null) { + return url; + } + + if (presentableFilesMap == null) { + return url; + } + + for (Map.Entry entry : presentableFilesMap.entrySet()) { + if (file == entry.getValue()) { + return entry.getKey().getUrl(); + } + } + return url; + } + private static void collectChildrenRecursively(@NotNull VirtualFile root, @NotNull VirtualFile anchor, @NotNull Collection result) { @@ -364,4 +388,13 @@ public class AndroidCompileUtil { RunConfiguration runConfiguration = CompileStepBeforeRun.getRunConfiguration(context); return !(runConfiguration instanceof JUnitConfiguration); } + + public static boolean isReleaseBuild(@NotNull CompileContext context) { + final Boolean value = context.getCompileScope().getUserData(RELEASE_BUILD_KEY); + return value != null && value.booleanValue(); + } + + public static void setReleaseBuild(@NotNull CompileScope compileScope) { + compileScope.putUserData(RELEASE_BUILD_KEY, Boolean.TRUE); + } } diff --git a/plugins/android/src/org/jetbrains/android/compiler/AndroidPackagingCompiler.java b/plugins/android/src/org/jetbrains/android/compiler/AndroidPackagingCompiler.java index a7738f792ee7..0db7a7adc348 100644 --- a/plugins/android/src/org/jetbrains/android/compiler/AndroidPackagingCompiler.java +++ b/plugins/android/src/org/jetbrains/android/compiler/AndroidPackagingCompiler.java @@ -48,6 +48,8 @@ import java.util.*; */ public class AndroidPackagingCompiler implements PackagingCompiler { + public static final String UNSIGNED_SUFFIX = ".unsigned"; + public void processOutdatedItem(CompileContext context, String url, @Nullable ValidityState state) { } @@ -125,13 +127,12 @@ public class AndroidPackagingCompiler implements PackagingCompiler { AndroidBundle.message("android.compilation.error.apk.path.not.specified", module.getName()), null, -1, -1); continue; } - AptPackagingItem item = - new AptPackagingItem(sdkPath, manifestFile, resPackagePath, outputPath, configuration.GENERATE_UNSIGNED_APK, module); - item.setNativeLibsFolders(collectNativeLibsFolders(facet)); - item.setClassesDexPath(classesDexPath); - item.setSourceRoots(sourceRoots); - item.setExternalLibraries(externalJars); - items.add(item); + items.add(createItem(module, facet, manifestFile, sourceRoots, externalJars, resPackagePath, classesDexPath, sdkPath, + outputPath, false)); + + items.add(createItem(module, facet, manifestFile, sourceRoots, externalJars, + resPackagePath + AndroidResourcesPackagingCompiler.RELEASE_SUFFIX, classesDexPath, sdkPath, + outputPath + UNSIGNED_SUFFIX, true)); } } } @@ -139,6 +140,24 @@ public class AndroidPackagingCompiler implements PackagingCompiler { return items.toArray(new ProcessingItem[items.size()]); } + private static AptPackagingItem createItem(Module module, + AndroidFacet facet, + VirtualFile manifestFile, + VirtualFile[] sourceRoots, + VirtualFile[] externalJars, + String resPackagePath, + String classesDexPath, + String sdkPath, + String outputPath, + boolean unsigned) { + AptPackagingItem item = new AptPackagingItem(sdkPath, manifestFile, resPackagePath, outputPath, unsigned, module); + item.setNativeLibsFolders(collectNativeLibsFolders(facet)); + item.setClassesDexPath(classesDexPath); + item.setSourceRoots(sourceRoots); + item.setExternalLibraries(externalJars); + return item; + } + @NotNull private static VirtualFile[] collectNativeLibsFolders(AndroidFacet facet) { List result = new ArrayList(); @@ -179,11 +198,13 @@ public class AndroidPackagingCompiler implements PackagingCompiler { continue; } + if (!shouldGenerateApk(item.myModule, context, item.isUnsigned())) { + continue; + } + try { - - String[] externalLibPaths = getPaths(item.getExternalLibraries()); - - final Map> apkuBuilderMessages = AndroidApkBuilder + final String[] externalLibPaths = getPaths(item.getExternalLibraries()); + final Map> messages = AndroidApkBuilder .execute(item.mySdkPath, item.getResPackagePath(), item.getClassesDexPath(), @@ -191,9 +212,8 @@ public class AndroidPackagingCompiler implements PackagingCompiler { externalLibPaths, item.getNativeLibsFolders(), item.getFinalPath(), - item.isGenerateUnsignedApk()); - - AndroidCompileUtil.addMessages(context, apkuBuilderMessages); + item.isUnsigned()); + AndroidCompileUtil.addMessages(context, messages); } catch (final IOException e) { ApplicationManager.getApplication().runReadAction(new Runnable() { @@ -210,6 +230,29 @@ public class AndroidPackagingCompiler implements PackagingCompiler { return result.toArray(new ProcessingItem[result.size()]); } + public static boolean shouldGenerateApk(Module module, CompileContext context, boolean unsigned) { + final boolean releaseBuild = AndroidCompileUtil.isReleaseBuild(context); + + if (!unsigned) { + return !releaseBuild; + } + + final AndroidFacet facet = AndroidFacet.getInstance(module); + if (facet == null) { + return true; + } + + if (releaseBuild) { + return true; + } + + if (facet.getConfiguration().GENERATE_UNSIGNED_APK) { + return true; + } + + return false; + } + @NotNull public String getDescription() { return "Android Packaging Compiler"; @@ -232,20 +275,20 @@ public class AndroidPackagingCompiler implements PackagingCompiler { private VirtualFile[] myNativeLibsFolders; private VirtualFile[] mySourceRoots; private VirtualFile[] myExternalLibraries; - private final boolean myGenerateUnsignedApk; + private final boolean myUnsigned; private final Module myModule; private AptPackagingItem(String sdkPath, @NotNull VirtualFile manifestFile, @NotNull String resPackagePath, @NotNull String finalPath, - boolean generateUnsignedApk, + boolean unsigned, @NotNull Module module) { mySdkPath = sdkPath; myManifestFile = manifestFile; myResPackagePath = resPackagePath; myFinalPath = finalPath; - myGenerateUnsignedApk = generateUnsignedApk; + myUnsigned = unsigned; myModule = module; } @@ -302,12 +345,12 @@ public class AndroidPackagingCompiler implements PackagingCompiler { @Nullable public ValidityState getValidityState() { - return new MyValidityState(myManifestFile, myResPackagePath, myClassesDexPath, myFinalPath, myGenerateUnsignedApk, mySourceRoots, + return new MyValidityState(myManifestFile, myResPackagePath, myClassesDexPath, myFinalPath, myUnsigned, mySourceRoots, myExternalLibraries, myNativeLibsFolders); } - public boolean isGenerateUnsignedApk() { - return myGenerateUnsignedApk; + public boolean isUnsigned() { + return myUnsigned; } } diff --git a/plugins/android/src/org/jetbrains/android/compiler/AndroidResourcesPackagingCompiler.java b/plugins/android/src/org/jetbrains/android/compiler/AndroidResourcesPackagingCompiler.java index f3845c0775be..90567df2deaf 100644 --- a/plugins/android/src/org/jetbrains/android/compiler/AndroidResourcesPackagingCompiler.java +++ b/plugins/android/src/org/jetbrains/android/compiler/AndroidResourcesPackagingCompiler.java @@ -17,21 +17,30 @@ package org.jetbrains.android.compiler; import com.android.sdklib.IAndroidTarget; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.compiler.*; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleManager; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.android.compiler.tools.AndroidApt; +import org.jetbrains.android.dom.manifest.Application; +import org.jetbrains.android.dom.manifest.Manifest; import org.jetbrains.android.facet.AndroidFacet; import org.jetbrains.android.facet.AndroidFacetConfiguration; import org.jetbrains.android.facet.AndroidRootUtil; import org.jetbrains.android.util.AndroidBundle; +import org.jetbrains.android.util.AndroidUtils; import org.jetbrains.annotations.NotNull; import java.io.DataInput; +import java.io.DataOutput; import java.io.File; import java.io.IOException; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Map; @@ -39,6 +48,10 @@ import java.util.Map; * @author Eugene.Kudelevsky */ public class AndroidResourcesPackagingCompiler implements ClassPostProcessingCompiler { + private static final Logger LOG = Logger.getInstance("#org.jetbrains.android.compiler.AndroidResourcesPackagingCompiler"); + + public static final String RELEASE_SUFFIX = ".release"; + @NotNull @Override public ProcessingItem[] getProcessingItems(CompileContext context) { @@ -65,7 +78,9 @@ public class AndroidResourcesPackagingCompiler implements ClassPostProcessingCom context.addMessage(CompilerMessageCategory.WARNING, "Resource directory not found for module " + module.getName(), null, -1, -1); } - items.add(new MyItem(module, target, manifestFile, resourcesDirPaths, assetsDirPath, outputPath)); + + items.add(new MyItem(module, target, manifestFile, resourcesDirPaths, assetsDirPath, outputPath, false)); + items.add(new MyItem(module, target, manifestFile, resourcesDirPaths, assetsDirPath, outputPath + RELEASE_SUFFIX, true)); } } } @@ -92,12 +107,32 @@ public class AndroidResourcesPackagingCompiler implements ClassPostProcessingCom continue; } + if (!AndroidPackagingCompiler.shouldGenerateApk(item.myModule, context, item.myReleasePackage)) { + continue; + } + + final VirtualFile preprocessedManifestFile; + try { + preprocessedManifestFile = item.myReleasePackage + ? item.myManifestFile + : copyManifestAndSetDebuggableToTrue(item.myModule, item.myManifestFile); + } + catch (IOException e) { + LOG.info(e); + context.addMessage(CompilerMessageCategory.ERROR, "Cannot preprocess AndroidManifest.xml for debug build", + item.myManifestFile.getUrl(), -1, -1); + continue; + } + + final Map presentableFilesMap = Collections.singletonMap(item.myManifestFile, preprocessedManifestFile); + try { Map> messages = AndroidApt.packageResources(item.myAndroidTarget, - item.myManifestFile.getPath(), - item.myResourceDirPaths, item.myAssetsDirPath, + preprocessedManifestFile.getPath(), + item.myResourceDirPaths, + item.myAssetsDirPath, item.myOutputPath); - AndroidCompileUtil.addMessages(context, messages); + AndroidCompileUtil.addMessages(context, messages, presentableFilesMap); } catch (final IOException e) { ApplicationManager.getApplication().runReadAction(new Runnable() { @@ -114,6 +149,58 @@ public class AndroidResourcesPackagingCompiler implements ClassPostProcessingCom return result.toArray(new ProcessingItem[result.size()]); } + private static VirtualFile copyManifestAndSetDebuggableToTrue(@NotNull final Module module, @NotNull final VirtualFile manifestFile) + throws IOException { + + final File dir = FileUtil.createTempDirectory("android_manifest_copy", "tmp"); + final VirtualFile vDir = LocalFileSystem.getInstance().findFileByIoFile(dir); + if (vDir == null) { + throw new IOException("Cannot create temp directory for manifest copy"); + } + + final VirtualFile[] manifestFileCopy = new VirtualFile[1]; + + ApplicationManager.getApplication().invokeAndWait(new Runnable() { + @Override + public void run() { + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + try { + manifestFileCopy[0] = manifestFile.copy(module.getProject(), vDir, manifestFile.getName()); + } + catch (IOException e) { + LOG.info(e); + return; + } + + if (manifestFileCopy[0] == null) { + return; + } + + final Manifest manifestInCopy = AndroidUtils.loadDomElement(module, manifestFileCopy[0], Manifest.class); + if (manifestInCopy == null) { + return; + } + + final Application applicationInCopy = manifestInCopy.getApplication(); + if (applicationInCopy == null) { + return; + } + applicationInCopy.getDebuggable().setValue(Boolean.TRUE.toString()); + } + }); + + ApplicationManager.getApplication().saveAll(); + } + }, ModalityState.defaultModalityState()); + + if (manifestFileCopy[0] == null) { + throw new IOException("Cannot copy manifest file to " + vDir.getPath()); + } + return manifestFileCopy[0]; + } + @NotNull @Override public String getDescription() { @@ -139,13 +226,15 @@ public class AndroidResourcesPackagingCompiler implements ClassPostProcessingCom final String myOutputPath; private final boolean myFileExists; + private final boolean myReleasePackage; private MyItem(Module module, IAndroidTarget androidTarget, VirtualFile manifestFile, String[] resourceDirPaths, String assetsDirPath, - String outputPath) { + String outputPath, + boolean releasePackage) { myModule = module; myAndroidTarget = androidTarget; myManifestFile = manifestFile; @@ -153,6 +242,7 @@ public class AndroidResourcesPackagingCompiler implements ClassPostProcessingCom myAssetsDirPath = assetsDirPath; myOutputPath = outputPath; myFileExists = new File(outputPath).exists(); + myReleasePackage = releasePackage; } @NotNull @@ -164,20 +254,23 @@ public class AndroidResourcesPackagingCompiler implements ClassPostProcessingCom @Override public ValidityState getValidityState() { - return new MyValidityState(myModule, myFileExists); + return new MyValidityState(myModule, myFileExists, myReleasePackage); } } private static class MyValidityState extends ResourcesValidityState { private final boolean myOutputFileExists; + private final boolean myReleaseBuild; - public MyValidityState(Module module, boolean outputFileExists) { + public MyValidityState(Module module, boolean outputFileExists, boolean releaseBuild) { super(module); myOutputFileExists = outputFileExists; + myReleaseBuild = releaseBuild; } public MyValidityState(DataInput is) throws IOException { super(is); + myReleaseBuild = is.readBoolean(); myOutputFileExists = true; } @@ -186,10 +279,20 @@ public class AndroidResourcesPackagingCompiler implements ClassPostProcessingCom if (!(otherState instanceof MyValidityState)) { return false; } - if (myOutputFileExists != ((MyValidityState)otherState).myOutputFileExists) { + final MyValidityState otherState1 = (MyValidityState)otherState; + if (myOutputFileExists != otherState1.myOutputFileExists) { + return false; + } + if (myReleaseBuild != otherState1.myReleaseBuild) { return false; } return super.equalsTo(otherState); } + + @Override + public void save(DataOutput os) throws IOException { + super.save(os); + os.writeBoolean(myReleaseBuild); + } } } diff --git a/plugins/android/src/org/jetbrains/android/compiler/ResourcesValidityState.java b/plugins/android/src/org/jetbrains/android/compiler/ResourcesValidityState.java index 4aa5018fbf59..80f593a39a59 100644 --- a/plugins/android/src/org/jetbrains/android/compiler/ResourcesValidityState.java +++ b/plugins/android/src/org/jetbrains/android/compiler/ResourcesValidityState.java @@ -49,7 +49,7 @@ public class ResourcesValidityState implements ValidityState { IAndroidTarget target = platform != null ? platform.getTarget() : null; myAndroidTargetName = target != null ? target.getFullName() : ""; - VirtualFile manifestFile = AndroidRootUtil.getManifestFile(module); + VirtualFile manifestFile = AndroidRootUtil.getManifestFileForCompiler(facet); if (manifestFile != null) { myResourceTimestamps.put(manifestFile.getPath(), manifestFile.getTimeStamp()); } diff --git a/plugins/android/src/org/jetbrains/android/compiler/tools/AndroidApkBuilder.java b/plugins/android/src/org/jetbrains/android/compiler/tools/AndroidApkBuilder.java index 1ab3d84bc543..1c011cff99ff 100644 --- a/plugins/android/src/org/jetbrains/android/compiler/tools/AndroidApkBuilder.java +++ b/plugins/android/src/org/jetbrains/android/compiler/tools/AndroidApkBuilder.java @@ -43,16 +43,13 @@ import java.util.*; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; -import static com.intellij.openapi.compiler.CompilerMessageCategory.ERROR; -import static com.intellij.openapi.compiler.CompilerMessageCategory.INFORMATION; -import static com.intellij.openapi.compiler.CompilerMessageCategory.WARNING; +import static com.intellij.openapi.compiler.CompilerMessageCategory.*; /** * @author yole */ public class AndroidApkBuilder { private static final String UNALIGNED_SUFFIX = ".unaligned"; - private static final String UNSIGNED_SUFFIX = ".unsigned"; private AndroidApkBuilder() { } @@ -114,18 +111,13 @@ public class AndroidApkBuilder { @NotNull String[] externalJars, @NotNull VirtualFile[] nativeLibsFolders, @NotNull String finalApk, - boolean generateUnsignedApk) throws IOException { - String unsignedApk = finalApk + UNSIGNED_SUFFIX; - - Map> map; - if (generateUnsignedApk) { - map = filterUsingKeystoreMessages( - finalPackage(resPackagePath, dexPath, sourceRoots, externalJars, nativeLibsFolders, unsignedApk, false)); - } - else { - map = new HashMap>(); + boolean unsigned) throws IOException { + if (unsigned) { + return filterUsingKeystoreMessages( + finalPackage(resPackagePath, dexPath, sourceRoots, externalJars, nativeLibsFolders, finalApk, false)); } + final Map> map = new HashMap>(); final String zipAlignPath = sdkPath + File.separator + AndroidUtils.toolPath(SdkConstants.FN_ZIPALIGN); boolean withAlignment = new File(zipAlignPath).exists(); String unalignedApk = finalApk + UNALIGNED_SUFFIX; diff --git a/plugins/android/src/org/jetbrains/android/exportSignedPackage/ApkStep.java b/plugins/android/src/org/jetbrains/android/exportSignedPackage/ApkStep.java index 2779fd25bbca..13e67e9d4f3d 100644 --- a/plugins/android/src/org/jetbrains/android/exportSignedPackage/ApkStep.java +++ b/plugins/android/src/org/jetbrains/android/exportSignedPackage/ApkStep.java @@ -27,7 +27,10 @@ import com.intellij.execution.process.ProcessEvent; import com.intellij.ide.util.PropertiesComponent; import com.intellij.ide.wizard.CommitStepException; import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.compiler.CompilerPaths; +import com.intellij.openapi.compiler.CompileContext; +import com.intellij.openapi.compiler.CompileScope; +import com.intellij.openapi.compiler.CompileStatusNotification; +import com.intellij.openapi.compiler.CompilerManager; import com.intellij.openapi.module.Module; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; @@ -38,6 +41,8 @@ import com.intellij.openapi.ui.TextFieldWithBrowseButton; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.android.compiler.AndroidCompileUtil; +import org.jetbrains.android.compiler.AndroidPackagingCompiler; import org.jetbrains.android.facet.AndroidFacet; import org.jetbrains.android.sdk.AndroidPlatform; import org.jetbrains.android.util.AndroidBundle; @@ -166,7 +171,8 @@ class ApkStep extends ExportSignedPackageWizardStep { assert certificate != null; SignedJarBuilder builder = new SignedJarBuilder(fos, privateKey, certificate); Module module = myWizard.getFacet().getModule(); - String srcApkPath = CompilerPaths.getModuleOutputPath(module, false) + '/' + module.getName() + ".apk"; + //String srcApkPath = CompilerPaths.getModuleOutputPath(module, false) + '/' + module.getName() + ".apk"; + String srcApkPath = myWizard.getFacet().getApkPath() + AndroidPackagingCompiler.UNSIGNED_SUFFIX; FileInputStream fis = new FileInputStream(new File(FileUtil.toSystemDependentName(srcApkPath))); try { builder.writeZip(fis, null); @@ -255,11 +261,23 @@ class ApkStep extends ExportSignedPackageWizardStep { catch (Exception e) { throw new CommitStepException(e.getMessage()); } - String title = AndroidBundle.message("android.extract.package.task.title"); - ProgressManager.getInstance().run(new Task.Backgroundable(myWizard.getProject(), title, true, null) { - - public void run(@NotNull ProgressIndicator indicator) { - createAndAlignApk(apkPath); + + final CompilerManager manager = CompilerManager.getInstance(myWizard.getProject()); + final CompileScope compileScope = manager.createModuleCompileScope(facet.getModule(), true); + AndroidCompileUtil.setReleaseBuild(compileScope); + + manager.make(compileScope, new CompileStatusNotification() { + public void finished(boolean aborted, int errors, int warnings, CompileContext compileContext) { + if (aborted || errors != 0) { + return; + } + + final String title = AndroidBundle.message("android.extract.package.task.title"); + ProgressManager.getInstance().run(new Task.Backgroundable(myWizard.getProject(), title, true, null) { + public void run(@NotNull ProgressIndicator indicator) { + createAndAlignApk(apkPath); + } + }); } }); } diff --git a/plugins/android/src/org/jetbrains/android/exportSignedPackage/CheckModulePanel.java b/plugins/android/src/org/jetbrains/android/exportSignedPackage/CheckModulePanel.java index c23f62b6bd93..a0ea8f4a2801 100644 --- a/plugins/android/src/org/jetbrains/android/exportSignedPackage/CheckModulePanel.java +++ b/plugins/android/src/org/jetbrains/android/exportSignedPackage/CheckModulePanel.java @@ -20,17 +20,12 @@ import com.intellij.openapi.compiler.DummyCompileContext; import com.intellij.openapi.module.Module; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.ui.VerticalFlowLayout; -import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.util.xml.converters.values.BooleanValueConverter; -import org.jetbrains.android.dom.manifest.Application; -import org.jetbrains.android.dom.manifest.Manifest; import org.jetbrains.android.facet.AndroidFacet; import org.jetbrains.android.util.AndroidBundle; import javax.swing.*; import java.awt.*; -import java.io.File; /** * @author Eugene.Kudelevsky @@ -49,25 +44,24 @@ public class CheckModulePanel extends JPanel { final DummyCompileContext compileContext = DummyCompileContext.getInstance(); VirtualFile outputDirectory = compileContext.getModuleOutputDirectory(module); if (outputDirectory != null) { - String outputOsPath = FileUtil.toSystemDependentName(outputDirectory.getPath()); - String apkFilePath = outputOsPath + File.separator + module.getName() + ".apk"; + /*String apkFilePath = facet.getApkPath(); File f = new File(apkFilePath); if (!f.isFile()) { addError(AndroidBundle.message("android.file.not.exist.error", f.getPath())); - } + }*/ } else { addError(AndroidBundle.message("android.unable.to.get.output.directory.error")); } - Manifest manifest = facet.getManifest(); + /*Manifest manifest = facet.getManifest(); assert manifest != null; Application application = manifest.getApplication(); assert application != null; String debuggable = application.getDebuggable().getValue(); if (debuggable != null && BooleanValueConverter.getInstance(true).isTrue(debuggable)) { addWarning(AndroidBundle.message("android.export.signed.package.debuggable.warning")); - } + }*/ } public boolean hasError() { From dc7343986c10f344a4fa203990489abf2bbf372b Mon Sep 17 00:00:00 2001 From: Alexey Pegov Date: Wed, 13 Apr 2011 15:28:50 +0400 Subject: [PATCH 07/30] IDEA-63663 Sort run configurations alphabetically if clean checkout (no workspace.xml were found) [r=ann] --- .../execution/impl/RunManagerImpl.java | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/execution/impl/RunManagerImpl.java b/platform/lang-impl/src/com/intellij/execution/impl/RunManagerImpl.java index ceb8b8d985bc..361ccb059d16 100644 --- a/platform/lang-impl/src/com/intellij/execution/impl/RunManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/execution/impl/RunManagerImpl.java @@ -287,19 +287,28 @@ public class RunManagerImpl extends RunManagerEx implements JDOMExternalizable, @Override public Collection getSortedConfigurations() { - if (!myOrdered && !myOrder.isEmpty()) { //compatibility + if (!myOrdered) { //compatibility final HashMap settings = - new HashMap(myConfigurations); //sort shared and local configurations + new HashMap(myConfigurations); //sort shared and local configurations myConfigurations.clear(); + final List order = new ArrayList(settings.keySet()); - Collections.sort(order, new Comparator() { - public int compare(final String o1, final String o2) { - return myOrder.indexOf(o1) - myOrder.indexOf(o2); - } - }); - for (String configName : order) { + if (myOrder.isEmpty()) { + // IDEA-63663 Sort run configurations alphabetically if clean checkout + Collections.sort(order); + } + else { + Collections.sort(order, new Comparator() { + public int compare(final String o1, final String o2) { + return myOrder.indexOf(o1) - myOrder.indexOf(o2); + } + }); + } + + for (final String configName : order) { myConfigurations.put(configName, settings.get(configName)); } + myOrdered = true; } return myConfigurations.values(); From e02e7562e8e9133482186ee282afba46f9dde0e4 Mon Sep 17 00:00:00 2001 From: Maxim Shafirov Date: Wed, 13 Apr 2011 15:27:05 +0400 Subject: [PATCH 08/30] IDEA-63971: mouse hits for marks inside folded ranges. --- .../editor/impl/EditorMarkupModelImpl.java | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorMarkupModelImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorMarkupModelImpl.java index dd41e8a8a8e4..c665fe5f779f 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorMarkupModelImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorMarkupModelImpl.java @@ -187,11 +187,18 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark private void getNearestHighlighters(MarkupModelEx markupModel, MouseEvent e, final double width, final Collection nearest) { if (0 > e.getX() || e.getX() >= width) return; - int startOffset = yPositionToOffset(e.getY()-getMinHeight(), true); - int endOffset = yPositionToOffset(e.getY()+getMinHeight(), false); + final int y = e.getY(); + int startOffset = yPositionToOffset(y -getMinHeight(), true); + int endOffset = yPositionToOffset(y +getMinHeight(), false); markupModel.processHighlightsOverlappingWith(startOffset, endOffset, new Processor() { public boolean process(RangeHighlighterEx highlighter) { - if (highlighter.getErrorStripeMarkColor() != null) nearest.add(highlighter); + if (highlighter.getErrorStripeMarkColor() != null) { + ProperTextRange range = offsetToYPosition(highlighter.getStartOffset(), highlighter.getEndOffset()); + if (range.getStartOffset() >= y - getMinHeight() * 2 && + range.getEndOffset() <= y + getMinHeight() * 2) { + nearest.add(highlighter); + } + } return true; } }); @@ -797,6 +804,16 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark if (line < 0) return 0; if (line >= document.getLineCount()) return document.getTextLength(); - return beginLine ? document.getLineStartOffset(line) : document.getLineEndOffset(line); + final FoldingModelEx foldingModel = myEditor.getFoldingModel(); + if (beginLine) { + final int offset = document.getLineStartOffset(line); + final FoldRegion startCollapsed = foldingModel.getCollapsedRegionAtOffset(offset); + return startCollapsed != null ? Math.min(offset, startCollapsed.getStartOffset()) : offset; + } + else { + final int offset = document.getLineEndOffset(line); + final FoldRegion startCollapsed = foldingModel.getCollapsedRegionAtOffset(offset); + return startCollapsed != null ? Math.max(offset, startCollapsed.getEndOffset()) : offset; + } } } From 4784542e30211dea881aad4d5815072a8e10da11 Mon Sep 17 00:00:00 2001 From: Maxim Shafirov Date: Wed, 13 Apr 2011 15:28:39 +0400 Subject: [PATCH 09/30] Tolerate to missing searchableOptions.xml --- .../ide/ui/search/SearchableOptionsRegistrarImpl.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/platform/platform-impl/src/com/intellij/ide/ui/search/SearchableOptionsRegistrarImpl.java b/platform/platform-impl/src/com/intellij/ide/ui/search/SearchableOptionsRegistrarImpl.java index 9da67123c1b8..7c4d63da879b 100644 --- a/platform/platform-impl/src/com/intellij/ide/ui/search/SearchableOptionsRegistrarImpl.java +++ b/platform/platform-impl/src/com/intellij/ide/ui/search/SearchableOptionsRegistrarImpl.java @@ -40,6 +40,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.event.DocumentEvent; +import java.net.URL; import java.util.*; import java.util.regex.Pattern; @@ -80,8 +81,14 @@ public class SearchableOptionsRegistrarImpl extends SearchableOptionsRegistrar { ContainerUtil.addAll(myStopWords, stopWords); //index + final URL indexResource = ResourceUtil.getResource(SearchableOptionsRegistrar.class, "/search/", "searchableOptions.xml"); + if (indexResource == null) { + LOG.info("No /search/searchableOptions.xml found, settings search won't work!"); + return; + } + Document document = - JDOMUtil.loadDocument(ResourceUtil.getResource(SearchableOptionsRegistrar.class, "/search/", "searchableOptions.xml")); + JDOMUtil.loadDocument(indexResource); Element root = document.getRootElement(); List configurables = root.getChildren("configurable"); for (final Object o : configurables) { From 37a88f9b3d6b6e258ba4288c0c7ad7c0a52b855d Mon Sep 17 00:00:00 2001 From: irengrig Date: Fri, 8 Apr 2011 17:53:17 +0400 Subject: [PATCH 10/30] progress for files --- .../changes/shelf/ShelveChangesManager.java | 5 +- .../com/intellij/vcsUtil/FilesProgress.java | 62 +++++++++++++++++++ 2 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 platform/vcs-impl/src/com/intellij/vcsUtil/FilesProgress.java diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelveChangesManager.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelveChangesManager.java index 97ec0edcd960..f904fb734eb7 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelveChangesManager.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelveChangesManager.java @@ -51,6 +51,7 @@ import com.intellij.util.continuation.*; import com.intellij.util.messages.MessageBus; import com.intellij.util.messages.Topic; import com.intellij.util.text.CharArrayCharSequence; +import com.intellij.vcsUtil.FilesProgress; import org.jdom.Element; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -237,9 +238,9 @@ public class ShelveChangesManager implements ProjectComponent, JDOMExternalizabl public List importChangeLists(final Collection files, final Consumer exceptionConsumer) { final List result = new ArrayList(files.size()); try { + final FilesProgress filesProgress = new FilesProgress(files.size(), "Processing "); for (VirtualFile file : files) { - ProgressManager.checkCanceled(); - + filesProgress.updateIndicator(file); final String description = file.getNameWithoutExtension().replace('_', ' '); final File patchPath = getPatchPath(description); final ShelvedChangeList list = new ShelvedChangeList(patchPath.getPath(), description, new SmartList(), diff --git a/platform/vcs-impl/src/com/intellij/vcsUtil/FilesProgress.java b/platform/vcs-impl/src/com/intellij/vcsUtil/FilesProgress.java new file mode 100644 index 000000000000..60dfc96bdf09 --- /dev/null +++ b/platform/vcs-impl/src/com/intellij/vcsUtil/FilesProgress.java @@ -0,0 +1,62 @@ +/* + * Copyright 2000-2011 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.vcsUtil; + +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.vfs.VirtualFile; + +/** + * @author irengrig + * Date: 4/8/11 + * Time: 5:15 PM + */ +public class FilesProgress { + private final double myTotal; + private final String myPrefix; + private final ProgressIndicator myProgressIndicator; + private int myCnt; + private boolean myInText2; + + public FilesProgress(double total, final String prefix) { + myTotal = total; + myPrefix = prefix; + myProgressIndicator = ProgressManager.getInstance().getProgressIndicator(); + myCnt = 0; + myInText2 = false; + } + + public void updateIndicator(final VirtualFile vf) { + if (myProgressIndicator == null) return; + myProgressIndicator.checkCanceled(); + if (myInText2) { + myProgressIndicator.setText2(myPrefix + getFileDescriptionForProgress(vf)); + } else { + myProgressIndicator.setText(myPrefix + getFileDescriptionForProgress(vf)); + } + myProgressIndicator.setFraction(myCnt/myTotal); + ++ myCnt; + } + + private static String getFileDescriptionForProgress(final VirtualFile file) { + final VirtualFile parent = file.getParent(); + return file.getName() + " (" + (parent == null ? file.getPath() : parent.getPath()) + ")"; + } + + public void setInText2(boolean inText2) { + myInText2 = inText2; + } +} From ab9d0b32e3a215fa31e14297f692b96ec94a8965 Mon Sep 17 00:00:00 2001 From: irengrig Date: Wed, 13 Apr 2011 13:19:11 +0400 Subject: [PATCH 11/30] VCS: use VCS plugin name for notifications group id --- .../idea/svn/SvnAuthenticationNotifier.java | 3 +-- .../src/org/jetbrains/idea/svn/SvnVcs.java | 14 +++++++++----- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnAuthenticationNotifier.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnAuthenticationNotifier.java index fd0e7e06eb24..8a2fdae3ed98 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnAuthenticationNotifier.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnAuthenticationNotifier.java @@ -45,14 +45,13 @@ import java.util.*; public class SvnAuthenticationNotifier extends GenericNotifierImpl { private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.SvnAuthenticationNotifier"); - private static final String ourGroupId = "SubversionId"; private final SvnVcs myVcs; private final RootsToWorkingCopies myRootsToWorkingCopies; private final Map myCopiesPassiveResults; private Timer myTimer; public SvnAuthenticationNotifier(final SvnVcs svnVcs) { - super(svnVcs.getProject(), ourGroupId, "Not Logged In to Subversion", NotificationType.ERROR); + super(svnVcs.getProject(), svnVcs.getDisplayName(), "Not Logged In to Subversion", NotificationType.ERROR); myVcs = svnVcs; myRootsToWorkingCopies = myVcs.getRootsToWorkingCopies(); myCopiesPassiveResults = Collections.synchronizedMap(new HashMap()); diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnVcs.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnVcs.java index b7f6a5aaba33..8b8af3ba5ce8 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnVcs.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnVcs.java @@ -21,6 +21,8 @@ import com.intellij.ide.FrameStateListener; import com.intellij.ide.FrameStateManager; import com.intellij.idea.RareLogger; import com.intellij.notification.*; +import com.intellij.notification.impl.NotificationSettings; +import com.intellij.notification.impl.NotificationsConfiguration; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; @@ -221,6 +223,10 @@ public class SvnVcs extends AbstractVcs { myFrameStateListener = new MyFrameStateListener(changeListManager, vcsDirtyScopeManager); myWorkingCopiesContent = new WorkingCopiesContent(this); + + // remove used some time before old notification group ids + NotificationsConfiguration.remove(new NotificationSettings[] {new NotificationSettings("SVN_NO_JNA", null), + new NotificationSettings("SVN_NO_CRYPT32", null), new NotificationSettings("SubversionId", null)}); } public void postStartup() { @@ -328,15 +334,13 @@ public class SvnVcs extends AbstractVcs { } } - private final static String UPGRADE_SUBVERSION_FORMAT = "Subversion"; - private void upgradeToRecentVersion(final SvnConfiguration.SvnSupportOptions supportOptions) { if (! supportOptions.upgradeTo16Asked()) { final SvnWorkingCopyChecker workingCopyChecker = new SvnWorkingCopyChecker(); if (workingCopyChecker.upgradeNeeded()) { - Notifications.Bus.notify(new Notification(UPGRADE_SUBVERSION_FORMAT, SvnBundle.message("upgrade.format.to16.question.title"), + Notifications.Bus.notify(new Notification(getDisplayName(), SvnBundle.message("upgrade.format.to16.question.title"), "Old format Subversion working copies could be upgraded to version 1.6.", NotificationType.INFORMATION, new NotificationListener() { public void hyperlinkUpdate(@NotNull Notification notification, @NotNull HyperlinkEvent event) { @@ -374,10 +378,10 @@ public class SvnVcs extends AbstractVcs { if (SystemInfo.isWindows) { if (! SVNJNAUtil.isJNAPresent()) { - Notifications.Bus.notify(new Notification("SVN_NO_JNA", "Subversion plugin: no JNA", + Notifications.Bus.notify(new Notification(getDisplayName(), "Subversion plugin: no JNA", "A problem with JNA initialization for svnkit library. Encryption is not available.", NotificationType.WARNING), NotificationDisplayType.BALLOON, myProject); } else if (! SVNJNAUtil.isWinCryptEnabled()) { - Notifications.Bus.notify(new Notification("SVN_NO_CRYPT32", "Subversion plugin: no encryption", + Notifications.Bus.notify(new Notification(getDisplayName(), "Subversion plugin: no encryption", "A problem with encryption module (Crypt32.dll) initialization for svnkit library. Encryption is not available.", NotificationType.WARNING), NotificationDisplayType.BALLOON, myProject); } } From f008573b3fa6612ec3cca922887929ec1840b9f1 Mon Sep 17 00:00:00 2001 From: irengrig Date: Wed, 13 Apr 2011 14:04:25 +0400 Subject: [PATCH 12/30] SVN: when changing notification ids, also make Subversion group sticky balloon - since group was used before, but for other kinds (will be done once) --- .../src/org/jetbrains/idea/svn/SvnVcs.java | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnVcs.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnVcs.java index 8b8af3ba5ce8..f4dd88bc9e23 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnVcs.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnVcs.java @@ -225,8 +225,22 @@ public class SvnVcs extends AbstractVcs { myWorkingCopiesContent = new WorkingCopiesContent(this); // remove used some time before old notification group ids - NotificationsConfiguration.remove(new NotificationSettings[] {new NotificationSettings("SVN_NO_JNA", null), - new NotificationSettings("SVN_NO_CRYPT32", null), new NotificationSettings("SubversionId", null)}); + correctNotificationIds(); + } + + private void correctNotificationIds() { + boolean notEmpty = NotificationsConfiguration.getSettings("SVN_NO_JNA") != null || + NotificationsConfiguration.getSettings("SVN_NO_CRYPT32") != null || + NotificationsConfiguration.getSettings("SubversionId") != null; + if (notEmpty) { + NotificationsConfiguration.remove(new NotificationSettings[] {new NotificationSettings("SVN_NO_JNA", null), + new NotificationSettings("SVN_NO_CRYPT32", null), new NotificationSettings("SubversionId", null)}); + // if group ids is being changed, set highest level first + final NotificationSettings settings = NotificationsConfiguration.getSettings(getDisplayName()); + if (settings != null) { + settings.setDisplayType(NotificationDisplayType.STICKY_BALLOON); + } + } } public void postStartup() { From 223eba72a6f03a23233097b43c26eeb6fc51ad9b Mon Sep 17 00:00:00 2001 From: irengrig Date: Wed, 13 Apr 2011 16:01:23 +0400 Subject: [PATCH 13/30] P4: detect offline state. Offline notification. Also when offline state edited from settings --- .../intellij/openapi/vcs/impl/GenericNotifierImpl.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/GenericNotifierImpl.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/GenericNotifierImpl.java index 245e377bc1ff..a2ca4b56f722 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/GenericNotifierImpl.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/GenericNotifierImpl.java @@ -28,7 +28,7 @@ import java.util.*; public abstract class GenericNotifierImpl { private final static Logger LOG = Logger.getInstance("#com.intellij.openapi.vcs.impl.GenericNotifier"); - private final Project myProject; + protected final Project myProject; @NotNull private final String myGroupId; //+- here @NotNull @@ -181,4 +181,10 @@ public abstract class GenericNotifierImpl { private static void log(final String s) { LOG.debug(s); } + + public boolean isEmpty() { + synchronized (myLock) { + return myState.isEmpty(); + } + } } From 838f8f67fcdb1907f44cc370f316cf2851b9b016 Mon Sep 17 00:00:00 2001 From: Shaverdova Elena Date: Wed, 13 Apr 2011 16:02:36 +0400 Subject: [PATCH 14/30] Phing initial commit --- .../openapi/actionSystem/ActionPlaces.java | 9 +- .../buildfiles/ForcedBuildFileAttribute.java | 107 ++++++++++++++++++ .../intellij/openapi/util/io/FileUtil.java | 53 ++++++++- .../openapi/util/io/FileUtilFindFileTest.java | 89 +++++++++++++++ plugins/ant/ant.iml | 5 +- .../lang/ant/ForcedAntFileAttribute.java | 43 ++++--- .../lang/ant/dom/AntDomFileDescription.java | 7 +- 7 files changed, 279 insertions(+), 34 deletions(-) create mode 100644 platform/platform-impl/src/com/intellij/buildfiles/ForcedBuildFileAttribute.java create mode 100644 platform/util/testSrc/com/intellij/openapi/util/io/FileUtilFindFileTest.java diff --git a/platform/platform-api/src/com/intellij/openapi/actionSystem/ActionPlaces.java b/platform/platform-api/src/com/intellij/openapi/actionSystem/ActionPlaces.java index bf31a84e1256..7b86e53a25d7 100644 --- a/platform/platform-api/src/com/intellij/openapi/actionSystem/ActionPlaces.java +++ b/platform/platform-api/src/com/intellij/openapi/actionSystem/ActionPlaces.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2011 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. @@ -103,10 +103,13 @@ public abstract class ActionPlaces { public static final String TFS_TREE_POPUP = "TfsTreePopup"; public static final String ACTION_PLACE_VCS_QUICK_LIST_POPUP_ACTION = "ActionPlace.VcsQuickListPopupAction"; + public static final String PHING_EXPLORER_POPUP = "PhingExplorerPopup"; + public static final String PHING_EXPLORER_TOOLBAR = "PhingExplorerToolbar"; + private static final String[] ourToolbarPlaces = new String[]{EDITOR_TOOLBAR, PROJECT_VIEW_TOOLBAR, TESTTREE_VIEW_TOOLBAR, MAIN_TOOLBAR, ANT_EXPLORER_TOOLBAR, ANT_MESSAGES_TOOLBAR, COMPILER_MESSAGES_TOOLBAR, TODO_VIEW_TOOLBAR, STRUCTURE_VIEW_TOOLBAR, USAGE_VIEW_TOOLBAR, DEBUGGER_TOOLBAR, CALL_HIERARCHY_VIEW_TOOLBAR, METHOD_HIERARCHY_VIEW_TOOLBAR, TYPE_HIERARCHY_VIEW_TOOLBAR, JAVADOC_TOOLBAR, - FILE_HISTORY_TOOLBAR, FILEHISTORY_VIEW_TOOLBAR, LVCS_DIRECTORY_HISTORY_TOOLBAR, CHANGES_VIEW_TOOLBAR, }; + FILE_HISTORY_TOOLBAR, FILEHISTORY_VIEW_TOOLBAR, LVCS_DIRECTORY_HISTORY_TOOLBAR, CHANGES_VIEW_TOOLBAR, PHING_EXPLORER_TOOLBAR }; public static boolean isToolbarPlace(@NotNull String place) { return ArrayUtil.find(ourToolbarPlaces, place) != -1; @@ -118,7 +121,7 @@ public abstract class ActionPlaces { STRUCTURE_VIEW_POPUP, TODO_VIEW_POPUP, COMPILER_MESSAGES_POPUP, ANT_MESSAGES_POPUP, ANT_EXPLORER_POPUP, UPDATE_POPUP, FILEVIEW_POPUP, CHECKOUT_POPUP, LVCS_DIRECTORY_HISTORY_POPUP, GUI_DESIGNER_EDITOR_POPUP, GUI_DESIGNER_COMPONENT_TREE_POPUP, GUI_DESIGNER_PROPERTY_INSPECTOR_POPUP, CREATE_EJB_POPUP, CHANGES_VIEW_POPUP, REMOTE_HOST_VIEW_POPUP, REMOTE_HOST_DIALOG_POPUP, TFS_TREE_POPUP, - ACTION_PLACE_VCS_QUICK_LIST_POPUP_ACTION + ACTION_PLACE_VCS_QUICK_LIST_POPUP_ACTION , PHING_EXPLORER_POPUP }; public static boolean isPopupPlace(@NotNull String place) { diff --git a/platform/platform-impl/src/com/intellij/buildfiles/ForcedBuildFileAttribute.java b/platform/platform-impl/src/com/intellij/buildfiles/ForcedBuildFileAttribute.java new file mode 100644 index 000000000000..9da3b5c2f09d --- /dev/null +++ b/platform/platform-impl/src/com/intellij/buildfiles/ForcedBuildFileAttribute.java @@ -0,0 +1,107 @@ +/* + * Copyright 2000-2011 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.buildfiles; + +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.util.Key; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.newvfs.FileAttribute; +import com.intellij.openapi.vfs.newvfs.NewVirtualFile; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +/** + * Created by IntelliJ IDEA. + * User: lene + * Date: 04.04.11 + * Time: 17:40 + */ +public class ForcedBuildFileAttribute { + private static final Logger LOG = Logger.getInstance("#" + ForcedBuildFileAttribute.class.getName()); + + private static final FileAttribute FRAMEWORK_FILE_ATTRIBUTE = new FileAttribute("forcedBuildFileFrameworkAttribute", 1, false); + private static final Key FRAMEWORK_FILE_MARKER = Key.create("forcedBuildFileFrameworkAttribute"); + + + private ForcedBuildFileAttribute() { + } + + public static boolean belongsToFramework(VirtualFile file, @NotNull String frameworkId) { + return frameworkId.equals(getFrameworkIdOfBuildFile(file)); + } + + @Nullable + public static String getFrameworkIdOfBuildFile(VirtualFile file) { + if (file instanceof NewVirtualFile) { + final DataInputStream is = FRAMEWORK_FILE_ATTRIBUTE.readAttribute(file); + if (is != null) { + try { + try { + /* + //todo[lene] IOUtil throws java.io.EOFException + at java.io.DataInputStream.readFully(DataInputStream.java:180) + at java.io.DataInputStream.readFully(DataInputStream.java:152) + at com.intellij.util.io.IOUtil.readString(IOUtil.java:40) + at com.intellij.buildfiles.ForcedBuildFileAttribute.getFrameworkIdOfBuildFile(ForcedBuildFileAttribute.java:59) + */ + return is.readUTF(); + } + finally { + is.close(); + } + } + catch (IOException e) { + LOG.error(e); + } + } + return ""; + } + return file.getUserData(FRAMEWORK_FILE_MARKER); + } + + + public static void forceFileToFramework(VirtualFile file, String frameworkId, boolean value) { + if (!value && !frameworkId.equals(getFrameworkIdOfBuildFile(file))) {//belongs to other framework + return; + } + forceBuildFile(file, frameworkId); + } + + + private static void forceBuildFile(VirtualFile file, String value) { + if (file instanceof NewVirtualFile) { + final DataOutputStream os = FRAMEWORK_FILE_ATTRIBUTE.writeAttribute(file); + try { + try { + os.writeUTF(value); + } + finally { + os.close(); + } + } + catch (IOException e) { + LOG.error(e); + } + } + else { + file.putUserData(FRAMEWORK_FILE_MARKER, value); + } + } +} diff --git a/platform/util/src/com/intellij/openapi/util/io/FileUtil.java b/platform/util/src/com/intellij/openapi/util/io/FileUtil.java index b6ee40302f1f..c406f88f1eee 100644 --- a/platform/util/src/com/intellij/openapi/util/io/FileUtil.java +++ b/platform/util/src/com/intellij/openapi/util/io/FileUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2011 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. @@ -120,7 +120,7 @@ public class FileUtil { * @param strict if {@code false} then this method returns {@code true} if {@code ancestor} * and {@code file} are equal * @return {@code true} if {@code ancestor} is parent of {@code file}; {@code false} otherwise - * @throws IOException this exception is never thrown and left here for backward compatibilty + * @throws IOException this exception is never thrown and left here for backward compatibilty */ public static boolean isAncestor(@NotNull File ancestor, @NotNull File file, boolean strict) throws IOException { File parent = strict ? getParentFile(file) : file; @@ -902,7 +902,7 @@ public class FileUtil { /** * Has duplicate: {@link com.intellij.coverage.listeners.CoverageListener#sanitize(java.lang.String, java.lang.String)} - * as FileUtil is not available in client's vm + * as FileUtil is not available in client's vm */ @NotNull public static String sanitizeFileName(@NotNull String name) { @@ -1016,4 +1016,51 @@ public class FileUtil { } return found; } + + /** + * Returns empty string for empty path. + * First checks whether provided path is a path of a file with sought-for name. + * Unless found, checks if provided file was a directory. In this case checks existance + * of child files with given names in order "as provided". Finally checks filename among + * brother-files of provided. Returns null if nothing found. + * + * @return path of the first of found files or empty string or null. + */ + @Nullable + public static String findFileInProvidedPath(String providedPath, String... fileNames) { + if (StringUtil.isEmpty(providedPath)) { + return ""; + } + + File providedFile = new File(providedPath); + if (providedFile.exists()) { + String name = providedFile.getName(); + for (String fileName : fileNames) { + if (name.equals(fileName)) { + return toSystemDependentName(providedFile.getPath()); + } + } + } + + if (providedFile.isDirectory()) { //user chose folder with file + for (String fileName : fileNames) { + File file = new File(providedFile, fileName); + if (fileName.equals(file.getName()) && file.exists()) { + return toSystemDependentName(file.getPath()); + } + } + } + + providedFile = providedFile.getParentFile(); //users chose wrong file in same directory + if (providedFile != null && providedFile.exists()) { + for (String fileName : fileNames) { + File file = new File(providedFile, fileName); + if (fileName.equals(file.getName()) && file.exists()) { + return toSystemDependentName(file.getPath()); + } + } + } + + return null; + } } diff --git a/platform/util/testSrc/com/intellij/openapi/util/io/FileUtilFindFileTest.java b/platform/util/testSrc/com/intellij/openapi/util/io/FileUtilFindFileTest.java new file mode 100644 index 000000000000..701128508a89 --- /dev/null +++ b/platform/util/testSrc/com/intellij/openapi/util/io/FileUtilFindFileTest.java @@ -0,0 +1,89 @@ +/* + * Copyright 2000-2011 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.openapi.util.io; + +import com.intellij.openapi.util.text.StringUtil; +import junit.framework.TestCase; + +import java.io.File; +import java.io.IOException; + +/** + * Created by IntelliJ IDEA. + * User: lene + * Date: 29.03.11 + * Time: 17:16 + */ +public class FileUtilFindFileTest extends TestCase { + private final File myTempFile; + private final File myFirstFile; + private final File mySecondFile; + + @SuppressWarnings({"JUnitTestCaseWithNonTrivialConstructors"}) + public FileUtilFindFileTest() throws IOException { + myTempFile = FileUtil.createTempDirectory("tEF", ""); //NON-NLS + myFirstFile = new File(myTempFile, "first"); + mySecondFile = new File(myTempFile, "second"); //NON-NLS + assertTrue(myFirstFile.createNewFile()); + assertTrue(mySecondFile.createNewFile()); + } + + public void testNonExistingFileInNonExistentDirectory() throws Exception { + String path = FileUtil.findFileInProvidedPath("123", "zero");//NON-NLS + assertTrue(StringUtil.isEmpty(path)); + } + + public void testNonExistingFileInDirectory() throws Exception { + String path = FileUtil.findFileInProvidedPath(myTempFile.getAbsolutePath(), "zero");//NON-NLS + assertTrue(StringUtil.isEmpty(path)); + } + + public void testNonExistingFile() throws Exception { + String path = + FileUtil.findFileInProvidedPath(myFirstFile.getAbsolutePath() + "123", myFirstFile.getName() + "123"); + assertTrue(StringUtil.isEmpty(path)); + } + + public void testExistingFileInDirectory() throws Exception { + String path = FileUtil.findFileInProvidedPath(myTempFile.getAbsolutePath(), "first"); + assertEquals(path, myFirstFile.getAbsolutePath()); + } + + public void testExistingFile() throws Exception { + String path = FileUtil.findFileInProvidedPath(myFirstFile.getAbsolutePath(), "first"); + assertEquals(path, myFirstFile.getAbsolutePath()); + } + + public void testTwoFilesOrderInDirectory() throws Exception { + String path = FileUtil.findFileInProvidedPath(myTempFile.getAbsolutePath(), "first", "second"); //NON-NLS + assertEquals(path, myFirstFile.getAbsolutePath()); + } + + public void testTwoFilesOrderInDirectory2() throws Exception { + String path = FileUtil.findFileInProvidedPath(myTempFile.getAbsolutePath(), "second", "first"); //NON-NLS + assertEquals(path, mySecondFile.getAbsolutePath()); + } + + public void testTwoFilesOrder() throws Exception { + String path = FileUtil.findFileInProvidedPath(myFirstFile.getAbsolutePath(), "first", "second");//NON-NLS + assertEquals(path, myFirstFile.getAbsolutePath()); + } + + public void testTwoFilesOrder2() throws Exception { + String path = FileUtil.findFileInProvidedPath(myFirstFile.getAbsolutePath(), "second", "first"); //NON-NLS + assertEquals(path, myFirstFile.getAbsolutePath()); + } +} diff --git a/plugins/ant/ant.iml b/plugins/ant/ant.iml index 3ff75610bb1f..54328549d2b6 100644 --- a/plugins/ant/ant.iml +++ b/plugins/ant/ant.iml @@ -9,8 +9,8 @@ - - + + @@ -23,6 +23,7 @@ + diff --git a/plugins/ant/src/com/intellij/lang/ant/ForcedAntFileAttribute.java b/plugins/ant/src/com/intellij/lang/ant/ForcedAntFileAttribute.java index 0b538377e2ee..0f18fd1f793c 100644 --- a/plugins/ant/src/com/intellij/lang/ant/ForcedAntFileAttribute.java +++ b/plugins/ant/src/com/intellij/lang/ant/ForcedAntFileAttribute.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2011 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,14 +15,15 @@ */ package com.intellij.lang.ant; +import com.intellij.buildfiles.ForcedBuildFileAttribute; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Key; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.newvfs.FileAttribute; import com.intellij.openapi.vfs.newvfs.NewVirtualFile; import java.io.DataInputStream; -import java.io.DataOutputStream; import java.io.IOException; /** @@ -31,15 +32,27 @@ import java.io.IOException; */ public class ForcedAntFileAttribute extends FileAttribute { private static final Logger LOG = Logger.getInstance("#com.intellij.lang.ant.ForcedAntFileAttribute"); - + + private static final String ANT_ID = "ant"; + private static final ForcedAntFileAttribute ourAttribute = new ForcedAntFileAttribute(); private static final Key ourAntFileMarker = Key.create("_forced_ant_attribute_"); - + public ForcedAntFileAttribute() { super("_forced_ant_attribute_", 1, true); } - + public static boolean isAntFile(VirtualFile file) { + String id = ForcedBuildFileAttribute.getFrameworkIdOfBuildFile(file); + return ANT_ID.equals(id) || (StringUtil.isEmpty(id) && isAntFileOld(file)); + } + + public static boolean mayBeAntFile(VirtualFile file) { + String id = ForcedBuildFileAttribute.getFrameworkIdOfBuildFile(file); + return StringUtil.isEmpty(id) || ANT_ID.equals(id); + } + + private static boolean isAntFileOld(VirtualFile file) { if (file instanceof NewVirtualFile) { final DataInputStream is = ourAttribute.readAttribute(file); if (is != null) { @@ -59,24 +72,8 @@ public class ForcedAntFileAttribute extends FileAttribute { } return Boolean.TRUE.equals(file.getUserData(ourAntFileMarker)); } - + public static void forceAntFile(VirtualFile file, boolean value) { - if (file instanceof NewVirtualFile) { - final DataOutputStream os = ourAttribute.writeAttribute(file); - try { - try { - os.writeBoolean(value); - } - finally { - os.close(); - } - } - catch (IOException e) { - LOG.error(e); - } - } - else { - file.putUserData(ourAntFileMarker, Boolean.valueOf(value)); - } + ForcedBuildFileAttribute.forceFileToFramework(file, ANT_ID, value); } } diff --git a/plugins/ant/src/com/intellij/lang/ant/dom/AntDomFileDescription.java b/plugins/ant/src/com/intellij/lang/ant/dom/AntDomFileDescription.java index d538de8a1f54..feff55d1ef19 100644 --- a/plugins/ant/src/com/intellij/lang/ant/dom/AntDomFileDescription.java +++ b/plugins/ant/src/com/intellij/lang/ant/dom/AntDomFileDescription.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2010 JetBrains s.r.o. + * Copyright 2000-2011 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. @@ -43,12 +43,13 @@ public class AntDomFileDescription extends AntFileDescription { final XmlDocument document = xmlFile.getDocument(); if (document != null) { final XmlTag tag = document.getRootTag(); + final VirtualFile vFile = xmlFile.getOriginalFile().getVirtualFile(); if (tag != null && ROOT_TAG_NAME.equals(tag.getName()) && tag.getContext() instanceof XmlDocument) { - if (tag.getAttributeValue("name") != null && tag.getAttributeValue("default") != null) { + if (tag.getAttributeValue("name") != null && tag.getAttributeValue("default") != null + && vFile != null && ForcedAntFileAttribute.mayBeAntFile(vFile)) { return true; } } - final VirtualFile vFile = xmlFile.getOriginalFile().getVirtualFile(); if (vFile != null && ForcedAntFileAttribute.isAntFile(vFile)) { return true; } From e36f6fa507101d47ed41e0ed271b7a735b9b460e Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Wed, 13 Apr 2011 14:58:08 +0200 Subject: [PATCH 15/30] IDEA-67323 (New inspection: StringBuffer/StringBuilder is modified but never queried) --- .../siyeh/InspectionGadgetsBundle.properties | 3 + .../com/siyeh/ig/InspectionGadgetsPlugin.java | 1 + ...hedStringBuilderQueryUpdateInspection.java | 374 ++++++++++++++++++ .../MismatchedStringBuilderQueryUpdate.html | 9 + 4 files changed, 387 insertions(+) create mode 100644 plugins/InspectionGadgets/src/com/siyeh/ig/bugs/MismatchedStringBuilderQueryUpdateInspection.java create mode 100644 plugins/InspectionGadgets/src/inspectionDescriptions/MismatchedStringBuilderQueryUpdate.html diff --git a/plugins/InspectionGadgets/src/com/siyeh/InspectionGadgetsBundle.properties b/plugins/InspectionGadgets/src/com/siyeh/InspectionGadgetsBundle.properties index 8ec733d3a713..254c1458b284 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/InspectionGadgetsBundle.properties +++ b/plugins/InspectionGadgets/src/com/siyeh/InspectionGadgetsBundle.properties @@ -1878,3 +1878,6 @@ arrays.hash.code.quickfix=Replace with 'Arrays.hashCode()' method.can.be.variable.arity.method.display.name=Method can be variable arity method method.can.be.variable.arity.method.problem.descriptor=#ref() can be converted to variable arity method convert.to.variable.arity.method.quickfix=Convert to variable arity method +mismatched.string.builder.query.update.display.name=Mismatched query and update of StringBuilder +mismatched.string.builder.updated.problem.descriptor=Contents of {0} #ref are updated, but never queried #loc +mismatched.string.builder.queried.problem.descriptor=Contents of {0} #ref are queried, but never updated #loc diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/InspectionGadgetsPlugin.java b/plugins/InspectionGadgets/src/com/siyeh/ig/InspectionGadgetsPlugin.java index 8188adaf9c1a..59217967ddfd 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/InspectionGadgetsPlugin.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/InspectionGadgetsPlugin.java @@ -552,6 +552,7 @@ public class InspectionGadgetsPlugin implements ApplicationComponent, } m_inspectionClasses.add(MismatchedArrayReadWriteInspection.class); m_inspectionClasses.add(MismatchedCollectionQueryUpdateInspection.class); + m_inspectionClasses.add(MismatchedStringBuilderQueryUpdateInspection.class); m_inspectionClasses.add(MisspelledCompareToInspection.class); m_inspectionClasses.add(MisspelledHashcodeInspection.class); m_inspectionClasses.add(MisspelledEqualsInspection.class); diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/bugs/MismatchedStringBuilderQueryUpdateInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/bugs/MismatchedStringBuilderQueryUpdateInspection.java new file mode 100644 index 000000000000..5cd627952100 --- /dev/null +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/bugs/MismatchedStringBuilderQueryUpdateInspection.java @@ -0,0 +1,374 @@ +/* + * Copyright 2011 Bas Leijdekkers + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.bugs; + +import com.intellij.psi.*; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.util.PsiUtil; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.psiutils.TypeUtils; +import com.siyeh.ig.psiutils.VariableAccessUtils; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; + +import java.util.HashSet; +import java.util.Set; + +public class MismatchedStringBuilderQueryUpdateInspection extends BaseInspection { + + @NonNls + private static final Set returnSelfNames = new HashSet(); + static { + returnSelfNames.add("append"); + returnSelfNames.add("appendCodePoint"); + returnSelfNames.add("delete"); + returnSelfNames.add("deleteCharAt"); + returnSelfNames.add("insert"); + returnSelfNames.add("replace"); + returnSelfNames.add("reverse"); + } + + @Override + @NotNull + public String getID(){ + return "MismatchedQueryAndUpdateOfStringBuilder"; + } + + @Nls + @NotNull + @Override + public String getDisplayName() { + return InspectionGadgetsBundle.message( + "mismatched.string.builder.query.update.display.name"); + } + + @NotNull + @Override + protected String buildErrorString(Object... infos) { + final boolean updated = ((Boolean)infos[0]).booleanValue(); + final PsiType type = (PsiType)infos[1]; //"StringBuilder"; + if(updated){ + return InspectionGadgetsBundle.message( + "mismatched.string.builder.updated.problem.descriptor", + type.getPresentableText()); + } else{ + return InspectionGadgetsBundle.message( + "mismatched.string.builder.queried.problem.descriptor", + type.getPresentableText()); + } + } + + @Override + public boolean isEnabledByDefault() { + return true; + } + + @Override + public boolean runForWholeFile() { + return true; + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new MismatchedQueryAndUpdateOfStringBuilderVisitor(); + } + + private static class MismatchedQueryAndUpdateOfStringBuilderVisitor + extends BaseInspectionVisitor { + + @Override + public void visitField(PsiField field) { + super.visitField(field); + if (!field.hasModifierProperty(PsiModifier.PRIVATE)) { + return; + } + final PsiClass containingClass = PsiUtil.getTopLevelClass(field); + if (!checkVariable(field, containingClass)) { + return; + } + final boolean queried = + stringBuilderContentsAreQueried(field, containingClass); + final boolean updated = + stringBuilderContentsAreUpdated(field, containingClass); + if (queried == updated) { + return; + } + registerFieldError(field, Boolean.valueOf(updated), + field.getType()); + } + + @Override + public void visitLocalVariable(PsiLocalVariable variable) { + super.visitLocalVariable(variable); + final PsiCodeBlock codeBlock = + PsiTreeUtil.getParentOfType(variable, PsiCodeBlock.class); + if (!checkVariable(variable, codeBlock)) { + return; + } + final boolean queried = + stringBuilderContentsAreQueried(variable, codeBlock); + final boolean updated = + stringBuilderContentsAreUpdated(variable, codeBlock); + if (queried == updated) { + return; + } + registerVariableError(variable, Boolean.valueOf(updated), + variable.getType()); + } + + private static boolean checkVariable(PsiVariable variable, + PsiElement context) { + if(context == null){ + return false; + } + if (!TypeUtils.variableHasTypeOrSubtype(variable, + "java.lang.AbstractStringBuilder")) { + return false; + } + if(VariableAccessUtils.variableIsAssigned(variable, context)){ + return false; + } + if(VariableAccessUtils.variableIsAssignedFrom(variable, context)){ + return false; + } + if(VariableAccessUtils.variableIsReturned(variable, context)){ + return false; + } + return !VariableAccessUtils.variableIsUsedInArrayInitializer( + variable, context); + } + + private static boolean stringBuilderContentsAreUpdated( + PsiVariable variable, PsiElement context) { + final PsiExpression initializer = variable.getInitializer(); + if (initializer != null && !isDefaultConstructorCall(initializer)) { + return true; + } + return isStringBuilderUpdated(variable, context); + } + + private static boolean stringBuilderContentsAreQueried( + PsiVariable variable, PsiElement context) { + return isStringBuilderQueried(variable, context); + } + + private static boolean isDefaultConstructorCall( + PsiExpression initializer) { + if (!(initializer instanceof PsiNewExpression)) { + return false; + } + final PsiNewExpression newExpression = + (PsiNewExpression) initializer; + final PsiJavaCodeReferenceElement classReference = + newExpression.getClassReference(); + if (classReference == null) { + return false; + } + final PsiElement target = classReference.resolve(); + if (!(target instanceof PsiClass)) { + return false; + } + final PsiClass aClass = (PsiClass) target; + final String qualifiedName = aClass.getQualifiedName(); + if (!"java.lang.StringBuilder".equals(qualifiedName) && + !"java.lang.StringBuffer".equals(qualifiedName)) { + return false; + } + final PsiExpressionList argumentList = + newExpression.getArgumentList(); + if (argumentList == null) { + return false; + } + final PsiExpression[] arguments = argumentList.getExpressions(); + if (arguments.length == 0) { + return true; + } + final PsiExpression argument = arguments[0]; + final PsiType argumentType = argument.getType(); + return PsiType.INT.equals(argumentType); + } + } + + public static boolean isStringBuilderUpdated(PsiVariable variable, + PsiElement context) { + final StringBuilderUpdateCalledVisitor visitor = + new StringBuilderUpdateCalledVisitor(variable); + context.accept(visitor); + return visitor.isUpdated(); + } + + private static class StringBuilderUpdateCalledVisitor + extends JavaRecursiveElementVisitor { + + @NonNls + private static final Set updateNames = new HashSet(); + static { + updateNames.add("append"); + updateNames.add("appendCodePoint"); + updateNames.add("delete"); + updateNames.add("delete"); + updateNames.add("deleteCharAt"); + updateNames.add("insert"); + updateNames.add("replace"); + updateNames.add("setCharAt"); + } + + private final PsiVariable variable; + boolean updated = false; + + public StringBuilderUpdateCalledVisitor(PsiVariable variable) { + this.variable = variable; + } + + public boolean isUpdated() { + return updated; + } + + @Override + public void visitMethodCallExpression( + PsiMethodCallExpression expression) { + super.visitMethodCallExpression(expression); + if (updated) { + return; + } + super.visitMethodCallExpression(expression); + final PsiReferenceExpression methodExpression = + expression.getMethodExpression(); + final String name = methodExpression.getReferenceName(); + if (!updateNames.contains(name)) { + return; + } + final PsiExpression qualifierExpression = + methodExpression.getQualifierExpression(); + if (hasReferenceToVariable(variable, qualifierExpression)) { + updated = true; + } + } + } + + public static boolean isStringBuilderQueried(PsiVariable variable, + PsiElement context) { + final StringBuilderQueryCalledVisitor visitor = + new StringBuilderQueryCalledVisitor(variable); + context.accept(visitor); + return visitor.isQueried(); + } + + private static class StringBuilderQueryCalledVisitor + extends JavaRecursiveElementVisitor { + + @NonNls + private static final Set queryNames = new HashSet(); + static { + queryNames.add("toString"); + queryNames.add("indexOf"); + queryNames.add("lastIndexOf"); + queryNames.add("capacity"); + queryNames.add("charAt"); + queryNames.add("codePointAt"); + queryNames.add("codePointBefore"); + queryNames.add("codePointCount"); + queryNames.add("equals"); + queryNames.add("getChars"); + queryNames.add("hashCode"); + queryNames.add("length"); + queryNames.add("offsetByCodePoints"); + queryNames.add("subSequence"); + queryNames.add("substring"); + } + + private final PsiVariable variable; + private boolean queried = false; + + private StringBuilderQueryCalledVisitor(PsiVariable variable) { + this.variable = variable; + } + + public boolean isQueried() { + return queried; + } + + @Override public void visitElement(@NotNull PsiElement element){ + if (queried) { + return; + } + super.visitElement(element); + } + + @Override + public void visitMethodCallExpression( + PsiMethodCallExpression expression) { + if (queried) { + return; + } + super.visitMethodCallExpression(expression); + final PsiReferenceExpression methodExpression = + expression.getMethodExpression(); + final String name = methodExpression.getReferenceName(); + if (!queryNames.contains(name)) { + return; + } + final PsiExpression qualifierExpression = + methodExpression.getQualifierExpression(); + if (hasReferenceToVariable(variable, qualifierExpression)) { + queried = true; + } + } + } + + private static boolean hasReferenceToVariable(PsiVariable variable, + PsiElement element) { + if (element instanceof PsiReferenceExpression) { + final PsiReferenceExpression referenceExpression = + (PsiReferenceExpression) element; + final PsiElement target = referenceExpression.resolve(); + if (variable.equals(target)) { + return true; + } + } else if (element instanceof PsiParenthesizedExpression) { + final PsiParenthesizedExpression parenthesizedExpression = + (PsiParenthesizedExpression) element; + final PsiExpression expression = + parenthesizedExpression.getExpression(); + return hasReferenceToVariable(variable, expression); + } else if (element instanceof PsiMethodCallExpression) { + final PsiMethodCallExpression methodCallExpression = + (PsiMethodCallExpression) element; + final PsiReferenceExpression methodExpression = + methodCallExpression.getMethodExpression(); + final String name = methodExpression.getReferenceName(); + if (returnSelfNames.contains(name)) { + return hasReferenceToVariable(variable, + methodExpression.getQualifierExpression()); + } + } else if (element instanceof PsiConditionalExpression) { + final PsiConditionalExpression conditionalExpression = + (PsiConditionalExpression) element; + final PsiExpression thenExpression = + conditionalExpression.getThenExpression(); + if (hasReferenceToVariable(variable, thenExpression)) { + return true; + } + final PsiExpression elseExpression = + conditionalExpression.getElseExpression(); + return hasReferenceToVariable(variable, elseExpression); + } + return false; + } +} diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/MismatchedStringBuilderQueryUpdate.html b/plugins/InspectionGadgets/src/inspectionDescriptions/MismatchedStringBuilderQueryUpdate.html new file mode 100644 index 000000000000..e90bc982f551 --- /dev/null +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/MismatchedStringBuilderQueryUpdate.html @@ -0,0 +1,9 @@ + + +This inspection reports any StringBuilder or StringBuffer fields or variables whose contents are read but not written, +or written but not read. Such mismatched reads and writes are pointless, and probably indicate +dead, incomplete or erroneous code. +

+New in 10.5, Powered by InspectionGadgets + + \ No newline at end of file From d93112a37734a59e110389113d43ca463629d29d Mon Sep 17 00:00:00 2001 From: anna Date: Tue, 12 Apr 2011 21:11:31 +0200 Subject: [PATCH 16/30] for groovy script without classes (EA-26890 - assert: UpdatePsiFileCopyright.checkComments) --- .../idea/copyright/psi/UpdatePsiFileCopyright.java | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) 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 2f83668fe8d7..e8387bf0de14 100644 --- a/plugins/copyright/src/com/maddyhome/idea/copyright/psi/UpdatePsiFileCopyright.java +++ b/plugins/copyright/src/com/maddyhome/idea/copyright/psi/UpdatePsiFileCopyright.java @@ -232,14 +232,12 @@ public abstract class UpdatePsiFileCopyright extends AbstractUpdateCopyright { } } - final int pos; - if (point == null) { - pos = 0; - } - else { + int pos = 0; + if (point != null) { final TextRange textRange = point.getTextRange(); - assert textRange != null : point.getClass(); - pos = textRange.getStartOffset(); + if (textRange != null) { + pos = textRange.getStartOffset(); + } } addAction(new CommentAction(pos, prefix, suffix)); } From acac378713f9f84534e59f144333f7560dbcecfb Mon Sep 17 00:00:00 2001 From: anna Date: Wed, 13 Apr 2011 13:53:09 +0200 Subject: [PATCH 17/30] EA-26903 - NPE: NotInSuperOrThisCallFilterBase.isOK --- .../refactoring/introduceField/IntroduceConstantHandler.java | 2 +- .../refactoring/introduceField/IntroduceFieldHandler.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/java/java-impl/src/com/intellij/refactoring/introduceField/IntroduceConstantHandler.java b/java/java-impl/src/com/intellij/refactoring/introduceField/IntroduceConstantHandler.java index 493bad86ca48..2f230c9def78 100644 --- a/java/java-impl/src/com/intellij/refactoring/introduceField/IntroduceConstantHandler.java +++ b/java/java-impl/src/com/intellij/refactoring/introduceField/IntroduceConstantHandler.java @@ -149,7 +149,7 @@ public class IntroduceConstantHandler extends BaseExpressionToFieldHandler { if (editor != null && editor.getSettings().isVariableInplaceRenameEnabled()) { new InplaceIntroduceConstantPopup(project, editor, parentClass, expr, localVariable, occurences, typeSelectorManager, anchorElement, anchorElementIfAll, - createOccurenceManager(expr, parentClass)).performInplaceIntroduce(); + expr != null ? createOccurenceManager(expr, parentClass) : null).performInplaceIntroduce(); return null; } diff --git a/java/java-impl/src/com/intellij/refactoring/introduceField/IntroduceFieldHandler.java b/java/java-impl/src/com/intellij/refactoring/introduceField/IntroduceFieldHandler.java index 5ec92ee88f56..b70ec6cfb072 100644 --- a/java/java-impl/src/com/intellij/refactoring/introduceField/IntroduceFieldHandler.java +++ b/java/java-impl/src/com/intellij/refactoring/introduceField/IntroduceFieldHandler.java @@ -108,7 +108,7 @@ public class IntroduceFieldHandler extends BaseExpressionToFieldHandler { if (editor != null && editor.getSettings().isVariableInplaceRenameEnabled()) { myInplaceIntroduceFieldPopup = new InplaceIntroduceFieldPopup(localVariable, parentClass, declareStatic, currentMethodConstructor, occurences, expr, typeSelectorManager, editor, - allowInitInMethod, allowInitInMethodIfAll, anchorElement, anchorElementIfAll, createOccurenceManager(expr, parentClass)); + allowInitInMethod, allowInitInMethodIfAll, anchorElement, anchorElementIfAll, expr != null ? createOccurenceManager(expr, parentClass) : null); myInplaceIntroduceFieldPopup.startTemplate(); return null; } From 5df673a98d16cd3d1028bf09075cb4c2943bbe14 Mon Sep 17 00:00:00 2001 From: anna Date: Wed, 13 Apr 2011 14:13:46 +0200 Subject: [PATCH 18/30] correct find for synthetic last parameter (e.g. GWT) --- .../find/findUsages/JavaFindUsagesHandler.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/java/java-impl/src/com/intellij/find/findUsages/JavaFindUsagesHandler.java b/java/java-impl/src/com/intellij/find/findUsages/JavaFindUsagesHandler.java index ac8ce9e62a65..418864150f3f 100644 --- a/java/java-impl/src/com/intellij/find/findUsages/JavaFindUsagesHandler.java +++ b/java/java-impl/src/com/intellij/find/findUsages/JavaFindUsagesHandler.java @@ -117,13 +117,16 @@ public class JavaFindUsagesHandler extends FindUsagesHandler{ for (int i = 0; i < overrides.length; i++) { overrides[i] = (PsiMethod)overrides[i].getNavigationElement(); } - PsiElement[] elementsToSearch = new PsiElement[overrides.length + 1]; - elementsToSearch[0] = parameter; + List elementsToSearch = new ArrayList(overrides.length + 1); + elementsToSearch.add(parameter); int idx = method.getParameterList().getParameterIndex(parameter); - for (int i = 0; i < overrides.length; i++) { - elementsToSearch[i + 1] = overrides[i].getParameterList().getParameters()[idx]; + for (PsiMethod override : overrides) { + final PsiParameter[] parameters = override.getParameterList().getParameters(); + if (idx < parameters.length) { + elementsToSearch.add(parameters[idx]); + } } - return elementsToSearch; + return elementsToSearch.toArray(new PsiElement[elementsToSearch.size()]); } From f4603c5347700cd910490b6915e5ebfa1caff626 Mon Sep 17 00:00:00 2001 From: anna Date: Wed, 13 Apr 2011 14:18:04 +0200 Subject: [PATCH 19/30] EA-26853 - AIOOBE: UnusedParametersInspection$1.visitElement --- .../unusedParameters/UnusedParametersInspection.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/java/java-impl/src/com/intellij/codeInspection/unusedParameters/UnusedParametersInspection.java b/java/java-impl/src/com/intellij/codeInspection/unusedParameters/UnusedParametersInspection.java index 72d563db0647..ee45346a18ed 100644 --- a/java/java-impl/src/com/intellij/codeInspection/unusedParameters/UnusedParametersInspection.java +++ b/java/java-impl/src/com/intellij/codeInspection/unusedParameters/UnusedParametersInspection.java @@ -122,7 +122,9 @@ public class UnusedParametersInspection extends GlobalJavaInspectionTool { final boolean[] found = {false}; for (int i = 0; i < derived.length && !found[0]; i++) { if (!scope.contains(derived[i])) { - PsiParameter psiParameter = derived[i].getParameterList().getParameters()[idx]; + final PsiParameter[] parameters = derived[i].getParameterList().getParameters(); + if (parameters.length >= idx) continue; + PsiParameter psiParameter = parameters[idx]; ReferencesSearch.search(psiParameter, helper.getUseScope(psiParameter), false).forEach(new PsiReferenceProcessorAdapter( new PsiReferenceProcessor() { public boolean execute(PsiReference element) { From dbc0319f166c081f7c5e5dceecd9b17e247ba712 Mon Sep 17 00:00:00 2001 From: anna Date: Wed, 13 Apr 2011 14:20:40 +0200 Subject: [PATCH 20/30] EA-26629 - IAE: JavaCodeStyleManagerImpl.shortenClassReferences --- .../com/intellij/codeInsight/intention/AddAnnotationFix.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/java-impl/src/com/intellij/codeInsight/intention/AddAnnotationFix.java b/java/java-impl/src/com/intellij/codeInsight/intention/AddAnnotationFix.java index b66970767844..0088f7be3f7f 100644 --- a/java/java-impl/src/com/intellij/codeInsight/intention/AddAnnotationFix.java +++ b/java/java-impl/src/com/intellij/codeInsight/intention/AddAnnotationFix.java @@ -175,7 +175,7 @@ public class AddAnnotationFix extends PsiElementBaseIntentionAction implements L } } - PsiAnnotation inserted = modifierList.addAnnotation(myAnnotation); + final @NotNull PsiAnnotation inserted = modifierList.addAnnotation(myAnnotation); if (myPairs != null) { for (PsiNameValuePair pair : myPairs) { inserted.setDeclaredAttributeValue(pair.getName(), pair.getValue()); From 9300034a9a237654330889b2e8049feb89d0a77b Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Wed, 13 Apr 2011 15:34:09 +0200 Subject: [PATCH 21/30] warn in more cases and optimization --- .../MismatchedArrayReadWriteInspection.java | 96 +++++++------------ .../MismatchedArrayReadWrite.java | 15 +++ .../mismatched_array_read_write/expected.xml | 29 ++++++ 3 files changed, 81 insertions(+), 59 deletions(-) diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/bugs/MismatchedArrayReadWriteInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/bugs/MismatchedArrayReadWriteInspection.java index 8865ef364677..0c2b00284509 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/bugs/MismatchedArrayReadWriteInspection.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/bugs/MismatchedArrayReadWriteInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2010 Dave Griffith, Bas Leijdekkers + * Copyright 2003-2011 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. @@ -18,7 +18,6 @@ package com.siyeh.ig.bugs; import com.intellij.psi.*; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; -import com.siyeh.HardcodedMethodConstants; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; @@ -78,11 +77,7 @@ public class MismatchedArrayReadWriteInspection extends BaseInspection{ return; } final PsiClass containingClass = PsiUtil.getTopLevelClass(field); - if(containingClass == null){ - return; - } - final PsiType type = field.getType(); - if(type.getArrayDimensions() == 0){ + if(!checkVariable(field, containingClass)){ return; } final boolean written = @@ -99,11 +94,7 @@ public class MismatchedArrayReadWriteInspection extends BaseInspection{ super.visitLocalVariable(variable); final PsiCodeBlock codeBlock = PsiTreeUtil.getParentOfType(variable, PsiCodeBlock.class); - if(codeBlock == null){ - return; - } - final PsiType type = variable.getType(); - if(type.getArrayDimensions() == 0){ + if(!checkVariable(variable, codeBlock)){ return; } final boolean written = @@ -115,6 +106,28 @@ public class MismatchedArrayReadWriteInspection extends BaseInspection{ registerVariableError(variable, Boolean.valueOf(written)); } + private static boolean checkVariable(PsiVariable variable, + PsiElement context) { + if(context == null){ + return false; + } + final PsiType type = variable.getType(); + if(type.getArrayDimensions() == 0){ + return false; + } + if(VariableAccessUtils.variableIsAssigned(variable, context)){ + return false; + } + if(VariableAccessUtils.variableIsAssignedFrom(variable, context)){ + return false; + } + if(VariableAccessUtils.variableIsReturned(variable, context)){ + return false; + } + return !VariableAccessUtils.variableIsUsedInArrayInitializer( + variable, context); + } + private static boolean arrayContentsAreWritten(PsiVariable variable, PsiElement context){ if(VariableAccessUtils.arrayContentsAreAssigned(variable, context)){ @@ -124,20 +137,7 @@ public class MismatchedArrayReadWriteInspection extends BaseInspection{ if(initializer != null && !isDefaultArrayInitializer(initializer)){ return true; } - if(VariableAccessUtils.variableIsAssigned(variable, context)){ - return true; - } - if(VariableAccessUtils.variableIsAssignedFrom(variable, context)){ - return true; - } - if(VariableAccessUtils.variableIsReturned(variable, context)){ - return true; - } - if(variableIsWrittenAsMethodArgument(variable, context)) { - return true; - } - return VariableAccessUtils.variableIsUsedInArrayInitializer(variable, - context); + return variableIsWrittenAsMethodArgument(variable, context); } private static boolean arrayContentsAreRead(PsiVariable variable, @@ -145,24 +145,7 @@ public class MismatchedArrayReadWriteInspection extends BaseInspection{ if(VariableAccessUtils.arrayContentsAreAccessed(variable, context)){ return true; } - final PsiExpression initializer = variable.getInitializer(); - if(initializer != null && !isDefaultArrayInitializer(initializer)){ - return true; - } - if(VariableAccessUtils.variableIsAssigned(variable, context)){ - return true; - } - if(VariableAccessUtils.variableIsAssignedFrom(variable, context)){ - return true; - } - if(VariableAccessUtils.variableIsReturned(variable, context)){ - return true; - } - if(variableIsReadAsMethodArgument(variable, context)) { - return true; - } - return VariableAccessUtils.variableIsUsedInArrayInitializer(variable, - context); + return variableIsReadAsMethodArgument(variable, context); } private static boolean isDefaultArrayInitializer( @@ -170,21 +153,16 @@ public class MismatchedArrayReadWriteInspection extends BaseInspection{ if (initializer instanceof PsiNewExpression) { final PsiNewExpression newExpression = (PsiNewExpression) initializer; - return newExpression.getArrayInitializer() == null; - } else if (initializer instanceof PsiMethodCallExpression) { - final PsiMethodCallExpression methodCallExpression = - (PsiMethodCallExpression) initializer; - final PsiReferenceExpression methodExpression = - methodCallExpression.getMethodExpression(); - final String methodName = methodExpression.getReferenceName(); - if (!HardcodedMethodConstants.CLONE.equals(methodName)) { - return false; - } - final PsiExpressionList argumentList = - methodCallExpression.getArgumentList(); - final PsiExpression[] expressions = - argumentList.getExpressions(); - return expressions.length == 0; + final PsiArrayInitializerExpression arrayInitializer = + newExpression.getArrayInitializer(); + return arrayInitializer == null || + isDefaultArrayInitializer(arrayInitializer); + } else if (initializer instanceof PsiArrayInitializerExpression) { + final PsiArrayInitializerExpression arrayInitializerExpression = + (PsiArrayInitializerExpression) initializer; + final PsiExpression[] initializers = + arrayInitializerExpression.getInitializers(); + return initializers.length == 0; } return false; } diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/mismatched_array_read_write/MismatchedArrayReadWrite.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/mismatched_array_read_write/MismatchedArrayReadWrite.java index 4feb02aadbb1..2084fd8cb339 100644 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/mismatched_array_read_write/MismatchedArrayReadWrite.java +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/mismatched_array_read_write/MismatchedArrayReadWrite.java @@ -85,4 +85,19 @@ class Test{ array[0][1]++; System.out.println(array[0][1]); } + + void foo1() { + final int[] barzoom = {}; + barzoom[2] = 3; + } + + void foo2() { + final int[] barzoom = new int[]{}; + barzoom[2] = 3; + } + + void foo3(Object[] otherArr) { + Object[] arr = otherArr.clone(); + for (int i = 0; i < 10; i++) arr[i] = i; + } } diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/mismatched_array_read_write/expected.xml b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/mismatched_array_read_write/expected.xml index c322f7e4b800..c1b9fc0fdffa 100644 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/mismatched_array_read_write/expected.xml +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/mismatched_array_read_write/expected.xml @@ -42,4 +42,33 @@ Contents of array <code>foo</code> are written to, but never read #loc + + MismatchedArrayReadWrite.java + 90 + Mismatched read and write of array + Contents of array <code>barzoom</code> are written to, but never read #loc + + + + MismatchedArrayReadWrite.java + 100 + Mismatched read and write of array + Contents of array <code>arr</code> are written to, but never read #loc + + + + MismatchedArrayReadWrite.java + 95 + Mismatched read and write of array + Contents of array <code>barzoom</code> are written to, but never read #loc + + + + MismatchedArrayReadWrite.java + 61 + Mismatched read and write of array + Contents of array <code>rowData</code> are written to, but never read #loc + + + \ No newline at end of file From 6535876a2c1f35e05c3f8f703a55572f15320c2e Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Wed, 13 Apr 2011 15:34:55 +0200 Subject: [PATCH 22/30] warn in more cases, optimization and cleanup --- .../ig/bugs/CollectionQueryCalledVisitor.java | 3 +- .../bugs/CollectionUpdateCalledVisitor.java | 2 +- ...atchedCollectionQueryUpdateInspection.java | 91 ++++++++----------- 3 files changed, 40 insertions(+), 56 deletions(-) diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/bugs/CollectionQueryCalledVisitor.java b/plugins/InspectionGadgets/src/com/siyeh/ig/bugs/CollectionQueryCalledVisitor.java index 580371d7c38c..b41d34758def 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/bugs/CollectionQueryCalledVisitor.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/bugs/CollectionQueryCalledVisitor.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2010 Dave Griffith, Bas Leijdekkers + * Copyright 2003-2011 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. @@ -19,7 +19,6 @@ import com.intellij.psi.*; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; -import java.util.HashSet; import java.util.Set; class CollectionQueryCalledVisitor extends JavaRecursiveElementVisitor{ diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/bugs/CollectionUpdateCalledVisitor.java b/plugins/InspectionGadgets/src/com/siyeh/ig/bugs/CollectionUpdateCalledVisitor.java index 1e2f0e229d11..cc409378e10f 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/bugs/CollectionUpdateCalledVisitor.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/bugs/CollectionUpdateCalledVisitor.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2010 Dave Griffith, Bas Leijdekkers + * Copyright 2003-2011 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. diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/bugs/MismatchedCollectionQueryUpdateInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/bugs/MismatchedCollectionQueryUpdateInspection.java index 1e84180335f5..c083663e2188 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/bugs/MismatchedCollectionQueryUpdateInspection.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/bugs/MismatchedCollectionQueryUpdateInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2010 Dave Griffith, Bas Leijdekkers + * Copyright 2003-2011 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. @@ -37,9 +37,11 @@ import java.awt.*; public class MismatchedCollectionQueryUpdateInspection extends BaseInspection { + @SuppressWarnings({"PublicField"}) public final ExternalizableStringSet queryNames = new ExternalizableStringSet("copyInto", "drainTo", "propertyNames", "save", "store", "write"); + @SuppressWarnings({"PublicField"}) public final ExternalizableStringSet updateNames = new ExternalizableStringSet("add", "clear", "drainTo", "insert", "load", "offer", "poll", "push", "put", "remove", "replace", @@ -140,16 +142,16 @@ public class MismatchedCollectionQueryUpdateInspection if(argumentList == null){ return false; } - final PsiExpression[] expressions = argumentList.getExpressions(); - for(final PsiExpression arg : expressions){ - final PsiType argType = arg.getType(); - if(argType == null){ + final PsiExpression[] arguments = argumentList.getExpressions(); + for(final PsiExpression argument : arguments){ + final PsiType argumentType = argument.getType(); + if(argumentType == null){ return false; } - if(CollectionUtils.isCollectionClassOrInterface(argType)){ + if(CollectionUtils.isCollectionClassOrInterface(argumentType)){ return false; } - if(argType instanceof PsiArrayType){ + if(argumentType instanceof PsiArrayType){ return false; } } @@ -165,11 +167,7 @@ public class MismatchedCollectionQueryUpdateInspection return; } final PsiClass containingClass = PsiUtil.getTopLevelClass(field); - if(containingClass == null){ - return; - } - final PsiType type = field.getType(); - if(!CollectionUtils.isCollectionClassOrInterface(type)){ + if (!checkVariable(field, containingClass)) { return; } final boolean written = @@ -182,16 +180,13 @@ public class MismatchedCollectionQueryUpdateInspection registerFieldError(field, Boolean.valueOf(written)); } - @Override public void visitLocalVariable(@NotNull PsiLocalVariable variable){ + @Override public void visitLocalVariable( + @NotNull PsiLocalVariable variable){ super.visitLocalVariable(variable); final PsiCodeBlock codeBlock = PsiTreeUtil.getParentOfType(variable, PsiCodeBlock.class); - if(codeBlock == null){ - return; - } - final PsiType type = variable.getType(); - if(!CollectionUtils.isCollectionClassOrInterface(type)){ + if (!checkVariable(variable, codeBlock)) { return; } final boolean written = @@ -203,6 +198,29 @@ public class MismatchedCollectionQueryUpdateInspection } } + private boolean checkVariable(PsiVariable variable, + PsiElement context) { + if (context == null) { + return false; + } + final PsiType type = variable.getType(); + if(!CollectionUtils.isCollectionClassOrInterface(type)){ + return false; + } + if(VariableAccessUtils.variableIsAssignedFrom(variable, context)){ + return false; + } + if(VariableAccessUtils.variableIsReturned(variable, context)){ + return false; + } + if(VariableAccessUtils.variableIsPassedAsMethodArgument(variable, + context)){ + return false; + } + return !VariableAccessUtils.variableIsUsedInArrayInitializer( + variable, context); + } + private boolean collectionContentsAreUpdated( PsiVariable variable, PsiElement context){ if(collectionUpdateCalled(variable, context)){ @@ -224,21 +242,7 @@ public class MismatchedCollectionQueryUpdateInspection } } } - if(VariableAccessUtils.variableIsAssigned(variable, context)){ - return true; - } - if(VariableAccessUtils.variableIsAssignedFrom(variable, context)){ - return true; - } - if(VariableAccessUtils.variableIsReturned(variable, context)){ - return true; - } - if(VariableAccessUtils.variableIsPassedAsMethodArgument(variable, - context)){ - return true; - } - return VariableAccessUtils.variableIsUsedInArrayInitializer(variable, - context); + return VariableAccessUtils.variableIsAssigned(variable, context); } private boolean collectionContentsAreQueried( @@ -246,26 +250,7 @@ public class MismatchedCollectionQueryUpdateInspection if(collectionQueryCalled(variable, context)){ return true; } - final PsiExpression initializer = variable.getInitializer(); - if(initializer != null && - !isEmptyCollectionInitializer(initializer)){ - return true; - } - if(collectionQueriedByAssignment(variable, context)){ - return true; - } - if(VariableAccessUtils.variableIsAssignedFrom(variable, context)){ - return true; - } - if(VariableAccessUtils.variableIsReturned(variable, context)){ - return true; - } - if(VariableAccessUtils.variableIsPassedAsMethodArgument(variable, - context)){ - return true; - } - return VariableAccessUtils.variableIsUsedInArrayInitializer(variable, - context); + return collectionQueriedByAssignment(variable, context); } private boolean collectionQueryCalled(PsiVariable variable, From 4b752f21572ebd1989a4405f26a19c6916a3c772 Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Tue, 12 Apr 2011 14:29:01 +0400 Subject: [PATCH 23/30] more robust tests --- .../codeInsight/daemon/impl/FileStatusMap.java | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/FileStatusMap.java b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/FileStatusMap.java index 2c5afda29564..f6f9725ec160 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/FileStatusMap.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/FileStatusMap.java @@ -150,13 +150,22 @@ public class FileStatusMap implements Disposable { } public void markAllFilesDirty() { - assert myAllowDirt; + assertAllowModifications(); LOG.debug("********************************* Mark all dirty"); synchronized (myDocumentToStatusMap) { myDocumentToStatusMap.clear(); } } + private void assertAllowModifications() { + try { + assert myAllowDirt; + } + finally { + myAllowDirt = true; //give next test a chance + } + } + public void markFileUpToDate(@NotNull Document document, @NotNull PsiFile file, int passId) { synchronized(myDocumentToStatusMap){ FileStatus status = myDocumentToStatusMap.get(document); @@ -204,7 +213,7 @@ public class FileStatusMap implements Disposable { } public void markFileScopeDirty(@NotNull Document document, int passId) { - assert myAllowDirt; + assertAllowModifications(); synchronized(myDocumentToStatusMap){ FileStatus status = myDocumentToStatusMap.get(document); if (status == null){ @@ -226,7 +235,7 @@ public class FileStatusMap implements Disposable { } public void markFileScopeDirtyDefensively(@NotNull PsiFile file) { - assert myAllowDirt; + assertAllowModifications(); if (LOG.isDebugEnabled()) { LOG.debug("********************************* Mark dirty file defensively: "+file.getName()); } @@ -242,7 +251,7 @@ public class FileStatusMap implements Disposable { } public void markFileScopeDirty(@NotNull Document document, @NotNull TextRange scope, int fileLength) { - assert myAllowDirt; + assertAllowModifications(); if (LOG.isDebugEnabled()) { LOG.debug("********************************* Mark dirty: "+scope); } From 5b6a43015df4285a8d2ecf029b6def97b80af33e Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Tue, 12 Apr 2011 15:18:03 +0400 Subject: [PATCH 24/30] IDEA-61333 --- .../intellij/psi/impl/source/PsiJavaFileBaseImpl.java | 10 +++++----- .../importDefaultPackage/x/InvalidUse.java | 5 +++++ .../codeInsight/daemon/AdvHighlightingTest.java | 1 + 3 files changed, 11 insertions(+), 5 deletions(-) create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/importDefaultPackage/x/InvalidUse.java diff --git a/java/java-impl/src/com/intellij/psi/impl/source/PsiJavaFileBaseImpl.java b/java/java-impl/src/com/intellij/psi/impl/source/PsiJavaFileBaseImpl.java index c9e5efb517cf..e3b86c672a69 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/PsiJavaFileBaseImpl.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/PsiJavaFileBaseImpl.java @@ -304,11 +304,11 @@ public abstract class PsiJavaFileBaseImpl extends PsiFileImpl implements PsiJava } } - if(classHint == null || classHint.shouldProcess(ElementClassHint.DeclarationKind.PACKAGE)){ - final PsiPackage rootPackage = JavaPsiFacade.getInstance(getProject()).findPackage(""); - processor.handleEvent(JavaScopeProcessorEvent.SET_CURRENT_FILE_CONTEXT, rootPackage); - if(rootPackage != null) rootPackage.processDeclarations(processor, state, null, place); - } + //if(classHint == null || classHint.shouldProcess(ElementClassHint.DeclarationKind.PACKAGE)){ + // final PsiPackage rootPackage = JavaPsiFacade.getInstance(getProject()).findPackage(""); + // processor.handleEvent(JavaScopeProcessorEvent.SET_CURRENT_FILE_CONTEXT, rootPackage); + // if(rootPackage != null) rootPackage.processDeclarations(processor, state, null, place); + //} final PsiImportList importList = getImportList(); final PsiImportStaticStatement[] importStaticStatements = importList.getImportStaticStatements(); diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/importDefaultPackage/x/InvalidUse.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/importDefaultPackage/x/InvalidUse.java new file mode 100644 index 000000000000..f02025936521 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/importDefaultPackage/x/InvalidUse.java @@ -0,0 +1,5 @@ +package x; + +class InvalidUse { + Test t = null; +} diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/AdvHighlightingTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/AdvHighlightingTest.java index 00ddf6298151..de3baf9edbdb 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/AdvHighlightingTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/AdvHighlightingTest.java @@ -79,6 +79,7 @@ public class AdvHighlightingTest extends DaemonAnalyzerTestCase { public void testAlreadyImportedClass() throws Exception { doTest(BASE_PATH+"/alreadyImportedClass/pack/AlreadyImportedClass.java", BASE_PATH+"/alreadyImportedClass", false, false); } public void testImportDefaultPackage() throws Exception { doTest(BASE_PATH+"/importDefaultPackage/x/Usage.java", BASE_PATH+"/importDefaultPackage", false, false); } public void testImportDefaultPackage2() throws Exception { doTest(BASE_PATH+"/importDefaultPackage/x/ImportOnDemandUsage.java", BASE_PATH+"/importDefaultPackage", false, false); } + public void testImportDefaultPackageInvalid() throws Exception { doTest(BASE_PATH+"/importDefaultPackage/x/InvalidUse.java", BASE_PATH+"/importDefaultPackage", false, false); } public void testScopeBased() throws Exception { NamedScope xScope = new NamedScope("xxx", new PatternPackageSet("x..*", PatternPackageSet.SCOPE_SOURCE, null)); From 46b0c56acb44cce2c526dee012288d1e07155b6a Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Wed, 13 Apr 2011 09:49:04 +0400 Subject: [PATCH 25/30] auto import broken again --- .../intellij/codeInsight/daemon/impl/DaemonListeners.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/DaemonListeners.java b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/DaemonListeners.java index 0746219ef8f5..8ceff750faef 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/DaemonListeners.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/DaemonListeners.java @@ -288,7 +288,7 @@ class DaemonListeners implements Disposable { LOG.assertTrue(((UserDataHolderEx)myProject).replace(DAEMON_INITIALIZED, Boolean.TRUE, null), "Daemon listeners already disposed for the project "+myProject); } - boolean canChangeFileSilently(PsiFileSystemItem file) { + boolean canChangeFileSilently(@NotNull PsiFileSystemItem file) { if (cutOperationJustHappened) return false; VirtualFile virtualFile = file.getVirtualFile(); if (virtualFile == null) return false; @@ -302,7 +302,7 @@ class DaemonListeners implements Disposable { return canUndo(virtualFile); } - private boolean canUndo(VirtualFile virtualFile) { + private boolean canUndo(@NotNull VirtualFile virtualFile) { for (FileEditor editor : FileEditorManager.getInstance(myProject).getEditors(virtualFile)) { if (UndoManager.getInstance(myProject).isUndoAvailable(editor)) return true; } @@ -312,13 +312,14 @@ class DaemonListeners implements Disposable { private static enum Result { CHANGED, UNCHANGED, NOT_SURE } + private Result vcsThinksItChanged(VirtualFile virtualFile, Project project) { AbstractVcs activeVcs = ProjectLevelVcsManager.getInstance(project).getVcsFor(virtualFile); if (activeVcs == null) return Result.NOT_SURE; FilePath path = new FilePathImpl(virtualFile); boolean vcsIsThinking = !VcsDirtyScopeManager.getInstance(myProject).whatFilesDirty(Arrays.asList(path)).isEmpty(); - if (vcsIsThinking) return Result.UNCHANGED; // do not modify file which is in the process of updating + if (vcsIsThinking) return Result.NOT_SURE; // do not modify file which is in the process of updating FileStatus status = FileStatusManager.getInstance(project).getStatus(virtualFile); if (status == FileStatus.UNKNOWN) return Result.NOT_SURE; From a077f083063ee1f60239f82c4b9d4a8c173d8c0a Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Wed, 13 Apr 2011 09:49:29 +0400 Subject: [PATCH 26/30] periodically purge internal file queues during batch processing --- .../psi/impl/PsiDocumentManagerImpl.java | 6 ++ .../openapi/command/impl/UndoableGroup.java | 97 ++++++++----------- .../impl/FileDocumentManagerImpl.java | 16 +++ 3 files changed, 64 insertions(+), 55 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/psi/impl/PsiDocumentManagerImpl.java b/platform/lang-impl/src/com/intellij/psi/impl/PsiDocumentManagerImpl.java index f5c0475e4f46..3540a25aaf4c 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/PsiDocumentManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/PsiDocumentManagerImpl.java @@ -33,6 +33,7 @@ import com.intellij.openapi.editor.ex.DocumentEx; import com.intellij.openapi.editor.impl.DocumentImpl; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.fileEditor.FileDocumentManagerAdapter; +import com.intellij.openapi.fileEditor.impl.FileDocumentManagerImpl; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; @@ -630,8 +631,13 @@ public class PsiDocumentManagerImpl extends PsiDocumentManager implements Projec if (commitNecessary && ApplicationManager.getApplication().getCurrentWriteAction(ExternalChangeAction.class) != null){ commitDocument(document); } + // avoid documents piling up during batch processing + if (FileDocumentManagerImpl.areTooManyDocumentsInTheQueue(myUncommittedDocuments)) { + commitAllDocuments(); + } } + private boolean isRelevant(FileViewProvider viewProvider) { VirtualFile virtualFile = viewProvider.getVirtualFile(); return !virtualFile.getFileType().isBinary() && viewProvider.getManager() == myPsiManager && !myPsiManager.getProject().isDisposed(); diff --git a/platform/platform-impl/src/com/intellij/openapi/command/impl/UndoableGroup.java b/platform/platform-impl/src/com/intellij/openapi/command/impl/UndoableGroup.java index 6368daa54529..781af952a723 100644 --- a/platform/platform-impl/src/com/intellij/openapi/command/impl/UndoableGroup.java +++ b/platform/platform-impl/src/com/intellij/openapi/command/impl/UndoableGroup.java @@ -33,9 +33,11 @@ import com.intellij.psi.PsiDocumentManager; import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; import gnu.trove.THashSet; -import org.jetbrains.annotations.NotNull; -import java.util.*; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Set; class UndoableGroup { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.command.impl.UndoableGroup"); @@ -115,71 +117,56 @@ class UndoableGroup { } } - private static void doInBulkMode(@NotNull final Runnable action, @NotNull Collection documents) { - Runnable runnable = action; - for (final DocumentEx document : documents) { - final Runnable oldRunnable = runnable; - runnable = new Runnable() { - @Override - public void run() { - doInBulkMode(oldRunnable, document); - } - }; - } - runnable.run(); - } - private static void doInBulkMode(@NotNull Runnable action, @NotNull DocumentEx document) { - boolean wasInBulkUpdate = document.isInBulkUpdate(); - document.setInBulkUpdate(true); - try { - action.run(); - } - finally { - if (!wasInBulkUpdate) { - document.setInBulkUpdate(false); - } - } - } - - private void doUndoOrRedo(final boolean isUndo) { - Runnable runnable = new Runnable() { + final boolean wrapInBulkUpdate = myActions.size() > 50; + // perform undo action by action, setting bulk update flag if possible + // if multiple consecutive actions share a document, then set the bulk flag only once + final Set bulkDocuments = new THashSet(); + ApplicationManager.getApplication().runWriteAction(new Runnable() { public void run() { try { - for (UndoableAction each : isUndo ? ContainerUtil.iterateBackward(myActions) : myActions) { - if (isUndo) { - each.undo(); + for (final UndoableAction action : isUndo ? ContainerUtil.iterateBackward(myActions) : myActions) { + final Collection newDocuments; + if (wrapInBulkUpdate) { + newDocuments = new THashSet(); + Set documentsToRemoveFromBulk = new THashSet(bulkDocuments); + DocumentReference[] affectedDocuments = action.getAffectedDocuments(); + if (affectedDocuments != null) { + for (DocumentReference affectedDocument : affectedDocuments) { + DocumentEx document = (DocumentEx)affectedDocument.getDocument(); + if (document == null) continue; + documentsToRemoveFromBulk.remove(document); + if (bulkDocuments.contains(document)) continue; + newDocuments.add(document); + document.setInBulkUpdate(true); + } + } + for (DocumentEx document : documentsToRemoveFromBulk) { + document.setInBulkUpdate(false); + } + bulkDocuments.removeAll(documentsToRemoveFromBulk); + bulkDocuments.addAll(newDocuments); } else { - each.redo(); + newDocuments = Collections.emptyList(); } + + if (isUndo) { + action.undo(); + } + else { + action.redo(); + } + } + for (DocumentEx bulkDocument : bulkDocuments) { + bulkDocument.setInBulkUpdate(false); } } catch (UnexpectedUndoException e) { reportUndoProblem(e, isUndo); } } - }; - if (myActions.size() > 50) { - final Collection documents = new THashSet(); - for (UndoableAction action : myActions) { - DocumentReference[] affectedDocuments = action.getAffectedDocuments(); - if (affectedDocuments != null) { - for (DocumentReference affectedDocument : affectedDocuments) { - documents.add((DocumentEx)affectedDocument.getDocument()); - } - } - } - final Runnable oldRunnable = runnable; - runnable = new Runnable() { - @Override - public void run() { - doInBulkMode(oldRunnable, documents); - } - }; - } - - ApplicationManager.getApplication().runWriteAction(runnable); + }); commitAllDocuments(); } diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileDocumentManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileDocumentManagerImpl.java index 294f15809460..be73a336fc01 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileDocumentManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileDocumentManagerImpl.java @@ -44,6 +44,7 @@ import com.intellij.openapi.ui.DialogBuilder; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.Key; +import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.*; import com.intellij.openapi.vfs.ex.dummy.DummyFileSystem; @@ -135,6 +136,11 @@ public class FileDocumentManagerImpl extends FileDocumentManager implements Appl Project project = currentCommand == null ? null : CommandProcessor.getInstance().getCurrentCommandProject(); String lineSeparator = CodeStyleFacade.getInstance(project).getLineSeparator(); document.putUserData(LINE_SEPARATOR_KEY, lineSeparator); + + // avoid documents piling up during batch processing + if (areTooManyDocumentsInTheQueue(myUnsavedDocuments)) { + saveAllDocuments(); + } } } ); @@ -148,6 +154,16 @@ public class FileDocumentManagerImpl extends FileDocumentManager implements Appl return document; } + public static boolean areTooManyDocumentsInTheQueue(Collection documents) { + if (documents.size() > 100) return true; + int totalSize = 0; + for (Document document : documents) { + totalSize += document.getTextLength(); + if (totalSize > 10 * FileUtil.MEGABYTE) return true; + } + return false; + } + private static Document createDocument(final CharSequence text) { return EditorFactory.getInstance().createDocument(text); } From 111b9c255f8c39afff4374736d9f7ba1161d146d Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Wed, 13 Apr 2011 15:23:12 +0400 Subject: [PATCH 27/30] read action --- .../src/com/intellij/slicer/DuplicateMap.java | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/java/java-impl/src/com/intellij/slicer/DuplicateMap.java b/java/java-impl/src/com/intellij/slicer/DuplicateMap.java index f22cd07fc0a7..ea8d0ec455cf 100644 --- a/java/java-impl/src/com/intellij/slicer/DuplicateMap.java +++ b/java/java-impl/src/com/intellij/slicer/DuplicateMap.java @@ -15,6 +15,8 @@ */ package com.intellij.slicer; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.util.Computable; import com.intellij.usageView.UsageInfo; import gnu.trove.THashMap; import gnu.trove.TObjectHashingStrategy; @@ -37,13 +39,17 @@ public class DuplicateMap { }; private final Map myDuplicates = new THashMap(USAGEINFO_EQUALITY); - public SliceNode putNodeCheckDupe(SliceNode node) { - SliceUsage usage = node.getValue(); - SliceNode eq = myDuplicates.get(usage); - if (eq == null) { - myDuplicates.put(usage, node); - } - return eq; + public SliceNode putNodeCheckDupe(final SliceNode node) { + return ApplicationManager.getApplication().runReadAction(new Computable() { + public SliceNode compute() { + SliceUsage usage = node.getValue(); + SliceNode eq = myDuplicates.get(usage); + if (eq == null) { + myDuplicates.put(usage, node); + } + return eq; + } + }); } public void clear() { From 45b06de6bdc9da8b1165f38edd7e0839181fabbf Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Wed, 13 Apr 2011 15:51:55 +0400 Subject: [PATCH 28/30] NPE --- .../src/com/intellij/usages/impl/UsagePreviewPanel.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/usageView/src/com/intellij/usages/impl/UsagePreviewPanel.java b/platform/usageView/src/com/intellij/usages/impl/UsagePreviewPanel.java index aa8e508889d4..aa754c421b36 100644 --- a/platform/usageView/src/com/intellij/usages/impl/UsagePreviewPanel.java +++ b/platform/usageView/src/com/intellij/usages/impl/UsagePreviewPanel.java @@ -110,7 +110,7 @@ public class UsagePreviewPanel extends JPanel implements Disposable { TextRange elementRange = psiElement.getTextRange(); TextRange infoRange = info.getRangeInElement(); - TextRange textRange = elementRange.intersection(infoRange); + TextRange textRange = infoRange == null ? null : elementRange.intersection(infoRange); if (textRange == null) textRange = elementRange; // hack to determine element range to highlight if (psiElement instanceof PsiNamedElement && !(psiElement instanceof PsiFile)) { From 6e02aab0ad30a7a93c175d4eb0bedf2c322e8ee9 Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Wed, 13 Apr 2011 16:20:34 +0400 Subject: [PATCH 29/30] cleanup --- .../daemon/impl/actions/SuppressFix.java | 16 +++++++++------- .../generation/OverrideImplementUtil.java | 2 +- .../intention/PsiElementBaseIntentionAction.java | 2 +- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/actions/SuppressFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/actions/SuppressFix.java index 98d76211c9a2..f647d669e1ca 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/actions/SuppressFix.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/actions/SuppressFix.java @@ -20,7 +20,10 @@ import com.intellij.codeInsight.CodeInsightUtilBase; import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer; import com.intellij.codeInsight.daemon.HighlightDisplayKey; import com.intellij.codeInsight.intention.AddAnnotationFix; -import com.intellij.codeInspection.*; +import com.intellij.codeInspection.InspectionsBundle; +import com.intellij.codeInspection.SuppressIntentionAction; +import com.intellij.codeInspection.SuppressManager; +import com.intellij.codeInspection.SuppressionUtil; import com.intellij.lang.java.JavaLanguage; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.editor.Editor; @@ -33,7 +36,6 @@ import com.intellij.psi.*; import com.intellij.psi.impl.source.jsp.jspJava.JspHolderMethod; import com.intellij.psi.javadoc.PsiDocComment; import com.intellij.psi.javadoc.PsiDocTag; -import com.intellij.psi.javadoc.PsiDocTagValue; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NotNull; @@ -133,14 +135,14 @@ public class SuppressFix extends SuppressIntentionAction { final PsiElement container, final PsiModifierListOwner modifierOwner, final String id) throws IncorrectOperationException { - PsiAnnotation annotation = AnnotationUtil.findAnnotation(modifierOwner, SuppressManagerImpl.SUPPRESS_INSPECTIONS_ANNOTATION_NAME); + PsiAnnotation annotation = AnnotationUtil.findAnnotation(modifierOwner, SuppressManager.SUPPRESS_INSPECTIONS_ANNOTATION_NAME); final PsiAnnotation newAnnotation = createNewAnnotation(project, editor, container, annotation, id); if (newAnnotation != null) { if (annotation != null && annotation.isPhysical()) { annotation.replace(newAnnotation); } else { final PsiNameValuePair[] attributes = newAnnotation.getParameterList().getAttributes(); - new AddAnnotationFix(SuppressManagerImpl.SUPPRESS_INSPECTIONS_ANNOTATION_NAME, modifierOwner, attributes).invoke(project, editor, container.getContainingFile()); + new AddAnnotationFix(SuppressManager.SUPPRESS_INSPECTIONS_ANNOTATION_NAME, modifierOwner, attributes).invoke(project, editor, container.getContainingFile()); } } } @@ -156,8 +158,8 @@ public class SuppressFix extends SuppressIntentionAction { final PsiNameValuePair[] attributes = annotation.getParameterList().getAttributes(); if (attributes.length == 1) { final String suppressedWarnings = attributes[0].getText(); - return JavaPsiFacade.getInstance(project).getElementFactory().createAnnotationFromText("@" + SuppressManagerImpl - .SUPPRESS_INSPECTIONS_ANNOTATION_NAME + "({" + suppressedWarnings + ", \"" + id + "\"})", container); + return JavaPsiFacade.getInstance(project).getElementFactory().createAnnotationFromText("@" + + SuppressManager.SUPPRESS_INSPECTIONS_ANNOTATION_NAME + "({" + suppressedWarnings + ", \"" + id + "\"})", container); } } else { @@ -174,7 +176,7 @@ public class SuppressFix extends SuppressIntentionAction { } else { return JavaPsiFacade.getInstance(project).getElementFactory() - .createAnnotationFromText("@" + SuppressManagerImpl.SUPPRESS_INSPECTIONS_ANNOTATION_NAME + "({\"" + id + "\"})", container); + .createAnnotationFromText("@" + SuppressManager.SUPPRESS_INSPECTIONS_ANNOTATION_NAME + "({\"" + id + "\"})", container); } return null; } diff --git a/java/java-impl/src/com/intellij/codeInsight/generation/OverrideImplementUtil.java b/java/java-impl/src/com/intellij/codeInsight/generation/OverrideImplementUtil.java index 078751201768..043b95770f67 100644 --- a/java/java-impl/src/com/intellij/codeInsight/generation/OverrideImplementUtil.java +++ b/java/java-impl/src/com/intellij/codeInsight/generation/OverrideImplementUtil.java @@ -327,7 +327,7 @@ public class OverrideImplementUtil { } } - public static void annotate(PsiMethod result, String fqn, String... annosToRemove) throws IncorrectOperationException { + public static void annotate(@NotNull PsiMethod result, String fqn, String... annosToRemove) throws IncorrectOperationException { Project project = result.getProject(); AddAnnotationFix fix = new AddAnnotationFix(fqn, result, annosToRemove); if (fix.isAvailable(project, null, result.getContainingFile())) { diff --git a/platform/lang-api/src/com/intellij/codeInsight/intention/PsiElementBaseIntentionAction.java b/platform/lang-api/src/com/intellij/codeInsight/intention/PsiElementBaseIntentionAction.java index 45b68cc920c9..ae3094770312 100644 --- a/platform/lang-api/src/com/intellij/codeInsight/intention/PsiElementBaseIntentionAction.java +++ b/platform/lang-api/src/com/intellij/codeInsight/intention/PsiElementBaseIntentionAction.java @@ -46,7 +46,7 @@ public abstract class PsiElementBaseIntentionAction extends BaseIntentionAction public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { final PsiElement element = getElement(editor, file); - return element == null ? false : isAvailable(project, editor, element); + return element != null && isAvailable(project, editor, element); } @Nullable From e3b1adc82b98d59e5cf63a0720f468f98f9cd812 Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Wed, 13 Apr 2011 17:38:32 +0400 Subject: [PATCH 30/30] trackInvalidation --- .../injected/editor/RangeMarkerWindow.java | 5 +++ .../openapi/editor/ex/RangeMarkerEx.java | 2 +- .../openapi/editor/impl/IntervalTreeImpl.java | 10 +++--- .../impl/PersistentRangeHighlighterImpl.java | 2 +- .../editor/impl/PersistentRangeMarker.java | 16 ++++----- .../openapi/editor/impl/RangeMarkerImpl.java | 36 +++++++++++++------ .../openapi/editor/impl/RangeMarkerTree.java | 10 +++--- 7 files changed, 50 insertions(+), 31 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/injected/editor/RangeMarkerWindow.java b/platform/lang-impl/src/com/intellij/injected/editor/RangeMarkerWindow.java index 82e9098b5200..1bdb50f08bec 100644 --- a/platform/lang-impl/src/com/intellij/injected/editor/RangeMarkerWindow.java +++ b/platform/lang-impl/src/com/intellij/injected/editor/RangeMarkerWindow.java @@ -65,6 +65,11 @@ public class RangeMarkerWindow implements RangeMarkerEx { myHostMarker.trackInvalidation(track); } + @Override + public boolean isTrackInvalidation() { + return myHostMarker.isTrackInvalidation(); + } + ////////////////////////////delegates public void setGreedyToLeft(final boolean greedy) { myHostMarker.setGreedyToLeft(greedy); diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/ex/RangeMarkerEx.java b/platform/platform-impl/src/com/intellij/openapi/editor/ex/RangeMarkerEx.java index 190d6c6c28bb..413a9b6ccdc1 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/ex/RangeMarkerEx.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/ex/RangeMarkerEx.java @@ -35,5 +35,5 @@ public interface RangeMarkerEx extends RangeMarker, MutableInterval, Segment { long getId(); void trackInvalidation(boolean track); - + boolean isTrackInvalidation(); } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/IntervalTreeImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/IntervalTreeImpl.java index a120b780dbb9..a314c8201b4e 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/IntervalTreeImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/IntervalTreeImpl.java @@ -45,7 +45,7 @@ public abstract class IntervalTreeImpl extends RedBla private final ReferenceQueue myReferenceQueue = new ReferenceQueue(); private int deadReferenceCount; - protected class IntervalNode extends Node implements MutableInterval/*, Iterable, Iterator*/ { + protected class IntervalNode extends Node implements MutableInterval { private volatile int myStart; private volatile int myEnd; private volatile boolean isValid = true; @@ -181,11 +181,11 @@ public abstract class IntervalTreeImpl extends RedBla return myEnd; } - public IntervalTreeImpl getTree() { + public IntervalTreeImpl getTree() { return IntervalTreeImpl.this; } - } + private void pushDeltaFromRoot(IntervalNode node) { if (normalized) return; if (node != null) { @@ -437,7 +437,7 @@ public abstract class IntervalTreeImpl extends RedBla } } - public IntervalNode addInterval(@NotNull T interval, int start, int end, boolean greedyToLeft, boolean greedyToRight, int layer) { + public IntervalTreeImpl.IntervalNode addInterval(@NotNull T interval, int start, int end, boolean greedyToLeft, boolean greedyToRight, int layer) { try { l.writeLock().lock(); checkMax(true); @@ -502,7 +502,7 @@ public abstract class IntervalTreeImpl extends RedBla int maxRightStart = r.second; if (!root.isValid()) { allValid.set(false); - if (assertInvalid) assert false : (T)root; + if (assertInvalid) assert false : root; return Trinity.create(Math.min(minLeftStart, minRightStart), Math.max(maxLeftStart, maxRightStart), Math.max(maxRightEnd, maxLeftEnd)); } IntervalNode parent = root.getParent(); diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/PersistentRangeHighlighterImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/PersistentRangeHighlighterImpl.java index 55b3bb8c3d9a..c74afce3ccff 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/PersistentRangeHighlighterImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/PersistentRangeHighlighterImpl.java @@ -45,7 +45,7 @@ class PersistentRangeHighlighterImpl extends RangeHighlighterImpl implements Ran if (PersistentRangeMarkerUtil.shouldTranslateViaDiff(event, this)) { setLine(event.translateLineViaDiff(getLine())); if (getLine() < 0 || getLine() >= getDocument().getLineCount()) { - invalidate(); + invalidate(e); } else { DocumentEx document = getDocument(); diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/PersistentRangeMarker.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/PersistentRangeMarker.java index 1c7d4a464300..61f9a3e365c2 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/PersistentRangeMarker.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/PersistentRangeMarker.java @@ -36,23 +36,23 @@ class PersistentRangeMarker extends RangeMarkerImpl { PersistentRangeMarker(DocumentEx document, int startOffset, int endOffset, boolean register) { super(document, startOffset, endOffset, register); - storeLinesAndCols(); + storeLinesAndCols(null); } - private void storeLinesAndCols() { + private void storeLinesAndCols(DocumentEvent e) { // document might have been changed already if (getStartOffset() < myDocument.getTextLength()) { myStartLine = myDocument.getLineNumber(getStartOffset()); myStartColumn = getStartOffset() - myDocument.getLineStartOffset(myStartLine); if (myStartColumn < 0) { - invalidate(); + invalidate(e); } } if (getEndOffset() < myDocument.getTextLength()) { myEndLine = myDocument.getLineNumber(getEndOffset()); myEndColumn = getEndOffset() - myDocument.getLineStartOffset(myEndLine); if (myEndColumn < 0) { - invalidate(); + invalidate(e); } } } @@ -63,7 +63,7 @@ class PersistentRangeMarker extends RangeMarkerImpl { if (PersistentRangeMarkerUtil.shouldTranslateViaDiff(event, this)){ myStartLine = event.translateLineViaDiffStrict(myStartLine); if (myStartLine < 0 || myStartLine >= getDocument().getLineCount()){ - invalidate(); + invalidate(e); } else{ setIntervalStart(getDocument().getLineStartOffset(myStartLine) + myStartColumn); @@ -71,7 +71,7 @@ class PersistentRangeMarker extends RangeMarkerImpl { myEndLine = event.translateLineViaDiffStrict(myEndLine); if (myEndLine < 0 || myEndLine >= getDocument().getLineCount()){ - invalidate(); + invalidate(e); } else{ setIntervalEnd(getDocument().getLineStartOffset(myEndLine) + myEndColumn); @@ -80,11 +80,11 @@ class PersistentRangeMarker extends RangeMarkerImpl { else { super.changedUpdateImpl(e); if (isValid()){ - storeLinesAndCols(); + storeLinesAndCols(e); } } if (getEndOffset() < getStartOffset() || getEndOffset() > getDocument().getTextLength()) { - invalidate(); + invalidate(e); } } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/RangeMarkerImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/RangeMarkerImpl.java index 2f52d3a489e9..c00edb2180d8 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/RangeMarkerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/RangeMarkerImpl.java @@ -19,7 +19,9 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.event.DocumentEvent; import com.intellij.openapi.editor.ex.DocumentEx; import com.intellij.openapi.editor.ex.RangeMarkerEx; +import com.intellij.openapi.util.Key; import com.intellij.openapi.util.UserDataHolderBase; +import com.intellij.util.Processor; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -30,7 +32,6 @@ public class RangeMarkerImpl extends UserDataHolderBase implements RangeMarkerEx protected final DocumentEx myDocument; RangeMarkerTree.RMNode myNode; - private boolean myTrackInvalidation; private final long myId; //private static long counter; @@ -91,8 +92,21 @@ public class RangeMarkerImpl extends UserDataHolderBase implements RangeMarkerEx return intervalEnd() + (node == null ? 0 : node.computeDeltaUpToRoot()); } - public void invalidate() { + public void invalidate(final DocumentEvent e) { setValid(false); + RangeMarkerTree.RMNode node = myNode; + + if (node != null) { + node.processAliveKeys(new Processor() { + @Override + public boolean process(RangeMarkerEx markerEx) { + if (markerEx.isTrackInvalidation()) { + LOG.error("Range marker invalidated: "+markerEx +"; say thanks to the "+e); + } + return true; + } + }); + } } @NotNull @@ -133,7 +147,7 @@ public class RangeMarkerImpl extends UserDataHolderBase implements RangeMarkerEx if (intervalStart() > intervalEnd() || intervalStart() < 0 || intervalEnd() > docLength - e.getNewLength() + e.getOldLength()) { LOG.error("RangeMarker" + (isGreedyToLeft() ? "[" : "(") + oldStart + ", " + oldEnd + (isGreedyToRight() ? "]" : ")") + " is invalid before update. Event = " + e + ". Doc length=" + docLength + "; "+getClass()); - invalidate(); + invalidate(e); return; } changedUpdateImpl(e); @@ -143,7 +157,7 @@ public class RangeMarkerImpl extends UserDataHolderBase implements RangeMarkerEx "old doc length=" + docLength + "; real doc length = "+myDocument.getTextLength()+ "; "+getClass()+"." + " Before update: '"+markerBefore+"'; After update: '"+this+"'"); - invalidate(); + invalidate(e); } } @@ -192,7 +206,7 @@ public class RangeMarkerImpl extends UserDataHolderBase implements RangeMarkerEx return; } - invalidate(); + invalidate(e); } private void processIfOnePoint(DocumentEvent e) { @@ -200,7 +214,7 @@ public class RangeMarkerImpl extends UserDataHolderBase implements RangeMarkerEx int oldLength = e.getOldLength(); int oldEnd = offset + oldLength; if (offset < intervalStart() && intervalStart() < oldEnd) { - invalidate(); + invalidate(e); return; } @@ -237,17 +251,17 @@ public class RangeMarkerImpl extends UserDataHolderBase implements RangeMarkerEx return node != null && node.isValid(); } + private static final Key TRACK_INVALIDATION_KEY = new Key("TRACK_INVALIDATION_KEY"); @Override public void trackInvalidation(boolean track) { - myTrackInvalidation = track; + putUserData(TRACK_INVALIDATION_KEY, track ? Boolean.TRUE : null); + } + public boolean isTrackInvalidation() { + return getUserData(TRACK_INVALIDATION_KEY) == Boolean.TRUE; } @Override public boolean setValid(boolean value) { - if (!value && myTrackInvalidation) { - LOG.error("Range marker invalidated"); - } - RangeMarkerTree.RMNode node = myNode; return node == null || node.setValid(value); } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/RangeMarkerTree.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/RangeMarkerTree.java index 5a15cb0e0fa2..a5ba44e06006 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/RangeMarkerTree.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/RangeMarkerTree.java @@ -82,10 +82,10 @@ public class RangeMarkerTree extends IntervalTreeImpl.RMNode addInterval(@NotNull T interval, int start, int end, boolean greedyToLeft, boolean greedyToRight, int layer) { RangeMarkerImpl marker = (RangeMarkerImpl)interval; marker.setValid(true); - RMNode node = (RMNode)super.addInterval(interval, start, end, greedyToLeft, greedyToRight, layer); + RangeMarkerTree.RMNode node = (RMNode)super.addInterval(interval, start, end, greedyToLeft, greedyToRight, layer); ((RangeMarkerImpl)interval).myNode = node; checkBelongsToTheTree(interval, true); @@ -106,8 +106,8 @@ public class RangeMarkerTree extends IntervalTreeImpl.RMNode lookupNode(@NotNull T key) { + return (RMNode)((RangeMarkerImpl)key).myNode; } public class RMNode extends IntervalNode { @@ -131,7 +131,7 @@ public class RangeMarkerTree extends IntervalTreeImpl