From e782c57d74000722f9db4c9426317410520670c6 Mon Sep 17 00:00:00 2001 From: Tor Norbye Date: Thu, 18 Sep 2014 11:43:07 -0700 Subject: Snapshot idea/138.2210 from git://git.jetbrains.org/idea/community.git Change-Id: I8f0204d7887ee78cf1fd8c09f936c5afff0edd2f --- .gitignore | 1 + .idea/codeStyleSettings.xml | 6 +- .idea/libraries/JSch.xml | 6 +- .idea/libraries/asm_tools.xml | 11 + .idea/libraries/bouncy_castle.xml | 10 + .idea/libraries/builder_model.xml | 11 + .idea/libraries/commons_compress.xml | 9 + .idea/libraries/easymock_tools.xml | 10 + .idea/libraries/freemarker_2_3_20.xml | 9 + .idea/libraries/guava_tools.xml | 11 + .idea/libraries/javawriter.xml | 11 + .idea/libraries/kxml2.xml | 9 + .idea/libraries/lombok_ast.xml | 16 + .idea/libraries/mockito.xml | 11 + .idea/libraries/winp.xml | 5 +- .idea/modules.xml | 27 +- .idea/vcs.xml | 2 + bin/WinLauncher/WinLauncher.exe | Bin 822784 -> 822784 bytes bin/WinLauncher/WinLauncher64.exe | Bin 839168 -> 839168 bytes bin/scripts/unix/idea.sh | 2 +- build/conf/mac/Contents/Info.plist | 2 - build/conf/mac/Contents/MacOS/idea | Bin 85564 -> 85588 bytes build/scripts/layouts.gant | 13 +- build/scripts/libLicenses.gant | 5 +- community-tests/src/tests/testGroups.properties | 3 - getPlugins.bat | 2 + getPlugins.sh | 2 + .../intellij/images/editor/impl/ImageEditorUI.java | 53 +- .../images/options/impl/OptionsManagerImpl.java | 7 +- java/compiler/impl/compiler-impl.iml | 1 - .../intellij/compiler/actions/CompileAction.java | 2 +- .../com/intellij/compiler/server/BuildManager.java | 26 +- .../artifacts/JarArtifactFromModulesDialog.form | 4 +- .../artifacts/JarArtifactFromModulesDialog.java | 2 +- .../packaging/impl/artifacts/JarArtifactType.java | 2 +- .../impl/elements/PackagingElementFactoryImpl.java | 2 +- .../com/intellij/debugger/DebugUIEnvironment.java | 3 - .../debugger/DefaultDebugUIEnvironment.java | 19 - .../debugger/actions/AdjustArrayRangeAction.java | 54 +- .../intellij/debugger/actions/DebuggerAction.java | 20 + .../actions/JavaMarkObjectActionHandler.java | 7 +- .../actions/JavaReferringObjectsValue.java | 1 + .../actions/ObjectMarkupPropertiesDialog.java | 5 +- .../intellij/debugger/actions/PopFrameAction.java | 213 +++- .../com/intellij/debugger/actions/ViewAsGroup.java | 11 +- .../debugger/engine/DebugProcessEvents.java | 42 +- .../intellij/debugger/engine/DebugProcessImpl.java | 93 +- .../com/intellij/debugger/engine/JVMNameUtil.java | 21 +- .../intellij/debugger/engine/JavaDebugProcess.java | 56 +- .../debugger/engine/JavaDebuggerEvaluator.java | 6 +- .../intellij/debugger/engine/JavaStaticGroup.java | 8 + .../com/intellij/debugger/engine/JavaValue.java | 13 +- .../debugger/engine/JavaValueModifier.java | 12 +- .../engine/evaluation/EvaluationContextImpl.java | 8 + .../expression/EvaluatorBuilderImpl.java | 78 +- .../evaluation/expression/MethodEvaluator.java | 8 +- .../expression/NewClassInstanceEvaluator.java | 6 +- .../expression/UnsupportedExpressionException.java | 28 + .../debugger/impl/DebuggerContextUtil.java | 7 +- .../debugger/impl/DebuggerManagerImpl.java | 3 +- .../intellij/debugger/impl/DebuggerSession.java | 38 +- .../intellij/debugger/impl/DebuggerUtilsEx.java | 23 +- .../debugger/impl/JavaEditorTextProviderImpl.java | 11 +- .../com/intellij/debugger/impl/PositionUtil.java | 1 + .../settings/DebuggerDataViewsConfigurable.java | 31 +- .../debugger/settings/DebuggerSettings.java | 8 + .../settings/DebuggerSteppingConfigurable.java | 51 + .../intellij/debugger/ui/DebuggerEditorImpl.java | 2 +- .../intellij/debugger/ui/DebuggerSessionTab.java | 28 +- .../com/intellij/debugger/ui/EvaluationDialog.java | 9 +- .../intellij/debugger/ui/JavaDebuggerSupport.java | 8 +- .../src/com/intellij/debugger/ui/ValueHint.java | 3 +- .../ui/impl/watch/ArgumentValueDescriptorImpl.java | 5 + .../debugger/ui/impl/watch/CompilingEvaluator.java | 210 +++- .../ui/impl/watch/EvaluationDescriptor.java | 92 +- .../ui/impl/watch/NodeDescriptorFactoryImpl.java | 9 +- .../debugger/ui/impl/watch/NodeManagerImpl.java | 8 +- .../impl/watch/UserExpressionDescriptorImpl.java | 10 +- .../ui/impl/watch/WatchItemDescriptor.java | 7 +- .../debugger/ui/tree/render/ArrayRenderer.java | 12 +- .../java/debugger/JavaDebuggerEditorsProvider.java | 17 +- .../src/com/intellij/debugger/SourcePosition.java | 4 +- .../com/intellij/debugger/engine/DebugProcess.java | 17 +- .../debugger/engine/JSR45PositionManager.java | 7 +- .../actions/JavaRerunFailedTestsAction.java | 7 +- .../application/ApplicationConfiguration.java | 46 +- .../BaseJavaApplicationCommandLineState.java | 70 ++ .../execution/impl/DefaultJavaProgramRunner.java | 12 +- .../jar/JarApplicationCommandLineState.java | 43 + .../execution/jar/JarApplicationConfigurable.form | 49 + .../execution/jar/JarApplicationConfigurable.java | 93 ++ .../execution/jar/JarApplicationConfiguration.java | 223 ++++ .../jar/JarApplicationConfigurationProducer.java | 63 ++ .../jar/JarApplicationConfigurationType.java | 54 + .../jar/JarApplicationDebuggerRunner.java | 37 + .../intellij/execution/junit/InheritorChooser.java | 8 +- .../execution/runners/ProcessProxyFactoryImpl.java | 7 +- .../execution/util/JavaParametersUtil.java | 3 +- .../configurations/CommandLineBuilder.java | 2 +- .../impl/ui/libraries/LibraryOptionsPanel.form | 2 +- .../impl/ui/libraries/LibraryOptionsPanel.java | 2 +- .../com/intellij/ide/RecentProjectsManager.java | 15 +- .../intellij/ide/actions/ImportModuleAction.java | 2 +- .../ide/palette/impl/PaletteComponentList.java | 401 -------- .../ide/palette/impl/PaletteContentWindow.java | 114 -- .../ide/palette/impl/PaletteGroupHeader.java | 211 ---- .../intellij/ide/palette/impl/PaletteManager.java | 229 ----- .../intellij/ide/palette/impl/PaletteWindow.java | 314 ------ .../ide/projectWizard/ProjectTypeStep.form | 9 +- .../ide/projectWizard/ProjectTypeStep.java | 59 +- .../configuration/JavaResourceRootEditHandler.java | 5 +- .../roots/ui/configuration/ModuleEditor.java | 4 +- .../ProjectStructureConfigurable.java | 4 +- .../DefaultLibraryRootsComponentDescriptor.java | 39 +- .../roots/LanguageLevelModuleExtension.java | 3 + .../InferredAnnotationsManagerImpl.java | 10 + .../daemon/impl/analysis/HighlightMethodUtil.java | 2 +- .../RedundantLambdaCodeBlockInspection.java | 31 + .../codeInspection/bytecodeAnalysis/Analysis.java | 8 +- .../BytecodeAnalysisConverter.java | 27 +- .../bytecodeAnalysis/BytecodeAnalysisIndex.java | 2 +- .../bytecodeAnalysis/ClassDataIndexer.java | 129 +-- .../codeInspection/bytecodeAnalysis/Combined.java | 335 ++++-- .../codeInspection/bytecodeAnalysis/Contracts.java | 7 +- .../codeInspection/bytecodeAnalysis/Data.java | 200 ++-- .../bytecodeAnalysis/NullableMethodAnalysis.java | 423 ++++++++ .../bytecodeAnalysis/Parameters.java | 3 +- .../bytecodeAnalysis/ProjectBytecodeAnalysis.java | 84 +- .../codeInspection/bytecodeAnalysis/Solver.java | 6 +- .../bytecodeAnalysis/asm/AnalyzerExt.java | 446 ++++++++ .../bytecodeAnalysis/asm/FramelessAnalyzer.java | 46 - .../bytecodeAnalysis/asm/InterpreterExt.java | 32 + .../bytecodeAnalysis/asm/LeakingParameters.java | 9 +- .../bytecodeAnalysis/asm/LiteAnalyzerExt.java | 222 ++++ .../asm/LiteFramelessAnalyzer.java | 6 +- .../bytecodeAnalysis/asm/Subroutine.java | 74 ++ .../codeInspection/dataFlow/ContractInference.java | 41 +- .../dataFlow/ControlFlowAnalyzer.java | 4 + .../dataFlow/StandardInstructionVisitor.java | 4 +- .../dataFlow/value/DfaExpressionFactory.java | 4 +- .../equalsAndHashcode/EqualsAndHashcode.java | 133 --- .../equalsAndHashcode/EqualsAndHashcodeBase.java | 134 +++ .../codeInsight/ExpectedTypesProvider.java | 28 +- .../ExternalAnnotationsLineMarkerProvider.java | 11 +- .../src/com/intellij/codeInsight/TailTypes.java | 6 + .../codeInsight/completion/JavaCompletionUtil.java | 2 +- .../completion/JavaDocCompletionContributor.java | 11 +- .../completion/PreferByKindWeigher.java | 6 +- .../ChangeMethodSignatureFromUsageFix.java | 6 + .../impl/quickfix/CreateMethodFromUsageFix.java | 2 +- .../impl/quickfix/CreateStaticMethodQuickFix.java | 81 ++ .../daemon/impl/quickfix/MoveClassToModuleFix.java | 4 +- .../surroundWith/JavaWithTryFinallySurrounder.java | 16 +- .../HighlightExceptionsHandlerFactory.java | 5 +- .../intention/impl/AddAnnotationIntention.java | 11 +- .../equalsAndHashcode/EqualsAndHashcode.java | 72 ++ .../InferNullityAnnotationsAction.java | 2 +- .../find/findUsages/JavaFindUsagesHandler.java | 207 ++-- .../ide/JavaLanguageCodeStyleSettingsProvider.java | 92 +- .../ide/actions/CreatePackageInfoAction.java | 4 +- .../impl/ClassesTreeStructureProvider.java | 5 +- .../ClassHierarchyScopeDescriptor.java | 30 +- .../src/com/intellij/jarFinder/FindJarFix.java | 4 +- .../impl/JavaAwareProjectJdkTableImpl.java | 13 +- .../impl/LanguageLevelProjectExtensionImpl.java | 4 +- .../psi/codeStyle/JavaCodeStyleSettings.java | 1 + .../formatter/java/JavaSpacePropertyProcessor.java | 2 +- .../java/wrap/impl/JavaChildWrapArranger.java | 26 +- .../file/PsiPackageImplementationHelperImpl.java | 52 +- .../extractMethod/AbstractExtractDialog.java | 23 +- .../extractMethod/ExtractMethodDialog.java | 5 +- .../extractMethod/ExtractMethodProcessor.java | 69 +- .../ExtractLightMethodObjectHandler.java | 254 +++++ .../ExtractMethodObjectDialog.java | 5 +- .../ExtractMethodObjectHandler.java | 47 +- .../ExtractMethodObjectProcessor.java | 66 +- .../InheritanceToDelegationDialog.java | 2 +- .../InheritanceToDelegationHandler.java | 14 +- .../intellij/refactoring/util/RefactoringUtil.java | 13 +- .../psi/search/searches/ClassInheritorsSearch.java | 14 +- .../src/com/intellij/psi/GenericsUtil.java | 4 +- .../src/com/intellij/psi/LambdaUtil.java | 11 + .../src/com/intellij/psi/PsiPackage.java | 4 +- .../src/com/intellij/psi/util/ClassUtil.java | 30 +- .../com/intellij/psi/util/MethodSignatureBase.java | 1 + .../src/com/intellij/psi/util/PsiUtil.java | 5 +- .../folding/impl/JavaFoldingBuilderBase.java | 5 + .../codeInsight/javadoc/JavaDocInfoGenerator.java | 44 +- .../core/CorePsiPackageImplementationHelper.java | 18 +- .../com/intellij/psi/NonClasspathClassFinder.java | 2 +- .../com/intellij/psi/impl/PsiSubstitutorImpl.java | 2 +- .../intellij/psi/impl/compiled/ClsFileImpl.java | 27 +- .../com/intellij/psi/impl/file/PsiPackageImpl.java | 21 +- .../impl/file/PsiPackageImplementationHelper.java | 18 +- .../com/intellij/psi/impl/source/PsiFieldImpl.java | 11 +- .../intellij/psi/impl/source/PsiParameterImpl.java | 20 + .../impl/source/resolve/PsiResolveHelperImpl.java | 13 +- .../resolve/graphInference/InferenceSession.java | 213 ++-- .../resolve/graphInference/InferenceVariable.java | 21 +- .../LambdaExpressionCompatibilityConstraint.java | 6 +- .../PsiMethodReferenceCompatibilityConstraint.java | 6 +- .../constraints/StrictSubtypingConstraint.java | 18 +- .../source/tree/java/PsiLambdaExpressionImpl.java | 58 +- .../JavaMethodsConflictResolver.java | 5 +- .../NoSemicolonInsideParentheses-out.java | 7 + .../smartType/NoSemicolonInsideParentheses.java | 7 + .../CaptureWildcardFromUnboundCaptureWildcard.java | 9 + .../genericsHighlighting/IDEA106985.java | 15 + .../genericsHighlighting/IDEA114797.java | 15 + .../IntersectionTypeStrictSubtypingConstraint.java | 15 + ...ResolveDependingOnAlreadyResolvedVariables.java | 17 + .../IncompatibleBoundsFromAssignment.java | 15 + .../lambda/highlighting/Ambiguity1.java | 2 +- .../lambda/highlighting/AmbiguityRawGenerics.java | 2 +- .../lambda/highlighting/EffectiveFinal.java | 34 + .../NoReturnTypeResolutionForThrownException.java | 2 +- .../DeepNestedLambdaExpressionsNoFormalParams.java | 70 ++ .../lambda/newLambda/IDEA126778.java | 27 + .../lambda/newLambda/NestedLambdaExpressions1.java | 27 + .../NestedLambdaExpressionsNoFormalParams.java | 55 + .../NestedLambdaExpressionsNoFormalParams1.java | 26 + ...aExpressionsNoFormalParamsStopAtStandalone.java | 32 + ...sizedExpressionsDuringConstrainsCollection.java | 13 + .../lambda/newMethodRef/IDEA127275_.java | 2 +- .../lambda/overloadResolution/IDEA102800.java | 31 + ...nentToApplicabilityOfExplicitlyTypedLambda.java | 17 + ...dValueCompatibilityOfImplicitlyTypedLambda.java | 23 + .../afterDisjunctionType.java | 15 + .../beforeDisjunctionType.java | 15 + .../afterVoidValueChangedNoConflict.java | 16 + .../beforeVoidValueChanged.java | 19 + .../beforeVoidValueChangedNoConflict.java | 18 + .../java/SurroundWithTryFinallyUsingIndents.java | 5 + .../SurroundWithTryFinallyUsingIndents_after.java | 9 + .../javadocIG/clickableFieldReference.html | 3 + .../javadocIG/clickableFieldReference.java | 7 + .../codeInsight/javadocIG/enumConstantOrdinal.html | 5 +- .../javadocIG/enumConstantOrdinal_quick.html | 3 + .../annotations/java/awt/annotations.xml | 98 ++ .../java/beans/beancontext/annotations.xml | 7 + .../annotations/java/io/annotations.xml | 51 + .../annotations/java/lang/annotations.xml | 102 ++ .../annotations/java/lang/invoke/annotations.xml | 154 +++ .../annotations/java/net/annotations.xml | 64 ++ .../annotations/java/security/annotations.xml | 18 + .../annotations/java/sql/annotations.xml | 9 + .../annotations/java/util/annotations.xml | 86 ++ .../java/util/concurrent/locks/annotations.xml | 18 + .../annotations/javax/swing/annotations.xml | 88 ++ .../org/apache/commons/collections/annotations.xml | 19 + .../apache/commons/collections/map/annotations.xml | 27 + .../org/apache/commons/lang/annotations.xml | 256 +++++ .../org/apache/commons/lang/enum/annotations.xml | 12 + .../org/apache/commons/lang/enums/annotations.xml | 12 + .../apache/commons/lang/exception/annotations.xml | 21 + .../org/apache/commons/lang/math/annotations.xml | 7 + .../org/apache/commons/lang/text/annotations.xml | 29 + .../org/apache/commons/lang/time/annotations.xml | 4 + .../org/apache/velocity/annotations.xml | 3 + .../org/apache/velocity/app/annotations.xml | 3 + .../org/apache/velocity/app/event/annotations.xml | 6 + .../velocity/app/event/implement/annotations.xml | 17 + .../org/apache/velocity/context/annotations.xml | 12 + .../org/apache/velocity/io/annotations.xml | 3 + .../org/apache/velocity/runtime/annotations.xml | 22 + .../velocity/runtime/directive/annotations.xml | 15 + .../velocity/runtime/parser/node/annotations.xml | 45 + .../velocity/runtime/resource/annotations.xml | 12 + .../runtime/resource/loader/annotations.xml | 6 + .../org/apache/velocity/util/annotations.xml | 15 + .../velocity/util/introspection/annotations.xml | 34 + .../dataFlow/fixture/RootThrowableCause.java | 17 + .../AnonymousClassParams.java | 8 + .../extractMethodObject4Debugger/InnerClass.java | 30 + .../InvokeReturnType.java | 9 + .../OffsetsAtCallSite.java | 44 + .../extractMethodObject4Debugger/ResultExpr.java | 25 + .../ResultStatements.java | 25 + .../SimpleGeneration.java | 5 + .../completion/SmartType18CompletionTest.java | 18 +- .../completion/SmartTypeCompletionTest.java | 2 + .../daemon/GenericsHighlightingTest.java | 3 + .../lambda/ConstraintsInferenceMiscTest.java | 6 +- .../daemon/lambda/Diamond8HighlightingTest.java | 4 +- .../lambda/ExceptionVariablesInferenceTest.java | 4 +- ...tionalExpressionIncompleteHighlightingTest.java | 2 +- ...FunctionalTypeWildcardParameterizationTest.java | 2 +- .../daemon/lambda/GenericsHighlighting8Test.java | 16 +- .../lambda/GraphInferenceHighlightingTest.java | 12 +- .../daemon/lambda/LambdaHighlightingTest.java | 2 +- .../daemon/lambda/LambdaInferenceTest.java | 4 +- .../daemon/lambda/LambdaParamsTest.java | 4 +- .../daemon/lambda/LambdaRedundantCastTest.java | 4 +- .../lambda/LightAdvHighlightingJdk8Test.java | 3 +- .../daemon/lambda/MethodRefHighlightingTest.java | 4 +- .../daemon/lambda/MostSpecificResolutionTest.java | 2 +- .../daemon/lambda/NewLambdaHighlightingTest.java | 32 +- .../lambda/NewMethodRefHighlightingTest.java | 6 +- .../daemon/lambda/OverloadResolutionTest.java | 63 ++ .../codeInsight/folding/JavaFoldingTest.groovy | 6 +- .../surroundWith/JavaSurroundWithTest.java | 14 + .../javadoc/JavaDocInfoGeneratorTest.java | 32 +- .../codeInsight/template/LiveTemplateTest.groovy | 44 +- .../ContractInferenceFromSourceTest.groovy | 39 +- .../codeInspection/DataFlowInspectionTest.java | 3 +- .../BytecodeAnalysisIntegrationTest.java | 34 +- .../bytecodeAnalysis/BytecodeAnalysisTest.java | 5 +- .../configurations/JavaCommandLineTest.java | 11 + .../psi/formatter/java/JavaFormatterWrapTest.java | 31 +- .../ExtractMethodObject4DebuggerTest.java | 179 ++++ .../refactoring/IntroduceVariableTest.java | 18 +- .../execution/filters/ExceptionWorker.java | 23 +- .../ide/palette/PaletteDragEventListener.java | 24 - .../src/com/intellij/ide/palette/PaletteGroup.java | 65 -- .../src/com/intellij/ide/palette/PaletteItem.java | 56 - .../intellij/ide/palette/PaletteItemProvider.java | 34 - .../deployment/debug/JavaDebuggerLauncherImpl.java | 5 - .../structuralsearch/JavaReplaceHandler.java | 12 +- .../daemon/LightDaemonAnalyzerTestCase.java | 17 +- .../com/intellij/compiler/CompilerTestUtil.java | 20 +- .../com/intellij/debugger/DebuggerTestCase.java | 33 +- .../ExecutionWithDebuggerToolsTestCase.java | 16 +- .../com/intellij/testFramework/CompilerTester.java | 42 +- .../intentions/ChangeClassParametersIntention.java | 4 +- jps/jps-builders/jps-builders.iml | 2 +- .../src/org/jetbrains/jps/api/GlobalOptions.java | 1 - .../src/org/jetbrains/jps/cmdline/BuildMain.java | 3 +- .../jetbrains/jps/cmdline/ClasspathBootstrap.java | 72 +- .../jps/incremental/IncProjectBuilder.java | 18 +- .../incremental/artifacts/impl/JarsBuilder.java | 2 +- .../incremental/java/ExternalJavacDescriptor.java | 36 - .../jps/incremental/java/JavaBuilder.java | 149 +-- .../jps/javac/ExternalJavacMessageHandler.java | 230 +++++ .../jetbrains/jps/javac/ExternalJavacProcess.java | 339 ++++++ .../jetbrains/jps/javac/ExternalJavacServer.java | 310 ++++++ .../src/org/jetbrains/jps/javac/JavacServer.java | 335 ------ .../jetbrains/jps/javac/JavacServerBootstrap.java | 109 +- .../org/jetbrains/jps/javac/JavacServerClient.java | 78 -- .../jps/javac/JavacServerResponseHandler.java | 231 ----- .../jetbrains/jps/model/java/LanguageLevel.java | 2 +- lib/jsch-0.1.50.jar | Bin 249317 -> 0 bytes lib/jsch-0.1.51.jar | Bin 0 -> 260010 bytes lib/required_for_dist.txt | 4 +- lib/src/ecjsrc-4.3.2.jar | Bin 1451984 -> 0 bytes lib/src/jsch-0.1.50.zip | Bin 351616 -> 0 bytes lib/src/jsch-0.1.51.zip | Bin 0 -> 359905 bytes lib/src/winp-1.17-patched-src.zip | Bin 17906 -> 0 bytes lib/src/winp-1.21-patched.zip | Bin 0 -> 44830 bytes lib/src/winp.patch | 26 + lib/winp-1.17-patched.jar | Bin 27960 -> 0 bytes lib/winp-1.21-patched.jar | Bin 0 -> 27809 bytes native/MacLauncher/Launcher.m | 4 +- native/WinLauncher/WinLauncher/WinLauncher.cpp | 15 +- .../scope/packageSet/FilePatternPackageSet.java | 6 +- .../TextEditorHighlightingPass.java | 1 + .../daemon/impl/UpdateHighlightersUtil.java | 4 +- .../intellij/codeInspection/InspectionEngine.java | 3 +- .../codeInspection/ex/InspectionProfileImpl.java | 13 +- .../com/intellij/codeInspection/ex/ToolsImpl.java | 23 +- .../src/com/intellij/concurrency/AsyncUtil.java | 46 +- .../concurrency/DefaultResultConsumer.java | 8 +- .../src/com/intellij/concurrency/DoWhile.java | 59 -- .../src/com/intellij/concurrency/Iterate.java | 36 - .../com/intellij/concurrency/ResultConsumer.java | 6 +- .../lang/folding/CustomFoldingBuilder.java | 4 +- .../intellij/openapi/components/RoamingType.java | 9 +- .../intellij/openapi/options/SchemeProcessor.java | 6 +- .../openapi/options/SchemesManagerFactory.java | 5 +- .../intellij/openapi/progress/ProgressManager.java | 1 + .../openapi/vfs/ex/http/HttpFileSystem.java | 9 +- .../com/intellij/psi/util/CachedValuesManager.java | 7 +- .../src/com/intellij/util/AbstractQuery.java | 23 +- .../core-api/src/com/intellij/util/ArrayQuery.java | 11 +- .../src/com/intellij/util/CollectionQuery.java | 13 +- .../core-api/src/com/intellij/util/EmptyQuery.java | 6 +- .../core-api/src/com/intellij/util/MergeQuery.java | 17 +- .../intellij/core/CoreApplicationEnvironment.java | 15 +- .../ide/plugins/IdeaPluginDescriptorImpl.java | 172 ++-- .../com/intellij/mock/MockComponentManager.java | 3 +- .../openapi/util/JDOMExternalizableAdapter.java | 2 + .../openapi/vfs/impl/http/HttpVirtualFile.java | 2 + ...ultiplePsiFilesPerDocumentFileViewProvider.java | 3 +- .../intellij/psi/SingleRootFileViewProvider.java | 16 +- .../psi/impl/file/impl/FileManagerImpl.java | 1 + .../psi/impl/source/tree/CompositeElement.java | 4 +- .../intellij/psi/impl/source/tree/FileElement.java | 2 +- .../rename/FragmentaryPsiReference.java | 21 + .../com/intellij/util/CachedValuesManagerImpl.java | 4 +- .../src/com/intellij/dvcs/push/PushSupport.java | 26 +- .../src/com/intellij/dvcs/push/PushTarget.java | 6 - .../com/intellij/dvcs/push/PushTargetPanel.java | 38 + .../src/com/intellij/dvcs/push/VcsError.java | 2 +- .../src/com/intellij/dvcs/repo/Repository.java | 11 +- .../dvcs/DvcsCommitAdditionalComponent.java | 7 +- .../com/intellij/dvcs/branch/DvcsBranchPopup.java | 174 ++++ .../com/intellij/dvcs/branch/DvcsBranchSync.java | 22 + .../dvcs/branch/DvcsMultiRootBranchConfig.java | 71 ++ .../dvcs/branch/DvcsSyncBranchSettings.java | 25 + .../src/com/intellij/dvcs/push/PushController.java | 131 ++- .../intellij/dvcs/push/RepositoryNodeListener.java | 4 +- .../intellij/dvcs/push/ui/EditableTreeNode.java | 10 +- .../src/com/intellij/dvcs/push/ui/PushLog.java | 87 +- .../com/intellij/dvcs/push/ui/RepositoryNode.java | 50 +- .../dvcs/push/ui/RepositoryWithBranchPanel.java | 85 +- .../dvcs/push/ui/SingleRepositoryNode.java | 13 +- .../dvcs/push/ui/VcsBranchEditorListener.java | 4 +- .../src/com/intellij/dvcs/repo/RepositoryUtil.java | 13 +- .../src/com/intellij/ide/ui/UISettings.java | 1 + .../intellij/openapi/actionSystem/AnAction.java | 4 +- .../cache/impl/id/PlatformIdTableBuilding.java | 23 +- .../psi/impl/cache/impl/todo/TodoIndex.java | 27 +- .../model/DefaultExternalSourceDirectorySet.java | 11 + .../model/ExternalSourceDirectorySet.java | 6 + .../service/project/manage/LibraryDataService.java | 2 +- .../test/AbstractExternalSystemTest.groovy | 35 +- .../test/ExternalSystemImportingTestCase.java | 8 +- platform/icons/src/debugger/commandLine.png | Bin 0 -> 210 bytes platform/icons/src/debugger/commandLine@2x.png | Bin 0 -> 534 bytes .../lang/cacheBuilder/DefaultWordsScanner.java | 2 +- .../lang/cacheBuilder/SimpleWordsScanner.java | 2 +- .../lang/cacheBuilder/VersionedWordsScanner.java | 22 + .../com/intellij/psi/search/PsiSearchHelper.java | 3 +- .../search/searches/DefinitionsScopedSearch.java | 9 +- .../psi/impl/cache/impl/id/FileTypeIdIndexer.java | 3 + .../intellij/psi/impl/cache/impl/id/IdIndex.java | 2 +- .../psi/impl/cache/impl/id/IdTableBuilding.java | 5 + .../cache/impl/todo/LexerBasedTodoIndexer.java | 4 +- .../impl/cache/impl/todo/VersionedTodoIndexer.java | 25 + .../psi/impl/search/PsiSearchHelperImpl.java | 350 +++---- .../diagnostic/logging/AdditionalTabComponent.java | 4 +- .../intellij/execution/DefaultExecutionResult.java | 11 +- .../com/intellij/execution/ExecutionResult.java | 1 + .../AdditionalTabComponentManager.java | 9 +- .../execution/configurations/LogFileOptions.java | 65 +- .../execution/configurations/ModuleRunProfile.java | 2 +- .../configurations/RunConfigurationBase.java | 3 +- .../configurations/RunConfigurationsSettings.java | 3 +- .../configurations/SearchScopeProvider.java | 4 +- .../SearchScopeProvidingRunProfile.java | 34 + .../configurations/SimpleJavaParameters.java | 10 +- .../filters/ConsoleDependentFilterProvider.java | 32 + .../runners/AsyncGenericProgramRunner.java | 13 +- .../intellij/execution/runners/ExecutionUtil.java | 4 + .../execution/ui/RunContentDescriptor.java | 21 +- .../lang-api/src/com/intellij/find/FindModel.java | 4 +- .../ide/util/projectWizard/ModuleBuilder.java | 67 +- .../documentation/DocumentationProviderEx.java | 1 + .../com/intellij/openapi/projectRoots/JdkUtil.java | 12 +- .../openapi/projectRoots/ui/PathEditor.java | 8 +- .../codeStyle/CodeStyleSettingsCustomizable.java | 1 + .../psi/codeStyle/CommonCodeStyleSettings.java | 65 +- .../autodetect/IndentOptionsDetector.java | 106 ++ .../psi/codeStyle/autodetect/IndentUsageInfo.java | 39 + .../autodetect/IndentUsageStatistics.java | 30 + .../autodetect/IndentUsageStatisticsImpl.java | 149 +++ .../psi/codeStyle/autodetect/LineIndentInfo.java | 56 + .../autodetect/LineIndentInfoBuilder.java | 80 ++ .../src/com/intellij/psi/util/PsiUtilBase.java | 5 + .../com/intellij/analysis/BaseAnalysisAction.java | 2 +- .../options/CodeStyleSchemesConfigurable.java | 5 + .../options/ModuleListCellRenderer.java | 13 +- .../application/options/ModulesComboBox.java | 10 + .../codeStyle/ManageCodeStyleSchemesDialog.java | 2 +- .../options/codeStyle/WrappingAndBracesPanel.java | 1 + .../action/AbstractArrangementRuleAction.java | 43 + .../action/AbstractMoveArrangementRuleAction.java | 12 +- .../action/AddArrangementRuleAction.java | 4 +- .../action/EditArrangementRuleAction.java | 7 +- .../action/RemoveArrangementRuleAction.java | 2 +- .../group/ArrangementGroupingRulesControl.java | 19 +- .../match/ArrangementMatchingRulesControl.java | 47 +- .../options/editor/EditorTabsConfigurable.form | 2 +- .../options/editor/EditorTabsConfigurable.java | 25 +- .../codeInsight/actions/FormatChangedTextUtil.java | 30 +- .../codeInsight/actions/ReformatCodeProcessor.java | 18 +- .../completion/CodeCompletionHandlerBase.java | 43 +- .../completion/CompletionProgressIndicator.java | 7 +- .../daemon/impl/DaemonCodeAnalyzerImpl.java | 6 +- .../daemon/impl/PassExecutorService.java | 15 +- .../documentation/DocumentationComponent.java | 5 +- .../documentation/DocumentationManager.java | 2 + .../codeInsight/documentation/QuickDocUtil.java | 64 +- .../hint/actions/ShowImplementationsAction.java | 25 +- .../intention/impl/QuickEditHandler.java | 19 +- .../template/impl/EditVariableDialog.java | 9 +- .../template/impl/TemplateListPanel.java | 12 + .../template/impl/TemplateSettings.java | 13 +- .../codeInsight/template/impl/TemplateState.java | 9 +- .../ElseExpressionPostfixTemplateBase.java | 3 +- .../postfix/templates/IfPostfixTemplateBase.java | 3 +- .../postfix/templates/PostfixLiveTemplate.java | 7 +- .../templates/StatementWrapPostfixTemplate.java | 3 +- .../templates/SurroundPostfixTemplateBase.java | 3 +- .../actions/GotoInspectionModel.java | 55 +- .../actions/InspectionListCellRenderer.java | 80 +- .../actions/ViewOfflineResultsAction.java | 2 +- .../logging/DebuggerLogConsoleManager.java | 1 + .../diagnostic/logging/LogConfigurationPanel.java | 37 +- .../diagnostic/logging/LogConsoleBase.java | 1 + .../diagnostic/logging/LogConsoleImpl.java | 34 +- .../diagnostic/logging/LogConsoleManager.java | 16 +- .../diagnostic/logging/LogConsoleManagerBase.java | 96 +- .../diagnostic/logging/LogFilesManager.java | 133 +-- .../actions/RunConfigurationsComboBoxAction.java | 2 +- .../com/intellij/execution/actions/StopAction.java | 7 +- .../execution/console/ConsoleExecuteAction.java | 29 +- .../execution/console/LanguageConsoleBuilder.java | 23 + .../execution/console/LanguageConsoleImpl.java | 5 + .../execution/console/UseConsoleInputAction.java | 56 + .../execution/impl/BeforeRunStepsPanel.java | 6 +- .../intellij/execution/impl/ConsoleViewImpl.java | 13 +- .../intellij/execution/impl/RunConfigurable.java | 12 +- .../execution/runners/RunContentBuilder.java | 53 +- .../src/com/intellij/execution/runners/RunTab.java | 74 +- .../execution/ui/CommonProgramParametersPanel.java | 7 +- .../com/intellij/find/EditorSearchComponent.java | 8 +- .../find/editorHeaderActions/SwitchToFind.java | 6 +- .../find/editorHeaderActions/SwitchToReplace.java | 4 +- .../find/editorHeaderActions/ToggleRegex.java | 6 - .../com/intellij/find/impl/FindInProjectTask.java | 15 +- .../com/intellij/find/impl/FindManagerImpl.java | 12 +- .../find/impl/livePreview/LivePreview.java | 34 +- .../impl/livePreview/LivePreviewController.java | 7 +- .../ide/RecentDirectoryProjectsManagerEx.java | 6 +- .../ide/actions/ExternalJavaDocAction.java | 2 +- .../com/intellij/ide/actions/GotoActionAction.java | 2 +- .../com/intellij/ide/actions/GotoActionBase.java | 10 +- .../actions/BrowseCallHierarchyAction.java | 2 +- .../actions/BrowseMethodHierarchyAction.java | 2 +- .../actions/BrowseTypeHierarchyAction.java | 2 +- .../ide/impl/convert/JDomConvertingUtil.java | 4 +- .../ide/navigationToolbar/NavBarListener.java | 24 +- .../ide/navigationToolbar/ui/AbstractNavBarUI.java | 4 +- .../ide/scratch/CreateScratchFileAction.java | 3 +- .../ide/util/gotoByName/GotoActionModel.java | 3 +- .../ide/util/scopeChooser/ScopeChooserCombo.java | 28 +- .../scopeChooser/ScopeChooserConfigurable.java | 2 +- .../customFolding/CustomFoldingRegionsPopup.java | 133 +++ .../lang/customFolding/GotoCustomRegionAction.java | 96 +- .../lang/customFolding/GotoCustomRegionDialog.form | 25 - .../lang/customFolding/GotoCustomRegionDialog.java | 169 --- .../components/impl/stores/ModuleStoreImpl.java | 36 +- .../impl/stores/ProjectWithModulesStoreImpl.java | 13 +- .../impl/PsiAwareFileEditorManagerImpl.java | 7 +- .../fileEditor/impl/TestEditorManagerImpl.java | 14 +- .../projectRoots/impl/ProjectJdkTableImpl.java | 8 +- .../ExportableApplicationLibraryTable.java | 5 +- .../roots/impl/storage/ClasspathStorage.java | 25 +- .../roots/libraries/ui/impl/RootDetectionUtil.java | 8 +- .../ui/configuration/ModulesAlphaComparator.java | 7 +- .../projectRoot/ProjectSdksModel.java | 16 +- .../intellij/openapi/util/registry/RegistryUi.java | 3 +- .../wm/impl/status/TogglePopupHintsPanel.java | 10 +- .../platform/templates/github/DownloadUtil.java | 3 +- .../InspectionProfileManagerImpl.java | 8 +- .../profile/codeInspection/ui/LevelChooser.java | 102 -- .../ui/SingleInspectionProfilePanel.java | 3 +- .../InspectionsConfigTreeTable.java | 2 +- .../ui/table/ScopesAndSeveritiesTable.java | 5 +- .../psi/impl/cache/impl/id/IdIndexImpl.java | 29 + .../impl/source/codeStyle/CodeStyleSchemeImpl.java | 5 +- .../source/codeStyle/CodeStyleSchemesImpl.java | 3 +- .../scope/packageSet/PackageSetFactoryImpl.java | 3 +- .../src/com/intellij/psi/stubs/StubIndexImpl.java | 8 +- .../actions/RefactoringQuickListPopupAction.java | 3 +- .../intellij/refactoring/rename/RenameUtil.java | 13 +- .../com/intellij/tools/BaseExternalToolsGroup.java | 2 +- .../src/com/intellij/tools/ToolsProcessor.java | 5 +- .../src/com/intellij/ui/ReplacePromptDialog.java | 1 + .../ui/tabs/EditorTabColorProviderImpl.java | 5 +- .../util/download/impl/FileDownloaderImpl.java | 12 +- .../intellij/util/indexing/ValueContainerImpl.java | 4 +- .../indexing/containers/ChangeBufferingList.java | 78 +- .../src/com/intellij/execution/util/ExecUtil.java | 60 +- .../src/com/intellij/ide/BrowserUtil.java | 5 +- .../intellij/ide/actions/ContextHelpAction.java | 2 +- .../intellij/ide/util/treeView/AbstractTreeUi.java | 4 +- .../ide/util/treeView/UpdaterTreeState.java | 5 +- .../com/intellij/notification/Notification.java | 3 +- .../intellij/notification/NotificationGroup.java | 6 + .../notification/NotificationListener.java | 4 +- .../openapi/actionSystem/ActionPlaces.java | 8 + .../intellij/openapi/actionSystem/Separator.java | 5 + .../openapi/actionSystem/ex/CheckboxAction.java | 20 +- .../openapi/actionSystem/ex/ComboBoxAction.java | 8 +- .../openapi/application/ApplicationStarterEx.java | 7 + .../openapi/diagnostic/ErrorReportSubmitter.java | 54 +- .../openapi/diagnostic/SubmittedReportInfo.java | 25 +- .../intellij/openapi/fileChooser/FileChooser.java | 2 +- .../openapi/fileChooser/FileChooserDialog.java | 17 +- .../openapi/fileEditor/FileEditorManager.java | 2 +- .../intellij/openapi/options/ConfigurableEP.java | 18 +- .../openapi/options/ConfigurableProvider.java | 11 + .../openapi/options/CurrentUserHolder.java | 1 + .../openapi/options/SearchableConfigurable.java | 6 + .../src/com/intellij/openapi/ui/DialogWrapper.java | 11 +- .../openapi/ui/MasterDetailsComponent.java | 20 +- .../src/com/intellij/openapi/ui/Messages.java | 52 + .../openapi/ui/popup/util/BaseListPopupStep.java | 30 +- .../intellij/openapi/vfs/newvfs/FileAttribute.java | 30 +- .../src/com/intellij/ui/CollectionListModel.java | 1 + .../src/com/intellij/ui/SimpleColoredText.java | 8 +- .../intellij/ui/components/labels/LinkLabel.java | 2 + .../src/com/intellij/ui/table/JBTable.java | 2 +- .../src/com/intellij/ui/treeStructure/Tree.java | 14 +- .../intellij/util/concurrency/QueueProcessor.java | 4 +- .../com/intellij/util/net/HttpConfigurable.java | 16 +- .../com/intellij/util/net/ssl/CertificateUtil.java | 64 +- .../src/com/intellij/util/ui/Animator.java | 10 +- platform/platform-impl/platform-impl.iml | 1 - .../com/intellij/concurrency/JobLauncherImpl.java | 12 +- .../designer/DesignerEditorPanelFacade.java | 29 + .../src/com/intellij/designer/LightFillLayout.java | 122 +++ .../src/com/intellij/designer/LightToolWindow.java | 562 ++++++++++ .../intellij/designer/LightToolWindowContent.java | 23 + .../intellij/designer/LightToolWindowManager.java | 323 ++++++ .../intellij/designer/ToggleEditorModeAction.java | 60 ++ .../src/com/intellij/diagnostic/ITNReporter.java | 118 +-- .../com/intellij/diagnostic/IdeErrorsDialog.java | 94 +- .../diagnostic/SubmitPerformanceReportAction.java | 175 ---- .../intellij/errorreport/ErrorReportSender.java | 98 -- .../com/intellij/errorreport/bean/ErrorBean.java | 11 +- .../src/com/intellij/errorreport/itn/ITNProxy.java | 326 +++--- .../featureStatistics/FeatureUsageTrackerImpl.java | 13 +- .../com/intellij/ide/ApplicationLoadListener.java | 3 +- .../src/com/intellij/ide/CommandLineProcessor.java | 2 +- .../ide/RecentDirectoryProjectsManager.java | 8 +- .../intellij/ide/RecentProjectsManagerBase.java | 11 +- .../src/com/intellij/ide/actions/AboutAction.java | 3 +- .../intellij/ide/actions/AssociateFileType.java | 2 +- .../ide/actions/BaseShowRecentFilesAction.java | 15 +- .../ide/actions/CreateLauncherScriptAction.java | 4 +- .../src/com/intellij/ide/actions/ExitAction.java | 3 +- .../ide/actions/OccurenceNavigatorActionBase.java | 4 +- .../intellij/ide/actions/PinActiveTabAction.java | 17 +- .../ide/actions/ShowRecentFilesAction.java | 5 +- .../intellij/ide/actions/ShowSettingsAction.java | 2 +- .../intellij/ide/actions/ShowSettingsUtilImpl.java | 2 +- .../src/com/intellij/ide/actions/SplitAction.java | 3 +- .../src/com/intellij/ide/actions/Switcher.java | 83 +- .../ide/customize/CustomizeIDEWizardDialog.java | 14 +- .../com/intellij/ide/dnd/aware/DnDAwareTree.java | 3 +- .../providers/masterKey/MasterKeyPasswordSafe.java | 2 +- .../com/intellij/ide/plugins/PluginManager.java | 1 + .../intellij/ide/ui/AppearanceConfigurable.java | 6 + .../src/com/intellij/ide/ui/AppearancePanel.form | 8 + .../com/intellij/ide/ui/laf/LafManagerImpl.java | 5 +- .../com/intellij/ide/ui/laf/intellijlaf.properties | 1 + .../src/com/intellij/ide/ui/search/SearchUtil.java | 88 -- .../ide/util/ProjectPropertiesComponentImpl.java | 8 +- .../intellij/internal/statistic/UsageTrigger.java | 9 +- .../ApplicationStatisticsPersistenceComponent.java | 6 +- .../UsageStatisticsPersistenceComponent.java | 6 +- .../impl/ui/NotificationsConfigurablePanel.java | 3 +- .../actionSystem/ComputableActionGroup.java | 7 + .../openapi/actionSystem/ex/QuickListsManager.java | 4 +- .../actionSystem/impl/AbbreviationManagerImpl.java | 6 +- .../actionSystem/impl/ActionManagerImpl.java | 75 +- .../openapi/actionSystem/impl/ChameleonAction.java | 32 +- .../intellij/openapi/actionSystem/impl/Utils.java | 26 +- .../openapi/application/PluginPathManager.java | 44 +- .../openapi/application/impl/ApplicationImpl.java | 4 +- .../impl/stores/ApplicationStoreImpl.java | 8 +- .../impl/stores/BaseFileConfigurableStoreImpl.java | 12 +- .../components/impl/stores/ComponentStoreImpl.java | 36 +- .../impl/stores/CompoundSaveSession.java | 10 +- .../impl/stores/DefaultProjectStoreImpl.java | 44 +- .../impl/stores/DefaultsStateStorage.java | 4 +- .../impl/stores/DirectoryBasedStorage.java | 95 +- .../components/impl/stores/FileBasedStorage.java | 134 +-- .../components/impl/stores/IApplicationStore.java | 10 +- .../components/impl/stores/IComponentStore.java | 12 +- .../impl/stores/OldStreamProviderAdapter.java | 6 +- .../impl/stores/ProjectStateStorageManager.java | 13 +- .../components/impl/stores/ProjectStoreImpl.java | 126 ++- .../impl/stores/RoamingTypeExtensionPointBean.java | 29 - .../impl/stores/StateStorageManager.java | 20 +- .../impl/stores/StateStorageManagerImpl.java | 129 ++- .../components/impl/stores/StorageUtil.java | 214 ++-- .../components/impl/stores/StreamProvider.java | 6 +- .../components/impl/stores/XmlElementStorage.java | 304 +++--- .../openapi/diff/ApplicationStarterBase.java | 20 +- .../com/intellij/openapi/diff/DiffApplication.java | 7 +- .../intellij/openapi/diff/MergeApplication.java | 11 +- .../actions/ScrollToTheEndToolbarAction.java | 2 +- .../colors/impl/EditorColorsManagerImpl.java | 4 +- .../intellij/openapi/editor/impl/EditorImpl.java | 7 +- .../editor/impl/LazyRangeMarkerFactoryImpl.java | 2 +- .../openapi/editor/impl/SelectionModelImpl.java | 12 + .../fileChooser/ex/FileChooserDialogImpl.java | 23 +- .../openapi/fileEditor/ex/FileEditorManagerEx.java | 1 + .../openapi/fileEditor/impl/EditorComposite.java | 4 +- .../fileEditor/impl/EditorEmptyTextPainter.java | 7 +- .../fileEditor/impl/EditorHistoryManager.java | 4 +- .../fileEditor/impl/EditorTabColorProvider.java | 5 +- .../fileEditor/impl/EditorTabbedContainer.java | 4 +- .../openapi/fileEditor/impl/EditorWindow.java | 15 +- .../fileEditor/impl/EditorWindowHolder.java | 5 +- .../openapi/fileEditor/impl/EditorsSplitters.java | 57 +- .../fileEditor/impl/FileEditorManagerImpl.java | 69 +- .../fileEditor/impl/IdeDocumentHistoryImpl.java | 32 +- .../openapi/fileEditor/impl/PreviewPanel.java | 403 +++++--- .../fileEditor/impl/http/HttpFileEditor.java | 7 +- .../impl/http/HttpFileEditorProvider.java | 2 +- .../fileEditor/impl/http/RemoteFilePanel.java | 1 + .../fileTypes/impl/FileTypeManagerImpl.java | 6 +- .../openapi/keymap/impl/IdeKeyEventDispatcher.java | 17 + .../openapi/keymap/impl/KeymapManagerImpl.java | 10 +- .../openapi/options/SchemesManagerFactoryImpl.java | 63 +- .../openapi/options/SchemesManagerImpl.java | 78 +- .../options/ex/ConfigurableExtensionPointUtil.java | 17 +- .../openapi/options/ex/ConfigurableVisitor.java | 144 +++ .../openapi/options/ex/ConfigurableWrapper.java | 20 +- .../openapi/options/ex/MixedConfigurableGroup.java | 178 ---- .../openapi/options/ex/NodeConfigurable.java | 77 -- .../options/ex/SortedConfigurableGroup.java | 195 ++++ .../options/newEditor/IdeSettingsDialog.java | 44 +- .../openapi/options/newEditor/OptionsEditor.java | 19 +- .../openapi/options/newEditor/OptionsTree.java | 12 - .../options/newEditor/SettingsTreeView.java | 305 +++--- .../intellij/openapi/project/DumbServiceImpl.java | 3 +- .../intellij/openapi/project/impl/ProjectImpl.java | 5 +- .../openapi/project/impl/ProjectManagerImpl.java | 7 +- .../ui/popup/MultiSelectionListPopupStep.java | 10 +- .../updateSettings/impl/CheckForUpdateAction.java | 3 +- .../updateSettings/impl/NoUpdatesPanel.form | 1 - .../vfs/ex/dummy/DummyCachingFileSystem.java | 29 +- .../openapi/vfs/newvfs/impl/VfsRootAccess.java | 4 +- .../openapi/vfs/newvfs/persistent/FSRecords.java | 67 +- .../vfs/newvfs/persistent/PersistentFSImpl.java | 4 +- .../intellij/openapi/wm/impl/CommandProcessor.java | 5 +- .../openapi/wm/impl/InternalDecorator.java | 201 ++-- .../openapi/wm/impl/WindowManagerImpl.java | 1 - .../intellij/ui/TableExpandableItemsHandler.java | 6 +- .../ui/content/impl/ContentManagerImpl.java | 10 +- .../intellij/ui/docking/impl/DockManagerImpl.java | 7 +- .../com/intellij/ui/messages/JBMacMessages.java | 2 +- .../src/com/intellij/ui/popup/WizardPopup.java | 2 +- .../intellij/ui/win/RecentProjectApplication.java | 3 +- .../src/com/intellij/util/IJSwingUtilities.java | 3 +- .../src/org/jetbrains/io/ChannelRegistrar.java | 4 +- .../src/messages/ApplicationBundle.properties | 11 +- .../src/messages/ExecutionBundle.properties | 17 +- .../messages/FeatureStatisticsBundle.properties | 2 +- .../src/messages/IdeBundle.properties | 16 +- .../src/messages/InspectionsBundle.properties | 2 + .../src/messages/OptionsBundle.properties | 24 +- .../src/messages/RefactoringBundle.properties | 2 +- .../src/messages/UIBundle.properties | 1 + .../src/META-INF/LangExtensionPoints.xml | 3 +- .../src/META-INF/PlatformExtensionPoints.xml | 2 +- .../src/META-INF/PlatformExtensions.xml | 3 + .../src/META-INF/VcsExtensions.xml | 3 +- .../platform-resources/src/META-INF/XmlPlugin.xml | 2 +- .../platform-resources/src/META-INF/xdebugger.xml | 3 +- platform/platform-resources/src/brokenPlugins.txt | 2 +- .../src/componentSets/Platform.xml | 5 - .../platform-resources/src/idea/LangActions.xml | 5 +- platform/platform-resources/src/launcher.py | 43 +- .../codeStyle/autodetect/manyComments.java | 51 + .../autodetect/manyZeroRelativeIndent.java | 31 + .../codeStyle/autodetect/simpleIndent.java | 14 + .../formatting/GeneralCodeFormatterTest.java | 4 +- .../ide/util/treeView/BaseTreeTestCase.java | 6 +- .../components/impl/ApplicationStoreTest.java | 76 +- .../impl/StateStorageManagerImplTest.java | 13 +- .../components/impl/XmlElementStorageTest.java | 56 +- .../progress/util/ProgressIndicatorTest.java | 329 ++++++ .../psi/autodetect/IndentAutoDetectionTest.java | 135 +++ .../intellij/openapi/application/PathMacros.java | 10 +- .../components/LastStorageChooserForWrite.java | 10 + .../src/com/intellij/openapi/components/State.java | 9 +- .../intellij/openapi/components/StateStorage.java | 15 +- .../com/intellij/openapi/components/Storage.java | 9 +- .../StorageAnnotationsDefaultValues.java | 87 -- .../openapi/components/StoragePathMacros.java | 8 +- .../openapi/project/DefaultProjectTypeEP.java | 36 + .../project/DefaultProjectTypeProvider.java | 35 - .../openapi/project/ProjectTypeService.java | 14 +- .../application/options/PathMacrosImpl.java | 106 +- .../src/com/intellij/core/CoreModuleManager.java | 5 +- .../src/com/intellij/core/CoreProjectLoader.java | 14 +- .../openapi/components/PathMacroManager.java | 6 - .../openapi/components/PathMacroSubstitutor.java | 5 +- .../components/TrackingPathMacroSubstitutor.java | 13 +- .../components/impl/BasePathMacroManager.java | 93 +- .../components/impl/ModulePathMacroManager.java | 9 +- .../impl/stores/DefaultStateSerializer.java | 5 +- .../impl/stores/DirectoryStorageData.java | 54 +- .../components/impl/stores/StorageData.java | 67 +- .../com/intellij/openapi/roots/impl/RootIndex.java | 13 +- .../roots/impl/libraries/JarDirectories.java | 2 +- .../src/messages/ProjectBundle.properties | 4 +- .../agent/util/CloudAgentLoggingHandler.java | 4 + platform/remote-servers/api/remote-servers-api.iml | 1 - .../remoteServer/util/CloudConfigurableBase.java | 66 +- .../util/CloudGitApplicationRuntime.java | 4 +- .../remoteServer/util/CloudLoggingHandlerImpl.java | 39 + .../util/CloudSilentLoggingHandlerImpl.java | 7 + .../jetbrains/debugger/sourcemap/SourceMap.java | 9 +- .../debugger/sourcemap/SourceMapDecoder.java | 12 +- .../debugger/BasicDebuggerViewSupport.java | 11 + .../org/jetbrains/debugger/LazyVariablesGroup.java | 2 +- .../src/org/jetbrains/debugger/MemberFilter.java | 5 + .../src/org/jetbrains/debugger/VariableView.java | 42 +- .../src/org/jetbrains/debugger/Variables.java | 32 +- .../jetbrains/debugger/frame/CallFrameView.java | 5 + .../sm/runner/GeneralTestEventsProcessor.java | 5 +- .../runner/states/TestComparisionFailedState.java | 12 +- .../structuralsearch/StructuralReplaceTest.java | 8 +- .../testFramework/src/com/intellij/mock/Mock.java | 2 +- .../com/intellij/mock/MockVirtualFileSystem.java | 21 +- .../intellij/testFramework/FlyIdeaTestCase.java | 35 +- .../intellij/testFramework/PlatformTestUtil.java | 3 +- .../intellij/testFramework/TestActionEvent.java | 9 +- .../com/intellij/testFramework/UsefulTestCase.java | 12 +- .../fixtures/IdeaTestFixtureFactory.java | 2 + .../testFramework/fixtures/TestFixtureBuilder.java | 2 + .../fixtures/impl/CodeInsightTestFixtureImpl.java | 12 +- .../fixtures/impl/HeavyTestFixtureBuilderImpl.java | 2 + .../fixtures/impl/IdeaTestFixtureFactoryImpl.java | 1 + .../fixtures/impl/LightTestFixtureBuilderImpl.java | 2 + platform/testFramework/testFramework.iml | 2 +- .../execution/testframework/AbstractTestProxy.java | 12 + .../intellij/execution/testframework/Filter.java | 10 + .../execution/testframework/TestTreeView.java | 9 +- .../testframework/TestTreeViewAction.java | 19 + .../testframework/TestTreeViewActionsPromoter.java | 38 + .../execution/testframework/TestsUIUtil.java | 6 +- .../actions/AbstractRerunFailedTestsAction.java | 170 ++- .../actions/RerunFailedTestsAction.java | 12 + .../actions/ViewAssertEqualsDiffAction.java | 69 +- .../testframework/stacktrace/DiffHyperlink.java | 88 +- .../testframework/ui/TestResultsPanel.java | 10 +- .../src/com/intellij/usages/ChunkExtractor.java | 2 +- .../usages/FindUsagesProcessPresentation.java | 10 + .../usages/impl/SearchForUsagesRunnable.java | 8 +- .../src/com/intellij/openapi/util/Condition.java | 5 +- .../src/com/intellij/openapi/util/Conditions.java | 12 + .../intellij/openapi/util/text/StringUtilRt.java | 22 +- .../util-rt/src/com/intellij/util/ArrayUtilRt.java | 7 +- .../intellij/util/containers/ContainerUtilRt.java | 51 + .../src/com/intellij/util/containers/HashMap.java | 8 +- .../src/com/intellij/util/containers/Stack.java | 5 +- platform/util/resources/misc/registry.properties | 18 +- .../configurations/CommandLineTokenizer.java | 10 +- platform/util/src/com/intellij/icons/AllIcons.java | 1 + .../intellij/openapi/application/PathManager.java | 8 +- .../intellij/openapi/util/JDOMExternalizer.java | 27 +- .../src/com/intellij/openapi/util/JDOMUtil.java | 22 +- .../src/com/intellij/openapi/util/io/FileUtil.java | 9 +- .../com/intellij/openapi/util/text/StringUtil.java | 268 ++++- platform/util/src/com/intellij/util/ArrayUtil.java | 63 +- .../util/src/com/intellij/util/ReflectionUtil.java | 52 +- platform/util/src/com/intellij/util/Restarter.java | 13 +- .../intellij/util/containers/ContainerUtil.java | 227 +++- .../com/intellij/util/containers/FactoryMap.java | 10 +- .../util/containers/TransferToEDTQueue.java | 3 +- ...pendableStorageBackedByResizableMappedFile.java | 3 +- .../intellij/util/io/PersistentEnumeratorBase.java | 4 +- .../util/messages/impl/MessageListenerList.java | 10 +- .../src/com/intellij/util/ui/JBSwingUtilities.java | 53 + platform/util/src/com/intellij/util/ui/UIUtil.java | 19 +- .../intellij/util/ui/tree/WideSelectionTreeUI.java | 3 +- .../configurations/CommandLineTokenizerTest.java | 60 ++ .../com/intellij/util/text/StringUtilTest.java | 15 +- .../openapi/diff/impl/dir/FrameDialogWrapper.java | 135 +++ .../openapi/vcs/actions/AnnotateToggleAction.java | 1 + .../vcs/changes/ChangesFragmentedDiffPanel.java | 2 +- .../vcs/changes/VcsDirtyScopeManagerImpl.java | 6 +- .../changes/committed/CommittedChangesCache.java | 13 +- .../vcs/changes/ui/CommitChangeListDialog.java | 5 + .../VcsManagerConfigurableProvider.java | 41 + .../intellij/openapi/vcs/ex/LineStatusTracker.java | 83 +- .../openapi/vcs/ex/RollbackLineStatusAction.java | 20 +- .../src/com/intellij/vcs/log/VcsLogProvider.java | 47 +- .../vcs/log/VcsLogProviderRequirementsEx.java | 9 +- platform/vcs-log/api/vcs-log-api.iml | 1 - .../src/com/intellij/vcs/log/graph/RowInfo.java | 3 + .../src/com/intellij/vcs/log/graph/RowType.java | 27 + .../graph/impl/facade/AbstractVisibleGraph.java | 6 + .../src/com/intellij/vcs/log/data/DataPack.java | 28 +- .../com/intellij/vcs/log/data/EmptyDataPack.java | 4 +- .../intellij/vcs/log/data/EmptyVisibleGraph.java | 7 + .../src/com/intellij/vcs/log/data/RefsModel.java | 6 +- .../vcs/log/data/VcsLogMultiRepoJoiner.java | 24 +- .../intellij/vcs/log/data/VcsLogRefresherImpl.java | 354 +++---- .../com/intellij/vcs/log/data/VcsLogSorter.java | 3 +- .../intellij/vcs/log/data/VcsUserRegistryImpl.java | 6 + .../src/com/intellij/vcs/log/impl/LogDataImpl.java | 74 ++ .../intellij/vcs/log/impl/RequirementsImpl.java | 10 +- .../intellij/vcs/log/ui/VcsStructureChooser.java | 1 + .../ui/filter/StructureFilterPopupComponent.java | 2 +- .../intellij/vcs/log/data/VcsLogRefresherTest.java | 10 +- .../intellij/vcs/log/impl/TestVcsLogProvider.java | 37 +- platform/vcs-log/impl/vcs-log-impl.iml | 2 +- .../src/com/intellij/xdebugger/XDebugProcess.java | 18 +- .../xdebugger/evaluation/XDebuggerEvaluator.java | 13 +- .../xdebugger/frame/XReferrersProvider.java | 6 +- .../src/com/intellij/xdebugger/frame/XValue.java | 6 + .../intellij/xdebugger/impl/DebuggerSupport.java | 32 +- .../intellij/xdebugger/impl/XDebugSessionImpl.java | 2 +- .../xdebugger/impl/XDebuggerManagerImpl.java | 50 +- .../actions/handlers/XMarkObjectActionHandler.java | 4 +- .../impl/evaluate/XDebuggerEditorLinePainter.java | 32 +- .../impl/evaluate/XDebuggerEvaluationDialog.java | 2 +- .../xdebugger/impl/evaluate/quick/XValueHint.java | 45 +- .../evaluate/quick/common/AbstractValueHint.java | 6 +- .../common/DebuggerTreeWithHistoryContainer.java | 4 +- .../quick/common/DebuggerTreeWithHistoryPopup.java | 18 +- .../xdebugger/impl/frame/WatchInplaceEditor.java | 6 +- .../intellij/xdebugger/impl/frame/XDebugView.java | 14 +- .../impl/frame/XDebugViewSessionListener.java | 7 +- .../xdebugger/impl/frame/XDebuggerFramesList.java | 19 +- .../intellij/xdebugger/impl/frame/XFramesView.java | 45 +- .../xdebugger/impl/frame/XVariablesViewBase.java | 119 +-- .../xdebugger/impl/frame/XWatchesViewImpl.java | 18 +- .../impl/settings/DebuggerConfigurable.java | 3 +- .../settings/DebuggerConfigurableProvider.java | 34 + .../xdebugger/impl/ui/DebuggerSessionTabBase.java | 57 +- .../xdebugger/impl/ui/XDebugSessionTab.java | 62 +- .../ui/tree/ValueMarkerPresentationDialog.java | 6 +- .../ui/tree/ValueMarkerPresentationDialogBase.java | 4 +- .../impl/ui/tree/nodes/WatchesRootNode.java | 4 +- .../impl/ui/tree/nodes/XValueContainerNode.java | 4 +- .../com/intellij/xdebugger/XDebuggerTestUtil.java | 100 +- .../com/intellij/xdebugger/XTestContainer.java | 2 +- .../xdebugger/XTestEvaluationCallback.java | 7 +- .../com/intellij/xdebugger/XTestValueNode.java | 2 +- .../src/META-INF/InspectionGadgets.xml | 20 +- .../com/siyeh/InspectionGadgetsBundle.properties | 26 +- .../ig/bugs/ResultSetIndexZeroInspection.java | 8 +- .../siyeh/ig/bugs/StringEqualityInspection.java | 10 +- ...sWithOnlyPrivateConstructorsInspectionBase.java | 3 + ...oopConditionNotUpdatedInsideLoopInspection.java | 6 +- .../DoubleBraceInitializationInspection.java | 216 ++++ .../NonThreadSafeLazyInitializationInspection.java | 246 ----- ...ThreadSafeLazyInitializationInspectionBase.java | 132 +++ ...onymousInnerClassMayBeStaticInspectionBase.java | 74 -- .../ig/javadoc/MissingPackageInfoInspection.java | 152 --- .../javadoc/MissingPackageInfoInspectionBase.java | 152 +++ ...onymousInnerClassMayBeStaticInspectionBase.java | 110 ++ .../ig/memory/InnerClassMayBeStaticInspection.java | 155 +++ .../ig/memory/InnerClassReferenceVisitor.java | 126 +++ .../InnerClassMayBeStaticInspection.java | 155 --- .../ig/performance/InnerClassReferenceVisitor.java | 149 --- .../src/com/siyeh/ig/psiutils/ClassUtils.java | 3 +- .../src/com/siyeh/ig/psiutils/ExpressionUtils.java | 9 +- .../ig/style/CStyleArrayDeclarationInspection.java | 102 +- .../ig/style/NestedMethodCallInspectionBase.java | 19 + .../threading/DoubleCheckedLockingInspection.java | 116 ++- .../NonThreadSafeLazyInitializationInspection.java | 216 ++++ .../AnonymousInnerClassMayBeStaticInspection.java | 30 - .../ig/javadoc/MissingPackageInfoInspection.java | 83 ++ .../AnonymousInnerClassMayBeStaticInspection.java | 30 + .../siyeh/ig/style/NestedMethodCallInspection.java | 11 +- .../AmbiguousFieldAccess.html | 8 +- .../AmbiguousMethodCall.html | 10 +- .../AnonymousInnerClassMayBeStatic.html | 9 +- .../CStyleArrayDeclaration.html | 13 +- .../DoubleBraceInitialization.html | 13 + .../DoubleCheckedLocking.html | 6 +- .../InnerClassMayBeStatic.html | 8 +- .../NonThreadSafeLazyInitialization.html | 10 +- .../PropertyValueSetToItself.html | 4 +- .../double_brace_initialization/Argument.java | 12 + .../double_brace_initialization/Field.after.java | 12 + .../double_brace_initialization/Field.java | 10 + .../LocalVariable.after.java | 13 + .../double_brace_initialization/LocalVariable.java | 14 + .../memory/inner_class_static/Simple.after.java | 18 + .../igfixes/memory/inner_class_static/Simple.java | 18 + .../inner_class_static/Simple.after.java | 18 - .../performance/inner_class_static/Simple.java | 18 - .../FieldWithWhitespace.after.java | 4 + .../FieldWithWhitespace.java | 4 + .../SimpleMethod.after.java | 6 + .../cstyle_array_declaration/SimpleMethod.java | 6 + .../make_field_volatile/Simple.after.java | 19 + .../threading/make_field_volatile/Simple.java | 19 + .../InstanceVariableReferenced.java | 16 + .../LocalVariableReferenced.java | 11 + .../NestedAssignment.java | 12 + .../Normal.after.java | 10 + .../Normal.java | 10 + .../StaticVariableReferenced.after.java | 16 + .../StaticVariableReferenced.java | 16 + .../igtest/bugs/ResultSetIndexZeroInspection.java | 14 - .../result_set_index_zero/ResultSetIndexZero.java | 21 + .../bugs/string_equality/StringEquality.java | 8 +- .../siyeh/igtest/bugs/string_equality/expected.xml | 9 - .../confusing/NestedMethodCallInspection.java | 35 - .../DoubleBraceInitialization.java | 32 + .../NonThreadSafeLazyInitialization.java | 64 ++ .../AnonymousInnerClassMayBeStatic.java | 93 ++ .../InnerClassMayBeStatic.java | 140 +++ .../InnerClassMayBeStaticInspection.java | 97 -- .../inner_class_may_be_static/expected.xml | 44 - .../style/CStyleArrayDeclarationInspection.java | 34 - .../CStyleArrayDeclaration.java | 38 + .../style/nested_method_call/NestedMethodCall.java | 43 + .../NonThreadSafeLazyInitializationInspection.java | 34 - .../visibility/ambiguous/AmbiguousMethodCall.java | 2 +- .../siyeh/igtest/visibility/ambiguous/expected.xml | 10 - .../AmbiguousFieldAccess.java | 17 +- .../visibility/ambiguous_field_access/expected.xml | 9 - .../testsrc/com/siyeh/ig/IGQuickFixesTestCase.java | 45 +- .../ig/bugs/ResultSetIndexZeroInspectionTest.java | 34 + .../ig/bugs/StringEqualityInspectionTest.java | 16 +- ...sWithOnlyPrivateConstructorsInspectionTest.java | 8 + .../DoubleBraceInitializationFixTest.java | 38 + .../fixes/memory/InnerClassMayBeStaticFixTest.java | 37 + .../performance/InnerClassMayBeStaticFixTest.java | 37 - .../fixes/style/CStyleArrayDeclarationFixTest.java | 37 + .../ig/fixes/threading/IntroduceHolderFixTest.java | 41 + .../fixes/threading/MakeFieldVolatileFixTest.java | 37 + .../DoubleBraceInitializationInspectionTest.java | 34 + ...ThreadSafeLazyInitializationInspectionTest.java | 34 + ...onymousInnerClassMayBeStaticInspectionTest.java | 34 + .../InnerClassMayBeStaticInspectionTest.java | 16 + .../InnerClassMayBeStaticInspectionTest.java | 11 - .../CStyleArrayDeclarationInspectionTest.java | 34 + .../ig/style/NestedMethodCallInspectionTest.java | 37 + .../AmbiguousFieldAccessInspectionTest.java | 14 +- .../AmbiguousMethodCallInspectionTest.java | 19 +- .../src/META-INF/IntentionPowerPack.xml | 6 + .../com/siyeh/IntentionPowerPackBundle.properties | 6 +- .../src/com/siyeh/ipp/base/Intention.java | 8 - .../siyeh/ipp/decls/SimplifyVariableIntention.java | 12 +- .../ipp/equality/ObjectEqualityPredicate.java | 36 +- .../ReplaceEqualityWithEqualsIntention.java | 31 +- .../ReplaceEqualityWithSafeEqualsIntention.java | 63 +- ...SplitDeclarationAndInitializationIntention.java | 9 +- .../com/siyeh/ipp/junit/CreateAssertIntention.java | 133 ++- .../com/siyeh/ipp/junit/CreateAssertPredicate.java | 59 +- .../simplify_variable/FieldWithWhitespace.java | 4 + .../FieldWithWhitespace_after.java | 4 + .../EnumComparison.java | 8 + .../NegatedObjectComparison.java | 6 + .../NegatedObjectComparison_after.java | 6 + .../NullComparison.java | 6 + .../PrimitiveComparison.java | 6 + .../SimpleObjectComparison.java | 6 + .../SimpleObjectComparison_after.java | 6 + .../EnumComparison.java | 10 + .../NegatedObjectComparison.java | 8 + .../NegatedObjectComparison_after.java | 8 + .../NullComparison.java | 8 + .../PrimitiveComparison.java | 8 + .../SimpleObjectComparison.java | 8 + .../SimpleObjectComparison_after.java | 8 + .../test/com/siyeh/ipp/initialization/Array.java | 3 + .../siyeh/ipp/initialization/ArrayInitializer.java | 2 +- .../com/siyeh/ipp/initialization/Array_after.java | 7 + .../junit/create_assert/AnonymousClassJUnit3.java | 12 + .../create_assert/AnonymousClassJUnit3_after.java | 12 + .../junit/create_assert/AnonymousClassJUnit4.java | 11 + .../create_assert/AnonymousClassJUnit4_after.java | 13 + .../siyeh/ipp/junit/create_assert/AssertFalse.java | 12 + .../ipp/junit/create_assert/AssertFalse_after.java | 12 + .../junit/create_assert/StaticImportJUnit4.java | 9 + .../create_assert/StaticImportJUnit4_after.java | 10 + .../ipp/types/lambda2anonymous/Ambiguity.java | 6 +- .../ipp/decls/SimplifyVariableIntentionTest.java | 38 + .../ReplaceEqualityWithEqualsIntentionTest.java | 42 + ...ReplaceEqualityWithSafeEqualsIntentionTest.java | 42 + ...tDeclarationAndInitializationIntentionTest.java | 1 + .../siyeh/ipp/junit/CreateAssertIntentionTest.java | 60 ++ .../intellij/promoter/ShortcutPromoterManager.java | 10 +- plugins/cvs/cvs-plugin/src/META-INF/plugin.xml | 8 +- .../jetbrains/idea/devkit/DevKitBundle.properties | 2 +- plugins/devkit/src/dom/Action.java | 3 + .../devkit/src/dom/impl/ExtensionDomExtender.java | 155 ++- .../src/dom/impl/PluginXmlDomFileDescription.java | 99 +- .../src/inspections/PluginXmlDomInspection.java | 126 ++- ...scriptionTypeRelatedItemLineMarkerProvider.java | 14 +- .../DevkitRelatedLineMarkerProviderBase.java | 44 + ...ntDeclarationRelatedItemLineMarkerProvider.java | 3 +- plugins/devkit/src/projectRoots/IdeaJdk.java | 12 + .../src/run/PluginRunConfigurationEditor.java | 20 +- .../testAssistant/NavigateToTestDataAction.java | 3 +- .../testAssistant/TestDataLineMarkerProvider.java | 16 +- .../src/testAssistant/TestLocationDataRule.java | 3 + plugins/devkit/src/util/PsiUtil.java | 5 + .../codeInsight/ExtensionsHighlighting.xml | 2 +- .../testData/codeInsight/deprecatedAttributes.xml | 8 +- .../codeInsight/deprecatedExtensionAttribute.xml | 2 +- .../pluginWithXInclude-extensionPoints.xml | 5 + .../pluginXmlDomStubs/pluginWithXInclude-main.xml | 10 + .../pluginXmlDomStubs/pluginWithXInclude.xml | 15 + .../codeInsight/PluginXmlDomStubsTest.java | 7 + ...ptionTypeRelatedItemLineMarkerProviderTest.java | 3 + ...clarationRelatedItemLineMarkerProviderTest.java | 7 +- plugins/git4idea/git4idea.iml | 2 +- plugins/git4idea/src/git4idea/GitUtil.java | 1 + .../git4idea/annotate/GitAnnotationProvider.java | 1 - .../src/git4idea/branch/GitBranchUtil.java | 3 + .../src/git4idea/branch/GitBranchWorker.java | 22 +- .../git4idea/checkin/GitCheckinHandlerFactory.java | 40 +- .../git4idea/src/git4idea/config/GitVcsPanel.form | 11 +- .../git4idea/src/git4idea/config/GitVcsPanel.java | 15 +- .../src/git4idea/config/GitVcsSettings.java | 29 +- .../src/git4idea/history/GitHistoryUtils.java | 197 ++-- .../src/git4idea/history/GitLogRecord.java | 33 +- .../git4idea/src/git4idea/log/GitLogProvider.java | 218 ++-- .../git4idea/src/git4idea/log/GitRefManager.java | 23 +- plugins/git4idea/src/git4idea/log/RefParser.java | 73 -- .../src/git4idea/repo/GitRepositoryFiles.java | 19 +- .../src/git4idea/repo/GitRepositoryImpl.java | 8 +- .../src/git4idea/repo/GitRepositoryUpdater.java | 24 +- .../src/git4idea/reset/GitResetAction.java | 11 +- .../src/git4idea/ui/branch/GitBranchPopup.java | 163 +-- .../git4idea/ui/branch/GitBranchSyncSetting.java | 25 - .../ui/branch/GitCompareBranchesDialog.java | 87 +- .../ui/branch/GitMultiRootBranchConfig.java | 49 +- .../validators/GitNewBranchNameValidator.java | 36 +- .../tests/git4idea/log/GitLogProviderTest.java | 54 +- .../tests/git4idea/log/GitRefManagerTest.java | 6 + plugins/git4idea/tests/git4idea/log/RefParser.java | 63 ++ .../git4idea/tests/git4idea/log/RefParserTest.java | 125 --- plugins/github/github.iml | 2 +- .../plugins/github/GithubCreateGistAction.java | 7 +- .../github/GithubCreatePullRequestAction.java | 6 +- .../github/GithubCreatePullRequestWorker.java | 1083 +++++++++++-------- .../plugins/github/GithubRebaseAction.java | 7 +- .../plugins/github/GithubShareAction.java | 15 +- .../plugins/github/api/GithubApiUtil.java | 632 +++--------- .../github/api/GithubChangeIssueStateRequest.java | 12 + .../plugins/github/api/GithubConnection.java | 491 +++++++++ .../plugins/github/api/GithubFullPath.java | 9 +- .../jetbrains/plugins/github/api/GithubUser.java | 10 +- .../plugins/github/api/GithubUserDetailed.java | 4 +- .../plugins/github/api/GithubUserRaw.java | 4 +- .../github/extensions/GithubCheckoutProvider.java | 7 +- .../plugins/github/tasks/GithubComment.java | 12 +- .../plugins/github/tasks/GithubRepository.java | 118 ++- .../github/tasks/GithubRepositoryEditor.java | 9 +- .../plugins/github/tasks/GithubRepositoryType.java | 7 + .../github/ui/GithubCreatePullRequestDialog.java | 249 +++-- .../github/ui/GithubCreatePullRequestPanel.form | 32 +- .../github/ui/GithubCreatePullRequestPanel.java | 138 +-- .../plugins/github/ui/GithubSelectForkDialog.java | 41 +- .../plugins/github/ui/GithubSelectForkPanel.java | 8 - .../plugins/github/ui/GithubSettingsPanel.java | 13 +- .../plugins/github/util/GithubNotifications.java | 26 +- .../plugins/github/util/GithubSettings.java | 11 + .../jetbrains/plugins/github/util/GithubUtil.java | 177 +++- .../plugins/github/GithubCreateGistTestBase.java | 5 +- .../github/GithubCreatePullRequestTestBase.java | 2 +- .../jetbrains/plugins/github/GithubIssuesTest.java | 19 +- .../plugins/github/GithubRequestPagingTest.java | 28 +- .../plugins/github/GithubShareProjectTest.java | 3 +- .../plugins/github/GithubShareProjectTestBase.java | 3 +- .../gradle/resources/i18n/GradleBundle.properties | 2 +- plugins/gradle/src/META-INF/plugin.xml | 2 +- .../execution/GradleOrderEnumeratorHandler.java | 2 + .../gradle/service/GradleInstallationManager.java | 2 +- .../BaseGradleProjectResolverExtension.java | 36 +- .../importing/GradleFoldersImportingTest.java | 36 +- .../gradle/importing/GradleImportingTestCase.java | 2 +- .../builder/ExternalProjectBuilderImpl.groovy | 6 + .../plugins/groovy/GroovyBundle.properties | 2 +- .../plugins/groovy/dsl/GroovyClassDescriptor.java | 2 +- plugins/groovy/src/META-INF/plugin.xml | 2 +- .../plugins/groovy/config/GroovyFacetUtil.java | 10 +- .../groovy/ivy/AbstractAttachSourceProvider.java | 2 +- .../lang/completion/GroovyCompletionUtil.java | 3 +- .../plugins/groovy/mvc/MvcActionBase.java | 3 +- .../groovy/compiler/GroovyDebuggerTest.groovy | 6 +- plugins/hg4idea/hg4idea.iml | 1 + plugins/hg4idea/src/META-INF/plugin.xml | 2 +- .../src/org/zmlx/hg4idea/HgProjectSettings.java | 15 +- .../hg4idea/action/HgAbstractGlobalAction.java | 2 +- .../hg4idea/action/HgBranchAbstractAction.java | 36 - .../src/org/zmlx/hg4idea/action/HgBranchPopup.java | 127 --- .../zmlx/hg4idea/action/HgBranchPopupActions.java | 262 ----- .../org/zmlx/hg4idea/action/HgBranchesAction.java | 33 - .../zmlx/hg4idea/action/HgCommonBranchActions.java | 133 --- .../hg4idea/branch/HgBranchAbstractAction.java | 38 + .../src/org/zmlx/hg4idea/branch/HgBranchPopup.java | 119 +++ .../zmlx/hg4idea/branch/HgBranchPopupActions.java | 270 +++++ .../src/org/zmlx/hg4idea/branch/HgBranchUtil.java | 77 ++ .../org/zmlx/hg4idea/branch/HgBranchesAction.java | 34 + .../zmlx/hg4idea/branch/HgCommonBranchActions.java | 141 +++ .../hg4idea/branch/HgMultiRootBranchConfig.java | 50 + .../org/zmlx/hg4idea/command/HgCommitCommand.java | 24 +- .../zmlx/hg4idea/execution/HgCommandExecutor.java | 2 +- .../src/org/zmlx/hg4idea/log/HgHistoryUtil.java | 6 +- .../src/org/zmlx/hg4idea/log/HgLogProvider.java | 28 +- .../zmlx/hg4idea/provider/HgChangeProvider.java | 44 +- .../provider/HgCurrentBinaryContentRevision.java | 18 +- .../hg4idea/provider/HgCurrentContentRevision.java | 2 +- .../provider/commit/HgCheckinEnvironment.java | 114 +- .../src/org/zmlx/hg4idea/push/HgPushSupport.java | 44 +- .../org/zmlx/hg4idea/push/HgPushTargetPanel.java | 88 ++ .../src/org/zmlx/hg4idea/push/HgTarget.java | 1 - .../org/zmlx/hg4idea/push/PushTargetTextField.java | 63 ++ .../src/org/zmlx/hg4idea/repo/HgRepoInfo.java | 16 +- .../src/org/zmlx/hg4idea/repo/HgRepository.java | 5 + .../org/zmlx/hg4idea/repo/HgRepositoryImpl.java | 27 +- .../org/zmlx/hg4idea/repo/HgRepositoryReader.java | 18 +- .../org/zmlx/hg4idea/status/ui/HgStatusWidget.java | 2 +- .../hg4idea/ui/HgConfigurationProjectPanel.form | 44 +- .../hg4idea/ui/HgConfigurationProjectPanel.java | 11 + .../hg4idea/src/org/zmlx/hg4idea/util/HgUtil.java | 22 +- plugins/java-decompiler/engine/.gitattributes | 1 + plugins/java-decompiler/engine/build.xml | 58 ++ plugins/java-decompiler/engine/readme.txt | 88 ++ .../java/decompiler/code/CodeConstants.java | 370 +++++++ .../java/decompiler/code/ConstantsUtil.java | 482 +++++++++ .../java/decompiler/code/ExceptionHandler.java | 61 ++ .../java/decompiler/code/ExceptionTable.java | 58 ++ .../decompiler/code/FullInstructionSequence.java | 38 + .../java/decompiler/code/IfInstruction.java | 36 + .../java/decompiler/code/Instruction.java | 126 +++ .../java/decompiler/code/InstructionSequence.java | 227 ++++ .../java/decompiler/code/JumpInstruction.java | 42 + .../decompiler/code/SimpleInstructionSequence.java | 39 + .../java/decompiler/code/SwitchInstruction.java | 97 ++ .../java/decompiler/code/cfg/BasicBlock.java | 266 +++++ .../java/decompiler/code/cfg/ControlFlowGraph.java | 884 ++++++++++++++++ .../decompiler/code/cfg/ExceptionRangeCFG.java | 129 +++ .../code/interpreter/InstructionImpact.java | 527 ++++++++++ .../java/decompiler/code/interpreter/Util.java | 286 +++++ .../decompiler/code/optinstructions/ALOAD.java | 60 ++ .../decompiler/code/optinstructions/ANEWARRAY.java | 33 + .../decompiler/code/optinstructions/ASTORE.java | 60 ++ .../decompiler/code/optinstructions/BIPUSH.java | 48 + .../decompiler/code/optinstructions/CHECKCAST.java | 34 + .../decompiler/code/optinstructions/DLOAD.java | 60 ++ .../decompiler/code/optinstructions/DSTORE.java | 60 ++ .../decompiler/code/optinstructions/FLOAD.java | 60 ++ .../decompiler/code/optinstructions/FSTORE.java | 60 ++ .../decompiler/code/optinstructions/GETFIELD.java | 33 + .../decompiler/code/optinstructions/GETSTATIC.java | 33 + .../java/decompiler/code/optinstructions/GOTO.java | 46 + .../decompiler/code/optinstructions/GOTO_W.java | 33 + .../java/decompiler/code/optinstructions/IINC.java | 43 + .../decompiler/code/optinstructions/ILOAD.java | 55 + .../code/optinstructions/INSTANCEOF.java | 33 + .../code/optinstructions/INVOKEDYNAMIC.java | 35 + .../code/optinstructions/INVOKEINTERFACE.java | 35 + .../code/optinstructions/INVOKESPECIAL.java | 33 + .../code/optinstructions/INVOKESTATIC.java | 33 + .../code/optinstructions/INVOKEVIRTUAL.java | 33 + .../decompiler/code/optinstructions/ISTORE.java | 55 + .../java/decompiler/code/optinstructions/JSR.java | 46 + .../decompiler/code/optinstructions/JSR_W.java | 33 + .../java/decompiler/code/optinstructions/LDC.java | 33 + .../decompiler/code/optinstructions/LDC2_W.java | 33 + .../decompiler/code/optinstructions/LDC_W.java | 33 + .../decompiler/code/optinstructions/LLOAD.java | 55 + .../code/optinstructions/LOOKUPSWITCH.java | 42 + .../decompiler/code/optinstructions/LSTORE.java | 55 + .../code/optinstructions/MULTIANEWARRAY.java | 34 + .../java/decompiler/code/optinstructions/NEW.java | 33 + .../decompiler/code/optinstructions/NEWARRAY.java | 33 + .../decompiler/code/optinstructions/PUTFIELD.java | 33 + .../decompiler/code/optinstructions/PUTSTATIC.java | 33 + .../java/decompiler/code/optinstructions/RET.java | 41 + .../decompiler/code/optinstructions/SIPUSH.java | 33 + .../code/optinstructions/TABLESWITCH.java | 42 + .../java/decompiler/main/AssertProcessor.java | 310 ++++++ .../decompiler/main/ClassReference14Processor.java | 296 ++++++ .../java/decompiler/main/ClassWriter.java | 987 ++++++++++++++++++ .../java/decompiler/main/ClassesProcessor.java | 431 ++++++++ .../java/decompiler/main/DecompilerContext.java | 150 +++ .../java/decompiler/main/EnumProcessor.java | 118 +++ .../jetbrains/java/decompiler/main/Fernflower.java | 94 ++ .../java/decompiler/main/InitializerProcessor.java | 323 ++++++ .../main/collectors/CounterContainer.java | 37 + .../main/collectors/ImportCollector.java | 148 +++ .../main/collectors/VarNamesCollector.java | 51 + .../decompiler/main/decompiler/BaseDecompiler.java | 47 + .../main/decompiler/ConsoleDecompiler.java | 299 ++++++ .../main/decompiler/PrintStreamLogger.java | 80 ++ .../decompiler/main/extern/IBytecodeProvider.java | 22 + .../decompiler/main/extern/IFernflowerLogger.java | 49 + .../main/extern/IFernflowerPreferences.java | 86 ++ .../decompiler/main/extern/IIdentifierRenamer.java | 35 + .../java/decompiler/main/extern/IResultSaver.java | 36 + .../java/decompiler/main/rels/ClassWrapper.java | 207 ++++ .../java/decompiler/main/rels/LambdaProcessor.java | 151 +++ .../main/rels/MethodProcessorThread.java | 259 +++++ .../java/decompiler/main/rels/MethodWrapper.java | 62 ++ .../decompiler/main/rels/NestedClassProcessor.java | 1030 +++++++++++++++++++ .../decompiler/main/rels/NestedMemberAccess.java | 449 ++++++++ .../decompiler/modules/code/DeadCodeHelper.java | 436 ++++++++ .../modules/decompiler/ClearStructHelper.java | 40 + .../modules/decompiler/ConcatenationHelper.java | 209 ++++ .../decompiler/modules/decompiler/DecHelper.java | 218 ++++ .../decompiler/modules/decompiler/DomHelper.java | 707 +++++++++++++ .../modules/decompiler/EliminateLoopsHelper.java | 214 ++++ .../decompiler/modules/decompiler/ExitHelper.java | 345 +++++++ .../modules/decompiler/ExprProcessor.java | 913 ++++++++++++++++ .../modules/decompiler/ExprentStack.java | 45 + .../modules/decompiler/FinallyProcessor.java | 1087 ++++++++++++++++++++ .../modules/decompiler/IdeaNotNullHelper.java | 336 ++++++ .../decompiler/modules/decompiler/IfHelper.java | 750 ++++++++++++++ .../decompiler/InlineSingleBlockHelper.java | 220 ++++ .../decompiler/modules/decompiler/LabelHelper.java | 506 +++++++++ .../modules/decompiler/LoopExtractHelper.java | 206 ++++ .../modules/decompiler/LowBreakHelper.java | 208 ++++ .../decompiler/modules/decompiler/MergeHelper.java | 421 ++++++++ .../modules/decompiler/PPandMMHelper.java | 154 +++ .../modules/decompiler/PrimitiveExprsList.java | 49 + .../decompiler/SecondaryFunctionsHelper.java | 432 ++++++++ .../modules/decompiler/SequenceHelper.java | 327 ++++++ .../modules/decompiler/SimplifyExprentsHelper.java | 853 +++++++++++++++ .../modules/decompiler/StackVarsProcessor.java | 735 +++++++++++++ .../decompiler/modules/decompiler/StatEdge.java | 104 ++ .../decompiler/StrongConnectivityHelper.java | 207 ++++ .../decompiler/decompose/DominatorEngine.java | 128 +++ .../decompose/DominatorTreeExceptionFilter.java | 184 ++++ .../decompose/FastExtendedPostdominanceHelper.java | 355 +++++++ .../decompose/GenericDominatorEngine.java | 152 +++ .../modules/decompiler/decompose/IGraph.java | 26 + .../modules/decompiler/decompose/IGraphNode.java | 23 + .../deobfuscator/ExceptionDeobfuscator.java | 330 ++++++ .../deobfuscator/IrreducibleCFGDeobfuscator.java | 245 +++++ .../modules/decompiler/exps/AnnotationExprent.java | 114 ++ .../modules/decompiler/exps/ArrayExprent.java | 126 +++ .../modules/decompiler/exps/AssertExprent.java | 51 + .../modules/decompiler/exps/AssignmentExprent.java | 204 ++++ .../modules/decompiler/exps/ConstExprent.java | 402 ++++++++ .../modules/decompiler/exps/ExitExprent.java | 151 +++ .../modules/decompiler/exps/Exprent.java | 133 +++ .../modules/decompiler/exps/FieldExprent.java | 200 ++++ .../modules/decompiler/exps/FunctionExprent.java | 615 +++++++++++ .../modules/decompiler/exps/IfExprent.java | 145 +++ .../modules/decompiler/exps/InvocationExprent.java | 515 ++++++++++ .../modules/decompiler/exps/MonitorExprent.java | 83 ++ .../modules/decompiler/exps/NewExprent.java | 504 +++++++++ .../modules/decompiler/exps/SwitchExprent.java | 114 ++ .../modules/decompiler/exps/VarExprent.java | 182 ++++ .../modules/decompiler/sforms/DirectGraph.java | 136 +++ .../modules/decompiler/sforms/DirectNode.java | 67 ++ .../decompiler/sforms/FlattenStatementsHelper.java | 574 +++++++++++ .../decompiler/sforms/SSAConstructorSparseEx.java | 529 ++++++++++ .../decompiler/sforms/SSAUConstructorSparseEx.java | 844 +++++++++++++++ .../decompiler/stats/BasicBlockStatement.java | 96 ++ .../decompiler/stats/CatchAllStatement.java | 233 +++++ .../modules/decompiler/stats/CatchStatement.java | 210 ++++ .../modules/decompiler/stats/DoStatement.java | 222 ++++ .../modules/decompiler/stats/GeneralStatement.java | 74 ++ .../modules/decompiler/stats/IfStatement.java | 416 ++++++++ .../modules/decompiler/stats/RootStatement.java | 48 + .../decompiler/stats/SequenceStatement.java | 144 +++ .../modules/decompiler/stats/Statement.java | 863 ++++++++++++++++ .../modules/decompiler/stats/SwitchStatement.java | 367 +++++++ .../decompiler/stats/SynchronizedStatement.java | 153 +++ .../modules/decompiler/vars/CheckTypesResult.java | 57 + .../decompiler/vars/VarDefinitionHelper.java | 355 +++++++ .../modules/decompiler/vars/VarProcessor.java | 129 +++ .../modules/decompiler/vars/VarTypeProcessor.java | 278 +++++ .../modules/decompiler/vars/VarVersionEdge.java | 56 + .../modules/decompiler/vars/VarVersionNode.java | 80 ++ .../modules/decompiler/vars/VarVersionPaar.java | 63 ++ .../modules/decompiler/vars/VarVersionsGraph.java | 167 +++ .../decompiler/vars/VarVersionsProcessor.java | 334 ++++++ .../modules/renamer/ClassWrapperNode.java | 55 + .../modules/renamer/ConverterHelper.java | 143 +++ .../modules/renamer/IdentifierConverter.java | 463 +++++++++ .../modules/renamer/PoolInterceptor.java | 50 + .../java/decompiler/struct/ContextUnit.java | 165 +++ .../java/decompiler/struct/IDecompiledData.java | 23 + .../java/decompiler/struct/StructClass.java | 195 ++++ .../java/decompiler/struct/StructContext.java | 189 ++++ .../java/decompiler/struct/StructField.java | 58 ++ .../java/decompiler/struct/StructMember.java | 87 ++ .../java/decompiler/struct/StructMethod.java | 392 +++++++ .../struct/attr/StructAnnDefaultAttribute.java | 35 + .../struct/attr/StructAnnotationAttribute.java | 184 ++++ .../attr/StructAnnotationParameterAttribute.java | 51 + .../struct/attr/StructAnnotationTypeAttribute.java | 194 ++++ .../attr/StructBootstrapMethodsAttribute.java | 66 ++ .../struct/attr/StructConstantValueAttribute.java | 34 + .../attr/StructEnclosingMethodAttribute.java | 55 + .../struct/attr/StructExceptionsAttribute.java | 52 + .../struct/attr/StructGeneralAttribute.java | 118 +++ .../attr/StructGenericSignatureAttribute.java | 35 + .../struct/attr/StructInnerClassesAttribute.java | 72 ++ .../attr/StructLocalVariableTableAttribute.java | 67 ++ .../decompiler/struct/consts/ConstantPool.java | 281 +++++ .../decompiler/struct/consts/LinkConstant.java | 165 +++ .../decompiler/struct/consts/PooledConstant.java | 117 +++ .../struct/consts/PrimitiveConstant.java | 126 +++ .../decompiler/struct/consts/VariableTypeEnum.java | 47 + .../java/decompiler/struct/gen/DataPoint.java | 100 ++ .../decompiler/struct/gen/FieldDescriptor.java | 59 ++ .../decompiler/struct/gen/MethodDescriptor.java | 102 ++ .../java/decompiler/struct/gen/VarType.java | 422 ++++++++ .../gen/generics/GenericClassDescriptor.java | 30 + .../gen/generics/GenericFieldDescriptor.java | 21 + .../struct/gen/generics/GenericMain.java | 249 +++++ .../gen/generics/GenericMethodDescriptor.java | 32 + .../struct/gen/generics/GenericType.java | 268 +++++ .../java/decompiler/struct/lazy/LazyLoader.java | 173 ++++ .../java/decompiler/util/DataInputFullStream.java | 55 + .../java/decompiler/util/FastFixedSetFactory.java | 360 +++++++ .../java/decompiler/util/FastSetFactory.java | 489 +++++++++ .../java/decompiler/util/FastSparseSetFactory.java | 552 ++++++++++ .../java/decompiler/util/InterpreterUtil.java | 175 ++++ .../jetbrains/java/decompiler/util/ListStack.java | 93 ++ .../java/decompiler/util/SFormsFastMapDirect.java | 414 ++++++++ .../java/decompiler/util/VBStyleCollection.java | 204 ++++ .../java/decompiler/BulkDecompilationTest.java | 110 ++ .../java/decompiler/DecompilerTestFixture.java | 88 ++ .../java/decompiler/SingleClassesTest.java | 125 +++ plugins/java-decompiler/engine/testData/bulk.jar | Bin 0 -> 1997 bytes .../engine/testData/bulk/META-INF/MANIFEST.MF | 0 .../engine/testData/bulk/pkg/Main.java | 0 .../engine/testData/bulk/pkg/res/Loader.java | 0 .../engine/testData/bulk/pkg/res/resource.txt | 0 .../testData/classes/InvalidMethodSignature.class | Bin 0 -> 761 bytes .../testData/classes/pkg/TestClassCast.class | Bin 0 -> 625 bytes .../testData/classes/pkg/TestClassFields.class | Bin 0 -> 415 bytes .../testData/classes/pkg/TestClassLambda.class | Bin 0 -> 3723 bytes .../testData/classes/pkg/TestClassLoop.class | Bin 0 -> 880 bytes .../classes/pkg/TestClassNestedInitializer$1.class | Bin 0 -> 477 bytes .../classes/pkg/TestClassNestedInitializer.class | Bin 0 -> 599 bytes .../testData/classes/pkg/TestClassSwitch.class | Bin 0 -> 464 bytes .../testData/classes/pkg/TestClassTypes.class | Bin 0 -> 1151 bytes .../engine/testData/classes/pkg/TestClassVar.class | Bin 0 -> 992 bytes .../testData/classes/pkg/TestCodeConstructs.class | Bin 0 -> 513 bytes .../testData/classes/pkg/TestConstants$A.class | Bin 0 -> 293 bytes .../testData/classes/pkg/TestConstants.class | Bin 0 -> 1668 bytes .../testData/classes/pkg/TestDebugSymbols.class | Bin 0 -> 878 bytes .../classes/pkg/TestDeprecations$ByAnno.class | Bin 0 -> 367 bytes .../classes/pkg/TestDeprecations$ByComment.class | Bin 0 -> 308 bytes .../testData/classes/pkg/TestDeprecations.class | Bin 0 -> 576 bytes .../engine/testData/classes/pkg/TestEnum$1.class | Bin 0 -> 363 bytes .../engine/testData/classes/pkg/TestEnum$2.class | Bin 0 -> 428 bytes .../engine/testData/classes/pkg/TestEnum.class | Bin 0 -> 1491 bytes .../testData/classes/pkg/TestExtendsList.class | Bin 0 -> 522 bytes .../classes/pkg/TestMethodParameters$1Local.class | Bin 0 -> 567 bytes .../classes/pkg/TestMethodParameters$C1.class | Bin 0 -> 519 bytes .../classes/pkg/TestMethodParameters$C2.class | Bin 0 -> 483 bytes .../classes/pkg/TestMethodParameters.class | Bin 0 -> 629 bytes .../testData/results/InvalidMethodSignature.dec | 26 + .../engine/testData/results/TestClassCast.dec | 15 + .../engine/testData/results/TestClassFields.dec | 10 + .../engine/testData/results/TestClassLambda.dec | 73 ++ .../engine/testData/results/TestClassLoop.dec | 43 + .../results/TestClassNestedInitializer.dec | 14 + .../engine/testData/results/TestClassSwitch.dec | 14 + .../engine/testData/results/TestClassTypes.dec | 49 + .../engine/testData/results/TestClassVar.dec | 40 + .../engine/testData/results/TestCodeConstructs.dec | 13 + .../engine/testData/results/TestConstants.dec | 73 ++ .../engine/testData/results/TestDebugSymbols.dec | 11 + .../engine/testData/results/TestDeprecations.dec | 27 + .../engine/testData/results/TestEnum.dec | 27 + .../engine/testData/results/TestExtendsList.dec | 11 + .../testData/results/TestMethodParameters.dec | 42 + .../engine/testData/src/pkg/TestClassCast.java | 28 + .../engine/testData/src/pkg/TestClassFields.java | 28 + .../engine/testData/src/pkg/TestClassLambda.java | 77 ++ .../engine/testData/src/pkg/TestClassLoop.java | 61 ++ .../src/pkg/TestClassNestedInitializer.java | 25 + .../engine/testData/src/pkg/TestClassSwitch.java | 30 + .../engine/testData/src/pkg/TestClassTypes.java | 69 ++ .../engine/testData/src/pkg/TestClassVar.java | 59 ++ .../testData/src/pkg/TestCodeConstructs.java | 27 + .../engine/testData/src/pkg/TestConstants.java | 64 ++ .../engine/testData/src/pkg/TestDebugSymbols.java | 27 + .../engine/testData/src/pkg/TestDeprecations.java | 36 + .../engine/testData/src/pkg/TestEnum.java | 36 + .../engine/testData/src/pkg/TestExtendsList.java | 26 + .../testData/src/pkg/TestMethodParameters.java | 40 + plugins/java-decompiler/java-decompiler.iml | 12 +- plugins/java-decompiler/lib/fernflower.jar | Bin 273340 -> 0 bytes plugins/java-decompiler/src/META-INF/plugin.xml | 2 + .../jetbrains/java/decompiler/IdeaDecompiler.java | 44 +- .../org/jetbrains/java/decompiler/IdeaLogger.java | 24 +- .../java/decompiler/IdeaDecompilerTest.java | 113 +- plugins/java-decompiler/testData/Anonymous$1.class | Bin 672 -> 0 bytes plugins/java-decompiler/testData/Anonymous.class | Bin 407 -> 0 bytes plugins/java-decompiler/testData/Anonymous.java | 13 - plugins/java-decompiler/testData/Anonymous.txt | 24 - .../java-decompiler/testData/CodeConstructs.class | Bin 324 -> 0 bytes .../java-decompiler/testData/CodeConstructs.java | 5 - .../java-decompiler/testData/CodeConstructs.txt | 15 - plugins/java-decompiler/testData/Constants$A.class | Bin 273 -> 0 bytes plugins/java-decompiler/testData/Constants.class | Bin 1640 -> 0 bytes plugins/java-decompiler/testData/Constants.java | 47 - plugins/java-decompiler/testData/Constants.txt | 82 -- .../testData/Deprecations$ByAnno.class | Bin 347 -> 0 bytes .../testData/Deprecations$ByComment.class | Bin 288 -> 0 bytes .../java-decompiler/testData/Deprecations.class | Bin 548 -> 0 bytes plugins/java-decompiler/testData/Deprecations.java | 19 - plugins/java-decompiler/testData/Deprecations.txt | 42 - plugins/java-decompiler/testData/Enum$1.class | Bin 335 -> 0 bytes plugins/java-decompiler/testData/Enum$2.class | Bin 392 -> 0 bytes plugins/java-decompiler/testData/Enum.class | Bin 1407 -> 0 bytes plugins/java-decompiler/testData/Enum.java | 19 - plugins/java-decompiler/testData/Enum.txt | 33 - plugins/java-decompiler/testData/ExtendsList.class | Bin 510 -> 0 bytes plugins/java-decompiler/testData/ExtendsList.java | 9 - plugins/java-decompiler/testData/ExtendsList.txt | 19 - .../testData/Parameters$1Local.class | Bin 501 -> 0 bytes .../java-decompiler/testData/Parameters$C1.class | Bin 453 -> 0 bytes .../java-decompiler/testData/Parameters$C2.class | Bin 445 -> 0 bytes plugins/java-decompiler/testData/Parameters.class | Bin 563 -> 0 bytes plugins/java-decompiler/testData/Parameters.java | 23 - plugins/java-decompiler/testData/Parameters.txt | 50 - plugins/java-i18n/java-i18n.iml | 1 - .../com/intellij/execution/ConfigurationUtil.java | 8 +- .../execution/junit/JUnitConfiguration.java | 61 +- .../com/intellij/execution/junit/TestCategory.java | 14 +- .../com/intellij/execution/junit/TestClass.java | 12 +- .../intellij/execution/junit/TestDirectory.java | 21 +- .../com/intellij/execution/junit/TestMethod.java | 13 +- .../com/intellij/execution/junit/TestMethods.java | 20 +- .../com/intellij/execution/junit/TestObject.java | 170 ++- .../com/intellij/execution/junit/TestPackage.java | 8 +- .../com/intellij/execution/junit/TestsPattern.java | 8 +- .../junit2/configuration/JUnitConfigurable.java | 1 + .../junit2/states/ComparisonFailureState.java | 17 +- .../intellij/execution/junit2/ui/ConsolePanel.java | 17 +- .../execution/junit2/ui/JUnitStatusLine.java | 27 +- .../execution/junit2/ui/JUnitTreeConsoleView.java | 3 + .../junit2/ui/actions/RerunFailedTestsAction.java | 22 +- .../rt/execution/junit/JUnitForkedStarter.java | 49 +- .../intellij/rt/execution/junit/JUnitStarter.java | 15 + .../references/MavenPathReferenceConverter.java | 5 +- .../common/MavenCommonParamReferenceProviders.java | 7 +- .../maven/project/MavenArtifactDownloader.java | 4 +- .../maven/src/main/resources/META-INF/plugin.xml | 2 +- .../idea/maven/MavenImportingTestCase.java | 2 +- .../maven/compiler/ManifestGenerationTest.java | 22 + .../maven/compiler/MavenCompilingTestCase.java | 60 +- .../lang/properties/PropertiesImplUtil.java | 11 +- .../org/jetbrains/idea/svn/SvnConfiguration.java | 7 +- .../idea/svn/dialogs/RepositoryTreeModel.java | 2 + .../idea/svn/dialogs/RepositoryTreeNode.java | 86 +- .../jetbrains/idea/svn/dialogs/SimpleTextNode.java | 46 + .../svn/dialogs/SvnRepositoryTreeCellRenderer.java | 15 +- .../idea/svn/dialogs/browserCache/CacheLoader.java | 15 +- .../idea/svn/dialogs/browserCache/Loader.java | 57 +- .../svn/dialogs/browserCache/RepositoryLoader.java | 34 +- .../com/intellij/tasks/jira/JiraRepository.java | 41 +- .../intellij/tasks/actions/SwitchTaskCombo.java | 2 +- .../intellij/tasks/gitlab/GitlabRepository.java | 24 +- .../impl/httpclient/NewBaseRepositoryImpl.java | 42 + .../intellij/tasks/redmine/RedmineRepository.java | 76 +- .../intellij/tasks/redmine/model/RedmineIssue.java | 4 +- .../tasks/integration/JiraIntegrationTest.java | 17 +- .../tasks/integration/RedmineIntegrationTest.java | 17 +- .../terminal/JBTerminalSystemSettingsProvider.java | 4 +- .../terminal/LocalTerminalDirectRunner.java | 2 +- plugins/testng/src/META-INF/plugin.xml | 1 + .../testng/TestNGReferenceContributor.java | 28 +- .../configuration/SearchingForTestsTask.java | 3 +- .../testng/configuration/TestNGRunnableState.java | 26 +- .../theoryinpractice/testng/model/TestProxy.java | 12 +- .../testng/ui/actions/RerunFailedTestsAction.java | 18 +- .../designer/AbstractToolWindowManager.java | 302 +----- .../designer/DesignerToolWindowManager.java | 8 +- .../src/com/intellij/designer/LightToolWindow.java | 559 ---------- .../intellij/designer/LightToolWindowContent.java | 23 - .../designer/PaletteToolWindowContent.java | 11 - .../intellij/designer/ToggleEditorModeAction.java | 65 -- .../designSurface/DesignerEditorPanel.java | 102 +- .../intellij/designer/palette/PalettePanel.java | 6 - .../designer/palette/PaletteToolWindowManager.java | 11 +- plugins/ui-designer/src/META-INF/plugin.xml | 11 +- .../ide/palette/PaletteDragEventListener.java | 24 + .../src/com/intellij/ide/palette/PaletteGroup.java | 65 ++ .../src/com/intellij/ide/palette/PaletteItem.java | 56 + .../intellij/ide/palette/PaletteItemProvider.java | 34 + .../ide/palette/impl/PaletteComponentList.java | 403 ++++++++ .../ide/palette/impl/PaletteContentWindow.java | 114 ++ .../ide/palette/impl/PaletteGroupHeader.java | 211 ++++ .../ide/palette/impl/PaletteToolWindowManager.java | 118 +++ .../intellij/ide/palette/impl/PaletteWindow.java | 351 +++++++ .../uiDesigner/AbstractToolWindowManager.java | 67 ++ .../com/intellij/uiDesigner/FormEditingUtil.java | 10 +- .../intellij/uiDesigner/FormHighlightingPass.java | 6 +- .../uiDesigner/actions/ExpandSelectionAction.java | 13 +- .../uiDesigner/actions/ResetValueAction.java | 7 +- .../actions/SelectAllComponentsAction.java | 12 +- .../uiDesigner/actions/ShowFormSourceAction.java | 32 + .../uiDesigner/actions/ShrinkSelectionAction.java | 6 +- .../componentTree/ComponentTreeBuilder.java | 4 +- .../designSurface/DesignDropTargetListener.java | 32 +- .../uiDesigner/designSurface/GlassLayer.java | 4 +- .../uiDesigner/designSurface/GridCaptionPanel.java | 20 +- .../uiDesigner/designSurface/GuiEditor.java | 213 ++-- .../designSurface/InsertComponentProcessor.java | 12 +- .../uiDesigner/designSurface/MainProcessor.java | 12 +- .../designSurface/PassiveDecorationLayer.java | 4 +- .../designSurface/QuickFixManagerImpl.java | 10 +- .../intellij/uiDesigner/editor/UIFormEditor.java | 4 +- .../uiDesigner/editor/UIFormEditorProvider.java | 8 +- .../propertyInspector/DesignerToolWindow.java | 139 +++ .../DesignerToolWindowManager.java | 131 +++ .../propertyInspector/PropertyInspectorTable.java | 3 +- .../UIDesignerToolWindowManager.java | 264 ----- .../propertyInspector/editors/BindingEditor.java | 5 +- .../editors/string/StringEditor.java | 6 +- .../properties/BindingProperty.java | 4 +- .../uiDesigner/quickFixes/ShowHintAction.java | 5 +- .../src/messages/UIDesignerBundle.properties | 5 +- .../xpath/xslt/run/HighlightingOutputConsole.java | 2 + .../lang/xpath/xslt/run/XsltRunConfiguration.java | 42 +- .../plugins/xpathView/ShowXPathAction.java | 2 +- .../intellij/plugins/xpathView/XPathAction.java | 9 +- .../plugins/xpathView/search/SearchScope.java | 9 +- .../xsltDebugger/XsltDebuggerExtension.java | 14 +- .../xsltDebugger/ui/AbstractTabComponent.java | 4 +- python/edu/build/pycharm_edu_build.gant | 8 +- python/edu/build/upload_pythonInfo.xml | 2 +- .../course-creator/resources/META-INF/plugin.xml | 14 + .../fileTemplates/internal/task.answer.ft | 1 + .../resources/fileTemplates/internal/task.py.ft | 1 - .../plugins/coursecreator/AnswerFileType.java | 40 + .../coursecreator/AnswerFileTypeFactory.java | 28 + .../coursecreator/CCEditorFactoryListener.java | 13 +- .../plugins/coursecreator/CCProjectComponent.java | 141 ++- .../plugins/coursecreator/CCProjectService.java | 52 +- .../CCRefactoringElementListenerProvider.java | 96 ++ .../plugins/coursecreator/CCRunTests.java | 284 +++++ .../plugins/coursecreator/RunTestsLineMarker.java | 109 ++ .../coursecreator/StudyDocumentListener.java | 5 - .../coursecreator/actions/AddTaskWindow.java | 5 +- .../plugins/coursecreator/actions/CCRename.java | 97 ++ .../coursecreator/actions/CCRenameLesson.java | 48 + .../coursecreator/actions/CCRenameTask.java | 57 + .../coursecreator/actions/CCShowPreview.java | 104 ++ .../coursecreator/actions/CreateCourseArchive.java | 188 ++-- .../plugins/coursecreator/actions/CreateTask.java | 14 +- .../coursecreator/actions/CreateTaskFile.java | 6 +- .../plugins/coursecreator/format/Course.java | 14 +- .../plugins/coursecreator/format/Lesson.java | 31 +- .../plugins/coursecreator/format/Task.java | 20 +- .../plugins/coursecreator/format/TaskFile.java | 27 +- .../plugins/coursecreator/format/TaskWindow.java | 11 +- .../coursecreator/projectView/CCDirectoryNode.java | 2 + .../projectView/CCTreeStructureProvider.java | 18 +- .../coursecreator/ui/CreateCourseArchivePanel.form | 2 +- .../coursecreator/ui/CreateTaskWindowDialog.java | 6 + .../coursecreator/ui/CreateTaskWindowPanel.java | 5 + .../edu/learn-python/resources/META-INF/plugin.xml | 7 +- .../resources/courses/introduction_course.zip | Bin 93557 -> 95274 bytes .../python/edu/StudyDirectoryProjectGenerator.java | 2 +- .../com/jetbrains/python/edu/StudyTestRunner.java | 4 +- .../edu/actions/StudyIntroductionCourseAction.java | 75 ++ .../python/edu/actions/StudyNewProject.java | 34 - .../edu/actions/StudyRefreshTaskFileAction.java | 211 ++-- .../edu/actions/StudyReloadCourseAction.java | 134 +++ .../edu/actions/StudyTaskNavigationAction.java | 7 + .../python/edu/projectView/StudyDirectoryNode.java | 55 + .../python/edu/ui/StudyToolWindowFactory.java | 11 + .../resources/idea/PyCharmEduApplicationInfo.xml | 2 +- python/helpers/packaging_tool.py | 14 +- python/helpers/pycharm/_bdd_utils.py | 37 +- python/helpers/pycharm/behave_runner.py | 38 +- python/helpers/pycharm/lettuce_runner.py | 51 +- .../helpers/pycharm_generator_utils/clr_tools.py | 13 +- python/helpers/pydev/django_debug.py | 124 --- python/helpers/pydev/django_frame.py | 132 --- python/helpers/pydev/pydev_log.py | 9 +- python/helpers/pydev/pydev_monkey_qt.py | 7 +- python/helpers/pydev/pydev_run_in_console.py | 19 +- python/helpers/pydev/pydevconsole.py | 13 +- python/helpers/pydev/pydevd.py | 185 ++-- python/helpers/pydev/pydevd_breakpoints.py | 2 +- python/helpers/pydev/pydevd_constants.py | 1 - python/helpers/pydev/pydevd_frame.py | 274 ++--- python/helpers/pydev/pydevd_frame_utils.py | 31 +- python/helpers/pydev/pydevd_plugin_utils.py | 85 ++ python/helpers/pydev/pydevd_plugins/__init__.py | 0 .../helpers/pydev/pydevd_plugins/django_debug.py | 357 +++++++ .../helpers/pydev/pydevd_plugins/jinja2_debug.py | 341 ++++++ python/helpers/pydev/pydevd_resolver.py | 14 +- python/helpers/pydev/pydevd_trace_api.py | 35 + python/helpers/pydev/pydevd_utils.py | 23 +- python/helpers/pydev/pydevd_vars.py | 11 +- python/helpers/pydev/test_debug.py | 4 +- python/helpers/pydev/tests/check_pydevconsole.py | 110 ++ .../helpers/pydev/tests/test_pydev_ipython_010.py | 80 ++ .../pydev/tests_python/_debugger_case_qthread3.py | 1 + python/helpers/pydev/third_party/pkgutil_old.py | 591 +++++++++++ python/helpers/pydev/third_party/pluginbase.py | 454 ++++++++ python/helpers/pydev/third_party/uuid_old.py | 541 ++++++++++ .../configuration/PyActiveSdkConfigurable.java | 18 +- .../actions/AbstractProjectSettingsStep.java | 74 +- .../actions/GenerateProjectCallback.java | 2 +- .../PythonDocumentationQuickInfoProvider.java | 25 + .../python/packaging/PyPackageManager.java | 42 +- .../python/packaging/PyPackageManagers.java | 22 - .../python/templateLanguages/PyTemplatesUtil.java | 4 +- .../psi-api/src/com/jetbrains/python/PyNames.java | 2 + .../jetbrains/python/debugger/IPyDebugProcess.java | 6 +- .../jetbrains/python/debugger/PyDebugValue.java | 53 +- .../jetbrains/python/debugger/PyFrameAccessor.java | 3 + .../python/debugger/PyReferrersLoader.java | 20 + .../python/debugger/PyReferringObjectsValue.java | 71 ++ .../jetbrains/python/debugger/PyThreadInfo.java | 2 +- .../jetbrains/python/debugger/PyTypeHandler.java | 3 + .../python/debugger/pydev/AbstractCommand.java | 12 +- .../python/debugger/pydev/ConsoleExecCommand.java | 2 +- .../python/debugger/pydev/GetReferrersCommand.java | 50 + .../python/debugger/pydev/GetVariableCommand.java | 32 +- .../debugger/pydev/MultiProcessDebugger.java | 12 +- .../python/debugger/pydev/ProcessDebugger.java | 17 +- .../python/debugger/pydev/ProtocolParser.java | 26 + .../python/debugger/pydev/PyDebugCallback.java | 12 + .../python/debugger/pydev/PyVariableLocator.java | 17 + .../python/debugger/pydev/RemoteDebugger.java | 50 +- .../debugger/pydev/RunCustomOperationCommand.java | 86 ++ .../src/com/jetbrains/rest/RestPythonUtil.java | 5 +- python/src/META-INF/pycharm-core.xml | 8 +- python/src/META-INF/python-core.xml | 25 +- .../src/com/jetbrains/python/PyBundle.properties | 3 + .../completion/PyKeywordCompletionContributor.java | 17 +- .../editorActions/moveUpDown/PyStatementMover.java | 31 +- .../smartEnter/fixers/PyWithFixer.java | 10 +- .../PyIntegratedToolsConfigurable.java | 2 +- .../python/console/PydevConsoleCommunication.java | 10 +- .../console/PythonDebugConsoleCommunication.java | 8 +- .../jetbrains/python/debugger/PyDebugProcess.java | 28 +- .../python/debugger/PyDebugSupportUtils.java | 22 +- .../debugger/PyExceptionBreakpointProperties.java | 5 + .../documentation/PythonDocumentationProvider.java | 14 +- .../findUsages/PyFunctionFindUsagesHandler.java | 4 +- .../hierarchy/PyHierarchyNodeDescriptor.java | 85 ++ .../python/hierarchy/PyTypeHierarchyBrowser.java | 4 +- .../hierarchy/PyTypeHierarchyNodeDescriptor.java | 86 -- .../hierarchy/call/PyCallHierarchyBrowser.java | 101 ++ .../hierarchy/call/PyCallHierarchyProvider.java | 76 ++ .../call/PyCallHierarchyTreeStructureBase.java | 85 ++ .../call/PyCalleeFunctionTreeStructure.java | 42 + .../call/PyCallerFunctionTreeStructure.java | 42 + .../hierarchy/call/PyStaticCallHierarchyUtil.java | 163 +++ .../PySubTypesHierarchyTreeStructure.java | 18 +- .../PySuperTypesHierarchyTreeStructure.java | 25 +- .../PyTypeHierarchyTreeStructure.java | 27 +- .../PyPackageRequirementsInspection.java | 97 +- .../quickfix/GenerateBinaryStubsFix.java | 2 +- .../quickfix/PyDefaultArgumentQuickFix.java | 2 +- .../StatementEffectFunctionCallQuickFix.java | 21 +- .../PyUnresolvedReferencesInspection.java | 5 +- .../python/packaging/PyPackageManagerImpl.java | 665 ++---------- .../python/packaging/PyPackageManagerUI.java | 367 +++++++ .../python/packaging/PyPackageManagersImpl.java | 24 +- .../packaging/PyRemotePackageManagerImpl.java | 177 ++++ .../packaging/ui/PyInstalledPackagesPanel.java | 119 +-- .../packaging/ui/PyPackageManagementService.java | 15 +- .../python/projectView/PyElementNode.java | 24 +- python/src/com/jetbrains/python/psi/PyUtil.java | 4 +- .../com/jetbrains/python/psi/impl/PyClassImpl.java | 32 +- .../python/psi/impl/PyElementPresentation.java | 76 ++ .../jetbrains/python/psi/impl/PyFunctionImpl.java | 84 +- .../python/psi/impl/PyNamedParameterImpl.java | 8 +- .../python/psi/impl/PyPresentableElementImpl.java | 73 -- .../python/psi/impl/PySingleStarParameterImpl.java | 20 +- .../psi/impl/PyStringLiteralExpressionImpl.java | 2 +- .../python/psi/impl/PyTargetExpressionImpl.java | 22 +- .../python/psi/impl/PyTupleParameterImpl.java | 15 +- .../python/psi/impl/PythonLanguageLevelPusher.java | 5 +- .../changeSignature/PyChangeSignatureHandler.java | 2 +- .../move/PyMoveClassOrFunctionDelegate.java | 3 +- .../python/sdk/CreateVirtualEnvDialog.java | 6 +- python/src/com/jetbrains/python/sdk/PySdkUtil.java | 153 ++- .../jetbrains/python/sdk/PythonSdkDetailsStep.java | 6 +- .../com/jetbrains/python/sdk/PythonSdkType.java | 26 +- .../com/jetbrains/python/sdk/PythonSdkUpdater.java | 3 +- .../python/sdk/flavors/WinPythonSdkFlavor.java | 33 +- .../python/sdk/skeletons/PySkeletonGenerator.java | 39 +- .../python/sdk/skeletons/PySkeletonRefresher.java | 152 ++- .../statistics/PyPackageUsagesCollector.java | 6 +- .../structureView/PyStructureViewElement.java | 33 +- .../python/testing/PyRerunFailedTestsAction.java | 33 +- .../testing/PythonTestCommandLineStateBase.java | 4 +- .../TestRunConfigurationReRunResponsible.java | 30 + .../python/testing/VFSTestFrameworkListener.java | 5 +- .../pytest/PyTestConfigurationProducer.java | 5 +- .../python/validation/Pep8ExternalAnnotator.java | 3 +- .../validation/StringLiteralQuotesAnnotator.java | 45 +- .../codeInsight/smartEnter/withOnlyColonMissing.py | 1 + .../smartEnter/withOnlyColonMissing_after.py | 2 + .../ArgumentList_callee_verification.xml | 1 + .../ArgumentList_caller_verification.xml | 1 + .../hierarchy/call/Static/ArgumentList/file_1.py | 8 + .../hierarchy/call/Static/ArgumentList/main.py | 8 + .../Constructor_callee_verification.xml | 8 + .../Constructor_caller_verification.xml | 6 + .../hierarchy/call/Static/Constructor/main.py | 32 + .../DefaultValue_callee_verification.xml | 1 + .../DefaultValue_caller_verification.xml | 4 + .../hierarchy/call/Static/DefaultValue/main.py | 30 + .../Inheritance_callee_verification.xml | 1 + .../Inheritance_caller_verification.xml | 4 + .../hierarchy/call/Static/Inheritance/main.py | 36 + .../InnerFunction_callee_verification.xml | 5 + .../InnerFunction_caller_verification.xml | 1 + .../hierarchy/call/Static/InnerFunction/main.py | 9 + .../Static/Lambda/Lambda_callee_verification.xml | 8 + .../Static/Lambda/Lambda_caller_verification.xml | 1 + .../hierarchy/call/Static/Lambda/file_1.py | 18 + .../testData/hierarchy/call/Static/Lambda/main.py | 18 + .../NestedCall/NestedCall_callee_verification.xml | 12 + .../NestedCall/NestedCall_caller_verification.xml | 1 + .../hierarchy/call/Static/NestedCall/file_1.py | 10 + .../hierarchy/call/Static/NestedCall/main.py | 10 + .../OverriddenMethod_callee_verification.xml | 3 + .../OverriddenMethod_caller_verification.xml | 5 + .../call/Static/OverriddenMethod/file_1.py | 8 + .../hierarchy/call/Static/OverriddenMethod/main.py | 29 + .../Parentheses_callee_verification.xml | 3 + .../Parentheses_caller_verification.xml | 3 + .../hierarchy/call/Static/Parentheses/file_1.py | 20 + .../hierarchy/call/Static/Parentheses/main.py | 7 + .../Static/Simple/Simple_callee_verification.xml | 3 + .../Static/Simple/Simple_caller_verification.xml | 3 + .../testData/hierarchy/call/Static/Simple/main.py | 10 + .../highlighting/multipleEscapedBackslashes.py | 2 + .../inspections/DefaultArgumentEmptyList.py | 2 +- .../inspections/DefaultArgumentEmptyList_after.py | 2 +- python/testData/mover/outsideFromDict.py | 5 + python/testData/mover/outsideFromDict_afterDown.py | 5 + python/testData/mover/outsideFromDict_afterUp.py | 5 + python/testData/mover/sameLevelAsDict.py | 9 + python/testData/mover/sameLevelAsDict_afterDown.py | 9 + python/testData/mover/sameLevelAsDict_afterUp.py | 9 + .../com/jetbrains/env/python/PyPackagingTest.java | 18 +- .../jetbrains/env/python/PythonDebuggerTest.java | 21 +- .../com/jetbrains/env/ut/PyUnitTestTask.java | 108 +- .../com/jetbrains/python/PySmartEnterTest.java | 5 + .../com/jetbrains/python/PyStatementMoverTest.java | 8 + .../jetbrains/python/PythonHighlightingTest.java | 4 + .../python/PythonKeywordCompletionTest.java | 11 + .../com/jetbrains/python/fixtures/PyTestCase.java | 66 +- .../python/hierarchy/PyCallHierarchyTest.java | 158 +++ .../python/refactoring/PyInlineLocalTest.java | 16 +- .../jetbrains/python/sdkTools/PyTestSdkTools.java | 2 +- .../src/messages/CompilerBundle.properties | 2 +- .../src/messages/DebuggerBundle.properties | 13 +- resources/src/META-INF/IdeaPlugin.xml | 4 + resources/src/componentSets/IdeaComponents.xml | 1 - resources/src/componentSets/Toolwindows.xml | 7 - resources/src/idea/JavaActions.xml | 4 +- resources/src/idea/RichPlatformActions.xml | 2 +- resources/src/idea/RichPlatformPlugin.xml | 3 - resources/src/idea/RunManager.xml | 2 +- .../quickfixes/DictionarySuggestionProvider.java | 3 +- .../spellchecker/state/CachedDictionaryState.java | 7 +- updater/src/com/intellij/updater/Runner.java | 8 +- .../util/xml/impl/DomInvocationHandler.java | 11 +- .../xml/impl/IndexedElementInvocationHandler.java | 2 +- .../util/xml/stubs/StubParentStrategy.java | 21 +- .../com/intellij/util/xml/stubs/DomStubTest.java | 28 +- .../options/editor/WebEditorOptionsForm.form | 21 +- .../options/editor/WebEditorOptionsProvider.java | 6 +- .../editorActions/XmlEqTypedHandler.java | 2 +- .../editorActions/XmlSlashTypedHandler.java | 2 + .../codeInsight/template/emmet/EmmetParser.java | 8 +- .../codeInsight/template/emmet/XmlEmmetParser.java | 2 +- .../template/emmet/nodes/MoreOperationNode.java | 8 +- .../xml/arrangement/XmlArrangementVisitor.java | 2 +- .../xml/refactoring/SchemaPrefixRenameHandler.java | 19 +- .../intellij/xml/refactoring/XmlInlineHandler.java | 7 +- .../io/fastCgi/FastCgiChannelHandler.java | 9 +- .../html/RelaxedHtmlFromRngElementDescriptor.java | 3 + .../relaxNG/convert/ConvertSchemaSettingsImpl.java | 2 +- .../resources/html5-schema/html5-svg-mathml.rnc | 2 +- .../resources/html5-schema/xhtml5-svg-mathml.rnc | 2 +- .../resources/patches/0007_html5_svg_tiny.patch | 11 - .../resources/patches/0008_xhtml5_svg_tiny.patch | 11 - .../options/editor/WebEditorOptions.java | 41 +- .../com/intellij/xml/util/XmlRefCountHolder.java | 14 + .../resources/standardSchemas/xlink.dtd | 66 ++ .../javaee/ExternalResourceManagerExImpl.java | 85 +- .../intellij/javaee/InternalResourceProvider.java | 1 + .../src/com/intellij/javaee/ProjectResources.java | 31 +- .../com/intellij/lexer/HtmlHighlightingLexer.java | 121 ++- .../com/intellij/psi/XmlElementFactoryImpl.java | 13 +- .../manipulators/XmlAttributeValueManipulator.java | 3 +- .../impl/manipulators/XmlTextManipulator.java | 3 + .../intellij/psi/impl/source/xml/XmlTagImpl.java | 89 +- .../src/com/intellij/xml/HtmlXmlExtension.java | 13 +- .../src/com/intellij/xml/XmlCoreEnvironment.java | 5 +- .../xml/impl/schema/XmlNSDescriptorImpl.java | 520 +++++----- .../src/com/intellij/xml/util/XmlUtil.java | 122 +-- 1886 files changed, 79465 insertions(+), 20226 deletions(-) create mode 100644 .idea/libraries/asm_tools.xml create mode 100644 .idea/libraries/bouncy_castle.xml create mode 100644 .idea/libraries/builder_model.xml create mode 100644 .idea/libraries/commons_compress.xml create mode 100644 .idea/libraries/easymock_tools.xml create mode 100644 .idea/libraries/freemarker_2_3_20.xml create mode 100644 .idea/libraries/guava_tools.xml create mode 100644 .idea/libraries/javawriter.xml create mode 100644 .idea/libraries/kxml2.xml create mode 100644 .idea/libraries/lombok_ast.xml create mode 100644 .idea/libraries/mockito.xml create mode 100644 getPlugins.bat create mode 100755 getPlugins.sh create mode 100644 java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/UnsupportedExpressionException.java create mode 100644 java/execution/impl/src/com/intellij/execution/application/BaseJavaApplicationCommandLineState.java create mode 100644 java/execution/impl/src/com/intellij/execution/jar/JarApplicationCommandLineState.java create mode 100644 java/execution/impl/src/com/intellij/execution/jar/JarApplicationConfigurable.form create mode 100644 java/execution/impl/src/com/intellij/execution/jar/JarApplicationConfigurable.java create mode 100644 java/execution/impl/src/com/intellij/execution/jar/JarApplicationConfiguration.java create mode 100644 java/execution/impl/src/com/intellij/execution/jar/JarApplicationConfigurationProducer.java create mode 100644 java/execution/impl/src/com/intellij/execution/jar/JarApplicationConfigurationType.java create mode 100644 java/execution/impl/src/com/intellij/execution/jar/JarApplicationDebuggerRunner.java delete mode 100644 java/idea-ui/src/com/intellij/ide/palette/impl/PaletteComponentList.java delete mode 100644 java/idea-ui/src/com/intellij/ide/palette/impl/PaletteContentWindow.java delete mode 100644 java/idea-ui/src/com/intellij/ide/palette/impl/PaletteGroupHeader.java delete mode 100644 java/idea-ui/src/com/intellij/ide/palette/impl/PaletteManager.java delete mode 100644 java/idea-ui/src/com/intellij/ide/palette/impl/PaletteWindow.java create mode 100644 java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/NullableMethodAnalysis.java create mode 100644 java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/asm/AnalyzerExt.java create mode 100644 java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/asm/InterpreterExt.java create mode 100644 java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/asm/LiteAnalyzerExt.java create mode 100644 java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/asm/Subroutine.java delete mode 100644 java/java-analysis-impl/src/com/intellij/codeInspection/equalsAndHashcode/EqualsAndHashcode.java create mode 100644 java/java-analysis-impl/src/com/intellij/codeInspection/equalsAndHashcode/EqualsAndHashcodeBase.java create mode 100644 java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateStaticMethodQuickFix.java create mode 100644 java/java-impl/src/com/intellij/codeInspection/equalsAndHashcode/EqualsAndHashcode.java create mode 100644 java/java-impl/src/com/intellij/refactoring/extractMethodObject/ExtractLightMethodObjectHandler.java create mode 100644 java/java-tests/testData/codeInsight/completion/smartType/NoSemicolonInsideParentheses-out.java create mode 100644 java/java-tests/testData/codeInsight/completion/smartType/NoSemicolonInsideParentheses.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/CaptureWildcardFromUnboundCaptureWildcard.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/IDEA106985.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/IDEA114797.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/constraints/IntersectionTypeStrictSubtypingConstraint.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/graphInference/FreshVariablesCreatedDuringResolveDependingOnAlreadyResolvedVariables.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/graphInference/IncompatibleBoundsFromAssignment.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/DeepNestedLambdaExpressionsNoFormalParams.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/IDEA126778.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/NestedLambdaExpressions1.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/NestedLambdaExpressionsNoFormalParams.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/NestedLambdaExpressionsNoFormalParams1.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/NestedLambdaExpressionsNoFormalParamsStopAtStandalone.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/ParenthesizedExpressionsDuringConstrainsCollection.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/overloadResolution/IDEA102800.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/overloadResolution/PertinentToApplicabilityOfExplicitlyTypedLambda.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/overloadResolution/VoidValueCompatibilityOfImplicitlyTypedLambda.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeMethodSignatureFromUsage8/afterDisjunctionType.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeMethodSignatureFromUsage8/beforeDisjunctionType.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/lambdaCodeBlock2Expr/afterVoidValueChangedNoConflict.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/lambdaCodeBlock2Expr/beforeVoidValueChanged.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/lambdaCodeBlock2Expr/beforeVoidValueChangedNoConflict.java create mode 100644 java/java-tests/testData/codeInsight/generation/surroundWith/java/SurroundWithTryFinallyUsingIndents.java create mode 100644 java/java-tests/testData/codeInsight/generation/surroundWith/java/SurroundWithTryFinallyUsingIndents_after.java create mode 100644 java/java-tests/testData/codeInsight/javadocIG/clickableFieldReference.html create mode 100644 java/java-tests/testData/codeInsight/javadocIG/clickableFieldReference.java create mode 100644 java/java-tests/testData/codeInsight/javadocIG/enumConstantOrdinal_quick.html create mode 100644 java/java-tests/testData/refactoring/extractMethodObject4Debugger/AnonymousClassParams.java create mode 100644 java/java-tests/testData/refactoring/extractMethodObject4Debugger/InnerClass.java create mode 100644 java/java-tests/testData/refactoring/extractMethodObject4Debugger/InvokeReturnType.java create mode 100644 java/java-tests/testData/refactoring/extractMethodObject4Debugger/OffsetsAtCallSite.java create mode 100644 java/java-tests/testData/refactoring/extractMethodObject4Debugger/ResultExpr.java create mode 100644 java/java-tests/testData/refactoring/extractMethodObject4Debugger/ResultStatements.java create mode 100644 java/java-tests/testData/refactoring/extractMethodObject4Debugger/SimpleGeneration.java create mode 100644 java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/OverloadResolutionTest.java create mode 100644 java/java-tests/testSrc/com/intellij/refactoring/ExtractMethodObject4DebuggerTest.java delete mode 100644 java/openapi/src/com/intellij/ide/palette/PaletteDragEventListener.java delete mode 100644 java/openapi/src/com/intellij/ide/palette/PaletteGroup.java delete mode 100644 java/openapi/src/com/intellij/ide/palette/PaletteItem.java delete mode 100644 java/openapi/src/com/intellij/ide/palette/PaletteItemProvider.java delete mode 100644 jps/jps-builders/src/org/jetbrains/jps/incremental/java/ExternalJavacDescriptor.java create mode 100644 jps/jps-builders/src/org/jetbrains/jps/javac/ExternalJavacMessageHandler.java create mode 100644 jps/jps-builders/src/org/jetbrains/jps/javac/ExternalJavacProcess.java create mode 100644 jps/jps-builders/src/org/jetbrains/jps/javac/ExternalJavacServer.java delete mode 100644 jps/jps-builders/src/org/jetbrains/jps/javac/JavacServer.java delete mode 100644 jps/jps-builders/src/org/jetbrains/jps/javac/JavacServerClient.java delete mode 100644 jps/jps-builders/src/org/jetbrains/jps/javac/JavacServerResponseHandler.java delete mode 100644 lib/jsch-0.1.50.jar create mode 100644 lib/jsch-0.1.51.jar delete mode 100644 lib/src/ecjsrc-4.3.2.jar delete mode 100644 lib/src/jsch-0.1.50.zip create mode 100644 lib/src/jsch-0.1.51.zip delete mode 100644 lib/src/winp-1.17-patched-src.zip create mode 100644 lib/src/winp-1.21-patched.zip create mode 100644 lib/src/winp.patch delete mode 100644 lib/winp-1.17-patched.jar create mode 100644 lib/winp-1.21-patched.jar delete mode 100644 platform/core-api/src/com/intellij/concurrency/DoWhile.java delete mode 100644 platform/core-api/src/com/intellij/concurrency/Iterate.java create mode 100644 platform/core-impl/src/com/intellij/refactoring/rename/FragmentaryPsiReference.java create mode 100644 platform/dvcs-api/src/com/intellij/dvcs/push/PushTargetPanel.java create mode 100644 platform/dvcs-impl/src/com/intellij/dvcs/branch/DvcsBranchPopup.java create mode 100644 platform/dvcs-impl/src/com/intellij/dvcs/branch/DvcsBranchSync.java create mode 100644 platform/dvcs-impl/src/com/intellij/dvcs/branch/DvcsMultiRootBranchConfig.java create mode 100644 platform/dvcs-impl/src/com/intellij/dvcs/branch/DvcsSyncBranchSettings.java create mode 100644 platform/icons/src/debugger/commandLine.png create mode 100644 platform/icons/src/debugger/commandLine@2x.png create mode 100644 platform/indexing-api/src/com/intellij/lang/cacheBuilder/VersionedWordsScanner.java create mode 100644 platform/indexing-impl/src/com/intellij/psi/impl/cache/impl/todo/VersionedTodoIndexer.java create mode 100644 platform/lang-api/src/com/intellij/execution/configurations/SearchScopeProvidingRunProfile.java create mode 100644 platform/lang-api/src/com/intellij/execution/filters/ConsoleDependentFilterProvider.java create mode 100644 platform/lang-api/src/com/intellij/psi/codeStyle/autodetect/IndentOptionsDetector.java create mode 100644 platform/lang-api/src/com/intellij/psi/codeStyle/autodetect/IndentUsageInfo.java create mode 100644 platform/lang-api/src/com/intellij/psi/codeStyle/autodetect/IndentUsageStatistics.java create mode 100644 platform/lang-api/src/com/intellij/psi/codeStyle/autodetect/IndentUsageStatisticsImpl.java create mode 100644 platform/lang-api/src/com/intellij/psi/codeStyle/autodetect/LineIndentInfo.java create mode 100644 platform/lang-api/src/com/intellij/psi/codeStyle/autodetect/LineIndentInfoBuilder.java create mode 100644 platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/action/AbstractArrangementRuleAction.java create mode 100644 platform/lang-impl/src/com/intellij/execution/console/UseConsoleInputAction.java create mode 100644 platform/lang-impl/src/com/intellij/lang/customFolding/CustomFoldingRegionsPopup.java delete mode 100644 platform/lang-impl/src/com/intellij/lang/customFolding/GotoCustomRegionDialog.form delete mode 100644 platform/lang-impl/src/com/intellij/lang/customFolding/GotoCustomRegionDialog.java delete mode 100644 platform/lang-impl/src/com/intellij/profile/codeInspection/ui/LevelChooser.java create mode 100644 platform/platform-impl/src/com/intellij/designer/DesignerEditorPanelFacade.java create mode 100644 platform/platform-impl/src/com/intellij/designer/LightFillLayout.java create mode 100644 platform/platform-impl/src/com/intellij/designer/LightToolWindow.java create mode 100644 platform/platform-impl/src/com/intellij/designer/LightToolWindowContent.java create mode 100644 platform/platform-impl/src/com/intellij/designer/LightToolWindowManager.java create mode 100644 platform/platform-impl/src/com/intellij/designer/ToggleEditorModeAction.java delete mode 100644 platform/platform-impl/src/com/intellij/diagnostic/SubmitPerformanceReportAction.java delete mode 100644 platform/platform-impl/src/com/intellij/errorreport/ErrorReportSender.java delete mode 100644 platform/platform-impl/src/com/intellij/openapi/components/impl/stores/RoamingTypeExtensionPointBean.java create mode 100644 platform/platform-impl/src/com/intellij/openapi/options/ex/ConfigurableVisitor.java delete mode 100644 platform/platform-impl/src/com/intellij/openapi/options/ex/MixedConfigurableGroup.java delete mode 100644 platform/platform-impl/src/com/intellij/openapi/options/ex/NodeConfigurable.java create mode 100644 platform/platform-impl/src/com/intellij/openapi/options/ex/SortedConfigurableGroup.java create mode 100644 platform/platform-tests/testData/codeStyle/autodetect/manyComments.java create mode 100644 platform/platform-tests/testData/codeStyle/autodetect/manyZeroRelativeIndent.java create mode 100644 platform/platform-tests/testData/codeStyle/autodetect/simpleIndent.java create mode 100644 platform/platform-tests/testSrc/com/intellij/openapi/progress/util/ProgressIndicatorTest.java create mode 100644 platform/platform-tests/testSrc/com/intellij/psi/autodetect/IndentAutoDetectionTest.java create mode 100644 platform/projectModel-api/src/com/intellij/openapi/components/LastStorageChooserForWrite.java delete mode 100644 platform/projectModel-api/src/com/intellij/openapi/components/StorageAnnotationsDefaultValues.java create mode 100644 platform/projectModel-api/src/com/intellij/openapi/project/DefaultProjectTypeEP.java delete mode 100644 platform/projectModel-api/src/com/intellij/openapi/project/DefaultProjectTypeProvider.java create mode 100644 platform/testRunner/src/com/intellij/execution/testframework/TestTreeViewAction.java create mode 100644 platform/testRunner/src/com/intellij/execution/testframework/TestTreeViewActionsPromoter.java create mode 100644 platform/testRunner/src/com/intellij/execution/testframework/actions/RerunFailedTestsAction.java create mode 100644 platform/util/src/com/intellij/util/ui/JBSwingUtilities.java create mode 100644 platform/util/testSrc/com/intellij/execution/configurations/CommandLineTokenizerTest.java create mode 100644 platform/vcs-impl/src/com/intellij/openapi/diff/impl/dir/FrameDialogWrapper.java create mode 100644 platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/VcsManagerConfigurableProvider.java create mode 100644 platform/vcs-log/graph-api/src/com/intellij/vcs/log/graph/RowType.java create mode 100644 platform/vcs-log/impl/src/com/intellij/vcs/log/impl/LogDataImpl.java create mode 100644 platform/xdebugger-impl/src/com/intellij/xdebugger/impl/settings/DebuggerConfigurableProvider.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/initialization/DoubleBraceInitializationInspection.java delete mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/initialization/NonThreadSafeLazyInitializationInspection.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/initialization/NonThreadSafeLazyInitializationInspectionBase.java delete mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/j2me/AnonymousInnerClassMayBeStaticInspectionBase.java delete mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/javadoc/MissingPackageInfoInspection.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/javadoc/MissingPackageInfoInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/memory/AnonymousInnerClassMayBeStaticInspectionBase.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/memory/InnerClassMayBeStaticInspection.java create mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/memory/InnerClassReferenceVisitor.java delete mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/performance/InnerClassMayBeStaticInspection.java delete mode 100644 plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/performance/InnerClassReferenceVisitor.java create mode 100644 plugins/InspectionGadgets/src/com/siyeh/ig/initialization/NonThreadSafeLazyInitializationInspection.java delete mode 100644 plugins/InspectionGadgets/src/com/siyeh/ig/j2me/AnonymousInnerClassMayBeStaticInspection.java create mode 100644 plugins/InspectionGadgets/src/com/siyeh/ig/javadoc/MissingPackageInfoInspection.java create mode 100644 plugins/InspectionGadgets/src/com/siyeh/ig/memory/AnonymousInnerClassMayBeStaticInspection.java create mode 100644 plugins/InspectionGadgets/src/inspectionDescriptions/DoubleBraceInitialization.html create mode 100644 plugins/InspectionGadgets/test/com/siyeh/igfixes/initialization/double_brace_initialization/Argument.java create mode 100644 plugins/InspectionGadgets/test/com/siyeh/igfixes/initialization/double_brace_initialization/Field.after.java create mode 100644 plugins/InspectionGadgets/test/com/siyeh/igfixes/initialization/double_brace_initialization/Field.java create mode 100644 plugins/InspectionGadgets/test/com/siyeh/igfixes/initialization/double_brace_initialization/LocalVariable.after.java create mode 100644 plugins/InspectionGadgets/test/com/siyeh/igfixes/initialization/double_brace_initialization/LocalVariable.java create mode 100644 plugins/InspectionGadgets/test/com/siyeh/igfixes/memory/inner_class_static/Simple.after.java create mode 100644 plugins/InspectionGadgets/test/com/siyeh/igfixes/memory/inner_class_static/Simple.java delete mode 100644 plugins/InspectionGadgets/test/com/siyeh/igfixes/performance/inner_class_static/Simple.after.java delete mode 100644 plugins/InspectionGadgets/test/com/siyeh/igfixes/performance/inner_class_static/Simple.java create mode 100644 plugins/InspectionGadgets/test/com/siyeh/igfixes/style/cstyle_array_declaration/FieldWithWhitespace.after.java create mode 100644 plugins/InspectionGadgets/test/com/siyeh/igfixes/style/cstyle_array_declaration/FieldWithWhitespace.java create mode 100644 plugins/InspectionGadgets/test/com/siyeh/igfixes/style/cstyle_array_declaration/SimpleMethod.after.java create mode 100644 plugins/InspectionGadgets/test/com/siyeh/igfixes/style/cstyle_array_declaration/SimpleMethod.java create mode 100644 plugins/InspectionGadgets/test/com/siyeh/igfixes/threading/make_field_volatile/Simple.after.java create mode 100644 plugins/InspectionGadgets/test/com/siyeh/igfixes/threading/make_field_volatile/Simple.java create mode 100644 plugins/InspectionGadgets/test/com/siyeh/igfixes/threading/non_thread_safe_lazy_initialization/InstanceVariableReferenced.java create mode 100644 plugins/InspectionGadgets/test/com/siyeh/igfixes/threading/non_thread_safe_lazy_initialization/LocalVariableReferenced.java create mode 100644 plugins/InspectionGadgets/test/com/siyeh/igfixes/threading/non_thread_safe_lazy_initialization/NestedAssignment.java create mode 100644 plugins/InspectionGadgets/test/com/siyeh/igfixes/threading/non_thread_safe_lazy_initialization/Normal.after.java create mode 100644 plugins/InspectionGadgets/test/com/siyeh/igfixes/threading/non_thread_safe_lazy_initialization/Normal.java create mode 100644 plugins/InspectionGadgets/test/com/siyeh/igfixes/threading/non_thread_safe_lazy_initialization/StaticVariableReferenced.after.java create mode 100644 plugins/InspectionGadgets/test/com/siyeh/igfixes/threading/non_thread_safe_lazy_initialization/StaticVariableReferenced.java delete mode 100644 plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/ResultSetIndexZeroInspection.java create mode 100644 plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/result_set_index_zero/ResultSetIndexZero.java delete mode 100644 plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/string_equality/expected.xml delete mode 100644 plugins/InspectionGadgets/test/com/siyeh/igtest/confusing/NestedMethodCallInspection.java create mode 100644 plugins/InspectionGadgets/test/com/siyeh/igtest/initialization/double_brace_initialization/DoubleBraceInitialization.java create mode 100644 plugins/InspectionGadgets/test/com/siyeh/igtest/initialization/non_thread_safe_lazy_initialization/NonThreadSafeLazyInitialization.java create mode 100644 plugins/InspectionGadgets/test/com/siyeh/igtest/memory/anonymous_inner_class_may_be_static/AnonymousInnerClassMayBeStatic.java create mode 100644 plugins/InspectionGadgets/test/com/siyeh/igtest/memory/inner_class_may_be_static/InnerClassMayBeStatic.java delete mode 100644 plugins/InspectionGadgets/test/com/siyeh/igtest/performance/inner_class_may_be_static/InnerClassMayBeStaticInspection.java delete mode 100644 plugins/InspectionGadgets/test/com/siyeh/igtest/performance/inner_class_may_be_static/expected.xml delete mode 100644 plugins/InspectionGadgets/test/com/siyeh/igtest/style/CStyleArrayDeclarationInspection.java create mode 100644 plugins/InspectionGadgets/test/com/siyeh/igtest/style/cstyle_array_declaration/CStyleArrayDeclaration.java create mode 100644 plugins/InspectionGadgets/test/com/siyeh/igtest/style/nested_method_call/NestedMethodCall.java delete mode 100644 plugins/InspectionGadgets/test/com/siyeh/igtest/threading/NonThreadSafeLazyInitializationInspection.java delete mode 100644 plugins/InspectionGadgets/test/com/siyeh/igtest/visibility/ambiguous/expected.xml delete mode 100644 plugins/InspectionGadgets/test/com/siyeh/igtest/visibility/ambiguous_field_access/expected.xml create mode 100644 plugins/InspectionGadgets/testsrc/com/siyeh/ig/bugs/ResultSetIndexZeroInspectionTest.java create mode 100644 plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/initialization/DoubleBraceInitializationFixTest.java create mode 100644 plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/memory/InnerClassMayBeStaticFixTest.java delete mode 100644 plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/performance/InnerClassMayBeStaticFixTest.java create mode 100644 plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/style/CStyleArrayDeclarationFixTest.java create mode 100644 plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/threading/IntroduceHolderFixTest.java create mode 100644 plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/threading/MakeFieldVolatileFixTest.java create mode 100644 plugins/InspectionGadgets/testsrc/com/siyeh/ig/initialization/DoubleBraceInitializationInspectionTest.java create mode 100644 plugins/InspectionGadgets/testsrc/com/siyeh/ig/initialization/NonThreadSafeLazyInitializationInspectionTest.java create mode 100644 plugins/InspectionGadgets/testsrc/com/siyeh/ig/memory/AnonymousInnerClassMayBeStaticInspectionTest.java create mode 100644 plugins/InspectionGadgets/testsrc/com/siyeh/ig/memory/InnerClassMayBeStaticInspectionTest.java delete mode 100644 plugins/InspectionGadgets/testsrc/com/siyeh/ig/performance/InnerClassMayBeStaticInspectionTest.java create mode 100644 plugins/InspectionGadgets/testsrc/com/siyeh/ig/style/CStyleArrayDeclarationInspectionTest.java create mode 100644 plugins/InspectionGadgets/testsrc/com/siyeh/ig/style/NestedMethodCallInspectionTest.java create mode 100644 plugins/IntentionPowerPak/test/com/siyeh/ipp/decls/simplify_variable/FieldWithWhitespace.java create mode 100644 plugins/IntentionPowerPak/test/com/siyeh/ipp/decls/simplify_variable/FieldWithWhitespace_after.java create mode 100644 plugins/IntentionPowerPak/test/com/siyeh/ipp/equality/replace_equality_with_equals/EnumComparison.java create mode 100644 plugins/IntentionPowerPak/test/com/siyeh/ipp/equality/replace_equality_with_equals/NegatedObjectComparison.java create mode 100644 plugins/IntentionPowerPak/test/com/siyeh/ipp/equality/replace_equality_with_equals/NegatedObjectComparison_after.java create mode 100644 plugins/IntentionPowerPak/test/com/siyeh/ipp/equality/replace_equality_with_equals/NullComparison.java create mode 100644 plugins/IntentionPowerPak/test/com/siyeh/ipp/equality/replace_equality_with_equals/PrimitiveComparison.java create mode 100644 plugins/IntentionPowerPak/test/com/siyeh/ipp/equality/replace_equality_with_equals/SimpleObjectComparison.java create mode 100644 plugins/IntentionPowerPak/test/com/siyeh/ipp/equality/replace_equality_with_equals/SimpleObjectComparison_after.java create mode 100644 plugins/IntentionPowerPak/test/com/siyeh/ipp/equality/replace_equality_with_safe_equals/EnumComparison.java create mode 100644 plugins/IntentionPowerPak/test/com/siyeh/ipp/equality/replace_equality_with_safe_equals/NegatedObjectComparison.java create mode 100644 plugins/IntentionPowerPak/test/com/siyeh/ipp/equality/replace_equality_with_safe_equals/NegatedObjectComparison_after.java create mode 100644 plugins/IntentionPowerPak/test/com/siyeh/ipp/equality/replace_equality_with_safe_equals/NullComparison.java create mode 100644 plugins/IntentionPowerPak/test/com/siyeh/ipp/equality/replace_equality_with_safe_equals/PrimitiveComparison.java create mode 100644 plugins/IntentionPowerPak/test/com/siyeh/ipp/equality/replace_equality_with_safe_equals/SimpleObjectComparison.java create mode 100644 plugins/IntentionPowerPak/test/com/siyeh/ipp/equality/replace_equality_with_safe_equals/SimpleObjectComparison_after.java create mode 100644 plugins/IntentionPowerPak/test/com/siyeh/ipp/initialization/Array.java create mode 100644 plugins/IntentionPowerPak/test/com/siyeh/ipp/initialization/Array_after.java create mode 100644 plugins/IntentionPowerPak/test/com/siyeh/ipp/junit/create_assert/AnonymousClassJUnit3.java create mode 100644 plugins/IntentionPowerPak/test/com/siyeh/ipp/junit/create_assert/AnonymousClassJUnit3_after.java create mode 100644 plugins/IntentionPowerPak/test/com/siyeh/ipp/junit/create_assert/AnonymousClassJUnit4.java create mode 100644 plugins/IntentionPowerPak/test/com/siyeh/ipp/junit/create_assert/AnonymousClassJUnit4_after.java create mode 100644 plugins/IntentionPowerPak/test/com/siyeh/ipp/junit/create_assert/AssertFalse.java create mode 100644 plugins/IntentionPowerPak/test/com/siyeh/ipp/junit/create_assert/AssertFalse_after.java create mode 100644 plugins/IntentionPowerPak/test/com/siyeh/ipp/junit/create_assert/StaticImportJUnit4.java create mode 100644 plugins/IntentionPowerPak/test/com/siyeh/ipp/junit/create_assert/StaticImportJUnit4_after.java create mode 100644 plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/decls/SimplifyVariableIntentionTest.java create mode 100644 plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/equality/ReplaceEqualityWithEqualsIntentionTest.java create mode 100644 plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/equality/ReplaceEqualityWithSafeEqualsIntentionTest.java create mode 100644 plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/junit/CreateAssertIntentionTest.java create mode 100644 plugins/devkit/src/navigation/DevkitRelatedLineMarkerProviderBase.java create mode 100644 plugins/devkit/testData/pluginXmlDomStubs/pluginWithXInclude-extensionPoints.xml create mode 100644 plugins/devkit/testData/pluginXmlDomStubs/pluginWithXInclude-main.xml create mode 100644 plugins/devkit/testData/pluginXmlDomStubs/pluginWithXInclude.xml delete mode 100644 plugins/git4idea/src/git4idea/log/RefParser.java delete mode 100644 plugins/git4idea/src/git4idea/ui/branch/GitBranchSyncSetting.java create mode 100644 plugins/git4idea/tests/git4idea/log/RefParser.java delete mode 100644 plugins/git4idea/tests/git4idea/log/RefParserTest.java create mode 100644 plugins/github/src/org/jetbrains/plugins/github/api/GithubChangeIssueStateRequest.java create mode 100644 plugins/github/src/org/jetbrains/plugins/github/api/GithubConnection.java delete mode 100644 plugins/hg4idea/src/org/zmlx/hg4idea/action/HgBranchAbstractAction.java delete mode 100644 plugins/hg4idea/src/org/zmlx/hg4idea/action/HgBranchPopup.java delete mode 100644 plugins/hg4idea/src/org/zmlx/hg4idea/action/HgBranchPopupActions.java delete mode 100644 plugins/hg4idea/src/org/zmlx/hg4idea/action/HgBranchesAction.java delete mode 100644 plugins/hg4idea/src/org/zmlx/hg4idea/action/HgCommonBranchActions.java create mode 100644 plugins/hg4idea/src/org/zmlx/hg4idea/branch/HgBranchAbstractAction.java create mode 100644 plugins/hg4idea/src/org/zmlx/hg4idea/branch/HgBranchPopup.java create mode 100644 plugins/hg4idea/src/org/zmlx/hg4idea/branch/HgBranchPopupActions.java create mode 100644 plugins/hg4idea/src/org/zmlx/hg4idea/branch/HgBranchUtil.java create mode 100644 plugins/hg4idea/src/org/zmlx/hg4idea/branch/HgBranchesAction.java create mode 100644 plugins/hg4idea/src/org/zmlx/hg4idea/branch/HgCommonBranchActions.java create mode 100644 plugins/hg4idea/src/org/zmlx/hg4idea/branch/HgMultiRootBranchConfig.java create mode 100644 plugins/hg4idea/src/org/zmlx/hg4idea/push/HgPushTargetPanel.java create mode 100644 plugins/hg4idea/src/org/zmlx/hg4idea/push/PushTargetTextField.java create mode 100644 plugins/java-decompiler/engine/.gitattributes create mode 100644 plugins/java-decompiler/engine/build.xml create mode 100644 plugins/java-decompiler/engine/readme.txt create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/code/CodeConstants.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/code/ConstantsUtil.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/code/ExceptionHandler.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/code/ExceptionTable.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/code/FullInstructionSequence.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/code/IfInstruction.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/code/Instruction.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/code/InstructionSequence.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/code/JumpInstruction.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/code/SimpleInstructionSequence.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/code/SwitchInstruction.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/code/cfg/BasicBlock.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/code/cfg/ControlFlowGraph.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/code/cfg/ExceptionRangeCFG.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/code/interpreter/InstructionImpact.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/code/interpreter/Util.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/code/optinstructions/ALOAD.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/code/optinstructions/ANEWARRAY.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/code/optinstructions/ASTORE.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/code/optinstructions/BIPUSH.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/code/optinstructions/CHECKCAST.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/code/optinstructions/DLOAD.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/code/optinstructions/DSTORE.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/code/optinstructions/FLOAD.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/code/optinstructions/FSTORE.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/code/optinstructions/GETFIELD.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/code/optinstructions/GETSTATIC.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/code/optinstructions/GOTO.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/code/optinstructions/GOTO_W.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/code/optinstructions/IINC.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/code/optinstructions/ILOAD.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/code/optinstructions/INSTANCEOF.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/code/optinstructions/INVOKEDYNAMIC.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/code/optinstructions/INVOKEINTERFACE.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/code/optinstructions/INVOKESPECIAL.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/code/optinstructions/INVOKESTATIC.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/code/optinstructions/INVOKEVIRTUAL.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/code/optinstructions/ISTORE.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/code/optinstructions/JSR.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/code/optinstructions/JSR_W.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/code/optinstructions/LDC.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/code/optinstructions/LDC2_W.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/code/optinstructions/LDC_W.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/code/optinstructions/LLOAD.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/code/optinstructions/LOOKUPSWITCH.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/code/optinstructions/LSTORE.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/code/optinstructions/MULTIANEWARRAY.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/code/optinstructions/NEW.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/code/optinstructions/NEWARRAY.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/code/optinstructions/PUTFIELD.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/code/optinstructions/PUTSTATIC.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/code/optinstructions/RET.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/code/optinstructions/SIPUSH.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/code/optinstructions/TABLESWITCH.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/AssertProcessor.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/ClassReference14Processor.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/ClassWriter.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/ClassesProcessor.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/DecompilerContext.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/EnumProcessor.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/Fernflower.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/InitializerProcessor.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/collectors/CounterContainer.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/collectors/ImportCollector.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/collectors/VarNamesCollector.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/decompiler/BaseDecompiler.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/decompiler/ConsoleDecompiler.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/decompiler/PrintStreamLogger.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/extern/IBytecodeProvider.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/extern/IFernflowerLogger.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/extern/IFernflowerPreferences.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/extern/IIdentifierRenamer.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/extern/IResultSaver.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/rels/ClassWrapper.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/rels/LambdaProcessor.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorThread.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/rels/MethodWrapper.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/rels/NestedClassProcessor.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/rels/NestedMemberAccess.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/code/DeadCodeHelper.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/ClearStructHelper.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/ConcatenationHelper.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/DecHelper.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/DomHelper.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/EliminateLoopsHelper.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/ExitHelper.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/ExprProcessor.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/ExprentStack.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/FinallyProcessor.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/IdeaNotNullHelper.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/IfHelper.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/InlineSingleBlockHelper.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/LabelHelper.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/LoopExtractHelper.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/LowBreakHelper.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/MergeHelper.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/PPandMMHelper.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/PrimitiveExprsList.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/SecondaryFunctionsHelper.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/SequenceHelper.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/SimplifyExprentsHelper.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/StackVarsProcessor.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/StatEdge.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/StrongConnectivityHelper.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/DominatorEngine.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/DominatorTreeExceptionFilter.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/FastExtendedPostdominanceHelper.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/GenericDominatorEngine.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/IGraph.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/decompose/IGraphNode.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/deobfuscator/ExceptionDeobfuscator.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/deobfuscator/IrreducibleCFGDeobfuscator.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/AnnotationExprent.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ArrayExprent.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/AssertExprent.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/AssignmentExprent.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ConstExprent.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ExitExprent.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/Exprent.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/FieldExprent.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/FunctionExprent.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/IfExprent.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/MonitorExprent.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/NewExprent.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/SwitchExprent.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/DirectGraph.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/DirectNode.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/FlattenStatementsHelper.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/SSAConstructorSparseEx.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/sforms/SSAUConstructorSparseEx.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/BasicBlockStatement.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchAllStatement.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/CatchStatement.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/DoStatement.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/GeneralStatement.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/IfStatement.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/RootStatement.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SequenceStatement.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/Statement.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SwitchStatement.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/stats/SynchronizedStatement.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/CheckTypesResult.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarProcessor.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarTypeProcessor.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionEdge.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionNode.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionPaar.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsGraph.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarVersionsProcessor.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/renamer/ClassWrapperNode.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/renamer/ConverterHelper.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/renamer/IdentifierConverter.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/renamer/PoolInterceptor.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/struct/ContextUnit.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/struct/IDecompiledData.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/struct/StructClass.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/struct/StructContext.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/struct/StructField.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/struct/StructMember.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/struct/StructMethod.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/struct/attr/StructAnnDefaultAttribute.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/struct/attr/StructAnnotationAttribute.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/struct/attr/StructAnnotationParameterAttribute.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/struct/attr/StructAnnotationTypeAttribute.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/struct/attr/StructBootstrapMethodsAttribute.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/struct/attr/StructConstantValueAttribute.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/struct/attr/StructEnclosingMethodAttribute.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/struct/attr/StructExceptionsAttribute.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/struct/attr/StructGeneralAttribute.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/struct/attr/StructGenericSignatureAttribute.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/struct/attr/StructInnerClassesAttribute.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/struct/attr/StructLocalVariableTableAttribute.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/struct/consts/ConstantPool.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/struct/consts/LinkConstant.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/struct/consts/PooledConstant.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/struct/consts/PrimitiveConstant.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/struct/consts/VariableTypeEnum.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/struct/gen/DataPoint.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/struct/gen/FieldDescriptor.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/struct/gen/MethodDescriptor.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/struct/gen/VarType.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericClassDescriptor.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericFieldDescriptor.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericMain.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericMethodDescriptor.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/struct/gen/generics/GenericType.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/struct/lazy/LazyLoader.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/util/DataInputFullStream.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/util/FastFixedSetFactory.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/util/FastSetFactory.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/util/FastSparseSetFactory.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/util/InterpreterUtil.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/util/ListStack.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/util/SFormsFastMapDirect.java create mode 100644 plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/util/VBStyleCollection.java create mode 100644 plugins/java-decompiler/engine/test/org/jetbrains/java/decompiler/BulkDecompilationTest.java create mode 100644 plugins/java-decompiler/engine/test/org/jetbrains/java/decompiler/DecompilerTestFixture.java create mode 100644 plugins/java-decompiler/engine/test/org/jetbrains/java/decompiler/SingleClassesTest.java create mode 100644 plugins/java-decompiler/engine/testData/bulk.jar create mode 100644 plugins/java-decompiler/engine/testData/bulk/META-INF/MANIFEST.MF create mode 100644 plugins/java-decompiler/engine/testData/bulk/pkg/Main.java create mode 100644 plugins/java-decompiler/engine/testData/bulk/pkg/res/Loader.java create mode 100644 plugins/java-decompiler/engine/testData/bulk/pkg/res/resource.txt create mode 100644 plugins/java-decompiler/engine/testData/classes/InvalidMethodSignature.class create mode 100644 plugins/java-decompiler/engine/testData/classes/pkg/TestClassCast.class create mode 100644 plugins/java-decompiler/engine/testData/classes/pkg/TestClassFields.class create mode 100644 plugins/java-decompiler/engine/testData/classes/pkg/TestClassLambda.class create mode 100644 plugins/java-decompiler/engine/testData/classes/pkg/TestClassLoop.class create mode 100644 plugins/java-decompiler/engine/testData/classes/pkg/TestClassNestedInitializer$1.class create mode 100644 plugins/java-decompiler/engine/testData/classes/pkg/TestClassNestedInitializer.class create mode 100644 plugins/java-decompiler/engine/testData/classes/pkg/TestClassSwitch.class create mode 100644 plugins/java-decompiler/engine/testData/classes/pkg/TestClassTypes.class create mode 100644 plugins/java-decompiler/engine/testData/classes/pkg/TestClassVar.class create mode 100644 plugins/java-decompiler/engine/testData/classes/pkg/TestCodeConstructs.class create mode 100644 plugins/java-decompiler/engine/testData/classes/pkg/TestConstants$A.class create mode 100644 plugins/java-decompiler/engine/testData/classes/pkg/TestConstants.class create mode 100644 plugins/java-decompiler/engine/testData/classes/pkg/TestDebugSymbols.class create mode 100644 plugins/java-decompiler/engine/testData/classes/pkg/TestDeprecations$ByAnno.class create mode 100644 plugins/java-decompiler/engine/testData/classes/pkg/TestDeprecations$ByComment.class create mode 100644 plugins/java-decompiler/engine/testData/classes/pkg/TestDeprecations.class create mode 100644 plugins/java-decompiler/engine/testData/classes/pkg/TestEnum$1.class create mode 100644 plugins/java-decompiler/engine/testData/classes/pkg/TestEnum$2.class create mode 100644 plugins/java-decompiler/engine/testData/classes/pkg/TestEnum.class create mode 100644 plugins/java-decompiler/engine/testData/classes/pkg/TestExtendsList.class create mode 100644 plugins/java-decompiler/engine/testData/classes/pkg/TestMethodParameters$1Local.class create mode 100644 plugins/java-decompiler/engine/testData/classes/pkg/TestMethodParameters$C1.class create mode 100644 plugins/java-decompiler/engine/testData/classes/pkg/TestMethodParameters$C2.class create mode 100644 plugins/java-decompiler/engine/testData/classes/pkg/TestMethodParameters.class create mode 100644 plugins/java-decompiler/engine/testData/results/InvalidMethodSignature.dec create mode 100644 plugins/java-decompiler/engine/testData/results/TestClassCast.dec create mode 100644 plugins/java-decompiler/engine/testData/results/TestClassFields.dec create mode 100644 plugins/java-decompiler/engine/testData/results/TestClassLambda.dec create mode 100644 plugins/java-decompiler/engine/testData/results/TestClassLoop.dec create mode 100644 plugins/java-decompiler/engine/testData/results/TestClassNestedInitializer.dec create mode 100644 plugins/java-decompiler/engine/testData/results/TestClassSwitch.dec create mode 100644 plugins/java-decompiler/engine/testData/results/TestClassTypes.dec create mode 100644 plugins/java-decompiler/engine/testData/results/TestClassVar.dec create mode 100644 plugins/java-decompiler/engine/testData/results/TestCodeConstructs.dec create mode 100644 plugins/java-decompiler/engine/testData/results/TestConstants.dec create mode 100644 plugins/java-decompiler/engine/testData/results/TestDebugSymbols.dec create mode 100644 plugins/java-decompiler/engine/testData/results/TestDeprecations.dec create mode 100644 plugins/java-decompiler/engine/testData/results/TestEnum.dec create mode 100644 plugins/java-decompiler/engine/testData/results/TestExtendsList.dec create mode 100644 plugins/java-decompiler/engine/testData/results/TestMethodParameters.dec create mode 100644 plugins/java-decompiler/engine/testData/src/pkg/TestClassCast.java create mode 100644 plugins/java-decompiler/engine/testData/src/pkg/TestClassFields.java create mode 100644 plugins/java-decompiler/engine/testData/src/pkg/TestClassLambda.java create mode 100644 plugins/java-decompiler/engine/testData/src/pkg/TestClassLoop.java create mode 100644 plugins/java-decompiler/engine/testData/src/pkg/TestClassNestedInitializer.java create mode 100644 plugins/java-decompiler/engine/testData/src/pkg/TestClassSwitch.java create mode 100644 plugins/java-decompiler/engine/testData/src/pkg/TestClassTypes.java create mode 100644 plugins/java-decompiler/engine/testData/src/pkg/TestClassVar.java create mode 100644 plugins/java-decompiler/engine/testData/src/pkg/TestCodeConstructs.java create mode 100644 plugins/java-decompiler/engine/testData/src/pkg/TestConstants.java create mode 100644 plugins/java-decompiler/engine/testData/src/pkg/TestDebugSymbols.java create mode 100644 plugins/java-decompiler/engine/testData/src/pkg/TestDeprecations.java create mode 100644 plugins/java-decompiler/engine/testData/src/pkg/TestEnum.java create mode 100644 plugins/java-decompiler/engine/testData/src/pkg/TestExtendsList.java create mode 100644 plugins/java-decompiler/engine/testData/src/pkg/TestMethodParameters.java delete mode 100644 plugins/java-decompiler/lib/fernflower.jar delete mode 100644 plugins/java-decompiler/testData/Anonymous$1.class delete mode 100644 plugins/java-decompiler/testData/Anonymous.class delete mode 100644 plugins/java-decompiler/testData/Anonymous.java delete mode 100644 plugins/java-decompiler/testData/Anonymous.txt delete mode 100644 plugins/java-decompiler/testData/CodeConstructs.class delete mode 100644 plugins/java-decompiler/testData/CodeConstructs.java delete mode 100644 plugins/java-decompiler/testData/CodeConstructs.txt delete mode 100644 plugins/java-decompiler/testData/Constants$A.class delete mode 100644 plugins/java-decompiler/testData/Constants.class delete mode 100644 plugins/java-decompiler/testData/Constants.java delete mode 100644 plugins/java-decompiler/testData/Constants.txt delete mode 100644 plugins/java-decompiler/testData/Deprecations$ByAnno.class delete mode 100644 plugins/java-decompiler/testData/Deprecations$ByComment.class delete mode 100644 plugins/java-decompiler/testData/Deprecations.class delete mode 100644 plugins/java-decompiler/testData/Deprecations.java delete mode 100644 plugins/java-decompiler/testData/Deprecations.txt delete mode 100644 plugins/java-decompiler/testData/Enum$1.class delete mode 100644 plugins/java-decompiler/testData/Enum$2.class delete mode 100644 plugins/java-decompiler/testData/Enum.class delete mode 100644 plugins/java-decompiler/testData/Enum.java delete mode 100644 plugins/java-decompiler/testData/Enum.txt delete mode 100644 plugins/java-decompiler/testData/ExtendsList.class delete mode 100644 plugins/java-decompiler/testData/ExtendsList.java delete mode 100644 plugins/java-decompiler/testData/ExtendsList.txt delete mode 100644 plugins/java-decompiler/testData/Parameters$1Local.class delete mode 100644 plugins/java-decompiler/testData/Parameters$C1.class delete mode 100644 plugins/java-decompiler/testData/Parameters$C2.class delete mode 100644 plugins/java-decompiler/testData/Parameters.class delete mode 100644 plugins/java-decompiler/testData/Parameters.java delete mode 100644 plugins/java-decompiler/testData/Parameters.txt create mode 100644 plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SimpleTextNode.java delete mode 100644 plugins/ui-designer-core/src/com/intellij/designer/LightToolWindow.java delete mode 100644 plugins/ui-designer-core/src/com/intellij/designer/LightToolWindowContent.java delete mode 100644 plugins/ui-designer-core/src/com/intellij/designer/ToggleEditorModeAction.java create mode 100644 plugins/ui-designer/src/com/intellij/ide/palette/PaletteDragEventListener.java create mode 100644 plugins/ui-designer/src/com/intellij/ide/palette/PaletteGroup.java create mode 100644 plugins/ui-designer/src/com/intellij/ide/palette/PaletteItem.java create mode 100644 plugins/ui-designer/src/com/intellij/ide/palette/PaletteItemProvider.java create mode 100644 plugins/ui-designer/src/com/intellij/ide/palette/impl/PaletteComponentList.java create mode 100644 plugins/ui-designer/src/com/intellij/ide/palette/impl/PaletteContentWindow.java create mode 100644 plugins/ui-designer/src/com/intellij/ide/palette/impl/PaletteGroupHeader.java create mode 100644 plugins/ui-designer/src/com/intellij/ide/palette/impl/PaletteToolWindowManager.java create mode 100644 plugins/ui-designer/src/com/intellij/ide/palette/impl/PaletteWindow.java create mode 100644 plugins/ui-designer/src/com/intellij/uiDesigner/AbstractToolWindowManager.java create mode 100644 plugins/ui-designer/src/com/intellij/uiDesigner/actions/ShowFormSourceAction.java create mode 100644 plugins/ui-designer/src/com/intellij/uiDesigner/propertyInspector/DesignerToolWindow.java create mode 100644 plugins/ui-designer/src/com/intellij/uiDesigner/propertyInspector/DesignerToolWindowManager.java delete mode 100644 plugins/ui-designer/src/com/intellij/uiDesigner/propertyInspector/UIDesignerToolWindowManager.java create mode 100644 python/edu/course-creator/resources/fileTemplates/internal/task.answer.ft delete mode 100644 python/edu/course-creator/resources/fileTemplates/internal/task.py.ft create mode 100644 python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/AnswerFileType.java create mode 100644 python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/AnswerFileTypeFactory.java create mode 100644 python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/CCRefactoringElementListenerProvider.java create mode 100644 python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/CCRunTests.java create mode 100644 python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/RunTestsLineMarker.java create mode 100644 python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CCRename.java create mode 100644 python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CCRenameLesson.java create mode 100644 python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CCRenameTask.java create mode 100644 python/edu/course-creator/src/org/jetbrains/plugins/coursecreator/actions/CCShowPreview.java create mode 100644 python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyIntroductionCourseAction.java delete mode 100644 python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyNewProject.java create mode 100644 python/edu/learn-python/src/com/jetbrains/python/edu/actions/StudyReloadCourseAction.java delete mode 100644 python/helpers/pydev/django_debug.py delete mode 100644 python/helpers/pydev/django_frame.py create mode 100644 python/helpers/pydev/pydevd_plugin_utils.py create mode 100644 python/helpers/pydev/pydevd_plugins/__init__.py create mode 100644 python/helpers/pydev/pydevd_plugins/django_debug.py create mode 100644 python/helpers/pydev/pydevd_plugins/jinja2_debug.py create mode 100644 python/helpers/pydev/pydevd_trace_api.py create mode 100644 python/helpers/pydev/tests/check_pydevconsole.py create mode 100644 python/helpers/pydev/tests/test_pydev_ipython_010.py create mode 100644 python/helpers/pydev/third_party/pkgutil_old.py create mode 100644 python/helpers/pydev/third_party/pluginbase.py create mode 100644 python/helpers/pydev/third_party/uuid_old.py create mode 100644 python/openapi/src/com/jetbrains/python/documentation/PythonDocumentationQuickInfoProvider.java create mode 100644 python/pydevSrc/com/jetbrains/python/debugger/PyReferrersLoader.java create mode 100644 python/pydevSrc/com/jetbrains/python/debugger/PyReferringObjectsValue.java create mode 100644 python/pydevSrc/com/jetbrains/python/debugger/pydev/GetReferrersCommand.java create mode 100644 python/pydevSrc/com/jetbrains/python/debugger/pydev/PyDebugCallback.java create mode 100644 python/pydevSrc/com/jetbrains/python/debugger/pydev/PyVariableLocator.java create mode 100644 python/pydevSrc/com/jetbrains/python/debugger/pydev/RunCustomOperationCommand.java create mode 100644 python/src/com/jetbrains/python/hierarchy/PyHierarchyNodeDescriptor.java delete mode 100644 python/src/com/jetbrains/python/hierarchy/PyTypeHierarchyNodeDescriptor.java create mode 100644 python/src/com/jetbrains/python/hierarchy/call/PyCallHierarchyBrowser.java create mode 100644 python/src/com/jetbrains/python/hierarchy/call/PyCallHierarchyProvider.java create mode 100644 python/src/com/jetbrains/python/hierarchy/call/PyCallHierarchyTreeStructureBase.java create mode 100644 python/src/com/jetbrains/python/hierarchy/call/PyCalleeFunctionTreeStructure.java create mode 100644 python/src/com/jetbrains/python/hierarchy/call/PyCallerFunctionTreeStructure.java create mode 100644 python/src/com/jetbrains/python/hierarchy/call/PyStaticCallHierarchyUtil.java create mode 100644 python/src/com/jetbrains/python/packaging/PyPackageManagerUI.java create mode 100644 python/src/com/jetbrains/python/packaging/PyRemotePackageManagerImpl.java create mode 100644 python/src/com/jetbrains/python/psi/impl/PyElementPresentation.java delete mode 100644 python/src/com/jetbrains/python/psi/impl/PyPresentableElementImpl.java create mode 100644 python/src/com/jetbrains/python/testing/TestRunConfigurationReRunResponsible.java create mode 100644 python/testData/codeInsight/smartEnter/withOnlyColonMissing.py create mode 100644 python/testData/codeInsight/smartEnter/withOnlyColonMissing_after.py create mode 100644 python/testData/hierarchy/call/Static/ArgumentList/ArgumentList_callee_verification.xml create mode 100644 python/testData/hierarchy/call/Static/ArgumentList/ArgumentList_caller_verification.xml create mode 100644 python/testData/hierarchy/call/Static/ArgumentList/file_1.py create mode 100644 python/testData/hierarchy/call/Static/ArgumentList/main.py create mode 100644 python/testData/hierarchy/call/Static/Constructor/Constructor_callee_verification.xml create mode 100644 python/testData/hierarchy/call/Static/Constructor/Constructor_caller_verification.xml create mode 100644 python/testData/hierarchy/call/Static/Constructor/main.py create mode 100644 python/testData/hierarchy/call/Static/DefaultValue/DefaultValue_callee_verification.xml create mode 100644 python/testData/hierarchy/call/Static/DefaultValue/DefaultValue_caller_verification.xml create mode 100644 python/testData/hierarchy/call/Static/DefaultValue/main.py create mode 100644 python/testData/hierarchy/call/Static/Inheritance/Inheritance_callee_verification.xml create mode 100644 python/testData/hierarchy/call/Static/Inheritance/Inheritance_caller_verification.xml create mode 100644 python/testData/hierarchy/call/Static/Inheritance/main.py create mode 100644 python/testData/hierarchy/call/Static/InnerFunction/InnerFunction_callee_verification.xml create mode 100644 python/testData/hierarchy/call/Static/InnerFunction/InnerFunction_caller_verification.xml create mode 100644 python/testData/hierarchy/call/Static/InnerFunction/main.py create mode 100644 python/testData/hierarchy/call/Static/Lambda/Lambda_callee_verification.xml create mode 100644 python/testData/hierarchy/call/Static/Lambda/Lambda_caller_verification.xml create mode 100644 python/testData/hierarchy/call/Static/Lambda/file_1.py create mode 100644 python/testData/hierarchy/call/Static/Lambda/main.py create mode 100644 python/testData/hierarchy/call/Static/NestedCall/NestedCall_callee_verification.xml create mode 100644 python/testData/hierarchy/call/Static/NestedCall/NestedCall_caller_verification.xml create mode 100644 python/testData/hierarchy/call/Static/NestedCall/file_1.py create mode 100644 python/testData/hierarchy/call/Static/NestedCall/main.py create mode 100644 python/testData/hierarchy/call/Static/OverriddenMethod/OverriddenMethod_callee_verification.xml create mode 100644 python/testData/hierarchy/call/Static/OverriddenMethod/OverriddenMethod_caller_verification.xml create mode 100644 python/testData/hierarchy/call/Static/OverriddenMethod/file_1.py create mode 100644 python/testData/hierarchy/call/Static/OverriddenMethod/main.py create mode 100644 python/testData/hierarchy/call/Static/Parentheses/Parentheses_callee_verification.xml create mode 100644 python/testData/hierarchy/call/Static/Parentheses/Parentheses_caller_verification.xml create mode 100644 python/testData/hierarchy/call/Static/Parentheses/file_1.py create mode 100644 python/testData/hierarchy/call/Static/Parentheses/main.py create mode 100644 python/testData/hierarchy/call/Static/Simple/Simple_callee_verification.xml create mode 100644 python/testData/hierarchy/call/Static/Simple/Simple_caller_verification.xml create mode 100644 python/testData/hierarchy/call/Static/Simple/main.py create mode 100644 python/testData/highlighting/multipleEscapedBackslashes.py create mode 100644 python/testData/mover/outsideFromDict.py create mode 100644 python/testData/mover/outsideFromDict_afterDown.py create mode 100644 python/testData/mover/outsideFromDict_afterUp.py create mode 100644 python/testData/mover/sameLevelAsDict.py create mode 100644 python/testData/mover/sameLevelAsDict_afterDown.py create mode 100644 python/testData/mover/sameLevelAsDict_afterUp.py create mode 100644 python/testSrc/com/jetbrains/python/hierarchy/PyCallHierarchyTest.java delete mode 100644 resources/src/componentSets/Toolwindows.xml delete mode 100644 xml/relaxng/src/resources/patches/0007_html5_svg_tiny.patch delete mode 100644 xml/relaxng/src/resources/patches/0008_xhtml5_svg_tiny.patch create mode 100644 xml/xml-psi-impl/resources/standardSchemas/xlink.dtd diff --git a/.gitignore b/.gitignore index e1a3e405ef99..3fb1a17163fc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +/android /config /system .idea/workspace.xml diff --git a/.idea/codeStyleSettings.xml b/.idea/codeStyleSettings.xml index c12c1c95e4a1..5593bda74b98 100644 --- a/.idea/codeStyleSettings.xml +++ b/.idea/codeStyleSettings.xml @@ -290,9 +290,9 @@ - diff --git a/.idea/libraries/JSch.xml b/.idea/libraries/JSch.xml index 196b08ba1091..27c51c853ea3 100644 --- a/.idea/libraries/JSch.xml +++ b/.idea/libraries/JSch.xml @@ -1,12 +1,12 @@ - + - - + + \ No newline at end of file diff --git a/.idea/libraries/asm_tools.xml b/.idea/libraries/asm_tools.xml new file mode 100644 index 000000000000..9435a42f3c35 --- /dev/null +++ b/.idea/libraries/asm_tools.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/bouncy_castle.xml b/.idea/libraries/bouncy_castle.xml new file mode 100644 index 000000000000..4e4673aa939e --- /dev/null +++ b/.idea/libraries/bouncy_castle.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/builder_model.xml b/.idea/libraries/builder_model.xml new file mode 100644 index 000000000000..e50dabd08689 --- /dev/null +++ b/.idea/libraries/builder_model.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/commons_compress.xml b/.idea/libraries/commons_compress.xml new file mode 100644 index 000000000000..a77ee366b254 --- /dev/null +++ b/.idea/libraries/commons_compress.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/easymock_tools.xml b/.idea/libraries/easymock_tools.xml new file mode 100644 index 000000000000..c9963855dbcf --- /dev/null +++ b/.idea/libraries/easymock_tools.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/freemarker_2_3_20.xml b/.idea/libraries/freemarker_2_3_20.xml new file mode 100644 index 000000000000..831297d1baf3 --- /dev/null +++ b/.idea/libraries/freemarker_2_3_20.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/guava_tools.xml b/.idea/libraries/guava_tools.xml new file mode 100644 index 000000000000..b492cf4758e7 --- /dev/null +++ b/.idea/libraries/guava_tools.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/javawriter.xml b/.idea/libraries/javawriter.xml new file mode 100644 index 000000000000..1d73b23855f5 --- /dev/null +++ b/.idea/libraries/javawriter.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/kxml2.xml b/.idea/libraries/kxml2.xml new file mode 100644 index 000000000000..fe007f5f7a9c --- /dev/null +++ b/.idea/libraries/kxml2.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/lombok_ast.xml b/.idea/libraries/lombok_ast.xml new file mode 100644 index 000000000000..9fe1aa4ea9f2 --- /dev/null +++ b/.idea/libraries/lombok_ast.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/mockito.xml b/.idea/libraries/mockito.xml new file mode 100644 index 000000000000..a1ae9af5cc45 --- /dev/null +++ b/.idea/libraries/mockito.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/winp.xml b/.idea/libraries/winp.xml index 6e23b6ef4581..07689aee601a 100644 --- a/.idea/libraries/winp.xml +++ b/.idea/libraries/winp.xml @@ -1,11 +1,12 @@ - + - + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml index 0322352e9df9..f23cddeafc3c 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -10,18 +10,26 @@ + + + + + + + + @@ -39,6 +47,7 @@ + @@ -46,13 +55,16 @@ + + - + + @@ -66,6 +78,7 @@ + @@ -116,12 +129,17 @@ + + + + + @@ -130,7 +148,9 @@ + + @@ -168,6 +188,8 @@ + + @@ -190,6 +212,7 @@ + @@ -220,8 +243,6 @@ - - diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 275077f82558..a1b9ce94e902 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -2,6 +2,8 @@ + + diff --git a/bin/WinLauncher/WinLauncher.exe b/bin/WinLauncher/WinLauncher.exe index 66b04fa1bfd8..afe000ae7459 100644 Binary files a/bin/WinLauncher/WinLauncher.exe and b/bin/WinLauncher/WinLauncher.exe differ diff --git a/bin/WinLauncher/WinLauncher64.exe b/bin/WinLauncher/WinLauncher64.exe index 12a18522deed..7dc6ef8deb5e 100644 Binary files a/bin/WinLauncher/WinLauncher64.exe and b/bin/WinLauncher/WinLauncher64.exe differ diff --git a/bin/scripts/unix/idea.sh b/bin/scripts/unix/idea.sh index 19b5e717160b..a4e545509029 100755 --- a/bin/scripts/unix/idea.sh +++ b/bin/scripts/unix/idea.sh @@ -99,7 +99,7 @@ fi VERSION_LOG=`"$MKTEMP" -t java.version.log.XXXXXX` "$JDK/bin/java" -version 2> "$VERSION_LOG" -"$GREP" "64-Bit|x86_64" "$VERSION_LOG" > /dev/null +"$GREP" "64-Bit|x86_64|amd64" "$VERSION_LOG" > /dev/null BITS=$? "$RM" -f "$VERSION_LOG" if [ $BITS -eq 0 ]; then diff --git a/build/conf/mac/Contents/Info.plist b/build/conf/mac/Contents/Info.plist index d13854c5f77a..3f26292b969a 100644 --- a/build/conf/mac/Contents/Info.plist +++ b/build/conf/mac/Contents/Info.plist @@ -86,8 +86,6 @@ VMOptions @@vmoptions@@ -Xbootclasspath/a:../lib/boot.jar - WorkingDirectory - $APP_PACKAGE/Contents/bin diff --git a/build/conf/mac/Contents/MacOS/idea b/build/conf/mac/Contents/MacOS/idea index 6ba553f866fd..0699ef280f54 100755 Binary files a/build/conf/mac/Contents/MacOS/idea and b/build/conf/mac/Contents/MacOS/idea differ diff --git a/build/scripts/layouts.gant b/build/scripts/layouts.gant index e33f920fe146..6eb85a76a4ae 100644 --- a/build/scripts/layouts.gant +++ b/build/scripts/layouts.gant @@ -254,9 +254,7 @@ def layoutFull(String home, String targetDirectory, String patchedDescriptorDir } public def layoutCommunityPlugins(String home) { - if (isDefined("androidPluginHome")) { - layoutAndroid(p("androidPluginHome")) - } + layoutAndroid("$home/android") dir("plugins") { def simplePlugins = ["commander", "copyright", "java-i18n", "hg4idea", "github"] //, "tasks-time-tracking"] @@ -554,14 +552,7 @@ public def layoutCommunityPlugins(String home) { } } - pluginDir("java-decompiler") { - dir("lib") { - jar("java-decompiler.jar") { - module("java-decompiler") - } - fileset(file: "$home/plugins/java-decompiler/lib/fernflower.jar") - } - } + layoutPlugin("java-decompiler") } } diff --git a/build/scripts/libLicenses.gant b/build/scripts/libLicenses.gant index 5319444eaae6..b4a3c835999e 100644 --- a/build/scripts/libLicenses.gant +++ b/build/scripts/libLicenses.gant @@ -196,7 +196,7 @@ libraryLicense(name: "JGoodies Forms", libraryName: "jgoodies-forms", version: " libraryLicense(name: "JGoodies Looks", libraryName: "jgoodies-looks", version: "2.4.2", license: "BSD ", url: "http://www.jgoodies.com/freeware/looks/", licenseUrl: "http://www.jgoodies.com/downloads/libraries.html") libraryLicense(name: "JGoodies Common", libraryName: "jgoodies-common", version: "1.2.1", license: "BSD ", url: "http://www.jgoodies.com/freeware/looks/", licenseUrl: "http://www.jgoodies.com/downloads/libraries.html") libraryLicense(name: "JNA", libraryName: "jna", version: "3.4.0", license: "LGPL 2.1", url: "https://jna.dev.java.net/", licenseUrl: "http://www.opensource.org/licenses/lgpl-2.1.php") -libraryLicense(name: "JSch", libraryName: "JSch", version: "0.1.50", license: "BSD", url: "http://www.jcraft.com/jsch/", licenseUrl: "http://www.jcraft.com/jsch/LICENSE.txt") +libraryLicense(name: "JSch", libraryName: "JSch", version: "0.1.51", license: "BSD", url: "http://www.jcraft.com/jsch/", licenseUrl: "http://www.jcraft.com/jsch/LICENSE.txt") libraryLicense(name: "jsch-agent-proxy", libraryName: "jsch-agent-proxy", version: "0.0.6", license: "BSD", url: "https://github.com/ymnk/jsch-agent-proxy", licenseUrl: "https://github.com/ymnk/jsch-agent-proxy/blob/master/LICENSE.txt") libraryLicense(name: "jsch-agent-proxy for svnkit trilead", libraryName: "jsch.agentproxy.svnkit-trilead-ssh2.jar", version: "0.0.6", license: "BSD", url: "https://github.com/ymnk/jsch-agent-proxy", licenseUrl: "https://github.com/ymnk/jsch-agent-proxy/blob/master/LICENSE.txt") libraryLicense(name: "JUnit", libraryName: "JUnit3", version: "3.8.1", license: "CPL 1.0", url: "http://junit.org/") @@ -243,7 +243,7 @@ libraryLicense(name: "TestNG", version: "5.7 snapshot", license: "Apache 2.0", u libraryLicense(name: "Trilead SSH", libraryName: "trilead-ssh2", version: "build 213 and 217", license: "BSD style (see LICENSE.txt in trilead.jar)", url: "http://www.trilead.com/SSH_Library/") libraryLicense(name: "Trove4j", version: "1.1 (with patches by JetBrains)", license: "LGPL", url: "http://trove4j.sourceforge.net/", licenseUrl: "http://trove4j.sourceforge.net/html/license.html") libraryLicense(name: "Velocity", version: "1.7", license: "Apache 2.0", url: "http://velocity.apache.org/", licenseUrl: "http://velocity.apache.org/index.html") -libraryLicense(name: "winp", version: "1.17 (patched)", license: "MIT", url: "http://java.net/projects/winp", licenseUrl: "http://opensource.org/licenses/mit-license.php") +libraryLicense(name: "winp", version: "1.21 (patched)", license: "MIT", url: "http://java.net/projects/winp", licenseUrl: "http://opensource.org/licenses/mit-license.php") libraryLicense(name: "Xalan", libraryName:"Xalan-2.7.1", version: "2.7.1", license: "Apache 2.0", url: "http://xml.apache.org/xalan-j/", licenseUrl: "http://xml.apache.org/xalan-j/") libraryLicense(name: "Xerces", version: "2.9.1", license: "Apache 2.0", url: "http://xerces.apache.org/xerces2-j/", licenseUrl: "http://xerces.apache.org/xerces2-j/") libraryLicense(name: "XML Commons (xml-apis.jar, resolver.jar)", version: "", license: "Apache 2.0, W3C Software License , public domain", url: "http://xml.apache.org/commons/", licenseUrl: "http://xml.apache.org/commons/licenses.html") @@ -285,4 +285,3 @@ jetbrainsLibrary("JPS") jetbrainsLibrary("Maven Embedder") jetbrainsLibrary("tcServiceMessages") jetbrainsLibrary("optimizedFileManager.jar") -jetbrainsLibrary("fernflower.jar") diff --git a/community-tests/src/tests/testGroups.properties b/community-tests/src/tests/testGroups.properties index a528fa8840df..5104b681ee6f 100644 --- a/community-tests/src/tests/testGroups.properties +++ b/community-tests/src/tests/testGroups.properties @@ -4,6 +4,3 @@ org.jetbrains.idea.svn.* org.jetbrains.idea.svn16.* com.intellij.util.net.ssl.* com.intellij.tasks.integration.live.* - -[DECOMPILER_TESTS] -org.jetbrains.java.decompiler.* diff --git a/getPlugins.bat b/getPlugins.bat new file mode 100644 index 000000000000..3cd8b977612a --- /dev/null +++ b/getPlugins.bat @@ -0,0 +1,2 @@ +call git clone git://git.jetbrains.org/idea/android.git android +call git clone git://git.jetbrains.org/idea/adt-tools-base.git android/tools-base diff --git a/getPlugins.sh b/getPlugins.sh new file mode 100755 index 000000000000..9afcebda7d70 --- /dev/null +++ b/getPlugins.sh @@ -0,0 +1,2 @@ +git clone git://git.jetbrains.org/idea/android.git android +git clone git://git.jetbrains.org/idea/adt-tools-base.git android/tools-base diff --git a/images/src/org/intellij/images/editor/impl/ImageEditorUI.java b/images/src/org/intellij/images/editor/impl/ImageEditorUI.java index f3d920d3d3c3..b14296626eba 100644 --- a/images/src/org/intellij/images/editor/impl/ImageEditorUI.java +++ b/images/src/org/intellij/images/editor/impl/ImageEditorUI.java @@ -16,9 +16,11 @@ package org.intellij.images.editor.impl; import com.intellij.ide.CopyPasteSupport; +import com.intellij.ide.CopyProvider; import com.intellij.ide.DeleteProvider; import com.intellij.ide.PsiActionSupportFactory; import com.intellij.openapi.actionSystem.*; +import com.intellij.openapi.ide.CopyPasteManager; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; @@ -46,12 +48,16 @@ import javax.swing.*; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import java.awt.*; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseWheelEvent; import java.awt.event.MouseWheelListener; import java.awt.image.BufferedImage; import java.awt.image.ColorModel; +import java.io.IOException; import java.util.Locale; /** @@ -59,7 +65,7 @@ import java.util.Locale; * * @author Alexey Efimov */ -final class ImageEditorUI extends JPanel implements DataProvider { +final class ImageEditorUI extends JPanel implements DataProvider, CopyProvider { @NonNls private static final String IMAGE_PANEL = "image"; @NonNls @@ -390,7 +396,7 @@ final class ImageEditorUI extends JPanel implements DataProvider { } else if (LangDataKeys.PSI_ELEMENT_ARRAY.is(dataId)) { return new PsiElement[]{(PsiElement)getData(CommonDataKeys.PSI_ELEMENT.getName())}; } else if (PlatformDataKeys.COPY_PROVIDER.is(dataId) && copyPasteSupport != null) { - return copyPasteSupport.getCopyProvider(); + return this; } else if (PlatformDataKeys.CUT_PROVIDER.is(dataId) && copyPasteSupport != null) { return copyPasteSupport.getCutProvider(); } else if (PlatformDataKeys.DELETE_ELEMENT_PROVIDER.is(dataId)) { @@ -401,4 +407,47 @@ final class ImageEditorUI extends JPanel implements DataProvider { return null; } + + @Override + public void performCopy(@NotNull DataContext dataContext) { + ImageDocument document = imageComponent.getDocument(); + BufferedImage image = document.getValue(); + CopyPasteManager.getInstance().setContents(new ImageTransferable(image)); + } + + @Override + public boolean isCopyEnabled(@NotNull DataContext dataContext) { + return true; + } + + @Override + public boolean isCopyVisible(@NotNull DataContext dataContext) { + return true; + } + + private static class ImageTransferable implements Transferable { + private final BufferedImage myImage; + + public ImageTransferable(@NotNull BufferedImage image) { + myImage = image; + } + + @Override + public DataFlavor[] getTransferDataFlavors() { + return new DataFlavor[] { DataFlavor.imageFlavor }; + } + + @Override + public boolean isDataFlavorSupported(DataFlavor dataFlavor) { + return DataFlavor.imageFlavor.equals(dataFlavor); + } + + @Override + public Object getTransferData(DataFlavor dataFlavor) throws UnsupportedFlavorException, IOException { + if (!DataFlavor.imageFlavor.equals(dataFlavor)) { + throw new UnsupportedFlavorException(dataFlavor); + } + return myImage; + } + } } diff --git a/images/src/org/intellij/images/options/impl/OptionsManagerImpl.java b/images/src/org/intellij/images/options/impl/OptionsManagerImpl.java index 50edd78feab0..146655408931 100644 --- a/images/src/org/intellij/images/options/impl/OptionsManagerImpl.java +++ b/images/src/org/intellij/images/options/impl/OptionsManagerImpl.java @@ -28,11 +28,8 @@ import org.jdom.Element; * @author Alexey Efimov */ @State( - name = "Images.OptionsManager", - roamingType = RoamingType.DISABLED, - storages = { - @Storage(file = StoragePathMacros.APP_CONFIG + "/images.support.xml") - } + name = "Images.OptionsManager", + storages = {@Storage(file = StoragePathMacros.APP_CONFIG + "/images.support.xml", roamingType = RoamingType.DISABLED)} ) final class OptionsManagerImpl extends OptionsManager implements PersistentStateComponent { private final OptionsImpl options = new OptionsImpl(); diff --git a/java/compiler/impl/compiler-impl.iml b/java/compiler/impl/compiler-impl.iml index c6e3e4f81eab..552d0dfe0d38 100644 --- a/java/compiler/impl/compiler-impl.iml +++ b/java/compiler/impl/compiler-impl.iml @@ -17,7 +17,6 @@ - diff --git a/java/compiler/impl/src/com/intellij/compiler/actions/CompileAction.java b/java/compiler/impl/src/com/intellij/compiler/actions/CompileAction.java index 38f230f92997..542f1331d2d5 100644 --- a/java/compiler/impl/src/com/intellij/compiler/actions/CompileAction.java +++ b/java/compiler/impl/src/com/intellij/compiler/actions/CompileAction.java @@ -110,7 +110,7 @@ public class CompileAction extends CompileActionBase { elementDescription = "'" + file.getName() + "'"; } else { - if (!ActionPlaces.MAIN_MENU.equals(event.getPlace())) { + if (!ActionPlaces.isMainMenuOrActionSearch(event.getPlace())) { // the action should be invisible in popups for non-java files presentation.setEnabled(false); presentation.setVisible(false); diff --git a/java/compiler/impl/src/com/intellij/compiler/server/BuildManager.java b/java/compiler/impl/src/com/intellij/compiler/server/BuildManager.java index 669cf92d37da..69bb36054f21 100644 --- a/java/compiler/impl/src/com/intellij/compiler/server/BuildManager.java +++ b/java/compiler/impl/src/com/intellij/compiler/server/BuildManager.java @@ -73,7 +73,10 @@ import com.intellij.openapi.vfs.newvfs.BulkFileListener; import com.intellij.openapi.vfs.newvfs.events.VFileEvent; import com.intellij.openapi.vfs.newvfs.impl.FileNameCache; import com.intellij.openapi.wm.IdeFrame; -import com.intellij.util.*; +import com.intellij.util.Alarm; +import com.intellij.util.ArrayUtil; +import com.intellij.util.Function; +import com.intellij.util.SmartList; import com.intellij.util.concurrency.Semaphore; import com.intellij.util.concurrency.SequentialTaskExecutor; import com.intellij.util.containers.IntArrayList; @@ -897,9 +900,6 @@ public class BuildManager implements ApplicationComponent{ if (!Registry.is("compiler.process.use.memory.temp.cache")) { cmdLine.addParameter("-D"+ GlobalOptions.USE_MEMORY_TEMP_CACHE_OPTION + "=false"); } - if (Registry.is("compiler.process.use.external.javac")) { - cmdLine.addParameter("-D"+ GlobalOptions.USE_EXTERNAL_JAVAC_OPTION); - } // javac's VM should use the same default locale that IDEA uses in order for javac to print messages in 'correct' language if (mySystemCharset != null) { @@ -907,13 +907,15 @@ public class BuildManager implements ApplicationComponent{ cmdLine.addParameter("-D" + CharsetToolkit.FILE_ENCODING_PROPERTY + "=" + mySystemCharset.name()); } cmdLine.addParameter("-D" + JpsGlobalLoader.FILE_TYPES_COMPONENT_NAME_KEY + "=" + FileTypeManagerImpl.getFileTypeComponentName()); - for (String name : new String[]{"user.language", "user.country", "user.region", PathManager.PROPERTY_HOME_PATH, - PathManager.PROPERTY_CONFIG_PATH, PathManager.PROPERTY_PLUGINS_PATH, PathManager.PROPERTY_PATHS_SELECTOR}) { + for (String name : new String[]{"user.language", "user.country", "user.region", PathManager.PROPERTY_PATHS_SELECTOR}) { final String value = System.getProperty(name); if (value != null) { cmdLine.addParameter("-D" + name + "=" + value); } } + cmdLine.addParameter("-D" + PathManager.PROPERTY_HOME_PATH + "=" + PathManager.getHomePath()); + cmdLine.addParameter("-D" + PathManager.PROPERTY_CONFIG_PATH + "=" + PathManager.getConfigPath()); + cmdLine.addParameter("-D" + PathManager.PROPERTY_PLUGINS_PATH + "=" + PathManager.getPluginsPath()); cmdLine.addParameter("-D" + GlobalOptions.LOG_DIR_OPTION + "=" + FileUtil.toSystemIndependentName(getBuildLogDirectory().getAbsolutePath())); @@ -941,7 +943,6 @@ public class BuildManager implements ApplicationComponent{ cmdLine.addParameter(launcherClass.getName()); final List cp = ClasspathBootstrap.getBuildProcessApplicationClasspath(true); - cp.add(getJpsPluginSystemClassesPath()); cp.addAll(myClasspathManager.getBuildProcessPluginsClasspath(project)); if (isProfilingMode) { cp.add(new File(workDirectory, "yjp-controller-api-redist.jar").getPath()); @@ -967,17 +968,6 @@ public class BuildManager implements ApplicationComponent{ }; } - private static String getJpsPluginSystemClassesPath() { - File classesRoot = new File(PathUtil.getJarPathForClass(BuildManager.class)); - if (classesRoot.isDirectory()) { - //running from sources: load classes from .../out/production/jps-plugin-system - return new File(classesRoot.getParentFile(), "jps-plugin-system").getAbsolutePath(); - } - else { - return new File(classesRoot.getParentFile(), "rt/jps-plugin-system.jar").getAbsolutePath(); - } - } - public File getBuildSystemDirectory() { return new File(mySystemDirectory, SYSTEM_ROOT); } diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/JarArtifactFromModulesDialog.form b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/JarArtifactFromModulesDialog.form index eed2b888d43f..aeffe083cd9e 100644 --- a/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/JarArtifactFromModulesDialog.form +++ b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/JarArtifactFromModulesDialog.form @@ -67,7 +67,7 @@ - + @@ -75,7 +75,7 @@ - + diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/JarArtifactFromModulesDialog.java b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/JarArtifactFromModulesDialog.java index 02d9e4228eec..ea2f4a64f622 100644 --- a/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/JarArtifactFromModulesDialog.java +++ b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/JarArtifactFromModulesDialog.java @@ -55,7 +55,7 @@ public class JarArtifactFromModulesDialog extends DialogWrapper { public JarArtifactFromModulesDialog(PackagingElementResolvingContext context) { super(context.getProject()); myContext = context; - setTitle("Create Jar from Modules"); + setTitle("Create JAR from Modules"); myMainClassLabel.setLabelFor(myMainClassField.getTextField()); myManifestDirLabel.setLabelFor(myManifestDirField.getTextField()); diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/JarArtifactType.java b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/JarArtifactType.java index 2a0af9007963..bd4494c6f4a3 100644 --- a/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/JarArtifactType.java +++ b/java/compiler/impl/src/com/intellij/packaging/impl/artifacts/JarArtifactType.java @@ -33,7 +33,7 @@ import java.util.List; */ public class JarArtifactType extends ArtifactType { public JarArtifactType() { - super("jar", "Jar"); + super("jar", "JAR"); } public static JarArtifactType getInstance() { diff --git a/java/compiler/impl/src/com/intellij/packaging/impl/elements/PackagingElementFactoryImpl.java b/java/compiler/impl/src/com/intellij/packaging/impl/elements/PackagingElementFactoryImpl.java index 0d1737b07e42..7e8f6016d591 100644 --- a/java/compiler/impl/src/com/intellij/packaging/impl/elements/PackagingElementFactoryImpl.java +++ b/java/compiler/impl/src/com/intellij/packaging/impl/elements/PackagingElementFactoryImpl.java @@ -274,7 +274,7 @@ public class PackagingElementFactoryImpl extends PackagingElementFactory { @NotNull @Override public PackagingElement createExtractedDirectory(@NotNull VirtualFile jarEntry) { - LOG.assertTrue(jarEntry.getFileSystem() instanceof JarFileSystem, "Expected file from jar but file from " + jarEntry.getFileSystem() + " found"); + LOG.assertTrue(jarEntry.getFileSystem() instanceof JarFileSystem, "Expected file from JAR but file from " + jarEntry.getFileSystem() + " found"); final String fullPath = jarEntry.getPath(); final int jarEnd = fullPath.indexOf(JarFileSystem.JAR_SEPARATOR); return new ExtractedDirectoryPackagingElement(fullPath.substring(0, jarEnd), fullPath.substring(jarEnd + 1)); diff --git a/java/debugger/impl/src/com/intellij/debugger/DebugUIEnvironment.java b/java/debugger/impl/src/com/intellij/debugger/DebugUIEnvironment.java index 8c89bada270d..04f4d583cf99 100644 --- a/java/debugger/impl/src/com/intellij/debugger/DebugUIEnvironment.java +++ b/java/debugger/impl/src/com/intellij/debugger/DebugUIEnvironment.java @@ -15,7 +15,6 @@ */ package com.intellij.debugger; -import com.intellij.diagnostic.logging.LogFilesManager; import com.intellij.execution.configurations.RunProfile; import com.intellij.execution.ui.RunContentDescriptor; import com.intellij.openapi.actionSystem.DefaultActionGroup; @@ -37,8 +36,6 @@ public interface DebugUIEnvironment { @Nullable Icon getIcon(); - void initLogs(RunContentDescriptor content, LogFilesManager logFilesManager); - void initActions(RunContentDescriptor content, DefaultActionGroup actionGroup); @Nullable diff --git a/java/debugger/impl/src/com/intellij/debugger/DefaultDebugUIEnvironment.java b/java/debugger/impl/src/com/intellij/debugger/DefaultDebugUIEnvironment.java index f29f3bfe572d..6f8b13e4d06a 100644 --- a/java/debugger/impl/src/com/intellij/debugger/DefaultDebugUIEnvironment.java +++ b/java/debugger/impl/src/com/intellij/debugger/DefaultDebugUIEnvironment.java @@ -15,14 +15,10 @@ */ package com.intellij.debugger; -import com.intellij.diagnostic.logging.LogFilesManager; -import com.intellij.diagnostic.logging.OutputFileUtil; import com.intellij.execution.Executor; import com.intellij.execution.configurations.RemoteConnection; -import com.intellij.execution.configurations.RunConfigurationBase; import com.intellij.execution.configurations.RunProfile; import com.intellij.execution.configurations.RunProfileState; -import com.intellij.execution.process.ProcessHandler; import com.intellij.execution.runners.ExecutionEnvironment; import com.intellij.execution.runners.RestartAction; import com.intellij.execution.ui.RunContentDescriptor; @@ -63,21 +59,6 @@ public class DefaultDebugUIEnvironment implements DebugUIEnvironment { return getRunProfile().getIcon(); } - @Override - public void initLogs(RunContentDescriptor content, LogFilesManager logFilesManager) { - ProcessHandler processHandler = content.getProcessHandler(); - if (getRunProfile() instanceof RunConfigurationBase) { - RunConfigurationBase runConfiguration = (RunConfigurationBase)getRunProfile(); - - logFilesManager.registerFileMatcher(runConfiguration); - - if (processHandler != null) { - logFilesManager.initLogConsoles(runConfiguration, processHandler); - OutputFileUtil.attachDumpListener(runConfiguration, processHandler, content.getExecutionConsole()); - } - } - } - @Override public void initActions(RunContentDescriptor content, DefaultActionGroup actionGroup) { Executor executor = myExecutionEnvironment.getExecutor(); diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/AdjustArrayRangeAction.java b/java/debugger/impl/src/com/intellij/debugger/actions/AdjustArrayRangeAction.java index ce375815bb77..0f5398a56cba 100644 --- a/java/debugger/impl/src/com/intellij/debugger/actions/AdjustArrayRangeAction.java +++ b/java/debugger/impl/src/com/intellij/debugger/actions/AdjustArrayRangeAction.java @@ -16,6 +16,7 @@ package com.intellij.debugger.actions; import com.intellij.debugger.engine.DebugProcessImpl; +import com.intellij.debugger.engine.JavaValue; import com.intellij.debugger.engine.events.SuspendContextCommandImpl; import com.intellij.debugger.impl.DebuggerContextImpl; import com.intellij.debugger.settings.ArrayRendererConfigurable; @@ -27,6 +28,9 @@ import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.options.Configurable; import com.intellij.openapi.options.ShowSettingsUtil; import com.intellij.openapi.project.Project; +import com.intellij.xdebugger.frame.XValue; +import com.intellij.xdebugger.impl.ui.tree.actions.XDebuggerTreeActionBase; +import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl; import org.jetbrains.annotations.Nullable; public class AdjustArrayRangeAction extends DebuggerAction { @@ -44,42 +48,48 @@ public class AdjustArrayRangeAction extends DebuggerAction { final Project project = debuggerContext.getProject(); - final DebuggerTreeNodeImpl selectedNode = getSelectedNode(e.getDataContext()); - if (selectedNode == null) { + final XValueNodeImpl node = XDebuggerTreeActionBase.getSelectedNode(e.getDataContext()); + if (node == null) { return; } - NodeDescriptorImpl descriptor = selectedNode.getDescriptor(); - if(!(descriptor instanceof ValueDescriptorImpl /*&& ((ValueDescriptorImpl)descriptor).isArray()*/)) { + + XValue container = node.getValueContainer(); + if (!(container instanceof JavaValue)) { return; } - final ArrayRenderer renderer = getArrayRenderer((ValueDescriptorImpl)descriptor)/*(ArrayRenderer)((ValueDescriptorImpl)selectedNode.getDescriptor()).getLastRenderer()*/; + final ValueDescriptorImpl descriptor = ((JavaValue)container).getDescriptor(); + ArrayRenderer renderer = getArrayRenderer(descriptor); if (renderer == null) { return; } - String title = createNodeTitle("", selectedNode); - String label = selectedNode.toString(); - int index = label.indexOf('='); - if (index > 0) { - title = title + " " + label.substring(index); - } + //String title = createNodeTitle("", selectedNode); + //String label = selectedNode.toString(); + //int index = label.indexOf('='); + //if (index > 0) { + // title = title + " " + label.substring(index); + //} + String title = node.getName(); final ArrayRenderer clonedRenderer = renderer.clone(); + clonedRenderer.setForced(true); if (ShowSettingsUtil.getInstance().editConfigurable(project, new NamedArrayConfigurable(title, clonedRenderer))) { debugProcess.getManagerThread().schedule(new SuspendContextCommandImpl(debuggerContext.getSuspendContext()) { @Override public void contextAction() throws Exception { - final ValueDescriptorImpl nodeDescriptor = (ValueDescriptorImpl)selectedNode.getDescriptor(); - final Renderer lastRenderer = nodeDescriptor.getLastRenderer(); + final Renderer lastRenderer = descriptor.getLastRenderer(); if (lastRenderer instanceof ArrayRenderer) { - selectedNode.setRenderer(clonedRenderer); + descriptor.setRenderer(clonedRenderer); + refreshViews(node); + //selectedNode.setRenderer(clonedRenderer); } else if (lastRenderer instanceof CompoundNodeRenderer) { final CompoundNodeRenderer compoundRenderer = (CompoundNodeRenderer)lastRenderer; final ChildrenRenderer childrenRenderer = compoundRenderer.getChildrenRenderer(); if (childrenRenderer instanceof ExpressionChildrenRenderer) { - ExpressionChildrenRenderer.setPreferableChildrenRenderer(nodeDescriptor, clonedRenderer); - selectedNode.calcRepresentation(); + ExpressionChildrenRenderer.setPreferableChildrenRenderer(descriptor, clonedRenderer); + refreshViews(node); + //selectedNode.calcRepresentation(); } } } @@ -90,10 +100,14 @@ public class AdjustArrayRangeAction extends DebuggerAction { @Override public void update(AnActionEvent e) { boolean enable = false; - DebuggerTreeNodeImpl selectedNode = getSelectedNode(e.getDataContext()); - if(selectedNode != null) { - NodeDescriptorImpl descriptor = selectedNode.getDescriptor(); - enable = descriptor instanceof ValueDescriptorImpl && getArrayRenderer((ValueDescriptorImpl)descriptor) != null; + XValueNodeImpl node = XDebuggerTreeActionBase.getSelectedNode(e.getDataContext()); + if (node == null) { + return; + } + XValue container = node.getValueContainer(); + if (container instanceof JavaValue) { + ValueDescriptorImpl descriptor = ((JavaValue)container).getDescriptor(); + enable = getArrayRenderer(descriptor) != null; } e.getPresentation().setVisible(enable); } diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/DebuggerAction.java b/java/debugger/impl/src/com/intellij/debugger/actions/DebuggerAction.java index 86ddfaa5fc08..2e87aa973285 100644 --- a/java/debugger/impl/src/com/intellij/debugger/actions/DebuggerAction.java +++ b/java/debugger/impl/src/com/intellij/debugger/actions/DebuggerAction.java @@ -22,6 +22,7 @@ package com.intellij.debugger.actions; import com.intellij.debugger.DebuggerManagerEx; +import com.intellij.debugger.engine.JavaDebugProcess; import com.intellij.debugger.impl.DebuggerContextImpl; import com.intellij.debugger.impl.DebuggerStateManager; import com.intellij.debugger.ui.impl.DebuggerTreePanel; @@ -32,6 +33,11 @@ import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.project.Project; import com.intellij.ui.DoubleClickListener; +import com.intellij.xdebugger.XDebugProcess; +import com.intellij.xdebugger.XDebugSession; +import com.intellij.xdebugger.impl.frame.XDebugView; +import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; @@ -153,4 +159,18 @@ public abstract class DebuggerAction extends AnAction { } }); } + + public static void refreshViews(@NotNull XValueNodeImpl node) { + refreshViews(XDebugView.getSession(node.getTree())); + } + + public static void refreshViews(@Nullable XDebugSession session) { + if (session != null) { + XDebugProcess process = session.getDebugProcess(); + if (process instanceof JavaDebugProcess) { + ((JavaDebugProcess)process).saveNodeHistory(); + } + session.rebuildViews(); + } + } } diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/JavaMarkObjectActionHandler.java b/java/debugger/impl/src/com/intellij/debugger/actions/JavaMarkObjectActionHandler.java index dcd0454c16aa..282a6dc9f30b 100644 --- a/java/debugger/impl/src/com/intellij/debugger/actions/JavaMarkObjectActionHandler.java +++ b/java/debugger/impl/src/com/intellij/debugger/actions/JavaMarkObjectActionHandler.java @@ -50,6 +50,8 @@ import java.util.Collections; import java.util.List; import java.util.Map; +import static com.intellij.openapi.actionSystem.PlatformDataKeys.CONTEXT_COMPONENT; + /* * Class SetValueAction * @author Jeka @@ -71,7 +73,8 @@ public class JavaMarkObjectActionHandler extends MarkObjectActionHandler { final DebuggerTree tree = node.getTree(); tree.saveState(node); - + + final Component parent = event.getData(CONTEXT_COMPONENT); final ValueDescriptorImpl valueDescriptor = ((ValueDescriptorImpl)descriptor); final DebuggerContextImpl debuggerContext = tree.getDebuggerContext(); final DebugProcessImpl debugProcess = debuggerContext.getDebugProcess(); @@ -93,7 +96,7 @@ public class JavaMarkObjectActionHandler extends MarkObjectActionHandler { final boolean suggestAdditionalMarkup = canSuggestAdditionalMarkup(debugProcess, valueDescriptor.getValue()); SwingUtilities.invokeAndWait(new Runnable() { public void run() { - ObjectMarkupPropertiesDialog dialog = new ObjectMarkupPropertiesDialog(defaultText, suggestAdditionalMarkup); + ObjectMarkupPropertiesDialog dialog = new ObjectMarkupPropertiesDialog(parent, defaultText, suggestAdditionalMarkup); dialog.show(); if (dialog.isOK()) { result.set(Pair.create(dialog.getConfiguredMarkup(), dialog.isMarkAdditionalFields())); diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/JavaReferringObjectsValue.java b/java/debugger/impl/src/com/intellij/debugger/actions/JavaReferringObjectsValue.java index 815ddd231f14..a362e7d7caff 100644 --- a/java/debugger/impl/src/com/intellij/debugger/actions/JavaReferringObjectsValue.java +++ b/java/debugger/impl/src/com/intellij/debugger/actions/JavaReferringObjectsValue.java @@ -61,6 +61,7 @@ public class JavaReferringObjectsValue extends JavaValue { @Override public void computeChildren(@NotNull final XCompositeNode node) { + if (checkContextNotResumed(node)) return; getEvaluationContext().getDebugProcess().getManagerThread().schedule( new SuspendContextCommandImpl(getEvaluationContext().getSuspendContext()) { @Override diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/ObjectMarkupPropertiesDialog.java b/java/debugger/impl/src/com/intellij/debugger/actions/ObjectMarkupPropertiesDialog.java index cd4aa14b7703..454b130307cf 100644 --- a/java/debugger/impl/src/com/intellij/debugger/actions/ObjectMarkupPropertiesDialog.java +++ b/java/debugger/impl/src/com/intellij/debugger/actions/ObjectMarkupPropertiesDialog.java @@ -20,6 +20,7 @@ import com.intellij.openapi.ui.ex.MultiLineLabel; import com.intellij.xdebugger.impl.ui.tree.ValueMarkerPresentationDialogBase; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; @@ -35,8 +36,8 @@ public class ObjectMarkupPropertiesDialog extends ValueMarkerPresentationDialogB private JPanel myAdditionalPropertiesPanel; private MultiLineLabel myDescriptionLabel; - public ObjectMarkupPropertiesDialog(@NotNull final String defaultText, boolean suggestAdditionalMarkup) { - super(defaultText); + public ObjectMarkupPropertiesDialog(@Nullable Component parent, @NotNull final String defaultText, boolean suggestAdditionalMarkup) { + super(parent, defaultText); mySuggestAdditionalMarkup = suggestAdditionalMarkup; myDescriptionLabel.setText("If the value is referenced by a constant field of an abstract class,\n" + "IDEA could additionally mark all values referenced from this class with the names of referencing fields."); diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/PopFrameAction.java b/java/debugger/impl/src/com/intellij/debugger/actions/PopFrameAction.java index f5aa685c7994..f89ae5ce8292 100644 --- a/java/debugger/impl/src/com/intellij/debugger/actions/PopFrameAction.java +++ b/java/debugger/impl/src/com/intellij/debugger/actions/PopFrameAction.java @@ -19,68 +19,212 @@ */ package com.intellij.debugger.actions; +import com.intellij.CommonBundle; import com.intellij.debugger.DebuggerBundle; +import com.intellij.debugger.SourcePosition; import com.intellij.debugger.engine.DebugProcessImpl; import com.intellij.debugger.engine.JavaStackFrame; import com.intellij.debugger.engine.SuspendContextImpl; import com.intellij.debugger.impl.DebuggerContextImpl; import com.intellij.debugger.jdi.StackFrameProxyImpl; import com.intellij.debugger.jdi.VirtualMachineProxyImpl; -import com.intellij.debugger.ui.impl.watch.*; +import com.intellij.debugger.settings.DebuggerSettings; +import com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeImpl; +import com.intellij.debugger.ui.impl.watch.StackFrameDescriptorImpl; +import com.intellij.debugger.ui.impl.watch.ThreadDescriptorImpl; import com.intellij.idea.ActionsBundle; import com.intellij.openapi.actionSystem.ActionPlaces; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.ui.MessageDialogBuilder; import com.intellij.openapi.ui.Messages; +import com.intellij.psi.PsiCodeBlock; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiStatement; +import com.intellij.psi.PsiTryStatement; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.ui.UIUtil; import com.intellij.xdebugger.XDebugSession; +import com.intellij.xdebugger.XDebuggerBundle; import com.intellij.xdebugger.XDebuggerManager; +import com.intellij.xdebugger.evaluation.EvaluationMode; +import com.intellij.xdebugger.evaluation.XDebuggerEvaluator; import com.intellij.xdebugger.frame.XStackFrame; +import com.intellij.xdebugger.frame.XValue; +import com.intellij.xdebugger.impl.breakpoints.XExpressionImpl; import com.sun.jdi.InvalidStackFrameException; import com.sun.jdi.NativeMethodException; import com.sun.jdi.VMDisconnectedException; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.ArrayList; +import java.util.List; + public class PopFrameAction extends DebuggerAction { public void actionPerformed(AnActionEvent e) { - Project project = e.getData(CommonDataKeys.PROJECT); - StackFrameProxyImpl stackFrame = getStackFrameProxy(e); + final Project project = e.getData(CommonDataKeys.PROJECT); + final JavaStackFrame stackFrame = getStackFrame(e); if(stackFrame == null) { return; } try { - DebuggerContextImpl debuggerContext = DebuggerAction.getDebuggerContext(e.getDataContext()); - DebugProcessImpl debugProcess = debuggerContext.getDebugProcess(); + final DebuggerContextImpl debuggerContext = DebuggerAction.getDebuggerContext(e.getDataContext()); + final DebugProcessImpl debugProcess = debuggerContext.getDebugProcess(); if(debugProcess == null) { return; } - debugProcess.getManagerThread().schedule(debugProcess.createPopFrameCommand(debuggerContext, stackFrame)); + + if (!DebuggerSettings.EVALUATE_FINALLY_NEVER.equals(DebuggerSettings.getInstance().EVALUATE_FINALLY_ON_POP_FRAME)) { + List statements = getFinallyStatements(debuggerContext.getSourcePosition()); + if (!statements.isEmpty()) { + StringBuilder sb = new StringBuilder(); + for (PsiStatement statement : statements) { + sb.append("\n").append(statement.getText()); + } + if (DebuggerSettings.EVALUATE_FINALLY_ALWAYS.equals(DebuggerSettings.getInstance().EVALUATE_FINALLY_ON_POP_FRAME)) { + evaluateAndPop(project, stackFrame, debuggerContext, debugProcess, sb); + return; + } + else { + int res = MessageDialogBuilder + .yesNoCancel(UIUtil.removeMnemonic(ActionsBundle.actionText(DebuggerActions.POP_FRAME)), + DebuggerBundle.message("warning.finally.block.detected") + sb) + .project(project) + .icon(Messages.getWarningIcon()) + .yesText(DebuggerBundle.message("button.execute.finally")) + .noText(DebuggerBundle.message("button.drop.anyway")) + .cancelText(CommonBundle.message("button.cancel")) + .doNotAsk( + new DialogWrapper.DoNotAskOption() { + @Override + public boolean isToBeShown() { + return !DebuggerSettings.EVALUATE_FINALLY_ALWAYS.equals(DebuggerSettings.getInstance().EVALUATE_FINALLY_ON_POP_FRAME) && + !DebuggerSettings.EVALUATE_FINALLY_NEVER.equals(DebuggerSettings.getInstance().EVALUATE_FINALLY_ON_POP_FRAME); + } + + @Override + public void setToBeShown(boolean value, int exitCode) { + if (!value) { + DebuggerSettings.getInstance().EVALUATE_FINALLY_ON_POP_FRAME = + exitCode == Messages.YES ? DebuggerSettings.EVALUATE_FINALLY_ALWAYS : DebuggerSettings.EVALUATE_FINALLY_NEVER; + } + else { + DebuggerSettings.getInstance().EVALUATE_FINALLY_ON_POP_FRAME = DebuggerSettings.EVALUATE_FINALLY_ASK; + } + } + + @Override + public boolean canBeHidden() { + return true; + } + + @Override + public boolean shouldSaveOptionsOnCancel() { + return false; + } + + @NotNull + @Override + public String getDoNotShowMessage() { + return CommonBundle.message("dialog.options.do.not.show"); + } + }) + .show(); + + switch (res) { + case Messages.CANCEL: + return; + case Messages.NO: + break; + case Messages.YES: // evaluate finally + evaluateAndPop(project, stackFrame, debuggerContext, debugProcess, sb); + return; + } + } + } + } + debugProcess.getManagerThread().schedule(debugProcess.createPopFrameCommand(debuggerContext, stackFrame.getStackFrameProxy())); } catch (NativeMethodException e2){ - Messages.showMessageDialog(project, DebuggerBundle.message("error.native.method.exception"), ActionsBundle.actionText(DebuggerActions.POP_FRAME), Messages.getErrorIcon()); + Messages.showMessageDialog(project, DebuggerBundle.message("error.native.method.exception"), + UIUtil.removeMnemonic(ActionsBundle.actionText(DebuggerActions.POP_FRAME)), Messages.getErrorIcon()); } catch (InvalidStackFrameException ignored) { } - catch(VMDisconnectedException vde) { + catch(VMDisconnectedException ignored) { } } - @Nullable - private static StackFrameProxyImpl getStackFrameProxy(AnActionEvent e) { - DebuggerTreeNodeImpl selectedNode = getSelectedNode(e.getDataContext()); - if(selectedNode != null) { - NodeDescriptorImpl descriptor = selectedNode.getDescriptor(); - if(descriptor instanceof StackFrameDescriptorImpl) { - if(selectedNode.getNextSibling() != null) { - StackFrameDescriptorImpl frameDescriptor = ((StackFrameDescriptorImpl)descriptor); - return frameDescriptor.getFrameProxy(); - } - return null; - } - else if(descriptor instanceof ThreadDescriptorImpl || descriptor instanceof ThreadGroupDescriptorImpl) { - return null; + private static void evaluateAndPop(final Project project, + final JavaStackFrame stackFrame, + final DebuggerContextImpl debuggerContext, + final DebugProcessImpl debugProcess, StringBuilder sb) { + XDebuggerEvaluator evaluator = stackFrame.getEvaluator(); + if (evaluator != null) { + evaluator.evaluate(XExpressionImpl.fromText(sb.toString(), EvaluationMode.CODE_FRAGMENT), + new XDebuggerEvaluator.XEvaluationCallback() { + @Override + public void evaluated(@NotNull XValue result) { + debugProcess.getManagerThread() + .schedule(debugProcess.createPopFrameCommand(debuggerContext, stackFrame.getStackFrameProxy())); + } + + @Override + public void errorOccurred(@NotNull final String errorMessage) { + ApplicationManager.getApplication().invokeLater(new Runnable() { + @Override + public void run() { + Messages + .showMessageDialog(project, DebuggerBundle.message("error.executing.finally", errorMessage), + UIUtil.removeMnemonic(ActionsBundle.actionText(DebuggerActions.POP_FRAME)), + Messages.getErrorIcon()); + } + }); + } + }, stackFrame.getSourcePosition()); + } + else { + Messages.showMessageDialog(project, XDebuggerBundle.message("xdebugger.evaluate.stack.frame.has.not.evaluator"), + UIUtil.removeMnemonic(ActionsBundle.actionText(DebuggerActions.POP_FRAME)), + Messages.getErrorIcon()); + } + } + + private static List getFinallyStatements(SourcePosition position) { + List res = new ArrayList(); + PsiElement element = position.getFile().findElementAt(position.getOffset()); + PsiTryStatement tryStatement = PsiTreeUtil.getParentOfType(element, PsiTryStatement.class); + while (tryStatement != null) { + PsiCodeBlock finallyBlock = tryStatement.getFinallyBlock(); + if (finallyBlock != null) { + ContainerUtil.addAll(res, finallyBlock.getStatements()); } + tryStatement = PsiTreeUtil.getParentOfType(tryStatement, PsiTryStatement.class); } + return res; + } + + @Nullable + private static JavaStackFrame getStackFrame(AnActionEvent e) { + //DebuggerTreeNodeImpl selectedNode = getSelectedNode(e.getDataContext()); + //if(selectedNode != null) { + // NodeDescriptorImpl descriptor = selectedNode.getDescriptor(); + // if(descriptor instanceof StackFrameDescriptorImpl) { + // if(selectedNode.getNextSibling() != null) { + // StackFrameDescriptorImpl frameDescriptor = ((StackFrameDescriptorImpl)descriptor); + // return frameDescriptor.getFrameProxy(); + // } + // return null; + // } + // else if(descriptor instanceof ThreadDescriptorImpl || descriptor instanceof ThreadGroupDescriptorImpl) { + // return null; + // } + //} Project project = e.getProject(); if (project != null) { @@ -89,18 +233,19 @@ public class PopFrameAction extends DebuggerAction { XStackFrame frame = session.getCurrentStackFrame(); if (frame instanceof JavaStackFrame) { StackFrameProxyImpl proxy = ((JavaStackFrame)frame).getStackFrameProxy(); - return !proxy.isBottom() ? proxy : null; + return !proxy.isBottom() ? ((JavaStackFrame)frame) : null; } } } - DebuggerContextImpl debuggerContext = DebuggerAction.getDebuggerContext(e.getDataContext()); - StackFrameProxyImpl frameProxy = debuggerContext.getFrameProxy(); - - if(frameProxy == null || frameProxy.isBottom()) { - return null; - } - return frameProxy; + //DebuggerContextImpl debuggerContext = DebuggerAction.getDebuggerContext(e.getDataContext()); + //StackFrameProxyImpl frameProxy = debuggerContext.getFrameProxy(); + // + //if(frameProxy == null || frameProxy.isBottom()) { + // return null; + //} + //return frameProxy; + return null; } private static boolean isAtBreakpoint(AnActionEvent e) { @@ -118,14 +263,14 @@ public class PopFrameAction extends DebuggerAction { public void update(AnActionEvent e) { boolean enable = false; - StackFrameProxyImpl stackFrameProxy = getStackFrameProxy(e); + JavaStackFrame stackFrame = getStackFrame(e); - if(stackFrameProxy != null && isAtBreakpoint(e)) { - VirtualMachineProxyImpl virtualMachineProxy = stackFrameProxy.getVirtualMachine(); + if(stackFrame != null && isAtBreakpoint(e)) { + VirtualMachineProxyImpl virtualMachineProxy = stackFrame.getStackFrameProxy().getVirtualMachine(); enable = virtualMachineProxy.canPopFrames(); } - if(ActionPlaces.MAIN_MENU.equals(e.getPlace()) || ActionPlaces.DEBUGGER_TOOLBAR.equals(e.getPlace())) { + if(ActionPlaces.isMainMenuOrActionSearch(e.getPlace()) || ActionPlaces.DEBUGGER_TOOLBAR.equals(e.getPlace())) { e.getPresentation().setEnabled(enable); } else { diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/ViewAsGroup.java b/java/debugger/impl/src/com/intellij/debugger/actions/ViewAsGroup.java index bd8efab637ac..fd0d8f6785b3 100644 --- a/java/debugger/impl/src/com/intellij/debugger/actions/ViewAsGroup.java +++ b/java/debugger/impl/src/com/intellij/debugger/actions/ViewAsGroup.java @@ -22,13 +22,11 @@ import com.intellij.debugger.impl.DebuggerContextImpl; import com.intellij.debugger.settings.NodeRendererSettings; import com.intellij.debugger.ui.impl.watch.NodeDescriptorImpl; import com.intellij.debugger.ui.impl.watch.ValueDescriptorImpl; -import com.intellij.debugger.ui.tree.render.DescriptorLabelListener; import com.intellij.debugger.ui.tree.render.NodeRenderer; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.DumbAware; import com.intellij.xdebugger.frame.XValue; -import com.intellij.xdebugger.frame.XValuePlace; import com.intellij.xdebugger.impl.ui.tree.actions.XDebuggerTreeActionBase; import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl; import org.jetbrains.annotations.NotNull; @@ -66,7 +64,7 @@ public class ViewAsGroup extends ActionGroup implements DumbAware { } XValue container = node.getValueContainer(); if (container instanceof JavaValue) { - if (((ValueDescriptorImpl)((JavaValue)container).getDescriptor()).getLastRenderer() != myNodeRenderer) { + if (((JavaValue)container).getDescriptor().getLastRenderer() != myNodeRenderer) { return false; } } @@ -83,11 +81,12 @@ public class ViewAsGroup extends ActionGroup implements DumbAware { public void threadAction() { XValue container = node.getValueContainer(); if (container instanceof JavaValue) { - final ValueDescriptorImpl valueDescriptor = (ValueDescriptorImpl)((JavaValue)container).getDescriptor(); + final ValueDescriptorImpl valueDescriptor = ((JavaValue)container).getDescriptor(); if (state) { valueDescriptor.setRenderer(myNodeRenderer); - valueDescriptor.updateRepresentation(debuggerContext.createEvaluationContext(), DescriptorLabelListener.DUMMY_LISTENER); - container.computePresentation(node, XValuePlace.TREE); + DebuggerAction.refreshViews(node); + //valueDescriptor.updateRepresentation(debuggerContext.createEvaluationContext(), DescriptorLabelListener.DUMMY_LISTENER); + //container.computePresentation(node, XValuePlace.TREE); } } } diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessEvents.java b/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessEvents.java index 4714ca49bd06..7640a959597e 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessEvents.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessEvents.java @@ -29,7 +29,6 @@ import com.intellij.debugger.requests.Requestor; import com.intellij.debugger.settings.DebuggerSettings; import com.intellij.debugger.ui.DebuggerPanelsManager; import com.intellij.debugger.ui.breakpoints.Breakpoint; -import com.intellij.debugger.ui.breakpoints.BreakpointManager; import com.intellij.debugger.ui.breakpoints.LineBreakpoint; import com.intellij.execution.configurations.RemoteConnection; import com.intellij.openapi.application.ApplicationManager; @@ -57,15 +56,15 @@ import com.sun.jdi.request.ThreadStartRequest; * @author lex */ public class DebugProcessEvents extends DebugProcessImpl { - private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.engine.DebugProcessEvents"); + private static final Logger LOG = Logger.getInstance(DebugProcessEvents.class); + private DebuggerEventThread myEventThread; - private final BreakpointManager myBreakpointManager; public DebugProcessEvents(Project project) { super(project); - myBreakpointManager = DebuggerManagerEx.getInstanceEx(getProject()).getBreakpointManager(); } + @Override protected void commitVM(final VirtualMachine vm) { super.commitVM(vm); if(vm != null) { @@ -134,18 +133,19 @@ public class DebugProcessEvents extends DebugProcessImpl { return myIsStopped; } + @Override public void run() { try { EventQueue eventQueue = myVmProxy.eventQueue(); while (!isStopped()) { try { final EventSet eventSet = eventQueue.remove(); - + final boolean methodWatcherActive = myReturnValueWatcher != null && myReturnValueWatcher.isEnabled(); int processed = 0; for (EventIterator eventIterator = eventSet.eventIterator(); eventIterator.hasNext();) { final Event event = eventIterator.nextEvent(); - + if (methodWatcherActive) { if (event instanceof MethodExitEvent) { if (myReturnValueWatcher.processMethodExitEvent((MethodExitEvent)event)) { @@ -158,6 +158,7 @@ public class DebugProcessEvents extends DebugProcessImpl { processed++; final ThreadReference thread = ((ThreadStartEvent)event).thread(); getManagerThread().schedule(new DebuggerCommandImpl() { + @Override protected void action() throws Exception { getVirtualMachineProxy().threadStarted(thread); myDebugProcessDispatcher.getMulticaster().threadStarted(DebugProcessEvents.this, thread); @@ -168,6 +169,7 @@ public class DebugProcessEvents extends DebugProcessImpl { processed++; final ThreadReference thread = ((ThreadDeathEvent)event).thread(); getManagerThread().schedule(new DebuggerCommandImpl() { + @Override protected void action() throws Exception { getVirtualMachineProxy().threadStopped(thread); myDebugProcessDispatcher.getMulticaster().threadStopped(DebugProcessEvents.this, thread); @@ -175,15 +177,15 @@ public class DebugProcessEvents extends DebugProcessImpl { }); } } - + if (processed == eventSet.size()) { eventSet.resume(); continue; } getManagerThread().invokeAndWait(new DebuggerCommandImpl() { + @Override protected void action() throws Exception { - if (eventSet.suspendPolicy() == EventRequest.SUSPEND_ALL && !DebuggerSession.enableBreakpointsDuringEvaluation()) { // check if there is already one request with policy SUSPEND_ALL for (SuspendContextImpl context : getSuspendManager().getEventContexts()) { @@ -193,12 +195,10 @@ public class DebugProcessEvents extends DebugProcessImpl { } } } - + final SuspendContextImpl suspendContext = getSuspendManager().pushSuspendContext(eventSet); - for (EventIterator eventIterator = eventSet.eventIterator(); eventIterator.hasNext();) { final Event event = eventIterator.nextEvent(); - //if (LOG.isDebugEnabled()) { // LOG.debug("EVENT : " + event); //} @@ -207,10 +207,7 @@ public class DebugProcessEvents extends DebugProcessImpl { //Sun WTK fails when J2ME when event set is resumed on VMStartEvent processVMStartEvent(suspendContext, (VMStartEvent)event); } - else if (event instanceof VMDeathEvent) { - processVMDeathEvent(suspendContext, event); - } - else if (event instanceof VMDisconnectEvent) { + else if (event instanceof VMDeathEvent || event instanceof VMDisconnectEvent) { processVMDeathEvent(suspendContext, event); } else if (event instanceof ClassPrepareEvent) { @@ -240,7 +237,6 @@ public class DebugProcessEvents extends DebugProcessImpl { } } }); - } catch (InternalException e) { LOG.debug(e); @@ -264,13 +260,15 @@ public class DebugProcessEvents extends DebugProcessImpl { } catch (VMDisconnectedException e) { invokeVMDeathEvent(); - } finally { + } + finally { Thread.interrupted(); // reset interrupted status } } private void invokeVMDeathEvent() { getManagerThread().invokeAndWait(new DebuggerCommandImpl() { + @Override protected void action() throws Exception { SuspendContextImpl suspendContext = getSuspendManager().pushSuspendContext(EventRequest.SUSPEND_NONE, 1); processVMDeathEvent(suspendContext, null); @@ -307,7 +305,7 @@ public class DebugProcessEvents extends DebugProcessImpl { if(myState.compareAndSet(STATE_INITIAL, STATE_ATTACHED)) { final VirtualMachineProxyImpl machineProxy = getVirtualMachineProxy(); final EventRequestManager requestManager = machineProxy.eventRequestManager(); - + if (machineProxy.canGetMethodReturnValues()) { myReturnValueWatcher = new MethodReturnValueWatcher(requestManager); } @@ -378,7 +376,7 @@ public class DebugProcessEvents extends DebugProcessImpl { RequestHint hint = (RequestHint)event.request().getProperty("hint"); deleteStepRequests(event.thread()); - + boolean shouldResume = false; final Project project = getProject(); @@ -426,8 +424,9 @@ public class DebugProcessEvents extends DebugProcessImpl { preprocessEvent(suspendContext, thread); //we use schedule to allow processing other events during processing this one - //this is especially nesessary if a method is breakpoint condition + //this is especially necessary if a method is breakpoint condition getManagerThread().schedule(new SuspendContextCommandImpl(suspendContext) { + @Override public void contextAction() throws Exception { final SuspendManager suspendManager = getSuspendManager(); SuspendContextImpl evaluatingContext = SuspendManagerUtil.getEvaluatingContext(suspendManager, getSuspendContext().getThread()); @@ -441,7 +440,7 @@ public class DebugProcessEvents extends DebugProcessImpl { final LocatableEventRequestor requestor = (LocatableEventRequestor) getRequestsManager().findRequestor(event.request()); boolean resumePreferred = requestor != null && DebuggerSettings.SUSPEND_NONE.equals(requestor.getSuspendPolicy()); - boolean requestHit = false; + boolean requestHit; try { requestHit = (requestor != null) && requestor.processLocatableEvent(this, event); } @@ -451,6 +450,7 @@ public class DebugProcessEvents extends DebugProcessImpl { } final boolean[] considerRequestHit = new boolean[]{true}; DebuggerInvocationUtil.invokeAndWait(getProject(), new Runnable() { + @Override public void run() { DebuggerPanelsManager.getInstance(getProject()).toFront(mySession); final String displayName = requestor instanceof Breakpoint? ((Breakpoint)requestor).getDisplayName() : requestor.getClass().getSimpleName(); diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessImpl.java b/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessImpl.java index 613217f93ac2..4f10e68440b9 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessImpl.java @@ -17,6 +17,7 @@ package com.intellij.debugger.engine; import com.intellij.Patches; import com.intellij.debugger.*; +import com.intellij.debugger.actions.DebuggerAction; import com.intellij.debugger.actions.DebuggerActions; import com.intellij.debugger.apiAdapters.ConnectionServiceWrapper; import com.intellij.debugger.engine.evaluation.*; @@ -43,10 +44,7 @@ import com.intellij.execution.CantRunException; import com.intellij.execution.ExecutionException; import com.intellij.execution.ExecutionResult; import com.intellij.execution.configurations.RemoteConnection; -import com.intellij.execution.process.ProcessAdapter; -import com.intellij.execution.process.ProcessEvent; -import com.intellij.execution.process.ProcessListener; -import com.intellij.execution.process.ProcessOutputTypes; +import com.intellij.execution.process.*; import com.intellij.execution.runners.ExecutionUtil; import com.intellij.idea.ActionsBundle; import com.intellij.openapi.Disposable; @@ -170,10 +168,7 @@ public abstract class DebugProcessImpl extends UserDataHolderBase implements Deb final DebuggerSession session = mySession; if (session != null && session.isAttached()) { session.refresh(true); - XDebugSession xDebugSession = mySession.getXDebugSession(); - if (xDebugSession != null) { - xDebugSession.rebuildViews(); - } + DebuggerAction.refreshViews(mySession.getXDebugSession()); } } }); @@ -354,10 +349,16 @@ public abstract class DebugProcessImpl extends UserDataHolderBase implements Deb return new CompoundPositionManager(new PositionManagerImpl(this)); } + @Override public void printToConsole(final String text) { myExecutionResult.getProcessHandler().notifyTextAvailable(text, ProcessOutputTypes.SYSTEM); } + @Override + public ProcessHandler getProcessHandler() { + return myExecutionResult.getProcessHandler(); + } + /** * * @param suspendContext @@ -653,8 +654,8 @@ public abstract class DebugProcessImpl extends UserDataHolderBase implements Deb public void addProcessListener(ProcessListener processListener) { synchronized(myProcessListeners) { - if(getExecutionResult() != null) { - getExecutionResult().getProcessHandler().addProcessListener(processListener); + if(getProcessHandler() != null) { + getProcessHandler().addProcessListener(processListener); } else { myProcessListeners.add(processListener); @@ -664,8 +665,8 @@ public abstract class DebugProcessImpl extends UserDataHolderBase implements Deb public void removeProcessListener(ProcessListener processListener) { synchronized (myProcessListeners) { - if(getExecutionResult() != null) { - getExecutionResult().getProcessHandler().removeProcessListener(processListener); + if(getProcessHandler() != null) { + getProcessHandler().removeProcessListener(processListener); } else { myProcessListeners.remove(processListener); @@ -917,11 +918,32 @@ public abstract class DebugProcessImpl extends UserDataHolderBase implements Deb ClassNotLoadedException, IncompatibleThreadStateException, InvalidTypeException; - public E start(EvaluationContextImpl evaluationContext, Method method) throws EvaluateException { - return start(evaluationContext, method, false); + + + E start(EvaluationContextImpl evaluationContext, Method method, boolean internalEvaluate) throws EvaluateException { + while (true) { + try { + return startInternal(evaluationContext, method, internalEvaluate); + } + catch (ClassNotLoadedException e) { + ReferenceType loadedClass = null; + try { + if (evaluationContext.isAutoLoadClasses()) { + loadedClass = loadClass(evaluationContext, e.className(), evaluationContext.getClassLoader()); + } + } + catch (Exception ignored) { + loadedClass = null; + } + if (loadedClass == null) { + throw EvaluateExceptionUtil.createEvaluateException(e); + } + } + } } - public E start(EvaluationContextImpl evaluationContext, Method method, boolean internalEvaluate) throws EvaluateException { + E startInternal(EvaluationContextImpl evaluationContext, Method method, boolean internalEvaluate) + throws EvaluateException, ClassNotLoadedException { DebuggerManagerThreadImpl.assertIsManagerThread(); SuspendContextImpl suspendContext = evaluationContext.getSuspendContext(); SuspendManagerUtil.assertSuspendContext(suspendContext); @@ -956,26 +978,7 @@ public abstract class DebugProcessImpl extends UserDataHolderBase implements Deb getVirtualMachineProxy().clearCaches(); - while (true) { - try { - return invokeMethodAndFork(suspendContext); - } - catch (ClassNotLoadedException e) { - ReferenceType loadedClass; - try { - loadedClass = evaluationContext.isAutoLoadClasses() ? loadClass(evaluationContext, e.className(), evaluationContext.getClassLoader()) : null; - } - catch (EvaluateException ignored) { - loadedClass = null; - } - if (loadedClass == null) { - throw EvaluateExceptionUtil.createEvaluateException(e); - } - } - } - } - catch (ClassNotLoadedException e) { - throw EvaluateExceptionUtil.createEvaluateException(e); + return invokeMethodAndFork(suspendContext); } catch (InvocationException e) { throw EvaluateExceptionUtil.createEvaluateException(e); @@ -1115,7 +1118,7 @@ public abstract class DebugProcessImpl extends UserDataHolderBase implements Deb } return objRef.invokeMethod(thread, method, args, invokePolicy | invocationOptions); } - }.start((EvaluationContextImpl)evaluationContext, method); + }.start((EvaluationContextImpl)evaluationContext, method, false); } private static ThreadReference getEvaluationThread(final EvaluationContext evaluationContext) throws EvaluateException { @@ -1182,7 +1185,7 @@ public abstract class DebugProcessImpl extends UserDataHolderBase implements Deb return classType.newInstance(thread, method, args, invokePolicy); } }; - return invokeCommand.start((EvaluationContextImpl)evaluationContext, method); + return invokeCommand.start((EvaluationContextImpl)evaluationContext, method, false); } public void clearCashes(int suspendPolicy) { @@ -1711,15 +1714,17 @@ public abstract class DebugProcessImpl extends UserDataHolderBase implements Deb createVirtualMachine(environment.getSessionName(), environment.isPollConnection()); + ExecutionResult executionResult; try { synchronized (myProcessListeners) { - myExecutionResult = environment.createExecutionResult(); - if (myExecutionResult == null) { + executionResult = environment.createExecutionResult(); + myExecutionResult = executionResult; + if (executionResult == null) { fail(); return null; } for (ProcessListener processListener : myProcessListeners) { - myExecutionResult.getProcessHandler().addProcessListener(processListener); + executionResult.getProcessHandler().addProcessListener(processListener); } myProcessListeners.clear(); } @@ -1732,7 +1737,7 @@ public abstract class DebugProcessImpl extends UserDataHolderBase implements Deb // writing to volatile field ensures the other threads will see the right values in non-volatile fields if (ApplicationManager.getApplication().isUnitTestMode()) { - return myExecutionResult; + return executionResult; } /* @@ -1760,7 +1765,7 @@ public abstract class DebugProcessImpl extends UserDataHolderBase implements Deb }); */ - return myExecutionResult; + return executionResult; } private void fail() { @@ -1885,8 +1890,8 @@ public abstract class DebugProcessImpl extends UserDataHolderBase implements Deb } MyProcessAdapter processListener = new MyProcessAdapter(); addProcessListener(processListener); - if(myExecutionResult != null) { - if(myExecutionResult.getProcessHandler().isStartNotified()) { + if (myExecutionResult != null) { + if (myExecutionResult.getProcessHandler().isStartNotified()) { processListener.run(); } } diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/JVMNameUtil.java b/java/debugger/impl/src/com/intellij/debugger/engine/JVMNameUtil.java index 13e8f155dff4..eeaa1c21015e 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/JVMNameUtil.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/JVMNameUtil.java @@ -285,13 +285,20 @@ public class JVMNameUtil { return DebuggerManager.getInstance(aClass.getProject()).getVMClassQualifiedName(aClass); } + public static JVMName getJVMConstructorSignature(@Nullable PsiMethod method, @Nullable PsiClass declaringClass) { + return getJVMSignature(method, true, declaringClass); + } + + public static JVMName getJVMSignature(@NotNull PsiMethod method) { + return getJVMSignature(method, method.isConstructor(), method.getContainingClass()); + } + @SuppressWarnings({"HardCodedStringLiteral"}) - public static JVMName getJVMSignature(PsiMethod method) { + private static JVMName getJVMSignature(@Nullable PsiMethod method, boolean constructor, @Nullable PsiClass declaringClass) { JVMNameBuffer signature = new JVMNameBuffer(); signature.append("("); - if (method.isConstructor()) { - final PsiClass declaringClass = method.getContainingClass(); + if (constructor) { if (declaringClass != null) { final PsiClass outerClass = declaringClass.getContainingClass(); if (outerClass != null) { @@ -302,11 +309,13 @@ public class JVMNameUtil { } } } - for (PsiParameter psiParameter : method.getParameterList().getParameters()) { - appendJVMSignature(signature, psiParameter.getType()); + if (method != null) { + for (PsiParameter psiParameter : method.getParameterList().getParameters()) { + appendJVMSignature(signature, psiParameter.getType()); + } } signature.append(")"); - if (!method.isConstructor()) { + if (!constructor && method != null) { appendJVMSignature(signature, method.getReturnType()); } else { diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/JavaDebugProcess.java b/java/debugger/impl/src/com/intellij/debugger/engine/JavaDebugProcess.java index 37ee00b94eaf..af55182e3607 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/JavaDebugProcess.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/JavaDebugProcess.java @@ -18,20 +18,18 @@ package com.intellij.debugger.engine; import com.intellij.debugger.DebuggerBundle; import com.intellij.debugger.actions.DebuggerActions; import com.intellij.debugger.engine.evaluation.EvaluationContext; -import com.intellij.debugger.engine.evaluation.EvaluationContextImpl; import com.intellij.debugger.engine.events.DebuggerCommandImpl; import com.intellij.debugger.engine.events.DebuggerContextCommandImpl; import com.intellij.debugger.impl.*; +import com.intellij.debugger.jdi.StackFrameProxyImpl; import com.intellij.debugger.settings.DebuggerSettings; import com.intellij.debugger.ui.DebuggerContentInfo; import com.intellij.debugger.ui.breakpoints.Breakpoint; import com.intellij.debugger.ui.impl.ThreadsPanel; import com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeImpl; import com.intellij.debugger.ui.impl.watch.MessageDescriptor; -import com.intellij.debugger.ui.impl.watch.NodeDescriptorImpl; import com.intellij.debugger.ui.impl.watch.NodeManagerImpl; import com.intellij.debugger.ui.tree.NodeDescriptor; -import com.intellij.debugger.ui.tree.render.DescriptorLabelListener; import com.intellij.execution.process.ProcessHandler; import com.intellij.execution.ui.ExecutionConsole; import com.intellij.execution.ui.ExecutionConsoleEx; @@ -150,29 +148,40 @@ public class JavaDebugProcess extends XDebugProcess { }; session.addSessionListener(new XDebugSessionAdapter() { @Override - public void beforeSessionResume() { - myJavaSession.getProcess().getManagerThread().schedule(new DebuggerCommandImpl() { - @Override - protected void action() throws Exception { - myNodeManager.setHistoryByContext(getDebuggerStateManager().getContext()); - } - @Override - public Priority getPriority() { - return Priority.NORMAL; - } - }); + public void sessionPaused() { + saveNodeHistory(); } @Override public void stackFrameChanged() { XStackFrame frame = session.getCurrentStackFrame(); if (frame instanceof JavaStackFrame) { - DebuggerContextUtil.setStackFrame(javaSession.getContextManager(), ((JavaStackFrame)frame).getStackFrameProxy()); + StackFrameProxyImpl frameProxy = ((JavaStackFrame)frame).getStackFrameProxy(); + DebuggerContextUtil.setStackFrame(javaSession.getContextManager(), frameProxy); + saveNodeHistory(frameProxy); } } }); } + public void saveNodeHistory() { + saveNodeHistory(getDebuggerStateManager().getContext().getFrameProxy()); + } + + private void saveNodeHistory(final StackFrameProxyImpl frameProxy) { + myJavaSession.getProcess().getManagerThread().invoke(new DebuggerCommandImpl() { + @Override + protected void action() throws Exception { + myNodeManager.setHistoryByContext(frameProxy); + } + + @Override + public Priority getPriority() { + return Priority.NORMAL; + } + }); + } + private DebuggerStateManager getDebuggerStateManager() { return myJavaSession.getContextManager(); } @@ -243,7 +252,7 @@ public class JavaDebugProcess extends XDebugProcess { @Nullable @Override protected ProcessHandler doGetProcessHandler() { - return myJavaSession.getProcess().getExecutionResult().getProcessHandler(); + return myJavaSession.getProcess().getProcessHandler(); } @NotNull @@ -302,23 +311,16 @@ public class JavaDebugProcess extends XDebugProcess { } @Override - public void registerAdditionalActions(@NotNull DefaultActionGroup leftToolbar, @NotNull DefaultActionGroup topToolbar) { + public void registerAdditionalActions(@NotNull DefaultActionGroup leftToolbar, @NotNull DefaultActionGroup topToolbar, @NotNull DefaultActionGroup settings) { Constraints beforeRunner = new Constraints(Anchor.BEFORE, "Runner.Layout"); leftToolbar.add(Separator.getInstance(), beforeRunner); leftToolbar.add(ActionManager.getInstance().getAction(DebuggerActions.EXPORT_THREADS), beforeRunner); leftToolbar.add(ActionManager.getInstance().getAction(DebuggerActions.DUMP_THREADS), beforeRunner); leftToolbar.add(Separator.getInstance(), beforeRunner); - for (AnAction action : leftToolbar.getChildren(null)) { - //TODO: maybe introduce API for extra settings? - if (action instanceof DefaultActionGroup && "DebuggerSettings".equals(action.getTemplatePresentation().getText())) { - DefaultActionGroup settings = (DefaultActionGroup)action; - addActionToGroup(settings, XDebuggerActions.AUTO_TOOLTIP); - settings.addAction(new AutoVarsSwitchAction(), Constraints.FIRST); - settings.addAction(new WatchLastMethodReturnValueAction(), Constraints.FIRST); - break; - } - } + addActionToGroup(settings, XDebuggerActions.AUTO_TOOLTIP); + settings.addAction(new AutoVarsSwitchAction(), Constraints.FIRST); + settings.addAction(new WatchLastMethodReturnValueAction(), Constraints.FIRST); } private static class AutoVarsSwitchAction extends ToggleAction { diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/JavaDebuggerEvaluator.java b/java/debugger/impl/src/com/intellij/debugger/engine/JavaDebuggerEvaluator.java index f2178c1d4fb5..2eab90666967 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/JavaDebuggerEvaluator.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/JavaDebuggerEvaluator.java @@ -82,9 +82,11 @@ public class JavaDebuggerEvaluator extends XDebuggerEvaluator { return; } JavaDebugProcess process = myDebugProcess.getXdebugProcess(); - if (process != null) { - callback.evaluated(JavaValue.create(null, descriptor, evalContext, process.getNodeManager(), true)); + if (process == null) { + callback.errorOccurred("No debug process"); + return; } + callback.evaluated(JavaValue.create(null, descriptor, evalContext, process.getNodeManager(), true)); } }); } diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/JavaStaticGroup.java b/java/debugger/impl/src/com/intellij/debugger/engine/JavaStaticGroup.java index 1293a0fdd0c0..60d0b708befb 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/JavaStaticGroup.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/JavaStaticGroup.java @@ -17,7 +17,9 @@ package com.intellij.debugger.engine; import com.intellij.debugger.engine.evaluation.EvaluationContextImpl; import com.intellij.debugger.engine.events.SuspendContextCommandImpl; +import com.intellij.debugger.settings.NodeRendererSettings; import com.intellij.debugger.ui.impl.watch.*; +import com.intellij.debugger.ui.tree.render.ClassRenderer; import com.intellij.xdebugger.frame.*; import com.sun.jdi.Field; import com.sun.jdi.ReferenceType; @@ -57,8 +59,14 @@ public class JavaStaticGroup extends XValueGroup implements NodeDescriptorProvid final ReferenceType refType = myStaticDescriptor.getType(); List fields = refType.allFields(); + + final ClassRenderer classRenderer = NodeRendererSettings.getInstance().getClassRenderer(); for (Field field : fields) { if (field.isStatic()) { + boolean isSynthetic = DebuggerUtils.isSynthetic(field); + if (!classRenderer.SHOW_SYNTHETICS && isSynthetic) { + continue; + } final FieldDescriptorImpl fieldDescriptor = myNodeManager.getFieldDescriptor(myStaticDescriptor, null, field); children.add(JavaValue.create(fieldDescriptor, myEvaluationContext, myNodeManager)); //final DebuggerTreeNodeImpl node = myNodeManager.createNode(fieldDescriptor, myEvaluationContext); diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/JavaValue.java b/java/debugger/impl/src/com/intellij/debugger/engine/JavaValue.java index 7b0ffffe98d5..b678cf30d661 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/JavaValue.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/JavaValue.java @@ -15,6 +15,7 @@ */ package com.intellij.debugger.engine; +import com.intellij.debugger.DebuggerBundle; import com.intellij.debugger.DebuggerInvocationUtil; import com.intellij.debugger.SourcePosition; import com.intellij.debugger.actions.JavaReferringObjectsValue; @@ -102,6 +103,7 @@ public class JavaValue extends XNamedValue implements NodeDescriptorProvider, XV } @Override + @NotNull public ValueDescriptorImpl getDescriptor() { return myValueDescriptor; } @@ -273,7 +275,7 @@ public class JavaValue extends XNamedValue implements NodeDescriptorProvider, XV @Override public void computeChildren(@NotNull final XCompositeNode node) { - if (myEvaluationContext.getSuspendContext().isResumed()) return; + if (checkContextNotResumed(node)) return; myEvaluationContext.getDebugProcess().getManagerThread().schedule(new SuspendContextCommandImpl(myEvaluationContext.getSuspendContext()) { @Override public Priority getPriority() { @@ -335,6 +337,14 @@ public class JavaValue extends XNamedValue implements NodeDescriptorProvider, XV }); } + protected boolean checkContextNotResumed(XCompositeNode node) { + if (myEvaluationContext.getSuspendContext().isResumed()) { + node.setErrorMessage(DebuggerBundle.message("error.context.has.changed")); + return true; + } + return false; + } + @Override public void computeSourcePosition(@NotNull final XNavigatable navigatable) { if (myEvaluationContext.getSuspendContext().isResumed()) return; @@ -419,6 +429,7 @@ public class JavaValue extends XNamedValue implements NodeDescriptorProvider, XV public String getEvaluationExpression() { if (evaluationExpression == null) { // TODO: change API to allow to calculate it asynchronously + if (myEvaluationContext.getSuspendContext().isResumed()) return null; DebugProcessImpl debugProcess = myEvaluationContext.getDebugProcess(); debugProcess.getManagerThread().invokeAndWait(new DebuggerCommandImpl() { @Override diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/JavaValueModifier.java b/java/debugger/impl/src/com/intellij/debugger/engine/JavaValueModifier.java index c7e59106beb8..5aa037191c17 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/JavaValueModifier.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/JavaValueModifier.java @@ -34,6 +34,7 @@ import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.progress.util.ProgressIndicatorListenerAdapter; import com.intellij.openapi.progress.util.ProgressWindowWithNotification; import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiFile; import com.intellij.ui.SimpleColoredComponent; @@ -62,9 +63,12 @@ public class JavaValueModifier extends XValueModifier { @Override public String getInitialValueEditorText() { Value value = myJavaValue.getDescriptor().getValue(); - if (value instanceof PrimitiveValue || value instanceof StringReference) { + if (value instanceof PrimitiveValue) { return myJavaValue.getValueString(); } + else if (value instanceof StringReference) { + return StringUtil.wrapWithDoubleQuote(DebuggerUtils.translateStringValue(myJavaValue.getValueString())); + } return null; } @@ -96,10 +100,12 @@ public class JavaValueModifier extends XValueModifier { @Override public void setValue(@NotNull String expression, @NotNull XModificationCallback callback) { final NodeDescriptorImpl descriptor = myJavaValue.getDescriptor(); - if (!(descriptor instanceof ValueDescriptorImpl)) { + if(!((ValueDescriptorImpl)descriptor).canSetValue()) { return; } - if(!((ValueDescriptorImpl)descriptor).canSetValue()) { + + if (myJavaValue.getEvaluationContext().getSuspendContext().isResumed()) { + callback.errorOccurred(DebuggerBundle.message("error.context.has.changed")); return; } diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/EvaluationContextImpl.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/EvaluationContextImpl.java index b6ca9eafe1f1..759d92fd57a8 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/EvaluationContextImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/EvaluationContextImpl.java @@ -30,6 +30,7 @@ public final class EvaluationContextImpl implements EvaluationContext{ private final SuspendContextImpl mySuspendContext; private final StackFrameProxyImpl myFrameProxy; private boolean myAutoLoadClasses = true; + private ClassLoaderReference myClassLoader; public EvaluationContextImpl(@NotNull SuspendContextImpl suspendContext, StackFrameProxyImpl frameProxy, @Nullable Value thisObject) { myThisObject = thisObject; @@ -77,9 +78,16 @@ public final class EvaluationContextImpl implements EvaluationContext{ @Override public ClassLoaderReference getClassLoader() throws EvaluateException { DebuggerManagerThreadImpl.assertIsManagerThread(); + if (myClassLoader != null) { + return myClassLoader; + } return myFrameProxy != null ? myFrameProxy.getClassLoader() : null; } + public void setClassLoader(ClassLoaderReference classLoader) { + myClassLoader = classLoader; + } + public boolean isAutoLoadClasses() { return myAutoLoadClasses; } diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/EvaluatorBuilderImpl.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/EvaluatorBuilderImpl.java index 83ad91b4e27d..7d0a4aeb0989 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/EvaluatorBuilderImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/EvaluatorBuilderImpl.java @@ -31,7 +31,7 @@ import com.intellij.debugger.engine.DebuggerUtils; import com.intellij.debugger.engine.JVMName; import com.intellij.debugger.engine.JVMNameUtil; import com.intellij.debugger.engine.evaluation.*; -import com.intellij.debugger.ui.DebuggerEditorImpl; +import com.intellij.debugger.impl.DebuggerUtilsEx; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.psi.*; @@ -66,8 +66,8 @@ public class EvaluatorBuilderImpl implements EvaluatorBuilder { final Project project = contextElement.getProject(); - CodeFragmentFactory factory = DebuggerEditorImpl.findAppropriateFactory(text, contextElement); - PsiCodeFragment codeFragment = new CodeFragmentFactoryContextWrapper(factory).createCodeFragment(text, contextElement, project); + CodeFragmentFactory factory = DebuggerUtilsEx.findAppropriateCodeFragmentFactory(text, contextElement); + PsiCodeFragment codeFragment = factory.createCodeFragment(text, contextElement, project); if (codeFragment == null) { throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", text.getText())); } @@ -184,6 +184,11 @@ public class EvaluatorBuilderImpl implements EvaluatorBuilder { return rEvaluator; } + @Override + public void visitTryStatement(PsiTryStatement statement) { + throw new EvaluateRuntimeException(new UnsupportedExpressionException(statement.getText())); + } + @Override public void visitStatement(PsiStatement statement) { throwEvaluateException(DebuggerBundle.message("evaluation.error.statement.not.supported", statement.getText())); @@ -741,24 +746,28 @@ public class EvaluatorBuilderImpl implements EvaluatorBuilder { } private int calcIterationCount(final PsiJavaCodeReferenceElement qualifier) { - int iterationCount = 0; if (qualifier != null) { - PsiElement targetClass = qualifier.resolve(); - if (targetClass == null || getContextPsiClass() == null) { - throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", qualifier.getText())); - } - try { - PsiClass aClass = getContextPsiClass(); - while (aClass != null && !aClass.equals(targetClass)) { - iterationCount++; - aClass = getOuterClass(aClass); - } - } - catch (Exception e) { - //noinspection ThrowableResultOfMethodCallIgnored - throw new EvaluateRuntimeException(EvaluateExceptionUtil.createEvaluateException(e)); + return calcIterationCount(qualifier.resolve(), qualifier.getText()); + } + return 0; + } + + private int calcIterationCount(PsiElement targetClass, String name) { + int iterationCount = 0; + if (targetClass == null || getContextPsiClass() == null) { + throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", name)); + } + try { + PsiClass aClass = getContextPsiClass(); + while (aClass != null && !aClass.equals(targetClass)) { + iterationCount++; + aClass = getOuterClass(aClass); } } + catch (Exception e) { + //noinspection ThrowableResultOfMethodCallIgnored + throw new EvaluateRuntimeException(EvaluateExceptionUtil.createEvaluateException(e)); + } return iterationCount; } @@ -962,12 +971,16 @@ public class EvaluatorBuilderImpl implements EvaluatorBuilder { } } + boolean defaultInterfaceMethod = false; + if (psiMethod != null) { processBoxingConversions(psiMethod.getParameterList().getParameters(), argExpressions, resolveResult.getSubstitutor(), argumentEvaluators); argumentEvaluators = wrapVarargs(psiMethod.getParameterList().getParameters(), argExpressions, resolveResult.getSubstitutor(), argumentEvaluators); + defaultInterfaceMethod = psiMethod.hasModifierProperty(PsiModifier.DEFAULT); } - myResult = new MethodEvaluator(objectEvaluator, contextClass, methodExpr.getReferenceName(), psiMethod != null ? JVMNameUtil.getJVMSignature(psiMethod) : null, argumentEvaluators); + myResult = new MethodEvaluator(objectEvaluator, contextClass, methodExpr.getReferenceName(), + psiMethod != null ? JVMNameUtil.getJVMSignature(psiMethod) : null, argumentEvaluators, defaultInterfaceMethod); } @Override @@ -1081,6 +1094,16 @@ public class EvaluatorBuilderImpl implements EvaluatorBuilder { } } + @Override + public void visitLambdaExpression(PsiLambdaExpression expression) { + throw new EvaluateRuntimeException(new UnsupportedExpressionException(DebuggerBundle.message("evaluation.error.lambda.evaluation.not.supported"))); + } + + @Override + public void visitMethodReferenceExpression(PsiMethodReferenceExpression expression) { + throw new EvaluateRuntimeException(new UnsupportedExpressionException(DebuggerBundle.message("evaluation.error.method.reference.evaluation.not.supported"))); + } + @Override public void visitNewExpression(final PsiNewExpression expression) { PsiType expressionPsiType = expression.getType(); @@ -1142,7 +1165,7 @@ public class EvaluatorBuilderImpl implements EvaluatorBuilder { else if (expressionPsiType instanceof PsiClassType){ // must be a class ref PsiClass aClass = ((PsiClassType)expressionPsiType).resolve(); if(aClass instanceof PsiAnonymousClass) { - throwEvaluateException(DebuggerBundle.message("evaluation.error.anonymous.class.evaluation.not.supported")); + throw new EvaluateRuntimeException(new UnsupportedExpressionException(DebuggerBundle.message("evaluation.error.anonymous.class.evaluation.not.supported"))); } PsiExpressionList argumentList = expression.getArgumentList(); if (argumentList == null) { @@ -1173,8 +1196,11 @@ public class EvaluatorBuilderImpl implements EvaluatorBuilder { argumentEvaluators = wrapVarargs(constructor.getParameterList().getParameters(), argExpressions, constructorResolveResult.getSubstitutor(), argumentEvaluators); } - //noinspection HardCodedStringLiteral - JVMName signature = constructor != null ? JVMNameUtil.getJVMSignature(constructor) : JVMNameUtil.getJVMRawText("()V"); + if (aClass != null && aClass.getContainingClass() != null && !aClass.hasModifierProperty(PsiModifier.STATIC)) { + argumentEvaluators = addThisEvaluator(argumentEvaluators, aClass.getContainingClass()); + } + + JVMName signature = JVMNameUtil.getJVMConstructorSignature(constructor, aClass); myResult = new NewClassInstanceEvaluator( new TypeEvaluator(JVMNameUtil.getJVMQualifiedName(expressionPsiType)), signature, @@ -1191,6 +1217,14 @@ public class EvaluatorBuilderImpl implements EvaluatorBuilder { } } + private Evaluator[] addThisEvaluator(Evaluator[] argumentEvaluators, PsiClass cls) { + Evaluator[] res = new Evaluator[argumentEvaluators.length+1]; + int depth = calcIterationCount(cls, "this"); + res[0] = new ThisEvaluator(depth); + System.arraycopy(argumentEvaluators, 0, res, 1, argumentEvaluators.length); + return res; + } + @Override public void visitArrayInitializerExpression(PsiArrayInitializerExpression expression) { PsiExpression[] initializers = expression.getInitializers(); diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/MethodEvaluator.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/MethodEvaluator.java index 6ba88653749b..80e42e3b61e2 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/MethodEvaluator.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/MethodEvaluator.java @@ -43,13 +43,19 @@ public class MethodEvaluator implements Evaluator { private final String myMethodName; private final Evaluator[] myArgumentEvaluators; private final Evaluator myObjectEvaluator; + private final boolean myCheckDefaultInterfaceMethod; public MethodEvaluator(Evaluator objectEvaluator, JVMName className, String methodName, JVMName signature, Evaluator[] argumentEvaluators) { + this(objectEvaluator, className, methodName, signature, argumentEvaluators, false); + } + + public MethodEvaluator(Evaluator objectEvaluator, JVMName className, String methodName, JVMName signature, Evaluator[] argumentEvaluators, boolean checkDefaultInterfaceMethod) { myObjectEvaluator = new DisableGC(objectEvaluator); myClassName = className; myMethodName = methodName; myMethodSignature = signature; myArgumentEvaluators = argumentEvaluators; + myCheckDefaultInterfaceMethod = checkDefaultInterfaceMethod; } @Override @@ -148,7 +154,7 @@ public class MethodEvaluator implements Evaluator { return debugProcess.invokeInstanceMethod(context, objRef, jdiMethod, args, ObjectReference.INVOKE_NONVIRTUAL); } // fix for default methods in interfaces, see IDEA-124066 - if (Boolean.valueOf(System.getProperty("debugger.invoke.default")) && jdiMethod.declaringType() instanceof InterfaceType) { + if (myCheckDefaultInterfaceMethod && jdiMethod.declaringType() instanceof InterfaceType) { return invokeDefaultMethod(debugProcess, context, objRef, myMethodName); } else { diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/NewClassInstanceEvaluator.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/NewClassInstanceEvaluator.java index c3e7f8173322..3bbb0c633cd6 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/NewClassInstanceEvaluator.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/NewClassInstanceEvaluator.java @@ -21,13 +21,13 @@ */ package com.intellij.debugger.engine.evaluation.expression; +import com.intellij.debugger.DebuggerBundle; import com.intellij.debugger.engine.DebugProcessImpl; +import com.intellij.debugger.engine.DebuggerUtils; import com.intellij.debugger.engine.JVMName; import com.intellij.debugger.engine.evaluation.EvaluateException; import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil; import com.intellij.debugger.engine.evaluation.EvaluationContextImpl; -import com.intellij.debugger.impl.DebuggerUtilsEx; -import com.intellij.debugger.DebuggerBundle; import com.sun.jdi.ClassType; import com.sun.jdi.Method; import com.sun.jdi.ObjectReference; @@ -55,7 +55,7 @@ class NewClassInstanceEvaluator implements Evaluator { } ClassType classType = (ClassType)obj; // find constructor - Method method = DebuggerUtilsEx.findMethod(classType, "", myConstructorSignature.getName(debugProcess)); + Method method = DebuggerUtils.findMethod(classType, "", myConstructorSignature.getName(debugProcess)); if (method == null) { throw EvaluateExceptionUtil.createEvaluateException( DebuggerBundle.message("evaluation.error.cannot.resolve.constructor", myConstructorSignature.getDisplayName(debugProcess))); diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/UnsupportedExpressionException.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/UnsupportedExpressionException.java new file mode 100644 index 000000000000..f4c513d4036c --- /dev/null +++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/expression/UnsupportedExpressionException.java @@ -0,0 +1,28 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.debugger.engine.evaluation.expression; + +import com.intellij.debugger.DebuggerBundle; +import com.intellij.debugger.engine.evaluation.EvaluateException; + +/** + * @author egor + */ +public class UnsupportedExpressionException extends EvaluateException { + public UnsupportedExpressionException(String message) { + super(message); + } +} diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerContextUtil.java b/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerContextUtil.java index 8408364f7e73..f36c9f14cee6 100644 --- a/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerContextUtil.java +++ b/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerContextUtil.java @@ -23,11 +23,12 @@ import com.intellij.debugger.jdi.StackFrameProxyImpl; import com.intellij.debugger.ui.impl.watch.ThreadDescriptorImpl; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.fileEditor.FileEditorManager; +import com.intellij.openapi.fileEditor.impl.FileEditorManagerImpl; import com.intellij.openapi.util.Couple; import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; -import com.intellij.psi.util.PsiUtilBase; import com.intellij.xdebugger.XDebugSession; import com.intellij.xdebugger.XSourcePosition; import org.jetbrains.annotations.NotNull; @@ -75,7 +76,9 @@ public class DebuggerContextUtil { final XDebugSession debugSession = session.getXDebugSession(); if (debugSession != null) { final XSourcePosition position = debugSession.getCurrentPosition(); - final Editor editor = PsiUtilBase.findEditor(psi); + Editor editor = ((FileEditorManagerImpl)FileEditorManager.getInstance(file.getProject())).getSelectedTextEditor(true); + + //final Editor editor = fileEditor instanceof TextEditorImpl ? ((TextEditorImpl)fileEditor).getEditor() : null; if (editor != null && position != null && file.getVirtualFile().equals(position.getFile())) { final Couple> usages = IdentifierHighlighterPass.getHighlightUsages(psi, file); final List ranges = new ArrayList(); diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerManagerImpl.java b/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerManagerImpl.java index 60c0d2dded3d..392a14b78bbf 100644 --- a/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerManagerImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerManagerImpl.java @@ -607,8 +607,7 @@ public class DebuggerManagerImpl extends DebuggerManagerEx implements Persistent } private void dispose(DebuggerSession session) { - ProcessHandler processHandler = session.getProcess().getExecutionResult().getProcessHandler(); - + ProcessHandler processHandler = session.getProcess().getProcessHandler(); synchronized (mySessions) { DebuggerSession removed = mySessions.remove(processHandler); LOG.assertTrue(removed != null); diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerSession.java b/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerSession.java index 0a5210f16c36..dbcf43fd6168 100644 --- a/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerSession.java +++ b/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerSession.java @@ -15,7 +15,10 @@ */ package com.intellij.debugger.impl; -import com.intellij.debugger.*; +import com.intellij.debugger.DebugEnvironment; +import com.intellij.debugger.DebuggerBundle; +import com.intellij.debugger.DebuggerInvocationUtil; +import com.intellij.debugger.SourcePosition; import com.intellij.debugger.engine.*; import com.intellij.debugger.engine.evaluation.EvaluateException; import com.intellij.debugger.engine.evaluation.EvaluationListener; @@ -29,14 +32,11 @@ import com.intellij.debugger.ui.breakpoints.BreakpointWithHighlighter; import com.intellij.debugger.ui.breakpoints.LineBreakpoint; import com.intellij.execution.ExecutionException; import com.intellij.execution.ExecutionResult; -import com.intellij.execution.Executor; -import com.intellij.execution.configurations.ModuleRunProfile; import com.intellij.execution.configurations.RemoteConnection; import com.intellij.execution.configurations.RemoteState; import com.intellij.execution.configurations.RunProfileState; +import com.intellij.execution.process.ProcessHandler; import com.intellij.execution.process.ProcessOutputTypes; -import com.intellij.execution.runners.ExecutionEnvironmentBuilder; -import com.intellij.execution.runners.ProgramRunner; import com.intellij.idea.ActionsBundle; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ModalityState; @@ -199,6 +199,7 @@ public class DebuggerSession implements AbstractDebuggerSession { ValueLookupManager.getInstance(getProject()).startListening(); } + @NotNull public DebuggerStateManager getContextManager() { return myContextManager; } @@ -251,7 +252,7 @@ public class DebuggerSession implements AbstractDebuggerSession { case STATE_DISPOSED: return DebuggerBundle.message("status.debug.stopped"); } - return myState.myDescription; + return null; } /* Stepping */ @@ -371,7 +372,7 @@ public class DebuggerSession implements AbstractDebuggerSession { } public boolean isRunning() { - return getState() == STATE_RUNNING && !getProcess().getExecutionResult().getProcessHandler().isProcessTerminated(); + return getState() == STATE_RUNNING && !getProcess().getProcessHandler().isProcessTerminated(); } private SuspendContextImpl getSuspendContext() { @@ -379,19 +380,6 @@ public class DebuggerSession implements AbstractDebuggerSession { return getContextManager().getContext().getSuspendContext(); } - @Nullable - protected ExecutionResult attach(@NotNull Executor executor, - @NotNull ProgramRunner runner, - @NotNull ModuleRunProfile profile, - @NotNull RunProfileState state, - RemoteConnection remoteConnection, - boolean pollConnection) throws ExecutionException { - return attach(new DefaultDebugEnvironment(new ExecutionEnvironmentBuilder(myDebugProcess.getProject(), executor).runProfile(profile).runner(runner).build(), - state, - remoteConnection, - pollConnection)); - } - @Nullable protected ExecutionResult attach(DebugEnvironment environment) throws ExecutionException { RemoteConnection remoteConnection = environment.getRemoteConnection(); @@ -603,7 +591,7 @@ public class DebuggerSession implements AbstractDebuggerSession { final String transportName = DebuggerBundle.getTransportName(connection); final String message = DebuggerBundle.message("status.connected", addressDisplayName, transportName); - process.getExecutionResult().getProcessHandler().notifyTextAvailable(message + "\n", ProcessOutputTypes.SYSTEM); + process.printToConsole(message + "\n"); DebuggerInvocationUtil.invokeLater(getProject(), new Runnable() { @Override public void run() { @@ -630,12 +618,12 @@ public class DebuggerSession implements AbstractDebuggerSession { @Override public void processDetached(final DebugProcessImpl debugProcess, boolean closedByUser) { if (!closedByUser) { - ExecutionResult executionResult = debugProcess.getExecutionResult(); - if(executionResult != null) { + ProcessHandler processHandler = debugProcess.getProcessHandler(); + if(processHandler != null) { final RemoteConnection connection = getProcess().getConnection(); final String addressDisplayName = DebuggerBundle.getAddressDisplayName(connection); final String transportName = DebuggerBundle.getTransportName(connection); - executionResult.getProcessHandler().notifyTextAvailable(DebuggerBundle.message("status.disconnected", addressDisplayName, transportName) + "\n", ProcessOutputTypes.SYSTEM); + processHandler.notifyTextAvailable(DebuggerBundle.message("status.disconnected", addressDisplayName, transportName) + "\n", ProcessOutputTypes.SYSTEM); } } DebuggerInvocationUtil.invokeLater(getProject(), new Runnable() { @@ -659,7 +647,7 @@ public class DebuggerSession implements AbstractDebuggerSession { public void threadStopped(DebugProcess proc, ThreadReference thread) { notifyThreadsRefresh(); } - + private void notifyThreadsRefresh() { if (!myUpdateAlarm.isDisposed()) { myUpdateAlarm.cancelAllRequests(); diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerUtilsEx.java b/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerUtilsEx.java index 026b7111d18f..ae6bb5dd7b27 100644 --- a/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerUtilsEx.java +++ b/java/debugger/impl/src/com/intellij/debugger/impl/DebuggerUtilsEx.java @@ -37,6 +37,7 @@ import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.fileTypes.StdFileTypes; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.*; @@ -403,17 +404,25 @@ public abstract class DebuggerUtilsEx extends DebuggerUtils { public abstract CompletionEditor createEditor(Project project, PsiElement context, @NonNls String recentsId); - @Nullable - public static CodeFragmentFactory getEffectiveCodeFragmentFactory(final PsiElement psiContext) { - final CodeFragmentFactory factory = ApplicationManager.getApplication().runReadAction(new Computable() { + @NotNull + public static CodeFragmentFactory findAppropriateCodeFragmentFactory(final TextWithImports text, final PsiElement context) { + CodeFragmentFactory factory = ApplicationManager.getApplication().runReadAction(new Computable() { @Override public CodeFragmentFactory compute() { - final List codeFragmentFactories = getCodeFragmentFactories(psiContext); - // the list always contains at least DefaultCodeFragmentFactory - return codeFragmentFactories.get(0); + final FileType fileType = text.getFileType(); + final List factories = getCodeFragmentFactories(context); + if (fileType == null) { + return factories.get(0); + } + for (CodeFragmentFactory factory : factories) { + if (factory.getFileType().equals(fileType)) { + return factory; + } + } + return DefaultCodeFragmentFactory.getInstance(); } }); - return factory != null? new CodeFragmentFactoryContextWrapper(factory) : null; + return new CodeFragmentFactoryContextWrapper(factory); } private static class SigReader { diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/JavaEditorTextProviderImpl.java b/java/debugger/impl/src/com/intellij/debugger/impl/JavaEditorTextProviderImpl.java index 02f4ee44fd53..4c8fb08ef09d 100644 --- a/java/debugger/impl/src/com/intellij/debugger/impl/JavaEditorTextProviderImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/impl/JavaEditorTextProviderImpl.java @@ -86,6 +86,10 @@ public class JavaEditorTextProviderImpl implements EditorTextProvider { public Pair findExpression(PsiElement element, boolean allowMethodCalls) { PsiElement expression = null; PsiElement parent = element.getParent(); + if (parent instanceof PsiLiteralExpression) { + element = parent; + parent = parent.getParent(); + } if (parent instanceof PsiVariable) { expression = element; } @@ -107,12 +111,7 @@ public class JavaEditorTextProviderImpl implements EditorTextProvider { else if (parent instanceof PsiThisExpression) { expression = parent; } - else if (parent instanceof PsiInstanceOfExpression - || parent instanceof PsiBinaryExpression - || parent instanceof PsiPolyadicExpression - || parent instanceof PsiPrefixExpression - || parent instanceof PsiConditionalExpression - ) { + else if (parent instanceof PsiExpression && !(parent instanceof PsiNewExpression)) { if (allowMethodCalls || !DebuggerUtils.hasSideEffects(parent)) { expression = parent; } diff --git a/java/debugger/impl/src/com/intellij/debugger/impl/PositionUtil.java b/java/debugger/impl/src/com/intellij/debugger/impl/PositionUtil.java index 2b23f371f528..47558e8c4da8 100644 --- a/java/debugger/impl/src/com/intellij/debugger/impl/PositionUtil.java +++ b/java/debugger/impl/src/com/intellij/debugger/impl/PositionUtil.java @@ -41,6 +41,7 @@ public class PositionUtil extends ContextUtil { return ContextUtil.getSourcePosition(context); } + @Nullable public static PsiElement getContextElement(final StackFrameContext context) { if(context instanceof DebuggerContextImpl) return ((DebuggerContextImpl) context).getContextElement(); diff --git a/java/debugger/impl/src/com/intellij/debugger/settings/DebuggerDataViewsConfigurable.java b/java/debugger/impl/src/com/intellij/debugger/settings/DebuggerDataViewsConfigurable.java index 452a924aed00..8785e687eff3 100644 --- a/java/debugger/impl/src/com/intellij/debugger/settings/DebuggerDataViewsConfigurable.java +++ b/java/debugger/impl/src/com/intellij/debugger/settings/DebuggerDataViewsConfigurable.java @@ -53,7 +53,7 @@ public class DebuggerDataViewsConfigurable implements SearchableConfigurable { private JCheckBox myCbShowObjectId; private StateRestoringCheckBox myCbShowStaticFinalFields; - private final ArrayRendererConfigurable myArrayRendererConfigurable; + //private final ArrayRendererConfigurable myArrayRendererConfigurable; private JCheckBox myCbEnableAlternateViews; private JCheckBox myCbEnableToString; @@ -66,12 +66,12 @@ public class DebuggerDataViewsConfigurable implements SearchableConfigurable { public DebuggerDataViewsConfigurable(@Nullable Project project) { myProject = project; - myArrayRendererConfigurable = new ArrayRendererConfigurable(NodeRendererSettings.getInstance().getArrayRenderer()); + //myArrayRendererConfigurable = new ArrayRendererConfigurable(NodeRendererSettings.getInstance().getArrayRenderer()); } @Override public void disposeUIResources() { - myArrayRendererConfigurable.disposeUIResources(); + //myArrayRendererConfigurable.disposeUIResources(); myToStringFilterEditor = null; myProject = null; } @@ -165,13 +165,14 @@ public class DebuggerDataViewsConfigurable implements SearchableConfigurable { panel.add(showPanel, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 3, 1, 1.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(4, 0, 0, 0), 0, 0)); - final JPanel arraysPanel = new JPanel(new BorderLayout(0, UIUtil.DEFAULT_VGAP)); - final JComponent arraysComponent = myArrayRendererConfigurable.createComponent(); - assert arraysComponent != null; - arraysPanel.add(arraysComponent, BorderLayout.CENTER); - arraysPanel.add(myCbHideNullArrayElements, BorderLayout.SOUTH); - arraysPanel.setBorder(IdeBorderFactory.createTitledBorder("Arrays", true)); - panel.add(arraysPanel, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 3, 1, 1.0, 0.0, GridBagConstraints.NORTH, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0)); + //final JPanel arraysPanel = new JPanel(new BorderLayout(0, UIUtil.DEFAULT_VGAP)); + //final JComponent arraysComponent = myArrayRendererConfigurable.createComponent(); + //assert arraysComponent != null; + //arraysPanel.add(arraysComponent, BorderLayout.CENTER); + //arraysPanel.add(myCbHideNullArrayElements, BorderLayout.SOUTH); + //arraysPanel.setBorder(IdeBorderFactory.createTitledBorder("Arrays", true)); + //panel.add(arraysPanel, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 3, 1, 1.0, 0.0, GridBagConstraints.NORTH, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0)); + panel.add(myCbHideNullArrayElements, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 3, 1, 1.0, 0.0, GridBagConstraints.NORTH, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0)); panel.add(myCbEnableAlternateViews, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(4, 0, 0, 10), 0, 0)); // starting 4-th row @@ -209,7 +210,7 @@ public class DebuggerDataViewsConfigurable implements SearchableConfigurable { myAutoTooltip.save(); - myArrayRendererConfigurable.apply(); + //myArrayRendererConfigurable.apply(); rendererSettings.fireRenderersChanged(); } @@ -250,7 +251,7 @@ public class DebuggerDataViewsConfigurable implements SearchableConfigurable { myRbFromList.setEnabled(toStringEnabled); myRbAllThatOverride.setEnabled(toStringEnabled); - myArrayRendererConfigurable.reset(); + //myArrayRendererConfigurable.reset(); } @Override @@ -266,9 +267,9 @@ public class DebuggerDataViewsConfigurable implements SearchableConfigurable { } private boolean areDefaultRenderersModified() { - if (myArrayRendererConfigurable.isModified()) { - return true; - } + //if (myArrayRendererConfigurable.isModified()) { + // return true; + //} final NodeRendererSettings rendererSettings = NodeRendererSettings.getInstance(); diff --git a/java/debugger/impl/src/com/intellij/debugger/settings/DebuggerSettings.java b/java/debugger/impl/src/com/intellij/debugger/settings/DebuggerSettings.java index bb3fafed746f..2855ab4dfc1f 100644 --- a/java/debugger/impl/src/com/intellij/debugger/settings/DebuggerSettings.java +++ b/java/debugger/impl/src/com/intellij/debugger/settings/DebuggerSettings.java @@ -20,6 +20,7 @@ import com.intellij.openapi.components.*; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.InvalidDataException; import com.intellij.openapi.util.WriteExternalException; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.ui.classFilter.ClassFilter; import com.intellij.util.containers.hash.LinkedHashMap; import com.intellij.util.xmlb.SkipDefaultValuesSerializationFilters; @@ -55,6 +56,10 @@ public class DebuggerSettings implements Cloneable, PersistentStateComponent myContentStates = new LinkedHashMap(); @@ -142,6 +149,7 @@ public class DebuggerSettings implements Cloneable, PersistentStateComponent { private JCheckBox myCbSkipClassLoaders; private ClassFilterEditor mySteppingFilterEditor; private JCheckBox myCbSkipSimpleGetters; + private JRadioButton myRbEvaluateFinallyAlways; + private JRadioButton myRbEvaluateFinallyNever; + private JRadioButton myRbEvaluateFinallyAsk; @Override public void reset(@NotNull DebuggerSettings settings) { @@ -45,6 +49,16 @@ class DebuggerSteppingConfigurable implements ConfigurableUi { mySteppingFilterEditor.setFilters(settings.getSteppingFilters()); mySteppingFilterEditor.setEnabled(settings.TRACING_FILTERS_ENABLED); + + if (DebuggerSettings.EVALUATE_FINALLY_ALWAYS.equals(settings.EVALUATE_FINALLY_ON_POP_FRAME)) { + myRbEvaluateFinallyAlways.setSelected(true); + } + else if (DebuggerSettings.EVALUATE_FINALLY_NEVER.equals(settings.EVALUATE_FINALLY_ON_POP_FRAME)) { + myRbEvaluateFinallyNever.setSelected(true); + } + else { + myRbEvaluateFinallyAsk.setSelected(true); + } } @Override @@ -59,6 +73,16 @@ class DebuggerSteppingConfigurable implements ConfigurableUi { settings.SKIP_CLASSLOADERS = myCbSkipClassLoaders.isSelected(); settings.TRACING_FILTERS_ENABLED = myCbStepInfoFiltersEnabled.isSelected(); + if (myRbEvaluateFinallyAlways.isSelected()) { + settings.EVALUATE_FINALLY_ON_POP_FRAME = DebuggerSettings.EVALUATE_FINALLY_ALWAYS; + } + else if (myRbEvaluateFinallyNever.isSelected()) { + settings.EVALUATE_FINALLY_ON_POP_FRAME = DebuggerSettings.EVALUATE_FINALLY_NEVER; + } + else { + settings.EVALUATE_FINALLY_ON_POP_FRAME = DebuggerSettings.EVALUATE_FINALLY_ASK; + } + mySteppingFilterEditor.stopEditing(); settings.setSteppingFilters(mySteppingFilterEditor.getFilters()); } @@ -94,6 +118,33 @@ class DebuggerSteppingConfigurable implements ConfigurableUi { mySteppingFilterEditor.setEnabled(myCbStepInfoFiltersEnabled.isSelected()); } }); + + myRbEvaluateFinallyAlways = new JRadioButton(DebuggerBundle.message("label.debugger.general.configurable.evaluate.finally.always")); + myRbEvaluateFinallyNever = new JRadioButton(DebuggerBundle.message("label.debugger.general.configurable.evaluate.finally.never")); + myRbEvaluateFinallyAsk = new JRadioButton(DebuggerBundle.message("label.debugger.general.configurable.evaluate.finally.ask")); + + int cbLeftOffset = 0; + final Border border = myCbSkipSimpleGetters.getBorder(); + if (border != null) { + final Insets insets = border.getBorderInsets(myCbSkipSimpleGetters); + if (insets != null) { + cbLeftOffset = insets.left; + } + } + + final ButtonGroup group = new ButtonGroup(); + group.add(myRbEvaluateFinallyAlways); + group.add(myRbEvaluateFinallyNever); + group.add(myRbEvaluateFinallyAsk); + final Box box = Box.createHorizontalBox(); + box.add(myRbEvaluateFinallyAlways); + box.add(myRbEvaluateFinallyNever); + box.add(myRbEvaluateFinallyAsk); + final JPanel evalFinallyPanel = new JPanel(new BorderLayout()); + evalFinallyPanel.add(box, BorderLayout.CENTER); + evalFinallyPanel.add(new JLabel(DebuggerBundle.message("label.debugger.general.configurable.evaluate.finally.on.pop")), BorderLayout.WEST); + panel.add(evalFinallyPanel, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 1.0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(4, cbLeftOffset, 0, 0), 0, 0)); + return panel; } } \ No newline at end of file diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/DebuggerEditorImpl.java b/java/debugger/impl/src/com/intellij/debugger/ui/DebuggerEditorImpl.java index deff5fbd2ae5..8805341c5c46 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/DebuggerEditorImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/DebuggerEditorImpl.java @@ -294,7 +294,7 @@ public abstract class DebuggerEditorImpl extends CompletionEditor{ } @NotNull - public static CodeFragmentFactory findAppropriateFactory(@NotNull TextWithImports text, @NotNull PsiElement context) { + private static CodeFragmentFactory findAppropriateFactory(@NotNull TextWithImports text, @Nullable PsiElement context) { for (CodeFragmentFactory factory : DebuggerUtilsEx.getCodeFragmentFactories(context)) { if (factory.getFileType().equals(text.getFileType())) { return factory; diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/DebuggerSessionTab.java b/java/debugger/impl/src/com/intellij/debugger/ui/DebuggerSessionTab.java index 47fc217751a4..0855ed3c1cc4 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/DebuggerSessionTab.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/DebuggerSessionTab.java @@ -41,7 +41,6 @@ import com.intellij.execution.filters.TextConsoleBuilder; import com.intellij.execution.filters.TextConsoleBuilderFactory; import com.intellij.execution.ui.ConsoleView; import com.intellij.execution.ui.ExecutionConsoleEx; -import com.intellij.execution.ui.RunContentDescriptor; import com.intellij.execution.ui.RunnerLayoutUi; import com.intellij.execution.ui.layout.PlaceInGrid; import com.intellij.icons.AllIcons; @@ -109,7 +108,7 @@ public class DebuggerSessionTab extends DebuggerSessionTabBase implements Dispos if (XDebuggerSettingsManager.getInstanceImpl().getGeneralSettings().isHideDebuggerOnProcessTermination()) { try { - ExecutionManager.getInstance(getProject()).getContentManager().hideRunContent(DefaultDebugExecutor.getDebugExecutorInstance(), myRunContentDescriptor); + ExecutionManager.getInstance(project).getContentManager().hideRunContent(DefaultDebugExecutor.getDebugExecutorInstance(), myRunContentDescriptor); } catch (NullPointerException e) { //if we can get closeProcess after the project have been closed @@ -130,8 +129,8 @@ public class DebuggerSessionTab extends DebuggerSessionTabBase implements Dispos topToolbar.add(Separator.getInstance(), new Constraints(Anchor.AFTER, DebuggerActions.POP_FRAME)); myUi.getOptions().setTopToolbar(topToolbar, ActionPlaces.DEBUGGER_TOOLBAR); - myWatchPanel = new MainWatchPanel(getProject(), getContextManager()); - myFramesPanel = new FramesPanel(getProject(), getContextManager()); + myWatchPanel = new MainWatchPanel(project, getContextManager()); + myFramesPanel = new FramesPanel(project, getContextManager()); final AlertIcon breakpointAlert = new AlertIcon(AllIcons.Debugger.BreakpointAlert); @@ -151,7 +150,7 @@ public class DebuggerSessionTab extends DebuggerSessionTabBase implements Dispos myUi.addContent(framesContent, 0, PlaceInGrid.left, false); // variables - myVariablesPanel = new VariablesPanel(getProject(), myStateManager, this); + myVariablesPanel = new VariablesPanel(project, myStateManager, this); myVariablesPanel.getFrameTree().setAutoVariablesMode(debuggerSettings.AUTO_VARIABLES_MODE); Content vars = myUi.createContent(DebuggerContentInfo.VARIABLES_CONTENT, myVariablesPanel, XDebuggerBundle.message("debugger.session.tab.variables.title"), AllIcons.Debugger.Value, null); @@ -200,11 +199,11 @@ public class DebuggerSessionTab extends DebuggerSessionTabBase implements Dispos } }); - ExecutionResult executionResult = debuggerSession.getProcess().getExecutionResult(); - myConsole = executionResult.getExecutionConsole(); - myRunContentDescriptor = new RunContentDescriptor(myConsole, executionResult.getProcessHandler(), myUi.getComponent(), getSessionName(), - environment.getIcon()); - initUI(executionResult); +// ExecutionResult executionResult = debuggerSession.getProcess().getExecutionResult(); +// myConsole = executionResult.getExecutionConsole(); +// myRunContentDescriptor = new RunContentDescriptor(myConsole, executionResult.getProcessHandler(), myUi.getComponent(), getSessionName(), +// environment.getIcon()); +// initUI(executionResult); } private static void updateStatus(final Content content) { @@ -226,11 +225,6 @@ public class DebuggerSessionTab extends DebuggerSessionTabBase implements Dispos return myWatchPanel; } - @Override - public RunContentDescriptor getRunContentDescriptor() { - return myRunContentDescriptor; - } - private void initUI(ExecutionResult executionResult) { if (ApplicationManager.getApplication().isUnitTestMode()) { return; @@ -270,18 +264,16 @@ public class DebuggerSessionTab extends DebuggerSessionTabBase implements Dispos } console.setActions(consoleActions, ActionPlaces.DEBUGGER_TOOLBAR, myConsole.getPreferredFocusableComponent()); - myDebugUIEnvironment.initLogs(myRunContentDescriptor, myManager); +// myDebugUIEnvironment.initLogs(myRunContentDescriptor, myManager); DefaultActionGroup leftToolbar = new DefaultActionGroup(); if (executionResult instanceof DefaultExecutionResult) { final AnAction[] actions = ((DefaultExecutionResult)executionResult).getRestartActions(); - if (actions != null) { leftToolbar.addAll(actions); if (actions.length > 0) { leftToolbar.addSeparator(); } - } } final AnAction[] profileActions = executionResult.getActions(); leftToolbar.addAll(profileActions); diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/EvaluationDialog.java b/java/debugger/impl/src/com/intellij/debugger/ui/EvaluationDialog.java index 6359af31e5a3..0acaf8779e1f 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/EvaluationDialog.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/EvaluationDialog.java @@ -30,7 +30,6 @@ import com.intellij.debugger.ui.impl.ValueNodeDnD; import com.intellij.debugger.ui.impl.WatchDebuggerTree; import com.intellij.debugger.ui.impl.WatchPanel; import com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeImpl; -import com.intellij.debugger.ui.impl.watch.EvaluationDescriptor; import com.intellij.debugger.ui.impl.watch.NodeDescriptorImpl; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.*; @@ -110,10 +109,10 @@ public abstract class EvaluationDialog extends DialogWrapper { try { setOKActionEnabled(false); NodeDescriptorImpl descriptor = myEvaluationPanel.getWatchTree().addWatch(codeToEvaluate, "result").getDescriptor(); - if (descriptor instanceof EvaluationDescriptor) { - final EvaluationDescriptor evalDescriptor = (EvaluationDescriptor)descriptor; - evalDescriptor.setCodeFragmentFactory(myEditor.getCurrentFactory()); - } + //if (descriptor instanceof EvaluationDescriptor) { + // final EvaluationDescriptor evalDescriptor = (EvaluationDescriptor)descriptor; + // evalDescriptor.setCodeFragmentFactory(myEditor.getCurrentFactory()); + //} myEvaluationPanel.getWatchTree().rebuild(getDebuggerContext()); descriptor.myIsExpanded = true; } diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/JavaDebuggerSupport.java b/java/debugger/impl/src/com/intellij/debugger/ui/JavaDebuggerSupport.java index ec2288fa7a57..e01b8742c4b6 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/JavaDebuggerSupport.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/JavaDebuggerSupport.java @@ -61,7 +61,7 @@ public class JavaDebuggerSupport extends DebuggerSupport { private final PauseActionHandler myPauseActionHandler = new PauseActionHandler(); private final ShowExecutionPointActionHandler myShowExecutionPointActionHandler = new ShowExecutionPointActionHandler(); //private final EvaluateActionHandler myEvaluateActionHandler = new EvaluateActionHandler(); - private final QuickEvaluateActionHandler myQuickEvaluateHandler = new QuickEvaluateActionHandler(); + //private final QuickEvaluateActionHandler myQuickEvaluateHandler = new QuickEvaluateActionHandler(); private final DebuggerActionHandler mySmartStepIntoHandler = new JvmSmartStepIntoActionHandler(); private final DebuggerActionHandler myAddToWatchedActionHandler = new AddToWatchActionHandler(); private final JavaMarkObjectActionHandler myMarkObjectActionHandler = new JavaMarkObjectActionHandler(); @@ -156,12 +156,6 @@ public class JavaDebuggerSupport extends DebuggerSupport { return DISABLED; } - @Override - @NotNull - public QuickEvaluateHandler getQuickEvaluateHandler() { - return myQuickEvaluateHandler; - } - @NotNull @Override public DebuggerActionHandler getAddToWatchesActionHandler() { diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/ValueHint.java b/java/debugger/impl/src/com/intellij/debugger/ui/ValueHint.java index 8a0877273335..9034e5c82b13 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/ValueHint.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/ValueHint.java @@ -94,9 +94,8 @@ public class ValueHint extends AbstractValueHint { return EvaluatorBuilderImpl.getInstance().build(myCurrentExpression, debuggerContext.getSourcePosition()); } - CodeFragmentFactory factory = DebuggerUtilsEx.getEffectiveCodeFragmentFactory(myCurrentExpression); TextWithImportsImpl textWithImports = new TextWithImportsImpl(CodeFragmentKind.EXPRESSION, myCurrentExpression.getText()); - if (factory == null) return null; + CodeFragmentFactory factory = DebuggerUtilsEx.findAppropriateCodeFragmentFactory(textWithImports, myCurrentExpression); JavaCodeFragment codeFragment = factory.createCodeFragment(textWithImports, myCurrentExpression.getContext(), getProject()); codeFragment.forceResolveScope(GlobalSearchScope.allScope(getProject())); return factory.getEvaluatorBuilder().build(codeFragment, debuggerContext.getSourcePosition()); diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ArgumentValueDescriptorImpl.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ArgumentValueDescriptorImpl.java index 7adee9d7c0eb..525abba6613f 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ArgumentValueDescriptorImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/ArgumentValueDescriptorImpl.java @@ -50,6 +50,11 @@ public class ArgumentValueDescriptorImpl extends ValueDescriptorImpl{ setLvalue(true); } + @Override + public boolean canSetValue() { + return false; + } + public boolean isPrimitive() { return myValue instanceof PrimitiveValue; } diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/CompilingEvaluator.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/CompilingEvaluator.java index 042216c40f05..467cf985f04c 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/CompilingEvaluator.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/CompilingEvaluator.java @@ -15,28 +15,57 @@ */ package com.intellij.debugger.ui.impl.watch; +import com.intellij.debugger.DebuggerInvocationUtil; +import com.intellij.debugger.EvaluatingComputable; +import com.intellij.debugger.engine.ContextUtil; import com.intellij.debugger.engine.DebugProcess; -import com.intellij.debugger.engine.evaluation.EvaluateException; -import com.intellij.debugger.engine.evaluation.EvaluationContext; -import com.intellij.debugger.engine.evaluation.TextWithImports; +import com.intellij.debugger.engine.evaluation.*; import com.intellij.debugger.engine.evaluation.expression.ExpressionEvaluator; import com.intellij.debugger.engine.evaluation.expression.Modifier; +import com.intellij.debugger.impl.DebuggerUtilsEx; import com.intellij.debugger.jdi.VirtualMachineProxyImpl; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Computable; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiCodeFragment; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiJavaFile; +import com.intellij.refactoring.extractMethodObject.ExtractLightMethodObjectHandler; import com.sun.jdi.*; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.org.objectweb.asm.ClassReader; +import org.jetbrains.org.objectweb.asm.ClassVisitor; +import org.jetbrains.org.objectweb.asm.ClassWriter; +import org.jetbrains.org.objectweb.asm.Opcodes; import javax.tools.*; import java.io.ByteArrayOutputStream; import java.net.URI; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; /** * @author egor */ -class CompilingEvaluator implements ExpressionEvaluator { +public class CompilingEvaluator implements ExpressionEvaluator { private final TextWithImports myText; - - public CompilingEvaluator(TextWithImports text) { + private final PsiCodeFragment myCodeFragment; + private final PsiElement myPsiContext; + @NotNull private final ExtractLightMethodObjectHandler.ExtractedData myData; + private final EvaluationDescriptor myDescriptor; + + public CompilingEvaluator(TextWithImports text, + PsiCodeFragment codeFragment, + PsiElement context, + @NotNull ExtractLightMethodObjectHandler.ExtractedData data, + EvaluationDescriptor descriptor) { myText = text; + myCodeFragment = codeFragment; + myPsiContext = context; + myData = data; + myDescriptor = descriptor; } @Override @@ -49,20 +78,41 @@ class CompilingEvaluator implements ExpressionEvaluator { return null; } + private TextWithImports getCallCode() { + return new TextWithImportsImpl(CodeFragmentKind.CODE_BLOCK, myData.getGeneratedCallText()); + } + @Override - public Value evaluate(EvaluationContext context) throws EvaluateException { + public Value evaluate(final EvaluationContext evaluationContext) throws EvaluateException { try { - DebugProcess process = context.getDebugProcess(); - ThreadReference threadReference = context.getSuspendContext().getThread().getThreadReference(); + DebugProcess process = evaluationContext.getDebugProcess(); + ThreadReference threadReference = evaluationContext.getSuspendContext().getThread().getThreadReference(); - ClassLoaderReference classLoader = getClassLoader(context); + ClassLoaderReference classLoader = getClassLoader(evaluationContext); Collection classes = compile(); - ClassType mainClass = defineClasses(classes, context, process, threadReference, classLoader); - - Method foo = mainClass.methodsByName(GEN_METHOD_NAME).get(0); - return mainClass.invokeMethod(threadReference, foo, Collections.emptyList() ,ClassType.INVOKE_SINGLE_THREADED); + ClassType mainClass = defineClasses(classes, evaluationContext, process, threadReference, classLoader); + + //Method foo = mainClass.methodsByName(GEN_METHOD_NAME).get(0); + //return mainClass.invokeMethod(threadReference, foo, Collections.emptyList() ,ClassType.INVOKE_SINGLE_THREADED); + + // invoke base evaluator on call code + final Project project = myPsiContext.getProject(); + ExpressionEvaluator evaluator = + DebuggerInvocationUtil.commitAndRunReadAction(project, new EvaluatingComputable() { + @Override + public ExpressionEvaluator compute() throws EvaluateException { + final TextWithImports callCode = getCallCode(); + PsiElement copyContext = myData.getAnchor(); + final CodeFragmentFactory factory = DebuggerUtilsEx.findAppropriateCodeFragmentFactory(callCode, copyContext); + return factory.getEvaluatorBuilder(). + build(factory.createCodeFragment(callCode, copyContext, project), + ContextUtil.getSourcePosition(evaluationContext)); + } + }); + ((EvaluationContextImpl)evaluationContext).setClassLoader(classLoader); + return evaluator.evaluate(evaluationContext); } catch (Exception e) { throw new EvaluateException(e.getMessage()); @@ -74,13 +124,13 @@ class CompilingEvaluator implements ExpressionEvaluator { // TODO: cache DebugProcess process = context.getDebugProcess(); ClassType loaderClass = (ClassType)process.findClass(context, "java.net.URLClassLoader", context.getClassLoader()); - Method ctorMethod = loaderClass.concreteMethodByName("", "([Ljava/net/URL;)V"); + Method ctorMethod = loaderClass.concreteMethodByName("", "([Ljava/net/URL;Ljava/lang/ClassLoader;)V"); ThreadReference threadReference = context.getSuspendContext().getThread().getThreadReference(); return (ClassLoaderReference)loaderClass.newInstance(threadReference, ctorMethod, - Arrays.asList(createURLArray(context)), ClassType.INVOKE_SINGLE_THREADED); + Arrays.asList(createURLArray(context), context.getClassLoader()), ClassType.INVOKE_SINGLE_THREADED); } - private static ClassType defineClasses(Collection classes, + private ClassType defineClasses(Collection classes, EvaluationContext context, DebugProcess process, ThreadReference threadReference, @@ -89,16 +139,34 @@ class CompilingEvaluator implements ExpressionEvaluator { VirtualMachineProxyImpl proxy = (VirtualMachineProxyImpl)process.getVirtualMachineProxy(); for (OutputFileObject cls : classes) { - Method defineMethod = ((ClassType)classLoader.referenceType()).concreteMethodByName("defineClass", "(Ljava/lang/String;[BII)Ljava/lang/Class;"); - byte[] bytes = cls.toByteArray(); - ArrayList args = new ArrayList(); - args.add(proxy.mirrorOf(cls.myOrigName)); - args.add(mirrorOf(bytes, context, process)); - args.add(proxy.mirrorOf(0)); - args.add(proxy.mirrorOf(bytes.length)); - classLoader.invokeMethod(threadReference, defineMethod, args, ClassType.INVOKE_SINGLE_THREADED); + if (cls.getName().contains(getGenClassName())) { + Method defineMethod = + ((ClassType)classLoader.referenceType()).concreteMethodByName("defineClass", "(Ljava/lang/String;[BII)Ljava/lang/Class;"); + byte[] bytes = changeSuperToMagicAccessor(cls.toByteArray()); + ArrayList args = new ArrayList(); + args.add(proxy.mirrorOf(cls.myOrigName)); + args.add(mirrorOf(bytes, context, process)); + args.add(proxy.mirrorOf(0)); + args.add(proxy.mirrorOf(bytes.length)); + classLoader.invokeMethod(threadReference, defineMethod, args, ClassType.INVOKE_SINGLE_THREADED); + } } - return (ClassType)process.findClass(context, GEN_CLASS_FULL_NAME, classLoader); + return (ClassType)process.findClass(context, getGenClassFullName(), classLoader); + } + + private static byte[] changeSuperToMagicAccessor(byte[] bytes) { + ClassWriter classWriter = new ClassWriter(0); + ClassVisitor classVisitor = new ClassVisitor(Opcodes.ASM5, classWriter) { + @Override + public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { + if ("java/lang/Object".equals(superName)) { + superName = "sun/reflect/MagicAccessorImpl"; + } + super.visit(version, access, name, signature, superName, interfaces); + } + }; + new ClassReader(bytes).accept(classVisitor, 0); + return classWriter.toByteArray(); } private static ArrayReference mirrorOf(byte[] bytes, EvaluationContext context, DebugProcess process) @@ -112,10 +180,90 @@ class CompilingEvaluator implements ExpressionEvaluator { return reference; } - private static final String GEN_CLASS_NAME = "Evaluator"; + public static String getGeneratedClassName() { + return GEN_CLASS_NAME; + } + + private static final String GEN_CLASS_NAME = "GeneratedEvaluationClass"; private static final String GEN_CLASS_PACKAGE = "dummy"; private static final String GEN_CLASS_FULL_NAME = GEN_CLASS_PACKAGE + '.' + GEN_CLASS_NAME; - private static final String GEN_METHOD_NAME = "eval"; + private static final String GEN_METHOD_NAME = "invoke"; + + private String getClassCode() { + if (myData != null) { + return ApplicationManager.getApplication().runReadAction(new Computable() { + @Override + public String compute() { + //String text = myData.getGeneratedInnerClass().getText(); + ////TODO: remove + //String prefix = "public static"; + //if (text.startsWith(prefix)) { + // text = "public" + text.substring(prefix.length()); + //} + //PsiElement[] children = ((PsiJavaFile)myPsiContext.getContainingFile()).getImportList().getChildren(); + //StringBuilder imports = new StringBuilder(); + //for (PsiElement child : children) { + // if (child instanceof PsiImportStatement) { + // String name = ((PsiImportStatement)child).getImportReference().getQualifiedName(); + // imports.append("import ").append(name).append(";"); + // } + //} + //text = text.replace("class " + GEN_CLASS_NAME, "class " + getGenClassName()); + //text = text.replace(GEN_CLASS_NAME + "(", getGenClassName() + "("); + //text = text.replace(((PsiClass)myData.getGeneratedInnerClass().getParent()).getName() + "." + GEN_CLASS_NAME, getGenClassName()); + //return "package " + getGenPackageName() + "; " + imports.toString() + text; + return myData.getGeneratedInnerClass().getContainingFile().getText(); + } + }); + } + return null; + } + + private String getGenPackageName() { + return ApplicationManager.getApplication().runReadAction(new Computable() { + @Override + public String compute() { + return ((PsiJavaFile)myData.getGeneratedInnerClass().getContainingFile()).getPackageName(); + } + }); + } + + private String getMainClassName() { + return ApplicationManager.getApplication().runReadAction(new Computable() { + @Override + public String compute() { + return ((PsiClass)myData.getGeneratedInnerClass().getParent()).getName(); + } + }); + } + + private String getGenClassName() { + return getMainClassName() + '$' + GEN_CLASS_NAME; + } + + private String getGenClassFullName() { + String packageName = getGenPackageName(); + if (packageName.isEmpty()) { + return getGenClassName(); + } + return packageName + '.' + getGenClassName(); + } + + //private String createClassCode() { + // return ApplicationManager.getApplication().runReadAction(new Computable() { + // @Override + // public String compute() { + // try { + // myExtractedData = + // ExtractLightMethodObjectHandler.extractLightMethodObject(myCodeFragment.getProject(), myFile , myCodeFragment, "test"); + // } + // catch (PrepareFailedException e) { + // e.printStackTrace(); + // } + // return null; + // } + // }); + //} private static String createClassCode(TextWithImports body) { StringBuilder text = new StringBuilder(); @@ -155,9 +303,9 @@ class CompilingEvaluator implements ExpressionEvaluator { MemoryFileManager manager = new MemoryFileManager(compiler); DiagnosticCollector diagnostic = new DiagnosticCollector(); if (!compiler.getTask(null, manager, diagnostic, null, null, Arrays - .asList(new SourceFileObject(GEN_CLASS_NAME, JavaFileObject.Kind.SOURCE, createClassCode(myText)))).call()) { + .asList(new SourceFileObject(getMainClassName(), JavaFileObject.Kind.SOURCE, getClassCode()))).call()) { // TODO: show only errors - throw new EvaluateException(diagnostic.getDiagnostics().get(0).getMessage(Locale.getDefault())); + throw new EvaluateException(diagnostic.getDiagnostics().get(0).toString()); } return manager.classes; } diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/EvaluationDescriptor.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/EvaluationDescriptor.java index 1f880513cacd..05e0a1fe6aec 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/EvaluationDescriptor.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/EvaluationDescriptor.java @@ -21,22 +21,26 @@ import com.intellij.debugger.DebuggerInvocationUtil; import com.intellij.debugger.EvaluatingComputable; import com.intellij.debugger.engine.ContextUtil; import com.intellij.debugger.engine.StackFrameContext; -import com.intellij.debugger.engine.evaluation.*; +import com.intellij.debugger.engine.evaluation.EvaluateException; +import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil; +import com.intellij.debugger.engine.evaluation.EvaluationContextImpl; +import com.intellij.debugger.engine.evaluation.TextWithImports; +import com.intellij.debugger.engine.evaluation.expression.UnsupportedExpressionException; import com.intellij.debugger.engine.evaluation.expression.ExpressionEvaluator; import com.intellij.debugger.engine.evaluation.expression.Modifier; import com.intellij.debugger.impl.DebuggerUtilsEx; import com.intellij.debugger.impl.PositionUtil; import com.intellij.debugger.jdi.StackFrameProxyImpl; +import com.intellij.debugger.jdi.VirtualMachineProxyImpl; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.registry.Registry; -import com.intellij.psi.PsiCodeFragment; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiExpression; -import com.intellij.psi.PsiExpressionCodeFragment; +import com.intellij.psi.*; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.refactoring.extractMethod.PrepareFailedException; +import com.intellij.refactoring.extractMethodObject.ExtractLightMethodObjectHandler; +import com.sun.jdi.ObjectCollectedException; import com.sun.jdi.ObjectReference; import com.sun.jdi.Value; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; /** * @author lex @@ -44,7 +48,6 @@ import org.jetbrains.annotations.Nullable; public abstract class EvaluationDescriptor extends ValueDescriptorImpl{ private Modifier myModifier; protected TextWithImports myText; - private CodeFragmentFactory myCodeFragmentFactory = null; // used to force specific context, e.g. from evaluation protected EvaluationDescriptor(TextWithImports text, Project project, Value value) { super(project, value); @@ -57,44 +60,60 @@ public abstract class EvaluationDescriptor extends ValueDescriptorImpl{ myText = text; } - public final void setCodeFragmentFactory(CodeFragmentFactory codeFragmentFactory) { - myCodeFragmentFactory = codeFragmentFactory != null? new CodeFragmentFactoryContextWrapper(codeFragmentFactory) : null; - } - - @Nullable - public final CodeFragmentFactory getCodeFragmentFactory() { - return myCodeFragmentFactory; - } - - protected final @NotNull CodeFragmentFactory getEffectiveCodeFragmentFactory(final PsiElement psiContext) { - if (myCodeFragmentFactory != null) { - return myCodeFragmentFactory; - } - return DebuggerUtilsEx.getEffectiveCodeFragmentFactory(psiContext); - } - protected abstract EvaluationContextImpl getEvaluationContext (EvaluationContextImpl evaluationContext); protected abstract PsiCodeFragment getEvaluationCode(StackFrameContext context) throws EvaluateException; + public PsiCodeFragment createCodeFragment(PsiElement context) { + TextWithImports text = getEvaluationText(); + final PsiCodeFragment fragment = + DebuggerUtilsEx.findAppropriateCodeFragmentFactory(text, context).createCodeFragment(text, context, myProject); + fragment.forceResolveScope(GlobalSearchScope.allScope(myProject)); + return fragment; + } + public final Value calcValue(final EvaluationContextImpl evaluationContext) throws EvaluateException { try { final EvaluationContextImpl thisEvaluationContext = getEvaluationContext(evaluationContext); - final ExpressionEvaluator evaluator; - if (Registry.is("debugger.compiling.evaluator")) { - evaluator = new CompilingEvaluator(getEvaluationText()); - } - else { + ExpressionEvaluator evaluator = null; + try { evaluator = DebuggerInvocationUtil.commitAndRunReadAction(myProject, new EvaluatingComputable() { public ExpressionEvaluator compute() throws EvaluateException { final PsiElement psiContext = PositionUtil.getContextElement(evaluationContext); - return getEffectiveCodeFragmentFactory(psiContext).getEvaluatorBuilder().build(getEvaluationCode(thisEvaluationContext), - ContextUtil - .getSourcePosition(thisEvaluationContext)); + return DebuggerUtilsEx.findAppropriateCodeFragmentFactory(getEvaluationText(), psiContext).getEvaluatorBuilder() + .build(getEvaluationCode(thisEvaluationContext), ContextUtil.getSourcePosition(thisEvaluationContext)); } }); } + catch (UnsupportedExpressionException ex) { + if (Registry.is("debugger.compiling.evaluator")) { + evaluator = DebuggerInvocationUtil.commitAndRunReadAction(myProject, new EvaluatingComputable() { + public ExpressionEvaluator compute() throws EvaluateException { + final PsiElement psiContext = PositionUtil.getContextElement(evaluationContext); + if (psiContext == null) { + return null; + } + PsiFile psiFile = psiContext.getContainingFile(); + PsiCodeFragment fragment = createCodeFragment(psiContext); + try { + ExtractLightMethodObjectHandler.ExtractedData data = ExtractLightMethodObjectHandler.extractLightMethodObject(myProject, + psiFile, fragment, CompilingEvaluator.getGeneratedClassName()); + if (data != null) { + return new CompilingEvaluator(getEvaluationText(), getEvaluationCode(thisEvaluationContext), psiContext, data, + EvaluationDescriptor.this); + } + } + catch (PrepareFailedException ignored) { + } + return null; + } + }); + } + if (evaluator == null) { + throw ex; + } + } if (!thisEvaluationContext.getDebugProcess().isAttached()) { throw EvaluateExceptionUtil.PROCESS_EXITED; @@ -106,7 +125,11 @@ public abstract class EvaluationDescriptor extends ValueDescriptorImpl{ final Value value = evaluator.evaluate(thisEvaluationContext); if (value instanceof ObjectReference) { - thisEvaluationContext.getSuspendContext().keep(((ObjectReference)value)); + ObjectReference objRef = (ObjectReference)value; + if (VirtualMachineProxyImpl.isCollected(objRef)) { + throw EvaluateExceptionUtil.OBJECT_WAS_COLLECTED; + } + thisEvaluationContext.getSuspendContext().keep(objRef); } myModifier = evaluator.getModifier(); setLvalue(myModifier != null); @@ -116,6 +139,9 @@ public abstract class EvaluationDescriptor extends ValueDescriptorImpl{ catch (final EvaluateException ex) { throw new EvaluateException(ex.getLocalizedMessage(), ex); } + catch (ObjectCollectedException ex) { + throw EvaluateExceptionUtil.OBJECT_WAS_COLLECTED; + } } public String calcValueName() { diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/NodeDescriptorFactoryImpl.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/NodeDescriptorFactoryImpl.java index 3455672fa4dc..0a94548338d4 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/NodeDescriptorFactoryImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/NodeDescriptorFactoryImpl.java @@ -93,6 +93,10 @@ public class NodeDescriptorFactoryImpl implements NodeDescriptorFactory { } public void deriveHistoryTree(DescriptorTree tree, final StackFrameContext context) { + deriveHistoryTree(tree, context.getFrameProxy()); + } + + public void deriveHistoryTree(DescriptorTree tree, final StackFrameProxy frameProxy) { final MarkedDescriptorTree descriptorTree = new MarkedDescriptorTree(); final MarkedDescriptorTree displayDescriptorTree = new MarkedDescriptorTree(); @@ -109,13 +113,12 @@ public class NodeDescriptorFactoryImpl implements NodeDescriptorFactory { myDescriptorSearcher = new DescriptorTreeSearcher(descriptorTree); myDisplayDescriptorSearcher = new DisplayDescriptorTreeSearcher(displayDescriptorTree); - myCurrentHistoryTree = createDescriptorTree(context, tree); + myCurrentHistoryTree = createDescriptorTree(frameProxy, tree); } - private static DescriptorTree createDescriptorTree(final StackFrameContext context, final DescriptorTree fromTree) { + private static DescriptorTree createDescriptorTree(final StackFrameProxy frameProxy, final DescriptorTree fromTree) { int frameCount = -1; int frameIndex = -1; - final StackFrameProxy frameProxy = context.getFrameProxy(); if (frameProxy != null) { try { final ThreadReferenceProxy threadReferenceProxy = frameProxy.threadProxy(); diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/NodeManagerImpl.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/NodeManagerImpl.java index 53e820da02fd..b76022122594 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/NodeManagerImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/NodeManagerImpl.java @@ -75,11 +75,15 @@ public class NodeManagerImpl extends NodeDescriptorFactoryImpl implements NodeMa } public void setHistoryByContext(final DebuggerContextImpl context) { + setHistoryByContext(context.getFrameProxy()); + } + + public void setHistoryByContext(StackFrameProxyImpl frameProxy) { if (myHistoryKey != null) { myHistories.put(myHistoryKey, getCurrentHistoryTree()); } - final String historyKey = getContextKey(context.getFrameProxy()); + final String historyKey = getContextKey(frameProxy); final DescriptorTree descriptorTree; if (historyKey != null) { final DescriptorTree historyTree = myHistories.get(historyKey); @@ -89,7 +93,7 @@ public class NodeManagerImpl extends NodeDescriptorFactoryImpl implements NodeMa descriptorTree = new DescriptorTree(true); } - deriveHistoryTree(descriptorTree, context); + deriveHistoryTree(descriptorTree, frameProxy); myHistoryKey = historyKey; } diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/UserExpressionDescriptorImpl.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/UserExpressionDescriptorImpl.java index c3dbed6c7fbb..35e73c2b6378 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/UserExpressionDescriptorImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/UserExpressionDescriptorImpl.java @@ -21,19 +21,18 @@ package com.intellij.debugger.ui.impl.watch; import com.intellij.debugger.DebuggerBundle; +import com.intellij.debugger.engine.DebuggerUtils; import com.intellij.debugger.engine.StackFrameContext; import com.intellij.debugger.engine.evaluation.EvaluateException; import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil; import com.intellij.debugger.engine.evaluation.EvaluationContextImpl; import com.intellij.debugger.engine.evaluation.TextWithImports; -import com.intellij.debugger.impl.DebuggerUtilsEx; import com.intellij.debugger.settings.NodeRendererSettings; import com.intellij.debugger.ui.tree.UserExpressionDescriptor; import com.intellij.debugger.ui.tree.render.ClassRenderer; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiCodeFragment; -import com.intellij.psi.search.GlobalSearchScope; import com.intellij.util.StringBuilderSpinAllocator; import com.sun.jdi.ObjectReference; import com.sun.jdi.Value; @@ -78,16 +77,13 @@ public class UserExpressionDescriptorImpl extends EvaluationDescriptor implement if(value instanceof ObjectReference) { final String typeName = value.type().name(); - final PsiClass psiClass = DebuggerUtilsEx.findClass(myTypeName, myProject, context.getDebugProcess().getSearchScope()); + final PsiClass psiClass = DebuggerUtils.findClass(myTypeName, myProject, context.getDebugProcess().getSearchScope()); if (psiClass == null) { throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.invalid.type.name", typeName)); } - final PsiCodeFragment fragment = - getEffectiveCodeFragmentFactory(psiClass).createCodeFragment(getEvaluationText(), psiClass, myProject); - fragment.forceResolveScope(GlobalSearchScope.allScope(myProject)); - return fragment; + return createCodeFragment(psiClass); } else { throw EvaluateExceptionUtil.createEvaluateException( diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/WatchItemDescriptor.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/WatchItemDescriptor.java index 30f5169fbd1e..f60825cee24c 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/WatchItemDescriptor.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/WatchItemDescriptor.java @@ -28,8 +28,6 @@ import com.intellij.debugger.impl.PositionUtil; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Comparing; import com.intellij.psi.PsiCodeFragment; -import com.intellij.psi.PsiElement; -import com.intellij.psi.search.GlobalSearchScope; import com.sun.jdi.Value; import org.jetbrains.annotations.Nullable; @@ -85,9 +83,6 @@ public class WatchItemDescriptor extends EvaluationDescriptor { } protected PsiCodeFragment getEvaluationCode(StackFrameContext context) throws EvaluateException { - final PsiElement psiContext = PositionUtil.getContextElement(context); - final PsiCodeFragment fragment = getEffectiveCodeFragmentFactory(psiContext).createCodeFragment(getEvaluationText(), psiContext, myProject); - fragment.forceResolveScope(GlobalSearchScope.allScope(myProject)); - return fragment; + return createCodeFragment(PositionUtil.getContextElement(context)); } } \ No newline at end of file diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ArrayRenderer.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ArrayRenderer.java index 9615aae98233..f7f05fcc496f 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ArrayRenderer.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ArrayRenderer.java @@ -65,6 +65,8 @@ public class ArrayRenderer extends NodeRendererImpl{ public int ENTRIES_LIMIT = 101; private final static String MORE_ELEMENTS = "..."; + private boolean myForced = false; + public ArrayRenderer() { myProperties.setEnabled(true); } @@ -97,13 +99,19 @@ public class ArrayRenderer extends NodeRendererImpl{ return ClassRenderer.calcLabel(descriptor); } + public void setForced(boolean forced) { + myForced = forced; + } + public void buildChildren(Value value, ChildrenBuilder builder, EvaluationContext evaluationContext) { DebuggerManagerThreadImpl.assertIsManagerThread(); List children = new ArrayList(); NodeManagerImpl nodeManager = (NodeManagerImpl)builder.getNodeManager(); NodeDescriptorFactory descriptorFactory = builder.getDescriptorManager(); - builder.initChildrenArrayRenderer(this); + if (!myForced) { + builder.initChildrenArrayRenderer(this); + } ArrayReference array = (ArrayReference)value; if (array.length() > 0) { @@ -182,7 +190,7 @@ public class ArrayRenderer extends NodeRendererImpl{ // children.add(0, nodeManager.createMessageNode(new MessageDescriptor(MORE_ELEMENTS, MessageDescriptor.SPECIAL))); //} - if(END_INDEX < array.length() - 1) { + if(!myForced && END_INDEX < array.length() - 1) { //children.add(nodeManager.createMessageNode(new MessageDescriptor(MORE_ELEMENTS, MessageDescriptor.SPECIAL))); builder.setRemaining(array.length()-END_INDEX); } diff --git a/java/debugger/impl/src/org/jetbrains/java/debugger/JavaDebuggerEditorsProvider.java b/java/debugger/impl/src/org/jetbrains/java/debugger/JavaDebuggerEditorsProvider.java index 8c85e494dea2..c3b19470e6a4 100644 --- a/java/debugger/impl/src/org/jetbrains/java/debugger/JavaDebuggerEditorsProvider.java +++ b/java/debugger/impl/src/org/jetbrains/java/debugger/JavaDebuggerEditorsProvider.java @@ -1,11 +1,9 @@ package org.jetbrains.java.debugger; import com.intellij.debugger.engine.evaluation.CodeFragmentFactory; -import com.intellij.debugger.engine.evaluation.CodeFragmentFactoryContextWrapper; import com.intellij.debugger.engine.evaluation.TextWithImports; import com.intellij.debugger.engine.evaluation.TextWithImportsImpl; import com.intellij.debugger.impl.DebuggerUtilsEx; -import com.intellij.debugger.ui.DebuggerEditorImpl; import com.intellij.ide.highlighter.JavaFileType; import com.intellij.lang.Language; import com.intellij.openapi.editor.Document; @@ -73,16 +71,15 @@ public class JavaDebuggerEditorsProvider extends XDebuggerEditorsProviderBase { boolean isPhysical) { TextWithImports text = TextWithImportsImpl.fromXExpression(expression); if (text != null && context != null) { - CodeFragmentFactory factory = new CodeFragmentFactoryContextWrapper(DebuggerEditorImpl.findAppropriateFactory(text, context)); + CodeFragmentFactory factory = DebuggerUtilsEx.findAppropriateCodeFragmentFactory(text, context); JavaCodeFragment codeFragment = factory.createPresentationCodeFragment(text, context, project); codeFragment.forceResolveScope(GlobalSearchScope.allScope(project)); - if (context != null) { - final PsiClass contextClass = PsiTreeUtil.getNonStrictParentOfType(context, PsiClass.class); - if (contextClass != null) { - final PsiClassType contextType = - JavaPsiFacade.getInstance(codeFragment.getProject()).getElementFactory().createType(contextClass); - codeFragment.setThisType(contextType); - } + + final PsiClass contextClass = PsiTreeUtil.getNonStrictParentOfType(context, PsiClass.class); + if (contextClass != null) { + final PsiClassType contextType = + JavaPsiFacade.getInstance(codeFragment.getProject()).getElementFactory().createType(contextClass); + codeFragment.setThisType(contextType); } return codeFragment; } diff --git a/java/debugger/openapi/src/com/intellij/debugger/SourcePosition.java b/java/debugger/openapi/src/com/intellij/debugger/SourcePosition.java index 45f50d34b922..86bdbde18201 100644 --- a/java/debugger/openapi/src/com/intellij/debugger/SourcePosition.java +++ b/java/debugger/openapi/src/com/intellij/debugger/SourcePosition.java @@ -233,7 +233,7 @@ public abstract class SourcePosition implements Navigatable{ } } - PsiElement element; + PsiElement element = null; int offset = startOffset; while (true) { final CharSequence charsSequence = document.getCharsSequence(); @@ -243,6 +243,8 @@ public abstract class SourcePosition implements Navigatable{ break; } } + if (offset >= charsSequence.length()) break; + element = rootElement.findElementAt(offset); if (element instanceof PsiComment) { diff --git a/java/debugger/openapi/src/com/intellij/debugger/engine/DebugProcess.java b/java/debugger/openapi/src/com/intellij/debugger/engine/DebugProcess.java index 66fea2d7f38c..7a3624b1d16a 100644 --- a/java/debugger/openapi/src/com/intellij/debugger/engine/DebugProcess.java +++ b/java/debugger/openapi/src/com/intellij/debugger/engine/DebugProcess.java @@ -22,6 +22,7 @@ import com.intellij.debugger.engine.jdi.VirtualMachineProxy; import com.intellij.debugger.engine.managerThread.DebuggerManagerThread; import com.intellij.debugger.requests.RequestManager; import com.intellij.execution.ExecutionResult; +import com.intellij.execution.process.ProcessHandler; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Key; import com.intellij.psi.search.GlobalSearchScope; @@ -37,7 +38,7 @@ import java.util.List; public interface DebugProcess { @NonNls String JAVA_STRATUM = "Java"; - T getUserData(Key key); + T getUserData(Key key); void putUserData(Key key, T value); Project getProject(); @@ -76,17 +77,17 @@ public interface DebugProcess { List args) throws EvaluateException; /** - * Is equivalent to invokeInstanceMethod(evaluationContext, classType, method, args, 0) + * Is equivalent to invokeInstanceMethod(evaluationContext, classType, method, args, 0) */ Value invokeMethod(EvaluationContext evaluationContext, ClassType classType, Method method, List args) throws EvaluateException; - Value invokeInstanceMethod(EvaluationContext evaluationContext, - ObjectReference objRef, - Method method, - List args, + Value invokeInstanceMethod(EvaluationContext evaluationContext, + ObjectReference objRef, + Method method, + List args, int invocationOptions) throws EvaluateException; ReferenceType findClass(EvaluationContext evaluationContext, @@ -112,4 +113,8 @@ public interface DebugProcess { */ @NotNull GlobalSearchScope getSearchScope(); + + void printToConsole(String text); + + ProcessHandler getProcessHandler(); } diff --git a/java/debugger/openapi/src/com/intellij/debugger/engine/JSR45PositionManager.java b/java/debugger/openapi/src/com/intellij/debugger/engine/JSR45PositionManager.java index f188409f607d..d33e9ade7e08 100644 --- a/java/debugger/openapi/src/com/intellij/debugger/engine/JSR45PositionManager.java +++ b/java/debugger/openapi/src/com/intellij/debugger/engine/JSR45PositionManager.java @@ -20,7 +20,6 @@ import com.intellij.debugger.NoDataException; import com.intellij.debugger.PositionManager; import com.intellij.debugger.SourcePosition; import com.intellij.debugger.requests.ClassPrepareRequestor; -import com.intellij.execution.process.ProcessOutputTypes; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.fileTypes.FileType; @@ -167,11 +166,11 @@ public abstract class JSR45PositionManager implements PositionManager { } catch(AbsentInformationException ignored) { } - catch(ClassNotPreparedException ignored) { + catch(ClassNotPreparedException ignored) { } catch (InternalError ignored) { - myDebugProcess.getExecutionResult().getProcessHandler().notifyTextAvailable( - DebuggerBundle.message("internal.error.locations.of.line", type.name()), ProcessOutputTypes.SYSTEM); + myDebugProcess.printToConsole( + DebuggerBundle.message("internal.error.locations.of.line", type.name())); } return null; } diff --git a/java/execution/impl/src/com/intellij/execution/actions/JavaRerunFailedTestsAction.java b/java/execution/impl/src/com/intellij/execution/actions/JavaRerunFailedTestsAction.java index 76e7175b87ff..3be2960bfcf7 100644 --- a/java/execution/impl/src/com/intellij/execution/actions/JavaRerunFailedTestsAction.java +++ b/java/execution/impl/src/com/intellij/execution/actions/JavaRerunFailedTestsAction.java @@ -22,6 +22,7 @@ package com.intellij.execution.actions; import com.intellij.execution.testframework.Filter; import com.intellij.execution.testframework.JavaAwareFilter; +import com.intellij.execution.testframework.TestConsoleProperties; import com.intellij.execution.testframework.actions.AbstractRerunFailedTestsAction; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.ComponentContainer; @@ -29,9 +30,10 @@ import com.intellij.psi.search.GlobalSearchScope; import org.jetbrains.annotations.NotNull; public class JavaRerunFailedTestsAction extends AbstractRerunFailedTestsAction { - - protected JavaRerunFailedTestsAction(@NotNull ComponentContainer componentContainer) { + public JavaRerunFailedTestsAction(@NotNull ComponentContainer componentContainer, @NotNull TestConsoleProperties consoleProperties) { super(componentContainer); + + init(consoleProperties); } @NotNull @@ -39,5 +41,4 @@ public class JavaRerunFailedTestsAction extends AbstractRerunFailedTestsAction { protected Filter getFilter(Project project, GlobalSearchScope searchScope) { return super.getFilter(project, searchScope).and(JavaAwareFilter.METHOD(project, searchScope)); } - } diff --git a/java/execution/impl/src/com/intellij/execution/application/ApplicationConfiguration.java b/java/execution/impl/src/com/intellij/execution/application/ApplicationConfiguration.java index 578d5fbba6ea..b736d9f0b127 100644 --- a/java/execution/impl/src/com/intellij/execution/application/ApplicationConfiguration.java +++ b/java/execution/impl/src/com/intellij/execution/application/ApplicationConfiguration.java @@ -21,20 +21,16 @@ import com.intellij.execution.configuration.EnvironmentVariablesComponent; import com.intellij.execution.configurations.*; import com.intellij.execution.filters.TextConsoleBuilderFactory; import com.intellij.execution.junit.RefactoringListeners; -import com.intellij.execution.process.KillableColoredProcessHandler; -import com.intellij.execution.process.OSProcessHandler; import com.intellij.execution.runners.ExecutionEnvironment; import com.intellij.execution.util.JavaParametersUtil; import com.intellij.execution.util.ProgramParametersUtil; import com.intellij.openapi.components.PathMacroManager; -import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.module.Module; import com.intellij.openapi.options.SettingsEditor; import com.intellij.openapi.options.SettingsEditorGroup; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.DefaultJDOMExternalizer; import com.intellij.openapi.util.InvalidDataException; -import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.WriteExternalException; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiElement; @@ -81,7 +77,7 @@ public class ApplicationConfiguration extends ModuleBasedConfiguration(this, env); JavaRunConfigurationModule module = getConfigurationModule(); state.setConsoleBuilder(TextConsoleBuilderFactory.getInstance().createBuilder(getProject(), module.getSearchScope())); return state; @@ -253,14 +249,9 @@ public class ApplicationConfiguration extends ModuleBasedConfiguration extends BaseJavaApplicationCommandLineState { + public JavaApplicationCommandLineState(@NotNull final T configuration, final ExecutionEnvironment environment) { + super(environment, configuration); } @Override @@ -269,37 +260,14 @@ public class ApplicationConfiguration extends ModuleBasedConfiguration extends JavaCommandLineState { + protected final T myConfiguration; + + public BaseJavaApplicationCommandLineState(ExecutionEnvironment environment, @NotNull final T configuration) { + super(environment); + myConfiguration = configuration; + } + + protected void setupJavaParameters(JavaParameters params) throws ExecutionException { + JavaParametersUtil.configureConfiguration(params, myConfiguration); + + for(RunConfigurationExtension ext: Extensions.getExtensions(RunConfigurationExtension.EP_NAME)) { + ext.updateJavaParameters(getConfiguration(), params, getRunnerSettings()); + } + } + + @NotNull + @Override + protected OSProcessHandler startProcess() throws ExecutionException { + OSProcessHandler handler = SystemInfo.isWindows ? super.startProcess() : KillableColoredProcessHandler.create(createCommandLine()); + RunnerSettings runnerSettings = getRunnerSettings(); + JavaRunConfigurationExtensionManager.getInstance().attachExtensionsToProcess(getConfiguration(), handler, runnerSettings); + return handler; + } + + @Override + protected boolean ansiColoringEnabled() { + return true; + } + + protected T getConfiguration() { + return myConfiguration; + } +} diff --git a/java/execution/impl/src/com/intellij/execution/impl/DefaultJavaProgramRunner.java b/java/execution/impl/src/com/intellij/execution/impl/DefaultJavaProgramRunner.java index 626a47509144..0b8f32964d59 100644 --- a/java/execution/impl/src/com/intellij/execution/impl/DefaultJavaProgramRunner.java +++ b/java/execution/impl/src/com/intellij/execution/impl/DefaultJavaProgramRunner.java @@ -34,7 +34,6 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Disposer; import com.intellij.unscramble.AnalyzeStacktraceUtil; import com.intellij.unscramble.ThreadDumpConsoleFactory; import com.intellij.unscramble.ThreadDumpParser; @@ -97,7 +96,6 @@ public class DefaultJavaProgramRunner extends JavaPatchableProgramRunner { onProcessStarted(env.getRunnerSettings(), executionResult); final RunContentBuilder contentBuilder = new RunContentBuilder(executionResult, env); - Disposer.register(env.getProject(), contentBuilder); if (shouldAddDefaultActions) { addDefaultActions(contentBuilder); } @@ -116,7 +114,7 @@ public class DefaultJavaProgramRunner extends JavaPatchableProgramRunner { final ExecutionResult executionResult = contentBuilder.getExecutionResult(); final ExecutionConsole executionConsole = executionResult.getExecutionConsole(); final JComponent consoleComponent = executionConsole != null ? executionConsole.getComponent() : null; - final ControlBreakAction controlBreakAction = new ControlBreakAction(contentBuilder.getProcessHandler()); + final ControlBreakAction controlBreakAction = new ControlBreakAction(executionResult.getProcessHandler()); if (consoleComponent != null) { controlBreakAction.registerCustomShortcutSet(controlBreakAction.getShortcutSet(), consoleComponent); final ProcessHandler processHandler = executionResult.getProcessHandler(); @@ -130,7 +128,7 @@ public class DefaultJavaProgramRunner extends JavaPatchableProgramRunner { }); } contentBuilder.addAction(controlBreakAction); - contentBuilder.addAction(new SoftExitAction(contentBuilder.getProcessHandler())); + contentBuilder.addAction(new SoftExitAction(executionResult.getProcessHandler())); } @@ -143,7 +141,7 @@ public class DefaultJavaProgramRunner extends JavaPatchableProgramRunner { } @Override - public void update(final AnActionEvent event) { + public void update(@NotNull final AnActionEvent event) { final Presentation presentation = event.getPresentation(); if (!isVisible()) { presentation.setVisible(false); @@ -172,7 +170,7 @@ public class DefaultJavaProgramRunner extends JavaPatchableProgramRunner { } @Override - public void actionPerformed(final AnActionEvent e) { + public void actionPerformed(@NotNull final AnActionEvent e) { ProcessProxy proxy = ProcessProxyFactory.getInstance().getAttachedProxy(myProcessHandler); if (proxy != null) { final WiseDumpThreadsListener wiseListener = Boolean.TRUE.equals(Boolean.getBoolean(ourWiseThreadDumpProperty)) ? @@ -254,7 +252,7 @@ public class DefaultJavaProgramRunner extends JavaPatchableProgramRunner { } @Override - public void actionPerformed(final AnActionEvent e) { + public void actionPerformed(@NotNull final AnActionEvent e) { ProcessProxy proxy = ProcessProxyFactory.getInstance().getAttachedProxy(myProcessHandler); if (proxy != null) { proxy.sendStop(); diff --git a/java/execution/impl/src/com/intellij/execution/jar/JarApplicationCommandLineState.java b/java/execution/impl/src/com/intellij/execution/jar/JarApplicationCommandLineState.java new file mode 100644 index 000000000000..c93ab85d4cec --- /dev/null +++ b/java/execution/impl/src/com/intellij/execution/jar/JarApplicationCommandLineState.java @@ -0,0 +1,43 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.execution.jar; + +import com.intellij.execution.ExecutionException; +import com.intellij.execution.application.BaseJavaApplicationCommandLineState; +import com.intellij.execution.configurations.JavaParameters; +import com.intellij.execution.runners.ExecutionEnvironment; +import com.intellij.execution.util.JavaParametersUtil; +import com.intellij.openapi.util.io.FileUtil; +import org.jetbrains.annotations.NotNull; + +/** + * @author nik + */ +public class JarApplicationCommandLineState extends BaseJavaApplicationCommandLineState { + public JarApplicationCommandLineState(@NotNull final JarApplicationConfiguration configuration, final ExecutionEnvironment environment) { + super(environment, configuration); + } + + @Override + protected JavaParameters createJavaParameters() throws ExecutionException { + final JavaParameters params = new JavaParameters(); + final String jreHome = myConfiguration.isAlternativeJrePathEnabled() ? myConfiguration.getAlternativeJrePath() : null; + params.setJdk(JavaParametersUtil.createProjectJdk(myConfiguration.getProject(), jreHome)); + setupJavaParameters(params); + params.setJarPath(FileUtil.toSystemDependentName(myConfiguration.getJarPath())); + return params; + } +} diff --git a/java/execution/impl/src/com/intellij/execution/jar/JarApplicationConfigurable.form b/java/execution/impl/src/com/intellij/execution/jar/JarApplicationConfigurable.form new file mode 100644 index 000000000000..db191dde6fa3 --- /dev/null +++ b/java/execution/impl/src/com/intellij/execution/jar/JarApplicationConfigurable.form @@ -0,0 +1,49 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/java/execution/impl/src/com/intellij/execution/jar/JarApplicationConfigurable.java b/java/execution/impl/src/com/intellij/execution/jar/JarApplicationConfigurable.java new file mode 100644 index 000000000000..03758cd031cb --- /dev/null +++ b/java/execution/impl/src/com/intellij/execution/jar/JarApplicationConfigurable.java @@ -0,0 +1,93 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.execution.jar; + +import com.intellij.application.options.ModulesComboBox; +import com.intellij.execution.ui.AlternativeJREPanel; +import com.intellij.execution.ui.CommonJavaParametersPanel; +import com.intellij.openapi.fileChooser.FileChooserDescriptor; +import com.intellij.openapi.options.ConfigurationException; +import com.intellij.openapi.options.SettingsEditor; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.LabeledComponent; +import com.intellij.openapi.ui.TextFieldWithBrowseButton; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.ui.PanelWithAnchor; +import com.intellij.util.ui.UIUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; + +public class JarApplicationConfigurable extends SettingsEditor implements PanelWithAnchor { + private CommonJavaParametersPanel myCommonProgramParameters; + private LabeledComponent myJarPathComponent; + private LabeledComponent myModuleComponent; + private JPanel myWholePanel; + + private AlternativeJREPanel myAlternativeJREPanel; + private final Project myProject; + private JComponent myAnchor; + + public JarApplicationConfigurable(final Project project) { + myProject = project; + myAnchor = UIUtil.mergeComponentsWithAnchor(myJarPathComponent, myCommonProgramParameters, myAlternativeJREPanel); + ModulesComboBox modulesComboBox = myModuleComponent.getComponent(); + modulesComboBox.allowEmptySelection(""); + modulesComboBox.fillModules(project); + } + + public void applyEditorTo(final JarApplicationConfiguration configuration) throws ConfigurationException { + myCommonProgramParameters.applyTo(configuration); + configuration.setAlternativeJrePath(myAlternativeJREPanel.getPath()); + configuration.setAlternativeJrePathEnabled(myAlternativeJREPanel.isPathEnabled()); + configuration.setJarPath(FileUtil.toSystemIndependentName(myJarPathComponent.getComponent().getText())); + configuration.setModule(myModuleComponent.getComponent().getSelectedModule()); + } + + public void resetEditorFrom(final JarApplicationConfiguration configuration) { + myCommonProgramParameters.reset(configuration); + myJarPathComponent.getComponent().setText(FileUtil.toSystemDependentName(configuration.getJarPath())); + myAlternativeJREPanel.init(configuration.getAlternativeJrePath(), configuration.isAlternativeJrePathEnabled()); + myModuleComponent.getComponent().setSelectedModule(configuration.getModule()); + } + + @NotNull + public JComponent createEditor() { + return myWholePanel; + } + + private void createUIComponents() { + myJarPathComponent = new LabeledComponent(); + TextFieldWithBrowseButton textFieldWithBrowseButton = new TextFieldWithBrowseButton(); + textFieldWithBrowseButton.addBrowseFolderListener("Choose JAR File", null, myProject, + new FileChooserDescriptor(false, false, true, true, false, false)); + myJarPathComponent.setComponent(textFieldWithBrowseButton); + } + + @Override + public JComponent getAnchor() { + return myAnchor; + } + + @Override + public void setAnchor(@Nullable JComponent anchor) { + myAnchor = anchor; + myCommonProgramParameters.setAnchor(anchor); + myAlternativeJREPanel.setAnchor(anchor); + myJarPathComponent.setAnchor(anchor); + } +} diff --git a/java/execution/impl/src/com/intellij/execution/jar/JarApplicationConfiguration.java b/java/execution/impl/src/com/intellij/execution/jar/JarApplicationConfiguration.java new file mode 100644 index 000000000000..c76b5873389a --- /dev/null +++ b/java/execution/impl/src/com/intellij/execution/jar/JarApplicationConfiguration.java @@ -0,0 +1,223 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.execution.jar; + +import com.intellij.diagnostic.logging.LogConfigurationPanel; +import com.intellij.execution.*; +import com.intellij.execution.configuration.EnvironmentVariablesComponent; +import com.intellij.execution.configurations.*; +import com.intellij.execution.runners.ExecutionEnvironment; +import com.intellij.execution.util.JavaParametersUtil; +import com.intellij.execution.util.ProgramParametersUtil; +import com.intellij.openapi.components.PathMacroManager; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.options.SettingsEditor; +import com.intellij.openapi.options.SettingsEditorGroup; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.InvalidDataException; +import com.intellij.openapi.util.WriteExternalException; +import com.intellij.util.xmlb.SkipDefaultValuesSerializationFilters; +import com.intellij.util.xmlb.XmlSerializer; +import org.jdom.Element; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * @author nik + */ +public class JarApplicationConfiguration extends LocatableConfigurationBase implements CommonJavaRunConfigurationParameters, SearchScopeProvidingRunProfile { + private static final SkipDefaultValuesSerializationFilters SERIALIZATION_FILTERS = new SkipDefaultValuesSerializationFilters(); + private JarApplicationConfigurationBean myBean = new JarApplicationConfigurationBean(); + private Map myEnvs = new LinkedHashMap(); + private JavaRunConfigurationModule myConfigurationModule; + + public JarApplicationConfiguration(Project project, ConfigurationFactory factory, String name) { + super(project, factory, name); + myConfigurationModule = new JavaRunConfigurationModule(project, true); + } + + @NotNull + @Override + public SettingsEditor getConfigurationEditor() { + SettingsEditorGroup group = new SettingsEditorGroup(); + group.addEditor(ExecutionBundle.message("run.configuration.configuration.tab.title"), new JarApplicationConfigurable(getProject())); + JavaRunConfigurationExtensionManager.getInstance().appendEditors(this, group); + group.addEditor(ExecutionBundle.message("logs.tab.title"), new LogConfigurationPanel()); + return group; + } + + @Override + public void readExternal(Element element) throws InvalidDataException { + PathMacroManager.getInstance(getProject()).expandPaths(element); + super.readExternal(element); + JavaRunConfigurationExtensionManager.getInstance().readExternal(this, element); + XmlSerializer.deserializeInto(myBean, element); + EnvironmentVariablesComponent.readExternal(element, getEnvs()); + myConfigurationModule.readExternal(element); + } + + public void setModule(Module module) { + myConfigurationModule.setModule(module); + } + + public Module getModule() { + return myConfigurationModule.getModule(); + } + + @Override + public void writeExternal(Element element) throws WriteExternalException { + super.writeExternal(element); + JavaRunConfigurationExtensionManager.getInstance().writeExternal(this, element); + XmlSerializer.serializeInto(myBean, element, SERIALIZATION_FILTERS); + EnvironmentVariablesComponent.writeExternal(element, getEnvs()); + PathMacroManager.getInstance(getProject()).collapsePathsRecursively(element); + if (myConfigurationModule.getModule() != null) { + myConfigurationModule.writeExternal(element); + } + } + + @Override + public void checkConfiguration() throws RuntimeConfigurationException { + JavaParametersUtil.checkAlternativeJRE(this); + ProgramParametersUtil.checkWorkingDirectoryExist(this, getProject(), null); + File jarFile = new File(getJarPath()); + if (!jarFile.exists()) { + throw new RuntimeConfigurationWarning("JAR file '" + jarFile.getAbsolutePath() + "' doesn't exist"); + } + JavaRunConfigurationExtensionManager.checkConfigurationIsValid(this); + } + + @NotNull + @Override + public Module[] getModules() { + Module module = myConfigurationModule.getModule(); + return module != null ? new Module[] {module}: Module.EMPTY_ARRAY; + } + + @Nullable + @Override + public RunProfileState getState(@NotNull Executor executor, @NotNull ExecutionEnvironment environment) throws ExecutionException { + return new JarApplicationCommandLineState(this, environment); + } + + public String getJarPath() { + return myBean.JAR_PATH; + } + + public void setJarPath(String jarPath) { + myBean.JAR_PATH = jarPath; + } + + @Override + public void setVMParameters(String value) { + myBean.VM_PARAMETERS = value; + } + + @Override + public String getVMParameters() { + return myBean.VM_PARAMETERS; + } + + @Override + public boolean isAlternativeJrePathEnabled() { + return myBean.ALTERNATIVE_JRE_PATH_ENABLED; + } + + @Override + public void setAlternativeJrePathEnabled(boolean enabled) { + myBean.ALTERNATIVE_JRE_PATH_ENABLED = enabled; + } + + @Override + public String getAlternativeJrePath() { + return myBean.ALTERNATIVE_JRE_PATH; + } + + @Override + public void setAlternativeJrePath(String path) { + myBean.ALTERNATIVE_JRE_PATH = path; + } + + @Nullable + @Override + public String getRunClass() { + return null; + } + + @Nullable + @Override + public String getPackage() { + return null; + } + + @Override + public void setProgramParameters(@Nullable String value) { + myBean.PROGRAM_PARAMETERS = value; + } + + @Nullable + @Override + public String getProgramParameters() { + return myBean.PROGRAM_PARAMETERS; + } + + @Override + public void setWorkingDirectory(@Nullable String value) { + myBean.WORKING_DIRECTORY = value; + } + + @Nullable + @Override + public String getWorkingDirectory() { + return myBean.WORKING_DIRECTORY; + } + + @Override + public void setEnvs(@NotNull Map envs) { + myEnvs.clear(); + myEnvs.putAll(envs); + } + + @NotNull + @Override + public Map getEnvs() { + return myEnvs; + } + + @Override + public void setPassParentEnvs(boolean passParentEnvs) { + myBean.PASS_PARENT_ENVS = passParentEnvs; + } + + @Override + public boolean isPassParentEnvs() { + return myBean.PASS_PARENT_ENVS; + } + + private static class JarApplicationConfigurationBean { + public String JAR_PATH = ""; + public String VM_PARAMETERS = ""; + public String PROGRAM_PARAMETERS = ""; + public String WORKING_DIRECTORY = ""; + public boolean ALTERNATIVE_JRE_PATH_ENABLED; + public String ALTERNATIVE_JRE_PATH = ""; + public boolean PASS_PARENT_ENVS = true; + } +} diff --git a/java/execution/impl/src/com/intellij/execution/jar/JarApplicationConfigurationProducer.java b/java/execution/impl/src/com/intellij/execution/jar/JarApplicationConfigurationProducer.java new file mode 100644 index 000000000000..b2b9f8fef81c --- /dev/null +++ b/java/execution/impl/src/com/intellij/execution/jar/JarApplicationConfigurationProducer.java @@ -0,0 +1,63 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.execution.jar; + +import com.intellij.execution.Location; +import com.intellij.execution.actions.ConfigurationContext; +import com.intellij.execution.actions.RunConfigurationProducer; +import com.intellij.openapi.util.Ref; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.io.FileUtilRt; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiElement; +import org.jetbrains.annotations.Nullable; + +/** + * @author nik + */ +public class JarApplicationConfigurationProducer extends RunConfigurationProducer { + public JarApplicationConfigurationProducer() { + super(JarApplicationConfigurationType.getInstance()); + } + + @Override + protected boolean setupConfigurationFromContext(JarApplicationConfiguration configuration, + ConfigurationContext context, + Ref sourceElement) { + VirtualFile file = getJarFileFromContext(context); + if (file != null) { + configuration.setName(file.getName()); + configuration.setJarPath(file.getPath()); + return true; + } + return false; + } + + @Nullable + private static VirtualFile getJarFileFromContext(ConfigurationContext context) { + Location location = context.getLocation(); + if (location == null) return null; + + VirtualFile file = location.getVirtualFile(); + return file != null && FileUtilRt.extensionEquals(file.getName(), "jar") ? file : null; + } + + @Override + public boolean isConfigurationFromContext(JarApplicationConfiguration configuration, ConfigurationContext context) { + VirtualFile file = getJarFileFromContext(context); + return file != null && FileUtil.pathsEqual(file.getPath(), configuration.getJarPath()); + } +} diff --git a/java/execution/impl/src/com/intellij/execution/jar/JarApplicationConfigurationType.java b/java/execution/impl/src/com/intellij/execution/jar/JarApplicationConfigurationType.java new file mode 100644 index 000000000000..8c7fdebc8c52 --- /dev/null +++ b/java/execution/impl/src/com/intellij/execution/jar/JarApplicationConfigurationType.java @@ -0,0 +1,54 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.execution.jar; + +import com.intellij.execution.ExecutionBundle; +import com.intellij.execution.configuration.ConfigurationFactoryEx; +import com.intellij.execution.configurations.ConfigurationType; +import com.intellij.execution.configurations.ConfigurationTypeBase; +import com.intellij.execution.configurations.ConfigurationTypeUtil; +import com.intellij.execution.configurations.RunConfiguration; +import com.intellij.icons.AllIcons; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.text.StringUtil; +import org.jetbrains.annotations.NotNull; + +public class JarApplicationConfigurationType extends ConfigurationTypeBase implements ConfigurationType { + @NotNull + public static JarApplicationConfigurationType getInstance() { + return ConfigurationTypeUtil.findConfigurationType(JarApplicationConfigurationType.class); + } + + public JarApplicationConfigurationType() { + super("JarApplication", ExecutionBundle.message("jar.application.configuration.name"), + ExecutionBundle.message("jar.application.configuration.description"), AllIcons.FileTypes.Archive); + addFactory(new ConfigurationFactoryEx(this) { + @Override + public void onNewConfigurationCreated(@NotNull RunConfiguration configuration) { + JarApplicationConfiguration jarApplicationConfiguration = (JarApplicationConfiguration)configuration; + if (StringUtil.isEmpty(jarApplicationConfiguration.getWorkingDirectory())) { + String baseDir = FileUtil.toSystemIndependentName(StringUtil.notNullize(configuration.getProject().getBasePath())); + jarApplicationConfiguration.setWorkingDirectory(baseDir); + } + } + + public RunConfiguration createTemplateConfiguration(Project project) { + return new JarApplicationConfiguration(project, this, ""); + } + }); + } +} diff --git a/java/execution/impl/src/com/intellij/execution/jar/JarApplicationDebuggerRunner.java b/java/execution/impl/src/com/intellij/execution/jar/JarApplicationDebuggerRunner.java new file mode 100644 index 000000000000..dc5bfbfb6129 --- /dev/null +++ b/java/execution/impl/src/com/intellij/execution/jar/JarApplicationDebuggerRunner.java @@ -0,0 +1,37 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.execution.jar; + +import com.intellij.debugger.impl.GenericDebuggerRunner; +import com.intellij.execution.configurations.RunProfile; +import com.intellij.execution.executors.DefaultDebugExecutor; +import org.jetbrains.annotations.NotNull; + +/** + * @author nik + */ +public class JarApplicationDebuggerRunner extends GenericDebuggerRunner { + @Override + public boolean canRun(@NotNull String executorId, @NotNull RunProfile profile) { + return DefaultDebugExecutor.EXECUTOR_ID.equals(executorId) && profile instanceof JarApplicationConfiguration; + } + + @NotNull + @Override + public String getRunnerId() { + return "JarDebug"; + } +} diff --git a/java/execution/impl/src/com/intellij/execution/junit/InheritorChooser.java b/java/execution/impl/src/com/intellij/execution/junit/InheritorChooser.java index d318ad47bb70..fc0e65f9742b 100644 --- a/java/execution/impl/src/com/intellij/execution/junit/InheritorChooser.java +++ b/java/execution/impl/src/com/intellij/execution/junit/InheritorChooser.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,6 +27,7 @@ import com.intellij.openapi.fileEditor.TextEditor; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.ui.popup.JBPopupFactory; import com.intellij.openapi.util.Condition; +import com.intellij.openapi.util.Conditions; import com.intellij.psi.*; import com.intellij.psi.search.searches.ClassInheritorsSearch; import com.intellij.psi.util.PsiClassUtil; @@ -59,7 +60,7 @@ public class InheritorChooser { final Runnable performRunnable, final PsiMethod psiMethod, final PsiClass containingClass) { - return runMethodInAbstractClass(context, performRunnable, psiMethod, containingClass, Condition.TRUE); + return runMethodInAbstractClass(context, performRunnable, psiMethod, containingClass, Conditions.alwaysTrue()); } public boolean runMethodInAbstractClass(final ConfigurationContext context, @@ -114,6 +115,7 @@ public class InheritorChooser { } } } + final int numberOfInheritors = classes.size(); final PsiClassListCellRenderer renderer = new PsiClassListCellRenderer() { @Override protected boolean customizeNonPsiElementLeftRenderer(ColoredListCellRenderer renderer, @@ -123,7 +125,7 @@ public class InheritorChooser { boolean selected, boolean hasFocus) { if (value == null) { - renderer.append("All"); + renderer.append("All (" + numberOfInheritors + ")"); return true; } return super.customizeNonPsiElementLeftRenderer(renderer, list, value, index, selected, hasFocus); diff --git a/java/execution/impl/src/com/intellij/execution/runners/ProcessProxyFactoryImpl.java b/java/execution/impl/src/com/intellij/execution/runners/ProcessProxyFactoryImpl.java index 6af48770e78d..4b20e2189202 100644 --- a/java/execution/impl/src/com/intellij/execution/runners/ProcessProxyFactoryImpl.java +++ b/java/execution/impl/src/com/intellij/execution/runners/ProcessProxyFactoryImpl.java @@ -30,15 +30,16 @@ import java.io.File; public class ProcessProxyFactoryImpl extends ProcessProxyFactory { public ProcessProxy createCommandLineProxy(final JavaCommandLine javaCmdLine) throws ExecutionException { ProcessProxyImpl proxy = null; - if (ProcessProxyImpl.useLauncher()) { + final JavaParameters javaParameters = javaCmdLine.getJavaParameters(); + String mainClass = javaParameters.getMainClass(); + if (ProcessProxyImpl.useLauncher() && mainClass != null) { try { proxy = new ProcessProxyImpl(); - final JavaParameters javaParameters = javaCmdLine.getJavaParameters(); JavaSdkUtil.addRtJar(javaParameters.getClassPath()); final ParametersList vmParametersList = javaParameters.getVMParametersList(); vmParametersList.defineProperty(ProcessProxyImpl.PROPERTY_PORT_NUMBER, String.valueOf(proxy.getPortNumber())); vmParametersList.defineProperty(ProcessProxyImpl.PROPERTY_BINPATH, PathManager.getBinPath()); - javaParameters.getProgramParametersList().prepend(javaParameters.getMainClass()); + javaParameters.getProgramParametersList().prepend(mainClass); javaParameters.setMainClass(ProcessProxyImpl.LAUNCH_MAIN_CLASS); } catch (ProcessProxyImpl.NoMoreSocketsException e) { diff --git a/java/execution/impl/src/com/intellij/execution/util/JavaParametersUtil.java b/java/execution/impl/src/com/intellij/execution/util/JavaParametersUtil.java index 18aead7fe7f1..4d948dae8a2d 100644 --- a/java/execution/impl/src/com/intellij/execution/util/JavaParametersUtil.java +++ b/java/execution/impl/src/com/intellij/execution/util/JavaParametersUtil.java @@ -27,7 +27,6 @@ import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleUtilCore; import com.intellij.openapi.project.Project; import com.intellij.openapi.projectRoots.JavaSdk; -import com.intellij.openapi.projectRoots.JavaSdkType; import com.intellij.openapi.projectRoots.ProjectJdkTable; import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.projectRoots.ex.PathUtilEx; @@ -118,7 +117,7 @@ public class JavaParametersUtil { return jreHome == null ? JavaParameters.getModuleJdk(module) : createAlternativeJdk(jreHome); } - private static Sdk createProjectJdk(final Project project, final String jreHome) throws CantRunException { + public static Sdk createProjectJdk(final Project project, final String jreHome) throws CantRunException { return jreHome == null ? createProjectJdk(project) : createAlternativeJdk(jreHome); } diff --git a/java/execution/openapi/src/com/intellij/execution/configurations/CommandLineBuilder.java b/java/execution/openapi/src/com/intellij/execution/configurations/CommandLineBuilder.java index ac081d04b5c7..8ae0d9bae3e3 100644 --- a/java/execution/openapi/src/com/intellij/execution/configurations/CommandLineBuilder.java +++ b/java/execution/openapi/src/com/intellij/execution/configurations/CommandLineBuilder.java @@ -79,7 +79,7 @@ public class CommandLineBuilder { if (exePath == null) { throw new CantRunException(ExecutionBundle.message("run.configuration.cannot.find.vm.executable")); } - if (javaParameters.getMainClass() == null) { + if (javaParameters.getMainClass() == null && javaParameters.getJarPath() == null) { throw new CantRunException(ExecutionBundle.message("main.class.is.not.specified.error.message")); } diff --git a/java/idea-ui/src/com/intellij/facet/impl/ui/libraries/LibraryOptionsPanel.form b/java/idea-ui/src/com/intellij/facet/impl/ui/libraries/LibraryOptionsPanel.form index a738a96aa50e..9e5dd85f6936 100644 --- a/java/idea-ui/src/com/intellij/facet/impl/ui/libraries/LibraryOptionsPanel.form +++ b/java/idea-ui/src/com/intellij/facet/impl/ui/libraries/LibraryOptionsPanel.form @@ -139,7 +139,7 @@ - + diff --git a/java/idea-ui/src/com/intellij/facet/impl/ui/libraries/LibraryOptionsPanel.java b/java/idea-ui/src/com/intellij/facet/impl/ui/libraries/LibraryOptionsPanel.java index 3bb96adb1141..a890bdad527d 100644 --- a/java/idea-ui/src/com/intellij/facet/impl/ui/libraries/LibraryOptionsPanel.java +++ b/java/idea-ui/src/com/intellij/facet/impl/ui/libraries/LibraryOptionsPanel.java @@ -490,7 +490,7 @@ public class LibraryOptionsPanel implements Disposable { else { path = PathUtil.getFileName(downloadPath); } - return MessageFormat.format("{0} {0, choice, 1#jar|2#jars} will be downloaded into {1} directory
" + + return MessageFormat.format("{0} {0, choice, 1#JAR|2#JARs} will be downloaded into {1} directory
" + "{2} library {3} will be created", downloadSettings.getSelectedDownloads().size(), path, diff --git a/java/idea-ui/src/com/intellij/ide/RecentProjectsManager.java b/java/idea-ui/src/com/intellij/ide/RecentProjectsManager.java index 6ac07343357a..e2f93bf16771 100644 --- a/java/idea-ui/src/com/intellij/ide/RecentProjectsManager.java +++ b/java/idea-ui/src/com/intellij/ide/RecentProjectsManager.java @@ -16,31 +16,30 @@ package com.intellij.ide; import com.intellij.ide.impl.ProjectUtil; -import com.intellij.openapi.components.RoamingType; -import com.intellij.openapi.components.State; -import com.intellij.openapi.components.Storage; -import com.intellij.openapi.components.StoragePathMacros; +import com.intellij.openapi.components.*; import com.intellij.openapi.project.Project; import com.intellij.util.messages.MessageBus; import org.jetbrains.annotations.NotNull; @State( name = "RecentProjectsManager", - roamingType = RoamingType.DISABLED, storages = { - @Storage( - file = StoragePathMacros.APP_CONFIG + "/other.xml" - )} + @Storage(file = StoragePathMacros.APP_CONFIG + "/other.xml", roamingType = RoamingType.DISABLED), + @Storage(file = StoragePathMacros.APP_CONFIG + "/recentProjects.xml", roamingType = RoamingType.DISABLED) + }, + storageChooser = LastStorageChooserForWrite.class ) public class RecentProjectsManager extends RecentProjectsManagerBase { public RecentProjectsManager(MessageBus messageBus) { super(messageBus); } + @Override protected String getProjectPath(@NotNull Project project) { return project.getPresentableUrl(); } + @Override protected void doOpenProject(@NotNull String projectPath, Project projectToClose, boolean forceOpenInNewFrame) { ProjectUtil.openProject(projectPath, projectToClose, forceOpenInNewFrame); } diff --git a/java/idea-ui/src/com/intellij/ide/actions/ImportModuleAction.java b/java/idea-ui/src/com/intellij/ide/actions/ImportModuleAction.java index 3d497b829d50..3f087a998304 100644 --- a/java/idea-ui/src/com/intellij/ide/actions/ImportModuleAction.java +++ b/java/idea-ui/src/com/intellij/ide/actions/ImportModuleAction.java @@ -116,7 +116,7 @@ public class ImportModuleAction extends AnAction { if (lastLocation != null) { toSelect = LocalFileSystem.getInstance().refreshAndFindFileByPath(lastLocation); } - VirtualFile[] files = chooser.choose(toSelect, project); + VirtualFile[] files = chooser.choose(project, toSelect); if (files.length == 0) { return null; } diff --git a/java/idea-ui/src/com/intellij/ide/palette/impl/PaletteComponentList.java b/java/idea-ui/src/com/intellij/ide/palette/impl/PaletteComponentList.java deleted file mode 100644 index ea4f6de53401..000000000000 --- a/java/idea-ui/src/com/intellij/ide/palette/impl/PaletteComponentList.java +++ /dev/null @@ -1,401 +0,0 @@ -/* - * Copyright 2000-2012 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.intellij.ide.palette.impl; - -import com.intellij.ide.dnd.*; -import com.intellij.ide.palette.PaletteGroup; -import com.intellij.ide.palette.PaletteItem; -import com.intellij.openapi.actionSystem.ActionGroup; -import com.intellij.openapi.actionSystem.ActionManager; -import com.intellij.openapi.actionSystem.ActionPlaces; -import com.intellij.openapi.actionSystem.ActionPopupMenu; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Pair; -import com.intellij.ui.ColoredListCellRenderer; -import com.intellij.ui.PopupHandler; -import com.intellij.ui.components.JBList; -import com.intellij.util.ui.PlatformColors; -import com.intellij.util.ui.UIUtil; -import org.jetbrains.annotations.NonNls; -import org.jetbrains.annotations.Nullable; - -import javax.swing.*; -import javax.swing.plaf.basic.BasicListUI; -import java.awt.*; -import java.awt.event.*; - -/** - * @author yole - */ -public class PaletteComponentList extends JBList { - private final Project myProject; - private final PaletteGroup myGroup; - private int myHoverIndex = -1; - private int myBeforeClickSelectedRow = -1; - private int myDropTargetIndex = -1; - private boolean myNeedClearSelection = false; - - public PaletteComponentList(Project project, PaletteGroup group) { - myProject = project; - myGroup = group; - setModel(new AbstractListModel() { - public int getSize() { - return myGroup.getItems().length; - } - - public Object getElementAt(int index) { - return myGroup.getItems() [index]; - } - }); - - addMouseListener(new MouseAdapter() { - @Override public void mouseEntered(MouseEvent e) { - setHoverIndex(locationToIndex(e.getPoint())); - } - - @Override public void mouseExited(MouseEvent e) { - setHoverIndex(-1); - } - - @Override public void mousePressed(MouseEvent e) { - myNeedClearSelection = (SwingUtilities.isLeftMouseButton(e) && - myBeforeClickSelectedRow >= 0 && - locationToIndex(e.getPoint()) == myBeforeClickSelectedRow && - !UIUtil.isControlKeyDown(e) && !e.isShiftDown()); - } - - @Override public void mouseReleased(MouseEvent e) { - if (SwingUtilities.isLeftMouseButton(e) && - myBeforeClickSelectedRow >= 0 && - locationToIndex(e.getPoint()) == myBeforeClickSelectedRow && - !UIUtil.isControlKeyDown(e) && !e.isShiftDown() && myNeedClearSelection) { - clearSelection(); - } - } - }); - - addMouseListener(new PopupHandler() { - public void invokePopup(final Component comp, final int x, final int y) { - requestFocusInWindow(); - SwingUtilities.invokeLater(new Runnable() { - public void run() { - int index = locationToIndex(new Point(x, y)); - PaletteItem[] items = myGroup.getItems(); - if (index >= 0 && index < items.length) { - if (getSelectedIndex() != index) { - addSelectionInterval(index, index); - } - PaletteItem item = items [index]; - ActionGroup group = item.getPopupActionGroup(); - if (group != null) { - ActionPopupMenu popupMenu = ActionManager.getInstance().createActionPopupMenu(ActionPlaces.UNKNOWN, group); - popupMenu.getComponent().show(comp, x, y); - } - } - } - }); - } - }); - - addMouseMotionListener(new MouseMotionAdapter() { - public void mouseMoved(MouseEvent e) { - setHoverIndex(locationToIndex(e.getPoint())); - } - }); - - addKeyListener(new KeyListener() { - public void keyPressed(KeyEvent e) { - PaletteManager.getInstance(myProject).notifyKeyEvent(e); - } - - public void keyReleased(KeyEvent e) { - PaletteManager.getInstance(myProject).notifyKeyEvent(e); - } - - public void keyTyped(KeyEvent e) { - PaletteManager.getInstance(myProject).notifyKeyEvent(e); - } - }); - - setCellRenderer(new ComponentCellRenderer()); - - setVisibleRowCount(0); - setLayoutOrientation(HORIZONTAL_WRAP); - setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - - final DnDManager dndManager = DnDManager.getInstance(); - dndManager.registerSource(new MyDnDSource(), this); - dndManager.registerTarget(new MyDnDTarget(), this); - - initActions(); - } - - private void setHoverIndex(final int index) { - if (index != myHoverIndex) { - if (myHoverIndex >= 0) repaint(getCellBounds(myHoverIndex, myHoverIndex)); - myHoverIndex = index; - if (myHoverIndex >= 0) repaint(getCellBounds(myHoverIndex, myHoverIndex)); - } - } - - private void setDropTargetIndex(final int index) { - if (index != myDropTargetIndex) { - myDropTargetIndex = index; - repaint(); - } - } - - @Override public void updateUI() { - setUI(new ComponentListUI()); - invalidate(); - } - - private void initActions() { - @NonNls ActionMap map = getActionMap(); - map.put( "selectPreviousRow", new MoveFocusAction( map.get( "selectPreviousRow" ), false ) ); - map.put( "selectNextRow", new MoveFocusAction( map.get( "selectNextRow" ), true ) ); - map.put( "selectPreviousColumn", new MoveFocusAction( new ChangeColumnAction( map.get( "selectPreviousColumn" ), false ), false ) ); - map.put( "selectNextColumn", new MoveFocusAction( new ChangeColumnAction( map.get( "selectNextColumn" ), true ), true ) ); - } - - Integer myTempWidth; - - public int getWidth () { - return (myTempWidth == null) ? super.getWidth () : myTempWidth.intValue (); - } - - public int getPreferredHeight(final int width) { - myTempWidth = width; - try { - return getUI().getPreferredSize(this).height; - } - finally { - myTempWidth = null; - } - } - - public void takeFocusFrom(PaletteGroupHeader paletteGroup, int indexToSelect) { - if (indexToSelect == -1) { - //this is not 'our' CategoryButton so we'll assume it's the one below this category list - indexToSelect = getModel().getSize() - 1; - } - else if (getModel().getSize() == 0) { - indexToSelect = -1; - } - requestFocus(); - setSelectedIndex(indexToSelect); - if (indexToSelect >= 0) { - ensureIndexIsVisible(indexToSelect); - } - } - - @Override protected void paintComponent(Graphics g) { - super.paintComponent(g); - if (myDropTargetIndex >= 0) { - int dropLineY; - Rectangle rc; - if (myDropTargetIndex == myGroup.getItems().length) { - rc = getCellBounds(myDropTargetIndex-1, myDropTargetIndex-1); - dropLineY = (int)rc.getMaxY()-1; - } - else { - rc = getCellBounds(myDropTargetIndex, myDropTargetIndex); - dropLineY = rc.y; - } - Graphics2D g2d = (Graphics2D) g; - g2d.setColor(PlatformColors.BLUE); - g2d.setStroke(new BasicStroke(2.0f)); - g2d.drawLine(rc.x, dropLineY, rc.x+rc.width, dropLineY); - g2d.drawLine(rc.x, dropLineY-2, rc.x, dropLineY+2); - g2d.drawLine(rc.x+rc.width, dropLineY-2, rc.x+rc.width, dropLineY+2); - } - } - - class ComponentListUI extends BasicListUI { - private ComponentListListener myListener; - - @Override protected void updateLayoutState() { - super.updateLayoutState(); - - if (list.getLayoutOrientation() == JList.HORIZONTAL_WRAP) { - Insets insets = list.getInsets(); - int listWidth = list.getWidth() - (insets.left + insets.right); - if (listWidth >= cellWidth) { - int columnCount = listWidth / cellWidth; - cellWidth = (columnCount == 0) ? 1 : listWidth / columnCount; - } - } - } - - @Override protected void installListeners() { - myListener = new ComponentListListener(); - addMouseListener(myListener); - super.installListeners(); - } - - @Override - protected void uninstallListeners() { - if (myListener != null) { - removeMouseListener(myListener); - } - super.uninstallListeners(); - } - - private class ComponentListListener extends MouseAdapter { - @Override public void mousePressed(MouseEvent e) { - myBeforeClickSelectedRow = list.getSelectedIndex(); - } - } - } - - private static class ComponentCellRenderer extends ColoredListCellRenderer { - protected void customizeCellRenderer(JList list, Object value, int index, boolean selected, boolean hasFocus) { - PaletteItem paletteItem = (PaletteItem) value; - clear(); - paletteItem.customizeCellRenderer(this, selected, hasFocus); - } - } - - private class MoveFocusAction extends AbstractAction { - private final Action defaultAction; - private final boolean focusNext; - - public MoveFocusAction(Action defaultAction, boolean focusNext) { - this.defaultAction = defaultAction; - this.focusNext = focusNext; - } - - public void actionPerformed(ActionEvent e) { - int selIndexBefore = getSelectedIndex(); - defaultAction.actionPerformed(e); - int selIndexCurrent = getSelectedIndex(); - if (selIndexBefore != selIndexCurrent) return; - - if (focusNext && 0 == selIndexCurrent) return; - - KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager(); - Container container = kfm.getCurrentFocusCycleRoot(); - FocusTraversalPolicy policy = container.getFocusTraversalPolicy(); - if (null == policy) policy = kfm.getDefaultFocusTraversalPolicy(); - Component next = focusNext - ? policy.getComponentAfter(container, PaletteComponentList.this) - : policy.getComponentBefore(container, PaletteComponentList.this); - if (null != next && next instanceof PaletteGroupHeader) { - clearSelection(); - next.requestFocus(); - ((PaletteGroupHeader)next).scrollRectToVisible(next.getBounds()); - } - } - } - - private class ChangeColumnAction extends AbstractAction { - private final Action defaultAction; - private final boolean selectNext; - - public ChangeColumnAction(Action defaultAction, boolean selectNext) { - this.defaultAction = defaultAction; - this.selectNext = selectNext; - } - - public void actionPerformed(ActionEvent e) { - int selIndexBefore = getSelectedIndex(); - defaultAction.actionPerformed(e); - int selIndexCurrent = getSelectedIndex(); - if ((selectNext && selIndexBefore < selIndexCurrent) || (!selectNext && selIndexBefore > selIndexCurrent)) return; - - if (selectNext) { - if (selIndexCurrent == selIndexBefore + 1) selIndexCurrent++; - if (selIndexCurrent < getModel().getSize() - 1) { - setSelectedIndex(selIndexCurrent + 1); - scrollRectToVisible(getCellBounds(selIndexCurrent + 1, selIndexCurrent + 1)); - } - } - else { - if (selIndexCurrent > 0) { - setSelectedIndex(selIndexCurrent - 1); - scrollRectToVisible(getCellBounds(selIndexCurrent - 1, selIndexCurrent - 1)); - } - } - } - } - - private class MyDnDTarget implements DnDTarget { - - public boolean update(DnDEvent aEvent) { - setHoverIndex(-1); - if (aEvent.getAttachedObject() instanceof PaletteItem) { - setDropTargetIndex(locationToTargetIndex(aEvent.getPoint())); - aEvent.setDropPossible(true); - } - else { - setDropTargetIndex(-1); - aEvent.setDropPossible(false); - } - return false; - } - - public void drop(DnDEvent aEvent) { - setDropTargetIndex(-1); - if (aEvent.getAttachedObject() instanceof PaletteItem) { - int index = locationToTargetIndex(aEvent.getPoint()); - if (index >= 0) { - myGroup.handleDrop(myProject, (PaletteItem) aEvent.getAttachedObject(), index); - } - } - } - - public void cleanUpOnLeave() { - setDropTargetIndex(-1); - } - - private int locationToTargetIndex(Point location) { - int row = locationToIndex(location); - if (row < 0) { - return -1; - } - Rectangle rc = getCellBounds(row, row); - return location.y < rc.getCenterY() ? row : row + 1; - } - - public void updateDraggedImage(Image image, Point dropPoint, Point imageOffset) { - } - } - - private class MyDnDSource implements DnDSource { - public boolean canStartDragging(DnDAction action, Point dragOrigin) { - int index = locationToIndex(dragOrigin); - return index >= 0 && myGroup.getItems() [index].startDragging() != null; - } - - public DnDDragStartBean startDragging(DnDAction action, Point dragOrigin) { - int index = locationToIndex(dragOrigin); - if (index < 0) return null; - return myGroup.getItems() [index].startDragging(); - } - - @Nullable - public Pair createDraggedImage(DnDAction action, Point dragOrigin) { - return null; - } - - public void dragDropEnd() { - } - - public void dropActionChanged(final int gestureModifiers) { - PaletteManager.getInstance(myProject).notifyDropActionChanged(gestureModifiers); - } - } -} diff --git a/java/idea-ui/src/com/intellij/ide/palette/impl/PaletteContentWindow.java b/java/idea-ui/src/com/intellij/ide/palette/impl/PaletteContentWindow.java deleted file mode 100644 index b5aaccad7b29..000000000000 --- a/java/idea-ui/src/com/intellij/ide/palette/impl/PaletteContentWindow.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright 2000-2009 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.intellij.ide.palette.impl; - -import org.jetbrains.annotations.Nullable; - -import javax.swing.*; -import java.awt.*; - -/** - * @author yole - */ -public class PaletteContentWindow extends JPanel implements Scrollable { - public PaletteContentWindow() { - setLayout(new PaletteLayoutManager()); - } - - public Dimension getPreferredScrollableViewportSize() { - return getPreferredSize(); - } - - public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) { - return 20; - } - - public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) { - return 100; - } - - public boolean getScrollableTracksViewportWidth() { - return true; - } - - public boolean getScrollableTracksViewportHeight() { - return false; - } - - @Nullable PaletteGroupHeader getLastGroupHeader() { - PaletteGroupHeader result = null; - for(Component comp: getComponents()) { - if (comp instanceof PaletteGroupHeader) { - result = (PaletteGroupHeader) comp; - } - } - return result; - } - - private static class PaletteLayoutManager implements LayoutManager { - - public void addLayoutComponent(String name, Component comp) { - } - - public void layoutContainer(Container parent) { - int width = parent.getWidth(); - - int height = 0; - for(Component c: parent.getComponents()) { - if (c instanceof PaletteGroupHeader) { - PaletteGroupHeader groupHeader = (PaletteGroupHeader) c; - groupHeader.setLocation(0, height); - if (groupHeader.isVisible()) { - groupHeader.setSize(width, groupHeader.getPreferredSize().height); - height += groupHeader.getPreferredSize().height; - } - else { - groupHeader.setSize(0, 0); - } - if (groupHeader.isSelected() || !groupHeader.isVisible()) { - PaletteComponentList componentList = groupHeader.getComponentList(); - componentList.setSize(width, componentList.getPreferredSize().height); - componentList.setLocation(0, height); - height += componentList.getHeight(); - } - } - } - } - - public Dimension minimumLayoutSize(Container parent) { - return new Dimension(0, 0); - } - - public Dimension preferredLayoutSize(Container parent) { - int height = 0; - int width = parent.getWidth(); - for(Component c: parent.getComponents()) { - if (c instanceof PaletteGroupHeader) { - PaletteGroupHeader groupHeader = (PaletteGroupHeader) c; - height += groupHeader.getHeight(); - if (groupHeader.isSelected()) { - height += groupHeader.getComponentList().getPreferredHeight(width); - } - } - } - return new Dimension(10 /* not used - tracks viewports width*/, height); - } - - public void removeLayoutComponent(Component comp) { - } - } -} diff --git a/java/idea-ui/src/com/intellij/ide/palette/impl/PaletteGroupHeader.java b/java/idea-ui/src/com/intellij/ide/palette/impl/PaletteGroupHeader.java deleted file mode 100644 index c98caefa3f75..000000000000 --- a/java/idea-ui/src/com/intellij/ide/palette/impl/PaletteGroupHeader.java +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Copyright 2000-2013 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.intellij.ide.palette.impl; - -import com.intellij.ide.dnd.DnDEvent; -import com.intellij.ide.dnd.DnDManager; -import com.intellij.ide.dnd.DnDTarget; -import com.intellij.ide.palette.PaletteGroup; -import com.intellij.ide.palette.PaletteItem; -import com.intellij.openapi.actionSystem.*; -import com.intellij.openapi.project.Project; -import com.intellij.ui.PopupHandler; -import com.intellij.util.ui.UIUtil; -import org.jetbrains.annotations.NonNls; -import org.jetbrains.annotations.Nullable; - -import javax.swing.*; -import javax.swing.border.CompoundBorder; -import java.awt.*; -import java.awt.event.*; - -/** - * @author yole - */ -public class PaletteGroupHeader extends JCheckBox implements DataProvider { - private final PaletteWindow myPaletteWindow; - private PaletteComponentList myComponentList; - private final PaletteGroup myGroup; - - public PaletteGroupHeader(PaletteWindow paletteWindow, PaletteGroup group) { - myPaletteWindow = paletteWindow; - myGroup = group; - if (group.getName() == null) { - setVisible(false); - } - else { - setText(group.getName()); - } - setSelected(true); - addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - if (myComponentList != null) { - myComponentList.setVisible(isSelected()); - } - } - }); - - addMouseListener(new PopupHandler() { - public void invokePopup(Component comp, int x, int y) { - myPaletteWindow.setLastFocusedGroup(PaletteGroupHeader.this); - showGroupPopupMenu(comp, x, y); - } - }); - - setIcon(UIUtil.getTreeCollapsedIcon()); - setSelectedIcon(UIUtil.getTreeExpandedIcon()); - setFont(getFont().deriveFont(Font.BOLD)); - setFocusPainted(false); - setMargin(new Insets(0, 3, 0, 3)); - setOpaque(true); - if (getBorder() instanceof CompoundBorder) { // from BasicLookAndFeel - Dimension pref = getPreferredSize(); - pref.height -= 3; - setPreferredSize(pref); - } - - DnDManager.getInstance().registerTarget(new DnDTarget() { - public boolean update(DnDEvent aEvent) { - setBorderPainted(true); - aEvent.setDropPossible(aEvent.getAttachedObject() instanceof PaletteItem); - return true; - } - - public void drop(DnDEvent aEvent) { - setBorderPainted(false); - if (aEvent.getAttachedObject() instanceof PaletteItem) { - myGroup.handleDrop(myPaletteWindow.getProject(), (PaletteItem) aEvent.getAttachedObject(), -1); - } - } - - public void cleanUpOnLeave() { - setBorderPainted(false); - } - - public void updateDraggedImage(Image image, Point dropPoint, Point imageOffset) { - } - }, this); - - addFocusListener(new FocusAdapter() { - @Override public void focusGained(FocusEvent e) { - myPaletteWindow.setLastFocusedGroup(PaletteGroupHeader.this); - } - }); - - initActions(); - } - - public void showGroupPopupMenu(final Component comp, final int x, final int y) { - ActionGroup group = myGroup.getPopupActionGroup(); - if (group != null) { - ActionPopupMenu popupMenu = ActionManager.getInstance().createActionPopupMenu(ActionPlaces.UNKNOWN, group); - popupMenu.getComponent().show(comp, x, y); - } - } - - private void initActions() { - @NonNls InputMap inputMap = getInputMap(WHEN_FOCUSED); - inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, false), "moveFocusDown"); - inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, false), "moveFocusUp"); - inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0, false), "collapse"); - inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0, false), "expand"); - - @NonNls ActionMap actionMap = getActionMap(); - actionMap.put("moveFocusDown", new MoveFocusAction(true)); - actionMap.put("moveFocusUp", new MoveFocusAction(false)); - actionMap.put("collapse", new ExpandAction(false)); - actionMap.put("expand", new ExpandAction(true)); - } - - @Override public Color getBackground() { - if (isFocusOwner()) { - return UIUtil.getListSelectionBackground(); - } - return super.getBackground(); - } - - @Override public Color getForeground() { - if (isFocusOwner()) { - return UIUtil.getListSelectionForeground(); - } - return super.getForeground(); - } - - public void setComponentList(final PaletteComponentList componentList) { - myComponentList = componentList; - } - - public PaletteComponentList getComponentList() { - return myComponentList; - } - - public PaletteGroup getGroup() { - return myGroup; - } - - @Nullable public Object getData(String dataId) { - Object data = myPaletteWindow.getData(dataId); - if (data != null) return data; - Project project = CommonDataKeys.PROJECT.getData(myPaletteWindow); - return myGroup.getData(project, dataId); - } - - private class MoveFocusAction extends AbstractAction { - private final boolean moveDown; - - public MoveFocusAction(boolean moveDown) { - this.moveDown = moveDown; - } - - public void actionPerformed(ActionEvent e) { - KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager(); - Container container = kfm.getCurrentFocusCycleRoot(); - FocusTraversalPolicy policy = container.getFocusTraversalPolicy(); - if (null == policy) policy = kfm.getDefaultFocusTraversalPolicy(); - Component next = - moveDown ? policy.getComponentAfter(container, PaletteGroupHeader.this) : policy.getComponentBefore(container, PaletteGroupHeader.this); - if (null != next && next instanceof PaletteComponentList) { - final PaletteComponentList list = (PaletteComponentList)next; - if (list.getModel().getSize() != 0) { - list.takeFocusFrom(PaletteGroupHeader.this, list == myComponentList ? 0 : -1); - return; - } - else { - next = moveDown ? policy.getComponentAfter(container, next) : policy.getComponentBefore(container, next); - } - } - if (null != next && next instanceof PaletteGroupHeader) { - next.requestFocus(); - } - } - } - - private class ExpandAction extends AbstractAction { - private final boolean expand; - - public ExpandAction(boolean expand) { - this.expand = expand; - } - - public void actionPerformed(ActionEvent e) { - if (expand == isSelected()) return; - setSelected(expand); - if (myComponentList != null) { - myComponentList.setVisible(isSelected()); - } - } - } -} diff --git a/java/idea-ui/src/com/intellij/ide/palette/impl/PaletteManager.java b/java/idea-ui/src/com/intellij/ide/palette/impl/PaletteManager.java deleted file mode 100644 index 1ad6a1cd858d..000000000000 --- a/java/idea-ui/src/com/intellij/ide/palette/impl/PaletteManager.java +++ /dev/null @@ -1,229 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.intellij.ide.palette.impl; - -import com.intellij.icons.AllIcons; -import com.intellij.ide.IdeBundle; -import com.intellij.ide.palette.PaletteDragEventListener; -import com.intellij.ide.palette.PaletteItem; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.components.ProjectComponent; -import com.intellij.openapi.fileEditor.FileEditorManager; -import com.intellij.openapi.fileEditor.FileEditorManagerEvent; -import com.intellij.openapi.fileEditor.FileEditorManagerListener; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.startup.StartupManager; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.openapi.wm.ToolWindow; -import com.intellij.openapi.wm.ToolWindowAnchor; -import com.intellij.openapi.wm.ToolWindowManager; -import com.intellij.util.containers.ContainerUtil; -import com.intellij.util.ui.update.MergingUpdateQueue; -import com.intellij.util.ui.update.Update; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import javax.swing.event.ListSelectionEvent; -import javax.swing.event.ListSelectionListener; -import java.awt.event.KeyEvent; -import java.awt.event.KeyListener; -import java.util.List; - -/** - * @author yole - */ -public class PaletteManager implements ProjectComponent { - private final Project myProject; - private final FileEditorManager myFileEditorManager; - private PaletteWindow myPaletteWindow; - private ToolWindow myPaletteToolWindow; - private final List myKeyListeners = ContainerUtil.createLockFreeCopyOnWriteList(); - private final List myDragEventListeners = ContainerUtil.createLockFreeCopyOnWriteList(); - private final List mySelectionListeners = ContainerUtil.createLockFreeCopyOnWriteList(); - - public PaletteManager(Project project, FileEditorManager fileEditorManager) { - myProject = project; - myFileEditorManager = fileEditorManager; - } - - @Override - public void projectOpened() { - if (!ApplicationManager.getApplication().isHeadlessEnvironment()) { - StartupManager.getInstance(myProject).registerPostStartupActivity(new Runnable() { - @Override - public void run() { - myPaletteWindow = new PaletteWindow(myProject); - myPaletteToolWindow = ToolWindowManager.getInstance(myProject). - registerToolWindow(IdeBundle.message("toolwindow.palette"), - myPaletteWindow, - ToolWindowAnchor.RIGHT, - myProject, - true); - myPaletteToolWindow.setIcon(AllIcons.Toolwindows.ToolWindowPalette); - setContent(); - final MyFileEditorManagerListener myListener = new MyFileEditorManagerListener(); - myFileEditorManager.addFileEditorManagerListener(myListener, myProject); - } - }); - } - } - - @Override - public void projectClosed() { - if (myPaletteWindow != null) { - myPaletteWindow.dispose(); - ToolWindowManager.getInstance(myProject).unregisterToolWindow(IdeBundle.message("toolwindow.palette")); - myPaletteWindow = null; - } - } - - @Override - @NotNull - public String getComponentName() { - return "PaletteManager"; - } - - @Override - public void initComponent() { - } - - @Override - public void disposeComponent() { - if (myPaletteWindow != null) { - myPaletteWindow.dispose(); - } - } - - public static PaletteManager getInstance(final Project project) { - return project.getComponent(PaletteManager.class); - } - - public void clearActiveItem() { - if (myPaletteWindow != null) { - myPaletteWindow.clearActiveItem(); - } - } - - @Nullable - public PaletteItem getActiveItem() { - if (myPaletteWindow != null) { - return myPaletteWindow.getActiveItem(); - } - return null; - } - - @Nullable - public T getActiveItem(Class cls) { - PaletteItem item = getActiveItem(); - if (item != null && item.getClass().isInstance(item)) { - //noinspection unchecked - return (T)item; - } - return null; - } - - public void addKeyListener(KeyListener l) { - myKeyListeners.add(l); - } - - public void removeKeyListener(KeyListener l) { - myKeyListeners.remove(l); - } - - public void addDragEventListener(PaletteDragEventListener l) { - myDragEventListeners.add(l); - } - - public void removeDragEventListener(PaletteDragEventListener l) { - myDragEventListeners.remove(l); - } - - public void addSelectionListener(ListSelectionListener l) { - mySelectionListeners.add(l); - } - - public void removeSelectionListener(ListSelectionListener l) { - mySelectionListeners.remove(l); - } - - private final MergingUpdateQueue myQueue = new MergingUpdateQueue("palette", 200, true, null); - - private void processFileEditorChange(@Nullable final VirtualFile selectedFile) { - myQueue.cancelAllUpdates(); - myQueue.queue(new Update("update") { - @Override - public void run() { - if (myPaletteWindow == null) return; - myPaletteWindow.refreshPaletteIfChanged(selectedFile); - setContent(); - } - }); - } - - private void setContent() { - if (myPaletteWindow.getActiveGroupCount() == 0) { - myPaletteToolWindow.setAvailable(false, null); - } - else { - myPaletteToolWindow.setAvailable(true, null); - myPaletteToolWindow.show(null); - } - } - - void notifyKeyEvent(final KeyEvent e) { - for (KeyListener l : myKeyListeners) { - if (e.getID() == KeyEvent.KEY_PRESSED) { - l.keyPressed(e); - } - else if (e.getID() == KeyEvent.KEY_RELEASED) { - l.keyReleased(e); - } - else if (e.getID() == KeyEvent.KEY_TYPED) { - l.keyTyped(e); - } - } - } - - void notifyDropActionChanged(int gestureModifiers) { - for (PaletteDragEventListener l : myDragEventListeners) { - l.dropActionChanged(gestureModifiers); - } - } - - void notifySelectionChanged(final ListSelectionEvent event) { - for (ListSelectionListener l : mySelectionListeners) { - l.valueChanged(event); - } - } - - private class MyFileEditorManagerListener implements FileEditorManagerListener { - @Override - public void fileOpened(@NotNull FileEditorManager source, @NotNull VirtualFile file) { - processFileEditorChange(file); - } - - @Override - public void fileClosed(@NotNull FileEditorManager source, @NotNull VirtualFile file) { - processFileEditorChange(null); - } - - @Override - public void selectionChanged(@NotNull FileEditorManagerEvent event) { - processFileEditorChange(event.getNewFile()); - } - } -} diff --git a/java/idea-ui/src/com/intellij/ide/palette/impl/PaletteWindow.java b/java/idea-ui/src/com/intellij/ide/palette/impl/PaletteWindow.java deleted file mode 100644 index 27f280386731..000000000000 --- a/java/idea-ui/src/com/intellij/ide/palette/impl/PaletteWindow.java +++ /dev/null @@ -1,314 +0,0 @@ -/* - * Copyright 2000-2009 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.intellij.ide.palette.impl; - -import com.intellij.ide.palette.PaletteGroup; -import com.intellij.ide.palette.PaletteItem; -import com.intellij.ide.palette.PaletteItemProvider; -import com.intellij.openapi.actionSystem.*; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.extensions.Extensions; -import com.intellij.openapi.fileEditor.FileEditorManager; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.ui.PopupHandler; -import com.intellij.ui.ScrollPaneFactory; -import com.intellij.ui.components.JBTabbedPane; -import com.intellij.util.ArrayUtil; -import com.intellij.util.containers.HashSet; -import org.jetbrains.annotations.NonNls; -import org.jetbrains.annotations.Nullable; - -import javax.swing.*; -import javax.swing.event.ListSelectionEvent; -import javax.swing.event.ListSelectionListener; -import java.awt.*; -import java.awt.dnd.DragSource; -import java.awt.dnd.DragSourceAdapter; -import java.awt.dnd.DragSourceDropEvent; -import java.awt.dnd.DragSourceListener; -import java.awt.event.KeyEvent; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.util.*; - -/** - * @author yole - */ -public class PaletteWindow extends JPanel implements DataProvider { - private final Project myProject; - private final ArrayList myGroupHeaders = new ArrayList(); - private final PaletteItemProvider[] myProviders; - private final MyPropertyChangeListener myPropertyChangeListener = new MyPropertyChangeListener(); - private final Set myGroups = new HashSet(); - private final JTabbedPane myTabbedPane = new JBTabbedPane(); - private final JScrollPane myScrollPane = ScrollPaneFactory.createScrollPane(); - private final MyListSelectionListener myListSelectionListener = new MyListSelectionListener(); - private PaletteGroupHeader myLastFocusedGroup; - - @NonNls private static final String ourHelpID = "guiDesigner.uiTour.palette"; - private PaletteManager myPaletteManager; - - private final DragSourceListener myDragSourceListener = new DragSourceAdapter() { - @Override - public void dragDropEnd(DragSourceDropEvent event) { - Component component = event.getDragSourceContext().getComponent(); - if (!event.getDropSuccess() && - component instanceof PaletteComponentList && - getRootPane() == ((JComponent)component).getRootPane()) { - clearActiveItem(); - } - } - }; - - public PaletteWindow(Project project) { - myProject = project; - myPaletteManager = PaletteManager.getInstance(myProject); - myProviders = Extensions.getExtensions(PaletteItemProvider.EP_NAME, project); - for (PaletteItemProvider provider : myProviders) { - provider.addListener(myPropertyChangeListener); - } - - setLayout(new GridLayout(1, 1)); - myScrollPane.addMouseListener(new MyScrollPanePopupHandler()); - myScrollPane.setBorder(null); - KeyStroke escStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0); - new ClearActiveItemAction().registerCustomShortcutSet(new CustomShortcutSet(escStroke), myScrollPane); - refreshPalette(); - - if (!ApplicationManager.getApplication().isHeadlessEnvironment()) { - DragSource.getDefaultDragSource().addDragSourceListener(myDragSourceListener); - } - } - - public void dispose() { - if (!ApplicationManager.getApplication().isHeadlessEnvironment()) { - DragSource.getDefaultDragSource().removeDragSourceListener(myDragSourceListener); - } - } - - public void refreshPalette() { - refreshPalette(null); - } - - public void refreshPalette(@Nullable VirtualFile selectedFile) { - for (PaletteGroupHeader groupHeader : myGroupHeaders) { - groupHeader.getComponentList().removeListSelectionListener(myListSelectionListener); - } - String[] oldTabNames = collectTabNames(myGroups); - myTabbedPane.removeAll(); - myGroupHeaders.clear(); - myGroups.clear(); - - final ArrayList currentGroups = collectCurrentGroups(selectedFile); - String[] tabNames = collectTabNames(currentGroups); - if (tabNames.length == 1) { - if (oldTabNames.length != 1) { - remove(myTabbedPane); - add(myScrollPane); - } - - PaletteContentWindow contentWindow = new PaletteContentWindow(); - myScrollPane.getViewport().setView(contentWindow); - - for (PaletteGroup group : currentGroups) { - addGroupToControl(group, contentWindow); - } - - final JComponent view = (JComponent)myScrollPane.getViewport().getView(); - if (view != null) { - view.revalidate(); - for (Component component : view.getComponents()) { - ((JComponent)component).revalidate(); - } - } - } - else { - if (oldTabNames.length <= 1) { - remove(myScrollPane); - add(myTabbedPane); - } - for (String tabName : tabNames) { - PaletteContentWindow contentWindow = new PaletteContentWindow(); - JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(contentWindow); - scrollPane.addMouseListener(new MyScrollPanePopupHandler()); - myTabbedPane.add(tabName, scrollPane); - for (PaletteGroup group : currentGroups) { - if (group.getTabName().equals(tabName)) { - addGroupToControl(group, contentWindow); - } - } - } - myTabbedPane.revalidate(); - } - } - - private void addGroupToControl(PaletteGroup group, JComponent control) { - PaletteGroupHeader groupHeader = new PaletteGroupHeader(this, group); - myGroupHeaders.add(groupHeader); - myGroups.add(group); - control.add(groupHeader); - PaletteComponentList componentList = new PaletteComponentList(myProject, group); - control.add(componentList); - groupHeader.setComponentList(componentList); - componentList.addListSelectionListener(myListSelectionListener); - } - - private static String[] collectTabNames(final Collection groups) { - Set result = new TreeSet(); - for (PaletteGroup group : groups) { - result.add(group.getTabName()); - } - return ArrayUtil.toStringArray(result); - } - - private ArrayList collectCurrentGroups(@Nullable VirtualFile selectedFile) { - ArrayList result = new ArrayList(); - if (selectedFile == null) { - VirtualFile[] editedFiles = FileEditorManager.getInstance(myProject).getSelectedFiles(); - if (editedFiles.length > 0) { - selectedFile = editedFiles[0]; - } - } - if (selectedFile != null) { - for (PaletteItemProvider provider : myProviders) { - PaletteGroup[] groups = provider.getActiveGroups(selectedFile); - Collections.addAll(result, groups); - } - } - return result; - } - - public void refreshPaletteIfChanged(VirtualFile selectedFile) { - Set currentGroups = new HashSet(collectCurrentGroups(selectedFile)); - if (!currentGroups.equals(myGroups)) { - refreshPalette(selectedFile); - } - } - - public int getActiveGroupCount() { - return myGroups.size(); - } - - public void clearActiveItem() { - if (getActiveItem() == null) return; - for (PaletteGroupHeader group : myGroupHeaders) { - group.getComponentList().clearSelection(); - } - ListSelectionEvent event = new ListSelectionEvent(this, -1, -1, false); - myPaletteManager.notifySelectionChanged(event); - } - - @Nullable - public PaletteItem getActiveItem() { - for (PaletteGroupHeader groupHeader : myGroupHeaders) { - if (groupHeader.isSelected() && groupHeader.getComponentList().getSelectedValue() != null) { - return (PaletteItem)groupHeader.getComponentList().getSelectedValue(); - } - } - return null; - } - - @Nullable - public Object getData(String dataId) { - if (PlatformDataKeys.HELP_ID.is(dataId)) { - return ourHelpID; - } - if (CommonDataKeys.PROJECT.is(dataId)) { - return myProject; - } - PaletteItem item = getActiveItem(); - if (item != null) { - Object data = item.getData(myProject, dataId); - if (data != null) return data; - } - for (PaletteGroupHeader groupHeader : myGroupHeaders) { - if ((groupHeader.isSelected() && groupHeader.getComponentList().getSelectedValue() != null) || groupHeader == myLastFocusedGroup) { - return groupHeader.getGroup().getData(myProject, dataId); - } - } - final int tabCount = collectTabNames(myGroups).length; - if (tabCount > 0) { - JScrollPane activeScrollPane; - if (tabCount == 1) { - activeScrollPane = myScrollPane; - } - else { - activeScrollPane = (JScrollPane)myTabbedPane.getSelectedComponent(); - } - PaletteContentWindow activeContentWindow = (PaletteContentWindow)activeScrollPane.getViewport().getView(); - PaletteGroupHeader groupHeader = activeContentWindow.getLastGroupHeader(); - if (groupHeader != null) { - return groupHeader.getGroup().getData(myProject, dataId); - } - } - return null; - } - - public Project getProject() { - return myProject; - } - - void setLastFocusedGroup(final PaletteGroupHeader focusedGroup) { - myLastFocusedGroup = focusedGroup; - for (PaletteGroupHeader group : myGroupHeaders) { - group.getComponentList().clearSelection(); - } - } - - private class MyListSelectionListener implements ListSelectionListener { - public void valueChanged(ListSelectionEvent e) { - PaletteComponentList sourceList = (PaletteComponentList)e.getSource(); - for (int i = e.getFirstIndex(); i <= e.getLastIndex(); i++) { - if (sourceList.isSelectedIndex(i)) { - // selection is being added - for (PaletteGroupHeader group : myGroupHeaders) { - if (group.getComponentList() != sourceList) { - group.getComponentList().clearSelection(); - } - } - break; - } - } - myPaletteManager.notifySelectionChanged(e); - } - } - - private class MyPropertyChangeListener implements PropertyChangeListener { - public void propertyChange(PropertyChangeEvent evt) { - refreshPalette(); - } - } - - private static class MyScrollPanePopupHandler extends PopupHandler { - public void invokePopup(Component comp, int x, int y) { - JScrollPane scrollPane = (JScrollPane)comp; - PaletteContentWindow contentWindow = (PaletteContentWindow)scrollPane.getViewport().getView(); - if (contentWindow != null) { - PaletteGroupHeader groupHeader = contentWindow.getLastGroupHeader(); - if (groupHeader != null) { - groupHeader.showGroupPopupMenu(comp, x, y); - } - } - } - } - - private class ClearActiveItemAction extends AnAction { - public void actionPerformed(AnActionEvent e) { - clearActiveItem(); - } - } -} diff --git a/java/idea-ui/src/com/intellij/ide/projectWizard/ProjectTypeStep.form b/java/idea-ui/src/com/intellij/ide/projectWizard/ProjectTypeStep.form index 7246f2cfb566..61333a8ed4ec 100644 --- a/java/idea-ui/src/com/intellij/ide/projectWizard/ProjectTypeStep.form +++ b/java/idea-ui/src/com/intellij/ide/projectWizard/ProjectTypeStep.form @@ -79,7 +79,14 @@ - + + + + + + + + diff --git a/java/idea-ui/src/com/intellij/ide/projectWizard/ProjectTypeStep.java b/java/idea-ui/src/com/intellij/ide/projectWizard/ProjectTypeStep.java index 10e11f74077f..94e551250aed 100644 --- a/java/idea-ui/src/com/intellij/ide/projectWizard/ProjectTypeStep.java +++ b/java/idea-ui/src/com/intellij/ide/projectWizard/ProjectTypeStep.java @@ -54,6 +54,7 @@ import com.intellij.ui.CollectionListModel; import com.intellij.ui.IdeBorderFactory; import com.intellij.ui.ListSpeedSearch; import com.intellij.ui.SingleSelectionModel; +import com.intellij.ui.components.JBLabel; import com.intellij.ui.components.JBList; import com.intellij.ui.popup.list.GroupedItemsListRenderer; import com.intellij.util.Function; @@ -81,10 +82,6 @@ import java.util.List; @SuppressWarnings("unchecked") public class ProjectTypeStep extends ModuleWizardStep implements SettingsStep, Disposable { - private static final String TEMPLATES_CARD = "templates card"; - private static final String FRAMEWORKS_CARD = "frameworks card"; - - private static final String PROJECT_WIZARD_GROUP = "project.wizard.group"; public static final Convertor PROVIDER_STRING_CONVERTOR = new Convertor() { @Override @@ -98,23 +95,14 @@ public class ProjectTypeStep extends ModuleWizardStep implements SettingsStep, D return node.getId(); } }; - - private JPanel myPanel; - private JPanel myOptionsPanel; - private JBList myProjectTypeList; - private ProjectTemplateList myTemplatesList; - private JPanel myFrameworksPanelPlaceholder; - private JPanel myHeaderPanel; - + private static final String TEMPLATES_CARD = "templates card"; + private static final String FRAMEWORKS_CARD = "frameworks card"; + private static final String PROJECT_WIZARD_GROUP = "project.wizard.group"; private final WizardContext myContext; private final NewProjectWizard myWizard; private final ModulesProvider myModulesProvider; private final AddSupportForFrameworksPanel myFrameworksPanel; private final ModuleBuilder.ModuleConfigurationUpdater myConfigurationUpdater; - @Nullable - private ModuleWizardStep mySettingsStep; - - @SuppressWarnings("MismatchedQueryAndUpdateOfCollection") private final FactoryMap myBuilders = new FactoryMap() { @Nullable @@ -125,6 +113,15 @@ public class ProjectTypeStep extends ModuleWizardStep implements SettingsStep, D }; private final Map myCustomSteps = new HashMap(); private final MultiMap myTemplatesMap; + private JPanel myPanel; + private JPanel myOptionsPanel; + private JBList myProjectTypeList; + private ProjectTemplateList myTemplatesList; + private JPanel myFrameworksPanelPlaceholder; + private JPanel myHeaderPanel; + private JBLabel myFrameworksLabel; + @Nullable + private ModuleWizardStep mySettingsStep; private String myCurrentCard; private TemplatesGroup myLastSelectedGroup; @@ -206,6 +203,8 @@ public class ProjectTypeStep extends ModuleWizardStep implements SettingsStep, D myFrameworksPanel = new AddSupportForFrameworksPanel(Collections.emptyList(), model, true, myHeaderPanel); Disposer.register(this, myFrameworksPanel); myFrameworksPanelPlaceholder.add(myFrameworksPanel.getMainPanel()); + myFrameworksLabel.setLabelFor(myFrameworksPanel.getFrameworksTree()); + myFrameworksLabel.setBorder(IdeBorderFactory.createEmptyBorder(3)); myConfigurationUpdater = new ModuleBuilder.ModuleConfigurationUpdater() { @Override @@ -258,6 +257,20 @@ public class ProjectTypeStep extends ModuleWizardStep implements SettingsStep, D myTemplatesList.restoreSelection(); } + private static ModuleType getModuleType(TemplatesGroup group) { + ModuleBuilder moduleBuilder = group.getModuleBuilder(); + return moduleBuilder == null ? null : moduleBuilder.getModuleType(); + } + + private static boolean matchFramework(ProjectCategory projectCategory, FrameworkSupportInModuleProvider framework) { + + FrameworkRole[] roles = framework.getRoles(); + if (roles.length == 0) return true; + + List acceptable = Arrays.asList(projectCategory.getAcceptableFrameworkRoles()); + return ContainerUtil.intersects(Arrays.asList(roles), acceptable); + } + private boolean isFrameworksMode() { return FRAMEWORKS_CARD.equals(myCurrentCard) && getSelectedBuilder().equals(myContext.getProjectBuilder()); } @@ -356,11 +369,6 @@ public class ProjectTypeStep extends ModuleWizardStep implements SettingsStep, D return groups; } - private static ModuleType getModuleType(TemplatesGroup group) { - ModuleBuilder moduleBuilder = group.getModuleBuilder(); - return moduleBuilder == null ? null : moduleBuilder.getModuleType(); - } - // new TemplatesGroup selected public void projectTypeChanged() { TemplatesGroup group = getSelectedGroup(); @@ -438,15 +446,6 @@ public class ProjectTypeStep extends ModuleWizardStep implements SettingsStep, D showCard(TEMPLATES_CARD); } - private static boolean matchFramework(ProjectCategory projectCategory, FrameworkSupportInModuleProvider framework) { - - FrameworkRole[] roles = framework.getRoles(); - if (roles.length == 0) return true; - - List acceptable = Arrays.asList(projectCategory.getAcceptableFrameworkRoles()); - return ContainerUtil.intersects(Arrays.asList(roles), acceptable); - } - private void setTemplatesList(TemplatesGroup group, Collection templates, boolean preserveSelection) { List list = new ArrayList(templates); ModuleBuilder moduleBuilder = group.getModuleBuilder(); diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/JavaResourceRootEditHandler.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/JavaResourceRootEditHandler.java index bf1efc89b0d5..0c7f20eb3313 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/JavaResourceRootEditHandler.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/JavaResourceRootEditHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,7 @@ package com.intellij.openapi.roots.ui.configuration; import com.intellij.icons.AllIcons; +import com.intellij.ui.JBColor; import org.jetbrains.annotations.NotNull; import org.jetbrains.jps.model.java.JavaResourceRootType; @@ -51,7 +52,7 @@ public class JavaResourceRootEditHandler extends JavaResourceRootEditHandlerBase @NotNull @Override public Color getRootsGroupColor() { - return new Color(0x812DF3); + return new JBColor(new Color(0x812DF3), new Color(127, 96, 144)); } @NotNull diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ModuleEditor.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ModuleEditor.java index 10df2939f6e7..a10265607029 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ModuleEditor.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ModuleEditor.java @@ -206,7 +206,9 @@ public abstract class ModuleEditor implements Place.Navigator, Disposable { myEditors.add(new ModuleConfigurableWrapper(moduleConfigurable)); } for(ModuleConfigurableEP extension : myModule.getExtensions(MODULE_CONFIGURABLES)) { - myEditors.add(new ModuleConfigurableWrapper(extension.createConfigurable())); + if (extension.canCreateConfigurable()) { + myEditors.add(new ModuleConfigurableWrapper(extension.createConfigurable())); + } } } diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ProjectStructureConfigurable.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ProjectStructureConfigurable.java index a5867d66d176..12ab9b4c0de6 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ProjectStructureConfigurable.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/ProjectStructureConfigurable.java @@ -197,9 +197,9 @@ public class ProjectStructureConfigurable extends BaseConfigurable implements Se myToolbarComponent = toolbar.getComponent(); if (Registry.is("ide.new.project.settings")) { left.setBackground(UIUtil.getSidePanelColor()); - } else { - left.add(myToolbarComponent, BorderLayout.NORTH); + myToolbarComponent.setBackground(UIUtil.getSidePanelColor()); } + left.add(myToolbarComponent, BorderLayout.NORTH); left.add(mySidePanel, BorderLayout.CENTER); mySplitter.setFirstComponent(left); diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/DefaultLibraryRootsComponentDescriptor.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/DefaultLibraryRootsComponentDescriptor.java index eb87df2a47e7..5b3e9302a14b 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/DefaultLibraryRootsComponentDescriptor.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/DefaultLibraryRootsComponentDescriptor.java @@ -17,6 +17,7 @@ package com.intellij.openapi.roots.ui.configuration.libraryEditor; import com.intellij.codeInsight.ExternalAnnotationsManager; import com.intellij.openapi.fileChooser.FileChooserDescriptor; +import com.intellij.openapi.fileChooser.FileElement; import com.intellij.openapi.fileTypes.StdFileTypes; import com.intellij.openapi.module.Module; import com.intellij.openapi.progress.ProgressIndicator; @@ -29,7 +30,9 @@ import com.intellij.openapi.roots.OrderRootType; import com.intellij.openapi.roots.libraries.ui.*; import com.intellij.openapi.roots.ui.OrderRootTypeUIFactory; import com.intellij.openapi.roots.ui.configuration.PathUIUtils; +import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileVisitor; @@ -45,6 +48,14 @@ import java.util.*; * @author nik */ public class DefaultLibraryRootsComponentDescriptor extends LibraryRootsComponentDescriptor { + private static final Set NATIVE_LIBRARY_EXTENSIONS = ContainerUtil.newTroveSet(FileUtil.PATH_HASHING_STRATEGY, "dll", "so", "dylib"); + public static final Condition LIBRARY_ROOT_CONDITION = new Condition() { + @Override + public boolean value(VirtualFile file) { + return FileElement.isArchive(file) || isNativeLibrary(file); + } + }; + @Override public OrderRootTypePresentation getRootTypePresentation(@NotNull OrderRootType type) { return getDefaultPresentation(type); @@ -67,10 +78,17 @@ public class DefaultLibraryRootsComponentDescriptor extends LibraryRootsComponen new AnnotationsRootFilter(), new NativeLibraryRootFilter()); } + private static boolean isNativeLibrary(VirtualFile file) { + String extension = file.getExtension(); + return extension != null && NATIVE_LIBRARY_EXTENSIONS.contains(extension); + } + @NotNull @Override public FileChooserDescriptor createAttachFilesChooserDescriptor(@Nullable String libraryName) { - FileChooserDescriptor descriptor = super.createAttachFilesChooserDescriptor(libraryName); + final FileChooserDescriptor descriptor = new FileChooserDescriptor(true, true, true, false, true, true).withFileFilter(LIBRARY_ROOT_CONDITION); + descriptor.setTitle(StringUtil.isEmpty(libraryName) ? ProjectBundle.message("library.attach.files.action") + : ProjectBundle.message("library.attach.files.to.library.action", libraryName)); descriptor.setDescription(ProjectBundle.message("library.java.attach.files.description")); return descriptor; } @@ -119,24 +137,25 @@ public class DefaultLibraryRootsComponentDescriptor extends LibraryRootsComponen } } - private static class NativeLibraryRootFilter extends RootFilter { - private static final Set NATIVE_LIBRARY_EXTENSIONS = ContainerUtil.newTroveSet(FileUtil.PATH_HASHING_STRATEGY, "dll", "so", "dylib"); - + private static class NativeLibraryRootFilter extends RootDetector { private NativeLibraryRootFilter() { - super(NativeLibraryOrderRootType.getInstance(), false, "external annotations"); + super(NativeLibraryOrderRootType.getInstance(), false, "native library location"); } + @NotNull @Override - public boolean isAccepted(@NotNull VirtualFile rootCandidate, @NotNull ProgressIndicator progressIndicator) { + public Collection detectRoots(@NotNull VirtualFile rootCandidate, @NotNull ProgressIndicator progressIndicator) { if (rootCandidate.isDirectory()) { for (VirtualFile file : rootCandidate.getChildren()) { - String extension = file.getExtension(); - if (extension != null && NATIVE_LIBRARY_EXTENSIONS.contains(extension)) { - return true; + if (isNativeLibrary(file)) { + return Collections.singleton(rootCandidate); } } } - return false; + else if (isNativeLibrary(rootCandidate)) { + return Collections.singleton(rootCandidate.getParent()); + } + return Collections.emptyList(); } } diff --git a/java/java-analysis-api/src/com/intellij/openapi/roots/LanguageLevelModuleExtension.java b/java/java-analysis-api/src/com/intellij/openapi/roots/LanguageLevelModuleExtension.java index 76ab3643cb79..c7068ec43482 100644 --- a/java/java-analysis-api/src/com/intellij/openapi/roots/LanguageLevelModuleExtension.java +++ b/java/java-analysis-api/src/com/intellij/openapi/roots/LanguageLevelModuleExtension.java @@ -76,6 +76,9 @@ public class LanguageLevelModuleExtension extends ModuleExtension result = ContainerUtil.newArrayList(); PsiAnnotation[] fromBytecode = ProjectBytecodeAnalysis.getInstance(listOwner.getProject()).findInferredAnnotations(listOwner); for (PsiAnnotation annotation : fromBytecode) { diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightMethodUtil.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightMethodUtil.java index 118c98efc9d2..83af5606d8c8 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightMethodUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightMethodUtil.java @@ -1026,7 +1026,7 @@ public class HighlightMethodUtil { description = JavaErrorMessages.message("extension.method.should.have.a.body"); additionalFixes.add(QUICK_FIX_FACTORY.createAddMethodBodyFix(method)); } - else if (isInterface && isStatic) { + else if (isInterface && isStatic && languageLevel.isAtLeast(LanguageLevel.JDK_1_8)) { description = "Static methods in interfaces should have a body"; } } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/RedundantLambdaCodeBlockInspection.java b/java/java-analysis-impl/src/com/intellij/codeInspection/RedundantLambdaCodeBlockInspection.java index 0dfa5df979bc..d65abf63dda3 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/RedundantLambdaCodeBlockInspection.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/RedundantLambdaCodeBlockInspection.java @@ -19,12 +19,23 @@ import com.intellij.codeInsight.daemon.GroupNames; import com.intellij.codeInsight.intention.HighPriorityAction; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; +import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.*; +import com.intellij.psi.impl.source.resolve.graphInference.InferenceSession; +import com.intellij.psi.infos.CandidateInfo; +import com.intellij.psi.infos.MethodCandidateInfo; +import com.intellij.psi.scope.conflictResolvers.JavaMethodsConflictResolver; import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.util.PsiUtil; +import com.intellij.util.ArrayUtil; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + /** * User: anna */ @@ -67,6 +78,26 @@ public class RedundantLambdaCodeBlockInspection extends BaseJavaBatchLocalInspec if (body instanceof PsiCodeBlock) { PsiExpression psiExpression = getExpression((PsiCodeBlock)body); if (psiExpression != null) { + if (!expression.isVoidCompatible() && LambdaUtil.isExpressionStatementExpression(psiExpression)) { + final PsiElement parent = PsiUtil.skipParenthesizedExprUp(expression.getParent()); + if (parent instanceof PsiExpressionList) { + final PsiElement gParent = parent.getParent(); + if (gParent instanceof PsiCallExpression) { + final CandidateInfo[] candidates = PsiResolveHelper.SERVICE.getInstance(gParent.getProject()) + .getReferencedMethodCandidates((PsiCallExpression)gParent, false); + if (candidates.length > 1) { + final List info = new ArrayList(Arrays.asList(candidates)); + final LanguageLevel level = PsiUtil.getLanguageLevel(parent); + final JavaMethodsConflictResolver conflictResolver = new JavaMethodsConflictResolver((PsiExpressionList)parent, level); + final int applicability = conflictResolver.checkApplicability(info); + conflictResolver.checkSpecifics(info, applicability, level); + if (info.size() > 1) { + return; + } + } + } + } + } final PsiElement errorElement; final PsiElement parent = psiExpression.getParent(); if (parent instanceof PsiReturnStatement) { diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Analysis.java b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Analysis.java index 01993d5e9dd0..61d018f2f5ae 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Analysis.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Analysis.java @@ -28,6 +28,8 @@ import org.jetbrains.org.objectweb.asm.tree.analysis.Frame; import java.util.*; +import static com.intellij.codeInspection.bytecodeAnalysis.Direction.*; + class AbstractValues { static final class ParamValue extends BasicValue { ParamValue(Type tp) { @@ -274,10 +276,8 @@ abstract class Analysis { } for (int i = 0; i < args.length; i++) { BasicValue value; - if (direction instanceof InOut && ((InOut)direction).paramIndex == i) { - value = new AbstractValues.ParamValue(args[i]); - } - else if (direction instanceof In && ((In)direction).paramIndex == i) { + if (direction instanceof InOut && ((InOut)direction).paramIndex == i || + direction instanceof In && ((In)direction).paramIndex == i) { value = new AbstractValues.ParamValue(args[i]); } else { diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisConverter.java b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisConverter.java index 84a5851e847d..91f1b685faa8 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisConverter.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisConverter.java @@ -28,6 +28,7 @@ import java.security.NoSuchAlgorithmException; import java.util.*; import static com.intellij.codeInspection.bytecodeAnalysis.ProjectBytecodeAnalysis.LOG; +import static com.intellij.codeInspection.bytecodeAnalysis.Direction.*; /** * @author lambdamix @@ -272,25 +273,35 @@ public class BytecodeAnalysisConverter { return null; } - private static int mkDirectionKey(Direction dir) { - if (dir instanceof Out) { + + static int mkDirectionKey(Direction dir) { + if (dir == Out) { return 0; - } else if (dir instanceof In) { + } + else if (dir == NullableOut) { + return 1; + } + else if (dir instanceof In) { In in = (In)dir; // nullity mask is 0/1 - return 8 * in.paramId() + 1 + in.nullityMask; - } else { + return 2 + 8 * in.paramId() + in.nullityMask; + } + else { InOut inOut = (InOut)dir; - return 8 * inOut.paramId() + 3 + inOut.valueId(); + return 4 + 8 * inOut.paramId() + inOut.valueId(); } } @NotNull private static Direction extractDirection(int directionKey) { if (directionKey == 0) { - return new Out(); + return Out; + } + else if (directionKey == 1) { + return NullableOut; } else { + directionKey--; int paramId = directionKey / 8; int subDirection = directionKey % 8; if (subDirection <= 2) { @@ -344,7 +355,7 @@ public class BytecodeAnalysisConverter { continue; } Direction direction = extractDirection(key.dirKey); - if (value == Value.NotNull && direction instanceof Out && methodKey.equals(key)) { + if (value == Value.NotNull && direction == Out && methodKey.equals(key)) { notNulls.add(key); } else if (direction instanceof InOut) { diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisIndex.java b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisIndex.java index a7070b882431..4311dd39c2c0 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisIndex.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisIndex.java @@ -40,7 +40,7 @@ public class BytecodeAnalysisIndex extends FileBasedIndexExtension".equals(methodNode.name); - Key primaryKey = new Key(method, new Out(), stable); + Key primaryKey = new Key(method, Out, stable); if (argumentTypes.length == 0 && !isInterestingResult) { return Pair.create(primaryKey, EMPTY_EQUATIONS); } @@ -172,15 +171,15 @@ public class ClassDataIndexer implements DataIndexer> processBranchingMethod(final Method method, - final MethodNode methodNode, - final RichControlFlow richControlFlow, - Type[] argumentTypes, - boolean isReferenceResult, - boolean isInterestingResult, - final boolean stable, - boolean jsr) throws AnalyzerException { - - List> result = new ArrayList>(argumentTypes.length * 4 + 1); + final MethodNode methodNode, + final RichControlFlow richControlFlow, + Type[] argumentTypes, + boolean isReferenceResult, + boolean isInterestingResult, + final boolean stable, + boolean jsr) throws AnalyzerException { + + List> result = new ArrayList>(argumentTypes.length * 4 + 2); boolean maybeLeakingParameter = isInterestingResult; for (Type argType : argumentTypes) { if (ASMUtils.isReferenceType(argType) || (isReferenceResult && ASMUtils.isBooleanType(argType))) { @@ -191,41 +190,25 @@ public class ClassDataIndexer implements DataIndexer origins = new NullableLazyValue() { - @Override - protected boolean[] compute() { - try { - return OriginsAnalysis.resultOrigins(leakingParametersAndFrames.frames, methodNode.instructions, richControlFlow.controlFlow); - } - catch (AnalyzerException e) { - LOG.debug("when processing " + method + " in " + presentableUrl, e); - return null; - } - } - }; + final boolean[] origins = + isInterestingResult ? + OriginsAnalysis.resultOrigins(leakingParametersAndFrames.frames, methodNode.instructions, richControlFlow.controlFlow) : + null; - NotNullLazyValue> outEquation = new NotNullLazyValue>() { - @NotNull - @Override - protected Equation compute() { - if (origins.getValue() != null) { - try { - return new InOutAnalysis(richControlFlow, new Out(), origins.getValue(), stable).analyze(); - } - catch (AnalyzerException ignored) { - } - } - return new Equation(new Key(method, new Out(), stable), FINAL_TOP); - } - }; + Equation outEquation = + isInterestingResult ? + new InOutAnalysis(richControlFlow, Out, origins, stable).analyze() : + null; if (isReferenceResult) { - result.add(outEquation.getValue()); + result.add(outEquation); + result.add(new Equation(new Key(method, NullableOut, stable), NullableMethodAnalysis.analyze(methodNode, origins, jsr))); } for (int i = 0; i < argumentTypes.length; i++) { @@ -259,47 +242,31 @@ public class ClassDataIndexer implements DataIndexer..." analysis - result.add(new InOutAnalysis(richControlFlow, new InOut(i, Value.Null), origins.getValue(), stable).analyze()); - } - else { - // @NotNull, so "null->fail" - result.add(new Equation(new Key(method, new InOut(i, Value.Null), stable), FINAL_BOT)); - } - result.add(new InOutAnalysis(richControlFlow, new InOut(i, Value.NotNull), origins.getValue(), stable).analyze()); + if (!notNullParam) { + // may be null on some branch, running "null->..." analysis + result.add(new InOutAnalysis(richControlFlow, new InOut(i, Value.Null), origins, stable).analyze()); } else { - // result origins analysis failed, approximating to Top - result.add(new Equation(new Key(method, new InOut(i, Value.Null), stable), FINAL_TOP)); - result.add(new Equation(new Key(method, new InOut(i, Value.NotNull), stable), FINAL_TOP)); + // @NotNull, so "null->fail" + result.add(new Equation(new Key(method, new InOut(i, Value.Null), stable), FINAL_BOT)); } + result.add(new InOutAnalysis(richControlFlow, new InOut(i, Value.NotNull), origins, stable).analyze()); } else { // parameter is not leaking, so a contract is the same as for the whole method - result.add(new Equation(new Key(method, new InOut(i, Value.Null), stable), outEquation.getValue().rhs)); - result.add(new Equation(new Key(method, new InOut(i, Value.NotNull), stable), outEquation.getValue().rhs)); + result.add(new Equation(new Key(method, new InOut(i, Value.Null), stable), outEquation.rhs)); + result.add(new Equation(new Key(method, new InOut(i, Value.NotNull), stable), outEquation.rhs)); } } if (ASMUtils.isBooleanType(argumentTypes[i]) && isInterestingResult) { if (leakingParameters[i]) { - if (origins.getValue() != null) { - // result origins analysis was ok - result.add(new InOutAnalysis(richControlFlow, new InOut(i, Value.False), origins.getValue(), stable).analyze()); - result.add(new InOutAnalysis(richControlFlow, new InOut(i, Value.True), origins.getValue(), stable).analyze()); - } - else { - // result origins analysis failed, approximating to Top - result.add(new Equation(new Key(method, new InOut(i, Value.False), stable), FINAL_TOP)); - result.add(new Equation(new Key(method, new InOut(i, Value.True), stable), FINAL_TOP)); - } + result.add(new InOutAnalysis(richControlFlow, new InOut(i, Value.False), origins, stable).analyze()); + result.add(new InOutAnalysis(richControlFlow, new InOut(i, Value.True), origins, stable).analyze()); } else { // parameter is not leaking, so a contract is the same as for the whole method - result.add(new Equation(new Key(method, new InOut(i, Value.False), stable), outEquation.getValue().rhs)); - result.add(new Equation(new Key(method, new InOut(i, Value.True), stable), outEquation.getValue().rhs)); + result.add(new Equation(new Key(method, new InOut(i, Value.False), stable), outEquation.rhs)); + result.add(new Equation(new Key(method, new InOut(i, Value.True), stable), outEquation.rhs)); } } } @@ -307,16 +274,17 @@ public class ClassDataIndexer implements DataIndexer> processNonBranchingMethod(Method method, - Type[] argumentTypes, - ControlFlowGraph graph, - boolean isReferenceResult, - boolean isBooleanResult, - boolean stable) throws AnalyzerException { - List> result = new ArrayList>(argumentTypes.length * 4 + 1); - CombinedSingleAnalysis analyzer = new CombinedSingleAnalysis(method, graph); + Type[] argumentTypes, + ControlFlowGraph graph, + boolean isReferenceResult, + boolean isBooleanResult, + boolean stable) throws AnalyzerException { + List> result = new ArrayList>(argumentTypes.length * 4 + 2); + CombinedAnalysis analyzer = new CombinedAnalysis(method, graph); analyzer.analyze(); if (isReferenceResult) { result.add(analyzer.outContractEquation(stable)); + result.add(analyzer.nullableResultEquation(stable)); } for (int i = 0; i < argumentTypes.length; i++) { Type argType = argumentTypes[i]; @@ -338,13 +306,14 @@ public class ClassDataIndexer implements DataIndexer> topEquations(Method method, - Type[] argumentTypes, - boolean isReferenceResult, - boolean isInterestingResult, - boolean stable) { - List> result = new ArrayList>(argumentTypes.length * 3 + 1); + Type[] argumentTypes, + boolean isReferenceResult, + boolean isInterestingResult, + boolean stable) { + List> result = new ArrayList>(argumentTypes.length * 4 + 2); if (isReferenceResult) { - result.add(new Equation(new Key(method, new Out(), stable), FINAL_TOP)); + result.add(new Equation(new Key(method, Out, stable), FINAL_TOP)); + result.add(new Equation(new Key(method, NullableOut, stable), FINAL_BOT)); } for (int i = 0; i < argumentTypes.length; i++) { Type argType = argumentTypes[i]; diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Combined.java b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Combined.java index 645c00260af1..dd10492785e7 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Combined.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Combined.java @@ -34,64 +34,116 @@ import java.util.Set; import static com.intellij.codeInspection.bytecodeAnalysis.AbstractValues.*; import static org.jetbrains.org.objectweb.asm.Opcodes.*; +import static com.intellij.codeInspection.bytecodeAnalysis.Direction.*; +import static com.intellij.codeInspection.bytecodeAnalysis.CombinedData.*; -final class ParamKey { - final Method method; - final int i; - final boolean stable; +// additional data structures for combined analysis +interface CombinedData { + final class ParamKey { + final Method method; + final int i; + final boolean stable; - ParamKey(Method method, int i, boolean stable) { - this.method = method; - this.i = i; - this.stable = stable; - } + ParamKey(Method method, int i, boolean stable) { + this.method = method; + this.i = i; + this.stable = stable; + } - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; - ParamKey paramKey = (ParamKey)o; + ParamKey paramKey = (ParamKey)o; - if (i != paramKey.i) return false; - if (stable != paramKey.stable) return false; - if (!method.equals(paramKey.method)) return false; + if (i != paramKey.i) return false; + if (stable != paramKey.stable) return false; + if (!method.equals(paramKey.method)) return false; - return true; + return true; + } + + @Override + public int hashCode() { + int result = method.hashCode(); + result = 31 * result + i; + result = 31 * result + (stable ? 1 : 0); + return result; + } } - @Override - public int hashCode() { - int result = method.hashCode(); - result = 31 * result + i; - result = 31 * result + (stable ? 1 : 0); - return result; + // value knowing at which instruction it was created + interface Trackable { + int getOriginInsnIndex(); } -} -final class CombinedCall extends BasicValue { - final Method method; - final boolean stableCall; - final List args; + final class TrackableCallValue extends BasicValue implements Trackable { + private final int originInsnIndex; + final Method method; + final List args; + final boolean stableCall; + final boolean thisCall; + + TrackableCallValue(int originInsnIndex, Type tp, Method method, List args, boolean stableCall, boolean thisCall) { + super(tp); + this.originInsnIndex = originInsnIndex; + this.method = method; + this.args = args; + this.stableCall = stableCall; + this.thisCall = thisCall; + } - CombinedCall(Type tp, Method method, boolean stableCall, List args) { - super(tp); - this.method = method; - this.stableCall = stableCall; - this.args = args; + @Override + public int getOriginInsnIndex() { + return originInsnIndex; + } } -} -final class NParamValue extends BasicValue { - final int n; - public NParamValue(Type type, int n) { - super(type); - this.n = n; + final class NthParamValue extends BasicValue { + final int n; + + public NthParamValue(Type type, int n) { + super(type); + this.n = n; + } + } + + final class TrackableNullValue extends BasicValue implements Trackable { + static final Type NullType = Type.getObjectType("null"); + private final int originInsnIndex; + public TrackableNullValue(int originInsnIndex) { + super(NullType); + this.originInsnIndex = originInsnIndex; + } + + @Override + public int getOriginInsnIndex() { + return originInsnIndex; + } } + + final class TrackableValue extends BasicValue implements Trackable { + private final int originInsnIndex; + + public TrackableValue(int originInsnIndex, Type type) { + super(type); + this.originInsnIndex = originInsnIndex; + } + + @Override + public int getOriginInsnIndex() { + return originInsnIndex; + } + } + + BasicValue ThisValue = new BasicValue(Type.getObjectType("java/lang/Object")); } -final class CombinedSingleAnalysis { +// specialized class for analyzing methods without branching in single pass +final class CombinedAnalysis { + private final ControlFlowGraph controlFlow; private final Method method; private final CombinedInterpreter interpreter; @@ -99,11 +151,11 @@ final class CombinedSingleAnalysis { private boolean exception; private final MethodNode methodNode; - CombinedSingleAnalysis(Method method, ControlFlowGraph controlFlow) { + CombinedAnalysis(Method method, ControlFlowGraph controlFlow) { this.method = method; this.controlFlow = controlFlow; methodNode = controlFlow.methodNode; - interpreter = new CombinedInterpreter(Type.getArgumentTypes(methodNode.desc).length); + interpreter = new CombinedInterpreter(methodNode.instructions, Type.getArgumentTypes(methodNode.desc).length); } final void analyze() throws AnalyzerException { @@ -144,11 +196,11 @@ final class CombinedSingleAnalysis { final Equation notNullParamEquation(int i, boolean stable) { final Key key = new Key(method, new In(i, In.NOT_NULL), stable); final Result result; - if (interpreter.dereferenced[i]) { + if (interpreter.dereferencedParams[i]) { result = new Final(Value.NotNull); } else { - Set calls = interpreter.callDerefs[i]; + Set calls = interpreter.parameterFlow[i]; if (calls == null || calls.isEmpty()) { result = new Final(Value.Top); } @@ -166,11 +218,11 @@ final class CombinedSingleAnalysis { final Equation nullableParamEquation(int i, boolean stable) { final Key key = new Key(method, new In(i, In.NULLABLE), stable); final Result result; - if (interpreter.dereferenced[i] || interpreter.notNullable[i] || returnValue instanceof NParamValue && ((NParamValue)returnValue).n == i) { + if (interpreter.dereferencedParams[i] || interpreter.notNullableParams[i] || returnValue instanceof NthParamValue && ((NthParamValue)returnValue).n == i) { result = new Final(Value.Top); } else { - Set calls = interpreter.callDerefs[i]; + Set calls = interpreter.parameterFlow[i]; if (calls == null || calls.isEmpty()) { result = new Final(Value.Null); } @@ -188,7 +240,7 @@ final class CombinedSingleAnalysis { final Equation contractEquation(int i, Value inValue, boolean stable) { final Key key = new Key(method, new InOut(i, inValue), stable); final Result result; - if (exception || (inValue == Value.Null && interpreter.dereferenced[i])) { + if (exception || (inValue == Value.Null && interpreter.dereferencedParams[i])) { result = new Final(Value.Bot); } else if (FalseValue == returnValue) { @@ -197,29 +249,29 @@ final class CombinedSingleAnalysis { else if (TrueValue == returnValue) { result = new Final(Value.True); } - else if (NullValue == returnValue) { + else if (returnValue instanceof TrackableNullValue) { result = new Final(Value.Null); } - else if (returnValue instanceof NotNullValue) { + else if (returnValue instanceof NotNullValue || ThisValue == returnValue) { result = new Final(Value.NotNull); } - else if (returnValue instanceof NParamValue && ((NParamValue)returnValue).n == i) { + else if (returnValue instanceof NthParamValue && ((NthParamValue)returnValue).n == i) { result = new Final(inValue); } - else if (returnValue instanceof CombinedCall) { - CombinedCall call = (CombinedCall)returnValue; + else if (returnValue instanceof TrackableCallValue) { + TrackableCallValue call = (TrackableCallValue)returnValue; HashSet keys = new HashSet(); for (int argI = 0; argI < call.args.size(); argI++) { BasicValue arg = call.args.get(argI); - if (arg instanceof NParamValue) { - NParamValue npv = (NParamValue)arg; + if (arg instanceof NthParamValue) { + NthParamValue npv = (NthParamValue)arg; if (npv.n == i) { keys.add(new Key(call.method, new InOut(argI, inValue), call.stableCall)); } } } if (ASMUtils.isReferenceType(call.getType())) { - keys.add(new Key(call.method, new Out(), call.stableCall)); + keys.add(new Key(call.method, Out, call.stableCall)); } if (keys.isEmpty()) { result = new Final(Value.Top); @@ -234,7 +286,7 @@ final class CombinedSingleAnalysis { } final Equation outContractEquation(boolean stable) { - final Key key = new Key(method, new Out(), stable); + final Key key = new Key(method, Out, stable); final Result result; if (exception) { result = new Final(Value.Bot); @@ -245,15 +297,15 @@ final class CombinedSingleAnalysis { else if (TrueValue == returnValue) { result = new Final(Value.True); } - else if (NullValue == returnValue) { + else if (returnValue instanceof TrackableNullValue) { result = new Final(Value.Null); } - else if (returnValue instanceof NotNullValue) { + else if (returnValue instanceof NotNullValue || returnValue == ThisValue) { result = new Final(Value.NotNull); } - else if (returnValue instanceof CombinedCall) { - CombinedCall call = (CombinedCall)returnValue; - Key callKey = new Key(call.method, new Out(), call.stableCall); + else if (returnValue instanceof TrackableCallValue) { + TrackableCallValue call = (TrackableCallValue)returnValue; + Key callKey = new Key(call.method, Out, call.stableCall); Set keys = new SingletonSet(callKey); result = new Pending(new SingletonSet>(new Product(Value.Top, keys))); } @@ -263,6 +315,28 @@ final class CombinedSingleAnalysis { return new Equation(key, result); } + final Equation nullableResultEquation(boolean stable) { + final Key key = new Key(method, NullableOut, stable); + final Result result; + if (exception || + returnValue instanceof Trackable && interpreter.dereferencedValues[((Trackable)returnValue).getOriginInsnIndex()]) { + result = new Final(Value.Bot); + } + else if (returnValue instanceof TrackableCallValue) { + TrackableCallValue call = (TrackableCallValue)returnValue; + Key callKey = new Key(call.method, NullableOut, call.stableCall || call.thisCall); + Set keys = new SingletonSet(callKey); + result = new Pending(new SingletonSet>(new Product(Value.Null, keys))); + } + else if (returnValue instanceof TrackableNullValue) { + result = new Final(Value.Null); + } + else { + result = new Final(Value.Bot); + } + return new Equation(key, result); + } + final Frame createStartFrame() { Frame frame = new Frame(methodNode.maxLocals, methodNode.maxStack); Type returnType = Type.getReturnType(methodNode.desc); @@ -272,10 +346,10 @@ final class CombinedSingleAnalysis { Type[] args = Type.getArgumentTypes(methodNode.desc); int local = 0; if ((methodNode.access & Opcodes.ACC_STATIC) == 0) { - frame.setLocal(local++, new AbstractValues.NotNullValue(Type.getObjectType(controlFlow.className))); + frame.setLocal(local++, ThisValue); } for (int i = 0; i < args.length; i++) { - BasicValue value = new NParamValue(args[i], i); + BasicValue value = new NthParamValue(args[i], i); frame.setLocal(local++, value); if (args[i].getSize() == 2) { frame.setLocal(local++, BasicValue.UNINITIALIZED_VALUE); @@ -289,25 +363,47 @@ final class CombinedSingleAnalysis { } final class CombinedInterpreter extends BasicInterpreter { - final boolean[] dereferenced; - final boolean[] notNullable; - final Set[] callDerefs; - - CombinedInterpreter(int arity) { - dereferenced = new boolean[arity]; - notNullable = new boolean[arity]; - callDerefs = new Set[arity]; + // Parameters dereferenced during execution of a method, tracked by parameter's indices. + // Dereferenced parameters are @NotNull. + final boolean[] dereferencedParams; + // Parameters, that are written to something or passed to an interface methods. + // This parameters cannot be @Nullable. + final boolean[] notNullableParams; + // parameterFlow(i) for i-th parameter stores a set parameter positions it is passed to + // parameter is @NotNull if any of its usages are @NotNull + final Set[] parameterFlow; + + // Trackable values that were dereferenced during execution of a method + // Values are are identified by `origin` index + final boolean[] dereferencedValues; + private final InsnList insns; + + CombinedInterpreter(InsnList insns, int arity) { + dereferencedParams = new boolean[arity]; + notNullableParams = new boolean[arity]; + parameterFlow = new Set[arity]; + this.insns = insns; + dereferencedValues = new boolean[insns.size()]; + } + + private int insnIndex(AbstractInsnNode insn) { + return insns.indexOf(insn); + } + + private static BasicValue track(int origin, BasicValue basicValue) { + return basicValue == null ? null : new TrackableValue(origin, basicValue.getType()); } @Override public BasicValue newOperation(AbstractInsnNode insn) throws AnalyzerException { + int origin = insnIndex(insn); switch (insn.getOpcode()) { case ICONST_0: return FalseValue; case ICONST_1: return TrueValue; case ACONST_NULL: - return NullValue; + return new TrackableNullValue(origin); case LDC: Object cst = ((LdcInsnNode)insn).cst; if (cst instanceof Type) { @@ -330,22 +426,26 @@ final class CombinedInterpreter extends BasicInterpreter { return new NotNullValue(Type.getObjectType(((TypeInsnNode)insn).desc)); default: } - return super.newOperation(insn); + return track(origin, super.newOperation(insn)); } @Override public BasicValue unaryOperation(AbstractInsnNode insn, BasicValue value) throws AnalyzerException { + int origin = insnIndex(insn); switch (insn.getOpcode()) { case GETFIELD: case ARRAYLENGTH: case MONITORENTER: - if (value instanceof NParamValue) { - dereferenced[((NParamValue)value).n] = true; + if (value instanceof NthParamValue) { + dereferencedParams[((NthParamValue)value).n] = true; } - return super.unaryOperation(insn, value); + if (value instanceof Trackable) { + dereferencedValues[((Trackable)value).getOriginInsnIndex()] = true; + } + return track(origin, super.unaryOperation(insn, value)); case CHECKCAST: - if (value instanceof NParamValue) { - return new NParamValue(Type.getObjectType(((TypeInsnNode)insn).desc), ((NParamValue)value).n); + if (value instanceof NthParamValue) { + return new NthParamValue(Type.getObjectType(((TypeInsnNode)insn).desc), ((NthParamValue)value).n); } break; case NEWARRAY: @@ -353,12 +453,23 @@ final class CombinedInterpreter extends BasicInterpreter { return new NotNullValue(super.unaryOperation(insn, value).getType()); default: } - return super.unaryOperation(insn, value); + return track(origin, super.unaryOperation(insn, value)); } @Override public BasicValue binaryOperation(AbstractInsnNode insn, BasicValue value1, BasicValue value2) throws AnalyzerException { switch (insn.getOpcode()) { + case PUTFIELD: + if (value1 instanceof NthParamValue) { + dereferencedParams[((NthParamValue)value1).n] = true; + } + if (value1 instanceof Trackable) { + dereferencedValues[((Trackable)value1).getOriginInsnIndex()] = true; + } + if (value2 instanceof NthParamValue) { + notNullableParams[((NthParamValue)value2).n] = true; + } + break; case IALOAD: case LALOAD: case FALOAD: @@ -367,21 +478,16 @@ final class CombinedInterpreter extends BasicInterpreter { case BALOAD: case CALOAD: case SALOAD: - if (value1 instanceof NParamValue) { - dereferenced[((NParamValue)value1).n] = true; - } - break; - case PUTFIELD: - if (value1 instanceof NParamValue) { - dereferenced[((NParamValue)value1).n] = true; + if (value1 instanceof NthParamValue) { + dereferencedParams[((NthParamValue)value1).n] = true; } - if (value2 instanceof NParamValue) { - notNullable[((NParamValue)value2).n] = true; + if (value1 instanceof Trackable) { + dereferencedValues[((Trackable)value1).getOriginInsnIndex()] = true; } break; default: } - return super.binaryOperation(insn, value1, value2); + return track(insnIndex(insn), super.binaryOperation(insn, value1, value2)); } @Override @@ -395,35 +501,46 @@ final class CombinedInterpreter extends BasicInterpreter { case BASTORE: case CASTORE: case SASTORE: - if (value1 instanceof NParamValue) { - dereferenced[((NParamValue)value1).n] = true; + if (value1 instanceof NthParamValue) { + dereferencedParams[((NthParamValue)value1).n] = true; + } + if (value1 instanceof Trackable) { + dereferencedValues[((Trackable)value1).getOriginInsnIndex()] = true; } break; case AASTORE: - if (value1 instanceof NParamValue) { - dereferenced[((NParamValue)value1).n] = true; + if (value1 instanceof NthParamValue) { + dereferencedParams[((NthParamValue)value1).n] = true; + } + if (value1 instanceof Trackable) { + dereferencedValues[((Trackable)value1).getOriginInsnIndex()] = true; } - if (value3 instanceof NParamValue) { - notNullable[((NParamValue)value3).n] = true; + if (value3 instanceof NthParamValue) { + notNullableParams[((NthParamValue)value3).n] = true; } break; default: } - return super.ternaryOperation(insn, value1, value2, value3); + return null; } @Override public BasicValue naryOperation(AbstractInsnNode insn, List values) throws AnalyzerException { int opCode = insn.getOpcode(); int shift = opCode == INVOKESTATIC ? 0 : 1; - + int origin = insnIndex(insn); switch (opCode) { case INVOKESPECIAL: case INVOKEINTERFACE: case INVOKEVIRTUAL: - if (values.get(0) instanceof NParamValue) { - dereferenced[((NParamValue)values.get(0)).n] = true; + BasicValue receiver = values.get(0); + if (receiver instanceof NthParamValue) { + dereferencedParams[((NthParamValue)receiver).n] = true; + } + if (receiver instanceof Trackable) { + dereferencedValues[((Trackable)receiver).getOriginInsnIndex()] = true; } + default: } switch (opCode) { @@ -437,29 +554,31 @@ final class CombinedInterpreter extends BasicInterpreter { Type retType = Type.getReturnType(mNode.desc); for (int i = shift; i < values.size(); i++) { - if (values.get(i) instanceof NParamValue) { - int n = ((NParamValue)values.get(i)).n; + if (values.get(i) instanceof NthParamValue) { + int n = ((NthParamValue)values.get(i)).n; if (opCode == INVOKEINTERFACE) { - notNullable[n] = true; + notNullableParams[n] = true; } else { - Set npKeys = callDerefs[n]; + Set npKeys = parameterFlow[n]; if (npKeys == null) { npKeys = new HashSet(); - callDerefs[n] = npKeys; + parameterFlow[n] = npKeys; } npKeys.add(new ParamKey(method, i - shift, stable)); } } } + BasicValue receiver = null; if (shift == 1) { - values.remove(0); + receiver = values.remove(0); } - return new CombinedCall(retType, method, stable, values); + boolean thisCall = (opCode == INVOKEINTERFACE || opCode == INVOKEVIRTUAL) && receiver == ThisValue; + return new TrackableCallValue(origin, retType, method, values, stable, thisCall); case MULTIANEWARRAY: return new NotNullValue(super.naryOperation(insn, values).getType()); default: } - return super.naryOperation(insn, values); + return track(origin, super.naryOperation(insn, values)); } } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Contracts.java b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Contracts.java index c382148abb05..73972871ee7b 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Contracts.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Contracts.java @@ -34,6 +34,7 @@ import java.util.Set; import static com.intellij.codeInspection.bytecodeAnalysis.AbstractValues.*; import static org.jetbrains.org.objectweb.asm.Opcodes.*; +import static com.intellij.codeInspection.bytecodeAnalysis.Direction.*; class InOutAnalysis extends Analysis> { @@ -396,7 +397,7 @@ class InOutInterpreter extends BasicInterpreter { } default: } - return super.ternaryOperation(insn, value1, value2, value3); + return null; } @Override @@ -436,7 +437,7 @@ class InOutInterpreter extends BasicInterpreter { } } if (isRefRetType) { - keys.add(new Key(method, new Out(), stable)); + keys.add(new Key(method, Out, stable)); } if (!keys.isEmpty()) { return new CallResultValue(retType, keys); @@ -444,7 +445,7 @@ class InOutInterpreter extends BasicInterpreter { } else if (isRefRetType) { HashSet keys = new HashSet(); - keys.add(new Key(method, new Out(), stable)); + keys.add(new Key(method, Out, stable)); return new CallResultValue(retType, keys); } } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Data.java b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Data.java index 55a842af684c..b76e31197482 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Data.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Data.java @@ -52,103 +52,109 @@ enum Value { Bot, NotNull, Null, True, False, Top } -interface Direction {} - -final class In implements Direction { - static final int NOT_NULL = 0; - static final int NULLABLE = 1; - final int paramIndex; - final int nullityMask; - - In(int paramIndex, int nullityMask) { - this.paramIndex = paramIndex; - this.nullityMask = nullityMask; - } - - @Override - public String toString() { - return "In " + paramIndex; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - In in = (In) o; - if (paramIndex != in.paramIndex) return false; - if (nullityMask != in.nullityMask) return false; - return true; - } - - @Override - public int hashCode() { - return 31*paramIndex + nullityMask; - } - - public int paramId() { - return paramIndex; - } - -} - -final class InOut implements Direction { - final int paramIndex; - final Value inValue; - - InOut(int paramIndex, Value inValue) { - this.paramIndex = paramIndex; - this.inValue = inValue; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - InOut inOut = (InOut) o; - - if (paramIndex != inOut.paramIndex) return false; - if (inValue != inOut.inValue) return false; - - return true; - } - - @Override - public int hashCode() { - int result = paramIndex; - result = 31 * result + inValue.ordinal(); - return result; - } - - @Override - public String toString() { - return "InOut " + paramIndex + " " + inValue.toString(); - } - - public int paramId() { - return paramIndex; - } - - public int valueId() { - return inValue.ordinal(); - } -} - -final class Out implements Direction { - @Override - public String toString() { - return "Out"; - } - - @Override - public int hashCode() { - return 1; - } - - @Override - public boolean equals(Object obj) { - return obj instanceof Out; - } +interface Direction { + final class In implements Direction { + static final int NOT_NULL = 0; + static final int NULLABLE = 1; + final int paramIndex; + final int nullityMask; + + In(int paramIndex, int nullityMask) { + this.paramIndex = paramIndex; + this.nullityMask = nullityMask; + } + + @Override + public String toString() { + return "In " + paramIndex; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + In in = (In)o; + if (paramIndex != in.paramIndex) return false; + if (nullityMask != in.nullityMask) return false; + return true; + } + + @Override + public int hashCode() { + return 31 * paramIndex + nullityMask; + } + + public int paramId() { + return paramIndex; + } + } + + final class InOut implements Direction { + final int paramIndex; + final Value inValue; + + InOut(int paramIndex, Value inValue) { + this.paramIndex = paramIndex; + this.inValue = inValue; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + InOut inOut = (InOut)o; + + if (paramIndex != inOut.paramIndex) return false; + if (inValue != inOut.inValue) return false; + + return true; + } + + @Override + public int hashCode() { + int result = paramIndex; + result = 31 * result + inValue.ordinal(); + return result; + } + + @Override + public String toString() { + return "InOut " + paramIndex + " " + inValue.toString(); + } + + public int paramId() { + return paramIndex; + } + + public int valueId() { + return inValue.ordinal(); + } + } + + Direction Out = new Direction() { + @Override + public String toString() { + return "Out"; + } + + @Override + public int hashCode() { + return -1; + } + }; + + Direction NullableOut = new Direction() { + @Override + public String toString() { + return "NullableOut"; + } + + @Override + public int hashCode() { + return -2; + } + }; } final class Key { diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/NullableMethodAnalysis.java b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/NullableMethodAnalysis.java new file mode 100644 index 000000000000..cf2e8d7a1da4 --- /dev/null +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/NullableMethodAnalysis.java @@ -0,0 +1,423 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.codeInspection.bytecodeAnalysis; + +import com.intellij.codeInspection.bytecodeAnalysis.asm.AnalyzerExt; +import com.intellij.codeInspection.bytecodeAnalysis.asm.InterpreterExt; +import com.intellij.codeInspection.bytecodeAnalysis.asm.LiteAnalyzerExt; +import org.jetbrains.org.objectweb.asm.Opcodes; +import org.jetbrains.org.objectweb.asm.Type; +import org.jetbrains.org.objectweb.asm.tree.*; +import org.jetbrains.org.objectweb.asm.tree.analysis.AnalyzerException; +import org.jetbrains.org.objectweb.asm.tree.analysis.BasicInterpreter; +import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue; +import org.jetbrains.org.objectweb.asm.tree.analysis.Frame; + +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import static com.intellij.codeInspection.bytecodeAnalysis.NullableMethodAnalysisData.*; + +interface NullableMethodAnalysisData { + Type NullType = Type.getObjectType("null"); + Type ThisType = Type.getObjectType("this"); + Type CallType = Type.getObjectType("/Call"); + + final class LabeledNull extends BasicValue { + final int origins; + + public LabeledNull(int origins) { + super(NullType); + this.origins = origins; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + LabeledNull that = (LabeledNull)o; + return origins == that.origins; + } + + @Override + public int hashCode() { + return origins; + } + } + + final class Calls extends BasicValue { + final int mergedLabels; + + public Calls(int mergedLabels) { + super(CallType); + this.mergedLabels = mergedLabels; + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + Calls calls = (Calls)o; + return mergedLabels == calls.mergedLabels; + } + + @Override + public int hashCode() { + return mergedLabels; + } + } + + final class Constraint { + final static Constraint EMPTY = new Constraint(0, 0); + + final int calls; + final int nulls; + + public Constraint(int calls, int nulls) { + this.calls = calls; + this.nulls = nulls; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Constraint that = (Constraint)o; + + if (calls != that.calls) return false; + if (nulls != that.nulls) return false; + + return true; + } + + @Override + public int hashCode() { + int result = calls; + result = 31 * result + nulls; + return result; + } + } + + BasicValue ThisValue = new BasicValue(ThisType); +} + +class NullableMethodAnalysis { + + static Result FinalNull = new Final(Value.Null); + static Result FinalBot = new Final(Value.Bot); + static BasicValue lNull = new LabeledNull(0); + + static Result analyze(MethodNode methodNode, boolean[] origins, boolean jsr) throws AnalyzerException { + InsnList insns = methodNode.instructions; + Constraint[] data = new Constraint[insns.size()]; + int[] originsMapping = mapOrigins(origins); + + NullableMethodInterpreter interpreter = new NullableMethodInterpreter(insns, origins, originsMapping); + Frame[] frames = + jsr ? + new AnalyzerExt(interpreter, data, Constraint.EMPTY).analyze("this", methodNode) : + new LiteAnalyzerExt(interpreter, data, Constraint.EMPTY).analyze("this", methodNode); + + BasicValue result = BasicValue.REFERENCE_VALUE; + for (int i = 0; i < frames.length; i++) { + Frame frame = frames[i]; + if (frame != null && insns.get(i).getOpcode() == Opcodes.ARETURN) { + BasicValue stackTop = frame.pop(); + result = combine(result, stackTop, data[i]); + } + } + if (result instanceof LabeledNull) { + return FinalNull; + } + if (result instanceof Calls) { + Calls calls = ((Calls)result); + int mergedMappedLabels = calls.mergedLabels; + if (mergedMappedLabels != 0) { + Set> sum = new HashSet>(); + Key[] createdKeys = interpreter.keys; + for (int origin = 0; origin < originsMapping.length; origin++) { + int mappedOrigin = originsMapping[origin]; + Key createdKey = createdKeys[origin]; + if (createdKey != null && (mergedMappedLabels & (1 << mappedOrigin)) != 0) { + sum.add(new Product(Value.Null, Collections.singleton(createdKey))); + } + } + if (!sum.isEmpty()) { + return new Pending(sum); + } + } + } + return FinalBot; + } + + private static int[] mapOrigins(boolean[] origins) { + int[] originsMapping = new int[origins.length]; + int mapped = 0; + for (int i = 0; i < origins.length; i++) { + originsMapping[i] = origins[i] ? mapped++ : -1; + } + return originsMapping; + } + + static BasicValue combine(BasicValue v1, BasicValue v2, Constraint constraint) { + if (v1 instanceof LabeledNull) { + return lNull; + } + else if (v2 instanceof LabeledNull) { + int v2Origins = ((LabeledNull)v2).origins; + int constraintOrigins = constraint.nulls; + int intersect = v2Origins & constraintOrigins; + return intersect == v2Origins ? v1 : lNull; + } + else if (v1 instanceof Calls) { + if (v2 instanceof Calls) { + Calls calls1 = (Calls)v1; + Calls calls2 = (Calls)v2; + int labels2 = calls2.mergedLabels; + int aliveLabels2 = labels2 - (labels2 & constraint.calls); + return new Calls(calls1.mergedLabels | aliveLabels2); + } else { + return v1; + } + } + else if (v2 instanceof Calls) { + Calls calls2 = (Calls)v2; + int labels2 = calls2.mergedLabels; + int aliveLabels2 = labels2 - (labels2 & constraint.calls); + return new Calls(aliveLabels2); + } + return BasicValue.REFERENCE_VALUE; + } +} + +class NullableMethodInterpreter extends BasicInterpreter implements InterpreterExt { + final InsnList insns; + final boolean[] origins; + private final int[] originsMapping; + final Key[] keys; + + Constraint constraint = null; + int delta = 0; + int nullsDelta = 0; + int notNullInsn = -1; + int notNullCall = 0; + int notNullNull = 0; + + NullableMethodInterpreter(InsnList insns, boolean[] origins, int[] originsMapping) { + this.insns = insns; + this.origins = origins; + this.originsMapping = originsMapping; + keys = new Key[originsMapping.length]; + } + + @Override + public BasicValue newValue(Type type) { + return ThisType.equals(type) ? ThisValue : super.newValue(type); + } + + @Override + public BasicValue newOperation(AbstractInsnNode insn) throws AnalyzerException { + if (insn.getOpcode() == Opcodes.ACONST_NULL) { + int insnIndex = insns.indexOf(insn); + if (origins[insnIndex]) { + return new LabeledNull(1 << originsMapping[insnIndex]); + } + } + return super.newOperation(insn); + } + + @Override + public BasicValue unaryOperation(AbstractInsnNode insn, BasicValue value) throws AnalyzerException { + switch (insn.getOpcode()) { + case GETFIELD: + case ARRAYLENGTH: + case MONITORENTER: + if (value instanceof Calls) { + delta = ((Calls)value).mergedLabels; + } + break; + case IFNULL: + if (value instanceof Calls) { + notNullInsn = insns.indexOf(insn) + 1; + notNullCall = ((Calls)value).mergedLabels; + } + else if (value instanceof LabeledNull) { + notNullInsn = insns.indexOf(insn) + 1; + notNullNull = ((LabeledNull)value).origins; + } + break; + case IFNONNULL: + if (value instanceof Calls) { + notNullInsn = insns.indexOf(((JumpInsnNode)insn).label); + notNullCall = ((Calls)value).mergedLabels; + } + else if (value instanceof LabeledNull) { + notNullInsn = insns.indexOf(((JumpInsnNode)insn).label); + notNullNull = ((LabeledNull)value).origins; + } + break; + default: + + } + return super.unaryOperation(insn, value); + } + + @Override + public BasicValue binaryOperation(AbstractInsnNode insn, BasicValue value1, BasicValue value2) throws AnalyzerException { + switch (insn.getOpcode()) { + case PUTFIELD: + case IALOAD: + case LALOAD: + case FALOAD: + case DALOAD: + case AALOAD: + case BALOAD: + case CALOAD: + case SALOAD: + if (value1 instanceof Calls) { + delta = ((Calls)value1).mergedLabels; + } + if (value1 instanceof LabeledNull){ + nullsDelta = ((LabeledNull)value1).origins; + } + break; + default: + } + return super.binaryOperation(insn, value1, value2); + } + + @Override + public BasicValue ternaryOperation(AbstractInsnNode insn, BasicValue value1, BasicValue value2, BasicValue value3) + throws AnalyzerException { + if (value1 instanceof Calls) { + delta = ((Calls)value1).mergedLabels; + } + if (value1 instanceof LabeledNull){ + nullsDelta = ((LabeledNull)value1).origins; + } + return null; + } + + @Override + public BasicValue naryOperation(AbstractInsnNode insn, List values) throws AnalyzerException { + int opCode = insn.getOpcode(); + switch (opCode) { + case INVOKESPECIAL: + case INVOKEINTERFACE: + case INVOKEVIRTUAL: + BasicValue receiver = values.get(0); + if (receiver instanceof Calls) { + delta = ((Calls)receiver).mergedLabels; + } + if (receiver instanceof LabeledNull){ + nullsDelta = ((LabeledNull)receiver).origins; + } + break; + default: + } + + switch (opCode) { + case INVOKESTATIC: + case INVOKESPECIAL: + case INVOKEVIRTUAL: + int insnIndex = insns.indexOf(insn); + if (origins[insnIndex]) { + boolean stable = (opCode == INVOKESTATIC) || + (opCode == INVOKESPECIAL) || + (values.get(0) == ThisValue); + MethodInsnNode mNode = ((MethodInsnNode)insn); + Method method = new Method(mNode.owner, mNode.name, mNode.desc); + int label = 1 << originsMapping[insnIndex]; + if (keys[insnIndex] == null) { + keys[insnIndex] = new Key(method, Direction.NullableOut, stable); + } + return new Calls(label); + } + break; + default: + } + return super.naryOperation(insn, values); + } + + @Override + public BasicValue merge(BasicValue v1, BasicValue v2) { + if (v1 instanceof LabeledNull) { + if (v2 instanceof LabeledNull) { + return new LabeledNull(((LabeledNull)v1).origins | ((LabeledNull)v2).origins); + } + else { + return v1; + } + } + else if (v2 instanceof LabeledNull) { + return v2; + } + else if (v1 instanceof Calls) { + if (v2 instanceof Calls) { + Calls calls1 = (Calls)v1; + Calls calls2 = (Calls)v2; + return new Calls(calls1.mergedLabels | calls2.mergedLabels); + } + else { + return v1; + } + } + else if (v2 instanceof Calls) { + return v2; + } + return super.merge(v1, v2); + } + + // ---------- InterpreterExt -------------- + + @Override + public void init(Constraint previous) { + constraint = previous; + delta = 0; + nullsDelta = 0; + + notNullInsn = -1; + notNullCall = 0; + notNullNull = 0; + } + + @Override + public Constraint getAfterData(int insn) { + Constraint afterData = mkAfterData(); + if (notNullInsn == insn) { + return new Constraint(afterData.calls | notNullCall, afterData.nulls | notNullNull); + } + return afterData; + } + + private Constraint mkAfterData() { + if (delta == 0 && nullsDelta == 0 && notNullInsn == -1) { + return constraint; + } + return new Constraint(constraint.calls | delta, constraint.nulls | nullsDelta); + } + + @Override + public Constraint merge(Constraint data1, Constraint data2) { + if (data1.equals(data2)) { + return data1; + } else { + return new Constraint(data1.calls | data2.calls, data1.nulls | data2.nulls); + } + } +} diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Parameters.java b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Parameters.java index a7c25782d48b..6102c44792bf 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Parameters.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Parameters.java @@ -37,6 +37,7 @@ import static com.intellij.codeInspection.bytecodeAnalysis.AbstractValues.Instan import static com.intellij.codeInspection.bytecodeAnalysis.AbstractValues.ParamValue; import static com.intellij.codeInspection.bytecodeAnalysis.PResults.*; import static org.jetbrains.org.objectweb.asm.Opcodes.*; +import static com.intellij.codeInspection.bytecodeAnalysis.Direction.*; abstract class PResults { // SoP = sum of products @@ -706,7 +707,7 @@ abstract class NullityInterpreter extends BasicInterpreter { break; default: } - return super.ternaryOperation(insn, value1, value2, value3); + return null; } @Override diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/ProjectBytecodeAnalysis.java b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/ProjectBytecodeAnalysis.java index aa44951961ad..610a6fecdaf5 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/ProjectBytecodeAnalysis.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/ProjectBytecodeAnalysis.java @@ -23,6 +23,7 @@ import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.ModificationTracker; +import com.intellij.openapi.util.registry.Registry; import com.intellij.psi.*; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.search.ProjectScope; @@ -39,14 +40,18 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.*; +import static com.intellij.codeInspection.bytecodeAnalysis.Direction.*; + /** * @author lambdamix */ public class ProjectBytecodeAnalysis { public static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.bytecodeAnalysis"); public static final Key INFERRED_ANNOTATION = Key.create("INFERRED_ANNOTATION"); + public static final String NULLABLE_METHOD_TRANSITIVITY = "java.annotations.inference.nullable.method.transitivity"; public static final int EQUATIONS_LIMIT = 1000; private final Project myProject; + private final boolean nullableMethodTransitivity; public static ProjectBytecodeAnalysis getInstance(@NotNull Project project) { return ServiceManager.getService(project, ProjectBytecodeAnalysis.class); @@ -54,6 +59,7 @@ public class ProjectBytecodeAnalysis { public ProjectBytecodeAnalysis(Project project) { myProject = project; + nullableMethodTransitivity = Registry.is(NULLABLE_METHOD_TRANSITIVITY); } @Nullable @@ -101,6 +107,7 @@ public class ProjectBytecodeAnalysis { ArrayList allKeys = contractKeys((PsiMethod)listOwner, primaryKey); MethodAnnotations methodAnnotations = loadMethodAnnotations((PsiMethod)listOwner, primaryKey, allKeys); boolean notNull = methodAnnotations.notNulls.contains(primaryKey); + boolean nullable = methodAnnotations.nullables.contains(primaryKey); String contractValue = methodAnnotations.contracts.get(primaryKey); if (notNull && contractValue != null) { return new PsiAnnotation[]{ @@ -108,11 +115,22 @@ public class ProjectBytecodeAnalysis { createAnnotationFromText("@" + ControlFlowAnalyzer.ORG_JETBRAINS_ANNOTATIONS_CONTRACT + "(" + contractValue + ")") }; } + if (nullable && contractValue != null) { + return new PsiAnnotation[]{ + getNullableAnnotation(), + createAnnotationFromText("@" + ControlFlowAnalyzer.ORG_JETBRAINS_ANNOTATIONS_CONTRACT + "(" + contractValue + ")") + }; + } else if (notNull) { return new PsiAnnotation[]{ getNotNullAnnotation() }; } + else if (nullable) { + return new PsiAnnotation[]{ + getNullableAnnotation() + }; + } else if (contractValue != null) { return new PsiAnnotation[]{ createAnnotationFromText("@" + ControlFlowAnalyzer.ORG_JETBRAINS_ANNOTATIONS_CONTRACT + "(" + contractValue + ")") @@ -172,7 +190,7 @@ public class ProjectBytecodeAnalysis { public static HKey getKey(@NotNull PsiModifierListOwner owner, MessageDigest md) { LOG.assertTrue(owner instanceof PsiCompiledElement, owner); if (owner instanceof PsiMethod) { - return BytecodeAnalysisConverter.psiKey((PsiMethod)owner, new Out(), md); + return BytecodeAnalysisConverter.psiKey((PsiMethod)owner, Out, md); } if (owner instanceof PsiParameter) { PsiElement parent = owner.getParent(); @@ -196,16 +214,18 @@ public class ProjectBytecodeAnalysis { private ParameterAnnotations loadParameterAnnotations(@NotNull HKey notNullKey) throws EquationsLimitException { - final Solver notNullSolver = new Solver(new ELattice(Value.NotNull, Value.Top)); - collectEquations(Collections.singletonList(notNullKey), notNullSolver); + Map> equationsCache = new HashMap>(); + + final Solver notNullSolver = new Solver(new ELattice(Value.NotNull, Value.Top), Value.Top); + collectEquations(Collections.singletonList(notNullKey), notNullSolver, equationsCache); HashMap notNullSolutions = notNullSolver.solve(); boolean notNull = (Value.NotNull == notNullSolutions.get(notNullKey)) || (Value.NotNull == notNullSolutions.get(notNullKey.mkUnstable())); - final Solver nullableSolver = new Solver(new ELattice(Value.Null, Value.Top)); + final Solver nullableSolver = new Solver(new ELattice(Value.Null, Value.Top), Value.Top); final HKey nullableKey = new HKey(notNullKey.key, notNullKey.dirKey + 1, true); - collectEquations(Collections.singletonList(nullableKey), nullableSolver); + collectEquations(Collections.singletonList(nullableKey), nullableSolver, equationsCache); HashMap nullableSolutions = nullableSolver.solve(); boolean nullable = (Value.Null == nullableSolutions.get(nullableKey)) || (Value.Null == nullableSolutions.get(nullableKey.mkUnstable())); @@ -215,15 +235,32 @@ public class ProjectBytecodeAnalysis { private MethodAnnotations loadMethodAnnotations(@NotNull PsiMethod owner, @NotNull HKey key, ArrayList allKeys) throws EquationsLimitException { MethodAnnotations result = new MethodAnnotations(); - final Solver solver = new Solver(new ELattice(Value.Bot, Value.Top)); - collectEquations(allKeys, solver); - HashMap solutions = solver.solve(); + Map> equationsCache = new HashMap>(); + + final Solver outSolver = new Solver(new ELattice(Value.Bot, Value.Top), Value.Top); + collectEquations(allKeys, outSolver, equationsCache); + HashMap solutions = outSolver.solve(); int arity = owner.getParameterList().getParameters().length; BytecodeAnalysisConverter.addMethodAnnotations(solutions, result, key, arity); + + final Solver nullableMethodSolver = new Solver(new ELattice(Value.Bot, Value.Null), Value.Bot); + HKey nullableKey = key.updateDirection(BytecodeAnalysisConverter.mkDirectionKey(NullableOut)); + if (nullableMethodTransitivity) { + collectEquations(Collections.singletonList(nullableKey), nullableMethodSolver, equationsCache); + } + else { + collectSingleEquation(nullableKey, nullableMethodSolver, equationsCache); + } + + HashMap nullableSolutions = nullableMethodSolver.solve(); + if (nullableSolutions.get(nullableKey) == Value.Null || nullableSolutions.get(nullableKey.negate()) == Value.Null) { + result.nullables.add(key); + } return result; } - private void collectEquations(List keys, Solver solver) throws EquationsLimitException { + private void collectEquations(List keys, Solver solver, @NotNull Map> cache) throws EquationsLimitException { + GlobalSearchScope librariesScope = ProjectScope.getLibrariesScope(myProject); HashSet queued = new HashSet(); Stack queue = new Stack(); @@ -233,7 +270,6 @@ public class ProjectBytecodeAnalysis { queued.add(key); } - HashMap> cache = new HashMap>(); FileBasedIndex index = FileBasedIndex.getInstance(); while (!queue.empty()) { @@ -275,6 +311,32 @@ public class ProjectBytecodeAnalysis { } } + private void collectSingleEquation(HKey hKey, Solver solver, @NotNull Map> cache) throws EquationsLimitException { + GlobalSearchScope librariesScope = ProjectScope.getLibrariesScope(myProject); + + FileBasedIndex index = FileBasedIndex.getInstance(); + + ProgressManager.checkCanceled(); + Bytes bytes = new Bytes(hKey.key); + + List hEquationss = cache.get(bytes); + if (hEquationss == null) { + hEquationss = index.getValues(BytecodeAnalysisIndex.NAME, bytes, librariesScope); + cache.put(bytes, hEquationss); + } + + for (HEquations hEquations : hEquationss) { + boolean stable = hEquations.stable; + for (DirectionResultPair pair : hEquations.results) { + int dirKey = pair.directionKey; + if (dirKey == hKey.dirKey) { + HResult result = pair.hResult; + solver.addEquation(new HEquation(new HKey(bytes.bytes, dirKey, stable), result)); + } + } + } + } + @NotNull private PsiAnnotation createAnnotationFromText(@NotNull final String text) throws IncorrectOperationException { PsiAnnotation annotation = JavaPsiFacade.getElementFactory(myProject).createAnnotationFromText(text, null); @@ -286,6 +348,8 @@ public class ProjectBytecodeAnalysis { class MethodAnnotations { // @NotNull keys final HashSet notNulls = new HashSet(); + // @Nullable keys + final HashSet nullables = new HashSet(); // @Contracts final HashMap contracts = new HashMap(); } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Solver.java b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Solver.java index 21e749e12b29..1bc2c3a020e9 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Solver.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Solver.java @@ -265,9 +265,11 @@ final class Solver { private final HResultUtil resultUtil; private final HashMap equations = new HashMap(); + private final Value unstableValue; - Solver(ELattice lattice) { + Solver(ELattice lattice, Value unstableValue) { this.lattice = lattice; + this.unstableValue = unstableValue; resultUtil = new HResultUtil(lattice); } @@ -324,7 +326,7 @@ final class Solver { Value value = solved.get(id); HKey[] pIds = id.stable ? new HKey[]{id, id.negate()} : new HKey[]{id.negate(), id}; - Value[] pVals = id.stable ? new Value[]{value, value} : new Value[]{value, lattice.top}; + Value[] pVals = id.stable ? new Value[]{value, value} : new Value[]{value, unstableValue}; for (int i = 0; i < pIds.length; i++) { HKey pId = pIds[i]; diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/asm/AnalyzerExt.java b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/asm/AnalyzerExt.java new file mode 100644 index 000000000000..0fae8d9d4c6c --- /dev/null +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/asm/AnalyzerExt.java @@ -0,0 +1,446 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.codeInspection.bytecodeAnalysis.asm; + +import org.jetbrains.org.objectweb.asm.Opcodes; +import org.jetbrains.org.objectweb.asm.Type; +import org.jetbrains.org.objectweb.asm.tree.*; +import org.jetbrains.org.objectweb.asm.tree.analysis.*; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Extended version of {@link org.jetbrains.org.objectweb.asm.tree.analysis.Analyzer}. + * It handles frames and additional data. + * + * @author lambdamix + */ +public class AnalyzerExt & InterpreterExt> implements Opcodes { + + private final MyInterpreter interpreter; + + private int n; + + private InsnList insns; + + private List[] handlers; + + private Frame[] frames; + + private Subroutine[] subroutines; + + private boolean[] queued; + + private int[] queue; + + private int top; + + public Data[] getData() { + return data; + } + + private Data[] data; + + public AnalyzerExt(final MyInterpreter interpreter, Data[] data, Data startData) { + this.interpreter = interpreter; + this.data = data; + if (data.length > 0) { + data[0] = startData; + } + } + + public Frame[] analyze(final String owner, final MethodNode m) throws AnalyzerException { + if ((m.access & (ACC_ABSTRACT | ACC_NATIVE)) != 0) { + frames = (Frame[]) new Frame[0]; + return frames; + } + final V refV = (V) BasicValue.REFERENCE_VALUE; + + n = m.instructions.size(); + insns = m.instructions; + handlers = (List[]) new List[n]; + frames = (Frame[]) new Frame[n]; + subroutines = new Subroutine[n]; + queued = new boolean[n]; + queue = new int[n]; + top = 0; + + // computes exception handlers for each instruction + for (int i = 0; i < m.tryCatchBlocks.size(); ++i) { + TryCatchBlockNode tcb = m.tryCatchBlocks.get(i); + int begin = insns.indexOf(tcb.start); + int end = insns.indexOf(tcb.end); + for (int j = begin; j < end; ++j) { + List insnHandlers = handlers[j]; + if (insnHandlers == null) { + insnHandlers = new ArrayList(); + handlers[j] = insnHandlers; + } + insnHandlers.add(tcb); + } + } + + // computes the subroutine for each instruction: + Subroutine main = new Subroutine(null, m.maxLocals, null); + List subroutineCalls = new ArrayList(); + Map subroutineHeads = new HashMap(); + findSubroutine(0, main, subroutineCalls); + while (!subroutineCalls.isEmpty()) { + JumpInsnNode jsr = (JumpInsnNode) subroutineCalls.remove(0); + Subroutine sub = subroutineHeads.get(jsr.label); + if (sub == null) { + sub = new Subroutine(jsr.label, m.maxLocals, jsr); + subroutineHeads.put(jsr.label, sub); + findSubroutine(insns.indexOf(jsr.label), sub, subroutineCalls); + } else { + sub.callers.add(jsr); + } + } + for (int i = 0; i < n; ++i) { + if (subroutines[i] != null && subroutines[i].start == null) { + subroutines[i] = null; + } + } + + // initializes the data structures for the control flow analysis + Frame current = newFrame(m.maxLocals, m.maxStack); + Frame handler = newFrame(m.maxLocals, m.maxStack); + current.setReturn(interpreter.newValue(Type.getReturnType(m.desc))); + Type[] args = Type.getArgumentTypes(m.desc); + int local = 0; + if ((m.access & ACC_STATIC) == 0) { + Type ctype = Type.getObjectType(owner); + current.setLocal(local++, interpreter.newValue(ctype)); + } + for (int i = 0; i < args.length; ++i) { + current.setLocal(local++, interpreter.newValue(args[i])); + if (args[i].getSize() == 2) { + current.setLocal(local++, interpreter.newValue(null)); + } + } + while (local < m.maxLocals) { + current.setLocal(local++, interpreter.newValue(null)); + } + + interpreter.init(data[0]); + merge(0, current, null); + + init(owner, m); + + // control flow analysis + while (top > 0) { + int insn = queue[--top]; + Frame f = frames[insn]; + Subroutine subroutine = subroutines[insn]; + queued[insn] = false; + + AbstractInsnNode insnNode = null; + try { + insnNode = m.instructions.get(insn); + int insnOpcode = insnNode.getOpcode(); + int insnType = insnNode.getType(); + + if (insnType == AbstractInsnNode.LABEL + || insnType == AbstractInsnNode.LINE + || insnType == AbstractInsnNode.FRAME) { + interpreter.init(data[insn]); + merge(insn + 1, f, subroutine); + newControlFlowEdge(insn, insn + 1); + } else { + // delta + interpreter.init(data[insn]); + current.init(f).execute(insnNode, interpreter); + subroutine = subroutine == null ? null : subroutine.copy(); + + if (insnNode instanceof JumpInsnNode) { + JumpInsnNode j = (JumpInsnNode) insnNode; + if (insnOpcode != GOTO && insnOpcode != JSR) { + merge(insn + 1, current, subroutine); + newControlFlowEdge(insn, insn + 1); + } + int jump = insns.indexOf(j.label); + if (insnOpcode == JSR) { + merge(jump, current, new Subroutine(j.label, + m.maxLocals, j)); + } else { + merge(jump, current, subroutine); + } + newControlFlowEdge(insn, jump); + } else if (insnNode instanceof LookupSwitchInsnNode) { + LookupSwitchInsnNode lsi = (LookupSwitchInsnNode) insnNode; + int jump = insns.indexOf(lsi.dflt); + merge(jump, current, subroutine); + newControlFlowEdge(insn, jump); + for (int j = 0; j < lsi.labels.size(); ++j) { + LabelNode label = lsi.labels.get(j); + jump = insns.indexOf(label); + merge(jump, current, subroutine); + newControlFlowEdge(insn, jump); + } + } else if (insnNode instanceof TableSwitchInsnNode) { + TableSwitchInsnNode tsi = (TableSwitchInsnNode) insnNode; + int jump = insns.indexOf(tsi.dflt); + merge(jump, current, subroutine); + newControlFlowEdge(insn, jump); + for (int j = 0; j < tsi.labels.size(); ++j) { + LabelNode label = tsi.labels.get(j); + jump = insns.indexOf(label); + merge(jump, current, subroutine); + newControlFlowEdge(insn, jump); + } + } else if (insnOpcode == RET) { + if (subroutine == null) { + throw new AnalyzerException(insnNode, + "RET instruction outside of a sub routine"); + } + for (int i = 0; i < subroutine.callers.size(); ++i) { + JumpInsnNode caller = subroutine.callers.get(i); + int call = insns.indexOf(caller); + if (frames[call] != null) { + merge(call + 1, frames[call], current, + subroutines[call], subroutine.access); + newControlFlowEdge(insn, call + 1); + } + } + } else if (insnOpcode != ATHROW && (insnOpcode < IRETURN || insnOpcode > RETURN)) { + if (subroutine != null) { + if (insnNode instanceof VarInsnNode) { + int var = ((VarInsnNode) insnNode).var; + subroutine.access[var] = true; + if (insnOpcode == LLOAD || insnOpcode == DLOAD + || insnOpcode == LSTORE + || insnOpcode == DSTORE) { + subroutine.access[var + 1] = true; + } + } else if (insnNode instanceof IincInsnNode) { + int var = ((IincInsnNode) insnNode).var; + subroutine.access[var] = true; + } + } + merge(insn + 1, current, subroutine); + newControlFlowEdge(insn, insn + 1); + } + } + + List insnHandlers = handlers[insn]; + if (insnHandlers != null) { + for (int i = 0; i < insnHandlers.size(); ++i) { + TryCatchBlockNode tcb = insnHandlers.get(i); + int jump = insns.indexOf(tcb.handler); + if (newControlFlowExceptionEdge(insn, tcb)) { + handler.init(f); + handler.clearStack(); + handler.push(refV); + merge(jump, handler, subroutine); + } + } + } + } catch (AnalyzerException e) { + throw new AnalyzerException(e.node, "Error at instruction " + + insn + ": " + e.getMessage(), e); + } catch (Exception e) { + throw new AnalyzerException(insnNode, "Error at instruction " + + insn + ": " + e.getMessage(), e); + } + } + + return frames; + } + + private void findSubroutine(int insn, final Subroutine sub, + final List calls) throws AnalyzerException { + while (true) { + if (insn < 0 || insn >= n) { + throw new AnalyzerException(null, + "Execution can fall off end of the code"); + } + if (subroutines[insn] != null) { + return; + } + subroutines[insn] = sub.copy(); + AbstractInsnNode node = insns.get(insn); + + // calls findSubroutine recursively on normal successors + if (node instanceof JumpInsnNode) { + if (node.getOpcode() == JSR) { + // do not follow a JSR, it leads to another subroutine! + calls.add(node); + } else { + JumpInsnNode jnode = (JumpInsnNode) node; + findSubroutine(insns.indexOf(jnode.label), sub, calls); + } + } else if (node instanceof TableSwitchInsnNode) { + TableSwitchInsnNode tsnode = (TableSwitchInsnNode) node; + findSubroutine(insns.indexOf(tsnode.dflt), sub, calls); + for (int i = tsnode.labels.size() - 1; i >= 0; --i) { + LabelNode l = tsnode.labels.get(i); + findSubroutine(insns.indexOf(l), sub, calls); + } + } else if (node instanceof LookupSwitchInsnNode) { + LookupSwitchInsnNode lsnode = (LookupSwitchInsnNode) node; + findSubroutine(insns.indexOf(lsnode.dflt), sub, calls); + for (int i = lsnode.labels.size() - 1; i >= 0; --i) { + LabelNode l = lsnode.labels.get(i); + findSubroutine(insns.indexOf(l), sub, calls); + } + } + + // calls findSubroutine recursively on exception handler successors + List insnHandlers = handlers[insn]; + if (insnHandlers != null) { + for (int i = 0; i < insnHandlers.size(); ++i) { + TryCatchBlockNode tcb = insnHandlers.get(i); + findSubroutine(insns.indexOf(tcb.handler), sub, calls); + } + } + + // if insn does not falls through to the next instruction, return. + switch (node.getOpcode()) { + case GOTO: + case RET: + case TABLESWITCH: + case LOOKUPSWITCH: + case IRETURN: + case LRETURN: + case FRETURN: + case DRETURN: + case ARETURN: + case RETURN: + case ATHROW: + return; + } + insn++; + } + } + + public Frame[] getFrames() { + return frames; + } + + public List getHandlers(final int insn) { + return handlers[insn]; + } + + protected void init(String owner, MethodNode m) throws AnalyzerException { + } + + protected Frame newFrame(final int nLocals, final int nStack) { + return new Frame(nLocals, nStack); + } + + protected Frame newFrame(final Frame src) { + return new Frame(src); + } + + protected void newControlFlowEdge(final int insn, final int successor) { + } + + protected boolean newControlFlowExceptionEdge(final int insn, + final int successor) { + return true; + } + + protected boolean newControlFlowExceptionEdge(final int insn, + final TryCatchBlockNode tcb) { + return newControlFlowExceptionEdge(insn, insns.indexOf(tcb.handler)); + } + + // ------------------------------------------------------------------------- + + private void merge(final int insn, final Frame frame, + final Subroutine subroutine) throws AnalyzerException { + Frame oldFrame = frames[insn]; + Subroutine oldSubroutine = subroutines[insn]; + boolean changes; + + if (oldFrame == null) { + frames[insn] = newFrame(frame); + changes = true; + } else { + changes = oldFrame.merge(frame, interpreter); + } + + if (oldSubroutine == null) { + if (subroutine != null) { + subroutines[insn] = subroutine.copy(); + changes = true; + } + } else { + if (subroutine != null) { + changes |= oldSubroutine.merge(subroutine); + } + } + if (changes && !queued[insn]) { + queued[insn] = true; + queue[top++] = insn; + } + + // delta + mergeData(insn, interpreter); + } + + private void merge(final int insn, final Frame beforeJSR, + final Frame afterRET, final Subroutine subroutineBeforeJSR, + final boolean[] access) throws AnalyzerException { + Frame oldFrame = frames[insn]; + Subroutine oldSubroutine = subroutines[insn]; + boolean changes; + + afterRET.merge(beforeJSR, access); + + if (oldFrame == null) { + frames[insn] = newFrame(afterRET); + changes = true; + } else { + changes = oldFrame.merge(afterRET, interpreter); + } + + if (oldSubroutine != null && subroutineBeforeJSR != null) { + changes |= oldSubroutine.merge(subroutineBeforeJSR); + } + if (changes && !queued[insn]) { + queued[insn] = true; + queue[top++] = insn; + } + + // delta + mergeData(insn, interpreter); + } + + private void mergeData(int insn, MyInterpreter interpreter) { + boolean changes = false; + + Data oldData = data[insn]; + Data newData = interpreter.getAfterData(insn); + + if (oldData == null) { + data[insn] = newData; + changes = true; + } else if (newData != null) { + Data mergedData = interpreter.merge(oldData, newData); + data[insn] = mergedData; + changes = !oldData.equals(mergedData); + } + + if (changes && !queued[insn]) { + queued[insn] = true; + queue[top++] = insn; + } + } +} diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/asm/FramelessAnalyzer.java b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/asm/FramelessAnalyzer.java index 5804723e9ec1..ae1c7b819cb8 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/asm/FramelessAnalyzer.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/asm/FramelessAnalyzer.java @@ -31,52 +31,6 @@ import java.util.Map; * So, the main point here is handling of subroutines (jsr) and try-catch-finally blocks. */ public class FramelessAnalyzer implements Opcodes { - static class Subroutine { - - LabelNode start; - boolean[] access; - List callers; - - private Subroutine() { - } - - Subroutine(@Nullable final LabelNode start, final int maxLocals, - @Nullable final JumpInsnNode caller) { - this.start = start; - this.access = new boolean[maxLocals]; - this.callers = new ArrayList(); - callers.add(caller); - } - - public Subroutine copy() { - Subroutine result = new Subroutine(); - result.start = start; - result.access = new boolean[access.length]; - System.arraycopy(access, 0, result.access, 0, access.length); - result.callers = new ArrayList(callers); - return result; - } - - public boolean merge(final Subroutine subroutine) throws AnalyzerException { - boolean changes = false; - for (int i = 0; i < access.length; ++i) { - if (subroutine.access[i] && !access[i]) { - access[i] = true; - changes = true; - } - } - if (subroutine.start == start) { - for (int i = 0; i < subroutine.callers.size(); ++i) { - JumpInsnNode caller = subroutine.callers.get(i); - if (!callers.contains(caller)) { - callers.add(caller); - changes = true; - } - } - } - return changes; - } - } private int n; private InsnList insns; private List[] handlers; diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/asm/InterpreterExt.java b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/asm/InterpreterExt.java new file mode 100644 index 000000000000..5b284a074882 --- /dev/null +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/asm/InterpreterExt.java @@ -0,0 +1,32 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.codeInspection.bytecodeAnalysis.asm; + +/** + * @author lambdamix + */ +public interface InterpreterExt { // self: Interpreter[_] => + + // init interpreter state by passing entry data + void init(Data previous); + + // exit data after execution for edge to insn + // there are may be different outcomes for different edges if an instruction was branching one + Data getAfterData(int insn); + + // merge two states + Data merge(Data data1, Data data2); +} diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/asm/LeakingParameters.java b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/asm/LeakingParameters.java index 1a7ab15722f9..97b129b9c774 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/asm/LeakingParameters.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/asm/LeakingParameters.java @@ -262,7 +262,7 @@ class ParametersUsage extends Interpreter { @Override public ParamsValue ternaryOperation(AbstractInsnNode insn, ParamsValue value1, ParamsValue value2, ParamsValue value3) { - return val1; + return null; } @Override @@ -458,7 +458,7 @@ class IParametersUsage extends Interpreter { break; default: } - return val1; + return null; } @Override @@ -560,6 +560,7 @@ class LeakingParametersCollector extends ParametersUsage { @Override public ParamsValue ternaryOperation(AbstractInsnNode insn, ParamsValue value1, ParamsValue value2, ParamsValue value3) { + boolean[] params; switch (insn.getOpcode()) { case IASTORE: case LASTORE: @@ -568,7 +569,7 @@ class LeakingParametersCollector extends ParametersUsage { case BASTORE: case CASTORE: case SASTORE: - boolean[] params = value1.params; + params = value1.params; for (int i = 0; i < arity; i++) { leaking[i] |= params[i]; } @@ -585,7 +586,7 @@ class LeakingParametersCollector extends ParametersUsage { break; default: } - return super.ternaryOperation(insn, value1, value2, value3); + return null; } @Override diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/asm/LiteAnalyzerExt.java b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/asm/LiteAnalyzerExt.java new file mode 100644 index 000000000000..06dd39ffab96 --- /dev/null +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/asm/LiteAnalyzerExt.java @@ -0,0 +1,222 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.codeInspection.bytecodeAnalysis.asm; + +import org.jetbrains.org.objectweb.asm.Opcodes; +import org.jetbrains.org.objectweb.asm.Type; +import org.jetbrains.org.objectweb.asm.tree.*; +import org.jetbrains.org.objectweb.asm.tree.analysis.*; + +import java.util.ArrayList; +import java.util.List; + +/** + * Extended version of {@link com.intellij.codeInspection.bytecodeAnalysis.asm.LiteAnalyzer}. + * It handles frames and additional data. + * + * @author lambdamix + */ +public class LiteAnalyzerExt & InterpreterExt> implements Opcodes { + + private final MyInterpreter interpreter; + private Frame[] frames; + private boolean[] queued; + private int[] queue; + private int top; + + public Data[] getData() { + return data; + } + + private Data[] data; + + public LiteAnalyzerExt(final MyInterpreter interpreter, Data[] data, Data startData) { + this.interpreter = interpreter; + this.data = data; + if (data.length > 0) { + data[0] = startData; + } + } + + public Frame[] analyze(final String owner, final MethodNode m) throws AnalyzerException { + if ((m.access & (ACC_ABSTRACT | ACC_NATIVE)) != 0) { + frames = (Frame[]) new Frame[0]; + return frames; + } + + final V refV = (V) BasicValue.REFERENCE_VALUE; + + int n = m.instructions.size(); + InsnList insns = m.instructions; + List[] handlers = (List[]) new List[n]; + frames = (Frame[]) new Frame[n]; + queued = new boolean[n]; + queue = new int[n]; + top = 0; + + // computes exception handlers for each instruction + for (int i = 0; i < m.tryCatchBlocks.size(); ++i) { + TryCatchBlockNode tcb = m.tryCatchBlocks.get(i); + int begin = insns.indexOf(tcb.start); + int end = insns.indexOf(tcb.end); + for (int j = begin; j < end; ++j) { + List insnHandlers = handlers[j]; + if (insnHandlers == null) { + insnHandlers = new ArrayList(); + handlers[j] = insnHandlers; + } + insnHandlers.add(tcb); + } + } + + // initializes the data structures for the control flow analysis + Frame current = newFrame(m.maxLocals, m.maxStack); + Frame handler = newFrame(m.maxLocals, m.maxStack); + current.setReturn(interpreter.newValue(Type.getReturnType(m.desc))); + Type[] args = Type.getArgumentTypes(m.desc); + int local = 0; + if ((m.access & ACC_STATIC) == 0) { + Type ctype = Type.getObjectType(owner); + current.setLocal(local++, interpreter.newValue(ctype)); + } + for (int i = 0; i < args.length; ++i) { + current.setLocal(local++, interpreter.newValue(args[i])); + if (args[i].getSize() == 2) { + current.setLocal(local++, interpreter.newValue(null)); + } + } + while (local < m.maxLocals) { + current.setLocal(local++, interpreter.newValue(null)); + } + + interpreter.init(data[0]); + merge(0, current); + + // control flow analysis + while (top > 0) { + int insn = queue[--top]; + Frame f = frames[insn]; + queued[insn] = false; + + AbstractInsnNode insnNode = null; + try { + insnNode = m.instructions.get(insn); + int insnOpcode = insnNode.getOpcode(); + int insnType = insnNode.getType(); + + if (insnType == AbstractInsnNode.LABEL || insnType == AbstractInsnNode.LINE || insnType == AbstractInsnNode.FRAME) { + interpreter.init(data[insn]); + merge(insn + 1, f); + } else { + // delta + interpreter.init(data[insn]); + current.init(f).execute(insnNode, interpreter); + + if (insnNode instanceof JumpInsnNode) { + JumpInsnNode j = (JumpInsnNode) insnNode; + if (insnOpcode != GOTO && insnOpcode != JSR) { + merge(insn + 1, current); + } + int jump = insns.indexOf(j.label); + merge(jump, current); + } else if (insnNode instanceof LookupSwitchInsnNode) { + LookupSwitchInsnNode lsi = (LookupSwitchInsnNode) insnNode; + int jump = insns.indexOf(lsi.dflt); + merge(jump, current); + for (int j = 0; j < lsi.labels.size(); ++j) { + LabelNode label = lsi.labels.get(j); + jump = insns.indexOf(label); + merge(jump, current); + } + } else if (insnNode instanceof TableSwitchInsnNode) { + TableSwitchInsnNode tsi = (TableSwitchInsnNode) insnNode; + int jump = insns.indexOf(tsi.dflt); + merge(jump, current); + for (int j = 0; j < tsi.labels.size(); ++j) { + LabelNode label = tsi.labels.get(j); + jump = insns.indexOf(label); + merge(jump, current); + } + } else if (insnOpcode != ATHROW + && (insnOpcode < IRETURN || insnOpcode > RETURN)) { + merge(insn + 1, current); + } + } + + List insnHandlers = handlers[insn]; + if (insnHandlers != null) { + for (int i = 0; i < insnHandlers.size(); ++i) { + TryCatchBlockNode tcb = insnHandlers.get(i); + int jump = insns.indexOf(tcb.handler); + handler.init(f); + handler.clearStack(); + handler.push(refV); + merge(jump, handler); + } + } + } catch (AnalyzerException e) { + throw new AnalyzerException(e.node, "Error at instruction " + insn + ": " + e.getMessage(), e); + } catch (Exception e) { + throw new AnalyzerException(insnNode, "Error at instruction " + insn + ": " + e.getMessage(), e); + } + } + + return frames; + } + + public Frame[] getFrames() { + return frames; + } + + protected Frame newFrame(final int nLocals, final int nStack) { + return new Frame(nLocals, nStack); + } + + protected Frame newFrame(final Frame src) { + return new Frame(src); + } + + // ------------------------------------------------------------------------- + + private void merge(final int insn, final Frame frame) throws AnalyzerException { + Frame oldFrame = frames[insn]; + boolean changes; + + if (oldFrame == null) { + frames[insn] = newFrame(frame); + changes = true; + } else { + changes = oldFrame.merge(frame, interpreter); + } + + Data oldData = data[insn]; + Data newData = interpreter.getAfterData(insn); + + if (oldData == null) { + data[insn] = newData; + changes = true; + } else if (newData != null) { + Data mergedData = interpreter.merge(oldData, newData); + data[insn] = mergedData; + changes |= !oldData.equals(mergedData); + } + + if (changes && !queued[insn]) { + queued[insn] = true; + queue[top++] = insn; + } + } +} diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/asm/LiteFramelessAnalyzer.java b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/asm/LiteFramelessAnalyzer.java index 0a9721018862..3cfb1ae51feb 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/asm/LiteFramelessAnalyzer.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/asm/LiteFramelessAnalyzer.java @@ -29,11 +29,11 @@ import java.util.List; public class LiteFramelessAnalyzer extends FramelessAnalyzer { @Override - protected void findSubroutine(int insn, FramelessAnalyzer.Subroutine sub, List calls) throws AnalyzerException { + protected void findSubroutine(int insn, Subroutine sub, List calls) throws AnalyzerException { } @Override - protected void merge(final int insn, final FramelessAnalyzer.Subroutine subroutine) throws AnalyzerException { + protected void merge(final int insn, final Subroutine subroutine) throws AnalyzerException { if (!wasQueued[insn]) { wasQueued[insn] = true; if (!queued[insn]) { @@ -44,7 +44,7 @@ public class LiteFramelessAnalyzer extends FramelessAnalyzer { } @Override - protected void merge(final int insn, final FramelessAnalyzer.Subroutine subroutineBeforeJSR, final boolean[] access) throws AnalyzerException { + protected void merge(final int insn, final Subroutine subroutineBeforeJSR, final boolean[] access) throws AnalyzerException { if (!wasQueued[insn]) { wasQueued[insn] = true; if (!queued[insn]) { diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/asm/Subroutine.java b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/asm/Subroutine.java new file mode 100644 index 000000000000..a484b1b1fdb1 --- /dev/null +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/asm/Subroutine.java @@ -0,0 +1,74 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.codeInspection.bytecodeAnalysis.asm; + +import org.jetbrains.annotations.Nullable; +import org.jetbrains.org.objectweb.asm.tree.JumpInsnNode; +import org.jetbrains.org.objectweb.asm.tree.LabelNode; +import org.jetbrains.org.objectweb.asm.tree.analysis.AnalyzerException; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author lambdamix + */ +public class Subroutine { + + LabelNode start; + boolean[] access; + List callers; + + private Subroutine() { + } + + Subroutine(@Nullable final LabelNode start, final int maxLocals, + @Nullable final JumpInsnNode caller) { + this.start = start; + this.access = new boolean[maxLocals]; + this.callers = new ArrayList(); + callers.add(caller); + } + + public Subroutine copy() { + Subroutine result = new Subroutine(); + result.start = start; + result.access = new boolean[access.length]; + System.arraycopy(access, 0, result.access, 0, access.length); + result.callers = new ArrayList(callers); + return result; + } + + public boolean merge(final Subroutine subroutine) throws AnalyzerException { + boolean changes = false; + for (int i = 0; i < access.length; ++i) { + if (subroutine.access[i] && !access[i]) { + access[i] = true; + changes = true; + } + } + if (subroutine.start == start) { + for (int i = 0; i < subroutine.callers.size(); ++i) { + JumpInsnNode caller = subroutine.callers.get(i); + if (!callers.contains(caller)) { + callers.add(caller); + changes = true; + } + } + } + return changes; + } +} diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInference.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInference.java index 7037fac8cfea..f919ac5f8796 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInference.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInference.java @@ -31,6 +31,7 @@ import com.siyeh.ig.psiutils.SideEffectChecker; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -59,12 +60,27 @@ public class ContractInference { class ContractInferenceInterpreter { private final PsiMethod myMethod; + private final ValueConstraint[] myEmptyConstraints; public ContractInferenceInterpreter(PsiMethod method) { myMethod = method; + myEmptyConstraints = MethodContract.createConstraintArray(myMethod.getParameterList().getParametersCount()); } List inferContracts() { + final boolean notNull = NullableNotNullManager.isNotNull(myMethod); + return ContainerUtil.filter(doInferContracts(), new Condition() { + @Override + public boolean value(MethodContract contract) { + if (notNull && contract.returnValue == NOT_NULL_VALUE && Arrays.equals(contract.arguments, myEmptyConstraints)) { + return false; + } + return true; + } + }); + } + + private List doInferContracts() { PsiCodeBlock body = myMethod.getBody(); PsiStatement[] statements = body == null ? PsiStatement.EMPTY_ARRAY : body.getStatements(); if (statements.length == 0) return Collections.emptyList(); @@ -101,8 +117,7 @@ class ContractInferenceInterpreter { } } - ValueConstraint[] emptyState = MethodContract.createConstraintArray(myMethod.getParameterList().getParametersCount()); - return visitStatements(Collections.singletonList(emptyState), statements); + return visitStatements(Collections.singletonList(myEmptyConstraints), statements); } @Nullable @@ -130,12 +145,12 @@ class ContractInferenceInterpreter { return RecursionManager.doPreventingRecursion(myMethod, true, new Computable>() { @Override public List compute() { - List delegateContracts = ControlFlowAnalyzer.getMethodContracts(targetMethod); - return ContainerUtil.mapNotNull(delegateContracts, new NullableFunction() { + final boolean notNull = NullableNotNullManager.isNotNull(targetMethod); + List fromDelegate = ContainerUtil.mapNotNull(ControlFlowAnalyzer.getMethodContracts(targetMethod), new NullableFunction() { @Nullable @Override public MethodContract fun(MethodContract delegateContract) { - ValueConstraint[] answer = MethodContract.createConstraintArray(myMethod.getParameterList().getParametersCount()); + ValueConstraint[] answer = myEmptyConstraints; for (int i = 0; i < delegateContract.arguments.length; i++) { if (i >= arguments.length) return null; @@ -155,9 +170,17 @@ class ContractInferenceInterpreter { } } } - return answer == null ? null : new MethodContract(answer, negated ? negateConstraint(delegateContract.returnValue) : delegateContract.returnValue); + ValueConstraint returnValue = negated ? negateConstraint(delegateContract.returnValue) : delegateContract.returnValue; + if (notNull && returnValue != THROW_EXCEPTION) { + returnValue = NOT_NULL_VALUE; + } + return answer == null ? null : new MethodContract(answer, returnValue); } }); + if (notNull) { + return ContainerUtil.concat(fromDelegate, Arrays.asList(new MethodContract(myEmptyConstraints, NOT_NULL_VALUE))); + } + return fromDelegate; } }); } @@ -370,12 +393,6 @@ class ContractInferenceInterpreter { else if (statement instanceof PsiDoWhileStatement) { result.addAll(visitStatements(states, ((PsiDoWhileStatement)statement).getBody())); } - else if (statement instanceof PsiTryStatement) { - PsiCodeBlock block = ((PsiTryStatement)statement).getTryBlock(); - if (block != null) { - result.addAll(visitStatements(states, block.getStatements())); - } - } break; // visit only the first statement unless it's 'if' whose 'then' always returns and the next statement is effectively 'else' } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java index 7539d7195701..92777dd659e0 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java @@ -1434,6 +1434,10 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { final PsiAnnotation contractAnno = findContractAnnotation(method); final int paramCount = method.getParameterList().getParametersCount(); if (contractAnno != null) { + if (AnnotationUtil.isInferredAnnotation(contractAnno) && PsiUtil.canBeOverriden(method)) { + return Collections.emptyList(); + } + return CachedValuesManager.getCachedValue(contractAnno, new CachedValueProvider>() { @Nullable @Override diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java index 36449128b494..96102b53f5d4 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java @@ -70,7 +70,9 @@ public class StandardInstructionVisitor extends InstructionVisitor { DfaValueFactory factory = runner.getFactory(); if (dfaSource instanceof DfaVariableValue && factory.getVarFactory().getAllQualifiedBy(var).contains(dfaSource)) { - dfaSource = factory.createTypeValue(((DfaVariableValue)dfaSource).getVariableType(), ((DfaVariableValue)dfaSource).getInherentNullability()); + Nullness nullability = memState.isNotNull(dfaSource) ? Nullness.NOT_NULL + : ((DfaVariableValue)dfaSource).getInherentNullability(); + dfaSource = factory.createTypeValue(((DfaVariableValue)dfaSource).getVariableType(), nullability); } if (var.getInherentNullability() == Nullness.NOT_NULL) { diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaExpressionFactory.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaExpressionFactory.java index 7ebce779c3a8..9b32f8a4c000 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaExpressionFactory.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaExpressionFactory.java @@ -19,6 +19,7 @@ import com.intellij.codeInspection.dataFlow.DfaPsiUtil; import com.intellij.codeInspection.dataFlow.Nullness; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Condition; +import com.intellij.openapi.util.Conditions; import com.intellij.openapi.util.registry.Registry; import com.intellij.psi.*; import com.intellij.psi.impl.JavaConstantExpressionEvaluator; @@ -50,8 +51,7 @@ public class DfaExpressionFactory { } catch (Exception e) { LOG.error(e); - //noinspection unchecked - return Condition.FALSE; + return Conditions.alwaysFalse(); } } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/equalsAndHashcode/EqualsAndHashcode.java b/java/java-analysis-impl/src/com/intellij/codeInspection/equalsAndHashcode/EqualsAndHashcode.java deleted file mode 100644 index 4c41d90a9afa..000000000000 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/equalsAndHashcode/EqualsAndHashcode.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * 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.codeInspection.equalsAndHashcode; - -import com.intellij.codeInspection.BaseJavaBatchLocalInspectionTool; -import com.intellij.codeInspection.InspectionsBundle; -import com.intellij.codeInspection.LocalQuickFix; -import com.intellij.codeInspection.ProblemsHolder; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.roots.ProjectRootManager; -import com.intellij.openapi.util.Computable; -import com.intellij.openapi.util.Pair; -import com.intellij.psi.*; -import com.intellij.psi.search.GlobalSearchScope; -import com.intellij.psi.util.CachedValueProvider; -import com.intellij.psi.util.CachedValuesManager; -import com.intellij.psi.util.MethodSignatureUtil; -import org.jetbrains.annotations.NonNls; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -/** - * @author max - */ -public class EqualsAndHashcode extends BaseJavaBatchLocalInspectionTool { - @Override - @NotNull - public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) { - final Project project = holder.getProject(); - Pair pair = CachedValuesManager.getManager(project).getCachedValue(project, new CachedValueProvider>() { - @Override - public Result> compute() { - final JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(project); - final PsiClass psiObjectClass = ApplicationManager.getApplication().runReadAction( - new Computable() { - @Override - @Nullable - public PsiClass compute() { - return psiFacade.findClass(CommonClassNames.JAVA_LANG_OBJECT, GlobalSearchScope.allScope(project)); - } - } - ); - if (psiObjectClass == null) { - return Result.create(null, ProjectRootManager.getInstance(project)); - } - PsiMethod[] methods = psiObjectClass.getMethods(); - PsiMethod myEquals = null; - PsiMethod myHashCode = null; - for (PsiMethod method : methods) { - @NonNls final String name = method.getName(); - if ("equals".equals(name)) { - myEquals = method; - } - else if ("hashCode".equals(name)) { - myHashCode = method; - } - } - return Result.create(Pair.create(myEquals, myHashCode), psiObjectClass); - } - }); - - if (pair == null) return new PsiElementVisitor() {}; - - //jdk wasn't configured for the project - final PsiMethod myEquals = pair.first; - final PsiMethod myHashCode = pair.second; - if (myEquals == null || myHashCode == null || !myEquals.isValid() || !myHashCode.isValid()) return new PsiElementVisitor() {}; - - return new JavaElementVisitor() { - @Override public void visitClass(PsiClass aClass) { - super.visitClass(aClass); - boolean [] hasEquals = {false}; - boolean [] hasHashCode = {false}; - processClass(aClass, hasEquals, hasHashCode, myEquals, myHashCode); - if (hasEquals[0] != hasHashCode[0]) { - PsiIdentifier identifier = aClass.getNameIdentifier(); - holder.registerProblem(identifier != null ? identifier : aClass, - hasEquals[0] - ? InspectionsBundle.message("inspection.equals.hashcode.only.one.defined.problem.descriptor", "equals()", "hashCode()") - : InspectionsBundle.message("inspection.equals.hashcode.only.one.defined.problem.descriptor","hashCode()", "equals()"), - (LocalQuickFix[])null); - } - } - }; - } - - private static void processClass(final PsiClass aClass, - final boolean[] hasEquals, - final boolean[] hasHashCode, - PsiMethod equals, PsiMethod hashcode) { - final PsiMethod[] methods = aClass.getMethods(); - for (PsiMethod method : methods) { - if (MethodSignatureUtil.areSignaturesEqual(method, equals)) { - hasEquals[0] = true; - } - else if (MethodSignatureUtil.areSignaturesEqual(method, hashcode)) { - hasHashCode[0] = true; - } - } - } - - @Override - @NotNull - public String getDisplayName() { - return InspectionsBundle.message("inspection.equals.hashcode.display.name"); - } - - @Override - @NotNull - public String getGroupDisplayName() { - return ""; - } - - @Override - @NotNull - public String getShortName() { - return "EqualsAndHashcode"; - } -} diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/equalsAndHashcode/EqualsAndHashcodeBase.java b/java/java-analysis-impl/src/com/intellij/codeInspection/equalsAndHashcode/EqualsAndHashcodeBase.java new file mode 100644 index 000000000000..6a5f0627d5e1 --- /dev/null +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/equalsAndHashcode/EqualsAndHashcodeBase.java @@ -0,0 +1,134 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.codeInspection.equalsAndHashcode; + +import com.intellij.codeInspection.*; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.roots.ProjectRootManager; +import com.intellij.openapi.util.Computable; +import com.intellij.openapi.util.Pair; +import com.intellij.psi.*; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.util.CachedValueProvider; +import com.intellij.psi.util.CachedValuesManager; +import com.intellij.psi.util.MethodSignatureUtil; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author max + */ +public class EqualsAndHashcodeBase extends BaseJavaBatchLocalInspectionTool { + @Override + @NotNull + public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, final boolean isOnTheFly) { + final Project project = holder.getProject(); + Pair pair = CachedValuesManager.getManager(project).getCachedValue(project, new CachedValueProvider>() { + @Override + public Result> compute() { + final JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(project); + final PsiClass psiObjectClass = ApplicationManager.getApplication().runReadAction( + new Computable() { + @Override + @Nullable + public PsiClass compute() { + return psiFacade.findClass(CommonClassNames.JAVA_LANG_OBJECT, GlobalSearchScope.allScope(project)); + } + } + ); + if (psiObjectClass == null) { + return Result.create(null, ProjectRootManager.getInstance(project)); + } + PsiMethod[] methods = psiObjectClass.getMethods(); + PsiMethod myEquals = null; + PsiMethod myHashCode = null; + for (PsiMethod method : methods) { + @NonNls final String name = method.getName(); + if ("equals".equals(name)) { + myEquals = method; + } + else if ("hashCode".equals(name)) { + myHashCode = method; + } + } + return Result.create(Pair.create(myEquals, myHashCode), psiObjectClass); + } + }); + + if (pair == null) return new PsiElementVisitor() {}; + + //jdk wasn't configured for the project + final PsiMethod myEquals = pair.first; + final PsiMethod myHashCode = pair.second; + if (myEquals == null || myHashCode == null || !myEquals.isValid() || !myHashCode.isValid()) return new PsiElementVisitor() {}; + + return new JavaElementVisitor() { + @Override public void visitClass(PsiClass aClass) { + super.visitClass(aClass); + boolean [] hasEquals = {false}; + boolean [] hasHashCode = {false}; + processClass(aClass, hasEquals, hasHashCode, myEquals, myHashCode); + if (hasEquals[0] != hasHashCode[0]) { + PsiIdentifier identifier = aClass.getNameIdentifier(); + holder.registerProblem(identifier != null ? identifier : aClass, + hasEquals[0] + ? InspectionsBundle.message("inspection.equals.hashcode.only.one.defined.problem.descriptor", "equals()", "hashCode()") + : InspectionsBundle.message("inspection.equals.hashcode.only.one.defined.problem.descriptor","hashCode()", "equals()"), + buildFixes(isOnTheFly, hasEquals[0])); + } + } + }; + } + + private static void processClass(final PsiClass aClass, + final boolean[] hasEquals, + final boolean[] hasHashCode, + PsiMethod equals, PsiMethod hashcode) { + final PsiMethod[] methods = aClass.getMethods(); + for (PsiMethod method : methods) { + if (MethodSignatureUtil.areSignaturesEqual(method, equals)) { + hasEquals[0] = true; + } + else if (MethodSignatureUtil.areSignaturesEqual(method, hashcode)) { + hasHashCode[0] = true; + } + } + } + + @Override + @NotNull + public String getDisplayName() { + return InspectionsBundle.message("inspection.equals.hashcode.display.name"); + } + + @Override + @NotNull + public String getGroupDisplayName() { + return ""; + } + + @Override + @NotNull + public String getShortName() { + return "EqualsAndHashcode"; + } + + protected LocalQuickFix[] buildFixes(boolean isOnTheFly, boolean hasEquals) { + return LocalQuickFix.EMPTY_ARRAY; + } +} diff --git a/java/java-impl/src/com/intellij/codeInsight/ExpectedTypesProvider.java b/java/java-impl/src/com/intellij/codeInsight/ExpectedTypesProvider.java index 08a071231410..0bf1c1cd7218 100644 --- a/java/java-impl/src/com/intellij/codeInsight/ExpectedTypesProvider.java +++ b/java/java-impl/src/com/intellij/codeInsight/ExpectedTypesProvider.java @@ -31,7 +31,10 @@ import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.search.PsiShortNamesCache; import com.intellij.psi.search.searches.DeepestSuperMethodsSearch; import com.intellij.psi.tree.IElementType; -import com.intellij.psi.util.*; +import com.intellij.psi.util.PropertyUtil; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.util.PsiUtil; +import com.intellij.psi.util.TypeConversionUtil; import com.intellij.util.ArrayUtil; import com.intellij.util.NullableFunction; import com.intellij.util.Processor; @@ -126,10 +129,6 @@ public class ExpectedTypesProvider { final boolean voidable, boolean usedAfter) { if (expr == null) return ExpectedTypeInfo.EMPTY_ARRAY; PsiElement parent = expr.getParent(); - while (parent instanceof PsiParenthesizedExpression) { - expr = (PsiExpression)parent; - parent = parent.getParent(); - } MyParentVisitor visitor = new MyParentVisitor(expr, forCompletion, classProvider, voidable, usedAfter); if (parent != null) { parent.accept(visitor); @@ -242,6 +241,25 @@ public class ExpectedTypesProvider { return myResult.toArray(new ExpectedTypeInfo[myResult.size()]); } + @Override + public void visitParenthesizedExpression(PsiParenthesizedExpression expression) { + PsiElement parent = expression.getParent(); + if (parent != null) { + final MyParentVisitor visitor = new MyParentVisitor(expression, myForCompletion, myClassProvider, myVoidable, myUsedAfter); + parent.accept(visitor); + for (final ExpectedTypeInfo info : visitor.myResult) { + myResult.add(createInfoImpl(info.getType(), info.getKind(), info.getDefaultType(), TailTypes.RPARENTH, info.getCalledMethod(), + new NullableComputable() { + @Nullable + @Override + public String compute() { + return ((ExpectedTypeInfoImpl)info).getExpectedName(); + } + })); + } + } + } + @Override public void visitAnnotationMethod(@NotNull final PsiAnnotationMethod method) { if (myExpr == method.getDefaultValue()) { diff --git a/java/java-impl/src/com/intellij/codeInsight/ExternalAnnotationsLineMarkerProvider.java b/java/java-impl/src/com/intellij/codeInsight/ExternalAnnotationsLineMarkerProvider.java index e373207222ea..8605a8f91a83 100644 --- a/java/java-impl/src/com/intellij/codeInsight/ExternalAnnotationsLineMarkerProvider.java +++ b/java/java-impl/src/com/intellij/codeInsight/ExternalAnnotationsLineMarkerProvider.java @@ -51,14 +51,14 @@ public class ExternalAnnotationsLineMarkerProvider implements LineMarkerProvider if (!(element instanceof PsiModifierListOwner)) return null; if (element instanceof PsiParameter || element instanceof PsiLocalVariable) return null; - if (!shouldShowSignature(preferCompiledElement((PsiModifierListOwner)element))) { + if (!shouldShowSignature((PsiModifierListOwner)element)) { return null; } final Function annotationsCollector = new Function() { @Override public String fun(PsiModifierListOwner owner) { - return XmlStringUtil.wrapInHtml(JavaDocInfoGenerator.generateSignature(preferCompiledElement(owner))); + return XmlStringUtil.wrapInHtml(JavaDocInfoGenerator.generateSignature(owner)); } }; return new LineMarkerInfo((PsiModifierListOwner)element, element.getTextOffset(), AllIcons.Gutter.ExtAnnotation, @@ -67,11 +67,6 @@ public class ExternalAnnotationsLineMarkerProvider implements LineMarkerProvider GutterIconRenderer.Alignment.LEFT); } - private static PsiModifierListOwner preferCompiledElement(PsiModifierListOwner element) { - PsiElement original = element.getOriginalElement(); - return original instanceof PsiModifierListOwner ? (PsiModifierListOwner)original : element; - } - private static boolean shouldShowSignature(PsiModifierListOwner owner) { if (hasNonCodeAnnotations(owner)) { return true; @@ -111,7 +106,7 @@ public class ExternalAnnotationsLineMarkerProvider implements LineMarkerProvider if (ref == null) return true; PsiElement target = ref.resolve(); - return !(target instanceof PsiClass) || JavaDocInfoGenerator.isDocumentedAnnotationType((PsiClass)target); + return !(target instanceof PsiClass) || JavaDocInfoGenerator.isDocumentedAnnotationType(target); } @Override diff --git a/java/java-impl/src/com/intellij/codeInsight/TailTypes.java b/java/java-impl/src/com/intellij/codeInsight/TailTypes.java index 923dd8213019..244067bd3f36 100644 --- a/java/java-impl/src/com/intellij/codeInsight/TailTypes.java +++ b/java/java-impl/src/com/intellij/codeInsight/TailTypes.java @@ -28,6 +28,12 @@ public class TailTypes { return styleSettings.SPACE_WITHIN_METHOD_CALL_PARENTHESES && editor.getDocument().getCharsSequence().charAt(tailOffset - 1) != '('; } }; + public static final TailType RPARENTH = new RParenthTailType(){ + @Override + protected boolean isSpaceWithinParentheses(final CommonCodeStyleSettings styleSettings, final Editor editor, final int tailOffset) { + return styleSettings.SPACE_WITHIN_PARENTHESES; + } + }; public static final TailType IF_RPARENTH = new RParenthTailType(){ @Override protected boolean isSpaceWithinParentheses(final CommonCodeStyleSettings styleSettings, final Editor editor, final int tailOffset) { diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java index 8cc9d06aed04..112b243e0ea3 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java @@ -544,7 +544,7 @@ public class JavaCompletionUtil { return JavaClassNameCompletionContributor.createClassLookupItems((PsiClass)completion, JavaClassNameCompletionContributor.AFTER_NEW.accepts(reference), JavaClassNameInsertHandler.JAVA_CLASS_INSERT_HANDLER, - Condition.TRUE); + Conditions.alwaysTrue()); } } diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaDocCompletionContributor.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaDocCompletionContributor.java index 2a9fc2f4a79f..978e5b24d2e8 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaDocCompletionContributor.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaDocCompletionContributor.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,7 +28,7 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.*; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Comparing; -import com.intellij.openapi.util.Condition; +import com.intellij.openapi.util.Conditions; import com.intellij.openapi.util.text.StringUtil; import com.intellij.patterns.PsiJavaPatterns; import com.intellij.profile.codeInspection.InspectionProjectProfileManager; @@ -43,7 +43,10 @@ import com.intellij.psi.javadoc.*; import com.intellij.psi.util.InheritanceUtil; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.TypeConversionUtil; -import com.intellij.util.*; +import com.intellij.util.IncorrectOperationException; +import com.intellij.util.ProcessingContext; +import com.intellij.util.Processor; +import com.intellij.util.SystemProperties; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.text.CharArrayUtil; import org.jetbrains.annotations.NonNls; @@ -81,7 +84,7 @@ public class JavaDocCompletionContributor extends CompletionContributor { if (ref instanceof PsiJavaReference) { result.stopHere(); - final JavaCompletionProcessor processor = new JavaCompletionProcessor(position, TrueFilter.INSTANCE, JavaCompletionProcessor.Options.CHECK_NOTHING, Condition.TRUE); + final JavaCompletionProcessor processor = new JavaCompletionProcessor(position, TrueFilter.INSTANCE, JavaCompletionProcessor.Options.CHECK_NOTHING, Conditions.alwaysTrue()); ((PsiJavaReference) ref).processVariants(processor); for (final CompletionElement _item : processor.getResults()) { diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/PreferByKindWeigher.java b/java/java-impl/src/com/intellij/codeInsight/completion/PreferByKindWeigher.java index ad53d9484959..cc67ca51de58 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/PreferByKindWeigher.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/PreferByKindWeigher.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ import com.intellij.codeInsight.completion.scope.JavaCompletionProcessor; import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.codeInsight.lookup.LookupElementWeigher; import com.intellij.openapi.util.Condition; +import com.intellij.openapi.util.Conditions; import com.intellij.patterns.ElementPattern; import com.intellij.psi.*; import com.intellij.psi.filters.getters.MembersGetter; @@ -127,8 +128,7 @@ public class PreferByKindWeigher extends LookupElementWeigher { }; } - //noinspection unchecked - return Condition.FALSE; + return Conditions.alwaysFalse(); } enum MyResult { diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ChangeMethodSignatureFromUsageFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ChangeMethodSignatureFromUsageFix.java index dbf454207cca..d753875208c3 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ChangeMethodSignatureFromUsageFix.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ChangeMethodSignatureFromUsageFix.java @@ -333,6 +333,9 @@ public class ChangeMethodSignatureFromUsageFix implements IntentionAction/*, Hig else { PsiType exprType = RefactoringUtil.getTypeByExpression(expression); if (exprType == null) return null; + if (exprType instanceof PsiDisjunctionType) { + exprType = ((PsiDisjunctionType)exprType).getLeastUpperBound(); + } final ParameterInfoImpl changedParameterInfo = new ParameterInfoImpl(i, parameter.getName(), exprType); result.add(changedParameterInfo); changedParams.add(changedParameterInfo); @@ -403,6 +406,9 @@ public class ChangeMethodSignatureFromUsageFix implements IntentionAction/*, Hig if (varargParam != null && pi >= parameters.length) return false; PsiType exprType = RefactoringUtil.getTypeByExpression(expression); if (exprType == null) return false; + if (exprType instanceof PsiDisjunctionType) { + exprType = ((PsiDisjunctionType)exprType).getLeastUpperBound(); + } JavaCodeStyleManager codeStyleManager = JavaCodeStyleManager.getInstance(expression.getProject()); String name = suggestUniqueParameterName(codeStyleManager, expression, exprType, existingNames); final ParameterInfoImpl newParameterInfo = new ParameterInfoImpl(-1, name, exprType, expression.getText().replace('\n', ' ')); diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateMethodFromUsageFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateMethodFromUsageFix.java index 8e93a6a0c817..ad3d3fc8010a 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateMethodFromUsageFix.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateMethodFromUsageFix.java @@ -182,7 +182,7 @@ public class CreateMethodFromUsageFix extends CreateFromUsageBaseFix { context); } - protected static PsiMethod createMethod(PsiClass targetClass, + public static PsiMethod createMethod(PsiClass targetClass, PsiClass parentClass, PsiMember enclosingContext, String methodName) { diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateStaticMethodQuickFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateStaticMethodQuickFix.java new file mode 100644 index 000000000000..8268ee58d44a --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateStaticMethodQuickFix.java @@ -0,0 +1,81 @@ +package com.intellij.codeInsight.daemon.impl.quickfix; + +import com.intellij.codeInsight.ExpectedTypeInfo; +import com.intellij.codeInsight.daemon.QuickFixBundle; +import com.intellij.codeInspection.LocalQuickFix; +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Pair; +import com.intellij.psi.*; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.util.PsiUtil; +import com.intellij.util.Function; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public class CreateStaticMethodQuickFix implements LocalQuickFix { + @NotNull + private final PsiClass targetClass; + @NotNull + private final String methodName; + @NotNull + private final List types; + + + public CreateStaticMethodQuickFix(@NotNull PsiClass aClass, + @NotNull String name, + @NotNull List types) { + targetClass = aClass; + methodName = name; + this.types = types; + } + + + @NotNull + @Override + public String getName() { + return QuickFixBundle.message("create.method.from.usage.family"); + } + + @NotNull + @Override + public String getFamilyName() { + return QuickFixBundle.message("create.method.from.usage.family"); + } + + @Override + public void applyFix(@NotNull final Project project, @NotNull ProblemDescriptor descriptor) { + boolean java8Interface = false; + if (targetClass.isInterface()) { + if (PsiUtil.isLanguageLevel8OrHigher(targetClass)) { + java8Interface = true; + } else { + return; + } + } + + PsiMethod method = CreateMethodFromUsageFix.createMethod(targetClass, null, null, methodName); + if (method == null) { + return; + } + + if (!java8Interface) { + PsiUtil.setModifierProperty(method, PsiModifier.PUBLIC, true); + } + PsiUtil.setModifierProperty(method, PsiModifier.STATIC, true); + + List> args = ContainerUtil.map(types, new Function>() { + @Override + public Pair fun(String s) { + return new Pair(null, PsiType.getTypeByName(s, project, GlobalSearchScope.allScope(project))); + } + }); + CreateMethodFromUsageFix.doCreate(targetClass, method, false, + args, + PsiSubstitutor.UNKNOWN, + ExpectedTypeInfo.EMPTY_ARRAY, + null); + } +} diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/MoveClassToModuleFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/MoveClassToModuleFix.java index d8caac1d59a6..9c1d38853b2c 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/MoveClassToModuleFix.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/MoveClassToModuleFix.java @@ -42,12 +42,12 @@ import com.intellij.refactoring.RefactoringActionHandler; import com.intellij.refactoring.RefactoringActionHandlerFactory; import com.intellij.ui.components.JBList; import com.intellij.util.IncorrectOperationException; -import org.codehaus.groovy.util.ListHashMap; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.model.java.JavaModuleSourceRootTypes; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -55,7 +55,7 @@ import java.util.Map; * @author cdr */ public class MoveClassToModuleFix implements IntentionAction { - private final Map myModules = new ListHashMap(); + private final Map myModules = new LinkedHashMap(); private final String myReferenceName; private final Module myCurrentModule; private final PsiDirectory mySourceRoot; diff --git a/java/java-impl/src/com/intellij/codeInsight/generation/surroundWith/JavaWithTryFinallySurrounder.java b/java/java-impl/src/com/intellij/codeInsight/generation/surroundWith/JavaWithTryFinallySurrounder.java index e757ddeacc17..28732a65390b 100644 --- a/java/java-impl/src/com/intellij/codeInsight/generation/surroundWith/JavaWithTryFinallySurrounder.java +++ b/java/java-impl/src/com/intellij/codeInsight/generation/surroundWith/JavaWithTryFinallySurrounder.java @@ -19,10 +19,8 @@ import com.intellij.codeInsight.CodeInsightBundle; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.editor.EditorModificationUtil; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.TextRange; -import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; import com.intellij.psi.codeStyle.CodeStyleManager; import com.intellij.util.IncorrectOperationException; @@ -65,16 +63,14 @@ class JavaWithTryFinallySurrounder extends JavaStatementsSurrounder{ if (finallyBlock == null) { return null; } - int offset = finallyBlock.getTextRange().getStartOffset() + 2; - editor.getCaretModel().moveToOffset(offset); - final Document document = editor.getDocument(); + Document document = editor.getDocument(); PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(document); + TextRange finallyBlockRange = finallyBlock.getTextRange(); + int newLineOffset = finallyBlockRange.getStartOffset() + 2; + editor.getCaretModel().moveToOffset(newLineOffset); editor.getSelectionModel().removeSelection(); - final PsiStatement[] tryBlockStatements = tryBlock.getStatements(); - LOG.assertTrue(tryBlockStatements.length > 0, tryBlock.getText()); - final PsiStatement firstTryStmt = tryBlockStatements[0]; - final int indent = firstTryStmt.getTextOffset() - document.getLineStartOffset(document.getLineNumber(firstTryStmt.getTextOffset())); - EditorModificationUtil.insertStringAtCaret(editor, StringUtil.repeat(" ", indent), false, true); + CodeStyleManager.getInstance(project).adjustLineIndent(document, newLineOffset); + PsiDocumentManager.getInstance(project).commitDocument(document); return new TextRange(editor.getCaretModel().getOffset(), editor.getCaretModel().getOffset()); } } \ No newline at end of file diff --git a/java/java-impl/src/com/intellij/codeInsight/highlighting/HighlightExceptionsHandlerFactory.java b/java/java-impl/src/com/intellij/codeInsight/highlighting/HighlightExceptionsHandlerFactory.java index c7bd10cc2b39..a1f206b4ba8e 100644 --- a/java/java-impl/src/com/intellij/codeInsight/highlighting/HighlightExceptionsHandlerFactory.java +++ b/java/java-impl/src/com/intellij/codeInsight/highlighting/HighlightExceptionsHandlerFactory.java @@ -19,6 +19,7 @@ import com.intellij.codeInsight.ExceptionUtil; import com.intellij.featureStatistics.FeatureUsageTracker; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.util.Condition; +import com.intellij.openapi.util.Conditions; import com.intellij.psi.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -57,7 +58,7 @@ public class HighlightExceptionsHandlerFactory extends HighlightUsagesHandlerFac final PsiCodeBlock tryBlock = tryStatement.getTryBlock(); if (tryBlock == null) return null; final Collection psiClassTypes = ExceptionUtil.collectUnhandledExceptions(tryBlock, tryBlock); - return new HighlightExceptionsHandler(editor, file, target, psiClassTypes.toArray(new PsiClassType[psiClassTypes.size()]), tryBlock, Condition.TRUE); + return new HighlightExceptionsHandler(editor, file, target, psiClassTypes.toArray(new PsiClassType[psiClassTypes.size()]), tryBlock, Conditions.alwaysTrue()); } @Nullable @@ -112,6 +113,6 @@ public class HighlightExceptionsHandlerFactory extends HighlightUsagesHandlerFac final Collection psiClassTypes = ExceptionUtil.collectUnhandledExceptions(method.getBody(), method.getBody()); - return new HighlightExceptionsHandler(editor, file, target, psiClassTypes.toArray(new PsiClassType[psiClassTypes.size()]), method.getBody(), Condition.TRUE); + return new HighlightExceptionsHandler(editor, file, target, psiClassTypes.toArray(new PsiClassType[psiClassTypes.size()]), method.getBody(), Conditions.alwaysTrue()); } } diff --git a/java/java-impl/src/com/intellij/codeInsight/intention/impl/AddAnnotationIntention.java b/java/java-impl/src/com/intellij/codeInsight/intention/impl/AddAnnotationIntention.java index 899da3035ce5..027928a4f695 100644 --- a/java/java-impl/src/com/intellij/codeInsight/intention/impl/AddAnnotationIntention.java +++ b/java/java-impl/src/com/intellij/codeInsight/intention/impl/AddAnnotationIntention.java @@ -56,9 +56,11 @@ public abstract class AddAnnotationIntention extends BaseIntentionAction { Pair annotations = getAnnotations(project); String toAdd = annotations.first; String[] toRemove = annotations.second; - if (toRemove.length > 0 && AnnotationUtil.isAnnotated(owner, toRemove[0], false, false)) return false; + if (toRemove.length > 0 && isAnnotatedSkipInferred(owner, toRemove)) { + return false; + } setText(AddAnnotationPsiFix.calcText(owner, toAdd)); - if (AnnotationUtil.isAnnotated(owner, toAdd, false, false)) return false; + if (isAnnotatedSkipInferred(owner, toAdd)) return false; if (owner instanceof PsiMethod) { PsiType returnType = ((PsiMethod)owner).getReturnType(); @@ -73,6 +75,11 @@ public abstract class AddAnnotationIntention extends BaseIntentionAction { return true; } + private static boolean isAnnotatedSkipInferred(PsiModifierListOwner owner, String... annoFqns) { + PsiAnnotation annotation = AnnotationUtil.findAnnotation(owner, false, annoFqns); + return annotation != null && !AnnotationUtil.isInferredAnnotation(annotation); + } + @Override public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException { PsiModifierListOwner owner = AddAnnotationPsiFix.getContainer(file, editor.getCaretModel().getOffset()); diff --git a/java/java-impl/src/com/intellij/codeInspection/equalsAndHashcode/EqualsAndHashcode.java b/java/java-impl/src/com/intellij/codeInspection/equalsAndHashcode/EqualsAndHashcode.java new file mode 100644 index 000000000000..3887640051c4 --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInspection/equalsAndHashcode/EqualsAndHashcode.java @@ -0,0 +1,72 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.codeInspection.equalsAndHashcode; + +import com.intellij.codeInsight.generation.GenerateEqualsHandler; +import com.intellij.codeInspection.InspectionsBundle; +import com.intellij.codeInspection.LocalQuickFix; +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.fileEditor.FileEditorManager; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiElement; +import org.jetbrains.annotations.NotNull; + +/** + * @author Bas Leijdekkers + */ +public class EqualsAndHashcode extends EqualsAndHashcodeBase { + + protected LocalQuickFix[] buildFixes(boolean isOnTheFly, boolean hasEquals) { + if (!isOnTheFly) { + return LocalQuickFix.EMPTY_ARRAY; + } + return new LocalQuickFix[]{new GenerateEqualsHashcodeFix(hasEquals)}; + } + + private static class GenerateEqualsHashcodeFix implements LocalQuickFix { + + private final boolean myHasEquals; + + public GenerateEqualsHashcodeFix(boolean hasEquals) { + myHasEquals = hasEquals; + } + + @NotNull + @Override + public String getName() { + return myHasEquals + ? InspectionsBundle.message("inspection.equals.hashcode.generate.hashcode.quickfix") + : InspectionsBundle.message("inspection.equals.hashcode.generate.equals.quickfix"); + } + + @NotNull + @Override + public String getFamilyName() { + return getName(); + } + + @Override + public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { + final Editor editor = FileEditorManager.getInstance(project).getSelectedTextEditor(); + if (editor == null) { + return; + } + final PsiElement element = descriptor.getPsiElement(); + new GenerateEqualsHandler().invoke(project, editor, element.getContainingFile()); + } + } +} diff --git a/java/java-impl/src/com/intellij/codeInspection/inferNullity/InferNullityAnnotationsAction.java b/java/java-impl/src/com/intellij/codeInspection/inferNullity/InferNullityAnnotationsAction.java index f9127f0b48a5..df49b83ff48d 100644 --- a/java/java-impl/src/com/intellij/codeInspection/inferNullity/InferNullityAnnotationsAction.java +++ b/java/java-impl/src/com/intellij/codeInspection/inferNullity/InferNullityAnnotationsAction.java @@ -159,7 +159,7 @@ public class InferNullityAnnotationsAction extends BaseAnalysisAction { } else if (Messages.showOkCancelDialog(project, "Infer Nullity Annotations requires that the nullity annotations" + " be available in all your project sources.\n\nYou will need to add annotations.jar as a library. " + - "It is possible to configure custom jar in e.g. Constant Conditions & Exceptions inspection or use JetBrains annotations available in installation. " + + "It is possible to configure custom JAR in e.g. Constant Conditions & Exceptions inspection or use JetBrains annotations available in installation. " + " IntelliJ IDEA nullity annotations are freely usable and redistributable under the Apache 2.0 license. Would you like to do it now?", INFER_NULLITY_ANNOTATIONS, Messages.getErrorIcon()) == Messages.OK) { ApplicationManager.getApplication().invokeLater(new Runnable() { 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 5c226b84500b..4aff727524bf 100644 --- a/java/java-impl/src/com/intellij/find/findUsages/JavaFindUsagesHandler.java +++ b/java/java-impl/src/com/intellij/find/findUsages/JavaFindUsagesHandler.java @@ -100,7 +100,7 @@ public class JavaFindUsagesHandler extends FindUsagesHandler{ return super.getFindUsagesDialog(isSingleFile, toShowInNewTab, mustOpenInNewTab); } - private static boolean askWhetherShouldSearchForParameterInOverridingMethods(final PsiElement psiElement, final PsiParameter parameter) { + private static boolean askWhetherShouldSearchForParameterInOverridingMethods(@NotNull PsiElement psiElement, @NotNull PsiParameter parameter) { return Messages.showOkCancelDialog(psiElement.getProject(), FindBundle.message("find.parameter.usages.in.overriding.methods.prompt", parameter.getName()), FindBundle.message("find.parameter.usages.in.overriding.methods.title"), @@ -283,10 +283,10 @@ public class JavaFindUsagesHandler extends FindUsagesHandler{ final JavaVariableFindUsagesOptions varOptions = (JavaVariableFindUsagesOptions) options; if (varOptions.isReadAccess || varOptions.isWriteAccess){ if (varOptions.isReadAccess && varOptions.isWriteAccess){ - if (!addElementUsages(element, processor, options)) return false; + if (!addElementUsages(element, options, processor)) return false; } else{ - if (!addElementUsages(element, new Processor() { + if (!addElementUsages(element, varOptions, new Processor() { @Override public boolean process(UsageInfo info) { final PsiElement element = info.getElement(); @@ -296,12 +296,12 @@ public class JavaFindUsagesHandler extends FindUsagesHandler{ } return true; } - }, varOptions)) return false; + })) return false; } } } else if (options.isUsages) { - if (!addElementUsages(element, processor, options)) return false; + if (!addElementUsages(element, options, processor)) return false; } boolean success = ApplicationManager.getApplication().runReadAction(new Computable() { @@ -325,29 +325,35 @@ public class JavaFindUsagesHandler extends FindUsagesHandler{ if (!success) return false; if (options instanceof JavaPackageFindUsagesOptions && ((JavaPackageFindUsagesOptions)options).isClassesUsages){ - if (!addClassesUsages((PsiPackage)element, processor, (JavaPackageFindUsagesOptions)options)) return false; + if (!addClassesUsages((PsiPackage)element, (JavaPackageFindUsagesOptions)options, processor)) return false; } if (options instanceof JavaClassFindUsagesOptions) { final JavaClassFindUsagesOptions classOptions = (JavaClassFindUsagesOptions)options; final PsiClass psiClass = (PsiClass)element; + PsiManager manager = ApplicationManager.getApplication().runReadAction(new Computable() { + @Override + public PsiManager compute() { + return psiClass.getManager(); + } + }); if (classOptions.isMethodsUsages){ - if (!addMethodsUsages(psiClass, processor, classOptions)) return false; + if (!addMethodsUsages(psiClass, manager, classOptions, processor)) return false; } if (classOptions.isFieldsUsages){ - if (!addFieldsUsages(psiClass, processor, classOptions)) return false; + if (!addFieldsUsages(psiClass, manager, classOptions, processor)) return false; } if (psiClass.isInterface()) { if (classOptions.isDerivedInterfaces){ if (classOptions.isImplementingClasses){ - if (!addInheritors(psiClass, processor, classOptions)) return false; + if (!addInheritors(psiClass, classOptions, processor)) return false; } else{ - if (!addDerivedInterfaces(psiClass, processor, classOptions)) return false; + if (!addDerivedInterfaces(psiClass, classOptions, processor)) return false; } } else if (classOptions.isImplementingClasses){ - if (!addImplementingClasses(psiClass, processor, classOptions)) return false; + if (!addImplementingClasses(psiClass, classOptions, processor)) return false; } if (classOptions.isImplementingClasses) { @@ -355,13 +361,13 @@ public class JavaFindUsagesHandler extends FindUsagesHandler{ new PsiElementProcessor() { @Override public boolean execute(@NotNull PsiFunctionalExpression expression) { - return addResult(processor, expression, options); + return addResult(expression, options, processor); } })); } } else if (classOptions.isDerivedClasses) { - if (!addInheritors(psiClass, processor, classOptions)) return false; + if (!addInheritors(psiClass, classOptions, processor)) return false; } } @@ -380,14 +386,14 @@ public class JavaFindUsagesHandler extends FindUsagesHandler{ new PsiElementProcessor() { @Override public boolean execute(@NotNull PsiFunctionalExpression expression) { - return addResult(processor, expression, options); + return addResult(expression, options, processor); } })); } } if (element instanceof PomTarget) { - if (!addAliasingUsages((PomTarget)element, processor, options)) return false; + if (!addAliasingUsages((PomTarget)element, options, processor)) return false; } final Boolean isSearchable = ApplicationManager.getApplication().runReadAction(new Computable() { @Override @@ -403,8 +409,8 @@ public class JavaFindUsagesHandler extends FindUsagesHandler{ } private static boolean addAliasingUsages(@NotNull PomTarget pomTarget, - @NotNull final Processor processor, - @NotNull final FindUsagesOptions options) { + @NotNull final FindUsagesOptions options, + @NotNull final Processor processor) { for (AliasingPsiTargetMapper aliasingPsiTargetMapper : Extensions.getExtensions(AliasingPsiTargetMapper.EP_NAME)) { for (AliasingPsiTarget psiTarget : aliasingPsiTargetMapper.getTargets(pomTarget)) { boolean success = ReferencesSearch @@ -412,7 +418,7 @@ public class JavaFindUsagesHandler extends FindUsagesHandler{ .forEach(new ReadActionProcessor() { @Override public boolean processInReadAction(final PsiReference reference) { - return addResult(processor, reference, options); + return addResult(reference, options, processor); } }); if (!success) return false; @@ -428,15 +434,15 @@ public class JavaFindUsagesHandler extends FindUsagesHandler{ new PsiElementProcessor() { @Override public boolean execute(@NotNull PsiMethod element) { - return addResult(processor, element.getNavigationElement(), options); + return addResult(element.getNavigationElement(), options, processor); } })); } private static boolean addClassesUsages(@NotNull PsiPackage aPackage, - @NotNull final Processor processor, - @NotNull final JavaPackageFindUsagesOptions options) { + @NotNull final JavaPackageFindUsagesOptions options, + @NotNull final Processor processor) { ProgressIndicator progress = ProgressManager.getInstance().getProgressIndicator(); if (progress != null){ progress.pushState(); @@ -446,18 +452,19 @@ public class JavaFindUsagesHandler extends FindUsagesHandler{ addClassesInPackage(aPackage, options.isIncludeSubpackages, classes); for (final PsiClass aClass : classes) { if (progress != null) { - progress.setText(FindBundle.message("find.searching.for.references.to.class.progress", ApplicationManager.getApplication().runReadAction(new Computable(){ + String name = ApplicationManager.getApplication().runReadAction(new Computable() { @Override public String compute() { return aClass.getName(); } - }))); + }); + progress.setText(FindBundle.message("find.searching.for.references.to.class.progress", name)); progress.checkCanceled(); } boolean success = ReferencesSearch.search(new ReferencesSearch.SearchParameters(aClass, options.searchScope, false, options.fastTrack)).forEach(new ReadActionProcessor() { @Override public boolean processInReadAction(final PsiReference psiReference) { - return addResult(processor, psiReference, options); + return addResult(psiReference, options, processor); } }); if (!success) return false; @@ -469,8 +476,13 @@ public class JavaFindUsagesHandler extends FindUsagesHandler{ return true; } - private static void addClassesInPackage(@NotNull PsiPackage aPackage, boolean includeSubpackages, @NotNull List array) { - PsiDirectory[] dirs = aPackage.getDirectories(); + private static void addClassesInPackage(@NotNull final PsiPackage aPackage, boolean includeSubpackages, @NotNull List array) { + PsiDirectory[] dirs = ApplicationManager.getApplication().runReadAction(new Computable() { + @Override + public PsiDirectory[] compute() { + return aPackage.getDirectories(); + } + }); for (PsiDirectory dir : dirs) { addClassesInDirectory(dir, includeSubpackages, array); } @@ -495,22 +507,40 @@ public class JavaFindUsagesHandler extends FindUsagesHandler{ } private static boolean addMethodsUsages(@NotNull final PsiClass aClass, - @NotNull final Processor processor, - @NotNull final JavaClassFindUsagesOptions options) { + @NotNull final PsiManager manager, + @NotNull final JavaClassFindUsagesOptions options, + @NotNull final Processor processor) { if (options.isIncludeInherited) { - final PsiManager manager = aClass.getManager(); - PsiMethod[] methods = aClass.getAllMethods(); - MethodsLoop: + final PsiMethod[] methods = ApplicationManager.getApplication().runReadAction(new Computable() { + @Override + public PsiMethod[] compute() { + return aClass.getAllMethods(); + } + }); for(int i = 0; i < methods.length; i++){ final PsiMethod method = methods[i]; - // filter overriden methods - MethodSignature methodSignature = method.getSignature(PsiSubstitutor.EMPTY); - for(int j = 0; j < i; j++){ - if (methodSignature.equals(methods[j].getSignature(PsiSubstitutor.EMPTY))) continue MethodsLoop; - } - final PsiClass methodClass = method.getContainingClass(); - if (methodClass != null && manager.areElementsEquivalent(methodClass, aClass)){ - if (!addElementUsages(methods[i], processor, options)) return false; + // filter overridden methods + final int finalI = i; + final PsiClass methodClass = + ApplicationManager.getApplication().runReadAction(new Computable() { + @Override + public PsiClass compute() { + MethodSignature methodSignature = method.getSignature(PsiSubstitutor.EMPTY); + for (int j = 0; j < finalI; j++) { + if (methodSignature.equals(methods[j].getSignature(PsiSubstitutor.EMPTY))) return null; + } + return method.getContainingClass(); + } + }); + if (methodClass == null) continue; + boolean equivalent = ApplicationManager.getApplication().runReadAction(new Computable() { + @Override + public Boolean compute() { + return manager.areElementsEquivalent(methodClass, aClass); + } + }); + if (equivalent){ + if (!addElementUsages(method, options, processor)) return false; } else { MethodReferencesSearch.SearchParameters parameters = @@ -519,7 +549,7 @@ public class JavaFindUsagesHandler extends FindUsagesHandler{ .forEach(new PsiReferenceProcessorAdapter(new PsiReferenceProcessor() { @Override public boolean execute(PsiReference reference) { - addResultFromReference(reference, methodClass, manager, aClass, processor, options); + addResultFromReference(reference, methodClass, manager, aClass, options, processor); return true; } })); @@ -528,35 +558,59 @@ public class JavaFindUsagesHandler extends FindUsagesHandler{ } } else { - for (PsiMethod method : aClass.getMethods()) { - if (!addElementUsages(method, processor, options)) return false; + PsiMethod[] methods = ApplicationManager.getApplication().runReadAction(new Computable() { + @Override + public PsiMethod[] compute() { + return aClass.getMethods(); + } + }); + for (PsiMethod method : methods) { + if (!addElementUsages(method, options, processor)) return false; } } return true; } private static boolean addFieldsUsages(@NotNull final PsiClass aClass, - @NotNull final Processor processor, - @NotNull final JavaClassFindUsagesOptions options) { + @NotNull final PsiManager manager, + @NotNull final JavaClassFindUsagesOptions options, + @NotNull final Processor processor) { if (options.isIncludeInherited) { - final PsiManager manager = aClass.getManager(); - PsiField[] fields = aClass.getAllFields(); - FieldsLoop: + final PsiField[] fields = ApplicationManager.getApplication().runReadAction(new Computable() { + @Override + public PsiField[] compute() { + return aClass.getAllFields(); + } + }); for (int i = 0; i < fields.length; i++) { final PsiField field = fields[i]; // filter hidden fields - for (int j = 0; j < i; j++) { - if (Comparing.strEqual(field.getName(), fields[j].getName())) continue FieldsLoop; - } - final PsiClass fieldClass = field.getContainingClass(); - if (manager.areElementsEquivalent(fieldClass, aClass)) { - if (!addElementUsages(fields[i], processor, options)) return false; + final int finalI = i; + final PsiClass fieldClass = + ApplicationManager.getApplication().runReadAction(new Computable() { + @Override + public PsiClass compute() { + for (int j = 0; j < finalI; j++) { + if (Comparing.strEqual(field.getName(), fields[j].getName())) return null; + } + return field.getContainingClass(); + } + }); + if (fieldClass == null) continue; + boolean equivalent = ApplicationManager.getApplication().runReadAction(new Computable() { + @Override + public Boolean compute() { + return manager.areElementsEquivalent(fieldClass, aClass); + } + }); + if (equivalent) { + if (!addElementUsages(fields[i], options, processor)) return false; } else { boolean success = ReferencesSearch.search(new ReferencesSearch.SearchParameters(field, options.searchScope, false, options.fastTrack)).forEach(new ReadActionProcessor() { @Override public boolean processInReadAction(final PsiReference reference) { - return addResultFromReference(reference, fieldClass, manager, aClass, processor, options); + return addResultFromReference(reference, fieldClass, manager, aClass, options, processor); } }); if (!success) return false; @@ -571,14 +625,14 @@ public class JavaFindUsagesHandler extends FindUsagesHandler{ } }); for (PsiField field : fields) { - if (!addElementUsages(field, processor, options)) return false; + if (!addElementUsages(field, options, processor)) return false; } } return true; } @Nullable - private static PsiClass getFieldOrMethodAccessedClass(@NotNull PsiReferenceExpression ref, PsiClass fieldOrMethodClass) { + private static PsiClass getFieldOrMethodAccessedClass(@NotNull PsiReferenceExpression ref, @NotNull PsiClass fieldOrMethodClass) { PsiElement[] children = ref.getChildren(); if (children.length > 1 && children[0] instanceof PsiExpression) { PsiExpression expr = (PsiExpression)children[0]; @@ -606,39 +660,39 @@ public class JavaFindUsagesHandler extends FindUsagesHandler{ } private static boolean addInheritors(@NotNull PsiClass aClass, - @NotNull final Processor processor, - @NotNull final JavaClassFindUsagesOptions options) { + @NotNull final JavaClassFindUsagesOptions options, + @NotNull final Processor processor) { return ClassInheritorsSearch.search(aClass, options.searchScope, options.isCheckDeepInheritance).forEach(new PsiElementProcessorAdapter( new PsiElementProcessor() { @Override public boolean execute(@NotNull PsiClass element) { - return addResult(processor, element, options); + return addResult(element, options, processor); } })); } private static boolean addDerivedInterfaces(@NotNull PsiClass anInterface, - @NotNull final Processor processor, - @NotNull final JavaClassFindUsagesOptions options) { + @NotNull final JavaClassFindUsagesOptions options, + @NotNull final Processor processor) { return ClassInheritorsSearch.search(anInterface, options.searchScope, options.isCheckDeepInheritance).forEach(new PsiElementProcessorAdapter( new PsiElementProcessor() { @Override public boolean execute(@NotNull PsiClass inheritor) { - return !inheritor.isInterface() || addResult(processor, inheritor, options); + return !inheritor.isInterface() || addResult(inheritor, options, processor); } })); } private static boolean addImplementingClasses(@NotNull PsiClass anInterface, - @NotNull final Processor processor, - @NotNull final JavaClassFindUsagesOptions options) { + @NotNull final JavaClassFindUsagesOptions options, + @NotNull final Processor processor) { return ClassInheritorsSearch.search(anInterface, options.searchScope, options.isCheckDeepInheritance).forEach(new PsiElementProcessorAdapter( new PsiElementProcessor() { @Override public boolean execute(@NotNull PsiClass inheritor) { - return inheritor.isInterface() || addResult(processor, inheritor, options); + return inheritor.isInterface() || addResult(inheritor, options, processor); } })); } @@ -647,14 +701,14 @@ public class JavaFindUsagesHandler extends FindUsagesHandler{ @NotNull PsiClass methodClass, @NotNull PsiManager manager, @NotNull PsiClass aClass, - @NotNull Processor processor, - @NotNull FindUsagesOptions options) { + @NotNull FindUsagesOptions options, + @NotNull Processor processor) { PsiElement refElement = reference.getElement(); if (refElement instanceof PsiReferenceExpression) { PsiClass usedClass = getFieldOrMethodAccessedClass((PsiReferenceExpression)refElement, methodClass); if (usedClass != null) { if (manager.areElementsEquivalent(usedClass, aClass) || usedClass.isInheritor(aClass, true)) { - if (!addResult(processor, refElement, options)) return false; + if (!addResult(refElement, options, processor)) return false; } } } @@ -662,8 +716,8 @@ public class JavaFindUsagesHandler extends FindUsagesHandler{ } private static boolean addElementUsages(@NotNull final PsiElement element, - @NotNull final Processor processor, - @NotNull final FindUsagesOptions options) { + @NotNull final FindUsagesOptions options, + @NotNull final Processor processor) { final SearchScope searchScope = options.searchScope; final PsiClass[] parentClass = new PsiClass[1]; if (element instanceof PsiMethod && ApplicationManager.getApplication().runReadAction(new Computable() { @@ -684,7 +738,7 @@ public class JavaFindUsagesHandler extends FindUsagesHandler{ .forEach(new ReadActionProcessor() { @Override public boolean processInReadAction(final PsiReference ref) { - return addResult(processor, ref, options); + return addResult(ref, options, processor); } }); } @@ -694,7 +748,7 @@ public class JavaFindUsagesHandler extends FindUsagesHandler{ final ReadActionProcessor consumer = new ReadActionProcessor() { @Override public boolean processInReadAction(final PsiReference ref) { - return addResult(processor, ref, options); + return addResult(ref, options, processor); } }; @@ -708,11 +762,13 @@ public class JavaFindUsagesHandler extends FindUsagesHandler{ return ReferencesSearch.search(new ReferencesSearch.SearchParameters(element, searchScope, false, options.fastTrack)).forEach(consumer); } - private static boolean addResult(@NotNull Processor processor, @NotNull PsiElement element, @NotNull FindUsagesOptions options) { + private static boolean addResult(@NotNull PsiElement element, + @NotNull FindUsagesOptions options, + @NotNull Processor processor) { return !filterUsage(element, options) || processor.process(new UsageInfo(element)); } - private static boolean addResult(Processor processor, PsiReference ref, FindUsagesOptions options) { + private static boolean addResult(@NotNull PsiReference ref, @NotNull FindUsagesOptions options, @NotNull Processor processor) { if (filterUsage(ref.getElement(), options)){ TextRange rangeInElement = ref.getRangeInElement(); return processor.process(new UsageInfo(ref.getElement(), rangeInElement.getStartOffset(), rangeInElement.getEndOffset(), false)); @@ -720,7 +776,7 @@ public class JavaFindUsagesHandler extends FindUsagesHandler{ return true; } - private static boolean filterUsage(PsiElement usage, FindUsagesOptions options) { + private static boolean filterUsage(PsiElement usage, @NotNull FindUsagesOptions options) { if (!(usage instanceof PsiJavaCodeReferenceElement)) { return true; } @@ -780,5 +836,4 @@ public class JavaFindUsagesHandler extends FindUsagesHandler{ } return super.findReferencesToHighlight(target, searchScope); } - } diff --git a/java/java-impl/src/com/intellij/ide/JavaLanguageCodeStyleSettingsProvider.java b/java/java-impl/src/com/intellij/ide/JavaLanguageCodeStyleSettingsProvider.java index 2f684304ae58..c6c4f6c144d2 100644 --- a/java/java-impl/src/com/intellij/ide/JavaLanguageCodeStyleSettingsProvider.java +++ b/java/java-impl/src/com/intellij/ide/JavaLanguageCodeStyleSettingsProvider.java @@ -19,6 +19,7 @@ import com.intellij.application.options.IndentOptionsEditor; import com.intellij.application.options.JavaIndentOptionsEditor; import com.intellij.lang.Language; import com.intellij.lang.java.JavaLanguage; +import com.intellij.openapi.application.ApplicationBundle; import com.intellij.openapi.fileTypes.StdFileTypes; import com.intellij.openapi.project.Project; import com.intellij.pom.java.LanguageLevel; @@ -57,8 +58,8 @@ public class JavaLanguageCodeStyleSettingsProvider extends LanguageCodeStyleSett @Override public void customizeSettings(@NotNull CodeStyleSettingsCustomizable consumer, @NotNull SettingsType settingsType) { - consumer.showAllStandardOptions(); if (settingsType == SettingsType.SPACING_SETTINGS) { + consumer.showAllStandardOptions(); consumer.showCustomOption(JavaCodeStyleSettings.class, "SPACES_WITHIN_ANGLE_BRACKETS", "Angle brackets",CodeStyleSettingsCustomizable.SPACES_WITHIN); String groupName = CodeStyleSettingsCustomizable.SPACES_IN_TYPE_ARGUMENTS; @@ -69,6 +70,95 @@ public class JavaLanguageCodeStyleSettingsProvider extends LanguageCodeStyleSett consumer.showCustomOption(JavaCodeStyleSettings.class, "SPACE_BEFORE_OPENING_ANGLE_BRACKET_IN_TYPE_PARAMETER", "Before opening angle bracket", groupName); consumer.showCustomOption(JavaCodeStyleSettings.class, "SPACE_AROUND_TYPE_BOUNDS_IN_TYPE_PARAMETERS", "Around type bounds", groupName); } + else if (settingsType == SettingsType.WRAPPING_AND_BRACES_SETTINGS) { + consumer.showStandardOptions("RIGHT_MARGIN", + "KEEP_CONTROL_STATEMENT_IN_ONE_LINE", + "LINE_COMMENT_AT_FIRST_COLUMN", + "BLOCK_COMMENT_AT_FIRST_COLUMN", + "KEEP_LINE_BREAKS", + "KEEP_FIRST_COLUMN_COMMENT", + "CALL_PARAMETERS_WRAP", + "PREFER_PARAMETERS_WRAP", + "CALL_PARAMETERS_LPAREN_ON_NEXT_LINE", + "CALL_PARAMETERS_RPAREN_ON_NEXT_LINE", + "METHOD_PARAMETERS_WRAP", + "METHOD_PARAMETERS_LPAREN_ON_NEXT_LINE", + "METHOD_PARAMETERS_RPAREN_ON_NEXT_LINE", + "RESOURCE_LIST_WRAP", + "RESOURCE_LIST_LPAREN_ON_NEXT_LINE", + "RESOURCE_LIST_RPAREN_ON_NEXT_LINE", + "EXTENDS_LIST_WRAP", + "THROWS_LIST_WRAP", + "EXTENDS_KEYWORD_WRAP", + "THROWS_KEYWORD_WRAP", + "METHOD_CALL_CHAIN_WRAP", + "PARENTHESES_EXPRESSION_LPAREN_WRAP", + "PARENTHESES_EXPRESSION_RPAREN_WRAP", + "BINARY_OPERATION_WRAP", + "BINARY_OPERATION_SIGN_ON_NEXT_LINE", + "TERNARY_OPERATION_WRAP", + "TERNARY_OPERATION_SIGNS_ON_NEXT_LINE", + "MODIFIER_LIST_WRAP", + "KEEP_SIMPLE_BLOCKS_IN_ONE_LINE", + "KEEP_SIMPLE_METHODS_IN_ONE_LINE", + "KEEP_SIMPLE_CLASSES_IN_ONE_LINE", + "KEEP_MULTIPLE_EXPRESSIONS_IN_ONE_LINE", + "FOR_STATEMENT_WRAP", + "FOR_STATEMENT_LPAREN_ON_NEXT_LINE", + "FOR_STATEMENT_RPAREN_ON_NEXT_LINE", + "ARRAY_INITIALIZER_WRAP", + "ARRAY_INITIALIZER_LBRACE_ON_NEXT_LINE", + "ARRAY_INITIALIZER_RBRACE_ON_NEXT_LINE", + "ASSIGNMENT_WRAP", + "PLACE_ASSIGNMENT_SIGN_ON_NEXT_LINE", + "LABELED_STATEMENT_WRAP", + "ASSERT_STATEMENT_WRAP", + "ASSERT_STATEMENT_COLON_ON_NEXT_LINE", + "IF_BRACE_FORCE", + "DOWHILE_BRACE_FORCE", + "WHILE_BRACE_FORCE", + "FOR_BRACE_FORCE", + "WRAP_LONG_LINES", + "METHOD_ANNOTATION_WRAP", + "CLASS_ANNOTATION_WRAP", + "FIELD_ANNOTATION_WRAP", + "PARAMETER_ANNOTATION_WRAP", + "VARIABLE_ANNOTATION_WRAP", + "ALIGN_MULTILINE_CHAINED_METHODS", + "ALIGN_MULTILINE_PARAMETERS", + "ALIGN_MULTILINE_PARAMETERS_IN_CALLS", + "ALIGN_MULTILINE_RESOURCES", + "ALIGN_MULTILINE_FOR", + "INDENT_WHEN_CASES", + "ALIGN_MULTILINE_BINARY_OPERATION", + "ALIGN_MULTILINE_ASSIGNMENT", + "ALIGN_MULTILINE_TERNARY_OPERATION", + "ALIGN_MULTILINE_THROWS_LIST", + "ALIGN_THROWS_KEYWORD", + "ALIGN_MULTILINE_EXTENDS_LIST", + "ALIGN_MULTILINE_METHOD_BRACKETS", + "ALIGN_MULTILINE_PARENTHESIZED_EXPRESSION", + "ALIGN_MULTILINE_ARRAY_INITIALIZER_EXPRESSION", + "ALIGN_GROUP_FIELD_DECLARATIONS", + "BRACE_STYLE", + "CLASS_BRACE_STYLE", + "METHOD_BRACE_STYLE", + "USE_FLYING_GEESE_BRACES", + "FLYING_GEESE_BRACES_GAP", + "DO_NOT_INDENT_TOP_LEVEL_CLASS_MEMBERS", + "ELSE_ON_NEW_LINE", + "WHILE_ON_NEW_LINE", + "CATCH_ON_NEW_LINE", + "FINALLY_ON_NEW_LINE", + "INDENT_CASE_FROM_SWITCH", + "SPECIAL_ELSE_IF_TREATMENT", + "ENUM_CONSTANTS_WRAP"); + String groupName = ApplicationBundle.message("wrapping.fields.annotation"); + consumer.showCustomOption(JavaCodeStyleSettings.class, "DO_NOT_WRAP_AFTER_SINGLE_ANNOTATION", "Do not wrap after single annotation", groupName); + } + else { + consumer.showAllStandardOptions(); + } } @Override diff --git a/java/java-impl/src/com/intellij/ide/actions/CreatePackageInfoAction.java b/java/java-impl/src/com/intellij/ide/actions/CreatePackageInfoAction.java index 64a8c1bcfcb5..8fd9f0d422db 100644 --- a/java/java-impl/src/com/intellij/ide/actions/CreatePackageInfoAction.java +++ b/java/java-impl/src/com/intellij/ide/actions/CreatePackageInfoAction.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -41,7 +41,7 @@ import org.jetbrains.jps.model.java.JavaModuleSourceRootTypes; */ public class CreatePackageInfoAction extends CreateFromTemplateActionBase implements DumbAware { - protected CreatePackageInfoAction() { + public CreatePackageInfoAction() { super(IdeBundle.message("action.create.new.package-info.title"), IdeBundle.message("action.create.new.package-info.description"), AllIcons.FileTypes.Java); } diff --git a/java/java-impl/src/com/intellij/ide/projectView/impl/ClassesTreeStructureProvider.java b/java/java-impl/src/com/intellij/ide/projectView/impl/ClassesTreeStructureProvider.java index 7e0515726610..ef745b3aaa8b 100644 --- a/java/java-impl/src/com/intellij/ide/projectView/impl/ClassesTreeStructureProvider.java +++ b/java/java-impl/src/com/intellij/ide/projectView/impl/ClassesTreeStructureProvider.java @@ -62,8 +62,9 @@ public class ClassesTreeStructureProvider implements SelectableTreeStructureProv if (originalElement instanceof PsiFile) { PsiFile classFile = (PsiFile)originalElement; final VirtualFile virtualClassFile = classFile.getVirtualFile(); - if (virtualClassFile != null && fileIndex.isInLibraryClasses(virtualClassFile) && - classOwner.getManager().areElementsEquivalent(classOwner.getContainingDirectory(), classFile.getContainingDirectory())) { + if (virtualClassFile != null && fileIndex.isInLibraryClasses(virtualClassFile) + && !classOwner.getManager().areElementsEquivalent(classOwner, classFile) + && classOwner.getManager().areElementsEquivalent(classOwner.getContainingDirectory(), classFile.getContainingDirectory())) { continue; } } diff --git a/java/java-impl/src/com/intellij/ide/util/scopeChooser/ClassHierarchyScopeDescriptor.java b/java/java-impl/src/com/intellij/ide/util/scopeChooser/ClassHierarchyScopeDescriptor.java index 356933823226..05b2578e5df1 100644 --- a/java/java-impl/src/com/intellij/ide/util/scopeChooser/ClassHierarchyScopeDescriptor.java +++ b/java/java-impl/src/com/intellij/ide/util/scopeChooser/ClassHierarchyScopeDescriptor.java @@ -69,23 +69,25 @@ public class ClassHierarchyScopeDescriptor extends ScopeDescriptor { chooser.showDialog(); PsiClass aClass = chooser.getSelected(); - if (aClass == null) return null; + if (aClass == null) { + myCachedScope = GlobalSearchScope.EMPTY_SCOPE; + } else { + final List classesToSearch = new LinkedList(); + classesToSearch.add(aClass); - final List classesToSearch = new LinkedList(); - classesToSearch.add(aClass); + classesToSearch.addAll(ClassInheritorsSearch.search(aClass, true).findAll()); - classesToSearch.addAll(ClassInheritorsSearch.search(aClass, true).findAll()); + FunctionalExpressionSearch.search(aClass).forEach(new Processor() { + @Override + public boolean process(PsiFunctionalExpression expression) { + classesToSearch.add(expression); + return true; + } + }); - FunctionalExpressionSearch.search(aClass).forEach(new Processor() { - @Override - public boolean process(PsiFunctionalExpression expression) { - classesToSearch.add(expression); - return true; - } - }); - - myCachedScope = new LocalSearchScope(PsiUtilCore.toPsiElementArray(classesToSearch), - IdeBundle.message("scope.hierarchy", ClassPresentationUtil.getNameForClass(aClass, true))); + myCachedScope = new LocalSearchScope(PsiUtilCore.toPsiElementArray(classesToSearch), + IdeBundle.message("scope.hierarchy", ClassPresentationUtil.getNameForClass(aClass, true))); + } } return myCachedScope; diff --git a/java/java-impl/src/com/intellij/jarFinder/FindJarFix.java b/java/java-impl/src/com/intellij/jarFinder/FindJarFix.java index ee203e34deac..e82f79652074 100644 --- a/java/java-impl/src/com/intellij/jarFinder/FindJarFix.java +++ b/java/java-impl/src/com/intellij/jarFinder/FindJarFix.java @@ -69,7 +69,7 @@ public abstract class FindJarFix implements IntentionActio @NotNull @Override public String getText() { - return "Find jar on web"; + return "Find JAR on web"; } @NotNull @@ -190,7 +190,7 @@ public abstract class FindJarFix implements IntentionActio } else { JBPopupFactory.getInstance() .createListPopupBuilder(libNames) - .setTitle("Select a jar file") + .setTitle("Select a JAR file") .setItemChoosenCallback(new Runnable() { @Override public void run() { diff --git a/java/java-impl/src/com/intellij/openapi/projectRoots/impl/JavaAwareProjectJdkTableImpl.java b/java/java-impl/src/com/intellij/openapi/projectRoots/impl/JavaAwareProjectJdkTableImpl.java index 44f0132acbb5..fd9fa64eae66 100644 --- a/java/java-impl/src/com/intellij/openapi/projectRoots/impl/JavaAwareProjectJdkTableImpl.java +++ b/java/java-impl/src/com/intellij/openapi/projectRoots/impl/JavaAwareProjectJdkTableImpl.java @@ -22,17 +22,16 @@ package com.intellij.openapi.projectRoots.impl; import com.intellij.openapi.components.*; import com.intellij.openapi.project.ProjectBundle; -import com.intellij.openapi.projectRoots.*; +import com.intellij.openapi.projectRoots.JavaSdk; +import com.intellij.openapi.projectRoots.ProjectJdkTable; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.projectRoots.SdkTypeId; import com.intellij.util.SystemProperties; import org.jdom.Element; @State( - name="ProjectJdkTable", - roamingType = RoamingType.DISABLED, - storages= { - @Storage( - file = StoragePathMacros.APP_CONFIG + "/jdk.table.xml" - )} + name = "ProjectJdkTable", + storages = {@Storage(file = StoragePathMacros.APP_CONFIG + "/jdk.table.xml", roamingType = RoamingType.DISABLED)} ) public class JavaAwareProjectJdkTableImpl extends ProjectJdkTableImpl { public static JavaAwareProjectJdkTableImpl getInstanceEx() { diff --git a/java/java-impl/src/com/intellij/openapi/roots/impl/LanguageLevelProjectExtensionImpl.java b/java/java-impl/src/com/intellij/openapi/roots/impl/LanguageLevelProjectExtensionImpl.java index 496795b85444..836d6e005113 100644 --- a/java/java-impl/src/com/intellij/openapi/roots/impl/LanguageLevelProjectExtensionImpl.java +++ b/java/java-impl/src/com/intellij/openapi/roots/impl/LanguageLevelProjectExtensionImpl.java @@ -97,7 +97,9 @@ public class LanguageLevelProjectExtensionImpl extends LanguageLevelProjectExten @Override public void languageLevelsChanged() { - JavaLanguageLevelPusher.pushLanguageLevel(myProject); + if (!myProject.isDefault()) { + JavaLanguageLevelPusher.pushLanguageLevel(myProject); + } } @Override diff --git a/java/java-impl/src/com/intellij/psi/codeStyle/JavaCodeStyleSettings.java b/java/java-impl/src/com/intellij/psi/codeStyle/JavaCodeStyleSettings.java index 7e117e3ed0c7..b7d0f71fe00d 100644 --- a/java/java-impl/src/com/intellij/psi/codeStyle/JavaCodeStyleSettings.java +++ b/java/java-impl/src/com/intellij/psi/codeStyle/JavaCodeStyleSettings.java @@ -30,4 +30,5 @@ public class JavaCodeStyleSettings extends CustomCodeStyleSettings { public boolean SPACE_BEFORE_OPENING_ANGLE_BRACKET_IN_TYPE_PARAMETER = false; public boolean SPACE_AROUND_TYPE_BOUNDS_IN_TYPE_PARAMETERS = true; + public boolean DO_NOT_WRAP_AFTER_SINGLE_ANNOTATION = false; } diff --git a/java/java-impl/src/com/intellij/psi/formatter/java/JavaSpacePropertyProcessor.java b/java/java-impl/src/com/intellij/psi/formatter/java/JavaSpacePropertyProcessor.java index 4d225e65d88b..5230805aa03c 100644 --- a/java/java-impl/src/com/intellij/psi/formatter/java/JavaSpacePropertyProcessor.java +++ b/java/java-impl/src/com/intellij/psi/formatter/java/JavaSpacePropertyProcessor.java @@ -1129,7 +1129,7 @@ public class JavaSpacePropertyProcessor extends JavaElementVisitor { @Override public void visitModifierList(PsiModifierList list) { - createSpaceInCode(true); + myResult = Spacing.createSpacing(1, 1, 0, mySettings.KEEP_LINE_BREAKS, mySettings.KEEP_BLANK_LINES_IN_CODE); } @Override diff --git a/java/java-impl/src/com/intellij/psi/formatter/java/wrap/impl/JavaChildWrapArranger.java b/java/java-impl/src/com/intellij/psi/formatter/java/wrap/impl/JavaChildWrapArranger.java index 954f03248b9c..5b05ced2d5a1 100644 --- a/java/java-impl/src/com/intellij/psi/formatter/java/wrap/impl/JavaChildWrapArranger.java +++ b/java/java-impl/src/com/intellij/psi/formatter/java/wrap/impl/JavaChildWrapArranger.java @@ -20,6 +20,7 @@ import com.intellij.formatting.WrapType; import com.intellij.lang.ASTNode; import com.intellij.psi.*; import com.intellij.psi.codeStyle.CommonCodeStyleSettings; +import com.intellij.psi.codeStyle.JavaCodeStyleSettings; import com.intellij.psi.formatter.FormatterUtil; import com.intellij.psi.formatter.java.JavaFormatterUtil; import com.intellij.psi.formatter.java.wrap.JavaWrapManager; @@ -29,6 +30,7 @@ import com.intellij.psi.impl.source.tree.CompositeElement; import com.intellij.psi.impl.source.tree.JavaElementType; import com.intellij.psi.tree.IElementType; import com.intellij.util.ArrayUtil; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import static com.intellij.psi.formatter.java.JavaFormatterUtil.getWrapType; @@ -69,6 +71,7 @@ public class JavaChildWrapArranger { CommonCodeStyleSettings settings, Wrap suggestedWrap, ReservedWrapsProvider reservedWrapsProvider) { + final JavaCodeStyleSettings javaSettings = settings.getRootSettings().getCustomSettings(JavaCodeStyleSettings.class); ASTNode directParent = child.getTreeParent(); int role = ((CompositeElement)directParent).getChildRole(child); @@ -153,7 +156,7 @@ public class JavaChildWrapArranger { if (prev != null && prev.getElementType() == JavaElementType.MODIFIER_LIST) { ASTNode last = prev.getLastChildNode(); if (last != null && last.getElementType() == JavaElementType.ANNOTATION) { - if (isTypeAnnotation(last.getPsi())) { + if (isTypeAnnotation(last.getPsi()) || javaSettings.DO_NOT_WRAP_AFTER_SINGLE_ANNOTATION && isFieldModifierListWithSingleAnnotation(prev)) { return Wrap.createWrap(WrapType.NONE, false); } else { @@ -182,6 +185,10 @@ public class JavaChildWrapArranger { ASTNode prev = FormatterUtil.getPreviousNonWhitespaceSibling(child); if (prev != null && prev.getElementType() == JavaElementType.ANNOTATION) { + if (javaSettings.DO_NOT_WRAP_AFTER_SINGLE_ANNOTATION && isFieldModifierListWithSingleAnnotation(parent)) { + return Wrap.createWrap(WrapType.NONE, false); + } + return Wrap.createWrap(getWrapType(getAnnotationWrapType(parent.getTreeParent(), child, settings)), true); } @@ -256,6 +263,23 @@ public class JavaChildWrapArranger { return suggestedWrap; } + private static boolean isFieldModifierListWithSingleAnnotation(@NotNull ASTNode elem) { + ASTNode parent = elem.getTreeParent(); + if (parent != null && parent.getElementType() == JavaElementType.FIELD) { + return isModifierListWithSingleAnnotation(elem); + } + return false; + } + + private static boolean isModifierListWithSingleAnnotation(@NotNull ASTNode elem) { + if (elem.getPsi() instanceof PsiModifierList) { + if (((PsiModifierList)elem.getPsi()).getAnnotations().length == 1) { + return true; + } + } + return false; + } + private static int getAnnotationWrapType(ASTNode parent, ASTNode child, CommonCodeStyleSettings settings) { IElementType nodeType = parent.getElementType(); diff --git a/java/java-impl/src/com/intellij/psi/impl/file/PsiPackageImplementationHelperImpl.java b/java/java-impl/src/com/intellij/psi/impl/file/PsiPackageImplementationHelperImpl.java index 658ee6561e68..0c49938053f0 100644 --- a/java/java-impl/src/com/intellij/psi/impl/file/PsiPackageImplementationHelperImpl.java +++ b/java/java-impl/src/com/intellij/psi/impl/file/PsiPackageImplementationHelperImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,11 +24,11 @@ import com.intellij.openapi.editor.Editor; import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleManager; -import com.intellij.openapi.module.ModuleUtil; -import com.intellij.openapi.roots.impl.ModifiableModelCommitter; +import com.intellij.openapi.module.ModuleUtilCore; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.*; -import com.intellij.openapi.vfs.VfsUtil; +import com.intellij.openapi.roots.impl.ModifiableModelCommitter; +import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.wm.ToolWindow; import com.intellij.openapi.wm.ToolWindowId; @@ -37,6 +37,9 @@ import com.intellij.psi.*; import com.intellij.psi.impl.PackagePrefixElementFinder; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.util.PsiModificationTracker; +import com.intellij.psi.util.PsiUtil; +import com.intellij.psi.util.PsiUtilCore; +import org.jetbrains.annotations.NotNull; import org.jetbrains.jps.model.java.JavaModuleSourceRootTypes; import java.util.ArrayList; @@ -46,22 +49,25 @@ import java.util.List; * @author yole */ public class PsiPackageImplementationHelperImpl extends PsiPackageImplementationHelper { + @NotNull @Override - public GlobalSearchScope adjustAllScope(PsiPackage psiPackage, GlobalSearchScope globalSearchScope) { + public GlobalSearchScope adjustAllScope(@NotNull PsiPackage psiPackage, @NotNull GlobalSearchScope globalSearchScope) { return NonClasspathClassFinder.addNonClasspathScope(psiPackage.getProject(), globalSearchScope); } + @NotNull @Override - public VirtualFile[] occursInPackagePrefixes(PsiPackage psiPackage) { + public VirtualFile[] occursInPackagePrefixes(@NotNull PsiPackage psiPackage) { List result = new ArrayList(); final Module[] modules = ModuleManager.getInstance(psiPackage.getProject()).getModules(); + String qualifiedName = psiPackage.getQualifiedName(); for (final Module module : modules) { for (final ContentEntry contentEntry : ModuleRootManager.getInstance(module).getContentEntries()) { final List sourceFolders = contentEntry.getSourceFolders(JavaModuleSourceRootTypes.SOURCES); for (final SourceFolder sourceFolder : sourceFolders) { final String packagePrefix = sourceFolder.getPackagePrefix(); - if (packagePrefix.startsWith(psiPackage.getQualifiedName())) { + if (packagePrefix.startsWith(qualifiedName)) { final VirtualFile file = sourceFolder.getFile(); if (file != null) { result.add(file); @@ -71,11 +77,11 @@ public class PsiPackageImplementationHelperImpl extends PsiPackageImplementation } } - return VfsUtil.toVirtualFileArray(result); + return VfsUtilCore.toVirtualFileArray(result); } @Override - public void handleQualifiedNameChange(final PsiPackage psiPackage, final String newQualifiedName) { + public void handleQualifiedNameChange(@NotNull final PsiPackage psiPackage, @NotNull final String newQualifiedName) { ApplicationManager.getApplication().assertWriteAccessAllowed(); final String oldQualifedName = psiPackage.getQualifiedName(); final boolean anyChanged = changePackagePrefixes(psiPackage, oldQualifedName, newQualifiedName); @@ -94,7 +100,7 @@ public class PsiPackageImplementationHelperImpl extends PsiPackageImplementation } } - private static boolean changePackagePrefixes(PsiPackage psiPackage, final String oldQualifiedName, final String newQualifiedName) { + private static boolean changePackagePrefixes(@NotNull PsiPackage psiPackage, @NotNull String oldQualifiedName, @NotNull String newQualifiedName) { final Module[] modules = ModuleManager.getInstance(psiPackage.getProject()).getModules(); List modelsToCommit = new ArrayList(); for (final Module module : modules) { @@ -128,7 +134,7 @@ public class PsiPackageImplementationHelperImpl extends PsiPackageImplementation } @Override - public void navigate(final PsiPackage psiPackage, final boolean requestFocus) { + public void navigate(@NotNull final PsiPackage psiPackage, final boolean requestFocus) { final Project project = psiPackage.getProject(); ToolWindow window = ToolWindowManager.getInstance(project).getToolWindow(ToolWindowId.PROJECT_VIEW); window.activate(null); @@ -143,7 +149,8 @@ public class PsiPackageImplementationHelperImpl extends PsiPackageImplementation }); } - private static PsiDirectory[] suggestMostAppropriateDirectories(PsiPackage psiPackage) { + @NotNull + private static PsiDirectory[] suggestMostAppropriateDirectories(@NotNull PsiPackage psiPackage) { final Project project = psiPackage.getProject(); PsiDirectory[] directories = null; final Editor editor = FileEditorManager.getInstance(project).getSelectedTextEditor(); @@ -151,10 +158,20 @@ public class PsiPackageImplementationHelperImpl extends PsiPackageImplementation final Document document = editor.getDocument(); final PsiFile psiFile = PsiDocumentManager.getInstance(project).getPsiFile(document); if (psiFile != null) { - final Module module = ModuleUtil.findModuleForPsiElement(psiFile); + final Module module = ModuleUtilCore.findModuleForPsiElement(psiFile); if (module != null) { - directories = psiPackage.getDirectories(GlobalSearchScope.moduleWithDependenciesScope(module)); - } else { + final VirtualFile virtualFile = PsiUtilCore.getVirtualFile(psiFile); + final boolean isInTests = + virtualFile != null && ModuleRootManager.getInstance(module).getFileIndex().isInTestSourceContent(virtualFile); + if (isInTests) { + directories = psiPackage.getDirectories(GlobalSearchScope.moduleTestsWithDependentsScope(module)); + } + + if (directories == null || directories.length == 0) { + directories = psiPackage.getDirectories(GlobalSearchScope.moduleWithDependenciesScope(module)); + } + } + else { directories = psiPackage.getDirectories(GlobalSearchScope.notScope(GlobalSearchScope.projectScope(project))); } } @@ -167,12 +184,13 @@ public class PsiPackageImplementationHelperImpl extends PsiPackageImplementation } @Override - public boolean packagePrefixExists(PsiPackage psiPackage) { + public boolean packagePrefixExists(@NotNull PsiPackage psiPackage) { return PackagePrefixElementFinder.getInstance(psiPackage.getProject()).packagePrefixExists(psiPackage.getQualifiedName()); } + @NotNull @Override - public Object[] getDirectoryCachedValueDependencies(PsiPackage psiPackage) { + public Object[] getDirectoryCachedValueDependencies(@NotNull PsiPackage psiPackage) { return new Object[] { PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT, ProjectRootManager.getInstance(psiPackage.getProject()) }; } } diff --git a/java/java-impl/src/com/intellij/refactoring/extractMethod/AbstractExtractDialog.java b/java/java-impl/src/com/intellij/refactoring/extractMethod/AbstractExtractDialog.java index 7e49181472b0..9f83b6ed080b 100644 --- a/java/java-impl/src/com/intellij/refactoring/extractMethod/AbstractExtractDialog.java +++ b/java/java-impl/src/com/intellij/refactoring/extractMethod/AbstractExtractDialog.java @@ -20,25 +20,18 @@ */ package com.intellij.refactoring.extractMethod; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.DialogWrapper; import com.intellij.psi.PsiModifier; import com.intellij.refactoring.util.VariableData; -public abstract class AbstractExtractDialog extends DialogWrapper { - protected AbstractExtractDialog(Project project) { - super(project, true); - } - - - public abstract String getChosenMethodName(); - - public abstract VariableData[] getChosenParameters(); +public interface AbstractExtractDialog { + String getChosenMethodName(); + VariableData[] getChosenParameters(); @PsiModifier.ModifierConstant - public abstract String getVisibility(); - - public abstract boolean isMakeStatic(); + String getVisibility(); + boolean isMakeStatic(); + boolean isChainedConstructor(); - public abstract boolean isChainedConstructor(); + void show(); + boolean isOK(); } \ No newline at end of file diff --git a/java/java-impl/src/com/intellij/refactoring/extractMethod/ExtractMethodDialog.java b/java/java-impl/src/com/intellij/refactoring/extractMethod/ExtractMethodDialog.java index f4cfccb496ad..fc933de2b80e 100644 --- a/java/java-impl/src/com/intellij/refactoring/extractMethod/ExtractMethodDialog.java +++ b/java/java-impl/src/com/intellij/refactoring/extractMethod/ExtractMethodDialog.java @@ -21,6 +21,7 @@ import com.intellij.openapi.editor.event.DocumentAdapter; import com.intellij.openapi.editor.event.DocumentEvent; import com.intellij.openapi.fileTypes.StdFileTypes; import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.ui.Splitter; import com.intellij.openapi.ui.VerticalFlowLayout; import com.intellij.openapi.util.text.StringUtil; @@ -59,7 +60,7 @@ import java.awt.event.*; * @author Konstantin Bulenkov */ @SuppressWarnings("MethodMayBeStatic") -public class ExtractMethodDialog extends AbstractExtractDialog { +public class ExtractMethodDialog extends DialogWrapper implements AbstractExtractDialog { public static final String EXTRACT_METHOD_DEFAULT_VISIBILITY = "extract.method.default.visibility"; private final Project myProject; private final PsiType myReturnType; @@ -96,7 +97,7 @@ public class ExtractMethodDialog extends AbstractExtractDialog { String title, String helpId, final PsiElement[] elementsToExtract) { - super(project); + super(project, true); myProject = project; myTargetClass = targetClass; myReturnType = returnType; diff --git a/java/java-impl/src/com/intellij/refactoring/extractMethod/ExtractMethodProcessor.java b/java/java-impl/src/com/intellij/refactoring/extractMethod/ExtractMethodProcessor.java index c61c29c58ff9..0b760502384a 100644 --- a/java/java-impl/src/com/intellij/refactoring/extractMethod/ExtractMethodProcessor.java +++ b/java/java-impl/src/com/intellij/refactoring/extractMethod/ExtractMethodProcessor.java @@ -463,14 +463,14 @@ public class ExtractMethodProcessor implements MatchProvider { protected void apply(final AbstractExtractDialog dialog) { myMethodName = dialog.getChosenMethodName(); myVariableDatum = dialog.getChosenParameters(); - myStatic |= dialog.isMakeStatic(); + myStatic = isStatic() | dialog.isMakeStatic(); myIsChainedConstructor = dialog.isChainedConstructor(); myMethodVisibility = dialog.getVisibility(); } protected AbstractExtractDialog createExtractMethodDialog(final boolean direct) { - return new ExtractMethodDialog(myProject, myTargetClass, myInputVariables, myReturnType, myTypeParameterList, - myThrownExceptions, myStatic, myCanBeStatic, myCanBeChainedConstructor, + return new ExtractMethodDialog(myProject, myTargetClass, myInputVariables, myReturnType, getTypeParameterList(), + getThrownExceptions(), isStatic(), isCanBeStatic(), myCanBeChainedConstructor, suggestInitialMethodName(), myRefactoringName, myHelpId, myElements) { protected boolean areTypesDirected() { @@ -575,10 +575,16 @@ public class ExtractMethodProcessor implements MatchProvider { chooseAnchor(); - int col = myEditor.getCaretModel().getLogicalPosition().column; - int line = myEditor.getCaretModel().getLogicalPosition().line; - LogicalPosition pos = new LogicalPosition(0, 0); - myEditor.getCaretModel().moveToLogicalPosition(pos); + LogicalPosition pos1; + if (myEditor != null) { + int col = myEditor.getCaretModel().getLogicalPosition().column; + int line = myEditor.getCaretModel().getLogicalPosition().line; + pos1 = new LogicalPosition(line, col); + LogicalPosition pos = new LogicalPosition(0, 0); + myEditor.getCaretModel().moveToLogicalPosition(pos); + } else { + pos1 = null; + } final SearchScope processConflictsScope = myMethodVisibility.equals(PsiModifier.PRIVATE) ? new LocalSearchScope(myTargetClass) : @@ -610,17 +616,18 @@ public class ExtractMethodProcessor implements MatchProvider { ApplicationManager.getApplication().runWriteAction(extract); } - LogicalPosition pos1 = new LogicalPosition(line, col); - myEditor.getCaretModel().moveToLogicalPosition(pos1); - int offset = myMethodCall.getMethodExpression().getTextRange().getStartOffset(); - myEditor.getCaretModel().moveToOffset(offset); - myEditor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE); - myEditor.getSelectionModel().removeSelection(); + if (myEditor != null) { + myEditor.getCaretModel().moveToLogicalPosition(pos1); + int offset = myMethodCall.getMethodExpression().getTextRange().getStartOffset(); + myEditor.getCaretModel().moveToOffset(offset); + myEditor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE); + myEditor.getSelectionModel().removeSelection(); + } } - private void doExtract() throws IncorrectOperationException { + public void doExtract() throws IncorrectOperationException { - PsiMethod newMethod = generateEmptyMethod(myThrownExceptions, myStatic); + PsiMethod newMethod = generateEmptyMethod(getThrownExceptions(), isStatic()); myExpression = myInputVariables.replaceWrappedReferences(myElements, myExpression); renameInputVariables(); @@ -991,6 +998,10 @@ public class ExtractMethodProcessor implements MatchProvider { return myTargetClass; } + public PsiType getReturnType() { + return myReturnType; + } + private PsiMethod generateEmptyMethod(PsiClassType[] exceptions, boolean isStatic) throws IncorrectOperationException { PsiMethod newMethod; if (myIsChainedConstructor) { @@ -1001,8 +1012,8 @@ public class ExtractMethodProcessor implements MatchProvider { PsiUtil.setModifierProperty(newMethod, PsiModifier.STATIC, isStatic); } PsiUtil.setModifierProperty(newMethod, myMethodVisibility, true); - if (myTypeParameterList != null) { - newMethod.getTypeParameterList().replace(myTypeParameterList); + if (getTypeParameterList() != null) { + newMethod.getTypeParameterList().replace(getTypeParameterList()); } PsiCodeBlock body = newMethod.getBody(); LOG.assertTrue(body != null); @@ -1400,4 +1411,28 @@ public class ExtractMethodProcessor implements MatchProvider { public InputVariables getInputVariables() { return myInputVariables; } + + public PsiTypeParameterList getTypeParameterList() { + return myTypeParameterList; + } + + public PsiClassType[] getThrownExceptions() { + return myThrownExceptions; + } + + public boolean isStatic() { + return myStatic; + } + + public boolean isCanBeStatic() { + return myCanBeStatic; + } + + public PsiElement[] getElements() { + return myElements; + } + + public PsiVariable[] getOutputVariables() { + return myOutputVariables; + } } diff --git a/java/java-impl/src/com/intellij/refactoring/extractMethodObject/ExtractLightMethodObjectHandler.java b/java/java-impl/src/com/intellij/refactoring/extractMethodObject/ExtractLightMethodObjectHandler.java new file mode 100644 index 000000000000..bb465b9848bb --- /dev/null +++ b/java/java-impl/src/com/intellij/refactoring/extractMethodObject/ExtractLightMethodObjectHandler.java @@ -0,0 +1,254 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.refactoring.extractMethodObject; + +import com.intellij.codeInsight.CodeInsightUtil; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Condition; +import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.*; +import com.intellij.psi.codeStyle.CodeStyleManager; +import com.intellij.psi.codeStyle.JavaCodeStyleManager; +import com.intellij.psi.controlFlow.*; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.refactoring.extractMethod.AbstractExtractDialog; +import com.intellij.refactoring.extractMethod.InputVariables; +import com.intellij.refactoring.extractMethod.PrepareFailedException; +import com.intellij.refactoring.util.RefactoringUtil; +import com.intellij.refactoring.util.VariableData; +import com.intellij.usageView.UsageInfo; +import com.intellij.util.Function; +import com.intellij.util.IncorrectOperationException; +import com.intellij.util.VisibilityUtil; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +public class ExtractLightMethodObjectHandler { + private static final Logger LOG = Logger.getInstance("#" + ExtractLightMethodObjectHandler.class.getName()); + + public static class ExtractedData { + private String myGeneratedCallText; + private PsiClass myGeneratedInnerClass; + private final PsiElement myAnchor; + + public ExtractedData(String generatedCallText, PsiClass generatedInnerClass, PsiElement anchor) { + myGeneratedCallText = generatedCallText; + myGeneratedInnerClass = generatedInnerClass; + myAnchor = anchor; + } + + public PsiElement getAnchor() { + return myAnchor; + } + + public String getGeneratedCallText() { + return myGeneratedCallText; + } + + public PsiClass getGeneratedInnerClass() { + return myGeneratedInnerClass; + } + } + + @Nullable + public static ExtractedData extractLightMethodObject(final Project project, + final PsiFile file, + @NotNull final PsiCodeFragment fragment, + final String methodName) throws PrepareFailedException { + PsiExpression expression = CodeInsightUtil.findExpressionInRange(fragment, 0, fragment.getTextLength()); + final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(project); + final PsiElement[] elements; + if (expression != null) { + elements = new PsiElement[] {elementFactory.createStatementFromText(expression.getText() + ";", expression)}; + } else { + elements = CodeInsightUtil.findStatementsInRange(fragment, 0, fragment.getTextLength()); + } + if (elements.length == 0) { + return null; + } + + final PsiFile copy = PsiFileFactory.getInstance(project) + .createFileFromText(file.getName(), file.getFileType(), file.getText(), file.getModificationStamp(), false); + + final PsiElement originalContext = fragment.getContext(); + if (originalContext == null) { + return null; + } + final TextRange range = originalContext.getTextRange(); + final PsiElement originalAnchor = + CodeInsightUtil.findElementInRange(copy, range.getStartOffset(), range.getEndOffset(), originalContext.getClass()); + //todo before this or super, not found etc + final PsiElement anchor = RefactoringUtil.getParentStatement(originalAnchor, false); + final PsiElement container = anchor.getParent(); + final PsiElement firstElementCopy = container.addRangeBefore(elements[0], elements[elements.length - 1], anchor); + final PsiElement[] elementsCopy = CodeInsightUtil.findStatementsInRange(copy, + firstElementCopy.getTextRange().getStartOffset(), + anchor.getTextRange().getStartOffset()); + if (elementsCopy[elementsCopy.length - 1] instanceof PsiExpressionStatement) { + final PsiExpression expr = ((PsiExpressionStatement)elementsCopy[elementsCopy.length - 1]).getExpression(); + if (!(expr instanceof PsiAssignmentExpression)) { + final PsiType expressionType = expr.getType(); + if (expressionType != null && expressionType != PsiType.VOID) { + final String uniqueResultName = JavaCodeStyleManager.getInstance(project).suggestUniqueVariableName("result", elementsCopy[0], true); + final String statementText = expressionType.getCanonicalText() + " " + uniqueResultName + " = " + expr.getText() + ";"; + elementsCopy[elementsCopy.length - 1] = elementsCopy[elementsCopy.length - 1] + .replace(elementFactory.createStatementFromText(statementText, elementsCopy[elementsCopy.length - 1])); + } + } + } + + LOG.assertTrue(elementsCopy[0].getParent() == container, "element: " + elementsCopy[0].getText() + "; container: " + container.getText()); + final int startOffsetInContainer = elementsCopy[0].getStartOffsetInParent(); + + final ControlFlow controlFlow; + try { + controlFlow = ControlFlowFactory.getInstance(project).getControlFlow(container, LocalsOrMyInstanceFieldsControlFlowPolicy.getInstance()); + } + catch (AnalysisCanceledException e) { + return null; + } + + List variables = ControlFlowUtil.getUsedVariables(controlFlow, + controlFlow.getStartOffset(elementsCopy[0]), + controlFlow.getEndOffset(elementsCopy[elementsCopy.length - 1])); + + variables = ContainerUtil.filter(variables, new Condition() { + @Override + public boolean value(PsiVariable variable) { + final PsiElement variableScope = variable instanceof PsiParameter ? ((PsiParameter)variable).getDeclarationScope() + : PsiTreeUtil.getParentOfType(variable, PsiCodeBlock.class, PsiForStatement.class); + return variableScope != null && PsiTreeUtil.isAncestor(variableScope, elementsCopy[elementsCopy.length - 1], false); + } + }); + + + final String outputVariables = StringUtil.join(variables, new Function() { + @Override + public String fun(PsiVariable variable) { + return "\"variable: \" + " + variable.getName(); + } + }, " +"); + PsiStatement outStatement = elementFactory.createStatementFromText("System.out.println(" + outputVariables + ");", anchor); + outStatement = (PsiStatement)container.addAfter(outStatement, elementsCopy[elementsCopy.length - 1]); + + copy.accept(new JavaRecursiveElementWalkingVisitor() { + private void makePublic(PsiMember method) { + if (method.hasModifierProperty(PsiModifier.PRIVATE)) { + VisibilityUtil.setVisibility(method.getModifierList(), PsiModifier.PUBLIC); + } + } + + @Override + public void visitMethod(PsiMethod method) { + super.visitMethod(method); + makePublic(method); + } + + @Override + public void visitField(PsiField field) { + super.visitField(field); + makePublic(field); + } + }); + + final ExtractMethodObjectProcessor extractMethodObjectProcessor = new ExtractMethodObjectProcessor(project, null, elementsCopy, "") { + @Override + protected AbstractExtractDialog createExtractMethodObjectDialog(MyExtractMethodProcessor processor) { + return new LightExtractMethodObjectDialog(this, methodName); + } + }; + extractMethodObjectProcessor.getExtractProcessor().setShowErrorDialogs(false); + + final ExtractMethodObjectProcessor.MyExtractMethodProcessor extractProcessor = extractMethodObjectProcessor.getExtractProcessor(); + if (extractProcessor.prepare()) { + if (extractProcessor.showDialog()) { + try { + extractProcessor.doExtract(); + final UsageInfo[] usages = extractMethodObjectProcessor.findUsages(); + extractMethodObjectProcessor.performRefactoring(usages); + extractMethodObjectProcessor.runChangeSignature(); + } + catch (IncorrectOperationException e) { + LOG.error(e); + } + if (extractMethodObjectProcessor.isCreateInnerClass()) { + extractMethodObjectProcessor.changeInstanceAccess(project); + } + final PsiElement method = extractMethodObjectProcessor.getMethod(); + LOG.assertTrue(method != null); + method.delete(); + } + } else { + return null; + } + + final int startOffset = startOffsetInContainer + container.getTextRange().getStartOffset(); + final String generatedCall = copy.getText().substring(startOffset, outStatement.getTextOffset()); + return new ExtractedData(generatedCall, + (PsiClass)CodeStyleManager.getInstance(project).reformat(extractMethodObjectProcessor.getInnerClass()), + originalAnchor); + } + + + private static class LightExtractMethodObjectDialog implements AbstractExtractDialog { + private final ExtractMethodObjectProcessor myProcessor; + private final String myMethodName; + + public LightExtractMethodObjectDialog(ExtractMethodObjectProcessor processor, String methodName) { + myProcessor = processor; + myMethodName = methodName; + } + + @Override + public String getChosenMethodName() { + return myMethodName; + } + + @Override + public VariableData[] getChosenParameters() { + final InputVariables inputVariables = myProcessor.getExtractProcessor().getInputVariables(); + return inputVariables.getInputVariables().toArray(new VariableData[inputVariables.getInputVariables().size()]); + } + + @Override + public String getVisibility() { + return PsiModifier.PUBLIC; + } + + @Override + public boolean isMakeStatic() { + return false; + } + + @Override + public boolean isChainedConstructor() { + return false; + } + + @Override + public void show() {} + + @Override + public boolean isOK() { + return true; + } + } +} diff --git a/java/java-impl/src/com/intellij/refactoring/extractMethodObject/ExtractMethodObjectDialog.java b/java/java-impl/src/com/intellij/refactoring/extractMethodObject/ExtractMethodObjectDialog.java index fddd19bb4630..f79280fd1365 100644 --- a/java/java-impl/src/com/intellij/refactoring/extractMethodObject/ExtractMethodObjectDialog.java +++ b/java/java-impl/src/com/intellij/refactoring/extractMethodObject/ExtractMethodObjectDialog.java @@ -19,6 +19,7 @@ import com.intellij.openapi.editor.event.DocumentAdapter; import com.intellij.openapi.editor.event.DocumentEvent; import com.intellij.openapi.help.HelpManager; import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.wm.IdeFocusManager; import com.intellij.psi.*; @@ -44,7 +45,7 @@ import java.awt.event.ActionListener; import java.util.Enumeration; -public class ExtractMethodObjectDialog extends AbstractExtractDialog { +public class ExtractMethodObjectDialog extends DialogWrapper implements AbstractExtractDialog { private final Project myProject; private final PsiType myReturnType; private final PsiTypeParameterList myTypeParameterList; @@ -86,7 +87,7 @@ public class ExtractMethodObjectDialog extends AbstractExtractDialog { public ExtractMethodObjectDialog(Project project, PsiClass targetClass, final InputVariables inputVariables, PsiType returnType, PsiTypeParameterList typeParameterList, PsiType[] exceptions, boolean isStatic, boolean canBeStatic, final PsiElement[] elementsToExtract, final boolean multipleExitPoints) { - super(project); + super(project, true); myProject = project; myTargetClass = targetClass; myReturnType = returnType; diff --git a/java/java-impl/src/com/intellij/refactoring/extractMethodObject/ExtractMethodObjectHandler.java b/java/java-impl/src/com/intellij/refactoring/extractMethodObject/ExtractMethodObjectHandler.java index d8fb3fd6ef87..459a7030457f 100644 --- a/java/java-impl/src/com/intellij/refactoring/extractMethodObject/ExtractMethodObjectHandler.java +++ b/java/java-impl/src/com/intellij/refactoring/extractMethodObject/ExtractMethodObjectHandler.java @@ -30,9 +30,7 @@ import com.intellij.openapi.editor.ScrollType; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Pass; import com.intellij.openapi.util.TextRange; -import com.intellij.psi.PsiDocumentManager; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; +import com.intellij.psi.*; import com.intellij.psi.impl.source.PostprocessReformattingAspect; import com.intellij.refactoring.HelpID; import com.intellij.refactoring.RefactoringActionHandler; @@ -55,7 +53,10 @@ public class ExtractMethodObjectHandler implements RefactoringActionHandler { }); } - private void invokeOnElements(@NotNull final Project project, @NotNull final Editor editor, @NotNull PsiFile file, @NotNull PsiElement[] elements) { + private static void invokeOnElements(@NotNull final Project project, + @NotNull final Editor editor, + @NotNull PsiFile file, + @NotNull PsiElement[] elements) { if (elements.length == 0) { String message = RefactoringBundle .getCannotRefactorMessage(RefactoringBundle.message("selected.block.should.represent.a.set.of.statements.or.an.expression")); @@ -63,17 +64,18 @@ public class ExtractMethodObjectHandler implements RefactoringActionHandler { return; } - final ExtractMethodObjectProcessor processor = new ExtractMethodObjectProcessor(project, editor, elements, ""); - final ExtractMethodObjectProcessor.MyExtractMethodProcessor extractProcessor = processor.getExtractProcessor(); try { - if (!extractProcessor.prepare()) return; + extractMethodObject(project, editor, new ExtractMethodObjectProcessor(project, editor, elements, "")); } catch (PrepareFailedException e) { CommonRefactoringUtil.showErrorHint(project, editor, e.getMessage(), ExtractMethodObjectProcessor.REFACTORING_NAME, HelpID.EXTRACT_METHOD_OBJECT); ExtractMethodHandler.highlightPrepareError(e, file, editor, project); - return; } + } + static void extractMethodObject(Project project, Editor editor, ExtractMethodObjectProcessor processor) throws PrepareFailedException { + final ExtractMethodObjectProcessor.MyExtractMethodProcessor extractProcessor = processor.getExtractProcessor(); + if (!extractProcessor.prepare()) return; if (!CommonRefactoringUtil.checkReadOnlyStatus(project, extractProcessor.getTargetClass().getContainingFile())) return; if (extractProcessor.showDialog()) { run(project, editor, processor, extractProcessor); @@ -81,11 +83,16 @@ public class ExtractMethodObjectHandler implements RefactoringActionHandler { } public static void run(@NotNull final Project project, - @NotNull final Editor editor, + final Editor editor, @NotNull final ExtractMethodObjectProcessor processor, @NotNull final ExtractMethodObjectProcessor.MyExtractMethodProcessor extractProcessor) { - final int offset = editor.getCaretModel().getOffset(); - final RangeMarker marker = editor.getDocument().createRangeMarker(new TextRange(offset, offset)); + final RangeMarker marker; + if (editor != null) { + final int offset = editor.getCaretModel().getOffset(); + marker = editor.getDocument().createRangeMarker(new TextRange(offset, offset)); + } else { + marker = null; + } CommandProcessor.getInstance().executeCommand(project, new Runnable() { public void run() { PostprocessReformattingAspect.getInstance(project).postponeFormattingInside(new Runnable() { @@ -98,7 +105,11 @@ public class ExtractMethodObjectHandler implements RefactoringActionHandler { } }); processor.run(); - processor.runChangeSignature(); + ApplicationManager.getApplication().runWriteAction(new Runnable() { + public void run() { + processor.runChangeSignature(); + } + }); } catch (IncorrectOperationException e) { LOG.error(e); @@ -110,7 +121,9 @@ public class ExtractMethodObjectHandler implements RefactoringActionHandler { if (processor.isCreateInnerClass()) { processor.moveUsedMethodsToInner(); PsiDocumentManager.getInstance(project).commitAllDocuments(); - DuplicatesImpl.processDuplicates(extractProcessor, project, editor); + if (editor != null) { + DuplicatesImpl.processDuplicates(extractProcessor, project, editor); + } } ApplicationManager.getApplication().runWriteAction(new Runnable() { @Override @@ -125,9 +138,11 @@ public class ExtractMethodObjectHandler implements RefactoringActionHandler { }); } }, ExtractMethodObjectProcessor.REFACTORING_NAME, ExtractMethodObjectProcessor.REFACTORING_NAME); - editor.getCaretModel().moveToOffset(marker.getStartOffset()); - marker.dispose(); - editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE); + if (editor != null) { + editor.getCaretModel().moveToOffset(marker.getStartOffset()); + marker.dispose(); + editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE); + } } public void invoke(@NotNull final Project project, @NotNull final PsiElement[] elements, final DataContext dataContext) { diff --git a/java/java-impl/src/com/intellij/refactoring/extractMethodObject/ExtractMethodObjectProcessor.java b/java/java-impl/src/com/intellij/refactoring/extractMethodObject/ExtractMethodObjectProcessor.java index 7f95c49c65b3..49f4b2727cee 100644 --- a/java/java-impl/src/com/intellij/refactoring/extractMethodObject/ExtractMethodObjectProcessor.java +++ b/java/java-impl/src/com/intellij/refactoring/extractMethodObject/ExtractMethodObjectProcessor.java @@ -37,10 +37,13 @@ import com.intellij.psi.codeStyle.VariableKind; import com.intellij.psi.controlFlow.ControlFlowUtil; import com.intellij.psi.impl.source.PsiImmediateClassType; import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.search.LocalSearchScope; +import com.intellij.psi.search.SearchScope; import com.intellij.psi.search.searches.ReferencesSearch; import com.intellij.psi.util.PropertyUtil; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; +import com.intellij.psi.util.PsiUtilCore; import com.intellij.refactoring.BaseRefactoringProcessor; import com.intellij.refactoring.HelpID; import com.intellij.refactoring.changeSignature.ChangeSignatureProcessor; @@ -52,6 +55,7 @@ import com.intellij.refactoring.ui.MemberSelectionPanel; import com.intellij.refactoring.util.RefactoringUtil; import com.intellij.refactoring.util.classMembers.MemberInfo; import com.intellij.refactoring.util.duplicates.Match; +import com.intellij.testFramework.LightVirtualFile; import com.intellij.usageView.UsageInfo; import com.intellij.usageView.UsageViewDescriptor; import com.intellij.usageView.UsageViewUtil; @@ -82,7 +86,7 @@ public class ExtractMethodObjectProcessor extends BaseRefactoringProcessor { private boolean myMadeStatic = false; private final Set myUsages = new LinkedHashSet(); private PsiClass myInnerClass; - private ChangeSignatureProcessor myChangeSignatureProcessor; + private boolean myChangeReturnType; private Runnable myCopyMethodToInner; public ExtractMethodObjectProcessor(Project project, Editor editor, PsiElement[] elements, final String innerClassName) { @@ -100,8 +104,12 @@ public class ExtractMethodObjectProcessor extends BaseRefactoringProcessor { @NotNull protected UsageInfo[] findUsages() { final ArrayList result = new ArrayList(); + final PsiClass containingClass = getMethod().getContainingClass(); + final SearchScope scope = PsiUtilCore.getVirtualFile(containingClass) == null + ? new LocalSearchScope(containingClass) + : GlobalSearchScope.projectScope(myProject); PsiReference[] refs = - ReferencesSearch.search(getMethod(), GlobalSearchScope.projectScope(myProject), false).toArray(PsiReference.EMPTY_ARRAY); + ReferencesSearch.search(getMethod(), scope, false).toArray(PsiReference.EMPTY_ARRAY); for (PsiReference ref : refs) { final PsiElement element = ref.getElement(); if (element != null && element.isValid()) { @@ -141,7 +149,7 @@ public class ExtractMethodObjectProcessor extends BaseRefactoringProcessor { return UsageViewUtil.removeDuplicatedUsages(usageInfos); } - protected void performRefactoring(final UsageInfo[] usages) { + public void performRefactoring(final UsageInfo[] usages) { try { if (isCreateInnerClass()) { myInnerClass = (PsiClass)getMethod().getContainingClass().add(myElementFactory.createClass(getInnerClassName())); @@ -258,12 +266,6 @@ public class ExtractMethodObjectProcessor extends BaseRefactoringProcessor { myInnerClass.add(GenerateMembersUtil.generateGetterPrototype(field)); } - PsiParameter[] params = getMethod().getParameterList().getParameters(); - ParameterInfoImpl[] infos = new ParameterInfoImpl[params.length]; - for (int i = 0; i < params.length; i++) { - PsiParameter param = params[i]; - infos[i] = new ParameterInfoImpl(i, param.getName(), param.getType()); - } final PsiCodeBlock body = getMethod().getBody(); LOG.assertTrue(body != null); final LinkedHashSet vars = new LinkedHashSet(); @@ -320,7 +322,7 @@ public class ExtractMethodObjectProcessor extends BaseRefactoringProcessor { for (PsiElement declaredElement : declaredElements) { if (declaredElement instanceof PsiVariable) { for (PsiVariable variable : outputVariables) { - PsiLocalVariable var = (PsiLocalVariable)declaredElement; + PsiVariable var = (PsiVariable)declaredElement; if (Comparing.strEqual(var.getName(), variable.getName())) { final PsiExpression initializer = var.getInitializer(); if (initializer == null) { @@ -385,23 +387,25 @@ public class ExtractMethodObjectProcessor extends BaseRefactoringProcessor { } } - myChangeSignatureProcessor = new ChangeSignatureProcessor(myProject, getMethod(), false, null, getMethod().getName(), - new PsiImmediateClassType(myInnerClass, PsiSubstitutor.EMPTY), infos); + myChangeReturnType = true; } void runChangeSignature() { - if (myChangeSignatureProcessor != null) { - myChangeSignatureProcessor.run(); - } if (myCopyMethodToInner != null) { - ApplicationManager.getApplication().runWriteAction(myCopyMethodToInner); + myCopyMethodToInner.run(); + } + if (myChangeReturnType) { + final PsiTypeElement typeElement = ((PsiLocalVariable)((PsiDeclarationStatement)JavaPsiFacade.getElementFactory(myProject) + .createStatementFromText(myInnerClassName + " l =null;", myInnerClass)).getDeclaredElements()[0]).getTypeElement(); + final PsiTypeElement innerMethodReturnTypeElement = myInnerMethod.getReturnTypeElement(); + LOG.assertTrue(innerMethodReturnTypeElement != null); + innerMethodReturnTypeElement.replace(typeElement); } } private String getPureName(PsiVariable var) { final JavaCodeStyleManager styleManager = JavaCodeStyleManager.getInstance(myProject); - final VariableKind kind = var instanceof PsiLocalVariable ? VariableKind.LOCAL_VARIABLE : VariableKind.PARAMETER; - return styleManager.variableNameToPropertyName(var.getName(), kind); + return styleManager.variableNameToPropertyName(var.getName(), styleManager.getVariableKind(var)); } public PsiExpression processMethodDeclaration( PsiExpressionList expressionList) throws IncorrectOperationException { @@ -624,6 +628,22 @@ public class ExtractMethodObjectProcessor extends BaseRefactoringProcessor { return myExtractProcessor; } + protected AbstractExtractDialog createExtractMethodObjectDialog(final MyExtractMethodProcessor processor) { + return new ExtractMethodObjectDialog(myProject, processor.getTargetClass(), processor.getInputVariables(), processor.getReturnType(), + processor.getTypeParameterList(), + processor.getThrownExceptions(), processor.isStatic(), processor.isCanBeStatic(), + processor.getElements(), myMultipleExitPoints){ + @Override + protected boolean isUsedAfter(PsiVariable variable) { + return ArrayUtil.find(processor.getOutputVariables(), variable) != -1; + } + }; + } + + public PsiClass getInnerClass() { + return myInnerClass; + } + public class MyExtractMethodProcessor extends ExtractMethodProcessor { public MyExtractMethodProcessor(Project project, @@ -640,19 +660,13 @@ public class ExtractMethodObjectProcessor extends BaseRefactoringProcessor { @Override protected void apply(final AbstractExtractDialog dialog) { super.apply(dialog); - myCreateInnerClass = ((ExtractMethodObjectDialog)dialog).createInnerClass(); + myCreateInnerClass = !(dialog instanceof ExtractMethodObjectDialog) || ((ExtractMethodObjectDialog)dialog).createInnerClass(); myInnerClassName = myCreateInnerClass ? StringUtil.capitalize(dialog.getChosenMethodName()) : dialog.getChosenMethodName(); } @Override protected AbstractExtractDialog createExtractMethodDialog(final boolean direct) { - return new ExtractMethodObjectDialog(myProject, myTargetClass, myInputVariables, myReturnType, myTypeParameterList, - myThrownExceptions, myStatic, myCanBeStatic, myElements, myMultipleExitPoints){ - @Override - protected boolean isUsedAfter(PsiVariable variable) { - return ArrayUtil.find(myOutputVariables, variable) != -1; - } - }; + return createExtractMethodObjectDialog(this); } @Override diff --git a/java/java-impl/src/com/intellij/refactoring/inheritanceToDelegation/InheritanceToDelegationDialog.java b/java/java-impl/src/com/intellij/refactoring/inheritanceToDelegation/InheritanceToDelegationDialog.java index 32e4992fe744..efff2b7790cd 100644 --- a/java/java-impl/src/com/intellij/refactoring/inheritanceToDelegation/InheritanceToDelegationDialog.java +++ b/java/java-impl/src/com/intellij/refactoring/inheritanceToDelegation/InheritanceToDelegationDialog.java @@ -33,7 +33,6 @@ import com.intellij.refactoring.ui.NameSuggestionsField; import com.intellij.refactoring.ui.RefactoringDialog; import com.intellij.refactoring.util.classMembers.InterfaceMemberDependencyGraph; import com.intellij.refactoring.util.classMembers.MemberInfo; -import com.intellij.util.containers.HashMap; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -45,6 +44,7 @@ import java.awt.event.ItemListener; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; public class InheritanceToDelegationDialog extends RefactoringDialog { private final PsiClass[] mySuperClasses; diff --git a/java/java-impl/src/com/intellij/refactoring/inheritanceToDelegation/InheritanceToDelegationHandler.java b/java/java-impl/src/com/intellij/refactoring/inheritanceToDelegation/InheritanceToDelegationHandler.java index c9daa8c1bbdb..7cc464bcb710 100644 --- a/java/java-impl/src/com/intellij/refactoring/inheritanceToDelegation/InheritanceToDelegationHandler.java +++ b/java/java-impl/src/com/intellij/refactoring/inheritanceToDelegation/InheritanceToDelegationHandler.java @@ -41,13 +41,10 @@ import com.intellij.refactoring.util.RefactoringHierarchyUtil; import com.intellij.refactoring.util.RefactoringMessageUtil; import com.intellij.refactoring.util.classMembers.MemberInfo; import com.intellij.refactoring.util.classMembers.MemberInfoStorage; -import com.intellij.util.containers.HashMap; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; +import java.util.*; public class InheritanceToDelegationHandler implements RefactoringActionHandler { private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.inheritanceToDelegation.InheritanceToDelegationHandler"); @@ -106,23 +103,26 @@ public class InheritanceToDelegationHandler implements RefactoringActionHandler if (!CommonRefactoringUtil.checkReadOnlyStatus(project, aClass)) return; - final PsiClass[] bases = aClass.getSupers(); + PsiClass[] bases = aClass.getSupers(); @NonNls final String javaLangObject = CommonClassNames.JAVA_LANG_OBJECT; + if (bases.length == 0 || bases.length == 1 && javaLangObject.equals(bases[0].getQualifiedName())) { String message = RefactoringBundle.getCannotRefactorMessage(RefactoringBundle.message("class.does.not.have.base.classes.or.interfaces", aClass.getQualifiedName())); CommonRefactoringUtil.showErrorHint(project, editor, message, REFACTORING_NAME, HelpID.INHERITANCE_TO_DELEGATION); return; } - final HashMap> basesToMemberInfos = new HashMap>(); + final HashMap> basesToMemberInfos = new LinkedHashMap>(); for (PsiClass base : bases) { + if (javaLangObject.equals(base.getQualifiedName())) continue; basesToMemberInfos.put(base, createBaseClassMemberInfos(base)); } + final Set baseClasses = basesToMemberInfos.keySet(); new InheritanceToDelegationDialog(project, aClass, - bases, basesToMemberInfos).show(); + baseClasses.toArray(new PsiClass[baseClasses.size()]), basesToMemberInfos).show(); } private static List createBaseClassMemberInfos(PsiClass baseClass) { diff --git a/java/java-impl/src/com/intellij/refactoring/util/RefactoringUtil.java b/java/java-impl/src/com/intellij/refactoring/util/RefactoringUtil.java index ed08349519a7..0cc9819c4574 100644 --- a/java/java-impl/src/com/intellij/refactoring/util/RefactoringUtil.java +++ b/java/java-impl/src/com/intellij/refactoring/util/RefactoringUtil.java @@ -30,10 +30,7 @@ import com.intellij.openapi.editor.markup.RangeHighlighter; import com.intellij.openapi.editor.markup.TextAttributes; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ProjectRootManager; -import com.intellij.openapi.util.Comparing; -import com.intellij.openapi.util.Condition; -import com.intellij.openapi.util.Pair; -import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.util.*; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; @@ -1045,13 +1042,13 @@ public class RefactoringUtil { } public static void fixJavadocsForParams(PsiMethod method, Set newParameters) throws IncorrectOperationException { - fixJavadocsForParams(method, newParameters, Condition.FALSE); + fixJavadocsForParams(method, newParameters, Conditions.>alwaysFalse()); } public static void fixJavadocsForParams(PsiMethod method, Set newParameters, Condition> eqCondition) throws IncorrectOperationException { - fixJavadocsForParams(method, newParameters, eqCondition, Condition.TRUE); + fixJavadocsForParams(method, newParameters, eqCondition, Conditions.alwaysTrue()); } public static void fixJavadocsForParams(PsiMethod method, @@ -1245,7 +1242,7 @@ public class RefactoringUtil { @Nullable public static PsiTypeParameterList createTypeParameterListWithUsedTypeParameters(@Nullable final PsiTypeParameterList fromList, @NotNull final PsiElement... elements) { - return createTypeParameterListWithUsedTypeParameters(fromList, Condition.TRUE, elements); + return createTypeParameterListWithUsedTypeParameters(fromList, Conditions.alwaysTrue(), elements); } @Nullable @@ -1290,7 +1287,7 @@ public class RefactoringUtil { } public static void collectTypeParameters(final Set used, final PsiElement element) { - collectTypeParameters(used, element, Condition.TRUE); + collectTypeParameters(used, element, Conditions.alwaysTrue()); } public static void collectTypeParameters(final Set used, final PsiElement element, final Condition filter) { diff --git a/java/java-indexing-api/src/com/intellij/psi/search/searches/ClassInheritorsSearch.java b/java/java-indexing-api/src/com/intellij/psi/search/searches/ClassInheritorsSearch.java index cc6bf9d3044d..ec3599fec62e 100644 --- a/java/java-indexing-api/src/com/intellij/psi/search/searches/ClassInheritorsSearch.java +++ b/java/java-indexing-api/src/com/intellij/psi/search/searches/ClassInheritorsSearch.java @@ -21,10 +21,7 @@ import com.intellij.openapi.extensions.ExtensionPointName; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressIndicatorProvider; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Computable; -import com.intellij.openapi.util.Condition; -import com.intellij.openapi.util.Pair; -import com.intellij.openapi.util.Ref; +import com.intellij.openapi.util.*; import com.intellij.psi.*; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.search.PsiSearchScopeUtil; @@ -106,7 +103,7 @@ public class ClassInheritorsSearch extends ExtensibleQueryFactoryalwaysTrue()); } public SearchParameters(@NotNull final PsiClass aClass, @NotNull SearchScope scope, final boolean checkDeep, final boolean checkInheritance, @@ -170,7 +167,12 @@ public class ClassInheritorsSearch extends ExtensibleQueryFactory search(@NotNull final PsiClass aClass, final boolean checkDeep) { - return search(aClass, aClass.getUseScope(), checkDeep); + return search(aClass, ApplicationManager.getApplication().runReadAction(new Computable() { + @Override + public SearchScope compute() { + return aClass.getUseScope(); + } + }), checkDeep); } public static Query search(@NotNull PsiClass aClass) { diff --git a/java/java-psi-api/src/com/intellij/psi/GenericsUtil.java b/java/java-psi-api/src/com/intellij/psi/GenericsUtil.java index c835a4171f74..4646aea635b3 100644 --- a/java/java-psi-api/src/com/intellij/psi/GenericsUtil.java +++ b/java/java-psi-api/src/com/intellij/psi/GenericsUtil.java @@ -262,7 +262,9 @@ public class GenericsUtil { for (PsiTypeParameter typeParameter : typeParams) { PsiType substituted = substitutor.substitute(typeParameter); if (substituted == null) return null; - substituted = PsiUtil.captureToplevelWildcards(substituted, context); + if (context != null) { + substituted = PsiUtil.captureToplevelWildcards(substituted, context); + } PsiClassType[] extendsTypes = typeParameter.getExtendsListTypes(); for (PsiClassType type : extendsTypes) { diff --git a/java/java-psi-api/src/com/intellij/psi/LambdaUtil.java b/java/java-psi-api/src/com/intellij/psi/LambdaUtil.java index fcf5e8945e7a..8ff42ca085a0 100644 --- a/java/java-psi-api/src/com/intellij/psi/LambdaUtil.java +++ b/java/java-psi-api/src/com/intellij/psi/LambdaUtil.java @@ -477,6 +477,17 @@ public class LambdaUtil { return true; } + //JLS 14.8 Expression Statements + public static boolean isExpressionStatementExpression(PsiElement body) { + return body instanceof PsiAssignmentExpression || + body instanceof PsiPrefixExpression && + (((PsiPrefixExpression)body).getOperationTokenType() == JavaTokenType.PLUSPLUS || + ((PsiPrefixExpression)body).getOperationTokenType() == JavaTokenType.MINUSMINUS) || + body instanceof PsiPostfixExpression || + body instanceof PsiCallExpression || + body instanceof PsiReferenceExpression && !body.isPhysical(); + } + public static class TypeParamsChecker extends PsiTypeVisitor { private PsiMethod myMethod; private final PsiClass myClass; diff --git a/java/java-psi-api/src/com/intellij/psi/PsiPackage.java b/java/java-psi-api/src/com/intellij/psi/PsiPackage.java index ed68dc2ac204..65e0af8265ad 100644 --- a/java/java-psi-api/src/com/intellij/psi/PsiPackage.java +++ b/java/java-psi-api/src/com/intellij/psi/PsiPackage.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -111,7 +111,7 @@ public interface PsiPackage extends PsiCheckedRenameElement, NavigationItem, Psi @NonNls String getName(); - boolean containsClassNamed(String name); + boolean containsClassNamed(@NotNull String name); @NotNull PsiClass[] findClassByShortName(@NotNull String name, @NotNull GlobalSearchScope scope); diff --git a/java/java-psi-api/src/com/intellij/psi/util/ClassUtil.java b/java/java-psi-api/src/com/intellij/psi/util/ClassUtil.java index 9f7f6e2cc164..b5b72565da19 100644 --- a/java/java-psi-api/src/com/intellij/psi/util/ClassUtil.java +++ b/java/java-psi-api/src/com/intellij/psi/util/ClassUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -33,6 +33,7 @@ public class ClassUtil { return null; } + @NotNull public static String extractClassName(@NotNull String fqName) { int i = fqName.lastIndexOf('.'); return i == -1 ? fqName : fqName.substring(i + 1); @@ -58,7 +59,7 @@ public class ClassUtil { return null; } - public static void formatClassName(@NotNull final PsiClass aClass, final StringBuilder buf) { + public static void formatClassName(@NotNull final PsiClass aClass, @NotNull StringBuilder buf) { final String qName = aClass.getQualifiedName(); if (qName != null) { buf.append(qName); @@ -78,7 +79,7 @@ public class ClassUtil { } @Nullable - private static PsiClass getContainerClass(final PsiClass aClass) { + private static PsiClass getContainerClass(@NotNull PsiClass aClass) { PsiElement parent = aClass.getContext(); while (parent != null && !(parent instanceof PsiClass)) { parent = parent.getContext(); @@ -113,11 +114,11 @@ public class ClassUtil { return result[0]; } - public static PsiClass findNonQualifiedClassByIndex(final String indexName, @NotNull final PsiClass containingClass) { + public static PsiClass findNonQualifiedClassByIndex(@NotNull String indexName, @NotNull final PsiClass containingClass) { return findNonQualifiedClassByIndex(indexName, containingClass, false); } - public static PsiClass findNonQualifiedClassByIndex(final String indexName, @NotNull final PsiClass containingClass, + public static PsiClass findNonQualifiedClassByIndex(@NotNull String indexName, @NotNull final PsiClass containingClass, final boolean jvmCompatible) { String prefix = getDigitPrefix(indexName); final int idx = !prefix.isEmpty() ? Integer.parseInt(prefix) : -1; @@ -167,6 +168,7 @@ public class ClassUtil { return result[0]; } + @NotNull private static String getDigitPrefix(@NotNull String indexName) { int i; for (i = 0; i < indexName.length(); i++) { @@ -186,24 +188,24 @@ public class ClassUtil { * @return found psiClass */ @Nullable - public static PsiClass findPsiClass(final PsiManager psiManager, String externalName){ + public static PsiClass findPsiClass(@NotNull PsiManager psiManager, @NotNull String externalName){ return findPsiClass(psiManager, externalName, null, false); } @Nullable - public static PsiClass findPsiClass(final PsiManager psiManager, - String externalName, + public static PsiClass findPsiClass(@NotNull PsiManager psiManager, + @NotNull String externalName, PsiClass psiClass, boolean jvmCompatible) { return findPsiClass(psiManager, externalName, psiClass, jvmCompatible, GlobalSearchScope.allScope(psiManager.getProject())); } @Nullable - public static PsiClass findPsiClass(final PsiManager psiManager, - String externalName, + public static PsiClass findPsiClass(@NotNull PsiManager psiManager, + @NotNull String externalName, @Nullable PsiClass psiClass, boolean jvmCompatible, - final GlobalSearchScope scope) { + @NotNull GlobalSearchScope scope) { for (int pos = 0; pos < externalName.length(); pos++) { if (externalName.charAt(pos) == '$') { PsiClass parentClass = psiClass; @@ -220,8 +222,8 @@ public class ClassUtil { } @Nullable - private static PsiClass findSubclass(final PsiManager psiManager, - final String externalName, + private static PsiClass findSubclass(@NotNull PsiManager psiManager, + @NotNull String externalName, final PsiClass psiClass, final boolean jvmCompatible) { for (int pos = 0; pos < externalName.length(); pos++) { @@ -251,7 +253,7 @@ public class ClassUtil { @Nullable - public static PsiClass findPsiClassByJVMName(final PsiManager manager, final String jvmClassName) { + public static PsiClass findPsiClassByJVMName(@NotNull PsiManager manager, @NotNull String jvmClassName) { return findPsiClass(manager, jvmClassName.replace('/', '.'), null, true); } } \ No newline at end of file diff --git a/java/java-psi-api/src/com/intellij/psi/util/MethodSignatureBase.java b/java/java-psi-api/src/com/intellij/psi/util/MethodSignatureBase.java index 150a39c4d647..de1c4bf3336a 100644 --- a/java/java-psi-api/src/com/intellij/psi/util/MethodSignatureBase.java +++ b/java/java-psi-api/src/com/intellij/psi/util/MethodSignatureBase.java @@ -75,6 +75,7 @@ public abstract class MethodSignatureBase implements MethodSignature { return myTypeParameters; } + @NotNull public PsiType[] getErasedParameterTypes() { PsiType[] result = myErasedParameterTypes; if (result == null) { diff --git a/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java b/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java index b38bb494c1e7..a7f92056a195 100644 --- a/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java +++ b/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java @@ -1108,8 +1108,11 @@ public final class PsiUtil extends PsiUtilCore { } public static PsiReturnStatement[] findReturnStatements(PsiMethod method) { + return findReturnStatements(method.getBody()); + } + + public static PsiReturnStatement[] findReturnStatements(PsiCodeBlock body) { ArrayList vector = new ArrayList(); - PsiCodeBlock body = method.getBody(); if (body != null) { addReturnStatements(vector, body); } diff --git a/java/java-psi-impl/src/com/intellij/codeInsight/folding/impl/JavaFoldingBuilderBase.java b/java/java-psi-impl/src/com/intellij/codeInsight/folding/impl/JavaFoldingBuilderBase.java index f4154bc4fe1a..9b6c23e79b00 100644 --- a/java/java-psi-impl/src/com/intellij/codeInsight/folding/impl/JavaFoldingBuilderBase.java +++ b/java/java-psi-impl/src/com/intellij/codeInsight/folding/impl/JavaFoldingBuilderBase.java @@ -625,6 +625,11 @@ public abstract class JavaFoldingBuilderBase extends CustomFoldingBuilder implem } int leftStart = method.getParameterList().getTextRange().getEndOffset(); + int bodyStart = body.getTextRange().getStartOffset(); + if (bodyStart > leftStart && !StringUtil.isEmptyOrSpaces(document.getCharsSequence().subSequence(leftStart + 1, bodyStart))) { + return false; + } + int leftEnd = statement.getTextRange().getStartOffset(); int rightStart = statement.getTextRange().getEndOffset(); int rightEnd = body.getTextRange().getEndOffset(); diff --git a/java/java-psi-impl/src/com/intellij/codeInsight/javadoc/JavaDocInfoGenerator.java b/java/java-psi-impl/src/com/intellij/codeInsight/javadoc/JavaDocInfoGenerator.java index 1f642b18d1b1..67b1ef813de8 100644 --- a/java/java-psi-impl/src/com/intellij/codeInsight/javadoc/JavaDocInfoGenerator.java +++ b/java/java-psi-impl/src/com/intellij/codeInsight/javadoc/JavaDocInfoGenerator.java @@ -501,6 +501,7 @@ public class JavaDocInfoGenerator { buffer.append(""); buffer.append(field.getName()); appendInitializer(buffer, field); + enumConstantOrdinal(buffer, field, field.getContainingClass(), "\n"); buffer.append(""); } @@ -638,7 +639,7 @@ public class JavaDocInfoGenerator { String text = o.toString(); PsiType type = variable.getType(); if (type.equalsToText(CommonClassNames.JAVA_LANG_STRING)) { - text = "\"" + StringUtil.shortenPathWithEllipsis(text, 120) + "\""; + text = "\"" + StringUtil.escapeLineBreak(StringUtil.shortenPathWithEllipsis(text, 120)) + "\""; } else if (type.equalsToText("char")) text = "'" + text + "'"; try { @@ -754,7 +755,20 @@ public class JavaDocInfoGenerator { } final PsiAnnotationMemberValue value = pair.getValue(); if (value != null) { - buffer.append(XmlStringUtil.escapeString(value.getText())); + if (value instanceof PsiArrayInitializerMemberValue) { + buffer.append("{"); + boolean firstMember = true; + + for(PsiAnnotationMemberValue memberValue:((PsiArrayInitializerMemberValue)value).getInitializers()) { + if (!firstMember) buffer.append(","); + firstMember = false; + + appendLinkOrText(buffer, memberValue, generateLink); + } + buffer.append("}"); + } else { + appendLinkOrText(buffer, value, generateLink); + } } } buffer.append(")"); @@ -778,6 +792,32 @@ public class JavaDocInfoGenerator { } } + private static void appendLinkOrText(StringBuilder buffer, + PsiAnnotationMemberValue memberValue, + boolean generateLink) { + if (generateLink && memberValue instanceof PsiQualifiedReferenceElement) { + String text = ((PsiQualifiedReferenceElement)memberValue).getCanonicalText(); + PsiElement resolve = ((PsiQualifiedReferenceElement)memberValue).resolve(); + + if (resolve instanceof PsiField) { + PsiField field = (PsiField)resolve; + PsiClass aClass = field.getContainingClass(); + int startOfPropertyNamePosition = text.lastIndexOf('.'); + + if (startOfPropertyNamePosition != -1) { + text = text.substring(0, startOfPropertyNamePosition) + '#' + text.substring(startOfPropertyNamePosition + 1); + } + else { + if (aClass != null) text = aClass.getQualifiedName() + '#' + field.getName(); + } + generateLink(buffer, text, aClass != null? aClass.getName() + '.' + field.getName():null, memberValue, false); + return; + } + } + + buffer.append(XmlStringUtil.escapeString(memberValue.getText())); + } + public static boolean isDocumentedAnnotationType(@Nullable PsiElement annotationType) { return annotationType instanceof PsiClass && AnnotationUtil.isAnnotated((PsiClass)annotationType, "java.lang.annotation.Documented", false); } diff --git a/java/java-psi-impl/src/com/intellij/core/CorePsiPackageImplementationHelper.java b/java/java-psi-impl/src/com/intellij/core/CorePsiPackageImplementationHelper.java index 5a97f596d983..51e010e05d29 100644 --- a/java/java-psi-impl/src/com/intellij/core/CorePsiPackageImplementationHelper.java +++ b/java/java-psi-impl/src/com/intellij/core/CorePsiPackageImplementationHelper.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiPackage; import com.intellij.psi.impl.file.PsiPackageImplementationHelper; import com.intellij.psi.search.GlobalSearchScope; +import org.jetbrains.annotations.NotNull; /** * @author yole @@ -27,31 +28,34 @@ import com.intellij.psi.search.GlobalSearchScope; public class CorePsiPackageImplementationHelper extends PsiPackageImplementationHelper { private static final ModificationTracker[] EMPTY_DEPENDENCY = {ModificationTracker.NEVER_CHANGED}; + @NotNull @Override - public GlobalSearchScope adjustAllScope(PsiPackage psiPackage, GlobalSearchScope globalSearchScope) { + public GlobalSearchScope adjustAllScope(@NotNull PsiPackage psiPackage, @NotNull GlobalSearchScope globalSearchScope) { return globalSearchScope; } + @NotNull @Override - public VirtualFile[] occursInPackagePrefixes(PsiPackage psiPackage) { + public VirtualFile[] occursInPackagePrefixes(@NotNull PsiPackage psiPackage) { return VirtualFile.EMPTY_ARRAY; } @Override - public void handleQualifiedNameChange(PsiPackage psiPackage, String newQualifiedName) { + public void handleQualifiedNameChange(@NotNull PsiPackage psiPackage, @NotNull String newQualifiedName) { } @Override - public void navigate(PsiPackage psiPackage, boolean requestFocus) { + public void navigate(@NotNull PsiPackage psiPackage, boolean requestFocus) { } @Override - public boolean packagePrefixExists(PsiPackage psiPackage) { + public boolean packagePrefixExists(@NotNull PsiPackage psiPackage) { return false; } + @NotNull @Override - public Object[] getDirectoryCachedValueDependencies(PsiPackage cachedValueProvider) { + public Object[] getDirectoryCachedValueDependencies(@NotNull PsiPackage cachedValueProvider) { return EMPTY_DEPENDENCY; } } diff --git a/java/java-psi-impl/src/com/intellij/psi/NonClasspathClassFinder.java b/java/java-psi-impl/src/com/intellij/psi/NonClasspathClassFinder.java index 59a4e10d40fe..7fc4a2245941 100644 --- a/java/java-psi-impl/src/com/intellij/psi/NonClasspathClassFinder.java +++ b/java/java-psi-impl/src/com/intellij/psi/NonClasspathClassFinder.java @@ -248,7 +248,7 @@ public abstract class NonClasspathClassFinder extends PsiElementFinder { } @NotNull - public static GlobalSearchScope addNonClasspathScope(Project project, GlobalSearchScope base) { + public static GlobalSearchScope addNonClasspathScope(@NotNull Project project, @NotNull GlobalSearchScope base) { GlobalSearchScope scope = base; for (PsiElementFinder finder : Extensions.getExtensions(EP_NAME, project)) { if (finder instanceof NonClasspathClassFinder) { diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/PsiSubstitutorImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/PsiSubstitutorImpl.java index 9c9525aef859..d53bb2e26820 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/PsiSubstitutorImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/PsiSubstitutorImpl.java @@ -389,7 +389,7 @@ public class PsiSubstitutorImpl implements PsiSubstitutor { } else { //unbounded - substituted = PsiWildcardType.createExtends(manager, substitutedBoundType); + substituted = substitutedBoundType instanceof PsiCapturedWildcardType ? ((PsiCapturedWildcardType)substitutedBoundType).getWildcard() : PsiWildcardType.createExtends(manager, substitutedBoundType); } } } diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/compiled/ClsFileImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/compiled/ClsFileImpl.java index 6ab8174ce28a..20c11a7ccb39 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/compiled/ClsFileImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/compiled/ClsFileImpl.java @@ -27,8 +27,7 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.extensions.PluginId; import com.intellij.openapi.fileTypes.FileType; -import com.intellij.openapi.progress.NonCancelableSection; -import com.intellij.openapi.progress.ProgressIndicatorProvider; +import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.DefaultProjectFactory; import com.intellij.openapi.roots.FileIndexFacade; import com.intellij.openapi.ui.Queryable; @@ -323,7 +322,7 @@ public class ClsFileImpl extends ClsRepositoryPsiElement synchronized (myMirrorLock) { mirrorTreeElement = myMirrorFileElement; if (mirrorTreeElement == null) { - VirtualFile file = getVirtualFile(); + final VirtualFile file = getVirtualFile(); CharSequence mirrorText = ClassFileDecompiler.decompileText(file); String ext = JavaFileType.INSTANCE.getDefaultExtension(); @@ -335,16 +334,18 @@ public class ClsFileImpl extends ClsRepositoryPsiElement mirrorTreeElement = SourceTreeToPsiMap.psiToTreeNotNull(mirror); // IMPORTANT: do not take lock too early - FileDocumentManager.saveToString() can run write action - NonCancelableSection section = ProgressIndicatorProvider.startNonCancelableSectionIfSupported(); - try { - setMirror(mirrorTreeElement); - } - catch (InvalidMirrorException e) { - LOG.error(file.getPath(), wrapException(e, file)); - } - finally { - section.done(); - } + final TreeElement finalMirrorTreeElement = mirrorTreeElement; + ProgressManager.getInstance().executeNonCancelableSection(new Runnable() { + @Override + public void run() { + try { + setMirror(finalMirrorTreeElement); + } + catch (InvalidMirrorException e) { + LOG.error(file.getPath(), wrapException(e, file)); + } + } + }); myMirrorFileElement = mirrorTreeElement; } diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/file/PsiPackageImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/file/PsiPackageImpl.java index 9129052bf74f..93fd1ebbd935 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/file/PsiPackageImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/file/PsiPackageImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,6 +23,7 @@ import com.intellij.navigation.ItemPresentationProviders; import com.intellij.openapi.project.DumbService; import com.intellij.openapi.ui.Queryable; import com.intellij.openapi.util.Condition; +import com.intellij.openapi.util.Conditions; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; import com.intellij.psi.impl.JavaPsiFacadeImpl; @@ -46,7 +47,6 @@ import org.jetbrains.annotations.Nullable; import java.util.*; public class PsiPackageImpl extends PsiPackageBase implements PsiPackage, Queryable { - public static boolean DEBUG = false; private volatile CachedValue myAnnotationList; private volatile CachedValue> myDirectories; private volatile CachedValue> myDirectoriesWithLibSources; @@ -72,6 +72,7 @@ public class PsiPackageImpl extends PsiPackageBase implements PsiPackage, Querya } } + @NotNull private CachedValue> createCachedDirectories(final boolean includeLibrarySources) { return CachedValuesManager.getManager(myManager.getProject()).createCachedValue(new CachedValueProvider>() { @Override @@ -131,6 +132,7 @@ public class PsiPackageImpl extends PsiPackageBase implements PsiPackage, Querya } } + @Override public String toString() { return "PsiPackage:" + getQualifiedName(); } @@ -141,6 +143,7 @@ public class PsiPackageImpl extends PsiPackageBase implements PsiPackage, Querya return getClasses(allScope()); } + @NotNull protected GlobalSearchScope allScope() { return PsiPackageImplementationHelper.getInstance().adjustAllScope(this, GlobalSearchScope.allScope(getProject())); } @@ -177,7 +180,7 @@ public class PsiPackageImpl extends PsiPackageBase implements PsiPackage, Querya } @NotNull - private PsiClass[] getCachedClassesByName(String name) { + private PsiClass[] getCachedClassesByName(@NotNull String name) { if (DumbService.getInstance(getProject()).isDumb()) { return getCachedClassInDumbMode(name); } @@ -215,7 +218,7 @@ public class PsiPackageImpl extends PsiPackageBase implements PsiPackage, Querya } @Override - public boolean containsClassNamed(String name) { + public boolean containsClassNamed(@NotNull String name) { return getCachedClassesByName(name).length > 0; } @@ -247,7 +250,7 @@ public class PsiPackageImpl extends PsiPackageBase implements PsiPackage, Querya } @Nullable - private PsiPackage findSubPackageByName(String name) { + private PsiPackage findSubPackageByName(@NotNull String name) { final String qName = getQualifiedName(); final String subpackageQName = qName.isEmpty() ? name : qName + "." + name; return getFacade().findPackage(subpackageQName); @@ -270,11 +273,11 @@ public class PsiPackageImpl extends PsiPackageBase implements PsiPackage, Querya if (nameHint != null) { final String shortName = nameHint.getName(state); final PsiClass[] classes = findClassByShortName(shortName, scope); - if (!processClasses(processor, state, classes, Condition.TRUE)) return false; + if (!processClasses(processor, state, classes, Conditions.alwaysTrue())) return false; } else { PsiClass[] classes = getClasses(scope); - if (!processClasses(processor, state, classes, nameCondition != null ? nameCondition : Condition.TRUE)) return false; + if (!processClasses(processor, state, classes, nameCondition != null ? nameCondition : Conditions.alwaysTrue())) return false; } } if (classHint == null || classHint.shouldProcess(ElementClassHint.DeclarationKind.PACKAGE)) { @@ -302,7 +305,9 @@ public class PsiPackageImpl extends PsiPackageBase implements PsiPackage, Querya return true; } - private static boolean processClasses(PsiScopeProcessor processor, ResolveState state, PsiClass[] classes, + private static boolean processClasses(@NotNull PsiScopeProcessor processor, + @NotNull ResolveState state, + @NotNull PsiClass[] classes, @NotNull Condition nameCondition) { for (PsiClass aClass : classes) { String name = aClass.getName(); diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/file/PsiPackageImplementationHelper.java b/java/java-psi-impl/src/com/intellij/psi/impl/file/PsiPackageImplementationHelper.java index 050786c617f0..4ca3f1fead43 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/file/PsiPackageImplementationHelper.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/file/PsiPackageImplementationHelper.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2011 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,22 +19,26 @@ import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiPackage; import com.intellij.psi.search.GlobalSearchScope; +import org.jetbrains.annotations.NotNull; /** * @author yole */ public abstract class PsiPackageImplementationHelper { - public abstract GlobalSearchScope adjustAllScope(PsiPackage psiPackage, GlobalSearchScope globalSearchScope); + @NotNull + public abstract GlobalSearchScope adjustAllScope(@NotNull PsiPackage psiPackage, @NotNull GlobalSearchScope globalSearchScope); - public abstract VirtualFile[] occursInPackagePrefixes(PsiPackage psiPackage); + @NotNull + public abstract VirtualFile[] occursInPackagePrefixes(@NotNull PsiPackage psiPackage); - public abstract void handleQualifiedNameChange(PsiPackage psiPackage, String newQualifiedName); + public abstract void handleQualifiedNameChange(@NotNull PsiPackage psiPackage, @NotNull String newQualifiedName); - public abstract void navigate(PsiPackage psiPackage, boolean requestFocus); + public abstract void navigate(@NotNull PsiPackage psiPackage, boolean requestFocus); - public abstract boolean packagePrefixExists(PsiPackage psiPackage); + public abstract boolean packagePrefixExists(@NotNull PsiPackage psiPackage); - public abstract Object[] getDirectoryCachedValueDependencies(PsiPackage cachedValueProvider); + @NotNull + public abstract Object[] getDirectoryCachedValueDependencies(@NotNull PsiPackage cachedValueProvider); public static PsiPackageImplementationHelper getInstance() { return ServiceManager.getService(PsiPackageImplementationHelper.class); diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiFieldImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiFieldImpl.java index 78c5c2d8fdc8..fd38e3ddaf56 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiFieldImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiFieldImpl.java @@ -399,9 +399,14 @@ public class PsiFieldImpl extends JavaStubPsiElement implements Ps @Override public PsiElement getOriginalElement() { - PsiClass originalClass = (PsiClass)getContainingClass().getOriginalElement(); - PsiField originalField = originalClass.findFieldByName(getName(), false); - return originalField != null ? originalField : this; + PsiClass containingClass = getContainingClass(); + if (containingClass != null) { + PsiField originalField = ((PsiClass)containingClass.getOriginalElement()).findFieldByName(getName(), false); + if (originalField != null) { + return originalField; + } + } + return this; } @Override diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiParameterImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiParameterImpl.java index e77e7ad01f5e..799f9b459e69 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiParameterImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiParameterImpl.java @@ -324,4 +324,24 @@ public class PsiParameterImpl extends JavaStubPsiElement imple final PsiElement declarationScope = getDeclarationScope(); return new LocalSearchScope(declarationScope); } + + @Override + public PsiElement getOriginalElement() { + PsiElement parent = getParent(); + if (parent instanceof PsiParameterList) { + PsiElement gParent = parent.getParent(); + if (gParent instanceof PsiMethod) { + PsiElement originalMethod = gParent.getOriginalElement(); + if (originalMethod instanceof PsiMethod) { + int index = ((PsiParameterList)parent).getParameterIndex(this); + PsiParameter[] originalParameters = ((PsiMethod)originalMethod).getParameterList().getParameters(); + if (index < originalParameters.length) { + return originalParameters[index]; + } + } + } + } + return this; + } + } diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/PsiResolveHelperImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/PsiResolveHelperImpl.java index 556bf7152b68..a27dfe5adb83 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/PsiResolveHelperImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/PsiResolveHelperImpl.java @@ -15,7 +15,6 @@ */ package com.intellij.psi.impl.source.resolve; -import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.*; @@ -211,16 +210,8 @@ public class PsiResolveHelperImpl implements PsiResolveHelper { .getSubstitutionForTypeParameter(typeParam, param, arg, isContraVariantPosition, languageLevel); } - private PsiInferenceHelper myTestHelper; - - public void setTestHelper(PsiInferenceHelper testHelper) { - myTestHelper = testHelper; - } - - public PsiInferenceHelper getInferenceHelper(LanguageLevel languageLevel) { - if (ApplicationManager.getApplication().isUnitTestMode() && myTestHelper != null) { - return myTestHelper; - } + @NotNull + public PsiInferenceHelper getInferenceHelper(@NotNull LanguageLevel languageLevel) { if (languageLevel.isAtLeast(LanguageLevel.JDK_1_8)) { return new PsiGraphInferenceHelper(myManager); } diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSession.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSession.java index 35db6c29bc0c..a1b4570e5361 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSession.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSession.java @@ -174,7 +174,6 @@ public class InferenceSession { } } } - return true; } if (expr instanceof PsiLambdaExpression) { if (!((PsiLambdaExpression)expr).hasFormalParameterTypes()) { @@ -234,7 +233,7 @@ public class InferenceSession { !MethodCandidateInfo.ourOverloadGuard.currentStack().contains(PsiUtil.skipParenthesizedExprUp(parent.getParent()))) { final Set additionalConstraints = new LinkedHashSet(); if (parameters.length > 0) { - collectAdditionalConstraints(parameters, args, properties.getMethod(), PsiSubstitutor.EMPTY, additionalConstraints, properties.isVarargs(), true); + collectAdditionalConstraints(parameters, args, properties.getMethod(), PsiSubstitutor.EMPTY, additionalConstraints, properties.isVarargs()); } if (!additionalConstraints.isEmpty() && !proceedWithAdditionalConstraints(additionalConstraints)) { @@ -264,67 +263,113 @@ public class InferenceSession { PsiMethod parentMethod, PsiSubstitutor siteSubstitutor, Set additionalConstraints, - boolean varargs, boolean toplevel) { + boolean varargs) { for (int i = 0; i < args.length; i++) { - if (args[i] != null) { - InferenceSession session = myNestedSessions.get(PsiTreeUtil.getParentOfType(args[i], PsiCallExpression.class)); - if (session == null) { - session = this; - } - PsiType parameterType = session.substituteWithInferenceVariables(getParameterType(parameters, i, siteSubstitutor, varargs)); - if (!isPertinentToApplicability(args[i], parentMethod)) { - additionalConstraints.add(new ExpressionCompatibilityConstraint(args[i], parameterType)); + final PsiExpression arg = PsiUtil.skipParenthesizedExprDown(args[i]); + if (arg != null) { + final InferenceSession nestedCallSession = findNestedCallSession(arg); + final PsiType parameterType = + nestedCallSession.substituteWithInferenceVariables(getParameterType(parameters, i, siteSubstitutor, varargs)); + if (!isPertinentToApplicability(arg, parentMethod)) { + additionalConstraints.add(new ExpressionCompatibilityConstraint(arg, parameterType)); } - additionalConstraints.add(new CheckedExceptionCompatibilityConstraint(args[i], parameterType)); - if (args[i] instanceof PsiCallExpression && PsiPolyExpressionUtil.isPolyExpression(args[i])) { + additionalConstraints.add(new CheckedExceptionCompatibilityConstraint(arg, parameterType)); + if (arg instanceof PsiCallExpression) { //If the expression is a poly class instance creation expression (15.9) or a poly method invocation expression (15.12), //the set contains all constraint formulas that would appear in the set C when determining the poly expression's invocation type. - final PsiCallExpression callExpression = (PsiCallExpression)args[i]; - collectAdditionalConstraints(additionalConstraints, callExpression); - } else if (args[i] instanceof PsiLambdaExpression && toplevel) { - final PsiType interfaceReturnType = LambdaUtil.getFunctionalInterfaceReturnType(parameterType); - if (interfaceReturnType != null) { - final List returnExpressions = LambdaUtil.getReturnExpressions((PsiLambdaExpression)args[i]); - for (PsiExpression returnExpression : returnExpressions) { - if (returnExpression instanceof PsiCallExpression) { - final PsiCallExpression callExpression = (PsiCallExpression)returnExpression; - collectAdditionalConstraints(additionalConstraints, callExpression); - } - } + final PsiMethod calledMethod = getCalledMethod((PsiCallExpression)arg); + if (PsiPolyExpressionUtil.isMethodCallPolyExpression(arg, calledMethod)) { + collectAdditionalConstraints(additionalConstraints, (PsiCallExpression)arg); } + } else if (arg instanceof PsiLambdaExpression) { + collectLambdaReturnExpression(additionalConstraints, (PsiLambdaExpression)arg, parameterType); } } } } + private static PsiMethod getCalledMethod(PsiCallExpression arg) { + final PsiExpressionList argumentList = arg.getArgumentList(); + if (argumentList == null || argumentList.getExpressions().length == 0) { + return null; + } + + MethodCandidateInfo.CurrentCandidateProperties properties = MethodCandidateInfo.getCurrentMethod(argumentList); + if (properties != null) { + return properties.getMethod(); + } + final JavaResolveResult resolveResult = getMethodResult(arg); + return resolveResult instanceof MethodCandidateInfo ? (PsiMethod)resolveResult.getElement() : null; + } + + private void collectLambdaReturnExpression(Set additionalConstraints, + PsiLambdaExpression lambdaExpression, + PsiType parameterType) { + final PsiType interfaceReturnType = LambdaUtil.getFunctionalInterfaceReturnType(parameterType); + if (interfaceReturnType != null) { + final List returnExpressions = LambdaUtil.getReturnExpressions(lambdaExpression); + for (PsiExpression returnExpression : returnExpressions) { + processReturnExpression(additionalConstraints, returnExpression, interfaceReturnType); + } + } + } + + private void processReturnExpression(Set additionalConstraints, + PsiExpression returnExpression, + PsiType functionalType) { + if (returnExpression instanceof PsiCallExpression) { + final PsiMethod calledMethod = getCalledMethod((PsiCallExpression)returnExpression); + if (PsiPolyExpressionUtil.isMethodCallPolyExpression(returnExpression, calledMethod)) { + collectAdditionalConstraints(additionalConstraints, (PsiCallExpression)returnExpression); + } + } + else if (returnExpression instanceof PsiParenthesizedExpression) { + processReturnExpression(additionalConstraints, ((PsiParenthesizedExpression)returnExpression).getExpression(), functionalType); + } + else if (returnExpression instanceof PsiConditionalExpression) { + processReturnExpression(additionalConstraints, ((PsiConditionalExpression)returnExpression).getThenExpression(), functionalType); + processReturnExpression(additionalConstraints, ((PsiConditionalExpression)returnExpression).getElseExpression(), functionalType); + } + else if (returnExpression instanceof PsiLambdaExpression) { + collectLambdaReturnExpression(additionalConstraints, (PsiLambdaExpression)returnExpression, functionalType); + } + } + private void collectAdditionalConstraints(final Set additionalConstraints, final PsiCallExpression callExpression) { PsiExpressionList argumentList = callExpression.getArgumentList(); if (argumentList != null) { - final PsiLambdaExpression expression = PsiTreeUtil.getParentOfType(argumentList, PsiLambdaExpression.class); - final Computable computableResolve = new Computable() { - @Override - public JavaResolveResult compute() { - return callExpression.resolveMethodGenerics(); - } - }; + final JavaResolveResult result = getMethodResult(callExpression); MethodCandidateInfo.CurrentCandidateProperties properties = MethodCandidateInfo.getCurrentMethod(argumentList); - final JavaResolveResult result = properties != null ? null : - expression == null - ? computableResolve.compute() - : PsiResolveHelper.ourGraphGuard.doPreventingRecursion(expression, false, computableResolve); final PsiMethod method = result instanceof MethodCandidateInfo ? ((MethodCandidateInfo)result).getElement() : properties != null ? properties.getMethod() : null; if (method != null) { final PsiExpression[] newArgs = argumentList.getExpressions(); final PsiParameter[] newParams = method.getParameterList().getParameters(); if (newParams.length > 0) { collectAdditionalConstraints(newParams, newArgs, method, result != null ? ((MethodCandidateInfo)result).getSiteSubstitutor() : properties.getSubstitutor(), - additionalConstraints, result != null ? ((MethodCandidateInfo)result).isVarargs() : properties.isVarargs(), false); + additionalConstraints, result != null ? ((MethodCandidateInfo)result).isVarargs() : properties.isVarargs()); } } } } + private static JavaResolveResult getMethodResult(final PsiCallExpression callExpression) { + final PsiExpressionList argumentList = callExpression.getArgumentList(); + + final PsiLambdaExpression expression = PsiTreeUtil.getParentOfType(argumentList, PsiLambdaExpression.class); + final Computable computableResolve = new Computable() { + @Override + public JavaResolveResult compute() { + return callExpression.resolveMethodGenerics(); + } + }; + MethodCandidateInfo.CurrentCandidateProperties properties = MethodCandidateInfo.getCurrentMethod(argumentList); + return properties != null ? null : + expression == null + ? computableResolve.compute() + : PsiResolveHelper.ourGraphGuard.doPreventingRecursion(expression, false, computableResolve); + } + public PsiSubstitutor retrieveNonPrimitiveEqualsBounds(Collection variables) { PsiSubstitutor substitutor = mySiteSubstitutor; for (InferenceVariable variable : variables) { @@ -702,7 +747,7 @@ public class InferenceSession { } final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(getManager().getProject()); - final PsiTypeParameter[] freshParameters = createFreshVariables(vars); + final PsiTypeParameter[] freshParameters = createFreshVariables(vars, substitutor); for (int i = 0; i < freshParameters.length; i++) { PsiTypeParameter parameter = freshParameters[i]; final InferenceVariable var = vars.get(i); @@ -725,7 +770,7 @@ public class InferenceSession { return substitutor; } - private PsiTypeParameter[] createFreshVariables(final List vars) { + private PsiTypeParameter[] createFreshVariables(final List vars, final PsiSubstitutor siteSubstitutor) { final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(getManager().getProject()); PsiSubstitutor substitutor = PsiSubstitutor.EMPTY; @@ -742,7 +787,7 @@ public class InferenceSession { final String classText = "class I<" + StringUtil.join(vars, new Function() { @Override public String fun(InferenceVariable variable) { - final PsiType glb = composeBound(variable, InferenceBound.UPPER, UPPER_BOUND_FUNCTION, ySubstitutor, true); + final PsiType glb = composeBound(variable, InferenceBound.UPPER, UPPER_BOUND_FUNCTION, ySubstitutor.putAll(siteSubstitutor), true); return getFreshVariableName(variable) + " extends " + glb.getInternalCanonicalText(); } }, ", ") + ">{}"; @@ -860,46 +905,67 @@ public class InferenceSession { //extract subset of constraints final Set subset = buildSubset(additionalConstraints); - //collect all input variables of selection + //collect all input variables of selection final Set varsToResolve = new LinkedHashSet(); for (ConstraintFormula formula : subset) { if (formula instanceof InputOutputConstraintFormula) { - final Set inputVariables = ((InputOutputConstraintFormula)formula).getInputVariables(this); - if (inputVariables != null) { - for (InferenceVariable inputVariable : inputVariables) { - varsToResolve.addAll(inputVariable.getDependencies(this)); - } - varsToResolve.addAll(inputVariables); - } + collectVarsToResolve(varsToResolve, (InputOutputConstraintFormula)formula); } } - //resolve input variables - PsiSubstitutor substitutor = resolveSubset(varsToResolve, siteSubstitutor); - if (substitutor == null) { - return false; + for (ConstraintFormula formula : subset) { + if (!processOneConstraint(formula, siteSubstitutor, varsToResolve)) return false; } + } + return true; + } - if (myContext instanceof PsiCallExpression) { - PsiExpressionList argumentList = ((PsiCallExpression)myContext).getArgumentList(); - LOG.assertTrue(argumentList != null); - MethodCandidateInfo.updateSubstitutor(argumentList, substitutor); + private void collectVarsToResolve(Set varsToResolve, InputOutputConstraintFormula formula) { + final Set inputVariables = formula.getInputVariables(this); + if (inputVariables != null) { + for (InferenceVariable inputVariable : inputVariables) { + varsToResolve.addAll(inputVariable.getDependencies(this)); } + varsToResolve.addAll(inputVariables); + } + } - try { - for (ConstraintFormula additionalConstraint : subset) { - additionalConstraint.apply(substitutor, true); - } - - myConstraints.addAll(subset); - if (!repeatInferencePhases(true)) { - return false; + private boolean processOneConstraint(ConstraintFormula formula, PsiSubstitutor siteSubstitutor, Set varsToResolve) { + if (formula instanceof ExpressionCompatibilityConstraint) { + final PsiExpression expression = ((ExpressionCompatibilityConstraint)formula).getExpression(); + final PsiCallExpression callExpression = PsiTreeUtil.getParentOfType(expression, PsiCallExpression.class, false); + if (callExpression != null) { + final InferenceSession session = myNestedSessions.get(callExpression); + if (session != null) { + formula.apply(session.myInferenceSubstitution, true); + collectVarsToResolve(varsToResolve, (InputOutputConstraintFormula)formula); } } - finally { - LambdaUtil.ourFunctionTypes.set(null); + } + + //resolve input variables + PsiSubstitutor substitutor = resolveSubset(varsToResolve, siteSubstitutor); + if (substitutor == null) { + return false; + } + + if (myContext instanceof PsiCallExpression) { + PsiExpressionList argumentList = ((PsiCallExpression)myContext).getArgumentList(); + LOG.assertTrue(argumentList != null); + MethodCandidateInfo.updateSubstitutor(argumentList, substitutor); + } + + try { + formula.apply(substitutor, true); + + myConstraints.add(formula); + if (!repeatInferencePhases(true)) { + return false; } } + finally { + LambdaUtil.ourFunctionTypes.set(null); + } return true; } @@ -1139,9 +1205,6 @@ public class InferenceSession { } final List returnExpressions = LambdaUtil.getReturnExpressions((PsiLambdaExpression)arg); - if (sReturnType == PsiType.VOID) { - return returnExpressions.isEmpty() && session == null; - } if (LambdaUtil.isFunctionalType(sReturnType) && LambdaUtil.isFunctionalType(tReturnType) && !TypeConversionUtil.isAssignable(TypeConversionUtil.erasure(sReturnType), TypeConversionUtil.erasure(tReturnType)) && @@ -1197,10 +1260,6 @@ public class InferenceSession { return true; } - if (sReturnType == PsiType.VOID && session != null) { - return false; - } - final boolean sPrimitive = sReturnType instanceof PsiPrimitiveType && sReturnType != PsiType.VOID; final boolean tPrimitive = tReturnType instanceof PsiPrimitiveType && tReturnType != PsiType.VOID; @@ -1295,6 +1354,14 @@ public class InferenceSession { return myInferenceSubstitution.substitute(type); } + public InferenceSession findNestedCallSession(PsiExpression arg) { + InferenceSession session = myNestedSessions.get(PsiTreeUtil.getParentOfType(arg, PsiCallExpression.class)); + if (session == null) { + session = this; + } + return session; + } + public PsiType startWithFreshVars(PsiType type) { PsiSubstitutor s = PsiSubstitutor.EMPTY; for (InferenceVariable variable : myInferenceVariables) { diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceVariable.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceVariable.java index 8d91b620f798..da7b4b78930a 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceVariable.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceVariable.java @@ -17,6 +17,7 @@ package com.intellij.psi.impl.source.resolve.graphInference; import com.intellij.psi.*; import com.intellij.psi.impl.light.LightTypeParameter; +import com.intellij.psi.util.PsiUtil; import java.util.*; @@ -49,6 +50,10 @@ public class InferenceVariable extends LightTypeParameter { } public boolean addBound(PsiType classType, InferenceBound inferenceBound) { + if (inferenceBound == InferenceBound.EQ && + PsiUtil.resolveClassInClassTypeOnly(classType) == this) { + return false; + } List list = myBounds.get(inferenceBound); if (list == null) { list = new ArrayList(); @@ -77,20 +82,18 @@ public class InferenceVariable extends LightTypeParameter { } } + if (!session.hasCapture(this) && dependencies.isEmpty()) { + return dependencies; + } + next: for (InferenceVariable variable : session.getInferenceVariables()) { if (!dependencies.contains(variable) && variable != this) { - nextBound: - for (List bounds : myBounds.values()) { //todo + for (List bounds : variable.myBounds.values()) { //todo if (bounds != null) { for (PsiType bound : bounds) { - final Set deps = new HashSet(); - session.collectDependencies(bound, deps); - if (deps.isEmpty()) { - continue nextBound; - } - - if (deps.contains(this)) { + final InferenceVariable inferenceVariable = session.getInferenceVariable(bound); + if (inferenceVariable == this) { dependencies.add(variable); continue next; } diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/LambdaExpressionCompatibilityConstraint.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/LambdaExpressionCompatibilityConstraint.java index c282481e79c0..f7e9badfa33c 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/LambdaExpressionCompatibilityConstraint.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/LambdaExpressionCompatibilityConstraint.java @@ -3,6 +3,7 @@ package com.intellij.psi.impl.source.resolve.graphInference.constraints; import com.intellij.psi.*; import com.intellij.psi.impl.source.resolve.graphInference.FunctionalInterfaceParameterizationUtil; import com.intellij.psi.impl.source.resolve.graphInference.InferenceSession; +import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; import java.util.List; @@ -62,8 +63,9 @@ public class LambdaExpressionCompatibilityConstraint implements ConstraintFormul if (returnExpressions.isEmpty() && !myExpression.isValueCompatible()) { //not value-compatible return false; } - returnType = session.substituteWithInferenceVariables(substitutor.substitute(returnType)); - if (!session.isProperType(returnType)) { + InferenceSession callsession = session.findNestedCallSession(myExpression); + returnType = callsession.substituteWithInferenceVariables(substitutor.substitute(returnType)); + if (!callsession.isProperType(returnType)) { for (PsiExpression returnExpression : returnExpressions) { constraints.add(new ExpressionCompatibilityConstraint(returnExpression, returnType)); } diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/PsiMethodReferenceCompatibilityConstraint.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/PsiMethodReferenceCompatibilityConstraint.java index fbfd8195fc11..b5e4188e1644 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/PsiMethodReferenceCompatibilityConstraint.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/PsiMethodReferenceCompatibilityConstraint.java @@ -169,11 +169,11 @@ public class PsiMethodReferenceCompatibilityConstraint implements ConstraintForm } LOG.assertTrue(referencedMethodReturnType != null, method); - session.initBounds(method.getTypeParameters()); + session.initBounds(myExpression, method.getTypeParameters()); if (!PsiTreeUtil.isContextAncestor(containingClass, myExpression, false) || PsiUtil.getEnclosingStaticElement(myExpression, containingClass) != null) { - session.initBounds(containingClass.getTypeParameters()); + session.initBounds(myExpression, containingClass.getTypeParameters()); } //if i) the method reference elides NonWildTypeArguments, @@ -243,7 +243,7 @@ public class PsiMethodReferenceCompatibilityConstraint implements ConstraintForm final PsiClass qualifierClass = PsiUtil.resolveClassInType(qualifierType); if (qualifierClass != null) { - session.initBounds(qualifierClass.getTypeParameters()); + session.initBounds(myExpression, qualifierClass.getTypeParameters()); constraints.add(new StrictSubtypingConstraint(session.substituteWithInferenceVariables(qualifierType), session.substituteWithInferenceVariables(substitutor.substitute(targetParameters[0].getType())))); } diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/StrictSubtypingConstraint.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/StrictSubtypingConstraint.java index 412b09eadec3..6b3c104aab35 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/StrictSubtypingConstraint.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/constraints/StrictSubtypingConstraint.java @@ -92,8 +92,22 @@ public class StrictSubtypingConstraint implements ConstraintFormula { return false; } - if (!(myS instanceof PsiClassType)) return false; - PsiClassType.ClassResolveResult SResult = ((PsiClassType)myS).resolveGenerics(); + PsiClassType.ClassResolveResult SResult = null; + if (myS instanceof PsiIntersectionType) { + for (PsiType conjunct : ((PsiIntersectionType)myS).getConjuncts()) { + if (conjunct instanceof PsiClassType) { + final PsiClassType.ClassResolveResult conjunctResult = ((PsiClassType)conjunct).resolveGenerics(); + if (InheritanceUtil.isInheritorOrSelf(conjunctResult.getElement(), CClass, true)) { + SResult = conjunctResult; + break; + } + } + } + } else if (myS instanceof PsiClassType) { + SResult = ((PsiClassType)myS).resolveGenerics(); + } + + if (SResult == null) return false; PsiClass SClass = SResult.getElement(); if (((PsiClassType)myT).isRaw()) { return SClass != null && InheritanceUtil.isInheritorOrSelf(SClass, CClass, true); diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiLambdaExpressionImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiLambdaExpressionImpl.java index 069f667d92cf..9d35ace98cf4 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiLambdaExpressionImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiLambdaExpressionImpl.java @@ -30,7 +30,6 @@ import com.intellij.psi.scope.PsiScopeProcessor; import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; -import com.intellij.util.containers.IntArrayList; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -78,16 +77,11 @@ public class PsiLambdaExpressionImpl extends ExpressionPsiElement implements Psi @Override public boolean isVoidCompatible() { final PsiElement body = getBody(); - if (body != null) { - try { - ControlFlow controlFlow = ControlFlowFactory.getInstance(getProject()).getControlFlow(body, LocalsOrMyInstanceFieldsControlFlowPolicy - .getInstance()); - int startOffset = controlFlow.getStartOffset(body); - int endOffset = controlFlow.getEndOffset(body); - return startOffset != -1 && endOffset != -1 && !ControlFlowUtil.canCompleteNormally(controlFlow, startOffset, endOffset); - } - catch (AnalysisCanceledException e) { - return true; + if (body instanceof PsiCodeBlock) { + for (PsiReturnStatement statement : PsiUtil.findReturnStatements((PsiCodeBlock)body)) { + if (statement.getReturnValue() != null) { + return false; + } } } return true; @@ -96,18 +90,25 @@ public class PsiLambdaExpressionImpl extends ExpressionPsiElement implements Psi @Override public boolean isValueCompatible() { final PsiElement body = getBody(); - if (body != null) { + if (body instanceof PsiCodeBlock) { try { - final ControlFlow controlFlow = - ControlFlowFactory.getInstance(getProject()).getControlFlow(body, LocalsOrMyInstanceFieldsControlFlowPolicy.getInstance(), false); - if (ControlFlowUtil.findExitPointsAndStatements(controlFlow, 0, controlFlow.getSize(), new IntArrayList(), - PsiReturnStatement.class, - PsiThrowStatement.class).isEmpty()) { + ControlFlow controlFlow = + ControlFlowFactory.getInstance(getProject()).getControlFlow(body, LocalsOrMyInstanceFieldsControlFlowPolicy + .getInstance()); + int startOffset = controlFlow.getStartOffset(body); + int endOffset = controlFlow.getEndOffset(body); + if (startOffset != -1 && endOffset != -1 && ControlFlowUtil.canCompleteNormally(controlFlow, startOffset, endOffset)) { return false; } } catch (AnalysisCanceledException e) { - return true; + return false; + } + + for (PsiReturnStatement statement : PsiUtil.findReturnStatements((PsiCodeBlock)body)) { + if (statement.getReturnValue() == null) { + return false; + } } } return true; @@ -200,17 +201,36 @@ public class PsiLambdaExpressionImpl extends ExpressionPsiElement implements Psi } } + + //A lambda expression (§15.27) is potentially compatible with a functional interface type (§9.8) if all of the following are true: + // The arity of the target type's function type is the same as the arity of the lambda expression. + // If the target type's function type has a void return, then the lambda body is either a statement expression (§14.8) or a void-compatible block (§15.27.2). + // If the target type's function type has a (non-void) return type, then the lambda body is either an expression or a value-compatible block (§15.27.2). + PsiType methodReturnType = interfaceMethod.getReturnType(); if (checkReturnType) { final String uniqueVarName = JavaCodeStyleManager.getInstance(getProject()).suggestUniqueVariableName("l", this, true); final String canonicalText = toArray(leftType).getCanonicalText(); final PsiStatement assignmentFromText = JavaPsiFacade.getElementFactory(getProject()) .createStatementFromText(canonicalText + " " + uniqueVarName + " = " + getText(), this); final PsiLocalVariable localVariable = (PsiLocalVariable)((PsiDeclarationStatement)assignmentFromText).getDeclaredElements()[0]; - PsiType methodReturnType = interfaceMethod.getReturnType(); if (methodReturnType != null) { return LambdaHighlightingUtil.checkReturnTypeCompatible((PsiLambdaExpression)localVariable.getInitializer(), substitutor.substitute(methodReturnType)) == null; } + } else { + final PsiElement body = getBody(); + if (methodReturnType == PsiType.VOID) { + if (body instanceof PsiCodeBlock) { + return isVoidCompatible(); + } else { + return LambdaUtil.isExpressionStatementExpression(body); + } + } else { + if (body instanceof PsiCodeBlock) { + return isValueCompatible(); + } + return body instanceof PsiExpression; + } } return true; } diff --git a/java/java-psi-impl/src/com/intellij/psi/scope/conflictResolvers/JavaMethodsConflictResolver.java b/java/java-psi-impl/src/com/intellij/psi/scope/conflictResolvers/JavaMethodsConflictResolver.java index 3d9ea08125a8..6bb33be05ea5 100644 --- a/java/java-psi-impl/src/com/intellij/psi/scope/conflictResolvers/JavaMethodsConflictResolver.java +++ b/java/java-psi-impl/src/com/intellij/psi/scope/conflictResolvers/JavaMethodsConflictResolver.java @@ -158,7 +158,8 @@ public class JavaMethodsConflictResolver implements PsiConflictResolver{ // A lambda expression or a method reference expression is potentially compatible with a type variable if the type variable is a type parameter of the candidate method. final PsiClass paramClass = PsiUtil.resolveClassInType(paramType); if (paramClass instanceof PsiTypeParameter && ((PsiTypeParameter)paramClass).getOwner() == method) continue; - if (!lambdaExpression.isAcceptable(((MethodCandidateInfo)conflict).getSubstitutor(false).substitute(paramType), lambdaExpression.hasFormalParameterTypes())) { + if (!lambdaExpression.isAcceptable(((MethodCandidateInfo)conflict).getSubstitutor(false).substitute(paramType), + InferenceSession.isPertinentToApplicability(lambdaExpression, method))) { iterator.remove(); } } @@ -397,7 +398,7 @@ public class JavaMethodsConflictResolver implements PsiConflictResolver{ } @MethodCandidateInfo.ApplicabilityLevelConstant - protected int checkApplicability(@NotNull List conflicts) { + public int checkApplicability(@NotNull List conflicts) { @MethodCandidateInfo.ApplicabilityLevelConstant int maxApplicabilityLevel = 0; boolean toFilter = false; for (CandidateInfo conflict : conflicts) { diff --git a/java/java-tests/testData/codeInsight/completion/smartType/NoSemicolonInsideParentheses-out.java b/java/java-tests/testData/codeInsight/completion/smartType/NoSemicolonInsideParentheses-out.java new file mode 100644 index 000000000000..3199c94ee908 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/smartType/NoSemicolonInsideParentheses-out.java @@ -0,0 +1,7 @@ +public class MyAspect { + + public void foo() { + String nameToUse = /*adjustName*/(toString()); //todo + } + +} diff --git a/java/java-tests/testData/codeInsight/completion/smartType/NoSemicolonInsideParentheses.java b/java/java-tests/testData/codeInsight/completion/smartType/NoSemicolonInsideParentheses.java new file mode 100644 index 000000000000..30c2f7d757af --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/smartType/NoSemicolonInsideParentheses.java @@ -0,0 +1,7 @@ +public class MyAspect { + + public void foo() { + String nameToUse = /*adjustName*/(to); //todo + } + +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/CaptureWildcardFromUnboundCaptureWildcard.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/CaptureWildcardFromUnboundCaptureWildcard.java new file mode 100644 index 000000000000..0abec87dc335 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/CaptureWildcardFromUnboundCaptureWildcard.java @@ -0,0 +1,9 @@ +class Test & Runnable> {} + +interface Supplier { + K get(); +} + +interface Getter extends Supplier { + public S get(); +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/IDEA106985.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/IDEA106985.java new file mode 100644 index 000000000000..b11bfa46632c --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/IDEA106985.java @@ -0,0 +1,15 @@ + +import java.util.Map; + +class Test { + + public static void main(String[] args) { + Map map = make(); + } + + + public static Map make() { + return null; + } + +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/IDEA114797.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/IDEA114797.java new file mode 100644 index 000000000000..9e4ee06ade1e --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/IDEA114797.java @@ -0,0 +1,15 @@ +import java.util.ArrayList; +import java.util.List; + +interface A{}; +interface B extends A{}; + +class GenericTest { + public static List convert(List list){ + return new ArrayList(); + } + + public static void test(){ + List as = convert(new ArrayList()); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/constraints/IntersectionTypeStrictSubtypingConstraint.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/constraints/IntersectionTypeStrictSubtypingConstraint.java new file mode 100644 index 000000000000..df18646a0009 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/constraints/IntersectionTypeStrictSubtypingConstraint.java @@ -0,0 +1,15 @@ +import java.io.Serializable; +import java.util.function.Supplier; + +class Test { + static class Loader { + + static Loader from(Supplier supplier) { + return new Loader<>(); + } + } + + Loader loader = Loader.from((I & Serializable) () -> ""); + + interface I extends Supplier{} +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/graphInference/FreshVariablesCreatedDuringResolveDependingOnAlreadyResolvedVariables.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/graphInference/FreshVariablesCreatedDuringResolveDependingOnAlreadyResolvedVariables.java new file mode 100644 index 000000000000..a3701f1b6cf8 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/graphInference/FreshVariablesCreatedDuringResolveDependingOnAlreadyResolvedVariables.java @@ -0,0 +1,17 @@ +class Test { + + class X> { + } + class Foo { + } + class Bar { + } + + private > X foo() { + return null; + } + + { + X x = foo(); + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/graphInference/IncompatibleBoundsFromAssignment.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/graphInference/IncompatibleBoundsFromAssignment.java new file mode 100644 index 000000000000..d5b8b03050d5 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/graphInference/IncompatibleBoundsFromAssignment.java @@ -0,0 +1,15 @@ +import java.util.Map; + +class Test { + + public static void main(String[] args) { + Map b = newMapTrie(); + Map> c = newMapTrie(); + Map>> d = newMapTrie(); + Map>>> e = newMapTrie(); + } + + public static > T newMapTrie() { + return null; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/Ambiguity1.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/Ambiguity1.java index e66630146e6f..02bbebd074b0 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/Ambiguity1.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/Ambiguity1.java @@ -12,7 +12,7 @@ class Ambiguity1 { static void m(I2 i2) {} { - m(()->{throw new AssertionError();}); + m(()->{throw new AssertionError();}); m(() -> {}); m(() -> { if (false) return; diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/AmbiguityRawGenerics.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/AmbiguityRawGenerics.java index 595787704437..6c664dd9e65a 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/AmbiguityRawGenerics.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/AmbiguityRawGenerics.java @@ -22,6 +22,6 @@ class AmbiguityRawGenerics { void foo(I3 s) { } void bar() { - foo(()-> { throw new RuntimeException(); }); + foo(()-> { throw new RuntimeException(); }); } } \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/EffectiveFinal.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/EffectiveFinal.java index 92b268394994..001b1f39c494 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/EffectiveFinal.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/highlighting/EffectiveFinal.java @@ -142,3 +142,37 @@ class IDEA128196 { new Thread(() -> System.out.println(value)); } } + +class FinalAssignmentInInitializer { + private final String x; + { + Runnable r = () -> x = ""; + x = ""; + } +} + +class AssignmentToFinalInsideLambda { + boolean isTrue() { + return true; + } + + Runnable r = () -> { + final int i; + if (isTrue()) { + i = 1; + } else { + i = 0; + } + }; + + void a() { + Runnable r = () -> { + final int i; + if (isTrue()) { + i = 1; + } else { + i = 0; + } + }; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/mostSpecific/NoReturnTypeResolutionForThrownException.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/mostSpecific/NoReturnTypeResolutionForThrownException.java index 81429131d099..1fe14f680abe 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/mostSpecific/NoReturnTypeResolutionForThrownException.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/mostSpecific/NoReturnTypeResolutionForThrownException.java @@ -7,7 +7,7 @@ class Test { void call(I2 p) { } void test() { - call(() -> { throw new RuntimeException(); }); + call(() -> { throw new RuntimeException(); }); call(() -> { if (true) return ""; throw new RuntimeException(); }); call(() -> { if (true) return; throw new RuntimeException(); }); } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/DeepNestedLambdaExpressionsNoFormalParams.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/DeepNestedLambdaExpressionsNoFormalParams.java new file mode 100644 index 000000000000..5a79aef6a18f --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/DeepNestedLambdaExpressionsNoFormalParams.java @@ -0,0 +1,70 @@ + +import java.util.List; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; + + +abstract class Simplified { + public List gerFirstTriangles() { + return flatMap(() -> flatMap(() -> map(z -> 1))).collect(Collectors.toList()); + } + + abstract R flatMap(Supplier mapper); + abstract Stream map(Function mapper); +} + +class PythagoreanTriangles { + + static class Triplet{ + private T t; + private U u; + private R r; + + public Triplet(T t, U u, R r){ + this.t = t; + this.u = u; + this.r = r; + } + + @Override + public String toString() { + return t.toString() + "," + u.toString() + "," + r.toString(); + } + } + + public void pythagoreanTriangles(Integer num){ + Stream numbers = IntStream.rangeClosed(1,num).boxed(); + + Stream> triangles = numbers.flatMap(x -> { + return IntStream.rangeClosed(1, x).boxed().flatMap(y -> { + return IntStream.rangeClosed(1, y).boxed().filter(z -> { + return x * x == y * y + z * z; + }).map(z1 -> new Triplet(x, y, z1)); + }); + }); + + triangles.forEach(System.out::println); + + } + + public List> gerFirstTriangles(long num){ + Stream infinity = Stream.iterate(1, (n) -> n + 1); + return infinity.flatMap(x -> { + return IntStream.rangeClosed(1, x).boxed().flatMap(y -> { + return IntStream.rangeClosed(1, y).boxed().filter(z -> { + return x * x == y * y + z * z; + }).map(z1 -> new Triplet(x, y, z1)); + }); + }).limit(num).collect(Collectors.toList()); + } + + + public static void main(String[] args) { + PythagoreanTriangles triangles = new PythagoreanTriangles(); + triangles.pythagoreanTriangles(10); + triangles.gerFirstTriangles(5l).forEach(System.out::println); + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/IDEA126778.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/IDEA126778.java new file mode 100644 index 000000000000..d8bdcc0d6c82 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/IDEA126778.java @@ -0,0 +1,27 @@ +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static java.util.stream.Collectors.groupingBy; +import static java.util.stream.Collectors.mapping; + +class Test { + private static Map multiDataPointsToPerTxnSummedValue(final Stream>> stream) { + return stream.flatMap(e -> e.getValue().entrySet().stream().map(ex -> + new Object() { + Long txId = ex.getKey(); + DataPoint dataPoint = new DataPoint(e.getKey(), ex.getValue()); + } + )).collect(groupingBy(t -> t.txId, mapping(t -> t.dataPoint, Collectors.summingLong(dataPoint -> dataPoint.getValue().longValue())))); + } + + static class MultiDataPoint {} + static class DateTime {} + + static class DataPoint { + DataPoint(DateTime t, Long val) {} + public Long getValue() { + return null; + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/NestedLambdaExpressions1.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/NestedLambdaExpressions1.java new file mode 100644 index 000000000000..c31497d1f65a --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/NestedLambdaExpressions1.java @@ -0,0 +1,27 @@ +class ExplicitLambdaNoParams { + interface I { + T a(); + } + + I foo(I iff) { return null;} + + { + foo(() -> foo(() -> 1)).a(); + I a1 = foo(() -> foo(() -> 1)).a(); + } +} + +class LambdaWithFormalParameterTypes { + + interface I { + T a(int p); + } + + I foo(I iff) { return null;} + + { + foo((int a) -> foo((int b) -> 1)).a(0); + I a1 = foo((int a) -> foo((int b) -> 1)).a(0); + } + +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/NestedLambdaExpressionsNoFormalParams.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/NestedLambdaExpressionsNoFormalParams.java new file mode 100644 index 000000000000..2d6254525d44 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/NestedLambdaExpressionsNoFormalParams.java @@ -0,0 +1,55 @@ +import java.util.function.Function; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +abstract class NoFormalParams { + interface I { + T a(int a); + } + + I foo(I i) { return null;} + + { + I i = foo(a -> foo(b -> 1)).a(0); + foo(a -> foo(b -> 1)).a(0); + } +} + +abstract class NoFormalParamTypeInferenceNeeded { + interface I { + T a(int a); + } + + abstract RR map(I mapper); + abstract R zip(Function zipper); + + { + map(a -> zip(text -> text)); + zip(a -> zip(text -> text)); + Integer zip = zip(a -> zip(text -> text)); + } + +} + +class IDEA124983 { + class Rectangle{ + public Rectangle(int i, int j, int h, int w) { + } + } + + void createGrid() { + IntStream.range(0, 4) + .mapToObj(i -> IntStream.range(0, 4).mapToObj(j -> { + Rectangle rect2 = new Rectangle(i * 64, j * 64, 64, 64); + return rect2; + })) + .flatMap(s -> s) + .forEach(this::add); + } + + void add(final Rectangle r) {} + + void simplified(final IntStream range) { + range.mapToObj(i -> range.mapToObj(j -> 1)).flatMap(s -> s); + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/NestedLambdaExpressionsNoFormalParams1.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/NestedLambdaExpressionsNoFormalParams1.java new file mode 100644 index 000000000000..a3ce57be1db8 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/NestedLambdaExpressionsNoFormalParams1.java @@ -0,0 +1,26 @@ +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +class IDEA129251 { + void simplified(final Stream pStream, final Stream pStream1, final Stream pStream2){ + pStream.flatMap(x -> Stream.concat(pStream1, pStream2.map(String::toUpperCase))); + } + + + private final Set m_allSubtablesColumns; + private final List m_subtablesDescription = null; + + { + m_allSubtablesColumns = m_subtablesDescription.stream(). + flatMap(desc -> Stream.concat(desc.getKeyColumns().stream().map(String::toUpperCase), + desc.getValueColumns().stream().map(String::toUpperCase))). + collect(Collectors.toSet()); + } + + abstract class SubtableDescription { + abstract List getKeyColumns(); + abstract List getValueColumns(); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/NestedLambdaExpressionsNoFormalParamsStopAtStandalone.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/NestedLambdaExpressionsNoFormalParamsStopAtStandalone.java new file mode 100644 index 000000000000..be7be3539813 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/NestedLambdaExpressionsNoFormalParamsStopAtStandalone.java @@ -0,0 +1,32 @@ +import java.util.Map; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Collector; +import java.util.stream.Stream; + +class MultiDataPoint { + public MultiDataPoint(final Map pCollect) {} + + public static void convertValueResults(final Stream pStream) { + map(() -> new MultiDataPoint(collect(toMap(r -> r.event.substring(0))))); + } + + static R collect(Collector collector) {return null;} + + static R map(Supplier s) { + return null; + } + + static Collector> toMap(Function< T, K> keyMapper) { + return null; + } + + static class Result { + public String event; + + public String getValue() { + return null; + } + } +} + diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/ParenthesizedExpressionsDuringConstrainsCollection.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/ParenthesizedExpressionsDuringConstrainsCollection.java new file mode 100644 index 000000000000..2dd69c3dcc79 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newLambda/ParenthesizedExpressionsDuringConstrainsCollection.java @@ -0,0 +1,13 @@ +import java.util.List; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +abstract class Test { + public List gerFirstTriangles() { + return flatMap((y -> (map((z1 -> (1)))))).collect(Collectors.toList()); + } + + abstract R flatMap(Function mapper); + abstract Stream map (Function mapper); +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newMethodRef/IDEA127275_.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newMethodRef/IDEA127275_.java index c002468c1b91..5743d171ad28 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newMethodRef/IDEA127275_.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newMethodRef/IDEA127275_.java @@ -4,7 +4,7 @@ import java.util.function.Function; class Main { { - List>> list = asList(of(Main::identity)); + List>> list = asList(of(Main::identity)); } static List asList(T a) { return null;} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/overloadResolution/IDEA102800.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/overloadResolution/IDEA102800.java new file mode 100644 index 000000000000..7c2cc7bdae5e --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/overloadResolution/IDEA102800.java @@ -0,0 +1,31 @@ +import java.util.function.IntFunction; +import java.util.function.IntUnaryOperator; +import java.util.function.Supplier; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +class Test { + + interface IntStream1 { + Stream map(IntFunction mapper); + IntStream1 map(IntUnaryOperator mapper); + + Stream boxed(); + } + + void fooBar(IntStream1 instr){ + Supplier> si = () -> instr.map ((i) -> (( i % 2) == 0) ? i : -i).boxed(); + System.out.println(si); + Supplier> si1 = () -> instr.map (null).boxed(); + System.out.println(si1); + } +} + +class TestInitial { + void fooBar(){ + Supplier> si = () -> IntStream.range(0, 20).map((i) -> ((i % 2) == 0) ? i : -i).boxed(); + System.out.println(si); + } +} + + diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/overloadResolution/PertinentToApplicabilityOfExplicitlyTypedLambda.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/overloadResolution/PertinentToApplicabilityOfExplicitlyTypedLambda.java new file mode 100644 index 000000000000..348108fc3443 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/overloadResolution/PertinentToApplicabilityOfExplicitlyTypedLambda.java @@ -0,0 +1,17 @@ +abstract class PertinentToApplicabilityOfExplicitlyTypedLambdaTest { + + interface A { + B m(int a); + } + + interface B { + int m(int b); + } + + abstract void foo(A a); + abstract void foo(B b); + + { + foo(x -> y -> 42); + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/overloadResolution/VoidValueCompatibilityOfImplicitlyTypedLambda.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/overloadResolution/VoidValueCompatibilityOfImplicitlyTypedLambda.java new file mode 100644 index 000000000000..1188a9584dca --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/overloadResolution/VoidValueCompatibilityOfImplicitlyTypedLambda.java @@ -0,0 +1,23 @@ +interface A { + int m(int x); +} + +interface B { + void m(boolean x); +} + +abstract class Test { + abstract void foo(A j); + abstract void foo(B i); + + void bar(Object o) { + foo(x -> { + return x += 1; + }); + foo(x -> x += 1); + foo(x -> 1); + foo(x -> !x); + foo(x -> ++x); + foo(x -> o instanceof String ? 1 : 0); + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeMethodSignatureFromUsage8/afterDisjunctionType.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeMethodSignatureFromUsage8/afterDisjunctionType.java new file mode 100644 index 000000000000..7835cfb40ae9 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeMethodSignatureFromUsage8/afterDisjunctionType.java @@ -0,0 +1,15 @@ +// "Add 'Exception' as 1st parameter to method 'f'" "true" +import java.io.FileInputStream; +import java.io.IOException; + +class Test { + public void createFileInputStream() { + try { + new FileInputStream("test"); + } catch (IOException |NullPointerException e) { + f(e); + } + } + + public void f(Exception e) { } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeMethodSignatureFromUsage8/beforeDisjunctionType.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeMethodSignatureFromUsage8/beforeDisjunctionType.java new file mode 100644 index 000000000000..ab28d7484f4d --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeMethodSignatureFromUsage8/beforeDisjunctionType.java @@ -0,0 +1,15 @@ +// "Add 'Exception' as 1st parameter to method 'f'" "true" +import java.io.FileInputStream; +import java.io.IOException; + +class Test { + public void createFileInputStream() { + try { + new FileInputStream("test"); + } catch (IOException |NullPointerException e) { + f(e); + } + } + + public void f() { } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/lambdaCodeBlock2Expr/afterVoidValueChangedNoConflict.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/lambdaCodeBlock2Expr/afterVoidValueChangedNoConflict.java new file mode 100644 index 000000000000..295537651974 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/lambdaCodeBlock2Expr/afterVoidValueChangedNoConflict.java @@ -0,0 +1,16 @@ +// "Replace with expression lambda" "true" +class Test { + { + a(() -> new Object(){}); + } + + void a(Supplier s) {} + void a(AI s) {} + + interface AI { + void m(); + } + interface Supplier { + T get(); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/lambdaCodeBlock2Expr/beforeVoidValueChanged.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/lambdaCodeBlock2Expr/beforeVoidValueChanged.java new file mode 100644 index 000000000000..20a6c0beac73 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/lambdaCodeBlock2Expr/beforeVoidValueChanged.java @@ -0,0 +1,19 @@ +// "Replace with expression lambda" "false" +interface A { + int m(int x); +} + +interface B { + void m(boolean x); +} + +abstract class X { + abstract void foo(A j); + abstract void foo(B i); + + void bar(Object o) { + foo(x -> { + return x += 1; + }); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/lambdaCodeBlock2Expr/beforeVoidValueChangedNoConflict.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/lambdaCodeBlock2Expr/beforeVoidValueChangedNoConflict.java new file mode 100644 index 000000000000..fbbf7ff5f527 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/lambdaCodeBlock2Expr/beforeVoidValueChangedNoConflict.java @@ -0,0 +1,18 @@ +// "Replace with expression lambda" "true" +class Test { + { + a(() -> { + return new Object(){}; + }); + } + + void a(Supplier s) {} + void a(AI s) {} + + interface AI { + void m(); + } + interface Supplier { + T get(); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/generation/surroundWith/java/SurroundWithTryFinallyUsingIndents.java b/java/java-tests/testData/codeInsight/generation/surroundWith/java/SurroundWithTryFinallyUsingIndents.java new file mode 100644 index 000000000000..c491a29b3380 --- /dev/null +++ b/java/java-tests/testData/codeInsight/generation/surroundWith/java/SurroundWithTryFinallyUsingIndents.java @@ -0,0 +1,5 @@ +class Test { + void test() { + int a = 2; + } +} diff --git a/java/java-tests/testData/codeInsight/generation/surroundWith/java/SurroundWithTryFinallyUsingIndents_after.java b/java/java-tests/testData/codeInsight/generation/surroundWith/java/SurroundWithTryFinallyUsingIndents_after.java new file mode 100644 index 000000000000..a3887076e98f --- /dev/null +++ b/java/java-tests/testData/codeInsight/generation/surroundWith/java/SurroundWithTryFinallyUsingIndents_after.java @@ -0,0 +1,9 @@ +class Test { + void test() { + try { + int a = 2; + } finally { + + } + } +} diff --git a/java/java-tests/testData/codeInsight/javadocIG/clickableFieldReference.html b/java/java-tests/testData/codeInsight/javadocIG/clickableFieldReference.html new file mode 100644 index 000000000000..28602edf96e3 --- /dev/null +++ b/java/java-tests/testData/codeInsight/javadocIG/clickableFieldReference.html @@ -0,0 +1,3 @@ +
@Bar(Baz.CONST value = {Baz.CONST}) 
+class Foo
+extends Object
\ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/javadocIG/clickableFieldReference.java b/java/java-tests/testData/codeInsight/javadocIG/clickableFieldReference.java new file mode 100644 index 000000000000..3f46d7e1d7ec --- /dev/null +++ b/java/java-tests/testData/codeInsight/javadocIG/clickableFieldReference.java @@ -0,0 +1,7 @@ +import java.lang.annotation.Documented; +class Text { Foo foo; } +@Bar(Baz.CONST, value = {Baz.CONST}) class Foo {} +@Documented @interface Bar {} +class Baz { + static int CONST; +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/javadocIG/enumConstantOrdinal.html b/java/java-tests/testData/codeInsight/javadocIG/enumConstantOrdinal.html index 11d610085775..613de9167782 100644 --- a/java/java-tests/testData/codeInsight/javadocIG/enumConstantOrdinal.html +++ b/java/java-tests/testData/codeInsight/javadocIG/enumConstantOrdinal.html @@ -1,3 +1,2 @@ -E - E A -Enum constant ordinal: 0 \ No newline at end of file + E
E A
+Enum constant ordinal: 0
\ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/javadocIG/enumConstantOrdinal_quick.html b/java/java-tests/testData/codeInsight/javadocIG/enumConstantOrdinal_quick.html new file mode 100644 index 000000000000..11d610085775 --- /dev/null +++ b/java/java-tests/testData/codeInsight/javadocIG/enumConstantOrdinal_quick.html @@ -0,0 +1,3 @@ +E + E A +Enum constant ordinal: 0 \ No newline at end of file diff --git a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/java/awt/annotations.xml b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/java/awt/annotations.xml index 2e75eb0e3c6c..68bc4c8bba79 100644 --- a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/java/awt/annotations.xml +++ b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/java/awt/annotations.xml @@ -132,15 +132,32 @@ + + + + + + + + + + + + + + + + + @@ -161,6 +178,24 @@ + + + + + + + + + + + + + + + + + + @@ -169,6 +204,9 @@ + + + @@ -194,9 +232,24 @@ + + + + + + + + + + + + + + + @@ -369,21 +422,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -504,6 +593,15 @@ + + + + + + + + + diff --git a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/java/beans/beancontext/annotations.xml b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/java/beans/beancontext/annotations.xml index ce4e092e389f..19cfde133b6b 100644 --- a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/java/beans/beancontext/annotations.xml +++ b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/java/beans/beancontext/annotations.xml @@ -12,6 +12,7 @@ + @@ -31,9 +32,15 @@ + + + + + + diff --git a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/java/io/annotations.xml b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/java/io/annotations.xml index 521adcf48636..4bc566ac9261 100644 --- a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/java/io/annotations.xml +++ b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/java/io/annotations.xml @@ -11,6 +11,12 @@ + + + + + + @@ -50,6 +56,9 @@ + + + @@ -133,15 +142,30 @@ + + + + + + + + + + + + + + + @@ -270,9 +294,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + @@ -466,6 +514,9 @@ + + + diff --git a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/java/lang/annotations.xml b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/java/lang/annotations.xml index 4862b313a917..00ee3b5c3075 100644 --- a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/java/lang/annotations.xml +++ b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/java/lang/annotations.xml @@ -250,6 +250,15 @@ + + + + + + + + + @@ -275,6 +284,9 @@ + + + @@ -286,12 +298,24 @@ + + + + + + + + + + + + @@ -303,6 +327,9 @@ + + + @@ -324,12 +351,18 @@ + + + + + + @@ -342,12 +375,18 @@ + + + + + + @@ -360,6 +399,12 @@ + + + + + + @@ -380,6 +425,21 @@ + + + + + + + + + + + + + + + @@ -390,6 +450,7 @@ + @@ -401,6 +462,16 @@ + + + + + + + + + + @@ -1465,6 +1536,12 @@ + + + + + + @@ -1486,6 +1563,9 @@ + + + @@ -1525,6 +1605,9 @@ + + + @@ -1543,6 +1626,15 @@ + + + + + + + + + @@ -1576,9 +1668,15 @@ + + + + + + @@ -1589,6 +1687,7 @@ + @@ -1608,6 +1707,9 @@ + + + diff --git a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/java/lang/invoke/annotations.xml b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/java/lang/invoke/annotations.xml index 42a07af318e1..aa3ff596e37d 100644 --- a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/java/lang/invoke/annotations.xml +++ b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/java/lang/invoke/annotations.xml @@ -118,19 +118,32 @@ + + + + + + + + + + + + + @@ -144,16 +157,26 @@ + + + + + + + + + + @@ -161,14 +184,19 @@ + + + + + @@ -176,6 +204,9 @@ + + + @@ -236,6 +267,7 @@ + @@ -271,6 +303,9 @@ + + + @@ -328,6 +363,9 @@ + + + @@ -389,6 +427,9 @@ + + + @@ -667,6 +708,9 @@ + + + @@ -826,6 +870,9 @@ + + + @@ -863,6 +910,9 @@ + + + @@ -893,6 +943,12 @@ + + + + + + @@ -913,6 +969,9 @@ + + + @@ -930,6 +989,9 @@ + + + @@ -938,12 +1000,21 @@ + + + + + + + + + @@ -1005,10 +1076,14 @@ + + + + @@ -1030,6 +1105,9 @@ + + + @@ -1039,15 +1117,24 @@ + + + + + + + + + @@ -1057,6 +1144,9 @@ + + + @@ -1093,6 +1183,9 @@ + + + @@ -1165,6 +1258,9 @@ + + + @@ -1271,9 +1367,15 @@ + + + + + + @@ -1304,6 +1406,9 @@ + + + @@ -1350,12 +1455,18 @@ + + + + + + @@ -1368,6 +1479,12 @@ + + + + + + @@ -1395,6 +1512,9 @@ + + + @@ -1404,9 +1524,15 @@ + + + + + + @@ -1433,6 +1559,12 @@ + + + + + + @@ -1674,10 +1806,14 @@ + + + + @@ -1688,6 +1824,9 @@ + + + @@ -1747,6 +1886,7 @@ + @@ -1754,6 +1894,9 @@ + + + @@ -1857,6 +2000,7 @@ + @@ -1873,6 +2017,9 @@ + + + @@ -1886,6 +2033,7 @@ + @@ -1896,6 +2044,9 @@ + + + @@ -2040,6 +2191,9 @@ + + + diff --git a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/java/net/annotations.xml b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/java/net/annotations.xml index be6006068f41..5a53068cca3b 100644 --- a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/java/net/annotations.xml +++ b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/java/net/annotations.xml @@ -41,6 +41,18 @@ + + + + + + + + + + + + @@ -67,6 +79,9 @@ + + + @@ -74,13 +89,20 @@ + + + + + + + @@ -127,12 +149,21 @@ + + + + + + + + + @@ -163,6 +194,18 @@ + + + + + + + + + + + + @@ -572,9 +615,15 @@ + + + + + + @@ -582,6 +631,10 @@ + + + + @@ -590,10 +643,20 @@ + + + + + + + + + + @@ -601,6 +664,7 @@ + diff --git a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/java/security/annotations.xml b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/java/security/annotations.xml index 597b80fe0d3f..b99175ab871b 100644 --- a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/java/security/annotations.xml +++ b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/java/security/annotations.xml @@ -45,6 +45,12 @@ + + + + + + @@ -120,6 +126,9 @@ + + + @@ -135,9 +144,18 @@ + + + + + + + + + diff --git a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/java/sql/annotations.xml b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/java/sql/annotations.xml index 1938616b2fff..194509a466c5 100644 --- a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/java/sql/annotations.xml +++ b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/java/sql/annotations.xml @@ -23,6 +23,9 @@ + + + @@ -89,6 +92,12 @@ + + + + + + diff --git a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/java/util/annotations.xml b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/java/util/annotations.xml index 5e73e2012e79..b00729c52c66 100644 --- a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/java/util/annotations.xml +++ b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/java/util/annotations.xml @@ -61,6 +61,9 @@ + + + @@ -70,6 +73,9 @@ + + + @@ -1101,6 +1107,7 @@ + @@ -1172,6 +1179,9 @@ + + + @@ -1392,6 +1402,9 @@ + + + @@ -1442,13 +1455,23 @@ + + + + + + + + + + @@ -1476,6 +1499,7 @@ + @@ -1510,15 +1534,24 @@ + + + + + + + + + @@ -1615,12 +1648,21 @@ + + + + + + + + + @@ -1676,6 +1718,7 @@ + @@ -1689,6 +1732,7 @@ + @@ -1711,6 +1755,9 @@ + + + @@ -1751,6 +1798,9 @@ + + + @@ -1835,6 +1885,7 @@ + @@ -1944,6 +1995,15 @@ + + + + + + + + + @@ -1997,12 +2057,18 @@ + + + + + + @@ -2016,10 +2082,26 @@ + + + + + + + + + + + + + + + + @@ -2027,6 +2109,7 @@ + @@ -2035,6 +2118,7 @@ + @@ -2043,6 +2127,7 @@ + @@ -2051,6 +2136,7 @@ + diff --git a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/java/util/concurrent/locks/annotations.xml b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/java/util/concurrent/locks/annotations.xml index 08c07b4d957f..56dd8599d15d 100644 --- a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/java/util/concurrent/locks/annotations.xml +++ b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/java/util/concurrent/locks/annotations.xml @@ -29,6 +29,12 @@ + + + + + + @@ -83,12 +89,18 @@ + + + + + + @@ -104,6 +116,9 @@ + + + @@ -119,6 +134,9 @@ + + + diff --git a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/javax/swing/annotations.xml b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/javax/swing/annotations.xml index 910842a47153..80dbfdb022ed 100644 --- a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/javax/swing/annotations.xml +++ b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/javax/swing/annotations.xml @@ -5,9 +5,15 @@ + + + + + + @@ -63,6 +69,9 @@ + + + @@ -94,6 +103,7 @@ + @@ -102,6 +112,7 @@ + @@ -114,6 +125,12 @@ + + + + + + @@ -123,6 +140,27 @@ + + + + + + + + + + + + + + + + + + + + + @@ -178,10 +216,20 @@ + + + + + + + + + + @@ -245,6 +293,9 @@ + + + @@ -343,6 +394,9 @@ + + + @@ -350,6 +404,10 @@ + + + + @@ -358,6 +416,10 @@ + + + + @@ -521,12 +583,21 @@ + + + + + + + + + @@ -537,6 +608,7 @@ + @@ -548,6 +620,7 @@ + @@ -575,6 +648,7 @@ + @@ -613,9 +687,15 @@ + + + + + + @@ -686,6 +766,7 @@ + @@ -696,12 +777,18 @@ + + + + + + @@ -709,6 +796,7 @@ + diff --git a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/commons/collections/annotations.xml b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/commons/collections/annotations.xml index 89a90c53dae2..2cce3e65d8e3 100644 --- a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/commons/collections/annotations.xml +++ b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/commons/collections/annotations.xml @@ -20,6 +20,15 @@ + + + + + + + + + @@ -27,10 +36,14 @@ + + + + @@ -40,6 +53,9 @@ + + + @@ -52,6 +68,9 @@ + + + diff --git a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/commons/collections/map/annotations.xml b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/commons/collections/map/annotations.xml index bd99ae8b862d..57a09af56b91 100644 --- a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/commons/collections/map/annotations.xml +++ b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/commons/collections/map/annotations.xml @@ -24,6 +24,9 @@ + + + @@ -38,6 +41,15 @@ + + + + + + + + + @@ -47,6 +59,9 @@ + + + @@ -93,6 +108,9 @@ + + + @@ -117,6 +135,12 @@ + + + + + + @@ -167,6 +191,9 @@ + + + diff --git a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/commons/lang/annotations.xml b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/commons/lang/annotations.xml index 7115dfb72473..e825ad4c8fa7 100644 --- a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/commons/lang/annotations.xml +++ b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/commons/lang/annotations.xml @@ -161,6 +161,9 @@ + + + @@ -168,10 +171,14 @@ + + + + @@ -179,6 +186,7 @@ + @@ -187,6 +195,7 @@ + @@ -195,6 +204,7 @@ + @@ -205,6 +215,9 @@ + + + @@ -212,10 +225,14 @@ + + + + @@ -223,6 +240,7 @@ + @@ -231,6 +249,7 @@ + @@ -239,6 +258,7 @@ + @@ -249,6 +269,9 @@ + + + @@ -256,10 +279,14 @@ + + + + @@ -267,6 +294,7 @@ + @@ -275,6 +303,7 @@ + @@ -283,6 +312,7 @@ + @@ -293,6 +323,9 @@ + + + @@ -300,10 +333,14 @@ + + + + @@ -311,6 +348,7 @@ + @@ -319,6 +357,7 @@ + @@ -327,6 +366,7 @@ + @@ -337,6 +377,9 @@ + + + @@ -344,10 +387,14 @@ + + + + @@ -355,6 +402,7 @@ + @@ -363,6 +411,7 @@ + @@ -371,6 +420,7 @@ + @@ -468,6 +518,9 @@ + + + @@ -475,10 +528,14 @@ + + + + @@ -486,6 +543,7 @@ + @@ -494,6 +552,7 @@ + @@ -502,6 +561,7 @@ + @@ -510,6 +570,7 @@ + @@ -518,6 +579,7 @@ + @@ -526,6 +588,7 @@ + @@ -534,6 +597,7 @@ + @@ -542,6 +606,7 @@ + @@ -550,6 +615,7 @@ + @@ -558,6 +624,7 @@ + @@ -574,6 +641,9 @@ + + + @@ -581,6 +651,7 @@ + @@ -588,6 +659,9 @@ + + + @@ -595,6 +669,7 @@ + @@ -603,6 +678,7 @@ + @@ -617,6 +693,7 @@ + @@ -627,6 +704,9 @@ + + + @@ -634,10 +714,14 @@ + + + + @@ -645,6 +729,7 @@ + @@ -653,6 +738,7 @@ + @@ -661,6 +747,7 @@ + @@ -671,6 +758,9 @@ + + + @@ -678,10 +768,14 @@ + + + + @@ -689,6 +783,7 @@ + @@ -697,6 +792,7 @@ + @@ -705,6 +801,7 @@ + @@ -794,6 +891,10 @@ + + + + @@ -802,6 +903,7 @@ + @@ -812,9 +914,15 @@ + + + + + + @@ -824,6 +932,9 @@ + + + @@ -834,6 +945,7 @@ + @@ -877,6 +989,7 @@ + @@ -904,6 +1017,7 @@ + @@ -915,6 +1029,7 @@ + @@ -950,6 +1065,7 @@ + @@ -970,6 +1086,7 @@ + @@ -978,6 +1095,7 @@ + @@ -989,6 +1107,7 @@ + @@ -1027,6 +1146,7 @@ + @@ -1035,6 +1155,7 @@ + @@ -1043,6 +1164,10 @@ + + + + @@ -1136,6 +1261,7 @@ + @@ -1144,6 +1270,7 @@ + @@ -1152,6 +1279,7 @@ + @@ -1160,6 +1288,7 @@ + @@ -1197,6 +1326,12 @@ + + + + + + @@ -1216,6 +1351,7 @@ + @@ -1233,10 +1369,20 @@ + + + + + + + + + + @@ -1253,6 +1399,7 @@ + @@ -1263,6 +1410,9 @@ + + + @@ -1351,6 +1501,7 @@ + @@ -1388,6 +1539,7 @@ + @@ -1410,6 +1562,7 @@ + @@ -1462,15 +1615,27 @@ + + + + + + + + + + + + @@ -1478,13 +1643,20 @@ + + + + + + + @@ -1492,6 +1664,7 @@ + @@ -1500,6 +1673,7 @@ + @@ -1508,6 +1682,7 @@ + @@ -1519,6 +1694,7 @@ + @@ -1527,6 +1703,7 @@ + @@ -1535,10 +1712,14 @@ + + + + @@ -1546,6 +1727,7 @@ + @@ -1898,6 +2080,7 @@ + @@ -1906,6 +2089,7 @@ + @@ -1926,16 +2110,19 @@ + + + @@ -1973,6 +2160,7 @@ + @@ -1993,6 +2181,7 @@ + @@ -2017,6 +2206,7 @@ + @@ -2034,6 +2224,9 @@ + + + @@ -2063,6 +2256,7 @@ + @@ -2077,6 +2271,7 @@ + @@ -2094,6 +2289,7 @@ + @@ -2102,6 +2298,7 @@ + @@ -2110,6 +2307,7 @@ + @@ -2121,6 +2319,7 @@ + @@ -2132,6 +2331,7 @@ + @@ -2140,6 +2340,7 @@ + @@ -2151,6 +2352,7 @@ + @@ -2159,6 +2361,7 @@ + @@ -2170,6 +2373,7 @@ + @@ -2178,6 +2382,7 @@ + @@ -2186,6 +2391,7 @@ + @@ -2194,6 +2400,7 @@ + @@ -2202,6 +2409,7 @@ + @@ -2210,6 +2418,7 @@ + @@ -2218,6 +2427,7 @@ + @@ -2229,6 +2439,7 @@ + @@ -2290,6 +2501,7 @@ + @@ -2314,6 +2526,7 @@ + @@ -2371,6 +2584,7 @@ + @@ -2379,6 +2593,7 @@ + @@ -2390,6 +2605,7 @@ + @@ -2398,6 +2614,7 @@ + @@ -2406,6 +2623,7 @@ + @@ -2414,6 +2632,7 @@ + @@ -2422,6 +2641,7 @@ + @@ -2457,6 +2677,7 @@ + @@ -2465,6 +2686,7 @@ + @@ -2473,6 +2695,7 @@ + @@ -2510,6 +2733,7 @@ + @@ -2524,6 +2748,7 @@ + @@ -2537,6 +2762,7 @@ + @@ -2551,6 +2777,7 @@ + @@ -2569,6 +2796,7 @@ + @@ -2577,6 +2805,7 @@ + @@ -2585,6 +2814,7 @@ + @@ -2596,6 +2826,7 @@ + @@ -2607,6 +2838,7 @@ + @@ -2615,6 +2847,7 @@ + @@ -2623,6 +2856,7 @@ + @@ -2631,6 +2865,7 @@ + @@ -2642,6 +2877,7 @@ + @@ -2653,6 +2889,7 @@ + @@ -2664,6 +2901,7 @@ + @@ -2675,6 +2913,7 @@ + @@ -2686,6 +2925,7 @@ + @@ -2694,6 +2934,7 @@ + @@ -2702,6 +2943,7 @@ + @@ -2713,6 +2955,7 @@ + @@ -2724,6 +2967,7 @@ + @@ -2732,6 +2976,7 @@ + @@ -2743,6 +2988,7 @@ + @@ -2764,6 +3010,7 @@ + @@ -2787,6 +3034,12 @@ + + + + + + @@ -2866,6 +3119,7 @@ + @@ -2931,6 +3185,7 @@ + @@ -2942,5 +3197,6 @@ + diff --git a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/commons/lang/enum/annotations.xml b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/commons/lang/enum/annotations.xml index 05b15d3460ce..0a18f70a8a8c 100644 --- a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/commons/lang/enum/annotations.xml +++ b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/commons/lang/enum/annotations.xml @@ -5,6 +5,9 @@ + + + @@ -20,6 +23,9 @@ + + + @@ -38,6 +44,9 @@ + + + @@ -50,4 +59,7 @@ + + + diff --git a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/commons/lang/enums/annotations.xml b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/commons/lang/enums/annotations.xml index 3b827c118787..88ab27cab4cb 100644 --- a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/commons/lang/enums/annotations.xml +++ b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/commons/lang/enums/annotations.xml @@ -5,6 +5,9 @@ + + + @@ -20,6 +23,9 @@ + + + @@ -38,6 +44,9 @@ + + + @@ -50,4 +59,7 @@ + + + diff --git a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/commons/lang/exception/annotations.xml b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/commons/lang/exception/annotations.xml index 585584b958f2..ba729507a8bc 100644 --- a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/commons/lang/exception/annotations.xml +++ b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/commons/lang/exception/annotations.xml @@ -86,6 +86,7 @@ + @@ -94,6 +95,13 @@ + + + + + + + @@ -102,10 +110,14 @@ + + + + @@ -153,4 +165,13 @@ + + + + + + + + + diff --git a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/commons/lang/math/annotations.xml b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/commons/lang/math/annotations.xml index 567cb620681f..d0657899989a 100644 --- a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/commons/lang/math/annotations.xml +++ b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/commons/lang/math/annotations.xml @@ -302,6 +302,7 @@ + @@ -310,6 +311,7 @@ + @@ -318,6 +320,7 @@ + @@ -326,6 +329,7 @@ + @@ -334,6 +338,7 @@ + @@ -342,6 +347,7 @@ + @@ -350,6 +356,7 @@ + diff --git a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/commons/lang/text/annotations.xml b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/commons/lang/text/annotations.xml index 0e46f59ecbed..044b34e70faf 100644 --- a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/commons/lang/text/annotations.xml +++ b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/commons/lang/text/annotations.xml @@ -37,11 +37,13 @@ + + @@ -422,6 +424,9 @@ + + + @@ -503,6 +508,7 @@ + @@ -511,6 +517,7 @@ + @@ -519,6 +526,7 @@ + @@ -527,6 +535,7 @@ + @@ -535,6 +544,7 @@ + @@ -543,6 +553,7 @@ + @@ -551,6 +562,7 @@ + @@ -559,6 +571,7 @@ + @@ -567,10 +580,14 @@ + + + + @@ -613,6 +630,18 @@ + + + + + + + + + + + + diff --git a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/commons/lang/time/annotations.xml b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/commons/lang/time/annotations.xml index 12183165228a..fbd05e4e413c 100644 --- a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/commons/lang/time/annotations.xml +++ b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/commons/lang/time/annotations.xml @@ -373,6 +373,9 @@ + + + @@ -400,6 +403,7 @@ + diff --git a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/velocity/annotations.xml b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/velocity/annotations.xml index 7ef87f095569..ae835d69fbff 100644 --- a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/velocity/annotations.xml +++ b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/velocity/annotations.xml @@ -5,4 +5,7 @@ + + + diff --git a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/velocity/app/annotations.xml b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/velocity/app/annotations.xml index fac918657687..ee0af29aafee 100644 --- a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/velocity/app/annotations.xml +++ b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/velocity/app/annotations.xml @@ -1,4 +1,7 @@ + + + diff --git a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/velocity/app/event/annotations.xml b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/velocity/app/event/annotations.xml index d1987cbafc38..6d0bbb6d0226 100644 --- a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/velocity/app/event/annotations.xml +++ b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/velocity/app/event/annotations.xml @@ -23,6 +23,9 @@ + + + @@ -95,6 +98,9 @@ + + + diff --git a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/velocity/app/event/implement/annotations.xml b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/velocity/app/event/implement/annotations.xml index 643ea130fd54..28deac2b6343 100644 --- a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/velocity/app/event/implement/annotations.xml +++ b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/velocity/app/event/implement/annotations.xml @@ -2,12 +2,18 @@ + + + + + + @@ -22,12 +28,18 @@ + + + + + + @@ -37,6 +49,9 @@ + + + @@ -102,6 +117,7 @@ + @@ -119,6 +135,7 @@ + diff --git a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/velocity/context/annotations.xml b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/velocity/context/annotations.xml index 62279b8f44db..7952023d2550 100644 --- a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/velocity/context/annotations.xml +++ b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/velocity/context/annotations.xml @@ -14,6 +14,7 @@ + @@ -22,6 +23,7 @@ + @@ -30,6 +32,7 @@ + @@ -37,12 +40,21 @@ + + + + + + + + + diff --git a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/velocity/io/annotations.xml b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/velocity/io/annotations.xml index f55b4257f29a..003fd3e71e3d 100644 --- a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/velocity/io/annotations.xml +++ b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/velocity/io/annotations.xml @@ -1,4 +1,7 @@ + + + diff --git a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/velocity/runtime/annotations.xml b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/velocity/runtime/annotations.xml index 1dd01a70efdd..20fa8692e567 100644 --- a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/velocity/runtime/annotations.xml +++ b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/velocity/runtime/annotations.xml @@ -31,6 +31,12 @@ + + + + + + @@ -41,6 +47,7 @@ + @@ -101,9 +108,24 @@ + + + + + + + + + + + + + + + diff --git a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/velocity/runtime/directive/annotations.xml b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/velocity/runtime/directive/annotations.xml index e9d35d5e61e8..e3142ca5a771 100644 --- a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/velocity/runtime/directive/annotations.xml +++ b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/velocity/runtime/directive/annotations.xml @@ -11,6 +11,9 @@ + + + @@ -128,6 +131,9 @@ + + + @@ -258,6 +264,15 @@ + + + + + + + + + diff --git a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/velocity/runtime/parser/node/annotations.xml b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/velocity/runtime/parser/node/annotations.xml index b6a2face76a9..d10a46a0230e 100644 --- a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/velocity/runtime/parser/node/annotations.xml +++ b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/velocity/runtime/parser/node/annotations.xml @@ -14,6 +14,9 @@ + + + @@ -66,6 +69,9 @@ + + + @@ -156,6 +162,9 @@ + + + @@ -189,6 +198,9 @@ + + + @@ -208,6 +220,9 @@ + + + @@ -230,6 +245,7 @@ + @@ -239,9 +255,15 @@ + + + + + + @@ -276,6 +298,9 @@ + + + @@ -308,6 +333,9 @@ + + + @@ -319,6 +347,9 @@ + + + @@ -452,6 +483,9 @@ + + + @@ -541,9 +575,18 @@ + + + + + + + + + @@ -572,6 +615,7 @@ + @@ -588,6 +632,7 @@ + diff --git a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/velocity/runtime/resource/annotations.xml b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/velocity/runtime/resource/annotations.xml index 6d9f27bfe3ff..cbc6c2b8383a 100644 --- a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/velocity/runtime/resource/annotations.xml +++ b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/velocity/runtime/resource/annotations.xml @@ -2,10 +2,22 @@ + + + + + + + + + + + + diff --git a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/velocity/runtime/resource/loader/annotations.xml b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/velocity/runtime/resource/loader/annotations.xml index ca5b1efb729c..315bc40466a1 100644 --- a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/velocity/runtime/resource/loader/annotations.xml +++ b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/velocity/runtime/resource/loader/annotations.xml @@ -72,6 +72,9 @@ + + + @@ -87,6 +90,9 @@ + + + diff --git a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/velocity/util/annotations.xml b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/velocity/util/annotations.xml index 8b1334446ad3..7bcfa1afe805 100644 --- a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/velocity/util/annotations.xml +++ b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/velocity/util/annotations.xml @@ -5,14 +5,23 @@ + + + + + + + + + @@ -68,6 +77,9 @@ + + + @@ -75,6 +87,7 @@ + @@ -89,6 +102,7 @@ + @@ -114,5 +128,6 @@ + diff --git a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/velocity/util/introspection/annotations.xml b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/velocity/util/introspection/annotations.xml index 4fd41ea336be..3f4478e881e8 100644 --- a/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/velocity/util/introspection/annotations.xml +++ b/java/java-tests/testData/codeInspection/bytecodeAnalysis/annotations/org/apache/velocity/util/introspection/annotations.xml @@ -1,7 +1,22 @@ + + + + + + + + + + + + + + + @@ -17,6 +32,9 @@ + + + @@ -48,6 +66,7 @@ + @@ -86,6 +105,7 @@ + @@ -100,6 +120,7 @@ + @@ -111,6 +132,7 @@ + @@ -118,6 +140,9 @@ + + + @@ -128,6 +153,7 @@ + @@ -139,6 +165,7 @@ + @@ -150,11 +177,18 @@ + + + + + + + diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/RootThrowableCause.java b/java/java-tests/testData/inspection/dataFlow/fixture/RootThrowableCause.java index 6086c92b40d4..1197fe817e8e 100644 --- a/java/java-tests/testData/inspection/dataFlow/fixture/RootThrowableCause.java +++ b/java/java-tests/testData/inspection/dataFlow/fixture/RootThrowableCause.java @@ -1,3 +1,6 @@ +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.Nullable; + class Doo { void foo(Throwable e) { @@ -9,4 +12,18 @@ class Doo { } } +} + +abstract class Test04 { + @Nullable + @Contract(pure = true) + abstract Test04 getParent(); + + Test04 getTopParent() { + Test04 top = this; + while (top.getParent() != null) { + top = top.getParent(); + } + return top; + } } \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/extractMethodObject4Debugger/AnonymousClassParams.java b/java/java-tests/testData/refactoring/extractMethodObject4Debugger/AnonymousClassParams.java new file mode 100644 index 000000000000..3a722d36aeea --- /dev/null +++ b/java/java-tests/testData/refactoring/extractMethodObject4Debugger/AnonymousClassParams.java @@ -0,0 +1,8 @@ +interface I { + void foo(int i) {} +} +class Sample { + void foo() { + System.out.println("hello world"); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/extractMethodObject4Debugger/InnerClass.java b/java/java-tests/testData/refactoring/extractMethodObject4Debugger/InnerClass.java new file mode 100644 index 000000000000..448724bf0543 --- /dev/null +++ b/java/java-tests/testData/refactoring/extractMethodObject4Debugger/InnerClass.java @@ -0,0 +1,30 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class Sample { + + void a() { + System.out.println(""); + } + + class I { + public I(int i) { + } + + void foo() { + bar(); + } + } +} diff --git a/java/java-tests/testData/refactoring/extractMethodObject4Debugger/InvokeReturnType.java b/java/java-tests/testData/refactoring/extractMethodObject4Debugger/InvokeReturnType.java new file mode 100644 index 000000000000..353941357c68 --- /dev/null +++ b/java/java-tests/testData/refactoring/extractMethodObject4Debugger/InvokeReturnType.java @@ -0,0 +1,9 @@ +package my; + +class Sample { + static void foo() { + int x =5; + int y =5; + int z =5; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/extractMethodObject4Debugger/OffsetsAtCallSite.java b/java/java-tests/testData/refactoring/extractMethodObject4Debugger/OffsetsAtCallSite.java new file mode 100644 index 000000000000..353564c51240 --- /dev/null +++ b/java/java-tests/testData/refactoring/extractMethodObject4Debugger/OffsetsAtCallSite.java @@ -0,0 +1,44 @@ +package my; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +class Java8Private { + public static void main(String[] args) { + new Java8Private().foo(); + } + + private void foo() { + final Map map = new HashMap(); + map.put(1, 2); + //evaluate here map.entrySet().stream().filter((a) -> (a.getKey()>0)); + new Inner(map).invoke(); + map.put(3, 5); + } + + private void zoo(int a) { + System.out.println("DONE " + a); + } + + + public class Inner extends MagicAccessorBridge { + final Map map; + + public Inner(Map map) { + this.map = map; + } + + void invoke() { + map.entrySet().stream().forEach((a) -> accessorZoo(Java8Private.this, a.getValue())); + } + + // accessor + void accessorZoo(Java8Private obj, int a) { + obj.zoo(a); + } + } +} + +class MagicAccessorBridge { +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/extractMethodObject4Debugger/ResultExpr.java b/java/java-tests/testData/refactoring/extractMethodObject4Debugger/ResultExpr.java new file mode 100644 index 000000000000..7a83d4c57bf3 --- /dev/null +++ b/java/java-tests/testData/refactoring/extractMethodObject4Debugger/ResultExpr.java @@ -0,0 +1,25 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class Sample { + + void a() { + System.out.println(""); + } + + private int foo() { + return 1; + } +} diff --git a/java/java-tests/testData/refactoring/extractMethodObject4Debugger/ResultStatements.java b/java/java-tests/testData/refactoring/extractMethodObject4Debugger/ResultStatements.java new file mode 100644 index 000000000000..20d69ea477b4 --- /dev/null +++ b/java/java-tests/testData/refactoring/extractMethodObject4Debugger/ResultStatements.java @@ -0,0 +1,25 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class Sample { + + void a() { + System.out.println(""); + } + + int foo() { + return 1; + } +} diff --git a/java/java-tests/testData/refactoring/extractMethodObject4Debugger/SimpleGeneration.java b/java/java-tests/testData/refactoring/extractMethodObject4Debugger/SimpleGeneration.java new file mode 100644 index 000000000000..873f2205e1af --- /dev/null +++ b/java/java-tests/testData/refactoring/extractMethodObject4Debugger/SimpleGeneration.java @@ -0,0 +1,5 @@ +class Sample { + void foo() { + System.out.println("hello world"); + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartType18CompletionTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartType18CompletionTest.java index eb3c4c4577b6..68aba5eca978 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartType18CompletionTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartType18CompletionTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,9 +17,6 @@ package com.intellij.codeInsight.completion; import com.intellij.JavaTestUtil; import com.intellij.codeInsight.lookup.Lookup; -import com.intellij.psi.JavaPsiFacade; -import com.intellij.psi.impl.source.resolve.PsiResolveHelperImpl; -import com.intellij.psi.impl.source.resolve.graphInference.PsiGraphInferenceHelper; import com.intellij.testFramework.LightProjectDescriptor; import org.jetbrains.annotations.NotNull; @@ -117,16 +114,9 @@ public class SmartType18CompletionTest extends LightFixtureCompletionTestCase { } public void testInferFromRawType() throws Exception { - final PsiResolveHelperImpl helper = (PsiResolveHelperImpl)JavaPsiFacade.getInstance(getProject()).getResolveHelper(); - helper.setTestHelper(new PsiGraphInferenceHelper(getPsiManager())); - try { - configureByFile("/" + getTestName(false) + ".java"); - assertNotNull(myItems); - assertTrue(myItems.length == 0); - } - finally { - helper.setTestHelper(null); - } + configureByFile("/" + getTestName(false) + ".java"); + assertNotNull(myItems); + assertTrue(myItems.length == 0); } public void testDiamondsInsideMethodCall() throws Exception { diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartTypeCompletionTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartTypeCompletionTest.java index 967de28820e8..2db5b7fbbfea 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartTypeCompletionTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartTypeCompletionTest.java @@ -701,6 +701,8 @@ public class SmartTypeCompletionTest extends LightFixtureCompletionTestCase { doTest(); } + public void testNoSemicolonInsideParentheses() { doTest(); } + public void testAssignFromTheSameFieldOfAnotherObject() throws Throwable { doTest(); } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/GenericsHighlightingTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/GenericsHighlightingTest.java index 09225b64bc1d..ac57a71e9c9f 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/GenericsHighlightingTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/GenericsHighlightingTest.java @@ -372,6 +372,9 @@ public class GenericsHighlightingTest extends LightDaemonAnalyzerTestCase { public void testIDEA126633() { doTest(LanguageLevel.JDK_1_7, JavaSdkVersion.JDK_1_7, false); } public void testIDEA124363() { doTest(LanguageLevel.JDK_1_7, JavaSdkVersion.JDK_1_7, false); } public void testIDEA78402() { doTest(LanguageLevel.JDK_1_7, JavaSdkVersion.JDK_1_7, false); } + public void testIDEA106985() { doTest(LanguageLevel.JDK_1_7, JavaSdkVersion.JDK_1_7, false); } + public void testIDEA114797() { doTest(LanguageLevel.JDK_1_7, JavaSdkVersion.JDK_1_7, false); } + public void testCaptureWildcardFromUnboundCaptureWildcard() { doTest(LanguageLevel.JDK_1_7, JavaSdkVersion.JDK_1_7, false); } public void testSuperCaptureSubstitutionWhenTypeParameterHasUpperBounds() { doTest(LanguageLevel.JDK_1_7, JavaSdkVersion.JDK_1_7, false); } public void testParameterBoundsWithCapturedWildcard() { doTest(LanguageLevel.JDK_1_7, JavaSdkVersion.JDK_1_7, false); } //jdk should propagate LL 1.4 but actually it provides LL 1.7?! diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/ConstraintsInferenceMiscTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/ConstraintsInferenceMiscTest.java index 42433d327e2d..08857080b060 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/ConstraintsInferenceMiscTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/ConstraintsInferenceMiscTest.java @@ -54,8 +54,12 @@ public class ConstraintsInferenceMiscTest extends LightDaemonAnalyzerTestCase { doTest(false); } + public void testIntersectionTypeStrictSubtypingConstraint() throws Exception { + doTest(false); + } + private void doTest(final boolean checkWarnings) { - doTestNewInference(BASE_PATH + "/" + getTestName(false) + ".java", checkWarnings, false); + doTest(BASE_PATH + "/" + getTestName(false) + ".java", checkWarnings, false); } @Override diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/Diamond8HighlightingTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/Diamond8HighlightingTest.java index eb73906a5f15..62c96e148ea1 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/Diamond8HighlightingTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/Diamond8HighlightingTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,7 +36,7 @@ public class Diamond8HighlightingTest extends LightDaemonAnalyzerTestCase { } private void doTest() throws Exception { - doTestNewInference(BASE_PATH + "/" + getTestName(false) + ".java", false, false); + doTest(BASE_PATH + "/" + getTestName(false) + ".java", false, false); } @Override diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/ExceptionVariablesInferenceTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/ExceptionVariablesInferenceTest.java index 0384c0f074db..c55c6ae2e775 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/ExceptionVariablesInferenceTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/ExceptionVariablesInferenceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -54,6 +54,6 @@ public class ExceptionVariablesInferenceTest extends LightDaemonAnalyzerTestCase } private void doTest(final boolean checkWarnings) throws Exception { - doTestNewInference(BASE_PATH + "/" + getTestName(false) + ".java", checkWarnings, false); + doTest(BASE_PATH + "/" + getTestName(false) + ".java", checkWarnings, false); } } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/FunctionalExpressionIncompleteHighlightingTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/FunctionalExpressionIncompleteHighlightingTest.java index e47f6acc9afc..5e34b963e321 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/FunctionalExpressionIncompleteHighlightingTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/FunctionalExpressionIncompleteHighlightingTest.java @@ -47,7 +47,7 @@ public class FunctionalExpressionIncompleteHighlightingTest extends LightDaemonA } private void doTest(final boolean checkWarnings) { - doTestNewInference(BASE_PATH + "/" + getTestName(false) + ".java", checkWarnings, false); + doTest(BASE_PATH + "/" + getTestName(false) + ".java", checkWarnings, false); } @Override diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/FunctionalTypeWildcardParameterizationTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/FunctionalTypeWildcardParameterizationTest.java index 599ae74cd00d..72f709a48697 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/FunctionalTypeWildcardParameterizationTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/FunctionalTypeWildcardParameterizationTest.java @@ -38,7 +38,7 @@ public class FunctionalTypeWildcardParameterizationTest extends LightDaemonAnaly private void doTest() { IdeaTestUtil.setTestVersion(JavaSdkVersion.JDK_1_8, getModule(), getTestRootDisposable()); - doTestNewInference(BASE_PATH + "/" + getTestName(false) + ".java", false, false); + doTest(BASE_PATH + "/" + getTestName(false) + ".java", false, false); } @Override diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/GenericsHighlighting8Test.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/GenericsHighlighting8Test.java index a66d999b283c..a919843a3164 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/GenericsHighlighting8Test.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/GenericsHighlighting8Test.java @@ -1,11 +1,11 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http:www.apache.org/licenses/LICENSE-2.0 + * 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, @@ -24,9 +24,6 @@ import com.intellij.openapi.projectRoots.JavaSdkVersion; import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.roots.LanguageLevelProjectExtension; import com.intellij.pom.java.LanguageLevel; -import com.intellij.psi.JavaPsiFacade; -import com.intellij.psi.impl.source.resolve.PsiResolveHelperImpl; -import com.intellij.psi.impl.source.resolve.graphInference.PsiGraphInferenceHelper; import com.intellij.testFramework.IdeaTestUtil; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -772,14 +769,7 @@ public class GenericsHighlighting8Test extends LightDaemonAnalyzerTestCase { private void doTest(boolean warnings) { LanguageLevelProjectExtension.getInstance(getJavaFacade().getProject()).setLanguageLevel(LanguageLevel.JDK_1_8); IdeaTestUtil.setTestVersion(JavaSdkVersion.JDK_1_8, getModule(), myTestRootDisposable); - final PsiResolveHelperImpl helper = (PsiResolveHelperImpl)JavaPsiFacade.getInstance(getProject()).getResolveHelper(); - helper.setTestHelper(new PsiGraphInferenceHelper(getPsiManager())); - try { - doTest(BASE_PATH + "/" + getTestName(false) + ".java", warnings, false); - } - finally { - helper.setTestHelper(null); - } + doTest(BASE_PATH + "/" + getTestName(false) + ".java", warnings, false); } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/GraphInferenceHighlightingTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/GraphInferenceHighlightingTest.java index 7585d661b289..69afb0db23bb 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/GraphInferenceHighlightingTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/GraphInferenceHighlightingTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -236,13 +236,21 @@ public class GraphInferenceHighlightingTest extends LightDaemonAnalyzerTestCase doTest(); } + public void testIncompatibleBoundsFromAssignment() throws Exception { + doTest(); + } + + public void testFreshVariablesCreatedDuringResolveDependingOnAlreadyResolvedVariables() throws Exception { + doTest(); + } + private void doTest() throws Exception { doTest(false); } private void doTest(final boolean checkWarnings) throws Exception { IdeaTestUtil.setTestVersion(JavaSdkVersion.JDK_1_8, getModule(), getTestRootDisposable()); - doTestNewInference(BASE_PATH + "/" + getTestName(false) + ".java", checkWarnings, false); + doTest(BASE_PATH + "/" + getTestName(false) + ".java", checkWarnings, false); } @Override diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/LambdaHighlightingTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/LambdaHighlightingTest.java index a3a326cfcf81..5ecab96d8926 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/LambdaHighlightingTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/LambdaHighlightingTest.java @@ -112,7 +112,7 @@ public class LambdaHighlightingTest extends LightDaemonAnalyzerTestCase { } private void doTest(final boolean checkWarnings) { - doTestNewInference(BASE_PATH + "/" + getTestName(false) + ".java", checkWarnings, false); + doTest(BASE_PATH + "/" + getTestName(false) + ".java", checkWarnings, false); } @Override diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/LambdaInferenceTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/LambdaInferenceTest.java index 96e60587a225..b13cfbc22e02 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/LambdaInferenceTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/LambdaInferenceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,6 +30,6 @@ public class LambdaInferenceTest extends LightDaemonAnalyzerTestCase { } private void doTest() throws Exception { - doTestNewInference(BASE_PATH + "/" + getTestName(false) + ".java", false, false); + doTest(BASE_PATH + "/" + getTestName(false) + ".java", false, false); } } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/LambdaParamsTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/LambdaParamsTest.java index 94ca90df2d02..faf1608c1cf0 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/LambdaParamsTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/LambdaParamsTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,6 +30,6 @@ public class LambdaParamsTest extends LightDaemonAnalyzerTestCase { public void testInferFromFormal() { doTest(); } private void doTest() { - doTestNewInference(BASE_PATH + "/" + getTestName(false) + ".java", false, false); + doTest(BASE_PATH + "/" + getTestName(false) + ".java", false, false); } } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/LambdaRedundantCastTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/LambdaRedundantCastTest.java index 36f98aa91cc6..ae5a97933ca8 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/LambdaRedundantCastTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/LambdaRedundantCastTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,6 +35,6 @@ public class LambdaRedundantCastTest extends LightDaemonAnalyzerTestCase { public void testIntersection() { doTest(); } public void testSer() { doTest(); } private void doTest() { - doTestNewInference(BASE_PATH + "/" + getTestName(false) + ".java", true, false); + doTest(BASE_PATH + "/" + getTestName(false) + ".java", true, false); } } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/LightAdvHighlightingJdk8Test.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/LightAdvHighlightingJdk8Test.java index 630acb0404b7..08a2b63550ff 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/LightAdvHighlightingJdk8Test.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/LightAdvHighlightingJdk8Test.java @@ -18,7 +18,6 @@ package com.intellij.codeInsight.daemon.lambda; import com.intellij.codeInsight.daemon.LightDaemonAnalyzerTestCase; import com.intellij.codeInspection.LocalInspectionTool; import com.intellij.codeInspection.compiler.JavacQuirksInspection; -import com.intellij.codeInspection.unusedSymbol.UnusedSymbolLocalInspection; import com.intellij.openapi.projectRoots.JavaSdkVersion; import com.intellij.openapi.projectRoots.Sdk; import com.intellij.testFramework.IdeaTestUtil; @@ -42,7 +41,7 @@ public class LightAdvHighlightingJdk8Test extends LightDaemonAnalyzerTestCase { private void doTest() { IdeaTestUtil.setTestVersion(JavaSdkVersion.JDK_1_8, getModule(), getTestRootDisposable()); - doTestNewInference(BASE_PATH + "/" + getTestName(false) + ".java", true, false); + doTest(BASE_PATH + "/" + getTestName(false) + ".java", true, false); } @Override diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/MethodRefHighlightingTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/MethodRefHighlightingTest.java index 156aac5613c1..d0bed5e9d5df 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/MethodRefHighlightingTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/MethodRefHighlightingTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -116,7 +116,7 @@ public class MethodRefHighlightingTest extends LightDaemonAnalyzerTestCase { private void doTest(boolean warnings) { IdeaTestUtil.setTestVersion(JavaSdkVersion.JDK_1_8, getModule(), getTestRootDisposable()); - doTestNewInference(BASE_PATH + "/" + getTestName(false) + ".java", warnings, false); + doTest(BASE_PATH + "/" + getTestName(false) + ".java", warnings, false); } @Override diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/MostSpecificResolutionTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/MostSpecificResolutionTest.java index 9bb7d4801944..294a3cc8a4f6 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/MostSpecificResolutionTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/MostSpecificResolutionTest.java @@ -121,7 +121,7 @@ public class MostSpecificResolutionTest extends LightDaemonAnalyzerTestCase { private void doTest(boolean warnings) { IdeaTestUtil.setTestVersion(JavaSdkVersion.JDK_1_8, getModule(), getTestRootDisposable()); - doTestNewInference(BASE_PATH + "/" + getTestName(false) + ".java", warnings, false); + doTest(BASE_PATH + "/" + getTestName(false) + ".java", warnings, false); } @Override diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/NewLambdaHighlightingTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/NewLambdaHighlightingTest.java index 1da75f51ba6f..712df26e356f 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/NewLambdaHighlightingTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/NewLambdaHighlightingTest.java @@ -22,6 +22,8 @@ import com.intellij.idea.Bombed; import com.intellij.openapi.projectRoots.JavaSdkVersion; import com.intellij.openapi.projectRoots.Sdk; import com.intellij.testFramework.IdeaTestUtil; +import junit.framework.Test; +import junit.framework.TestSuite; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -71,21 +73,23 @@ public class NewLambdaHighlightingTest extends LightDaemonAnalyzerTestCase { public void testIDEA121315() { doTest(); } public void testIDEA118965comment() { doTest(); } public void testIDEA122074() { doTest(); } + @Bombed(day = 30, month = Calendar.SEPTEMBER) public void testIDEA122084() { doTest(); } public void testAdditionalConstraintDependsOnNonMentionedVars() { doTest(); } public void testIDEA122616() { doTest(); } public void testIDEA122700() { doTest(); } public void testIDEA122406() { doTest(); } - @Bombed(day = 30, month = Calendar.SEPTEMBER) public void testNestedCallsInsideLambdaReturnExpression() { doTest(); } @Bombed(day = 30, month = Calendar.SEPTEMBER) public void testIDEA123731() { doTest(); } public void testIDEA123869() { doTest(); } + @Bombed(day = 30, month = Calendar.SEPTEMBER) public void testIDEA123848() { doTest(); } public void testOnlyLambdaAtTypeParameterPlace() { doTest(); } public void testLiftedIntersectionType() { doTest(); } public void testInferenceFromReturnStatements() { doTest(); } public void testDownUpThroughLambdaReturnStatements() { doTest(); } + @Bombed(day = 30, month = Calendar.SEPTEMBER) public void testIDEA124547() { doTest(); } public void testIDEA118362() { doTest(); } public void testIDEA126056() { doTest(); } @@ -94,12 +98,17 @@ public class NewLambdaHighlightingTest extends LightDaemonAnalyzerTestCase { public void testIDEA126109() { doTest(); } public void testIDEA126809() { doTest(); } public void testIDEA124424() { doTest(); } + public void testNestedLambdaExpressions1() { doTest(); } + public void testNestedLambdaExpressionsNoFormalParams() { doTest(); } + @Bombed(day = 30, month = Calendar.SEPTEMBER) + public void testNestedLambdaExpressionsNoFormalParams1() { doTest(); } + public void testDeepNestedLambdaExpressionsNoFormalParams() { doTest(); } + public void testNestedLambdaExpressionsNoFormalParamsStopAtStandalone() { doTest(); } public void testIDEA127596() throws Exception { doTest(); } - @Bombed(day = 30, month = Calendar.SEPTEMBER) public void testIDEA124983() throws Exception { doTest(); } @@ -115,17 +124,34 @@ public class NewLambdaHighlightingTest extends LightDaemonAnalyzerTestCase { doTest(); } + public void testParenthesizedExpressionsDuringConstrainsCollection() throws Exception { + doTest(); + } + + @Bombed(day = 30, month = Calendar.SEPTEMBER) + public void testIDEA126778() throws Exception { + doTest(); + } + private void doTest() { doTest(false); } private void doTest(boolean warnings) { IdeaTestUtil.setTestVersion(JavaSdkVersion.JDK_1_8, getModule(), getTestRootDisposable()); - doTestNewInference(BASE_PATH + "/" + getTestName(false) + ".java", warnings, false); + doTest(BASE_PATH + "/" + getTestName(false) + ".java", warnings, false); } @Override protected Sdk getProjectJDK() { return IdeaTestUtil.getMockJdk18(); } +/* + public static Test suite() { + final TestSuite suite = new TestSuite(); + for (int i = 0; i < 100; i++) { + suite.addTestSuite(NewLambdaHighlightingTest.class); + } + return suite; + }*/ } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/NewMethodRefHighlightingTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/NewMethodRefHighlightingTest.java index c981137d0253..0b8a66fe5b39 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/NewMethodRefHighlightingTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/NewMethodRefHighlightingTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -288,7 +288,6 @@ public class NewMethodRefHighlightingTest extends LightDaemonAnalyzerTestCase { doTest(); } - @Bombed(day = 30, month = Calendar.SEPTEMBER) public void testIDEA127275_() throws Exception { doTest(); } @@ -305,7 +304,6 @@ public class NewMethodRefHighlightingTest extends LightDaemonAnalyzerTestCase { doTest(); } - @Bombed(day = 30, month = Calendar.SEPTEMBER) public void testAdditionalConstraints3Level() throws Exception { doTest(); } @@ -320,7 +318,7 @@ public class NewMethodRefHighlightingTest extends LightDaemonAnalyzerTestCase { private void doTest(boolean warnings) { IdeaTestUtil.setTestVersion(JavaSdkVersion.JDK_1_8, getModule(), getTestRootDisposable()); - doTestNewInference(BASE_PATH + "/" + getTestName(false) + ".java", warnings, false); + doTest(BASE_PATH + "/" + getTestName(false) + ".java", warnings, false); } @Override diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/OverloadResolutionTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/OverloadResolutionTest.java new file mode 100644 index 000000000000..0e5c5a2f3d78 --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/OverloadResolutionTest.java @@ -0,0 +1,63 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.codeInsight.daemon.lambda; + +import com.intellij.codeInsight.daemon.LightDaemonAnalyzerTestCase; +import com.intellij.codeInspection.LocalInspectionTool; +import com.intellij.codeInspection.unusedSymbol.UnusedSymbolLocalInspection; +import com.intellij.openapi.projectRoots.JavaSdkVersion; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.testFramework.IdeaTestUtil; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; + +public class OverloadResolutionTest extends LightDaemonAnalyzerTestCase { + @NonNls static final String BASE_PATH = "/codeInsight/daemonCodeAnalyzer/lambda/overloadResolution"; + + @NotNull + @Override + protected LocalInspectionTool[] configureLocalInspectionTools() { + return new LocalInspectionTool[]{ + new UnusedSymbolLocalInspection(), + }; + } + + public void testPertinentToApplicabilityOfExplicitlyTypedLambda() throws Exception { + doTest(); + } + + public void testVoidValueCompatibilityOfImplicitlyTypedLambda() throws Exception { + doTest(); + } + + public void testIDEA102800() throws Exception { + doTest(); + } + + private void doTest() { + doTest(true); + } + + private void doTest(boolean warnings) { + IdeaTestUtil.setTestVersion(JavaSdkVersion.JDK_1_8, getModule(), getTestRootDisposable()); + doTest(BASE_PATH + "/" + getTestName(false) + ".java", warnings, false); + } + + @Override + protected Sdk getProjectJDK() { + return IdeaTestUtil.getMockJdk18(); + } +} diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/folding/JavaFoldingTest.groovy b/java/java-tests/testSrc/com/intellij/codeInsight/folding/JavaFoldingTest.groovy index 095de51543af..3907b9828888 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/folding/JavaFoldingTest.groovy +++ b/java/java-tests/testSrc/com/intellij/codeInsight/folding/JavaFoldingTest.groovy @@ -578,10 +578,14 @@ class Test { return 0; } + int arrayMethod(int param)[] { + return new int[]; + } + }""" PsiClass fooClass = JavaPsiFacade.getInstance(project).findClass('Foo', GlobalSearchScope.allScope(project)) def regions = myFixture.editor.foldingModel.allFoldRegions.sort { it.startOffset } - assert regions.size() == 3 + assert regions.size() == 4 checkAccessorFolding(regions[0], regions[1], fooClass.methods[0]) } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/generation/surroundWith/JavaSurroundWithTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/generation/surroundWith/JavaSurroundWithTest.java index 60be917bde7c..8bd53fdabfbf 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/generation/surroundWith/JavaSurroundWithTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/generation/surroundWith/JavaSurroundWithTest.java @@ -17,6 +17,7 @@ package com.intellij.codeInsight.generation.surroundWith; import com.intellij.codeInsight.template.impl.TemplateManagerImpl; import com.intellij.codeInsight.template.impl.TemplateState; +import com.intellij.ide.highlighter.JavaFileType; import com.intellij.lang.LanguageSurrounders; import com.intellij.lang.java.JavaLanguage; import com.intellij.lang.surroundWith.SurroundDescriptor; @@ -24,6 +25,7 @@ import com.intellij.lang.surroundWith.Surrounder; import com.intellij.openapi.editor.SelectionModel; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiElement; +import com.intellij.psi.codeStyle.CommonCodeStyleSettings; import com.intellij.testFramework.LightCodeInsightTestCase; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; @@ -156,6 +158,18 @@ public class JavaSurroundWithTest extends LightCodeInsightTestCase { doTest(getTestName(false), new JavaWithIfElseExpressionSurrounder()); } + public void testSurroundWithTryFinallyUsingIndents() { + CommonCodeStyleSettings.IndentOptions indentOptions = getCurrentCodeStyleSettings().getIndentOptions(JavaFileType.INSTANCE); + boolean oldUseTabs = indentOptions.USE_TAB_CHARACTER; + try { + indentOptions.USE_TAB_CHARACTER = true; + doTest(getTestName(false), new JavaWithTryFinallySurrounder()); + } + finally { + indentOptions.USE_TAB_CHARACTER = oldUseTabs; + } + } + private void doTest(@NotNull String fileName, final Surrounder surrounder) { configureByFile(BASE_PATH + fileName + ".java"); diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/javadoc/JavaDocInfoGeneratorTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/javadoc/JavaDocInfoGeneratorTest.java index 8bac921916d3..409ebcc681c1 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/javadoc/JavaDocInfoGeneratorTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/javadoc/JavaDocInfoGeneratorTest.java @@ -99,11 +99,26 @@ public class JavaDocInfoGeneratorTest extends CodeInsightTestCase { public void testEnumConstantOrdinal() throws Exception { PsiClass psiClass = getTestClass(); PsiField field = psiClass.getFields() [0]; - final File htmlPath = new File(JavaTestUtil.getJavaTestDataPath() + "/codeInsight/javadocIG/" + getTestName(true) + ".html"); - String htmlText = FileUtil.loadFile(htmlPath); - String docInfo = new JavaDocumentationProvider().getQuickNavigateInfo(field, field); + String docInfo = new JavaDocumentationProvider().generateDoc(field, field); assertNotNull(docInfo); - assertEquals(StringUtil.convertLineSeparators(htmlText.trim()), StringUtil.convertLineSeparators(docInfo.trim())); + assertEquals(exampleHtmlFileText(getTestName(true)), StringUtil.convertLineSeparators(docInfo.trim())); + + docInfo = new JavaDocumentationProvider().getQuickNavigateInfo(field, field); + assertNotNull(docInfo); + assertEquals(exampleHtmlFileText(getTestName(true) + "_quick"), StringUtil.convertLineSeparators(docInfo.trim())); + } + + public void testClickableFieldReference() throws Exception { + PsiClass aClass = getTestClass(); + PsiTypeElement element = aClass.getFields()[0].getTypeElement(); + String docInfo = new JavaDocumentationProvider().generateDoc(element.getInnermostComponentReferenceElement().resolve(), element); + assertNotNull(docInfo); + assertEquals(exampleHtmlFileText(getTestName(true)), StringUtil.convertLineSeparators(docInfo.trim())); + } + + private static String exampleHtmlFileText(String name) throws IOException { + final File htmlPath = new File(JavaTestUtil.getJavaTestDataPath() + "/codeInsight/javadocIG/" + name + ".html"); + return StringUtil.convertLineSeparators(FileUtil.loadFile(htmlPath).trim()); } public void testClassTypeParamsPresentation() throws Exception { @@ -111,11 +126,10 @@ public class JavaDocInfoGeneratorTest extends CodeInsightTestCase { final PsiReferenceList extendsList = psiClass.getExtendsList(); final PsiJavaCodeReferenceElement referenceElement = extendsList.getReferenceElements()[0]; final PsiClass superClass = extendsList.getReferencedTypes()[0].resolve(); - final File htmlPath = new File(JavaTestUtil.getJavaTestDataPath() + "/codeInsight/javadocIG/" + getTestName(true) + ".html"); - String htmlText = FileUtil.loadFile(htmlPath); + String docInfo = new JavaDocumentationProvider().getQuickNavigateInfo(superClass, referenceElement); assertNotNull(docInfo); - assertEquals(StringUtil.convertLineSeparators(htmlText.trim()), StringUtil.convertLineSeparators(docInfo.trim())); + assertEquals(exampleHtmlFileText(getTestName(true)), StringUtil.convertLineSeparators(docInfo.trim())); } private void doTestField() throws Exception { @@ -136,11 +150,9 @@ public class JavaDocInfoGeneratorTest extends CodeInsightTestCase { } private void verifyJavaDoc(final PsiElement field) throws IOException { - final File htmlPath = new File(JavaTestUtil.getJavaTestDataPath() + "/codeInsight/javadocIG/" + getTestName(true) + ".html"); - String htmlText = FileUtil.loadFile(htmlPath); String docInfo = new JavaDocInfoGenerator(getProject(), field).generateDocInfo(null); assertNotNull(docInfo); - assertEquals(StringUtil.convertLineSeparators(htmlText.trim()), StringUtil.convertLineSeparators(docInfo.trim())); + assertEquals(exampleHtmlFileText(getTestName(true)), StringUtil.convertLineSeparators(docInfo.trim())); } public void testPackageInfo() throws Exception { diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/template/LiveTemplateTest.groovy b/java/java-tests/testSrc/com/intellij/codeInsight/template/LiveTemplateTest.groovy index d866ddd783cf..6abd2e809cdd 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/template/LiveTemplateTest.groovy +++ b/java/java-tests/testSrc/com/intellij/codeInsight/template/LiveTemplateTest.groovy @@ -14,16 +14,14 @@ * limitations under the License. */ package com.intellij.codeInsight.template + import com.intellij.JavaTestUtil import com.intellij.codeInsight.CodeInsightSettings import com.intellij.codeInsight.lookup.LookupManager import com.intellij.codeInsight.lookup.impl.LookupImpl import com.intellij.codeInsight.lookup.impl.LookupManagerImpl import com.intellij.codeInsight.template.impl.* -import com.intellij.codeInsight.template.macro.ClassNameCompleteMacro -import com.intellij.codeInsight.template.macro.CompleteMacro -import com.intellij.codeInsight.template.macro.MethodReturnTypeMacro -import com.intellij.codeInsight.template.macro.SnakeCaseMacro +import com.intellij.codeInsight.template.macro.* import com.intellij.openapi.Disposable import com.intellij.openapi.command.WriteCommandAction import com.intellij.openapi.editor.Editor @@ -39,6 +37,7 @@ import com.intellij.util.ui.UIUtil import org.jetbrains.annotations.NotNull import static com.intellij.codeInsight.template.Template.Property.USE_STATIC_IMPORT_IF_POSSIBLE + /** * @author spleaner */ @@ -462,7 +461,8 @@ class Foo { @Override protected void invokeTestRunnable(@NotNull final Runnable runnable) throws Exception { - if (name in ["testNavigationActionsDontTerminateTemplate", "testTemplateWithEnd", "testDisappearingVar", "test escape string characters in soutv"]) { + if (name in ["testNavigationActionsDontTerminateTemplate", "testTemplateWithEnd", "testDisappearingVar", + "test escape string characters in soutv", "test do not replace macro value with empty result"]) { runnable.run(); return; } @@ -821,6 +821,40 @@ class Foo { abs(PI); } } +""" + } + + public void "test do not replace macro value with empty result"() { + myFixture.configureByText "a.java", """\ +class Foo { + { + + } +} +""" + final TemplateManager manager = TemplateManager.getInstance(getProject()); + final Template template = manager.createTemplate("xxx", "user", '$VAR1$ $VAR2$ $VAR1$'); + template.addVariable("VAR1", "", "", true) + template.addVariable("VAR2", new MacroCallNode(new FileNameMacro()), new ConstantNode("default"), true) + ((TemplateImpl)template).templateContext.setEnabled(contextType(JavaCodeContextType.class), true) + addTemplate(template, testRootDisposable) + + startTemplate(template); + myFixture.checkResult """\ +class Foo { + { + a.java + } +} +""" + myFixture.type 'test' + + myFixture.checkResult """\ +class Foo { + { + test a.java test + } +} """ } } diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/ContractInferenceFromSourceTest.groovy b/java/java-tests/testSrc/com/intellij/codeInspection/ContractInferenceFromSourceTest.groovy index c3bb014d8342..d8b6fb2c53df 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/ContractInferenceFromSourceTest.groovy +++ b/java/java-tests/testSrc/com/intellij/codeInspection/ContractInferenceFromSourceTest.groovy @@ -161,11 +161,10 @@ class ContractInferenceFromSourceTest extends LightCodeInsightFixtureTestCase { assert c == ['null -> fail'] } - void "_test no NotNull duplication"() { + void "test no return value NotNull duplication"() { def c = inferContracts(""" - boolean smth(@org.jetbrains.annotations.NotNull Object o) { - if (o == null) throw new RuntimeException(); - return o.hashCode() == 1; + @org.jetbrains.annotations.NotNull String smth(Object o) { + return "abc"; } """) assert c == [] @@ -334,29 +333,41 @@ class ContractInferenceFromSourceTest extends LightCodeInsightFixtureTestCase { assert c == ['null -> null'] } - public void "test go inside try"() { + public void "test use invoked method notnull"() { def c = inferContracts(""" final Object foo(Object bar) { - try { - if (bar == null) return null; - bar = smth(bar); - } finally {} - return new String("abc"); + if (bar == null) return null; + return doo(); } + + @org.jetbrains.annotations.NotNull Object doo() {} """) - assert c == ['null -> null'] + assert c == ['null -> null', '!null -> !null'] } - public void "test use invoked method notnull"() { + public void "test use delegated method notnull"() { def c = inferContracts(""" final Object foo(Object bar) { - if (bar == null) return null; return doo(); } @org.jetbrains.annotations.NotNull Object doo() {} """) - assert c == ['null -> null', '!null -> !null'] + assert c == ['_ -> !null'] + } + + public void "test use delegated method notnull with contracts"() { + def c = inferContracts(""" + final Object foo(Object bar, Object o2) { + return doo(o2); + } + + @org.jetbrains.annotations.NotNull Object doo(Object o) { + if (o == null) throw new RuntimeException(); + return smth(); + } + """) + assert c == ['_, null -> fail', '_, _ -> !null'] } private String inferContract(String method) { diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java b/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java index a10d02d31fca..0acf071bbf4a 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java @@ -113,7 +113,8 @@ public class DataFlowInspectionTest extends LightCodeInsightFixtureTestCase { public void testReturningNullFromVoidMethod() throws Throwable { doTest(); } public void testCatchRuntimeException() throws Throwable { doTest(); } - public void testCatchThrowable() throws Throwable { doTest(); } + // IDEA-129331 + //public void testCatchThrowable() throws Throwable { doTest(); } public void testNotNullCatchParameter() { doTest(); } public void testAssertFailInCatch() throws Throwable { diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisIntegrationTest.java b/java/java-tests/testSrc/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisIntegrationTest.java index ab6d92263162..8cde2d7f1bc2 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisIntegrationTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisIntegrationTest.java @@ -133,22 +133,37 @@ public class BytecodeAnalysisIntegrationTest extends JavaCodeInsightFixtureTestC return; } - // not null-result - String externalOutAnnotation = - myExternalAnnotationsManager.findExternalAnnotation(method, AnnotationUtil.NOT_NULL) == null ? "null" : "@NotNull"; - String inferredOutAnnotation = - myInferredAnnotationsManager.findInferredAnnotation(method, AnnotationUtil.NOT_NULL) == null ? "null" : "@NotNull"; String methodKey = PsiFormatUtil.getExternalName(method, false, Integer.MAX_VALUE); - if (!externalOutAnnotation.equals(inferredOutAnnotation)) { - diffs.add(methodKey + ": " + externalOutAnnotation + " != " + inferredOutAnnotation); + { + // @NotNull method + String externalNotNullMethodAnnotation = + myExternalAnnotationsManager.findExternalAnnotation(method, AnnotationUtil.NOT_NULL) == null ? "null" : "@NotNull"; + String inferredNotNullMethodAnnotation = + myInferredAnnotationsManager.findInferredAnnotation(method, AnnotationUtil.NOT_NULL) == null ? "null" : "@NotNull"; + + if (!externalNotNullMethodAnnotation.equals(inferredNotNullMethodAnnotation)) { + diffs.add(methodKey + ": " + externalNotNullMethodAnnotation + " != " + inferredNotNullMethodAnnotation); + } + } + + { + // @Nullable method + String externalNullableMethodAnnotation = + myExternalAnnotationsManager.findExternalAnnotation(method, AnnotationUtil.NULLABLE) == null ? "null" : "@Nullable"; + String inferredNullableMethodAnnotation = + myInferredAnnotationsManager.findInferredAnnotation(method, AnnotationUtil.NULLABLE) == null ? "null" : "@Nullable"; + + if (!externalNullableMethodAnnotation.equals(inferredNullableMethodAnnotation)) { + diffs.add(methodKey + ": " + externalNullableMethodAnnotation + " != " + inferredNullableMethodAnnotation); + } } for (PsiParameter parameter : method.getParameterList().getParameters()) { String parameterKey = PsiFormatUtil.getExternalName(parameter, false, Integer.MAX_VALUE); { - // @NotNull + // @NotNull parameter String externalNotNull = myExternalAnnotationsManager.findExternalAnnotation(parameter, AnnotationUtil.NOT_NULL) == null ? "null" : "@NotNull"; String inferredNotNull = @@ -159,7 +174,7 @@ public class BytecodeAnalysisIntegrationTest extends JavaCodeInsightFixtureTestC } { - // @Nullable + // @Nullable parameter String externalNullable = myExternalAnnotationsManager.findExternalAnnotation(parameter, AnnotationUtil.NULLABLE) == null ? "null" : "@Nullable"; String inferredNullable = @@ -170,6 +185,7 @@ public class BytecodeAnalysisIntegrationTest extends JavaCodeInsightFixtureTestC } } + // @Contract PsiAnnotation externalContractAnnotation = myExternalAnnotationsManager.findExternalAnnotation(method, ORG_JETBRAINS_ANNOTATIONS_CONTRACT); PsiAnnotation inferredContractAnnotation = diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisTest.java b/java/java-tests/testSrc/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisTest.java index 2cbf3fbdfd6c..abae78e2a8e5 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisTest.java @@ -184,14 +184,13 @@ public class BytecodeAnalysisTest extends JavaCodeInsightFixtureTestCase { } private void checkCompoundId(Method method, PsiMethod psiMethod, boolean noKey) throws IOException { - Direction direction = new Out(); System.out.println(); System.out.println(method.internalClassName); System.out.println(method.methodName); System.out.println(method.methodDesc); - HKey psiKey = BytecodeAnalysisConverter.psiKey(psiMethod, direction, myMessageDigest); + HKey psiKey = BytecodeAnalysisConverter.psiKey(psiMethod, Direction.Out, myMessageDigest); if (noKey) { assertTrue(null == psiKey); return; @@ -199,7 +198,7 @@ public class BytecodeAnalysisTest extends JavaCodeInsightFixtureTestCase { else { assertFalse(null == psiKey); } - HKey asmKey = BytecodeAnalysisConverter.asmKey(new Key(method, direction, true), myMessageDigest); + HKey asmKey = BytecodeAnalysisConverter.asmKey(new Key(method, Direction.Out, true), myMessageDigest); Assert.assertEquals(asmKey, psiKey); } diff --git a/java/java-tests/testSrc/com/intellij/execution/configurations/JavaCommandLineTest.java b/java/java-tests/testSrc/com/intellij/execution/configurations/JavaCommandLineTest.java index 2708ac91181c..e4d0bc039c5d 100644 --- a/java/java-tests/testSrc/com/intellij/execution/configurations/JavaCommandLineTest.java +++ b/java/java-tests/testSrc/com/intellij/execution/configurations/JavaCommandLineTest.java @@ -48,6 +48,14 @@ public class JavaCommandLineTest extends LightIdeaTestCase { } } + public void testJarParameter() throws CantRunException { + JavaParameters javaParameters = new JavaParameters(); + javaParameters.setJdk(getProjectJDK()); + javaParameters.setJarPath("my-jar-file.jar"); + String commandLineString = CommandLineBuilder.createFromJavaParameters(javaParameters).getCommandLineString(); + assertTrue(commandLineString, commandLineString.contains("-jar my-jar-file.jar")); + } + public void testClasspath() throws CantRunException { JavaParameters javaParameters; String commandLineString; @@ -55,12 +63,14 @@ public class JavaCommandLineTest extends LightIdeaTestCase { javaParameters = new JavaParameters(); final Sdk internalJdk = JavaAwareProjectJdkTableImpl.getInstanceEx().getInternalJdk(); javaParameters.setJdk(internalJdk); + javaParameters.getClassPath().add("my-jar-file.jar"); javaParameters.setMainClass("Main"); commandLineString = CommandLineBuilder.createFromJavaParameters(javaParameters).getCommandLineString(); assertTrue(containsClassPath(commandLineString)); javaParameters = new JavaParameters(); javaParameters.setJdk(internalJdk); + javaParameters.getClassPath().add("my-jar-file.jar"); javaParameters.setMainClass("Main"); javaParameters.getVMParametersList().add("-cp"); javaParameters.getVMParametersList().add(".."); @@ -70,6 +80,7 @@ public class JavaCommandLineTest extends LightIdeaTestCase { javaParameters = new JavaParameters(); javaParameters.setJdk(internalJdk); + javaParameters.getClassPath().add("my-jar-file.jar"); javaParameters.setMainClass("Main"); javaParameters.getVMParametersList().add("-classpath"); javaParameters.getVMParametersList().add(".."); diff --git a/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterWrapTest.java b/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterWrapTest.java index c06370b06743..b16e23e83e37 100644 --- a/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterWrapTest.java +++ b/java/java-tests/testSrc/com/intellij/psi/formatter/java/JavaFormatterWrapTest.java @@ -16,7 +16,6 @@ package com.intellij.psi.formatter.java; import com.intellij.idea.Bombed; -import com.intellij.lang.java.JavaLanguage; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.codeStyle.CommonCodeStyleSettings; @@ -448,4 +447,34 @@ public class JavaFormatterWrapTest extends AbstractJavaFormatterTest { " public @TA String m();\n" + "}"); } + + public void testKeepSingleFieldAnnotationOnSameLine() { + getJavaSettings().DO_NOT_WRAP_AFTER_SINGLE_ANNOTATION = true; + doClassTest( + "@NotNull public String result = \"OK\"\n" + + "@NotNull String newResult = \"OK\"\n" + + "@NotNull\n" + + "@Deprecated public String bad = \"bad\"", + + "@NotNull public String result = \"OK\"\n" + + "@NotNull String newResult = \"OK\"\n" + + "@NotNull\n" + + "@Deprecated\n" + + "public String bad = \"bad\"" + ); + } + + public void testMoveSingleAnnotationOnSameLine() { + getJavaSettings().DO_NOT_WRAP_AFTER_SINGLE_ANNOTATION = true; + getSettings().KEEP_LINE_BREAKS = false; + doClassTest( + "@NotNull\n" + + "public String test = \"tst\";\n" + + "String ok = \"ok\";\n", + "@NotNull public String test = \"tst\";\n" + + "String ok = \"ok\";\n" + ); + } + + } diff --git a/java/java-tests/testSrc/com/intellij/refactoring/ExtractMethodObject4DebuggerTest.java b/java/java-tests/testSrc/com/intellij/refactoring/ExtractMethodObject4DebuggerTest.java new file mode 100644 index 000000000000..9321cf9a6695 --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/refactoring/ExtractMethodObject4DebuggerTest.java @@ -0,0 +1,179 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * User: anna + * Date: 06-May-2008 + */ +package com.intellij.refactoring; + +import com.intellij.JavaTestUtil; +import com.intellij.idea.Bombed; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.psi.*; +import com.intellij.refactoring.extractMethodObject.ExtractLightMethodObjectHandler; +import com.intellij.testFramework.IdeaTestUtil; +import org.jetbrains.annotations.NotNull; + +import java.util.Calendar; + +public class ExtractMethodObject4DebuggerTest extends LightRefactoringTestCase { + @NotNull + @Override + protected String getTestDataPath() { + return JavaTestUtil.getJavaTestDataPath(); + } + + private void doTest(String evaluatedText, String expectedCallSite, String expectedClass) throws Exception { + final String testName = getTestName(false); + configureByFile("/refactoring/extractMethodObject4Debugger/" + testName + ".java"); + final int offset = getEditor().getCaretModel().getOffset(); + final PsiElement context = getFile().findElementAt(offset); + final JavaCodeFragment fragment = JavaCodeFragmentFactory.getInstance(getProject()).createCodeBlockCodeFragment(evaluatedText, context, false); + final ExtractLightMethodObjectHandler.ExtractedData extractedData = + ExtractLightMethodObjectHandler.extractLightMethodObject(getProject(), getFile(), fragment, "test"); + assertNotNull(extractedData); + assertEquals(expectedCallSite, extractedData.getGeneratedCallText()); + final PsiClass innerClass = extractedData.getGeneratedInnerClass(); + assertEquals(expectedClass, innerClass.getText()); + } + + public void testSimpleGeneration() throws Exception { + doTest("int i = 0; int j = 0;", "Test test = new Test().invoke();int i = test.getI();int j = test.getJ();", + + "public class Test {\n" + + " private int i;\n" + + " private int j;\n" + + "\n" + + " public int getI() {\n" + + " return i;\n" + + " }\n" + + "\n" + + " public int getJ() {\n" + + " return j;\n" + + " }\n" + + "\n" + + " public Test invoke() {\n" + + " i = 0;\n" + + " j = 0;\n" + + " return this;\n" + + " }\n" + + " }"); + } + + public void testInvokeReturnType() throws Exception { + doTest("x = 6; y = 6;", "Test test = new Test().invoke();x = test.getX();y = test.getY();", + + "public static class Test {\n" + + " private int x;\n" + + " private int y;\n" + + "\n" + + " public int getX() {\n" + + " return x;\n" + + " }\n" + + "\n" + + " public int getY() {\n" + + " return y;\n" + + " }\n" + + "\n" + + " public Test invoke() {\n" + + " x = 6;\n" + + " y = 6;\n" + + " return this;\n" + + " }\n" + + " }"); + } + + public void testAnonymousClassParams() throws Exception { + doTest("new I() {public void foo(int i) {i++;}};", "I result = new Test().invoke();", + + "public class Test {\n" + + " public I invoke() {\n" + + " return new I() {\n" + + " public void foo(int i) {\n" + + " i++;\n" + + " }\n" + + " };\n" + + " }\n" + + " }"); + } + + public void testInnerClass() throws Exception { + doTest(" new I(2).foo()", "new Test().invoke();", + + "public class Test {\n" + + " public void invoke() {\n" + + " new Sample.I(2).foo();\n" + + " }\n" + + " }"); + } + + public void testResultExpr() throws Exception { + doTest(" foo()", "int result = new Test().invoke();", + + "public class Test {\n" + + " public int invoke() {\n" + + " return foo();\n" + + " }\n" + + " }"); + } + + public void testResultStatements() throws Exception { + doTest("int i = 0;\nfoo()", "Test test = new Test().invoke();int i = test.getI();int result = test.getResult();", + + "public class Test {\n" + + " private int i;\n" + + " private int result;\n" + + "\n" + + " public int getI() {\n" + + " return i;\n" + + " }\n" + + "\n" + + " public int getResult() {\n" + + " return result;\n" + + " }\n" + + "\n" + + " public Test invoke() {\n" + + " i = 0;\n" + + " result = foo();\n" + + " return this;\n" + + " }\n" + + " }"); + } + + + public void testOffsetsAtCallSite() throws Exception { + doTest("map.entrySet().stream().filter((a) -> (a.getKey()>0));", + "java.util.stream.Stream> result = new Test(map).invoke();", + "public class Test {\n" + + " private Map map;\n" + + "\n" + + " public Test(Map map) {\n" + + " this.map = map;\n" + + " }\n" + + "\n" + + " public Stream> invoke() {\n" + + " return map.entrySet().stream().filter((a) -> (a.getKey() > 0));\n" + + " }\n" + + " }"); + } + + @Override + protected Sdk getProjectJDK() { + return IdeaTestUtil.getMockJdk18(); + } +} diff --git a/java/java-tests/testSrc/com/intellij/refactoring/IntroduceVariableTest.java b/java/java-tests/testSrc/com/intellij/refactoring/IntroduceVariableTest.java index 34745372977e..6ef80d01903e 100644 --- a/java/java-tests/testSrc/com/intellij/refactoring/IntroduceVariableTest.java +++ b/java/java-tests/testSrc/com/intellij/refactoring/IntroduceVariableTest.java @@ -18,9 +18,10 @@ package com.intellij.refactoring; import com.intellij.JavaTestUtil; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; -import com.intellij.psi.*; -import com.intellij.psi.impl.source.resolve.PsiResolveHelperImpl; -import com.intellij.psi.impl.source.resolve.graphInference.PsiGraphInferenceHelper; +import com.intellij.psi.CommonClassNames; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiExpression; +import com.intellij.psi.PsiType; import com.intellij.refactoring.introduce.inplace.OccurrencesChooser; import com.intellij.refactoring.introduceVariable.InputValidator; import com.intellij.refactoring.introduceVariable.IntroduceVariableBase; @@ -95,15 +96,8 @@ public class IntroduceVariableTest extends LightCodeInsightTestCase { } public void testExpectedType8Inference() { - final PsiResolveHelperImpl helper = (PsiResolveHelperImpl)JavaPsiFacade.getInstance(getProject()).getResolveHelper(); - helper.setTestHelper(new PsiGraphInferenceHelper(getPsiManager())); - try { - doTest(new MockIntroduceVariableHandler("temp", true, false, false, - "java.util.Map>")); - } - finally { - helper.setTestHelper(null); - } + doTest(new MockIntroduceVariableHandler("temp", true, false, false, + "java.util.Map>")); } public void testMethodCall() { diff --git a/java/openapi/src/com/intellij/execution/filters/ExceptionWorker.java b/java/openapi/src/com/intellij/execution/filters/ExceptionWorker.java index 680ae8f744ce..c912e57999dd 100644 --- a/java/openapi/src/com/intellij/execution/filters/ExceptionWorker.java +++ b/java/openapi/src/com/intellij/execution/filters/ExceptionWorker.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -48,18 +48,6 @@ public class ExceptionWorker { private static final String AT_PREFIX = AT + " "; private static final String STANDALONE_AT = " " + AT + " "; - private static final TextAttributes HYPERLINK_ATTRIBUTES; - private static final TextAttributes LIBRARY_HYPERLINK_ATTRIBUTES; - - static { - HYPERLINK_ATTRIBUTES = EditorColorsManager.getInstance().getGlobalScheme().getAttributes(CodeInsightColors.HYPERLINK_ATTRIBUTES); - - LIBRARY_HYPERLINK_ATTRIBUTES = HYPERLINK_ATTRIBUTES.clone(); - Color libTextColor = UIUtil.getInactiveTextColor(); - LIBRARY_HYPERLINK_ATTRIBUTES.setForegroundColor(libTextColor); - LIBRARY_HYPERLINK_ATTRIBUTES.setEffectColor(libTextColor); - } - private final Project myProject; private Filter.Result myResult; private PsiClass[] myClasses = PsiClass.EMPTY_ARRAY; @@ -129,13 +117,16 @@ public class ExceptionWorker { } List virtualFiles; - TextAttributes attributes; + TextAttributes attributes = EditorColorsManager.getInstance().getGlobalScheme().getAttributes(CodeInsightColors.HYPERLINK_ATTRIBUTES); if (virtualFilesInContent.isEmpty()) { - attributes = LIBRARY_HYPERLINK_ATTRIBUTES; + Color libTextColor = UIUtil.getInactiveTextColor(); + attributes = attributes.clone(); + attributes.setForegroundColor(libTextColor); + attributes.setEffectColor(libTextColor); + virtualFiles = virtualFilesInLibraries; } else { - attributes = HYPERLINK_ATTRIBUTES; virtualFiles = virtualFilesInContent; } HyperlinkInfo linkInfo = HyperlinkInfoFactory.getInstance().createMultipleFilesHyperlinkInfo(virtualFiles, lineNumber - 1, myProject); diff --git a/java/openapi/src/com/intellij/ide/palette/PaletteDragEventListener.java b/java/openapi/src/com/intellij/ide/palette/PaletteDragEventListener.java deleted file mode 100644 index 1a6b681b2a48..000000000000 --- a/java/openapi/src/com/intellij/ide/palette/PaletteDragEventListener.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2000-2009 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.intellij.ide.palette; - -/** - * @author yole - */ -public interface PaletteDragEventListener { - void dropActionChanged(int gestureModifiers); -} diff --git a/java/openapi/src/com/intellij/ide/palette/PaletteGroup.java b/java/openapi/src/com/intellij/ide/palette/PaletteGroup.java deleted file mode 100644 index a19c1ab8e60d..000000000000 --- a/java/openapi/src/com/intellij/ide/palette/PaletteGroup.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2000-2009 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.intellij.ide.palette; - -import com.intellij.openapi.actionSystem.ActionGroup; -import com.intellij.openapi.project.Project; -import org.jetbrains.annotations.Nullable; - -/** - * @author yole - */ -public interface PaletteGroup { - PaletteGroup[] EMPTY_ARRAY = new PaletteGroup[0]; - - PaletteItem[] getItems(); - - /** - * Returns the text of the group header for the palette group. - * - * @return the text of the group header for the palette group, or null if no header should be shown. - */ - @Nullable String getName(); - - String getTabName(); - - /** - * Returns the action group from which the context menu is built when the palette - * item is right-clicked. - * - * @return the action group, or null if no context menu should be shown. - */ - @Nullable ActionGroup getPopupActionGroup(); - - /** - * Returns the data for the specified data constant. - * - * @param project the project in the context of which data is requested. - * @param dataId the data constant id (see {@link com.intellij.openapi.actionSystem.PlatformDataKeys}). - * @return the data item, or null if no data is available for this constant. - */ - @Nullable Object getData(Project project, String dataId); - - /** - * Processes the drop of a palette item on the specified index in the palette group. - * - * @param project the project to which the drop target palette belongs. - * @param item the dropped item. - * @param index the index at which the dropped item should be inserted (from 0 to getItems().length). - */ - void handleDrop(Project project, PaletteItem item, int index); -} diff --git a/java/openapi/src/com/intellij/ide/palette/PaletteItem.java b/java/openapi/src/com/intellij/ide/palette/PaletteItem.java deleted file mode 100644 index 372839d67266..000000000000 --- a/java/openapi/src/com/intellij/ide/palette/PaletteItem.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2000-2009 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.intellij.ide.palette; - -import com.intellij.ide.dnd.DnDDragStartBean; -import com.intellij.openapi.actionSystem.ActionGroup; -import com.intellij.openapi.project.Project; -import com.intellij.ui.ColoredListCellRenderer; -import org.jetbrains.annotations.Nullable; - -/** - * @author yole - */ -public interface PaletteItem { - void customizeCellRenderer(ColoredListCellRenderer cellRenderer, - boolean selected, - boolean hasFocus); - - /** - * Processes dragging the item. - * - * @return the drag start bean for the drag process, or null if the item cannot be dragged. - */ - @Nullable DnDDragStartBean startDragging(); - - /** - * Returns the action group from which the context menu is built when the palette - * item is right-clicked. - * - * @return the action group, or null if no context menu should be shown. - */ - @Nullable ActionGroup getPopupActionGroup(); - - /** - * Returns the data for the specified data constant. - * - * @param project the project in the context of which data is requested. - * @param dataId the data constant id (see {@link com.intellij.openapi.actionSystem.PlatformDataKeys}). - * @return the data item, or null if no data is available for this constant. - */ - @Nullable Object getData(Project project, String dataId); -} diff --git a/java/openapi/src/com/intellij/ide/palette/PaletteItemProvider.java b/java/openapi/src/com/intellij/ide/palette/PaletteItemProvider.java deleted file mode 100644 index 3f1b99d36f52..000000000000 --- a/java/openapi/src/com/intellij/ide/palette/PaletteItemProvider.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2000-2009 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.intellij.ide.palette; - -import com.intellij.openapi.extensions.ExtensionPointName; -import com.intellij.openapi.vfs.VirtualFile; - -import java.beans.PropertyChangeListener; - -/** - * @author yole - */ -public interface PaletteItemProvider { - ExtensionPointName EP_NAME = ExtensionPointName.create("com.intellij.paletteItemProvider"); - - PaletteGroup[] getActiveGroups(VirtualFile virtualFile); - - void addListener(PropertyChangeListener listener); - void removeListener(PropertyChangeListener listener); -} diff --git a/java/remote-servers/impl/src/com/intellij/remoteServer/impl/runtime/deployment/debug/JavaDebuggerLauncherImpl.java b/java/remote-servers/impl/src/com/intellij/remoteServer/impl/runtime/deployment/debug/JavaDebuggerLauncherImpl.java index 0ff284e054dd..c56597c938c2 100644 --- a/java/remote-servers/impl/src/com/intellij/remoteServer/impl/runtime/deployment/debug/JavaDebuggerLauncherImpl.java +++ b/java/remote-servers/impl/src/com/intellij/remoteServer/impl/runtime/deployment/debug/JavaDebuggerLauncherImpl.java @@ -7,7 +7,6 @@ import com.intellij.debugger.engine.DebugProcess; import com.intellij.debugger.engine.DebugProcessAdapter; import com.intellij.debugger.engine.RemoteDebugProcessHandler; import com.intellij.debugger.ui.DebuggerPanelsManager; -import com.intellij.diagnostic.logging.LogFilesManager; import com.intellij.execution.*; import com.intellij.execution.configurations.RemoteConnection; import com.intellij.execution.configurations.RunProfile; @@ -97,10 +96,6 @@ public class JavaDebuggerLauncherImpl extends JavaDebuggerLauncher { return myExecutionEnvironment.getRunProfile().getIcon(); } - @Override - public void initLogs(RunContentDescriptor content, LogFilesManager logFilesManager) { - } - @Override public void initActions(RunContentDescriptor content, DefaultActionGroup actionGroup) { actionGroup.add(new CloseAction(myExecutionEnvironment.getExecutor(), content, myExecutionEnvironment.getProject())); diff --git a/java/structuralsearch-java/src/com/intellij/structuralsearch/JavaReplaceHandler.java b/java/structuralsearch-java/src/com/intellij/structuralsearch/JavaReplaceHandler.java index 562c8aee753c..3164a297a8b9 100644 --- a/java/structuralsearch-java/src/com/intellij/structuralsearch/JavaReplaceHandler.java +++ b/java/structuralsearch-java/src/com/intellij/structuralsearch/JavaReplaceHandler.java @@ -477,7 +477,17 @@ public class JavaReplaceHandler extends StructuralReplaceHandler { return; } super.visitReferenceExpression(expression); - if (offset + expression.getTextLength() < finalStartOffset) + if (offset + expression.getTextLength() < finalStartOffset) { + return; + } + final PsiElement target = expression.resolve(); + if (!(target instanceof PsiMember)) { + return; + } + final PsiMember member = (PsiMember)target; + if (!member.hasModifierProperty(PsiModifier.STATIC)) { + return; + } if (expression.getQualifierExpression() == null) { return; } diff --git a/java/testFramework/src/com/intellij/codeInsight/daemon/LightDaemonAnalyzerTestCase.java b/java/testFramework/src/com/intellij/codeInsight/daemon/LightDaemonAnalyzerTestCase.java index ff98bd23b858..d2b25c6d3d04 100644 --- a/java/testFramework/src/com/intellij/codeInsight/daemon/LightDaemonAnalyzerTestCase.java +++ b/java/testFramework/src/com/intellij/codeInsight/daemon/LightDaemonAnalyzerTestCase.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,11 +24,8 @@ import com.intellij.lang.injection.InjectedLanguageManager; import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.vfs.VirtualFileFilter; -import com.intellij.psi.JavaPsiFacade; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiFile; -import com.intellij.psi.impl.source.resolve.PsiResolveHelperImpl; -import com.intellij.psi.impl.source.resolve.graphInference.PsiGraphInferenceHelper; import com.intellij.testFramework.ExpectedHighlightingData; import com.intellij.testFramework.FileTreeAccessFilter; import com.intellij.testFramework.HighlightTestInfo; @@ -83,18 +80,6 @@ public abstract class LightDaemonAnalyzerTestCase extends LightCodeInsightTestCa doTestConfiguredFile(checkWarnings, checkInfos, filePath); } - protected void doTestNewInference(@NonNls String filePath, boolean checkWarnings, boolean checkInfos) { - final PsiResolveHelperImpl helper = (PsiResolveHelperImpl)JavaPsiFacade.getInstance(getProject()).getResolveHelper(); - helper.setTestHelper(new PsiGraphInferenceHelper(getPsiManager())); - try { - configureByFile(filePath); - doTestConfiguredFile(checkWarnings, checkInfos, filePath); - } - finally { - helper.setTestHelper(null); - } - } - protected void doTest(@NonNls String filePath, boolean checkWarnings, boolean checkWeakWarnings, boolean checkInfos) { configureByFile(filePath); doTestConfiguredFile(checkWarnings, checkWeakWarnings, checkInfos, filePath); diff --git a/java/testFramework/src/com/intellij/compiler/CompilerTestUtil.java b/java/testFramework/src/com/intellij/compiler/CompilerTestUtil.java index 0f8f11ce6788..e5a9ee47daed 100644 --- a/java/testFramework/src/com/intellij/compiler/CompilerTestUtil.java +++ b/java/testFramework/src/com/intellij/compiler/CompilerTestUtil.java @@ -8,10 +8,15 @@ import com.intellij.openapi.application.WriteAction; import com.intellij.openapi.application.ex.ApplicationManagerEx; import com.intellij.openapi.fileTypes.FileTypeManager; import com.intellij.openapi.fileTypes.impl.FileTypeManagerImpl; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.projectRoots.ProjectJdkTable; +import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.projectRoots.impl.JavaAwareProjectJdkTableImpl; import com.intellij.openapi.projectRoots.impl.ProjectJdkTableImpl; +import com.intellij.openapi.roots.ModuleRootManager; +import com.intellij.openapi.roots.ModuleRootModificationUtil; import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.util.JDOMUtil; import com.intellij.openapi.util.WriteExternalException; @@ -27,6 +32,7 @@ import org.jetbrains.jps.model.serialization.JpsGlobalLoader; import java.io.File; import java.io.IOException; +import java.util.ArrayList; import java.util.List; /** @@ -91,8 +97,20 @@ public class CompilerTestUtil { new WriteAction() { protected void run(final Result result) { ApplicationManagerEx.getApplicationEx().doNotSave(true); + Module[] modules = ModuleManager.getInstance(project).getModules(); JavaAwareProjectJdkTableImpl table = JavaAwareProjectJdkTableImpl.getInstanceEx(); - table.removeJdk(table.getInternalJdk()); + Sdk internalJdk = table.getInternalJdk(); + List modulesToRestore = new ArrayList(); + for (Module module : modules) { + Sdk sdk = ModuleRootManager.getInstance(module).getSdk(); + if (sdk != null && sdk.equals(internalJdk)) { + modulesToRestore.add(module); + } + } + table.removeJdk(internalJdk); + for (Module module : modulesToRestore) { + ModuleRootModificationUtil.setModuleSdk(module, internalJdk); + } BuildManager.getInstance().clearState(project); } }.execute(); diff --git a/java/testFramework/src/com/intellij/debugger/DebuggerTestCase.java b/java/testFramework/src/com/intellij/debugger/DebuggerTestCase.java index 2ec9ae61e17b..06ebf7875e6e 100644 --- a/java/testFramework/src/com/intellij/debugger/DebuggerTestCase.java +++ b/java/testFramework/src/com/intellij/debugger/DebuggerTestCase.java @@ -28,17 +28,16 @@ import com.intellij.debugger.settings.DebuggerSettings; import com.intellij.debugger.settings.NodeRendererSettings; import com.intellij.debugger.ui.breakpoints.BreakpointManager; import com.intellij.execution.ExecutionException; -import com.intellij.execution.ExecutionResult; import com.intellij.execution.Executor; import com.intellij.execution.configurations.*; import com.intellij.execution.executors.DefaultDebugExecutor; import com.intellij.execution.process.ProcessAdapter; import com.intellij.execution.process.ProcessEvent; +import com.intellij.execution.process.ProcessHandler; import com.intellij.execution.process.ProcessOutputTypes; import com.intellij.execution.runners.ExecutionEnvironment; import com.intellij.execution.runners.ExecutionEnvironmentBuilder; import com.intellij.execution.runners.ProgramRunner; -import com.intellij.execution.ui.ExecutionConsole; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.editor.Document; @@ -46,7 +45,10 @@ import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx; import com.intellij.openapi.module.Module; import com.intellij.openapi.options.SettingsEditor; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.*; +import com.intellij.openapi.util.Computable; +import com.intellij.openapi.util.InvalidDataException; +import com.intellij.openapi.util.Key; +import com.intellij.openapi.util.WriteExternalException; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.JavaPsiFacade; import com.intellij.psi.PsiClass; @@ -88,11 +90,11 @@ public abstract class DebuggerTestCase extends ExecutionWithDebuggerToolsTestCas protected void runTest() throws Throwable { super.runTest(); if(getDebugProcess() != null) { - getDebugProcess().getExecutionResult().getProcessHandler().startNotify(); - waitProcess(getDebugProcess().getExecutionResult().getProcessHandler()); + getDebugProcess().getProcessHandler().startNotify(); + waitProcess(getDebugProcess().getProcessHandler()); waitForCompleted(); //disposeSession(myDebuggerSession); - assertNull(DebuggerManagerEx.getInstanceEx(myProject).getDebugProcess(getDebugProcess().getExecutionResult().getProcessHandler())); + assertNull(DebuggerManagerEx.getInstanceEx(myProject).getDebugProcess(getDebugProcess().getProcessHandler())); myDebuggerSession = null; } if(myConsoleBuffer != null) { @@ -119,11 +121,6 @@ public abstract class DebuggerTestCase extends ExecutionWithDebuggerToolsTestCas @Override protected void tearDown() throws Exception { FileEditorManagerEx.getInstanceEx(getProject()).closeAllFiles(); - ExecutionResult executionResult = myDebugProcess == null ? null : myDebugProcess.getExecutionResult(); - ExecutionConsole console = executionResult == null ? null : executionResult.getExecutionConsole(); - if (console != null) { - Disposer.dispose(console); - } myConsoleBuffer = null; super.tearDown(); } @@ -134,7 +131,7 @@ public abstract class DebuggerTestCase extends ExecutionWithDebuggerToolsTestCas myDebugProcess = myDebuggerSession.getProcess(); } - protected DebuggerSession createLocalSession(final JavaParameters javaParameters, final String sessionName) throws ExecutionException, InterruptedException { + protected DebuggerSession createLocalSession(final JavaParameters javaParameters) throws ExecutionException, InterruptedException { createBreakpoints(javaParameters.getMainClass()); DebuggerSettings.getInstance().DEBUGGER_TRANSPORT = DebuggerSettings.SOCKET_TRANSPORT; @@ -145,7 +142,7 @@ public abstract class DebuggerTestCase extends ExecutionWithDebuggerToolsTestCas ExecutionEnvironment environment = new ExecutionEnvironmentBuilder(myProject, DefaultDebugExecutor.getDebugExecutorInstance()) .runnerSettings(debuggerRunnerSettings) - .setRunProfile(new MockConfiguration()) + .runProfile(new MockConfiguration()) .build(); final JavaCommandLineState javaCommandLineState = new JavaCommandLineState(environment){ @Override @@ -241,7 +238,7 @@ public abstract class DebuggerTestCase extends ExecutionWithDebuggerToolsTestCas } }); - final ExecutionResult executionResult = debuggerSession[0].getProcess().getExecutionResult(); + final ProcessHandler processHandler = debuggerSession[0].getProcess().getProcessHandler(); debuggerSession[0].getProcess().addProcessListener(new ProcessAdapter() { @Override public void onTextAvailable(ProcessEvent event, Key outputType) { @@ -250,7 +247,7 @@ public abstract class DebuggerTestCase extends ExecutionWithDebuggerToolsTestCas }); DebugProcessImpl process = - (DebugProcessImpl)DebuggerManagerEx.getInstanceEx(myProject).getDebugProcess(executionResult.getProcessHandler()); + (DebugProcessImpl)DebuggerManagerEx.getInstanceEx(myProject).getDebugProcess(processHandler); assertNotNull(process); return debuggerSession[0]; } @@ -293,9 +290,9 @@ public abstract class DebuggerTestCase extends ExecutionWithDebuggerToolsTestCas debuggerSession = attachVM(remoteConnection, true); } - ExecutionResult executionResult = debuggerSession.getProcess().getExecutionResult(); + ProcessHandler processHandler = debuggerSession.getProcess().getProcessHandler(); DebugProcessImpl process = (DebugProcessImpl)DebuggerManagerEx.getInstanceEx(myProject) - .getDebugProcess(executionResult.getProcessHandler()); + .getDebugProcess(processHandler); assertNotNull(process); return debuggerSession; @@ -319,7 +316,7 @@ public abstract class DebuggerTestCase extends ExecutionWithDebuggerToolsTestCas } } }); - debuggerSession[0].getProcess().getExecutionResult().getProcessHandler().addProcessListener(new ProcessAdapter() { + debuggerSession[0].getProcess().getProcessHandler().addProcessListener(new ProcessAdapter() { @Override public void onTextAvailable(ProcessEvent event, Key outputType) { print(event.getText(), outputType); diff --git a/java/testFramework/src/com/intellij/debugger/ExecutionWithDebuggerToolsTestCase.java b/java/testFramework/src/com/intellij/debugger/ExecutionWithDebuggerToolsTestCase.java index f295ee6dd40e..c4f7d920969d 100644 --- a/java/testFramework/src/com/intellij/debugger/ExecutionWithDebuggerToolsTestCase.java +++ b/java/testFramework/src/com/intellij/debugger/ExecutionWithDebuggerToolsTestCase.java @@ -26,7 +26,6 @@ import com.intellij.debugger.impl.PositionUtil; import com.intellij.debugger.impl.PrioritizedTask; import com.intellij.debugger.impl.SynchronizationBasedSemaphore; import com.intellij.debugger.jdi.StackFrameProxyImpl; -import com.intellij.debugger.settings.DebuggerSettings; import com.intellij.debugger.ui.breakpoints.Breakpoint; import com.intellij.debugger.ui.breakpoints.BreakpointManager; import com.intellij.execution.ExecutionException; @@ -62,7 +61,7 @@ public abstract class ExecutionWithDebuggerToolsTestCase extends ExecutionTestCa private final SynchronizationBasedSemaphore myScriptRunnablesSema = new SynchronizationBasedSemaphore(); protected static final int RATHER_LATER_INVOKES_N = 10; public DebugProcessImpl myDebugProcess = null; - private final CompositeException exception = new CompositeException(); + private final CompositeException myException = new CompositeException(); private class InvokeRatherLaterRequest { private final DebuggerCommandImpl myDebuggerCommand; @@ -107,7 +106,9 @@ public abstract class ExecutionWithDebuggerToolsTestCase extends ExecutionTestCa @Override protected void tearDown() throws Exception { super.tearDown(); - if (!exception.isEmpty()) throw exception; + synchronized (myException) { + if (!myException.isEmpty()) throw myException; + } } protected void onBreakpoint(SuspendContextRunnable runnable) { @@ -131,11 +132,11 @@ public abstract class ExecutionWithDebuggerToolsTestCase extends ExecutionTestCa suspendContextRunnable.run(suspendContext); } catch (Exception e) { - exception.add(e); + addException(e); error(e); } catch (AssertionError e) { - exception.add(e); + addException(e); } if (myScriptRunnables.isEmpty()) { @@ -270,6 +271,11 @@ public abstract class ExecutionWithDebuggerToolsTestCase extends ExecutionTestCa }); } + protected void addException(Throwable e) { + synchronized (myException) { + myException.add(e); + } + } protected void error(Throwable th) { fail(StringUtil.getThrowableText(th)); diff --git a/java/testFramework/src/com/intellij/testFramework/CompilerTester.java b/java/testFramework/src/com/intellij/testFramework/CompilerTester.java index a3c05c6ec3fd..27f36509f60e 100644 --- a/java/testFramework/src/com/intellij/testFramework/CompilerTester.java +++ b/java/testFramework/src/com/intellij/testFramework/CompilerTester.java @@ -46,18 +46,24 @@ import javax.swing.*; import java.io.File; import java.io.IOException; import java.util.ArrayList; +import java.util.Collections; import java.util.List; /** * @author peter */ public class CompilerTester { - - private Module myModule; + private Project myProject; + private List myModules; private TempDirTestFixture myMainOutput; public CompilerTester(Module module) throws Exception { - myModule = module; + this(module.getProject(), Collections.singletonList(module)); + } + + public CompilerTester(Project project, List modules) throws Exception { + myProject = project; + myModules = modules; myMainOutput = new TempDirTestFixtureImpl(); myMainOutput.setUp(); @@ -67,10 +73,11 @@ public class CompilerTester { //noinspection ConstantConditions CompilerProjectExtension.getInstance(getProject()).setCompilerOutputUrl(myMainOutput.findOrCreateDir("out").getUrl()); CompilerTestUtil.enableExternalCompiler(); - ModuleRootModificationUtil.setModuleSdk(myModule, JavaAwareProjectJdkTableImpl.getInstanceEx().getInternalJdk()); + for (Module module : myModules) { + ModuleRootModificationUtil.setModuleSdk(module, JavaAwareProjectJdkTableImpl.getInstanceEx().getInternalJdk()); + } } }.execute(); - } public void tearDown() { @@ -84,12 +91,12 @@ public class CompilerTester { } finally { myMainOutput = null; - myModule = null; + myModules = null; } } private Project getProject() { - return myModule.getProject(); + return myProject; } public void deleteClassFile(final String className) throws IOException { @@ -173,6 +180,15 @@ public class CompilerTester { }); } + public List make(final CompileScope scope) { + return runCompiler(new Consumer() { + @Override + public void consume(ErrorReportingCallback callback) { + CompilerManager.getInstance(getProject()).make(scope, callback); + } + }); + } + public List compileFiles(final VirtualFile... files) { return runCompiler(new Consumer() { @Override @@ -192,11 +208,13 @@ public class CompilerTester { try { getProject().save(); CompilerTestUtil.saveApplicationSettings(); - final VirtualFile moduleFile = myModule.getModuleFile(); - File ioFile = VfsUtil.virtualToIoFile(moduleFile); - if (!ioFile.exists()) { - getProject().save(); - assert ioFile.exists() : "File does not exist: " + ioFile.getPath(); + for (Module module : myModules) { + final VirtualFile moduleFile = module.getModuleFile(); + File ioFile = VfsUtil.virtualToIoFile(moduleFile); + if (!ioFile.exists()) { + getProject().save(); + assert ioFile.exists() : "File does not exist: " + ioFile.getPath(); + } } runnable.consume(callback); } diff --git a/java/typeMigration/src/com/intellij/refactoring/typeMigration/intentions/ChangeClassParametersIntention.java b/java/typeMigration/src/com/intellij/refactoring/typeMigration/intentions/ChangeClassParametersIntention.java index 9571807cd229..b65e5364f57b 100644 --- a/java/typeMigration/src/com/intellij/refactoring/typeMigration/intentions/ChangeClassParametersIntention.java +++ b/java/typeMigration/src/com/intellij/refactoring/typeMigration/intentions/ChangeClassParametersIntention.java @@ -48,7 +48,9 @@ public class ChangeClassParametersIntention extends PsiElementBaseIntentionActio final PsiMember member = PsiTreeUtil.getParentOfType(parameterList, PsiMember.class); if (member instanceof PsiAnonymousClass) { final PsiClassType.ClassResolveResult result = ((PsiAnonymousClass)member).getBaseClassType().resolveGenerics(); - return result.getElement() != null && ((PsiAnonymousClass)member).getBaseClassReference().getParameterList() == parameterList; + final PsiClass baseClass = result.getElement(); + return baseClass != null && baseClass.getTypeParameters().length == parameterList.getTypeParameterElements().length && + ((PsiAnonymousClass)member).getBaseClassReference().getParameterList() == parameterList; } } return false; diff --git a/jps/jps-builders/jps-builders.iml b/jps/jps-builders/jps-builders.iml index 993bfad6f750..cfafe90aa84a 100644 --- a/jps/jps-builders/jps-builders.iml +++ b/jps/jps-builders/jps-builders.iml @@ -31,7 +31,7 @@ - + diff --git a/jps/jps-builders/src/org/jetbrains/jps/api/GlobalOptions.java b/jps/jps-builders/src/org/jetbrains/jps/api/GlobalOptions.java index b1f36a69cb1e..223d57a532f9 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/api/GlobalOptions.java +++ b/jps/jps-builders/src/org/jetbrains/jps/api/GlobalOptions.java @@ -21,7 +21,6 @@ package org.jetbrains.jps.api; */ public interface GlobalOptions { String USE_MEMORY_TEMP_CACHE_OPTION = "use.memory.temp.cache"; - String USE_EXTERNAL_JAVAC_OPTION = "use.external.javac.process"; String GENERATE_CLASSPATH_INDEX_OPTION = "generate.classpath.index"; String COMPILE_PARALLEL_OPTION = "compile.parallel"; String COMPILE_PARALLEL_MAX_THREADS_OPTION = "compile.parallel.max.threads"; diff --git a/jps/jps-builders/src/org/jetbrains/jps/cmdline/BuildMain.java b/jps/jps-builders/src/org/jetbrains/jps/cmdline/BuildMain.java index 8634769222a0..befddeff6157 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/cmdline/BuildMain.java +++ b/jps/jps-builders/src/org/jetbrains/jps/cmdline/BuildMain.java @@ -41,6 +41,7 @@ import org.jetbrains.jps.service.SharedThreadPool; import java.io.*; import java.net.InetSocketAddress; import java.util.UUID; +import java.util.concurrent.TimeUnit; /** * @author Eugene Zhuravlev @@ -214,7 +215,7 @@ public class BuildMain { public void run() { //noinspection finally try { - ourEventLoopGroup.shutdownGracefully(); + ourEventLoopGroup.shutdownGracefully(0, 15, TimeUnit.SECONDS); } finally { System.exit(0); diff --git a/jps/jps-builders/src/org/jetbrains/jps/cmdline/ClasspathBootstrap.java b/jps/jps-builders/src/org/jetbrains/jps/cmdline/ClasspathBootstrap.java index 3e5d2514c5a2..030f083652d9 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/cmdline/ClasspathBootstrap.java +++ b/jps/jps-builders/src/org/jetbrains/jps/cmdline/ClasspathBootstrap.java @@ -32,7 +32,7 @@ import net.n3.nanoxml.IXMLBuilder; import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.builders.java.JavaCompilingTool; import org.jetbrains.jps.builders.java.JavaSourceTransformer; -import org.jetbrains.jps.javac.JavacServer; +import org.jetbrains.jps.javac.ExternalJavacProcess; import org.jetbrains.jps.model.JpsModel; import org.jetbrains.jps.model.impl.JpsModelImpl; import org.jetbrains.jps.model.serialization.JpsProjectLoader; @@ -145,6 +145,8 @@ public class ClasspathBootstrap { cp.add(getResourcePath(NotNullVerifyingInstrumenter.class)); // not-null cp.add(getResourcePath(IXMLBuilder.class)); // nano-xml cp.add(getResourcePath(SequenceLock.class)); // jsr166 + cp.add(getJpsPluginSystemClassesPath().getAbsolutePath().replace('\\', '/')); + //don't forget to update layoutCommunityJps() in layouts.gant accordingly if (!isLauncherUsed) { @@ -176,9 +178,9 @@ public class ClasspathBootstrap { } } - public static List getJavacServerClasspath(String sdkHome, JavaCompilingTool compilingTool) { + public static List getExternalJavacProcessClasspath(String sdkHome, JavaCompilingTool compilingTool) { final Set cp = new LinkedHashSet(); - cp.add(getResourceFile(JavacServer.class)); // self + cp.add(getResourceFile(ExternalJavacProcess.class)); // self // util for (String path : PathManager.getUtilClassPath()) { cp.add(new File(path)); @@ -187,7 +189,8 @@ public class ClasspathBootstrap { cp.add(getResourceFile(JpsModelImpl.class)); // jps-model-impl cp.add(getResourceFile(Message.class)); // protobuf cp.add(getResourceFile(NetUtil.class)); // netty - + cp.add(getJpsPluginSystemClassesPath()); + final Class optimizedFileManagerClass = getOptimizedFileManagerClass(); if (optimizedFileManagerClass != null) { cp.add(getResourceFile(optimizedFileManagerClass)); // optimizedFileManager, if applicable @@ -201,38 +204,33 @@ public class ClasspathBootstrap { LOG.info(th); } - final JavaCompiler systemCompiler = ToolProvider.getSystemJavaCompiler(); - if (systemCompiler != null) { - try { - final String localJarPath = FileUtil.toSystemIndependentName(getResourceFile(systemCompiler.getClass()).getPath()); - final String localJavaHome = FileUtil.toSystemIndependentName(SystemProperties.getJavaHome()); - if (FileUtil.pathsEqual(localJavaHome, FileUtil.toSystemIndependentName(sdkHome))) { - cp.add(new File(localJarPath)); - } - else { - // sdkHome is not the same as the sdk used to run this process - final File candidate = new File(sdkHome, "lib/tools.jar"); - if (candidate.exists()) { - cp.add(candidate); - } - else { - // last resort - String relPath = FileUtil.getRelativePath(localJavaHome, localJarPath, '/'); + try { + final String localJavaHome = FileUtil.toSystemIndependentName(SystemProperties.getJavaHome()); + // sdkHome is not the same as the sdk used to run this process + final File candidate = new File(sdkHome, "lib/tools.jar"); + if (candidate.exists()) { + cp.add(candidate); + } + else { + // last resort + final JavaCompiler systemCompiler = ToolProvider.getSystemJavaCompiler(); + if (systemCompiler != null) { + final String localJarPath = FileUtil.toSystemIndependentName(getResourceFile(systemCompiler.getClass()).getPath()); + String relPath = FileUtil.getRelativePath(localJavaHome, localJarPath, '/'); + if (relPath != null) { + if (relPath.contains("..")) { + relPath = FileUtil.getRelativePath(FileUtil.toSystemIndependentName(new File(localJavaHome).getParent()), localJarPath, '/'); + } if (relPath != null) { - if (relPath.contains("..")) { - relPath = FileUtil.getRelativePath(FileUtil.toSystemIndependentName(new File(localJavaHome).getParent()), localJarPath, '/'); - } - if (relPath != null) { - final File targetFile = new File(sdkHome, relPath); - cp.add(targetFile); // tools.jar - } + final File targetFile = new File(sdkHome, relPath); + cp.add(targetFile); // tools.jar } } } } - catch (Throwable th) { - LOG.info(th); - } + } + catch (Throwable th) { + LOG.info(th); } cp.addAll(compilingTool.getAdditionalClasspath()); @@ -286,4 +284,16 @@ public class ClasspathBootstrap { public static File getResourceFile(Class aClass) { return new File(getResourcePath(aClass)); } + + private static File getJpsPluginSystemClassesPath() { + File classesRoot = new File(getResourcePath(ClasspathBootstrap.class)); + if (classesRoot.isDirectory()) { + //running from sources: load classes from .../out/production/jps-plugin-system + return new File(classesRoot.getParentFile(), "jps-plugin-system"); + } + else { + return new File(classesRoot.getParentFile(), "rt/jps-plugin-system.jar"); + } + } + } diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/IncProjectBuilder.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/IncProjectBuilder.java index 88fdef9eaad2..f13664ec3c8a 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/IncProjectBuilder.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/IncProjectBuilder.java @@ -36,7 +36,6 @@ import org.jetbrains.jps.ModuleChunk; import org.jetbrains.jps.TimingLog; import org.jetbrains.jps.api.CanceledStatus; import org.jetbrains.jps.api.GlobalOptions; -import org.jetbrains.jps.api.RequestFuture; import org.jetbrains.jps.builders.*; import org.jetbrains.jps.builders.impl.BuildOutputConsumerImpl; import org.jetbrains.jps.builders.impl.BuildTargetChunk; @@ -50,12 +49,12 @@ import org.jetbrains.jps.builders.storage.SourceToOutputMapping; import org.jetbrains.jps.cmdline.BuildRunner; import org.jetbrains.jps.cmdline.ProjectDescriptor; import org.jetbrains.jps.incremental.fs.BuildFSState; -import org.jetbrains.jps.incremental.java.ExternalJavacDescriptor; import org.jetbrains.jps.incremental.messages.*; import org.jetbrains.jps.incremental.storage.BuildTargetConfiguration; import org.jetbrains.jps.incremental.storage.OneToManyPathsMapping; import org.jetbrains.jps.incremental.storage.OutputToTargetRegistry; import org.jetbrains.jps.indices.ModuleExcludeIndex; +import org.jetbrains.jps.javac.ExternalJavacServer; import org.jetbrains.jps.javac.JavacMain; import org.jetbrains.jps.model.java.JpsJavaExtensionService; import org.jetbrains.jps.model.java.compiler.JpsJavaCompilerConfiguration; @@ -313,17 +312,10 @@ public class IncProjectBuilder { pd.timestamps.getStorage().force(); pd.dataManager.flush(false); } - final ExternalJavacDescriptor descriptor = ExternalJavacDescriptor.KEY.get(context); - if (descriptor != null) { - try { - final RequestFuture future = descriptor.client.sendShutdownRequest(); - future.waitFor(500L, TimeUnit.MILLISECONDS); - } - finally { - // ensure process is not running - descriptor.process.destroyProcess(); - } - ExternalJavacDescriptor.KEY.set(context, null); + final ExternalJavacServer server = ExternalJavacServer.KEY.get(context); + if (server != null) { + server.stop(); + ExternalJavacServer.KEY.set(context, null); } } diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/impl/JarsBuilder.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/impl/JarsBuilder.java index ba43cc7ea05e..c5ce16567be4 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/impl/JarsBuilder.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/artifacts/impl/JarsBuilder.java @@ -181,7 +181,7 @@ public class JarsBuilder { packedFilePaths, -1); } else { - LOG.debug("nested jar file " + relativePath + " for " + jar.getPresentableDestination() + " not found"); + LOG.debug("nested JAR file " + relativePath + " for " + jar.getPresentableDestination() + " not found"); } } } diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/java/ExternalJavacDescriptor.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/java/ExternalJavacDescriptor.java deleted file mode 100644 index b8882decdf4d..000000000000 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/java/ExternalJavacDescriptor.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2000-2012 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.jps.incremental.java; - -import com.intellij.execution.process.BaseOSProcessHandler; -import org.jetbrains.jps.incremental.GlobalContextKey; -import org.jetbrains.jps.javac.JavacServerClient; - -/** - * @author Eugene Zhuravlev - * Date: 1/24/12 - */ -public class ExternalJavacDescriptor { - public static final GlobalContextKey KEY = GlobalContextKey.create("_external_javac_descriptor_"); - - public final BaseOSProcessHandler process; - public final JavacServerClient client; - - public ExternalJavacDescriptor(BaseOSProcessHandler process, JavacServerClient client) { - this.process = process; - this.client = client; - } -} diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/java/JavaBuilder.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/java/JavaBuilder.java index 77538c6b6881..94b00d9838a1 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/java/JavaBuilder.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/java/JavaBuilder.java @@ -15,7 +15,6 @@ */ package org.jetbrains.jps.incremental.java; -import com.intellij.execution.process.BaseOSProcessHandler; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Key; @@ -32,8 +31,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.ModuleChunk; import org.jetbrains.jps.ProjectPaths; -import org.jetbrains.jps.api.GlobalOptions; -import org.jetbrains.jps.api.RequestFuture; import org.jetbrains.jps.builders.BuildRootIndex; import org.jetbrains.jps.builders.DirtyFilesHolder; import org.jetbrains.jps.builders.FileProcessor; @@ -63,13 +60,11 @@ import org.jetbrains.jps.model.module.JpsModule; import org.jetbrains.jps.model.module.JpsModuleType; import org.jetbrains.jps.service.JpsServiceManager; -import javax.tools.Diagnostic; -import javax.tools.JavaFileObject; +import javax.tools.*; import java.io.*; import java.net.ServerSocket; import java.util.*; import java.util.concurrent.Executor; -import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; /** @@ -81,7 +76,6 @@ public class JavaBuilder extends ModuleLevelBuilder { public static final String BUILDER_NAME = "java"; private static final String JAVA_EXTENSION = "java"; private static final String DOT_JAVA_EXTENSION = "." + JAVA_EXTENSION; - public static final boolean USE_EMBEDDED_JAVAC = System.getProperty(GlobalOptions.USE_EXTERNAL_JAVAC_OPTION) == null; private static final Key JAVA_COMPILER_VERSION_KEY = Key.create("_java_compiler_version_"); public static final Key IS_ENABLED = Key.create("_java_compiler_enabled_"); private static final Key COMPILING_TOOL = Key.create("_java_compiling_tool_"); @@ -396,7 +390,7 @@ public class JavaBuilder extends ModuleLevelBuilder { } try { final boolean rc; - if (USE_EMBEDDED_JAVAC) { + if (!shouldForkCompilerProcess(context, chunk, compilingTool)) { final Collection _platformCp = calcEffectivePlatformCp(platformCp, options, compilingTool); if (_platformCp == null) { diagnosticSink.report(new PlainMessageDiagnostic(Diagnostic.Kind.ERROR, @@ -409,16 +403,18 @@ public class JavaBuilder extends ModuleLevelBuilder { ); } else { - final JavacServerClient client = ensureJavacServerLaunched(context, compilingTool); - final RequestFuture future = client.sendCompileRequest( - options, files, classpath, platformCp, sourcePath, outs, diagnosticSink, classesConsumer - ); - while (!future.waitFor(100L, TimeUnit.MILLISECONDS)) { - if (context.getCancelStatus().isCanceled()) { - future.cancel(false); - } + // fork external javac + final String sdkHome = getChunkSdkHome(chunk); + if (sdkHome == null) { + diagnosticSink.report(new PlainMessageDiagnostic(Diagnostic.Kind.ERROR, "Cannot start javac process for " + chunk.getName() + ": unknown JDK home path.\nPlease check project configuration.")); + return true; } - rc = future.getMessageHandler().isTerminatedSuccessfully(); + + final List vmOptions = getCompilationVMOptions(context, compilingTool); + final ExternalJavacServer server = ensureJavacServerStarted(context); + rc = server.forkJavac( + context, options, vmOptions, files, classpath, platformCp, sourcePath, outs, diagnosticSink, classesConsumer, sdkHome, compilingTool + ); } return rc; } @@ -426,6 +422,20 @@ public class JavaBuilder extends ModuleLevelBuilder { counter.await(); } } + + private static boolean shouldForkCompilerProcess(CompileContext context, ModuleChunk chunk, JavaCompilingTool tool) { + final int compilerSdkVersion = getCompilerSdkVersion(context); + if (compilerSdkVersion < 9) { + // javac up to version 9 supports all previous releases + return false; + } + final int chunkSdkVersion = getChunkSdkVersion(chunk); + if (chunkSdkVersion < 0) { + return false; // was not able to determine jdk version, assuming in-process compiler + } + // according to JEP 182: Retiring javac "one plus three back" policy + return Math.abs(compilerSdkVersion - chunkSdkVersion) > 3; + } // If platformCp of the build process is the same as the target plafform, do not specify platformCp explicitly // this will allow javac to resolve against ct.sym file, which is required for the "compilation profiles" feature @@ -482,31 +492,16 @@ public class JavaBuilder extends ModuleLevelBuilder { }); } - private static synchronized JavacServerClient ensureJavacServerLaunched(@NotNull CompileContext context, @NotNull JavaCompilingTool compilingTool) throws Exception { - final ExternalJavacDescriptor descriptor = ExternalJavacDescriptor.KEY.get(context); - if (descriptor != null) { - return descriptor.client; + private static synchronized ExternalJavacServer ensureJavacServerStarted(@NotNull CompileContext context) throws Exception { + ExternalJavacServer server = ExternalJavacServer.KEY.get(context); + if (server != null) { + return server; } - // start server here - final int port = findFreePort(); - final int heapSize = getJavacServerHeapSize(context); - - // use the same jdk that used to run the build process - final String javaHome = SystemProperties.getJavaHome(); - - final BaseOSProcessHandler processHandler = JavacServerBootstrap.launchJavacServer( - javaHome, heapSize, port, Utils.getSystemRoot(), getCompilationVMOptions(context, compilingTool), compilingTool - ); - final JavacServerClient client = new JavacServerClient(); - try { - client.connect("127.0.0.1", port); - } - catch (Throwable ex) { - processHandler.destroyProcess(); - throw new Exception("Failed to connect to external javac process: ", ex); - } - ExternalJavacDescriptor.KEY.set(context, new ExternalJavacDescriptor(processHandler, client)); - return client; + final int listenPort = findFreePort(); + server = new ExternalJavacServer(); + server.start(listenPort); + ExternalJavacServer.KEY.set(context, server); + return server; } private static int convertToNumber(String ver) { @@ -564,17 +559,10 @@ public class JavaBuilder extends ModuleLevelBuilder { } catch (IOException e) { e.printStackTrace(System.err); - return JavacServer.DEFAULT_SERVER_PORT; + return ExternalJavacServer.DEFAULT_SERVER_PORT; } } - private static int getJavacServerHeapSize(CompileContext context) { - final JpsProject project = context.getProjectDescriptor().getProject(); - final JpsJavaCompilerConfiguration config = JpsJavaExtensionService.getInstance().getOrCreateCompilerConfiguration(project); - final JpsJavaCompilerOptions options = config.getCurrentCompilerOptions(); - return options.MAXIMUM_HEAP_SIZE; - } - private static final Key> JAVAC_OPTIONS = Key.create("_javac_options_"); private static final Key> JAVAC_VM_OPTIONS = Key.create("_javac_vm_options_"); private static final Key USER_DEFINED_BYTECODE_TARGET = Key.create("_user_defined_bytecode_target_"); @@ -628,19 +616,12 @@ public class JavaBuilder extends ModuleLevelBuilder { options.add(langLevel); } - JpsJavaCompilerConfiguration compilerConfiguration = JpsJavaExtensionService.getInstance().getOrCreateCompilerConfiguration( - context.getProjectDescriptor().getProject()); + final JpsJavaCompilerConfiguration compilerConfiguration = JpsJavaExtensionService.getInstance().getOrCreateCompilerConfiguration( + context.getProjectDescriptor().getProject() + ); + String bytecodeTarget = null; - int chunkSdkVersion = -1; for (JpsModule module : chunk.getModules()) { - final JpsSdk sdk = module.getSdk(JpsJavaSdkType.INSTANCE); - if (sdk != null) { - final int moduleSdkVersion = convertToNumber(sdk.getVersionString()); - if (moduleSdkVersion != 0 /*could determine the version*/&& (chunkSdkVersion < 0 || chunkSdkVersion > moduleSdkVersion)) { - chunkSdkVersion = moduleSdkVersion; - } - } - final String moduleTarget = compilerConfiguration.getByteCodeTargetLevel(module.getName()); if (moduleTarget == null) { continue; @@ -662,6 +643,7 @@ public class JavaBuilder extends ModuleLevelBuilder { } final int compilerSdkVersion = getCompilerSdkVersion(context); + final int chunkSdkVersion = getChunkSdkVersion(chunk); if (bytecodeTarget != null) { options.add("-target"); @@ -721,17 +703,19 @@ public class JavaBuilder extends ModuleLevelBuilder { } private static String getLanguageLevel(JpsModule module) { - LanguageLevel level = JpsJavaExtensionService.getInstance().getLanguageLevel(module); - if (level == null) return null; - switch (level) { - case JDK_1_3: return "1.3"; - case JDK_1_4: return "1.4"; - case JDK_1_5: return "1.5"; - case JDK_1_6: return "1.6"; - case JDK_1_7: return "1.7"; - case JDK_1_8: return "8"; - default: return null; + final LanguageLevel level = JpsJavaExtensionService.getInstance().getLanguageLevel(module); + if (level != null) { + switch (level) { + case JDK_1_3: return "1.3"; + case JDK_1_4: return "1.4"; + case JDK_1_5: return "1.5"; + case JDK_1_6: return "1.6"; + case JDK_1_7: return "1.7"; + case JDK_1_8: return "8"; + case JDK_1_9: return "9"; + } } + return null; } private static boolean isEncodingSet(List options) { @@ -743,7 +727,6 @@ public class JavaBuilder extends ModuleLevelBuilder { return false; } - private static int getCompilerSdkVersion(CompileContext context) { final Integer cached = JAVA_COMPILER_VERSION_KEY.get(context); if (cached != null) { @@ -754,6 +737,30 @@ public class JavaBuilder extends ModuleLevelBuilder { return javaVersion; } + private static int getChunkSdkVersion(ModuleChunk chunk) { + int chunkSdkVersion = -1; + for (JpsModule module : chunk.getModules()) { + final JpsSdk sdk = module.getSdk(JpsJavaSdkType.INSTANCE); + if (sdk != null) { + final int moduleSdkVersion = convertToNumber(sdk.getVersionString()); + if (moduleSdkVersion != 0 /*could determine the version*/&& (chunkSdkVersion < 0 || chunkSdkVersion > moduleSdkVersion)) { + chunkSdkVersion = moduleSdkVersion; + } + } + } + return chunkSdkVersion; + } + + private static String getChunkSdkHome(ModuleChunk chunk) { + for (JpsModule module : chunk.getModules()) { + final JpsSdk sdk = module.getSdk(JpsJavaSdkType.INSTANCE); + if (sdk != null) { + return sdk.getHomePath(); + } + } + return null; + } + private static void loadCommonJavacOptions(@NotNull CompileContext context, @NotNull JavaCompilingTool compilingTool) { final List options = new ArrayList(); final List vmOptions = new ArrayList(); diff --git a/jps/jps-builders/src/org/jetbrains/jps/javac/ExternalJavacMessageHandler.java b/jps/jps-builders/src/org/jetbrains/jps/javac/ExternalJavacMessageHandler.java new file mode 100644 index 000000000000..fd5bc65d1e5b --- /dev/null +++ b/jps/jps-builders/src/org/jetbrains/jps/javac/ExternalJavacMessageHandler.java @@ -0,0 +1,230 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.jps.javac; + +import com.google.protobuf.ByteString; +import com.google.protobuf.MessageLite; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jps.incremental.BinaryContent; + +import javax.tools.*; +import java.io.File; +import java.net.URI; +import java.util.Collection; +import java.util.Locale; + +/** + * @author Eugene Zhuravlev + * Date: 1/22/12 + */ +public class ExternalJavacMessageHandler { + private final DiagnosticOutputConsumer myDiagnosticSink; + private final OutputFileConsumer myOutputSink; + @Nullable + private final String myEncodingName; + private volatile boolean myTerminatedSuccessfully; + + public ExternalJavacMessageHandler(DiagnosticOutputConsumer diagnosticSink, + OutputFileConsumer outputSink, + @Nullable final String encodingName) { + myDiagnosticSink = diagnosticSink; + myOutputSink = outputSink; + myEncodingName = encodingName; + } + + public boolean handleMessage(MessageLite message) { + try { + final JavacRemoteProto.Message msg = (JavacRemoteProto.Message)message; + final JavacRemoteProto.Message.Type messageType = msg.getMessageType(); + + if (messageType == JavacRemoteProto.Message.Type.RESPONSE) { + final JavacRemoteProto.Message.Response response = msg.getResponse(); + final JavacRemoteProto.Message.Response.Type responseType = response.getResponseType(); + + if (responseType == JavacRemoteProto.Message.Response.Type.BUILD_MESSAGE) { + final JavacRemoteProto.Message.Response.CompileMessage compileMessage = response.getCompileMessage(); + final JavacRemoteProto.Message.Response.CompileMessage.Kind messageKind = compileMessage.getKind(); + + if (messageKind == JavacRemoteProto.Message.Response.CompileMessage.Kind.STD_OUT) { + if (compileMessage.hasText()) { + myDiagnosticSink.outputLineAvailable(compileMessage.getText()); + } + } + else { + final String sourceUri = compileMessage.hasSourceUri()? compileMessage.getSourceUri() : null; + final JavaFileObject srcFileObject = sourceUri != null? new DummyJavaFileObject(URI.create(sourceUri)) : null; + myDiagnosticSink.report(new DummyDiagnostic(convertKind(messageKind), srcFileObject, compileMessage)); + } + + return false; + } + + if (responseType == JavacRemoteProto.Message.Response.Type.OUTPUT_OBJECT) { + final JavacRemoteProto.Message.Response.OutputObject outputObject = response.getOutputObject(); + final JavacRemoteProto.Message.Response.OutputObject.Kind kind = outputObject.getKind(); + + final String outputRoot = outputObject.hasOutputRoot()? outputObject.getOutputRoot() : null; + final File outputRootFile = outputRoot != null? new File(outputRoot) : null; + + final BinaryContent fileObjectContent; + final ByteString content = outputObject.hasContent()? outputObject.getContent() : null; + if (content != null) { + final byte[] bytes = content.toByteArray(); + fileObjectContent = new BinaryContent(bytes, 0, bytes.length); + } + else { + fileObjectContent = null; + } + + final String sourceUri = outputObject.hasSourceUri()? outputObject.getSourceUri() : null; + final URI srcUri = sourceUri != null? URI.create(sourceUri) : null; + final OutputFileObject fileObject = new OutputFileObject( + null, + outputRootFile, + outputObject.hasRelativePath()? outputObject.getRelativePath() : null, + new File(outputObject.getFilePath()), + convertKind(kind), + outputObject.hasClassName()? outputObject.getClassName() : null, + srcUri, + myEncodingName, fileObjectContent + ); + + myOutputSink.save(fileObject); + return false; + } + + if (responseType == JavacRemoteProto.Message.Response.Type.SRC_FILE_LOADED) { + final JavacRemoteProto.Message.Response.OutputObject outputObject = response.getOutputObject(); + final File file = new File(outputObject.getFilePath()); + myDiagnosticSink.javaFileLoaded(file); + return false; + } + + if (responseType == JavacRemoteProto.Message.Response.Type.CLASS_DATA) { + final JavacRemoteProto.Message.Response.ClassData data = response.getClassData(); + final String className = data.getClassName(); + final Collection imports = data.getImportStatementList(); + final Collection staticImports = data.getStaticImportList(); + myDiagnosticSink.registerImports(className, imports, staticImports); + return false; + } + + if (responseType == JavacRemoteProto.Message.Response.Type.BUILD_COMPLETED) { + if (response.hasCompletionStatus()) { + myTerminatedSuccessfully = response.getCompletionStatus(); + } + return true; + } + + throw new Exception("Unsupported response type: " + responseType.name()); + } + + if (messageType == JavacRemoteProto.Message.Type.FAILURE) { + final JavacRemoteProto.Message.Failure failure = msg.getFailure(); + final StringBuilder buf = new StringBuilder(); + if (failure.hasDescription()) { + buf.append(failure.getDescription()); + } + if (failure.hasStacktrace()) { + if (buf.length() > 0) { + buf.append("\n"); + } + buf.append(failure.getStacktrace()); + } + myDiagnosticSink.report(new PlainMessageDiagnostic(Diagnostic.Kind.ERROR, buf.toString())); + return true; + } + + throw new Exception("Unsupported message type: " + messageType.name()); + } + catch (Throwable e) { + myDiagnosticSink.report(new PlainMessageDiagnostic(Diagnostic.Kind.ERROR, e.getMessage())); + return true; + } + } + + public boolean isTerminatedSuccessfully() { + return myTerminatedSuccessfully; + } + + private static Diagnostic.Kind convertKind(JavacRemoteProto.Message.Response.CompileMessage.Kind kind) { + switch (kind) { + case ERROR: return Diagnostic.Kind.ERROR; + case WARNING: return Diagnostic.Kind.WARNING; + case MANDATORY_WARNING: return Diagnostic.Kind.MANDATORY_WARNING; + case NOTE: return Diagnostic.Kind.NOTE; + default : return Diagnostic.Kind.OTHER; + } + } + + private static OutputFileObject.Kind convertKind(JavacRemoteProto.Message.Response.OutputObject.Kind kind) { + switch (kind) { + case CLASS: return JavaFileObject.Kind.CLASS; + case HTML: return JavaFileObject.Kind.HTML; + case SOURCE: return JavaFileObject.Kind.SOURCE; + default : return JavaFileObject.Kind.OTHER; + } + } + + private static class DummyDiagnostic implements Diagnostic { + + private final Kind myMessageKind; + private final JavaFileObject mySrcFileObject; + private final JavacRemoteProto.Message.Response.CompileMessage myCompileMessage; + + public DummyDiagnostic(final Kind messageKind, JavaFileObject srcFileObject, JavacRemoteProto.Message.Response.CompileMessage compileMessage) { + myMessageKind = messageKind; + mySrcFileObject = srcFileObject; + myCompileMessage = compileMessage; + } + + public Kind getKind() { + return myMessageKind; + } + + public JavaFileObject getSource() { + return mySrcFileObject; + } + + public long getPosition() { + return myCompileMessage.hasProblemLocationOffset()? myCompileMessage.getProblemLocationOffset() : -1; + } + + public long getStartPosition() { + return myCompileMessage.hasProblemBeginOffset()? myCompileMessage.getProblemBeginOffset() : -1; + } + + public long getEndPosition() { + return myCompileMessage.hasProblemEndOffset()? myCompileMessage.getProblemEndOffset() : -1; + } + + public long getLineNumber() { + return myCompileMessage.hasLine()? myCompileMessage.getLine() : -1; + } + + public long getColumnNumber() { + return myCompileMessage.hasColumn()? myCompileMessage.getColumn() : -1; + } + + public String getCode() { + return null; + } + + public String getMessage(Locale locale) { + return myCompileMessage.hasText()? myCompileMessage.getText() : null; + } + } +} diff --git a/jps/jps-builders/src/org/jetbrains/jps/javac/ExternalJavacProcess.java b/jps/jps-builders/src/org/jetbrains/jps/javac/ExternalJavacProcess.java new file mode 100644 index 000000000000..d01ae7af6b4d --- /dev/null +++ b/jps/jps-builders/src/org/jetbrains/jps/javac/ExternalJavacProcess.java @@ -0,0 +1,339 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.jps.javac; + +import io.netty.bootstrap.Bootstrap; +import io.netty.channel.*; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.nio.NioSocketChannel; +import io.netty.handler.codec.protobuf.ProtobufDecoder; +import io.netty.handler.codec.protobuf.ProtobufEncoder; +import io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder; +import io.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender; +import io.netty.util.internal.logging.InternalLoggerFactory; +import io.netty.util.internal.logging.Log4JLoggerFactory; +import org.apache.log4j.ConsoleAppender; +import org.apache.log4j.Level; +import org.apache.log4j.PatternLayout; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jps.api.CanceledStatus; +import org.jetbrains.jps.builders.impl.java.JavacCompilerTool; +import org.jetbrains.jps.builders.java.JavaBuilderUtil; +import org.jetbrains.jps.builders.java.JavaCompilingTool; +import org.jetbrains.jps.service.SharedThreadPool; + +import javax.tools.*; +import java.io.File; +import java.util.*; +import java.util.concurrent.TimeUnit; + +/** + * @author Eugene Zhuravlev + * Date: 1/22/12 + */ +public class ExternalJavacProcess { + public static final String JPS_JAVA_COMPILING_TOOL_PROPERTY = "jps.java.compiling.tool"; + private final ChannelInitializer myChannelInitializer; + private final EventLoopGroup myEventLoopGroup; + private volatile ChannelFuture myConnectFuture; + private volatile CancelHandler myCancelHandler; + + static { + org.apache.log4j.Logger root = org.apache.log4j.Logger.getRootLogger(); + if (!root.getAllAppenders().hasMoreElements()) { + root.setLevel(Level.INFO); + root.addAppender(new ConsoleAppender(new PatternLayout(PatternLayout.DEFAULT_CONVERSION_PATTERN))); + } + InternalLoggerFactory.setDefaultFactory(new Log4JLoggerFactory()); + } + + public ExternalJavacProcess() { + final JavacRemoteProto.Message msgDefaultInstance = JavacRemoteProto.Message.getDefaultInstance(); + + myEventLoopGroup = new NioEventLoopGroup(1, SharedThreadPool.getInstance()); + myChannelInitializer = new ChannelInitializer() { + @Override + protected void initChannel(Channel channel) throws Exception { + channel.pipeline().addLast(new ProtobufVarint32FrameDecoder(), + new ProtobufDecoder(msgDefaultInstance), + new ProtobufVarint32LengthFieldPrepender(), + new ProtobufEncoder(), + new CompilationRequestsHandler() + ); + } + }; + } + + //static volatile long myGlobalStart; + + public static void main(String[] args) { + //myGlobalStart = System.currentTimeMillis(); + UUID uuid = null; + String host = null; + int port = -1; + if (args.length > 0) { + try { + uuid = UUID.fromString(args[0]); + } + catch (Exception e) { + System.err.println("Error parsing session id: " + e.getMessage()); + System.exit(-1); + } + + host = args[1]; + + try { + port = Integer.parseInt(args[2]); + } + catch (NumberFormatException e) { + System.err.println("Error parsing port: " + e.getMessage()); + System.exit(-1); + } + } + else { + System.err.println("Insufficient parameters"); + System.exit(-1); + } + + final ExternalJavacProcess process = new ExternalJavacProcess(); + try { + //final long connectStart = System.currentTimeMillis(); + if (process.connect(host, port)) { + //final long connectEnd = System.currentTimeMillis(); + //System.err.println("Connected in " + (connectEnd - connectStart) + " ms; since start: " + (connectEnd - myGlobalStart)); + process.myConnectFuture.channel().writeAndFlush( + JavacProtoUtil.toMessage(uuid, JavacProtoUtil.createRequestAckResponse()) + ); + } + else { + System.err.println("Failed to connect to parent process"); + System.exit(-1); + } + } + catch (Throwable throwable) { + throwable.printStackTrace(System.err); + System.exit(-1); + } + } + + private boolean connect(final String host, final int port) throws Throwable { + final Bootstrap bootstrap = new Bootstrap().group(myEventLoopGroup).channel(NioSocketChannel.class).handler(myChannelInitializer); + bootstrap.option(ChannelOption.TCP_NODELAY, true).option(ChannelOption.SO_KEEPALIVE, true); + final ChannelFuture future = bootstrap.connect(host, port).syncUninterruptibly(); + if (future.isSuccess()) { + myConnectFuture = future; + return true; + } + return false; + } + + private static JavacRemoteProto.Message compile(final ChannelHandlerContext context, + final UUID sessionId, + List options, + Collection files, + Collection classpath, + Collection platformCp, + Collection sourcePath, + Map> outs, + final CanceledStatus canceledStatus) { + //final long compileStart = System.currentTimeMillis(); + //System.err.println("Compile start; since global start: " + (compileStart - myGlobalStart)); + final DiagnosticOutputConsumer diagnostic = new DiagnosticOutputConsumer() { + @Override + public void javaFileLoaded(File file) { + context.channel().writeAndFlush(JavacProtoUtil.toMessage(sessionId, JavacProtoUtil.createSourceFileLoadedResponse(file))); + } + + @Override + public void outputLineAvailable(String line) { + context.channel().writeAndFlush(JavacProtoUtil.toMessage(sessionId, JavacProtoUtil.createStdOutputResponse(line))); + } + + @Override + public void report(Diagnostic diagnostic) { + final JavacRemoteProto.Message.Response response = JavacProtoUtil.createBuildMessageResponse(diagnostic); + context.channel().writeAndFlush(JavacProtoUtil.toMessage(sessionId, response)); + } + + @Override + public void registerImports(String className, Collection imports, Collection staticImports) { + final JavacRemoteProto.Message.Response response = JavacProtoUtil.createClassDataResponse(className, imports, staticImports); + context.channel().writeAndFlush(JavacProtoUtil.toMessage(sessionId, response)); + } + }; + + final OutputFileConsumer outputSink = new OutputFileConsumer() { + @Override + public void save(@NotNull OutputFileObject fileObject) { + context.channel().writeAndFlush(JavacProtoUtil.toMessage(sessionId, JavacProtoUtil.createOutputObjectResponse(fileObject))); + } + }; + + try { + JavaCompilingTool tool = getCompilingTool(); + final boolean rc = JavacMain.compile(options, files, classpath, platformCp, sourcePath, outs, diagnostic, outputSink, canceledStatus, + tool); + return JavacProtoUtil.toMessage(sessionId, JavacProtoUtil.createBuildCompletedResponse(rc)); + } + catch (Throwable e) { + //noinspection UseOfSystemOutOrSystemErr + e.printStackTrace(System.err); + return JavacProtoUtil.toMessage(sessionId, JavacProtoUtil.createFailure(e.getMessage(), e)); + } + //finally { + // final long compileEnd = System.currentTimeMillis(); + // System.err.println("Compiled in " + (compileEnd - compileStart) + " ms; since global start: " + (compileEnd - myGlobalStart)); + //} + } + + private static JavaCompilingTool getCompilingTool() { + String property = System.getProperty(JPS_JAVA_COMPILING_TOOL_PROPERTY); + if (property != null) { + JavaCompilingTool tool = JavaBuilderUtil.findCompilingTool(property); + if (tool != null) { + return tool; + } + } + return new JavacCompilerTool(); + } + + @ChannelHandler.Sharable + private class CompilationRequestsHandler extends SimpleChannelInboundHandler { + @Override + public void channelRead0(final ChannelHandlerContext context, JavacRemoteProto.Message message) throws Exception { + final UUID sessionId = JavacProtoUtil.fromProtoUUID(message.getSessionId()); + final JavacRemoteProto.Message.Type messageType = message.getMessageType(); + + JavacRemoteProto.Message reply = null; + + try { + if (messageType == JavacRemoteProto.Message.Type.REQUEST) { + final JavacRemoteProto.Message.Request request = message.getRequest(); + final JavacRemoteProto.Message.Request.Type requestType = request.getRequestType(); + if (requestType == JavacRemoteProto.Message.Request.Type.COMPILE) { + if (myCancelHandler == null) { // if not running yet + final List options = request.getOptionList(); + final List files = toFiles(request.getFileList()); + final List cp = toFiles(request.getClasspathList()); + final List platformCp = toFiles(request.getPlatformClasspathList()); + final List srcPath = toFiles(request.getSourcepathList()); + + final Map> outs = new HashMap>(); + for (JavacRemoteProto.Message.Request.OutputGroup outputGroup : request.getOutputList()) { + final Set srcRoots = new HashSet(); + for (String root : outputGroup.getSourceRootList()) { + srcRoots.add(new File(root)); + } + outs.put(new File(outputGroup.getOutputRoot()), srcRoots); + } + + final CancelHandler cancelHandler = new CancelHandler(); + myCancelHandler = cancelHandler; + SharedThreadPool.getInstance().executeOnPooledThread(new Runnable() { + @Override + public void run() { + try { + context.channel().writeAndFlush( + compile(context, sessionId, options, files, cp, platformCp, srcPath, outs, cancelHandler) + ).awaitUninterruptibly(); + } + finally { + myCancelHandler = null; + ExternalJavacProcess.this.stop(); + } + } + }); + } + } + else if (requestType == JavacRemoteProto.Message.Request.Type.CANCEL){ + cancelBuild(); + } + else if (requestType == JavacRemoteProto.Message.Request.Type.SHUTDOWN){ + cancelBuild(); + new Thread("StopThread") { + @Override + public void run() { + //noinspection finally + ExternalJavacProcess.this.stop(); + } + }.start(); + } + else { + reply = JavacProtoUtil.toMessage(sessionId, JavacProtoUtil.createFailure("Unsupported request type: " + requestType.name(), null)); + } + } + else { + reply = JavacProtoUtil.toMessage(sessionId, JavacProtoUtil.createFailure("Unsupported message: " + messageType.name(), null)); + } + } + finally { + if (reply != null) { + context.channel().writeAndFlush(reply); + } + } + } + } + + public void stop() { + try { + //final long stopStart = System.currentTimeMillis(); + //System.err.println("Exiting. Since global start " + (stopStart - myGlobalStart)); + final ChannelFuture future = myConnectFuture; + if (future != null) { + future.channel().close().await(); + } + myEventLoopGroup.shutdownGracefully(0, 15, TimeUnit.SECONDS).await(); + //final long stopEnd = System.currentTimeMillis(); + //System.err.println("Stop completed in " + (stopEnd - stopStart) + "ms; since global start: " + ((stopEnd - myGlobalStart))); + System.exit(0); + } + catch (Throwable e) { + e.printStackTrace(System.err); + System.exit(-1); + } + } + + private static List toFiles(List paths) { + final List files = new ArrayList(paths.size()); + for (String path : paths) { + files.add(new File(path)); + } + return files; + } + + public void cancelBuild() { + final CancelHandler cancelHandler = myCancelHandler; + if (cancelHandler != null) { + cancelHandler.cancel(); + } + } + + private static class CancelHandler implements CanceledStatus { + private volatile boolean myIsCanceled = false; + + private CancelHandler() { + } + + public void cancel() { + myIsCanceled = true; + } + + @Override + public boolean isCanceled() { + return myIsCanceled; + } + } +} diff --git a/jps/jps-builders/src/org/jetbrains/jps/javac/ExternalJavacServer.java b/jps/jps-builders/src/org/jetbrains/jps/javac/ExternalJavacServer.java new file mode 100644 index 000000000000..f5ff5b175bd8 --- /dev/null +++ b/jps/jps-builders/src/org/jetbrains/jps/javac/ExternalJavacServer.java @@ -0,0 +1,310 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.jps.javac; + +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.util.concurrency.Semaphore; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.*; +import io.netty.channel.group.ChannelGroup; +import io.netty.channel.group.ChannelGroupFuture; +import io.netty.channel.group.DefaultChannelGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.handler.codec.protobuf.ProtobufDecoder; +import io.netty.handler.codec.protobuf.ProtobufEncoder; +import io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder; +import io.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender; +import io.netty.util.AttributeKey; +import io.netty.util.concurrent.ImmediateEventExecutor; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jps.builders.java.JavaCompilingTool; +import org.jetbrains.jps.incremental.CompileContext; +import org.jetbrains.jps.incremental.GlobalContextKey; +import org.jetbrains.jps.incremental.Utils; +import org.jetbrains.jps.model.JpsProject; +import org.jetbrains.jps.model.java.JpsJavaExtensionService; +import org.jetbrains.jps.model.java.compiler.JpsJavaCompilerConfiguration; +import org.jetbrains.jps.model.java.compiler.JpsJavaCompilerOptions; +import org.jetbrains.jps.service.SharedThreadPool; + +import javax.tools.*; +import java.io.File; +import java.util.*; +import java.util.concurrent.TimeUnit; + +/** + * @author Eugene Zhuravlev + * Date: 1/22/12 + */ +@SuppressWarnings("UseOfSystemOutOrSystemErr") +public class ExternalJavacServer { + private static final Logger LOG = Logger.getInstance("#org.jetbrains.jps.javac.ExternalJavacServer"); + public static final GlobalContextKey KEY = GlobalContextKey.create("_external_javac_server_"); + + public static final int DEFAULT_SERVER_PORT = 7878; + private static final AttributeKey SESSION_DESCRIPTOR = AttributeKey.valueOf("ExternalJavacServer.JavacProcessDescriptor"); + + private ChannelRegistrar myChannelRegistrar; + private final Map myMessageHandlers = new HashMap(); + private int myListenPort = DEFAULT_SERVER_PORT; + + public void start(int listenPort) { + final ServerBootstrap bootstrap = new ServerBootstrap().group(new NioEventLoopGroup(1, SharedThreadPool.getInstance())).channel(NioServerSocketChannel.class); + bootstrap.childOption(ChannelOption.TCP_NODELAY, true).childOption(ChannelOption.SO_KEEPALIVE, true); + myChannelRegistrar = new ChannelRegistrar(); + final ChannelHandler compilationRequestsHandler = new CompilationRequestsHandler(); + bootstrap.childHandler(new ChannelInitializer() { + @Override + protected void initChannel(Channel channel) throws Exception { + channel.pipeline().addLast(myChannelRegistrar, + new ProtobufVarint32FrameDecoder(), + new ProtobufDecoder(JavacRemoteProto.Message.getDefaultInstance()), + new ProtobufVarint32LengthFieldPrepender(), + new ProtobufEncoder(), + compilationRequestsHandler); + } + }); + myChannelRegistrar.add(bootstrap.bind(listenPort).syncUninterruptibly().channel()); + myListenPort = listenPort; + } + + private static int getExternalJavacHeapSize(CompileContext context) { + final JpsProject project = context.getProjectDescriptor().getProject(); + final JpsJavaCompilerConfiguration config = JpsJavaExtensionService.getInstance().getOrCreateCompilerConfiguration(project); + final JpsJavaCompilerOptions options = config.getCurrentCompilerOptions(); + return options.MAXIMUM_HEAP_SIZE; + } + + + public boolean forkJavac(CompileContext context, List options, + List vmOptions, Collection files, + Collection classpath, + Collection platformCp, + Collection sourcePath, + Map> outs, + DiagnosticOutputConsumer diagnosticSink, + OutputFileConsumer outputSink, + final String javaHome, final JavaCompilingTool compilingTool) { + final ExternalJavacMessageHandler rh = new ExternalJavacMessageHandler(diagnosticSink, outputSink, getEncodingName(options)); + final JavacRemoteProto.Message.Request request = JavacProtoUtil.createCompilationRequest(options, files, classpath, platformCp, sourcePath, outs); + final UUID uuid = UUID.randomUUID(); + final JavacProcessDescriptor processDescriptor = new JavacProcessDescriptor(uuid, rh, request); + synchronized (myMessageHandlers) { + myMessageHandlers.put(uuid, processDescriptor); + } + try { + final JavacServerBootstrap.ExternalJavacProcessHandler processHandler = JavacServerBootstrap.launchExternalJavacProcess( + uuid, javaHome, getExternalJavacHeapSize(context), myListenPort, Utils.getSystemRoot(), vmOptions, compilingTool + ); + + while (!processDescriptor.waitFor(300L)) { + if (processHandler.isProcessTerminated() && processDescriptor.channel == null && processHandler.getExitCode() != 0) { + // process terminated abnormally and no communication took place + processDescriptor.setDone(); + break; + } + if (context.getCancelStatus().isCanceled()) { + processDescriptor.cancelBuild(); + } + } + + return rh.isTerminatedSuccessfully(); + } + catch (Throwable e) { + LOG.info(e); + diagnosticSink.report(new PlainMessageDiagnostic(Diagnostic.Kind.ERROR, e.getMessage())); + } + finally { + unregisterMessageHandler(uuid); + } + return false; + } + + private void unregisterMessageHandler(UUID uuid) { + final JavacProcessDescriptor descriptor; + synchronized (myMessageHandlers) { + descriptor = myMessageHandlers.remove(uuid); + } + if (descriptor != null) { + descriptor.setDone(); + } + } + + @Nullable + private static String getEncodingName(List options) { + boolean found = false; + for (String option : options) { + if (found) { + return option; + } + if ("-encoding".equalsIgnoreCase(option)) { + found = true; + } + } + return null; + } + + public void stop() { + myChannelRegistrar.close().awaitUninterruptibly(); + } + + @ChannelHandler.Sharable + private class CompilationRequestsHandler extends SimpleChannelInboundHandler { + @Override + public void channelUnregistered(ChannelHandlerContext ctx) throws Exception { + JavacProcessDescriptor descriptor = ctx.attr(SESSION_DESCRIPTOR).get(); + if (descriptor != null) { + descriptor.setDone(); + } + super.channelUnregistered(ctx); + } + + @Override + public void channelRead0(final ChannelHandlerContext context, JavacRemoteProto.Message message) throws Exception { + JavacProcessDescriptor descriptor = context.attr(SESSION_DESCRIPTOR).get(); + + UUID sessionId; + if (descriptor == null) { + // this is the first message for this session, so fill session data with missing info + sessionId = JavacProtoUtil.fromProtoUUID(message.getSessionId()); + + descriptor = myMessageHandlers.get(sessionId); + if (descriptor != null) { + descriptor.channel = context.channel(); + context.attr(SESSION_DESCRIPTOR).set(descriptor); + } + } + else { + sessionId = descriptor.sessionId; + } + + final ExternalJavacMessageHandler handler = descriptor != null? descriptor.handler : null; + + final JavacRemoteProto.Message.Type messageType = message.getMessageType(); + + JavacRemoteProto.Message reply = null; + try { + if (messageType == JavacRemoteProto.Message.Type.RESPONSE) { + final JavacRemoteProto.Message.Response response = message.getResponse(); + final JavacRemoteProto.Message.Response.Type responseType = response.getResponseType(); + if (handler != null) { + if (responseType == JavacRemoteProto.Message.Response.Type.REQUEST_ACK) { + final JavacRemoteProto.Message.Request request = descriptor.request; + if (request != null) { + reply = JavacProtoUtil.toMessage(sessionId, request); + descriptor.request = null; + } + } + else { + final boolean terminateOk = handler.handleMessage(message); + if (terminateOk) { + descriptor.setDone(); + } + } + } + else { + reply = JavacProtoUtil.toMessage(sessionId, JavacProtoUtil.createCancelRequest()); + } + } + else { + reply = JavacProtoUtil.toMessage(sessionId, JavacProtoUtil.createFailure("Unsupported message: " + messageType.name(), null)); + } + } + finally { + if (reply != null) { + context.channel().writeAndFlush(reply); + } + } + } + } + + @ChannelHandler.Sharable + private static final class ChannelRegistrar extends ChannelInboundHandlerAdapter { + private final ChannelGroup openChannels = new DefaultChannelGroup(ImmediateEventExecutor.INSTANCE); + + public boolean isEmpty() { + return openChannels.isEmpty(); + } + + public void add(@NotNull Channel serverChannel) { + assert serverChannel instanceof ServerChannel; + openChannels.add(serverChannel); + } + + @Override + public void channelActive(ChannelHandlerContext context) throws Exception { + // we don't need to remove channel on close - ChannelGroup do it + openChannels.add(context.channel()); + super.channelActive(context); + } + + public ChannelGroupFuture close() { + EventLoopGroup eventLoopGroup = null; + for (Channel channel : openChannels) { + if (channel instanceof ServerChannel) { + eventLoopGroup = channel.eventLoop().parent(); + break; + } + } + + ChannelGroupFuture future; + try { + future = openChannels.close(); + } + finally { + assert eventLoopGroup != null; + eventLoopGroup.shutdownGracefully(0, 15, TimeUnit.SECONDS); + } + return future; + } + } + + private static class JavacProcessDescriptor { + @NotNull + final UUID sessionId; + @NotNull + final ExternalJavacMessageHandler handler; + volatile JavacRemoteProto.Message.Request request; + volatile Channel channel; + private final Semaphore myDone = new Semaphore(); + + public JavacProcessDescriptor(@NotNull UUID sessionId, @NotNull ExternalJavacMessageHandler handler, @NotNull JavacRemoteProto.Message.Request request) { + this.sessionId = sessionId; + this.handler = handler; + this.request = request; + myDone.down(); + } + + public void cancelBuild() { + if (channel != null) { + channel.writeAndFlush(JavacProtoUtil.toMessage(sessionId, JavacProtoUtil.createCancelRequest())); + } + } + + public void setDone() { + myDone.up(); + } + + + public boolean waitFor(long timeout) { + return myDone.waitFor(timeout); + } + + } + +} \ No newline at end of file diff --git a/jps/jps-builders/src/org/jetbrains/jps/javac/JavacServer.java b/jps/jps-builders/src/org/jetbrains/jps/javac/JavacServer.java deleted file mode 100644 index 0ecfe7617160..000000000000 --- a/jps/jps-builders/src/org/jetbrains/jps/javac/JavacServer.java +++ /dev/null @@ -1,335 +0,0 @@ -/* - * Copyright 2000-2012 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.jps.javac; - -import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.*; -import io.netty.channel.group.ChannelGroup; -import io.netty.channel.group.ChannelGroupFuture; -import io.netty.channel.group.DefaultChannelGroup; -import io.netty.channel.nio.NioEventLoopGroup; -import io.netty.channel.socket.nio.NioServerSocketChannel; -import io.netty.handler.codec.protobuf.ProtobufDecoder; -import io.netty.handler.codec.protobuf.ProtobufEncoder; -import io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder; -import io.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender; -import io.netty.util.concurrent.ImmediateEventExecutor; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.jps.api.CanceledStatus; -import org.jetbrains.jps.builders.impl.java.JavacCompilerTool; -import org.jetbrains.jps.builders.java.JavaBuilderUtil; -import org.jetbrains.jps.builders.java.JavaCompilingTool; -import org.jetbrains.jps.service.SharedThreadPool; - -import javax.tools.*; -import java.io.File; -import java.util.*; - -/** - * @author Eugene Zhuravlev - * Date: 1/22/12 - */ -@SuppressWarnings("UseOfSystemOutOrSystemErr") -public class JavacServer { - public static final int DEFAULT_SERVER_PORT = 7878; - public static final String SERVER_SUCCESS_START_MESSAGE = "Javac server started successfully. Listening on port: "; - public static final String SERVER_ERROR_START_MESSAGE = "Error starting Javac Server: "; - public static final String JPS_JAVA_COMPILING_TOOL_PROPERTY = "jps.java.compiling.tool"; - - private ChannelRegistrar myChannelRegistrar; - - public void start(int listenPort) { - final ServerBootstrap bootstrap = new ServerBootstrap().group(new NioEventLoopGroup(1, SharedThreadPool.getInstance())).channel(NioServerSocketChannel.class); - bootstrap.childOption(ChannelOption.TCP_NODELAY, true).childOption(ChannelOption.SO_KEEPALIVE, true); - myChannelRegistrar = new ChannelRegistrar(); - final ChannelHandler compilationRequestsHandler = new CompilationRequestsHandler(); - bootstrap.childHandler(new ChannelInitializer() { - @Override - protected void initChannel(Channel channel) throws Exception { - channel.pipeline().addLast(myChannelRegistrar, - new ProtobufVarint32FrameDecoder(), - new ProtobufDecoder(JavacRemoteProto.Message.getDefaultInstance()), - new ProtobufVarint32LengthFieldPrepender(), - new ProtobufEncoder(), - compilationRequestsHandler); - } - }); - myChannelRegistrar.add(bootstrap.bind(listenPort).syncUninterruptibly().channel()); - } - - public void stop() { - myChannelRegistrar.close().awaitUninterruptibly(); - } - - public static void main(String[] args) { - JavacServer server = null; - try { - int port = DEFAULT_SERVER_PORT; - if (args.length > 0) { - try { - port = Integer.parseInt(args[0]); - } - catch (NumberFormatException e) { - System.err.println("Error parsing port: " + e.getMessage()); - System.exit(-1); - } - } - - server = new JavacServer(); - server.start(port); - final JavacServer finalServer = server; - Runtime.getRuntime().addShutdownHook(new Thread("Shutdown hook thread") { - @Override - public void run() { - finalServer.stop(); - } - }); - - System.out.println("Server classpath: " + System.getProperty("java.class.path")); - System.err.println(SERVER_SUCCESS_START_MESSAGE + port); - } - catch (Throwable e) { - System.err.println(SERVER_ERROR_START_MESSAGE + e.getMessage()); - e.printStackTrace(System.err); - try { - if (server != null) { - server.stop(); - } - } - finally { - System.exit(-1); - } - } - } - - public static JavacRemoteProto.Message compile(final ChannelHandlerContext context, - final UUID sessionId, - List options, - Collection files, - Collection classpath, - Collection platformCp, - Collection sourcePath, - Map> outs, - final CanceledStatus canceledStatus) { - final DiagnosticOutputConsumer diagnostic = new DiagnosticOutputConsumer() { - @Override - public void javaFileLoaded(File file) { - context.channel().writeAndFlush(JavacProtoUtil.toMessage(sessionId, JavacProtoUtil.createSourceFileLoadedResponse(file))); - } - - @Override - public void outputLineAvailable(String line) { - context.channel().writeAndFlush(JavacProtoUtil.toMessage(sessionId, JavacProtoUtil.createStdOutputResponse(line))); - } - - @Override - public void report(Diagnostic diagnostic) { - final JavacRemoteProto.Message.Response response = JavacProtoUtil.createBuildMessageResponse(diagnostic); - context.channel().writeAndFlush(JavacProtoUtil.toMessage(sessionId, response)); - } - - @Override - public void registerImports(String className, Collection imports, Collection staticImports) { - final JavacRemoteProto.Message.Response response = JavacProtoUtil.createClassDataResponse(className, imports, staticImports); - context.channel().writeAndFlush(JavacProtoUtil.toMessage(sessionId, response)); - } - }; - - final OutputFileConsumer outputSink = new OutputFileConsumer() { - @Override - public void save(@NotNull OutputFileObject fileObject) { - context.channel().writeAndFlush(JavacProtoUtil.toMessage(sessionId, JavacProtoUtil.createOutputObjectResponse(fileObject))); - } - }; - - try { - JavaCompilingTool tool = getCompilingTool(); - final boolean rc = JavacMain.compile(options, files, classpath, platformCp, sourcePath, outs, diagnostic, outputSink, canceledStatus, tool); - return JavacProtoUtil.toMessage(sessionId, JavacProtoUtil.createBuildCompletedResponse(rc)); - } - catch (Throwable e) { - //noinspection UseOfSystemOutOrSystemErr - e.printStackTrace(System.err); - return JavacProtoUtil.toMessage(sessionId, JavacProtoUtil.createFailure(e.getMessage(), e)); - } - } - - private static JavaCompilingTool getCompilingTool() { - String property = System.getProperty(JPS_JAVA_COMPILING_TOOL_PROPERTY); - if (property != null) { - JavaCompilingTool tool = JavaBuilderUtil.findCompilingTool(property); - if (tool != null) { - return tool; - } - } - return new JavacCompilerTool(); - } - - private final Set myCancelHandlers = Collections.synchronizedSet(new HashSet()); - - public void cancelBuilds() { - synchronized (myCancelHandlers) { - for (CancelHandler handler : myCancelHandlers) { - handler.cancel(); - } - } - } - - private static List toFiles(List paths) { - final List files = new ArrayList(paths.size()); - for (String path : paths) { - files.add(new File(path)); - } - return files; - } - - @ChannelHandler.Sharable - private class CompilationRequestsHandler extends SimpleChannelInboundHandler { - @Override - public void channelRead0(final ChannelHandlerContext context, JavacRemoteProto.Message message) throws Exception { - final UUID sessionId = JavacProtoUtil.fromProtoUUID(message.getSessionId()); - final JavacRemoteProto.Message.Type messageType = message.getMessageType(); - - JavacRemoteProto.Message reply = null; - - try { - if (messageType == JavacRemoteProto.Message.Type.REQUEST) { - final JavacRemoteProto.Message.Request request = message.getRequest(); - final JavacRemoteProto.Message.Request.Type requestType = request.getRequestType(); - if (requestType == JavacRemoteProto.Message.Request.Type.COMPILE) { - final List options = request.getOptionList(); - final List files = toFiles(request.getFileList()); - final List cp = toFiles(request.getClasspathList()); - final List platformCp = toFiles(request.getPlatformClasspathList()); - final List srcPath = toFiles(request.getSourcepathList()); - - final Map> outs = new HashMap>(); - for (JavacRemoteProto.Message.Request.OutputGroup outputGroup : request.getOutputList()) { - final Set srcRoots = new HashSet(); - for (String root : outputGroup.getSourceRootList()) { - srcRoots.add(new File(root)); - } - outs.put(new File(outputGroup.getOutputRoot()), srcRoots); - } - - final CancelHandler cancelHandler = new CancelHandler(); - myCancelHandlers.add(cancelHandler); - SharedThreadPool.getInstance().executeOnPooledThread(new Runnable() { - @Override - public void run() { - try { - context.channel() - .writeAndFlush(compile(context, sessionId, options, files, cp, platformCp, srcPath, outs, cancelHandler)); - } - finally { - myCancelHandlers.remove(cancelHandler); - } - } - }); - } - else if (requestType == JavacRemoteProto.Message.Request.Type.CANCEL){ - cancelBuilds(); - reply = JavacProtoUtil.toMessage(sessionId, JavacProtoUtil.createRequestAckResponse()); - } - else if (requestType == JavacRemoteProto.Message.Request.Type.SHUTDOWN){ - cancelBuilds(); - new Thread("StopThread") { - @Override - public void run() { - //noinspection finally - try { - JavacServer.this.stop(); - } - finally { - System.exit(0); - } - } - }.start(); - } - else { - reply = JavacProtoUtil.toMessage(sessionId, JavacProtoUtil.createFailure("Unsupported request type: " + requestType.name(), null)); - } - } - else { - reply = JavacProtoUtil.toMessage(sessionId, JavacProtoUtil.createFailure("Unsupported message: " + messageType.name(), null)); - } - } - finally { - if (reply != null) { - context.channel().writeAndFlush(reply); - } - } - } - } - - @ChannelHandler.Sharable - private static final class ChannelRegistrar extends ChannelInboundHandlerAdapter { - private final ChannelGroup openChannels = new DefaultChannelGroup(ImmediateEventExecutor.INSTANCE); - - public boolean isEmpty() { - return openChannels.isEmpty(); - } - - public void add(@NotNull Channel serverChannel) { - assert serverChannel instanceof ServerChannel; - openChannels.add(serverChannel); - } - - @Override - public void channelActive(ChannelHandlerContext context) throws Exception { - // we don't need to remove channel on close - ChannelGroup do it - openChannels.add(context.channel()); - - super.channelActive(context); - } - - public ChannelGroupFuture close() { - EventLoopGroup eventLoopGroup = null; - for (Channel channel : openChannels) { - if (channel instanceof ServerChannel) { - eventLoopGroup = channel.eventLoop().parent(); - break; - } - } - - ChannelGroupFuture future; - try { - future = openChannels.close(); - } - finally { - assert eventLoopGroup != null; - eventLoopGroup.shutdownGracefully(); - } - return future; - } - } - - private static class CancelHandler implements CanceledStatus { - private volatile boolean myIsCanceled = false; - - private CancelHandler() { - } - - public void cancel() { - myIsCanceled = true; - } - - @Override - public boolean isCanceled() { - return myIsCanceled; - } - } -} \ No newline at end of file diff --git a/jps/jps-builders/src/org/jetbrains/jps/javac/JavacServerBootstrap.java b/jps/jps-builders/src/org/jetbrains/jps/javac/JavacServerBootstrap.java index bb66e9c6b09f..ecf53395eb53 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/javac/JavacServerBootstrap.java +++ b/jps/jps-builders/src/org/jetbrains/jps/javac/JavacServerBootstrap.java @@ -20,11 +20,9 @@ import com.intellij.execution.process.ProcessAdapter; import com.intellij.execution.process.ProcessEvent; import com.intellij.execution.process.ProcessOutputTypes; import com.intellij.openapi.util.Key; -import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.util.concurrency.Semaphore; import org.jetbrains.jps.builders.java.JavaCompilingTool; import org.jetbrains.jps.cmdline.ClasspathBootstrap; import org.jetbrains.jps.service.SharedThreadPool; @@ -32,6 +30,7 @@ import org.jetbrains.jps.service.SharedThreadPool; import java.io.File; import java.util.ArrayList; import java.util.List; +import java.util.UUID; import java.util.concurrent.Future; /** @@ -40,15 +39,15 @@ import java.util.concurrent.Future; */ public class JavacServerBootstrap { - public static BaseOSProcessHandler launchJavacServer(String sdkHomePath, - int heapSize, - int port, - File workingDir, - List vmOptions, - JavaCompilingTool compilingTool) throws Exception { + public static ExternalJavacProcessHandler launchExternalJavacProcess(UUID uuid, String sdkHomePath, + int heapSize, + int port, + File workingDir, + List vmOptions, + JavaCompilingTool compilingTool) throws Exception { final List cmdLine = new ArrayList(); appendParam(cmdLine, getVMExecutablePath(sdkHomePath)); - appendParam(cmdLine, "-XX:MaxPermSize=150m"); + //appendParam(cmdLine, "-XX:MaxPermSize=150m"); //appendParam(cmdLine, "-XX:ReservedCodeCacheSize=64m"); appendParam(cmdLine, "-Djava.awt.headless=true"); final int xms = heapSize / 2; @@ -59,7 +58,7 @@ public class JavacServerBootstrap { // debugging //appendParam(cmdLine, "-XX:+HeapDumpOnOutOfMemoryError"); - //appendParam(cmdLine, "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5009"); + //appendParam(cmdLine, "-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5009"); // javac's VM should use the same default locale that IDEA uses in order for javac to print messages in 'correct' language final String encoding = System.getProperty("file.encoding"); @@ -83,18 +82,20 @@ public class JavacServerBootstrap { appendParam(cmdLine, "-Duser.region=" + region); } - appendParam(cmdLine, "-D" + JavacServer.JPS_JAVA_COMPILING_TOOL_PROPERTY + "=" + compilingTool.getId()); + appendParam(cmdLine, "-D" + ExternalJavacProcess.JPS_JAVA_COMPILING_TOOL_PROPERTY + "=" + compilingTool.getId()); // this will disable standard extensions to ensure javac is loaded from the right tools.jar appendParam(cmdLine, "-Djava.ext.dirs="); - + + appendParam(cmdLine, "-Dlog4j.defaultInitOverride=true"); + for (String option : vmOptions) { appendParam(cmdLine, option); } appendParam(cmdLine, "-classpath"); - final List cp = ClasspathBootstrap.getJavacServerClasspath(sdkHomePath, compilingTool); + final List cp = ClasspathBootstrap.getExternalJavacProcessClasspath(sdkHomePath, compilingTool); final StringBuilder classpath = new StringBuilder(); for (File file : cp) { if (classpath.length() > 0) { @@ -104,7 +105,9 @@ public class JavacServerBootstrap { } appendParam(cmdLine, classpath.toString()); - appendParam(cmdLine, org.jetbrains.jps.javac.JavacServer.class.getName()); + appendParam(cmdLine, org.jetbrains.jps.javac.ExternalJavacProcess.class.getName()); + appendParam(cmdLine, uuid.toString()); + appendParam(cmdLine, "127.0.0.1"); appendParam(cmdLine, Integer.toString(port)); workingDir.mkdirs(); @@ -115,68 +118,31 @@ public class JavacServerBootstrap { builder.directory(workingDir); final Process process = builder.start(); - final BaseOSProcessHandler processHandler = new BaseOSProcessHandler(process, null, null) { + final ExternalJavacProcessHandler processHandler = new ExternalJavacProcessHandler(process); + processHandler.addProcessListener(new ProcessAdapter() { @Override - protected Future executeOnPooledThread(Runnable task) { - return SharedThreadPool.getInstance().executeOnPooledThread(task); + public void processTerminated(ProcessEvent event) { + processHandler.setExitCode(event.getExitCode()); } - }; - configureProcessHandler(processHandler); - - return processHandler; - } - private static void configureProcessHandler(final BaseOSProcessHandler processHandler) throws Exception { - processHandler.addProcessListener(new ProcessAdapter() { public void onTextAvailable(ProcessEvent event, Key outputType) { final String text = event.getText(); if (!StringUtil.isEmptyOrSpaces(text)) { if (outputType == ProcessOutputTypes.STDOUT) { - System.out.print("JAVAC_SERVER: " + text); - } - else if (outputType == ProcessOutputTypes.STDERR){ - System.err.print("JAVAC_SERVER: " + text); + System.out.print("JAVAC_PROCESS: " + text); } - } - } - }); - final Semaphore semaphore = new Semaphore(); - semaphore.down(); - final Ref serverStartMessage = new Ref(null); - processHandler.addProcessListener(new ProcessAdapter() { - public void processTerminated(ProcessEvent event) { - try { - processHandler.removeProcessListener(this); - } - finally { - semaphore.up(); - } - } - - public void onTextAvailable(ProcessEvent event, Key outputType) { - if (outputType == ProcessOutputTypes.STDERR) { - final String text = event.getText(); - if (text != null && (text.contains(JavacServer.SERVER_SUCCESS_START_MESSAGE) || text.contains(JavacServer.SERVER_ERROR_START_MESSAGE))) { - try { - processHandler.removeProcessListener(this); - serverStartMessage.set(text); - } - finally { - semaphore.up(); - } + else if (outputType == ProcessOutputTypes.STDERR) { + System.err.print("JAVAC_PROCESS: " + text); } } } }); processHandler.startNotify(); - semaphore.waitFor(); - - final String startupMsg = serverStartMessage.get(); - if (startupMsg == null || !startupMsg.contains(JavacServer.SERVER_SUCCESS_START_MESSAGE)) { - throw new Exception("Server startup failed: " + startupMsg); - } + return processHandler; } + + private static void appendParam(List cmdLine, String param) { if (SystemInfo.isWindows) { if (param.contains("\"")) { @@ -192,4 +158,25 @@ public class JavacServerBootstrap { public static String getVMExecutablePath(String sdkHome) { return sdkHome + "/bin/java"; } + + public static class ExternalJavacProcessHandler extends BaseOSProcessHandler { + private volatile int myExitCode; + + ExternalJavacProcessHandler(Process process) { + super(process, null, null); + } + + @Override + protected Future executeOnPooledThread(Runnable task) { + return SharedThreadPool.getInstance().executeOnPooledThread(task); + } + + void setExitCode(int exitCode) { + myExitCode = exitCode; + } + + public int getExitCode() { + return myExitCode; + } + } } diff --git a/jps/jps-builders/src/org/jetbrains/jps/javac/JavacServerClient.java b/jps/jps-builders/src/org/jetbrains/jps/javac/JavacServerClient.java deleted file mode 100644 index 8085068b56e6..000000000000 --- a/jps/jps-builders/src/org/jetbrains/jps/javac/JavacServerClient.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2000-2012 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.jps.javac; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.jps.api.RequestFuture; -import org.jetbrains.jps.client.SimpleProtobufClient; -import org.jetbrains.jps.client.UUIDGetter; -import org.jetbrains.jps.service.SharedThreadPool; - -import java.io.File; -import java.util.*; - -/** - * @author Eugene Zhuravlev - * Date: 1/22/12 - */ -public class JavacServerClient extends SimpleProtobufClient{ - - public JavacServerClient() { - super(JavacRemoteProto.Message.getDefaultInstance(), SharedThreadPool.getInstance(), new UUIDGetter() { - @Override - @NotNull - public UUID getSessionUUID(@NotNull JavacRemoteProto.Message message) { - final JavacRemoteProto.Message.UUID uuid = message.getSessionId(); - return new UUID(uuid.getMostSigBits(), uuid.getLeastSigBits()); - } - }); - } - - public RequestFuture sendCompileRequest(List options, Collection files, Collection classpath, Collection platformCp, Collection sourcePath, Map> outs, DiagnosticOutputConsumer diagnosticSink, OutputFileConsumer outputSink) { - final JavacServerResponseHandler rh = new JavacServerResponseHandler(diagnosticSink, outputSink, getEncodingName(options)); - final JavacRemoteProto.Message.Request request = JavacProtoUtil.createCompilationRequest(options, files, classpath, platformCp, sourcePath, outs); - return sendRequest(request, rh, new RequestFuture.CancelAction() { - @Override - public void cancel(RequestFuture javacServerResponseHandlerRequestFuture) throws Exception { - sendRequest(JavacProtoUtil.createCancelRequest(), null, null); - } - }); - } - - public RequestFuture sendShutdownRequest() { - return sendRequest(JavacProtoUtil.createShutdownRequest(), null, null); - } - - private RequestFuture sendRequest(final JavacRemoteProto.Message.Request request, final JavacServerResponseHandler responseHandler, final RequestFuture.CancelAction cancelAction) { - final UUID requestId = UUID.randomUUID(); - return sendMessage(requestId, JavacProtoUtil.toMessage(requestId, request), responseHandler, cancelAction); - } - - @Nullable - private static String getEncodingName(List options) { - boolean found = false; - for (String option : options) { - if (found) { - return option; - } - if ("-encoding".equalsIgnoreCase(option)) { - found = true; - } - } - return null; - } -} diff --git a/jps/jps-builders/src/org/jetbrains/jps/javac/JavacServerResponseHandler.java b/jps/jps-builders/src/org/jetbrains/jps/javac/JavacServerResponseHandler.java deleted file mode 100644 index 688bf0596ac4..000000000000 --- a/jps/jps-builders/src/org/jetbrains/jps/javac/JavacServerResponseHandler.java +++ /dev/null @@ -1,231 +0,0 @@ -/* - * Copyright 2000-2012 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.jps.javac; - -import com.google.protobuf.ByteString; -import com.google.protobuf.MessageLite; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.jps.client.ProtobufResponseHandler; -import org.jetbrains.jps.incremental.BinaryContent; - -import javax.tools.Diagnostic; -import javax.tools.JavaFileObject; -import java.io.File; -import java.net.URI; -import java.util.Collection; -import java.util.Locale; - -/** - * @author Eugene Zhuravlev - * Date: 1/22/12 - */ -public class JavacServerResponseHandler implements ProtobufResponseHandler{ - private final DiagnosticOutputConsumer myDiagnosticSink; - private final OutputFileConsumer myOutputSink; - @Nullable - private final String myEncodingName; - private volatile boolean myTerminatedSuccessfully; - - public JavacServerResponseHandler(DiagnosticOutputConsumer diagnosticSink, OutputFileConsumer outputSink, @Nullable final String encodingName) { - myDiagnosticSink = diagnosticSink; - myOutputSink = outputSink; - myEncodingName = encodingName; - } - - public boolean handleMessage(MessageLite message) throws Exception { - final JavacRemoteProto.Message msg = (JavacRemoteProto.Message)message; - final JavacRemoteProto.Message.Type messageType = msg.getMessageType(); - - if (messageType == JavacRemoteProto.Message.Type.RESPONSE) { - final JavacRemoteProto.Message.Response response = msg.getResponse(); - final JavacRemoteProto.Message.Response.Type responseType = response.getResponseType(); - - if (responseType == JavacRemoteProto.Message.Response.Type.BUILD_MESSAGE) { - final JavacRemoteProto.Message.Response.CompileMessage compileMessage = response.getCompileMessage(); - final JavacRemoteProto.Message.Response.CompileMessage.Kind messageKind = compileMessage.getKind(); - - if (messageKind == JavacRemoteProto.Message.Response.CompileMessage.Kind.STD_OUT) { - if (compileMessage.hasText()) { - myDiagnosticSink.outputLineAvailable(compileMessage.getText()); - } - } - else { - final String sourceUri = compileMessage.hasSourceUri()? compileMessage.getSourceUri() : null; - final JavaFileObject srcFileObject = sourceUri != null? new DummyJavaFileObject(URI.create(sourceUri)) : null; - myDiagnosticSink.report(new DummyDiagnostic(convertKind(messageKind), srcFileObject, compileMessage)); - } - - return false; - } - - if (responseType == JavacRemoteProto.Message.Response.Type.OUTPUT_OBJECT) { - final JavacRemoteProto.Message.Response.OutputObject outputObject = response.getOutputObject(); - final JavacRemoteProto.Message.Response.OutputObject.Kind kind = outputObject.getKind(); - - final String outputRoot = outputObject.hasOutputRoot()? outputObject.getOutputRoot() : null; - final File outputRootFile = outputRoot != null? new File(outputRoot) : null; - - final BinaryContent fileObjectContent; - final ByteString content = outputObject.hasContent()? outputObject.getContent() : null; - if (content != null) { - final byte[] bytes = content.toByteArray(); - fileObjectContent = new BinaryContent(bytes, 0, bytes.length); - } - else { - fileObjectContent = null; - } - - final String sourceUri = outputObject.hasSourceUri()? outputObject.getSourceUri() : null; - final URI srcUri = sourceUri != null? URI.create(sourceUri) : null; - final OutputFileObject fileObject = new OutputFileObject( - null, - outputRootFile, - outputObject.hasRelativePath()? outputObject.getRelativePath() : null, - new File(outputObject.getFilePath()), - convertKind(kind), - outputObject.hasClassName()? outputObject.getClassName() : null, - srcUri, - myEncodingName, fileObjectContent - ); - - myOutputSink.save(fileObject); - return false; - } - - if (responseType == JavacRemoteProto.Message.Response.Type.SRC_FILE_LOADED) { - final JavacRemoteProto.Message.Response.OutputObject outputObject = response.getOutputObject(); - final File file = new File(outputObject.getFilePath()); - myDiagnosticSink.javaFileLoaded(file); - return false; - } - - if (responseType == JavacRemoteProto.Message.Response.Type.CLASS_DATA) { - final JavacRemoteProto.Message.Response.ClassData data = response.getClassData(); - final String className = data.getClassName(); - final Collection imports = data.getImportStatementList(); - final Collection staticImports = data.getStaticImportList(); - myDiagnosticSink.registerImports(className, imports, staticImports); - return false; - } - - if (responseType == JavacRemoteProto.Message.Response.Type.BUILD_COMPLETED) { - if (response.hasCompletionStatus()) { - myTerminatedSuccessfully = response.getCompletionStatus(); - } - return true; - } - - if (responseType == JavacRemoteProto.Message.Response.Type.REQUEST_ACK) { - return true; - } - - throw new Exception("Unsupported response type: " + responseType.name()); - } - - if (messageType == JavacRemoteProto.Message.Type.FAILURE) { - final JavacRemoteProto.Message.Failure failure = msg.getFailure(); - final StringBuilder buf = new StringBuilder(); - if (failure.hasDescription()) { - buf.append(failure.getDescription()); - } - if (failure.hasStacktrace()) { - if (buf.length() > 0) { - buf.append("\n"); - } - buf.append(failure.getStacktrace()); - } - myDiagnosticSink.report(new PlainMessageDiagnostic(Diagnostic.Kind.ERROR, buf.toString())); - return true; - } - - throw new Exception("Unsupported message type: " + messageType.name()); - } - - public boolean isTerminatedSuccessfully() { - return myTerminatedSuccessfully; - } - - private static Diagnostic.Kind convertKind(JavacRemoteProto.Message.Response.CompileMessage.Kind kind) { - switch (kind) { - case ERROR: return Diagnostic.Kind.ERROR; - case WARNING: return Diagnostic.Kind.WARNING; - case MANDATORY_WARNING: return Diagnostic.Kind.MANDATORY_WARNING; - case NOTE: return Diagnostic.Kind.NOTE; - default : return Diagnostic.Kind.OTHER; - } - } - - private static OutputFileObject.Kind convertKind(JavacRemoteProto.Message.Response.OutputObject.Kind kind) { - switch (kind) { - case CLASS: return JavaFileObject.Kind.CLASS; - case HTML: return JavaFileObject.Kind.HTML; - case SOURCE: return JavaFileObject.Kind.SOURCE; - default : return JavaFileObject.Kind.OTHER; - } - } - - public void sessionTerminated() { - } - - private static class DummyDiagnostic implements Diagnostic { - - private final Kind myMessageKind; - private final JavaFileObject mySrcFileObject; - private final JavacRemoteProto.Message.Response.CompileMessage myCompileMessage; - - public DummyDiagnostic(final Kind messageKind, JavaFileObject srcFileObject, JavacRemoteProto.Message.Response.CompileMessage compileMessage) { - myMessageKind = messageKind; - mySrcFileObject = srcFileObject; - myCompileMessage = compileMessage; - } - - public Kind getKind() { - return myMessageKind; - } - - public JavaFileObject getSource() { - return mySrcFileObject; - } - - public long getPosition() { - return myCompileMessage.hasProblemLocationOffset()? myCompileMessage.getProblemLocationOffset() : -1; - } - - public long getStartPosition() { - return myCompileMessage.hasProblemBeginOffset()? myCompileMessage.getProblemBeginOffset() : -1; - } - - public long getEndPosition() { - return myCompileMessage.hasProblemEndOffset()? myCompileMessage.getProblemEndOffset() : -1; - } - - public long getLineNumber() { - return myCompileMessage.hasLine()? myCompileMessage.getLine() : -1; - } - - public long getColumnNumber() { - return myCompileMessage.hasColumn()? myCompileMessage.getColumn() : -1; - } - - public String getCode() { - return null; - } - - public String getMessage(Locale locale) { - return myCompileMessage.hasText()? myCompileMessage.getText() : null; - } - } -} diff --git a/jps/model-api/src/org/jetbrains/jps/model/java/LanguageLevel.java b/jps/model-api/src/org/jetbrains/jps/model/java/LanguageLevel.java index 689776eafda0..3d9b302d38cf 100644 --- a/jps/model-api/src/org/jetbrains/jps/model/java/LanguageLevel.java +++ b/jps/model-api/src/org/jetbrains/jps/model/java/LanguageLevel.java @@ -19,5 +19,5 @@ package org.jetbrains.jps.model.java; * @author nik */ public enum LanguageLevel { - JDK_1_3, JDK_1_4, JDK_1_5, JDK_1_6, JDK_1_7, JDK_1_8 + JDK_1_3, JDK_1_4, JDK_1_5, JDK_1_6, JDK_1_7, JDK_1_8, JDK_1_9 } diff --git a/lib/jsch-0.1.50.jar b/lib/jsch-0.1.50.jar deleted file mode 100644 index de6cd0cda26b..000000000000 Binary files a/lib/jsch-0.1.50.jar and /dev/null differ diff --git a/lib/jsch-0.1.51.jar b/lib/jsch-0.1.51.jar new file mode 100644 index 000000000000..70d13988ccec Binary files /dev/null and b/lib/jsch-0.1.51.jar differ diff --git a/lib/required_for_dist.txt b/lib/required_for_dist.txt index 094e94784836..997eb52a6fc6 100644 --- a/lib/required_for_dist.txt +++ b/lib/required_for_dist.txt @@ -30,7 +30,7 @@ jgoodies-looks-2.4.2.jar jh.jar jna-utils.jar jna.jar -jsch-0.1.50.jar +jsch-0.1.51.jar jsch.agentproxy.connector-factory.jar jsch.agentproxy.core.jar jsch.agentproxy.pageant.jar @@ -63,7 +63,7 @@ snappy-in-java-0.3.jar swingx-core-1.6.2.jar trove4j.jar velocity.jar -winp-1.17-patched.jar +winp-1.21-patched.jar xbean.jar xerces.jar xmlrpc-2.0.jar diff --git a/lib/src/ecjsrc-4.3.2.jar b/lib/src/ecjsrc-4.3.2.jar deleted file mode 100644 index 5d54db8bb843..000000000000 Binary files a/lib/src/ecjsrc-4.3.2.jar and /dev/null differ diff --git a/lib/src/jsch-0.1.50.zip b/lib/src/jsch-0.1.50.zip deleted file mode 100644 index a90c40d2f272..000000000000 Binary files a/lib/src/jsch-0.1.50.zip and /dev/null differ diff --git a/lib/src/jsch-0.1.51.zip b/lib/src/jsch-0.1.51.zip new file mode 100644 index 000000000000..f4652c479d8c Binary files /dev/null and b/lib/src/jsch-0.1.51.zip differ diff --git a/lib/src/winp-1.17-patched-src.zip b/lib/src/winp-1.17-patched-src.zip deleted file mode 100644 index 02f7972bc776..000000000000 Binary files a/lib/src/winp-1.17-patched-src.zip and /dev/null differ diff --git a/lib/src/winp-1.21-patched.zip b/lib/src/winp-1.21-patched.zip new file mode 100644 index 000000000000..24a308323632 Binary files /dev/null and b/lib/src/winp-1.21-patched.zip differ diff --git a/lib/src/winp.patch b/lib/src/winp.patch new file mode 100644 index 000000000000..c3828306e8ee --- /dev/null +++ b/lib/src/winp.patch @@ -0,0 +1,26 @@ +diff -ur winp-winp-1.17/src/main/java/org/jvnet/winp/Native.java winp-1.17-patched/src/main/java/org/jvnet/winp/Native.java +--- winp-winp-1.17/src/main/java/org/jvnet/winp/Native.java 2013-01-03 09:26:13.000000000 +0400 ++++ winp-1.17-patched/src/main/java/org/jvnet/winp/Native.java 2013-02-25 16:23:24.000000000 +0400 +@@ -47,6 +47,7 @@ + private static final Logger LOGGER = Logger.getLogger(Native.class.getName()); + // system property holding the preferred folder for copying the dll file to. + private static final String DLL_TARGET = "winp.folder.preferred"; ++ private static final String UNPACK_DLL_TO_PARENT_DIR = "winp.unpack.dll.to.parent.dir"; + + static { + load(); +@@ -61,7 +62,12 @@ + final URL res = Native.class.getClassLoader().getResource(dllName+".dll"); + if(res!=null) { + String url = res.toExternalForm(); +- if(url.startsWith("jar:") || url.startsWith("wsjar:")) { ++ ++ //patched by JetBrains: do not try to unpack the dll file to the directory containing the jar file by default. ++ // It can fail because the process has no rights to write to that directory and also pollutes the project directories if the jar is used in development mode. ++ boolean unpackToParentDir = Boolean.parseBoolean(System.getProperty(UNPACK_DLL_TO_PARENT_DIR)); ++ ++ if(unpackToParentDir && (url.startsWith("jar:") || url.startsWith("wsjar:"))) { + int idx = url.lastIndexOf('!'); + String filePortion = url.substring(url.indexOf(':')+1,idx); + while(filePortion.startsWith("/")) + diff --git a/lib/winp-1.17-patched.jar b/lib/winp-1.17-patched.jar deleted file mode 100644 index 21a670e11aba..000000000000 Binary files a/lib/winp-1.17-patched.jar and /dev/null differ diff --git a/lib/winp-1.21-patched.jar b/lib/winp-1.21-patched.jar new file mode 100644 index 000000000000..76141dcd8911 Binary files /dev/null and b/lib/winp-1.21-patched.jar differ diff --git a/native/MacLauncher/Launcher.m b/native/MacLauncher/Launcher.m index 630cf741fdab..7e899f9da040 100644 --- a/native/MacLauncher/Launcher.m +++ b/native/MacLauncher/Launcher.m @@ -326,8 +326,8 @@ NSDictionary *parseProperties() { NSLog(@"Cannot chdir to working directory at %@", cwd); } } else { - NSLog(@"Info.plist is corrupted, Absent WorkingDirectory key."); - exit(-1); + NSString *dir = [[NSFileManager defaultManager] currentDirectoryPath]; + NSLog(@"WorkingDirectory is absent in Info.plist. Current Directory: %@", dir); } } diff --git a/native/WinLauncher/WinLauncher/WinLauncher.cpp b/native/WinLauncher/WinLauncher/WinLauncher.cpp index 3249a9a1f0de..681cb46ae6aa 100644 --- a/native/WinLauncher/WinLauncher/WinLauncher.cpp +++ b/native/WinLauncher/WinLauncher/WinLauncher.cpp @@ -194,11 +194,16 @@ bool LocateJVM() return result; } - std::string jreDir = GetAdjacentDir(need64BitJRE ? "jre64":"jre"); - if (FindValidJVM(jreDir.c_str()) && Is64BitJRE(jvmPath) == need64BitJRE) - { - return true; - } + + std::vector jrePaths; + if(need64BitJRE) jrePaths.push_back(GetAdjacentDir("jre64")); + jrePaths.push_back(GetAdjacentDir("jre")); + for(std::vector::iterator it = jrePaths.begin(); it != jrePaths.end(); ++it) { + if (FindValidJVM((*it).c_str()) && Is64BitJRE(jvmPath) == need64BitJRE) + { + return true; + } + } if (FindJVMInRegistry()) { diff --git a/platform/analysis-api/src/com/intellij/psi/search/scope/packageSet/FilePatternPackageSet.java b/platform/analysis-api/src/com/intellij/psi/search/scope/packageSet/FilePatternPackageSet.java index 4fed9ea06538..db0c71b9f99a 100644 --- a/platform/analysis-api/src/com/intellij/psi/search/scope/packageSet/FilePatternPackageSet.java +++ b/platform/analysis-api/src/com/intellij/psi/search/scope/packageSet/FilePatternPackageSet.java @@ -56,12 +56,12 @@ public class FilePatternPackageSet extends PatternBasedPackageSet { if (modulePattern.startsWith("group:")) { int idx = modulePattern.indexOf(':', 6); if (idx == -1) idx = modulePattern.length(); - myModuleGroupPattern = Pattern.compile(StringUtil.replace(modulePattern.substring(6, idx), "*", ".*")); + myModuleGroupPattern = Pattern.compile(StringUtil.escapeToRegexp(modulePattern.substring(6, idx))); if (idx < modulePattern.length() - 1) { - myModulePattern = Pattern.compile(StringUtil.replace(modulePattern.substring(idx + 1), "*", ".*")); + myModulePattern = Pattern.compile(StringUtil.escapeToRegexp(modulePattern.substring(idx + 1))); } } else { - myModulePattern = Pattern.compile(StringUtil.replace(modulePattern, "*", ".*")); + myModulePattern = Pattern.compile(StringUtil.escapeToRegexp(modulePattern)); } } myFilePattern = filePattern != null ? Pattern.compile(convertToRegexp(filePattern, '/')) : null; diff --git a/platform/analysis-impl/src/com/intellij/codeHighlighting/TextEditorHighlightingPass.java b/platform/analysis-impl/src/com/intellij/codeHighlighting/TextEditorHighlightingPass.java index 6e30e2d4ac4d..a4a2d3fda58d 100644 --- a/platform/analysis-impl/src/com/intellij/codeHighlighting/TextEditorHighlightingPass.java +++ b/platform/analysis-impl/src/com/intellij/codeHighlighting/TextEditorHighlightingPass.java @@ -141,6 +141,7 @@ public abstract class TextEditorHighlightingPass implements HighlightingPass { myStartingPredecessorIds = startingPredecessorIds; } + @Override @NonNls public String toString() { return getClass() + "; id=" + getId(); diff --git a/platform/analysis-impl/src/com/intellij/codeInsight/daemon/impl/UpdateHighlightersUtil.java b/platform/analysis-impl/src/com/intellij/codeInsight/daemon/impl/UpdateHighlightersUtil.java index bd63e19c8657..123263a566c8 100644 --- a/platform/analysis-impl/src/com/intellij/codeInsight/daemon/impl/UpdateHighlightersUtil.java +++ b/platform/analysis-impl/src/com/intellij/codeInsight/daemon/impl/UpdateHighlightersUtil.java @@ -49,7 +49,7 @@ import java.util.List; public class UpdateHighlightersUtil { private static final Comparator BY_START_OFFSET_NODUPS = new Comparator() { @Override - public int compare(HighlightInfo o1, HighlightInfo o2) { + public int compare(@NotNull HighlightInfo o1, @NotNull HighlightInfo o2) { int d = o1.getActualStartOffset() - o2.getActualStartOffset(); if (d != 0) return d; d = o1.getActualEndOffset() - o2.getActualEndOffset(); @@ -212,7 +212,7 @@ public class UpdateHighlightersUtil { if (!atStart) return true; if (!info.isFromInjection() && info.getEndOffset() < document.getTextLength() && (info.getEndOffset() <= startOffset || info.getStartOffset()>=endOffset)) return true; // injections are oblivious to restricting range - if (info.isFileLevelAnnotation() && psiFile.getViewProvider().isPhysical()) { + if (info.isFileLevelAnnotation()) { codeAnalyzer.addFileLevelHighlight(project, group, info, psiFile); changed[0] = true; return true; diff --git a/platform/analysis-impl/src/com/intellij/codeInspection/InspectionEngine.java b/platform/analysis-impl/src/com/intellij/codeInspection/InspectionEngine.java index 713b545c6247..d8f019a7327e 100644 --- a/platform/analysis-impl/src/com/intellij/codeInspection/InspectionEngine.java +++ b/platform/analysis-impl/src/com/intellij/codeInspection/InspectionEngine.java @@ -33,6 +33,7 @@ import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.DumbService; import com.intellij.openapi.util.Condition; +import com.intellij.openapi.util.Conditions; import com.intellij.openapi.util.ProperTextRange; import com.intellij.openapi.util.TextRange; import com.intellij.psi.*; @@ -113,7 +114,7 @@ public class InspectionEngine { TextRange range = file.getTextRange(); final LocalInspectionToolSession session = new LocalInspectionToolSession(file, range.getStartOffset(), range.getEndOffset()); Divider.divideInsideAndOutside(file, range.getStartOffset(), range.getEndOffset(), range, elements, new ArrayList(), - Collections.emptyList(), Collections.emptyList(), true, Condition.TRUE); + Collections.emptyList(), Collections.emptyList(), true, Conditions.alwaysTrue()); MultiMap toolToLanguages = getToolsForElements(toolWrappers, DumbService.isDumb(file.getProject()), elements, Collections.emptyList()); List>> entries = new ArrayList>>(toolToLanguages.entrySet()); diff --git a/platform/analysis-impl/src/com/intellij/codeInspection/ex/InspectionProfileImpl.java b/platform/analysis-impl/src/com/intellij/codeInspection/ex/InspectionProfileImpl.java index 0a1861e8000e..346a4446090a 100644 --- a/platform/analysis-impl/src/com/intellij/codeInspection/ex/InspectionProfileImpl.java +++ b/platform/analysis-impl/src/com/intellij/codeInspection/ex/InspectionProfileImpl.java @@ -772,18 +772,17 @@ public class InspectionProfileImpl extends ProfileEx implements ModifiableModel, return InspectionProfileImplHolder.DEFAULT_PROFILE; } - public Document saveToDocument() throws WriteExternalException { + public Element saveToDocument() throws WriteExternalException { if (isLocal()) { Element root = new Element(ROOT_ELEMENT_TAG); root.setAttribute(PROFILE_NAME_TAG, myName); writeExternal(root); //myVisibleTreeState.writeExternal(root); - return new Document(root); + return root; } else { return null; } - } @Override @@ -886,13 +885,13 @@ public class InspectionProfileImpl extends ProfileEx implements ModifiableModel, getTools(toolId, project).removeScope(scopeIdx); } - public void removeScope(@NotNull String toolId, @NotNull NamedScope scope, Project project) { - getTools(toolId, project).removeScope(scope); + public void removeScope(@NotNull String toolId, @NotNull String scopeName, Project project) { + getTools(toolId, project).removeScope(scopeName); } - public void removeScopes(@NotNull List toolIds, @NotNull NamedScope scope, Project project) { + public void removeScopes(@NotNull List toolIds, @NotNull String scopeName, Project project) { for (final String toolId : toolIds) { - removeScope(toolId, scope, project); + removeScope(toolId, scopeName, project); } } diff --git a/platform/analysis-impl/src/com/intellij/codeInspection/ex/ToolsImpl.java b/platform/analysis-impl/src/com/intellij/codeInspection/ex/ToolsImpl.java index 267c0ee67fc3..caeb02a2316f 100644 --- a/platform/analysis-impl/src/com/intellij/codeInspection/ex/ToolsImpl.java +++ b/platform/analysis-impl/src/com/intellij/codeInspection/ex/ToolsImpl.java @@ -267,25 +267,26 @@ public class ToolsImpl implements Tools { public void removeScope(int scopeIdx) { if (myTools != null && scopeIdx >= 0 && myTools.size() > scopeIdx) { myTools.remove(scopeIdx); - if (myTools.isEmpty()) { - myTools = null; - setEnabled(myDefaultState.isEnabled()); - } + checkToolsIsEmpty(); } } - public void removeScope(final NamedScope scope) { + public void removeScope(final @NotNull String scopeName) { if (myTools != null) { - for (final ScopeToolState tool : myTools) { - if (Comparing.equal(tool.getScopeName(), scope.getName())) { + for (ScopeToolState tool : myTools) { + if (scopeName.equals(tool.getScopeName())) { myTools.remove(tool); break; } } - if (myTools.isEmpty()) { - myTools = null; - setEnabled(myDefaultState.isEnabled()); - } + checkToolsIsEmpty(); + } + } + + private void checkToolsIsEmpty() { + if (myTools.isEmpty()) { + myTools = null; + setEnabled(myDefaultState.isEnabled()); } } diff --git a/platform/core-api/src/com/intellij/concurrency/AsyncUtil.java b/platform/core-api/src/com/intellij/concurrency/AsyncUtil.java index 93c7573c0381..72c0a85aac72 100644 --- a/platform/core-api/src/com/intellij/concurrency/AsyncUtil.java +++ b/platform/core-api/src/com/intellij/concurrency/AsyncUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,12 +18,17 @@ package com.intellij.concurrency; import org.jetbrains.annotations.NotNull; import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; /** * Author: dmitrylomov */ public class AsyncUtil { + private static final AsyncFuture TRUE = createConst(true); + private static final AsyncFuture FALSE = createConst(false); + public static V get(@NotNull Future result) { try { return result.get(); @@ -41,4 +46,43 @@ public class AsyncUtil { } } } + + private static AsyncFuture createConst(final boolean result) { + return new AsyncFuture() { + @Override + public void addConsumer(@NotNull Executor executor, @NotNull ResultConsumer consumer) { + consumer.onSuccess(result); + } + + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + return false; + } + + @Override + public boolean isCancelled() { + return false; + } + + @Override + public boolean isDone() { + return true; + } + + @Override + public Boolean get() { + return result; + } + + @Override + public Boolean get(long timeout, @NotNull TimeUnit unit) { + return result; + } + }; + } + + @NotNull + public static AsyncFuture wrapBoolean(boolean result) { + return result ? TRUE : FALSE; + } } diff --git a/platform/core-api/src/com/intellij/concurrency/DefaultResultConsumer.java b/platform/core-api/src/com/intellij/concurrency/DefaultResultConsumer.java index 7230cd469e13..0e54993fe84b 100644 --- a/platform/core-api/src/com/intellij/concurrency/DefaultResultConsumer.java +++ b/platform/core-api/src/com/intellij/concurrency/DefaultResultConsumer.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,13 +15,15 @@ */ package com.intellij.concurrency; +import org.jetbrains.annotations.NotNull; + /** * Author: dmitrylomov */ public class DefaultResultConsumer implements ResultConsumer { private final AsyncFutureResult myResult; - public DefaultResultConsumer(AsyncFutureResult result) { + public DefaultResultConsumer(@NotNull AsyncFutureResult result) { myResult = result; } @@ -31,7 +33,7 @@ public class DefaultResultConsumer implements ResultConsumer { } @Override - public void onFailure(Throwable t) { + public void onFailure(@NotNull Throwable t) { myResult.setException(t); } } diff --git a/platform/core-api/src/com/intellij/concurrency/DoWhile.java b/platform/core-api/src/com/intellij/concurrency/DoWhile.java deleted file mode 100644 index b77e3258c984..000000000000 --- a/platform/core-api/src/com/intellij/concurrency/DoWhile.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2000-2013 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.concurrency; - -import org.jetbrains.annotations.NotNull; - -/** - * Author: dmitrylomov - */ -public abstract class DoWhile { - private final AsyncFutureResult myResult = AsyncFutureFactory.getInstance().createAsyncFutureResult(); - private final SameThreadExecutorWithTrampoline myExecutor = new SameThreadExecutorWithTrampoline(); - - @NotNull - public AsyncFutureResult getResult() { - body().addConsumer(myExecutor, new MyConsumer()); - return myResult; - } - - @NotNull - protected abstract AsyncFuture body(); - protected abstract boolean condition(); - - private class MyConsumer extends DefaultResultConsumer { - public MyConsumer() { - super(DoWhile.this.myResult); - } - - @Override - public void onSuccess(Boolean value) { - if (!value.booleanValue()) { - myResult.set(false); - } - else { - if(!condition()) { - myResult.set(true); - } - else { - body().addConsumer(myExecutor, this); - } - } - } - } - - -} diff --git a/platform/core-api/src/com/intellij/concurrency/Iterate.java b/platform/core-api/src/com/intellij/concurrency/Iterate.java deleted file mode 100644 index e158999f4da5..000000000000 --- a/platform/core-api/src/com/intellij/concurrency/Iterate.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.intellij.concurrency; - -import org.jetbrains.annotations.NotNull; - -import java.util.Iterator; - -/** - * Author: dmitrylomov - */ -public abstract class Iterate extends DoWhile { - private final Iterator myIterator; - private boolean myIsDone; - - public Iterate(@NotNull Iterable iterable) { - myIterator = iterable.iterator(); - myIsDone = false; - } - - @NotNull - @Override - protected final AsyncFuture body() { - if (!myIterator.hasNext()) { - myIsDone = true; - return AsyncFutureFactory.wrap(true); - } - return process(myIterator.next()); - } - - @NotNull - protected abstract AsyncFuture process(T t); - - @Override - protected boolean condition() { - return !myIsDone; - } -} diff --git a/platform/core-api/src/com/intellij/concurrency/ResultConsumer.java b/platform/core-api/src/com/intellij/concurrency/ResultConsumer.java index 80618f0b9c79..409d5a9439a4 100644 --- a/platform/core-api/src/com/intellij/concurrency/ResultConsumer.java +++ b/platform/core-api/src/com/intellij/concurrency/ResultConsumer.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,10 +15,12 @@ */ package com.intellij.concurrency; +import org.jetbrains.annotations.NotNull; + /** * Author: dmitrylomov */ public interface ResultConsumer { void onSuccess(V value); - void onFailure(Throwable t); + void onFailure(@NotNull Throwable t); } diff --git a/platform/core-api/src/com/intellij/lang/folding/CustomFoldingBuilder.java b/platform/core-api/src/com/intellij/lang/folding/CustomFoldingBuilder.java index f43c1c9b55ae..ab03627d7d6a 100644 --- a/platform/core-api/src/com/intellij/lang/folding/CustomFoldingBuilder.java +++ b/platform/core-api/src/com/intellij/lang/folding/CustomFoldingBuilder.java @@ -45,7 +45,9 @@ public abstract class CustomFoldingBuilder extends FoldingBuilderEx implements P if (CustomFoldingProvider.getAllProviders().length > 0) { myDefaultProvider = null; ASTNode rootNode = root.getNode(); - addCustomFoldingRegionsRecursively(new FoldingStack(rootNode), rootNode, descriptors, 0); + if (rootNode != null) { + addCustomFoldingRegionsRecursively(new FoldingStack(rootNode), rootNode, descriptors, 0); + } } buildLanguageFoldRegions(descriptors, root, document, quick); return descriptors.toArray(new FoldingDescriptor[descriptors.size()]); diff --git a/platform/core-api/src/com/intellij/openapi/components/RoamingType.java b/platform/core-api/src/com/intellij/openapi/components/RoamingType.java index 0cbf78053953..ba4b6d8b36e6 100644 --- a/platform/core-api/src/com/intellij/openapi/components/RoamingType.java +++ b/platform/core-api/src/com/intellij/openapi/components/RoamingType.java @@ -16,5 +16,12 @@ package com.intellij.openapi.components; public enum RoamingType { - DISABLED, PER_PLATFORM, GLOBAL, PER_USER + DISABLED, + PER_PLATFORM, + @Deprecated + /** + * Use {@link #PER_USER} instead + */ + GLOBAL, + PER_USER } diff --git a/platform/core-api/src/com/intellij/openapi/options/SchemeProcessor.java b/platform/core-api/src/com/intellij/openapi/options/SchemeProcessor.java index 94ec194b9f89..5acdbe7cacf6 100644 --- a/platform/core-api/src/com/intellij/openapi/options/SchemeProcessor.java +++ b/platform/core-api/src/com/intellij/openapi/options/SchemeProcessor.java @@ -19,18 +19,22 @@ import com.intellij.openapi.util.InvalidDataException; import com.intellij.openapi.util.WriteExternalException; import org.jdom.Document; import org.jdom.JDOMException; +import org.jdom.Parent; import org.jetbrains.annotations.NotNull; import java.io.IOException; public interface SchemeProcessor { T readScheme(@NotNull Document schemeContent) throws InvalidDataException, IOException, JDOMException; - Document writeScheme(@NotNull T scheme) throws WriteExternalException; + + Parent writeScheme(@NotNull T scheme) throws WriteExternalException; boolean shouldBeSaved(@NotNull T scheme); + void initScheme(@NotNull T scheme); void onSchemeAdded(@NotNull T scheme); + void onSchemeDeleted(@NotNull T scheme); void onCurrentSchemeChanged(final Scheme oldCurrentScheme); diff --git a/platform/core-api/src/com/intellij/openapi/options/SchemesManagerFactory.java b/platform/core-api/src/com/intellij/openapi/options/SchemesManagerFactory.java index a0c0ee7dad62..abd37269985f 100644 --- a/platform/core-api/src/com/intellij/openapi/options/SchemesManagerFactory.java +++ b/platform/core-api/src/com/intellij/openapi/options/SchemesManagerFactory.java @@ -25,9 +25,8 @@ public abstract class SchemesManagerFactory { public static final ExtensionPointName SCHEME_OWNER = ExtensionPointName.create("com.intellij.schemeOwner"); public abstract SchemesManager createSchemesManager(@NotNull String fileSpec, - @NotNull SchemeProcessor processor, - @NotNull RoamingType roamingType); - + @NotNull SchemeProcessor processor, @NotNull RoamingType roamingType); + @NotNull public static SchemesManagerFactory getInstance() { return ServiceManager.getService(SchemesManagerFactory.class); } diff --git a/platform/core-api/src/com/intellij/openapi/progress/ProgressManager.java b/platform/core-api/src/com/intellij/openapi/progress/ProgressManager.java index 3f0601ba951b..377bcc520e27 100644 --- a/platform/core-api/src/com/intellij/openapi/progress/ProgressManager.java +++ b/platform/core-api/src/com/intellij/openapi/progress/ProgressManager.java @@ -34,6 +34,7 @@ public abstract class ProgressManager extends ProgressIndicatorProvider { private static final ProgressManager ourInstance = ServiceManager.getService(ProgressManager.class); } + @NotNull public static ProgressManager getInstance() { return ProgressManagerHolder.ourInstance; } diff --git a/platform/core-api/src/com/intellij/openapi/vfs/ex/http/HttpFileSystem.java b/platform/core-api/src/com/intellij/openapi/vfs/ex/http/HttpFileSystem.java index a42441fe49de..af6c670ab22a 100644 --- a/platform/core-api/src/com/intellij/openapi/vfs/ex/http/HttpFileSystem.java +++ b/platform/core-api/src/com/intellij/openapi/vfs/ex/http/HttpFileSystem.java @@ -23,13 +23,6 @@ import com.intellij.util.io.URLUtil; import org.jetbrains.annotations.NotNull; public abstract class HttpFileSystem extends DeprecatedVirtualFileSystem { - @Deprecated - @SuppressWarnings("UnusedDeclaration") - /** - * @deprecated use {@link com.intellij.util.io.URLUtil#HTTP_PROTOCOL} - */ - public static final String PROTOCOL = URLUtil.HTTP_PROTOCOL; - public static HttpFileSystem getInstance() { return (HttpFileSystem)VirtualFileManager.getInstance().getFileSystem(URLUtil.HTTP_PROTOCOL); } @@ -43,4 +36,4 @@ public abstract class HttpFileSystem extends DeprecatedVirtualFileSystem { public abstract void removeFileListener(@NotNull HttpVirtualFileListener listener); public abstract VirtualFile createChild(@NotNull VirtualFile parent, @NotNull String name, boolean isDirectory); -} \ No newline at end of file +} diff --git a/platform/core-api/src/com/intellij/psi/util/CachedValuesManager.java b/platform/core-api/src/com/intellij/psi/util/CachedValuesManager.java index a8a82bed411a..4c7c12d79906 100644 --- a/platform/core-api/src/com/intellij/psi/util/CachedValuesManager.java +++ b/platform/core-api/src/com/intellij/psi/util/CachedValuesManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -42,9 +42,12 @@ public abstract class CachedValuesManager { * @param trackValue if value tracking required. T should be trackable in this case. * @return new CachedValue instance. */ + @NotNull public abstract CachedValue createCachedValue(@NotNull CachedValueProvider provider, boolean trackValue); + @NotNull public abstract ParameterizedCachedValue createParameterizedCachedValue(@NotNull ParameterizedCachedValueProvider provider, boolean trackValue); + @NotNull public CachedValue createCachedValue(@NotNull CachedValueProvider provider) { return createCachedValue(provider, true); } @@ -54,7 +57,6 @@ public abstract class CachedValuesManager { @NotNull ParameterizedCachedValueProvider provider, boolean trackValue, P parameter) { - ParameterizedCachedValue value; if (dataHolder instanceof UserDataHolderEx) { @@ -100,6 +102,7 @@ public abstract class CachedValuesManager { } private final ConcurrentMap> keyForProvider = new ConcurrentHashMap>(); + @NotNull public Key> getKeyForClass(@NotNull Class providerClass) { String name = providerClass.getName(); assert name != null : providerClass + " doesn't have a name; can't be used for cache value provider"; diff --git a/platform/core-api/src/com/intellij/util/AbstractQuery.java b/platform/core-api/src/com/intellij/util/AbstractQuery.java index 1c29ef6437ac..219aae614140 100644 --- a/platform/core-api/src/com/intellij/util/AbstractQuery.java +++ b/platform/core-api/src/com/intellij/util/AbstractQuery.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,9 +16,7 @@ package com.intellij.util; import com.intellij.concurrency.AsyncFuture; -import com.intellij.concurrency.AsyncFutureFactory; -import com.intellij.concurrency.AsyncFutureResult; -import com.intellij.concurrency.FinallyFuture; +import com.intellij.concurrency.AsyncUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -84,26 +82,13 @@ public abstract class AbstractQuery implements Query { @NotNull @Override public AsyncFuture forEachAsync(@NotNull Processor consumer) { - assertNotProcessing(); - myIsProcessing = true; - return new FinallyFuture(processResultsAsync(consumer), new Runnable() { - @Override - public void run() { - myIsProcessing = false; - } - }); + return AsyncUtil.wrapBoolean(forEach(consumer)); } protected abstract boolean processResults(@NotNull Processor consumer); @NotNull protected AsyncFuture processResultsAsync(@NotNull Processor consumer) { - final AsyncFutureResult result = AsyncFutureFactory.getInstance().createAsyncFutureResult(); - try { - result.set(processResults(consumer)); - } catch (Throwable t) { - result.setException(t); - } - return result; + return AsyncUtil.wrapBoolean(processResults(consumer)); } } diff --git a/platform/core-api/src/com/intellij/util/ArrayQuery.java b/platform/core-api/src/com/intellij/util/ArrayQuery.java index abe2f1001fe0..1b7760146de6 100644 --- a/platform/core-api/src/com/intellij/util/ArrayQuery.java +++ b/platform/core-api/src/com/intellij/util/ArrayQuery.java @@ -17,8 +17,7 @@ package com.intellij.util; import com.intellij.concurrency.AsyncFuture; -import com.intellij.concurrency.AsyncFutureFactory; -import com.intellij.concurrency.AsyncFutureResult; +import com.intellij.concurrency.AsyncUtil; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; @@ -55,13 +54,7 @@ public class ArrayQuery implements Query { @NotNull @Override public AsyncFuture forEachAsync(@NotNull final Processor consumer) { - final AsyncFutureResult result = AsyncFutureFactory.getInstance().createAsyncFutureResult(); - try { - result.set(forEach(consumer)); - } catch (Throwable t){ - result.setException(t); - } - return result; + return AsyncUtil.wrapBoolean(forEach(consumer)); } diff --git a/platform/core-api/src/com/intellij/util/CollectionQuery.java b/platform/core-api/src/com/intellij/util/CollectionQuery.java index cd90ab55edd9..253182cef0c1 100644 --- a/platform/core-api/src/com/intellij/util/CollectionQuery.java +++ b/platform/core-api/src/com/intellij/util/CollectionQuery.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,8 +17,7 @@ package com.intellij.util; import com.intellij.concurrency.AsyncFuture; -import com.intellij.concurrency.AsyncFutureFactory; -import com.intellij.concurrency.AsyncFutureResult; +import com.intellij.concurrency.AsyncUtil; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; @@ -55,13 +54,7 @@ public class CollectionQuery implements Query { @NotNull @Override public AsyncFuture forEachAsync(@NotNull Processor consumer) { - AsyncFutureResult result = AsyncFutureFactory.getInstance().createAsyncFutureResult(); - try { - result.set(forEach(consumer)); - } catch (Throwable t) { - result.setException(t); - } - return result; + return AsyncUtil.wrapBoolean(forEach(consumer)); } @NotNull diff --git a/platform/core-api/src/com/intellij/util/EmptyQuery.java b/platform/core-api/src/com/intellij/util/EmptyQuery.java index 465100505450..18a32a5a94b4 100644 --- a/platform/core-api/src/com/intellij/util/EmptyQuery.java +++ b/platform/core-api/src/com/intellij/util/EmptyQuery.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,7 +16,7 @@ package com.intellij.util; import com.intellij.concurrency.AsyncFuture; -import com.intellij.concurrency.AsyncFutureFactory; +import com.intellij.concurrency.AsyncUtil; import org.jetbrains.annotations.NotNull; import java.util.Collection; @@ -48,7 +48,7 @@ public class EmptyQuery implements Query { @NotNull @Override public AsyncFuture forEachAsync(@NotNull Processor consumer) { - return AsyncFutureFactory.wrap(true); + return AsyncUtil.wrapBoolean(true); } @NotNull diff --git a/platform/core-api/src/com/intellij/util/MergeQuery.java b/platform/core-api/src/com/intellij/util/MergeQuery.java index 699659b93055..35d122639cd3 100644 --- a/platform/core-api/src/com/intellij/util/MergeQuery.java +++ b/platform/core-api/src/com/intellij/util/MergeQuery.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -66,13 +66,13 @@ public class MergeQuery implements Query{ fq.addConsumer(SameThreadExecutor.INSTANCE, new DefaultResultConsumer(result) { @Override public void onSuccess(Boolean value) { - if (!value.booleanValue()) { - result.set(false); - } - else { + if (value.booleanValue()) { final AsyncFuture fq2 = processSubQueryAsync(consumer, myQuery2); fq2.addConsumer(SameThreadExecutor.INSTANCE, new DefaultResultConsumer(result)); } + else { + result.set(false); + } } }); return result; @@ -85,12 +85,7 @@ public class MergeQuery implements Query{ } private AsyncFuture processSubQueryAsync(@NotNull final Processor consumer, @NotNull Query query1) { - return query1.forEachAsync(new Processor() { - @Override - public boolean process(final V t) { - return consumer.process(t); - } - }); + return query1.forEachAsync((Processor)consumer); } @NotNull diff --git a/platform/core-impl/src/com/intellij/core/CoreApplicationEnvironment.java b/platform/core-impl/src/com/intellij/core/CoreApplicationEnvironment.java index f82e7a80d5fb..cbb35b4f9812 100644 --- a/platform/core-impl/src/com/intellij/core/CoreApplicationEnvironment.java +++ b/platform/core-impl/src/com/intellij/core/CoreApplicationEnvironment.java @@ -36,10 +36,7 @@ import com.intellij.openapi.extensions.ExtensionsArea; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.fileTypes.*; import com.intellij.openapi.progress.*; -import com.intellij.openapi.util.ClassExtension; -import com.intellij.openapi.util.Disposer; -import com.intellij.openapi.util.KeyedExtensionCollector; -import com.intellij.openapi.util.StaticGetter; +import com.intellij.openapi.util.*; import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.openapi.vfs.VirtualFileSystem; import com.intellij.openapi.vfs.encoding.EncodingRegistry; @@ -175,15 +172,7 @@ public class CoreApplicationEnvironment { ProgressIndicator progress, boolean failFastOnAcquireReadAction, @NotNull Processor thingProcessor) { - final AsyncFutureResult asyncFutureResult = AsyncFutureFactory.getInstance().createAsyncFutureResult(); - try { - final boolean result = invokeConcurrentlyUnderProgress(things, progress, failFastOnAcquireReadAction, thingProcessor); - asyncFutureResult.set(result); - } - catch (Throwable t) { - asyncFutureResult.setException(t); - } - return asyncFutureResult; + return AsyncUtil.wrapBoolean(invokeConcurrentlyUnderProgress(things, progress, failFastOnAcquireReadAction, thingProcessor)); } @NotNull diff --git a/platform/core-impl/src/com/intellij/ide/plugins/IdeaPluginDescriptorImpl.java b/platform/core-impl/src/com/intellij/ide/plugins/IdeaPluginDescriptorImpl.java index 72d290e61e25..939ab753574b 100644 --- a/platform/core-impl/src/com/intellij/ide/plugins/IdeaPluginDescriptorImpl.java +++ b/platform/core-impl/src/com/intellij/ide/plugins/IdeaPluginDescriptorImpl.java @@ -53,17 +53,17 @@ import java.util.*; * @author mike */ public class IdeaPluginDescriptorImpl implements IdeaPluginDescriptor { - private static final Logger LOG = Logger.getInstance("#com.intellij.ide.plugins.PluginDescriptor"); - public static final IdeaPluginDescriptorImpl[] EMPTY_ARRAY = new IdeaPluginDescriptorImpl[0]; - private String myName; - private PluginId myId; + private static final Logger LOG = Logger.getInstance("#com.intellij.ide.plugins.PluginDescriptor"); + private static final StringInterner ourInterner = new WeakStringInterner(); private final NullableLazyValue myDescription = new NullableLazyValue() { @Override protected String compute() { return computeDescription(); } }; + private String myName; + private PluginId myId; private String myResourceBundleBaseName; private String myChangeNotes; private String myVersion; @@ -93,39 +93,62 @@ public class IdeaPluginDescriptorImpl implements IdeaPluginDescriptor { private boolean myUseIdeaClassLoader; private boolean myUseCoreClassLoader; private boolean myEnabled = true; - private String mySinceBuild; private String myUntilBuild; - private Boolean mySkipped; - private List myModules = null; public IdeaPluginDescriptorImpl(@NotNull File pluginPath) { myPath = pluginPath; } - IdeaPluginDescriptorImpl() { + @NotNull + public static String intern(@NotNull String s) { + return ourInterner.intern(s); } - public void setPath(@NotNull File path) { - myPath = path; + public static void internJDOMElement(@NotNull Element rootElement) { + JDOMUtil.internElement(rootElement, ourInterner); } - @Override - public File getPath() { - return myPath; + @Nullable + private static List copyElements(final Element[] elements) { + if (elements != null) { + List result = new ArrayList(); + for (Element extensionsRoot : elements) { + for (final Object o : extensionsRoot.getChildren()) { + Element element = (Element)o; + internJDOMElement(element); + result.add(element); + } + } + return result; + } + return null; } - private static final StringInterner ourInterner = new WeakStringInterner(); + @SuppressWarnings({"HardCodedStringLiteral"}) + private static String createDescriptionKey(final PluginId id) { + return "plugin." + id + ".description"; + } - @NotNull - public static String intern(@NotNull String s) { - return ourInterner.intern(s); + private static ComponentConfig[] mergeComponents(ComponentConfig[] first, ComponentConfig[] second) { + if (first == null) { + return second; + } + if (second == null) { + return first; + } + return ArrayUtil.mergeArrays(first, second); } - public static void internJDOMElement(@NotNull Element rootElement) { - JDOMUtil.internElement(rootElement, ourInterner); + @Override + public File getPath() { + return myPath; + } + + public void setPath(@NotNull File path) { + myPath = path; } public void readExternal(@NotNull Document document, @NotNull URL url) throws InvalidDataException, FileNotFoundException { @@ -243,7 +266,7 @@ public class IdeaPluginDescriptorImpl implements IdeaPluginDescriptor { myExtensions.putValue(ExtensionsAreaImpl.extractEPName(extension), extension); } } - + List extensionPoints = copyElements(pluginBean.extensionPoints); if (extensionPoints != null) { myExtensionsPoints = new MultiMap(); @@ -251,7 +274,7 @@ public class IdeaPluginDescriptorImpl implements IdeaPluginDescriptor { myExtensionsPoints.putValue(extensionPoint.getAttributeValue(ExtensionsAreaImpl.ATTRIBUTE_AREA), extensionPoint); } } - + myActionsElements = copyElements(pluginBean.actions); if (pluginBean.modules != null && !pluginBean.modules.isEmpty()) { @@ -259,27 +282,6 @@ public class IdeaPluginDescriptorImpl implements IdeaPluginDescriptor { } } - @Nullable - private static List copyElements(final Element[] elements) { - if (elements != null) { - List result = new ArrayList(); - for (Element extensionsRoot : elements) { - for (final Object o : extensionsRoot.getChildren()) { - Element element = (Element)o; - internJDOMElement(element); - result.add(element); - } - } - return result; - } - return null; - } - - @SuppressWarnings({"HardCodedStringLiteral"}) - private static String createDescriptionKey(final PluginId id) { - return "plugin." + id + ".description"; - } - void registerExtensionPoints(ExtensionsArea area) { if (myExtensionsPoints != null) { for (Element element : myExtensionsPoints.get(area.getAreaClass())) { @@ -317,7 +319,6 @@ public class IdeaPluginDescriptorImpl implements IdeaPluginDescriptor { return myDependencies; } - @Override @NotNull public PluginId[] getOptionalDependentPluginIds() { @@ -329,6 +330,11 @@ public class IdeaPluginDescriptorImpl implements IdeaPluginDescriptor { return myVendor; } + public void setVendor( final String val ) + { + myVendor = val; + } + @Override public String getVersion() { return myVersion; @@ -344,6 +350,17 @@ public class IdeaPluginDescriptorImpl implements IdeaPluginDescriptor { return myCategory; } + /* + This setter was explicitly defined to be able to set a category for a + descriptor outside its loading from the xml file. + Problem was that most commonly plugin authors do not publish the plugin's + category in its .xml file so to be consistent in plugins representation + (e.g. in the Plugins form) we have to set this value outside. + */ + public void setCategory( String category ){ + myCategory = category; + } + @SuppressWarnings("UnusedDeclaration") // Used in Upsource @Nullable public MultiMap getExtensionsPoints() { @@ -418,16 +435,31 @@ public class IdeaPluginDescriptorImpl implements IdeaPluginDescriptor { return myVendorEmail; } + public void setVendorEmail( final String val ) + { + myVendorEmail = val; + } + @Override public String getVendorUrl() { return myVendorUrl; } + public void setVendorUrl( final String val ) + { + myVendorUrl = val; + } + @Override public String getUrl() { return url; } + public void setUrl( final String val ) + { + url = val; + } + @NonNls public String toString() { return "PluginDescriptor[name='" + myName + "', classpath='" + myPath + "']"; @@ -469,17 +501,6 @@ public class IdeaPluginDescriptorImpl implements IdeaPluginDescriptor { return myId; } - /* - This setter was explicitly defined to be able to set a category for a - descriptor outside its loading from the xml file. - Problem was that most commonly plugin authors do not publish the plugin's - category in its .xml file so to be consistent in plugins representation - (e.g. in the Plugins form) we have to set this value outside. - */ - public void setCategory( String category ){ - myCategory = category; - } - /* This setter was explicitly defined to be able to set downloads count for a descriptor outside its loading from the xml file since this information @@ -494,6 +515,10 @@ public class IdeaPluginDescriptorImpl implements IdeaPluginDescriptor { return myDownloadCounter; } + public long getDate(){ + return myDate; + } + /* This setter was explicitly defined to be able to set date for a descriptor outside its loading from the xml file since this information @@ -503,27 +528,6 @@ public class IdeaPluginDescriptorImpl implements IdeaPluginDescriptor { myDate = date; } - public long getDate(){ - return myDate; - } - - public void setVendor( final String val ) - { - myVendor = val; - } - public void setVendorEmail( final String val ) - { - myVendorEmail = val; - } - public void setVendorUrl( final String val ) - { - myVendorUrl = val; - } - public void setUrl( final String val ) - { - url = val; - } - @Override public ClassLoader getPluginClassLoader() { return myLoader != null ? myLoader : getClass().getClassLoader(); @@ -534,6 +538,10 @@ public class IdeaPluginDescriptorImpl implements IdeaPluginDescriptor { return myVendorLogoPath; } + public void setVendorLogoPath(final String vendorLogoPath) { + myVendorLogoPath = vendorLogoPath; + } + @Override public boolean getUseIdeaClassLoader() { return myUseIdeaClassLoader; @@ -547,10 +555,6 @@ public class IdeaPluginDescriptorImpl implements IdeaPluginDescriptor { myUseCoreClassLoader = useCoreClassLoader; } - public void setVendorLogoPath(final String vendorLogoPath) { - myVendorLogoPath = vendorLogoPath; - } - private String computeDescription() { ResourceBundle bundle = null; if (myResourceBundleBaseName != null) { @@ -635,16 +639,6 @@ public class IdeaPluginDescriptorImpl implements IdeaPluginDescriptor { myModuleComponents = mergeComponents(myModuleComponents, descriptor.myModuleComponents); } - private static ComponentConfig[] mergeComponents(ComponentConfig[] first, ComponentConfig[] second) { - if (first == null) { - return second; - } - if (second == null) { - return first; - } - return ArrayUtil.mergeArrays(first, second); - } - public Boolean getSkipped() { return mySkipped; } diff --git a/platform/core-impl/src/com/intellij/mock/MockComponentManager.java b/platform/core-impl/src/com/intellij/mock/MockComponentManager.java index c026211c232a..8e047fe60b4d 100644 --- a/platform/core-impl/src/com/intellij/mock/MockComponentManager.java +++ b/platform/core-impl/src/com/intellij/mock/MockComponentManager.java @@ -20,6 +20,7 @@ import com.intellij.openapi.components.BaseComponent; import com.intellij.openapi.components.ComponentManager; import com.intellij.openapi.extensions.ExtensionPointName; import com.intellij.openapi.util.Condition; +import com.intellij.openapi.util.Conditions; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.UserDataHolderBase; import com.intellij.util.containers.ConcurrentHashSet; @@ -137,6 +138,6 @@ public class MockComponentManager extends UserDataHolderBase implements Componen @NotNull @Override public Condition getDisposed() { - return Condition.FALSE; + return Conditions.alwaysFalse(); } } diff --git a/platform/core-impl/src/com/intellij/openapi/util/JDOMExternalizableAdapter.java b/platform/core-impl/src/com/intellij/openapi/util/JDOMExternalizableAdapter.java index 5248f4a0e535..8ba56e77f160 100644 --- a/platform/core-impl/src/com/intellij/openapi/util/JDOMExternalizableAdapter.java +++ b/platform/core-impl/src/com/intellij/openapi/util/JDOMExternalizableAdapter.java @@ -20,8 +20,10 @@ import com.intellij.openapi.components.PersistentStateComponent; import com.intellij.openapi.diagnostic.Logger; import org.jdom.Element; +@Deprecated /** * @author Dmitry Avdeev + * @deprecated to remove in IDEA 14 */ public class JDOMExternalizableAdapter implements PersistentStateComponent { diff --git a/platform/core-impl/src/com/intellij/openapi/vfs/impl/http/HttpVirtualFile.java b/platform/core-impl/src/com/intellij/openapi/vfs/impl/http/HttpVirtualFile.java index 990418dc9c51..1e0f0bcd68b5 100644 --- a/platform/core-impl/src/com/intellij/openapi/vfs/impl/http/HttpVirtualFile.java +++ b/platform/core-impl/src/com/intellij/openapi/vfs/impl/http/HttpVirtualFile.java @@ -16,10 +16,12 @@ package com.intellij.openapi.vfs.impl.http; import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.annotations.Nullable; /** * @author nik */ public abstract class HttpVirtualFile extends VirtualFile { + @Nullable public abstract RemoteFileInfo getFileInfo(); } diff --git a/platform/core-impl/src/com/intellij/psi/MultiplePsiFilesPerDocumentFileViewProvider.java b/platform/core-impl/src/com/intellij/psi/MultiplePsiFilesPerDocumentFileViewProvider.java index c6554f83fb7a..d8291fbd7f8d 100644 --- a/platform/core-impl/src/com/intellij/psi/MultiplePsiFilesPerDocumentFileViewProvider.java +++ b/platform/core-impl/src/com/intellij/psi/MultiplePsiFilesPerDocumentFileViewProvider.java @@ -97,10 +97,11 @@ public abstract class MultiplePsiFilesPerDocumentFileViewProvider extends Single @Override - public PsiFile getCachedPsi(Language target) { + public PsiFile getCachedPsi(@NotNull Language target) { return myRoots.get(target); } + @NotNull @Override public FileElement[] getKnownTreeRoots() { List files = new ArrayList(myRoots.size()); diff --git a/platform/core-impl/src/com/intellij/psi/SingleRootFileViewProvider.java b/platform/core-impl/src/com/intellij/psi/SingleRootFileViewProvider.java index bed56c25292d..63a7f205c11c 100644 --- a/platform/core-impl/src/com/intellij/psi/SingleRootFileViewProvider.java +++ b/platform/core-impl/src/com/intellij/psi/SingleRootFileViewProvider.java @@ -53,6 +53,7 @@ import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.lang.ref.Reference; import java.lang.ref.SoftReference; import java.util.Collections; import java.util.List; @@ -68,7 +69,7 @@ public class SingleRootFileViewProvider extends UserDataHolderBase implements Fi private final boolean myPhysical; private final AtomicReference myPsiFile = new AtomicReference(); private volatile Content myContent; - private volatile SoftReference myDocument; + private volatile Reference myDocument; @NotNull private final Language myBaseLanguage; public SingleRootFileViewProvider(@NotNull PsiManager manager, @NotNull VirtualFile file) { @@ -213,10 +214,11 @@ public class SingleRootFileViewProvider extends UserDataHolderBase implements Fi } - public PsiFile getCachedPsi(Language target) { + public PsiFile getCachedPsi(@NotNull Language target) { return myPsiFile.get(); } + @NotNull public FileElement[] getKnownTreeRoots() { PsiFile psiFile = myPsiFile.get(); if (psiFile == null || !(psiFile instanceof PsiFileImpl)) return new FileElement[0]; @@ -257,8 +259,7 @@ public class SingleRootFileViewProvider extends UserDataHolderBase implements Fi protected boolean isIgnored() { final VirtualFile file = getVirtualFile(); - if (file instanceof LightVirtualFile) return false; - return FileTypeRegistry.getInstance().isFileIgnored(file); + return !(file instanceof LightVirtualFile) && FileTypeRegistry.getInstance().isFileIgnored(file); } @Nullable @@ -357,7 +358,7 @@ public class SingleRootFileViewProvider extends UserDataHolderBase implements Fi Document document = com.intellij.reference.SoftReference.dereference(myDocument); if (document == null/* TODO[ik] make this change && isEventSystemEnabled()*/) { document = FileDocumentManager.getInstance().getDocument(getVirtualFile()); - myDocument = new SoftReference(document); + myDocument = document == null ? null : new SoftReference(document); } if (document != null && getContent() instanceof VirtualFileContent) { setContent(new DocumentContent()); @@ -446,16 +447,17 @@ public class SingleRootFileViewProvider extends UserDataHolderBase implements Fi return null; } - public void forceCachedPsi(final PsiFile psiFile) { + public void forceCachedPsi(@NotNull PsiFile psiFile) { myPsiFile.set(psiFile); ((PsiManagerEx)myManager).getFileManager().setViewProvider(getVirtualFile(), this); } + @NotNull private Content getContent() { return myContent; } - private void setContent(final Content content) { + private void setContent(@NotNull Content content) { // temporarily commented //if (myPhysical) { // final Content oldContent = myContent; diff --git a/platform/core-impl/src/com/intellij/psi/impl/file/impl/FileManagerImpl.java b/platform/core-impl/src/com/intellij/psi/impl/file/impl/FileManagerImpl.java index 6b2877d83351..e54b200507ca 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/file/impl/FileManagerImpl.java +++ b/platform/core-impl/src/com/intellij/psi/impl/file/impl/FileManagerImpl.java @@ -527,6 +527,7 @@ public class FileManagerImpl implements FileManager { } void reloadFromDisk(@NotNull PsiFile file, boolean ignoreDocument) { + ApplicationManager.getApplication().assertWriteAccessAllowed(); VirtualFile vFile = file.getVirtualFile(); assert vFile != null; diff --git a/platform/core-impl/src/com/intellij/psi/impl/source/tree/CompositeElement.java b/platform/core-impl/src/com/intellij/psi/impl/source/tree/CompositeElement.java index c73d0bd485ce..f8bfc611ce55 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/source/tree/CompositeElement.java +++ b/platform/core-impl/src/com/intellij/psi/impl/source/tree/CompositeElement.java @@ -54,10 +54,10 @@ public class CompositeElement extends TreeElement { private TreeElement firstChild = null; private TreeElement lastChild = null; - private volatile int myModificationsCount = 0; + private volatile int myModificationsCount; private volatile int myCachedLength = -1; private volatile int myHC = -1; - private volatile PsiElement myWrapper = null; + private volatile PsiElement myWrapper; private static final boolean ASSERT_THREADING = true;//DebugUtil.CHECK || ApplicationManagerEx.getApplicationEx().isInternal() || ApplicationManagerEx.getApplicationEx().isUnitTestMode(); public CompositeElement(@NotNull IElementType type) { diff --git a/platform/core-impl/src/com/intellij/psi/impl/source/tree/FileElement.java b/platform/core-impl/src/com/intellij/psi/impl/source/tree/FileElement.java index 76d5c421094d..d3071ddbca7d 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/source/tree/FileElement.java +++ b/platform/core-impl/src/com/intellij/psi/impl/source/tree/FileElement.java @@ -70,7 +70,7 @@ public class FileElement extends LazyParseableElement implements FileASTNode, Ge return psiElementCopy.getTreeElement(); } - public void setCharTable(CharTable table) { + public void setCharTable(@NotNull CharTable table) { myCharTable = table; } diff --git a/platform/core-impl/src/com/intellij/refactoring/rename/FragmentaryPsiReference.java b/platform/core-impl/src/com/intellij/refactoring/rename/FragmentaryPsiReference.java new file mode 100644 index 000000000000..49a3c5a0cc3c --- /dev/null +++ b/platform/core-impl/src/com/intellij/refactoring/rename/FragmentaryPsiReference.java @@ -0,0 +1,21 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.refactoring.rename; + +public interface FragmentaryPsiReference extends BindablePsiReference { + boolean isReadOnlyFragment(); + boolean isFragmentOnlyRename(); +} diff --git a/platform/core-impl/src/com/intellij/util/CachedValuesManagerImpl.java b/platform/core-impl/src/com/intellij/util/CachedValuesManagerImpl.java index 9c05387f2137..e63589b40cb5 100644 --- a/platform/core-impl/src/com/intellij/util/CachedValuesManagerImpl.java +++ b/platform/core-impl/src/com/intellij/util/CachedValuesManagerImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,11 +36,13 @@ public class CachedValuesManagerImpl extends CachedValuesManager { myFactory = factory == null ? new DefaultCachedValuesFactory(project) : factory; } + @NotNull @Override public CachedValue createCachedValue(@NotNull CachedValueProvider provider, boolean trackValue) { return myFactory.createCachedValue(provider, trackValue); } + @NotNull @Override public ParameterizedCachedValue createParameterizedCachedValue(@NotNull ParameterizedCachedValueProvider provider, boolean trackValue) { return myFactory.createParameterizedCachedValue(provider, trackValue); diff --git a/platform/dvcs-api/src/com/intellij/dvcs/push/PushSupport.java b/platform/dvcs-api/src/com/intellij/dvcs/push/PushSupport.java index fbf6f148c3b1..a1ffabfab844 100644 --- a/platform/dvcs-api/src/com/intellij/dvcs/push/PushSupport.java +++ b/platform/dvcs-api/src/com/intellij/dvcs/push/PushSupport.java @@ -19,12 +19,9 @@ import com.intellij.dvcs.repo.Repository; import com.intellij.dvcs.repo.RepositoryManager; import com.intellij.openapi.extensions.ExtensionPointName; import com.intellij.openapi.vcs.AbstractVcs; -import com.intellij.ui.SimpleColoredText; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.List; - /** * Base class to provide vcs-specific info */ @@ -49,27 +46,12 @@ public abstract class PushSupport getTargetNames(@NotNull Repo repository); - /** * @return current source(branch) for repository */ @NotNull public abstract Source getSource(@NotNull Repo repository); - /** - * Parse user input string, and create the VALID target for push - * - * @see #validateSpec(Repository, PushSpec) - */ - @NotNull - public abstract Target createTarget(@NotNull Repo repository, @NotNull String targetName); - /** * @return RepositoryManager for vcs */ @@ -81,11 +63,7 @@ public abstract class PushSupport createTargetPanel(@NotNull Repo repository, @Nullable Target defaultTarget); - public abstract SimpleColoredText renderTarget(@Nullable Target target); } diff --git a/platform/dvcs-api/src/com/intellij/dvcs/push/PushTarget.java b/platform/dvcs-api/src/com/intellij/dvcs/push/PushTarget.java index d7ed69d0875c..df6bfaefa79b 100644 --- a/platform/dvcs-api/src/com/intellij/dvcs/push/PushTarget.java +++ b/platform/dvcs-api/src/com/intellij/dvcs/push/PushTarget.java @@ -15,15 +15,9 @@ */ package com.intellij.dvcs.push; -import org.jetbrains.annotations.NotNull; - - /** * Destination for push action. (Remote for git or push-path for mercurial). */ public interface PushTarget { - @NotNull - //todo rename - > getName or smth - String getPresentation(); } diff --git a/platform/dvcs-api/src/com/intellij/dvcs/push/PushTargetPanel.java b/platform/dvcs-api/src/com/intellij/dvcs/push/PushTargetPanel.java new file mode 100644 index 000000000000..ede2f17410ee --- /dev/null +++ b/platform/dvcs-api/src/com/intellij/dvcs/push/PushTargetPanel.java @@ -0,0 +1,38 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.dvcs.push; + +import com.intellij.openapi.ui.ValidationInfo; +import com.intellij.ui.ColoredTreeCellRenderer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; + +public abstract class PushTargetPanel extends JPanel { + + abstract public void render(@NotNull ColoredTreeCellRenderer renderer); + + @NotNull + abstract public T getValue(); + + public abstract void fireOnCancel(); + + public abstract void fireOnChange(); + + @Nullable + public abstract ValidationInfo verify(); +} diff --git a/platform/dvcs-api/src/com/intellij/dvcs/push/VcsError.java b/platform/dvcs-api/src/com/intellij/dvcs/push/VcsError.java index b76095629ccd..e6c0e210429a 100644 --- a/platform/dvcs-api/src/com/intellij/dvcs/push/VcsError.java +++ b/platform/dvcs-api/src/com/intellij/dvcs/push/VcsError.java @@ -42,6 +42,6 @@ public class VcsError { } public static VcsError createEmptyTargetError(@NotNull String name) { - return new VcsError("Please, specify remote push path for repository " + name + "."); + return new VcsError("Please, specify not empty remote push path for repository " + name + "."); } } diff --git a/platform/dvcs-api/src/com/intellij/dvcs/repo/Repository.java b/platform/dvcs-api/src/com/intellij/dvcs/repo/Repository.java index 47cc17866c08..a71df5c39307 100644 --- a/platform/dvcs-api/src/com/intellij/dvcs/repo/Repository.java +++ b/platform/dvcs-api/src/com/intellij/dvcs/repo/Repository.java @@ -48,12 +48,9 @@ import org.jetbrains.annotations.Nullable; * If one needs a really 100 % up-to-date value, one should call {@link #update()} and then get...(). * update() is a synchronous read from repository file (.git or .hg), so it is guaranteed to query the real value. *

- * - * @author Nadya Zabrodina */ public interface Repository extends Disposable { - /** * Current state of the repository. */ @@ -62,24 +59,29 @@ public interface Repository extends Disposable { * HEAD is on branch, no merge process is in progress (and no rebase as well). */ NORMAL, + /** * During merge (for instance, merge failed with conflicts that weren't immediately resolved). */ MERGING { + @NotNull @Override public String toString() { return "Merging"; } }, + /** * During rebase. */ REBASING { + @NotNull @Override public String toString() { return "Rebasing"; } }, + /** * Detached HEAD state, but not during rebase (for example, manual checkout of a commit hash). */ @@ -98,6 +100,9 @@ public interface Repository extends Disposable { @NotNull State getState(); + @Nullable + String getCurrentBranchName(); + @Nullable AbstractVcs getVcs(); diff --git a/platform/dvcs-impl/src/com/intellij/dvcs/DvcsCommitAdditionalComponent.java b/platform/dvcs-impl/src/com/intellij/dvcs/DvcsCommitAdditionalComponent.java index 26c87c7de627..76d58125b719 100644 --- a/platform/dvcs-impl/src/com/intellij/dvcs/DvcsCommitAdditionalComponent.java +++ b/platform/dvcs-impl/src/com/intellij/dvcs/DvcsCommitAdditionalComponent.java @@ -43,9 +43,6 @@ import java.io.File; import java.util.*; import java.util.List; -/** - * @author Nadya Zabrodina - */ public abstract class DvcsCommitAdditionalComponent implements RefreshableOnComponent { private static final Logger log = Logger.getInstance(DvcsCommitAdditionalComponent.class); @@ -179,4 +176,8 @@ public abstract class DvcsCommitAdditionalComponent implements RefreshableOnComp @Nullable protected abstract String getLastCommitMessage(@NotNull VirtualFile repo) throws VcsException; + + public boolean isAmend() { + return myAmend.isSelected(); + } } diff --git a/platform/dvcs-impl/src/com/intellij/dvcs/branch/DvcsBranchPopup.java b/platform/dvcs-impl/src/com/intellij/dvcs/branch/DvcsBranchPopup.java new file mode 100644 index 000000000000..a98cf27ab5d6 --- /dev/null +++ b/platform/dvcs-impl/src/com/intellij/dvcs/branch/DvcsBranchPopup.java @@ -0,0 +1,174 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.dvcs.branch; + +import com.intellij.dvcs.DvcsUtil; +import com.intellij.dvcs.repo.AbstractRepositoryManager; +import com.intellij.dvcs.repo.Repository; +import com.intellij.dvcs.ui.BranchActionGroupPopup; +import com.intellij.notification.Notification; +import com.intellij.notification.NotificationListener; +import com.intellij.openapi.actionSystem.ActionGroup; +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.DefaultActionGroup; +import com.intellij.openapi.options.ShowSettingsUtil; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.popup.ListPopup; +import com.intellij.openapi.util.Condition; +import com.intellij.openapi.vcs.AbstractVcs; +import com.intellij.openapi.vcs.VcsNotifier; +import com.intellij.ui.popup.list.ListPopupImpl; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import javax.swing.event.HyperlinkEvent; +import java.util.List; + +public abstract class DvcsBranchPopup { + @NotNull protected final Project myProject; + @NotNull protected final AbstractRepositoryManager myRepositoryManager; + @NotNull protected final DvcsSyncBranchSettings myVcsSettings; + @NotNull protected final AbstractVcs myVcs; + @NotNull protected final DvcsMultiRootBranchConfig myMultiRootBranchConfig; + + @NotNull protected final Repo myCurrentRepository; + @NotNull protected final ListPopupImpl myPopup; + + protected DvcsBranchPopup(@NotNull Repo currentRepository, + @NotNull AbstractRepositoryManager repositoryManager, + @NotNull DvcsMultiRootBranchConfig multiRootBranchConfig, + @NotNull DvcsSyncBranchSettings vcsSettings, + @NotNull Condition preselectActionCondition) { + myProject = currentRepository.getProject(); + myCurrentRepository = currentRepository; + myRepositoryManager = repositoryManager; + myVcs = currentRepository.getVcs(); + myVcsSettings = vcsSettings; + myMultiRootBranchConfig = multiRootBranchConfig; + String title = createPopupTitle(currentRepository); + myPopup = new BranchActionGroupPopup(title, myProject, preselectActionCondition, createActions()); + + initBranchSyncPolicyIfNotInitialized(); + setCurrentBranchInfo(); + warnThatBranchesDivergedIfNeeded(); + } + + public ListPopup asListPopup() { + return myPopup; + } + + private void initBranchSyncPolicyIfNotInitialized() { + if (myRepositoryManager.moreThanOneRoot() && myVcsSettings.getSyncSetting() == DvcsBranchSync.NOT_DECIDED) { + if (!myMultiRootBranchConfig.diverged()) { + notifyAboutSyncedBranches(); + myVcsSettings.setSyncSetting(DvcsBranchSync.SYNC); + } + else { + myVcsSettings.setSyncSetting(DvcsBranchSync.DONT); + } + } + } + + @NotNull + private String createPopupTitle(@NotNull Repo currentRepository) { + String title = myVcs.getDisplayName() + " Branches"; + if (myRepositoryManager.moreThanOneRoot() && + (myMultiRootBranchConfig.diverged() || myVcsSettings.getSyncSetting() == DvcsBranchSync.DONT)) { + title += " in " + DvcsUtil.getShortRepositoryName(currentRepository); + } + return title; + } + + protected void setCurrentBranchInfo() { + String branchText = "Current branch : "; + myPopup.setAdText(branchText + myCurrentRepository.getCurrentBranchName(), SwingConstants.CENTER); + } + + private void notifyAboutSyncedBranches() { + String description = + "You have several " + myVcs.getDisplayName() + "roots in the project and they all are checked out at the same branch. " + + "We've enabled synchronous branch control for the project.
" + + "If you wish to control branches in different roots separately, " + + "you may disable the setting."; + NotificationListener listener = new NotificationListener() { + @Override + public void hyperlinkUpdate(@NotNull Notification notification, @NotNull HyperlinkEvent event) { + if (event.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { + ShowSettingsUtil.getInstance().showSettingsDialog(myProject, myVcs.getConfigurable().getDisplayName()); + if (myVcsSettings.getSyncSetting() == DvcsBranchSync.DONT) { + notification.expire(); + } + } + } + }; + VcsNotifier.getInstance(myProject).notifyImportantInfo("Synchronous branch control enabled", description, listener); + } + + @NotNull + private ActionGroup createActions() { + DefaultActionGroup popupGroup = new DefaultActionGroup(null, false); + AbstractRepositoryManager repositoryManager = myRepositoryManager; + if (repositoryManager.moreThanOneRoot()) { + if (userWantsSyncControl()) { + fillWithCommonRepositoryActions(popupGroup, repositoryManager); + } + else { + fillPopupWithCurrentRepositoryActions(popupGroup, createRepositoriesActions()); + } + } + else { + fillPopupWithCurrentRepositoryActions(popupGroup, null); + } + popupGroup.addSeparator(); + return popupGroup; + } + + private boolean userWantsSyncControl() { + return (myVcsSettings.getSyncSetting() != DvcsBranchSync.DONT); + } + + protected abstract void fillWithCommonRepositoryActions(@NotNull DefaultActionGroup popupGroup, + @NotNull AbstractRepositoryManager repositoryManager); + + @NotNull + protected List filterRepositoriesNotOnThisBranch(@NotNull final String branch, + @NotNull List allRepositories) { + return ContainerUtil.filter(allRepositories, new Condition() { + @Override + public boolean value(Repo repository) { + return !branch.equals(repository.getCurrentBranchName()); + } + }); + } + + private void warnThatBranchesDivergedIfNeeded() { + if (myRepositoryManager.moreThanOneRoot() && myMultiRootBranchConfig.diverged() && userWantsSyncControl()) { + myPopup.setWarning("Branches have diverged"); + } + } + + @NotNull + protected abstract DefaultActionGroup createRepositoriesActions(); + + protected boolean highlightCurrentRepo() { + return !userWantsSyncControl() || myMultiRootBranchConfig.diverged(); + } + + protected abstract void fillPopupWithCurrentRepositoryActions(@NotNull DefaultActionGroup popupGroup, + @Nullable DefaultActionGroup actions); +} diff --git a/platform/dvcs-impl/src/com/intellij/dvcs/branch/DvcsBranchSync.java b/platform/dvcs-impl/src/com/intellij/dvcs/branch/DvcsBranchSync.java new file mode 100644 index 000000000000..9fab273be2e1 --- /dev/null +++ b/platform/dvcs-impl/src/com/intellij/dvcs/branch/DvcsBranchSync.java @@ -0,0 +1,22 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.dvcs.branch; + +public enum DvcsBranchSync { + SYNC, + DONT, + NOT_DECIDED +} diff --git a/platform/dvcs-impl/src/com/intellij/dvcs/branch/DvcsMultiRootBranchConfig.java b/platform/dvcs-impl/src/com/intellij/dvcs/branch/DvcsMultiRootBranchConfig.java new file mode 100644 index 000000000000..ee24209167fa --- /dev/null +++ b/platform/dvcs-impl/src/com/intellij/dvcs/branch/DvcsMultiRootBranchConfig.java @@ -0,0 +1,71 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.dvcs.branch; + +import com.intellij.dvcs.repo.Repository; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; + +public abstract class DvcsMultiRootBranchConfig { + @NotNull protected final Collection myRepositories; + + public DvcsMultiRootBranchConfig(@NotNull Collection repositories) { + myRepositories = repositories; + } + + public boolean diverged() { + return getCurrentBranch() == null; + } + + @Nullable + public String getCurrentBranch() { + String commonBranch = null; + for (Repo repository : myRepositories) { + String branchName = repository.getCurrentBranchName(); + if (branchName == null) { + return null; + } + // NB: if all repositories are in the rebasing state on the same branches, this branch is returned + if (commonBranch == null) { + commonBranch = branchName; + } + else if (!commonBranch.equals(branchName)) { + return null; + } + } + return commonBranch; + } + + @Nullable + public Repository.State getState() { + Repository.State commonState = null; + for (Repo repository : myRepositories) { + Repository.State state = repository.getState(); + if (commonState == null) { + commonState = state; + } + else if (!commonState.equals(state)) { + return null; + } + } + return commonState; + } + + @NotNull + public abstract Collection getLocalBranchNames(); +} diff --git a/platform/dvcs-impl/src/com/intellij/dvcs/branch/DvcsSyncBranchSettings.java b/platform/dvcs-impl/src/com/intellij/dvcs/branch/DvcsSyncBranchSettings.java new file mode 100644 index 000000000000..115872a66552 --- /dev/null +++ b/platform/dvcs-impl/src/com/intellij/dvcs/branch/DvcsSyncBranchSettings.java @@ -0,0 +1,25 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.dvcs.branch; + +import org.jetbrains.annotations.NotNull; + +public interface DvcsSyncBranchSettings { + @NotNull + DvcsBranchSync getSyncSetting(); + + void setSyncSetting(@NotNull DvcsBranchSync syncSetting); +} diff --git a/platform/dvcs-impl/src/com/intellij/dvcs/push/PushController.java b/platform/dvcs-impl/src/com/intellij/dvcs/push/PushController.java index 45f5c68e1d0a..cd0cc7a1ef7a 100644 --- a/platform/dvcs-impl/src/com/intellij/dvcs/push/PushController.java +++ b/platform/dvcs-impl/src/com/intellij/dvcs/push/PushController.java @@ -20,31 +20,32 @@ import com.intellij.dvcs.push.ui.*; import com.intellij.dvcs.repo.Repository; import com.intellij.dvcs.repo.RepositoryManager; import com.intellij.openapi.Disposable; -import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.Task; -import com.intellij.openapi.progress.impl.ProgressManagerImpl; import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.MessageType; import com.intellij.openapi.ui.ValidationInfo; +import com.intellij.openapi.ui.popup.util.PopupUtil; import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Disposer; -import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vcs.AbstractVcs; import com.intellij.ui.CheckedTreeNode; -import com.intellij.ui.SimpleColoredText; -import com.intellij.ui.SimpleTextAttributes; import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.hash.HashMap; +import com.intellij.util.ui.UIUtil; import com.intellij.vcs.log.VcsFullCommitDetails; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import javax.swing.*; import javax.swing.tree.DefaultMutableTreeNode; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.*; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicReference; public class PushController implements Disposable { @@ -56,6 +57,7 @@ public class PushController implements Disposable { private boolean mySingleRepoProject; private static final int DEFAULT_CHILDREN_PRESENTATION_NUMBER = 20; private final Map myAdditionalValuesMap; + private final ExecutorService myExecutorService = Executors.newSingleThreadExecutor(); private final Map myView2Model = new TreeMap(); //todo need to sort repositories in ui tree using natural order @@ -162,33 +164,31 @@ public class PushController implements Disposable { if (target == null) { model.setError(VcsError.createEmptyTargetError(repoName)); } - RepositoryWithBranchPanel repoPanel = new RepositoryWithBranchPanel(myProject, repoName, - support.getSource(repository).getPresentation(), - target == null ? "" : target.getPresentation(), - support.getTargetNames(repository)); + final PushTargetPanel pushTargetPanel = support.createTargetPanel(repository, target); + RepositoryWithBranchPanel repoPanel = + new RepositoryWithBranchPanel(repoName, support.getSource(repository).getPresentation(), pushTargetPanel); + repoPanel.setInputVerifier(new InputVerifier() { + @Override + public boolean verify(JComponent input) { + ValidationInfo error = pushTargetPanel.verify(); + if (error != null) { + //noinspection ConstantConditions + PopupUtil.showBalloonForComponent(error.component, error.message, MessageType.WARNING, false, myProject); + } + return error == null; + } + }); final RepositoryNode repoNode = isSingleRepositoryProject - ? new SingleRepositoryNode(repoPanel, support.renderTarget(target)) - : new RepositoryNode(repoPanel, support.renderTarget(target)); + ? new SingleRepositoryNode(repoPanel) + : new RepositoryNode(repoPanel); myView2Model.put(repoNode, model); repoNode.setChecked(model.isSelected()); - repoPanel.addRepoNodeListener(new RepositoryNodeListener() { + repoPanel.addRepoNodeListener(new RepositoryNodeListener() { @Override - public void onTargetChanged(String newValue) { - VcsError validationError = support.validate(model.getRepository(), newValue); - if (validationError == null) { - T newTarget = support.createTarget(repository, newValue); - repoNode.setTargetPresentation(support.renderTarget(newTarget)); - model.setTarget(newTarget); - model.clearErrors(); - loadCommits(model, repoNode, false); - } - else { - repoNode.setTargetPresentation(StringUtil.isEmptyOrSpaces(newValue) - ? support.renderTarget(null) - : new SimpleColoredText(newValue, SimpleTextAttributes.ERROR_ATTRIBUTES)); - model.setError(validationError); // todo may be should accept and store errors collection, now store one major target error - model.setTarget(null); - } + public void onTargetChanged(T newTarget) { + model.setTarget(newTarget); + model.clearErrors(); + loadCommits(model, repoNode, false); myDialog.updateButtons(); } @@ -209,56 +209,49 @@ public class PushController implements Disposable { final T target = model.getTarget(); if (target == null) return; //todo should be removed when commit loader executor will be modified myPushLog.startLoading(node); - final ProgressIndicator indicator = node.startLoading(); final PushSupport support = model.getSupport(); final AtomicReference result = new AtomicReference(); - Task.Backgroundable task = new Task.Backgroundable(myProject, "Loading Commits", true) { - + Runnable task = new Runnable() { @Override - public void onCancel() { - node.stopLoading(); - } - - @Override - public void onSuccess() { - OutgoingResult outgoing = result.get(); - List errors = outgoing.getErrors(); - if (!errors.isEmpty()) { - myPushLog.setChildren(node, ContainerUtil.map(errors, new Function() { - @Override - public DefaultMutableTreeNode fun(final VcsError error) { - VcsLinkedText errorLinkText = new VcsLinkedText(error.getText(), new VcsLinkListener() { + public void run() { + OutgoingResult outgoing = support.getOutgoingCommitsProvider() + .getOutgoingCommits(model.getRepository(), new PushSpec(model.getSource(), model.getTarget()), initial); + result.compareAndSet(null, outgoing); + UIUtil.invokeAndWaitIfNeeded(new Runnable() { + @Override + public void run() { + OutgoingResult outgoing = result.get(); + List errors = outgoing.getErrors(); + if (!errors.isEmpty()) { + myPushLog.setChildren(node, ContainerUtil.map(errors, new Function() { @Override - public void hyperlinkActivated(@NotNull DefaultMutableTreeNode sourceNode) { - error.handleError(new CommitLoader() { + public DefaultMutableTreeNode fun(final VcsError error) { + VcsLinkedText errorLinkText = new VcsLinkedText(error.getText(), new VcsLinkListener() { @Override - public void reloadCommits() { - loadCommits(model, node, false); + public void hyperlinkActivated(@NotNull DefaultMutableTreeNode sourceNode) { + error.handleError(new CommitLoader() { + @Override + public void reloadCommits() { + loadCommits(model, node, false); + } + }); } }); + return new TextWithLinkNode(errorLinkText); } - }); - return new TextWithLinkNode(errorLinkText); + }), model.isSelected()); } - }), model.isSelected()); - } - else { - model.setLoadedCommits(outgoing.getCommits()); - myPushLog.setChildren(node, - getPresentationForCommits(PushController.this.myProject, model.getLoadedCommits(), - model.getNumberOfShownCommits()), model.isSelected()); - } - } - - @Override - public void run(@NotNull ProgressIndicator indicator) { - OutgoingResult outgoing = support.getOutgoingCommitsProvider() - .getOutgoingCommits(model.getRepository(), new PushSpec(model.getSource(), model.getTarget()), initial); - result.compareAndSet(null, outgoing); + else { + model.setLoadedCommits(outgoing.getCommits()); + myPushLog.setChildren(node, + getPresentationForCommits(PushController.this.myProject, model.getLoadedCommits(), + model.getNumberOfShownCommits()), model.isSelected()); + } + } + }); } }; - - ProgressManagerImpl.runProcessWithProgressAsynchronously(task, indicator, null, ModalityState.any()); + node.startLoading(myExecutorService.submit(task, result)); } public PushLog getPushPanelLog() { @@ -315,9 +308,7 @@ public class PushController implements Disposable { @Override public void dispose() { - for (RepositoryNode node : myView2Model.keySet()) { - node.stopLoading(); - } + myExecutorService.shutdownNow(); } private void addMoreCommits(RepositoryNode repositoryNode) { diff --git a/platform/dvcs-impl/src/com/intellij/dvcs/push/RepositoryNodeListener.java b/platform/dvcs-impl/src/com/intellij/dvcs/push/RepositoryNodeListener.java index 7ca285fc95aa..91bb711cc384 100644 --- a/platform/dvcs-impl/src/com/intellij/dvcs/push/RepositoryNodeListener.java +++ b/platform/dvcs-impl/src/com/intellij/dvcs/push/RepositoryNodeListener.java @@ -15,9 +15,9 @@ */ package com.intellij.dvcs.push; -public interface RepositoryNodeListener { +public interface RepositoryNodeListener { - void onTargetChanged(String newValue); + void onTargetChanged(T newTarget); void onSelectionChanged(boolean isSelected); } diff --git a/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/EditableTreeNode.java b/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/EditableTreeNode.java index b5747de91bf7..aab70818082d 100644 --- a/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/EditableTreeNode.java +++ b/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/EditableTreeNode.java @@ -15,9 +15,12 @@ */ package com.intellij.dvcs.push.ui; -import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.dvcs.push.OutgoingResult; import org.jetbrains.annotations.NotNull; +import java.util.concurrent.Future; +import java.util.concurrent.atomic.AtomicReference; + public interface EditableTreeNode extends CustomRenderedTreeNode { void fireOnChange(); @@ -28,8 +31,5 @@ public interface EditableTreeNode extends CustomRenderedTreeNode { void stopLoading(); - @NotNull - ProgressIndicator startLoading(); - - String getValue(); + void startLoading(@NotNull Future> future); } diff --git a/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/PushLog.java b/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/PushLog.java index 0b4bcb0fbef0..1539b94216c0 100644 --- a/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/PushLog.java +++ b/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/PushLog.java @@ -15,6 +15,7 @@ */ package com.intellij.dvcs.push.ui; +import com.intellij.dvcs.push.PushTargetPanel; import com.intellij.openapi.actionSystem.CommonShortcuts; import com.intellij.openapi.actionSystem.DataKey; import com.intellij.openapi.actionSystem.DataSink; @@ -25,7 +26,10 @@ import com.intellij.openapi.vcs.VcsDataKeys; import com.intellij.openapi.vcs.changes.Change; import com.intellij.openapi.vcs.changes.committed.CommittedChangesTreeBrowser; import com.intellij.openapi.vcs.changes.ui.ChangesBrowser; -import com.intellij.ui.*; +import com.intellij.ui.CheckboxTree; +import com.intellij.ui.CheckedTreeNode; +import com.intellij.ui.ColoredTreeCellRenderer; +import com.intellij.ui.ScrollPaneFactory; import com.intellij.ui.components.JBTextField; import com.intellij.util.ArrayUtil; import com.intellij.util.ui.tree.TreeUtil; @@ -37,7 +41,10 @@ import javax.swing.event.CellEditorListener; import javax.swing.event.ChangeEvent; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; -import javax.swing.tree.*; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.DefaultTreeModel; +import javax.swing.tree.TreePath; +import javax.swing.tree.TreeSelectionModel; import java.awt.*; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; @@ -45,17 +52,14 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.EventObject; -import java.util.concurrent.locks.ReentrantReadWriteLock; public class PushLog extends JPanel implements TypeSafeDataProvider { - private final ReentrantReadWriteLock TREE_CONSTRUCTION_LOCK = new ReentrantReadWriteLock(); - private static final String START_EDITING = "startEditing"; private final ChangesBrowser myChangesBrowser; private final CheckboxTree myTree; private final MyTreeCellRenderer myTreeCellRenderer; - //private final AtomicBoolean myIgnoreStopEditing = new AtomicBoolean(false); + private boolean myEditingSucceeded; public PushLog(Project project, CheckedTreeNode root) { DefaultTreeModel treeModel = new DefaultTreeModel(root); @@ -89,6 +93,23 @@ public class PushLog extends JPanel implements TypeSafeDataProvider { } return ""; } + + @Override + public boolean stopEditing() { + DefaultMutableTreeNode node = (DefaultMutableTreeNode)myTree.getLastSelectedPathComponent(); + if (node instanceof EditableTreeNode) { + JComponent editedComponent = (JComponent)node.getUserObject(); + InputVerifier verifier = editedComponent.getInputVerifier(); + if (verifier != null && !verifier.verify(editedComponent)) return false; + } + myEditingSucceeded = true; + try { + return super.stopEditing(); + } + finally { + myEditingSucceeded = false; + } + } }; myTree.setEditable(true); MyTreeCellEditor treeCellEditor = new MyTreeCellEditor(new JBTextField()); @@ -140,6 +161,9 @@ public class PushLog extends JPanel implements TypeSafeDataProvider { } }); myTree.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_F2, 0), START_EDITING); + //override default tree behaviour. + myTree.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), ""); + myTree.setRowHeight(0); ToolTipManager.sharedInstance().registerComponent(myTree); @@ -197,8 +221,14 @@ public class PushLog extends JPanel implements TypeSafeDataProvider { @Override protected boolean processKeyBinding(KeyStroke ks, KeyEvent e, int condition, boolean pressed) { - if (e.getKeyCode() == KeyEvent.VK_ENTER && myTree.isEditing()) { - myTree.stopEditing(); + if (e.getKeyCode() == KeyEvent.VK_ENTER && pressed) { + if (myTree.isEditing()) { + myTree.stopEditing(); + } + else { + DefaultMutableTreeNode node = (DefaultMutableTreeNode)myTree.getLastSelectedPathComponent(); + myTree.startEditingAtPath(TreeUtil.getPathFromRoot(node)); + } return true; } return super.processKeyBinding(ks, e, condition, pressed); @@ -239,7 +269,7 @@ public class PushLog extends JPanel implements TypeSafeDataProvider { Object tag = me.getClickCount() >= clickCountToStart ? PushLogTreeUtil.getTagAtForRenderer(myTreeCellRenderer, me) : null; - return tag instanceof EditorTextField; + return tag instanceof PushTargetPanel; } //if keyboard event - then anEvent will be null =( See BasicTreeUi TreePath treePath = myTree.getAnchorSelectionPath(); @@ -251,7 +281,7 @@ public class PushLog extends JPanel implements TypeSafeDataProvider { //Implement the one CellEditor method that AbstractCellEditor doesn't. public Object getCellEditorValue() { - return ((RepositoryWithBranchPanel)editorComponent).getRemoteTargetName(); + return myEditingSucceeded ? ((RepositoryWithBranchPanel)editorComponent).getEditableValue() : null; } } @@ -285,34 +315,19 @@ public class PushLog extends JPanel implements TypeSafeDataProvider { public void setChildren(DefaultMutableTreeNode parentNode, @NotNull Collection childrenNodes, boolean shouldExpand) { - try { - TREE_CONSTRUCTION_LOCK.writeLock().lock(); - parentNode.removeAllChildren(); - for (DefaultMutableTreeNode child : childrenNodes) { - parentNode.add(child); - } - final DefaultTreeModel model = ((DefaultTreeModel)myTree.getModel()); - model.nodeStructureChanged(parentNode); - TreePath path = TreeUtil.getPathFromRoot(parentNode); - //myIgnoreStopEditing.set(true); - if (shouldExpand) { - myTree.expandPath(path); - } - else { - myTree.collapsePath(path); - } + parentNode.removeAllChildren(); + for (DefaultMutableTreeNode child : childrenNodes) { + parentNode.add(child); } - finally { - TREE_CONSTRUCTION_LOCK.writeLock().unlock(); - //myIgnoreStopEditing.set(false); + final DefaultTreeModel model = ((DefaultTreeModel)myTree.getModel()); + model.nodeStructureChanged(parentNode); + TreePath path = TreeUtil.getPathFromRoot(parentNode); + if (shouldExpand) { + myTree.expandPath(path); } - } - - public void startEditNode(@NotNull TreeNode node) { - TreePath path = TreeUtil.getPathFromRoot(node); - if (!myTree.isEditing()) { - myTree.setSelectionPath(path); - myTree.startEditingAtPath(path); + else { + myTree.collapsePath(path); } } + } diff --git a/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/RepositoryNode.java b/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/RepositoryNode.java index 05e752c4be85..4808064f5c8d 100644 --- a/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/RepositoryNode.java +++ b/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/RepositoryNode.java @@ -15,29 +15,26 @@ */ package com.intellij.dvcs.push.ui; -import com.intellij.openapi.progress.EmptyProgressIndicator; -import com.intellij.openapi.progress.ProgressIndicator; -import com.intellij.ui.*; +import com.intellij.dvcs.push.OutgoingResult; +import com.intellij.dvcs.push.PushTargetPanel; +import com.intellij.ui.CheckedTreeNode; +import com.intellij.ui.ColoredTreeCellRenderer; +import com.intellij.ui.SimpleTextAttributes; import org.jetbrains.annotations.NotNull; import javax.swing.*; import javax.swing.border.EmptyBorder; import java.awt.*; -import java.util.ArrayList; +import java.util.concurrent.Future; +import java.util.concurrent.atomic.AtomicReference; public class RepositoryNode extends CheckedTreeNode implements EditableTreeNode, Comparable { @NotNull private final RepositoryWithBranchPanel myRepositoryPanel; - @NotNull protected SimpleColoredText myTargetPresentation; - private ProgressIndicator myCurrentIndicator; + private Future> myFuture; - public RepositoryNode(@NotNull RepositoryWithBranchPanel repositoryPanel, @NotNull SimpleColoredText targetPresentation) { + public RepositoryNode(@NotNull RepositoryWithBranchPanel repositoryPanel) { super(repositoryPanel); myRepositoryPanel = repositoryPanel; - myTargetPresentation = targetPresentation; - } - - public void setTargetPresentation(@NotNull SimpleColoredText targetPresentation) { - myTargetPresentation = targetPresentation; } public boolean isCheckboxVisible() { @@ -51,31 +48,17 @@ public class RepositoryNode extends CheckedTreeNode implements EditableTreeNode, renderer.appendFixedTextFragmentWidth(120); renderer.append(myRepositoryPanel.getSourceName(), SimpleTextAttributes.REGULAR_ATTRIBUTES); renderer.append(myRepositoryPanel.getArrow(), SimpleTextAttributes.REGULAR_ATTRIBUTES); - EditorTextField textField = myRepositoryPanel.getRemoteTextFiled(); - renderTargetName(renderer, textField); - Insets insets = BorderFactory.createEmptyBorder().getBorderInsets(textField); + PushTargetPanel pushTargetPanel = myRepositoryPanel.getTargetPanel(); + pushTargetPanel.render(renderer); + Insets insets = BorderFactory.createEmptyBorder().getBorderInsets(pushTargetPanel); renderer.setBorder(new EmptyBorder(insets)); } - protected void renderTargetName(@NotNull ColoredTreeCellRenderer renderer, @NotNull EditorTextField textField) { - ArrayList strings = myTargetPresentation.getTexts(); - ArrayList attributes = myTargetPresentation.getAttributes(); - for (int i = 0; i < strings.size(); i++) { - renderer.append(strings.get(i), attributes.get(i), textField); - } - } - @Override public Object getUserObject() { return myRepositoryPanel; } - @Override - @NotNull - public String getValue() { - return myRepositoryPanel.getRemoteTargetName(); - } - @Override public void fireOnChange() { myRepositoryPanel.fireOnChange(); @@ -93,15 +76,14 @@ public class RepositoryNode extends CheckedTreeNode implements EditableTreeNode, @Override public void stopLoading() { - if (myCurrentIndicator != null && myCurrentIndicator.isRunning()) { - myCurrentIndicator.cancel(); + if (myFuture != null && !myFuture.isDone()) { + myFuture.cancel(true); } } @Override - @NotNull - public ProgressIndicator startLoading() { - return myCurrentIndicator = new EmptyProgressIndicator(); + public void startLoading(@NotNull Future> future) { + myFuture = future; } public int compareTo(@NotNull RepositoryNode repositoryNode) { diff --git a/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/RepositoryWithBranchPanel.java b/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/RepositoryWithBranchPanel.java index cd93d0653c36..29a32237ca3a 100644 --- a/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/RepositoryWithBranchPanel.java +++ b/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/RepositoryWithBranchPanel.java @@ -15,18 +15,15 @@ */ package com.intellij.dvcs.push.ui; +import com.intellij.dvcs.push.PushTarget; +import com.intellij.dvcs.push.PushTargetPanel; import com.intellij.dvcs.push.RepositoryNodeListener; -import com.intellij.openapi.editor.ex.EditorEx; -import com.intellij.openapi.project.Project; import com.intellij.ui.ColoredTreeCellRenderer; import com.intellij.ui.SimpleTextAttributes; -import com.intellij.ui.TextFieldWithAutoCompletion; -import com.intellij.ui.TextFieldWithAutoCompletionListProvider; import com.intellij.ui.components.JBCheckBox; import com.intellij.ui.components.JBLabel; import com.intellij.ui.components.panels.NonOpaquePanel; import com.intellij.util.containers.ContainerUtil; -import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; import javax.swing.*; @@ -34,23 +31,20 @@ import javax.swing.tree.TreeCellRenderer; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.awt.event.FocusAdapter; -import java.awt.event.FocusEvent; import java.util.List; -public class RepositoryWithBranchPanel extends NonOpaquePanel implements TreeCellRenderer { +public class RepositoryWithBranchPanel extends NonOpaquePanel implements TreeCellRenderer { private final JBCheckBox myRepositoryCheckbox; - private final TextFieldWithAutoCompletion myDestBranchTextField; + private final PushTargetPanel myDestPushTargetPanelComponent; private final JBLabel myLocalBranch; private final JLabel myArrowLabel; private final JLabel myRepositoryLabel; private final ColoredTreeCellRenderer myTextRenderer; - @NotNull private final List myListeners = ContainerUtil.createLockFreeCopyOnWriteList(); - private String myOldDestination; + @NotNull private final List> myListeners = ContainerUtil.createLockFreeCopyOnWriteList(); - public RepositoryWithBranchPanel(Project project, @NotNull String repoName, - @NotNull String sourceName, String targetName, @NotNull final List targetVariants) { + public RepositoryWithBranchPanel(@NotNull String repoName, + @NotNull String sourceName, @NotNull PushTargetPanel destPushTargetPanelComponent) { super(); setLayout(new BorderLayout()); myRepositoryCheckbox = new JBCheckBox(); @@ -65,37 +59,7 @@ public class RepositoryWithBranchPanel extends NonOpaquePanel implements TreeCel myRepositoryLabel = new JLabel(repoName); myLocalBranch = new JBLabel(sourceName); myArrowLabel = new JLabel(" -> "); - myOldDestination = targetName; - TextFieldWithAutoCompletionListProvider provider = - new TextFieldWithAutoCompletion.StringsCompletionProvider(targetVariants, null) { - @Override - public int compare(String item1, String item2) { - return Integer.valueOf(ContainerUtil.indexOf(targetVariants, item1)).compareTo(ContainerUtil.indexOf(targetVariants, item2)); - } - }; - myDestBranchTextField = new TextFieldWithAutoCompletion(project, provider, true, targetName) { - - @Override - public boolean shouldHaveBorder() { - return false; - } - - @Override - protected void updateBorder(@NotNull final EditorEx editor) { - } - }; - myDestBranchTextField.setBorder(UIUtil.getTableFocusCellHighlightBorder()); - myDestBranchTextField.setOneLineMode(true); - myDestBranchTextField.setOpaque(true); - FocusAdapter focusListener = new FocusAdapter() { - @Override - public void focusGained(FocusEvent e) { - myDestBranchTextField.selectAll(); - } - }; - myDestBranchTextField.addFocusListener(focusListener); - addFocusListener(focusListener); - + myDestPushTargetPanelComponent = destPushTargetPanelComponent; myTextRenderer = new ColoredTreeCellRenderer() { public void customizeCellRenderer(@NotNull JTree tree, Object value, @@ -115,7 +79,7 @@ public class RepositoryWithBranchPanel extends NonOpaquePanel implements TreeCel add(myRepositoryCheckbox, BorderLayout.WEST); JPanel panel = new NonOpaquePanel(new BorderLayout()); panel.add(myTextRenderer, BorderLayout.WEST); - panel.add(myDestBranchTextField, BorderLayout.CENTER); + panel.add(myDestPushTargetPanelComponent, BorderLayout.CENTER); add(panel, BorderLayout.CENTER); } @@ -132,15 +96,6 @@ public class RepositoryWithBranchPanel extends NonOpaquePanel implements TreeCel return myArrowLabel.getText(); } - public TextFieldWithAutoCompletion getRemoteTextFiled() { - return myDestBranchTextField; - } - - @NotNull - public String getRemoteTargetName() { - return myDestBranchTextField.getText(); - } - @Override public Component getTreeCellRendererComponent(JTree tree, Object value, @@ -168,20 +123,20 @@ public class RepositoryWithBranchPanel extends NonOpaquePanel implements TreeCel if (bounds != null) { setPreferredSize(new Dimension(tree.getWidth() - bounds.x, bounds.height)); } - myDestBranchTextField.grabFocus(); - myDestBranchTextField.requestFocus(); + myDestPushTargetPanelComponent.grabFocus(); + myDestPushTargetPanelComponent.requestFocus(); revalidate(); return this; } - public void addRepoNodeListener(@NotNull RepositoryNodeListener listener) { + public void addRepoNodeListener(@NotNull RepositoryNodeListener listener) { myListeners.add(listener); } public void fireOnChange() { - myOldDestination = myDestBranchTextField.getText(); - for (RepositoryNodeListener listener : myListeners) { - listener.onTargetChanged(myOldDestination); + myDestPushTargetPanelComponent.fireOnChange(); + for (RepositoryNodeListener listener : myListeners) { + listener.onTargetChanged(myDestPushTargetPanelComponent.getValue()); } } @@ -192,7 +147,15 @@ public class RepositoryWithBranchPanel extends NonOpaquePanel implements TreeCel } public void fireOnCancel() { - myDestBranchTextField.setText(myOldDestination); + myDestPushTargetPanelComponent.fireOnCancel(); + } + + public PushTargetPanel getTargetPanel() { + return myDestPushTargetPanelComponent; + } + + public T getEditableValue() { + return myDestPushTargetPanelComponent.getValue(); } } diff --git a/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/SingleRepositoryNode.java b/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/SingleRepositoryNode.java index 81fb2bd1a9d0..8506e1d50fea 100644 --- a/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/SingleRepositoryNode.java +++ b/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/SingleRepositoryNode.java @@ -15,9 +15,8 @@ */ package com.intellij.dvcs.push.ui; +import com.intellij.dvcs.push.PushTargetPanel; import com.intellij.ui.ColoredTreeCellRenderer; -import com.intellij.ui.EditorTextField; -import com.intellij.ui.SimpleColoredText; import com.intellij.ui.SimpleTextAttributes; import org.jetbrains.annotations.NotNull; @@ -29,8 +28,8 @@ public class SingleRepositoryNode extends RepositoryNode { @NotNull private final RepositoryWithBranchPanel myRepositoryPanel; - public SingleRepositoryNode(@NotNull RepositoryWithBranchPanel repositoryPanel, @NotNull SimpleColoredText customTargetPresentation) { - super(repositoryPanel, customTargetPresentation); + public SingleRepositoryNode(@NotNull RepositoryWithBranchPanel repositoryPanel) { + super(repositoryPanel); myRepositoryPanel = repositoryPanel; } @@ -43,9 +42,9 @@ public class SingleRepositoryNode extends RepositoryNode { public void render(@NotNull ColoredTreeCellRenderer renderer) { renderer.append(myRepositoryPanel.getSourceName(), SimpleTextAttributes.REGULAR_ATTRIBUTES); renderer.append(myRepositoryPanel.getArrow(), SimpleTextAttributes.REGULAR_ATTRIBUTES); - EditorTextField textField = myRepositoryPanel.getRemoteTextFiled(); - renderTargetName(renderer, textField); - Insets insets = BorderFactory.createEmptyBorder().getBorderInsets(textField); + PushTargetPanel pushTargetPanel = myRepositoryPanel.getTargetPanel(); + pushTargetPanel.render(renderer); + Insets insets = BorderFactory.createEmptyBorder().getBorderInsets(pushTargetPanel); renderer.setBorder(new EmptyBorder(insets)); } } diff --git a/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/VcsBranchEditorListener.java b/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/VcsBranchEditorListener.java index d8e35fd25d53..0b3df11db78a 100644 --- a/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/VcsBranchEditorListener.java +++ b/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/VcsBranchEditorListener.java @@ -15,9 +15,9 @@ */ package com.intellij.dvcs.push.ui; +import com.intellij.dvcs.push.PushTargetPanel; import com.intellij.openapi.vcs.changes.issueLinks.LinkMouseListenerBase; import com.intellij.ui.CheckboxTree; -import com.intellij.ui.EditorTextField; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -35,7 +35,7 @@ public class VcsBranchEditorListener extends LinkMouseListenerBase { public void mouseMoved(MouseEvent e) { Component component = (Component)e.getSource(); Object tag = getTagAt(e); - if (tag != null && tag instanceof EditorTextField) { + if (tag != null && tag instanceof PushTargetPanel) { component.setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR)); } else if (tag != null && tag instanceof TextWithLinkNode) { diff --git a/platform/dvcs-impl/src/com/intellij/dvcs/repo/RepositoryUtil.java b/platform/dvcs-impl/src/com/intellij/dvcs/repo/RepositoryUtil.java index 5a5a555768ba..35b5bceebff4 100644 --- a/platform/dvcs-impl/src/com/intellij/dvcs/repo/RepositoryUtil.java +++ b/platform/dvcs-impl/src/com/intellij/dvcs/repo/RepositoryUtil.java @@ -20,7 +20,7 @@ import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.openapi.vfs.VfsUtil; +import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.Consumer; import com.intellij.util.Processor; @@ -101,15 +101,10 @@ public class RepositoryUtil { } public static void visitAllChildrenRecursively(@Nullable VirtualFile dir) { - if (dir == null) { - return; + if (dir != null) { + //noinspection unchecked + VfsUtilCore.processFilesRecursively(dir, Processor.TRUE); } - VfsUtil.processFilesRecursively(dir, new Processor() { - @Override - public boolean process(VirtualFile virtualFile) { - return true; - } - }); } public static class Updater implements Consumer { diff --git a/platform/editor-ui-api/src/com/intellij/ide/ui/UISettings.java b/platform/editor-ui-api/src/com/intellij/ide/ui/UISettings.java index 9f5f5d5720da..c61ce5f8f946 100644 --- a/platform/editor-ui-api/src/com/intellij/ide/ui/UISettings.java +++ b/platform/editor-ui-api/src/com/intellij/ide/ui/UISettings.java @@ -121,6 +121,7 @@ public class UISettings extends SimpleModificationTracker implements PersistentS public boolean MARK_MODIFIED_TABS_WITH_ASTERISK = false; public boolean SHOW_TABS_TOOLTIPS = true; public boolean SHOW_DIRECTORY_FOR_NON_UNIQUE_FILENAMES = true; + public boolean NAVIGATE_TO_PREVIEW = false; private final EventDispatcher myDispatcher = EventDispatcher.create(UISettingsListener.class); diff --git a/platform/editor-ui-api/src/com/intellij/openapi/actionSystem/AnAction.java b/platform/editor-ui-api/src/com/intellij/openapi/actionSystem/AnAction.java index 42da54a61d72..33be4de4b37d 100644 --- a/platform/editor-ui-api/src/com/intellij/openapi/actionSystem/AnAction.java +++ b/platform/editor-ui-api/src/com/intellij/openapi/actionSystem/AnAction.java @@ -229,7 +229,7 @@ public abstract class AnAction implements PossiblyDumbAware { * * @param e Carries information on the invocation place and data available */ - public void update(AnActionEvent e) { + public void update(@NotNull AnActionEvent e) { } /** @@ -268,7 +268,7 @@ public abstract class AnAction implements PossiblyDumbAware { * * @param e Carries information on the invocation place */ - public abstract void actionPerformed(AnActionEvent e); + public abstract void actionPerformed(@NotNull AnActionEvent e); protected void setShortcutSet(ShortcutSet shortcutSet) { myShortcutSet = shortcutSet; diff --git a/platform/editor-ui-ex/src/com/intellij/psi/impl/cache/impl/id/PlatformIdTableBuilding.java b/platform/editor-ui-ex/src/com/intellij/psi/impl/cache/impl/id/PlatformIdTableBuilding.java index 6a3dd82b237e..b1669144551a 100644 --- a/platform/editor-ui-ex/src/com/intellij/psi/impl/cache/impl/id/PlatformIdTableBuilding.java +++ b/platform/editor-ui-ex/src/com/intellij/psi/impl/cache/impl/id/PlatformIdTableBuilding.java @@ -23,17 +23,21 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.ex.util.LexerEditorHighlighter; import com.intellij.openapi.editor.highlighter.EditorHighlighter; import com.intellij.openapi.editor.highlighter.HighlighterIterator; -import com.intellij.openapi.fileTypes.*; +import com.intellij.openapi.fileTypes.FileType; +import com.intellij.openapi.fileTypes.InternalFileType; +import com.intellij.openapi.fileTypes.LanguageFileType; +import com.intellij.openapi.fileTypes.PlainTextFileType; import com.intellij.openapi.fileTypes.impl.CustomSyntaxTableFileType; import com.intellij.openapi.util.Key; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.CustomHighlighterTokenType; -import com.intellij.psi.impl.cache.impl.BaseFilterLexer; import com.intellij.psi.impl.cache.CacheUtil; +import com.intellij.psi.impl.cache.impl.BaseFilterLexer; import com.intellij.psi.impl.cache.impl.IndexPatternUtil; import com.intellij.psi.impl.cache.impl.OccurrenceConsumer; import com.intellij.psi.impl.cache.impl.todo.TodoIndexEntry; import com.intellij.psi.impl.cache.impl.todo.TodoIndexers; +import com.intellij.psi.impl.cache.impl.todo.VersionedTodoIndexer; import com.intellij.psi.search.IndexPattern; import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.TokenSet; @@ -116,7 +120,7 @@ public abstract class PlatformIdTableBuilding { return ourTodoIndexers.containsKey(fileType) || TodoIndexers.INSTANCE.forFileType(fileType) != null || fileType instanceof InternalFileType; } - private static class CompositeTodoIndexer implements DataIndexer { + private static class CompositeTodoIndexer extends VersionedTodoIndexer { private final DataIndexer[] indexers; public CompositeTodoIndexer(@NotNull DataIndexer... indexers) { @@ -139,9 +143,18 @@ public abstract class PlatformIdTableBuilding { } return result; } + + @Override + public int getVersion() { + int version = super.getVersion(); + for(DataIndexer dataIndexer:indexers) { + version += dataIndexer instanceof VersionedTodoIndexer ? ((VersionedTodoIndexer)dataIndexer).getVersion() : 0xFF; + } + return version; + } } - private static class TokenSetTodoIndexer implements DataIndexer { + private static class TokenSetTodoIndexer extends VersionedTodoIndexer { @NotNull private final TokenSet myCommentTokens; private final VirtualFile myFile; @@ -201,7 +214,7 @@ public abstract class PlatformIdTableBuilding { } } - public static class PlainTextTodoIndexer implements DataIndexer { + public static class PlainTextTodoIndexer extends VersionedTodoIndexer { @Override @NotNull public Map map(@NotNull final FileContent inputData) { diff --git a/platform/editor-ui-ex/src/com/intellij/psi/impl/cache/impl/todo/TodoIndex.java b/platform/editor-ui-ex/src/com/intellij/psi/impl/cache/impl/todo/TodoIndex.java index d955354cca7f..f2101b6e59a4 100644 --- a/platform/editor-ui-ex/src/com/intellij/psi/impl/cache/impl/todo/TodoIndex.java +++ b/platform/editor-ui-ex/src/com/intellij/psi/impl/cache/impl/todo/TodoIndex.java @@ -20,8 +20,10 @@ import com.intellij.lang.Language; import com.intellij.lang.LanguageParserDefinitions; import com.intellij.lang.ParserDefinition; import com.intellij.openapi.fileTypes.FileType; +import com.intellij.openapi.fileTypes.FileTypeRegistry; import com.intellij.openapi.fileTypes.LanguageFileType; import com.intellij.openapi.fileTypes.impl.CustomSyntaxTableFileType; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.impl.cache.impl.id.PlatformIdTableBuilding; import com.intellij.psi.search.IndexPatternProvider; @@ -39,7 +41,9 @@ import java.beans.PropertyChangeListener; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; +import java.util.Arrays; import java.util.Collections; +import java.util.Comparator; import java.util.Map; /** @@ -49,7 +53,10 @@ import java.util.Map; public class TodoIndex extends FileBasedIndexExtension { @NonNls public static final ID NAME = ID.create("TodoIndex"); - public TodoIndex(MessageBus messageBus) { + private final FileTypeRegistry myFileTypeManager; + + public TodoIndex(MessageBus messageBus, FileTypeRegistry manager) { + myFileTypeManager = manager; messageBus.connect().subscribe(IndexPatternProvider.INDEX_PATTERNS_CHANGED, new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { @@ -127,7 +134,23 @@ public class TodoIndex extends FileBasedIndexExtension @Override public int getVersion() { - return 8; + int version = 8; + FileType[] types = myFileTypeManager.getRegisteredFileTypes(); + Arrays.sort(types, new Comparator() { + @Override + public int compare(FileType o1, FileType o2) { + return Comparing.compare(o1.getName(), o2.getName()); + } + }); + + for(FileType fileType:types) { + DataIndexer indexer = TodoIndexers.INSTANCE.forFileType(fileType); + if (indexer == null) continue; + + int versionFromIndexer = indexer instanceof VersionedTodoIndexer ? (((VersionedTodoIndexer)indexer).getVersion()) : 0xFF; + version = version * 31 + (versionFromIndexer ^ indexer.getClass().getName().hashCode()); + } + return version; } @Override diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/DefaultExternalSourceDirectorySet.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/DefaultExternalSourceDirectorySet.java index d6a8d7aa6291..8121c0ccaa8a 100644 --- a/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/DefaultExternalSourceDirectorySet.java +++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/DefaultExternalSourceDirectorySet.java @@ -43,6 +43,8 @@ public class DefaultExternalSourceDirectorySet implements ExternalSourceDirector @NotNull private List myFilters; + private boolean myInheritedCompilerOutput; + public DefaultExternalSourceDirectorySet() { mySrcDirs = new HashSet(); myExcludes = new HashSet(); @@ -88,6 +90,15 @@ public class DefaultExternalSourceDirectorySet implements ExternalSourceDirector return myOutputDir; } + @Override + public boolean isCompilerOutputPathInherited() { + return myInheritedCompilerOutput; + } + + public void setInheritedCompilerOutput(boolean inheritedCompilerOutput) { + myInheritedCompilerOutput = inheritedCompilerOutput; + } + @NotNull @Override public Set getIncludes() { diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/ExternalSourceDirectorySet.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/ExternalSourceDirectorySet.java index 70f0bed3d177..530d4d4c0695 100644 --- a/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/ExternalSourceDirectorySet.java +++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/ExternalSourceDirectorySet.java @@ -36,6 +36,12 @@ public interface ExternalSourceDirectorySet extends Serializable { @NotNull File getOutputDir(); + /** + * Returns true if compiler output for this ExternalSourceDirectorySet should is inherited from IDEA project + * @return true if compiler output path is inherited, false otherwise + */ + boolean isCompilerOutputPathInherited(); + @NotNull Set getExcludes(); @NotNull diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/LibraryDataService.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/LibraryDataService.java index d04d728966bb..873a12fe8eb3 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/LibraryDataService.java +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/LibraryDataService.java @@ -152,7 +152,7 @@ public class LibraryDataService implements ProjectDataService aClass) { diff --git a/platform/external-system-impl/testSrc/com/intellij/openapi/externalSystem/test/ExternalSystemImportingTestCase.java b/platform/external-system-impl/testSrc/com/intellij/openapi/externalSystem/test/ExternalSystemImportingTestCase.java index 6107a00b7107..d55ceb0fe97c 100644 --- a/platform/external-system-impl/testSrc/com/intellij/openapi/externalSystem/test/ExternalSystemImportingTestCase.java +++ b/platform/external-system-impl/testSrc/com/intellij/openapi/externalSystem/test/ExternalSystemImportingTestCase.java @@ -157,6 +157,11 @@ public abstract class ExternalSystemImportingTestCase extends ExternalSystemTest assertEquals(testOutput, getAbsolutePath(e.getCompilerOutputUrlForTests())); } + protected void assertModuleInheritedOutput(String moduleName) { + CompilerModuleExtension e = getCompilerExtension(moduleName); + assertTrue(e.isCompilerOutputPathInherited()); + } + private static String getAbsolutePath(String path) { path = VfsUtil.urlToPath(path); path = PathUtil.getCanonicalPath(path); @@ -168,8 +173,7 @@ public abstract class ExternalSystemImportingTestCase extends ExternalSystemTest } protected CompilerModuleExtension getCompilerExtension(String module) { - ModuleRootManager m = getRootManager(module); - return CompilerModuleExtension.getInstance(m.getModule()); + return CompilerModuleExtension.getInstance(getModule(module)); } protected void assertModuleLibDep(String moduleName, String depName) { diff --git a/platform/icons/src/debugger/commandLine.png b/platform/icons/src/debugger/commandLine.png new file mode 100644 index 000000000000..3d2333b0e7e8 Binary files /dev/null and b/platform/icons/src/debugger/commandLine.png differ diff --git a/platform/icons/src/debugger/commandLine@2x.png b/platform/icons/src/debugger/commandLine@2x.png new file mode 100644 index 000000000000..a70992337f1e Binary files /dev/null and b/platform/icons/src/debugger/commandLine@2x.png differ diff --git a/platform/indexing-api/src/com/intellij/lang/cacheBuilder/DefaultWordsScanner.java b/platform/indexing-api/src/com/intellij/lang/cacheBuilder/DefaultWordsScanner.java index c4a306286708..824202fd0807 100644 --- a/platform/indexing-api/src/com/intellij/lang/cacheBuilder/DefaultWordsScanner.java +++ b/platform/indexing-api/src/com/intellij/lang/cacheBuilder/DefaultWordsScanner.java @@ -27,7 +27,7 @@ import org.jetbrains.annotations.NotNull; * @author max */ -public class DefaultWordsScanner implements WordsScanner { +public class DefaultWordsScanner extends VersionedWordsScanner { private final Lexer myLexer; private final TokenSet myIdentifierTokenSet; private final TokenSet myCommentTokenSet; diff --git a/platform/indexing-api/src/com/intellij/lang/cacheBuilder/SimpleWordsScanner.java b/platform/indexing-api/src/com/intellij/lang/cacheBuilder/SimpleWordsScanner.java index 6273654982a9..069c8643ae19 100644 --- a/platform/indexing-api/src/com/intellij/lang/cacheBuilder/SimpleWordsScanner.java +++ b/platform/indexing-api/src/com/intellij/lang/cacheBuilder/SimpleWordsScanner.java @@ -25,7 +25,7 @@ import com.intellij.util.text.CharArrayUtil; * * @author max */ -public class SimpleWordsScanner implements WordsScanner { +public class SimpleWordsScanner extends VersionedWordsScanner { public void processWords(CharSequence fileText, Processor processor) { int index = 0; WordOccurrence occurrence = null; diff --git a/platform/indexing-api/src/com/intellij/lang/cacheBuilder/VersionedWordsScanner.java b/platform/indexing-api/src/com/intellij/lang/cacheBuilder/VersionedWordsScanner.java new file mode 100644 index 000000000000..f8d27825d14e --- /dev/null +++ b/platform/indexing-api/src/com/intellij/lang/cacheBuilder/VersionedWordsScanner.java @@ -0,0 +1,22 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.lang.cacheBuilder; + +public abstract class VersionedWordsScanner implements WordsScanner { + public int getVersion() { + return 1; + } +} diff --git a/platform/indexing-api/src/com/intellij/psi/search/PsiSearchHelper.java b/platform/indexing-api/src/com/intellij/psi/search/PsiSearchHelper.java index bef3d0350563..8f2db591888d 100644 --- a/platform/indexing-api/src/com/intellij/psi/search/PsiSearchHelper.java +++ b/platform/indexing-api/src/com/intellij/psi/search/PsiSearchHelper.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -58,7 +58,6 @@ public interface PsiSearchHelper { * * @param identifier the identifier to search. * @param searchScope the scope in which occurrences are searched. - * @param processor * @return false if processor returned false, true otherwise */ boolean processCommentsContainingIdentifier(@NotNull String identifier, @NotNull SearchScope searchScope, @NotNull Processor processor); diff --git a/platform/indexing-api/src/com/intellij/psi/search/searches/DefinitionsScopedSearch.java b/platform/indexing-api/src/com/intellij/psi/search/searches/DefinitionsScopedSearch.java index 4e94617e7681..001712e0f529 100644 --- a/platform/indexing-api/src/com/intellij/psi/search/searches/DefinitionsScopedSearch.java +++ b/platform/indexing-api/src/com/intellij/psi/search/searches/DefinitionsScopedSearch.java @@ -63,8 +63,13 @@ public class DefinitionsScopedSearch extends ExtensibleQueryFactory() { + @Override + public SearchScope compute() { + return element.getUseScope(); + } + }), true); } public SearchParameters(@NotNull PsiElement element, @NotNull SearchScope scope, final boolean checkDeep) { diff --git a/platform/indexing-impl/src/com/intellij/psi/impl/cache/impl/id/FileTypeIdIndexer.java b/platform/indexing-impl/src/com/intellij/psi/impl/cache/impl/id/FileTypeIdIndexer.java index f770ac1d5d97..5c1246c7a50b 100644 --- a/platform/indexing-impl/src/com/intellij/psi/impl/cache/impl/id/FileTypeIdIndexer.java +++ b/platform/indexing-impl/src/com/intellij/psi/impl/cache/impl/id/FileTypeIdIndexer.java @@ -24,4 +24,7 @@ import com.intellij.util.indexing.FileContent; * Date: Jan 16, 2008 */ public abstract class FileTypeIdIndexer implements DataIndexer { + public int getVersion() { + return 1; + } } diff --git a/platform/indexing-impl/src/com/intellij/psi/impl/cache/impl/id/IdIndex.java b/platform/indexing-impl/src/com/intellij/psi/impl/cache/impl/id/IdIndex.java index 9929c1c586d2..af193348c71b 100644 --- a/platform/indexing-impl/src/com/intellij/psi/impl/cache/impl/id/IdIndex.java +++ b/platform/indexing-impl/src/com/intellij/psi/impl/cache/impl/id/IdIndex.java @@ -91,7 +91,7 @@ public class IdIndex extends FileBasedIndexExtension { @Override public int getVersion() { - return 12 + (ourSnapshotMappingsEnabled ? 0xFF:0); // TODO: version should enumerate all word scanner versions and build version upon that set + return 13 + (ourSnapshotMappingsEnabled ? 0xFF:0); // TODO: version should enumerate all word scanner versions and build version upon that set } @Override diff --git a/platform/indexing-impl/src/com/intellij/psi/impl/cache/impl/id/IdTableBuilding.java b/platform/indexing-impl/src/com/intellij/psi/impl/cache/impl/id/IdTableBuilding.java index 8519453e3e6c..c2ea447400cc 100644 --- a/platform/indexing-impl/src/com/intellij/psi/impl/cache/impl/id/IdTableBuilding.java +++ b/platform/indexing-impl/src/com/intellij/psi/impl/cache/impl/id/IdTableBuilding.java @@ -161,6 +161,11 @@ public class IdTableBuilding { }); return consumer.getResult(); } + + @Override + public int getVersion() { + return myScanner instanceof VersionedWordsScanner ? ((VersionedWordsScanner)myScanner).getVersion() : -1; + } } public static void scanWords(final ScanWordProcessor processor, final CharSequence chars, final int startOffset, final int endOffset) { diff --git a/platform/indexing-impl/src/com/intellij/psi/impl/cache/impl/todo/LexerBasedTodoIndexer.java b/platform/indexing-impl/src/com/intellij/psi/impl/cache/impl/todo/LexerBasedTodoIndexer.java index b05ef6a9853a..04e27e91dc63 100644 --- a/platform/indexing-impl/src/com/intellij/psi/impl/cache/impl/todo/LexerBasedTodoIndexer.java +++ b/platform/indexing-impl/src/com/intellij/psi/impl/cache/impl/todo/LexerBasedTodoIndexer.java @@ -18,7 +18,6 @@ package com.intellij.psi.impl.cache.impl.todo; import com.intellij.psi.impl.cache.impl.BaseFilterLexerUtil; import com.intellij.psi.impl.cache.impl.IdAndToDoScannerBasedOnFilterLexer; -import com.intellij.util.indexing.DataIndexer; import com.intellij.util.indexing.FileContent; import org.jetbrains.annotations.NotNull; @@ -28,8 +27,7 @@ import java.util.Map; * @author Eugene Zhuravlev * Date: Jan 20, 2008 */ -public abstract class LexerBasedTodoIndexer implements DataIndexer, - IdAndToDoScannerBasedOnFilterLexer { +public abstract class LexerBasedTodoIndexer extends VersionedTodoIndexer implements IdAndToDoScannerBasedOnFilterLexer { @Override @NotNull public Map map(@NotNull final FileContent inputData) { diff --git a/platform/indexing-impl/src/com/intellij/psi/impl/cache/impl/todo/VersionedTodoIndexer.java b/platform/indexing-impl/src/com/intellij/psi/impl/cache/impl/todo/VersionedTodoIndexer.java new file mode 100644 index 000000000000..028c24b77eed --- /dev/null +++ b/platform/indexing-impl/src/com/intellij/psi/impl/cache/impl/todo/VersionedTodoIndexer.java @@ -0,0 +1,25 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.psi.impl.cache.impl.todo; + +import com.intellij.util.indexing.DataIndexer; +import com.intellij.util.indexing.FileContent; + +public abstract class VersionedTodoIndexer implements DataIndexer { + public int getVersion() { + return 1; + } +} diff --git a/platform/indexing-impl/src/com/intellij/psi/impl/search/PsiSearchHelperImpl.java b/platform/indexing-impl/src/com/intellij/psi/impl/search/PsiSearchHelperImpl.java index b5c3dd17afb7..ba8d9ce3500e 100644 --- a/platform/indexing-impl/src/com/intellij/psi/impl/search/PsiSearchHelperImpl.java +++ b/platform/indexing-impl/src/com/intellij/psi/impl/search/PsiSearchHelperImpl.java @@ -23,7 +23,6 @@ import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressIndicatorProvider; -import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.progress.util.TooManyUsagesStatus; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.FileIndexFacade; @@ -56,7 +55,6 @@ import org.jetbrains.annotations.Nullable; import java.io.IOException; import java.util.*; -import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; @@ -132,9 +130,7 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { short searchContext, boolean caseSensitive, boolean processInjectedPsi) { - final AsyncFuture result = - processElementsWithWordAsync(processor, searchScope, text, searchContext, caseSensitive, processInjectedPsi, null); - return AsyncUtil.get(result); + return processElementsWithWord(processor, searchScope, text, searchContext, caseSensitive, processInjectedPsi, null); } @NotNull @@ -144,50 +140,39 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { @NotNull final String text, final short searchContext, final boolean caseSensitively) { - return processElementsWithWordAsync(processor, searchScope, text, searchContext, caseSensitively, shouldProcessInjectedPsi(searchScope), null); - } - - @NotNull - private AsyncFuture processElementsWithWordAsync(@NotNull final TextOccurenceProcessor processor, - @NotNull SearchScope searchScope, - @NotNull final String text, - final short searchContext, - final boolean caseSensitively, - boolean processInjectedPsi, - @Nullable String containerName) { + boolean result = + processElementsWithWord(processor, searchScope, text, searchContext, caseSensitively, shouldProcessInjectedPsi(searchScope), null); + return AsyncUtil.wrapBoolean(result); + } + + private boolean processElementsWithWord(@NotNull final TextOccurenceProcessor processor, + @NotNull SearchScope searchScope, + @NotNull final String text, + final short searchContext, + final boolean caseSensitively, + boolean processInjectedPsi, + @Nullable String containerName) { if (text.isEmpty()) { - return AsyncFutureFactory.wrapException(new IllegalArgumentException("Cannot search for elements with empty text")); + throw new IllegalArgumentException("Cannot search for elements with empty text"); } final ProgressIndicator progress = ProgressIndicatorProvider.getGlobalProgressIndicator(); if (searchScope instanceof GlobalSearchScope) { StringSearcher searcher = new StringSearcher(text, caseSensitively, true, searchContext == UsageSearchContext.IN_STRINGS); - return processElementsWithTextInGlobalScopeAsync(processor, - (GlobalSearchScope)searchScope, - searcher, - searchContext, caseSensitively, containerName, progress, processInjectedPsi); + return processElementsWithTextInGlobalScope(processor, + (GlobalSearchScope)searchScope, + searcher, + searchContext, caseSensitively, containerName, progress, processInjectedPsi); } LocalSearchScope scope = (LocalSearchScope)searchScope; PsiElement[] scopeElements = scope.getScope(); final StringSearcher searcher = new StringSearcher(text, caseSensitively, true, searchContext == UsageSearchContext.IN_STRINGS); Processor localProcessor = localProcessor(processor, progress, processInjectedPsi, searcher); - return wrapInFuture(Arrays.asList(scopeElements), progress, localProcessor); - } - - private static AsyncFuture wrapInFuture(@NotNull List files, final ProgressIndicator progress, @NotNull Processor processor) { - AsyncFutureResult asyncFutureResult = AsyncFutureFactory.getInstance().createAsyncFutureResult(); - try { - boolean result = JobLauncher.getInstance().invokeConcurrentlyUnderProgress(files, progress, true, true, processor); - asyncFutureResult.set(result); - } - catch (Throwable t) { - asyncFutureResult.setException(t); - } - return asyncFutureResult; + return JobLauncher.getInstance().invokeConcurrentlyUnderProgress(Arrays.asList(scopeElements), progress, true, true, localProcessor); } private static boolean shouldProcessInjectedPsi(SearchScope scope) { - return scope instanceof LocalSearchScope ? !((LocalSearchScope)scope).isIgnoreInjectedPsi() : true; + return !(scope instanceof LocalSearchScope) || !((LocalSearchScope)scope).isIgnoreInjectedPsi(); } @NotNull @@ -213,14 +198,13 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { }; } - @NotNull - private AsyncFuture processElementsWithTextInGlobalScopeAsync(@NotNull final TextOccurenceProcessor processor, - @NotNull final GlobalSearchScope scope, - @NotNull final StringSearcher searcher, - final short searchContext, - final boolean caseSensitively, - String containerName, - final ProgressIndicator progress, final boolean processInjectedPsi) { + private boolean processElementsWithTextInGlobalScope(@NotNull final TextOccurenceProcessor processor, + @NotNull final GlobalSearchScope scope, + @NotNull final StringSearcher searcher, + final short searchContext, + final boolean caseSensitively, + String containerName, + final ProgressIndicator progress, final boolean processInjectedPsi) { if (Thread.holdsLock(PsiLock.LOCK)) { throw new AssertionError("You must not run search from within updating PSI activity. Please consider invokeLatering it instead."); } @@ -244,70 +228,28 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { getFilesWithText(scope, searchContext, caseSensitively, text+" "+containerName, progress, intersectionWithContainerFiles); if (!intersectionWithContainerFiles.isEmpty()) { int totalSize = fileSet.size(); - AsyncFuture intersectionResult = processPsiFileRootsAsync(intersectionWithContainerFiles, totalSize, 0, progress, localProcessor); - AsyncFuture result; - try { - if (intersectionResult.get()) { - fileSet.removeAll(intersectionWithContainerFiles); - if (fileSet.isEmpty()) { - result = intersectionResult; - } - else { - AsyncFuture restResult = - processPsiFileRootsAsync(new ArrayList(fileSet), totalSize, intersectionWithContainerFiles.size(), progress, localProcessor); - result = bind(intersectionResult, restResult); - } - } - else { - result = intersectionResult; + boolean result = processPsiFileRoots(intersectionWithContainerFiles, totalSize, 0, progress, + localProcessor); + + if (result) { + fileSet.removeAll(intersectionWithContainerFiles); + if (!fileSet.isEmpty()) { + result = processPsiFileRoots(new ArrayList(fileSet), totalSize, intersectionWithContainerFiles.size(), progress, localProcessor); } } - catch (ExecutionException e) { - Throwable cause = e.getCause(); - if (cause instanceof RuntimeException) throw (RuntimeException)cause; - if (cause instanceof Error) throw (Error)cause; - result = AsyncFutureFactory.wrapException(cause); - } - catch (InterruptedException e) { - result = AsyncFutureFactory.wrapException(e); - } - return popStateAfter(result, progress); - } - } - - AsyncFuture result = fileSet.isEmpty() - ? AsyncFutureFactory.wrap(Boolean.TRUE) - : processPsiFileRootsAsync(new ArrayList(fileSet), fileSet.size(), 0, progress, localProcessor); - return popStateAfter(result, progress); - } - - @NotNull - private static FinallyFuture popStateAfter(@NotNull AsyncFuture result, final ProgressIndicator progress) { - return new FinallyFuture(result, new Runnable() { - @Override - public void run() { if (progress != null) { progress.popState(); } + return result; } - }); - } - - @NotNull - private static AsyncFuture bind(@NotNull AsyncFuture first, @NotNull final AsyncFuture second) { - final AsyncFutureResult totalResult = AsyncFutureFactory.getInstance().createAsyncFutureResult(); - first.addConsumer(SameThreadExecutor.INSTANCE, new ResultConsumer() { - @Override - public void onSuccess(Boolean value) { - second.addConsumer(SameThreadExecutor.INSTANCE, new DefaultResultConsumer(totalResult)); - } + } - @Override - public void onFailure(Throwable t) { - totalResult.setException(t); - } - }); - return totalResult; + boolean result = + fileSet.isEmpty() || processPsiFileRoots(new ArrayList(fileSet), fileSet.size(), 0, progress, localProcessor); + if (progress != null) { + progress.popState(); + } + return result; } /** @@ -315,20 +257,19 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { * @param totalSize the number of files to scan in both passes. Can be different from files.size() in case of * two-pass scan, where we first scan files containing container name and then all the rest files. * @param alreadyProcessedFiles the number of files scanned in previous pass. + * @return true if completed */ - @NotNull - private AsyncFuture processPsiFileRootsAsync(@NotNull List files, - final int totalSize, - int alreadyProcessedFiles, - final ProgressIndicator progress, - @NotNull final Processor localProcessor) { + private boolean processPsiFileRoots(@NotNull List files, + final int totalSize, + int alreadyProcessedFiles, + final ProgressIndicator progress, + @NotNull final Processor localProcessor) { myManager.startBatchFilesProcessingMode(); - final AtomicInteger counter = new AtomicInteger(alreadyProcessedFiles); - final AtomicBoolean canceled = new AtomicBoolean(false); - - AsyncFutureResult asyncFutureResult = AsyncFutureFactory.getInstance().createAsyncFutureResult(); - final List failedFiles = Collections.synchronizedList(new SmartList()); try { + final AtomicInteger counter = new AtomicInteger(alreadyProcessedFiles); + final AtomicBoolean canceled = new AtomicBoolean(false); + + final List failedFiles = Collections.synchronizedList(new SmartList()); boolean completed = JobLauncher.getInstance().invokeConcurrentlyUnderProgress(files, progress, false, false, new Processor() { @Override @@ -357,14 +298,12 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { }); } } - asyncFutureResult.set(completed); - myManager.finishBatchFilesProcessingMode(); + return completed; } - catch (Throwable t) { - asyncFutureResult.setException(t); + finally { + myManager.finishBatchFilesProcessingMode(); } - return asyncFutureResult; } private void processVirtualFile(@NotNull final VirtualFile vfile, @@ -423,9 +362,6 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { if (progress != null) { progress.checkCanceled(); } - else { - ProgressManager.checkCanceled(); - } } private void getFilesWithText(@NotNull GlobalSearchScope scope, @@ -648,65 +584,32 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { final Map> collectors = ContainerUtil.newHashMap(); collectors.put(collector, processor); + ProgressIndicator progress = ProgressIndicatorProvider.getGlobalProgressIndicator(); appendCollectorsFromQueryRequests(collectors); - - final ProgressIndicator progress = ProgressIndicatorProvider.getGlobalProgressIndicator(); - final DoWhile doWhile = new DoWhile() { - @NotNull - @Override - protected AsyncFuture body() { - final AsyncFutureResult result = AsyncFutureFactory.getInstance().createAsyncFutureResult(); - MultiMap, RequestWithProcessor> globals = new MultiMap, RequestWithProcessor>(); - final List> customs = ContainerUtil.newArrayList(); - final Set locals = ContainerUtil.newLinkedHashSet(); - Map> localProcessors = new THashMap>(); - distributePrimitives(collectors, locals, globals, customs, localProcessors, progress); - AsyncFuture future = processGlobalRequestsOptimizedAsync(globals, progress, localProcessors); - future.addConsumer(SameThreadExecutor.INSTANCE, new DefaultResultConsumer(result) { - @Override - public void onSuccess(Boolean value) { - if (!value.booleanValue()) { - result.set(value); - } - else { - final Iterate iterate = new Iterate(locals) { - @NotNull - @Override - protected AsyncFuture process(RequestWithProcessor local) { - return processSingleRequestAsync(local.request, local.refProcessor); - } - }; - - iterate.getResult() - .addConsumer(SameThreadExecutor.INSTANCE, new DefaultResultConsumer(result) { - @Override - public void onSuccess(Boolean value) { - if (!value.booleanValue()) { - result.set(false); - return; - } - for (Computable custom : customs) { - if (!custom.compute()) { - result.set(false); - return; - } - } - result.set(true); - } - }); - } + boolean result; + do { + MultiMap, RequestWithProcessor> globals = new MultiMap, RequestWithProcessor>(); + final List> customs = ContainerUtil.newArrayList(); + final Set locals = ContainerUtil.newLinkedHashSet(); + Map> localProcessors = new THashMap>(); + distributePrimitives(collectors, locals, globals, customs, localProcessors, progress); + result = processGlobalRequestsOptimized(globals, progress, localProcessors); + if (result) { + for (RequestWithProcessor local : locals) { + result = processSingleRequest(local.request, local.refProcessor); + if (!result) break; + } + if (result) { + for (Computable custom : customs) { + result = custom.compute(); + if (!result) break; } - }); - return result; - } - - @Override - protected boolean condition() { - return appendCollectorsFromQueryRequests(collectors); + } + if (!result) break; } - }; - - return doWhile.getResult(); + } + while(appendCollectorsFromQueryRequests(collectors)); + return AsyncUtil.wrapBoolean(result); } private static boolean appendCollectorsFromQueryRequests(@NotNull Map> collectors) { @@ -725,19 +628,18 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { return changed; } - @NotNull - private AsyncFuture processGlobalRequestsOptimizedAsync(@NotNull MultiMap, RequestWithProcessor> singles, - final ProgressIndicator progress, - @NotNull final Map> localProcessors) { + private boolean processGlobalRequestsOptimized(@NotNull MultiMap, RequestWithProcessor> singles, + final ProgressIndicator progress, + @NotNull final Map> localProcessors) { if (singles.isEmpty()) { - return AsyncFutureFactory.wrap(true); + return true; } if (singles.size() == 1) { final Collection requests = singles.values(); if (requests.size() == 1) { final RequestWithProcessor theOnly = requests.iterator().next(); - return processSingleRequestAsync(theOnly.request, theOnly.refProcessor); + return processSingleRequest(theOnly.request, theOnly.refProcessor); } } @@ -745,64 +647,55 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { progress.pushState(); progress.setText(PsiBundle.message("psi.scanning.files.progress")); } + boolean result; - // intersectionCandidateFiles holds files containing words from all requests in `singles` and words in corresponding container names - final MultiMap intersectionCandidateFiles = createMultiMap(); - // restCandidateFiles holds files containing words from all requests in `singles` but EXCLUDING words in corresponding container names - final MultiMap restCandidateFiles = createMultiMap(); - collectFiles(singles, progress, intersectionCandidateFiles, restCandidateFiles); - - if (intersectionCandidateFiles.isEmpty() && restCandidateFiles.isEmpty()) { - return AsyncFutureFactory.wrap(true); - } + try { + // intersectionCandidateFiles holds files containing words from all requests in `singles` and words in corresponding container names + final MultiMap intersectionCandidateFiles = createMultiMap(); + // restCandidateFiles holds files containing words from all requests in `singles` but EXCLUDING words in corresponding container names + final MultiMap restCandidateFiles = createMultiMap(); + collectFiles(singles, progress, intersectionCandidateFiles, restCandidateFiles); - if (progress != null) { - final Set allWords = new TreeSet(); - for (RequestWithProcessor singleRequest : localProcessors.keySet()) { - allWords.add(singleRequest.request.word); + if (intersectionCandidateFiles.isEmpty() && restCandidateFiles.isEmpty()) { + return true; } - progress.setText(PsiBundle.message("psi.search.for.word.progress", getPresentableWordsDescription(allWords))); - } - AsyncFuture result; - if (intersectionCandidateFiles.isEmpty()) { - result = processCandidatesAsync(progress, localProcessors, restCandidateFiles, restCandidateFiles.size(), 0); - } - else { - int totalSize = restCandidateFiles.size() + intersectionCandidateFiles.size(); - AsyncFuture intersectionResult = processCandidatesAsync(progress, localProcessors, intersectionCandidateFiles, totalSize, 0); - try { - if (intersectionResult.get()) { - AsyncFuture restResult = processCandidatesAsync(progress, localProcessors, restCandidateFiles, totalSize, intersectionCandidateFiles.size()); - result = bind(intersectionResult, restResult); - } - else { - result = intersectionResult; + if (progress != null) { + final Set allWords = new TreeSet(); + for (RequestWithProcessor singleRequest : localProcessors.keySet()) { + allWords.add(singleRequest.request.word); } + progress.setText(PsiBundle.message("psi.search.for.word.progress", getPresentableWordsDescription(allWords))); } - catch (ExecutionException e) { - Throwable cause = e.getCause(); - if (cause instanceof RuntimeException) throw (RuntimeException)cause; - if (cause instanceof Error) throw (Error)cause; - result = AsyncFutureFactory.wrapException(cause); + + if (intersectionCandidateFiles.isEmpty()) { + result = processCandidates(progress, localProcessors, restCandidateFiles, restCandidateFiles.size(), 0); + } + else { + int totalSize = restCandidateFiles.size() + intersectionCandidateFiles.size(); + result = processCandidates(progress, localProcessors, intersectionCandidateFiles, totalSize, 0); + if (result) { + result = processCandidates(progress, localProcessors, restCandidateFiles, totalSize, intersectionCandidateFiles.size()); + } } - catch (InterruptedException e) { - result = AsyncFutureFactory.wrapException(e); + } + finally { + if (progress != null) { + progress.popState(); } } - return popStateAfter(result, progress); + return result; } - @NotNull - private AsyncFuture processCandidatesAsync(final ProgressIndicator progress, - @NotNull final Map> localProcessors, - @NotNull final MultiMap candidateFiles, - int totalSize, - int alreadyProcessedFiles) { + private boolean processCandidates(final ProgressIndicator progress, + @NotNull final Map> localProcessors, + @NotNull final MultiMap candidateFiles, + int totalSize, + int alreadyProcessedFiles) { List files = new ArrayList(candidateFiles.keySet()); - return processPsiFileRootsAsync(files, totalSize, alreadyProcessedFiles, progress, new Processor() { + return processPsiFileRoots(files, totalSize, alreadyProcessedFiles, progress, new Processor() { @Override public boolean process(final PsiFile psiRoot) { return ApplicationUtil.tryRunReadAction(new Computable() { @@ -1023,10 +916,9 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { collection.add(singleRequest); } - @NotNull - private AsyncFuture processSingleRequestAsync(@NotNull PsiSearchRequest single, @NotNull Processor consumer) { - return processElementsWithWordAsync(adaptProcessor(single, consumer), single.searchScope, single.word, single.searchContext, - single.caseSensitive, shouldProcessInjectedPsi(single.searchScope), single.containerName); + private boolean processSingleRequest(@NotNull PsiSearchRequest single, @NotNull Processor consumer) { + return processElementsWithWord(adaptProcessor(single, consumer), single.searchScope, single.word, single.searchContext, + single.caseSensitive, shouldProcessInjectedPsi(single.searchScope), single.containerName); } @NotNull diff --git a/platform/lang-api/src/com/intellij/diagnostic/logging/AdditionalTabComponent.java b/platform/lang-api/src/com/intellij/diagnostic/logging/AdditionalTabComponent.java index b45abc181736..4991071477fb 100644 --- a/platform/lang-api/src/com/intellij/diagnostic/logging/AdditionalTabComponent.java +++ b/platform/lang-api/src/com/intellij/diagnostic/logging/AdditionalTabComponent.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.intellij.diagnostic.logging; import com.intellij.openapi.ui.ComponentContainer; @@ -36,6 +35,7 @@ public abstract class AdditionalTabComponent extends JPanel implements Component protected AdditionalTabComponent() { } + @NotNull public abstract String getTabTitle(); @Nullable diff --git a/platform/lang-api/src/com/intellij/execution/DefaultExecutionResult.java b/platform/lang-api/src/com/intellij/execution/DefaultExecutionResult.java index 8a951da7d74f..75f117122e96 100644 --- a/platform/lang-api/src/com/intellij/execution/DefaultExecutionResult.java +++ b/platform/lang-api/src/com/intellij/execution/DefaultExecutionResult.java @@ -34,7 +34,8 @@ public class DefaultExecutionResult implements ExecutionResult { private final ExecutionConsole myConsole; private final ProcessHandler myProcessHandler; private AnAction[] myActions; - private AnAction[] myRestartActions; + @NotNull + private AnAction[] myRestartActions = AnAction.EMPTY_ARRAY; private final List myStopActions = new ArrayList(); public DefaultExecutionResult() { @@ -67,12 +68,14 @@ public class DefaultExecutionResult implements ExecutionResult { myActions = actions; } + @NotNull public AnAction[] getRestartActions() { return myRestartActions; } - public void setRestartActions(AnAction... restartActions) { - myRestartActions = restartActions; + // TODO: Find all usages, make sure there is no null and make this method NotNull + public void setRestartActions(@Nullable AnAction... restartActions) { + myRestartActions = (restartActions != null ? restartActions : AnAction.EMPTY_ARRAY); } public void addStopAction(AnAction action) { @@ -104,7 +107,7 @@ public class DefaultExecutionResult implements ExecutionResult { @Override public void actionPerformed(final AnActionEvent e) { - if(myProcessHandler.detachIsDefault()) { + if (myProcessHandler.detachIsDefault()) { myProcessHandler.detachProcess(); } else { diff --git a/platform/lang-api/src/com/intellij/execution/ExecutionResult.java b/platform/lang-api/src/com/intellij/execution/ExecutionResult.java index 116ce555e94d..91f888aa8dc6 100644 --- a/platform/lang-api/src/com/intellij/execution/ExecutionResult.java +++ b/platform/lang-api/src/com/intellij/execution/ExecutionResult.java @@ -40,6 +40,7 @@ public interface ExecutionResult { */ AnAction[] getActions(); + /** * Returns the ProcessHandler attached to the running process. * diff --git a/platform/lang-api/src/com/intellij/execution/configurations/AdditionalTabComponentManager.java b/platform/lang-api/src/com/intellij/execution/configurations/AdditionalTabComponentManager.java index 26f1273b2698..701f1b294003 100644 --- a/platform/lang-api/src/com/intellij/execution/configurations/AdditionalTabComponentManager.java +++ b/platform/lang-api/src/com/intellij/execution/configurations/AdditionalTabComponentManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - /* * Created by IntelliJ IDEA. * User: Anna.Kozlova @@ -23,8 +22,10 @@ package com.intellij.execution.configurations; import com.intellij.diagnostic.logging.AdditionalTabComponent; +import org.jetbrains.annotations.NotNull; public interface AdditionalTabComponentManager { - void addAdditionalTabComponent(AdditionalTabComponent component, final String id); - void removeAdditionalTabComponent(AdditionalTabComponent component); + void addAdditionalTabComponent(@NotNull AdditionalTabComponent component, @NotNull String id); + + void removeAdditionalTabComponent(@NotNull AdditionalTabComponent component); } \ No newline at end of file diff --git a/platform/lang-api/src/com/intellij/execution/configurations/LogFileOptions.java b/platform/lang-api/src/com/intellij/execution/configurations/LogFileOptions.java index 6f23831c9e3a..0a4132d83a52 100644 --- a/platform/lang-api/src/com/intellij/execution/configurations/LogFileOptions.java +++ b/platform/lang-api/src/com/intellij/execution/configurations/LogFileOptions.java @@ -20,6 +20,8 @@ import com.intellij.openapi.util.InvalidDataException; import com.intellij.openapi.util.JDOMExternalizable; import com.intellij.openapi.util.WriteExternalException; import com.intellij.openapi.util.io.FileUtil; +import com.intellij.util.SmartList; +import com.intellij.util.containers.SmartHashSet; import org.jdom.Element; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -27,8 +29,7 @@ import org.jetbrains.annotations.Nullable; import java.io.File; import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.HashSet; +import java.util.Collections; import java.util.List; import java.util.Set; import java.util.regex.Pattern; @@ -80,46 +81,50 @@ public class LogFileOptions implements JDOMExternalizable { return myPathPattern; } + @NotNull public Set getPaths(){ - Set result = new HashSet(); - final File logFile = new File(myPathPattern); + File logFile = new File(myPathPattern); if (logFile.exists()){ - result.add(myPathPattern); + return Collections.singleton(myPathPattern); + } + + int dirIndex = myPathPattern.lastIndexOf(File.separator); + if (dirIndex == -1) { + return Collections.emptySet(); + } + + List files = new SmartList(); + collectMatchedFiles(new File(myPathPattern.substring(0, dirIndex)), Pattern.compile(FileUtil.convertAntToRegexp(myPathPattern.substring(dirIndex + File.separator.length()))), files); + if (files.isEmpty()) { + return Collections.emptySet(); + } + + if (myShowAll) { + SmartHashSet result = new SmartHashSet(); + result.ensureCapacity(files.size()); + for (File file : files) { + result.add(file.getPath()); + } return result; } - final int dirIndex = myPathPattern.lastIndexOf(File.separator); - if (dirIndex != -1) { - final ArrayList files = new ArrayList(); - final String basePath = myPathPattern.substring(0, dirIndex); - final String pattern = myPathPattern.substring(dirIndex + File.separator.length()); - collectMatchedFiles(new File(basePath), Pattern.compile(FileUtil.convertAntToRegexp(pattern)), files); - if (!files.isEmpty()) { - if (myShowAll) { - for (File file : files) { - result.add(file.getPath()); + else { + File lastFile = null; + for (File file : files) { + if (lastFile != null) { + if (file.lastModified() > lastFile.lastModified()) { + lastFile = file; } } else { - File lastFile = null; - for (File file : files) { - if (lastFile != null) { - if (file.lastModified() > lastFile.lastModified()) { - lastFile = file; - } - } - else { - lastFile = file; - } - } - assert lastFile != null; - result.add(lastFile.getPath()); + lastFile = file; } } + assert lastFile != null; + return Collections.singleton(lastFile.getPath()); } - return result; } - public static void collectMatchedFiles(final File root, final Pattern pattern, final List files) { + public static void collectMatchedFiles(@NotNull File root, @NotNull Pattern pattern, @NotNull List files) { final File[] dirs = root.listFiles(); if (dirs == null) return; for (File dir : dirs) { diff --git a/platform/lang-api/src/com/intellij/execution/configurations/ModuleRunProfile.java b/platform/lang-api/src/com/intellij/execution/configurations/ModuleRunProfile.java index 9a1dfad2955a..45519c91baeb 100644 --- a/platform/lang-api/src/com/intellij/execution/configurations/ModuleRunProfile.java +++ b/platform/lang-api/src/com/intellij/execution/configurations/ModuleRunProfile.java @@ -19,5 +19,5 @@ package com.intellij.execution.configurations; /** * @author spleaner */ -public interface ModuleRunProfile extends RunProfileWithCompileBeforeLaunchOption { +public interface ModuleRunProfile extends RunProfileWithCompileBeforeLaunchOption, SearchScopeProvidingRunProfile { } diff --git a/platform/lang-api/src/com/intellij/execution/configurations/RunConfigurationBase.java b/platform/lang-api/src/com/intellij/execution/configurations/RunConfigurationBase.java index 0e852c6c577a..d0a180114587 100644 --- a/platform/lang-api/src/com/intellij/execution/configurations/RunConfigurationBase.java +++ b/platform/lang-api/src/com/intellij/execution/configurations/RunConfigurationBase.java @@ -154,8 +154,9 @@ public abstract class RunConfigurationBase extends UserDataHolderBase return myPredefinedLogFiles; } + @NotNull public ArrayList getAllLogFiles() { - final ArrayList list = new ArrayList(myLogFiles); + ArrayList list = new ArrayList(myLogFiles); for (PredefinedLogFile predefinedLogFile : myPredefinedLogFiles) { final LogFileOptions options = getOptionsForPredefinedLogFile(predefinedLogFile); if (options != null) { diff --git a/platform/lang-api/src/com/intellij/execution/configurations/RunConfigurationsSettings.java b/platform/lang-api/src/com/intellij/execution/configurations/RunConfigurationsSettings.java index 259fd4e7484c..732976cf73a7 100644 --- a/platform/lang-api/src/com/intellij/execution/configurations/RunConfigurationsSettings.java +++ b/platform/lang-api/src/com/intellij/execution/configurations/RunConfigurationsSettings.java @@ -17,12 +17,11 @@ package com.intellij.execution.configurations; import com.intellij.openapi.extensions.ExtensionPointName; import com.intellij.openapi.options.UnnamedConfigurable; -import com.intellij.openapi.project.Project; import org.jetbrains.annotations.NotNull; public interface RunConfigurationsSettings { ExtensionPointName EXTENSION_POINT = ExtensionPointName.create("com.intellij.runConfigurationsSettings"); @NotNull - UnnamedConfigurable createConfigurable(@NotNull Project project); + UnnamedConfigurable createConfigurable(); } \ No newline at end of file diff --git a/platform/lang-api/src/com/intellij/execution/configurations/SearchScopeProvider.java b/platform/lang-api/src/com/intellij/execution/configurations/SearchScopeProvider.java index ea6b158e0bc2..e3f0c22a2132 100644 --- a/platform/lang-api/src/com/intellij/execution/configurations/SearchScopeProvider.java +++ b/platform/lang-api/src/com/intellij/execution/configurations/SearchScopeProvider.java @@ -28,8 +28,8 @@ public class SearchScopeProvider { @NotNull public static GlobalSearchScope createSearchScope(@NotNull Project project, @Nullable RunProfile runProfile) { Module[] modules = null; - if (runProfile instanceof ModuleRunProfile) { - modules = ((ModuleRunProfile)runProfile).getModules(); + if (runProfile instanceof SearchScopeProvidingRunProfile) { + modules = ((SearchScopeProvidingRunProfile)runProfile).getModules(); } if (modules == null || modules.length == 0) { return GlobalSearchScope.allScope(project); diff --git a/platform/lang-api/src/com/intellij/execution/configurations/SearchScopeProvidingRunProfile.java b/platform/lang-api/src/com/intellij/execution/configurations/SearchScopeProvidingRunProfile.java new file mode 100644 index 000000000000..afdd273384e8 --- /dev/null +++ b/platform/lang-api/src/com/intellij/execution/configurations/SearchScopeProvidingRunProfile.java @@ -0,0 +1,34 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.intellij.execution.configurations; + +import com.intellij.openapi.module.Module; +import org.jetbrains.annotations.NotNull; + +/** + * Base interface for run configurations that can specify which part of the project should be used to search sources. This information + * will be used to provide more accurate navigation to sources from stack traces, debugger, etc + * + * @author nik + */ +public interface SearchScopeProvidingRunProfile extends RunProfile { + /** + * @return modules where to search sources for this configuration + */ + @NotNull + Module[] getModules(); +} \ No newline at end of file diff --git a/platform/lang-api/src/com/intellij/execution/configurations/SimpleJavaParameters.java b/platform/lang-api/src/com/intellij/execution/configurations/SimpleJavaParameters.java index 7b3c45d5c7de..59fe6c2f1be2 100644 --- a/platform/lang-api/src/com/intellij/execution/configurations/SimpleJavaParameters.java +++ b/platform/lang-api/src/com/intellij/execution/configurations/SimpleJavaParameters.java @@ -40,11 +40,16 @@ public class SimpleJavaParameters extends SimpleProgramParameters { private Charset myCharset = CharsetToolkit.getDefaultSystemCharset(); private boolean myUseDynamicClasspath; private boolean myUseDynamicVMOptions; - + private String myJarPath; + public String getMainClass() { return myMainClass; } + public String getJarPath() { + return myJarPath; + } + /** * @return jdk used to launch the application. * If the instance of the JavaParameters is used to configure app server startup script, @@ -62,6 +67,9 @@ public class SimpleJavaParameters extends SimpleProgramParameters { public void setMainClass(@NonNls final String mainClass) { myMainClass = mainClass; } + public void setJarPath(@NonNls final String jarPath) { + myJarPath = jarPath; + } public PathsList getClassPath() { return myClassPath; diff --git a/platform/lang-api/src/com/intellij/execution/filters/ConsoleDependentFilterProvider.java b/platform/lang-api/src/com/intellij/execution/filters/ConsoleDependentFilterProvider.java new file mode 100644 index 000000000000..1b5f625b9b0e --- /dev/null +++ b/platform/lang-api/src/com/intellij/execution/filters/ConsoleDependentFilterProvider.java @@ -0,0 +1,32 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.execution.filters; + +import com.intellij.execution.ui.ConsoleView; +import com.intellij.openapi.project.Project; +import com.intellij.psi.search.GlobalSearchScope; +import org.jetbrains.annotations.NotNull; + +public abstract class ConsoleDependentFilterProvider implements ConsoleFilterProvider { + @NotNull + public abstract Filter[] getDefaultFilters(@NotNull ConsoleView consoleView, @NotNull Project project, @NotNull GlobalSearchScope scope); + + @NotNull + @Override + public Filter[] getDefaultFilters(@NotNull Project project) { + return Filter.EMPTY_ARRAY; + } +} \ No newline at end of file diff --git a/platform/lang-api/src/com/intellij/execution/runners/AsyncGenericProgramRunner.java b/platform/lang-api/src/com/intellij/execution/runners/AsyncGenericProgramRunner.java index ce7145adeaf9..6b63ec402320 100644 --- a/platform/lang-api/src/com/intellij/execution/runners/AsyncGenericProgramRunner.java +++ b/platform/lang-api/src/com/intellij/execution/runners/AsyncGenericProgramRunner.java @@ -21,6 +21,7 @@ import com.intellij.execution.RunProfileStarter; import com.intellij.execution.configurations.RunProfileState; import com.intellij.execution.configurations.RunnerSettings; import com.intellij.execution.ui.RunContentDescriptor; +import com.intellij.openapi.project.Project; import com.intellij.openapi.util.AsyncResult; import com.intellij.util.Consumer; import com.intellij.util.ui.UIUtil; @@ -61,7 +62,17 @@ public abstract class AsyncGenericProgramRunner * @return RunProfileStarter async result */ @NotNull - protected abstract AsyncResult prepare(@NotNull ExecutionEnvironment environment, @NotNull RunProfileState state) throws ExecutionException; + protected AsyncResult prepare(@NotNull ExecutionEnvironment environment, @NotNull RunProfileState state) throws ExecutionException { + return prepare(environment.getProject(), environment, state); + } + + /** + * @deprecated override {@link #prepare(ExecutionEnvironment, com.intellij.execution.configurations.RunProfileState)} instead + */ + @Deprecated + protected AsyncResult prepare(@NotNull Project project, @NotNull ExecutionEnvironment environment, @NotNull RunProfileState state) throws ExecutionException { + throw new UnsupportedOperationException(); + } private static void startRunProfile(@NotNull ExecutionEnvironment environment, @NotNull RunProfileState state, diff --git a/platform/lang-api/src/com/intellij/execution/runners/ExecutionUtil.java b/platform/lang-api/src/com/intellij/execution/runners/ExecutionUtil.java index b6c300c3f7e2..5a395ab5dd79 100644 --- a/platform/lang-api/src/com/intellij/execution/runners/ExecutionUtil.java +++ b/platform/lang-api/src/com/intellij/execution/runners/ExecutionUtil.java @@ -112,6 +112,10 @@ public class ExecutionUtil { UIUtil.invokeLaterIfNeeded(new Runnable() { @Override public void run() { + if (project.isDisposed()) { + return; + } + ToolWindowManager toolWindowManager = ToolWindowManager.getInstance(project); if (toolWindowManager.canShowNotification(toolWindowId)) { //noinspection SSBasedInspection diff --git a/platform/lang-api/src/com/intellij/execution/ui/RunContentDescriptor.java b/platform/lang-api/src/com/intellij/execution/ui/RunContentDescriptor.java index 60e7f03d54db..5a23ed45b1a9 100644 --- a/platform/lang-api/src/com/intellij/execution/ui/RunContentDescriptor.java +++ b/platform/lang-api/src/com/intellij/execution/ui/RunContentDescriptor.java @@ -15,11 +15,13 @@ */ package com.intellij.execution.ui; +import com.intellij.execution.DefaultExecutionResult; import com.intellij.execution.ExecutionResult; import com.intellij.execution.configurations.RunProfile; import com.intellij.execution.process.ProcessHandler; import com.intellij.ide.HelpIdProvider; import com.intellij.openapi.Disposable; +import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.Disposer; import com.intellij.ui.content.Content; @@ -44,6 +46,8 @@ public class RunContentDescriptor implements Disposable { private Content myContent; private Runnable myRestarter; + @NotNull + private AnAction[] myRestartActions = AnAction.EMPTY_ARRAY; public RunContentDescriptor(@Nullable ExecutionConsole executionConsole, @Nullable ProcessHandler processHandler, @@ -66,7 +70,19 @@ public class RunContentDescriptor implements Disposable { } public RunContentDescriptor(@NotNull RunProfile profile, @NotNull ExecutionResult executionResult, @NotNull RunnerLayoutUi ui) { - this(executionResult.getExecutionConsole(), executionResult.getProcessHandler(), ui.getComponent(), profile.getName(), profile.getIcon()); + this(executionResult.getExecutionConsole(), executionResult.getProcessHandler(), ui.getComponent(), profile.getName(), + profile.getIcon()); + if (executionResult instanceof DefaultExecutionResult) { + myRestartActions = ((DefaultExecutionResult)executionResult).getRestartActions(); + } + } + + /** + * @return actions to restart or rerun + */ + @NotNull + public AnAction[] getRestartActions() { + return myRestartActions.clone(); } public ExecutionConsole getExecutionConsole() { @@ -81,6 +97,8 @@ public class RunContentDescriptor implements Disposable { } myComponent = null; myRestarter = null; + myProcessHandler = null; + myContent = null; } /** @@ -127,6 +145,7 @@ public class RunContentDescriptor implements Disposable { myContent = content; } + @SuppressWarnings("UnusedDeclaration") @Nullable @Deprecated /** diff --git a/platform/lang-api/src/com/intellij/find/FindModel.java b/platform/lang-api/src/com/intellij/find/FindModel.java index 5dafc2db8acf..d07187796872 100644 --- a/platform/lang-api/src/com/intellij/find/FindModel.java +++ b/platform/lang-api/src/com/intellij/find/FindModel.java @@ -107,9 +107,7 @@ public class FindModel extends UserDataHolderBase implements Cloneable { if (!multiline) { initStringToFindNoMultiline(this, getStringToFind()); } - else { - setRegularExpressions(false); - } + isMultiline = multiline; notifyObservers(); } diff --git a/platform/lang-api/src/com/intellij/ide/util/projectWizard/ModuleBuilder.java b/platform/lang-api/src/com/intellij/ide/util/projectWizard/ModuleBuilder.java index fddb2b2f50b7..861123ab7839 100644 --- a/platform/lang-api/src/com/intellij/ide/util/projectWizard/ModuleBuilder.java +++ b/platform/lang-api/src/com/intellij/ide/util/projectWizard/ModuleBuilder.java @@ -25,6 +25,8 @@ import com.intellij.openapi.module.*; import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.project.DumbAwareRunnable; import com.intellij.openapi.project.Project; +import com.intellij.openapi.project.ProjectType; +import com.intellij.openapi.project.ProjectTypeService; import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.roots.ContentEntry; import com.intellij.openapi.roots.ModifiableRootModel; @@ -56,12 +58,12 @@ public abstract class ModuleBuilder extends AbstractModuleBuilder { public static final ExtensionPointName EP_NAME = ExtensionPointName.create("com.intellij.moduleBuilder"); private static final Logger LOG = Logger.getInstance("#com.intellij.ide.util.projectWizard.ModuleBuilder"); + private final Set myUpdaters = new HashSet(); + private final EventDispatcher myDispatcher = EventDispatcher.create(ModuleBuilderListener.class); protected Sdk myJdk; private String myName; @NonNls private String myModuleFilePath; private String myContentEntryPath; - private final Set myUpdaters = new HashSet(); - private final EventDispatcher myDispatcher = EventDispatcher.create(ModuleBuilderListener.class); @NotNull public static List getAllBuilders() { @@ -81,6 +83,17 @@ public abstract class ModuleBuilder extends AbstractModuleBuilder { }); } + public static void deleteModuleFile(String moduleFilePath) { + final File moduleFile = new File(moduleFilePath); + if (moduleFile.exists()) { + FileUtil.delete(moduleFile); + } + final VirtualFile file = LocalFileSystem.getInstance().findFileByIoFile(moduleFile); + if (file != null) { + file.refresh(false, false); + } + } + protected boolean isAvailable() { return true; } @@ -94,6 +107,11 @@ public abstract class ModuleBuilder extends AbstractModuleBuilder { return myName; } + @Override + public void setName(String name) { + myName = acceptParameter(name); + } + @Override @Nullable public String getBuilderId() { @@ -167,24 +185,19 @@ public abstract class ModuleBuilder extends AbstractModuleBuilder { return Collections.emptyList(); } - @Override - public void setName(String name) { - myName = acceptParameter(name); - } - public String getModuleFilePath() { return myModuleFilePath; } - public void addModuleConfigurationUpdater(ModuleConfigurationUpdater updater) { - myUpdaters.add(updater); - } - @Override public void setModuleFilePath(@NonNls String path) { myModuleFilePath = acceptParameter(path); } + public void addModuleConfigurationUpdater(ModuleConfigurationUpdater updater) { + myUpdaters.add(updater); + } + @Nullable public String getContentEntryPath() { if (myContentEntryPath == null) { @@ -259,6 +272,7 @@ public abstract class ModuleBuilder extends AbstractModuleBuilder { updater.update(module, modifiableModel); } modifiableModel.commit(); + setProjectType(module); } private void onModuleInitialized(final Module module) { @@ -269,6 +283,17 @@ public abstract class ModuleBuilder extends AbstractModuleBuilder { public abstract ModuleType getModuleType(); + protected ProjectType getProjectType() { + return null; + } + + protected void setProjectType(Module module) { + ProjectType projectType = getProjectType(); + if (projectType != null && ProjectTypeService.getProjectType(module.getProject()) == null) { + ProjectTypeService.setProjectType(module.getProject(), projectType); + } + } + @NotNull public Module createAndCommitIfNeeded(@NotNull Project project, @Nullable ModifiableModuleModel model, boolean runFromProjectWizard) throws InvalidDataException, ConfigurationException, IOException, JDOMException, ModuleWithNameAlreadyExists { @@ -295,7 +320,6 @@ public abstract class ModuleBuilder extends AbstractModuleBuilder { return module; } - public void addListener(ModuleBuilderListener listener) { myDispatcher.addListener(listener); } @@ -340,17 +364,6 @@ public abstract class ModuleBuilder extends AbstractModuleBuilder { return null; } - public static void deleteModuleFile(String moduleFilePath) { - final File moduleFile = new File(moduleFilePath); - if (moduleFile.exists()) { - FileUtil.delete(moduleFile); - } - final VirtualFile file = LocalFileSystem.getInstance().findFileByIoFile(moduleFile); - if (file != null) { - file.refresh(false, false); - } - } - public Icon getBigIcon() { return getModuleType().getBigIcon(); } @@ -396,14 +409,14 @@ public abstract class ModuleBuilder extends AbstractModuleBuilder { myModuleFilePath = from.getModuleFilePath(); } - public void setModuleJdk(Sdk jdk) { - myJdk = jdk; - } - public Sdk getModuleJdk() { return myJdk; } + public void setModuleJdk(Sdk jdk) { + myJdk = jdk; + } + @NotNull public FrameworkRole getDefaultAcceptableRole() { return getModuleType().getDefaultAcceptableRole(); diff --git a/platform/lang-api/src/com/intellij/lang/documentation/DocumentationProviderEx.java b/platform/lang-api/src/com/intellij/lang/documentation/DocumentationProviderEx.java index 8b9143a0d995..9d7a10d6ba5e 100644 --- a/platform/lang-api/src/com/intellij/lang/documentation/DocumentationProviderEx.java +++ b/platform/lang-api/src/com/intellij/lang/documentation/DocumentationProviderEx.java @@ -29,6 +29,7 @@ import java.util.List; * @author peter */ public class DocumentationProviderEx implements DocumentationProvider { + @Nullable @Override public String getQuickNavigateInfo(PsiElement element, PsiElement originalElement) { return null; diff --git a/platform/lang-api/src/com/intellij/openapi/projectRoots/JdkUtil.java b/platform/lang-api/src/com/intellij/openapi/projectRoots/JdkUtil.java index 4291e9b401bf..337909b318e5 100644 --- a/platform/lang-api/src/com/intellij/openapi/projectRoots/JdkUtil.java +++ b/platform/lang-api/src/com/intellij/openapi/projectRoots/JdkUtil.java @@ -247,7 +247,15 @@ public class JdkUtil { } final String mainClass = javaParameters.getMainClass(); - commandLine.addParameter(mainClass); + String jarPath = javaParameters.getJarPath(); + if (mainClass != null) { + commandLine.addParameter(mainClass); + } + else if (jarPath != null) { + commandLine.addParameter("-jar"); + commandLine.addParameter(jarPath); + } + commandLine.addParameters(javaParameters.getProgramParametersList().getList()); commandLine.setWorkDirectory(javaParameters.getWorkingDirectory()); @@ -260,7 +268,7 @@ public class JdkUtil { ParametersList parametersList) { commandLine.addParameters(parametersList.getList()); appendEncoding(javaParameters, commandLine, parametersList); - if (!parametersList.hasParameter("-classpath") && !parametersList.hasParameter("-cp")){ + if (!parametersList.hasParameter("-classpath") && !parametersList.hasParameter("-cp") && !javaParameters.getClassPath().getPathList().isEmpty()){ commandLine.addParameter("-classpath"); commandLine.addParameter(javaParameters.getClassPath().getPathsString()); } diff --git a/platform/lang-api/src/com/intellij/openapi/projectRoots/ui/PathEditor.java b/platform/lang-api/src/com/intellij/openapi/projectRoots/ui/PathEditor.java index ecebb9db73f3..ae9f0142f246 100644 --- a/platform/lang-api/src/com/intellij/openapi/projectRoots/ui/PathEditor.java +++ b/platform/lang-api/src/com/intellij/openapi/projectRoots/ui/PathEditor.java @@ -23,7 +23,6 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.fileChooser.FileChooser; import com.intellij.openapi.fileChooser.FileChooserDescriptor; -import com.intellij.openapi.fileChooser.FileChooserDialog; import com.intellij.openapi.fileTypes.FileTypes; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Computable; @@ -67,7 +66,6 @@ public class PathEditor { public PathEditor(final FileChooserDescriptor descriptor) { myDescriptor = descriptor; - myDescriptor.putUserData(FileChooserDialog.PREFER_LAST_OVER_TO_SELECT, Boolean.TRUE); myModel = createListModel(); } @@ -176,12 +174,8 @@ public class PathEditor { } protected VirtualFile[] doAdd() { - VirtualFile baseDir = myAddBaseDir; Project project = CommonDataKeys.PROJECT.getData(DataManager.getInstance().getDataContext(myPanel)); - if (baseDir == null && project != null) { - baseDir = project.getBaseDir(); - } - VirtualFile[] files = FileChooser.chooseFiles(myDescriptor, myPanel, project, baseDir); + VirtualFile[] files = FileChooser.chooseFiles(myDescriptor, myPanel, project, myAddBaseDir); files = adjustAddedFileSet(myPanel, files); List added = new ArrayList(files.length); for (VirtualFile vFile : files) { diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/CodeStyleSettingsCustomizable.java b/platform/lang-api/src/com/intellij/psi/codeStyle/CodeStyleSettingsCustomizable.java index 290078e39aa9..2ef2c998b3b4 100644 --- a/platform/lang-api/src/com/intellij/psi/codeStyle/CodeStyleSettingsCustomizable.java +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/CodeStyleSettingsCustomizable.java @@ -214,6 +214,7 @@ public interface CodeStyleSettingsCustomizable { String WRAPPING_KEEP = ApplicationBundle.message("wrapping.keep.when.reformatting"); String WRAPPING_BRACES = ApplicationBundle.message("wrapping.brace.placement"); + String WRAPPING_COMMENTS = ApplicationBundle.message("wrapping.comments"); String WRAPPING_METHOD_PARAMETERS = ApplicationBundle.message("wrapping.method.parameters"); String WRAPPING_METHOD_PARENTHESES = ApplicationBundle.message("wrapping.method.parentheses"); String WRAPPING_METHOD_ARGUMENTS_WRAPPING = ApplicationBundle.message("wrapping.method.arguments"); diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/CommonCodeStyleSettings.java b/platform/lang-api/src/com/intellij/psi/codeStyle/CommonCodeStyleSettings.java index 299d17215fdf..fa284c8fd33c 100644 --- a/platform/lang-api/src/com/intellij/psi/codeStyle/CommonCodeStyleSettings.java +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/CommonCodeStyleSettings.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,7 +21,7 @@ import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.util.*; import com.intellij.psi.codeStyle.arrangement.ArrangementSettings; import com.intellij.psi.codeStyle.arrangement.ArrangementUtil; -import com.intellij.util.containers.HashSet; +import com.intellij.util.ReflectionUtil; import com.intellij.util.xmlb.SkipDefaultValuesSerializationFilters; import com.intellij.util.xmlb.XmlSerializer; import org.intellij.lang.annotations.MagicConstant; @@ -31,8 +31,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.util.Arrays; import java.util.Set; /** @@ -155,63 +153,20 @@ public class CommonCodeStyleSettings { protected static void copyPublicFields(Object from, Object to) { assert from != to; - copyFields(to.getClass().getFields(), from, to); + ReflectionUtil.copyFields(to.getClass().getFields(), from, to); } void copyNonDefaultValuesFrom(CommonCodeStyleSettings from) { CommonCodeStyleSettings defaultSettings = new CommonCodeStyleSettings(null); PARENT_SETTINGS_INSTALLED = - copyFields(getClass().getFields(), from, this, new SupportedFieldsDiffFilter(from, getSupportedFields(), defaultSettings) { - @Override - public boolean isAccept(@NotNull Field field) { - if ("RIGHT_MARGIN".equals(field.getName())) return false; // Never copy RIGHT_MARGIN, it is inherited automatically if -1 - return super.isAccept(field); - } - }); - } - - private static void copyFields(Field[] fields, Object from, Object to) { - copyFields(fields, from, to, null); - } - - private static boolean copyFields(Field[] fields, Object from, Object to, @Nullable DifferenceFilter diffFilter) { - Set sourceFields = new HashSet(Arrays.asList(from.getClass().getFields())); - boolean valuesChanged = false; - for (Field field : fields) { - if (sourceFields.contains(field)) { - if (isPublic(field) && !isFinal(field)) { - try { - if (diffFilter == null || diffFilter.isAccept(field)) { - copyFieldValue(from, to, field); - valuesChanged = true; - } - } - catch (Exception e) { - throw new RuntimeException(e); + ReflectionUtil + .copyFields(getClass().getFields(), from, this, new SupportedFieldsDiffFilter(from, getSupportedFields(), defaultSettings) { + @Override + public boolean isAccept(@NotNull Field field) { + if ("RIGHT_MARGIN".equals(field.getName())) return false; // Never copy RIGHT_MARGIN, it is inherited automatically if -1 + return super.isAccept(field); } - } - } - } - return valuesChanged; - } - - private static void copyFieldValue(final Object from, Object to, final Field field) - throws IllegalAccessException { - Class fieldType = field.getType(); - if (fieldType.isPrimitive() || fieldType.equals(String.class)) { - field.set(to, field.get(from)); - } - else { - throw new RuntimeException("Field not copied " + field.getName()); - } - } - - private static boolean isPublic(final Field field) { - return (field.getModifiers() & Modifier.PUBLIC) != 0; - } - - private static boolean isFinal(final Field field) { - return (field.getModifiers() & Modifier.FINAL) != 0; + }); } @Nullable diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/autodetect/IndentOptionsDetector.java b/platform/lang-api/src/com/intellij/psi/codeStyle/autodetect/IndentOptionsDetector.java new file mode 100644 index 000000000000..527ee9e79a49 --- /dev/null +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/autodetect/IndentOptionsDetector.java @@ -0,0 +1,106 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.psi.codeStyle.autodetect; + +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiDocumentManager; +import com.intellij.psi.PsiFile; +import com.intellij.psi.codeStyle.CodeStyleSettingsManager; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +import static com.intellij.psi.codeStyle.CommonCodeStyleSettings.*; + +public class IndentOptionsDetector { + private static Logger LOG = Logger.getInstance("#com.intellij.psi.codeStyle.CommonCodeStyleSettings.IndentOptionsDetector"); + + private static final double RATE_THRESHOLD = 0.8; + private static final int MIN_LINES_THRESHOLD = 50; + private static final int MAX_INDENT_TO_DETECT = 8; + + private final PsiFile myFile; + private final Project myProject; + private final Document myDocument; + + public IndentOptionsDetector(@NotNull PsiFile file) { + myFile = file; + myProject = file.getProject(); + myDocument = PsiDocumentManager.getInstance(myProject).getDocument(myFile); + } + + @NotNull + public IndentOptions getIndentOptions() { + IndentOptions indentOptions = (IndentOptions)CodeStyleSettingsManager.getSettings(myProject).getIndentOptions(myFile.getFileType()).clone(); + + if (myDocument != null) { + List linesInfo = new LineIndentInfoBuilder(myDocument.getCharsSequence()).build(); + IndentUsageStatistics stats = new IndentUsageStatisticsImpl(linesInfo); + adjustIndentOptions(indentOptions, stats); + } + + return indentOptions; + } + + private void adjustIndentOptions(@NotNull IndentOptions indentOptions, @NotNull IndentUsageStatistics stats) { + int linesWithTabs = stats.getTotalLinesWithLeadingTabs(); + int linesWithWhiteSpaceIndent = stats.getTotalLinesWithLeadingSpaces(); + + int totalLines = linesWithTabs + linesWithWhiteSpaceIndent; + double lineWithTabsRate = (double)linesWithTabs / totalLines; + + if (linesWithTabs > MIN_LINES_THRESHOLD && lineWithTabsRate > RATE_THRESHOLD) { + if (!indentOptions.USE_TAB_CHARACTER) { + indentOptions.USE_TAB_CHARACTER = true; + LOG.info("Detected tab usage in" + myFile); + } + } + else if (linesWithWhiteSpaceIndent > MIN_LINES_THRESHOLD && (1 - lineWithTabsRate) > RATE_THRESHOLD) { + int newIndentSize = getPositiveIndentSize(stats); + if (newIndentSize > 0 && indentOptions.INDENT_SIZE != newIndentSize) { + indentOptions.INDENT_SIZE = newIndentSize; + LOG.info("Detected indent size: " + newIndentSize + " for file " + myFile); + } + } + } + + private static int getPositiveIndentSize(@NotNull IndentUsageStatistics stats) { + int totalIndentSizesDetected = stats.getTotalIndentSizesDetected(); + if (totalIndentSizesDetected == 0) return -1; + + IndentUsageInfo maxUsedIndentInfo = stats.getKMostUsedIndentInfo(0); + int maxUsedIndentSize = maxUsedIndentInfo.getIndentSize(); + + if (maxUsedIndentSize == 0) { + if (totalIndentSizesDetected < 1) return -1; + + maxUsedIndentInfo = stats.getKMostUsedIndentInfo(1); + maxUsedIndentSize = maxUsedIndentInfo.getIndentSize(); + } + + if (maxUsedIndentSize <= MAX_INDENT_TO_DETECT) { + int totalUsagesWithoutZeroIndent = stats.getTotalLinesWithLeadingSpaces() - stats.getTimesIndentUsed(0); + double usageRate = (double)maxUsedIndentInfo.getTimesUsed() / totalUsagesWithoutZeroIndent; + if (usageRate > RATE_THRESHOLD) { + return maxUsedIndentSize; + } + } + + return -1; + } +} diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/autodetect/IndentUsageInfo.java b/platform/lang-api/src/com/intellij/psi/codeStyle/autodetect/IndentUsageInfo.java new file mode 100644 index 000000000000..a600483a07a6 --- /dev/null +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/autodetect/IndentUsageInfo.java @@ -0,0 +1,39 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.psi.codeStyle.autodetect; + +public class IndentUsageInfo { + private final int indentSize; + private final int timesUsed; + + public IndentUsageInfo(int indentSize, int timesUsed) { + this.indentSize = indentSize; + this.timesUsed = timesUsed; + } + + public int getIndentSize() { + return indentSize; + } + + public int getTimesUsed() { + return timesUsed; + } + + @Override + public String toString() { + return "indent: " + indentSize + ", used " + timesUsed; + } +} diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/autodetect/IndentUsageStatistics.java b/platform/lang-api/src/com/intellij/psi/codeStyle/autodetect/IndentUsageStatistics.java new file mode 100644 index 000000000000..7e3bee7221da --- /dev/null +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/autodetect/IndentUsageStatistics.java @@ -0,0 +1,30 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.psi.codeStyle.autodetect; + +public interface IndentUsageStatistics { + + int getTotalLinesWithLeadingTabs(); + + int getTotalLinesWithLeadingSpaces(); + + IndentUsageInfo getKMostUsedIndentInfo(int k); + + int getTotalIndentSizesDetected(); + + int getTimesIndentUsed(int indent); + +} diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/autodetect/IndentUsageStatisticsImpl.java b/platform/lang-api/src/com/intellij/psi/codeStyle/autodetect/IndentUsageStatisticsImpl.java new file mode 100644 index 000000000000..99de432f101d --- /dev/null +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/autodetect/IndentUsageStatisticsImpl.java @@ -0,0 +1,149 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.psi.codeStyle.autodetect; + +import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.containers.Stack; +import gnu.trove.TIntIntHashMap; +import gnu.trove.TIntIntIterator; +import org.jetbrains.annotations.NotNull; + +import java.util.Comparator; +import java.util.List; + +public class IndentUsageStatisticsImpl implements IndentUsageStatistics { + private static final Comparator DECREASING_ORDER = new Comparator() { + @Override + public int compare(@NotNull IndentUsageInfo o1, @NotNull IndentUsageInfo o2) { + return o1.getTimesUsed() < o2.getTimesUsed() ? 1 : o1.getTimesUsed() == o2.getTimesUsed() ? 0 : -1; + } + }; + + private List myLineInfos; + + private int myPreviousLineIndent; + private int myPreviousRelativeIndent; + + private int myTotalLinesWithTabs = 0; + private int myTotalLinesWithWhiteSpaces = 0; + + private TIntIntHashMap myIndentToUsagesMap = new TIntIntHashMap(); + private List myIndentUsages = ContainerUtil.newArrayList(); + private Stack myParentIndents = ContainerUtil.newStack(new IndentData(0, 0)); + + public IndentUsageStatisticsImpl(@NotNull List lineInfos) { + myLineInfos = lineInfos; + buildIndentToUsagesMap(); + myIndentUsages = toIndentUsageList(myIndentToUsagesMap); + ContainerUtil.sort(myIndentUsages, DECREASING_ORDER); + } + + @NotNull + private static List toIndentUsageList(@NotNull TIntIntHashMap indentToUsages) { + List indentUsageInfos = ContainerUtil.newArrayList(); + TIntIntIterator it = indentToUsages.iterator(); + while (it.hasNext()) { + it.advance(); + indentUsageInfos.add(new IndentUsageInfo(it.key(), it.value())); + } + return indentUsageInfos; + } + + public void buildIndentToUsagesMap() { + myPreviousLineIndent = 0; + myPreviousRelativeIndent = 0; + + for (LineIndentInfo lineInfo : myLineInfos) { + if (lineInfo.isLineWithTabs()) { + myTotalLinesWithTabs++; + } + else if (lineInfo.isLineWithWhiteSpaceIndent()) { + handleWhiteSpaceIndent(lineInfo.getIndentSize()); + } + } + } + + @NotNull + private IndentData findParentIndent(int indent) { + while (myParentIndents.size() != 1 && myParentIndents.peek().indent > indent) { + myParentIndents.pop(); + } + return myParentIndents.peek(); + } + + private void handleWhiteSpaceIndent(int currentIndent) { + int relativeIndent = currentIndent - myPreviousLineIndent; + if (relativeIndent < 0) { + IndentData indentData = findParentIndent(currentIndent); + myPreviousLineIndent = indentData.indent; + myPreviousRelativeIndent = indentData.relativeIndent; + relativeIndent = currentIndent - myPreviousLineIndent; + } + + if (relativeIndent == 0) { + relativeIndent = myPreviousRelativeIndent; + } + else { + myParentIndents.push(new IndentData(currentIndent, relativeIndent)); + } + + increaseIndentUsage(relativeIndent); + + myPreviousRelativeIndent = relativeIndent; + myPreviousLineIndent = currentIndent; + myTotalLinesWithWhiteSpaces++; + } + + private void increaseIndentUsage(int relativeIndent) { + int timesUsed = myIndentToUsagesMap.get(relativeIndent); + myIndentToUsagesMap.put(relativeIndent, ++timesUsed); + } + + @Override + public int getTotalLinesWithLeadingTabs() { + return myTotalLinesWithTabs; + } + + @Override + public int getTotalLinesWithLeadingSpaces() { + return myTotalLinesWithWhiteSpaces; + } + + @Override + public IndentUsageInfo getKMostUsedIndentInfo(int k) { + return myIndentUsages.get(k); + } + + @Override + public int getTimesIndentUsed(int indent) { + return myIndentToUsagesMap.get(indent); + } + + @Override + public int getTotalIndentSizesDetected() { + return myIndentToUsagesMap.size(); + } + + private static class IndentData { + public final int indent; + public final int relativeIndent; + + public IndentData(int indent, int relativeIndent) { + this.indent = indent; + this.relativeIndent = relativeIndent; + } + } +} diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/autodetect/LineIndentInfo.java b/platform/lang-api/src/com/intellij/psi/codeStyle/autodetect/LineIndentInfo.java new file mode 100644 index 000000000000..d85a4cbcaa0e --- /dev/null +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/autodetect/LineIndentInfo.java @@ -0,0 +1,56 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.psi.codeStyle.autodetect; + +import org.jetbrains.annotations.NotNull; + +public class LineIndentInfo { + public static final LineIndentInfo EMPTY_LINE = new LineIndentInfo(LineType.EMPTY_LINE, -1); + public static final LineIndentInfo LINE_WITH_COMMENT = new LineIndentInfo(LineType.LINE_WITH_COMMENT, -1); + public static final LineIndentInfo LINE_WITH_TABS = new LineIndentInfo(LineType.LINE_WITH_TABS, -1); + + private final int myIndentSize; + private final LineType myType; + + private LineIndentInfo(@NotNull LineType type, int indentSize) { + myType = type; + myIndentSize = indentSize; + } + + @NotNull + public static LineIndentInfo newWhiteSpaceIndent(int indentSize) { + return new LineIndentInfo(LineType.LINE_WITH_WHITESPACE_INDENT, indentSize); + } + + public int getIndentSize() { + return myIndentSize; + } + + public boolean isLineWithWhiteSpaceIndent() { + return myType == LineType.LINE_WITH_WHITESPACE_INDENT; + } + + public boolean isLineWithTabs() { + return myType == LineType.LINE_WITH_TABS; + } + + private enum LineType { + EMPTY_LINE, + LINE_WITH_COMMENT, + LINE_WITH_TABS, + LINE_WITH_WHITESPACE_INDENT + } +} diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/autodetect/LineIndentInfoBuilder.java b/platform/lang-api/src/com/intellij/psi/codeStyle/autodetect/LineIndentInfoBuilder.java new file mode 100644 index 000000000000..8efbbc495750 --- /dev/null +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/autodetect/LineIndentInfoBuilder.java @@ -0,0 +1,80 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.psi.codeStyle.autodetect; + +import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.text.CharArrayUtil; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public class LineIndentInfoBuilder { + private static final int MAX_LINES_TO_PROCESS = 500; + + private final CharSequence myText; + private final int myLength; + + public LineIndentInfoBuilder(@NotNull CharSequence text) { + myText = text; + myLength = text.length(); + } + + @NotNull + public List build() { + List lineIndentInfos = ContainerUtil.newArrayList(); + + int lineStartOffset = 0; + int currentLine = 0; + + while (lineStartOffset < myText.length() && currentLine < MAX_LINES_TO_PROCESS) { + int lineEndOffset = getLineEndOffset(lineStartOffset); + int textStartOffset = CharArrayUtil.shiftForward(myText, lineStartOffset, lineEndOffset, " \t"); + + if (textStartOffset != lineEndOffset) { + lineIndentInfos.add(createInfoFromWhiteSpaceRange(lineStartOffset, textStartOffset)); + } else { + lineIndentInfos.add(LineIndentInfo.EMPTY_LINE); + } + + lineStartOffset = lineEndOffset + 1; + currentLine++; + } + + return lineIndentInfos; + } + + @NotNull + private LineIndentInfo createInfoFromWhiteSpaceRange(int lineStartOffset, int textStartOffset) { + if (myText.charAt(textStartOffset) == '*') { + return LineIndentInfo.LINE_WITH_COMMENT; + } + else if (CharArrayUtil.indexOf(myText, "\t", lineStartOffset, textStartOffset) > 0) { + return LineIndentInfo.LINE_WITH_TABS; + } + else { + int indentSize = textStartOffset - lineStartOffset; + return LineIndentInfo.newWhiteSpaceIndent(indentSize); + } + } + + private int getLineEndOffset(int lineStartOffset) { + int lineEndOffset = CharArrayUtil.indexOf(myText, "\n", lineStartOffset, myLength); + if (lineEndOffset < 0) { + lineEndOffset = myText.length(); + } + return lineEndOffset; + } +} diff --git a/platform/lang-api/src/com/intellij/psi/util/PsiUtilBase.java b/platform/lang-api/src/com/intellij/psi/util/PsiUtilBase.java index 4565832c310b..b63ee1d6ab2d 100644 --- a/platform/lang-api/src/com/intellij/psi/util/PsiUtilBase.java +++ b/platform/lang-api/src/com/intellij/psi/util/PsiUtilBase.java @@ -40,7 +40,9 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; +import java.awt.*; import java.util.*; +import java.util.List; public class PsiUtilBase extends PsiUtilCore implements PsiEditorUtil { private static final Logger LOG = Logger.getInstance("#com.intellij.psi.util.PsiUtilBase"); @@ -235,6 +237,9 @@ public class PsiUtilBase extends PsiUtilCore implements PsiEditorUtil { */ @Nullable public static Editor findEditor(@NotNull PsiElement element) { + if (!EventQueue.isDispatchThread()) { + LOG.warn("Invoke findEditor() from EDT only. Otherwise, it causes deadlocks."); + } PsiFile psiFile = element.getContainingFile(); VirtualFile virtualFile = PsiUtilCore.getVirtualFile(element); if (virtualFile == null) { diff --git a/platform/lang-impl/src/com/intellij/analysis/BaseAnalysisAction.java b/platform/lang-impl/src/com/intellij/analysis/BaseAnalysisAction.java index c63f8cdc2fd7..af46c5178f9f 100644 --- a/platform/lang-impl/src/com/intellij/analysis/BaseAnalysisAction.java +++ b/platform/lang-impl/src/com/intellij/analysis/BaseAnalysisAction.java @@ -70,7 +70,7 @@ public abstract class BaseAnalysisAction extends AnAction { } AnalysisScope scope = getInspectionScope(dataContext); LOG.assertTrue(scope != null); - final boolean rememberScope = e.getPlace().equals(ActionPlaces.MAIN_MENU); + final boolean rememberScope = ActionPlaces.isMainMenuOrActionSearch(e.getPlace()); final AnalysisUIOptions uiOptions = AnalysisUIOptions.getInstance(project); PsiElement element = CommonDataKeys.PSI_ELEMENT.getData(dataContext); BaseAnalysisActionDialog dlg = new BaseAnalysisActionDialog(AnalysisScopeBundle.message("specify.analysis.scope", myTitle), diff --git a/platform/lang-impl/src/com/intellij/application/options/CodeStyleSchemesConfigurable.java b/platform/lang-impl/src/com/intellij/application/options/CodeStyleSchemesConfigurable.java index 760d17e67d24..758aef6a9065 100644 --- a/platform/lang-impl/src/com/intellij/application/options/CodeStyleSchemesConfigurable.java +++ b/platform/lang-impl/src/com/intellij/application/options/CodeStyleSchemesConfigurable.java @@ -302,6 +302,11 @@ public class CodeStyleSchemesConfigurable extends SearchableConfigurable.Parent. @Override public boolean isModified() { if (myModel != null) { + if (Registry.is("ide.new.settings.dialog")) { + if (myPanels != null && myPanels.size() > 0 && myPanels.get(0).isModified()) { + return true; + } + } boolean schemeListModified = myModel.isSchemeListModified(); if (schemeListModified) { myApplyCompleted = false; diff --git a/platform/lang-impl/src/com/intellij/application/options/ModuleListCellRenderer.java b/platform/lang-impl/src/com/intellij/application/options/ModuleListCellRenderer.java index f544517bb5d4..6bc5f6505c92 100644 --- a/platform/lang-impl/src/com/intellij/application/options/ModuleListCellRenderer.java +++ b/platform/lang-impl/src/com/intellij/application/options/ModuleListCellRenderer.java @@ -3,6 +3,7 @@ package com.intellij.application.options; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleType; import com.intellij.ui.ListCellRendererWrapper; +import org.jetbrains.annotations.NotNull; import javax.swing.*; @@ -10,10 +11,20 @@ import javax.swing.*; * @author yole */ public class ModuleListCellRenderer extends ListCellRendererWrapper { + private final String myEmptySelectionText; + + public ModuleListCellRenderer() { + this("[none]"); + } + + public ModuleListCellRenderer(@NotNull String emptySelectionText) { + myEmptySelectionText = emptySelectionText; + } + @Override public void customize(JList list, Module module, int index, boolean selected, boolean hasFocus) { if (module == null) { - setText("[none]"); + setText(myEmptySelectionText); } else { setIcon(ModuleType.get(module).getIcon()); diff --git a/platform/lang-impl/src/com/intellij/application/options/ModulesComboBox.java b/platform/lang-impl/src/com/intellij/application/options/ModulesComboBox.java index 17434cb0c7ba..4c2ef3a3d16c 100644 --- a/platform/lang-impl/src/com/intellij/application/options/ModulesComboBox.java +++ b/platform/lang-impl/src/com/intellij/application/options/ModulesComboBox.java @@ -36,6 +36,7 @@ import java.util.List; */ public class ModulesComboBox extends ComboBox { private final SortedComboBoxModel myModel; + private boolean myAllowEmptySelection; public ModulesComboBox() { this(new SortedComboBoxModel(ModulesAlphaComparator.INSTANCE)); @@ -58,8 +59,17 @@ public class ModulesComboBox extends ComboBox { setRenderer(new ModuleListCellRenderer()); } + public void allowEmptySelection(@NotNull String emptySelectionText) { + myAllowEmptySelection = true; + myModel.add(null); + setRenderer(new ModuleListCellRenderer(emptySelectionText)); + } + public void setModules(@NotNull Collection modules) { myModel.setAll(modules); + if (myAllowEmptySelection) { + myModel.add(null); + } } public void fillModules(@NotNull Project project) { diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/ManageCodeStyleSchemesDialog.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/ManageCodeStyleSchemesDialog.java index 653f6b51e8bd..282f44d04a60 100644 --- a/platform/lang-impl/src/com/intellij/application/options/codeStyle/ManageCodeStyleSchemesDialog.java +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/ManageCodeStyleSchemesDialog.java @@ -214,7 +214,7 @@ public class ManageCodeStyleSchemesDialog extends DialogWrapper { return !file.isDirectory() && importer.getSourceExtension().equals(file.getExtension()); } }, null, myContentPane); - VirtualFile[] selection = fileChooser.choose(CodeStyleSchemesUIConfiguration.Util.getRecentImportFile(), null); + VirtualFile[] selection = fileChooser.choose(null, CodeStyleSchemesUIConfiguration.Util.getRecentImportFile()); if (selection.length == 1) { VirtualFile selectedFile = selection[0]; selectedFile.refresh(false, false); diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/WrappingAndBracesPanel.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/WrappingAndBracesPanel.java index c192d0e2893a..2972dacf2ceb 100644 --- a/platform/lang-impl/src/com/intellij/application/options/codeStyle/WrappingAndBracesPanel.java +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/WrappingAndBracesPanel.java @@ -43,6 +43,7 @@ public class WrappingAndBracesPanel extends OptionTableWithPreviewPanel { addOption("KEEP_SIMPLE_CLASSES_IN_ONE_LINE", ApplicationBundle.message("wrapping.keep.simple.classes.in.one.line"), WRAPPING_KEEP); addOption("WRAP_LONG_LINES", ApplicationBundle.message("wrapping.long.lines"), null); + addOption("WRAP_COMMENTS", ApplicationBundle.message("wrapping.comments.wrap.at.right.margin"), WRAPPING_COMMENTS); addOption("CLASS_BRACE_STYLE", ApplicationBundle.message("wrapping.brace.placement.class.declaration"), WRAPPING_BRACES, BRACE_PLACEMENT_OPTIONS, BRACE_PLACEMENT_VALUES); addOption("METHOD_BRACE_STYLE", ApplicationBundle.message("wrapping.brace.placement.method.declaration"), WRAPPING_BRACES, BRACE_PLACEMENT_OPTIONS, BRACE_PLACEMENT_VALUES); diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/action/AbstractArrangementRuleAction.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/action/AbstractArrangementRuleAction.java new file mode 100644 index 000000000000..3036ca9ed43e --- /dev/null +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/action/AbstractArrangementRuleAction.java @@ -0,0 +1,43 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.application.options.codeStyle.arrangement.action; + +import com.intellij.application.options.codeStyle.arrangement.match.ArrangementMatchingRulesControl; +import com.intellij.openapi.actionSystem.AnAction; +import org.jetbrains.annotations.NotNull; + +import java.awt.*; + +/** + * @author Svetlana.Zemlyanskaya + */ +public abstract class AbstractArrangementRuleAction extends AnAction { + + protected void scrollRowToVisible(@NotNull ArrangementMatchingRulesControl control, int row) { + final Rectangle rect = control.getCellRect(row, 0, false); + if (row != control.getEditingRow() - 1) { + control.scrollRectToVisible(rect); + } + else { + final Rectangle editorRect = control.getCellRect(row + 1, 0, false); + if(!rect.isEmpty() && !editorRect.isEmpty()) { + final int height = (int)(rect.getHeight() + editorRect.getHeight()); + final Rectangle visibleRect = new Rectangle((int)rect.getX(), (int)rect.getY(), (int)rect.getWidth(), height); + control.scrollRectToVisible(visibleRect); + } + } + } +} diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/action/AbstractMoveArrangementRuleAction.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/action/AbstractMoveArrangementRuleAction.java index 0cbfa2265dc8..b790c17a659d 100644 --- a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/action/AbstractMoveArrangementRuleAction.java +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/action/AbstractMoveArrangementRuleAction.java @@ -17,7 +17,6 @@ package com.intellij.application.options.codeStyle.arrangement.action; import com.intellij.application.options.codeStyle.arrangement.match.ArrangementMatchingRulesControl; import com.intellij.application.options.codeStyle.arrangement.match.ArrangementMatchingRulesModel; -import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.project.DumbAware; import org.jetbrains.annotations.NotNull; @@ -30,7 +29,7 @@ import java.util.List; * @author Denis Zhdanov * @since 11/13/12 7:17 PM */ -public abstract class AbstractMoveArrangementRuleAction extends AnAction implements DumbAware { +public abstract class AbstractMoveArrangementRuleAction extends AbstractArrangementRuleAction implements DumbAware { @Override public void update(AnActionEvent e) { @@ -95,8 +94,17 @@ public abstract class AbstractMoveArrangementRuleAction extends AnAction impleme } + int visibleRow = -1; if (newRowToEdit >= 0) { control.showEditor(newRowToEdit); + visibleRow = newRowToEdit; + } + else if (!mappings.isEmpty()) { + visibleRow = mappings.get(0)[1]; + } + + if (visibleRow != -1) { + scrollRowToVisible(control, visibleRow); } } }); diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/action/AddArrangementRuleAction.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/action/AddArrangementRuleAction.java index f19b4513a628..fdde6eab9cbc 100644 --- a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/action/AddArrangementRuleAction.java +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/action/AddArrangementRuleAction.java @@ -19,7 +19,6 @@ import com.intellij.application.options.codeStyle.arrangement.match.ArrangementM import com.intellij.application.options.codeStyle.arrangement.match.ArrangementMatchingRulesModel; import com.intellij.application.options.codeStyle.arrangement.match.EmptyArrangementRuleComponent; import com.intellij.icons.AllIcons; -import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.application.ApplicationBundle; import com.intellij.openapi.project.DumbAware; @@ -31,7 +30,7 @@ import org.jetbrains.annotations.NotNull; * @author Denis Zhdanov * @since 8/24/12 1:54 PM */ -public class AddArrangementRuleAction extends AnAction implements DumbAware { +public class AddArrangementRuleAction extends AbstractArrangementRuleAction implements DumbAware { public AddArrangementRuleAction() { getTemplatePresentation().setText(ApplicationBundle.message("arrangement.action.rule.add.text")); @@ -64,6 +63,7 @@ public class AddArrangementRuleAction extends AnAction implements DumbAware { } showEditor(control, rowToEdit); control.getSelectionModel().setSelectionInterval(rowToEdit, rowToEdit); + scrollRowToVisible(control, rowToEdit); } @NotNull diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/action/EditArrangementRuleAction.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/action/EditArrangementRuleAction.java index ffccce252176..14beaa21a1d9 100644 --- a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/action/EditArrangementRuleAction.java +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/action/EditArrangementRuleAction.java @@ -16,7 +16,6 @@ package com.intellij.application.options.codeStyle.arrangement.action; import com.intellij.application.options.codeStyle.arrangement.match.ArrangementMatchingRulesControl; -import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.Toggleable; import com.intellij.openapi.application.ApplicationBundle; @@ -27,7 +26,7 @@ import gnu.trove.TIntArrayList; * @author Denis Zhdanov * @since 10/29/12 11:01 AM */ -public class EditArrangementRuleAction extends AnAction implements DumbAware, Toggleable { +public class EditArrangementRuleAction extends AbstractArrangementRuleAction implements DumbAware, Toggleable { public EditArrangementRuleAction() { getTemplatePresentation().setText(ApplicationBundle.message("arrangement.action.rule.edit.text")); @@ -50,6 +49,8 @@ public class EditArrangementRuleAction extends AnAction implements DumbAware, To if (rows.size() != 1) { return; } - control.showEditor(rows.get(0)); + final int row = rows.get(0); + control.showEditor(row); + scrollRowToVisible(control, row); } } diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/action/RemoveArrangementRuleAction.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/action/RemoveArrangementRuleAction.java index e1abf16f7eb1..5fbcdccd38e9 100644 --- a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/action/RemoveArrangementRuleAction.java +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/action/RemoveArrangementRuleAction.java @@ -39,7 +39,7 @@ public class RemoveArrangementRuleAction extends AnAction implements DumbAware { @Override public void update(AnActionEvent e) { ArrangementMatchingRulesControl control = ArrangementMatchingRulesControl.KEY.getData(e.getDataContext()); - e.getPresentation().setEnabled(control != null && !control.getSelectedModelRows().isEmpty()); + e.getPresentation().setEnabled(control != null && !control.getSelectedModelRows().isEmpty() && control.getEditingRow() == -1); e.getPresentation().setIcon(SystemInfoRt.isMac ? AllIcons.ToolbarDecorator.Mac.Remove : AllIcons.ToolbarDecorator.Remove); } diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/group/ArrangementGroupingRulesControl.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/group/ArrangementGroupingRulesControl.java index 581eb0aa96a2..a847aecd289f 100644 --- a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/group/ArrangementGroupingRulesControl.java +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/group/ArrangementGroupingRulesControl.java @@ -38,6 +38,7 @@ import java.awt.event.MouseEvent; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Set; /** * @author Denis Zhdanov @@ -95,17 +96,21 @@ public class ArrangementGroupingRulesControl extends JBTable { model.removeRow(model.getRowCount() - 1); } - List types = ContainerUtilRt.newArrayList(myComponents.keySet()); + final Set groupingTokens = ContainerUtilRt.newHashSet(myComponents.keySet()); + for (ArrangementGroupingRule rule : rules) { + final ArrangementSettingsToken groupingType = rule.getGroupingType(); + ArrangementGroupingComponent component = myComponents.get(groupingType); + component.setSelected(true); + component.setOrderType(rule.getOrderType()); + model.addRow(new Object[]{component}); + groupingTokens.remove(groupingType); + } + + List types = ContainerUtilRt.newArrayList(groupingTokens); types = mySettingsManager.sort(types); for (ArrangementSettingsToken type : types) { model.addRow(new Object[]{myComponents.get(type)}); } - for (ArrangementGroupingRule rule : rules) { - ArrangementGroupingComponent component = myComponents.get(rule.getGroupingType()); - component.setSelected(true); - ArrangementSettingsToken orderType = rule.getOrderType(); - component.setOrderType(orderType); - } } @NotNull diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/match/ArrangementMatchingRulesControl.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/match/ArrangementMatchingRulesControl.java index 145ae1094e0b..a77e5cce28f0 100644 --- a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/match/ArrangementMatchingRulesControl.java +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/match/ArrangementMatchingRulesControl.java @@ -527,35 +527,52 @@ public class ArrangementMatchingRulesControl extends JBTable { private class MyValidator { @Nullable private String validate(int index) { - if (mySectionRuleManager == null || getModel().getSize() < index) { + if (getModel().getSize() < index) { return null; } + if (mySectionRuleManager != null) { + final ArrangementSectionRuleData data = extractSectionText(index); + if (data != null) { + return validateSectionRule(data, index); + } + } + + final Object target = getModel().getElementAt(index); + if (target instanceof StdArrangementMatchRule) { + for (int i = 0; i < index; i++) { + final Object element = getModel().getElementAt(i); + if (element instanceof StdArrangementMatchRule && target.equals(element)) { + return ApplicationBundle.message("arrangement.settings.validation.duplicate.matching.rule"); + } + } + } + return null; + } + + @Nullable + private String validateSectionRule(@NotNull ArrangementSectionRuleData data, int index) { int startSectionIndex = -1; - final Set rules = ContainerUtil.newHashSet(); + final Set sectionRules = ContainerUtil.newHashSet(); for (int i = 0; i < index; i++) { final ArrangementSectionRuleData section = extractSectionText(i); if (section != null) { startSectionIndex = section.isSectionStart() ? i : -1; if (StringUtil.isNotEmpty(section.getText())) { - rules.add(section.getText()); + sectionRules.add(section.getText()); } } } + if (StringUtil.isNotEmpty(data.getText()) && sectionRules.contains(data.getText())) { + return ApplicationBundle.message("arrangement.settings.validation.duplicate.section.text"); + } - final ArrangementSectionRuleData data = extractSectionText(index); - if (data != null) { - if (StringUtil.isNotEmpty(data.getText()) && rules.contains(data.getText())) { - return ApplicationBundle.message("arrangement.settings.validation.duplicate.section.text"); + if (!data.isSectionStart()) { + if (startSectionIndex == -1) { + return ApplicationBundle.message("arrangement.settings.validation.end.section.rule.without.start"); } - - if (!data.isSectionStart()) { - if (startSectionIndex == -1) { - return ApplicationBundle.message("arrangement.settings.validation.end.section.rule.without.start"); - } - else if (startSectionIndex == index - 1) { - return ApplicationBundle.message("arrangement.settings.validation.empty.section.rule"); - } + else if (startSectionIndex == index - 1) { + return ApplicationBundle.message("arrangement.settings.validation.empty.section.rule"); } } return null; diff --git a/platform/lang-impl/src/com/intellij/application/options/editor/EditorTabsConfigurable.form b/platform/lang-impl/src/com/intellij/application/options/editor/EditorTabsConfigurable.form index edb9fcdb9e18..4bc9490d0090 100644 --- a/platform/lang-impl/src/com/intellij/application/options/editor/EditorTabsConfigurable.form +++ b/platform/lang-impl/src/com/intellij/application/options/editor/EditorTabsConfigurable.form @@ -109,7 +109,7 @@ - + diff --git a/platform/lang-impl/src/com/intellij/application/options/editor/EditorTabsConfigurable.java b/platform/lang-impl/src/com/intellij/application/options/editor/EditorTabsConfigurable.java index 35a34085d035..93cde6a72f6d 100644 --- a/platform/lang-impl/src/com/intellij/application/options/editor/EditorTabsConfigurable.java +++ b/platform/lang-impl/src/com/intellij/application/options/editor/EditorTabsConfigurable.java @@ -46,6 +46,7 @@ public class EditorTabsConfigurable implements EditorOptionsProvider { private JCheckBox myShowDirectoryInTabCheckBox; private JRadioButton myActivateRightNeighbouringTabRadioButton; private JTextField myTabTitleLimitField; + private JLabel myTabTitleLimitLabel; public EditorTabsConfigurable() { myEditorTabPlacement.setModel(new DefaultComboBoxModel(new Object[]{ @@ -69,21 +70,15 @@ public class EditorTabsConfigurable implements EditorOptionsProvider { private void revalidateSingleRowCheckbox() { final int i = ((Integer)myEditorTabPlacement.getSelectedItem()).intValue(); - if (i == UISettings.TABS_NONE) { - myHideKnownExtensions.setEnabled(false); - myScrollTabLayoutInEditorCheckBox.setEnabled(false); - myCbModifiedTabsMarkedWithAsterisk.setEnabled(false); - myShowTabsTooltipsCheckBox.setEnabled(false); - myShowCloseButtonOnCheckBox.setEnabled(false); - myShowDirectoryInTabCheckBox.setEnabled(false); - } else { - myHideKnownExtensions.setEnabled(true); - myScrollTabLayoutInEditorCheckBox.setEnabled(true); - myCbModifiedTabsMarkedWithAsterisk.setEnabled(true); - myShowTabsTooltipsCheckBox.setEnabled(true); - myShowCloseButtonOnCheckBox.setEnabled(true); - myShowDirectoryInTabCheckBox.setEnabled(true); - } + boolean none = i == UISettings.TABS_NONE; + myHideKnownExtensions.setEnabled(!none); + myScrollTabLayoutInEditorCheckBox.setEnabled(!none); + myCbModifiedTabsMarkedWithAsterisk.setEnabled(!none); + myShowTabsTooltipsCheckBox.setEnabled(!none); + myShowCloseButtonOnCheckBox.setEnabled(!none); + myShowDirectoryInTabCheckBox.setEnabled(!none); + myTabTitleLimitField.setEnabled(!none); + myTabTitleLimitLabel.setEnabled(!none); if (SwingConstants.TOP == i) { myScrollTabLayoutInEditorCheckBox.setEnabled(true); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/actions/FormatChangedTextUtil.java b/platform/lang-impl/src/com/intellij/codeInsight/actions/FormatChangedTextUtil.java index 3b51cc9ad976..dadafc7a7d84 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/actions/FormatChangedTextUtil.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/actions/FormatChangedTextUtil.java @@ -250,7 +250,7 @@ public class FormatChangedTextUtil { } @NotNull - public static List getChangedTextRanges(@NotNull Project project, @NotNull PsiFile file) { + public static List getChangedTextRanges(@NotNull Project project, @NotNull PsiFile file) throws FilesTooBigForDiffException { Change change = ChangeListManager.getInstance(project).getChange(file.getVirtualFile()); if (change == null) { return ContainerUtilRt.emptyList(); @@ -281,7 +281,10 @@ public class FormatChangedTextUtil { } @NotNull - private static List calculateChangedTextRanges(@NotNull Project project, @NotNull PsiFile file, @NotNull String contentFromVcs) { + private static List calculateChangedTextRanges(@NotNull Project project, + @NotNull PsiFile file, + @NotNull String contentFromVcs) throws FilesTooBigForDiffException + { Document documentFromVcs = ((EditorFactoryImpl)EditorFactory.getInstance()).createDocument(contentFromVcs, true, false); Document document = PsiDocumentManager.getInstance(project).getDocument(file); @@ -289,23 +292,16 @@ public class FormatChangedTextUtil { return ContainerUtil.emptyList(); } - try { - List changedRanges; - - LineStatusTracker tracker = LineStatusTrackerManager.getInstance(project).getLineStatusTracker(document); - if (tracker != null) { - changedRanges = tracker.getRanges(); - } - else { - changedRanges = new RangesBuilder(document, documentFromVcs).getRanges(); - } - - return getChangedTextRanges(document, changedRanges); + List changedRanges; + LineStatusTracker tracker = LineStatusTrackerManager.getInstance(project).getLineStatusTracker(document); + if (tracker != null) { + changedRanges = tracker.getRanges(); } - catch (FilesTooBigForDiffException e) { - LOG.error("Error while calculating changed ranges for: " + file.getVirtualFile(), e); - return ContainerUtil.emptyList(); + else { + changedRanges = new RangesBuilder(document, documentFromVcs).getRanges(); } + + return getChangedTextRanges(document, changedRanges); } @NotNull diff --git a/platform/lang-impl/src/com/intellij/codeInsight/actions/ReformatCodeProcessor.java b/platform/lang-impl/src/com/intellij/codeInsight/actions/ReformatCodeProcessor.java index ff032c22747b..270f2f92f3cf 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/actions/ReformatCodeProcessor.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/actions/ReformatCodeProcessor.java @@ -18,6 +18,10 @@ package com.intellij.codeInsight.actions; import com.intellij.codeInsight.CodeInsightBundle; import com.intellij.formatting.FormattingProgressTask; +import com.intellij.notification.Notification; +import com.intellij.notification.NotificationType; +import com.intellij.openapi.application.ApplicationBundle; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; @@ -27,6 +31,7 @@ import com.intellij.psi.PsiFile; import com.intellij.psi.codeStyle.CodeStyleManager; import com.intellij.util.IncorrectOperationException; import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.diff.FilesTooBigForDiffException; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -90,6 +95,17 @@ public class ReformatCodeProcessor extends AbstractLayoutCodeProcessor { CodeStyleManager.getInstance(myProject).reformatText(file, ranges); return !FormattingProgressTask.FORMATTING_CANCELLED_FLAG.get(); } + catch (FilesTooBigForDiffException e) { + LOG.info("Error while calculating changed ranges for: " + file.getVirtualFile(), e); + if (!ApplicationManager.getApplication().isUnitTestMode()) { + Notification notification = new Notification(ApplicationBundle.message("reformat.changed.text.file.too.big.notification.groupId"), + ApplicationBundle.message("reformat.changed.text.file.too.big.notification.title"), + ApplicationBundle.message("reformat.changed.text.file.too.big.notification.text", file.getName()), + NotificationType.INFORMATION); + notification.notify(file.getProject()); + } + return false; + } catch (IncorrectOperationException e) { LOG.error(e); return false; @@ -102,7 +118,7 @@ public class ReformatCodeProcessor extends AbstractLayoutCodeProcessor { } @NotNull - private Collection getRangesToFormat(boolean processChangedTextOnly, PsiFile file) { + private Collection getRangesToFormat(boolean processChangedTextOnly, PsiFile file) throws FilesTooBigForDiffException { if (processChangedTextOnly) { return FormatChangedTextUtil.getChangedTextRanges(myProject, file); } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/completion/CodeCompletionHandlerBase.java b/platform/lang-impl/src/com/intellij/codeInsight/completion/CodeCompletionHandlerBase.java index 3bdd05f58a40..dd506593d942 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/completion/CodeCompletionHandlerBase.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/CodeCompletionHandlerBase.java @@ -65,9 +65,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; -import java.util.concurrent.atomic.AtomicReference; public class CodeCompletionHandlerBase { private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.completion.CodeCompletionHandlerBase"); @@ -306,16 +304,24 @@ public class CodeCompletionHandlerBase { CompletionServiceImpl.setCompletionPhase(synchronous ? new CompletionPhase.Synchronous(indicator) : new CompletionPhase.BgCalculation(indicator)); - final AtomicReference data = indicator.startCompletion(initContext); + indicator.startCompletion(initContext); if (!synchronous) { return; } if (freezeSemaphore.waitFor(2000)) { - final LookupElement[] allItems = data.get(); - if (allItems != null && !indicator.isRunning() && !indicator.isCanceled()) { // the completion is really finished, now we may auto-insert or show lookup - completionFinished(initContext.getStartOffset(), initContext.getSelectionEndOffset(), indicator, allItems, hasModifiers); + if (!indicator.isRunning() && !indicator.isCanceled()) { // the completion is really finished, now we may auto-insert or show lookup + try { + indicator.getLookup().refreshUi(true, false); + } + catch (Exception e) { + CompletionServiceImpl.setCompletionPhase(CompletionPhase.NoCompletion); + LOG.error(e); + return; + } + + completionFinished(indicator, hasModifiers); return; } } @@ -324,9 +330,9 @@ public class CodeCompletionHandlerBase { indicator.showLookup(); } - private static void checkNotSync(CompletionProgressIndicator indicator, LookupElement[] allItems) { + private static void checkNotSync(CompletionProgressIndicator indicator, List allItems) { if (CompletionServiceImpl.isPhase(CompletionPhase.Synchronous.class)) { - LOG.error("sync phase survived: " + Arrays.toString(allItems) + "; indicator=" + CompletionServiceImpl.getCompletionPhase().indicator + "; myIndicator=" + indicator); + LOG.error("sync phase survived: " + allItems + "; indicator=" + CompletionServiceImpl.getCompletionPhase().indicator + "; myIndicator=" + indicator); CompletionServiceImpl.setCompletionPhase(CompletionPhase.NoCompletion); } } @@ -345,17 +351,16 @@ public class CodeCompletionHandlerBase { private static PsiElement findCompletionPositionLeaf(CompletionContext newContext, int offset, PsiFile fileCopy, PsiFile originalFile) { final PsiElement insertedElement = newContext.file.findElementAt(offset); CompletionAssertions.assertCompletionPositionPsiConsistent(newContext, offset, fileCopy, originalFile, insertedElement); - assert insertedElement != null; return insertedElement; } - private AutoCompletionDecision shouldAutoComplete(final CompletionProgressIndicator indicator, final LookupElement[] items) { + private AutoCompletionDecision shouldAutoComplete(final CompletionProgressIndicator indicator, List items) { if (!invokedExplicitly) { return AutoCompletionDecision.SHOW_LOOKUP; } final CompletionParameters parameters = indicator.getParameters(); - final LookupElement item = items[0]; - if (items.length == 1) { + final LookupElement item = items.get(0); + if (items.size() == 1) { final AutoCompletionPolicy policy = getAutocompletionPolicy(item); if (policy == AutoCompletionPolicy.NEVER_AUTOCOMPLETE) return AutoCompletionDecision.SHOW_LOOKUP; if (policy == AutoCompletionPolicy.ALWAYS_AUTOCOMPLETE) return AutoCompletionDecision.insertItem(item); @@ -367,11 +372,11 @@ public class CodeCompletionHandlerBase { if (isInsideIdentifier(indicator.getOffsetMap())) { return AutoCompletionDecision.SHOW_LOOKUP; } - if (items.length == 1 && getAutocompletionPolicy(item) == AutoCompletionPolicy.GIVE_CHANCE_TO_OVERWRITE) { + if (items.size() == 1 && getAutocompletionPolicy(item) == AutoCompletionPolicy.GIVE_CHANCE_TO_OVERWRITE) { return AutoCompletionDecision.insertItem(item); } - AutoCompletionContext context = new AutoCompletionContext(parameters, items, indicator.getOffsetMap(), indicator.getLookup()); + AutoCompletionContext context = new AutoCompletionContext(parameters, items.toArray(new LookupElement[items.size()]), indicator.getOffsetMap(), indicator.getLookup()); for (final CompletionContributor contributor : CompletionContributor.forParameters(parameters)) { final AutoCompletionDecision decision = contributor.handleAutoCompletionPossibility(context); if (decision != null) { @@ -401,12 +406,9 @@ public class CodeCompletionHandlerBase { return offsetMap.getOffset(CompletionInitializationContext.IDENTIFIER_END_OFFSET) != offsetMap.getOffset(CompletionInitializationContext.SELECTION_END_OFFSET); } - - protected void completionFinished(final int offset1, - final int offset2, - final CompletionProgressIndicator indicator, - final LookupElement[] items, boolean hasModifiers) { - if (items.length == 0) { + protected void completionFinished(final CompletionProgressIndicator indicator, boolean hasModifiers) { + final List items = indicator.getLookup().getItems(); + if (items.isEmpty()) { LookupManager.getInstance(indicator.getProject()).hideActiveLookup(); Caret nextCaret = getNextCaretToProcess(indicator.getEditor()); @@ -424,7 +426,6 @@ public class CodeCompletionHandlerBase { LOG.assertTrue(!indicator.isCanceled(), "canceled"); try { - indicator.getLookup().refreshUi(true, false); final AutoCompletionDecision decision = shouldAutoComplete(indicator, items); if (decision == AutoCompletionDecision.SHOW_LOOKUP) { CompletionServiceImpl.setCompletionPhase(new CompletionPhase.ItemsCalculated(indicator)); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionProgressIndicator.java b/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionProgressIndicator.java index 8ca94d80463c..4f02d6bc5e54 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionProgressIndicator.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionProgressIndicator.java @@ -81,7 +81,6 @@ import java.beans.PropertyChangeListener; import java.util.List; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.atomic.AtomicReference; /** * @author peter @@ -742,7 +741,7 @@ public class CompletionProgressIndicator extends ProgressIndicatorBase implement return false; } - AtomicReference startCompletion(final CompletionInitializationContext initContext) { + void startCompletion(final CompletionInitializationContext initContext) { boolean sync = ApplicationManager.getApplication().isUnitTestMode() && !CompletionAutoPopupHandler.ourTestingAutopopup; final CompletionThreading strategy = sync ? new SyncCompletion() : new AsyncCompletion(); @@ -754,12 +753,11 @@ public class CompletionProgressIndicator extends ProgressIndicatorBase implement }); final WeighingDelegate weigher = strategy.delegateWeighing(this); - final AtomicReference data = new AtomicReference(null); class CalculateItems implements Runnable { @Override public void run() { try { - data.set(calculateItems(initContext, weigher)); + calculateItems(initContext, weigher); } catch (ProcessCanceledException ignore) { cancel(); // some contributor may just throw PCE; if indicator is not canceled everything will hang @@ -771,7 +769,6 @@ public class CompletionProgressIndicator extends ProgressIndicatorBase implement } } strategy.startThread(this, new CalculateItems()); - return data; } private LookupElement[] calculateItems(CompletionInitializationContext initContext, WeighingDelegate weigher) { diff --git a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/DaemonCodeAnalyzerImpl.java b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/DaemonCodeAnalyzerImpl.java index 4ac5e686c5bc..0d02b32baed4 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/DaemonCodeAnalyzerImpl.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/DaemonCodeAnalyzerImpl.java @@ -58,6 +58,7 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.openapi.vfs.newvfs.RefreshQueueImpl; import com.intellij.packageDependencies.DependencyValidationManager; +import com.intellij.psi.FileViewProvider; import com.intellij.psi.PsiCompiledElement; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiFile; @@ -184,8 +185,9 @@ public class DaemonCodeAnalyzerImpl extends DaemonCodeAnalyzerEx implements JDOM @Override public void cleanFileLevelHighlights(@NotNull Project project, final int group, PsiFile psiFile) { - if (psiFile == null || !psiFile.getViewProvider().isPhysical()) return; - VirtualFile vFile = psiFile.getViewProvider().getVirtualFile(); + if (psiFile == null) return; + FileViewProvider provider = psiFile.getViewProvider(); + VirtualFile vFile = provider.getVirtualFile(); final FileEditorManager manager = FileEditorManager.getInstance(project); for (FileEditor fileEditor : manager.getEditors(vFile)) { final List infos = fileEditor.getUserData(FILE_LEVEL_HIGHLIGHTS); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/PassExecutorService.java b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/PassExecutorService.java index 44df90322011..24c3d8b4a6fb 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/PassExecutorService.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/PassExecutorService.java @@ -34,6 +34,7 @@ import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.fileEditor.FileEditor; import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.fileEditor.TextEditor; +import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; @@ -94,6 +95,12 @@ public class PassExecutorService implements Disposable { } catch (ProcessCanceledException ignored) { + } + catch (Error e) { + throw e; + } + catch (RuntimeException e) { + throw e; } catch (Throwable throwable) { LOG.error(throwable); @@ -118,6 +125,10 @@ public class PassExecutorService implements Disposable { LOG.assertTrue(!(editor instanceof EditorWindow)); document = editor.getDocument(); } + else { + VirtualFile virtualFile = ((FileEditorManagerEx)FileEditorManager.getInstance(myProject)).getFile(fileEditor); + document = virtualFile == null ? null : FileDocumentManager.getInstance().getDocument(virtualFile); + } int prevId = 0; for (final HighlightingPass pass : passes) { @@ -534,7 +545,7 @@ public class PassExecutorService implements Disposable { return result; } - private void sortById(@NotNull List result) { + private static void sortById(@NotNull List result) { ContainerUtil.quickSort(result, new Comparator() { @Override public int compare(TextEditorHighlightingPass o1, TextEditorHighlightingPass o2) { @@ -548,7 +559,7 @@ public class PassExecutorService implements Disposable { return ConcurrencyUtil.cacheOrGet(threads, Thread.currentThread(), threads.size()); } - public static void log(ProgressIndicator progressIndicator, TextEditorHighlightingPass pass, @NonNls Object... info) { + public static void log(ProgressIndicator progressIndicator, TextEditorHighlightingPass pass, @NonNls @NotNull Object... info) { if (LOG.isDebugEnabled()) { CharSequence docText = pass == null ? "" : StringUtil.first(pass.getDocument().getCharsSequence(), 10, true); synchronized (PassExecutorService.class) { diff --git a/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationComponent.java b/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationComponent.java index 50f69088033b..10515ba07bef 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationComponent.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationComponent.java @@ -106,7 +106,10 @@ public class DocumentationComponent extends JPanel implements Disposable, DataPr @Override public Image get(Object key) { PsiElement element = getElement(); - return element == null ? null : myManager.getElementImage(element, ((URL)key).toExternalForm()); + if (element == null) return null; + URL url = (URL)key; + Image inMemory = myManager.getElementImage(element, url.toExternalForm()); + return inMemory != null ? inMemory : Toolkit.getDefaultToolkit().createImage(url); } }; diff --git a/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationManager.java b/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationManager.java index 63484f890225..319ce54196da 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationManager.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationManager.java @@ -378,6 +378,8 @@ public class DocumentationManager extends DockablePopupManager unique = new LinkedHashSet(Arrays.asList(targetElements)); - for (PsiElement elt : targetElements) { - final PsiFile containingFile = elt.getContainingFile(); - LOG.assertTrue(containingFile != null, elt); - PsiFile psiFile = containingFile.getOriginalFile(); - if (psiFile.getVirtualFile() == null) unique.remove(elt); + final Set unique = new LinkedHashSet(Arrays.asList(targetElements)); + for (final PsiElement elt : targetElements) { + ApplicationManager.getApplication().runReadAction(new Runnable() { + @Override + public void run() { + final PsiFile containingFile = elt.getContainingFile(); + LOG.assertTrue(containingFile != null, elt); + PsiFile psiFile = containingFile.getOriginalFile(); + if (psiFile.getVirtualFile() == null) unique.remove(elt); + } + }); } // special case for Python (PY-237) // if the definition is the tree parent of the target element, filter out the target element @@ -392,9 +397,9 @@ public class ShowImplementationsAction extends AnAction implements PopupAction { } private static class ImplementationsUpdaterTask extends BackgroundUpdaterTask { - private String myCaption; - private Editor myEditor; - private PsiElement myElement; + private final String myCaption; + private final Editor myEditor; + private final PsiElement myElement; private final boolean myIncludeSelf; private PsiElement[] myElements; diff --git a/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/QuickEditHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/QuickEditHandler.java index 517f23c5a198..b236e1061315 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/QuickEditHandler.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/QuickEditHandler.java @@ -72,7 +72,6 @@ import org.jetbrains.annotations.TestOnly; import javax.swing.*; import java.awt.*; -import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; @@ -97,8 +96,7 @@ public class QuickEditHandler extends DocumentAdapter implements Disposable { @Nullable private final PsiFile myInjectedFile; - private final List> myMarkers = - new LinkedList>(); + private final List> myMarkers = ContainerUtil.newLinkedList(); @Nullable private final RangeMarker myAltFullRange; @@ -199,7 +197,16 @@ public class QuickEditHandler extends DocumentAdapter implements Disposable { } public boolean isValid() { - return myNewVirtualFile.isValid() && (myAltFullRange == null && myInjectedFile.isValid() || myAltFullRange.isValid()); + boolean valid = myNewVirtualFile.isValid() && (myAltFullRange == null && myInjectedFile.isValid() || myAltFullRange.isValid()); + if (valid) { + for (Trinity t : myMarkers) { + if (!t.first.isValid() || !t.second.isValid() || t.third.getElement() == null) { + valid = false; + break; + } + } + } + return valid; } public void navigate(int injectedOffset) { @@ -285,6 +292,7 @@ public class QuickEditHandler extends DocumentAdapter implements Disposable { } else if (e.getDocument() == myNewDocument) { commitToOriginal(); + if (!isValid()) closeEditor(); } else if (e.getDocument() == myOrigDocument) { if (myCommittingToOriginal || myAltFullRange != null && myAltFullRange.isValid()) return; @@ -359,7 +367,6 @@ public class QuickEditHandler extends DocumentAdapter implements Disposable { private void commitToOriginal() { - if (!isValid()) return; VirtualFile origVirtualFile = PsiUtilCore.getVirtualFile(myNewFile.getContext()); myCommittingToOriginal = true; try { @@ -401,7 +408,7 @@ public class QuickEditHandler extends DocumentAdapter implements Disposable { ProperTextRange insideHost = null; StringBuilder sb = new StringBuilder(); for (Trinity entry : map.get(host)) { - RangeMarker origMarker = entry.first; + RangeMarker origMarker = entry.first; // check for validity? int hostOffset = host.getTextRange().getStartOffset(); ProperTextRange localInsideHost = new ProperTextRange(origMarker.getStartOffset() - hostOffset, origMarker.getEndOffset() - hostOffset); RangeMarker rangeMarker = entry.second; diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/EditVariableDialog.java b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/EditVariableDialog.java index e32fab35508b..bab6ade7b935 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/EditVariableDialog.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/EditVariableDialog.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2011 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,6 +26,7 @@ import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.help.HelpManager; +import com.intellij.openapi.ui.ComboBox; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.ui.ToolbarDecorator; import com.intellij.ui.table.JBTable; @@ -107,11 +108,11 @@ class EditVariableDialog extends DialogWrapper { myTable.getSelectionModel().setSelectionInterval(0, 0); } - JComboBox comboField = new JComboBox(); + ComboBox comboField = new ComboBox(); Macro[] macros = MacroFactory.getMacros(); Arrays.sort(macros, new Comparator () { @Override - public int compare(Macro m1, Macro m2) { + public int compare(@NotNull Macro m1, @NotNull Macro m2) { return m1.getPresentableName().compareTo(m2.getPresentableName()); } }); @@ -231,11 +232,13 @@ class EditVariableDialog extends DialogWrapper { } } + @NotNull @Override public String getColumnName(int column) { return myNames[column]; } + @NotNull @Override public Class getColumnClass(int c) { if (c <= 2) { diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateListPanel.java b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateListPanel.java index 9a7a724e2d5d..1c0b72a5a454 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateListPanel.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateListPanel.java @@ -19,6 +19,7 @@ package com.intellij.codeInsight.template.impl; import com.intellij.application.options.ExportSchemeAction; import com.intellij.application.options.SchemesToImportPopup; import com.intellij.codeInsight.CodeInsightBundle; +import com.intellij.icons.AllIcons; import com.intellij.ide.DataManager; import com.intellij.ide.dnd.*; import com.intellij.ide.dnd.aware.DnDAwareTree; @@ -690,6 +691,17 @@ public class TemplateListPanel extends JPanel implements Disposable { public void updateButton(AnActionEvent e) { e.getPresentation().setEnabled(getTemplate(getSingleSelectedIndex()) != null); } + }).addExtraAction(new AnActionButton("Restore deleted defaults", AllIcons.General.TodoDefault) { + @Override + public void actionPerformed(@NotNull AnActionEvent e) { + TemplateSettings.getInstance().reset(); + reset(); + } + + @Override + public boolean isEnabled() { + return super.isEnabled() && !TemplateSettings.getInstance().getDeletedTemplates().isEmpty(); + } }); if (getSchemesManager().isExportAvailable()) { decorator.addExtraAction(new AnActionButton("Share...", PlatformIcons.EXPORT_ICON) { diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateSettings.java b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateSettings.java index abf28256aaef..cdefbe83a252 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateSettings.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateSettings.java @@ -158,6 +158,10 @@ public class TemplateSettings implements PersistentStateComponent, Expo this.key = key; } + @Override + public String toString() { + return getKey()+"@" + getGroupName(); + } } private TemplateKey myLastSelectedTemplate; @@ -185,7 +189,7 @@ public class TemplateSettings implements PersistentStateComponent, Expo } @Override - public Document writeScheme(@NotNull final TemplateGroup template) throws WriteExternalException { + public Element writeScheme(@NotNull final TemplateGroup template) throws WriteExternalException { Element templateSetElement = new Element(TEMPLATE_SET); templateSetElement.setAttribute(GROUP, template.getName()); @@ -195,7 +199,7 @@ public class TemplateSettings implements PersistentStateComponent, Expo } } - return new Document(templateSetElement); + return templateSetElement; } @Override @@ -750,4 +754,9 @@ public class TemplateSettings implements PersistentStateComponent, Expo public List getDeletedTemplates() { return myDeletedTemplates; } + + public void reset() { + myDeletedTemplates.clear(); + loadDefaultLiveTemplates(); + } } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateState.java b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateState.java index 0c838cd05ec5..2e620c2379c9 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateState.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/impl/TemplateState.java @@ -723,7 +723,10 @@ public class TemplateState implements Disposable { ExpressionContext context = createExpressionContext(start); Result result = isQuick ? expressionNode.calculateQuickResult(context) : expressionNode.calculateResult(context); - if ((result == null || result.equalsToText("", element)) && defaultValue != null) { + if (isQuick && isEmptyResult(result, element) && !oldValue.isEmpty()) { + return; + } + if (isEmptyResult(result, element) && defaultValue != null) { result = defaultValue.calculateResult(context); } if (element != null) { @@ -743,6 +746,10 @@ public class TemplateState implements Disposable { } } + private static boolean isEmptyResult(Result result, PsiElement context) { + return result == null || result.equalsToText("", context); + } + private void replaceString(String newValue, int start, int end, int segmentNumber) { String oldText = myDocument.getCharsSequence().subSequence(start, end).toString(); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/ElseExpressionPostfixTemplateBase.java b/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/ElseExpressionPostfixTemplateBase.java index 1e9c58488f33..4c810dcf4ff3 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/ElseExpressionPostfixTemplateBase.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/ElseExpressionPostfixTemplateBase.java @@ -16,6 +16,7 @@ package com.intellij.codeInsight.template.postfix.templates; import com.intellij.openapi.util.Condition; +import com.intellij.openapi.util.Conditions; import com.intellij.psi.PsiElement; import org.jetbrains.annotations.NotNull; @@ -28,7 +29,7 @@ public abstract class ElseExpressionPostfixTemplateBase extends SurroundPostfixT @SuppressWarnings("unchecked") protected ElseExpressionPostfixTemplateBase(@NotNull PostfixTemplatePsiInfo psiInfo) { - super("else", "if (!expr)", psiInfo, Condition.TRUE); + super("else", "if (!expr)", psiInfo, Conditions.alwaysTrue()); } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/IfPostfixTemplateBase.java b/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/IfPostfixTemplateBase.java index 90a21a269fed..b7e402f31f66 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/IfPostfixTemplateBase.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/IfPostfixTemplateBase.java @@ -17,6 +17,7 @@ package com.intellij.codeInsight.template.postfix.templates; import com.intellij.openapi.util.Condition; +import com.intellij.openapi.util.Conditions; import com.intellij.psi.PsiElement; import org.jetbrains.annotations.NotNull; @@ -29,6 +30,6 @@ public abstract class IfPostfixTemplateBase extends SurroundPostfixTemplateBase @SuppressWarnings("unchecked") protected IfPostfixTemplateBase(@NotNull PostfixTemplatePsiInfo psiInfo) { - super("if", "if (expr)", psiInfo, Condition.TRUE); + super("if", "if (expr)", psiInfo, Conditions.alwaysTrue()); } } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixLiveTemplate.java b/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixLiveTemplate.java index 0190fadbcf02..61544faf2c81 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixLiveTemplate.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/PostfixLiveTemplate.java @@ -32,6 +32,7 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.util.Condition; +import com.intellij.openapi.util.Conditions; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDocumentManager; @@ -266,15 +267,13 @@ public class PostfixLiveTemplate extends CustomLiveTemplateBase { PsiFile copyFile = copyFile(file, fileContentWithoutKey); Document copyDocument = copyFile.getViewProvider().getDocument(); if (copyDocument == null) { - //noinspection unchecked - return Condition.FALSE; + return Conditions.alwaysFalse(); } copyFile = provider.preCheck(copyFile, editor, newOffset); copyDocument = copyFile.getViewProvider().getDocument(); if (copyDocument == null) { - //noinspection unchecked - return Condition.FALSE; + return Conditions.alwaysFalse(); } final PsiElement context = CustomTemplateCallback.getContext(copyFile, positiveOffset(newOffset)); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/StatementWrapPostfixTemplate.java b/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/StatementWrapPostfixTemplate.java index 5b5c3624f33b..33b8ba541f66 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/StatementWrapPostfixTemplate.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/StatementWrapPostfixTemplate.java @@ -17,6 +17,7 @@ package com.intellij.codeInsight.template.postfix.templates; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.util.Condition; +import com.intellij.openapi.util.Conditions; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiElement; import org.jetbrains.annotations.NotNull; @@ -28,7 +29,7 @@ public abstract class StatementWrapPostfixTemplate extends PostfixTemplateWithEx protected StatementWrapPostfixTemplate(@NotNull String name, @NotNull String descr, @NotNull PostfixTemplatePsiInfo psiInfo) { - super(name, descr, psiInfo, Condition.TRUE); + super(name, descr, psiInfo, Conditions.alwaysTrue()); } protected StatementWrapPostfixTemplate(@NotNull String name, diff --git a/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/SurroundPostfixTemplateBase.java b/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/SurroundPostfixTemplateBase.java index 29ea4d39ac64..09ca27a3f948 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/SurroundPostfixTemplateBase.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/template/postfix/templates/SurroundPostfixTemplateBase.java @@ -18,6 +18,7 @@ package com.intellij.codeInsight.template.postfix.templates; import com.intellij.lang.surroundWith.Surrounder; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.util.Condition; +import com.intellij.openapi.util.Conditions; import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiElement; import org.jetbrains.annotations.NotNull; @@ -35,7 +36,7 @@ public abstract class SurroundPostfixTemplateBase extends StatementWrapPostfixTe protected SurroundPostfixTemplateBase(@NotNull String name, @NotNull String descr, @NotNull PostfixTemplatePsiInfo psiInfo) { - super(name, descr, psiInfo, Condition.TRUE); + super(name, descr, psiInfo, Conditions.alwaysTrue()); } diff --git a/platform/lang-impl/src/com/intellij/codeInspection/actions/GotoInspectionModel.java b/platform/lang-impl/src/com/intellij/codeInspection/actions/GotoInspectionModel.java index 4c6efc2b7897..ddcce12a9236 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/actions/GotoInspectionModel.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/actions/GotoInspectionModel.java @@ -22,13 +22,11 @@ import com.intellij.codeInspection.ex.ScopeToolState; import com.intellij.ide.IdeBundle; import com.intellij.ide.util.gotoByName.SimpleChooseByNameModel; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.profile.codeInspection.InspectionProfileManager; -import com.intellij.util.ArrayUtil; -import com.intellij.util.containers.MultiMap; import javax.swing.*; import java.util.HashMap; -import java.util.HashSet; import java.util.Map; import java.util.Set; @@ -36,11 +34,10 @@ import java.util.Set; * @author Konstantin Bulenkov */ public class GotoInspectionModel extends SimpleChooseByNameModel { - private final MultiMap myToolNames = MultiMap.createSmartList(); - private final Map> myGroupNames = new HashMap>(); - private final Map myToolShortNames = new HashMap(); + private static final InspectionToolWrapper[] EMPTY_WRAPPERS_ARRAY = new InspectionToolWrapper[0]; + private final Map myToolNames = new HashMap(); private final String[] myNames; - private final ListCellRenderer myListCellRenderer = new InspectionListCellRenderer(); + private final InspectionListCellRenderer myListCellRenderer = new InspectionListCellRenderer(); public GotoInspectionModel(Project project) { @@ -48,28 +45,14 @@ public class GotoInspectionModel extends SimpleChooseByNameModel { final InspectionProfileImpl rootProfile = (InspectionProfileImpl)InspectionProfileManager.getInstance().getRootProfile(); for (ScopeToolState state : rootProfile.getAllTools(project)) { InspectionToolWrapper tool = state.getTool(); - InspectionToolWrapper workingTool = tool; - if (tool instanceof LocalInspectionToolWrapper) { - workingTool = LocalInspectionToolWrapper.findTool2RunInBatch(project, null, tool.getShortName()); - if (workingTool == null) { - continue; - } + if (tool instanceof LocalInspectionToolWrapper && ((LocalInspectionToolWrapper)tool).isUnfair()) { + continue; } - myToolNames.putValue(tool.getDisplayName(), workingTool); - final String groupName = tool.getGroupDisplayName(); - Set toolsInGroup = myGroupNames.get(groupName); - if (toolsInGroup == null) { - toolsInGroup = new HashSet(); - myGroupNames.put(groupName, toolsInGroup); - } - toolsInGroup.add(workingTool); - myToolShortNames.put(tool.getShortName(), workingTool); + final String name = tool.getDisplayName() + " " + StringUtil.join(tool.getGroupPath(), " "); + myToolNames.put(name, tool); } - - final Set nameIds = new HashSet(); - nameIds.addAll(myToolNames.keySet()); - nameIds.addAll(myGroupNames.keySet()); - myNames = ArrayUtil.toStringArray(nameIds); + final Set strings = myToolNames.keySet(); + myNames = strings.toArray(new String[strings.size()]); } @Override @@ -83,25 +66,19 @@ public class GotoInspectionModel extends SimpleChooseByNameModel { } @Override - public Object[] getElementsByName(final String id, final String pattern) { - final Set result = new HashSet(); - result.addAll(myToolNames.get(id)); - InspectionToolWrapper e = myToolShortNames.get(id); - if (e != null) { - result.add(e); - } - final Set entries = myGroupNames.get(id); - if (entries != null) { - result.addAll(entries); + public Object[] getElementsByName(final String name, final String pattern) { + final InspectionToolWrapper tool = myToolNames.get(name); + if (tool == null) { + return EMPTY_WRAPPERS_ARRAY; } - return result.toArray(new InspectionToolWrapper[result.size()]); + return new InspectionToolWrapper[] {tool}; } @Override public String getElementName(final Object element) { if (element instanceof InspectionToolWrapper) { InspectionToolWrapper entry = (InspectionToolWrapper)element; - return entry.getDisplayName() + " " + entry.getGroupDisplayName(); + return entry.getDisplayName() + " " + StringUtil.join(entry.getGroupPath(), " "); } return null; } diff --git a/platform/lang-impl/src/com/intellij/codeInspection/actions/InspectionListCellRenderer.java b/platform/lang-impl/src/com/intellij/codeInspection/actions/InspectionListCellRenderer.java index 8608a2d13142..c7386b2ffcb5 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/actions/InspectionListCellRenderer.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/actions/InspectionListCellRenderer.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +20,10 @@ import com.intellij.ide.util.gotoByName.ChooseByNameBase; import com.intellij.lang.Language; import com.intellij.openapi.fileTypes.LanguageFileType; import com.intellij.openapi.fileTypes.UnknownFileType; +import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.util.registry.Registry; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.codeStyle.MinusculeMatcher; import com.intellij.ui.JBColor; import com.intellij.ui.SimpleColoredComponent; import com.intellij.ui.SimpleTextAttributes; @@ -31,6 +35,8 @@ import org.jetbrains.annotations.NotNull; import javax.swing.*; import java.awt.*; +import java.util.ArrayList; +import java.util.List; /** * @author Konstantin Bulenkov @@ -38,24 +44,31 @@ import java.awt.*; @SuppressWarnings({"GtkPreferredJComboBoxRenderer"}) public class InspectionListCellRenderer extends DefaultListCellRenderer implements MatcherHolder { private Matcher myMatcher; - private final SimpleTextAttributes SELECTED; - private final SimpleTextAttributes PLAIN; + private final SimpleTextAttributes mySelected; + private final SimpleTextAttributes myPlain; + private final SimpleTextAttributes myHighlighted; public InspectionListCellRenderer() { - SELECTED = new SimpleTextAttributes(UIUtil.getListSelectionBackground(), - UIUtil.getListSelectionForeground(), - JBColor.RED, - SimpleTextAttributes.STYLE_PLAIN); - PLAIN = new SimpleTextAttributes(UIUtil.getListBackground(), - UIUtil.getListForeground(), - JBColor.RED, - SimpleTextAttributes.STYLE_PLAIN); + mySelected = new SimpleTextAttributes(UIUtil.getListSelectionBackground(), + UIUtil.getListSelectionForeground(), + JBColor.RED, + SimpleTextAttributes.STYLE_PLAIN); + myPlain = new SimpleTextAttributes(UIUtil.getListBackground(), + UIUtil.getListForeground(), + JBColor.RED, + SimpleTextAttributes.STYLE_PLAIN); + myHighlighted = new SimpleTextAttributes(UIUtil.getListBackground(), + UIUtil.getListForeground(), + null, + SimpleTextAttributes.STYLE_SEARCH_MATCH); } @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean sel, boolean focus) { - final JPanel panel = new JPanel(new BorderLayout()); + final BorderLayout layout = new BorderLayout(); + layout.setHgap(5); + final JPanel panel = new JPanel(layout); panel.setOpaque(true); final Color bg = sel ? UIUtil.getListSelectionBackground() : UIUtil.getListBackground(); @@ -63,15 +76,37 @@ public class InspectionListCellRenderer extends DefaultListCellRenderer implemen panel.setBackground(bg); panel.setForeground(fg); - SimpleTextAttributes attr = sel ? SELECTED : PLAIN; if (value instanceof InspectionToolWrapper) { final InspectionToolWrapper toolWrapper = (InspectionToolWrapper)value; + final String inspectionName = " " + toolWrapper.getDisplayName(); + final String groupName = StringUtil.join(toolWrapper.getGroupPath(), " | "); + final String matchingText = inspectionName + "|" + groupName; + List fragments = ((MinusculeMatcher)myMatcher).matchingFragments(matchingText); + List adjustedFragments = new ArrayList(); + if (fragments != null) { + adjustedFragments.addAll(fragments); + } + final int splitPoint = adjustRanges(adjustedFragments, inspectionName.length() + 1); final SimpleColoredComponent c = new SimpleColoredComponent(); - SpeedSearchUtil.appendColoredFragmentForMatcher(" " + toolWrapper.getDisplayName(), c, attr, myMatcher, bg, sel); + final boolean matchHighlighting = Registry.is("ide.highlight.match.in.selected.only") && !sel; + if (matchHighlighting) { + c.append(inspectionName, myPlain); + } + else { + final List ranges = adjustedFragments.subList(0, splitPoint); + SpeedSearchUtil.appendColoredFragments(c, inspectionName, ranges, sel ? mySelected : myPlain, myHighlighted); + } panel.add(c, BorderLayout.WEST); final SimpleColoredComponent group = new SimpleColoredComponent(); - SpeedSearchUtil.appendColoredFragmentForMatcher(toolWrapper.getGroupDisplayName() + " ", group, attr, myMatcher, bg, sel); + if (matchHighlighting) { + group.append(groupName, SimpleTextAttributes.GRAYED_ATTRIBUTES); + } + else { + final SimpleTextAttributes attributes = sel ? mySelected : SimpleTextAttributes.GRAYED_ATTRIBUTES; + final List ranges = adjustedFragments.subList(splitPoint, adjustedFragments.size()); + SpeedSearchUtil.appendColoredFragments(group, groupName, ranges, attributes, myHighlighted); + } final JPanel right = new JPanel(new BorderLayout()); right.setBackground(bg); right.setForeground(fg); @@ -91,6 +126,21 @@ public class InspectionListCellRenderer extends DefaultListCellRenderer implemen return panel; } + private static int adjustRanges(List ranges, int offset) { + int result = 0; + for (int i = 0; i < ranges.size(); i++) { + final TextRange range = ranges.get(i); + final int startOffset = range.getStartOffset(); + if (startOffset < offset) { + result = i + 1; + } + else { + ranges.set(i, new TextRange(startOffset - offset, range.getEndOffset() - offset)); + } + } + return result; + } + @NotNull private static Icon getIcon(@NotNull InspectionToolWrapper tool) { Icon icon = null; diff --git a/platform/lang-impl/src/com/intellij/codeInspection/actions/ViewOfflineResultsAction.java b/platform/lang-impl/src/com/intellij/codeInspection/actions/ViewOfflineResultsAction.java index 0a7c8b24a275..b1ee98ac92fe 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/actions/ViewOfflineResultsAction.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/actions/ViewOfflineResultsAction.java @@ -77,7 +77,7 @@ public class ViewOfflineResultsAction extends AnAction implements DumbAware { final Presentation presentation = event.getPresentation(); final Project project = event.getData(CommonDataKeys.PROJECT); presentation.setEnabled(project != null); - presentation.setVisible(ActionPlaces.MAIN_MENU.equals(event.getPlace()) && !PlatformUtils.isCidr()); + presentation.setVisible(ActionPlaces.isMainMenuOrActionSearch(event.getPlace()) && !PlatformUtils.isCidr()); } @Override diff --git a/platform/lang-impl/src/com/intellij/diagnostic/logging/DebuggerLogConsoleManager.java b/platform/lang-impl/src/com/intellij/diagnostic/logging/DebuggerLogConsoleManager.java index 6f619641acc0..c24c0e869b97 100644 --- a/platform/lang-impl/src/com/intellij/diagnostic/logging/DebuggerLogConsoleManager.java +++ b/platform/lang-impl/src/com/intellij/diagnostic/logging/DebuggerLogConsoleManager.java @@ -16,6 +16,7 @@ package com.intellij.diagnostic.logging; +@Deprecated /** * User: anna * Date: 01-Feb-2006 diff --git a/platform/lang-impl/src/com/intellij/diagnostic/logging/LogConfigurationPanel.java b/platform/lang-impl/src/com/intellij/diagnostic/logging/LogConfigurationPanel.java index b043b3e294a2..305af75de669 100644 --- a/platform/lang-impl/src/com/intellij/diagnostic/logging/LogConfigurationPanel.java +++ b/platform/lang-impl/src/com/intellij/diagnostic/logging/LogConfigurationPanel.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.intellij.diagnostic.logging; import com.intellij.diagnostic.DiagnosticBundle; @@ -32,10 +31,12 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.ui.*; import com.intellij.ui.components.JBCheckBox; import com.intellij.ui.table.TableView; +import com.intellij.util.SmartList; import com.intellij.util.ui.AbstractTableCellEditor; import com.intellij.util.ui.CellEditorComponentWithBrowseButton; import com.intellij.util.ui.ColumnInfo; import com.intellij.util.ui.ListTableModel; +import gnu.trove.THashMap; import org.jetbrains.annotations.NotNull; import javax.swing.*; @@ -44,7 +45,6 @@ import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import java.util.Map; @@ -61,19 +61,20 @@ public class LogConfigurationPanel extends Setti private TextFieldWithBrowseButton myOutputFile; private JCheckBox myShowConsoleOnStdOutCb; private JCheckBox myShowConsoleOnStdErrCb; - private final Map myLog2Predefined = new HashMap(); - private final List myUnresolvedPredefined = new ArrayList(); - - private final ColumnInfo IS_SHOW = new MyIsActiveColumnInfo(); - private final ColumnInfo FILE = new MyLogFileColumnInfo(); - private final ColumnInfo IS_SKIP_CONTENT = new MyIsSkipColumnInfo(); + private final Map myLog2Predefined = new THashMap(); + private final List myUnresolvedPredefined = new SmartList(); public LogConfigurationPanel() { + ColumnInfo IS_SHOW = new MyIsActiveColumnInfo(); + ColumnInfo FILE = new MyLogFileColumnInfo(); + ColumnInfo IS_SKIP_CONTENT = new MyIsSkipColumnInfo(); + myModel = new ListTableModel(IS_SHOW, FILE, IS_SKIP_CONTENT); myFilesTable = new TableView(myModel); myFilesTable.getEmptyText().setText(DiagnosticBundle.message("log.monitor.no.files")); final JTableHeader tableHeader = myFilesTable.getTableHeader(); + @SuppressWarnings("ConstantConditions") final FontMetrics fontMetrics = tableHeader.getFontMetrics(tableHeader.getFont()); int preferredWidth = fontMetrics.stringWidth(IS_SHOW.getName()) + 20; @@ -109,7 +110,7 @@ public class LogConfigurationPanel extends Setti public void run(AnActionButton button) { TableUtil.stopEditing(myFilesTable); final int[] selected = myFilesTable.getSelectedRows(); - if (selected == null || selected.length == 0) return; + if (selected.length == 0) return; for (int i = selected.length - 1; i >= 0; i--) { myModel.removeRow(selected[i]); } @@ -130,8 +131,8 @@ public class LogConfigurationPanel extends Setti @Override public void run(AnActionButton button) { final int selectedRow = myFilesTable.getSelectedRow(); - final LogFileOptions selectedOptions = myFilesTable.getSelectedObject(); - showEditorDialog(selectedOptions); + //noinspection ConstantConditions + showEditorDialog(myFilesTable.getSelectedObject()); myModel.fireTableDataChanged(); myFilesTable.setRowSelectionInterval(selectedRow, selectedRow); } @@ -156,7 +157,7 @@ public class LogConfigurationPanel extends Setti TextComponentAccessor.TEXT_FIELD_WHOLE_TEXT); myRedirectOutputCb.addActionListener(new ActionListener() { @Override - public void actionPerformed(ActionEvent e) { + public void actionPerformed(@NotNull ActionEvent e) { myOutputFile.setEnabled(myRedirectOutputCb.isSelected()); } }); @@ -218,14 +219,14 @@ public class LogConfigurationPanel extends Setti @Override protected void resetEditorFrom(final RunConfigurationBase configuration) { ArrayList list = new ArrayList(); - final ArrayList logFiles = configuration.getLogFiles(); + final List logFiles = configuration.getLogFiles(); for (LogFileOptions setting : logFiles) { list.add( new LogFileOptions(setting.getName(), setting.getPathPattern(), setting.isEnabled(), setting.isSkipContent(), setting.isShowAll())); } myLog2Predefined.clear(); myUnresolvedPredefined.clear(); - final ArrayList predefinedLogFiles = configuration.getPredefinedLogFiles(); + final List predefinedLogFiles = configuration.getPredefinedLogFiles(); for (PredefinedLogFile predefinedLogFile : predefinedLogFiles) { PredefinedLogFile logFile = new PredefinedLogFile(predefinedLogFile); final LogFileOptions options = configuration.getOptionsForPredefinedLogFile(logFile); @@ -306,8 +307,9 @@ public class LogConfigurationPanel extends Setti @Override public TableCellRenderer getRenderer(final LogFileOptions p0) { return new DefaultTableCellRenderer() { + @NotNull @Override - public Component getTableCellRendererComponent(JTable table, + public Component getTableCellRendererComponent(@NotNull JTable table, Object value, boolean isSelected, boolean hasFocus, @@ -315,6 +317,7 @@ public class LogConfigurationPanel extends Setti int column) { final Component renderer = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); setText(((LogFileOptions)value).getName()); + //noinspection ConstantConditions setBackground(isSelected ? table.getSelectionBackground() : table.getBackground()); setBorder(null); return renderer; @@ -418,7 +421,7 @@ public class LogConfigurationPanel extends Setti getChildComponent().setBorder(null); myComponent.getComponentWithButton().getButton().addActionListener(new ActionListener() { @Override - public void actionPerformed(ActionEvent e) { + public void actionPerformed(@NotNull ActionEvent e) { showEditorDialog(myLogFileOptions); JTextField textField = getChildComponent(); textField.setText(myLogFileOptions.getName()); diff --git a/platform/lang-impl/src/com/intellij/diagnostic/logging/LogConsoleBase.java b/platform/lang-impl/src/com/intellij/diagnostic/logging/LogConsoleBase.java index c6226b65035e..f5882d92bcb4 100644 --- a/platform/lang-impl/src/com/intellij/diagnostic/logging/LogConsoleBase.java +++ b/platform/lang-impl/src/com/intellij/diagnostic/logging/LogConsoleBase.java @@ -249,6 +249,7 @@ public abstract class LogConsoleBase extends AdditionalTabComponent implements L activate(); } + @NotNull @Override public String getTabTitle() { return myTitle; diff --git a/platform/lang-impl/src/com/intellij/diagnostic/logging/LogConsoleImpl.java b/platform/lang-impl/src/com/intellij/diagnostic/logging/LogConsoleImpl.java index 5933634dfcbc..1f8a48d7c605 100644 --- a/platform/lang-impl/src/com/intellij/diagnostic/logging/LogConsoleImpl.java +++ b/platform/lang-impl/src/com/intellij/diagnostic/logging/LogConsoleImpl.java @@ -17,7 +17,7 @@ package com.intellij.diagnostic.logging; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.io.FileUtilRt; import com.intellij.psi.search.GlobalSearchScope; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -44,7 +44,7 @@ public abstract class LogConsoleImpl extends LogConsoleBase { @NotNull File file, @NotNull Charset charset, long skippedContents, - String title, + @NotNull String title, final boolean buildInActions) { this(project, file, charset, skippedContents, title, buildInActions, GlobalSearchScope.allScope(project)); } @@ -53,7 +53,7 @@ public abstract class LogConsoleImpl extends LogConsoleBase { @NotNull File file, @NotNull Charset charset, long skippedContents, - String title, + @NotNull String title, final boolean buildInActions, final GlobalSearchScope searchScope) { super(project, getReader(file, charset, skippedContents), title, buildInActions, new DefaultLogFilterModel(project), @@ -64,27 +64,30 @@ public abstract class LogConsoleImpl extends LogConsoleBase { } @Nullable - private static Reader getReader(@NotNull final File file, @NotNull final Charset charset, final long skippedContents) { - Reader reader = null; + private static Reader getReader(@NotNull File file, @NotNull Charset charset, long skippedContents) { try { try { - final FileInputStream inputStream = new FileInputStream(file); - reader = new BufferedReader(new InputStreamReader(inputStream, charset)); - if (file.length() >= skippedContents) { //do not skip forward - //noinspection ResultOfMethodCallIgnored - inputStream.skip(skippedContents); + @SuppressWarnings("IOResourceOpenedButNotSafelyClosed") + FileInputStream inputStream = new FileInputStream(file); + //do not skip forward + if (file.length() >= skippedContents) { + long skipped = 0; + while (skipped < skippedContents) { + skipped += inputStream.skip(skippedContents - skipped); + } } + return new BufferedReader(new InputStreamReader(inputStream, charset)); } catch (FileNotFoundException ignored) { - if (FileUtil.createIfDoesntExist(file)) { - reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), charset)); + if (FileUtilRt.createIfNotExists(file)) { + return new BufferedReader(new InputStreamReader(new FileInputStream(file), charset)); } + return null; } } catch (Throwable ignored) { - reader = null; + return null; } - return reader; } @Override @@ -104,9 +107,10 @@ public abstract class LogConsoleImpl extends LogConsoleBase { return null; } - final long length = myFile.length(); + long length = myFile.length(); if (length < myOldLength) { reader.close(); + //noinspection IOResourceOpenedButNotSafelyClosed reader = new BufferedReader(new InputStreamReader(new FileInputStream(myFile), myCharset)); } myOldLength = length; diff --git a/platform/lang-impl/src/com/intellij/diagnostic/logging/LogConsoleManager.java b/platform/lang-impl/src/com/intellij/diagnostic/logging/LogConsoleManager.java index 08407236006d..e5807c674b97 100644 --- a/platform/lang-impl/src/com/intellij/diagnostic/logging/LogConsoleManager.java +++ b/platform/lang-impl/src/com/intellij/diagnostic/logging/LogConsoleManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,22 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.intellij.diagnostic.logging; import com.intellij.execution.configurations.AdditionalTabComponentManager; +import com.intellij.execution.configurations.RunConfigurationBase; import org.jetbrains.annotations.NotNull; import java.nio.charset.Charset; -import java.io.Reader; - -/** - * User: anna - * Date: 01-Feb-2006 - */ public interface LogConsoleManager extends AdditionalTabComponentManager { - void addLogConsole(final String name, final String path, @NotNull Charset charset, final long skippedContent); - void addLogConsole(final String name, Reader reader, final String id); - void removeLogConsole(final String pathOrId); + void addLogConsole(@NotNull String name, @NotNull String path, @NotNull Charset charset, long skippedContent, @NotNull RunConfigurationBase runConfiguration); + + void removeLogConsole(@NotNull String pathOrId); } diff --git a/platform/lang-impl/src/com/intellij/diagnostic/logging/LogConsoleManagerBase.java b/platform/lang-impl/src/com/intellij/diagnostic/logging/LogConsoleManagerBase.java index cb034daeaf5a..6e65ede4e04e 100644 --- a/platform/lang-impl/src/com/intellij/diagnostic/logging/LogConsoleManagerBase.java +++ b/platform/lang-impl/src/com/intellij/diagnostic/logging/LogConsoleManagerBase.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,8 +16,8 @@ package com.intellij.diagnostic.logging; import com.intellij.execution.configurations.RunConfigurationBase; +import com.intellij.execution.configurations.RunProfile; import com.intellij.execution.process.ProcessHandler; -import com.intellij.execution.runners.ExecutionEnvironment; import com.intellij.execution.ui.RunnerLayoutUi; import com.intellij.openapi.Disposable; import com.intellij.openapi.project.Project; @@ -28,93 +28,42 @@ import com.intellij.ui.content.Content; import com.intellij.ui.content.ContentManagerAdapter; import com.intellij.ui.content.ContentManagerEvent; import com.intellij.util.ArrayUtil; +import gnu.trove.THashMap; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.io.File; -import java.io.Reader; import java.nio.charset.Charset; -import java.util.HashMap; import java.util.Map; -/** - * Created by IntelliJ IDEA. - * User: michael.golubev - */ public abstract class LogConsoleManagerBase implements LogConsoleManager, Disposable { - private final Project myProject; - - private final Map myAdditionalContent = new HashMap(); - - private ExecutionEnvironment myEnvironment; + private final Map myAdditionalContent = new THashMap(); private final GlobalSearchScope mySearchScope; - /** - * @deprecated use {@link #LogConsoleManagerBase(com.intellij.openapi.project.Project, com.intellij.psi.search.GlobalSearchScope)} - * to remove in IDEA 15 - */ - @SuppressWarnings("UnusedDeclaration") - protected LogConsoleManagerBase(@NotNull Project project) { - this(project, GlobalSearchScope.allScope(project)); - } - protected LogConsoleManagerBase(@NotNull Project project, @NotNull GlobalSearchScope searchScope) { myProject = project; mySearchScope = searchScope; } - protected final Project getProject() { - return myProject; - } - - public void setEnvironment(@NotNull ExecutionEnvironment environment) { - myEnvironment = environment; - } - - protected final ExecutionEnvironment getEnvironment() { - return myEnvironment; - } - @Override - public void addLogConsole(final String name, final String path, @NotNull Charset charset, final long skippedContent) { - addLogConsole(name, path, charset, skippedContent, getDefaultIcon()); + public void addLogConsole(@NotNull String name, @NotNull String path, @NotNull Charset charset, long skippedContent, @NotNull RunConfigurationBase runConfiguration) { + addLogConsole(name, path, charset, skippedContent, getDefaultIcon(), runConfiguration); } - public void addLogConsole(final String name, final String path, @NotNull Charset charset, final long skippedContent, Icon icon) { + public void addLogConsole(final String name, final String path, @NotNull Charset charset, final long skippedContent, Icon icon, @Nullable RunProfile runProfile) { doAddLogConsole(new LogConsoleImpl(myProject, new File(path), charset, skippedContent, name, false, mySearchScope) { - @Override public boolean isActive() { return isConsoleActive(path); } - }, path, icon); - } - - @Override - public void addLogConsole(String name, Reader reader, final String id) { - addLogConsole(name, reader, id, getDefaultIcon()); - } - - public void addLogConsole(String name, Reader reader, final String id, Icon icon) { - doAddLogConsole(new LogConsoleBase(myProject, - reader, - name, - false, - new DefaultLogFilterModel(myProject), mySearchScope) { - - @Override - public boolean isActive() { - return isConsoleActive(id); - } - }, id, icon); + }, path, icon, runProfile); } - private void doAddLogConsole(final LogConsoleBase log, - final String id, - Icon icon) { - if (myEnvironment != null && myEnvironment.getRunProfile() instanceof RunConfigurationBase) { - ((RunConfigurationBase)myEnvironment.getRunProfile()).customizeLogConsole(log); + private void doAddLogConsole(@NotNull final LogConsoleBase log, String id, Icon icon, @Nullable RunProfile runProfile) { + if (runProfile instanceof RunConfigurationBase) { + ((RunConfigurationBase)runProfile).customizeLogConsole(log); } log.attachStopLogConsoleTrackingListener(getProcessHandler()); addAdditionalTabComponent(log, id, icon); @@ -133,33 +82,28 @@ public abstract class LogConsoleManagerBase implements LogConsoleManager, Dispos } @Override - public void removeLogConsole(final String path) { - final Content content = getUi().findContent(path); + public void removeLogConsole(@NotNull String path) { + Content content = getUi().findContent(path); if (content != null) { - final LogConsoleBase log = (LogConsoleBase)content.getComponent(); - removeAdditionalTabComponent(log); + removeAdditionalTabComponent((LogConsoleBase)content.getComponent()); } } @Override - public void addAdditionalTabComponent(final AdditionalTabComponent tabComponent, final String id) { + public void addAdditionalTabComponent(@NotNull AdditionalTabComponent tabComponent, @NotNull String id) { addAdditionalTabComponent(tabComponent, id, getDefaultIcon()); } - public Content addAdditionalTabComponent(final AdditionalTabComponent tabComponent, String id, Icon icon) { - final Content logContent = createLogContent(tabComponent, id, icon); + public Content addAdditionalTabComponent(@NotNull AdditionalTabComponent tabComponent, @NotNull String id, @Nullable Icon icon) { + Content logContent = getUi().createContent(id, (ComponentWithActions)tabComponent, tabComponent.getTabTitle(), icon, + tabComponent.getPreferredFocusableComponent()); myAdditionalContent.put(tabComponent, logContent); getUi().addContent(logContent); return logContent; } - protected Content createLogContent(AdditionalTabComponent tabComponent, String id, Icon icon) { - return getUi().createContent(id, (ComponentWithActions)tabComponent, tabComponent.getTabTitle(), icon, - tabComponent.getPreferredFocusableComponent()); - } - @Override - public void removeAdditionalTabComponent(AdditionalTabComponent component) { + public void removeAdditionalTabComponent(@NotNull AdditionalTabComponent component) { Disposer.dispose(component); final Content content = myAdditionalContent.remove(component); if (!getUi().isDisposed()) { diff --git a/platform/lang-impl/src/com/intellij/diagnostic/logging/LogFilesManager.java b/platform/lang-impl/src/com/intellij/diagnostic/logging/LogFilesManager.java index a581a15ed6b8..0f95b541ce8a 100644 --- a/platform/lang-impl/src/com/intellij/diagnostic/logging/LogFilesManager.java +++ b/platform/lang-impl/src/com/intellij/diagnostic/logging/LogFilesManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,135 +13,64 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.intellij.diagnostic.logging; import com.intellij.execution.configurations.LogFileOptions; import com.intellij.execution.configurations.RunConfigurationBase; import com.intellij.execution.process.ProcessHandler; -import com.intellij.openapi.Disposable; -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Conditions; -import com.intellij.openapi.util.Disposer; -import com.intellij.util.Alarm; -import gnu.trove.THashSet; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; -import javax.swing.*; import java.io.File; -import java.util.*; - -/** - * User: anna - * Date: 01-Feb-2006 - */ -public class LogFilesManager implements Disposable { - public static final Logger LOG = Logger.getInstance(LogFilesManager.class); +import java.util.Set; +import java.util.TreeMap; - private static final int UPDATE_INTERVAL = 500; - - private final Map> myLogFileManagerMap = new LinkedHashMap>(); - private final Runnable myUpdateRequest; +public class LogFilesManager { private final LogConsoleManager myManager; - private final Alarm myUpdateAlarm = new Alarm(Alarm.ThreadToUse.POOLED_THREAD, this); - private boolean myDisposed; - public LogFilesManager(@NotNull final Project project, LogConsoleManager manager, Disposable parentDisposable) { + public LogFilesManager(@NotNull LogConsoleManager manager) { myManager = manager; - Disposer.register(parentDisposable, this); - - myUpdateRequest = new Runnable() { - @Override - public void run() { - if (project.isDisposed() || myDisposed) return; - myUpdateAlarm.cancelAllRequests(); - for (final LogFileOptions logFile : myLogFileManagerMap.keySet()) { - final Set oldFiles = myLogFileManagerMap.get(logFile); - final Set newFiles = logFile.getPaths(); // should not be called in UI thread - myLogFileManagerMap.put(logFile, newFiles); - - final Set obsoleteFiles = new THashSet(oldFiles); - obsoleteFiles.removeAll(newFiles); - - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - if (project.isDisposed() || myDisposed) return; - - addConfigurationConsoles(logFile, new Condition() { - @Override - public boolean value(final String file) { - return !oldFiles.contains(file); - } - }, newFiles); - for (String each : obsoleteFiles) { - myManager.removeLogConsole(each); - } - myUpdateAlarm.addRequest(myUpdateRequest, UPDATE_INTERVAL); - } - }); - } - } - }; } - public void registerFileMatcher(@NotNull RunConfigurationBase runConfiguration) { - final ArrayList logFiles = runConfiguration.getAllLogFiles(); - for (LogFileOptions logFile : logFiles) { - if (logFile.isEnabled()) { - myLogFileManagerMap.put(logFile, logFile.getPaths()); + public void addLogConsoles(@NotNull RunConfigurationBase runConfiguration, @Nullable ProcessHandler startedProcess) { + for (LogFileOptions logFileOptions : runConfiguration.getAllLogFiles()) { + if (logFileOptions.isEnabled()) { + addConfigurationConsoles(logFileOptions, Conditions.alwaysTrue(), logFileOptions.getPaths(), runConfiguration); } } - Alarm updateAlarm = myUpdateAlarm; - if (updateAlarm != null) { - updateAlarm.addRequest(myUpdateRequest, UPDATE_INTERVAL); - } + runConfiguration.createAdditionalTabComponents(myManager, startedProcess); } - @Override - public void dispose() { - myDisposed = true; - if (myUpdateAlarm != null) { - myUpdateAlarm.cancelAllRequests(); + private void addConfigurationConsoles(@NotNull LogFileOptions logFile, @NotNull Condition shouldInclude, @NotNull Set paths, @NotNull RunConfigurationBase runConfiguration) { + if (paths.isEmpty()) { + return; } - } - public void initLogConsoles(@NotNull RunConfigurationBase base, ProcessHandler startedProcess) { - List logFiles = base.getAllLogFiles(); - for (LogFileOptions logFile : logFiles) { - if (logFile.isEnabled()) { - addConfigurationConsoles(logFile, Conditions.alwaysTrue(), logFile.getPaths()); + TreeMap titleToPath = new TreeMap(); + if (paths.size() == 1) { + String path = paths.iterator().next(); + if (shouldInclude.value(path)) { + titleToPath.put(logFile.getName(), path); } } - base.createAdditionalTabComponents(myManager, startedProcess); - } - - private void addConfigurationConsoles(final LogFileOptions logFile, Condition shouldInclude, final Set paths) { - if (!paths.isEmpty()) { - final TreeMap title2Path = new TreeMap(); - if (paths.size() == 1) { - final String path = paths.iterator().next(); + else { + for (String path : paths) { if (shouldInclude.value(path)) { - title2Path.put(logFile.getName(), path); - } - } - else { - for (String path : paths) { - if (shouldInclude.value(path)) { - String title = new File(path).getName(); - if (title2Path.containsKey(title)) { - title = path; - } - title2Path.put(title, path); + String title = new File(path).getName(); + if (titleToPath.containsKey(title)) { + title = path; } + titleToPath.put(title, path); } } - for (final String title : title2Path.keySet()) { - final String path = title2Path.get(title); - myManager.addLogConsole(title, path, logFile.getCharset(), logFile.isSkipContent() ? new File(path).length() : 0); - } + } + + for (String title : titleToPath.keySet()) { + String path = titleToPath.get(title); + assert path != null; + myManager.addLogConsole(title, path, logFile.getCharset(), logFile.isSkipContent() ? new File(path).length() : 0, runConfiguration); } } } diff --git a/platform/lang-impl/src/com/intellij/execution/actions/RunConfigurationsComboBoxAction.java b/platform/lang-impl/src/com/intellij/execution/actions/RunConfigurationsComboBoxAction.java index 2a124dfcca4a..e0895646e2e3 100644 --- a/platform/lang-impl/src/com/intellij/execution/actions/RunConfigurationsComboBoxAction.java +++ b/platform/lang-impl/src/com/intellij/execution/actions/RunConfigurationsComboBoxAction.java @@ -65,7 +65,7 @@ public class RunConfigurationsComboBoxAction extends ComboBoxAction implements D public void update(final AnActionEvent e) { final Presentation presentation = e.getPresentation(); final Project project = e.getData(CommonDataKeys.PROJECT); - if (ActionPlaces.MAIN_MENU.equals(e.getPlace())) { + if (ActionPlaces.isMainMenuOrActionSearch(e.getPlace())) { presentation.setDescription(ExecutionBundle.message("choose.run.configuration.action.description")); presentation.setEnabled(findFrame(e.getData(PlatformDataKeys.CONTEXT_COMPONENT)) != null); return; diff --git a/platform/lang-impl/src/com/intellij/execution/actions/StopAction.java b/platform/lang-impl/src/com/intellij/execution/actions/StopAction.java index 75466528ae2f..596b3900ba5b 100644 --- a/platform/lang-impl/src/com/intellij/execution/actions/StopAction.java +++ b/platform/lang-impl/src/com/intellij/execution/actions/StopAction.java @@ -56,7 +56,7 @@ class StopAction extends DumbAwareAction implements AnAction.TransparentUpdate { Icon icon = getTemplatePresentation().getIcon(); String description = getTemplatePresentation().getDescription(); Presentation presentation = e.getPresentation(); - if (ActionPlaces.MAIN_MENU.equals(e.getPlace())) { + if (ActionPlaces.isMainMenuOrActionSearch(e.getPlace())) { enable = !getCancellableProcesses(e.getProject()).isEmpty() || !getActiveDescriptors(e.getDataContext()).isEmpty(); presentation.setText(getTemplatePresentation().getText()); } @@ -79,7 +79,8 @@ class StopAction extends DumbAwareAction implements AnAction.TransparentUpdate { presentation.setText(getTemplatePresentation().getText()); } else { - presentation.setText(ExecutionBundle.message("stop.configuration.action.name", runProfile == null ? contentDescriptor.getDisplayName() : runProfile.getName())); + presentation.setText(ExecutionBundle.message("stop.configuration.action.name", + runProfile == null ? contentDescriptor.getDisplayName() : runProfile.getName())); } } @@ -95,7 +96,7 @@ class StopAction extends DumbAwareAction implements AnAction.TransparentUpdate { Project project = e.getProject(); List> backgroundTasks = getCancellableProcesses(project); - if (ActionPlaces.MAIN_MENU.equals(e.getPlace())) { + if (ActionPlaces.isMainMenuOrActionSearch(e.getPlace())) { if (activeProcessHandler != null && !activeProcessHandler.isProcessTerminating() && !activeProcessHandler.isProcessTerminated() && backgroundTasks.isEmpty()) { stopProcess(activeProcessHandler); diff --git a/platform/lang-impl/src/com/intellij/execution/console/ConsoleExecuteAction.java b/platform/lang-impl/src/com/intellij/execution/console/ConsoleExecuteAction.java index fcec06644765..f5bec9ac6be4 100644 --- a/platform/lang-impl/src/com/intellij/execution/console/ConsoleExecuteAction.java +++ b/platform/lang-impl/src/com/intellij/execution/console/ConsoleExecuteAction.java @@ -18,6 +18,7 @@ package com.intellij.execution.console; import com.intellij.codeInsight.lookup.Lookup; import com.intellij.codeInsight.lookup.LookupManager; import com.intellij.execution.process.ConsoleHistoryModel; +import com.intellij.execution.ui.ConsoleViewContentType; import com.intellij.icons.AllIcons; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.EmptyAction; @@ -39,7 +40,7 @@ public class ConsoleExecuteAction extends DumbAwareAction { private final LanguageConsoleView myConsoleView; private final LanguageConsoleImpl myConsole; - private final ConsoleExecuteActionHandler myExecuteActionHandler; + final ConsoleExecuteActionHandler myExecuteActionHandler; private final Condition myEnabledCondition; @SuppressWarnings("UnusedDeclaration") @@ -95,7 +96,7 @@ public class ConsoleExecuteAction extends DumbAwareAction { } @Override - public final void update(AnActionEvent e) { + public final void update(@NotNull AnActionEvent e) { EditorEx editor = myConsole.getConsoleEditor(); boolean enabled = !editor.isRendererMode() && isEnabled() && (myExecuteActionHandler.isEmptyCommandExecutionAllowed() || !StringUtil.isEmptyOrSpaces(editor.getDocument().getCharsSequence())); @@ -109,7 +110,7 @@ public class ConsoleExecuteAction extends DumbAwareAction { } @Override - public final void actionPerformed(AnActionEvent e) { + public final void actionPerformed(@NotNull AnActionEvent e) { myExecuteActionHandler.runExecuteAction(myConsole, myConsoleView); } @@ -139,6 +140,8 @@ public class ConsoleExecuteAction extends DumbAwareAction { private boolean myAddToHistory = true; final boolean myPreserveMarkup; + boolean useProcessStdIn; + public ConsoleExecuteActionHandler(boolean preserveMarkup) { myCommandHistoryModel = new ConsoleHistoryModel(); myPreserveMarkup = preserveMarkup; @@ -156,19 +159,25 @@ public class ConsoleExecuteAction extends DumbAwareAction { myAddToHistory = addCurrentToHistory; } - /** - * @deprecated - */ protected void beforeExecution(@NotNull LanguageConsoleImpl console) { } protected void runExecuteAction(@NotNull LanguageConsoleImpl console, @Nullable LanguageConsoleView consoleView) { - //noinspection deprecation - beforeExecution(console); + if (!useProcessStdIn) { + beforeExecution(console); + } - String text = console.prepareExecuteAction(myAddToHistory, myPreserveMarkup, true); + String text = console.prepareExecuteAction(myAddToHistory && !useProcessStdIn, myPreserveMarkup, true); ((UndoManagerImpl)UndoManager.getInstance(console.getProject())).invalidateActionsFor(DocumentReferenceManager.getInstance().create(console.getCurrentEditor().getDocument())); - addToCommandHistoryAndExecute(console, consoleView, text); + + if (useProcessStdIn) { + assert consoleView != null; + consoleView.print(text, ConsoleViewContentType.USER_INPUT); + consoleView.print("\n", ConsoleViewContentType.USER_INPUT); + } + else { + addToCommandHistoryAndExecute(console, consoleView, text); + } } private void addToCommandHistoryAndExecute(@NotNull LanguageConsoleImpl console, @Nullable LanguageConsoleView consoleView, @NotNull String text) { diff --git a/platform/lang-impl/src/com/intellij/execution/console/LanguageConsoleBuilder.java b/platform/lang-impl/src/com/intellij/execution/console/LanguageConsoleBuilder.java index 329b9f0e3f13..a20b8c706ff2 100644 --- a/platform/lang-impl/src/com/intellij/execution/console/LanguageConsoleBuilder.java +++ b/platform/lang-impl/src/com/intellij/execution/console/LanguageConsoleBuilder.java @@ -1,6 +1,8 @@ package com.intellij.execution.console; +import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer; import com.intellij.execution.process.ProcessHandler; +import com.intellij.ide.util.PropertiesComponent; import com.intellij.lang.Language; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.editor.Document; @@ -60,6 +62,8 @@ public final class LanguageConsoleBuilder { private boolean oneLineInput; + private String processInputStateKey; + // todo to be removed public LanguageConsoleBuilder(@SuppressWarnings("NullableProblems") @NotNull LanguageConsoleView consoleView) { this.consoleView = consoleView; @@ -87,6 +91,7 @@ public final class LanguageConsoleBuilder { return this; } + @NotNull public LanguageConsoleBuilder initActions(@NotNull BaseConsoleExecuteActionHandler executeActionHandler, @NotNull String historyType) { if (consoleView == null) { this.executeActionHandler = executeActionHandler; @@ -150,6 +155,13 @@ public final class LanguageConsoleBuilder { return this; } + @NotNull + public LanguageConsoleBuilder processInputStateKey(@Nullable String value) { + processInputStateKey = value; + return this; + } + + @NotNull public LanguageConsoleView build(@NotNull Project project, @NotNull Language language) { GutteredLanguageConsole console = new GutteredLanguageConsole(language.getDisplayName() + " Console", project, language, gutterContentProvider, psiFileFactory); if (oneLineInput) { @@ -160,6 +172,17 @@ public final class LanguageConsoleBuilder { assert historyType != null; doInitAction(consoleView, executeActionHandler, historyType); } + + if (processInputStateKey != null) { + assert executeActionHandler != null; + if (PropertiesComponent.getInstance().getBoolean(processInputStateKey, false)) { + executeActionHandler.useProcessStdIn = true; + DaemonCodeAnalyzer daemonCodeAnalyzer = DaemonCodeAnalyzer.getInstance(consoleView.getProject()); + daemonCodeAnalyzer.setHighlightingEnabled(consoleView.getConsole().getFile(), false); + } + consoleView.addCustomConsoleAction(new UseConsoleInputAction(processInputStateKey)); + } + console.initComponents(); return consoleView; } diff --git a/platform/lang-impl/src/com/intellij/execution/console/LanguageConsoleImpl.java b/platform/lang-impl/src/com/intellij/execution/console/LanguageConsoleImpl.java index d7f40f35a219..711184d9b705 100644 --- a/platform/lang-impl/src/com/intellij/execution/console/LanguageConsoleImpl.java +++ b/platform/lang-impl/src/com/intellij/execution/console/LanguageConsoleImpl.java @@ -659,6 +659,7 @@ public class LanguageConsoleImpl implements Disposable, TypeSafeDataProvider { if (myCurrentEditor != null && myCurrentEditor.isDisposed()) { myCurrentEditor = null; } + if (!isValid()) return; ApplicationManager.getApplication().runReadAction(myUiUpdateRunnable); } } @@ -680,6 +681,10 @@ public class LanguageConsoleImpl implements Disposable, TypeSafeDataProvider { return ObjectUtils.assertNotNull(myVirtualFile.getLanguage()); } + public boolean isValid() { + return myVirtualFile.isValid() && myProject.isOpen(); + } + public void setLanguage(@NotNull Language language) { myVirtualFile.setLanguage(language); myVirtualFile.setContent(myEditorDocument, myEditorDocument.getText(), false); diff --git a/platform/lang-impl/src/com/intellij/execution/console/UseConsoleInputAction.java b/platform/lang-impl/src/com/intellij/execution/console/UseConsoleInputAction.java new file mode 100644 index 000000000000..5b450e3bd154 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/execution/console/UseConsoleInputAction.java @@ -0,0 +1,56 @@ +package com.intellij.execution.console; + +import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer; +import com.intellij.icons.AllIcons; +import com.intellij.ide.util.PropertiesComponent; +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.LangDataKeys; +import com.intellij.openapi.actionSystem.ToggleAction; +import com.intellij.openapi.actionSystem.ex.ActionUtil; +import com.intellij.openapi.project.DumbAware; +import com.intellij.psi.PsiFile; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +final class UseConsoleInputAction extends ToggleAction implements DumbAware { + private final String processInputStateKey; + private boolean useProcessStdIn; + + public UseConsoleInputAction(@NotNull String processInputStateKey) { + super("Use Console Input", null, AllIcons.Debugger.CommandLine); + + this.processInputStateKey = processInputStateKey; + useProcessStdIn = PropertiesComponent.getInstance().getBoolean(processInputStateKey, false); + } + + @Override + public boolean isSelected(@Nullable AnActionEvent event) { + return !useProcessStdIn; + } + + @Override + public void setSelected(AnActionEvent event, boolean state) { + useProcessStdIn = !state; + + LanguageConsoleView consoleView = (LanguageConsoleView)event.getData(LangDataKeys.CONSOLE_VIEW); + assert consoleView != null; + DaemonCodeAnalyzer daemonCodeAnalyzer = DaemonCodeAnalyzer.getInstance(consoleView.getProject()); + PsiFile file = consoleView.getConsole().getFile(); + daemonCodeAnalyzer.setHighlightingEnabled(file, state); + daemonCodeAnalyzer.restart(file); + if (state) { + PropertiesComponent.getInstance().unsetValue(processInputStateKey); + } + else { + PropertiesComponent.getInstance().setValue(processInputStateKey, "true"); + } + + List actions = ActionUtil.getActions(consoleView.getConsole().getConsoleEditor().getComponent()); + ConsoleExecuteAction action = ContainerUtil.findInstance(actions, ConsoleExecuteAction.class); + action.myExecuteActionHandler.useProcessStdIn = !state; + } +} \ No newline at end of file diff --git a/platform/lang-impl/src/com/intellij/execution/impl/BeforeRunStepsPanel.java b/platform/lang-impl/src/com/intellij/execution/impl/BeforeRunStepsPanel.java index de5405b87a89..191c0f79b25c 100644 --- a/platform/lang-impl/src/com/intellij/execution/impl/BeforeRunStepsPanel.java +++ b/platform/lang-impl/src/com/intellij/execution/impl/BeforeRunStepsPanel.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,7 +28,7 @@ import com.intellij.openapi.actionSystem.impl.SimpleDataContext; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.ui.popup.JBPopupFactory; import com.intellij.openapi.ui.popup.ListPopup; -import com.intellij.openapi.util.Condition; +import com.intellij.openapi.util.Conditions; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.SystemInfo; @@ -300,7 +300,7 @@ class BeforeRunStepsPanel extends JPanel { final ListPopup popup = popupFactory.createActionGroupPopup(ExecutionBundle.message("add.new.run.configuration.acrtion.name"), actionGroup, SimpleDataContext.getProjectContext(myRunConfiguration.getProject()), false, false, false, null, - -1, Condition.TRUE); + -1, Conditions.alwaysTrue()); popup.show(button.getPreferredPopupPoint()); } diff --git a/platform/lang-impl/src/com/intellij/execution/impl/ConsoleViewImpl.java b/platform/lang-impl/src/com/intellij/execution/impl/ConsoleViewImpl.java index 3fce510e725e..ae5cfe829f6c 100644 --- a/platform/lang-impl/src/com/intellij/execution/impl/ConsoleViewImpl.java +++ b/platform/lang-impl/src/com/intellij/execution/impl/ConsoleViewImpl.java @@ -290,9 +290,16 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo myFilters = new CompositeFilter(project); if (usePredefinedMessageFilter) { for (ConsoleFilterProvider eachProvider : Extensions.getExtensions(ConsoleFilterProvider.FILTER_PROVIDERS)) { - Filter[] filters = eachProvider instanceof ConsoleFilterProviderEx - ? ((ConsoleFilterProviderEx)eachProvider).getDefaultFilters(project, searchScope) - : eachProvider.getDefaultFilters(project); + Filter[] filters; + if (eachProvider instanceof ConsoleDependentFilterProvider) { + filters = ((ConsoleDependentFilterProvider)eachProvider).getDefaultFilters(this, project, searchScope); + } + else if (eachProvider instanceof ConsoleFilterProviderEx) { + filters = ((ConsoleFilterProviderEx)eachProvider).getDefaultFilters(project, searchScope); + } + else { + filters = eachProvider.getDefaultFilters(project); + } for (Filter filter : filters) { myFilters.addFilter(filter); } diff --git a/platform/lang-impl/src/com/intellij/execution/impl/RunConfigurable.java b/platform/lang-impl/src/com/intellij/execution/impl/RunConfigurable.java index 95d4db09c297..1a006c294275 100644 --- a/platform/lang-impl/src/com/intellij/execution/impl/RunConfigurable.java +++ b/platform/lang-impl/src/com/intellij/execution/impl/RunConfigurable.java @@ -588,15 +588,9 @@ class RunConfigurable extends BaseConfigurable { @Override public JComponent createComponent() { - for (RunConfigurationsSettings each : Extensions.getExtensions(RunConfigurationsSettings.EXTENSION_POINT)) { - try { - UnnamedConfigurable configurable = each.createConfigurable(myProject); - myAdditionalSettings.add(Pair.create(configurable, configurable.createComponent())); - } - catch (NoSuchMethodError e) { - // in case someone has already implemented old RunConfigurationsSettings.createConfigurable() - LOG.error(e); - } + for (RunConfigurationsSettings each : Extensions.getExtensions(RunConfigurationsSettings.EXTENSION_POINT, myProject)) { + UnnamedConfigurable configurable = each.createConfigurable(); + myAdditionalSettings.add(Pair.create(configurable, configurable.createComponent())); } myWholePanel = new JPanel(new BorderLayout()); diff --git a/platform/lang-impl/src/com/intellij/execution/runners/RunContentBuilder.java b/platform/lang-impl/src/com/intellij/execution/runners/RunContentBuilder.java index f16f8dadfb45..14877309db74 100644 --- a/platform/lang-impl/src/com/intellij/execution/runners/RunContentBuilder.java +++ b/platform/lang-impl/src/com/intellij/execution/runners/RunContentBuilder.java @@ -23,7 +23,6 @@ import com.intellij.execution.configurations.RunConfigurationBase; import com.intellij.execution.configurations.RunProfile; import com.intellij.execution.configurations.SearchScopeProvider; import com.intellij.execution.impl.ConsoleViewImpl; -import com.intellij.execution.process.ProcessHandler; import com.intellij.execution.ui.*; import com.intellij.execution.ui.actions.CloseAction; import com.intellij.execution.ui.layout.PlaceInGrid; @@ -41,7 +40,6 @@ import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import javax.swing.*; import java.util.Collection; import java.util.List; @@ -113,14 +111,13 @@ public class RunContentBuilder extends RunTab { @NotNull private RunContentDescriptor createDescriptor() { - final RunProfile profile = getEnvironment().getRunProfile(); + final RunProfile profile = myEnvironment.getRunProfile(); if (ApplicationManager.getApplication().isUnitTestMode()) { - RunContentDescriptor contentDescriptor = new RunContentDescriptor(profile, myExecutionResult, myUi); - Disposer.register(contentDescriptor, this); - return contentDescriptor; + return new RunContentDescriptor(profile, myExecutionResult, myUi); } final ExecutionConsole console = myExecutionResult.getExecutionConsole(); + RunContentDescriptor contentDescriptor = new RunContentDescriptor(profile, myExecutionResult, myUi); if (console != null) { if (console instanceof ExecutionConsoleEx) { ((ExecutionConsoleEx)console).buildUi(myUi); @@ -128,17 +125,15 @@ public class RunContentBuilder extends RunTab { else { buildConsoleUiDefault(myUi, console); } - initLogConsoles(profile, myExecutionResult.getProcessHandler(), console); + initLogConsoles(profile, contentDescriptor, console); } - RunContentDescriptor contentDescriptor = new RunContentDescriptor(profile, myExecutionResult, myUi); - Disposer.register(contentDescriptor, this); myUi.getOptions().setLeftToolbar(createActionToolbar(contentDescriptor), ActionPlaces.UNKNOWN); if (profile instanceof RunConfigurationBase) { if (console instanceof ObservableConsoleView && !ApplicationManager.getApplication().isUnitTestMode()) { ((ObservableConsoleView)console).addChangeListener(new ConsoleToFrontListener((RunConfigurationBase)profile, - getProject(), - getEnvironment().getExecutor(), + myProject, + myEnvironment.getExecutor(), contentDescriptor, myUi), this); @@ -182,19 +177,16 @@ public class RunContentBuilder extends RunTab { } @NotNull - private ActionGroup createActionToolbar(@NotNull RunContentDescriptor contentDescriptor) { + private ActionGroup createActionToolbar(@NotNull final RunContentDescriptor contentDescriptor) { final DefaultActionGroup actionGroup = new DefaultActionGroup(); actionGroup.add(ActionManager.getInstance().getAction(IdeActions.ACTION_RERUN)); - if (myExecutionResult instanceof DefaultExecutionResult) { - final AnAction[] actions = ((DefaultExecutionResult)myExecutionResult).getRestartActions(); - if (actions != null) { - actionGroup.addAll(actions); - if (actions.length > 0) { - actionGroup.addSeparator(); - } - } + final AnAction[] actions = contentDescriptor.getRestartActions(); + actionGroup.addAll(actions); + if (actions.length > 0) { + actionGroup.addSeparator(); } + actionGroup.add(ActionManager.getInstance().getAction(IdeActions.ACTION_STOP_PROGRAM)); if (myExecutionResult instanceof DefaultExecutionResult) { actionGroup.addAll(((DefaultExecutionResult)myExecutionResult).getAdditionalStopActions()); @@ -215,37 +207,24 @@ public class RunContentBuilder extends RunTab { actionGroup.add(myUi.getOptions().getLayoutActions()); actionGroup.addSeparator(); actionGroup.add(PinToolwindowTabAction.getPinAction()); - actionGroup.add(new CloseAction(getEnvironment().getExecutor(), contentDescriptor, getProject())); + actionGroup.add(new CloseAction(myEnvironment.getExecutor(), contentDescriptor, myProject)); final String helpId = contentDescriptor.getHelpId(); - actionGroup.add(new ContextHelpAction(helpId != null ? helpId : getEnvironment().getExecutor().getHelpId())); + actionGroup.add(new ContextHelpAction(helpId != null ? helpId : myEnvironment.getExecutor().getHelpId())); return actionGroup; } - @Override - public ProcessHandler getProcessHandler() { - return myExecutionResult.getProcessHandler(); - } - /** * @param reuseContent see {@link RunContentDescriptor#myContent} */ public RunContentDescriptor showRunContent(@Nullable RunContentDescriptor reuseContent) { RunContentDescriptor descriptor = createDescriptor(); + Disposer.register(descriptor, this); + Disposer.register(myProject, descriptor); RunContentManagerImpl.copyContentAndBehavior(descriptor, reuseContent); myRunContentDescriptor = descriptor; return descriptor; } - @Override - protected RunnerLayoutUi getUi() { - return myUi; - } - - @Override - protected Icon getDefaultIcon() { - return AllIcons.Debugger.Console; - } - public static class ConsoleToFrontListener implements ConsoleViewImpl.ChangeListener { @NotNull private final RunConfigurationBase myRunConfigurationBase; @NotNull private final Project myProject; diff --git a/platform/lang-impl/src/com/intellij/execution/runners/RunTab.java b/platform/lang-impl/src/com/intellij/execution/runners/RunTab.java index 2edc2098ce15..6628c78f1851 100644 --- a/platform/lang-impl/src/com/intellij/execution/runners/RunTab.java +++ b/platform/lang-impl/src/com/intellij/execution/runners/RunTab.java @@ -25,6 +25,8 @@ import com.intellij.execution.process.ProcessHandler; import com.intellij.execution.ui.ExecutionConsole; import com.intellij.execution.ui.RunContentDescriptor; import com.intellij.execution.ui.RunnerLayoutUi; +import com.intellij.icons.AllIcons; +import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.DataProvider; import com.intellij.openapi.actionSystem.LangDataKeys; import com.intellij.openapi.project.Project; @@ -33,12 +35,20 @@ import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -public abstract class RunTab extends LogConsoleManagerBase implements DataProvider { +import javax.swing.*; + +public abstract class RunTab implements DataProvider, Disposable { @NotNull protected final RunnerLayoutUi myUi; - protected final LogFilesManager myManager; + private LogFilesManager myManager; protected RunContentDescriptor myRunContentDescriptor; + protected ExecutionEnvironment myEnvironment; + protected final Project myProject; + private final GlobalSearchScope mySearchScope; + + private LogConsoleManagerBase logConsoleManager; + protected RunTab(@NotNull ExecutionEnvironment environment, @NotNull String runnerType) { this(environment.getProject(), SearchScopeProvider.createSearchScope(environment.getProject(), environment.getRunProfile()), @@ -46,15 +56,21 @@ public abstract class RunTab extends LogConsoleManagerBase implements DataProvid environment.getExecutor().getId(), environment.getRunProfile().getName()); - setEnvironment(environment); + myEnvironment = environment; } - protected RunTab(@NotNull Project project, @NotNull GlobalSearchScope searchScope, @NotNull String runnerType, @NotNull String runnerTitle, @NotNull String sessionName) { - super(project, searchScope); + @Override + public void dispose() { + myRunContentDescriptor = null; + myEnvironment = null; + logConsoleManager = null; + } - myManager = new LogFilesManager(project, this, this); + protected RunTab(@NotNull Project project, @NotNull GlobalSearchScope searchScope, @NotNull String runnerType, @NotNull String runnerTitle, @NotNull String sessionName) { + myProject = project; + mySearchScope = searchScope; - myUi = RunnerLayoutUi.Factory.getInstance(getProject()).create(runnerType, runnerTitle, sessionName, this); + myUi = RunnerLayoutUi.Factory.getInstance(project).create(runnerType, runnerTitle, sessionName, this); myUi.getContentManager().addDataProvider(this); } @@ -62,11 +78,10 @@ public abstract class RunTab extends LogConsoleManagerBase implements DataProvid @Override public Object getData(@NonNls String dataId) { if (LangDataKeys.RUN_PROFILE.is(dataId)) { - ExecutionEnvironment environment = getEnvironment(); - return environment == null ? null : environment.getRunProfile(); + return myEnvironment == null ? null : myEnvironment.getRunProfile(); } else if (LangDataKeys.EXECUTION_ENVIRONMENT.is(dataId)) { - return getEnvironment(); + return myEnvironment; } else if (LangDataKeys.RUN_CONTENT_DESCRIPTOR.is(dataId)) { return myRunContentDescriptor; @@ -74,21 +89,40 @@ public abstract class RunTab extends LogConsoleManagerBase implements DataProvid return null; } - @Override - public final void setEnvironment(@NotNull ExecutionEnvironment environment) { - super.setEnvironment(environment); + @NotNull + public LogConsoleManagerBase getLogConsoleManager() { + if (logConsoleManager == null) { + logConsoleManager = new LogConsoleManagerBase(myProject, mySearchScope) { + @Override + protected Icon getDefaultIcon() { + return AllIcons.Debugger.Console; + } + + @Override + protected RunnerLayoutUi getUi() { + return myUi; + } - RunProfile profile = environment.getRunProfile(); - if (profile instanceof RunConfigurationBase) { - myManager.registerFileMatcher((RunConfigurationBase)profile); + @Override + public ProcessHandler getProcessHandler() { + return myRunContentDescriptor == null ? null : myRunContentDescriptor.getProcessHandler(); + } + }; } + return logConsoleManager; } - protected final void initLogConsoles(@NotNull RunProfile runConfiguration, @Nullable ProcessHandler processHandler, @Nullable ExecutionConsole console) { - if (runConfiguration instanceof RunConfigurationBase && processHandler != null) { + protected final void initLogConsoles(@NotNull RunProfile runConfiguration, @NotNull RunContentDescriptor contentDescriptor, @Nullable ExecutionConsole console) { + ProcessHandler processHandler = contentDescriptor.getProcessHandler(); + if (runConfiguration instanceof RunConfigurationBase) { RunConfigurationBase configuration = (RunConfigurationBase)runConfiguration; - myManager.initLogConsoles(configuration, processHandler); - OutputFileUtil.attachDumpListener(configuration, processHandler, console); + if (myManager == null) { + myManager = new LogFilesManager(getLogConsoleManager()); + } + myManager.addLogConsoles(configuration, processHandler); + if (processHandler != null) { + OutputFileUtil.attachDumpListener(configuration, processHandler, console); + } } } } diff --git a/platform/lang-impl/src/com/intellij/execution/ui/CommonProgramParametersPanel.java b/platform/lang-impl/src/com/intellij/execution/ui/CommonProgramParametersPanel.java index 0f31d6223027..79284abf8356 100644 --- a/platform/lang-impl/src/com/intellij/execution/ui/CommonProgramParametersPanel.java +++ b/platform/lang-impl/src/com/intellij/execution/ui/CommonProgramParametersPanel.java @@ -56,6 +56,7 @@ public class CommonProgramParametersPanel extends JPanel implements PanelWithAnc protected JComponent myAnchor; private Module myModuleContext = null; + private boolean myHasModuleMacro = false; public CommonProgramParametersPanel() { this(true); @@ -120,7 +121,7 @@ public class CommonProgramParametersPanel extends JPanel implements PanelWithAnc @Override public void actionPerformed(ActionEvent e) { List macros = new SmartList(PathMacros.getInstance().getUserMacroNames()); - if (myModuleContext != null) { + if (myModuleContext != null || myHasModuleMacro) { macros.add(PathMacroUtil.MODULE_DIR_MACRO_NAME); } @@ -172,6 +173,10 @@ public class CommonProgramParametersPanel extends JPanel implements PanelWithAnc myModuleContext = moduleContext; } + public void setHasModuleMacro() { + myHasModuleMacro = true; + } + public LabeledComponent getProgramParametersComponent() { return myProgramParametersComponent; } diff --git a/platform/lang-impl/src/com/intellij/find/EditorSearchComponent.java b/platform/lang-impl/src/com/intellij/find/EditorSearchComponent.java index 9613ac0dd33f..4d7eee35a462 100644 --- a/platform/lang-impl/src/com/intellij/find/EditorSearchComponent.java +++ b/platform/lang-impl/src/com/intellij/find/EditorSearchComponent.java @@ -425,6 +425,10 @@ public class EditorSearchComponent extends EditorHeaderComponent implements Data myToolbarComponent.setOpaque(false); } + public void selectAllText() { + mySearchField.selectAll(); + } + private boolean secondaryActionsAvailable() { return FindManagerImpl.ourHasSearchInCommentsAndLiterals; } @@ -483,10 +487,11 @@ public class EditorSearchComponent extends EditorHeaderComponent implements Data } private void updateUIWithFindModel() { - + boolean needToResetFocus = false; myActionsToolbar.updateActionsImmediately(); if ((myFindModel.isMultiline() && mySearchField instanceof JTextField) || (!myFindModel.isMultiline() && mySearchField instanceof JTextArea)) { + needToResetFocus = mySearchField.hasFocus(); myLeftComponent.removeAll(); myRightComponent.removeAll(); myReplaceRootComponent = null; @@ -529,6 +534,7 @@ public class EditorSearchComponent extends EditorHeaderComponent implements Data updateReplaceButton(); Utils.setSmallerFontForChildren(myToolbarComponent); revalidate(); + if (needToResetFocus) mySearchField.requestFocusInWindow(); } private static boolean wholeWordsApplicable(String stringToFind) { diff --git a/platform/lang-impl/src/com/intellij/find/editorHeaderActions/SwitchToFind.java b/platform/lang-impl/src/com/intellij/find/editorHeaderActions/SwitchToFind.java index 440a76c87608..7e4b555d264c 100644 --- a/platform/lang-impl/src/com/intellij/find/editorHeaderActions/SwitchToFind.java +++ b/platform/lang-impl/src/com/intellij/find/editorHeaderActions/SwitchToFind.java @@ -36,8 +36,10 @@ public class SwitchToFind extends EditorHeaderAction implements DumbAware { action.actionPerformed(e); return; } - - final FindModel findModel = getEditorSearchComponent().getFindModel(); + + EditorSearchComponent component = getEditorSearchComponent(); + final FindModel findModel = component.getFindModel(); FindUtil.configureFindModel(false, null, findModel, false); + component.selectAllText(); } } diff --git a/platform/lang-impl/src/com/intellij/find/editorHeaderActions/SwitchToReplace.java b/platform/lang-impl/src/com/intellij/find/editorHeaderActions/SwitchToReplace.java index f667f019db17..2d8b48aad855 100644 --- a/platform/lang-impl/src/com/intellij/find/editorHeaderActions/SwitchToReplace.java +++ b/platform/lang-impl/src/com/intellij/find/editorHeaderActions/SwitchToReplace.java @@ -33,7 +33,9 @@ public class SwitchToReplace extends EditorHeaderAction { @Override public void actionPerformed(AnActionEvent e) { - final FindModel findModel = getEditorSearchComponent().getFindModel(); + EditorSearchComponent component = getEditorSearchComponent(); + final FindModel findModel = component.getFindModel(); FindUtil.configureFindModel(true, null, findModel, false); + component.selectAllText(); } } diff --git a/platform/lang-impl/src/com/intellij/find/editorHeaderActions/ToggleRegex.java b/platform/lang-impl/src/com/intellij/find/editorHeaderActions/ToggleRegex.java index 1a4ea149df7d..472517ab336d 100644 --- a/platform/lang-impl/src/com/intellij/find/editorHeaderActions/ToggleRegex.java +++ b/platform/lang-impl/src/com/intellij/find/editorHeaderActions/ToggleRegex.java @@ -29,12 +29,6 @@ public class ToggleRegex extends EditorHeaderToggleAction { return getEditorSearchComponent().getFindModel().isRegularExpressions(); } - @Override - public void update(AnActionEvent e) { - super.update(e); - e.getPresentation().setVisible(!getEditorSearchComponent().getFindModel().isMultiline()); - } - @Override public void setSelected(AnActionEvent e, boolean state) { final FindModel findModel = getEditorSearchComponent().getFindModel(); diff --git a/platform/lang-impl/src/com/intellij/find/impl/FindInProjectTask.java b/platform/lang-impl/src/com/intellij/find/impl/FindInProjectTask.java index 7dfdbbce16d7..822ffbb64037 100644 --- a/platform/lang-impl/src/com/intellij/find/impl/FindInProjectTask.java +++ b/platform/lang-impl/src/com/intellij/find/impl/FindInProjectTask.java @@ -38,6 +38,7 @@ import com.intellij.openapi.project.ProjectCoreUtil; import com.intellij.openapi.roots.*; import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.Condition; +import com.intellij.openapi.util.Conditions; import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.util.text.TrigramBuilder; @@ -105,7 +106,7 @@ class FindInProjectTask { final Pattern pattern = FindInProjectUtil.createFileMaskRegExp(filter); //noinspection unchecked - myFileMask = pattern == null ? Condition.TRUE : new Condition() { + myFileMask = pattern == null ? Conditions.alwaysTrue() : new Condition() { @Override public boolean value(VirtualFile file) { return file != null && pattern.matcher(file.getName()).matches(); @@ -127,6 +128,9 @@ class FindInProjectTask { } }); myProgress.setIndeterminate(false); + if (LOG.isDebugEnabled()) { + LOG.debug("Searching for " + myFindModel.getStringToFind() + " in " + filesForFastWordSearch.size() + " indexed files"); + } searchInFiles(filesForFastWordSearch, processPresentation, consumer); @@ -136,6 +140,10 @@ class FindInProjectTask { final Collection otherFiles = collectFilesInScope(filesForFastWordSearch, skipIndexed); myProgress.setIndeterminate(false); + if (LOG.isDebugEnabled()) { + LOG.debug("Searching for " + myFindModel.getStringToFind() + " in " + otherFiles.size() + " non-indexed files"); + } + long start = System.currentTimeMillis(); searchInFiles(otherFiles, processPresentation, consumer); if (skipIndexed && otherFiles.size() > 1000) { @@ -143,7 +151,10 @@ class FindInProjectTask { } } catch (ProcessCanceledException e) { - // fine + processPresentation.setCanceled(true); + if (LOG.isDebugEnabled()) { + LOG.debug("Usage search canceled", e); + } } if (!myLargeFiles.isEmpty()) { diff --git a/platform/lang-impl/src/com/intellij/find/impl/FindManagerImpl.java b/platform/lang-impl/src/com/intellij/find/impl/FindManagerImpl.java index b7d58e926e30..f077008068ad 100644 --- a/platform/lang-impl/src/com/intellij/find/impl/FindManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/find/impl/FindManagerImpl.java @@ -446,8 +446,16 @@ public class FindManagerImpl extends FindManager implements PersistentStateCompo final FindModel model = new FindModel(); model.copyFrom(findmodel); final String s = model.getStringToFind(); - model.setStringToFind(StringUtil.escapeToRegexp(s)); - model.setRegularExpressions(true); + String newStringToFind; + + if (findmodel.isRegularExpressions()) { + newStringToFind = StringUtil.replace(s, "\n", "\\n\\s*"); // add \\s* for convenience + } else { + newStringToFind = StringUtil.escapeToRegexp(s); + model.setRegularExpressions(true); + } + model.setStringToFind(newStringToFind); + return model; } return findmodel; diff --git a/platform/lang-impl/src/com/intellij/find/impl/livePreview/LivePreview.java b/platform/lang-impl/src/com/intellij/find/impl/livePreview/LivePreview.java index 995a67ecafa1..e104c1664857 100644 --- a/platform/lang-impl/src/com/intellij/find/impl/livePreview/LivePreview.java +++ b/platform/lang-impl/src/com/intellij/find/impl/livePreview/LivePreview.java @@ -24,7 +24,6 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.SelectionModel; -import com.intellij.openapi.editor.colors.CodeInsightColors; import com.intellij.openapi.editor.colors.EditorColors; import com.intellij.openapi.editor.colors.EditorColorsManager; import com.intellij.openapi.editor.event.*; @@ -53,9 +52,7 @@ import java.io.PrintStream; import java.util.*; import java.util.List; -public class LivePreview extends DocumentAdapter implements SearchResults.SearchResultsListener, - SelectionListener { - +public class LivePreview extends DocumentAdapter implements SearchResults.SearchResultsListener, SelectionListener { private static final Key IN_SELECTION_KEY = Key.create("LivePreview.IN_SELECTION_KEY"); private static final Object IN_SELECTION1 = new Object(); private static final Object IN_SELECTION2 = new Object(); @@ -73,6 +70,13 @@ public class LivePreview extends DocumentAdapter implements SearchResults.Search private String myReplacementPreviewText; private static boolean NotFound; + private final Set myHighlighters = new HashSet(); + private RangeHighlighter myCursorHighlighter; + private final List myVisibleAreaListenersToRemove = new ArrayList(); + private Delegate myDelegate; + private final SearchResults mySearchResults; + private Balloon myReplacementBalloon; + @Override public void selectionChanged(SelectionEvent e) { updateInSelectionHighlighters(); @@ -87,27 +91,15 @@ public class LivePreview extends DocumentAdapter implements SearchResults.Search } public interface Delegate { - @Nullable - String getStringToReplace(Editor editor, FindResult findResult); - + String getStringToReplace(@NotNull Editor editor, @Nullable FindResult findResult); } - private final Set myHighlighters = new HashSet(); - - private RangeHighlighter myCursorHighlighter; - private final List myVisibleAreaListenersToRemove = new ArrayList(); - - private static TextAttributes strikout() { - return EditorColorsManager.getInstance().getGlobalScheme().getAttributes(CodeInsightColors.DEPRECATED_ATTRIBUTES).clone(); + private static TextAttributes strikeout() { + Color color = EditorColorsManager.getInstance().getGlobalScheme().getDefaultForeground(); + return new TextAttributes(null, null, color, EffectType.STRIKEOUT, 0); } - private Delegate myDelegate; - - private final SearchResults mySearchResults; - - private Balloon myReplacementBalloon; - @Override public void searchResultsUpdated(SearchResults sr) { final Project project = mySearchResults.getProject(); @@ -323,7 +315,7 @@ public class LivePreview extends DocumentAdapter implements SearchResults.Search attributes.setEffectColor(attributes.getBackgroundColor()); } if (mySearchResults.isExcluded(range)) { - highlightRange(range, strikout(), myHighlighters); + highlightRange(range, strikeout(), myHighlighters); } else { highlightRange(range, attributes, myHighlighters); } diff --git a/platform/lang-impl/src/com/intellij/find/impl/livePreview/LivePreviewController.java b/platform/lang-impl/src/com/intellij/find/impl/livePreview/LivePreviewController.java index 94a6c3f9c1bc..a894781d5cb1 100644 --- a/platform/lang-impl/src/com/intellij/find/impl/livePreview/LivePreviewController.java +++ b/platform/lang-impl/src/com/intellij/find/impl/livePreview/LivePreviewController.java @@ -19,7 +19,9 @@ import com.intellij.find.*; import com.intellij.find.impl.FindResultImpl; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.editor.*; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.RangeMarker; +import com.intellij.openapi.editor.SelectionModel; import com.intellij.openapi.editor.event.DocumentAdapter; import com.intellij.openapi.editor.event.DocumentEvent; import com.intellij.openapi.editor.event.SelectionEvent; @@ -29,6 +31,7 @@ import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.vfs.ReadonlyStatusHandler; import com.intellij.util.Alarm; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; @@ -147,7 +150,7 @@ public class LivePreviewController implements LivePreview.Delegate, FindUtil.Rep } @Override - public String getStringToReplace(Editor editor, FindResult findResult) { + public String getStringToReplace(@NotNull Editor editor, @Nullable FindResult findResult) { if (findResult == null) { return null; } diff --git a/platform/lang-impl/src/com/intellij/ide/RecentDirectoryProjectsManagerEx.java b/platform/lang-impl/src/com/intellij/ide/RecentDirectoryProjectsManagerEx.java index 21207519fb6b..b9086439906b 100644 --- a/platform/lang-impl/src/com/intellij/ide/RecentDirectoryProjectsManagerEx.java +++ b/platform/lang-impl/src/com/intellij/ide/RecentDirectoryProjectsManagerEx.java @@ -29,11 +29,7 @@ import org.jetbrains.annotations.NotNull; */ @State( name = "RecentDirectoryProjectsManager", - roamingType = RoamingType.DISABLED, - storages = { - @Storage( - file = StoragePathMacros.APP_CONFIG + "/other.xml" - )} + storages = {@Storage(file = StoragePathMacros.APP_CONFIG + "/other.xml", roamingType = RoamingType.DISABLED)} ) public class RecentDirectoryProjectsManagerEx extends RecentDirectoryProjectsManager { public RecentDirectoryProjectsManagerEx(MessageBus messageBus) { diff --git a/platform/lang-impl/src/com/intellij/ide/actions/ExternalJavaDocAction.java b/platform/lang-impl/src/com/intellij/ide/actions/ExternalJavaDocAction.java index 71b02f867d8a..50525bc0e288 100644 --- a/platform/lang-impl/src/com/intellij/ide/actions/ExternalJavaDocAction.java +++ b/platform/lang-impl/src/com/intellij/ide/actions/ExternalJavaDocAction.java @@ -131,7 +131,7 @@ public class ExternalJavaDocAction extends AnAction { } if (editor != null) { presentation.setEnabled(enabled); - if (event.getPlace().equals(ActionPlaces.MAIN_MENU)) { + if (ActionPlaces.isMainMenuOrActionSearch(event.getPlace())) { presentation.setVisible(true); } else { diff --git a/platform/lang-impl/src/com/intellij/ide/actions/GotoActionAction.java b/platform/lang-impl/src/com/intellij/ide/actions/GotoActionAction.java index e57d1eba05ef..b54a1989e692 100644 --- a/platform/lang-impl/src/com/intellij/ide/actions/GotoActionAction.java +++ b/platform/lang-impl/src/com/intellij/ide/actions/GotoActionAction.java @@ -96,7 +96,7 @@ public class GotoActionAction extends GotoActionBase implements DumbAware { final DataContext context = DataManager.getInstance().getDataContext(component); final AnActionEvent event = new AnActionEvent(e == null ? null : e.getInputEvent(), context, - e == null ? ActionPlaces.UNKNOWN : e.getPlace(), + ActionPlaces.ACTION_SEARCH, presentation, ActionManager.getInstance(), e == null ? 0 : e.getModifiers()); diff --git a/platform/lang-impl/src/com/intellij/ide/actions/GotoActionBase.java b/platform/lang-impl/src/com/intellij/ide/actions/GotoActionBase.java index c109de7a00a9..5da112b375b9 100644 --- a/platform/lang-impl/src/com/intellij/ide/actions/GotoActionBase.java +++ b/platform/lang-impl/src/com/intellij/ide/actions/GotoActionBase.java @@ -62,7 +62,7 @@ public abstract class GotoActionBase extends AnAction { private int myHistoryIndex = 0; @Override - public void actionPerformed(AnActionEvent e) { + public void actionPerformed(@NotNull AnActionEvent e) { LOG.assertTrue(!getClass().equals(myInAction)); try { myInAction = getClass(); @@ -79,7 +79,7 @@ public abstract class GotoActionBase extends AnAction { protected abstract void gotoActionPerformed(AnActionEvent e); @Override - public void update(final AnActionEvent event) { + public void update(@NotNull final AnActionEvent event) { final Presentation presentation = event.getPresentation(); final DataContext dataContext = event.getDataContext(); final Project project = CommonDataKeys.PROJECT.getData(dataContext); @@ -260,7 +260,7 @@ public abstract class GotoActionBase extends AnAction { abstract class HistoryAction extends DumbAwareAction { @Override - public void update(AnActionEvent e) { + public void update(@NotNull AnActionEvent e) { e.getPresentation().setEnabled(historyEnabled()); } @@ -277,7 +277,7 @@ public abstract class GotoActionBase extends AnAction { new HistoryAction() { @Override - public void actionPerformed(AnActionEvent e) { + public void actionPerformed(@NotNull AnActionEvent e) { List strings = ourHistory.get(myInAction); setText(strings); myHistoryIndex = myHistoryIndex >= strings.size() - 1 ? 0 : myHistoryIndex + 1; @@ -287,7 +287,7 @@ public abstract class GotoActionBase extends AnAction { new HistoryAction() { @Override - public void actionPerformed(AnActionEvent e) { + public void actionPerformed(@NotNull AnActionEvent e) { List strings = ourHistory.get(myInAction); setText(strings); myHistoryIndex = myHistoryIndex <= 0 ? strings.size() - 1 : myHistoryIndex - 1; diff --git a/platform/lang-impl/src/com/intellij/ide/hierarchy/actions/BrowseCallHierarchyAction.java b/platform/lang-impl/src/com/intellij/ide/hierarchy/actions/BrowseCallHierarchyAction.java index 853d859f9f82..9350b4b1d73c 100644 --- a/platform/lang-impl/src/com/intellij/ide/hierarchy/actions/BrowseCallHierarchyAction.java +++ b/platform/lang-impl/src/com/intellij/ide/hierarchy/actions/BrowseCallHierarchyAction.java @@ -30,7 +30,7 @@ public final class BrowseCallHierarchyAction extends BrowseHierarchyActionBase { @Override public final void update(final AnActionEvent event){ final Presentation presentation = event.getPresentation(); - if (!ActionPlaces.MAIN_MENU.equals(event.getPlace())) { + if (!ActionPlaces.isMainMenuOrActionSearch(event.getPlace())) { presentation.setText(IdeBundle.message("action.browse.call.hierarchy")); } diff --git a/platform/lang-impl/src/com/intellij/ide/hierarchy/actions/BrowseMethodHierarchyAction.java b/platform/lang-impl/src/com/intellij/ide/hierarchy/actions/BrowseMethodHierarchyAction.java index 7cd056f7bb16..5a23c6b3925c 100644 --- a/platform/lang-impl/src/com/intellij/ide/hierarchy/actions/BrowseMethodHierarchyAction.java +++ b/platform/lang-impl/src/com/intellij/ide/hierarchy/actions/BrowseMethodHierarchyAction.java @@ -30,7 +30,7 @@ public final class BrowseMethodHierarchyAction extends BrowseHierarchyActionBase @Override public final void update(final AnActionEvent event){ final Presentation presentation = event.getPresentation(); - if (!ActionPlaces.MAIN_MENU.equals(event.getPlace())) { + if (!ActionPlaces.isMainMenuOrActionSearch(event.getPlace())) { presentation.setText(IdeBundle.message("action.browse.method.hierarchy")); } super.update(event); diff --git a/platform/lang-impl/src/com/intellij/ide/hierarchy/actions/BrowseTypeHierarchyAction.java b/platform/lang-impl/src/com/intellij/ide/hierarchy/actions/BrowseTypeHierarchyAction.java index 41234ef79679..6ac33fc1047a 100644 --- a/platform/lang-impl/src/com/intellij/ide/hierarchy/actions/BrowseTypeHierarchyAction.java +++ b/platform/lang-impl/src/com/intellij/ide/hierarchy/actions/BrowseTypeHierarchyAction.java @@ -30,7 +30,7 @@ public final class BrowseTypeHierarchyAction extends BrowseHierarchyActionBase { @Override public final void update(final AnActionEvent event){ final Presentation presentation = event.getPresentation(); - if (!ActionPlaces.MAIN_MENU.equals(event.getPlace())) { + if (!ActionPlaces.isMainMenuOrActionSearch(event.getPlace())) { presentation.setText(IdeBundle.message("action.browse.type.hierarchy")); } super.update(event); diff --git a/platform/lang-impl/src/com/intellij/ide/impl/convert/JDomConvertingUtil.java b/platform/lang-impl/src/com/intellij/ide/impl/convert/JDomConvertingUtil.java index b2f040064fe4..21e55f9ab089 100644 --- a/platform/lang-impl/src/com/intellij/ide/impl/convert/JDomConvertingUtil.java +++ b/platform/lang-impl/src/com/intellij/ide/impl/convert/JDomConvertingUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -103,7 +103,7 @@ public class JDomConvertingUtil extends JDomSerializationUtil { } public static void copyChildren(Element from, Element to) { - copyChildren(from, to, Condition.TRUE); + copyChildren(from, to, Conditions.alwaysTrue()); } public static void copyChildren(Element from, Element to, Condition filter) { diff --git a/platform/lang-impl/src/com/intellij/ide/navigationToolbar/NavBarListener.java b/platform/lang-impl/src/com/intellij/ide/navigationToolbar/NavBarListener.java index 3fe38cea2ed3..e0a0d946c9c5 100644 --- a/platform/lang-impl/src/com/intellij/ide/navigationToolbar/NavBarListener.java +++ b/platform/lang-impl/src/com/intellij/ide/navigationToolbar/NavBarListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,8 @@ package com.intellij.ide.navigationToolbar; import com.intellij.ProjectTopics; import com.intellij.ide.actions.CopyAction; import com.intellij.ide.actions.CutAction; +import com.intellij.ide.ui.LafManager; +import com.intellij.ide.ui.LafManagerListener; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.actionSystem.ex.AnActionListener; @@ -57,7 +59,8 @@ import java.util.List; */ public class NavBarListener extends WolfTheProblemSolver.ProblemListener implements ActionListener, FocusListener, FileStatusListener, AnActionListener, FileEditorManagerListener, - PsiTreeChangeListener, ModuleRootListener, NavBarModelListener, PropertyChangeListener, KeyListener, WindowFocusListener { + PsiTreeChangeListener, ModuleRootListener, NavBarModelListener, PropertyChangeListener, KeyListener, WindowFocusListener, + LafManagerListener { private static final String LISTENER = "NavBarListener"; private static final String BUS = "NavBarMessageBus"; private final NavBarPanel myPanel; @@ -89,6 +92,8 @@ public class NavBarListener extends WolfTheProblemSolver.ProblemListener if (window != null) { window.addWindowFocusListener(listener); } + } else { + LafManager.getInstance().addLafManagerListener(listener); } } @@ -107,6 +112,7 @@ public class NavBarListener extends WolfTheProblemSolver.ProblemListener if (connection != null) { connection.disconnect(); } + LafManager.getInstance().removeLafManagerListener(listener); } } @@ -356,15 +362,19 @@ public class NavBarListener extends WolfTheProblemSolver.ProblemListener } @Override - public void windowLostFocus(WindowEvent e) { - final Window window = e.getWindow(); - final Window oppositeWindow = e.getOppositeWindow(); + public void lookAndFeelChanged(LafManager source) { + myPanel.getNavBarUI().clearItems(); + myPanel.revalidate(); + myPanel.repaint(); } + //---- Ignored @Override - public void windowGainedFocus(WindowEvent e) { - } + public void windowLostFocus(WindowEvent e) {} + + @Override + public void windowGainedFocus(WindowEvent e) {} @Override public void keyTyped(KeyEvent e) {} diff --git a/platform/lang-impl/src/com/intellij/ide/navigationToolbar/ui/AbstractNavBarUI.java b/platform/lang-impl/src/com/intellij/ide/navigationToolbar/ui/AbstractNavBarUI.java index 1201e146869d..5e0f6e58d106 100644 --- a/platform/lang-impl/src/com/intellij/ide/navigationToolbar/ui/AbstractNavBarUI.java +++ b/platform/lang-impl/src/com/intellij/ide/navigationToolbar/ui/AbstractNavBarUI.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -40,7 +40,7 @@ import java.util.Map; */ public abstract class AbstractNavBarUI implements NavBarUI { - private final Map> myCache = new THashMap>(); + private final static Map> myCache = new THashMap>(); private enum ImageType { INACTIVE, NEXT_ACTIVE, ACTIVE, INACTIVE_FLOATING, NEXT_ACTIVE_FLOATING, ACTIVE_FLOATING, diff --git a/platform/lang-impl/src/com/intellij/ide/scratch/CreateScratchFileAction.java b/platform/lang-impl/src/com/intellij/ide/scratch/CreateScratchFileAction.java index 024c3feabe5e..7a190f6be71c 100644 --- a/platform/lang-impl/src/com/intellij/ide/scratch/CreateScratchFileAction.java +++ b/platform/lang-impl/src/com/intellij/ide/scratch/CreateScratchFileAction.java @@ -34,6 +34,7 @@ import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.containers.ContainerUtilRt; import com.intellij.util.ui.EmptyIcon; import org.jetbrains.annotations.NotNull; @@ -136,7 +137,7 @@ public class CreateScratchFileAction extends AnAction implements DumbAware { @NotNull public static List getLanguages() { - Set result = ContainerUtil.newTreeSet(new Comparator() { + Set result = ContainerUtilRt.newTreeSet(new Comparator() { @Override public int compare(@NotNull Language l1, @NotNull Language l2) { return l1.getDisplayName().compareTo(l2.getDisplayName()); diff --git a/platform/lang-impl/src/com/intellij/ide/util/gotoByName/GotoActionModel.java b/platform/lang-impl/src/com/intellij/ide/util/gotoByName/GotoActionModel.java index 348283dc3885..05d9bb771978 100644 --- a/platform/lang-impl/src/com/intellij/ide/util/gotoByName/GotoActionModel.java +++ b/platform/lang-impl/src/com/intellij/ide/util/gotoByName/GotoActionModel.java @@ -40,6 +40,7 @@ import com.intellij.ui.components.JBLabel; import com.intellij.ui.speedSearch.SpeedSearchUtil; import com.intellij.util.ArrayUtil; import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.containers.ContainerUtilRt; import com.intellij.util.ui.EmptyIcon; import com.intellij.util.ui.UIUtil; import org.apache.oro.text.regex.*; @@ -492,7 +493,7 @@ public class GotoActionModel implements ChooseByNameModel, CustomMatcherModel, C @NotNull @Override public SortedSet sort(@NotNull Set elements) { - TreeSet objects = ContainerUtil.newTreeSet(this); + TreeSet objects = ContainerUtilRt.newTreeSet(this); objects.addAll(elements); return objects; } diff --git a/platform/lang-impl/src/com/intellij/ide/util/scopeChooser/ScopeChooserCombo.java b/platform/lang-impl/src/com/intellij/ide/util/scopeChooser/ScopeChooserCombo.java index 8297a3db8e1f..98190010159b 100644 --- a/platform/lang-impl/src/com/intellij/ide/util/scopeChooser/ScopeChooserCombo.java +++ b/platform/lang-impl/src/com/intellij/ide/util/scopeChooser/ScopeChooserCombo.java @@ -29,6 +29,7 @@ import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.module.*; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.vfs.VirtualFile; @@ -411,21 +412,32 @@ public class ScopeChooserCombo extends ComboboxWithBrowseButton implements Dispo } } - if (dataContext != null) { - final VirtualFile[] files = CommonDataKeys.VIRTUAL_FILE_ARRAY.getData(dataContext); - if (files != null) { - final List openFiles = Arrays.asList(files); - result.add(new DelegatingGlobalSearchScope(GlobalSearchScope.filesScope(project, openFiles)){ + ContainerUtil.addIfNotNull(result, getSelectedFilesScope(project, dataContext)); + + return ContainerUtil.newArrayList(result); + } + + @Nullable + private static SearchScope getSelectedFilesScope(final Project project, @Nullable DataContext dataContext) { + final VirtualFile[] filesOrDirs = dataContext == null ? null : CommonDataKeys.VIRTUAL_FILE_ARRAY.getData(dataContext); + if (filesOrDirs != null) { + final List selectedFiles = ContainerUtil.filter(filesOrDirs, new Condition() { + @Override + public boolean value(VirtualFile file) { + return !file.isDirectory(); + } + }); + if (!selectedFiles.isEmpty()) { + return new DelegatingGlobalSearchScope(GlobalSearchScope.filesScope(project, selectedFiles)){ @NotNull @Override public String getDisplayName() { return "Selected Files"; } - }); + }; } } - - return ContainerUtil.newArrayList(result); + return null; } protected static Set collectFiles(Set usages, boolean findFirst) { diff --git a/platform/lang-impl/src/com/intellij/ide/util/scopeChooser/ScopeChooserConfigurable.java b/platform/lang-impl/src/com/intellij/ide/util/scopeChooser/ScopeChooserConfigurable.java index 96de48a0db13..a2bf7a47af28 100644 --- a/platform/lang-impl/src/com/intellij/ide/util/scopeChooser/ScopeChooserConfigurable.java +++ b/platform/lang-impl/src/com/intellij/ide/util/scopeChooser/ScopeChooserConfigurable.java @@ -81,7 +81,7 @@ public class ScopeChooserConfigurable extends MasterDetailsComponent implements @Override protected Dimension getPanelPreferredSize() { - return new Dimension(-1, -1); + return new Dimension(400, 200); } @Override diff --git a/platform/lang-impl/src/com/intellij/lang/customFolding/CustomFoldingRegionsPopup.java b/platform/lang-impl/src/com/intellij/lang/customFolding/CustomFoldingRegionsPopup.java new file mode 100644 index 000000000000..203179709dae --- /dev/null +++ b/platform/lang-impl/src/com/intellij/lang/customFolding/CustomFoldingRegionsPopup.java @@ -0,0 +1,133 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.lang.customFolding; + +import com.intellij.ide.IdeBundle; +import com.intellij.lang.folding.FoldingDescriptor; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.ScrollType; +import com.intellij.openapi.fileEditor.ex.IdeDocumentHistory; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.popup.JBPopup; +import com.intellij.openapi.ui.popup.JBPopupFactory; +import com.intellij.openapi.ui.popup.PopupChooserBuilder; +import com.intellij.psi.PsiElement; +import com.intellij.ui.components.JBList; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.util.*; + +/** + * @author Rustam Vishnyakov + */ +public class CustomFoldingRegionsPopup { + private final @NotNull JBList myRegionsList; + private final @NotNull JBPopup myPopup; + private final @NotNull Editor myEditor; + + CustomFoldingRegionsPopup(@NotNull Collection descriptors, + @NotNull final Editor editor, + @NotNull final Project project) { + myEditor = editor; + myRegionsList = new JBList(); + //noinspection unchecked + myRegionsList.setModel(new MyListModel(orderByPosition(descriptors))); + myRegionsList.setSelectedIndex(0); + + final PopupChooserBuilder popupBuilder = JBPopupFactory.getInstance().createListPopupBuilder(myRegionsList); + myPopup = popupBuilder + .setTitle(IdeBundle.message("goto.custom.region.command")) + .setResizable(false) + .setMovable(false) + .setItemChoosenCallback(new Runnable() { + @Override + public void run() { + PsiElement navigationElement = getNavigationElement(); + if (navigationElement != null) { + navigateTo(editor, navigationElement); + IdeDocumentHistory.getInstance(project).includeCurrentCommandAsNavigation(); + } + } + }).createPopup(); + } + + void show() { + myPopup.showInBestPositionFor(myEditor); + } + + private static class MyListModel extends DefaultListModel { + private MyListModel(Collection descriptors) { + for (FoldingDescriptor descriptor : descriptors) { + //noinspection unchecked + super.addElement(new MyFoldingDescriptorWrapper(descriptor)); + } + } + } + + private static class MyFoldingDescriptorWrapper { + private final @NotNull FoldingDescriptor myDescriptor; + + private MyFoldingDescriptorWrapper(@NotNull FoldingDescriptor descriptor) { + myDescriptor = descriptor; + } + + @NotNull + public FoldingDescriptor getDescriptor() { + return myDescriptor; + } + + @Nullable + @Override + public String toString() { + return myDescriptor.getPlaceholderText(); + } + } + + @Nullable + public PsiElement getNavigationElement() { + Object selection = myRegionsList.getSelectedValue(); + if (selection instanceof MyFoldingDescriptorWrapper) { + return ((MyFoldingDescriptorWrapper)selection).getDescriptor().getElement().getPsi(); + } + return null; + } + + private static Collection orderByPosition(Collection descriptors) { + List sorted = new ArrayList(descriptors.size()); + sorted.addAll(descriptors); + Collections.sort(sorted, new Comparator() { + @Override + public int compare(FoldingDescriptor descriptor1, FoldingDescriptor descriptor2) { + int pos1 = descriptor1.getElement().getTextRange().getStartOffset(); + int pos2 = descriptor2.getElement().getTextRange().getStartOffset(); + return pos1 - pos2; + } + }); + return sorted; + } + + private static void navigateTo(@NotNull Editor editor, @NotNull PsiElement element) { + int offset = element.getTextRange().getStartOffset(); + if (offset >= 0 && offset < editor.getDocument().getTextLength()) { + editor.getCaretModel().removeSecondaryCarets(); + editor.getCaretModel().moveToOffset(offset); + editor.getScrollingModel().scrollToCaret(ScrollType.CENTER); + editor.getSelectionModel().removeSelection(); + } + } +} diff --git a/platform/lang-impl/src/com/intellij/lang/customFolding/GotoCustomRegionAction.java b/platform/lang-impl/src/com/intellij/lang/customFolding/GotoCustomRegionAction.java index 016f7f14cc4d..e0117affbcad 100644 --- a/platform/lang-impl/src/com/intellij/lang/customFolding/GotoCustomRegionAction.java +++ b/platform/lang-impl/src/com/intellij/lang/customFolding/GotoCustomRegionAction.java @@ -16,26 +16,35 @@ package com.intellij.lang.customFolding; import com.intellij.ide.IdeBundle; -import com.intellij.openapi.actionSystem.AnAction; -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.actionSystem.CommonDataKeys; -import com.intellij.openapi.actionSystem.PlatformDataKeys; -import com.intellij.openapi.actionSystem.Presentation; +import com.intellij.lang.Language; +import com.intellij.lang.folding.*; +import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.command.CommandProcessor; +import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.editor.ScrollType; -import com.intellij.openapi.fileEditor.ex.IdeDocumentHistory; import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.DumbService; import com.intellij.openapi.project.Project; -import com.intellij.psi.PsiElement; +import com.intellij.openapi.ui.MessageType; +import com.intellij.openapi.ui.popup.Balloon; +import com.intellij.openapi.ui.popup.JBPopupFactory; +import com.intellij.openapi.util.Disposer; +import com.intellij.psi.FileViewProvider; +import com.intellij.psi.PsiDocumentManager; +import com.intellij.psi.PsiFile; +import com.intellij.util.containers.HashSet; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; +import java.util.Set; /** * @author Rustam Vishnyakov */ -public class GotoCustomRegionAction extends AnAction implements DumbAware { +public class GotoCustomRegionAction extends AnAction implements DumbAware, PopupAction { @Override - public void actionPerformed(AnActionEvent e) { + public void actionPerformed(final AnActionEvent e) { final Project project = e.getProject(); final Editor editor = e.getData(CommonDataKeys.EDITOR); if (Boolean.TRUE.equals(e.getData(PlatformDataKeys.IS_MODAL_CONTEXT))) { @@ -52,14 +61,13 @@ public class GotoCustomRegionAction extends AnAction implements DumbAware { new Runnable() { @Override public void run() { - GotoCustomRegionDialog dialog = new GotoCustomRegionDialog(project, editor); - dialog.show(); - if (dialog.isOK()) { - PsiElement navigationElement = dialog.getNavigationElement(); - if (navigationElement != null) { - navigateTo(editor, navigationElement); - IdeDocumentHistory.getInstance(project).includeCurrentCommandAsNavigation(); - } + Collection foldingDescriptors = getCustomFoldingDescriptors(editor, project); + if (foldingDescriptors.size() > 0) { + CustomFoldingRegionsPopup regionsPopup = new CustomFoldingRegionsPopup(foldingDescriptors, editor, project); + regionsPopup.show(); + } + else { + notifyCustomRegionsUnavailable(editor, project); } } }, @@ -71,7 +79,7 @@ public class GotoCustomRegionAction extends AnAction implements DumbAware { @Override public void update(AnActionEvent e) { Presentation presentation = e.getPresentation(); - presentation.setText("Custom Region..."); + presentation.setText(IdeBundle.message("goto.custom.region.menu.item")); final Editor editor = e.getData(CommonDataKeys.EDITOR); final Project project = e.getProject(); boolean isAvailable = editor != null && project != null; @@ -79,13 +87,49 @@ public class GotoCustomRegionAction extends AnAction implements DumbAware { presentation.setVisible(isAvailable); } - private static void navigateTo(Editor editor, PsiElement element) { - int offset = element.getTextRange().getStartOffset(); - if (offset >= 0 && offset < editor.getDocument().getTextLength()) { - editor.getCaretModel().removeSecondaryCarets(); - editor.getCaretModel().moveToOffset(offset); - editor.getScrollingModel().scrollToCaret(ScrollType.CENTER); - editor.getSelectionModel().removeSelection(); + @NotNull + private static Collection getCustomFoldingDescriptors(@NotNull Editor editor, @NotNull Project project) { + Set foldingDescriptors = new HashSet(); + final Document document = editor.getDocument(); + PsiDocumentManager documentManager = PsiDocumentManager.getInstance(project); + PsiFile file = documentManager != null ? documentManager.getPsiFile(document) : null; + if (file != null) { + final FileViewProvider viewProvider = file.getViewProvider(); + for (final Language language : viewProvider.getLanguages()) { + final PsiFile psi = viewProvider.getPsi(language); + final FoldingBuilder foldingBuilder = LanguageFolding.INSTANCE.forLanguage(language); + if (psi != null) { + for (FoldingDescriptor descriptor : LanguageFolding.buildFoldingDescriptors(foldingBuilder, psi, document, false)) { + CustomFoldingBuilder customFoldingBuilder = getCustomFoldingBuilder(foldingBuilder, descriptor); + if (customFoldingBuilder != null) { + if (customFoldingBuilder.isCustomRegionStart(descriptor.getElement())) { + foldingDescriptors.add(descriptor); + } + } + } + } + } } + return foldingDescriptors; + } + + @Nullable + private static CustomFoldingBuilder getCustomFoldingBuilder(FoldingBuilder builder, FoldingDescriptor descriptor) { + if (builder instanceof CustomFoldingBuilder) return (CustomFoldingBuilder)builder; + FoldingBuilder originalBuilder = descriptor.getElement().getUserData(CompositeFoldingBuilder.FOLDING_BUILDER); + if (originalBuilder instanceof CustomFoldingBuilder) return (CustomFoldingBuilder)originalBuilder; + return null; + } + + private static void notifyCustomRegionsUnavailable(@NotNull Editor editor, @NotNull Project project) { + final JBPopupFactory popupFactory = JBPopupFactory.getInstance(); + Balloon balloon = popupFactory + .createHtmlTextBalloonBuilder(IdeBundle.message("goto.custom.region.message.unavailable"), MessageType.INFO, null) + .setFadeoutTime(2000) + .setHideOnClickOutside(true) + .setHideOnKeyOutside(true) + .createBalloon(); + Disposer.register(project, balloon); + balloon.show(popupFactory.guessBestPopupLocation(editor), Balloon.Position.above); } } diff --git a/platform/lang-impl/src/com/intellij/lang/customFolding/GotoCustomRegionDialog.form b/platform/lang-impl/src/com/intellij/lang/customFolding/GotoCustomRegionDialog.form deleted file mode 100644 index c155616c8186..000000000000 --- a/platform/lang-impl/src/com/intellij/lang/customFolding/GotoCustomRegionDialog.form +++ /dev/null @@ -1,25 +0,0 @@ - -
- - - - - - - - - - - - - - - - - - - - - - -
diff --git a/platform/lang-impl/src/com/intellij/lang/customFolding/GotoCustomRegionDialog.java b/platform/lang-impl/src/com/intellij/lang/customFolding/GotoCustomRegionDialog.java deleted file mode 100644 index 49d63dfe24b9..000000000000 --- a/platform/lang-impl/src/com/intellij/lang/customFolding/GotoCustomRegionDialog.java +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright 2000-2012 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.intellij.lang.customFolding; - -import com.intellij.ide.IdeBundle; -import com.intellij.lang.Language; -import com.intellij.lang.folding.*; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.DialogWrapper; -import com.intellij.psi.FileViewProvider; -import com.intellij.psi.PsiDocumentManager; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; -import com.intellij.ui.components.JBList; -import com.intellij.ui.components.JBScrollPane; -import com.intellij.util.containers.HashSet; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import javax.swing.*; -import java.awt.*; -import java.util.*; -import java.util.List; - -/** - * @author Rustam Vishnyakov - */ -public class GotoCustomRegionDialog extends DialogWrapper { - private JBList myRegionsList; - private JPanel myContentPane; - private JBScrollPane myScrollPane; - private final Editor myEditor; - private final Project myProject; - - protected GotoCustomRegionDialog(@Nullable Project project, @NotNull Editor editor) { - super(project); - myEditor = editor; - myProject = project; - Collection descriptors = getCustomFoldingDescriptors(); - init(); - if (descriptors.size() == 0) { - myScrollPane.setVisible(false); - myContentPane.add(new JLabel(IdeBundle.message("goto.custom.region.message.unavailable")), BorderLayout.NORTH); - setOKActionEnabled(false); - } - else { - myRegionsList.setModel(new MyListModel(orderByPosition(descriptors))); - myRegionsList.setSelectedIndex(0); - } - setTitle(IdeBundle.message("goto.custom.region.command")); - } - - @Override - public JComponent getPreferredFocusedComponent() { - if (!myRegionsList.isEmpty()) { - return myRegionsList; - } - return super.getPreferredFocusedComponent(); - } - - @Override - protected JComponent createCenterPanel() { - return myContentPane; - } - - private Collection getCustomFoldingDescriptors() { - Set foldingDescriptors = new HashSet(); - final Document document = myEditor.getDocument(); - PsiDocumentManager documentManager = PsiDocumentManager.getInstance(myProject); - PsiFile file = documentManager != null ? documentManager.getPsiFile(document) : null; - if (file != null) { - final FileViewProvider viewProvider = file.getViewProvider(); - for (final Language language : viewProvider.getLanguages()) { - final PsiFile psi = viewProvider.getPsi(language); - final FoldingBuilder foldingBuilder = LanguageFolding.INSTANCE.forLanguage(language); - if (psi != null) { - for (FoldingDescriptor descriptor : LanguageFolding.buildFoldingDescriptors(foldingBuilder, psi, document, false)) { - CustomFoldingBuilder customFoldingBuilder = getCustomFoldingBuilder(foldingBuilder, descriptor); - if (customFoldingBuilder != null) { - if (customFoldingBuilder.isCustomRegionStart(descriptor.getElement())) { - foldingDescriptors.add(descriptor); - } - } - } - } - } - } - return foldingDescriptors; - } - - private static Collection orderByPosition(Collection descriptors) { - List sorted = new ArrayList(descriptors.size()); - sorted.addAll(descriptors); - Collections.sort(sorted, new Comparator() { - @Override - public int compare(FoldingDescriptor descriptor1, FoldingDescriptor descriptor2) { - int pos1 = descriptor1.getElement().getTextRange().getStartOffset(); - int pos2 = descriptor2.getElement().getTextRange().getStartOffset(); - return pos1 - pos2; - } - }); - return sorted; - } - - private void createUIComponents() { - myRegionsList = new JBList(); - myScrollPane = new JBScrollPane(myRegionsList); - } - - @Nullable - private static CustomFoldingBuilder getCustomFoldingBuilder(FoldingBuilder builder, FoldingDescriptor descriptor) { - if (builder instanceof CustomFoldingBuilder) return (CustomFoldingBuilder)builder; - FoldingBuilder originalBuilder = descriptor.getElement().getUserData(CompositeFoldingBuilder.FOLDING_BUILDER); - if (originalBuilder instanceof CustomFoldingBuilder) return (CustomFoldingBuilder)originalBuilder; - return null; - } - - - private static class MyListModel extends DefaultListModel { - private MyListModel(Collection descriptors) { - for (FoldingDescriptor descriptor : descriptors) { - super.addElement(new MyFoldingDescriptorWrapper(descriptor)); - } - } - } - - private static class MyFoldingDescriptorWrapper { - private final @NotNull FoldingDescriptor myDescriptor; - - private MyFoldingDescriptorWrapper(@NotNull FoldingDescriptor descriptor) { - myDescriptor = descriptor; - } - - @NotNull - public FoldingDescriptor getDescriptor() { - return myDescriptor; - } - - @Nullable - @Override - public String toString() { - return myDescriptor.getPlaceholderText(); - } - } - - @Nullable - public PsiElement getNavigationElement() { - Object selection = myRegionsList.getSelectedValue(); - if (selection instanceof MyFoldingDescriptorWrapper) { - return ((MyFoldingDescriptorWrapper)selection).getDescriptor().getElement().getPsi(); - } - return null; - } -} diff --git a/platform/lang-impl/src/com/intellij/openapi/components/impl/stores/ModuleStoreImpl.java b/platform/lang-impl/src/com/intellij/openapi/components/impl/stores/ModuleStoreImpl.java index e35319819e32..216f368d4a1d 100644 --- a/platform/lang-impl/src/com/intellij/openapi/components/impl/stores/ModuleStoreImpl.java +++ b/platform/lang-impl/src/com/intellij/openapi/components/impl/stores/ModuleStoreImpl.java @@ -29,25 +29,26 @@ import com.intellij.openapi.startup.StartupManager; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.ArrayUtil; +import com.intellij.util.PathUtilRt; import gnu.trove.THashMap; import org.jdom.Attribute; import org.jdom.Element; -import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.File; import java.io.IOException; -import java.util.*; +import java.util.Arrays; +import java.util.Collection; +import java.util.Map; +import java.util.Set; public class ModuleStoreImpl extends BaseFileConfigurableStoreImpl implements IModuleStore { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.components.impl.stores.ModuleStoreImpl"); - @NonNls private static final String MODULE_FILE_MACRO = "MODULE_FILE"; private final ModuleImpl myModule; - public static final String DEFAULT_STATE_STORAGE = "$" + MODULE_FILE_MACRO + "$"; - + public static final String DEFAULT_STATE_STORAGE = "$MODULE_FILE$"; @SuppressWarnings({"UnusedDeclaration"}) public ModuleStoreImpl(final ComponentManagerImpl componentManager, final ModuleImpl module) { @@ -56,8 +57,8 @@ public class ModuleStoreImpl extends BaseFileConfigurableStoreImpl implements IM } @Override - protected XmlElementStorage getMainStorage() { - final XmlElementStorage storage = (XmlElementStorage)getStateStorageManager().getFileStateStorage(DEFAULT_STATE_STORAGE); + protected FileBasedStorage getMainStorage() { + FileBasedStorage storage = (FileBasedStorage)getStateStorageManager().getStateStorage(DEFAULT_STATE_STORAGE, RoamingType.PER_USER); assert storage != null; return storage; } @@ -116,8 +117,8 @@ public class ModuleStoreImpl extends BaseFileConfigurableStoreImpl implements IM } @Override - public void load(@NotNull final Element rootElement) throws IOException { - super.load(rootElement); + public void load(@NotNull Element rootElement, @Nullable PathMacroSubstitutor pathMacroSubstitutor, boolean intern) { + super.load(rootElement, pathMacroSubstitutor, intern); for (Attribute attribute : rootElement.getAttributes()) { myOptions.put(attribute.getName(), attribute.getValue()); @@ -132,8 +133,7 @@ public class ModuleStoreImpl extends BaseFileConfigurableStoreImpl implements IM @Override @NotNull protected Element save() { - final Element root = super.save(); - + Element root = super.save(); myOptions.put(VERSION_OPTION, Integer.toString(myVersion)); String[] options = ArrayUtil.toStringArray(myOptions.keySet()); Arrays.sort(options); @@ -187,31 +187,25 @@ public class ModuleStoreImpl extends BaseFileConfigurableStoreImpl implements IM LocalFileSystem.getInstance().refreshAndFindFileByPath(path); final StateStorageManager storageManager = getStateStorageManager(); storageManager.clearStateStorage(DEFAULT_STATE_STORAGE); - storageManager.addMacro(MODULE_FILE_MACRO, path); + storageManager.addMacro(DEFAULT_STATE_STORAGE, path); } @Override @Nullable public VirtualFile getModuleFile() { - final FileBasedStorage storage = (FileBasedStorage)getStateStorageManager().getFileStateStorage(DEFAULT_STATE_STORAGE); - assert storage != null; - return storage.getVirtualFile(); + return getMainStorage().getVirtualFile(); } @Override @NotNull public String getModuleFilePath() { - final FileBasedStorage storage = (FileBasedStorage)getStateStorageManager().getFileStateStorage(DEFAULT_STATE_STORAGE); - assert storage != null; - return storage.getFilePath(); + return getMainStorage().getFilePath(); } @Override @NotNull public String getModuleFileName() { - final FileBasedStorage storage = (FileBasedStorage)getStateStorageManager().getFileStateStorage(DEFAULT_STATE_STORAGE); - assert storage != null; - return storage.getFileName(); + return PathUtilRt.getFileName(getMainStorage().getFilePath()); } @Override diff --git a/platform/lang-impl/src/com/intellij/openapi/components/impl/stores/ProjectWithModulesStoreImpl.java b/platform/lang-impl/src/com/intellij/openapi/components/impl/stores/ProjectWithModulesStoreImpl.java index eb03fa86fdab..c109c8c0e14d 100644 --- a/platform/lang-impl/src/com/intellij/openapi/components/impl/stores/ProjectWithModulesStoreImpl.java +++ b/platform/lang-impl/src/com/intellij/openapi/components/impl/stores/ProjectWithModulesStoreImpl.java @@ -25,10 +25,10 @@ import com.intellij.openapi.module.impl.ModuleImpl; import com.intellij.openapi.project.impl.ProjectImpl; import com.intellij.openapi.util.Pair; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.util.io.fs.IFile; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -102,15 +102,13 @@ public class ProjectWithModulesStoreImpl extends ProjectStoreImpl { @NotNull @Override - public List getAllStorageFiles(final boolean includingSubStructures) { - final List result = super.getAllStorageFiles(includingSubStructures); - + public List getAllStorageFiles(final boolean includingSubStructures) { + List result = super.getAllStorageFiles(includingSubStructures); if (includingSubStructures) { for (SaveSession moduleSaveSession : myModuleSaveSessions) { result.addAll(moduleSaveSession.getAllStorageFiles(true)); } } - return result; } @@ -172,10 +170,9 @@ public class ProjectWithModulesStoreImpl extends ProjectStoreImpl { } @Override - protected void collectSubfilesToSave(final List result) throws IOException { + protected void collectSubFilesToSave(final List result) throws IOException { for (SaveSession moduleSaveSession : myModuleSaveSessions) { - final List moduleFiles = moduleSaveSession.getAllStorageFilesToSave(true); - result.addAll(moduleFiles); + result.addAll(moduleSaveSession.getAllStorageFilesToSave(true)); } } } diff --git a/platform/lang-impl/src/com/intellij/openapi/fileEditor/impl/PsiAwareFileEditorManagerImpl.java b/platform/lang-impl/src/com/intellij/openapi/fileEditor/impl/PsiAwareFileEditorManagerImpl.java index 7713734d07de..eaed20f2960d 100644 --- a/platform/lang-impl/src/com/intellij/openapi/fileEditor/impl/PsiAwareFileEditorManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/openapi/fileEditor/impl/PsiAwareFileEditorManagerImpl.java @@ -102,8 +102,9 @@ public class PsiAwareFileEditorManagerImpl extends FileEditorManagerImpl { return myProblemSolver.isProblemFile(file); } + @NotNull @Override - public String getFileTooltipText(final VirtualFile file) { + public String getFileTooltipText(@NotNull final VirtualFile file) { final StringBuilder tooltipText = new StringBuilder(); final Module module = ModuleUtilCore.findModuleForFile(file, getProject()); if (module != null) { @@ -116,7 +117,7 @@ public class PsiAwareFileEditorManagerImpl extends FileEditorManagerImpl { } @Override - protected Editor getOpenedEditor(final Editor editor, final boolean focusEditor) { + protected Editor getOpenedEditor(@NotNull final Editor editor, final boolean focusEditor) { PsiDocumentManager documentManager = PsiDocumentManager.getInstance(getProject()); Document document = editor.getDocument(); PsiFile psiFile = documentManager.getPsiFile(document); @@ -200,7 +201,7 @@ public class PsiAwareFileEditorManagerImpl extends FileEditorManagerImpl { updateFile(file); } - private void updateFile(final VirtualFile file) { + private void updateFile(@NotNull VirtualFile file) { queueUpdateFile(file); } } diff --git a/platform/lang-impl/src/com/intellij/openapi/fileEditor/impl/TestEditorManagerImpl.java b/platform/lang-impl/src/com/intellij/openapi/fileEditor/impl/TestEditorManagerImpl.java index 79ac777a6fd8..958daee0098a 100644 --- a/platform/lang-impl/src/com/intellij/openapi/fileEditor/impl/TestEditorManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/openapi/fileEditor/impl/TestEditorManagerImpl.java @@ -38,6 +38,7 @@ import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiManager; import com.intellij.testFramework.LightVirtualFile; +import com.intellij.util.IncorrectOperationException; import com.intellij.util.containers.HashMap; import org.jdom.Element; import org.jetbrains.annotations.NonNls; @@ -195,7 +196,7 @@ public class TestEditorManagerImpl extends FileEditorManagerEx implements Applic return openTextEditor(descriptor, false); } - private FileEditorProvider getProvider(FileEditor editor) { + private static FileEditorProvider getProvider(FileEditor editor) { return new FileEditorProvider() { @Override public boolean accept(@NotNull Project project, @NotNull VirtualFile file) { @@ -205,18 +206,17 @@ public class TestEditorManagerImpl extends FileEditorManagerEx implements Applic @Override @NotNull public FileEditor createEditor(@NotNull Project project, @NotNull VirtualFile file) { - return null; + throw new IncorrectOperationException(); } @Override public void disposeEditor(@NotNull FileEditor editor) { - //Disposer.dispose(editor); } @Override @NotNull public FileEditorState readState(@NotNull Element sourceElement, @NotNull Project project, @NotNull VirtualFile file) { - return null; + throw new IncorrectOperationException(); } @Override @@ -233,7 +233,7 @@ public class TestEditorManagerImpl extends FileEditorManagerEx implements Applic @Override @NotNull public FileEditorPolicy getPolicy() { - return null; + throw new IncorrectOperationException(); } }; } @@ -482,7 +482,7 @@ public class TestEditorManagerImpl extends FileEditorManagerEx implements Applic @NotNull @Override public EditorsSplitters getSplitters() { - return null; + throw new IncorrectOperationException(); } @NotNull @@ -492,6 +492,6 @@ public class TestEditorManagerImpl extends FileEditorManagerEx implements Applic } @Override - public void setSelectedEditor(@NotNull VirtualFile file, String fileEditorProviderId) { + public void setSelectedEditor(@NotNull VirtualFile file, @NotNull String fileEditorProviderId) { } } diff --git a/platform/lang-impl/src/com/intellij/openapi/projectRoots/impl/ProjectJdkTableImpl.java b/platform/lang-impl/src/com/intellij/openapi/projectRoots/impl/ProjectJdkTableImpl.java index d5becac99ede..d36fe1b1e05d 100644 --- a/platform/lang-impl/src/com/intellij/openapi/projectRoots/impl/ProjectJdkTableImpl.java +++ b/platform/lang-impl/src/com/intellij/openapi/projectRoots/impl/ProjectJdkTableImpl.java @@ -41,12 +41,8 @@ import java.util.List; import java.util.Map; @State( - name="ProjectJdkTable", - roamingType = RoamingType.DISABLED, - storages= { - @Storage( - file = StoragePathMacros.APP_CONFIG + "/jdk.table.xml" - )} + name = "ProjectJdkTable", + storages = {@Storage(file = StoragePathMacros.APP_CONFIG + "/jdk.table.xml", roamingType = RoamingType.DISABLED)} ) public class ProjectJdkTableImpl extends ProjectJdkTable implements PersistentStateComponent, ExportableComponent { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.projectRoots.impl.ProjectJdkTableImpl"); diff --git a/platform/lang-impl/src/com/intellij/openapi/roots/impl/libraries/ExportableApplicationLibraryTable.java b/platform/lang-impl/src/com/intellij/openapi/roots/impl/libraries/ExportableApplicationLibraryTable.java index 4be66eedde76..c3bed20a5052 100644 --- a/platform/lang-impl/src/com/intellij/openapi/roots/impl/libraries/ExportableApplicationLibraryTable.java +++ b/platform/lang-impl/src/com/intellij/openapi/roots/impl/libraries/ExportableApplicationLibraryTable.java @@ -27,10 +27,7 @@ import java.io.File; */ @State( name = "libraryTable", - roamingType = RoamingType.DISABLED, - storages = { - @Storage( file = StoragePathMacros.APP_CONFIG + "/applicationLibraries.xml") - } + storages = {@Storage(file = StoragePathMacros.APP_CONFIG + "/applicationLibraries.xml", roamingType = RoamingType.DISABLED)} ) public class ExportableApplicationLibraryTable extends ApplicationLibraryTable implements ExportableComponent { @Override diff --git a/platform/lang-impl/src/com/intellij/openapi/roots/impl/storage/ClasspathStorage.java b/platform/lang-impl/src/com/intellij/openapi/roots/impl/storage/ClasspathStorage.java index b1f034d2dcb2..36e344ff0acc 100644 --- a/platform/lang-impl/src/com/intellij/openapi/roots/impl/storage/ClasspathStorage.java +++ b/platform/lang-impl/src/com/intellij/openapi/roots/impl/storage/ClasspathStorage.java @@ -39,8 +39,6 @@ import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.*; import com.intellij.openapi.vfs.tracker.VirtualFileTracker; import com.intellij.util.containers.ContainerUtil; -import com.intellij.util.io.fs.FileSystem; -import com.intellij.util.io.fs.IFile; import com.intellij.util.messages.MessageBus; import org.jdom.Element; import org.jetbrains.annotations.Nls; @@ -104,7 +102,7 @@ public class ClasspathStorage implements StateStorage { @Override @Nullable - public T getState(final Object component, final String componentName, Class stateClass, @Nullable T mergeInto) + public T getState(final Object component, @NotNull final String componentName, Class stateClass, @Nullable T mergeInto) throws StateStorageException { assert component instanceof ModuleRootManager; assert componentName.equals("NewModuleRootManager"); @@ -144,7 +142,7 @@ public class ClasspathStorage implements StateStorage { } @Override - public boolean hasState(final Object component, final String componentName, final Class aClass, final boolean reloadData) + public boolean hasState(final Object component, @NotNull final String componentName, final Class aClass, final boolean reloadData) throws StateStorageException { return true; } @@ -192,10 +190,9 @@ public class ClasspathStorage implements StateStorage { return session; } - private static void convert2Io(List list, ArrayList virtualFiles) { + private static void convert2Io(List list, ArrayList virtualFiles) { for (VirtualFile virtualFile : virtualFiles) { - final File ioFile = VfsUtilCore.virtualToIoFile(virtualFile); - list.add(FileSystem.FILE_SYSTEM.createFile(ioFile.getAbsolutePath())); + list.add(VfsUtilCore.virtualToIoFile(virtualFile)); } } @@ -384,12 +381,12 @@ public class ClasspathStorage implements StateStorage { } @Override - public Set getClasspath(final ModifiableRootModel model, final Element element) throws IOException, InvalidDataException { + public Set getClasspath(final ModifiableRootModel model, final Element element) throws InvalidDataException { throw new InvalidDataException(getDescription()); } @Override - public void setClasspath(ModuleRootModel model) throws IOException, WriteExternalException { + public void setClasspath(ModuleRootModel model) throws WriteExternalException { throw new WriteExternalException(getDescription()); } }; @@ -426,9 +423,9 @@ public class ClasspathStorage implements StateStorage { @NotNull @Override - public Collection getStorageFilesToSave() throws StateStorageException { + public Collection getStorageFilesToSave() throws StateStorageException { if (needsSave()) { - final List list = new ArrayList(); + final List list = new ArrayList(); final ArrayList virtualFiles = new ArrayList(); getFileSet().listModifiedFiles(virtualFiles); convert2Io(list, virtualFiles); @@ -441,9 +438,9 @@ public class ClasspathStorage implements StateStorage { @NotNull @Override - public List getAllStorageFiles() { - final List list = new ArrayList(); - final ArrayList virtualFiles = new ArrayList(); + public List getAllStorageFiles() { + List list = new ArrayList(); + ArrayList virtualFiles = new ArrayList(); getFileSet().listFiles(virtualFiles); convert2Io(list, virtualFiles); return list; diff --git a/platform/lang-impl/src/com/intellij/openapi/roots/libraries/ui/impl/RootDetectionUtil.java b/platform/lang-impl/src/com/intellij/openapi/roots/libraries/ui/impl/RootDetectionUtil.java index 5a3efe1a31b7..ca273ba2373d 100644 --- a/platform/lang-impl/src/com/intellij/openapi/roots/libraries/ui/impl/RootDetectionUtil.java +++ b/platform/lang-impl/src/com/intellij/openapi/roots/libraries/ui/impl/RootDetectionUtil.java @@ -70,7 +70,7 @@ public class RootDetectionUtil { try { for (VirtualFile rootCandidate : rootCandidates) { final Collection roots = detector.detectRoots(rootCandidate, indicator); - if (!roots.isEmpty() && allRootsHaveOneTypeAndEqualTo(roots, rootCandidate)) { + if (!roots.isEmpty() && allRootsHaveOneTypeAndEqualToOrAreDirectParentOf(roots, rootCandidate)) { for (DetectedLibraryRoot root : roots) { final LibraryRootType libraryRootType = root.getTypes().get(0); result.add(new OrderRoot(root.getFile(), libraryRootType.getType(), libraryRootType.isJarDirectory())); @@ -81,7 +81,7 @@ public class RootDetectionUtil { final HashMap names = new HashMap(); for (LibraryRootType type : root.getTypes()) { final String typeName = detector.getRootTypeName(type); - LOG.assertTrue(typeName != null, "Unexpected root type " + type.getType().name() + (type.isJarDirectory() ? " (jar directory)" : "") + ", detectors: " + detector); + LOG.assertTrue(typeName != null, "Unexpected root type " + type.getType().name() + (type.isJarDirectory() ? " (JAR directory)" : "") + ", detectors: " + detector); names.put(type, typeName); } suggestedRoots.add(new SuggestedChildRootInfo(rootCandidate, root, names)); @@ -141,9 +141,9 @@ public class RootDetectionUtil { return result; } - private static boolean allRootsHaveOneTypeAndEqualTo(Collection roots, VirtualFile candidate) { + private static boolean allRootsHaveOneTypeAndEqualToOrAreDirectParentOf(Collection roots, VirtualFile candidate) { for (DetectedLibraryRoot root : roots) { - if (root.getTypes().size() > 1 || !root.getFile().equals(candidate)) { + if (root.getTypes().size() > 1 || !root.getFile().equals(candidate) && !root.getFile().equals(candidate.getParent())) { return false; } } diff --git a/platform/lang-impl/src/com/intellij/openapi/roots/ui/configuration/ModulesAlphaComparator.java b/platform/lang-impl/src/com/intellij/openapi/roots/ui/configuration/ModulesAlphaComparator.java index 8a561e4003b4..61a5c71861c3 100644 --- a/platform/lang-impl/src/com/intellij/openapi/roots/ui/configuration/ModulesAlphaComparator.java +++ b/platform/lang-impl/src/com/intellij/openapi/roots/ui/configuration/ModulesAlphaComparator.java @@ -29,8 +29,9 @@ public class ModulesAlphaComparator implements Comparator{ @Override public int compare(Module module1, Module module2) { - final String name1 = module1.getName(); - final String name2 = module2.getName(); - return name1.compareToIgnoreCase(name2); + if (module1 == null && module2 == null) return 0; + if (module1 == null && module2 != null) return -1; + if (module1 != null && module2 == null) return 1; + return module1.getName().compareToIgnoreCase(module2.getName()); } } diff --git a/platform/lang-impl/src/com/intellij/openapi/roots/ui/configuration/projectRoot/ProjectSdksModel.java b/platform/lang-impl/src/com/intellij/openapi/roots/ui/configuration/projectRoot/ProjectSdksModel.java index e4c690ec05ed..87df147a745f 100644 --- a/platform/lang-impl/src/com/intellij/openapi/roots/ui/configuration/projectRoot/ProjectSdksModel.java +++ b/platform/lang-impl/src/com/intellij/openapi/roots/ui/configuration/projectRoot/ProjectSdksModel.java @@ -86,6 +86,15 @@ public class ProjectSdksModel implements SdkModel { } public void reset(@Nullable Project project) { + resetSdkModel(); + if (project != null) { + myProjectSdk = findSdk(ProjectRootManager.getInstance(project).getProjectSdkName()); + } + myModified = false; + myInitialized = true; + } + + private void resetSdkModel() { myProjectSdks.clear(); final Sdk[] projectSdks = ProjectJdkTable.getInstance().getAllJdks(); for (Sdk sdk : projectSdks) { @@ -96,11 +105,6 @@ public class ProjectSdksModel implements SdkModel { LOG.error(e); } } - if (project != null) { - myProjectSdk = findSdk(ProjectRootManager.getInstance(project).getProjectSdkName()); - } - myModified = false; - myInitialized = true; } public void disposeUIResources() { @@ -154,6 +158,7 @@ public class ProjectSdksModel implements SdkModel { for (Sdk originalJdk : itemsInTable) { final Sdk modifiedJdk = myProjectSdks.get(originalJdk); LOG.assertTrue(modifiedJdk != null); + LOG.assertTrue(originalJdk != modifiedJdk); jdkTable.updateJdk(originalJdk, modifiedJdk); } // Add new items to table @@ -166,6 +171,7 @@ public class ProjectSdksModel implements SdkModel { } } }); + resetSdkModel(); myModified = false; } diff --git a/platform/lang-impl/src/com/intellij/openapi/util/registry/RegistryUi.java b/platform/lang-impl/src/com/intellij/openapi/util/registry/RegistryUi.java index 65f2d758fcfd..a30ccd677682 100644 --- a/platform/lang-impl/src/com/intellij/openapi/util/registry/RegistryUi.java +++ b/platform/lang-impl/src/com/intellij/openapi/util/registry/RegistryUi.java @@ -471,9 +471,10 @@ public class RegistryUi implements Disposable { public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { myValue = ((MyTableModel)table.getModel()).getRegistryValue(row); if (myValue.asColor(null) != null) { - final Color color = ColorChooser.chooseColor(table, "Choose color", ((RegistryValue)value).asColor(Color.WHITE)); + final Color color = ColorChooser.chooseColor(table, "Choose color", myValue.asColor(Color.WHITE)); if (color != null) { myValue.setValue(color.getRed() + "," + color.getGreen() + "," + color.getBlue()); + keyChanged(myValue.getKey()); } return null; } else if (myValue.isBoolean()) { diff --git a/platform/lang-impl/src/com/intellij/openapi/wm/impl/status/TogglePopupHintsPanel.java b/platform/lang-impl/src/com/intellij/openapi/wm/impl/status/TogglePopupHintsPanel.java index b30661e6e840..a83f0b5703da 100644 --- a/platform/lang-impl/src/com/intellij/openapi/wm/impl/status/TogglePopupHintsPanel.java +++ b/platform/lang-impl/src/com/intellij/openapi/wm/impl/status/TogglePopupHintsPanel.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -125,21 +125,21 @@ public class TogglePopupHintsPanel extends EditorBasedWidget implements StatusBa if (isStateChangeable(file)) { if (PowerSaveMode.isEnabled()) { myCurrentIcon = AllIcons.Ide.HectorNo; - myToolTipText = "Code analysis is disabled in power save mode. "; + myToolTipText = "Code analysis is disabled in power save mode.\n"; } else if (HighlightingLevelManager.getInstance(myProject).shouldInspect(file)) { myCurrentIcon = AllIcons.Ide.HectorOn; myToolTipText = "Current inspection profile: " + InspectionProjectProfileManager.getInstance(file.getProject()).getInspectionProfile().getName() + - ". "; + ".\n"; } else if (HighlightingLevelManager.getInstance(myProject).shouldHighlight(file)) { myCurrentIcon = AllIcons.Ide.HectorSyntax; - myToolTipText = "Highlighting level is: Syntax. "; + myToolTipText = "Highlighting level is: Syntax.\n"; } else { myCurrentIcon = AllIcons.Ide.HectorOff; - myToolTipText = "Inspections are off. "; + myToolTipText = "Inspections are off.\n"; } myToolTipText += UIBundle.message("popup.hints.panel.click.to.configure.highlighting.tooltip.text"); } diff --git a/platform/lang-impl/src/com/intellij/platform/templates/github/DownloadUtil.java b/platform/lang-impl/src/com/intellij/platform/templates/github/DownloadUtil.java index bbffc7c98ba4..02ddd0b64608 100644 --- a/platform/lang-impl/src/com/intellij/platform/templates/github/DownloadUtil.java +++ b/platform/lang-impl/src/com/intellij/platform/templates/github/DownloadUtil.java @@ -1,5 +1,6 @@ package com.intellij.platform.templates.github; +import com.intellij.openapi.application.ex.ApplicationInfoEx; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; @@ -19,7 +20,6 @@ import java.net.HttpURLConnection; import java.net.URLConnection; import java.util.Locale; import java.util.concurrent.Callable; -import java.util.concurrent.TimeUnit; /** * @author Sergey Simonchik @@ -176,6 +176,7 @@ public class DownloadUtil { URLConnection urlConnection = HttpConfigurable.getInstance().openConnection(location); HttpURLConnection httpURLConnection = ObjectUtils.tryCast(urlConnection, HttpURLConnection.class); try { + urlConnection.setRequestProperty("User-Agent", ApplicationInfoEx.getInstanceEx().getFullApplicationName()); urlConnection.connect(); InputStream in = urlConnection.getInputStream(); int contentLength = urlConnection.getContentLength(); diff --git a/platform/lang-impl/src/com/intellij/profile/codeInspection/InspectionProfileManagerImpl.java b/platform/lang-impl/src/com/intellij/profile/codeInspection/InspectionProfileManagerImpl.java index de8ee09198c1..08c9e9f0d518 100644 --- a/platform/lang-impl/src/com/intellij/profile/codeInspection/InspectionProfileManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/profile/codeInspection/InspectionProfileManagerImpl.java @@ -42,13 +42,17 @@ import com.intellij.openapi.options.*; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectManager; import com.intellij.openapi.ui.Messages; -import com.intellij.openapi.util.*; +import com.intellij.openapi.util.Comparing; +import com.intellij.openapi.util.InvalidDataException; +import com.intellij.openapi.util.JDOMExternalizable; +import com.intellij.openapi.util.WriteExternalException; import com.intellij.profile.Profile; import com.intellij.util.ArrayUtil; import com.intellij.util.ui.UIUtil; import org.jdom.Document; import org.jdom.Element; import org.jdom.JDOMException; +import org.jdom.Parent; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.TestOnly; @@ -94,7 +98,7 @@ public class InspectionProfileManagerImpl extends InspectionProfileManager imple @Override - public Document writeScheme(@NotNull final InspectionProfileImpl scheme) throws WriteExternalException { + public Element writeScheme(@NotNull final InspectionProfileImpl scheme) throws WriteExternalException { return scheme.saveToDocument(); } diff --git a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/LevelChooser.java b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/LevelChooser.java deleted file mode 100644 index 5bc792545c96..000000000000 --- a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/LevelChooser.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright 2000-2012 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * User: anna - * Date: 19-Apr-2009 - */ -package com.intellij.profile.codeInspection.ui; - -import com.intellij.codeHighlighting.HighlightDisplayLevel; -import com.intellij.codeInsight.daemon.impl.HighlightInfoType; -import com.intellij.codeInsight.daemon.impl.SeverityRegistrar; -import com.intellij.codeInsight.daemon.impl.SeverityUtil; -import com.intellij.codeInspection.ex.SeverityEditorDialog; -import com.intellij.ui.ListCellRendererWrapper; -import com.intellij.lang.annotation.HighlightSeverity; -import com.intellij.ui.ComboboxWithBrowseButton; -import org.jetbrains.annotations.NotNull; - -import javax.swing.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.util.TreeSet; - -public class LevelChooser extends ComboboxWithBrowseButton { - public LevelChooser(final SeverityRegistrar severityRegistrar) { - final JComboBox comboBox = getComboBox(); - final DefaultComboBoxModel model = new DefaultComboBoxModel(); - comboBox.setModel(model); - fillModel(model, severityRegistrar); - getButton().setToolTipText("Edit severities (" + getButton().getToolTipText(null) + ")"); - - comboBox.setRenderer(new ListCellRendererWrapper() { - @Override - public void customize(final JList list, final HighlightSeverity value, final int index, final boolean selected, final boolean hasFocus) { - if (value != null) { - setText(SingleInspectionProfilePanel.renderSeverity(value)); - setIcon(HighlightDisplayLevel.find(value).getIcon()); - } - } - }); - - addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - final SeverityEditorDialog dlg = - new SeverityEditorDialog(LevelChooser.this, (HighlightSeverity)getComboBox().getSelectedItem(), severityRegistrar); - dlg.show(); - if (dlg.isOK()) { - final Object item = getComboBox().getSelectedItem(); - fillModel(model, severityRegistrar); - final HighlightInfoType type = dlg.getSelectedType(); - if (type != null) { - getComboBox().setSelectedItem(type.getSeverity(null)); - } - else { - getComboBox().setSelectedItem(item); - } - } - } - }); - } - - private static void fillModel(DefaultComboBoxModel model, final SeverityRegistrar severityRegistrar) { - model.removeAllElements(); - final TreeSet severities = new TreeSet(severityRegistrar); - for (SeverityRegistrar.SeverityBasedTextAttributes type : SeverityUtil.getRegisteredHighlightingInfoTypes(severityRegistrar)) { - severities.add(type.getSeverity()); - } - severities.add(HighlightSeverity.ERROR); - severities.add(HighlightSeverity.WARNING); - severities.add(HighlightSeverity.WEAK_WARNING); - severities.add(HighlightSeverity.GENERIC_SERVER_ERROR_OR_WARNING); - for (HighlightSeverity severity : severities) { - model.addElement(severity); - } - } - - @NotNull - public HighlightDisplayLevel getLevel() { - HighlightSeverity severity = (HighlightSeverity)getComboBox().getSelectedItem(); - if (severity == null) return HighlightDisplayLevel.WARNING; - return HighlightDisplayLevel.find(severity); - } - - public void setLevel(HighlightDisplayLevel level) { - getComboBox().setSelectedItem(level.getSeverity()); - } -} \ No newline at end of file diff --git a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/SingleInspectionProfilePanel.java b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/SingleInspectionProfilePanel.java index 81d0bf651688..2320d059c146 100644 --- a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/SingleInspectionProfilePanel.java +++ b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/SingleInspectionProfilePanel.java @@ -1005,11 +1005,10 @@ public class SingleInspectionProfilePanel extends JPanel { northPanel.add(createTreeToolbarPanel().getComponent(), new GridBagConstraints(1, 0, 1, 1, 0.5, 1, GridBagConstraints.BASELINE_LEADING, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0)); treePanel.add(northPanel, BorderLayout.NORTH); - myMainSplitter = new Splitter(false); + myMainSplitter = new Splitter(false, myProperties.getFloat(VERTICAL_DIVIDER_PROPORTION, 0.5f), 0.01f, 0.99f); myMainSplitter.setFirstComponent(treePanel); myMainSplitter.setSecondComponent(myRightSplitter); myMainSplitter.setHonorComponentsMinimumSize(false); - myMainSplitter.setProportion(myProperties.getFloat(VERTICAL_DIVIDER_PROPORTION, 0.5f)); final JPanel panel = new JPanel(new BorderLayout()); panel.add(myMainSplitter, BorderLayout.CENTER); diff --git a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/inspectionsTree/InspectionsConfigTreeTable.java b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/inspectionsTree/InspectionsConfigTreeTable.java index 5607816ad13c..fe36408cf61a 100644 --- a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/inspectionsTree/InspectionsConfigTreeTable.java +++ b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/inspectionsTree/InspectionsConfigTreeTable.java @@ -220,7 +220,7 @@ public class InspectionsConfigTreeTable extends TreeTable { @Override public void setValueAt(final Object aValue, final Object node, final int column) { LOG.assertTrue(column == IS_ENABLED_COLUMN); - LOG.assertTrue(aValue != null); + LOG.assertTrue(aValue != null, "node = " + node); final boolean doEnable = (Boolean) aValue; for (final InspectionConfigTreeNode aNode : InspectionsAggregationUtil.getInspectionsNodes((InspectionConfigTreeNode) node)) { final String toolId = aNode.getKey().toString(); diff --git a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/table/ScopesAndSeveritiesTable.java b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/table/ScopesAndSeveritiesTable.java index 385b764af027..58e35083a247 100644 --- a/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/table/ScopesAndSeveritiesTable.java +++ b/platform/lang-impl/src/com/intellij/profile/codeInspection/ui/table/ScopesAndSeveritiesTable.java @@ -357,6 +357,9 @@ public class ScopesAndSeveritiesTable extends JBTable { } else if (columnIndex == SCOPE_ENABLED_COLUMN) { final NamedScope scope = getScope(rowIndex); + if (scope == null) { + return; + } if ((Boolean)value) { if (rowIndex == lastRowIndex()) { myInspectionProfile.enableToolsByDefault(myKeyNames, myProject); @@ -381,7 +384,7 @@ public class ScopesAndSeveritiesTable extends JBTable { @Override public void removeRow(final int idx) { if (idx != lastRowIndex()) { - myInspectionProfile.removeScopes(myKeyNames, getScope(idx), myProject); + myInspectionProfile.removeScopes(myKeyNames, getScopeName(idx), myProject); refreshAggregatedScopes(); myTableSettings.onScopeRemoved(getRowCount()); } diff --git a/platform/lang-impl/src/com/intellij/psi/impl/cache/impl/id/IdIndexImpl.java b/platform/lang-impl/src/com/intellij/psi/impl/cache/impl/id/IdIndexImpl.java index 537d997e5a50..1773a260a706 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/cache/impl/id/IdIndexImpl.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/cache/impl/id/IdIndexImpl.java @@ -15,6 +15,9 @@ */ package com.intellij.psi.impl.cache.impl.id; +import com.intellij.openapi.fileTypes.FileType; +import com.intellij.openapi.fileTypes.FileTypeRegistry; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.ThreadLocalCachedIntArray; import com.intellij.util.indexing.CustomInputsIndexFileBasedIndexExtension; import com.intellij.util.io.DataExternalizer; @@ -27,9 +30,35 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Comparator; public class IdIndexImpl extends IdIndex implements CustomInputsIndexFileBasedIndexExtension { private static final ThreadLocalCachedIntArray spareBufferLocal = new ThreadLocalCachedIntArray(); + private final FileTypeRegistry myFileTypeManager; + + public IdIndexImpl(FileTypeRegistry manager) { + myFileTypeManager = manager; + } + + @Override + public int getVersion() { + FileType[] types = myFileTypeManager.getRegisteredFileTypes(); + Arrays.sort(types, new Comparator() { + @Override + public int compare(FileType o1, FileType o2) { + return Comparing.compare(o1.getName(), o2.getName()); + } + }); + + int version = super.getVersion(); + for(FileType fileType:types) { + if (!isIndexable(fileType)) continue; + FileTypeIdIndexer indexer = IdIndexers.INSTANCE.forFileType(fileType); + if (indexer == null) continue; + version = version * 31 + (indexer.getVersion() ^ indexer.getClass().getName().hashCode()); + } + return version; + } @NotNull @Override diff --git a/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/CodeStyleSchemeImpl.java b/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/CodeStyleSchemeImpl.java index 99320b44c5e6..59f3767c3a3b 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/CodeStyleSchemeImpl.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/CodeStyleSchemeImpl.java @@ -161,12 +161,11 @@ public class CodeStyleSchemeImpl implements JDOMExternalizable, CodeStyleScheme, } } - public Document saveToDocument() throws WriteExternalException { + public Element saveToDocument() throws WriteExternalException { Element newElement = new Element(CODE_SCHEME); newElement.setAttribute(NAME, getName()); writeExternal(newElement); - - return new Document(newElement); + return newElement; } @Override diff --git a/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/CodeStyleSchemesImpl.java b/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/CodeStyleSchemesImpl.java index b12dee17e9fd..87914f151df3 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/CodeStyleSchemesImpl.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/source/codeStyle/CodeStyleSchemesImpl.java @@ -29,6 +29,7 @@ import com.intellij.psi.codeStyle.CodeStyleScheme; import com.intellij.psi.codeStyle.CodeStyleSchemes; import org.jdom.Document; import org.jdom.JDOMException; +import org.jdom.Parent; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -57,7 +58,7 @@ public abstract class CodeStyleSchemesImpl extends CodeStyleSchemes implements E } @Override - public Document writeScheme(@NotNull final CodeStyleSchemeImpl scheme) throws WriteExternalException { + public Parent writeScheme(@NotNull final CodeStyleSchemeImpl scheme) throws WriteExternalException { return scheme.saveToDocument(); } diff --git a/platform/lang-impl/src/com/intellij/psi/search/scope/packageSet/PackageSetFactoryImpl.java b/platform/lang-impl/src/com/intellij/psi/search/scope/packageSet/PackageSetFactoryImpl.java index 1a3e45d72df7..10f1ec94555e 100644 --- a/platform/lang-impl/src/com/intellij/psi/search/scope/packageSet/PackageSetFactoryImpl.java +++ b/platform/lang-impl/src/com/intellij/psi/search/scope/packageSet/PackageSetFactoryImpl.java @@ -137,8 +137,7 @@ public class PackageSetFactoryImpl extends PackageSetFactory { else if (myLexer.getTokenType() == ScopeTokenTypes.COLON) { pattern.append(":"); } else { - error(AnalysisScopeBundle.message("error.packageset.token.expectations", getTokenText())); - break; + pattern.append(getTokenText()); } myLexer.advance(); } diff --git a/platform/lang-impl/src/com/intellij/psi/stubs/StubIndexImpl.java b/platform/lang-impl/src/com/intellij/psi/stubs/StubIndexImpl.java index 493d1d6cd519..ffc7de7c9b12 100644 --- a/platform/lang-impl/src/com/intellij/psi/stubs/StubIndexImpl.java +++ b/platform/lang-impl/src/com/intellij/psi/stubs/StubIndexImpl.java @@ -63,11 +63,7 @@ import java.util.concurrent.locks.Lock; @State( name = "FileBasedIndex", - roamingType = RoamingType.DISABLED, - storages = { - @Storage( - file = StoragePathMacros.APP_CONFIG + "/stubIndex.xml") - } + storages = {@Storage(file = StoragePathMacros.APP_CONFIG + "/stubIndex.xml", roamingType = RoamingType.DISABLED)} ) public class StubIndexImpl extends StubIndex implements ApplicationComponent, PersistentStateComponent { private static final AtomicReference ourForcedClean = new AtomicReference(null); @@ -109,7 +105,7 @@ public class StubIndexImpl extends StubIndex implements ApplicationComponent, Pe myStubProcessingHelper = new StubProcessingHelper(fileBasedIndex); } - + @Nullable public static StubIndexImpl getInstanceOrInvalidate() { if (ourForcedClean.compareAndSet(null, Boolean.TRUE)) { diff --git a/platform/lang-impl/src/com/intellij/refactoring/actions/RefactoringQuickListPopupAction.java b/platform/lang-impl/src/com/intellij/refactoring/actions/RefactoringQuickListPopupAction.java index 159279570cf2..f166c5d08981 100644 --- a/platform/lang-impl/src/com/intellij/refactoring/actions/RefactoringQuickListPopupAction.java +++ b/platform/lang-impl/src/com/intellij/refactoring/actions/RefactoringQuickListPopupAction.java @@ -98,7 +98,8 @@ public class RefactoringQuickListPopupAction extends QuickSwitchSchemeAction { @Override public void update(AnActionEvent e) { super.update(e); - e.getPresentation().setVisible(e.getPlace() == ActionPlaces.MAIN_MENU || e.getPlace() == ActionPlaces.ACTION_PLACE_QUICK_LIST_POPUP_ACTION); + e.getPresentation().setVisible( + ActionPlaces.isMainMenuOrActionSearch(e.getPlace()) || ActionPlaces.ACTION_PLACE_QUICK_LIST_POPUP_ACTION.equals(e.getPlace())); } @Override diff --git a/platform/lang-impl/src/com/intellij/refactoring/rename/RenameUtil.java b/platform/lang-impl/src/com/intellij/refactoring/rename/RenameUtil.java index 59595bcc81d0..490efeea1734 100644 --- a/platform/lang-impl/src/com/intellij/refactoring/rename/RenameUtil.java +++ b/platform/lang-impl/src/com/intellij/refactoring/rename/RenameUtil.java @@ -240,10 +240,17 @@ public class RenameUtil { for (UsageInfo usage : usages) { final PsiReference ref = usage.getReference(); if (ref instanceof BindablePsiReference) { - try { - ref.bindToElement(namedElement); + boolean fallback = true; + if (!(ref instanceof FragmentaryPsiReference + && ((FragmentaryPsiReference)ref).isFragmentOnlyRename())) { + try { + ref.bindToElement(namedElement); + fallback = false; + } + catch (IncorrectOperationException ignored) { + } } - catch (IncorrectOperationException e) {//fall back to old scheme + if (fallback) {//fall back to old scheme ref.handleElementRename(newName); } } diff --git a/platform/lang-impl/src/com/intellij/tools/BaseExternalToolsGroup.java b/platform/lang-impl/src/com/intellij/tools/BaseExternalToolsGroup.java index 4d0d2867e387..7dd25a6ec7dd 100644 --- a/platform/lang-impl/src/com/intellij/tools/BaseExternalToolsGroup.java +++ b/platform/lang-impl/src/com/intellij/tools/BaseExternalToolsGroup.java @@ -102,7 +102,7 @@ public abstract class BaseExternalToolsGroup extends SimpleActio ) { return tool.isShownInProjectViews(); } - else if (ActionPlaces.MAIN_MENU.equals(context)) { + else if (ActionPlaces.isMainMenuOrActionSearch(context)) { return tool.isShownInMainMenu(); } else if (ActionPlaces.USAGE_VIEW_POPUP.equals(context)) { diff --git a/platform/lang-impl/src/com/intellij/tools/ToolsProcessor.java b/platform/lang-impl/src/com/intellij/tools/ToolsProcessor.java index 1efa7b05d19b..3846d9c87416 100644 --- a/platform/lang-impl/src/com/intellij/tools/ToolsProcessor.java +++ b/platform/lang-impl/src/com/intellij/tools/ToolsProcessor.java @@ -25,6 +25,7 @@ import com.intellij.openapi.util.text.StringUtil; import org.jdom.Document; import org.jdom.Element; import org.jdom.JDOMException; +import org.jdom.Parent; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -134,7 +135,7 @@ abstract public class ToolsProcessor extends BaseSchemeProcessor protected abstract T createTool(); @Override - public Document writeScheme(@NotNull final ToolsGroup scheme) throws WriteExternalException { + public Element writeScheme(@NotNull final ToolsGroup scheme) throws WriteExternalException { Element groupElement = new Element(TOOL_SET); if (scheme.getName() != null) { groupElement.setAttribute(ATTRIBUTE_NAME, scheme.getName()); @@ -144,7 +145,7 @@ abstract public class ToolsProcessor extends BaseSchemeProcessor saveTool(tool, groupElement); } - return new Document(groupElement); + return groupElement; } @Override diff --git a/platform/lang-impl/src/com/intellij/ui/ReplacePromptDialog.java b/platform/lang-impl/src/com/intellij/ui/ReplacePromptDialog.java index 27622cad5a93..3cf239f46632 100644 --- a/platform/lang-impl/src/com/intellij/ui/ReplacePromptDialog.java +++ b/platform/lang-impl/src/com/intellij/ui/ReplacePromptDialog.java @@ -51,6 +51,7 @@ public class ReplacePromptDialog extends DialogWrapper { replaceAction.putValue(DEFAULT_ACTION,Boolean.TRUE); if (myException == null) { if (myIsMultiple){ + setCancelButtonText(UIBundle.message("replace.prompt.review.action")); return new Action[]{ replaceAction, createSkipAction(), diff --git a/platform/lang-impl/src/com/intellij/ui/tabs/EditorTabColorProviderImpl.java b/platform/lang-impl/src/com/intellij/ui/tabs/EditorTabColorProviderImpl.java index 93ebe1f0573a..5c2083710f2a 100644 --- a/platform/lang-impl/src/com/intellij/ui/tabs/EditorTabColorProviderImpl.java +++ b/platform/lang-impl/src/com/intellij/ui/tabs/EditorTabColorProviderImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ import com.intellij.openapi.fileEditor.impl.EditorTabColorProvider; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.ui.FileColorManager; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.awt.*; @@ -31,7 +32,7 @@ public class EditorTabColorProviderImpl implements EditorTabColorProvider { @Override @Nullable - public Color getEditorTabColor(Project project, VirtualFile file) { + public Color getEditorTabColor(@NotNull Project project, @NotNull VirtualFile file) { FileColorManager colorManager = FileColorManager.getInstance(project); return colorManager.isEnabledForTabs() ? colorManager.getFileColor(file) : null; } diff --git a/platform/lang-impl/src/com/intellij/util/download/impl/FileDownloaderImpl.java b/platform/lang-impl/src/com/intellij/util/download/impl/FileDownloaderImpl.java index e9be287a24d3..20d889d1edfa 100644 --- a/platform/lang-impl/src/com/intellij/util/download/impl/FileDownloaderImpl.java +++ b/platform/lang-impl/src/com/intellij/util/download/impl/FileDownloaderImpl.java @@ -379,6 +379,7 @@ public class FileDownloaderImpl implements FileDownloader { private final ProgressIndicator myParent; private final int myTasksCount; private final AtomicDouble myTotalFraction; + private final Object myLock = new Object(); private LinkedHashMap myText2Stack = new LinkedHashMap(); private ConcurrentTasksProgressManager(ProgressIndicator parent, int tasksCount) { @@ -398,12 +399,17 @@ public class FileDownloaderImpl implements FileDownloader { public void setText2(@NotNull SubTaskProgressIndicator subTask, @Nullable String text) { if (text != null) { - myText2Stack.put(subTask, text); + synchronized (myLock) { + myText2Stack.put(subTask, text); + } myParent.setText2(text); } else { - myText2Stack.remove(subTask); - String prev = myText2Stack.getLastValue(); + String prev; + synchronized (myLock) { + myText2Stack.remove(subTask); + prev = myText2Stack.getLastValue(); + } if (prev != null) { myParent.setText2(prev); } diff --git a/platform/lang-impl/src/com/intellij/util/indexing/ValueContainerImpl.java b/platform/lang-impl/src/com/intellij/util/indexing/ValueContainerImpl.java index 3e7f3bd03739..90f29dccb4d1 100644 --- a/platform/lang-impl/src/com/intellij/util/indexing/ValueContainerImpl.java +++ b/platform/lang-impl/src/com/intellij/util/indexing/ValueContainerImpl.java @@ -458,7 +458,7 @@ class ValueContainerImpl extends UpdatableValueContainer implement } else { // serialize positive file ids with delta encoding ChangeBufferingList originalInput = (ChangeBufferingList)fileSetObject; - IntIterator intIterator = originalInput.intIterator(); + IntIterator intIterator = originalInput.rawIntIterator(); DataInputOutputUtil.writeINT(out, -intIterator.size()); if (intIterator.hasAscendingOrder()) { @@ -493,7 +493,7 @@ class ValueContainerImpl extends UpdatableValueContainer implement final int[] bits = ourSpareBuffer.getBuffer(bitsLength); for(int i = 0; i < bitsLength; ++i) bits[i] = 0; - intIterator = originalInput.intIterator(); + intIterator = originalInput.rawIntIterator(); while(intIterator.hasNext()) { final int id = intIterator.next() - offset; bits[id >> INT_BITS_SHIFT] |= (1 << (id)); diff --git a/platform/lang-impl/src/com/intellij/util/indexing/containers/ChangeBufferingList.java b/platform/lang-impl/src/com/intellij/util/indexing/containers/ChangeBufferingList.java index 103f7d5fb587..a1373c06a23f 100644 --- a/platform/lang-impl/src/com/intellij/util/indexing/containers/ChangeBufferingList.java +++ b/platform/lang-impl/src/com/intellij/util/indexing/containers/ChangeBufferingList.java @@ -19,8 +19,6 @@ import com.intellij.util.indexing.DebugAssertions; import com.intellij.util.indexing.ValueContainer; import gnu.trove.TIntProcedure; -import java.util.Arrays; - import static com.intellij.util.indexing.DebugAssertions.DEBUG; /** @@ -37,7 +35,8 @@ public class ChangeBufferingList implements Cloneable { //static final int MAX_FILES = 100; private volatile int[] changes; private short length; - private short removals; + private boolean hasRemovals; + private volatile boolean mayHaveDupes; private volatile RandomAccessIntContainer randomAccessContainer; private IdSet checkSet; @@ -75,7 +74,8 @@ public class ChangeBufferingList implements Cloneable { private void addChange(int value) { changes[length++] = value; - if (value < 0) ++removals; + if (value < 0 && !hasRemovals) hasRemovals = true; + if(!mayHaveDupes) mayHaveDupes = true; } public void remove(int value) { @@ -118,37 +118,26 @@ public class ChangeBufferingList implements Cloneable { RandomAccessIntContainer idSet; if (randomAccessContainer == null) { - int someElementsNumberEstimation = length - removals; + int someElementsNumberEstimation = length; int[] minMax = calcMinMax(changes, length); // todo we can check these lengths instead of only relying upon reaching MAX_FILES - int lengthOfBitSet = IdBitSet.sizeInBytes(minMax[1], minMax[0]); - int lengthOfIntSet = 4 * length; + //int lengthOfBitSet = IdBitSet.sizeInBytes(minMax[1], minMax[0]); + //int lengthOfIntSet = 4 * length; if (someElementsNumberEstimation < MAX_FILES) { - if (removals == 0) { - if (DEBUG) { - ValueContainer.IntIterator sorted = SortedFileIdSetIterator.getTransientIterator(new ChangesIterator(changes, length)); - int lastIndex = 0; - while(sorted.hasNext()) { - currentChanges[lastIndex++] = sorted.next(); - } - - DebugAssertions.assertTrue(lastIndex == length); - idSet = new SortedIdSet(currentChanges, lastIndex); - } else { - Arrays.sort(currentChanges, 0, length); - idSet = new SortedIdSet(currentChanges, length); + if (!hasRemovals) { + if (mayHaveDupes) { + mergeChangesRemovingDupes(); } + idSet = new SortedIdSet(currentChanges, length); + copyChanges = false; } else { idSet = new SortedIdSet(Math.max(someElementsNumberEstimation, 3)); } } - else if (removals == 0) { - if (lengthOfBitSet > lengthOfIntSet) { - int a = 1; - } + else if (!hasRemovals) { idSet = new IdBitSet(changes, length, 0); copyChanges = false; } else { @@ -186,13 +175,26 @@ public class ChangeBufferingList implements Cloneable { } length = 0; - removals = 0; + hasRemovals = false; + mayHaveDupes = false; randomAccessContainer = idSet; changes = null; return randomAccessContainer; } } + private void mergeChangesRemovingDupes() { // duplicated ids can be present for some index due to cancellation of indexing for next index + int[] currentChanges = changes; + ValueContainer.IntIterator sorted = SortedFileIdSetIterator.getTransientIterator(new ChangesIterator(currentChanges, length)); + int lastIndex = 0; + while(sorted.hasNext()) { + currentChanges[lastIndex++] = sorted.next(); + } + + length = (short)lastIndex; + mayHaveDupes = false; + } + public void ensureCapacity(int diff) { RandomAccessIntContainer intContainer = randomAccessContainer; if (length == MAX_FILES) { @@ -224,7 +226,7 @@ public class ChangeBufferingList implements Cloneable { if (DEBUG) DebugAssertions.assertTrue(checkSet.isEmpty()); return true; } - if (removals == 0) { + if (!hasRemovals) { boolean b = length == 0; if (DEBUG) DebugAssertions.assertTrue(b == checkSet.isEmpty()); return b; @@ -252,14 +254,24 @@ public class ChangeBufferingList implements Cloneable { public ValueContainer.IntIterator intIterator() { RandomAccessIntContainer intContainer = randomAccessContainer; - if (intContainer == null && removals == 0) { - ValueContainer.IntIterator iterator = new ChangesIterator(changes, length); - if (DEBUG) { - ValueContainer.IntIterator iteratorSurelyWithoutDupes = SortedFileIdSetIterator.getTransientIterator(iterator); - DebugAssertions.assertTrue(iteratorSurelyWithoutDupes.size() == length); - iterator = iterator.createCopyInInitialState(); + if (intContainer == null && !hasRemovals) { + int[] currentChanges = changes; + if (currentChanges != null) { + if (mayHaveDupes) { + synchronized (currentChanges) { + if (mayHaveDupes) mergeChangesRemovingDupes(); + } + } + return new ChangesIterator(currentChanges, length); } - return iterator; + } + return getRandomAccessContainer().intIterator(); + } + + public ValueContainer.IntIterator rawIntIterator() { + RandomAccessIntContainer intContainer = randomAccessContainer; + if (intContainer == null && !hasRemovals) { + return new ChangesIterator(changes, length); // dupes are possible } return getRandomAccessContainer().intIterator(); } diff --git a/platform/platform-api/src/com/intellij/execution/util/ExecUtil.java b/platform/platform-api/src/com/intellij/execution/util/ExecUtil.java index 00c20735da1b..ca99fca910f8 100644 --- a/platform/platform-api/src/com/intellij/execution/util/ExecUtil.java +++ b/platform/platform-api/src/com/intellij/execution/util/ExecUtil.java @@ -83,16 +83,35 @@ public class ExecUtil { private ExecUtil() { } + /** + * Run the command using safe escaping and quoting when appropriate. + * + * @param command the command and its arguments, can contain any characters + * @param workDir working directory + * @param environment environment variables + * @return the running process + */ + @NotNull + public static Process exec(@NotNull final List command, @Nullable final String workDir, + @Nullable final Map environment) throws ExecutionException { + assert command.size() > 0; + final GeneralCommandLine commandLine = new GeneralCommandLine(command); + if (workDir != null) { + commandLine.setWorkDirectory(workDir); + } + if (environment != null) { + commandLine.getEnvironment().putAll(environment); + } + return commandLine.createProcess(); + } + public static int execAndGetResult(final String... command) throws ExecutionException, InterruptedException { assert command != null && command.length > 0; return execAndGetResult(Arrays.asList(command)); } public static int execAndGetResult(@NotNull final List command) throws ExecutionException, InterruptedException { - assert command.size() > 0; - final GeneralCommandLine commandLine = new GeneralCommandLine(command); - final Process process = commandLine.createProcess(); - return process.waitFor(); + return exec(command, null, null).waitFor(); } @NotNull @@ -151,10 +170,7 @@ public class ExecUtil { @NotNull public static ProcessOutput execAndGetOutput(@NotNull final List command, @Nullable final String workDir) throws ExecutionException { - assert command.size() > 0; - final GeneralCommandLine commandLine = new GeneralCommandLine(command); - commandLine.setWorkDirectory(workDir); - final Process process = commandLine.createProcess(); + final Process process = exec(command, workDir, null); final CapturingProcessHandler processHandler = new CapturingProcessHandler(process); return processHandler.runProcess(); } @@ -167,7 +183,7 @@ public class ExecUtil { @Nullable public static String execAndReadLine(@Nullable Charset charset, final String... command) { try { - return readFirstLine(new GeneralCommandLine(command).createProcess().getInputStream(), charset); + return readFirstLine(exec(Arrays.asList(command), null, null).getInputStream(), charset); } catch (Exception ignored) { return null; @@ -199,12 +215,12 @@ public class ExecUtil { * @param command the command and its arguments, can contain any characters * @param prompt the prompt string for the users * @param workDir working directory + * @param environment environment variables * @return the results of running the process */ @NotNull - public static ProcessOutput sudoAndGetOutput(@NotNull List command, - @NotNull String prompt, - @Nullable String workDir) throws IOException, ExecutionException { + public static Process sudo(@NotNull final List command, @NotNull final String prompt, @Nullable final String workDir, + @Nullable final Map environment) throws ExecutionException, IOException { if (SystemInfo.isMac) { final String escapedCommandLine = StringUtil.join(command, new Function() { @Override @@ -213,28 +229,28 @@ public class ExecUtil { } }, " & \" \" & "); final String escapedScript = "do shell script " + escapedCommandLine + " with administrator privileges"; - return execAndGetOutput(Arrays.asList(getOsascriptPath(), "-e", escapedScript), workDir); + return exec(Arrays.asList(getOsascriptPath(), "-e", escapedScript), workDir, environment); } else if ("root".equals(System.getenv("USER"))) { - return execAndGetOutput(command, workDir); + return exec(command, workDir, environment); } else if (hasGkSudo.getValue()) { final List sudoCommand = new ArrayList(); sudoCommand.addAll(Arrays.asList("gksudo", "--message", prompt, "--")); sudoCommand.addAll(command); - return execAndGetOutput(sudoCommand, workDir); + return exec(sudoCommand, workDir, environment); } else if (hasKdeSudo.getValue()) { final List sudoCommand = new ArrayList(); sudoCommand.addAll(Arrays.asList("kdesudo", "--comment", prompt, "--")); sudoCommand.addAll(command); - return execAndGetOutput(sudoCommand, workDir); + return exec(sudoCommand, workDir, environment); } else if (hasPkExec.getValue()) { final List sudoCommand = new ArrayList(); sudoCommand.add("pkexec"); sudoCommand.addAll(command); - return execAndGetOutput(sudoCommand, workDir); + return exec(sudoCommand, workDir, environment); } else if (SystemInfo.isUnix && hasTerminalApp()) { final String escapedCommandLine = StringUtil.join(command, new Function() { @@ -253,12 +269,20 @@ public class ExecUtil { "echo\n" + "read -p \"Press Enter to close this window...\" TEMP\n" + "exit $STATUS\n"); - return execAndGetOutput(getTerminalCommand("Install", script.getAbsolutePath()), workDir); + return exec(getTerminalCommand("Install", script.getAbsolutePath()), workDir, environment); } throw new UnsupportedSystemException(); } + @NotNull + public static ProcessOutput sudoAndGetOutput(@NotNull List command, @NotNull String prompt, + @Nullable String workDir) throws IOException, ExecutionException { + final Process process = sudo(command, prompt, workDir, null); + final CapturingProcessHandler processHandler = new CapturingProcessHandler(process); + return processHandler.runProcess(); + } + @NotNull private static String escapeAppleScriptArgument(@NotNull String arg) { return "quoted form of \"" + arg.replace("\"", "\\\"") + "\""; diff --git a/platform/platform-api/src/com/intellij/ide/BrowserUtil.java b/platform/platform-api/src/com/intellij/ide/BrowserUtil.java index cd1d1b0dba49..85c0efd2b5cd 100644 --- a/platform/platform-api/src/com/intellij/ide/BrowserUtil.java +++ b/platform/platform-api/src/com/intellij/ide/BrowserUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,6 +35,7 @@ import java.net.URL; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Locale; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -53,7 +54,7 @@ public class BrowserUtil { private BrowserUtil() { } public static boolean isAbsoluteURL(String url) { - return ourExternalPrefix.matcher(url.toLowerCase()).find(); + return ourExternalPrefix.matcher(url.toLowerCase(Locale.ENGLISH)).find(); } public static String getDocURL(String url) { diff --git a/platform/platform-api/src/com/intellij/ide/actions/ContextHelpAction.java b/platform/platform-api/src/com/intellij/ide/actions/ContextHelpAction.java index b453bb2e174c..1ba3368f2066 100644 --- a/platform/platform-api/src/com/intellij/ide/actions/ContextHelpAction.java +++ b/platform/platform-api/src/com/intellij/ide/actions/ContextHelpAction.java @@ -55,7 +55,7 @@ public class ContextHelpAction extends AnAction implements DumbAware { return; } - if (ActionPlaces.MAIN_MENU.equals(event.getPlace())) { + if (ActionPlaces.isMainMenuOrActionSearch(event.getPlace())) { DataContext dataContext = event.getDataContext(); presentation.setEnabled(getHelpId(dataContext) != null); } diff --git a/platform/platform-api/src/com/intellij/ide/util/treeView/AbstractTreeUi.java b/platform/platform-api/src/com/intellij/ide/util/treeView/AbstractTreeUi.java index 10285d05fbee..cd8499850430 100644 --- a/platform/platform-api/src/com/intellij/ide/util/treeView/AbstractTreeUi.java +++ b/platform/platform-api/src/com/intellij/ide/util/treeView/AbstractTreeUi.java @@ -1305,7 +1305,7 @@ public class AbstractTreeUi { } if (isSelectionInside(node)) { - addSelectionPath(getPathFor(node), true, Condition.TRUE, null); + addSelectionPath(getPathFor(node), true, Conditions.alwaysTrue(), null); } processInnerChange(new Runnable() { @@ -4853,7 +4853,7 @@ public class AbstractTreeUi { } if (pathToSelect != null && myTree.isSelectionEmpty()) { - addSelectionPath(pathToSelect, true, Condition.FALSE, null); + addSelectionPath(pathToSelect, true, Conditions.alwaysFalse(), null); } } } diff --git a/platform/platform-api/src/com/intellij/ide/util/treeView/UpdaterTreeState.java b/platform/platform-api/src/com/intellij/ide/util/treeView/UpdaterTreeState.java index d8ab18646aee..e1b0d7f63fe6 100644 --- a/platform/platform-api/src/com/intellij/ide/util/treeView/UpdaterTreeState.java +++ b/platform/platform-api/src/com/intellij/ide/util/treeView/UpdaterTreeState.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ package com.intellij.ide.util.treeView; import com.intellij.openapi.util.ActionCallback; import com.intellij.openapi.util.Condition; +import com.intellij.openapi.util.Conditions; import com.intellij.util.ArrayUtil; import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; @@ -214,7 +215,7 @@ public class UpdaterTreeState { if (!children.contains(eachToSelect)) { toSelect.remove(); if (!myToSelect.containsKey(readyElement) && !myUi.getSelectedElements().contains(eachToSelect)) { - addAdjustedSelection(eachToSelect, Condition.FALSE, null); + addAdjustedSelection(eachToSelect, Conditions.alwaysFalse(), null); } } } diff --git a/platform/platform-api/src/com/intellij/notification/Notification.java b/platform/platform-api/src/com/intellij/notification/Notification.java index 674e20282334..7b3ecd67ca47 100644 --- a/platform/platform-api/src/com/intellij/notification/Notification.java +++ b/platform/platform-api/src/com/intellij/notification/Notification.java @@ -20,6 +20,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.popup.Balloon; import com.intellij.openapi.ui.popup.JBPopupAdapter; import com.intellij.openapi.ui.popup.LightweightWindowEvent; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.reference.SoftReference; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -62,7 +63,7 @@ public class Notification { myType = type; myListener = listener; - LOG.assertTrue(myContent.trim().length() > 0, "Notification should have content, groupId: " + myGroupId); + LOG.assertTrue(!StringUtil.isEmptyOrSpaces(myContent), "Notification should have content, groupId: " + myGroupId); } @SuppressWarnings("MethodMayBeStatic") diff --git a/platform/platform-api/src/com/intellij/notification/NotificationGroup.java b/platform/platform-api/src/com/intellij/notification/NotificationGroup.java index df68daff1584..a1b838676f41 100644 --- a/platform/platform-api/src/com/intellij/notification/NotificationGroup.java +++ b/platform/platform-api/src/com/intellij/notification/NotificationGroup.java @@ -54,18 +54,22 @@ public final class NotificationGroup { ourRegisteredGroups.put(displayId, this); } + @NotNull public static NotificationGroup balloonGroup(@NotNull String displayId) { return new NotificationGroup(displayId, NotificationDisplayType.BALLOON, true); } + @NotNull public static NotificationGroup logOnlyGroup(@NotNull String displayId) { return new NotificationGroup(displayId, NotificationDisplayType.NONE, true); } + @NotNull public static NotificationGroup toolWindowGroup(@NotNull String displayId, @NotNull String toolWindowId, final boolean logByDefault) { return new NotificationGroup(displayId, NotificationDisplayType.TOOL_WINDOW, logByDefault, toolWindowId); } + @NotNull public static NotificationGroup toolWindowGroup(@NotNull String displayId, @NotNull String toolWindowId) { return toolWindowGroup(displayId, toolWindowId, true); } @@ -78,10 +82,12 @@ public final class NotificationGroup { return createNotification(content, type.toNotificationType()); } + @NotNull public Notification createNotification(@NotNull final String content, @NotNull final NotificationType type) { return createNotification("", content, type, null); } + @NotNull public Notification createNotification(@NotNull final String title, @NotNull final String content, @NotNull final NotificationType type, diff --git a/platform/platform-api/src/com/intellij/notification/NotificationListener.java b/platform/platform-api/src/com/intellij/notification/NotificationListener.java index 8577d7d6a469..e88204f6888a 100644 --- a/platform/platform-api/src/com/intellij/notification/NotificationListener.java +++ b/platform/platform-api/src/com/intellij/notification/NotificationListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,7 +35,7 @@ public interface NotificationListener { protected abstract void hyperlinkActivated(@NotNull Notification notification, @NotNull HyperlinkEvent e); } - NotificationListener URL_OPENING_LISTENER = new UrlOpeningListener(false); + NotificationListener URL_OPENING_LISTENER = new UrlOpeningListener(true); class UrlOpeningListener extends Adapter { private final boolean myExpireNotification; 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 16a81d2218d2..aa1248775a21 100644 --- a/platform/platform-api/src/com/intellij/openapi/actionSystem/ActionPlaces.java +++ b/platform/platform-api/src/com/intellij/openapi/actionSystem/ActionPlaces.java @@ -26,6 +26,9 @@ import org.jetbrains.annotations.NotNull; public abstract class ActionPlaces { public static final String UNKNOWN = "unknown"; + /** + * consider to use {@link #isMainMenuOrActionSearch(String)} instead + */ public static final String MAIN_MENU = "MainMenu"; public static final String MAIN_TOOLBAR = "MainToolbar"; public static final String EDITOR_POPUP = "EditorPopup"; @@ -45,6 +48,7 @@ public abstract class ActionPlaces { public static final String STATUS_BAR_PLACE = "StatusBarPlace"; public static final String SCOPE_VIEW_POPUP = "ScopeViewPopup"; + public static final String ACTION_SEARCH = "GoToAction"; public static final String TESTTREE_VIEW_POPUP = "TestTreeViewPopup"; public static final String TESTTREE_VIEW_TOOLBAR = "TestTreeViewToolbar"; @@ -127,6 +131,10 @@ public abstract class ActionPlaces { return ArrayUtil.find(ourToolbarPlaces, place) != -1; } + public static boolean isMainMenuOrActionSearch(String place) { + return MAIN_MENU.equals(place) || ACTION_SEARCH.equals(place); + } + private static final String[] ourPopupPlaces = {EDITOR_POPUP, EDITOR_TAB_POPUP, COMMANDER_POPUP, PROJECT_VIEW_POPUP, FAVORITES_VIEW_POPUP, SCOPE_VIEW_POPUP, TESTTREE_VIEW_POPUP, TESTSTATISTICS_VIEW_POPUP, TYPE_HIERARCHY_VIEW_POPUP, METHOD_HIERARCHY_VIEW_POPUP, CALL_HIERARCHY_VIEW_POPUP, J2EE_ATTRIBUTES_VIEW_POPUP, J2EE_VIEW_POPUP, USAGE_VIEW_POPUP, diff --git a/platform/platform-api/src/com/intellij/openapi/actionSystem/Separator.java b/platform/platform-api/src/com/intellij/openapi/actionSystem/Separator.java index 2dd2ee50d120..191c8c01ce32 100644 --- a/platform/platform-api/src/com/intellij/openapi/actionSystem/Separator.java +++ b/platform/platform-api/src/com/intellij/openapi/actionSystem/Separator.java @@ -41,6 +41,11 @@ public final class Separator extends AnAction implements DumbAware { return ourInstance; } + @Override + public String toString() { + return "Separator (" + myText + ")"; + } + @Override public void actionPerformed(AnActionEvent e){ throw new UnsupportedOperationException(); diff --git a/platform/platform-api/src/com/intellij/openapi/actionSystem/ex/CheckboxAction.java b/platform/platform-api/src/com/intellij/openapi/actionSystem/ex/CheckboxAction.java index 5f179baad831..3e52f26d0b87 100644 --- a/platform/platform-api/src/com/intellij/openapi/actionSystem/ex/CheckboxAction.java +++ b/platform/platform-api/src/com/intellij/openapi/actionSystem/ex/CheckboxAction.java @@ -43,12 +43,8 @@ public abstract class CheckboxAction extends ToggleAction implements CustomCompo public JComponent createCustomComponent(Presentation presentation) { // this component cannot be stored right here because of action system architecture: // one action can be shown on multiple toolbars simultaneously - JCheckBox checkBox = new JCheckBox(presentation.getText()); + JCheckBox checkBox = new JCheckBox(); checkBox.setOpaque(false); - checkBox.setToolTipText(presentation.getDescription()); - checkBox.setMnemonic(presentation.getMnemonic()); - checkBox.setDisplayedMnemonicIndex(presentation.getDisplayedMnemonicIndex()); - checkBox.setSelected(Boolean.TRUE.equals(presentation.getClientProperty(SELECTED_PROPERTY))); checkBox.addActionListener(new ActionListener() { @Override @@ -69,13 +65,19 @@ public abstract class CheckboxAction extends ToggleAction implements CustomCompo @Override public void update(final AnActionEvent e) { super.update(e); - Object property = e.getPresentation().getClientProperty(CUSTOM_COMPONENT_PROPERTY); + Presentation presentation = e.getPresentation(); + Object property = presentation.getClientProperty(CUSTOM_COMPONENT_PROPERTY); if (property instanceof JCheckBox) { JCheckBox checkBox = (JCheckBox)property; - checkBox.setSelected(Boolean.TRUE.equals(e.getPresentation().getClientProperty(SELECTED_PROPERTY))); - checkBox.setEnabled(e.getPresentation().isEnabled()); - checkBox.setVisible(e.getPresentation().isVisible()); + checkBox.setText(presentation.getText()); + checkBox.setToolTipText(presentation.getDescription()); + checkBox.setMnemonic(presentation.getMnemonic()); + checkBox.setDisplayedMnemonicIndex(presentation.getDisplayedMnemonicIndex()); + checkBox.setSelected(Boolean.TRUE.equals(presentation.getClientProperty(SELECTED_PROPERTY))); + + checkBox.setEnabled(presentation.isEnabled()); + checkBox.setVisible(presentation.isVisible()); } } } diff --git a/platform/platform-api/src/com/intellij/openapi/actionSystem/ex/ComboBoxAction.java b/platform/platform-api/src/com/intellij/openapi/actionSystem/ex/ComboBoxAction.java index 7398bf706c78..6ce522141f16 100644 --- a/platform/platform-api/src/com/intellij/openapi/actionSystem/ex/ComboBoxAction.java +++ b/platform/platform-api/src/com/intellij/openapi/actionSystem/ex/ComboBoxAction.java @@ -26,10 +26,7 @@ import com.intellij.openapi.util.IconLoader; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.wm.IdeFocusManager; -import com.intellij.ui.ColorUtil; -import com.intellij.ui.Gray; -import com.intellij.ui.IdeBorderFactory; -import com.intellij.ui.JBColor; +import com.intellij.ui.*; import com.intellij.ui.awt.RelativePoint; import com.intellij.util.ui.GraphicsUtil; import com.intellij.util.ui.UIUtil; @@ -103,7 +100,7 @@ public abstract class ComboBoxAction extends AnAction implements CustomComponent return 1; } - protected class ComboBoxButton extends JButton { + protected class ComboBoxButton extends JButton implements UserActivityProviderComponent { private final Presentation myPresentation; private boolean myForcePressed = false; private PropertyChangeListener myButtonSynchronizer; @@ -227,6 +224,7 @@ public abstract class ComboBoxAction extends AnAction implements CustomComponent } }); repaint(); + fireStateChanged(); } }; diff --git a/platform/platform-api/src/com/intellij/openapi/application/ApplicationStarterEx.java b/platform/platform-api/src/com/intellij/openapi/application/ApplicationStarterEx.java index c4b3a6384f56..86e322f6e0e3 100644 --- a/platform/platform-api/src/com/intellij/openapi/application/ApplicationStarterEx.java +++ b/platform/platform-api/src/com/intellij/openapi/application/ApplicationStarterEx.java @@ -15,6 +15,8 @@ */ package com.intellij.openapi.application; +import org.jetbrains.annotations.Nullable; + /** * Implementers of the interface declared via {@link com.intellij.ExtensionPoints#APPLICATION_STARTER} * may be capable of processing an external command line within a running IntelliJ Platform instance. @@ -28,5 +30,10 @@ public abstract class ApplicationStarterEx implements ApplicationStarter { return false; } + @Deprecated public void processExternalCommandLine(String[] args) { } + + public void processExternalCommandLine(String[] args, @Nullable String currentDirectory) { + processExternalCommandLine(args); + } } diff --git a/platform/platform-api/src/com/intellij/openapi/diagnostic/ErrorReportSubmitter.java b/platform/platform-api/src/com/intellij/openapi/diagnostic/ErrorReportSubmitter.java index 9f1b8e06b39e..c68e45189a95 100644 --- a/platform/platform-api/src/com/intellij/openapi/diagnostic/ErrorReportSubmitter.java +++ b/platform/platform-api/src/com/intellij/openapi/diagnostic/ErrorReportSubmitter.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,8 @@ package com.intellij.openapi.diagnostic; import com.intellij.openapi.extensions.PluginAware; import com.intellij.openapi.extensions.PluginDescriptor; import com.intellij.util.Consumer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.awt.*; @@ -44,33 +46,43 @@ public abstract class ErrorReportSubmitter implements PluginAware { } /** - * @return "Report to vendor" action text to be used in Error Reporter user interface. For example: "Report to JetBrains". + * @return an action text to be used in Error Reporter user interface, e.g. "Report to JetBrains". */ public abstract String getReportActionText(); /** - * This method is called whenever fatal error (aka exception) in plugin code had happened and user decided to report this problem to - * plugin vendor. - * @param events sequence of the fatal error descriptors. Fatal errors that happened immediately one after another most probably caused - * by first one that happened so it's a common practice to submit only first one. Array passed is guaranteed to have at least one element. - * @param parentComponent one usually wants to show up a dialog asking user for additional details and probably authentication info. - * parentComponent parameter is passed so dialog that would come up would be properly aligned with its parent dialog (IDE Fatal Errors). - * @return submission result status. + * This method is called whenever an exception in a plugin code had happened and a user decided to report a problem to the plugin vendor. + * + * @param events a non-empty sequence of error descriptors. + * @param additionalInfo additional information provided by a user. + * @param parentComponent UI component to use as a parent in any UI activity from a submitter. + * @param consumer a callback to be called after sending is finished (or failed). + * @return {@code true} if reporting was started, {@code false} if a report can't be sent at the moment. */ - public abstract SubmittedReportInfo submit(IdeaLoggingEvent[] events, Component parentComponent); - - public void submitAsync(IdeaLoggingEvent[] events, - String additionalInfo, - Component parentComponent, - Consumer consumer) { - consumer.consume(submit(events, parentComponent)); + @SuppressWarnings("deprecation") + public boolean submit(@NotNull IdeaLoggingEvent[] events, + @Nullable String additionalInfo, + @NotNull Component parentComponent, + @NotNull Consumer consumer) { + return trySubmitAsync(events, additionalInfo, parentComponent, consumer); } - public boolean trySubmitAsync(IdeaLoggingEvent[] events, - String additionalInfo, - Component parentComponent, - Consumer consumer) { - submitAsync(events, additionalInfo, parentComponent, consumer); + /** @deprecated implement {@link #submit(IdeaLoggingEvent[], String, Component, Consumer)} (to be removed in IDEA 16) */ + @SuppressWarnings({"deprecation", "unused"}) + public boolean trySubmitAsync(IdeaLoggingEvent[] events, String info, Component parent, Consumer consumer) { + submitAsync(events, info, parent, consumer); return true; } + + /** @deprecated implement {@link #submit(IdeaLoggingEvent[], String, Component, Consumer)} (to be removed in IDEA 16) */ + @SuppressWarnings({"deprecation", "unused"}) + public void submitAsync(IdeaLoggingEvent[] events, String info, Component parent, Consumer consumer) { + consumer.consume(submit(events, parent)); + } + + /** @deprecated implement {@link #submit(IdeaLoggingEvent[], String, Component, Consumer)} (to be removed in IDEA 16) */ + @SuppressWarnings({"deprecation", "unused"}) + public SubmittedReportInfo submit(IdeaLoggingEvent[] events, Component parent) { + throw new UnsupportedOperationException("Deprecated API called"); + } } diff --git a/platform/platform-api/src/com/intellij/openapi/diagnostic/SubmittedReportInfo.java b/platform/platform-api/src/com/intellij/openapi/diagnostic/SubmittedReportInfo.java index 6bad8c45df93..8ead30567681 100644 --- a/platform/platform-api/src/com/intellij/openapi/diagnostic/SubmittedReportInfo.java +++ b/platform/platform-api/src/com/intellij/openapi/diagnostic/SubmittedReportInfo.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,6 +15,9 @@ */ package com.intellij.openapi.diagnostic; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + /** * Simple bean representing error submission status. */ @@ -31,29 +34,27 @@ public class SubmittedReportInfo { DUPLICATE, /** - * Submission failed. (For network connection reasons for example) + * Submission failed (e.g. because of network problem) */ FAILED } - private final String myURL; + private final String myUrl; private final String myLinkText; private final SubmissionStatus myStatus; - /** - * Create new submission status bean - * @param URL url that points to newly created issue. Optional. Pass null value if N/A or failed - * @param linkText short text that UI interface pointing to the issue should have. - * @param status submission success/failure - */ - public SubmittedReportInfo(final String URL, final String linkText, final SubmissionStatus status) { - myURL = URL; + public SubmittedReportInfo(SubmissionStatus status) { + this(null, null, status); + } + + public SubmittedReportInfo(@Nullable String url, @Nullable String linkText, @NotNull SubmissionStatus status) { + myUrl = url; myLinkText = linkText; myStatus = status; } public String getURL() { - return myURL; + return myUrl; } public String getLinkText() { diff --git a/platform/platform-api/src/com/intellij/openapi/fileChooser/FileChooser.java b/platform/platform-api/src/com/intellij/openapi/fileChooser/FileChooser.java index 9e0bf738a7cf..fee268813d14 100644 --- a/platform/platform-api/src/com/intellij/openapi/fileChooser/FileChooser.java +++ b/platform/platform-api/src/com/intellij/openapi/fileChooser/FileChooser.java @@ -52,7 +52,7 @@ public class FileChooser { @Nullable final Project project, @Nullable final VirtualFile toSelect) { final FileChooserDialog chooser = FileChooserFactory.getInstance().createFileChooser(descriptor, project, parent); - return chooser.choose(toSelect, project); + return chooser.choose(project, toSelect); } @Nullable diff --git a/platform/platform-api/src/com/intellij/openapi/fileChooser/FileChooserDialog.java b/platform/platform-api/src/com/intellij/openapi/fileChooser/FileChooserDialog.java index c12ab422a942..324979dff228 100644 --- a/platform/platform-api/src/com/intellij/openapi/fileChooser/FileChooserDialog.java +++ b/platform/platform-api/src/com/intellij/openapi/fileChooser/FileChooserDialog.java @@ -23,7 +23,22 @@ import org.jetbrains.annotations.Nullable; public interface FileChooserDialog { DataKey PREFER_LAST_OVER_TO_SELECT = PathChooserDialog.PREFER_LAST_OVER_EXPLICIT; - + + /** + * @deprecated Please use {@link #choose(com.intellij.openapi.project.Project, com.intellij.openapi.vfs.VirtualFile...)} because + * it supports several selections + */ + @Deprecated @NotNull VirtualFile[] choose(@Nullable VirtualFile toSelect, @Nullable Project project); + + /** + * Choose one or more files + * + * @param project use this project (you may pass null if you already set project in ctor) + * @param toSelect files to be selected automatically. + * @return files chosen by user + */ + @NotNull + VirtualFile[] choose(@Nullable Project project, @NotNull VirtualFile... toSelect); } \ No newline at end of file diff --git a/platform/platform-api/src/com/intellij/openapi/fileEditor/FileEditorManager.java b/platform/platform-api/src/com/intellij/openapi/fileEditor/FileEditorManager.java index 86e719eef48d..dcb1327f1a10 100644 --- a/platform/platform-api/src/com/intellij/openapi/fileEditor/FileEditorManager.java +++ b/platform/platform-api/src/com/intellij/openapi/fileEditor/FileEditorManager.java @@ -197,5 +197,5 @@ public abstract class FileEditorManager { * @param fileEditorProviderId the ID of the file editor to open; matches the return value of * {@link com.intellij.openapi.fileEditor.FileEditorProvider#getEditorTypeId()} */ - public abstract void setSelectedEditor(@NotNull VirtualFile file, String fileEditorProviderId); + public abstract void setSelectedEditor(@NotNull VirtualFile file, @NotNull String fileEditorProviderId); } diff --git a/platform/platform-api/src/com/intellij/openapi/options/ConfigurableEP.java b/platform/platform-api/src/com/intellij/openapi/options/ConfigurableEP.java index 78a4c1ece130..828907c13cfb 100644 --- a/platform/platform-api/src/com/intellij/openapi/options/ConfigurableEP.java +++ b/platform/platform-api/src/com/intellij/openapi/options/ConfigurableEP.java @@ -175,9 +175,25 @@ public class ConfigurableEP extends AbstractExten return getDisplayName(); } + public boolean canCreateConfigurable() { + if (providerClass == null) { + return implementationClass != null || instanceClass != null; + } + try { + ConfigurableProvider provider = instantiate(providerClass, myPicoContainer); + return provider.canCreateConfigurable(); // do not load heavy configurables + } + catch (Exception ignored) { + return true; // see InstanceFromProviderFactory#compute + } + } + private class InstanceFromProviderFactory extends AtomicNotNullLazyValue implements NullableFactory { public T create() { - return (T)getValue().createConfigurable(); + ConfigurableProvider provider = getValue(); + return provider.canCreateConfigurable() + ? (T)provider.createConfigurable() + : null; } @NotNull diff --git a/platform/platform-api/src/com/intellij/openapi/options/ConfigurableProvider.java b/platform/platform-api/src/com/intellij/openapi/options/ConfigurableProvider.java index 5a71ffa73050..cf2bc0390c5a 100644 --- a/platform/platform-api/src/com/intellij/openapi/options/ConfigurableProvider.java +++ b/platform/platform-api/src/com/intellij/openapi/options/ConfigurableProvider.java @@ -29,4 +29,15 @@ public abstract class ConfigurableProvider { @Nullable public abstract Configurable createConfigurable(); + /** + * Defines whether this provider creates a configurable or not. + * Note that the {@code createConfigurable} method will be called + * if this method returns {@code true}. + * + * @return {@code true} if this provider creates configurable, + * {@code false} otherwise + */ + public boolean canCreateConfigurable() { + return true; + } } diff --git a/platform/platform-api/src/com/intellij/openapi/options/CurrentUserHolder.java b/platform/platform-api/src/com/intellij/openapi/options/CurrentUserHolder.java index 473dce3dda6c..2395fa27f4c1 100644 --- a/platform/platform-api/src/com/intellij/openapi/options/CurrentUserHolder.java +++ b/platform/platform-api/src/com/intellij/openapi/options/CurrentUserHolder.java @@ -15,6 +15,7 @@ */ package com.intellij.openapi.options; +@Deprecated public interface CurrentUserHolder { String getCurrentUserName(); } diff --git a/platform/platform-api/src/com/intellij/openapi/options/SearchableConfigurable.java b/platform/platform-api/src/com/intellij/openapi/options/SearchableConfigurable.java index e98bb05aba99..b426e35d77a8 100644 --- a/platform/platform-api/src/com/intellij/openapi/options/SearchableConfigurable.java +++ b/platform/platform-api/src/com/intellij/openapi/options/SearchableConfigurable.java @@ -34,6 +34,12 @@ public interface SearchableConfigurable extends Configurable { interface Parent extends SearchableConfigurable, Composite { boolean hasOwnContent(); + + /** + * @deprecated use {@link ConfigurableProvider#canCreateConfigurable()} instead + * to specify configurables which should not be visible + */ + @Deprecated boolean isVisible(); abstract class Abstract implements Parent { diff --git a/platform/platform-api/src/com/intellij/openapi/ui/DialogWrapper.java b/platform/platform-api/src/com/intellij/openapi/ui/DialogWrapper.java index 741f67dee2c4..a291ab32e950 100644 --- a/platform/platform-api/src/com/intellij/openapi/ui/DialogWrapper.java +++ b/platform/platform-api/src/com/intellij/openapi/ui/DialogWrapper.java @@ -2001,12 +2001,19 @@ public abstract class DialogWrapper { */ public interface DoNotAskOption { + /** + * @return default selection state of checkbox (false -> checkbox selected) + */ boolean isToBeShown(); - void setToBeShown(boolean value, int exitCode); + /** + * @param toBeShown - if dialog should be shown next time (checkbox selected -> false) + * @param exitCode of corresponding DialogWrapper + */ + void setToBeShown(boolean toBeShown, int exitCode); /** - * Should be 'true' for checkbox to be visible. + * @return true if checkbox should be shown */ boolean canBeHidden(); diff --git a/platform/platform-api/src/com/intellij/openapi/ui/MasterDetailsComponent.java b/platform/platform-api/src/com/intellij/openapi/ui/MasterDetailsComponent.java index a262ea0a3b85..547fa03e438d 100644 --- a/platform/platform-api/src/com/intellij/openapi/ui/MasterDetailsComponent.java +++ b/platform/platform-api/src/com/intellij/openapi/ui/MasterDetailsComponent.java @@ -131,7 +131,7 @@ public abstract class MasterDetailsComponent implements Configurable, DetailsCom protected MasterDetailsComponent(MasterDetailsState state) { myState = state; - mySplitter = Registry.is("ide.new.project.settings") ? new OnePixelSplitter(false, .2f) : new JBSplitter(false, .2f); + mySplitter = isNewProjectSettings() ? new OnePixelSplitter(false, .2f) : new JBSplitter(false, .2f); mySplitter.setSplitterProportionKey("ProjectStructure.SecondLevelElements"); mySplitter.setHonorComponentsMinimumSize(true); @@ -139,6 +139,20 @@ public abstract class MasterDetailsComponent implements Configurable, DetailsCom reInitWholePanelIfNeeded(); } + private boolean isNewProjectSettings() { + if (!Registry.is("ide.new.project.settings")) { + return false; + } + try { + // assume that only project structure dialog uses the following base class for details: + String name = "com.intellij.openapi.roots.ui.configuration.projectRoot.BaseStructureConfigurable"; + return Class.forName(name).isAssignableFrom(getClass()); + } + catch (ClassNotFoundException ignored) { + return false; + } + } + protected void reInitWholePanelIfNeeded() { if (!myToReInitWholePanel) return; @@ -170,7 +184,7 @@ public abstract class MasterDetailsComponent implements Configurable, DetailsCom } }; - if (Registry.is("ide.new.project.settings")) { + if (isNewProjectSettings()) { ToolbarDecorator decorator = ToolbarDecorator.createDecorator(myTree); DefaultActionGroup group = createToolbarActionGroup(); if (group != null) { @@ -281,7 +295,7 @@ public abstract class MasterDetailsComponent implements Configurable, DetailsCom } private void initToolbar() { - if (Registry.is("ide.new.project.settings")) return; + if (isNewProjectSettings()) return; DefaultActionGroup group = createToolbarActionGroup(); if (group != null) { final JComponent component = ActionManager.getInstance().createActionToolbar(ActionPlaces.UNKNOWN, group, true).getComponent(); diff --git a/platform/platform-api/src/com/intellij/openapi/ui/Messages.java b/platform/platform-api/src/com/intellij/openapi/ui/Messages.java index 1a07b33d696d..e963871cfd5f 100644 --- a/platform/platform-api/src/com/intellij/openapi/ui/Messages.java +++ b/platform/platform-api/src/com/intellij/openapi/ui/Messages.java @@ -373,6 +373,33 @@ public class Messages { return result; } + /** + * @return {@link #YES} if user pressed "Yes" or {@link #NO} if user pressed "No" button. + */ + @YesNoResult + public static int showYesNoDialog(@Nullable Project project, + String message, + @NotNull String title, + @NotNull String yesText, + @NotNull String noText, + @Nullable Icon icon, + @Nullable DialogWrapper.DoNotAskOption doNotAskOption) { + try { + if (canShowMacSheetPanel()) { + return MacMessages.getInstance() + .showYesNoDialog(title, message, yesText, noText, WindowManager.getInstance().suggestParentWindow(project), doNotAskOption); + } + } + catch (Exception exception) { + LOG.error(exception); + } + + int result = showDialog(project, message, title, new String[]{yesText, noText}, 0, icon, doNotAskOption) == 0 ? YES : NO; + //noinspection ConstantConditions + LOG.assertTrue(result == YES || result == NO, result); + return result; + } + /** * @return {@link #YES} if user pressed "Yes" or {@link #NO} if user pressed "No" button. */ @@ -394,6 +421,31 @@ public class Messages { return result; } + /** + * @return {@link #YES} if user pressed "Yes" or {@link #NO} if user pressed "No" button. + */ + @YesNoResult + public static int showYesNoDialog(@Nullable Project project, + String message, + @NotNull String title, + @Nullable Icon icon, + @Nullable DialogWrapper.DoNotAskOption doNotAskOption) { + try { + if (canShowMacSheetPanel()) { + return MacMessages.getInstance().showYesNoDialog(title, message, YES_BUTTON, NO_BUTTON, + WindowManager.getInstance().suggestParentWindow(project), doNotAskOption); + } + } + catch (Exception exception) { + LOG.error(exception); + } + + int result = showYesNoDialog(project, message, title, YES_BUTTON, NO_BUTTON, icon, doNotAskOption); + + LOG.assertTrue(result == YES || result == NO, result); + return result; + } + /** * @return {@link #YES} if user pressed "Yes" or {@link #NO} if user pressed "No" button. diff --git a/platform/platform-api/src/com/intellij/openapi/ui/popup/util/BaseListPopupStep.java b/platform/platform-api/src/com/intellij/openapi/ui/popup/util/BaseListPopupStep.java index 2a55c3bd342a..3bb5302e7f2e 100644 --- a/platform/platform-api/src/com/intellij/openapi/ui/popup/util/BaseListPopupStep.java +++ b/platform/platform-api/src/com/intellij/openapi/ui/popup/util/BaseListPopupStep.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2011 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -32,16 +32,16 @@ public class BaseListPopupStep extends BaseStep implements ListPopupStep myIcons; private int myDefaultOptionIndex = -1; - public BaseListPopupStep(@Nullable String aTitle, T[] aValues) { - this(aTitle, aValues, new Icon[]{}); + public BaseListPopupStep(@Nullable String title, T[] values) { + this(title, values, new Icon[]{}); } - public BaseListPopupStep(@Nullable String aTitle, List aValues) { - this(aTitle, aValues, new ArrayList()); + public BaseListPopupStep(@Nullable String title, List values) { + this(title, values, new ArrayList()); } - public BaseListPopupStep(@Nullable String aTitle, T[] aValues, Icon[] aIcons) { - this(aTitle, Arrays.asList(aValues), Arrays.asList(aIcons)); + public BaseListPopupStep(@Nullable String title, T[] values, Icon[] icons) { + this(title, Arrays.asList(values), Arrays.asList(icons)); } public BaseListPopupStep(@Nullable String aTitle, @NotNull List aValues, Icon aSameIcon) { @@ -53,16 +53,16 @@ public class BaseListPopupStep extends BaseStep implements ListPopupStep aValues, List aIcons) { - init(aTitle, aValues, aIcons); + public BaseListPopupStep(@Nullable String title, @NotNull List values, List icons) { + init(title, values, icons); } protected BaseListPopupStep() { } - protected final void init(@Nullable String aTitle, @NotNull List aValues, @Nullable List aIcons) { - myTitle = aTitle; - myValues = new ArrayList(aValues); - myIcons = aIcons; + protected final void init(@Nullable String title, @NotNull List values, @Nullable List icons) { + myTitle = title; + myValues = new ArrayList(values); + myIcons = icons; } @Nullable @@ -79,8 +79,8 @@ public class BaseListPopupStep extends BaseStep implements ListPopupStep extends AbstractListModel implements Editabl Collections.sort(myItems, comparator); } + @NotNull public List getItems() { return Collections.unmodifiableList(myItems); } diff --git a/platform/platform-api/src/com/intellij/ui/SimpleColoredText.java b/platform/platform-api/src/com/intellij/ui/SimpleColoredText.java index af851951e363..8c899f1a6b5c 100644 --- a/platform/platform-api/src/com/intellij/ui/SimpleColoredText.java +++ b/platform/platform-api/src/com/intellij/ui/SimpleColoredText.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -45,6 +45,12 @@ public class SimpleColoredText implements ColoredTextContainer { myAttributes.add(attributes); } + public void insert(int index, @NotNull String fragment, @NotNull SimpleTextAttributes attributes) { + myTexts.add(index, fragment); + myCachedToString = null; + myAttributes.add(index, attributes); + } + @Override public void append(@NotNull String fragment, @NotNull SimpleTextAttributes attributes, Object tag) { append(fragment, attributes); diff --git a/platform/platform-api/src/com/intellij/ui/components/labels/LinkLabel.java b/platform/platform-api/src/com/intellij/ui/components/labels/LinkLabel.java index 129217ec7e4a..0416008d1d57 100644 --- a/platform/platform-api/src/com/intellij/ui/components/labels/LinkLabel.java +++ b/platform/platform-api/src/com/intellij/ui/components/labels/LinkLabel.java @@ -204,6 +204,8 @@ public class LinkLabel extends JLabel { } private boolean isInClickableArea(Point pt) { + Insets insets = getInsets(); // border is set + pt.translate(-insets.left, -insets.top); if (getIcon() != null) { if (pt.getX() < getIcon().getIconWidth() && pt.getY() < getIcon().getIconHeight()) { return true; diff --git a/platform/platform-api/src/com/intellij/ui/table/JBTable.java b/platform/platform-api/src/com/intellij/ui/table/JBTable.java index c1146be40aba..a65c209399bf 100644 --- a/platform/platform-api/src/com/intellij/ui/table/JBTable.java +++ b/platform/platform-api/src/com/intellij/ui/table/JBTable.java @@ -572,7 +572,7 @@ public class JBTable extends JTable implements ComponentWithEmptyText, Component private final class MyMouseListener extends MouseAdapter { @Override public void mousePressed(@NotNull final MouseEvent e) { - if (SwingUtilities.isRightMouseButton(e)) { + if (JBSwingUtilities.isRightMouseButton(e)) { final int[] selectedRows = getSelectedRows(); if (selectedRows.length < 2) { final int row = rowAtPoint(e.getPoint()); diff --git a/platform/platform-api/src/com/intellij/ui/treeStructure/Tree.java b/platform/platform-api/src/com/intellij/ui/treeStructure/Tree.java index c8c50bc94680..30314135a543 100644 --- a/platform/platform-api/src/com/intellij/ui/treeStructure/Tree.java +++ b/platform/platform-api/src/com/intellij/ui/treeStructure/Tree.java @@ -20,14 +20,12 @@ import com.intellij.ide.util.treeView.*; import com.intellij.openapi.ui.GraphicsConfig; import com.intellij.openapi.ui.Queryable; import com.intellij.openapi.util.Condition; +import com.intellij.openapi.util.Conditions; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.SystemInfo; import com.intellij.ui.*; import com.intellij.util.ReflectionUtil; -import com.intellij.util.ui.AsyncProcessIcon; -import com.intellij.util.ui.ComponentWithEmptyText; -import com.intellij.util.ui.StatusText; -import com.intellij.util.ui.UIUtil; +import com.intellij.util.ui.*; import com.intellij.util.ui.tree.WideSelectionTreeUI; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -143,7 +141,7 @@ public class Tree extends JTree implements ComponentWithEmptyText, ComponentWith @SuppressWarnings("unchecked") @NotNull protected Condition getWideSelectionBackgroundCondition() { - return Condition.TRUE; + return Conditions.alwaysTrue(); } @Override @@ -673,8 +671,8 @@ public class Tree extends JTree implements ComponentWithEmptyText, ComponentWith private class MyMouseListener extends MouseAdapter { @Override public void mousePressed(MouseEvent mouseevent) { - if (!SwingUtilities.isLeftMouseButton(mouseevent) && - (SwingUtilities.isRightMouseButton(mouseevent) || SwingUtilities.isMiddleMouseButton(mouseevent))) { + if (!JBSwingUtilities.isLeftMouseButton(mouseevent) && + (JBSwingUtilities.isRightMouseButton(mouseevent) || JBSwingUtilities.isMiddleMouseButton(mouseevent))) { TreePath treepath = getPathForLocation(mouseevent.getX(), mouseevent.getY()); if (treepath != null) { if (getSelectionModel().getSelectionMode() != TreeSelectionModel.SINGLE_TREE_SELECTION) { @@ -842,7 +840,7 @@ public class Tree extends JTree implements ComponentWithEmptyText, ComponentWith * @return the deepest visible component of the renderer */ @Nullable - protected Component getDeepestRendererComponentAt(int x, int y) { + public Component getDeepestRendererComponentAt(int x, int y) { int row = getRowForLocation(x, y); if (row >= 0) { TreeCellRenderer renderer = getCellRenderer(); diff --git a/platform/platform-api/src/com/intellij/util/concurrency/QueueProcessor.java b/platform/platform-api/src/com/intellij/util/concurrency/QueueProcessor.java index 34238e57c635..ffcb0870dc3c 100644 --- a/platform/platform-api/src/com/intellij/util/concurrency/QueueProcessor.java +++ b/platform/platform-api/src/com/intellij/util/concurrency/QueueProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2010 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -75,7 +75,7 @@ public class QueueProcessor { * Constructs a QueueProcessor, which will autostart as soon as the first element is added to it. */ public QueueProcessor(@NotNull Consumer processor) { - this(processor, Condition.FALSE); + this(processor, Conditions.alwaysFalse()); } /** diff --git a/platform/platform-api/src/com/intellij/util/net/HttpConfigurable.java b/platform/platform-api/src/com/intellij/util/net/HttpConfigurable.java index 0f9855001b63..757706127a38 100644 --- a/platform/platform-api/src/com/intellij/util/net/HttpConfigurable.java +++ b/platform/platform-api/src/com/intellij/util/net/HttpConfigurable.java @@ -61,7 +61,7 @@ import java.util.*; @Storage(file = StoragePathMacros.APP_CONFIG + "/other.xml"), @Storage(file = StoragePathMacros.APP_CONFIG + "/proxy.settings.xml") }, - storageChooser = HttpConfigurable.StorageChooser.class + storageChooser = LastStorageChooserForWrite.class ) public class HttpConfigurable implements PersistentStateComponent, ExportableApplicationComponent { public static final int CONNECTION_TIMEOUT = SystemProperties.getIntProperty("idea.connection.timeout", 10000); @@ -519,20 +519,6 @@ public class HttpConfigurable implements PersistentStateComponent { - @Override - public Storage[] selectStorages(Storage[] storages, HttpConfigurable component, StateStorageOperation operation) { - if (operation == StateStorageOperation.WRITE) { - for (Storage storage : storages) { - if (storage.file().equals(StoragePathMacros.APP_CONFIG + "/proxy.settings.xml")) { - return new Storage[] {storage}; - } - } - } - return storages; - } - } - public static class ProxyInfo { public boolean myStore; public String myUsername; diff --git a/platform/platform-api/src/com/intellij/util/net/ssl/CertificateUtil.java b/platform/platform-api/src/com/intellij/util/net/ssl/CertificateUtil.java index a1e36236c0e4..ff86990d7d16 100644 --- a/platform/platform-api/src/com/intellij/util/net/ssl/CertificateUtil.java +++ b/platform/platform-api/src/com/intellij/util/net/ssl/CertificateUtil.java @@ -1,8 +1,21 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.intellij.util.net.ssl; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.util.io.StreamUtil; -import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -13,55 +26,46 @@ import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; /** + * Names in constants match + * Standard Algorithm Name Documentation. + * * @author Mikhail Golubev */ public class CertificateUtil { - private static final Logger LOG = Logger.getInstance(CertificateUtil.class); + public static final String X509 = "X.509"; + public static final String JKS = "JKS"; + public static final String PKCS12 = "PKCS12"; + public static final String PKIX = "PKIX"; + public static final String TLS = "TLS"; private static final CertificateFactory ourFactory = createFactory(); private static CertificateFactory createFactory() { try { - return CertificateFactory.getInstance("X509"); + return CertificateFactory.getInstance(X509); } catch (CertificateException e) { - throw new AssertionError("Can't initialize X509 certificate factory"); + throw new RuntimeException("Can't initialize X.509 certificate factory", e); } } - // Standard Names - // See complete reference at http://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html - // certificate format - @Nls public static final String X509 = "X.509"; - // Java Key Store - standard type of keystores used by keytool utility - @Nls public static final String JKS = "JKS"; - // another standard type of keystore - @Nls public static final String PKCS12 = "PKCS12"; - // type of trust manager factory - @Nls public static final String PKIX = "PKIX"; - @Nls public static final String TLS = "TLS"; - - /** - * Utility class - */ - private CertificateUtil() { - // empty - } + private CertificateUtil() { } @Nullable public static X509Certificate loadX509Certificate(@NotNull String path) { - InputStream stream = null; try { - stream = new FileInputStream(path); - return (X509Certificate)ourFactory.generateCertificate(stream); + InputStream stream = new FileInputStream(path); + try { + return (X509Certificate)ourFactory.generateCertificate(stream); + } + finally { + stream.close(); + } } catch (Exception e) { - LOG.error("Can't add certificate for path: " + path, e); + Logger.getInstance(CertificateUtil.class).error("Can't add certificate for path: " + path, e); return null; } - finally { - StreamUtil.closeStream(stream); - } } /** diff --git a/platform/platform-api/src/com/intellij/util/ui/Animator.java b/platform/platform-api/src/com/intellij/util/ui/Animator.java index 94da2b15ba7a..55578fe8d994 100644 --- a/platform/platform-api/src/com/intellij/util/ui/Animator.java +++ b/platform/platform-api/src/com/intellij/util/ui/Animator.java @@ -64,7 +64,7 @@ public abstract class Animator implements Disposable { reset(); - if (ApplicationManager.getApplication() == null) { + if (noApplication()) { animationDone(); } } @@ -133,8 +133,7 @@ public abstract class Animator implements Disposable { } public void resume() { - final Application app = ApplicationManager.getApplication(); - if (app == null || app.isUnitTestMode()) { + if (noApplication()) { animationDone(); return; } @@ -164,6 +163,11 @@ public abstract class Animator implements Disposable { } } + protected boolean noApplication() { + Application app = ApplicationManager.getApplication(); + return app == null || app.isUnitTestMode(); + } + public abstract void paintNow(int frame, int totalFrames, int cycle); @Override diff --git a/platform/platform-impl/platform-impl.iml b/platform/platform-impl/platform-impl.iml index a0880c4ac95a..ab82f91384ff 100644 --- a/platform/platform-impl/platform-impl.iml +++ b/platform/platform-impl/platform-impl.iml @@ -16,7 +16,6 @@ - diff --git a/platform/platform-impl/src/com/intellij/concurrency/JobLauncherImpl.java b/platform/platform-impl/src/com/intellij/concurrency/JobLauncherImpl.java index b33611da8866..876474b9124b 100644 --- a/platform/platform-impl/src/com/intellij/concurrency/JobLauncherImpl.java +++ b/platform/platform-impl/src/com/intellij/concurrency/JobLauncherImpl.java @@ -146,19 +146,11 @@ public class JobLauncherImpl extends JobLauncher { // This implementation is not really async @NotNull @Override - public AsyncFutureResult invokeConcurrentlyUnderProgressAsync(@NotNull List things, + public AsyncFuture invokeConcurrentlyUnderProgressAsync(@NotNull List things, ProgressIndicator progress, boolean failFastOnAcquireReadAction, @NotNull Processor thingProcessor) { - final AsyncFutureResult asyncFutureResult = AsyncFutureFactory.getInstance().createAsyncFutureResult(); - try { - final boolean result = invokeConcurrentlyUnderProgress(things, progress, failFastOnAcquireReadAction, thingProcessor); - asyncFutureResult.set(result); - } - catch (Throwable t) { - asyncFutureResult.setException(t); - } - return asyncFutureResult; + return AsyncUtil.wrapBoolean(invokeConcurrentlyUnderProgress(things, progress, failFastOnAcquireReadAction, thingProcessor)); } @NotNull diff --git a/platform/platform-impl/src/com/intellij/designer/DesignerEditorPanelFacade.java b/platform/platform-impl/src/com/intellij/designer/DesignerEditorPanelFacade.java new file mode 100644 index 000000000000..fa9ecf8610ac --- /dev/null +++ b/platform/platform-impl/src/com/intellij/designer/DesignerEditorPanelFacade.java @@ -0,0 +1,29 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.designer; + +import com.intellij.openapi.ui.ThreeComponentsSplitter; + +/** + * @author Alexander Lobas + */ +public interface DesignerEditorPanelFacade { + Object getClientProperty(Object key); + + void putClientProperty(Object key, Object value); + + ThreeComponentsSplitter getContentSplitter(); +} \ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/designer/LightFillLayout.java b/platform/platform-impl/src/com/intellij/designer/LightFillLayout.java new file mode 100644 index 000000000000..af0ca87f1e05 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/designer/LightFillLayout.java @@ -0,0 +1,122 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.designer; + +import com.intellij.util.Function; + +import javax.swing.*; +import java.awt.*; + +/** + * @author Alexander Lobas + */ +public class LightFillLayout implements LayoutManager2 { + @Override + public void addLayoutComponent(Component comp, Object constraints) { + } + + @Override + public float getLayoutAlignmentX(Container target) { + return 0.5f; + } + + @Override + public float getLayoutAlignmentY(Container target) { + return 0.5f; + } + + @Override + public void invalidateLayout(Container target) { + } + + @Override + public void addLayoutComponent(String name, Component comp) { + } + + @Override + public void removeLayoutComponent(Component comp) { + } + + @Override + public Dimension maximumLayoutSize(Container target) { + return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE); + } + + @Override + public Dimension preferredLayoutSize(Container parent) { + return layoutSize(parent, new Function() { + @Override + public Dimension fun(Component component) { + return component.getPreferredSize(); + } + }); + } + + @Override + public Dimension minimumLayoutSize(Container parent) { + return layoutSize(parent, new Function() { + @Override + public Dimension fun(Component component) { + return component.getMinimumSize(); + } + }); + } + + private static Dimension layoutSize(Container parent, Function getSize) { + Component toolbar = parent.getComponent(0); + Dimension toolbarSize = toolbar.isVisible() ? getSize.fun(toolbar) : new Dimension(); + Dimension contentSize = getSize.fun(parent.getComponent(1)); + int extraWidth = 0; + JComponent jParent = (JComponent)parent; + if (jParent.getClientProperty(LightToolWindow.LEFT_MIN_KEY) != null) { + extraWidth += LightToolWindow.MINIMIZE_WIDTH; + } + if (jParent.getClientProperty(LightToolWindow.RIGHT_MIN_KEY) != null) { + extraWidth += LightToolWindow.MINIMIZE_WIDTH; + } + return new Dimension(Math.max(toolbarSize.width, contentSize.width + extraWidth), toolbarSize.height + contentSize.height); + } + + @Override + public void layoutContainer(Container parent) { + int leftWidth = 0; + int rightWidth = 0; + JComponent jParent = (JComponent)parent; + JComponent left = (JComponent)jParent.getClientProperty(LightToolWindow.LEFT_MIN_KEY); + if (left != null) { + leftWidth = LightToolWindow.MINIMIZE_WIDTH; + } + JComponent right = (JComponent)jParent.getClientProperty(LightToolWindow.RIGHT_MIN_KEY); + if (right != null) { + rightWidth = LightToolWindow.MINIMIZE_WIDTH; + } + int extraWidth = leftWidth + rightWidth; + + int width = parent.getWidth() - extraWidth; + int height = parent.getHeight(); + Component toolbar = parent.getComponent(0); + Dimension toolbarSize = toolbar.isVisible() ? toolbar.getPreferredSize() : new Dimension(); + toolbar.setBounds(leftWidth, 0, width, toolbarSize.height); + parent.getComponent(1).setBounds(leftWidth, toolbarSize.height, width, height - toolbarSize.height); + + if (left != null) { + left.setBounds(0, 0, leftWidth, height); + } + if (right != null) { + right.setBounds(width + leftWidth, 0, rightWidth, height); + } + } +} \ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/designer/LightToolWindow.java b/platform/platform-impl/src/com/intellij/designer/LightToolWindow.java new file mode 100644 index 000000000000..ce3cb33deb90 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/designer/LightToolWindow.java @@ -0,0 +1,562 @@ +/* + * Copyright 2000-2013 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.designer; + +import com.intellij.icons.AllIcons; +import com.intellij.ide.util.PropertiesComponent; +import com.intellij.openapi.actionSystem.*; +import com.intellij.openapi.actionSystem.impl.ActionManagerImpl; +import com.intellij.openapi.actionSystem.impl.MenuItemPresentationFactory; +import com.intellij.openapi.keymap.KeymapUtil; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.ThreeComponentsSplitter; +import com.intellij.openapi.util.SystemInfo; +import com.intellij.openapi.wm.*; +import com.intellij.openapi.wm.ex.ToolWindowEx; +import com.intellij.openapi.wm.impl.AnchoredButton; +import com.intellij.openapi.wm.impl.InternalDecorator; +import com.intellij.openapi.wm.impl.StripeButtonUI; +import com.intellij.openapi.wm.impl.content.ToolWindowContentUi; +import com.intellij.ui.*; +import com.intellij.ui.components.panels.Wrapper; +import com.intellij.ui.tabs.TabsUtil; +import com.intellij.util.ui.EmptyIcon; +import com.intellij.util.ui.UIUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; +import java.awt.image.BufferedImage; + +/** + * @author Alexander Lobas + */ +public class LightToolWindow extends JPanel { + public static final String LEFT_MIN_KEY = "left"; + public static final String RIGHT_MIN_KEY = "right"; + public static final int MINIMIZE_WIDTH = 25; + private static final String IGNORE_WIDTH_KEY = "ignore_width"; + + private final LightToolWindowContent myContent; + private final JComponent myFocusedComponent; + private final ThreeComponentsSplitter myContentSplitter; + private ToolWindowAnchor myAnchor; + private final Project myProject; + private final LightToolWindowManager myManager; + private final PropertiesComponent myPropertiesComponent; + private boolean myShowContent; + private final String myShowStateKey; + private int myCurrentWidth; + private final String myWidthKey; + private final JPanel myMinimizeComponent; + private final AnchoredButton myMinimizeButton; + + private final TogglePinnedModeAction myToggleAutoHideModeAction = new TogglePinnedModeAction(); + private final ToggleDockModeAction myToggleDockModeAction = new ToggleDockModeAction(); + private final ToggleFloatingModeAction myToggleFloatingModeAction = new ToggleFloatingModeAction(); + private final ToggleSideModeAction myToggleSideModeAction = new ToggleSideModeAction(); + + private final ComponentListener myWidthListener = new ComponentAdapter() { + @Override + public void componentResized(ComponentEvent e) { + int width = isLeft() ? myContentSplitter.getFirstSize() : myContentSplitter.getLastSize(); + if (width > 0 && width != myCurrentWidth && myContentSplitter.getInnerComponent().getClientProperty(IGNORE_WIDTH_KEY) == null) { + myCurrentWidth = width; + myPropertiesComponent.setValue(myWidthKey, Integer.toString(width)); + } + } + }; + + public LightToolWindow(@NotNull LightToolWindowContent content, + @NotNull String title, + @NotNull Icon icon, + @NotNull JComponent component, + @NotNull JComponent focusedComponent, + @NotNull ThreeComponentsSplitter contentSplitter, + @Nullable ToolWindowAnchor anchor, + @NotNull LightToolWindowManager manager, + @NotNull Project project, + @NotNull PropertiesComponent propertiesComponent, + @NotNull String key, + int defaultWidth, + @Nullable AnAction[] actions) { + super(new BorderLayout()); + myContent = content; + myFocusedComponent = focusedComponent; + myContentSplitter = contentSplitter; + myAnchor = anchor; + myProject = project; + myManager = manager; + myPropertiesComponent = propertiesComponent; + + myShowStateKey = LightToolWindowManager.EDITOR_MODE + key + ".SHOW"; + myWidthKey = LightToolWindowManager.EDITOR_MODE + key + ".WIDTH"; + + HeaderPanel header = new HeaderPanel(); + header.setLayout(new BorderLayout()); + add(header, BorderLayout.NORTH); + + JLabel titleLabel = new JLabel(title); + titleLabel.setBorder(IdeBorderFactory.createEmptyBorder(2, 5, 2, 10)); + titleLabel.setFont(UIUtil.getLabelFont(UIUtil.FontSize.SMALL)); + header.add(titleLabel, BorderLayout.CENTER); + + JPanel actionPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 5, 0)); + actionPanel.setBorder(IdeBorderFactory.createEmptyBorder(3, 0, 2, 0)); + actionPanel.setOpaque(false); + header.add(actionPanel, BorderLayout.EAST); + + if (actions != null) { + for (AnAction action : actions) { + addAction(actionPanel, action); + } + + actionPanel.add(new JLabel(AllIcons.General.Divider)); + } + + addAction(actionPanel, new GearAction()); + addAction(actionPanel, new HideAction()); + + JPanel contentWrapper = new JPanel(new BorderLayout()); + contentWrapper.setBorder(IdeBorderFactory.createBorder(SideBorder.TOP)); + contentWrapper.add(component, BorderLayout.CENTER); + + add(contentWrapper, BorderLayout.CENTER); + + addMouseListener(new MouseAdapter() { + public void mouseReleased(final MouseEvent e) { + IdeFocusManager.getInstance(myProject).requestFocus(myFocusedComponent, true); + } + }); + + addMouseListener(new PopupHandler() { + public void invokePopup(Component component, int x, int y) { + showGearPopup(component, x, y); + } + }); + + myMinimizeButton = new AnchoredButton(title, icon) { + @Override + public void updateUI() { + setUI(StripeButtonUI.createUI(this)); + setFont(UIUtil.getLabelFont(UIUtil.FontSize.SMALL)); + } + + @Override + public int getMnemonic2() { + return 0; + } + + @Override + public ToolWindowAnchor getAnchor() { + return myAnchor; + } + }; + myMinimizeButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + myMinimizeButton.setSelected(false); + updateContent(true, true); + } + }); + myMinimizeButton.setBorder(BorderFactory.createEmptyBorder(5, 5, 0, 5)); + myMinimizeButton.setFocusable(false); + + myMinimizeButton.setRolloverEnabled(true); + myMinimizeButton.setOpaque(false); + + myMinimizeComponent = new JPanel() { + @Override + public void doLayout() { + Dimension size = myMinimizeButton.getPreferredSize(); + myMinimizeButton.setBounds(0, 0, getWidth(), size.height); + } + }; + myMinimizeComponent.add(myMinimizeButton); + + configureBorder(); + configureWidth(defaultWidth); + updateContent(myPropertiesComponent.getBoolean(myShowStateKey, true), false); + } + + private void configureBorder() { + int borderStyle = isLeft() ? SideBorder.RIGHT : SideBorder.LEFT; + setBorder(IdeBorderFactory.createBorder(borderStyle)); + myMinimizeComponent.setBorder(IdeBorderFactory.createBorder(borderStyle)); + } + + private void configureWidth(int defaultWidth) { + myCurrentWidth = myPropertiesComponent.getOrInitInt(myWidthKey, defaultWidth); + updateWidth(); + myContentSplitter.getInnerComponent().addComponentListener(myWidthListener); + } + + private void updateWidth() { + if (isLeft()) { + myContentSplitter.setFirstSize(myCurrentWidth); + } + else { + myContentSplitter.setLastSize(myCurrentWidth); + } + } + + public void updateAnchor(ToolWindowAnchor newAnchor) { + JComponent minimizeParent = myContentSplitter.getInnerComponent(); + minimizeParent.putClientProperty(IGNORE_WIDTH_KEY, Boolean.TRUE); + + if (myShowContent) { + Object oldWindow = isLeft() ? myContentSplitter.getFirstComponent() : myContentSplitter.getLastComponent(); + if (oldWindow == this) { + setContentComponent(null); + } + } + else { + String key = getMinKey(); + if (minimizeParent.getClientProperty(key) == myMinimizeComponent) { + minimizeParent.putClientProperty(key, null); + } + minimizeParent.putClientProperty(isLeft() ? RIGHT_MIN_KEY : LEFT_MIN_KEY, myMinimizeComponent); + minimizeParent.revalidate(); + } + + myAnchor = newAnchor; + configureBorder(); + updateWidth(); + + if (myShowContent) { + setContentComponent(this); + } + + minimizeParent.putClientProperty(IGNORE_WIDTH_KEY, null); + } + + private void updateContent(boolean show, boolean flag) { + myShowContent = show; + + String key = getMinKey(); + + JComponent minimizeParent = myContentSplitter.getInnerComponent(); + + if (show) { + minimizeParent.putClientProperty(key, null); + minimizeParent.remove(myMinimizeComponent); + } + + setContentComponent(show ? this : null); + + if (!show) { + minimizeParent.putClientProperty(key, myMinimizeComponent); + minimizeParent.add(myMinimizeComponent); + } + + minimizeParent.revalidate(); + + if (flag) { + myPropertiesComponent.setValue(myShowStateKey, Boolean.toString(show)); + } + } + + private void setContentComponent(JComponent component) { + if (isLeft()) { + myContentSplitter.setFirstComponent(component); + } + else { + myContentSplitter.setLastComponent(component); + } + } + + public void dispose() { + JComponent minimizeParent = myContentSplitter.getInnerComponent(); + minimizeParent.removeComponentListener(myWidthListener); + + setContentComponent(null); + myContent.dispose(); + + if (!myShowContent) { + minimizeParent.putClientProperty(getMinKey(), null); + minimizeParent.remove(myMinimizeComponent); + minimizeParent.revalidate(); + } + } + + private String getMinKey() { + return isLeft() ? LEFT_MIN_KEY : RIGHT_MIN_KEY; + } + + public Object getContent() { + return myContent; + } + + private boolean isLeft() { + return myAnchor == ToolWindowAnchor.LEFT; + } + + private boolean isActive() { + IdeFocusManager fm = IdeFocusManager.getInstance(myProject); + Component component = fm.getFocusedDescendantFor(this); + if (component != null) { + return true; + } + Component owner = fm.getLastFocusedFor(WindowManager.getInstance().getIdeFrame(myProject)); + return owner != null && SwingUtilities.isDescendingFrom(owner, this); + } + + private void addAction(JPanel actionPanel, AnAction action) { + actionPanel.add(new ActionButton(action)); + } + + private DefaultActionGroup createGearPopupGroup() { + DefaultActionGroup group = new DefaultActionGroup(); + + group.add(myManager.createGearActions()); + group.addSeparator(); + + ToolWindowType type = myManager.getToolWindow().getType(); + if (type == ToolWindowType.DOCKED) { + group.add(myToggleAutoHideModeAction); + group.add(myToggleDockModeAction); + group.add(myToggleFloatingModeAction); + group.add(myToggleSideModeAction); + } + else if (type == ToolWindowType.FLOATING) { + group.add(myToggleAutoHideModeAction); + group.add(myToggleFloatingModeAction); + } + else if (type == ToolWindowType.SLIDING) { + group.add(myToggleDockModeAction); + group.add(myToggleFloatingModeAction); + } + + return group; + } + + private void showGearPopup(Component component, int x, int y) { + ActionPopupMenu popupMenu = + ((ActionManagerImpl)ActionManager.getInstance()) + .createActionPopupMenu(ToolWindowContentUi.POPUP_PLACE, createGearPopupGroup(), new MenuItemPresentationFactory(true)); + popupMenu.getComponent().show(component, x, y); + } + + private class GearAction extends AnAction { + public GearAction() { + Presentation presentation = getTemplatePresentation(); + presentation.setIcon(AllIcons.General.Gear); + presentation.setHoveredIcon(AllIcons.General.GearHover); + } + + @Override + public void actionPerformed(AnActionEvent e) { + int x = 0; + int y = 0; + InputEvent inputEvent = e.getInputEvent(); + if (inputEvent instanceof MouseEvent) { + x = ((MouseEvent)inputEvent).getX(); + y = ((MouseEvent)inputEvent).getY(); + } + + showGearPopup(inputEvent.getComponent(), x, y); + } + } + + private class HideAction extends AnAction { + public HideAction() { + Presentation presentation = getTemplatePresentation(); + presentation.setText(UIBundle.message("tool.window.hide.action.name")); + if (isLeft()) { + presentation.setIcon(AllIcons.General.HideLeftPart); + presentation.setHoveredIcon(AllIcons.General.HideLeftPartHover); + } + else { + presentation.setIcon(AllIcons.General.HideRightPart); + presentation.setHoveredIcon(AllIcons.General.HideRightPartHover); + } + } + + @Override + public void actionPerformed(AnActionEvent e) { + updateContent(false, true); + } + } + + private class TogglePinnedModeAction extends ToggleAction { + public TogglePinnedModeAction() { + copyFrom(ActionManager.getInstance().getAction(InternalDecorator.TOGGLE_PINNED_MODE_ACTION_ID)); + } + + @Override + public boolean isSelected(AnActionEvent e) { + return !myManager.getToolWindow().isAutoHide(); + } + + @Override + public void setSelected(AnActionEvent e, boolean state) { + ToolWindow window = myManager.getToolWindow(); + window.setAutoHide(!window.isAutoHide()); + myManager.setEditorMode(null); + } + } + + private class ToggleDockModeAction extends ToggleAction { + public ToggleDockModeAction() { + copyFrom(ActionManager.getInstance().getAction(InternalDecorator.TOGGLE_DOCK_MODE_ACTION_ID)); + } + + @Override + public boolean isSelected(AnActionEvent e) { + return myManager.getToolWindow().getType() == ToolWindowType.DOCKED; + } + + @Override + public void setSelected(AnActionEvent e, boolean state) { + ToolWindow window = myManager.getToolWindow(); + ToolWindowType type = window.getType(); + if (type == ToolWindowType.DOCKED) { + window.setType(ToolWindowType.SLIDING, null); + } + else if (type == ToolWindowType.SLIDING) { + window.setType(ToolWindowType.DOCKED, null); + } + myManager.setEditorMode(null); + } + } + + private class ToggleFloatingModeAction extends ToggleAction { + public ToggleFloatingModeAction() { + copyFrom(ActionManager.getInstance().getAction(InternalDecorator.TOGGLE_FLOATING_MODE_ACTION_ID)); + } + + @Override + public boolean isSelected(AnActionEvent e) { + return myManager.getToolWindow().getType() == ToolWindowType.FLOATING; + } + + @Override + public void setSelected(AnActionEvent e, boolean state) { + ToolWindow window = myManager.getToolWindow(); + ToolWindowType type = window.getType(); + if (type == ToolWindowType.FLOATING) { + window.setType(((ToolWindowEx)window).getInternalType(), null); + } + else { + window.setType(ToolWindowType.FLOATING, null); + } + myManager.setEditorMode(null); + } + } + + private class ToggleSideModeAction extends ToggleAction { + public ToggleSideModeAction() { + copyFrom(ActionManager.getInstance().getAction(InternalDecorator.TOGGLE_SIDE_MODE_ACTION_ID)); + } + + @Override + public boolean isSelected(AnActionEvent e) { + return myManager.getToolWindow().isSplitMode(); + } + + @Override + public void setSelected(AnActionEvent e, boolean state) { + myManager.getToolWindow().setSplitMode(state, null); + myManager.setEditorMode(null); + } + } + + private class ActionButton extends Wrapper implements ActionListener { + private final AnAction myAction; + + public ActionButton(AnAction action) { + myAction = action; + + Presentation presentation = action.getTemplatePresentation(); + InplaceButton button = new InplaceButton(KeymapUtil.createTooltipText(presentation.getText(), action), EmptyIcon.ICON_16, this) { + @Override + public boolean isActive() { + return LightToolWindow.this.isActive(); + } + }; + button.setHoveringEnabled(!SystemInfo.isMac); + setContent(button); + + Icon icon = presentation.getIcon(); + Icon hoveredIcon = presentation.getHoveredIcon(); + button.setIcons(icon, icon, hoveredIcon == null ? icon : hoveredIcon); + } + + @Override + public void actionPerformed(ActionEvent e) { + InputEvent inputEvent = e.getSource() instanceof InputEvent ? (InputEvent)e.getSource() : null; + myAction.actionPerformed(AnActionEvent.createFromInputEvent(myAction, inputEvent, ActionPlaces.UNKNOWN)); + } + } + + private class HeaderPanel extends JPanel { + private BufferedImage myActiveImage; + private BufferedImage myImage; + + @Override + public Dimension getPreferredSize() { + Dimension size = super.getPreferredSize(); + return new Dimension(size.width, TabsUtil.getTabsHeight()); + } + + @Override + public Dimension getMinimumSize() { + Dimension size = super.getMinimumSize(); + return new Dimension(size.width, TabsUtil.getTabsHeight()); + } + + protected void _paintComponent(Graphics g) { // XXX: visual artifacts on linux + Rectangle r = getBounds(); + + Image image; + if (isActive()) { + if (myActiveImage == null || myActiveImage.getHeight() != r.height) { + myActiveImage = drawToBuffer(true, r.height); + } + image = myActiveImage; + } + else { + if (myImage == null || myImage.getHeight() != r.height) { + myImage = drawToBuffer(false, r.height); + } + image = myImage; + } + + Graphics2D g2d = (Graphics2D)g; + Rectangle clipBounds = g2d.getClip().getBounds(); + for (int x = clipBounds.x; x < clipBounds.x + clipBounds.width; x += 150) { + g2d.drawImage(image, x, 0, null); + } + } + + protected boolean isActive() { + return LightToolWindow.this.isActive(); + } + } + + private static BufferedImage drawToBuffer(boolean active, int height) { + final int width = 150; + + BufferedImage image = UIUtil.createImage(width, height, BufferedImage.TYPE_INT_ARGB); + Graphics2D g = image.createGraphics(); + UIUtil.drawHeader(g, 0, width, height, active, true, false, false); + g.dispose(); + + return image; + } +} \ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/designer/LightToolWindowContent.java b/platform/platform-impl/src/com/intellij/designer/LightToolWindowContent.java new file mode 100644 index 000000000000..c3e00f7236f5 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/designer/LightToolWindowContent.java @@ -0,0 +1,23 @@ +/* + * Copyright 2000-2013 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.designer; + +/** + * @author Alexander Lobas + */ +public interface LightToolWindowContent { + void dispose(); +} \ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/designer/LightToolWindowManager.java b/platform/platform-impl/src/com/intellij/designer/LightToolWindowManager.java new file mode 100644 index 000000000000..0bfb083dec94 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/designer/LightToolWindowManager.java @@ -0,0 +1,323 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.designer; + +import com.intellij.ide.util.PropertiesComponent; +import com.intellij.openapi.actionSystem.ActionGroup; +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.DefaultActionGroup; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.components.ProjectComponent; +import com.intellij.openapi.fileEditor.FileEditor; +import com.intellij.openapi.fileEditor.FileEditorManager; +import com.intellij.openapi.fileEditor.FileEditorManagerEvent; +import com.intellij.openapi.fileEditor.FileEditorManagerListener; +import com.intellij.openapi.project.DumbAwareRunnable; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.startup.StartupManager; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.wm.ToolWindow; +import com.intellij.openapi.wm.ToolWindowAnchor; +import com.intellij.openapi.wm.ex.ToolWindowEx; +import com.intellij.util.ParameterizedRunnable; +import com.intellij.util.messages.MessageBusConnection; +import com.intellij.util.ui.update.MergingUpdateQueue; +import com.intellij.util.ui.update.Update; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; + +/** + * @author Alexander Lobas + */ +public abstract class LightToolWindowManager implements ProjectComponent { + public static final String EDITOR_MODE = "UI_DESIGNER_EDITOR_MODE."; + + private final MergingUpdateQueue myWindowQueue = new MergingUpdateQueue(getComponentName(), 200, true, null); + protected final Project myProject; + protected final FileEditorManager myFileEditorManager; + protected volatile ToolWindow myToolWindow; + private volatile boolean myToolWindowDisposed; + + private final PropertiesComponent myPropertiesComponent; + public final String myEditorModeKey; + private ToggleEditorModeAction myLeftEditorModeAction; + private ToggleEditorModeAction myRightEditorModeAction; + + private MessageBusConnection myConnection; + private final FileEditorManagerListener myListener = new FileEditorManagerListener() { + @Override + public void fileOpened(@NotNull FileEditorManager source, @NotNull VirtualFile file) { + bindToDesigner(getActiveDesigner()); + } + + @Override + public void fileClosed(@NotNull FileEditorManager source, @NotNull VirtualFile file) { + ApplicationManager.getApplication().invokeLater(new Runnable() { + @Override + public void run() { + bindToDesigner(getActiveDesigner()); + } + }); + } + + @Override + public void selectionChanged(@NotNull FileEditorManagerEvent event) { + bindToDesigner(getDesigner(event.getNewEditor())); + } + }; + + ////////////////////////////////////////////////////////////////////////////////////////// + // + // ToolWindow + // + ////////////////////////////////////////////////////////////////////////////////////////// + + protected LightToolWindowManager(Project project, FileEditorManager fileEditorManager) { + myProject = project; + myFileEditorManager = fileEditorManager; + myPropertiesComponent = PropertiesComponent.getInstance(myProject); + myEditorModeKey = EDITOR_MODE + getComponentName() + ".STATE"; + } + + @Override + public void projectOpened() { + initToolWindow(); + + StartupManager.getInstance(myProject).runWhenProjectIsInitialized(new DumbAwareRunnable() { + public void run() { + if (getEditorMode() == null) { + initListeners(); + bindToDesigner(getActiveDesigner()); + } + } + }); + } + + @Override + public void projectClosed() { + if (!myToolWindowDisposed) { + disposeComponent(); + myToolWindowDisposed = true; + myToolWindow = null; + } + } + + private void initListeners() { + myConnection = myProject.getMessageBus().connect(myProject); + myConnection.subscribe(FileEditorManagerListener.FILE_EDITOR_MANAGER, myListener); + } + + private void removeListeners() { + myConnection.disconnect(); + myConnection = null; + } + + @Nullable + protected abstract DesignerEditorPanelFacade getDesigner(FileEditor editor); + + @Nullable + public DesignerEditorPanelFacade getActiveDesigner() { + for (FileEditor editor : myFileEditorManager.getSelectedEditors()) { + DesignerEditorPanelFacade designer = getDesigner(editor); + if (designer != null) { + return designer; + } + } + + return null; + } + + private void bindToDesigner(final DesignerEditorPanelFacade designer) { + myWindowQueue.cancelAllUpdates(); + myWindowQueue.queue(new Update("update") { + @Override + public void run() { + if (myToolWindowDisposed) { + return; + } + if (myToolWindow == null) { + if (designer == null) { + return; + } + initToolWindow(); + } + updateToolWindow(designer); + } + }); + } + + protected abstract void initToolWindow(); + + protected abstract void updateToolWindow(@Nullable DesignerEditorPanelFacade designer); + + protected final void initGearActions() { + ToolWindowEx toolWindow = (ToolWindowEx)myToolWindow; + toolWindow.setAdditionalGearActions(new DefaultActionGroup(createGearActions())); + } + + protected abstract ToolWindowAnchor getAnchor(); + + @Override + public void initComponent() { + } + + @Override + public void disposeComponent() { + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // + // LightToolWindow + // + ////////////////////////////////////////////////////////////////////////////////////////// + + public final ActionGroup createGearActions() { + DefaultActionGroup group = new DefaultActionGroup("In Editor Mode", true); + + if (myLeftEditorModeAction == null) { + myLeftEditorModeAction = createToggleAction(ToolWindowAnchor.LEFT); + } + group.add(myLeftEditorModeAction); + + if (myRightEditorModeAction == null) { + myRightEditorModeAction = createToggleAction(ToolWindowAnchor.RIGHT); + } + group.add(myRightEditorModeAction); + + return group; + } + + protected abstract ToggleEditorModeAction createToggleAction(ToolWindowAnchor anchor); + + public final void bind(@NotNull DesignerEditorPanelFacade designer) { + if (isEditorMode()) { + myCreateAction.run(designer); + } + } + + public final void dispose(@NotNull DesignerEditorPanelFacade designer) { + if (isEditorMode()) { + disposeContent(designer); + } + } + + protected final Object getContent(@NotNull DesignerEditorPanelFacade designer) { + LightToolWindow toolWindow = (LightToolWindow)designer.getClientProperty(getComponentName()); + return toolWindow.getContent(); + } + + protected abstract LightToolWindow createContent(@NotNull DesignerEditorPanelFacade designer); + + protected final LightToolWindow createContent(@NotNull DesignerEditorPanelFacade designer, + @NotNull LightToolWindowContent content, + @NotNull String title, + @NotNull Icon icon, + @NotNull JComponent component, + @NotNull JComponent focusedComponent, + int defaultWidth, + @Nullable AnAction[] actions) { + return new LightToolWindow(content, + title, + icon, + component, + focusedComponent, + designer.getContentSplitter(), + getEditorMode(), + this, + myProject, + myPropertiesComponent, + getComponentName(), + defaultWidth, + actions); + } + + protected final void disposeContent(DesignerEditorPanelFacade designer) { + String key = getComponentName(); + LightToolWindow toolWindow = (LightToolWindow)designer.getClientProperty(key); + designer.putClientProperty(key, null); + toolWindow.dispose(); + } + + private final ParameterizedRunnable myCreateAction = new ParameterizedRunnable() { + @Override + public void run(DesignerEditorPanelFacade designer) { + designer.putClientProperty(getComponentName(), createContent(designer)); + } + }; + + private final ParameterizedRunnable myUpdateAnchorAction = + new ParameterizedRunnable() { + @Override + public void run(DesignerEditorPanelFacade designer) { + LightToolWindow toolWindow = (LightToolWindow)designer.getClientProperty(getComponentName()); + toolWindow.updateAnchor(getEditorMode()); + } + }; + + private final ParameterizedRunnable myDisposeAction = new ParameterizedRunnable() { + @Override + public void run(DesignerEditorPanelFacade designer) { + disposeContent(designer); + } + }; + + private void runUpdateContent(ParameterizedRunnable action) { + for (FileEditor editor : myFileEditorManager.getAllEditors()) { + DesignerEditorPanelFacade designer = getDesigner(editor); + if (designer != null) { + action.run(designer); + } + } + } + + protected final boolean isEditorMode() { + return getEditorMode() != null; + } + + @Nullable + final ToolWindowAnchor getEditorMode() { + String value = myPropertiesComponent.getValue(myEditorModeKey); + if (value == null) { + return getAnchor(); + } + return value.equals("ToolWindow") ? null : ToolWindowAnchor.fromText(value); + } + + final void setEditorMode(@Nullable ToolWindowAnchor newState) { + ToolWindowAnchor oldState = getEditorMode(); + myPropertiesComponent.setValue(myEditorModeKey, newState == null ? "ToolWindow" : newState.toString()); + + if (oldState != null && newState != null) { + runUpdateContent(myUpdateAnchorAction); + } + else if (newState != null) { + removeListeners(); + updateToolWindow(null); + runUpdateContent(myCreateAction); + } + else { + runUpdateContent(myDisposeAction); + initListeners(); + bindToDesigner(getActiveDesigner()); + } + } + + final ToolWindow getToolWindow() { + return myToolWindow; + } +} \ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/designer/ToggleEditorModeAction.java b/platform/platform-impl/src/com/intellij/designer/ToggleEditorModeAction.java new file mode 100644 index 000000000000..668a0405f4b2 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/designer/ToggleEditorModeAction.java @@ -0,0 +1,60 @@ +/* + * Copyright 2000-2013 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.designer; + +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.ToggleAction; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.wm.ToolWindowAnchor; + +/** + * @author Alexander Lobas + */ +public abstract class ToggleEditorModeAction extends ToggleAction { + protected final LightToolWindowManager myManager; + protected final Project myProject; + private final ToolWindowAnchor myAnchor; + + public ToggleEditorModeAction(LightToolWindowManager manager, Project project, ToolWindowAnchor anchor) { + super(StringUtil.capitalize(anchor.toString()), "Pin/unpin tool window to " + anchor + " side UI Designer Editor", null); + myManager = manager; + myProject = project; + myAnchor = anchor; + } + + @Override + public boolean isSelected(AnActionEvent e) { + return myAnchor == myManager.getEditorMode(); + } + + @Override + public void setSelected(AnActionEvent e, boolean state) { + if (state) { + myManager.setEditorMode(myAnchor); + + LightToolWindowManager manager = getOppositeManager(); + if (manager.getEditorMode() == myAnchor) { + manager.setEditorMode(myAnchor == ToolWindowAnchor.LEFT ? ToolWindowAnchor.RIGHT : ToolWindowAnchor.LEFT); + } + } + else { + myManager.setEditorMode(null); + } + } + + protected abstract LightToolWindowManager getOppositeManager(); +} \ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/diagnostic/ITNReporter.java b/platform/platform-impl/src/com/intellij/diagnostic/ITNReporter.java index e430f9fdb513..b951b59ed9dd 100644 --- a/platform/platform-impl/src/com/intellij/diagnostic/ITNReporter.java +++ b/platform/platform-impl/src/com/intellij/diagnostic/ITNReporter.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,11 +16,11 @@ package com.intellij.diagnostic; import com.intellij.CommonBundle; -import com.intellij.errorreport.ErrorReportSender; import com.intellij.errorreport.bean.ErrorBean; import com.intellij.errorreport.error.InternalEAPException; import com.intellij.errorreport.error.NoSuchEAPUserException; import com.intellij.errorreport.error.UpdateAvailableException; +import com.intellij.errorreport.itn.ITNProxy; import com.intellij.ide.DataManager; import com.intellij.ide.plugins.IdeaPluginDescriptor; import com.intellij.ide.plugins.PluginManager; @@ -32,6 +32,7 @@ import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.ErrorReportSubmitter; import com.intellij.openapi.diagnostic.IdeaLoggingEvent; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.diagnostic.SubmittedReportInfo; import com.intellij.openapi.extensions.PluginId; import com.intellij.openapi.project.Project; @@ -39,7 +40,7 @@ import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.Consumer; import com.intellij.xml.util.XmlStringUtil; -import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; import javax.swing.*; import java.awt.*; @@ -49,60 +50,39 @@ import java.awt.*; */ public class ITNReporter extends ErrorReportSubmitter { private static int previousExceptionThreadId = 0; - private static boolean wasException = false; - @NonNls private static final String URL_HEADER = "http://www.intellij.net/tracker/idea/viewSCR?publicId="; + @Override public String getReportActionText() { return DiagnosticBundle.message("error.report.to.jetbrains.action"); } - public SubmittedReportInfo submit(IdeaLoggingEvent[] events, Component parentComponent) { - // obsolete API - return new SubmittedReportInfo(null, "0", SubmittedReportInfo.SubmissionStatus.FAILED); - } - @Override - public boolean trySubmitAsync(IdeaLoggingEvent[] events, - String additionalInfo, - Component parentComponent, - Consumer consumer) { - return sendError(events[0], additionalInfo, parentComponent, consumer); + public boolean submit(@NotNull IdeaLoggingEvent[] events, + String additionalInfo, + @NotNull Component parentComponent, + @NotNull Consumer consumer) { + ErrorBean errorBean = new ErrorBean(events[0].getThrowable(), IdeaLogger.ourLastActionId); + return doSubmit(events[0], parentComponent, consumer, errorBean, additionalInfo); } /** - * Could be used to enable error reporting even in release versions from non-internal mode. - * @return false by default. + * Used to enable error reporting even in release versions. */ - @SuppressWarnings({"MethodMayBeStatic", "UnusedParameters"}) public boolean showErrorInRelease(IdeaLoggingEvent event) { return false; } - /** - * @noinspection ThrowablePrintStackTrace - */ - private static boolean sendError(IdeaLoggingEvent event, - String additionalInfo, - final Component parentComponent, - final Consumer callback) { - ErrorBean errorBean = new ErrorBean(event.getThrowable(), IdeaLogger.ourLastActionId); - - return doSubmit(event, parentComponent, callback, errorBean, additionalInfo); - } - private static boolean doSubmit(final IdeaLoggingEvent event, - final Component parentComponent, - final Consumer callback, - final ErrorBean errorBean, - final String description) { + final Component parentComponent, + final Consumer callback, + final ErrorBean errorBean, + final String description) { final DataContext dataContext = DataManager.getInstance().getDataContext(parentComponent); final Project project = CommonDataKeys.PROJECT.getData(dataContext); - final ErrorReportConfigurable errorReportConfigurable = ErrorReportConfigurable.getInstance(); - if (!errorReportConfigurable.KEEP_ITN_PASSWORD && - !StringUtil.isEmpty(errorReportConfigurable.ITN_LOGIN) && - StringUtil.isEmpty(errorReportConfigurable.getPlainItnPassword())) { - final JetBrainsAccountDialog dlg = new JetBrainsAccountDialog(parentComponent); + ErrorReportConfigurable settings = ErrorReportConfigurable.getInstance(); + if (!settings.KEEP_ITN_PASSWORD && !StringUtil.isEmpty(settings.ITN_LOGIN) && StringUtil.isEmpty(settings.getPlainItnPassword())) { + JetBrainsAccountDialog dlg = new JetBrainsAccountDialog(parentComponent); dlg.show(); if (!dlg.isOK()) { return false; @@ -129,55 +109,46 @@ public class ITNReporter extends ErrorReportSubmitter { } Object data = event.getData(); - if (data instanceof AbstractMessage) { errorBean.setAssigneeId(((AbstractMessage)data).getAssigneeId()); } - if (data instanceof LogMessageEx) { errorBean.setAttachments(((LogMessageEx)data).getAttachments()); } - @NonNls String login = errorReportConfigurable.ITN_LOGIN; - @NonNls String password = errorReportConfigurable.getPlainItnPassword(); - if (login.trim().length() == 0 && password.trim().length() == 0) { + String login = settings.ITN_LOGIN; + String password = settings.getPlainItnPassword(); + if (StringUtil.isEmptyOrSpaces(login) && StringUtil.isEmptyOrSpaces(password)) { login = "idea_anonymous"; password = "guest"; } - ErrorReportSender.sendError(project, login, password, errorBean, new Consumer() { - @SuppressWarnings({"AssignmentToStaticFieldFromInstanceMethod"}) + ITNProxy.sendError(project, login, password, errorBean, new Consumer() { @Override public void consume(Integer threadId) { - previousExceptionThreadId = threadId; - wasException = true; - final SubmittedReportInfo reportInfo = new SubmittedReportInfo(URL_HEADER + threadId, String.valueOf(threadId), - SubmittedReportInfo.SubmissionStatus.NEW_ISSUE); + updatePreviousThreadId(threadId); + String url = ITNProxy.getBrowseUrl(threadId); + String linkText = String.valueOf(threadId); + final SubmittedReportInfo reportInfo = new SubmittedReportInfo(url, linkText, SubmittedReportInfo.SubmissionStatus.NEW_ISSUE); callback.consume(reportInfo); ApplicationManager.getApplication().invokeLater(new Runnable() { @Override public void run() { StringBuilder text = new StringBuilder(); - final String url = IdeErrorsDialog.getUrl(reportInfo, true); - IdeErrorsDialog.appendSubmissionInformation(reportInfo, text, url); - text.append("."); - if (reportInfo.getStatus() != SubmittedReportInfo.SubmissionStatus.FAILED) { - text.append("
").append(DiagnosticBundle.message("error.report.gratitude")); - } - - NotificationType type = reportInfo.getStatus() == SubmittedReportInfo.SubmissionStatus.FAILED - ? NotificationType.ERROR - : NotificationType.INFORMATION; - NotificationListener listener = url != null ? new NotificationListener.UrlOpeningListener(true) : null; - ReportMessages.GROUP.createNotification(ReportMessages.ERROR_REPORT, - XmlStringUtil.wrapInHtml(text), - type, listener).setImportant(false).notify(project); + IdeErrorsDialog.appendSubmissionInformation(reportInfo, text); + text.append('.').append("
").append(DiagnosticBundle.message("error.report.gratitude")); + String content = XmlStringUtil.wrapInHtml(text); + ReportMessages.GROUP + .createNotification(ReportMessages.ERROR_REPORT, content, NotificationType.INFORMATION, NotificationListener.URL_OPENING_LISTENER) + .setImportant(false) + .notify(project); } }); } }, new Consumer() { @Override public void consume(final Exception e) { + Logger.getInstance(ITNReporter.class).info("reporting failed: " + e); ApplicationManager.getApplication().invokeLater(new Runnable() { @Override public void run() { @@ -192,13 +163,12 @@ public class ITNReporter extends ErrorReportSubmitter { msg = DiagnosticBundle.message("error.report.sending.failure"); } if (e instanceof UpdateAvailableException) { - String message = DiagnosticBundle.message( - "error.report.new.eap.build.message", e.getMessage()); - showMessageDialog(parentComponent, project, message, CommonBundle.getWarningTitle(), Messages.getWarningIcon()); - callback.consume(new SubmittedReportInfo(null, "0", SubmittedReportInfo.SubmissionStatus.FAILED)); + String message = DiagnosticBundle.message("error.report.new.eap.build.message", e.getMessage()); + showMessageDialog(parentComponent, project, message, CommonBundle.getWarningTitle(), Messages.getWarningIcon()); + callback.consume(new SubmittedReportInfo(SubmittedReportInfo.SubmissionStatus.FAILED)); } else if (showYesNoDialog(parentComponent, project, msg, ReportMessages.ERROR_REPORT, Messages.getErrorIcon()) != Messages.YES) { - callback.consume(new SubmittedReportInfo(null, "0", SubmittedReportInfo.SubmissionStatus.FAILED)); + callback.consume(new SubmittedReportInfo(SubmittedReportInfo.SubmissionStatus.FAILED)); } else { if (e instanceof NoSuchEAPUserException) { @@ -225,10 +195,15 @@ public class ITNReporter extends ErrorReportSubmitter { return true; } + private static void updatePreviousThreadId(Integer threadId) { + previousExceptionThreadId = threadId; + } + private static void showMessageDialog(Component parentComponent, Project project, String message, String title, Icon icon) { if (parentComponent.isShowing()) { Messages.showMessageDialog(parentComponent, message, title, icon); - } else { + } + else { Messages.showMessageDialog(project, message, title, icon); } } @@ -237,7 +212,8 @@ public class ITNReporter extends ErrorReportSubmitter { private static int showYesNoDialog(Component parentComponent, Project project, String message, String title, Icon icon) { if (parentComponent.isShowing()) { return Messages.showYesNoDialog(parentComponent, message, title, icon); - } else { + } + else { return Messages.showYesNoDialog(project, message, title, icon); } } diff --git a/platform/platform-impl/src/com/intellij/diagnostic/IdeErrorsDialog.java b/platform/platform-impl/src/com/intellij/diagnostic/IdeErrorsDialog.java index dfe5e6375df4..14b0cc550e45 100644 --- a/platform/platform-impl/src/com/intellij/diagnostic/IdeErrorsDialog.java +++ b/platform/platform-impl/src/com/intellij/diagnostic/IdeErrorsDialog.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -563,9 +563,9 @@ public class IdeErrorsDialog extends DialogWrapper implements MessagePoolListene String url = null; if (message.isSubmitted()) { - final SubmittedReportInfo info = message.getSubmissionInfo(); - url = getUrl(info, getSubmitter(throwable) instanceof ITNReporter); - appendSubmissionInformation(info, text, url); + SubmittedReportInfo info = message.getSubmissionInfo(); + url = info.getURL(); + appendSubmissionInformation(info, text); text.append(". "); } else if (message.isSubmitting()) { @@ -578,33 +578,18 @@ public class IdeErrorsDialog extends DialogWrapper implements MessagePoolListene myInfoLabel.setHyperlinkTarget(url); } - public static void appendSubmissionInformation(SubmittedReportInfo info, StringBuilder out, @Nullable String url) { + public static void appendSubmissionInformation(SubmittedReportInfo info, StringBuilder out) { if (info.getStatus() == SubmittedReportInfo.SubmissionStatus.FAILED) { out.append(" ").append(DiagnosticBundle.message("error.list.message.submission.failed")); } - else { - if (info.getLinkText() != null) { - out.append(" ").append(DiagnosticBundle.message("error.list.message.submitted.as.link", url, info.getLinkText())); - if (info.getStatus() == SubmittedReportInfo.SubmissionStatus.DUPLICATE) { - out.append(" ").append(DiagnosticBundle.message("error.list.message.duplicate")); - } - } - else { - out.append(DiagnosticBundle.message("error.list.message.submitted")); + else if (info.getURL() != null && info.getLinkText() != null) { + out.append(" ").append(DiagnosticBundle.message("error.list.message.submitted.as.link", info.getURL(), info.getLinkText())); + if (info.getStatus() == SubmittedReportInfo.SubmissionStatus.DUPLICATE) { + out.append(" ").append(DiagnosticBundle.message("error.list.message.duplicate")); } } - } - - @Nullable - public static String getUrl(SubmittedReportInfo info, boolean reportedToJetbrains) { - if (info.getStatus() == SubmittedReportInfo.SubmissionStatus.FAILED || info.getLinkText() == null) { - return null; - } - if (reportedToJetbrains) { - return "http://ea.jetbrains.com/browser/ea_reports/" + info.getLinkText(); - } else { - return info.getURL(); + out.append(DiagnosticBundle.message("error.list.message.submitted")); } } @@ -919,38 +904,37 @@ public class IdeErrorsDialog extends DialogWrapper implements MessagePoolListene private boolean reportMessage(final AbstractMessage logMessage, final boolean dialogClosed) { final ErrorReportSubmitter submitter = getSubmitter(logMessage.getThrowable()); + if (submitter == null) return false; - if (submitter != null) { - logMessage.setSubmitting(true); - if (!dialogClosed) { - updateControls(); - } - Container parentComponent; - if (dialogClosed) { - IdeFrame ideFrame = UIUtil.getParentOfType(IdeFrame.class, getContentPane()); - parentComponent = ideFrame.getComponent(); - } - else { - parentComponent = getContentPane(); - } - return submitter.trySubmitAsync(getEvents(logMessage), logMessage.getAdditionalInfo(), parentComponent, - new Consumer() { - @Override - public void consume(final SubmittedReportInfo submittedReportInfo) { - logMessage.setSubmitting(false); - logMessage.setSubmitted(submittedReportInfo); - ApplicationManager.getApplication().invokeLater(new Runnable() { - @Override - public void run() { - if (!dialogClosed) { - updateOnSubmit(); - } - } - }); - } - }); + logMessage.setSubmitting(true); + if (!dialogClosed) { + updateControls(); } - return false; + Container parentComponent; + if (dialogClosed) { + IdeFrame ideFrame = UIUtil.getParentOfType(IdeFrame.class, getContentPane()); + parentComponent = ideFrame.getComponent(); + } + else { + parentComponent = getContentPane(); + } + + return submitter.submit( + getEvents(logMessage), logMessage.getAdditionalInfo(), parentComponent, new Consumer() { + @Override + public void consume(final SubmittedReportInfo submittedReportInfo) { + logMessage.setSubmitting(false); + logMessage.setSubmitted(submittedReportInfo); + ApplicationManager.getApplication().invokeLater(new Runnable() { + @Override + public void run() { + if (!dialogClosed) { + updateOnSubmit(); + } + } + }); + } + }); } private IdeaLoggingEvent[] getEvents(final AbstractMessage logMessage) { diff --git a/platform/platform-impl/src/com/intellij/diagnostic/SubmitPerformanceReportAction.java b/platform/platform-impl/src/com/intellij/diagnostic/SubmitPerformanceReportAction.java deleted file mode 100644 index 88798381f8a8..000000000000 --- a/platform/platform-impl/src/com/intellij/diagnostic/SubmitPerformanceReportAction.java +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright 2000-2013 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.diagnostic; - -import com.intellij.openapi.actionSystem.AnAction; -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.actionSystem.CommonDataKeys; -import com.intellij.openapi.application.ApplicationInfo; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.application.PathManager; -import com.intellij.openapi.progress.ProgressIndicator; -import com.intellij.openapi.progress.ProgressManager; -import com.intellij.openapi.progress.Task; -import com.intellij.openapi.project.DumbAware; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.Messages; -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.util.SystemProperties; -import com.intellij.util.io.ZipUtil; -import org.apache.commons.net.ftp.FTPClient; -import org.apache.commons.net.ftp.FTPReply; -import org.jetbrains.annotations.NonNls; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.io.*; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.zip.ZipOutputStream; - -/** - * @author yole - */ -public class SubmitPerformanceReportAction extends AnAction implements DumbAware { - private final DateFormat myDateFormat = new SimpleDateFormat("yyyyMMdd-HHmmss"); - - private static final String MESSAGE_TITLE = "Submit Performance Report"; - - public void actionPerformed(final AnActionEvent e) { - String reportFileName = "perf_" + ApplicationInfo.getInstance().getBuild().asString() + "_" + - SystemProperties.getUserName() + "_" + myDateFormat.format(new Date()) + ".zip"; - final File reportPath = new File(SystemProperties.getUserHome(), reportFileName); - final File logDir = new File(PathManager.getLogPath()); - final Project project = e.getData(CommonDataKeys.PROJECT); - - final boolean[] archiveCreated = new boolean[1]; - final boolean completed = ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable() { - public void run() { - try { - ZipOutputStream zip = new ZipOutputStream(new FileOutputStream(reportPath)); - ZipUtil.addDirToZipRecursively(zip, reportPath, logDir, "", new FileFilter() { - public boolean accept(final File pathname) { - ProgressManager.checkCanceled(); - - if (logDir.equals(pathname.getParentFile())) { - return pathname.getPath().contains("threadDumps"); - } - return true; - } - }, null); - zip.close(); - archiveCreated[0] = true; - } - catch (final IOException ex) { - ApplicationManager.getApplication().invokeLater(new Runnable() { - public void run() { - Messages.showErrorDialog(project, "Failed to create performance report archive: " + ex.getMessage(), MESSAGE_TITLE); - } - }); - } - } - }, "Collecting Performance Report data", true, project); - - if (!completed || - !archiveCreated[0]) { - return; - } - - int rc = Messages.showYesNoDialog(project, "The performance report has been saved to\n" + reportPath + - "\n\nWould you like to submit it to JetBrains?", MESSAGE_TITLE, - Messages.getQuestionIcon()); - if (rc == Messages.YES) { - ProgressManager.getInstance().run(new Task.Backgroundable(project, "Uploading Performance Report") { - public void run(@NotNull final ProgressIndicator indicator) { - final String error = uploadFileToFTP(reportPath, "ftp.intellij.net", ".uploads", indicator); - if (error != null) { - ApplicationManager.getApplication().invokeLater(new Runnable() { - public void run() { - Messages.showErrorDialog(error, MESSAGE_TITLE); - } - }); - } - } - }); - } - } - - @Nullable - private static String uploadFileToFTP(final File reportPath, @NonNls final String ftpSite, @NonNls final String directory, - final ProgressIndicator indicator) { - FTPClient ftp = new FTPClient(); - ftp.setConnectTimeout(30 * 1000); - try { - indicator.setText("Connecting to server..."); - ftp.connect(ftpSite); - indicator.setText("Connected to server"); - - if (!ftp.login("anonymous", "anonymous@jetbrains.com")) { - return "Failed to login"; - } - indicator.setText("Logged in"); - - // After connection attempt, you should check the reply code to verify - // success. - int reply = ftp.getReplyCode(); - - if (!FTPReply.isPositiveCompletion(reply)) { - ftp.disconnect(); - return "FTP server refused connection: " + reply; - } - if (!ftp.changeWorkingDirectory(directory)) { - return "Failed to change directory"; - } - - // else won't work behind FW - ftp.enterLocalPassiveMode(); - - if (!ftp.setFileType(FTPClient.BINARY_FILE_TYPE)) { - return "Failed to switch to binary mode"; - } - - indicator.setText("Transferring (" + StringUtil.formatFileSize(reportPath.length()) + ")"); - FileInputStream readStream = new FileInputStream(reportPath); - try { - if (!ftp.storeFile(reportPath.getName(), readStream)) { - return "Failed to upload file"; - } - } catch (IOException e) { - return "Error during transfer: " + e.getMessage(); - } - finally { - readStream.close(); - } - ftp.logout(); - return null; - } - catch (IOException e) { - return "Failed to upload: " + e.getMessage(); - } - finally { - if (ftp.isConnected()) { - try { - ftp.disconnect(); - } - catch (IOException ioe) { - // do nothing - } - } - } - } -} diff --git a/platform/platform-impl/src/com/intellij/errorreport/ErrorReportSender.java b/platform/platform-impl/src/com/intellij/errorreport/ErrorReportSender.java deleted file mode 100644 index 7cce2895fd85..000000000000 --- a/platform/platform-impl/src/com/intellij/errorreport/ErrorReportSender.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2000-2009 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.errorreport; - -import com.intellij.diagnostic.DiagnosticBundle; -import com.intellij.errorreport.bean.ErrorBean; -import com.intellij.errorreport.itn.ITNProxy; -import com.intellij.idea.IdeaLogger; -import com.intellij.openapi.progress.EmptyProgressIndicator; -import com.intellij.openapi.progress.ProgressIndicator; -import com.intellij.openapi.progress.ProgressManager; -import com.intellij.openapi.progress.Task; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.util.Consumer; -import com.intellij.util.net.HttpConfigurable; -import org.jetbrains.annotations.NonNls; -import org.jetbrains.annotations.NotNull; - -/** - * Created by IntelliJ IDEA. - * User: stathik - * Date: May 22, 2003 - * Time: 8:57:19 PM - * To change this template use Options | File Templates. - */ -public class ErrorReportSender { - @NonNls public static final String PREPARE_URL = "http://www.intellij.net/"; - - private ErrorReportSender() { - } - - static class SendTask { - private final Project myProject; - private String myLogin; - private String myPassword; - private ErrorBean errorBean; - - public SendTask(final Project project, ErrorBean errorBean) { - myProject = project; - this.errorBean = errorBean; - } - - public void setCredentials(String login, String password) { - myLogin = login; - myPassword = password; - } - - public void sendReport(final Consumer callback, final Consumer errback) { - Task.Backgroundable task = new Task.Backgroundable(myProject, DiagnosticBundle.message("title.submitting.error.report")) { - @Override - public void run(@NotNull ProgressIndicator indicator) { - try { - HttpConfigurable.getInstance().prepareURL(PREPARE_URL); - - if (!StringUtil.isEmpty(myLogin)) { - int threadId = ITNProxy.postNewThread( - myLogin, - myPassword, - errorBean, - IdeaLogger.getOurCompilationTimestamp()); - callback.consume(threadId); - } - } - catch (Exception ex) { - errback.consume(ex); - } - } - }; - if (myProject == null) { - task.run(new EmptyProgressIndicator()); - } - else { - ProgressManager.getInstance().run(task); - } - } - } - - public static void sendError(Project project, String login, String password, ErrorBean error, - Consumer callback, Consumer errback) { - SendTask sendTask = new SendTask(project, error); - sendTask.setCredentials(login, password); - sendTask.sendReport(callback, errback); - } -} diff --git a/platform/platform-impl/src/com/intellij/errorreport/bean/ErrorBean.java b/platform/platform-impl/src/com/intellij/errorreport/bean/ErrorBean.java index 89832a8b7864..d4c17f7a89d7 100644 --- a/platform/platform-impl/src/com/intellij/errorreport/bean/ErrorBean.java +++ b/platform/platform-impl/src/com/intellij/errorreport/bean/ErrorBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,10 +16,9 @@ package com.intellij.errorreport.bean; import com.intellij.openapi.diagnostic.Attachment; +import com.intellij.util.ExceptionUtil; import org.jetbrains.annotations.NonNls; -import java.io.ByteArrayOutputStream; -import java.io.PrintStream; import java.util.Collections; import java.util.List; @@ -41,11 +40,7 @@ public class ErrorBean { public ErrorBean(Throwable throwable, String lastAction) { if (throwable != null) { message = throwable.getMessage(); - - ByteArrayOutputStream stream = new ByteArrayOutputStream(); - //noinspection IOResourceOpenedButNotSafelyClosed - throwable.printStackTrace(new PrintStream(stream, true)); - stackTrace = stream.toString(); + stackTrace = ExceptionUtil.getThrowableText(throwable); } this.lastAction = lastAction; } diff --git a/platform/platform-impl/src/com/intellij/errorreport/itn/ITNProxy.java b/platform/platform-impl/src/com/intellij/errorreport/itn/ITNProxy.java index ec112437520b..4339b36cbf22 100644 --- a/platform/platform-impl/src/com/intellij/errorreport/itn/ITNProxy.java +++ b/platform/platform-impl/src/com/intellij/errorreport/itn/ITNProxy.java @@ -20,202 +20,292 @@ import com.intellij.errorreport.bean.ErrorBean; import com.intellij.errorreport.error.InternalEAPException; import com.intellij.errorreport.error.NoSuchEAPUserException; import com.intellij.errorreport.error.UpdateAvailableException; +import com.intellij.idea.IdeaLogger; import com.intellij.openapi.application.Application; -import com.intellij.openapi.application.ApplicationInfo; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ApplicationNamesInfo; import com.intellij.openapi.application.ex.ApplicationInfoEx; import com.intellij.openapi.diagnostic.Attachment; +import com.intellij.openapi.progress.EmptyProgressIndicator; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.progress.Task; +import com.intellij.openapi.project.Project; import com.intellij.openapi.updateSettings.impl.UpdateSettings; -import com.intellij.openapi.util.Couple; +import com.intellij.openapi.util.SystemInfo; +import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.util.Consumer; import com.intellij.util.SystemProperties; -import com.intellij.util.net.HttpConfigurable; -import org.jetbrains.annotations.NonNls; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.net.ssl.CertificateUtil; +import org.jetbrains.annotations.NotNull; +import javax.net.ssl.*; import java.io.*; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLEncoder; -import java.util.ArrayList; +import java.security.GeneralSecurityException; +import java.security.KeyStore; +import java.security.cert.Certificate; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; import java.util.Calendar; -import java.util.Iterator; -import java.util.List; +import java.util.Map; +import java.util.Set; /** - * Created by IntelliJ IDEA. - * User: stathik - * Date: Aug 4, 2003 - * Time: 8:12:00 PM - * To change this template use Options | File Templates. + * @author stathik + * @since Aug 4, 2003 */ public class ITNProxy { - @NonNls public static final String ENCODING = "UTF8"; - public static final String POST_DELIMITER = "&"; + private static final String NEW_THREAD_VIEW_URL = "https://ea.jetbrains.com/browser/ea_reports/"; + private static final String NEW_THREAD_POST_URL = "https://ea-report.jetbrains.com/trackerRpc/idea/createScr"; + private static final String ENCODING = "UTF8"; + + public static void sendError(Project project, + final String login, + final String password, + final ErrorBean error, + final Consumer callback, + final Consumer errback) { + if (StringUtil.isEmpty(login)) { + return; + } - @NonNls public static final String NEW_THREAD_URL = "http://www.intellij.net/trackerRpc/idea/createScr"; + Task.Backgroundable task = new Task.Backgroundable(project, DiagnosticBundle.message("title.submitting.error.report")) { + @Override + public void run(@NotNull ProgressIndicator indicator) { + try { + int threadId = postNewThread(login, password, error); + callback.consume(threadId); + } + catch (Exception ex) { + errback.consume(ex); + } + } + }; + + if (project == null) { + task.run(new EmptyProgressIndicator()); + } + else { + ProgressManager.getInstance().run(task); + } + } - @NonNls private static final String HTTP_CONTENT_LENGTH = "Content-Length"; - @NonNls private static final String HTTP_CONTENT_TYPE = "Content-Type"; - @NonNls private static final String HTTP_WWW_FORM = "application/x-www-form-urlencoded"; - @NonNls private static final String HTTP_POST = "POST"; + public static String getBrowseUrl(int threadId) { + return NEW_THREAD_VIEW_URL + threadId; + } - public static int postNewThread (String login, String password, ErrorBean error, String compilationTimestamp) - throws IOException, NoSuchEAPUserException, InternalEAPException, UpdateAvailableException { + private static SSLContext ourSslContext; - @NonNls List> params = createParametersFor(login, - password, - error, - compilationTimestamp, - ApplicationManager.getApplication(), - (ApplicationInfoEx) ApplicationInfo.getInstance(), - ApplicationNamesInfo.getInstance(), - UpdateSettings.getInstance()); + private static int postNewThread(String login, String password, ErrorBean error) throws Exception { + if (ourSslContext == null) { + ourSslContext = initContext(); + } - HttpURLConnection connection = post(new URL(NEW_THREAD_URL), join(params)); + Map params = createParameters(login, password, error); + HttpURLConnection connection = post(new URL(NEW_THREAD_POST_URL), join(params)); int responseCode = connection.getResponseCode(); - if (responseCode != HttpURLConnection.HTTP_OK) { throw new InternalEAPException(DiagnosticBundle.message("error.http.result.code", responseCode)); } - String reply; - - InputStream is = new BufferedInputStream(connection.getInputStream()); + String response; + InputStream is = connection.getInputStream(); try { - reply = readFrom(is); - } finally { + byte[] bytes = FileUtil.loadBytes(is); + response = new String(bytes, ENCODING); + } + finally { is.close(); } - if ("unauthorized".equals(reply)) { + if ("unauthorized".equals(response)) { throw new NoSuchEAPUserException(login); } - - if (reply.startsWith("update ")) { - throw new UpdateAvailableException(reply.substring(7)); + if (response.startsWith("update ")) { + throw new UpdateAvailableException(response.substring(7)); } - - if (reply.startsWith("message ")) { - throw new InternalEAPException(reply.substring(8)); + if (response.startsWith("message ")) { + throw new InternalEAPException(response.substring(8)); } try { - return Integer.valueOf(reply.trim()).intValue(); - } catch (NumberFormatException ex) { - // Tibor!!!! :-E + return Integer.valueOf(response.trim()).intValue(); + } + catch (NumberFormatException ex) { throw new InternalEAPException(DiagnosticBundle.message("error.itn.returns.wrong.data")); } } - private static List> createParametersFor(String login, - String password, - ErrorBean error, - String compilationTimestamp, Application application, ApplicationInfoEx appInfo, - ApplicationNamesInfo namesInfo, - UpdateSettings updateSettings) { - @NonNls List> params = new ArrayList>(); - - params.add(Couple.of("protocol.version", "1")); + private static Map createParameters(String login, String password, ErrorBean error) { + Map params = ContainerUtil.newLinkedHashMap(40); - params.add(Couple.of("user.login", login)); - params.add(Couple.of("user.password", password)); + params.put("protocol.version", "1"); - params.add(Couple.of("os.name", SystemProperties.getOsName())); + params.put("user.login", login); + params.put("user.password", password); - params.add(Couple.of("java.version", SystemProperties.getJavaVersion())); - params.add(Couple.of("java.vm.vendor", SystemProperties.getJavaVmVendor())); + params.put("os.name", SystemProperties.getOsName()); + params.put("java.version", SystemProperties.getJavaVersion()); + params.put("java.vm.vendor", SystemProperties.getJavaVmVendor()); - params.add(Couple.of("app.name", namesInfo.getProductName())); - params.add(Couple.of("app.name.full", namesInfo.getFullProductName())); - params.add(Couple.of("app.name.version", appInfo.getVersionName())); - params.add(Couple.of("app.eap", Boolean.toString(appInfo.isEAP()))); - params.add(Couple.of("app.internal", Boolean.toString(application.isInternal()))); - params.add(Couple.of("app.build", appInfo.getBuild().asString())); - params.add(Couple.of("app.version.major", appInfo.getMajorVersion())); - params.add(Couple.of("app.version.minor", appInfo.getMinorVersion())); - params.add(Couple.of("app.build.date", format(appInfo.getBuildDate()))); - params.add(Couple.of("app.build.date.release", format(appInfo.getMajorReleaseBuildDate()))); - params.add(Couple.of("app.compilation.timestamp", compilationTimestamp)); + ApplicationInfoEx appInfo = ApplicationInfoEx.getInstanceEx(); + ApplicationNamesInfo namesInfo = ApplicationNamesInfo.getInstance(); + Application application = ApplicationManager.getApplication(); + params.put("app.name", namesInfo.getProductName()); + params.put("app.name.full", namesInfo.getFullProductName()); + params.put("app.name.version", appInfo.getVersionName()); + params.put("app.eap", Boolean.toString(appInfo.isEAP())); + params.put("app.internal", Boolean.toString(application.isInternal())); + params.put("app.build", appInfo.getBuild().asString()); + params.put("app.version.major", appInfo.getMajorVersion()); + params.put("app.version.minor", appInfo.getMinorVersion()); + params.put("app.build.date", format(appInfo.getBuildDate())); + params.put("app.build.date.release", format(appInfo.getMajorReleaseBuildDate())); + params.put("app.compilation.timestamp", IdeaLogger.getOurCompilationTimestamp()); - params.add(Couple.of("update.channel.status", updateSettings.getSelectedChannelStatus().getCode())); - params.add(Couple.of("update.ignored.builds", StringUtil.join(updateSettings.getIgnoredBuildNumbers(), ","))); + UpdateSettings updateSettings = UpdateSettings.getInstance(); + params.put("update.channel.status", updateSettings.getSelectedChannelStatus().getCode()); + params.put("update.ignored.builds", StringUtil.join(updateSettings.getIgnoredBuildNumbers(), ",")); - params.add(Couple.of("plugin.name", error.getPluginName())); - params.add(Couple.of("plugin.version", error.getPluginVersion())); + params.put("plugin.name", error.getPluginName()); + params.put("plugin.version", error.getPluginVersion()); - params.add(Couple.of("last.action", error.getLastAction())); - params.add(Couple.of("previous.exception", - error.getPreviousException() == null ? null : Integer.toString(error.getPreviousException()))); + params.put("last.action", error.getLastAction()); + params.put("previous.exception", error.getPreviousException() == null ? null : Integer.toString(error.getPreviousException())); - params.add(Couple.of("error.message", error.getMessage())); - params.add(Couple.of("error.stacktrace", error.getStackTrace())); + params.put("error.message", error.getMessage()); + params.put("error.stacktrace", error.getStackTrace()); + params.put("error.description", error.getDescription()); - params.add(Couple.of("error.description", error.getDescription())); - - params.add(Couple.of("assignee.id", error.getAssigneeId() == null ? null : Integer.toString(error.getAssigneeId()))); + params.put("assignee.id", error.getAssigneeId() == null ? null : Integer.toString(error.getAssigneeId())); for (Attachment attachment : error.getAttachments()) { - params.add(Couple.of("attachment.name", attachment.getName())); - params.add(Couple.of("attachment.value", attachment.getEncodedBytes())); + params.put("attachment.name", attachment.getName()); + params.put("attachment.value", attachment.getEncodedBytes()); } return params; } - private static String readFrom(InputStream is) throws IOException { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - int c; - while ((c = is.read()) != -1) { - out.write(c); - } - String s = out.toString(); - out.close(); - return s; - } - private static String format(Calendar calendar) { return calendar == null ? null : Long.toString(calendar.getTime().getTime()); } + private static byte[] join(Map params) throws UnsupportedEncodingException { + StringBuilder builder = new StringBuilder(); + for (Map.Entry param : params.entrySet()) { + if (StringUtil.isEmpty(param.getKey())) { + throw new IllegalArgumentException(param.toString()); + } + if (builder.length() > 0) { + builder.append('&'); + } + if (StringUtil.isNotEmpty(param.getValue())) { + builder.append(param.getKey()).append('=').append(URLEncoder.encode(param.getValue(), ENCODING)); + } + } + return builder.toString().getBytes(ENCODING); + } + private static HttpURLConnection post(URL url, byte[] bytes) throws IOException { - HttpURLConnection connection = (HttpURLConnection)HttpConfigurable.getInstance().openConnection(url.toString()); + HttpsURLConnection connection = (HttpsURLConnection)url.openConnection(); + + connection.setSSLSocketFactory(ourSslContext.getSocketFactory()); + if (!SystemInfo.isJavaVersionAtLeast("1.7") || !SystemProperties.getBooleanProperty("jsse.enableSNIExtension", true)) { + connection.setHostnameVerifier(new EaHostnameVerifier(url.getHost(), "ftp.intellij.net")); + } - connection.setRequestMethod(HTTP_POST); + connection.setRequestMethod("POST"); connection.setDoInput(true); connection.setDoOutput(true); - connection.setRequestProperty(HTTP_CONTENT_TYPE, String.format("%s; charset=%s", HTTP_WWW_FORM, ENCODING)); - connection.setRequestProperty(HTTP_CONTENT_LENGTH, Integer.toString(bytes.length)); + connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=" + ENCODING); + connection.setRequestProperty("Content-Length", Integer.toString(bytes.length)); - OutputStream out = new BufferedOutputStream(connection.getOutputStream()); + OutputStream out = connection.getOutputStream(); try { out.write(bytes); - out.flush(); - } finally { + } + finally { out.close(); } return connection; } - private static byte[] join(List> params) throws UnsupportedEncodingException { - StringBuilder builder = new StringBuilder(); - - Iterator> it = params.iterator(); - - while (it.hasNext()) { - Couple param = it.next(); - - if (StringUtil.isEmpty(param.first)) - throw new IllegalArgumentException(param.toString()); + private synchronized static SSLContext initContext() throws GeneralSecurityException, IOException { + CertificateFactory cf = CertificateFactory.getInstance(CertificateUtil.X509); + Certificate ca = cf.generateCertificate(new ByteArrayInputStream(JB_CA_CERT.getBytes(ENCODING))); + KeyStore ks = KeyStore.getInstance(CertificateUtil.JKS); + ks.load(null, null); + ks.setCertificateEntry("JetBrains CA", ca); + TrustManagerFactory tmf = TrustManagerFactory.getInstance(CertificateUtil.X509); + tmf.init(ks); + SSLContext ctx = SSLContext.getInstance("TLS"); + ctx.init(null, tmf.getTrustManagers(), null); + return ctx; + } - if (StringUtil.isNotEmpty(param.second)) - builder.append(param.first).append("=").append(URLEncoder.encode(param.second, ENCODING)); + private static class EaHostnameVerifier implements HostnameVerifier { + private final Set myAllowedHosts; - if (it.hasNext()) - builder.append(POST_DELIMITER); + public EaHostnameVerifier(@NotNull String... allowedHosts) { + myAllowedHosts = ContainerUtil.newHashSet(allowedHosts); } - return builder.toString().getBytes(); + @Override + public boolean verify(String hostname, SSLSession session) { + try { + Certificate[] certificates = session.getPeerCertificates(); + if (certificates.length > 0) { + Certificate certificate = certificates[0]; + if (certificate instanceof X509Certificate) { + String cn = CertificateUtil.getCommonName((X509Certificate)certificate); + return myAllowedHosts.contains(cn); + } + } + } + catch (SSLPeerUnverifiedException ignored) { } + return false; + } } + + @SuppressWarnings("SpellCheckingInspection") private static final String JB_CA_CERT = + "-----BEGIN CERTIFICATE-----\n" + + "MIIFvjCCA6agAwIBAgIQMYHnK1dpIZVCoitWqBwhXjANBgkqhkiG9w0BAQsFADBn\n" + + "MRMwEQYKCZImiZPyLGQBGRYDTmV0MRgwFgYKCZImiZPyLGQBGRYISW50ZWxsaUox\n" + + "FDASBgoJkiaJk/IsZAEZFgRMYWJzMSAwHgYDVQQDExdKZXRCcmFpbnMgRW50ZXJw\n" + + "cmlzZSBDQTAeFw0xMjEyMjkxMDEyMzJaFw0zMjEyMjkxMDIyMzBaMGcxEzARBgoJ\n" + + "kiaJk/IsZAEZFgNOZXQxGDAWBgoJkiaJk/IsZAEZFghJbnRlbGxpSjEUMBIGCgmS\n" + + "JomT8ixkARkWBExhYnMxIDAeBgNVBAMTF0pldEJyYWlucyBFbnRlcnByaXNlIENB\n" + + "MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAzPCE2gPgKECo5CB3BTAw\n" + + "4XrrNpg+YwTMzeNNDYs4VdPzBq0snWsbm5qP6z1GBGUTr4agERQUxc4//gZMR0UJ\n" + + "89GWVNYPbZ/MrkfyaOiem8xosuZ+7WoFu4nYnKbBBMBA7S2idrPSmPv2wYiHJCY7\n" + + "eN2AdViiFSAUeGw/7pIgou92/4Bbm6SSzRBKBYfRIfwq0ZgETSIjhNR5o3XJB5i2\n" + + "CkSjMk7kNiMWBaq+Alv+Um/xMFnl5jiq9H7YAALgH/mZHr8ANniSyBwkj4r/7GQ3\n" + + "UIYwoLrGxSOSEY9UhEpdqQkRbSSjQiFYMlhYEAtLERK4KZObTuUgdiE6Wk38EOKZ\n" + + "wy1eE/EIh8vWBHFSH5opPSK4dyamxj9o5c2g1hJ07ZBUCV/nsrKb+ruMkwBfI286\n" + + "+HPTMUmoKuUfSfHZ5TiuF5EvcSD7Df2ZCFpRugPs26FRGvtsiBMEmu4u6fu5RNkh\n" + + "s7Ueq6ISblt6dj/youywiAZnyrtNKJVyK0m051g9b2IokHjrk9XTswTqBHDjZKYr\n" + + "YG/5jDSSzvR/ptR9YIrHF0a9A6LQLZ6ews4FUO6O/RhiYXV8FggD7ZUg019OBUx3\n" + + "rF1L3GBYA8YhYP/N18r8DqOaFgUiRDyeRMbka9OXZ2KJT6iL+mOfg/svSW8lc4Ly\n" + + "EgcyJ9sk7MRwrhlp3Kc0W7UCAwEAAaNmMGQwEwYJKwYBBAGCNxQCBAYeBABDAEEw\n" + + "CwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFB/HK/yYoWW9\n" + + "vr2XAyhcMmV3gSfGMBAGCSsGAQQBgjcVAQQDAgEAMA0GCSqGSIb3DQEBCwUAA4IC\n" + + "AQBnYu49dZRBK9W3voy6bgzz64sZfX51/RIA6aaoHAH3U1bC8EepChqWeRgijGCD\n" + + "CBvLTk7bk/7fgXPPvL+8RwYaxEewCi7t1RQKqPmNvUnEnw28OLvYLBEO7a4yeN5Y\n" + + "YaZwdfVH+0qMvTqMQku5p5Xx3dY+DAm4EqXEFD0svfeMJmOA+R1CIqRz1CXnN2FY\n" + + "A+86m7WLmGZ8oWlRUJDa1etqrE3ZxXHH/IunVJOGOfaQVkid3u3ageyUOnMw/iME\n" + + "7vi0UNVYVsCjXYZxrzCDLCxtguZaV4rMYvLRt1oUxZ+VnmdVa3aW0W//GQ70sqh2\n" + + "KQDtIF6Iumf8ya4vA0+K+AAowOSR/k4jQzlWQdZvJNMHP/Jc0OyJyHEegjtWssrS\n" + + "NoRtI6V4j277ugWF1Xpt1x0YxYyGSZTI4rqGLqVT8x6Llr24YaHCdp56rKWC/5ob\n" + + "IFZ7tJys7oQqof11ANDExrnHv/FEE39VDlfEIUVGyCpsyKbzO7MPfdOce2bIaQOS\n" + + "dQ76TpYClrnezikJgp9MSQmd3+ozs9w1upGynHNGNmVhzZ5sex9voWcGoyjmOFhs\n" + + "wg13S9Hjy3VYq8y0krRYLEGLctd4vnxWGzJzUNSnqezwHZRl4v4Ejp3dQUZP+5sY\n" + + "1F81Vj1G264YnZAcWp5x3GTI4K6+k9Xx3pwUPcKOYdlpZQ==\n" + + "-----END CERTIFICATE-----\n"; } diff --git a/platform/platform-impl/src/com/intellij/featureStatistics/FeatureUsageTrackerImpl.java b/platform/platform-impl/src/com/intellij/featureStatistics/FeatureUsageTrackerImpl.java index 21a44f1c6718..78b13aa63940 100644 --- a/platform/platform-impl/src/com/intellij/featureStatistics/FeatureUsageTrackerImpl.java +++ b/platform/platform-impl/src/com/intellij/featureStatistics/FeatureUsageTrackerImpl.java @@ -15,10 +15,7 @@ */ package com.intellij.featureStatistics; -import com.intellij.openapi.components.PersistentStateComponent; -import com.intellij.openapi.components.State; -import com.intellij.openapi.components.Storage; -import com.intellij.openapi.components.StoragePathMacros; +import com.intellij.openapi.components.*; import com.intellij.openapi.project.Project; import com.intellij.util.xmlb.XmlSerializer; import org.jdom.Element; @@ -30,9 +27,9 @@ import java.util.Set; @SuppressWarnings({"NonPrivateFieldAccessedInSynchronizedContext"}) @State( - name = "FeatureUsageStatistics", - storages = {@Storage( - file = StoragePathMacros.APP_CONFIG + "/feature.usage.statistics.xml")}) + name = "FeatureUsageStatistics", + storages = {@Storage(file = StoragePathMacros.APP_CONFIG + "/feature.usage.statistics.xml", roamingType = RoamingType.DISABLED)} +) public class FeatureUsageTrackerImpl extends FeatureUsageTracker implements PersistentStateComponent { private static final int HOUR = 1000 * 60 * 60; private static final long DAY = HOUR * 24; @@ -94,7 +91,7 @@ public class FeatureUsageTrackerImpl extends FeatureUsageTracker implements Pers if (descriptor != null && System.currentTimeMillis() - descriptor.getLastTimeUsed() > 10 * DAY) { return true; } - + return isToBeShown(featureId, project, HOUR); } diff --git a/platform/platform-impl/src/com/intellij/ide/ApplicationLoadListener.java b/platform/platform-impl/src/com/intellij/ide/ApplicationLoadListener.java index f1d11ff938d9..b1432f87e10d 100644 --- a/platform/platform-impl/src/com/intellij/ide/ApplicationLoadListener.java +++ b/platform/platform-impl/src/com/intellij/ide/ApplicationLoadListener.java @@ -17,9 +17,10 @@ package com.intellij.ide; import com.intellij.openapi.application.Application; import com.intellij.openapi.extensions.ExtensionPointName; +import org.jetbrains.annotations.NotNull; public interface ApplicationLoadListener { ExtensionPointName EP_NAME = ExtensionPointName.create("com.intellij.ApplicationLoadListener"); - void beforeApplicationLoaded(Application application); + void beforeApplicationLoaded(@NotNull Application application); } diff --git a/platform/platform-impl/src/com/intellij/ide/CommandLineProcessor.java b/platform/platform-impl/src/com/intellij/ide/CommandLineProcessor.java index c288150bd7ab..293c4a55eeae 100644 --- a/platform/platform-impl/src/com/intellij/ide/CommandLineProcessor.java +++ b/platform/platform-impl/src/com/intellij/ide/CommandLineProcessor.java @@ -141,7 +141,7 @@ public class CommandLineProcessor { starter instanceof ApplicationStarterEx && ((ApplicationStarterEx)starter).canProcessExternalCommandLine()) { LOG.info("Processing command with " + starter); - ((ApplicationStarterEx) starter).processExternalCommandLine(ArrayUtil.toStringArray(args)); + ((ApplicationStarterEx) starter).processExternalCommandLine(ArrayUtil.toStringArray(args), currentDirectory); return null; } } diff --git a/platform/platform-impl/src/com/intellij/ide/RecentDirectoryProjectsManager.java b/platform/platform-impl/src/com/intellij/ide/RecentDirectoryProjectsManager.java index 4189b6adf98b..59b115a43105 100644 --- a/platform/platform-impl/src/com/intellij/ide/RecentDirectoryProjectsManager.java +++ b/platform/platform-impl/src/com/intellij/ide/RecentDirectoryProjectsManager.java @@ -34,17 +34,14 @@ import org.jetbrains.annotations.Nullable; */ @State( name = "RecentDirectoryProjectsManager", - roamingType = RoamingType.DISABLED, - storages = { - @Storage( - file = StoragePathMacros.APP_CONFIG + "/other.xml" - )} + storages = {@Storage(file = StoragePathMacros.APP_CONFIG + "/other.xml", roamingType = RoamingType.DISABLED)} ) public class RecentDirectoryProjectsManager extends RecentProjectsManagerBase { public RecentDirectoryProjectsManager(MessageBus messageBus) { super(messageBus); } + @Override @Nullable protected String getProjectPath(@NotNull Project project) { final ProjectBaseDirectory baseDir = ProjectBaseDirectory.getInstance(project); @@ -52,6 +49,7 @@ public class RecentDirectoryProjectsManager extends RecentProjectsManagerBase { return baseDirVFile != null ? FileUtil.toSystemDependentName(baseDirVFile.getPath()) : null; } + @Override protected void doOpenProject(@NotNull String projectPath, Project projectToClose, boolean forceOpenInNewFrame) { final VirtualFile projectDir = LocalFileSystem.getInstance().findFileByPath(FileUtil.toSystemIndependentName(projectPath)); if (projectDir != null) { diff --git a/platform/platform-impl/src/com/intellij/ide/RecentProjectsManagerBase.java b/platform/platform-impl/src/com/intellij/ide/RecentProjectsManagerBase.java index 1a6bffbcd297..7991c3af0104 100644 --- a/platform/platform-impl/src/com/intellij/ide/RecentProjectsManagerBase.java +++ b/platform/platform-impl/src/com/intellij/ide/RecentProjectsManagerBase.java @@ -86,6 +86,7 @@ public abstract class RecentProjectsManagerBase implements ProjectManagerListene messageBus.connect().subscribe(AppLifecycleListener.TOPIC, new MyAppLifecycleListener()); } + @Override public State getState() { synchronized (myStateLock) { myState.validateRecentProjects(); @@ -93,6 +94,7 @@ public abstract class RecentProjectsManagerBase implements ProjectManagerListene } } + @Override public void loadState(final State state) { synchronized (myStateLock) { myState = state; @@ -212,7 +214,7 @@ public abstract class RecentProjectsManagerBase implements ProjectManagerListene if (addClearListItem) { AnAction clearListAction = new DumbAwareAction(IdeBundle.message("action.clear.list")) { @Override - public void actionPerformed(AnActionEvent e) { + public void actionPerformed(@NotNull AnActionEvent e) { String message = IdeBundle.message("action.clear.list.message"); String title = IdeBundle.message("action.clear.list.title"); if (Messages.showOkCancelDialog(e.getProject(), message, title, Messages.getQuestionIcon()) == Messages.OK) { @@ -249,6 +251,7 @@ public abstract class RecentProjectsManagerBase implements ProjectManagerListene return file.exists() && (!file.isDirectory() || new File(file, Project.DIRECTORY_STORE_FOLDER).exists()); } + @Override public void projectOpened(final Project project) { String path = getProjectPath(project); if (path != null) { @@ -269,6 +272,7 @@ public abstract class RecentProjectsManagerBase implements ProjectManagerListene } } + @Override public void projectClosed(final Project project) { Project[] openProjects = ProjectManager.getInstance().getOpenProjects(); if (openProjects.length > 0) { @@ -347,6 +351,7 @@ public abstract class RecentProjectsManagerBase implements ProjectManagerListene } private class MyAppLifecycleListener extends AppLifecycleListener.Adapter { + @Override public void appFrameCreated(final String[] commandLineArgs, @NotNull final Ref willOpenProject) { if (!ApplicationManager.getApplication().isHeadlessEnvironment()) { ProjectManager.getInstance().addProjectManagerListener(RecentProjectsManagerBase.this); @@ -356,19 +361,23 @@ public abstract class RecentProjectsManagerBase implements ProjectManagerListene } } + @Override public void appStarting(Project projectFromCommandLine) { if (projectFromCommandLine != null) return; doReopenLastProject(); } + @Override public void projectFrameClosed() { updateLastProjectPath(); } + @Override public void projectOpenFailed() { updateLastProjectPath(); } + @Override public void appClosing() { updateLastProjectPath(); } diff --git a/platform/platform-impl/src/com/intellij/ide/actions/AboutAction.java b/platform/platform-impl/src/com/intellij/ide/actions/AboutAction.java index e0c9373bbf2b..43cc4d0be9d8 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/AboutAction.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/AboutAction.java @@ -16,6 +16,7 @@ package com.intellij.ide.actions; import com.intellij.ide.DataManager; +import com.intellij.openapi.actionSystem.ActionPlaces; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; @@ -31,7 +32,7 @@ import java.awt.*; public class AboutAction extends AnAction implements DumbAware { @Override public void update(AnActionEvent e) { - e.getPresentation().setVisible(!SystemInfo.isMacSystemMenu); + e.getPresentation().setVisible(!SystemInfo.isMacSystemMenu || !ActionPlaces.MAIN_MENU.equals(e.getPlace())); e.getPresentation().setDescription("Show information about " + ApplicationNamesInfo.getInstance().getFullProductName()); } diff --git a/platform/platform-impl/src/com/intellij/ide/actions/AssociateFileType.java b/platform/platform-impl/src/com/intellij/ide/actions/AssociateFileType.java index 77544ae1d14c..8b1375d9f933 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/AssociateFileType.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/AssociateFileType.java @@ -41,7 +41,7 @@ public class AssociateFileType extends AnAction { // the action should also be available for files which have been auto-detected as text or as a particular language (IDEA-79574) haveSmthToDo = FileTypeManager.getInstance().getFileTypeByFileName(file.getName()) == FileTypes.UNKNOWN; } - presentation.setVisible(haveSmthToDo || ActionPlaces.MAIN_MENU.equals(e.getPlace())); + presentation.setVisible(haveSmthToDo || ActionPlaces.isMainMenuOrActionSearch(e.getPlace())); presentation.setEnabled(haveSmthToDo); } diff --git a/platform/platform-impl/src/com/intellij/ide/actions/BaseShowRecentFilesAction.java b/platform/platform-impl/src/com/intellij/ide/actions/BaseShowRecentFilesAction.java index 442d09b8b3bd..505d0f5eef3d 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/BaseShowRecentFilesAction.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/BaseShowRecentFilesAction.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -39,6 +39,7 @@ import com.intellij.ui.components.JBList; import com.intellij.util.ArrayUtil; import com.intellij.util.Function; import com.intellij.util.IconUtil; +import org.jetbrains.annotations.NotNull; import javax.swing.*; import javax.swing.event.ListSelectionEvent; @@ -53,11 +54,11 @@ import java.io.File; public abstract class BaseShowRecentFilesAction extends AnAction implements DumbAware { private static final Color BORDER_COLOR = Gray._135; - public void actionPerformed(AnActionEvent e) { + public void actionPerformed(@NotNull AnActionEvent e) { show(CommonDataKeys.PROJECT.getData(e.getDataContext())); } - public void update(AnActionEvent event){ + public void update(@NotNull AnActionEvent event){ Presentation presentation = event.getPresentation(); Project project = CommonDataKeys.PROJECT.getData(event.getDataContext()); presentation.setEnabled(project != null); @@ -93,7 +94,7 @@ public abstract class BaseShowRecentFilesAction extends AnAction implements Dumb final JList list = new JBList(model); list.addKeyListener( new KeyAdapter() { - public void keyPressed(KeyEvent e) { + public void keyPressed(@NotNull KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_DELETE) { int index = list.getSelectedIndex(); if (index == -1 || index >= list.getModel().getSize()){ @@ -150,7 +151,7 @@ public abstract class BaseShowRecentFilesAction extends AnAction implements Dumb JPanel footerPanel = new JPanel(new BorderLayout()) { @Override - protected void paintComponent(Graphics g) { + protected void paintComponent(@NotNull Graphics g) { super.paintComponent(g); g.setColor(BORDER_COLOR); g.drawLine(0, 0, getWidth(), 0); @@ -237,7 +238,7 @@ public abstract class BaseShowRecentFilesAction extends AnAction implements Dumb } @Override - public void actionPerformed(ActionEvent e) { + public void actionPerformed(@NotNull ActionEvent e) { if (myPopup != null) { myPopup.cancel(); } @@ -272,7 +273,7 @@ public abstract class BaseShowRecentFilesAction extends AnAction implements Dumb return fullText; } - public void valueChanged(final ListSelectionEvent e) { + public void valueChanged(@NotNull final ListSelectionEvent e) { //noinspection SSBasedInspection SwingUtilities.invokeLater(new Runnable() { public void run() { diff --git a/platform/platform-impl/src/com/intellij/ide/actions/CreateLauncherScriptAction.java b/platform/platform-impl/src/com/intellij/ide/actions/CreateLauncherScriptAction.java index 411f2d9d93bb..0b696c8f4496 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/CreateLauncherScriptAction.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/CreateLauncherScriptAction.java @@ -134,12 +134,12 @@ public class CreateLauncherScriptAction extends DumbAwareAction { private static File createLauncherScriptFile() throws IOException, ExecutionException { String runPath = PathManager.getHomePath(); + final String productName = ApplicationNamesInfo.getInstance().getProductName().toLowerCase(); if (!SystemInfo.isMac) { // for Macs just use "*.app" - final String productName = ApplicationNamesInfo.getInstance().getProductName().toLowerCase(); runPath += "/bin/" + productName + ".sh"; } else if (runPath.endsWith(CONTENTS)) { - runPath = runPath.substring(0, runPath.length() - CONTENTS.length()); + runPath += "/MacOS/" + productName; } String launcherContents = ExecUtil.loadTemplate(CreateLauncherScriptAction.class.getClassLoader(), "launcher.py", newHashMap(asList("$CONFIG_PATH$", "$RUN_PATH$"), diff --git a/platform/platform-impl/src/com/intellij/ide/actions/ExitAction.java b/platform/platform-impl/src/com/intellij/ide/actions/ExitAction.java index 52487a57f9f2..3017b8624828 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/ExitAction.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/ExitAction.java @@ -16,6 +16,7 @@ */ package com.intellij.ide.actions; +import com.intellij.openapi.actionSystem.ActionPlaces; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.application.ex.ApplicationManagerEx; @@ -24,7 +25,7 @@ import com.intellij.openapi.util.SystemInfo; public class ExitAction extends AnAction implements DumbAware { public void update(AnActionEvent e) { - e.getPresentation().setVisible(!SystemInfo.isMacSystemMenu); + e.getPresentation().setVisible(!SystemInfo.isMacSystemMenu || !ActionPlaces.MAIN_MENU.equals(e.getPlace())); } public void actionPerformed(AnActionEvent e) { diff --git a/platform/platform-impl/src/com/intellij/ide/actions/OccurenceNavigatorActionBase.java b/platform/platform-impl/src/com/intellij/ide/actions/OccurenceNavigatorActionBase.java index f2314a728913..c7ae78f0cd59 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/OccurenceNavigatorActionBase.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/OccurenceNavigatorActionBase.java @@ -68,14 +68,14 @@ abstract class OccurenceNavigatorActionBase extends AnAction implements DumbAwar if (project == null) { presentation.setEnabled(false); // make it invisible only in main menu to avoid initial invisibility in toolbars - presentation.setVisible(!ActionPlaces.MAIN_MENU.equals(event.getPlace())); + presentation.setVisible(!ActionPlaces.isMainMenuOrActionSearch(event.getPlace())); return; } OccurenceNavigator navigator = getNavigator(event.getDataContext()); if (navigator == null) { presentation.setEnabled(false); // make it invisible only in main menu to avoid initial invisibility in toolbars - presentation.setVisible(!ActionPlaces.MAIN_MENU.equals(event.getPlace())); + presentation.setVisible(!ActionPlaces.isMainMenuOrActionSearch(event.getPlace())); return; } presentation.setVisible(true); diff --git a/platform/platform-impl/src/com/intellij/ide/actions/PinActiveTabAction.java b/platform/platform-impl/src/com/intellij/ide/actions/PinActiveTabAction.java index ddfc1a3989aa..560082bf9e64 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/PinActiveTabAction.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/PinActiveTabAction.java @@ -116,12 +116,17 @@ public class PinActiveTabAction extends ToggleAction implements DumbAware { super.update(e); Presentation presentation = e.getPresentation(); DataContext context = e.getDataContext(); - if (getFile(context) != null) { - presentation.setEnabledAndVisible(true); - } - else { - Content content = getContent(context); - presentation.setEnabledAndVisible(content != null && content.isPinnable()); + EditorWindow window = getEditorWindow(context); + if (window == null || window.getOwner().isPreview()) { + presentation.setEnabledAndVisible(false); + } else { + if (getFile(context) != null) { + presentation.setEnabledAndVisible(true); + } + else { + Content content = getContent(context); + presentation.setEnabledAndVisible(content != null && content.isPinnable()); + } } if (ActionPlaces.EDITOR_TAB_POPUP.equals(e.getPlace()) || ViewContext.CELL_POPUP_PLACE.equals(e.getPlace())) { diff --git a/platform/platform-impl/src/com/intellij/ide/actions/ShowRecentFilesAction.java b/platform/platform-impl/src/com/intellij/ide/actions/ShowRecentFilesAction.java index 59ac567af38a..abd875af728f 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/ShowRecentFilesAction.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/ShowRecentFilesAction.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,13 +25,14 @@ import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; +import org.jetbrains.annotations.NotNull; /** * @author Konstantin Bulenkov */ public class ShowRecentFilesAction extends DumbAwareAction { @Override - public void actionPerformed(AnActionEvent e) { + public void actionPerformed(@NotNull AnActionEvent e) { final Project project = e.getData(CommonDataKeys.PROJECT); if (project != null) { FeatureUsageTracker.getInstance().triggerFeatureUsed("navigation.recent.files"); diff --git a/platform/platform-impl/src/com/intellij/ide/actions/ShowSettingsAction.java b/platform/platform-impl/src/com/intellij/ide/actions/ShowSettingsAction.java index eda6ec9f2d7c..5ced3f0db498 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/ShowSettingsAction.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/ShowSettingsAction.java @@ -34,7 +34,7 @@ public class ShowSettingsAction extends AnAction implements DumbAware { @Override public void update(AnActionEvent e) { - if (SystemInfo.isMac && e.getPlace().equals(ActionPlaces.MAIN_MENU)) { + if (SystemInfo.isMac && ActionPlaces.isMainMenuOrActionSearch(e.getPlace())) { // It's called from Preferences in App menu. e.getPresentation().setVisible(false); } diff --git a/platform/platform-impl/src/com/intellij/ide/actions/ShowSettingsUtilImpl.java b/platform/platform-impl/src/com/intellij/ide/actions/ShowSettingsUtilImpl.java index 2c5cc5b02282..8d6f806802c2 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/ShowSettingsUtilImpl.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/ShowSettingsUtilImpl.java @@ -75,7 +75,7 @@ public class ShowSettingsUtilImpl extends ShowSettingsUtil { new IdeConfigurablesGroup()}; return Registry.is("ide.new.settings.dialog") - ? MixedConfigurableGroup.getGroups(getConfigurables(groups, true)) + ? new ConfigurableGroup[]{new SortedConfigurableGroup(getConfigurables(groups, true))} : groups; } diff --git a/platform/platform-impl/src/com/intellij/ide/actions/SplitAction.java b/platform/platform-impl/src/com/intellij/ide/actions/SplitAction.java index fb55f3649484..9e9a349bb3fd 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/SplitAction.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/SplitAction.java @@ -60,7 +60,8 @@ public abstract class SplitAction extends AnAction implements DumbAware { final int minimum = myCloseSource ? 2 : 1; final boolean enabled = project != null && window != null - && window.getTabCount() >= minimum; + && window.getTabCount() >= minimum + && !window.getOwner().isPreview(); event.getPresentation().setEnabled(enabled); } } diff --git a/platform/platform-impl/src/com/intellij/ide/actions/Switcher.java b/platform/platform-impl/src/com/intellij/ide/actions/Switcher.java index cb35b5a504f9..cecf0f60a803 100644 --- a/platform/platform-impl/src/com/intellij/ide/actions/Switcher.java +++ b/platform/platform-impl/src/com/intellij/ide/actions/Switcher.java @@ -94,7 +94,7 @@ public class Switcher extends AnAction implements DumbAware { public void run() { synchronized (Switcher.class) { if (SWITCHER != null) { - SWITCHER.navigate(); + SWITCHER.navigate(false); } } } @@ -135,7 +135,7 @@ public class Switcher extends AnAction implements DumbAware { @NonNls private static final String SWITCHER_TITLE = "Switcher"; - public void actionPerformed(AnActionEvent e) { + public void actionPerformed(@NotNull AnActionEvent e) { final Project project = CommonDataKeys.PROJECT.getData(e.getDataContext()); if (project == null) return; @@ -200,7 +200,7 @@ public class Switcher extends AnAction implements DumbAware { jList.setSelectedIndex(jList.getAnchorSelectionIndex()); } if (jList.getSelectedIndex() != -1) { - navigate(); + navigate(false); } } return true; @@ -226,7 +226,7 @@ public class Switcher extends AnAction implements DumbAware { descriptions = new JPanel(new BorderLayout()) { @Override - protected void paintComponent(Graphics g) { + protected void paintComponent(@NotNull Graphics g) { super.paintComponent(g); g.setColor(UIUtil.isUnderDarcula() ? SEPARATOR_COLOR : BORDER_COLOR); g.drawLine(0, 0, getWidth(), 0); @@ -249,7 +249,7 @@ public class Switcher extends AnAction implements DumbAware { final Map map = ContainerUtil.reverseMap(twShortcuts); Collections.sort(windows, new Comparator() { @Override - public int compare(ToolWindow o1, ToolWindow o2) { + public int compare(@NotNull ToolWindow o1, @NotNull ToolWindow o2) { return StringUtil.compare(map.get(o1), map.get(o2), false); } }); @@ -297,7 +297,7 @@ public class Switcher extends AnAction implements DumbAware { toolWindows.addMouseMotionListener(this); myClickListener.installOn(toolWindows); toolWindows.getSelectionModel().addListSelectionListener(new ListSelectionListener() { - public void valueChanged(ListSelectionEvent e) { + public void valueChanged(@NotNull ListSelectionEvent e) { if (!toolWindows.isSelectionEmpty() && !files.isSelectionEmpty()) { files.clearSelection(); } @@ -306,7 +306,7 @@ public class Switcher extends AnAction implements DumbAware { separator = new JPanel() { @Override - protected void paintComponent(Graphics g) { + protected void paintComponent(@NotNull Graphics g) { super.paintComponent(g); g.setColor(SEPARATOR_COLOR); g.drawLine(0, 0, 0, getHeight()); @@ -374,7 +374,7 @@ public class Switcher extends AnAction implements DumbAware { JPanel myPanel = new JPanel(new BorderLayout()); JLabel myLabel = new JLabel() { @Override - protected void paintComponent(Graphics g) { + protected void paintComponent(@NotNull Graphics g) { GraphicsConfig config = new GraphicsConfig(g); ((Graphics2D)g).setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.3f)); super.paintComponent(g); @@ -425,7 +425,7 @@ public class Switcher extends AnAction implements DumbAware { return fullText; } - public void valueChanged(final ListSelectionEvent e) { + public void valueChanged(@NotNull final ListSelectionEvent e) { ApplicationManager.getApplication().invokeLater(new Runnable() { public void run() { updatePathLabel(); @@ -466,7 +466,7 @@ public class Switcher extends AnAction implements DumbAware { files.setSelectionMode(pinned ? ListSelectionModel.MULTIPLE_INTERVAL_SELECTION : ListSelectionModel.SINGLE_SELECTION); files.getSelectionModel().addListSelectionListener(new ListSelectionListener() { - public void valueChanged(ListSelectionEvent e) { + public void valueChanged(@NotNull ListSelectionEvent e) { if (!files.isSelectionEmpty() && !toolWindows.isSelectionEmpty()) { toolWindows.getSelectionModel().clearSelection(); } @@ -526,21 +526,21 @@ public class Switcher extends AnAction implements DumbAware { if (isPinnedMode()) { new AnAction(null ,null ,null) { @Override - public void actionPerformed(AnActionEvent e) { + public void actionPerformed(@NotNull AnActionEvent e) { changeSelection(); } }.registerCustomShortcutSet(CustomShortcutSet.fromString("TAB"), this, myPopup); new AnAction(null, null, null) { @Override - public void actionPerformed(AnActionEvent e) { + public void actionPerformed(@NotNull AnActionEvent e) { //suppress all actions to activate a toolwindow : IDEA-71277 } }.registerCustomShortcutSet(TW_SHORTCUT, this, myPopup); new AnAction(null, null, null) { @Override - public void actionPerformed(AnActionEvent e) { + public void actionPerformed(@NotNull AnActionEvent e) { if (mySpeedSearch != null && mySpeedSearch.isPopupActive()) { mySpeedSearch.hidePopup(); } else { @@ -590,24 +590,24 @@ public class Switcher extends AnAction implements DumbAware { return ((KeyboardShortcut)shortcutSet.getShortcuts()[0]).getFirstKeyStroke().getModifiers(); } - public void keyTyped(KeyEvent e) { + public void keyTyped(@NotNull KeyEvent e) { } public void keyReleased(KeyEvent e) { - if ((e.getKeyCode() == CTRL_KEY && isAutoHide()) - || e.getKeyCode() == VK_ENTER) { - navigate(); - } else - if (e.getKeyCode() == VK_LEFT) { + if (e.getKeyCode() == CTRL_KEY && isAutoHide() || e.getKeyCode() == VK_ENTER) { + navigate(e.isShiftDown()); + } + else if (e.getKeyCode() == VK_LEFT) { goLeft(); - } else if (e.getKeyCode() == VK_RIGHT) { + } + else if (e.getKeyCode() == VK_RIGHT) { goRight(); } } KeyEvent lastEvent; - public void keyPressed(KeyEvent e) { - if ((mySpeedSearch != null && mySpeedSearch.isPopupActive()) || lastEvent == e) return; + public void keyPressed(@NotNull KeyEvent e) { + if (mySpeedSearch != null && mySpeedSearch.isPopupActive() || lastEvent == e) return; lastEvent = e; switch (e.getKeyCode()) { case VK_UP: @@ -668,7 +668,7 @@ public class Switcher extends AnAction implements DumbAware { if (value instanceof FileInfo) { final FileInfo info = (FileInfo)value; final VirtualFile virtualFile = info.first; - final FileEditorManagerImpl editorManager = ((FileEditorManagerImpl)FileEditorManager.getInstance(project)); + final FileEditorManagerImpl editorManager = (FileEditorManagerImpl)FileEditorManager.getInstance(project); final JList jList = getSelectedList(); final EditorWindow wnd = findAppropriateWindow(info); if (wnd == null) { @@ -836,7 +836,7 @@ public class Switcher extends AnAction implements DumbAware { } } - void navigate() { + void navigate(final boolean openInNewWindow) { final Object[] values = getSelectedList().getSelectedValues(); myPopup.closeOk(null); if (values.length > 0 && values[0] instanceof ToolWindow) { @@ -850,14 +850,19 @@ public class Switcher extends AnAction implements DumbAware { if (value instanceof FileInfo) { final FileInfo info = (FileInfo)value; - if (info.second != null) { + VirtualFile file = info.first; + if (openInNewWindow) { + manager.openFileInNewWindow(file); + } + else if (info.second != null) { EditorWindow wnd = findAppropriateWindow(info); if (wnd != null) { - manager.openFileImpl2(wnd, info.first, true); - manager.addSelectionRecord(info.first, wnd); + manager.openFileImpl2(wnd, file, true); + manager.addSelectionRecord(file, wnd); } - } else { - manager.openFile(info.first, true, true); + } + else { + manager.openFile(file, true, true); } } } @@ -873,13 +878,13 @@ public class Switcher extends AnAction implements DumbAware { return ArrayUtil.contains(info.second, windows) ? info.second : windows.length > 0 ? windows[0] : null; } - public void mouseClicked(MouseEvent e) { + public void mouseClicked(@NotNull MouseEvent e) { } private boolean mouseMovedFirstTime = true; private JList mouseMoveSrc = null; private int mouseMoveListIndex = -1; - public void mouseMoved(MouseEvent e) { + public void mouseMoved(@NotNull MouseEvent e) { if (mouseMovedFirstTime) { mouseMovedFirstTime = false; return; @@ -907,15 +912,15 @@ public class Switcher extends AnAction implements DumbAware { files.repaint(); } - public void mousePressed(MouseEvent e) {} - public void mouseReleased(MouseEvent e) {} - public void mouseEntered(MouseEvent e) {} - public void mouseExited(MouseEvent e) { + public void mousePressed(@NotNull MouseEvent e) {} + public void mouseReleased(@NotNull MouseEvent e) {} + public void mouseEntered(@NotNull MouseEvent e) {} + public void mouseExited(@NotNull MouseEvent e) { mouseMoveSrc = null; mouseMoveListIndex = -1; repaintLists(); } - public void mouseDragged(MouseEvent e) {} + public void mouseDragged(@NotNull MouseEvent e) {} private class SwitcherSpeedSearch extends SpeedSearchBase implements PropertyChangeListener { private Object[] myElements; @@ -1031,7 +1036,7 @@ public class Switcher extends AnAction implements DumbAware { } @Override - public void propertyChange(PropertyChangeEvent evt) { + public void propertyChange(@NotNull PropertyChangeEvent evt) { final MyList list = getSelectedList(); final Object value = list.getSelectedValue(); if (project.isDisposed()) { @@ -1068,7 +1073,7 @@ public class Switcher extends AnAction implements DumbAware { private Rectangle dBounds; @Override - public void layoutContainer(Container target) { + public void layoutContainer(@NotNull Container target) { final JScrollPane scrollPane = UIUtil.getParentOfType(JScrollPane.class, files); JComponent filesPane = scrollPane != null ? scrollPane : files; if (sBounds == null || !target.isShowing()) { @@ -1142,7 +1147,7 @@ public class Switcher extends AnAction implements DumbAware { } @Override - public void processKeyEvent(KeyEvent e) { + public void processKeyEvent(@NotNull KeyEvent e) { super.processKeyEvent(e); } } diff --git a/platform/platform-impl/src/com/intellij/ide/customize/CustomizeIDEWizardDialog.java b/platform/platform-impl/src/com/intellij/ide/customize/CustomizeIDEWizardDialog.java index af3c193ca9d9..04beaa0f370e 100644 --- a/platform/platform-impl/src/com/intellij/ide/customize/CustomizeIDEWizardDialog.java +++ b/platform/platform-impl/src/com/intellij/ide/customize/CustomizeIDEWizardDialog.java @@ -181,10 +181,22 @@ public class CustomizeIDEWizardDialog extends DialogWrapper implements ActionLis return null;//Prevent closing by } + @Override + public void doCancelAction() { + doOKAction(); + } + @Override protected void doOKAction() { for (AbstractCustomizeWizardStep step : mySteps) { - if (!step.beforeOkAction()) return; + if (!step.beforeOkAction()) { + int index = mySteps.indexOf(step); + if (myIndex != index) { + myIndex = index; + initCurrentStep(true); + } + return; + } } super.doOKAction(); } diff --git a/platform/platform-impl/src/com/intellij/ide/dnd/aware/DnDAwareTree.java b/platform/platform-impl/src/com/intellij/ide/dnd/aware/DnDAwareTree.java index e871ff1872c2..7411c70e5984 100644 --- a/platform/platform-impl/src/com/intellij/ide/dnd/aware/DnDAwareTree.java +++ b/platform/platform-impl/src/com/intellij/ide/dnd/aware/DnDAwareTree.java @@ -19,6 +19,7 @@ import com.intellij.ide.dnd.DnDAware; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.SystemInfo; import com.intellij.ui.treeStructure.Tree; +import com.intellij.util.ui.JBSwingUtilities; import com.intellij.util.ui.UIUtil; import com.intellij.util.ui.tree.TreeUtil; import com.intellij.util.ui.tree.WideSelectionTreeUI; @@ -54,7 +55,7 @@ public class DnDAwareTree extends Tree implements DnDAware { @Override protected void processMouseMotionEvent(MouseEvent e) { - if (SystemInfo.isMac && SwingUtilities.isRightMouseButton(e) && e.getID() == MouseEvent.MOUSE_DRAGGED) return; + if (SystemInfo.isMac && JBSwingUtilities.isRightMouseButton(e) && e.getID() == MouseEvent.MOUSE_DRAGGED) return; super.processMouseMotionEvent(e); } diff --git a/platform/platform-impl/src/com/intellij/ide/passwordSafe/impl/providers/masterKey/MasterKeyPasswordSafe.java b/platform/platform-impl/src/com/intellij/ide/passwordSafe/impl/providers/masterKey/MasterKeyPasswordSafe.java index de5d67b5e2d8..4d3a502c253e 100644 --- a/platform/platform-impl/src/com/intellij/ide/passwordSafe/impl/providers/masterKey/MasterKeyPasswordSafe.java +++ b/platform/platform-impl/src/com/intellij/ide/passwordSafe/impl/providers/masterKey/MasterKeyPasswordSafe.java @@ -193,7 +193,7 @@ public class MasterKeyPasswordSafe extends BasePasswordSafeProvider { } return myKey.get().get(); } - }, project == null ? Condition.FALSE : project.getDisposed()); + }, project == null ? Conditions.alwaysFalse() : project.getDisposed()); if (key instanceof byte[]) return (byte[])key; if (key instanceof PasswordSafeException) throw (PasswordSafeException)key; diff --git a/platform/platform-impl/src/com/intellij/ide/plugins/PluginManager.java b/platform/platform-impl/src/com/intellij/ide/plugins/PluginManager.java index 57329fb8fe9d..e88a17416fc1 100644 --- a/platform/platform-impl/src/com/intellij/ide/plugins/PluginManager.java +++ b/platform/platform-impl/src/com/intellij/ide/plugins/PluginManager.java @@ -183,6 +183,7 @@ public class PluginManager extends PluginManagerCore { } else if (myPlugins2Enable != null && ENABLE.equals(description)) { disabledPlugins.removeAll(myPlugins2Enable); + PluginManagerMain.notifyPluginsWereUpdated("Changes were applied", null); } try { diff --git a/platform/platform-impl/src/com/intellij/ide/ui/AppearanceConfigurable.java b/platform/platform-impl/src/com/intellij/ide/ui/AppearanceConfigurable.java index cc6e16aba6c2..9accbc5bc6c8 100644 --- a/platform/platform-impl/src/com/intellij/ide/ui/AppearanceConfigurable.java +++ b/platform/platform-impl/src/com/intellij/ide/ui/AppearanceConfigurable.java @@ -174,6 +174,9 @@ public class AppearanceConfigurable extends BaseConfigurable implements Searchab update |= settings.SHOW_EDITOR_TOOLTIP != myComponent.myEditorTooltipCheckBox.isSelected(); settings.SHOW_EDITOR_TOOLTIP = myComponent.myEditorTooltipCheckBox.isSelected(); + update |= settings.NAVIGATE_TO_PREVIEW != myComponent.myNavigateToPreviewCheckBox.isSelected(); + settings.NAVIGATE_TO_PREVIEW = myComponent.myNavigateToPreviewCheckBox.isSelected(); + update |= settings.DISABLE_MNEMONICS_IN_CONTROLS != myComponent.myDisableMnemonicInControlsCheckBox.isSelected(); settings.DISABLE_MNEMONICS_IN_CONTROLS = myComponent.myDisableMnemonicInControlsCheckBox.isSelected(); @@ -280,6 +283,7 @@ public class AppearanceConfigurable extends BaseConfigurable implements Searchab myComponent.myLeftLayoutCheckBox.setSelected(settings.LEFT_HORIZONTAL_SPLIT); myComponent.myRightLayoutCheckBox.setSelected(settings.RIGHT_HORIZONTAL_SPLIT); myComponent.myEditorTooltipCheckBox.setSelected(settings.SHOW_EDITOR_TOOLTIP); + myComponent.myNavigateToPreviewCheckBox.setSelected(settings.NAVIGATE_TO_PREVIEW); myComponent.myDisableMnemonicInControlsCheckBox.setSelected(settings.DISABLE_MNEMONICS_IN_CONTROLS); boolean alphaModeEnabled = WindowManagerEx.getInstanceEx().isAlphaModeSupported(); @@ -325,6 +329,7 @@ public class AppearanceConfigurable extends BaseConfigurable implements Searchab isModified |= myComponent.myLeftLayoutCheckBox.isSelected() != settings.LEFT_HORIZONTAL_SPLIT; isModified |= myComponent.myRightLayoutCheckBox.isSelected() != settings.RIGHT_HORIZONTAL_SPLIT; isModified |= myComponent.myEditorTooltipCheckBox.isSelected() != settings.SHOW_EDITOR_TOOLTIP; + isModified |= myComponent.myNavigateToPreviewCheckBox.isSelected() != settings.NAVIGATE_TO_PREVIEW; isModified |= myComponent.myHideIconsInQuickNavigation.isSelected() != settings.SHOW_ICONS_IN_QUICK_NAVIGATION; @@ -397,6 +402,7 @@ public class AppearanceConfigurable extends BaseConfigurable implements Searchab private JSlider myInitialTooltipDelaySlider; private ComboBox myPresentationModeFontSize; private JCheckBox myEditorTooltipCheckBox; + private JCheckBox myNavigateToPreviewCheckBox; private JCheckBox myAllowStatusBar; private JCheckBox myAllowLineNumbers; private JCheckBox myAllowAnnotations; diff --git a/platform/platform-impl/src/com/intellij/ide/ui/AppearancePanel.form b/platform/platform-impl/src/com/intellij/ide/ui/AppearancePanel.form index 550e8e2482e1..7590feba2ad8 100644 --- a/platform/platform-impl/src/com/intellij/ide/ui/AppearancePanel.form +++ b/platform/platform-impl/src/com/intellij/ide/ui/AppearancePanel.form @@ -401,6 +401,14 @@ + + + + + + + + diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/LafManagerImpl.java b/platform/platform-impl/src/com/intellij/ide/ui/laf/LafManagerImpl.java index 1036307e97d7..7e00dc3a9841 100644 --- a/platform/platform-impl/src/com/intellij/ide/ui/laf/LafManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/ide/ui/laf/LafManagerImpl.java @@ -87,9 +87,8 @@ import java.util.List; */ @State( name = "LafManager", - roamingType = RoamingType.PER_PLATFORM, - storages = {@Storage( - file = StoragePathMacros.APP_CONFIG + "/options.xml")}) + storages = {@Storage(file = StoragePathMacros.APP_CONFIG + "/options.xml", roamingType = RoamingType.PER_PLATFORM)} +) public final class LafManagerImpl extends LafManager implements ApplicationComponent, PersistentStateComponent { private static final Logger LOG = Logger.getInstance("#com.intellij.ide.ui.LafManager"); diff --git a/platform/platform-impl/src/com/intellij/ide/ui/laf/intellijlaf.properties b/platform/platform-impl/src/com/intellij/ide/ui/laf/intellijlaf.properties index f721abfdb92b..ef0a4214db11 100644 --- a/platform/platform-impl/src/com/intellij/ide/ui/laf/intellijlaf.properties +++ b/platform/platform-impl/src/com/intellij/ide/ui/laf/intellijlaf.properties @@ -118,6 +118,7 @@ CheckBox.darcula.focused.backgroundColor2.selected=3B98FB ComboBoxUI=com.intellij.ide.ui.laf.darcula.ui.DarculaComboBoxUI ComboBox.disabledBackground=e8e8e8 +ComboBox.disabledForeground=777777 ComboBox.squareButton=false ComboBox.darcula.arrowFillColor=6e9bd5 ComboBox.darcula.arrowFocusedFillColor=2d82ed diff --git a/platform/platform-impl/src/com/intellij/ide/ui/search/SearchUtil.java b/platform/platform-impl/src/com/intellij/ide/ui/search/SearchUtil.java index 8febc9ad392a..a0df30abae1c 100644 --- a/platform/platform-impl/src/com/intellij/ide/ui/search/SearchUtil.java +++ b/platform/platform-impl/src/com/intellij/ide/ui/search/SearchUtil.java @@ -41,8 +41,6 @@ import javax.swing.*; import javax.swing.border.Border; import javax.swing.border.TitledBorder; import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.util.*; @@ -314,18 +312,6 @@ public class SearchUtil { } } - public static Runnable lightOptions(final SearchableConfigurable configurable, - final JComponent component, - final String option, - final GlassPanel glassPanel, - final boolean forceSelect) { - return new Runnable() { - public void run() { - traverseComponentsTree(configurable, glassPanel, component, option, forceSelect); - } - }; - } - public static String markup(@NonNls @NotNull String textToMarkup, @Nullable String filter) { if (filter == null || filter.length() == 0) { return textToMarkup; @@ -575,80 +561,6 @@ public class SearchUtil { return null; } - public static void showHintPopup(final ConfigurableSearchTextField searchField, - final JBPopup[] activePopup, - final Alarm showHintAlarm, - final Consumer selectConfigurable, - final Project project) { - for (JBPopup aPopup : activePopup) { - if (aPopup != null) { - aPopup.cancel(); - } - } - - final JBPopup popup = createPopup(searchField, activePopup, showHintAlarm, selectConfigurable, project, 0); //no selection - if (popup != null) { - popup.showUnderneathOf(searchField); - searchField.requestFocusInWindow(); - } - - activePopup[0] = popup; - activePopup[1] = null; - } - - - public static void registerKeyboardNavigation(final ConfigurableSearchTextField searchField, - final JBPopup[] activePopup, - final Alarm showHintAlarm, - final Consumer selectConfigurable, - final Project project) { - final Consumer shower = new Consumer() { - public void consume(final Integer direction) { - if (activePopup[0] != null) { - activePopup[0].cancel(); - } - - if (activePopup[1] != null && activePopup[1].isVisible()) { - return; - } - - final JBPopup popup = createPopup(searchField, activePopup, showHintAlarm, selectConfigurable, project, direction.intValue()); - if (popup != null) { - popup.showUnderneathOf(searchField); - } - activePopup[0] = null; - activePopup[1] = popup; - } - }; - searchField.registerKeyboardAction(new ActionListener() { - public void actionPerformed(ActionEvent e) { - shower.consume(1); - } - }, KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), JComponent.WHEN_IN_FOCUSED_WINDOW); - searchField.registerKeyboardAction(new ActionListener() { - public void actionPerformed(ActionEvent e) { - shower.consume(-1); - } - }, KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), JComponent.WHEN_IN_FOCUSED_WINDOW); - - searchField.addKeyboardListener(new KeyAdapter() { - public void keyPressed(KeyEvent e) { - if (e.getKeyCode() == KeyEvent.VK_ESCAPE && searchField.getText().length() > 0) { - e.consume(); - if (cancelPopups(activePopup)) return; - searchField.setText(""); - } - else if (e.getKeyCode() == KeyEvent.VK_ENTER) { - searchField.addCurrentTextToHistory(); - cancelPopups(activePopup); - if (e.getModifiers() == 0) { - e.consume(); - } - } - } - }); - } - private static boolean cancelPopups(final JBPopup[] activePopup) { for (JBPopup popup : activePopup) { if (popup != null && popup.isVisible()) { diff --git a/platform/platform-impl/src/com/intellij/ide/util/ProjectPropertiesComponentImpl.java b/platform/platform-impl/src/com/intellij/ide/util/ProjectPropertiesComponentImpl.java index e44921e6d5df..4b11b44b2bc9 100644 --- a/platform/platform-impl/src/com/intellij/ide/util/ProjectPropertiesComponentImpl.java +++ b/platform/platform-impl/src/com/intellij/ide/util/ProjectPropertiesComponentImpl.java @@ -15,15 +15,13 @@ */ package com.intellij.ide.util; -import com.intellij.openapi.components.RoamingType; import com.intellij.openapi.components.State; import com.intellij.openapi.components.Storage; import com.intellij.openapi.components.StoragePathMacros; @State( - name = "PropertiesComponent", - roamingType = RoamingType.DISABLED, - storages = {@Storage( - file = StoragePathMacros.WORKSPACE_FILE)}) + name = "PropertiesComponent", + storages = {@Storage(file = StoragePathMacros.WORKSPACE_FILE)} +) public class ProjectPropertiesComponentImpl extends PropertiesComponentImpl { } diff --git a/platform/platform-impl/src/com/intellij/internal/statistic/UsageTrigger.java b/platform/platform-impl/src/com/intellij/internal/statistic/UsageTrigger.java index 40282a89eded..99e62b9da292 100644 --- a/platform/platform-impl/src/com/intellij/internal/statistic/UsageTrigger.java +++ b/platform/platform-impl/src/com/intellij/internal/statistic/UsageTrigger.java @@ -34,7 +34,10 @@ import java.util.Set; /** * User: ksafonov */ -@State(name = "UsageTrigger", roamingType = RoamingType.DISABLED, storages = {@Storage(file = StoragePathMacros.APP_CONFIG + "/statistics.application.usages.xml")}) +@State( + name = "UsageTrigger", + storages = {@Storage(file = StoragePathMacros.APP_CONFIG + "/statistics.application.usages.xml", roamingType = RoamingType.DISABLED)} +) public class UsageTrigger implements PersistentStateComponent { public static class State { @@ -72,7 +75,7 @@ public class UsageTrigger implements PersistentStateComponent { diff --git a/platform/platform-impl/src/com/intellij/internal/statistic/persistence/UsageStatisticsPersistenceComponent.java b/platform/platform-impl/src/com/intellij/internal/statistic/persistence/UsageStatisticsPersistenceComponent.java index 65298387a902..d60be232c9ea 100644 --- a/platform/platform-impl/src/com/intellij/internal/statistic/persistence/UsageStatisticsPersistenceComponent.java +++ b/platform/platform-impl/src/com/intellij/internal/statistic/persistence/UsageStatisticsPersistenceComponent.java @@ -35,11 +35,7 @@ import java.util.Set; @State( name = "UsagesStatistic", - roamingType = RoamingType.DISABLED, - storages = { - @Storage( - file = StoragePathMacros.APP_CONFIG + "/usage.statistics.xml" - )} + storages = {@Storage(file = StoragePathMacros.APP_CONFIG + "/usage.statistics.xml", roamingType = RoamingType.DISABLED)} ) public class UsageStatisticsPersistenceComponent extends BasicSentUsagesPersistenceComponent implements NamedComponent, PersistentStateComponent { diff --git a/platform/platform-impl/src/com/intellij/notification/impl/ui/NotificationsConfigurablePanel.java b/platform/platform-impl/src/com/intellij/notification/impl/ui/NotificationsConfigurablePanel.java index 2d6a768a92c2..50ca0941f621 100644 --- a/platform/platform-impl/src/com/intellij/notification/impl/ui/NotificationsConfigurablePanel.java +++ b/platform/platform-impl/src/com/intellij/notification/impl/ui/NotificationsConfigurablePanel.java @@ -125,6 +125,7 @@ public class NotificationsConfigurablePanel extends JPanel implements Disposable public NotificationsTable() { super(new NotificationsTableModel()); + setSelectionMode(ListSelectionModel.SINGLE_SELECTION); final TableColumn idColumn = getColumnModel().getColumn(ID_COLUMN); idColumn.setPreferredWidth(200); @@ -151,7 +152,7 @@ public class NotificationsConfigurablePanel extends JPanel implements Disposable displayTypeColumn.setCellRenderer(new ComboBoxTableRenderer(NotificationDisplayType.values()) { @Override protected void customizeComponent(NotificationDisplayType value, JTable table, boolean isSelected) { - super.customizeComponent(value, table, isSelected); + super.customizeComponent(myDisplayBalloons.isSelected() ? value : NotificationDisplayType.NONE, table, isSelected); if (!myDisplayBalloons.isSelected() && !isSelected) { setBackground(UIUtil.getComboBoxDisabledBackground()); setForeground(UIUtil.getComboBoxDisabledForeground()); diff --git a/platform/platform-impl/src/com/intellij/openapi/actionSystem/ComputableActionGroup.java b/platform/platform-impl/src/com/intellij/openapi/actionSystem/ComputableActionGroup.java index b5027fd9df81..3b0e247180b2 100644 --- a/platform/platform-impl/src/com/intellij/openapi/actionSystem/ComputableActionGroup.java +++ b/platform/platform-impl/src/com/intellij/openapi/actionSystem/ComputableActionGroup.java @@ -55,6 +55,13 @@ public abstract class ComputableActionGroup extends ActionGroup implements DumbA protected abstract CachedValueProvider createChildrenProvider(@NotNull ActionManager actionManager); public abstract static class Simple extends ComputableActionGroup { + protected Simple() { + } + + protected Simple(boolean popup) { + super(popup); + } + @NotNull @Override protected final CachedValueProvider createChildrenProvider(@NotNull final ActionManager actionManager) { diff --git a/platform/platform-impl/src/com/intellij/openapi/actionSystem/ex/QuickListsManager.java b/platform/platform-impl/src/com/intellij/openapi/actionSystem/ex/QuickListsManager.java index bf7c79695c94..eb9436481767 100644 --- a/platform/platform-impl/src/com/intellij/openapi/actionSystem/ex/QuickListsManager.java +++ b/platform/platform-impl/src/com/intellij/openapi/actionSystem/ex/QuickListsManager.java @@ -70,10 +70,10 @@ public class QuickListsManager implements ExportableApplicationComponent, NamedJ return loadListFromDocument(schemeContent); } - public Document writeScheme(@NotNull final QuickList scheme) throws WriteExternalException { + public Element writeScheme(@NotNull final QuickList scheme) throws WriteExternalException { Element element = new Element(LIST_TAG); scheme.writeExternal(element); - return new Document(element); + return element; } public boolean shouldBeSaved(@NotNull final QuickList scheme) { diff --git a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/AbbreviationManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/AbbreviationManagerImpl.java index 0d116c7a927f..fd96e54fc98e 100644 --- a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/AbbreviationManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/AbbreviationManagerImpl.java @@ -31,11 +31,7 @@ import java.util.*; */ @State( name = "AbbreviationManager", - roamingType = RoamingType.PER_PLATFORM, - storages = { - @Storage( - file = StoragePathMacros.APP_CONFIG + "/abbreviations.xml" - )} + storages = {@Storage(file = StoragePathMacros.APP_CONFIG + "/abbreviations.xml", roamingType = RoamingType.PER_PLATFORM)} ) public class AbbreviationManagerImpl extends AbbreviationManager implements ExportableComponent, PersistentStateComponent { diff --git a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionManagerImpl.java index c5a9b01c4d21..32e823bd9758 100644 --- a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ActionManagerImpl.java @@ -70,6 +70,7 @@ import java.awt.*; import java.awt.event.*; import java.util.*; import java.util.List; +import java.util.concurrent.Future; public final class ActionManagerImpl extends ActionManagerEx implements ApplicationComponent { @NonNls public static final String ACTION_ELEMENT_NAME = "action"; @@ -360,7 +361,7 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat } private static void assertActionIsGroupOrStub(final AnAction action) { - if (!(action instanceof ActionGroup || action instanceof ActionStub)) { + if (!(action instanceof ActionGroup || action instanceof ActionStub || action instanceof ChameleonAction)) { LOG.error("Action : " + action + "; class: " + action.getClass()); } } @@ -520,11 +521,9 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat LOG.assertTrue(action.equals(stub)); AnAction anAction = convertStub(stub); - - addToMap(stub.getId(), anAction, stub.getPluginId(), stub.getProjectType() == null ? null : new ProjectType(stub.getProjectType())); myAction2Id.put(anAction, stub.getId()); - return anAction; + return addToMap(stub.getId(), anAction, stub.getPluginId(), stub.getProjectType()); } @Override @@ -653,7 +652,7 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat } } else { - registerAction(id, action, pluginId); + registerAction(id, action, pluginId, element.getAttributeValue(PROJECT_TYPE)); } } } @@ -991,8 +990,12 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat @Override public void registerAction(@NotNull String actionId, @NotNull AnAction action, @Nullable PluginId pluginId) { + registerAction(actionId, action, pluginId, null); + } + + public void registerAction(@NotNull String actionId, @NotNull AnAction action, @Nullable PluginId pluginId, @Nullable String projectType) { synchronized (myLock) { - if (!addToMap(actionId, action, pluginId, null)) return; + if (addToMap(actionId, action, pluginId, projectType) == null) return; if (myAction2Id.containsKey(action)) { reportActionError(pluginId, "action was already registered for another ID. ID is " + myAction2Id.get(action) + getPluginInfo(pluginId)); @@ -1012,31 +1015,42 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat } } - public boolean addToMap(String actionId, AnAction action, PluginId pluginId, ProjectType projectType) { - if (myId2Action.containsKey(actionId)) { - // make sure id+projectType is unique - AnAction o = myId2Action.get(actionId); - ChameleonAction chameleonAction; - if (o instanceof ChameleonAction) { - chameleonAction = (ChameleonAction)o; - } - else { - chameleonAction = new ChameleonAction(o, projectType); - myId2Action.put(actionId, chameleonAction); - } - AnAction old = chameleonAction.addAction(action, projectType); - if (old != null) { - reportActionError(pluginId, - "action with the ID \"" + actionId + "\" was already registered. Action being registered is " + action + - "; Registered action is " + - myId2Action.get(actionId) + getPluginInfo(pluginId)); - return false; - } + private AnAction addToMap(String actionId, AnAction action, PluginId pluginId, String projectType) { + if (projectType != null || myId2Action.containsKey(actionId)) { + return registerChameleon(actionId, action, pluginId, projectType); } else { myId2Action.put(actionId, action); + return action; } - return true; + } + + private AnAction registerChameleon(String actionId, AnAction action, PluginId pluginId, String projectType) { + ProjectType type = projectType == null ? null : new ProjectType(projectType); + // make sure id+projectType is unique + AnAction o = myId2Action.get(actionId); + ChameleonAction chameleonAction; + if (o == null) { + chameleonAction = new ChameleonAction(action, type); + myId2Action.put(actionId, chameleonAction); + return chameleonAction; + } + if (o instanceof ChameleonAction) { + chameleonAction = (ChameleonAction)o; + } + else { + chameleonAction = new ChameleonAction(o, type); + myId2Action.put(actionId, chameleonAction); + } + AnAction old = chameleonAction.addAction(action, type); + if (old != null) { + reportActionError(pluginId, + "action with the ID \"" + actionId + "\" was already registered. Action being registered is " + action + + "; Registered action is " + + myId2Action.get(actionId) + getPluginInfo(pluginId)); + return null; + } + return chameleonAction; } @Override @@ -1053,7 +1067,7 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat return; } } - AnAction oldValue = (AnAction)myId2Action.remove(actionId); + AnAction oldValue = myId2Action.remove(actionId); myAction2Id.remove(oldValue); myId2Index.remove(actionId); for (PluginId pluginName : myPlugin2Id.keySet()) { @@ -1251,7 +1265,7 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat } } - public void preloadActions() { + public Future preloadActions() { if (myPreloadActionsRunnable == null) { myPreloadActionsRunnable = new Runnable() { @Override @@ -1262,8 +1276,9 @@ public final class ActionManagerImpl extends ActionManagerEx implements Applicat } } }; - ApplicationManager.getApplication().executeOnPooledThread(myPreloadActionsRunnable); + return ApplicationManager.getApplication().executeOnPooledThread(myPreloadActionsRunnable); } + return null; } private void doPreloadActions() { diff --git a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ChameleonAction.java b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ChameleonAction.java index 4b46dc191c0c..f4e92f7eae02 100644 --- a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ChameleonAction.java +++ b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/ChameleonAction.java @@ -15,14 +15,13 @@ */ package com.intellij.openapi.actionSystem.impl; -import com.intellij.openapi.actionSystem.ActionStub; -import com.intellij.openapi.actionSystem.AnAction; -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.actionSystem.CommonDataKeys; +import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectType; import com.intellij.openapi.project.ProjectTypeService; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.TestOnly; import java.util.HashMap; import java.util.Map; @@ -36,6 +35,7 @@ public class ChameleonAction extends AnAction { public ChameleonAction(@NotNull AnAction first, ProjectType projectType) { addAction(first, projectType); + copyFrom(myActions.values().iterator().next()); } public AnAction addAction(AnAction action, ProjectType projectType) { @@ -48,21 +48,35 @@ public class ChameleonAction extends AnAction { } @Override - public void actionPerformed(AnActionEvent e) { - getAction(e).actionPerformed(e); + public void actionPerformed(@NotNull AnActionEvent e) { + AnAction action = getAction(e); + assert action != null; + action.actionPerformed(e); } @Override - public void update(AnActionEvent e) { - super.update(e); + public void update(@NotNull AnActionEvent e) { + AnAction action = getAction(e); + if (action != null) { + e.getPresentation().setVisible(true); + action.update(e); + } + else { + e.getPresentation().setVisible(false); + } } + @Nullable private AnAction getAction(AnActionEvent e) { Project project = CommonDataKeys.PROJECT.getData(e.getDataContext()); ProjectType projectType = ProjectTypeService.getProjectType(project); AnAction action = myActions.get(projectType); if (action == null) action = myActions.get(null); - if (action == null) action = myActions.values().iterator().next(); return action; } + + @TestOnly + public Map getActions() { + return myActions; + } } diff --git a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/Utils.java b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/Utils.java index 85c0555f9bc6..9af1d30c28bf 100644 --- a/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/Utils.java +++ b/platform/platform-impl/src/com/intellij/openapi/actionSystem/impl/Utils.java @@ -26,6 +26,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.util.ActionCallback; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.registry.Registry; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.wm.IdeFocusManager; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NonNls; @@ -164,7 +165,7 @@ public class Utils{ } } else if (child instanceof Separator) { - if (!list.isEmpty() && !(list.get(list.size() - 1) instanceof Separator)) { + if (!StringUtil.isEmpty(((Separator)child).getText()) || (!list.isEmpty() && !(list.get(list.size() - 1) instanceof Separator))) { list.add(child); } } @@ -284,8 +285,10 @@ public class Utils{ for (int i = 0, size = list.size(); i < size; i++) { final AnAction action = list.get(i); if (action instanceof Separator) { - if (i > 0 && i < size - 1) { + final String text = ((Separator)action).getText(); + if (!StringUtil.isEmpty(text) || (i > 0 && i < size - 1)) { component.add(new JPopupMenu.Separator() { + private final JMenuItem myMenu = !StringUtil.isEmpty(text) ? new JMenuItem(text) : null; @Override public Insets getInsets() { final Insets insets = super.getInsets(); @@ -295,13 +298,30 @@ public class Utils{ return fix ? new Insets(2, insets.left, 3, insets.right) : insets; // workaround for Sun bug #6636964 } + @Override + public void doLayout() { + super.doLayout(); + if (myMenu != null) { + myMenu.setBounds(getBounds()); + } + } + @Override protected void paintComponent(Graphics g) { if (UIUtil.isUnderWindowsClassicLookAndFeel() || UIUtil.isUnderDarcula() || UIUtil.isUnderWindowsLookAndFeel()) { g.setColor(component.getBackground()); g.fillRect(0, 0, getWidth(), getHeight()); } - super.paintComponent(g); + if (myMenu != null) { + myMenu.paint(g); + } else { + super.paintComponent(g); + } + } + + @Override + public Dimension getPreferredSize() { + return myMenu != null ? myMenu.getPreferredSize() : super.getPreferredSize(); } }); } diff --git a/platform/platform-impl/src/com/intellij/openapi/application/PluginPathManager.java b/platform/platform-impl/src/com/intellij/openapi/application/PluginPathManager.java index 1ab050053e28..9bad9d2d3774 100644 --- a/platform/platform-impl/src/com/intellij/openapi/application/PluginPathManager.java +++ b/platform/platform-impl/src/com/intellij/openapi/application/PluginPathManager.java @@ -16,8 +16,10 @@ package com.intellij.openapi.application; import com.intellij.openapi.util.io.FileUtil; +import org.jetbrains.annotations.NotNull; import java.io.File; +import java.io.FileFilter; import java.util.*; /** @@ -32,26 +34,38 @@ public class PluginPathManager { private static List findSubrepos() { List result = new ArrayList(); - File[] subdirs = new File(PathManager.getHomePath()).listFiles(); - if (subdirs == null) return result; - Arrays.sort(subdirs, new Comparator() { + File[] gitRoots = getSortedGitRoots(new File(PathManager.getHomePath())); + for (File subdir : gitRoots) { + File pluginsDir = new File(subdir, "plugins"); + if (pluginsDir.exists()) { + result.add(pluginsDir); + } + else { + result.add(subdir); + } + result.addAll(Arrays.asList(getSortedGitRoots(subdir))); + } + return result; + } + + @NotNull + private static File[] getSortedGitRoots(@NotNull File dir) { + File[] gitRoots = dir.listFiles(new FileFilter() { + @Override + public boolean accept(File child) { + return child.isDirectory() && new File(child, ".git").exists(); + } + }); + if (gitRoots == null) { + return new File[0]; + } + Arrays.sort(gitRoots, new Comparator() { @Override public int compare(File file, File file2) { return FileUtil.compareFiles(file, file2); } }); - for (File subdir : subdirs) { - if (new File(subdir, ".git").exists()) { - File pluginsDir = new File(subdir, "plugins"); - if (pluginsDir.exists()) { - result.add(pluginsDir); - } - else { - result.add(subdir); - } - } - } - return result; + return gitRoots; } } diff --git a/platform/platform-impl/src/com/intellij/openapi/application/impl/ApplicationImpl.java b/platform/platform-impl/src/com/intellij/openapi/application/impl/ApplicationImpl.java index e3a36a2e59ab..a8eca00b505e 100644 --- a/platform/platform-impl/src/com/intellij/openapi/application/impl/ApplicationImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/application/impl/ApplicationImpl.java @@ -531,10 +531,10 @@ public class ApplicationImpl extends PlatformComponentManagerImpl implements App @Override public void load(@Nullable String optionsPath) throws IOException { - load(PathManager.getConfigPath(), optionsPath); + load(PathManager.getConfigPath(), optionsPath == null ? PathManager.getOptionsPath() : optionsPath); } - public void load(@NotNull String configPath, @Nullable String optionsPath) throws IOException { + public void load(@NotNull String configPath, @NotNull String optionsPath) throws IOException { getStateStore().setOptionsPath(optionsPath); getStateStore().setConfigPath(configPath); diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ApplicationStoreImpl.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ApplicationStoreImpl.java index a00533d3244b..bf03cb11f1d6 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ApplicationStoreImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ApplicationStoreImpl.java @@ -36,7 +36,6 @@ class ApplicationStoreImpl extends ComponentStoreImpl implements IApplicationSto private static final String XML_EXTENSION = ".xml"; private static final String DEFAULT_STORAGE_SPEC = StoragePathMacros.APP_CONFIG + "/" + PathManager.DEFAULT_OPTIONS_FILE_NAME + XML_EXTENSION; - private static final String OPTIONS_MACRO = "OPTIONS"; private static final String ROOT_ELEMENT_NAME = "application"; private final ApplicationImpl myApplication; @@ -93,14 +92,13 @@ class ApplicationStoreImpl extends ComponentStoreImpl implements IApplicationSto } @Override - public void setOptionsPath(final String path) { - myStateStorageManager.addMacro(StoragePathMacros.getMacroName(StoragePathMacros.APP_CONFIG), path); - myStateStorageManager.addMacro(OPTIONS_MACRO, path); + public void setOptionsPath(@NotNull String path) { + myStateStorageManager.addMacro(StoragePathMacros.APP_CONFIG, path); } @Override public void setConfigPath(@NotNull final String configPath) { - myStateStorageManager.addMacro(StoragePathMacros.getMacroName(StoragePathMacros.ROOT_CONFIG), configPath); + myStateStorageManager.addMacro(StoragePathMacros.ROOT_CONFIG, configPath); myConfigPath = configPath; } diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/BaseFileConfigurableStoreImpl.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/BaseFileConfigurableStoreImpl.java index 25dd8a05cab3..dc07fee151e2 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/BaseFileConfigurableStoreImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/BaseFileConfigurableStoreImpl.java @@ -59,8 +59,8 @@ abstract class BaseFileConfigurableStoreImpl extends ComponentStoreImpl { } @Override - public void load(@NotNull final Element rootElement) throws IOException { - super.load(rootElement); + public void load(@NotNull Element rootElement, @Nullable PathMacroSubstitutor pathMacroSubstitutor, boolean intern) { + super.load(rootElement, pathMacroSubstitutor, intern); final String v = rootElement.getAttributeValue(VERSION_OPTION); if (v != null) { @@ -74,7 +74,11 @@ abstract class BaseFileConfigurableStoreImpl extends ComponentStoreImpl { @Override @NotNull protected Element save() { - final Element root = super.save(); + Element root = super.save(); + if (root == null) { + root = new Element(myRootElementName); + } + root.setAttribute(VERSION_OPTION, Integer.toString(myVersion)); return root; } @@ -113,7 +117,7 @@ abstract class BaseFileConfigurableStoreImpl extends ComponentStoreImpl { } public BaseStorageData getMainStorageData() throws StateStorageException { - return (BaseStorageData) getMainStorage().getStorageData(false); + return (BaseStorageData)getMainStorage().getStorageData(); } @Nullable diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ComponentStoreImpl.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ComponentStoreImpl.java index fc2017b57a4a..ed63bf12d4c4 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ComponentStoreImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ComponentStoreImpl.java @@ -29,12 +29,12 @@ import com.intellij.openapi.util.*; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.ArrayUtil; import com.intellij.util.ReflectionUtil; -import com.intellij.util.io.fs.IFile; import gnu.trove.THashMap; import org.jdom.Element; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.io.File; import java.io.IOException; import java.lang.reflect.Type; import java.util.*; @@ -46,12 +46,6 @@ public abstract class ComponentStoreImpl implements IComponentStore { private final List mySettingsSavingComponents = Collections.synchronizedList(new ArrayList()); @Nullable private SaveSessionImpl mySession; - @Deprecated - @Nullable - private StateStorage getStateStorage(@NotNull final Storage storageSpec) throws StateStorageException { - return getStateStorageManager().getStateStorage(storageSpec); - } - @Nullable protected abstract StateStorage getDefaultsStorage(); @@ -255,9 +249,10 @@ public abstract class ComponentStoreImpl implements IComponentStore { Storage[] storageSpecs = getComponentStorageSpecs(component, StateStorageOperation.READ); for (Storage storageSpec : storageSpecs) { - StateStorage stateStorage = getStateStorage(storageSpec); - if (stateStorage == null || !stateStorage.hasState(component, name, stateClass, reloadData)) continue; - state = stateStorage.getState(component, name, stateClass, state); + StateStorage stateStorage = getStateStorageManager().getStateStorage(storageSpec); + if (stateStorage != null && stateStorage.hasState(component, name, stateClass, reloadData)) { + state = stateStorage.getState(component, name, stateClass, state); + } } if (state != null) { @@ -320,17 +315,15 @@ public abstract class ComponentStoreImpl implements IComponentStore { } assert storages.length > 0; - final Class defaultClass = - StorageAnnotationsDefaultValues.NullStateStorageChooser.class; - final Class storageChooserClass = stateSpec.storageChooser(); - final StateStorageChooser> defaultStateStorageChooser = getDefaultStateStorageChooser(); - assert storageChooserClass != defaultClass || defaultStateStorageChooser != null : "State chooser not specified for: " + - persistentStateComponent.getClass(); - - if (storageChooserClass == defaultClass) { + if (storageChooserClass == StateStorageChooser.class) { + StateStorageChooser> defaultStateStorageChooser = getDefaultStateStorageChooser(); + assert defaultStateStorageChooser != null : "State chooser not specified for: " + persistentStateComponent.getClass(); return defaultStateStorageChooser.selectStorages(storages, persistentStateComponent, operation); } + else if (storageChooserClass == LastStorageChooserForWrite.class) { + return LastStorageChooserForWrite.INSTANCE.selectStorages(storages, persistentStateComponent, operation); + } else { try { @SuppressWarnings("unchecked") @@ -361,7 +354,7 @@ public abstract class ComponentStoreImpl implements IComponentStore { @NotNull @Override - public List getAllStorageFilesToSave(final boolean includingSubStructures) throws IOException { + public List getAllStorageFilesToSave(final boolean includingSubStructures) throws IOException { try { return myStorageManagerSaveSession.getAllStorageFilesToSave(); } @@ -426,12 +419,10 @@ public abstract class ComponentStoreImpl implements IComponentStore { protected void commit() throws StateStorageException { final StateStorageManager storageManager = getStateStorageManager(); - final StateStorageManager.ExternalizationSession session = storageManager.startExternalization(); String[] names = ArrayUtil.toStringArray(myComponents.keySet()); Arrays.sort(names); - for (String name : names) { Object component = myComponents.get(name); if (component instanceof PersistentStateComponent) { @@ -452,10 +443,9 @@ public abstract class ComponentStoreImpl implements IComponentStore { @NotNull @Override - public List getAllStorageFiles(final boolean includingSubStructures) { + public List getAllStorageFiles(final boolean includingSubStructures) { return myStorageManagerSaveSession.getAllStorageFiles(); } - } @Override diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/CompoundSaveSession.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/CompoundSaveSession.java index 51cc717726f3..aacdb58aeeec 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/CompoundSaveSession.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/CompoundSaveSession.java @@ -18,9 +18,9 @@ package com.intellij.openapi.components.impl.stores; import com.intellij.openapi.components.StateStorage; import com.intellij.openapi.components.StateStorageException; import com.intellij.util.SmartList; -import com.intellij.util.io.fs.IFile; import gnu.trove.THashMap; +import java.io.File; import java.util.List; import java.util.Map; @@ -36,8 +36,8 @@ public class CompoundSaveSession { } } - public List getAllStorageFilesToSave() throws StateStorageException { - List result = new SmartList(); + public List getAllStorageFilesToSave() throws StateStorageException { + List result = new SmartList(); for (StateStorage.SaveSession saveSession : mySaveSessions.values()) { result.addAll(saveSession.getStorageFilesToSave()); } @@ -70,8 +70,8 @@ public class CompoundSaveSession { return mySaveSessions.get(storage); } - public List getAllStorageFiles() { - List result = new SmartList(); + public List getAllStorageFiles() { + List result = new SmartList(); for (StateStorage.SaveSession saveSession : mySaveSessions.values()) { result.addAll(saveSession.getAllStorageFiles()); } diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/DefaultProjectStoreImpl.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/DefaultProjectStoreImpl.java index 6f0c3b8122cf..fad17c8d2889 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/DefaultProjectStoreImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/DefaultProjectStoreImpl.java @@ -21,13 +21,12 @@ import com.intellij.openapi.project.impl.ProjectImpl; import com.intellij.openapi.project.impl.ProjectManagerImpl; import com.intellij.openapi.util.Pair; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.util.io.fs.IFile; -import org.jdom.Document; import org.jdom.Element; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.io.File; import java.io.IOException; import java.util.Collection; import java.util.Collections; @@ -40,10 +39,10 @@ public class DefaultProjectStoreImpl extends ProjectStoreImpl { private final ProjectManagerImpl myProjectManager; @NonNls private static final String ROOT_TAG_NAME = "defaultProject"; - public DefaultProjectStoreImpl(final ProjectImpl project, final ProjectManagerImpl projectManager) { + public DefaultProjectStoreImpl(@NotNull ProjectImpl project, final ProjectManagerImpl projectManager) { super(project); - myProjectManager = projectManager; + myProjectManager = projectManager; myElement = projectManager.getDefaultProjectRootElement(); } @@ -53,29 +52,28 @@ public class DefaultProjectStoreImpl extends ProjectStoreImpl { return element != null ? element.clone() : null; } - @NotNull @Override protected StateStorageManager createStateStorageManager() { - Document _d = null; + Element _d = null; if (myElement != null) { myElement.detach(); - _d = new Document(myElement); + _d = myElement; } final ComponentManager componentManager = getComponentManager(); final PathMacroManager pathMacroManager = PathMacroManager.getInstance(componentManager); - final Document document = _d; + final Element element = _d; - final XmlElementStorage storage = new XmlElementStorage(pathMacroManager.createTrackingSubstitutor(), componentManager, - ROOT_TAG_NAME, null, "", ComponentRoamingManager.getInstance(), + final XmlElementStorage storage = new XmlElementStorage("", RoamingType.DISABLED, pathMacroManager.createTrackingSubstitutor(), componentManager, + ROOT_TAG_NAME, null, ComponentVersionProvider.EMPTY) { @Override @Nullable - protected Document loadDocument() throws StateStorageException { - return document; + protected Element loadLocalData() { + return element; } @Override @@ -96,26 +94,28 @@ public class DefaultProjectStoreImpl extends ProjectStoreImpl { @Override protected void doSave() throws StateStorageException { - myProjectManager.setDefaultProjectRootElement(getDocumentToSave().getRootElement()); + Element element = getElementToSave(); + myProjectManager.setDefaultProjectRootElement(element == null ? null : element); } @NotNull @Override - public Collection getStorageFilesToSave() throws StateStorageException { + public Collection getStorageFilesToSave() throws StateStorageException { return Collections.emptyList(); } @NotNull @Override - public List getAllStorageFiles() { + public List getAllStorageFiles() { return Collections.emptyList(); } } }; + //noinspection deprecation return new StateStorageManager() { @Override - public void addMacro(String macro, String expansion) { + public void addMacro(@NotNull String macro, @NotNull String expansion) { throw new UnsupportedOperationException("Method addMacro not implemented in " + getClass()); } @@ -131,6 +131,12 @@ public class DefaultProjectStoreImpl extends ProjectStoreImpl { return storage; } + @Nullable + @Override + public StateStorage getStateStorage(@NotNull String fileSpec, @NotNull RoamingType roamingType) { + return storage; + } + @Override @Nullable public StateStorage getFileStateStorage(@NotNull String fileSpec) { @@ -158,6 +164,7 @@ public class DefaultProjectStoreImpl extends ProjectStoreImpl { storage.finishSave(((MySaveSession)saveSession).saveSession); } + @NotNull @Override public String expandMacros(@NotNull String file) { throw new UnsupportedOperationException("Method expandMacros not implemented in " + getClass()); @@ -186,6 +193,7 @@ public class DefaultProjectStoreImpl extends ProjectStoreImpl { throw new UnsupportedOperationException("Method getStreamProviders not implemented in " + getClass()); } + @NotNull @Override public Collection getStorageFileNames() { throw new UnsupportedOperationException("Method getStorageFileNames not implemented in " + getClass()); @@ -238,13 +246,13 @@ public class DefaultProjectStoreImpl extends ProjectStoreImpl { @NotNull @Override - public List getAllStorageFilesToSave() throws StateStorageException { + public List getAllStorageFilesToSave() throws StateStorageException { return Collections.emptyList(); } @NotNull @Override - public List getAllStorageFiles() { + public List getAllStorageFiles() { return Collections.emptyList(); } diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/DefaultsStateStorage.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/DefaultsStateStorage.java index b62db63b73c2..9415aee49b09 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/DefaultsStateStorage.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/DefaultsStateStorage.java @@ -65,12 +65,12 @@ class DefaultsStateStorage implements StateStorage { } @Nullable - public T getState(final Object component, final String componentName, final Class stateClass, @Nullable final T mergeInto) throws + public T getState(final Object component, @NotNull final String componentName, final Class stateClass, @Nullable final T mergeInto) throws StateStorageException { return DefaultStateSerializer.deserializeState(getState(component, componentName), stateClass, mergeInto); } - public boolean hasState(final Object component, final String componentName, final Class aClass, final boolean reloadData) throws StateStorageException { + public boolean hasState(final Object component, @NotNull final String componentName, final Class aClass, final boolean reloadData) throws StateStorageException { final URL url = DecodeDefaultsUtil.getDefaults(component, componentName); return url != null; } diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/DirectoryBasedStorage.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/DirectoryBasedStorage.java index cda453be5cd2..912c239cffcb 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/DirectoryBasedStorage.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/DirectoryBasedStorage.java @@ -26,7 +26,8 @@ import com.intellij.openapi.util.WriteExternalException; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.*; import com.intellij.openapi.vfs.tracker.VirtualFileTracker; -import com.intellij.util.io.fs.IFile; +import com.intellij.util.SmartList; +import com.intellij.util.containers.SmartHashSet; import com.intellij.util.messages.MessageBus; import org.jdom.Element; import org.jetbrains.annotations.NotNull; @@ -37,16 +38,13 @@ import java.io.File; import java.io.IOException; import java.util.*; -import static com.intellij.util.io.fs.FileSystem.FILE_SYSTEM; - //todo: support missing plugins //todo: support storage data public class DirectoryBasedStorage implements StateStorage, Disposable { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.components.impl.stores.DirectoryBasedStorage"); - private static final IFile[] EMPTY_FILES = new IFile[0]; private final TrackingPathMacroSubstitutor myPathMacroSubstitutor; - private final IFile myDir; + private final File myDir; private final StateSplitter mySplitter; private final FileTypeManager myFileTypeManager; @@ -59,7 +57,7 @@ public class DirectoryBasedStorage implements StateStorage, Disposable { @NotNull Disposable parentDisposable, @NotNull PicoContainer picoContainer) { myPathMacroSubstitutor = pathMacroSubstitutor; - myDir = FILE_SYSTEM.createFile(dir); + myDir = new File(dir); mySplitter = splitter; Disposer.register(parentDisposable, this); @@ -96,7 +94,7 @@ public class DirectoryBasedStorage implements StateStorage, Disposable { @Override @Nullable - public T getState(final Object component, final String componentName, Class stateClass, @Nullable T mergeInto) + public T getState(final Object component, @NotNull final String componentName, Class stateClass, @Nullable T mergeInto) throws StateStorageException { if (myStorageData == null) myStorageData = loadState(); @@ -116,7 +114,7 @@ public class DirectoryBasedStorage implements StateStorage, Disposable { @Override - public boolean hasState(final Object component, final String componentName, final Class aClass, final boolean reloadData) throws StateStorageException { + public boolean hasState(final Object component, @NotNull String componentName, final Class aClass, final boolean reloadData) throws StateStorageException { if (!myDir.exists()) return false; if (reloadData) myStorageData = null; return true; @@ -180,32 +178,28 @@ public class DirectoryBasedStorage implements StateStorage, Disposable { @Override public void save() throws StateStorageException { assert mySession == this; - final Set currentNames = new HashSet(); - - IFile[] children = myDir.exists() ? myDir.listFiles() : EMPTY_FILES; - for (IFile child : children) { - final String fileName = child.getName(); - if (!myFileTypeManager.isFileIgnored(fileName) && StringUtil.endsWithIgnoreCase(fileName, ".xml")) { - currentNames.add(fileName); + final Set currentNames = new SmartHashSet(); + File[] children = myDir.listFiles(); + if (children != null) { + for (File child : children) { + final String fileName = child.getName(); + if (!myFileTypeManager.isFileIgnored(fileName) && StringUtil.endsWithIgnoreCase(fileName, ".xml")) { + currentNames.add(fileName); + } } } myStorageData.process(new DirectoryStorageData.StorageDataProcessor() { @Override - public void process(final String componentName, final IFile file, final Element element) { + public void process(final String componentName, final File file, final Element element) { currentNames.remove(file.getName()); if (myPathMacroSubstitutor != null) { myPathMacroSubstitutor.collapsePaths(element); } - if (file.getTimeStamp() <= myStorageData.getLastTimeStamp()) { - if (!myDir.exists()) { - myDir.createParentDirs(); - myDir.mkDir(); - } - - StorageUtil.save(file, element, MySaveSession.this); + if (file.lastModified() <= myStorageData.getLastTimeStamp()) { + StorageUtil.save(file, element, MySaveSession.this, false, null); myStorageData.updateLastTimestamp(file); } } @@ -217,15 +211,14 @@ public class DirectoryBasedStorage implements StateStorage, Disposable { public void run() { if (myDir.exists()) { for (String name : currentNames) { - IFile child = myDir.getChild(name); - - if (child.getTimeStamp() > myStorageData.getLastTimeStamp()) { + File child = new File(myDir, name); + if (child.lastModified() > myStorageData.getLastTimeStamp()) { // do not touch new files during VC update (which aren't read yet) // now got an opposite problem: file is recreated if was removed by VC during update. return; } - final VirtualFile virtualFile = StorageUtil.getVirtualFile(child); + final VirtualFile virtualFile = LocalFileSystem.getInstance().findFileByIoFile(child); if (virtualFile != null) { try { LOG.debug("Removing configuration file: " + virtualFile.getPresentableUrl()); @@ -272,23 +265,26 @@ public class DirectoryBasedStorage implements StateStorage, Disposable { @Override @NotNull - public Collection getStorageFilesToSave() throws StateStorageException { + public Collection getStorageFilesToSave() throws StateStorageException { assert mySession == this; if (!myDir.exists()) return getAllStorageFiles(); assert myDir.isDirectory() : myDir.getPath(); - final List filesToSave = new ArrayList(); - - IFile[] children = myDir.listFiles(); - final Set currentChildNames = new HashSet(); - for (IFile child : children) { - if (!myFileTypeManager.isFileIgnored(child.getName())) currentChildNames.add(child.getName()); + final List filesToSave = new ArrayList(); + final Set currentChildNames = new SmartHashSet(); + File[] children = myDir.listFiles(); + if (children != null) { + for (File child : children) { + if (!myFileTypeManager.isFileIgnored(child.getName())) { + currentChildNames.add(child.getName()); + } + } } myStorageData.process(new DirectoryStorageData.StorageDataProcessor() { @Override - public void process(final String componentName, final IFile file, final Element element) { + public void process(final String componentName, final File file, final Element element) { if (currentChildNames.contains(file.getName())) { currentChildNames.remove(file.getName()); @@ -301,13 +297,11 @@ public class DirectoryBasedStorage implements StateStorage, Disposable { filesToSave.add(file); } } - } }); for (String childName : currentChildNames) { - final IFile child = myDir.getChild(childName); - filesToSave.add(child); + filesToSave.add(new File(myDir, childName)); } return filesToSave; @@ -315,8 +309,8 @@ public class DirectoryBasedStorage implements StateStorage, Disposable { @Override @NotNull - public List getAllStorageFiles() { - return new ArrayList(myStorageData.getAllStorageFiles().keySet()); + public List getAllStorageFiles() { + return new SmartList(myStorageData.getAllStorageFiles().keySet()); } } @@ -328,24 +322,25 @@ public class DirectoryBasedStorage implements StateStorage, Disposable { } @Override - public void setState(@NotNull final Object component, final String componentName, @NotNull final Object state, final Storage storageSpec) - throws StateStorageException { + public void setState(@NotNull final Object component, final String componentName, @NotNull final Object state, final Storage storageSpec) { assert mySession == this; setState(componentName, state, storageSpec); } - private void setState(final String componentName, @NotNull Object state, final Storage storageSpec) throws StateStorageException { + private void setState(final String componentName, @NotNull Object state, final Storage storageSpec) { try { final Element element = DefaultStateSerializer.serializeState(state, storageSpec); - for (Pair pair : mySplitter.splitState(element)) { - Element e = pair.first; - String name = pair.second; + if (element != null) { + for (Pair pair : mySplitter.splitState(element)) { + Element e = pair.first; + String name = pair.second; - Element statePart = new Element(StorageData.COMPONENT); - statePart.setAttribute(StorageData.NAME, componentName); - statePart.addContent(e.detach()); + Element statePart = new Element(StorageData.COMPONENT); + statePart.setAttribute(StorageData.NAME, componentName); + statePart.addContent(e.detach()); - myStorageData.put(componentName, myDir.getChild(name), statePart, false); + myStorageData.put(componentName, new File(myDir, name), statePart, false); + } } } catch (WriteExternalException e) { diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/FileBasedStorage.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/FileBasedStorage.java index 923072d4fb1d..54fd9d65cb92 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/FileBasedStorage.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/FileBasedStorage.java @@ -22,19 +22,14 @@ import com.intellij.openapi.Disposable; import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.PathManager; -import com.intellij.openapi.components.ServiceManager; -import com.intellij.openapi.components.StateStorageException; -import com.intellij.openapi.components.StoragePathMacros; -import com.intellij.openapi.components.TrackingPathMacroSubstitutor; +import com.intellij.openapi.components.*; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.util.JDOMUtil; +import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream; +import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.*; import com.intellij.openapi.vfs.tracker.VirtualFileTracker; -import com.intellij.util.io.fs.FileSystem; -import com.intellij.util.io.fs.IFile; import com.intellij.util.messages.MessageBus; -import org.jdom.Document; import org.jdom.Element; import org.jdom.JDOMException; import org.jetbrains.annotations.NonNls; @@ -44,46 +39,40 @@ import org.picocontainer.PicoContainer; import java.io.File; import java.io.IOException; -import java.io.InputStream; import java.util.Collection; import java.util.Collections; import java.util.List; public class FileBasedStorage extends XmlElementStorage { - private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.components.impl.stores.FileBasedStorage"); + private static final Logger LOG = Logger.getInstance(FileBasedStorage.class); private static boolean ourConfigDirectoryRefreshed = false; private final String myFilePath; - private final IFile myFile; - private final String myRootElementName; + private final File myFile; private volatile VirtualFile myCachedVirtualFile; - public FileBasedStorage(@Nullable TrackingPathMacroSubstitutor pathMacroManager, - StreamProvider streamProvider, - String filePath, - String fileSpec, - String rootElementName, + public FileBasedStorage(@NotNull String filePath, + @NotNull String fileSpec, + @Nullable RoamingType roamingType, + @Nullable TrackingPathMacroSubstitutor pathMacroManager, + @NotNull String rootElementName, @NotNull Disposable parentDisposable, PicoContainer picoContainer, - ComponentRoamingManager componentRoamingManager, + @Nullable StreamProvider streamProvider, ComponentVersionProvider componentVersionProvider) { - super(pathMacroManager, parentDisposable, rootElementName, streamProvider, fileSpec, componentRoamingManager, componentVersionProvider); + super(fileSpec, roamingType, pathMacroManager, parentDisposable, rootElementName, streamProvider, componentVersionProvider); refreshConfigDirectoryOnce(); - myRootElementName = rootElementName; myFilePath = filePath; - myFile = FileSystem.FILE_SYSTEM.createFile(myFilePath); + myFile = new File(filePath); VirtualFileTracker virtualFileTracker = ServiceManager.getService(VirtualFileTracker.class); MessageBus messageBus = (MessageBus)picoContainer.getComponentInstanceOfType(MessageBus.class); if (virtualFileTracker != null && messageBus != null) { - final String path = myFile.getAbsolutePath(); - final String fileUrl = LocalFileSystem.PROTOCOL_PREFIX + path.replace(File.separatorChar, '/'); - final Listener listener = messageBus.syncPublisher(STORAGE_TOPIC); - virtualFileTracker.addTracker(fileUrl, new VirtualFileAdapter() { + virtualFileTracker.addTracker(LocalFileSystem.PROTOCOL_PREFIX + myFile.getAbsolutePath().replace(File.separatorChar, '/'), new VirtualFileAdapter() { @Override public void fileMoved(@NotNull VirtualFileMoveEvent event) { myCachedVirtualFile = null; @@ -94,6 +83,11 @@ public class FileBasedStorage extends XmlElementStorage { myCachedVirtualFile = null; } + @Override + public void fileCreated(@NotNull VirtualFileEvent event) { + myCachedVirtualFile = event.getFile(); + } + @Override public void contentsChanged(@NotNull final VirtualFileEvent event) { if (!isDisposed()) { @@ -130,13 +124,6 @@ public class FileBasedStorage extends XmlElementStorage { return new FileSaveSession(externalizationSession); } - public void resetProviderCache() { - myProviderUpToDateHash = -1; - if (myRemoteVersionProvider != null) { - myRemoteVersionProvider.myProviderVersions = null; - } - } - private class FileSaveSession extends MySaveSession { protected FileSaveSession(MyExternalizationSession externalizationSession) { super(externalizationSession); @@ -145,9 +132,11 @@ public class FileBasedStorage extends XmlElementStorage { @Override protected boolean physicalContentNeedsSave() { VirtualFile file = getVirtualFile(); - if (file == null || !file.exists()) + if (file == null || !file.exists()) { return !myStorageData.isEmpty(); - return !StorageUtil.contentEquals(getDocumentToSave(), file); + } + Element element = getElementToSave(); + return element == null || !StorageUtil.contentEquals(element, file); } @Override @@ -169,14 +158,13 @@ public class FileBasedStorage extends XmlElementStorage { } LOG.assertTrue(myFile != null); - myCachedVirtualFile = StorageUtil.save(myFile, getDocumentToSave(), this); + myCachedVirtualFile = StorageUtil.save(myFile, getElementToSave(), this, true, myCachedVirtualFile); } @NotNull @Override - public Collection getStorageFilesToSave() throws StateStorageException { - boolean needsSave = needsSave(); - if (needsSave) { + public Collection getStorageFilesToSave() { + if (needsSave()) { if (LOG.isDebugEnabled()) { LOG.info("File " + myFileSpec + " needs save; hash=" + myUpToDateHash + "; currentHash=" + calcHash() + "; " + "content needs save=" + physicalContentNeedsSave()); @@ -190,21 +178,17 @@ public class FileBasedStorage extends XmlElementStorage { @NotNull @Override - public List getAllStorageFiles() { + public List getAllStorageFiles() { return Collections.singletonList(myFile); } } - @Override - protected void loadState(final StorageData result, final Element element) throws StateStorageException { - ((FileStorageData)result).myFilePath = myFile.getAbsolutePath(); - super.loadState(result, element); - } - @Override @NotNull protected StorageData createStorageData() { - return new FileStorageData(myRootElementName); + FileStorageData data = new FileStorageData(myRootElementName); + data.myFilePath = myFilePath; + return data; } public static class FileStorageData extends StorageData { @@ -234,18 +218,24 @@ public class FileBasedStorage extends XmlElementStorage { public VirtualFile getVirtualFile() { VirtualFile virtualFile = myCachedVirtualFile; if (virtualFile == null) { - myCachedVirtualFile = virtualFile = StorageUtil.getVirtualFile(myFile); + myCachedVirtualFile = virtualFile = LocalFileSystem.getInstance().findFileByIoFile(myFile); } return virtualFile; } + @NotNull public File getFile() { - return new File(myFile.getPath()); + return myFile; + } + + @NotNull + public String getFilePath() { + return myFilePath; } @Override @Nullable - protected Document loadDocument() throws StateStorageException { + protected Element loadLocalData() { myBlockSavingTheContent = false; try { VirtualFile file = getVirtualFile(); @@ -256,7 +246,7 @@ public class FileBasedStorage extends XmlElementStorage { if (file.getLength() == 0) { return processReadException(null); } - return loadDocumentImpl(file); + return StorageData.load(file); } catch (final JDOMException e) { return processReadException(e); @@ -267,7 +257,7 @@ public class FileBasedStorage extends XmlElementStorage { } @Nullable - private Document processReadException(@Nullable final Exception e) { + private Element processReadException(@Nullable final Exception e) { boolean contentTruncated = e == null; myBlockSavingTheContent = isProjectOrModuleOrWorkspaceFile() && !contentTruncated; if (!ApplicationManager.getApplication().isUnitTestMode() && !ApplicationManager.getApplication().isHeadlessEnvironment()) { @@ -291,45 +281,27 @@ public class FileBasedStorage extends XmlElementStorage { return isProjectOrModuleOrWorkspaceFile() && !contentTruncated ? "Please correct the file content" : "File content will be recreated"; } - private static Document loadDocumentImpl(final VirtualFile file) throws IOException, JDOMException { - InputStream stream = file.getInputStream(); - try { - return JDOMUtil.loadDocument(stream); - } - finally { - stream.close(); - } - } - - public String getFileName() { - return myFile.getName(); - } - - public String getFilePath() { - return myFilePath; - } - @Override public void setDefaultState(final Element element) { element.setName(myRootElementName); super.setDefaultState(element); } - protected boolean physicalContentNeedsSave(final Document doc) { - VirtualFile file = getVirtualFile(); - return file == null || !file.exists() || !StorageUtil.contentEquals(doc, file); - } - @Nullable public File updateFileExternallyFromStreamProviders() throws IOException { - StorageData loadedData = loadData(true); - Document document = getDocument(loadedData); - if (physicalContentNeedsSave(document)) { - File file = new File(myFile.getAbsolutePath()); - JDOMUtil.writeDocument(document, file, "\n"); - return file; + Element element = getElement(loadData(true)); + if (element == null) { + FileUtil.delete(myFile); + return null; } - return null; + BufferExposingByteArrayOutputStream out = StorageUtil.newContentIfDiffers(element, getVirtualFile()); + if (out == null) { + return null; + } + + File file = new File(myFile.getAbsolutePath()); + FileUtil.writeToFile(file, out.getInternalBuffer(), 0, out.size()); + return file; } } diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/IApplicationStore.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/IApplicationStore.java index c669bd1f37be..0ebfae887771 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/IApplicationStore.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/IApplicationStore.java @@ -15,18 +15,18 @@ */ package com.intellij.openapi.components.impl.stores; -import com.intellij.openapi.components.StateStorageException; -import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.components.StateStorage; +import com.intellij.openapi.components.StateStorageException; import com.intellij.openapi.util.Pair; +import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.NotNull; -import java.util.Set; -import java.util.Collection; import java.io.IOException; +import java.util.Collection; +import java.util.Set; public interface IApplicationStore extends IComponentStore { - void setOptionsPath(String path); + void setOptionsPath(@NotNull String path); void setConfigPath(@NotNull String configPath); diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/IComponentStore.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/IComponentStore.java index 0427a8be15ab..93a65a2eb50b 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/IComponentStore.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/IComponentStore.java @@ -19,10 +19,10 @@ import com.intellij.openapi.components.StateStorage; import com.intellij.openapi.components.StateStorageException; import com.intellij.openapi.util.Pair; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.util.io.fs.IFile; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.io.File; import java.io.IOException; import java.util.List; import java.util.Set; @@ -54,17 +54,19 @@ public interface IComponentStore { interface SaveSession { @NotNull - List getAllStorageFilesToSave(final boolean includingSubStructures) throws IOException; + List getAllStorageFilesToSave(final boolean includingSubStructures) throws IOException; + @NotNull SaveSession save() throws IOException; + void finishSave(); + void reset(); @Nullable - Set analyzeExternalChanges(@NotNull Set> changedFiles); + Set analyzeExternalChanges(@NotNull Set> changedFiles); @NotNull - List getAllStorageFiles(final boolean includingSubStructures); + List getAllStorageFiles(final boolean includingSubStructures); } - } diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/OldStreamProviderAdapter.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/OldStreamProviderAdapter.java index 2120030db2c1..ac7bf84f6b3e 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/OldStreamProviderAdapter.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/OldStreamProviderAdapter.java @@ -12,6 +12,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; +@SuppressWarnings("deprecation") final class OldStreamProviderAdapter extends StreamProvider implements CurrentUserHolder { final com.intellij.openapi.options.StreamProvider myProvider; private final RoamingType myRoamingType; @@ -32,9 +33,8 @@ final class OldStreamProviderAdapter extends StreamProvider implements CurrentUs } @Override - public boolean saveContent(@NotNull String fileSpec, @NotNull byte[] content, int size, @NotNull RoamingType roamingType, boolean async) throws IOException { + public void saveContent(@NotNull String fileSpec, @NotNull byte[] content, int size, @NotNull RoamingType roamingType, boolean async) throws IOException { myProvider.saveContent(fileSpec, new BufferExposingByteArrayInputStream(content, size), size, roamingType, async); - return false; } @Nullable @@ -65,4 +65,4 @@ final class OldStreamProviderAdapter extends StreamProvider implements CurrentUs public String getCurrentUserName() { return myProvider instanceof CurrentUserHolder ? ((CurrentUserHolder)myProvider).getCurrentUserName() : null; } -} \ No newline at end of file +} diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ProjectStateStorageManager.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ProjectStateStorageManager.java index 0eed50a65131..c7b93afbe8d8 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ProjectStateStorageManager.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ProjectStateStorageManager.java @@ -56,17 +56,12 @@ class ProjectStateStorageManager extends StateStorageManagerImpl { assert config != null : "Couldn't find old storage for " + component.getClass().getName(); final boolean workspace = isWorkspace(config.options); - String macro = StoragePathMacros.getMacroName(workspace ? StoragePathMacros.WORKSPACE_FILE : StoragePathMacros.PROJECT_FILE); - - String name = "$" + macro + "$"; - - StateStorage storage = getFileStateStorage(name); - + String fileSpec = workspace ? StoragePathMacros.WORKSPACE_FILE : StoragePathMacros.PROJECT_FILE; + StateStorage storage = getStateStorage(fileSpec, workspace ? RoamingType.DISABLED : RoamingType.PER_USER); if (operation == StateStorageOperation.READ && storage != null && workspace && !storage.hasState(component, componentName, Element.class, false)) { - name = StoragePathMacros.PROJECT_FILE; + fileSpec = StoragePathMacros.PROJECT_FILE; } - - return name; + return fileSpec; } @Override diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ProjectStoreImpl.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ProjectStoreImpl.java index 6288c62ab9e5..01e2f9e036d0 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ProjectStoreImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/ProjectStoreImpl.java @@ -39,13 +39,14 @@ import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.io.FileUtilRt; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.*; +import com.intellij.util.PathUtilRt; +import com.intellij.util.SmartList; import com.intellij.util.containers.OrderedSet; -import com.intellij.util.io.fs.FileSystem; -import com.intellij.util.io.fs.IFile; import org.jdom.Element; import org.jdom.JDOMException; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.io.*; import java.lang.annotation.Annotation; @@ -55,23 +56,21 @@ import java.util.List; import java.util.Set; class ProjectStoreImpl extends BaseFileConfigurableStoreImpl implements IProjectStore { - private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.components.impl.stores.ProjectStoreImpl"); + private static final Logger LOG = Logger.getInstance(ProjectStoreImpl.class); @NonNls private static final String OLD_PROJECT_SUFFIX = "_old."; @NonNls static final String OPTION_WORKSPACE = "workspace"; - @NonNls static final String DEFAULT_STATE_STORAGE = StoragePathMacros.PROJECT_FILE; - static final Storage DEFAULT_STORAGE_ANNOTATION = new MyStorage(); private static int originalVersion = -1; protected ProjectImpl myProject; private StorageScheme myScheme = StorageScheme.DEFAULT; - private String myCachedLocation; private String myPresentableUrl; - ProjectStoreImpl(final ProjectImpl project) { + ProjectStoreImpl(@NotNull ProjectImpl project) { super(project); + myProject = project; } @@ -169,15 +168,15 @@ class ProjectStoreImpl extends BaseFileConfigurableStoreImpl implements IProject final File dirStore = file.isDirectory() ? new File(file, Project.DIRECTORY_STORE_FOLDER) : new File(file.getParentFile(), Project.DIRECTORY_STORE_FOLDER); - stateStorageManager.addMacro(StoragePathMacros.getMacroName(StoragePathMacros.PROJECT_FILE), new File(dirStore, "misc.xml").getPath()); + stateStorageManager.addMacro(StoragePathMacros.PROJECT_FILE, new File(dirStore, "misc.xml").getPath()); final File ws = new File(dirStore, "workspace.xml"); - stateStorageManager.addMacro(StoragePathMacros.getMacroName(StoragePathMacros.WORKSPACE_FILE), ws.getPath()); + stateStorageManager.addMacro(StoragePathMacros.WORKSPACE_FILE, ws.getPath()); if (!ws.exists() && !file.isDirectory()) { useOldWsContent(filePath, ws); } - stateStorageManager.addMacro(StoragePathMacros.getMacroName(StoragePathMacros.PROJECT_CONFIG_DIR), dirStore.getPath()); + stateStorageManager.addMacro(StoragePathMacros.PROJECT_CONFIG_DIR, dirStore.getPath()); ApplicationManager.getApplication().invokeAndWait(new Runnable() { @Override @@ -189,10 +188,10 @@ class ProjectStoreImpl extends BaseFileConfigurableStoreImpl implements IProject else { myScheme = StorageScheme.DEFAULT; - stateStorageManager.addMacro(StoragePathMacros.getMacroName(StoragePathMacros.PROJECT_FILE), filePath); + stateStorageManager.addMacro(StoragePathMacros.PROJECT_FILE, filePath); final String workspacePath = composeWsPath(filePath); - stateStorageManager.addMacro(StoragePathMacros.getMacroName(StoragePathMacros.WORKSPACE_FILE), workspacePath); + stateStorageManager.addMacro(StoragePathMacros.WORKSPACE_FILE, workspacePath); ApplicationManager.getApplication().invokeAndWait(new Runnable() { @Override @@ -202,7 +201,6 @@ class ProjectStoreImpl extends BaseFileConfigurableStoreImpl implements IProject }, ModalityState.defaultModalityState()); } - myCachedLocation = null; myPresentableUrl = null; } @@ -258,18 +256,26 @@ class ProjectStoreImpl extends BaseFileConfigurableStoreImpl implements IProject final String path = getProjectFilePath(); if (!StringUtil.isEmptyOrSpaces(path)) { - return myScheme == StorageScheme.DEFAULT ? new File(path).getParent() : new File(path).getParentFile().getParent(); + return getBasePath(new File(path)); } //we are not yet initialized completely ("open directory", etc) - final StateStorage s = getStateStorageManager().getFileStateStorage(StoragePathMacros.PROJECT_FILE); - if (!(s instanceof FileBasedStorage)) return null; - final FileBasedStorage storage = (FileBasedStorage)s; + StateStorage storage = getStateStorageManager().getStateStorage(StoragePathMacros.PROJECT_FILE, RoamingType.PER_USER); + if (!(storage instanceof FileBasedStorage)) { + return null; + } - final File file = storage.getFile(); - if (file == null) return null; + return getBasePath(((FileBasedStorage)storage).getFile()); + } - return myScheme == StorageScheme.DEFAULT ? file.getParent() : file.getParentFile().getParent(); + private String getBasePath(@NotNull File file) { + if (myScheme == StorageScheme.DEFAULT) { + return file.getParent(); + } + else { + File parentFile = file.getParentFile(); + return parentFile == null ? null : parentFile.getParent(); + } } @NotNull @@ -302,7 +308,7 @@ class ProjectStoreImpl extends BaseFileConfigurableStoreImpl implements IProject return baseDir.getName().replace(":", ""); } else { - String temp = getProjectFileName(); + String temp = PathUtilRt.getFileName(((FileBasedStorage)getProjectFileStorage()).getFilePath()); FileType fileType = FileTypeManager.getInstance().getFileTypeByFileName(temp); if (fileType instanceof ProjectFileType) { temp = temp.substring(0, temp.length() - fileType.getDefaultExtension().length() - 1); @@ -315,13 +321,6 @@ class ProjectStoreImpl extends BaseFileConfigurableStoreImpl implements IProject } } - @NotNull - private String getProjectFileName() { - final FileBasedStorage storage = (FileBasedStorage)getStateStorageManager().getFileStateStorage(StoragePathMacros.PROJECT_FILE); - assert storage != null; - return storage.getFileName(); - } - @NotNull @Override public StorageScheme getStorageScheme() { @@ -349,26 +348,28 @@ class ProjectStoreImpl extends BaseFileConfigurableStoreImpl implements IProject @Override public VirtualFile getProjectFile() { - if (myProject.isDefault()) return null; - final FileBasedStorage storage = (FileBasedStorage)getStateStorageManager().getFileStateStorage(StoragePathMacros.PROJECT_FILE); + return myProject.isDefault() ? null : ((FileBasedStorage)getProjectFileStorage()).getVirtualFile(); + } + + @NotNull + private XmlElementStorage getProjectFileStorage() { + // XmlElementStorage if default project, otherwise FileBasedStorage + XmlElementStorage storage = (XmlElementStorage)getStateStorageManager().getStateStorage(StoragePathMacros.PROJECT_FILE, RoamingType.PER_USER); assert storage != null; - return storage.getVirtualFile(); + return storage; } @Override public VirtualFile getWorkspaceFile() { if (myProject.isDefault()) return null; - final FileBasedStorage storage = (FileBasedStorage)getStateStorageManager().getFileStateStorage(StoragePathMacros.WORKSPACE_FILE); + final FileBasedStorage storage = (FileBasedStorage)getStateStorageManager().getStateStorage(StoragePathMacros.WORKSPACE_FILE, RoamingType.DISABLED); assert storage != null; return storage.getVirtualFile(); } @Override public void loadProjectFromTemplate(@NotNull final ProjectImpl defaultProject) { - final StateStorage stateStorage = getStateStorageManager().getFileStateStorage(DEFAULT_STATE_STORAGE); - - assert stateStorage instanceof XmlElementStorage; - XmlElementStorage xmlElementStorage = (XmlElementStorage)stateStorage; + XmlElementStorage stateStorage = getProjectFileStorage(); defaultProject.save(); final IProjectStore projectStore = defaultProject.getStateStore(); @@ -376,24 +377,19 @@ class ProjectStoreImpl extends BaseFileConfigurableStoreImpl implements IProject DefaultProjectStoreImpl defaultProjectStore = (DefaultProjectStoreImpl)projectStore; final Element element = defaultProjectStore.getStateCopy(); if (element != null) { - xmlElementStorage.setDefaultState(element); + stateStorage.setDefaultState(element); } } @NotNull @Override public String getProjectFilePath() { - if (myProject.isDefault()) return ""; - final FileBasedStorage storage = (FileBasedStorage)getStateStorageManager().getFileStateStorage(StoragePathMacros.PROJECT_FILE); - assert storage != null; - return storage.getFilePath(); + return myProject.isDefault() ? "" : ((FileBasedStorage)getProjectFileStorage()).getFilePath(); } @Override protected XmlElementStorage getMainStorage() { - final XmlElementStorage storage = (XmlElementStorage)getStateStorageManager().getFileStateStorage(DEFAULT_STATE_STORAGE); - assert storage != null; - return storage; + return getProjectFileStorage(); } @NotNull @@ -402,7 +398,6 @@ class ProjectStoreImpl extends BaseFileConfigurableStoreImpl implements IProject return new ProjectStateStorageManager(PathMacroManager.getInstance(getComponentManager()).createTrackingSubstitutor(), myProject); } - static class ProjectStorageData extends BaseStorageData { protected final Project myProject; @@ -447,16 +442,16 @@ class ProjectStoreImpl extends BaseFileConfigurableStoreImpl implements IProject } @Override - public void load(@NotNull final Element root) throws IOException { - final String v = root.getAttributeValue(VERSION_OPTION); + public void load(@NotNull Element rootElement, @Nullable PathMacroSubstitutor pathMacroSubstitutor, boolean intern) { + final String v = rootElement.getAttributeValue(VERSION_OPTION); //noinspection AssignmentToStaticFieldFromInstanceMethod originalVersion = v != null ? Integer.parseInt(v) : 0; if (originalVersion != ProjectManagerImpl.CURRENT_FORMAT_VERSION) { - convert(root, originalVersion); + convert(rootElement, originalVersion); } - super.load(root); + super.load(rootElement, pathMacroSubstitutor, intern); } protected void convert(final Element root, final int originalVersion) { @@ -479,19 +474,16 @@ class ProjectStoreImpl extends BaseFileConfigurableStoreImpl implements IProject @NotNull @Override - public List getAllStorageFilesToSave(final boolean includingSubStructures) throws IOException { - List result = new ArrayList(); - + public List getAllStorageFilesToSave(final boolean includingSubStructures) throws IOException { + List result = new SmartList(); if (includingSubStructures) { - collectSubfilesToSave(result); + collectSubFilesToSave(result); } - result.addAll(super.getAllStorageFilesToSave(false)); - return result; } - protected void collectSubfilesToSave(final List result) throws IOException { } + protected void collectSubFilesToSave(final List result) throws IOException { } @NotNull @Override @@ -523,10 +515,10 @@ class ProjectStoreImpl extends BaseFileConfigurableStoreImpl implements IProject return ApplicationManager.getApplication().runReadAction(new Computable() { @Override public ReadonlyStatusHandler.OperationStatus compute() { - final List filesToSave; + final List filesToSave; try { filesToSave = getAllStorageFilesToSave(true); - final Iterator iterator = filesToSave.iterator(); + final Iterator iterator = filesToSave.iterator(); while (iterator.hasNext()) { if (!iterator.next().exists()) { iterator.remove(); @@ -543,14 +535,12 @@ class ProjectStoreImpl extends BaseFileConfigurableStoreImpl implements IProject if (myProject.isToSaveProjectName()) { final VirtualFile baseDir = getProjectBaseDir(); if (baseDir != null && baseDir.isValid()) { - filesToSave.add(FileSystem.FILE_SYSTEM - .createFile(new File(new File(baseDir.getPath(), Project.DIRECTORY_STORE_FOLDER), ProjectImpl.NAME_FILE).getPath())); + filesToSave.add(new File(new File(baseDir.getPath(), Project.DIRECTORY_STORE_FOLDER), ProjectImpl.NAME_FILE)); } } - for (IFile file : filesToSave) { + for (File file : filesToSave) { final VirtualFile virtualFile = LocalFileSystem.getInstance().findFileByIoFile(file); - if (virtualFile != null) { virtualFile.refresh(false, false); if (virtualFile.isValid() && !virtualFile.isWritable()) { @@ -670,7 +660,7 @@ class ProjectStoreImpl extends BaseFileConfigurableStoreImpl implements IProject @Override public String file() { - return DEFAULT_STATE_STORAGE; + return StoragePathMacros.PROJECT_FILE; } @Override @@ -678,16 +668,22 @@ class ProjectStoreImpl extends BaseFileConfigurableStoreImpl implements IProject return StorageScheme.DEFAULT; } + @Override + public RoamingType roamingType() { + return RoamingType.PER_USER; + } + @Override public Class storageClass() { - return StorageAnnotationsDefaultValues.NullStateStorage.class; + return StateStorage.class; } @Override public Class stateSplitter() { - return StorageAnnotationsDefaultValues.NullStateSplitter.class; + return StateSplitter.class; } + @NotNull @Override public Class annotationType() { throw new UnsupportedOperationException("Method annotationType not implemented in " + getClass()); diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/RoamingTypeExtensionPointBean.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/RoamingTypeExtensionPointBean.java deleted file mode 100644 index ff5d16361228..000000000000 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/RoamingTypeExtensionPointBean.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2000-2009 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.components.impl.stores; - -import com.intellij.util.xmlb.annotations.Attribute; - -/** - * @deprecated use {@link com.intellij.openapi.components.RoamingType#DISABLED} - */ -@Deprecated -public class RoamingTypeExtensionPointBean { - @Attribute("component") - public String componentName; - @Attribute("type") - public String roamingType; -} diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StateStorageManager.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StateStorageManager.java index 68d882d2b204..d3e9d1c03d2e 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StateStorageManager.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StateStorageManager.java @@ -19,10 +19,10 @@ import com.intellij.openapi.components.*; import com.intellij.openapi.options.StreamProvider; import com.intellij.openapi.util.Pair; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.util.io.fs.IFile; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.io.File; import java.util.Collection; import java.util.List; import java.util.Set; @@ -31,7 +31,7 @@ import java.util.Set; * @author mike */ public interface StateStorageManager { - void addMacro(String macro, String expansion); + void addMacro(@NotNull String macro, @NotNull String expansion); @Nullable TrackingPathMacroSubstitutor getMacroSubstitutor(); @@ -40,8 +40,16 @@ public interface StateStorageManager { StateStorage getStateStorage(@NotNull Storage storageSpec) throws StateStorageException; @Nullable + StateStorage getStateStorage(@NotNull String fileSpec, @NotNull RoamingType roamingType); + + @Deprecated + @Nullable + /** + * @deprecated Use {@link #getStateStorage(String, com.intellij.openapi.components.RoamingType)} + */ StateStorage getFileStateStorage(@NotNull String fileSpec); + @NotNull Collection getStorageFileNames(); void clearStateStorage(@NotNull String file); @@ -57,7 +65,7 @@ public interface StateStorageManager { @Nullable StateStorage getOldStorage(Object component, String componentName, StateStorageOperation operation) throws StateStorageException; - @Nullable + @NotNull String expandMacros(@NotNull String file); @Deprecated @@ -81,11 +89,11 @@ public interface StateStorageManager { Set analyzeExternalChanges(@NotNull Set> files); @NotNull - List getAllStorageFilesToSave() throws StateStorageException; + List getAllStorageFilesToSave() throws StateStorageException; @NotNull - List getAllStorageFiles(); + List getAllStorageFiles(); void save() throws StateStorageException; } -} \ No newline at end of file +} diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StateStorageManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StateStorageManagerImpl.java index 431c9a97820c..651acce271c0 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StateStorageManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StateStorageManagerImpl.java @@ -24,12 +24,14 @@ import com.intellij.openapi.options.CurrentUserHolder; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.JDOMUtil; import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.RoamingTypeDisabled; import com.intellij.openapi.util.io.FileUtilRt; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.ObjectUtils; +import com.intellij.util.PathUtilRt; +import com.intellij.util.ReflectionUtil; import com.intellij.util.SmartList; -import com.intellij.util.io.fs.IFile; import gnu.trove.THashMap; import gnu.trove.THashSet; import gnu.trove.TObjectLongHashMap; @@ -99,9 +101,14 @@ public abstract class StateStorageManagerImpl implements StateStorageManager, Di } @Override - public synchronized void addMacro(String macro, String expansion) { - // avoid hundreds of $MODULE_FILE$ instances - myMacros.put(("$" + macro + "$").intern(), expansion); + public synchronized void addMacro(@NotNull String macro, @NotNull String expansion) { + assert !macro.isEmpty(); + // backward compatibility + if (macro.charAt(0) != '$') { + LOG.warn("Add macros instead of macro name: " + macro); + expansion = '$' + macro + '$'; + } + myMacros.put(macro, expansion); } @Override @@ -123,15 +130,15 @@ public abstract class StateStorageManagerImpl implements StateStorageManager, Di } } - @Override @Nullable - public StateStorage getFileStateStorage(@NotNull String fileName) { + @Override + public StateStorage getStateStorage(@NotNull String fileSpec, @NotNull RoamingType roamingType) { myStorageLock.lock(); try { - StateStorage stateStorage = myStorages.get(fileName); + StateStorage stateStorage = myStorages.get(fileSpec); if (stateStorage == null) { - stateStorage = createFileStateStorage(fileName); - putStorageToMap(fileName, stateStorage); + stateStorage = createFileStateStorage(fileSpec, roamingType); + putStorageToMap(fileSpec, stateStorage); } return stateStorage; } @@ -140,6 +147,13 @@ public abstract class StateStorageManagerImpl implements StateStorageManager, Di } } + @Override + @Nullable + public StateStorage getFileStateStorage(@NotNull String fileSpec) { + return getStateStorage(fileSpec, RoamingType.PER_USER); + } + + @NotNull @Override public Collection getStorageFileNames() { myStorageLock.lock(); @@ -176,19 +190,19 @@ public abstract class StateStorageManagerImpl implements StateStorageManager, Di @Nullable private StateStorage createStateStorage(Storage storageSpec) throws StateStorageException { - if (!storageSpec.storageClass().equals(StorageAnnotationsDefaultValues.NullStateStorage.class)) { + if (!storageSpec.storageClass().equals(StateStorage.class)) { String key = UUID.randomUUID().toString(); ((MutablePicoContainer)myPicoContainer).registerComponentImplementation(key, storageSpec.storageClass()); return (StateStorage)myPicoContainer.getComponentInstance(key); } - if (!storageSpec.stateSplitter().equals(StorageAnnotationsDefaultValues.NullStateSplitter.class)) { + if (!storageSpec.stateSplitter().equals(StateSplitter.class)) { return createDirectoryStateStorage(storageSpec.file(), storageSpec.stateSplitter()); } - return createFileStateStorage(storageSpec.file()); + return createFileStateStorage(storageSpec.file(), storageSpec.roamingType()); } private static String getStorageSpecId(Storage storageSpec) { - if (!storageSpec.storageClass().equals(StorageAnnotationsDefaultValues.NullStateStorage.class)) { + if (!storageSpec.storageClass().equals(StateStorage.class)) { return storageSpec.storageClass().getName(); } else { @@ -209,41 +223,30 @@ public abstract class StateStorageManagerImpl implements StateStorageManager, Di @Nullable private StateStorage createDirectoryStateStorage(String file, Class splitterClass) throws StateStorageException { - String expandedFile = expandMacros(file); - if (expandedFile == null) { - myStorages.put(file, null); - return null; - } - final StateSplitter splitter; try { - splitter = splitterClass.newInstance(); + splitter = ReflectionUtil.newInstance(splitterClass); } - catch (InstantiationException e) { + catch (RuntimeException e) { throw new StateStorageException(e); } - catch (IllegalAccessException e) { - throw new StateStorageException(e); - } - - return new DirectoryBasedStorage(myPathMacroSubstitutor, expandedFile, splitter, this, myPicoContainer); + return new DirectoryBasedStorage(myPathMacroSubstitutor, expandMacros(file), splitter, this, myPicoContainer); } @Nullable - private StateStorage createFileStateStorage(@NotNull final String fileSpec) { + private StateStorage createFileStateStorage(@NotNull final String fileSpec, @Nullable RoamingType roamingType) { String expandedFile = expandMacros(fileSpec); - if (expandedFile == null) { - myStorages.put(fileSpec, null); - return null; - } - String extension = FileUtilRt.getExtension(new File(expandedFile).getName()); - if (!ourHeadlessEnvironment && extension.isEmpty()) { + if (!ourHeadlessEnvironment && PathUtilRt.getFileName(expandedFile).lastIndexOf('.') < 0) { throw new IllegalArgumentException("Extension is missing for storage file: " + expandedFile); } - return new FileBasedStorage(getMacroSubstitutor(fileSpec), getStreamProvider(), expandedFile, fileSpec, myRootTagName, this, - myPicoContainer, ComponentRoamingManager.getInstance(), this) { + if (roamingType != RoamingType.PER_USER && fileSpec.equals(StoragePathMacros.WORKSPACE_FILE)) { + roamingType = RoamingType.DISABLED; + } + + return new FileBasedStorage(expandedFile, fileSpec, roamingType, getMacroSubstitutor(fileSpec), myRootTagName, this, + myPicoContainer, getStreamProvider(), this) { @Override @NotNull protected StorageData createStorageData() { @@ -271,8 +274,7 @@ public abstract class StateStorageManagerImpl implements StateStorageManager, Di String filePath = getNotNullVersionsFilePath(); if (filePath != null) { try { - Document document = JDOMUtil.loadDocument(new File(filePath)); - loadComponentVersions(result, document); + loadComponentVersions(result, JDOMUtil.loadDocument(new File(filePath))); } catch (JDOMException e) { LOG.debug(e); @@ -294,6 +296,7 @@ public abstract class StateStorageManagerImpl implements StateStorageManager, Di public static void loadComponentVersions(TObjectLongHashMap result, Document document) { List componentObjects = document.getRootElement().getChildren("component"); + result.ensureCapacity(componentObjects.size()); for (Element component : componentObjects) { String name = component.getAttributeValue("name"); String version = component.getAttributeValue("version"); @@ -330,26 +333,21 @@ public abstract class StateStorageManagerImpl implements StateStorageManager, Di private static final Pattern MACRO_PATTERN = Pattern.compile("(\\$[^\\$]*\\$)"); @Override - @Nullable + @NotNull public synchronized String expandMacros(@NotNull String file) { - final Matcher matcher = MACRO_PATTERN.matcher(file); + Matcher matcher = MACRO_PATTERN.matcher(file); while (matcher.find()) { String m = matcher.group(1); - if (!myMacros.containsKey(m) || !ApplicationManager.getApplication().isUnitTestMode() && myMacros.get(m) == null) { - throw new IllegalArgumentException("Unknown macro: " + m + " in storage spec: " + file); + if (!myMacros.containsKey(m)) { + throw new IllegalArgumentException("Unknown macro: " + m + " in storage file spec: " + file); } } - String actualFile = file; - + String expanded = file; for (String macro : myMacros.keySet()) { - final String replacement = myMacros.get(macro); - if (replacement != null) { - actualFile = StringUtil.replace(actualFile, macro, replacement); - } + expanded = StringUtil.replace(expanded, macro, myMacros.get(macro)); } - - return actualFile; + return expanded; } @NotNull @@ -424,7 +422,9 @@ public abstract class StateStorageManagerImpl implements StateStorageManager, Di @Override @Nullable public StateStorage getOldStorage(Object component, String componentName, StateStorageOperation operation) throws StateStorageException { - return getFileStateStorage(getOldStorageSpec(component, componentName, operation)); + String oldStorageSpec = getOldStorageSpec(component, componentName, operation); + //noinspection deprecation + return oldStorageSpec == null ? null : getStateStorage(oldStorageSpec, component instanceof RoamingTypeDisabled ? RoamingType.DISABLED : RoamingType.PER_USER); } @Nullable @@ -440,14 +440,14 @@ public abstract class StateStorageManagerImpl implements StateStorageManager, Di @Override @NotNull - public List getAllStorageFilesToSave() throws StateStorageException { + public List getAllStorageFilesToSave() throws StateStorageException { assert mySession == this; return myCompoundSaveSession.getAllStorageFilesToSave(); } @Override @NotNull - public List getAllStorageFiles() { + public List getAllStorageFiles() { return myCompoundSaveSession.getAllStorageFiles(); } @@ -503,15 +503,16 @@ public abstract class StateStorageManagerImpl implements StateStorageManager, Di } public void save() { - if (!isDirty) return; + if (!isDirty) { + return; + } + String filePath = getNotNullVersionsFilePath(); if (filePath != null) { - File dir = new File(filePath).getParentFile(); - if (!dir.isDirectory() && !dir.mkdirs()) { - LOG.warn("Unable to create: " + dir); - } + File file = new File(filePath); + FileUtilRt.createParentDirs(file); try { - JDOMUtil.writeDocument(new Document(createComponentVersionsXml(getComponentVersions())), filePath, "\n"); + JDOMUtil.writeParent(createComponentVersionsXml(getComponentVersions()), file, "\n"); isDirty = false; } catch (IOException e) { @@ -546,6 +547,7 @@ public abstract class StateStorageManagerImpl implements StateStorageManager, Di return root; } + @SuppressWarnings("deprecation") private static class OldStreamProviderManager extends StreamProvider implements CurrentUserHolder { private final List myStreamProviders = new SmartList(); @@ -575,22 +577,17 @@ public abstract class StateStorageManagerImpl implements StateStorageManager, Di } @Override - public boolean saveContent(@NotNull String fileSpec, @NotNull byte[] content, int size, @NotNull RoamingType roamingType, boolean async) throws IOException { - boolean result = false; + public void saveContent(@NotNull String fileSpec, @NotNull byte[] content, int size, @NotNull RoamingType roamingType, boolean async) throws IOException { for (StreamProvider streamProvider : myStreamProviders) { try { - if (streamProvider.isEnabled() && streamProvider.isApplicable(fileSpec, roamingType) && streamProvider.saveContent(fileSpec, content, size, roamingType, async)) { - result = true; + if (streamProvider.isEnabled() && streamProvider.isApplicable(fileSpec, roamingType)) { + streamProvider.saveContent(fileSpec, content, size, roamingType, async); } } - catch (ConnectException e) { - LOG.debug("Cannot send user profile to server: " + e.getLocalizedMessage()); - } catch (Exception e) { LOG.debug(e); } } - return result; } @Override diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StorageUtil.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StorageUtil.java index bd732705446a..2344c8526b0a 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StorageUtil.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StorageUtil.java @@ -26,7 +26,6 @@ import com.intellij.openapi.editor.DocumentRunnable; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectBundle; import com.intellij.openapi.project.ex.ProjectEx; -import com.intellij.openapi.util.Couple; import com.intellij.openapi.util.JDOMUtil; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream; @@ -38,7 +37,6 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.SystemProperties; import com.intellij.util.UniqueFileNamesProvider; import com.intellij.util.containers.ContainerUtil; -import com.intellij.util.io.fs.IFile; import com.intellij.util.ui.UIUtil; import org.jdom.Document; import org.jdom.Element; @@ -59,11 +57,12 @@ import java.util.Set; * @author mike */ public class StorageUtil { - private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.components.impl.stores.StorageUtil"); + private static final Logger LOG = Logger.getInstance(StorageUtil.class); private static final boolean DUMP_COMPONENT_STATES = SystemProperties.getBooleanProperty("idea.log.externally.changed.component.states", false); @SuppressWarnings("SpellCheckingInspection") private static final SimpleDateFormat LOG_DIR_FORMAT = new SimpleDateFormat("yyyyMMdd-HHmmss"); + private static final Pair NON_EXISTENT_FILE_DATA = Pair.create(null, SystemProperties.getLineSeparator()); private StorageUtil() { } @@ -110,34 +109,86 @@ public class StorageUtil { return notified; } + + public static boolean isEmpty(@Nullable Parent element) { + if (element == null) { + return true; + } + else if (element instanceof Element) { + return JDOMUtil.isEmpty((Element)element); + } + else { + Document document = (Document)element; + return !document.hasRootElement() || JDOMUtil.isEmpty(document.getRootElement()); + } + } + + /** + * Due to historical reasons files in ROOT_CONFIG don’t wrapped into document (xml prolog) opposite to files in APP_CONFIG + */ @Nullable - static VirtualFile save(@NotNull IFile file, Parent element, Object requestor) throws StateStorageException { - try { - String lineSeparator; - String oldText; - if (file.exists()) { - VirtualFile vFile = LocalFileSystem.getInstance().findFileByIoFile(file); - Couple pair = loadFile(vFile); - lineSeparator = pair.second; - oldText = pair.first; + static VirtualFile save(@NotNull File file, @Nullable Parent element, Object requestor, boolean wrapAsDocument, @Nullable VirtualFile cachedVirtualFile) throws StateStorageException { + if (isEmpty(element)) { + if (!file.exists()) { + return null; + } + + VirtualFile virtualFile = cachedVirtualFile; + if (virtualFile == null || !virtualFile.isValid()) { + virtualFile = LocalFileSystem.getInstance().findFileByIoFile(file); + } + if (virtualFile == null) { + LOG.info("Cannot find virtual file " + file.getAbsolutePath()); + FileUtil.delete(file); } else { - oldText = null; - lineSeparator = SystemProperties.getLineSeparator(); - file.createParentDirs(); + AccessToken token = ApplicationManager.getApplication().acquireWriteActionLock(DocumentRunnable.IgnoreDocumentRunnable.class); + try { + virtualFile.delete(requestor); + } + catch (IOException e) { + throw new StateStorageException(e); + } + finally { + token.finish(); + } } + return null; + } - String text = JDOMUtil.writeParent(element, lineSeparator); - if (text.equals(oldText)) { - return null; + VirtualFile virtualFile = cachedVirtualFile == null || !cachedVirtualFile.isValid() ? null : cachedVirtualFile; + Parent document = !wrapAsDocument || element instanceof Document ? element : new Document((Element)element); + try { + BufferExposingByteArrayOutputStream byteOut; + if (file.exists()) { + if (virtualFile == null) { + virtualFile = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(file); + } + + Pair pair = loadFile(virtualFile); + byteOut = writeToBytes(document, pair.second); + if (equal(pair.first, byteOut)) { + return null; + } + } + else { + FileUtil.createParentDirs(file); + byteOut = writeToBytes(document, SystemProperties.getLineSeparator()); } // mark this action as modifying the file which daemon analyzer should ignore AccessToken token = ApplicationManager.getApplication().acquireWriteActionLock(DocumentRunnable.IgnoreDocumentRunnable.class); try { - VirtualFile virtualFile = getOrCreateVirtualFile(requestor, file); - byte[] bytes = text.getBytes(CharsetToolkit.UTF8); - virtualFile.setBinaryContent(bytes, -1, -1, requestor); + if (virtualFile == null) { + virtualFile = getOrCreateVirtualFile(requestor, file); + } + OutputStream virtualFileOut = virtualFile.getOutputStream(requestor); + try { + byteOut.writeTo(virtualFileOut); + } + finally { + virtualFileOut.close(); + } return virtualFile; } finally { @@ -150,70 +201,78 @@ public class StorageUtil { } @NotNull - static VirtualFile getOrCreateVirtualFile(final Object requestor, final IFile ioFile) throws IOException { - VirtualFile vFile = getVirtualFile(ioFile); - - if (vFile == null) { - vFile = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(ioFile); - } + private static BufferExposingByteArrayOutputStream writeToBytes(@NotNull Parent element, @NotNull String lineSeparator) throws IOException { + BufferExposingByteArrayOutputStream out = new BufferExposingByteArrayOutputStream(512); + JDOMUtil.writeParent(element, out, lineSeparator); + return out; + } - if (vFile == null) { - final IFile parentFile = ioFile.getParentFile(); - final VirtualFile parentVFile = - LocalFileSystem.getInstance().refreshAndFindFileByIoFile(parentFile); // need refresh if the directory has just been created - if (parentVFile == null) { - throw new IOException(ProjectBundle.message("project.configuration.save.file.not.found", parentFile.getPath())); + @NotNull + static VirtualFile getOrCreateVirtualFile(@Nullable Object requestor, @NotNull File ioFile) throws IOException { + VirtualFile virtualFile = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(ioFile); + if (virtualFile == null) { + File parentFile = ioFile.getParentFile(); + // need refresh if the directory has just been created + VirtualFile parentVirtualFile = parentFile == null ? null : LocalFileSystem.getInstance().refreshAndFindFileByIoFile(parentFile); + if (parentVirtualFile == null) { + throw new IOException(ProjectBundle.message("project.configuration.save.file.not.found", parentFile == null ? "" : parentFile.getPath())); } - vFile = parentVFile.createChildData(requestor, ioFile.getName()); + virtualFile = parentVirtualFile.createChildData(requestor, ioFile.getName()); } - - return vFile; - } - - @Nullable - static VirtualFile getVirtualFile(final IFile ioFile) { - return LocalFileSystem.getInstance().findFileByIoFile(ioFile); + return virtualFile; } /** * @return pair.first - file contents (null if file does not exist), pair.second - file line separators */ - private static Couple loadFile(@Nullable final VirtualFile file) throws IOException { + @NotNull + private static Pair loadFile(@Nullable final VirtualFile file) throws IOException { if (file == null || !file.exists()) { - return Couple.of(null, SystemProperties.getLineSeparator()); + return NON_EXISTENT_FILE_DATA; } - String fileText = new String(file.contentsToByteArray(), CharsetToolkit.UTF8); - final int index = fileText.indexOf('\n'); - return Couple.of(fileText, index == -1 - ? SystemProperties.getLineSeparator() - : index - 1 >= 0 ? fileText.charAt(index - 1) == '\r' ? "\r\n" : "\n" : "\n"); + byte[] bytes = file.contentsToByteArray(); + String lineSeparator = file.getDetectedLineSeparator(); + if (lineSeparator == null) { + String fileText = new String(bytes, CharsetToolkit.UTF8); + final int index = fileText.indexOf('\n'); + lineSeparator = index == -1 + ? SystemProperties.getLineSeparator() + : index - 1 >= 0 ? fileText.charAt(index - 1) == '\r' ? "\r\n" : "\n" : "\n"; + } + return Pair.create(bytes, lineSeparator); } - public static boolean contentEquals(@NotNull final Document document, @NotNull final VirtualFile file) { + public static boolean contentEquals(@NotNull Parent element, @NotNull VirtualFile file) { + return newContentIfDiffers(element, file) == null; + } + + @Nullable + public static BufferExposingByteArrayOutputStream newContentIfDiffers(@NotNull Parent element, @Nullable VirtualFile file) { try { - final Couple pair = loadFile(file); - return pair.first != null && pair.first.equals(JDOMUtil.writeDocument(document, pair.second)); + Pair pair = loadFile(file); + BufferExposingByteArrayOutputStream out = writeToBytes(element, pair.second); + return pair.first != null && equal(pair.first, out) ? null : out; } catch (IOException e) { LOG.debug(e); - return false; + return null; } } - public static boolean contentEquals(@NotNull final Element element, @NotNull final VirtualFile file) { - try { - final Couple pair = loadFile(file); - return pair.first != null && pair.first.equals(printElement(element, pair.second)); - } - catch (IOException e) { - LOG.debug(e); + public static boolean equal(byte[] a1, @NotNull BufferExposingByteArrayOutputStream out) { + int length = out.size(); + if (a1.length != length) { return false; } - } - static String printElement(final Element element, final String lineSeparator) throws StateStorageException { - return JDOMUtil.writeElement(element, lineSeparator); + byte[] internalBuffer = out.getInternalBuffer(); + for (int i = 0; i < length; i++) { + if (a1[i] != internalBuffer[i]) { + return false; + } + } + return true; } @Nullable @@ -229,6 +288,7 @@ public class StorageUtil { } } + @SuppressWarnings("Contract") @Nullable public static Document loadDocument(@Nullable InputStream stream) { if (stream == null) { @@ -252,29 +312,20 @@ public class StorageUtil { } @NotNull - public static BufferExposingByteArrayOutputStream documentToBytes(@NotNull Document document, boolean useSystemLineSeparator) throws IOException { - BufferExposingByteArrayOutputStream out = new BufferExposingByteArrayOutputStream(512); - OutputStreamWriter writer = new OutputStreamWriter(out, CharsetToolkit.UTF8_CHARSET); - try { - JDOMUtil.writeDocument(document, writer, useSystemLineSeparator ? SystemProperties.getLineSeparator() : "\n"); - return out; - } - finally { - writer.close(); - } + public static BufferExposingByteArrayOutputStream elementToBytes(@NotNull Parent element, boolean useSystemLineSeparator) throws IOException { + return writeToBytes(element, useSystemLineSeparator ? SystemProperties.getLineSeparator() : "\n"); } - public static boolean sendContent(@NotNull StreamProvider provider, @NotNull String fileSpec, @NotNull Document copy, @NotNull RoamingType type, boolean async) { + public static void sendContent(@NotNull StreamProvider provider, @NotNull String fileSpec, @NotNull Parent element, @NotNull RoamingType type, boolean async) { if (!provider.isApplicable(fileSpec, type)) { - return false; + return; } try { - return doSendContent(provider, fileSpec, copy, type, async); + doSendContent(provider, fileSpec, element, type, async); } catch (IOException e) { LOG.warn(e); - return false; } } @@ -287,10 +338,10 @@ public class StorageUtil { /** * You must call {@link StreamProvider#isApplicable(String, com.intellij.openapi.components.RoamingType)} before */ - public static boolean doSendContent(StreamProvider provider, String fileSpec, Document copy, RoamingType type, boolean async) throws IOException { + public static void doSendContent(@NotNull StreamProvider provider, @NotNull String fileSpec, @NotNull Parent element, @NotNull RoamingType type, boolean async) throws IOException { // we should use standard line-separator (\n) - stream provider can share file content on any OS - BufferExposingByteArrayOutputStream content = documentToBytes(copy, false); - return provider.saveContent(fileSpec, content.getInternalBuffer(), content.size(), type, async); + BufferExposingByteArrayOutputStream content = elementToBytes(element, false); + provider.saveContent(fileSpec, content.getInternalBuffer(), content.size(), type, async); } public static void logStateDiffInfo(Set> changedFiles, Set componentNames) { @@ -309,10 +360,9 @@ public class StorageUtil { StateStorage storage = pair.second; if (storage instanceof XmlElementStorage) { - Document state = ((XmlElementStorage)storage).logComponents(); + Element state = ((XmlElementStorage)storage).logComponents(); if (state != null) { - File logFile = new File(logDirectory, "prev_" + file.getName()); - JDOMUtil.writeDocument(state, logFile, "\n"); + JDOMUtil.writeParent(state, new File(logDirectory, "prev_" + file.getName()), "\n"); } } diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StreamProvider.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StreamProvider.java index ec57d8c63601..cf49b927ad0e 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StreamProvider.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/StreamProvider.java @@ -12,7 +12,9 @@ import java.util.Collections; public abstract class StreamProvider { public static final StreamProvider[] EMPTY_ARRAY = new StreamProvider[0]; - public abstract boolean isEnabled(); + public boolean isEnabled() { + return true; + } /** * If true, special version file per storage file will keep version of component. @@ -37,7 +39,7 @@ public abstract class StreamProvider { * @param roamingType * @param async */ - public abstract boolean saveContent(@NotNull String fileSpec, @NotNull byte[] content, int size, @NotNull RoamingType roamingType, boolean async) throws IOException; + public abstract void saveContent(@NotNull String fileSpec, @NotNull byte[] content, int size, @NotNull RoamingType roamingType, boolean async) throws IOException; @Nullable public abstract InputStream loadContent(@NotNull String fileSpec, @NotNull RoamingType roamingType) throws IOException; diff --git a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/XmlElementStorage.java b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/XmlElementStorage.java index b028676168d5..76f47db77533 100644 --- a/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/XmlElementStorage.java +++ b/platform/platform-impl/src/com/intellij/openapi/components/impl/stores/XmlElementStorage.java @@ -15,44 +15,48 @@ */ package com.intellij.openapi.components.impl.stores; -import com.intellij.ide.plugins.IdeaPluginDescriptorImpl; import com.intellij.openapi.Disposable; import com.intellij.openapi.components.*; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.options.CurrentUserHolder; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.JDOMUtil; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.WriteExternalException; import com.intellij.openapi.vfs.SafeWriteRequestor; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.util.io.fs.IFile; import gnu.trove.THashMap; +import gnu.trove.THashSet; import gnu.trove.TObjectLongHashMap; import org.jdom.Document; import org.jdom.Element; +import org.jdom.JDOMException; import org.jdom.filter.ElementFilter; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.io.File; import java.io.IOException; +import java.io.InputStream; import java.util.*; public abstract class XmlElementStorage implements StateStorage, Disposable { - private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.components.impl.stores.XmlElementStorage"); + private static final Logger LOG = Logger.getInstance(XmlElementStorage.class); + + private final static RoamingElementFilter DISABLED_ROAMING_ELEMENT_FILTER = new RoamingElementFilter(RoamingType.DISABLED); private static final String ATTR_NAME = "name"; private static final String VERSION_FILE_SUFFIX = ".ver"; protected TrackingPathMacroSubstitutor myPathMacroSubstitutor; - @NotNull private final String myRootElementName; + @NotNull protected final String myRootElementName; private Object mySession; private StorageData myLoadedData; protected final StreamProvider myStreamProvider; protected final String myFileSpec; - private final ComponentRoamingManager myComponentRoamingManager; protected boolean myBlockSavingTheContent = false; protected int myUpToDateHash = -1; - protected int myProviderUpToDateHash = -1; + private int myProviderUpToDateHash = -1; private boolean mySavingDisabled = false; private final Map myStorageComponentStates = new THashMap(); // at load we store Element, on setState Integer of hash @@ -60,6 +64,8 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { private final ComponentVersionProvider myLocalVersionProvider; protected final RemoteComponentVersionProvider myRemoteVersionProvider; + private final RoamingType myRoamingType; + protected ComponentVersionListener myListener = new ComponentVersionListener(){ @Override public void componentStateChanged(String componentName) { @@ -69,18 +75,18 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { private boolean myDisposed; - protected XmlElementStorage(@Nullable TrackingPathMacroSubstitutor pathMacroSubstitutor, + protected XmlElementStorage(@NotNull String fileSpec, + @Nullable RoamingType roamingType, + @Nullable TrackingPathMacroSubstitutor pathMacroSubstitutor, @NotNull Disposable parentDisposable, @NotNull String rootElementName, @Nullable StreamProvider streamProvider, - String fileSpec, - ComponentRoamingManager componentRoamingManager, ComponentVersionProvider componentVersionProvider) { + myFileSpec = fileSpec; + myRoamingType = roamingType == null ? RoamingType.PER_USER : roamingType; myPathMacroSubstitutor = pathMacroSubstitutor; myRootElementName = rootElementName; - myStreamProvider = streamProvider; - myFileSpec = fileSpec; - myComponentRoamingManager = componentRoamingManager; + myStreamProvider = myRoamingType == RoamingType.DISABLED ? null : streamProvider; Disposer.register(parentDisposable, this); myLocalVersionProvider = componentVersionProvider; @@ -92,36 +98,39 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { } @Nullable - protected abstract Document loadDocument() throws StateStorageException; + protected abstract Element loadLocalData(); @Nullable - public synchronized Element getState(final String componentName) throws StateStorageException { + public synchronized Element getState(@NotNull String componentName) { final StorageData storageData = getStorageData(false); final Element state = storageData.getState(componentName); - if (state != null) { if (!myStorageComponentStates.containsKey(componentName)) { myStorageComponentStates.put(componentName, state); } storageData.removeState(componentName); } - return state; } @Override - public boolean hasState(final Object component, final String componentName, final Class aClass, final boolean reloadData) throws StateStorageException { + public boolean hasState(final Object component, @NotNull String componentName, final Class aClass, final boolean reloadData) throws StateStorageException { return getStorageData(reloadData).hasState(componentName); } @Override @Nullable - public T getState(final Object component, final String componentName, Class stateClass, @Nullable T mergeInto) throws StateStorageException { + public T getState(final Object component, @NotNull String componentName, Class stateClass, @Nullable T mergeInto) throws StateStorageException { return DefaultStateSerializer.deserializeState(getState(componentName), stateClass, mergeInto); } @NotNull - protected StorageData getStorageData(final boolean reloadData) throws StateStorageException { + protected StorageData getStorageData() { + return getStorageData(false); + } + + @NotNull + private StorageData getStorageData(boolean reloadData) { if (myLoadedData != null && !reloadData) { return myLoadedData; } @@ -131,48 +140,48 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { } @NotNull - protected StorageData loadData(boolean useProvidersData) throws StateStorageException { - Document document = loadDocument(); + protected StorageData loadData(boolean useProvidersData) { StorageData result = createStorageData(); - if (document != null) { - loadState(result, document.getRootElement()); - } - if (useProvidersData && myStreamProvider != null && myStreamProvider.isEnabled()) { - for (RoamingType roamingType : RoamingType.values()) { - if (roamingType != RoamingType.DISABLED && roamingType != RoamingType.GLOBAL) { - try { - Document sharedDocument = StorageUtil.loadDocument(myStreamProvider.loadContent(myFileSpec, roamingType)); - if (sharedDocument != null) { - filterOutOfDate(sharedDocument.getRootElement()); - loadState(result, sharedDocument.getRootElement()); - } - } - catch (Exception e) { - LOG.warn(e); - } - } + boolean wasLoaded = false; + try { + wasLoaded = loadDataFromStreamProvider(result); + } + catch (Exception e) { + LOG.warn(e); + } + + //noinspection deprecation + if (wasLoaded && !myStreamProvider.isVersioningRequired() && !(myStreamProvider instanceof OldStreamProviderAdapter || myStreamProvider instanceof CurrentUserHolder)) { + // we don't use local data if stream provider has one (to preserve backward compatibility, we don't use this logic for old stream providers) + return result; } } + Element element = loadLocalData(); + if (element != null) { + loadState(result, element); + } + return result; } - protected void loadState(final StorageData result, final Element element) throws StateStorageException { - if (myPathMacroSubstitutor != null) { - myPathMacroSubstitutor.expandPaths(element); + private boolean loadDataFromStreamProvider(@NotNull StorageData result) throws IOException, JDOMException { + assert myStreamProvider != null; + InputStream inputStream = myStreamProvider.loadContent(myFileSpec, myRoamingType); + if (inputStream == null) { + return false; } - IdeaPluginDescriptorImpl.internJDOMElement(element); + Element element = JDOMUtil.loadDocument(inputStream).getRootElement(); + filterOutOfDate(element); + loadState(result, element); + return true; + } - try { - result.load(element); - result.checkUnknownMacros(myPathMacroSubstitutor); - } - catch (IOException e) { - throw new StateStorageException(e); - } + private void loadState(@NotNull StorageData result, @NotNull Element element) { + result.load(element, myPathMacroSubstitutor, true); } @NotNull @@ -182,25 +191,15 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { public void setDefaultState(final Element element) { myLoadedData = createStorageData(); - try { - loadState(myLoadedData, element); - } - catch (StateStorageException e) { - LOG.error(e); - } + loadState(myLoadedData, element); } @Override @NotNull public ExternalizationSession startExternalization() { - try { - final ExternalizationSession session = new MyExternalizationSession(getStorageData(false).clone(), myListener); - mySession = session; - return session; - } - catch (StateStorageException e) { - throw new RuntimeException(e); - } + ExternalizationSession session = new MyExternalizationSession(getStorageData().clone(), myListener); + mySession = session; + return session; } @Override @@ -226,19 +225,19 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { @NotNull @Override - public Collection getStorageFilesToSave() throws StateStorageException { + public Collection getStorageFilesToSave() throws StateStorageException { return Collections.emptySet(); } @NotNull @Override - public List getAllStorageFiles() { + public List getAllStorageFiles() { return Collections.emptyList(); } }; } - protected abstract MySaveSession createSaveSession(final MyExternalizationSession externalizationSession); + protected abstract MySaveSession createSaveSession(MyExternalizationSession externalizationSession); @Override public void finishSave(@NotNull final SaveSession saveSession) { @@ -265,22 +264,26 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { } @Override - public void setState(@NotNull final Object component, final String componentName, @NotNull final Object state, final Storage storageSpec) throws StateStorageException { + public void setState(@NotNull Object component, @NotNull String componentName, @NotNull Object state, @Nullable Storage storageSpec) { assert mySession == this; + Element element; try { - setState(componentName, DefaultStateSerializer.serializeState(state, storageSpec)); + element = DefaultStateSerializer.serializeState(state, storageSpec); } catch (WriteExternalException e) { LOG.debug(e); + return; } - } - private synchronized void setState(final String componentName, final Element element) { - if (element.getAttributes().isEmpty() && element.getChildren().isEmpty()) { + if (element == null || JDOMUtil.isEmpty(element)) { return; } + setState(componentName, element); + } + + private synchronized void setState(@NotNull String componentName, @NotNull Element element) { myStorageData.setState(componentName, element); int hash = JDOMUtil.getTreeHash(element); try { @@ -296,8 +299,12 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { } } - protected Document getDocument(StorageData data) { - final Element element = data.save(); + @Nullable + protected Element getElement(@NotNull StorageData data) { + Element element = data.save(); + if (element == null || JDOMUtil.isEmpty(element)) { + return null; + } if (myPathMacroSubstitutor != null) { try { @@ -308,18 +315,18 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { } } - return new Document(element); + return element; } protected abstract class MySaveSession implements SaveSession, SafeWriteRequestor { final StorageData myStorageData; - private Document myDocumentToSave; + private Element myElementToSave; public MySaveSession(MyExternalizationSession externalizationSession) { myStorageData = externalizationSession.myStorageData; } - public final boolean needsSave() throws StateStorageException { + public final boolean needsSave() { assert mySession == this; return _needsSave(calcHash()); } @@ -382,11 +389,10 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { try { if (myStreamProvider != null && myStreamProvider.isEnabled() && (myProviderUpToDateHash == -1 || myProviderUpToDateHash != hash)) { try { - //noinspection IfStatementWithIdenticalBranches - if (saveForProvider(myStreamProvider)) { - //noinspection UnnecessaryReturnStatement - return; - } + saveForProvider(); + } + catch (IOException e) { + LOG.warn(e); } finally { myProviderUpToDateHash = hash; @@ -398,9 +404,9 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { } } - private void saveLocally(final Integer hash) { + private void saveLocally(int hash) { try { - if (!isHashUpToDate(hash) && _needsSave(hash)) { + if (!(myUpToDateHash != -1 && myUpToDateHash == hash) && _needsSave(hash)) { doSave(); } } @@ -409,15 +415,15 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { } } - private boolean saveForProvider(@NotNull StreamProvider streamProvider) { - if (!streamProvider.isApplicable(myFileSpec, RoamingType.PER_USER)) { - return false; + private void saveForProvider() throws IOException { + if (!myStreamProvider.isApplicable(myFileSpec, myRoamingType)) { + return; } - Document document = getDocumentToSave(); - Element rootElement = document.getRootElement(); - if (rootElement.getChildren().isEmpty()) { - return false; + Element element = getElementToSave(); + if (element == null || element.getChildren().isEmpty()) { + myStreamProvider.delete(myFileSpec, myRoamingType); + return; } // skip the whole document if some component has disabled roaming type @@ -425,59 +431,34 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { // one exclusion: workspace file (you don't have choice in this case) // for example, it is important for ICS ProjectId - we cannot keep project in another place, // but this project id must not be shared - if (!myFileSpec.equals(StoragePathMacros.WORKSPACE_FILE) && - rootElement.getContent(new RoamingElementFilter(RoamingType.DISABLED)).iterator().hasNext()) { - return false; - } - - RoamingElementFilter perPlatformFilter = new RoamingElementFilter(RoamingType.PER_PLATFORM); - if (rootElement.getContent(perPlatformFilter).iterator().hasNext()) { - return doSaveForProvider(rootElement, new RoamingElementFilter(RoamingType.PER_USER)) || - doSaveForProvider(rootElement, perPlatformFilter); + if (myFileSpec.equals(StoragePathMacros.WORKSPACE_FILE)) { + Element copiedElement = JDOMUtil.cloneElement(element, DISABLED_ROAMING_ELEMENT_FILTER); + if (copiedElement != null) { + doSaveForProvider(copiedElement, DISABLED_ROAMING_ELEMENT_FILTER.myRoamingType, myStreamProvider); + } } else { - return doSaveForProvider(document, RoamingType.PER_USER, streamProvider); + doSaveForProvider(element, myRoamingType, myStreamProvider); } } - private boolean doSaveForProvider(Element element, RoamingElementFilter filter) { - Element copiedElement = JDOMUtil.cloneElement(element, filter); - return copiedElement != null && doSaveForProvider(new Document(copiedElement), filter.myRoamingType, myStreamProvider); - } - - private boolean doSaveForProvider(Document actualDocument, RoamingType roamingType, StreamProvider streamProvider) { - try { - boolean result = StorageUtil.doSendContent(streamProvider, myFileSpec, actualDocument, roamingType, true); - if (streamProvider.isVersioningRequired()) { - TObjectLongHashMap versions = loadVersions(actualDocument.getRootElement().getChildren(StorageData.COMPONENT)); - if (!versions.isEmpty()) { - Document versionDoc = new Document(StateStorageManagerImpl.createComponentVersionsXml(versions)); - StorageUtil.doSendContent(streamProvider, myFileSpec + VERSION_FILE_SUFFIX, versionDoc, roamingType, true); - } + private void doSaveForProvider(@NotNull Element element, @NotNull RoamingType roamingType, @NotNull StreamProvider streamProvider) throws IOException { + StorageUtil.doSendContent(streamProvider, myFileSpec, element, roamingType, true); + if (streamProvider.isVersioningRequired()) { + TObjectLongHashMap versions = loadVersions(element.getChildren(StorageData.COMPONENT)); + if (!versions.isEmpty()) { + Element versionDoc = StateStorageManagerImpl.createComponentVersionsXml(versions); + StorageUtil.doSendContent(streamProvider, myFileSpec + VERSION_FILE_SUFFIX, versionDoc, roamingType, true); } - return result; - } - catch (IOException e) { - LOG.warn(e); - return false; } } - private boolean isHashUpToDate(final Integer hash) { - return myUpToDateHash != -1 && myUpToDateHash == hash; - } - - protected Document getDocumentToSave() { - if (myDocumentToSave != null) return myDocumentToSave; - - final Element element = myStorageData.save(); - myDocumentToSave = new Document(element); - - if (myPathMacroSubstitutor != null) { - myPathMacroSubstitutor.collapsePaths(element); + @Nullable + protected Element getElementToSave() { + if (myElementToSave == null) { + myElementToSave = getElement(myStorageData); } - - return myDocumentToSave; + return myElementToSave; } public StorageData getData() { @@ -488,12 +469,12 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { @Nullable public Set analyzeExternalChanges(@NotNull final Set> changedFiles) { try { - Document document = loadDocument(); + Element element = loadLocalData(); StorageData storageData = createStorageData(); - if (document == null) { + if (element == null) { return Collections.emptySet(); } - loadState(storageData, document.getRootElement()); + loadState(storageData, element); return storageData.getDifference(myStorageData, myPathMacroSubstitutor); } catch (StateStorageException e) { @@ -502,21 +483,6 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { return null; } - - private class RoamingElementFilter extends ElementFilter { - final RoamingType myRoamingType; - - public RoamingElementFilter(RoamingType roamingType) { - super(StorageData.COMPONENT); - - myRoamingType = roamingType; - } - - @Override - public boolean matches(Object obj) { - return super.matches(obj) && myComponentRoamingManager.getRoamingType(((Element)obj).getAttributeValue(StorageData.NAME)) == myRoamingType; - } - } } private TObjectLongHashMap loadVersions(List elements) { @@ -545,11 +511,9 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { @Override public void reload(@NotNull final Set changedComponents) throws StateStorageException { final StorageData storageData = loadData(false); - final StorageData oldLoadedData = myLoadedData; - if (oldLoadedData != null) { - Set componentsToRetain = new HashSet(oldLoadedData.myComponentStates.keySet()); + Set componentsToRetain = new THashSet(oldLoadedData.myComponentStates.keySet()); componentsToRetain.addAll(changedComponents); // add empty configuration tags for removed components @@ -568,7 +532,7 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { myLoadedData = storageData; } - private void filterOutOfDate(Element element) { + private void filterOutOfDate(@NotNull Element element) { if (myRemoteVersionProvider == null) { return; } @@ -587,12 +551,19 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { } @Nullable - Document logComponents() throws StateStorageException { - return mySession instanceof MySaveSession ? getDocument(((MySaveSession)mySession).myStorageData) : null; + Element logComponents() { + return mySession instanceof MySaveSession ? getElement(((MySaveSession)mySession).myStorageData) : null; } - protected class RemoteComponentVersionProvider implements ComponentVersionProvider { - protected TObjectLongHashMap myProviderVersions; + public void resetProviderCache() { + myProviderUpToDateHash = -1; + if (myRemoteVersionProvider != null) { + myRemoteVersionProvider.myProviderVersions = null; + } + } + + private final class RemoteComponentVersionProvider implements ComponentVersionProvider { + private TObjectLongHashMap myProviderVersions; @Override public long getVersion(String name) { @@ -632,4 +603,19 @@ public abstract class XmlElementStorage implements StateStorage, Disposable { } } } + + private static class RoamingElementFilter extends ElementFilter { + final RoamingType myRoamingType; + + public RoamingElementFilter(RoamingType roamingType) { + super(StorageData.COMPONENT); + + myRoamingType = roamingType; + } + + @Override + public boolean matches(Object obj) { + return super.matches(obj) && ComponentRoamingManager.getInstance().getRoamingType(((Element)obj).getAttributeValue(StorageData.NAME)) == myRoamingType; + } + } } diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/ApplicationStarterBase.java b/platform/platform-impl/src/com/intellij/openapi/diff/ApplicationStarterBase.java index b508686ade00..369acf473cbc 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/ApplicationStarterBase.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/ApplicationStarterBase.java @@ -26,6 +26,7 @@ import com.intellij.openapi.vfs.JarFileSystem; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.io.File; import java.io.FileInputStream; @@ -57,13 +58,13 @@ public abstract class ApplicationStarterBase extends ApplicationStarterEx { } @Override - public void processExternalCommandLine(String[] args) { + public void processExternalCommandLine(String[] args, @Nullable String currentDirectory) { if (!checkArguments(args)) { Messages.showMessageDialog(getUsageMessage(), StringUtil.toTitleCase(getCommandName()), Messages.getInformationIcon()); return; } try { - processCommand(args); + processCommand(args, currentDirectory); } catch (Exception e) { Messages.showMessageDialog(String.format("Error showing %s: %s", getCommandName(), e.getMessage()), @@ -86,7 +87,7 @@ public abstract class ApplicationStarterBase extends ApplicationStarterEx { public abstract String getUsageMessage(); - protected abstract void processCommand(String[] args) throws Exception; + protected abstract void processCommand(String[] args, @Nullable String currentDirectory) throws Exception; @Override public void premain(String[] args) { @@ -99,7 +100,7 @@ public abstract class ApplicationStarterBase extends ApplicationStarterEx { @Override public void main(String[] args) { try { - processCommand(args); + processCommand(args, null); } catch (Exception e) { e.printStackTrace(); @@ -116,12 +117,12 @@ public abstract class ApplicationStarterBase extends ApplicationStarterEx { System.exit(0); } - public static VirtualFile findOrCreateFile(String path) throws IOException { + public static VirtualFile findOrCreateFile(String path, @Nullable String currentDirectory) throws IOException { final VirtualFile file = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(new File(path)); if (file == null) { boolean result = new File(path).createNewFile(); if (result) { - return findFile(path); + return findFile(path, currentDirectory); } else { throw new FileNotFoundException("Can't create file " + path); @@ -167,11 +168,10 @@ public abstract class ApplicationStarterBase extends ApplicationStarterEx { } @NotNull - public static VirtualFile findFile(final String path) throws OperationFailedException { + public static VirtualFile findFile(final String path, @Nullable String currentDirectory) throws OperationFailedException { File ioFile = new File(path); - if (!ioFile.exists()) { - final String dir = PathManager.getOriginalWorkingDir(); - ioFile = new File(dir + File.separator + path); + if (!ioFile.isAbsolute() && currentDirectory != null) { + ioFile = new File(currentDirectory, path); } final VirtualFile file = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(ioFile); if (file == null) { diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/DiffApplication.java b/platform/platform-impl/src/com/intellij/openapi/diff/DiffApplication.java index 742a405f350e..143df9617310 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/DiffApplication.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/DiffApplication.java @@ -21,6 +21,7 @@ import com.intellij.openapi.application.ApplicationNamesInfo; import com.intellij.openapi.fileTypes.UnknownFileType; import com.intellij.openapi.project.ProjectManager; import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.annotations.Nullable; /** * @author max @@ -37,11 +38,11 @@ public class DiffApplication extends ApplicationStarterBase { return DiffBundle.message("diff.application.usage.parameters.and.description", scriptName); } - public void processCommand(String[] args) throws OperationFailedException { + public void processCommand(String[] args, @Nullable String currentDirectory) throws OperationFailedException { final String path1 = args[1]; final String path2 = args[2]; - final VirtualFile file1 = findFile(path1); - final VirtualFile file2 = findFile(path2); + final VirtualFile file1 = findFile(path1, currentDirectory); + final VirtualFile file2 = findFile(path2, currentDirectory); final boolean areDirs = areDirs(file1, file2); final boolean areJars = areJars(file1, file2); if (areDirs || areJars) { diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/MergeApplication.java b/platform/platform-impl/src/com/intellij/openapi/diff/MergeApplication.java index 0c088929ea49..d1d6cf6cb9f5 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/MergeApplication.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/MergeApplication.java @@ -18,6 +18,7 @@ package com.intellij.openapi.diff; import com.intellij.openapi.application.ApplicationNamesInfo; import com.intellij.openapi.project.ProjectManager; import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.annotations.Nullable; /** * @author Konstantin Bulenkov @@ -34,11 +35,11 @@ public class MergeApplication extends ApplicationStarterBase { } @Override - protected void processCommand(String[] args) throws Exception { - final VirtualFile left = findFile(args[1]); - final VirtualFile right = findFile(args[2]); - final VirtualFile middle = findFile(args[3]); - final VirtualFile result = findOrCreateFile(args.length == 4 ? args[3] : args[4]); + protected void processCommand(String[] args, @Nullable String currentDirectory) throws Exception { + final VirtualFile left = findFile(args[1], currentDirectory); + final VirtualFile right = findFile(args[2], currentDirectory); + final VirtualFile middle = findFile(args[3], currentDirectory); + final VirtualFile result = findOrCreateFile(args.length == 4 ? args[3] : args[4], currentDirectory); MergeRequest request = DiffRequestFactory.getInstance() .createMergeRequest(getText(left), getText(right), getText(middle), result, diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/actions/ScrollToTheEndToolbarAction.java b/platform/platform-impl/src/com/intellij/openapi/editor/actions/ScrollToTheEndToolbarAction.java index cc1be90ef046..dab14bdadcc2 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/actions/ScrollToTheEndToolbarAction.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/actions/ScrollToTheEndToolbarAction.java @@ -48,7 +48,7 @@ public class ScrollToTheEndToolbarAction extends DumbAwareAction { Rectangle visibleArea = myEditor.getScrollingModel().getVisibleArea(); Dimension size = myEditor.getContentComponent().getSize(); boolean isEndVisible = visibleArea.y + visibleArea.height >= size.height; - boolean isOnLastLine = document.getLineNumber(caretOffset) == document.getLineCount() - 1; + boolean isOnLastLine = document.getLineCount() == 0 || document.getLineNumber(caretOffset) == document.getLineCount() - 1; e.getPresentation().setEnabled(!isEndVisible || !isOnLastLine); } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/colors/impl/EditorColorsManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/colors/impl/EditorColorsManagerImpl.java index cf3c196a9d78..0d0377bdf7fe 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/colors/impl/EditorColorsManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/colors/impl/EditorColorsManagerImpl.java @@ -382,7 +382,7 @@ public class EditorColorsManagerImpl extends EditorColorsManager implements Name } @Override - public Document writeScheme(@NotNull final EditorColorsSchemeImpl scheme) { + public Element writeScheme(@NotNull final EditorColorsSchemeImpl scheme) { Element root = new Element(SCHEME_NODE_NAME); try { scheme.writeExternal(root); @@ -392,7 +392,7 @@ public class EditorColorsManagerImpl extends EditorColorsManager implements Name return null; } - return new Document(root); + return root; } @Override diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java index 51f6ec4a8cdb..165429ce56df 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java @@ -81,10 +81,7 @@ import com.intellij.util.containers.ContainerUtilRt; import com.intellij.util.messages.MessageBusConnection; import com.intellij.util.text.CharArrayCharSequence; import com.intellij.util.text.CharArrayUtil; -import com.intellij.util.ui.ButtonlessScrollBarUI; -import com.intellij.util.ui.GraphicsUtil; -import com.intellij.util.ui.MacUIUtil; -import com.intellij.util.ui.UIUtil; +import com.intellij.util.ui.*; import com.intellij.util.ui.update.Activatable; import com.intellij.util.ui.update.UiNotifyConnector; import gnu.trove.TIntArrayList; @@ -4106,7 +4103,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi } private void processMouseDragged(@NotNull MouseEvent e) { - if (SwingUtilities.isRightMouseButton(e)) { + if (JBSwingUtilities.isRightMouseButton(e)) { return; } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/LazyRangeMarkerFactoryImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/LazyRangeMarkerFactoryImpl.java index 5a9c704173aa..c4b9a38914cf 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/LazyRangeMarkerFactoryImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/LazyRangeMarkerFactoryImpl.java @@ -56,7 +56,7 @@ public class LazyRangeMarkerFactoryImpl extends LazyRangeMarkerFactory { private void transformRangeMarkers(@NotNull DocumentEvent e) { Document document = e.getDocument(); VirtualFile file = fileDocumentManager.getFile(document); - if (file == null) { + if (file == null || myProject.isDisposed()) { return; } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/SelectionModelImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/SelectionModelImpl.java index 731d1e26a289..574780a661c2 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/SelectionModelImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/SelectionModelImpl.java @@ -24,6 +24,7 @@ */ package com.intellij.openapi.editor.impl; +import com.intellij.openapi.Disposable; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.*; @@ -36,6 +37,7 @@ import com.intellij.openapi.editor.ex.DocumentEx; import com.intellij.openapi.editor.ex.PrioritizedDocumentListener; import com.intellij.openapi.editor.ex.util.EditorUtil; import com.intellij.openapi.editor.markup.TextAttributes; +import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Pair; import com.intellij.util.ArrayUtil; import com.intellij.util.containers.ContainerUtil; @@ -464,6 +466,16 @@ public class SelectionModelImpl implements SelectionModel, PrioritizedDocumentLi mySelectionListeners.add(listener); } + public void addSelectionListener(final SelectionListener listener, Disposable parent) { + mySelectionListeners.add(listener); + Disposer.register(parent, new Disposable() { + @Override + public void dispose() { + mySelectionListeners.remove(listener); + } + }); + } + @Override public void removeSelectionListener(SelectionListener listener) { boolean success = mySelectionListeners.remove(listener); diff --git a/platform/platform-impl/src/com/intellij/openapi/fileChooser/ex/FileChooserDialogImpl.java b/platform/platform-impl/src/com/intellij/openapi/fileChooser/ex/FileChooserDialogImpl.java index d585ec5a7b7d..2938f419909c 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileChooser/ex/FileChooserDialogImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileChooser/ex/FileChooserDialogImpl.java @@ -114,16 +114,32 @@ public class FileChooserDialogImpl extends DialogWrapper implements FileChooserD @Override @NotNull - public VirtualFile[] choose(@Nullable VirtualFile toSelect, Project project) { + public VirtualFile[] choose(@Nullable final Project project, @NotNull final VirtualFile... toSelect) { init(); - if (myProject == null && project != null) { + if ((myProject == null) && (project != null)) { myProject = project; } - restoreSelection(toSelect); + if (toSelect.length == 1) { + restoreSelection(toSelect[0]); + } + else if (toSelect.length == 0) { + restoreSelection(null); // select last opened file + } + else { + selectInTree(toSelect, true); + } + show(); return myChosenFiles; } + + @NotNull + @Override + public VirtualFile[] choose(@Nullable final VirtualFile toSelect, @Nullable final Project project) { + return choose(project, toSelect); + } + @Override public void choose(@Nullable VirtualFile toSelect, @NotNull Consumer> callback) { init(); @@ -163,7 +179,6 @@ public class FileChooserDialogImpl extends DialogWrapper implements FileChooserD if (file != null && file.getFileSystem() instanceof LocalFileSystem) { saveRecent(file.getPath()); } - } protected void saveRecent(String path) { diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/ex/FileEditorManagerEx.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/ex/FileEditorManagerEx.java index 30eb78cf706f..c074a4965493 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/ex/FileEditorManagerEx.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/ex/FileEditorManagerEx.java @@ -164,6 +164,7 @@ public abstract class FileEditorManagerEx extends FileEditorManager implements B public abstract boolean isInsideChange(); + @Override @Nullable public final Object getData(@NotNull String dataId, @NotNull Editor editor, @NotNull Caret caret) { for (final EditorDataProvider dataProvider : myDataProviders) { diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorComposite.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorComposite.java index 48a5b7c9a4dc..d8a704d891bf 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorComposite.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorComposite.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -66,7 +66,7 @@ public abstract class EditorComposite implements Disposable { /** * File for which composite is created */ - private final VirtualFile myFile; + @NotNull private final VirtualFile myFile; /** * Whether the composite is pinned or not */ diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorEmptyTextPainter.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorEmptyTextPainter.java index 4807812e5047..101f4a7ddfed 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorEmptyTextPainter.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorEmptyTextPainter.java @@ -54,9 +54,12 @@ public class EditorEmptyTextPainter { UIUtil.TextPainter painter = new UIUtil.TextPainter().withLineSpacing(1.5f); painter.withShadow(true, new JBColor(Gray._200.withAlpha(100), Gray._0.withAlpha(255))); - painter.appendLine("No files are open").underlined(new JBColor(Gray._150, Gray._180)); + painter.appendLine("No files are open"); - advertiseActions(splitters, painter); + if (!splitters.isPreview()) { + painter.underlined(new JBColor(Gray._150, Gray._180)); + advertiseActions(splitters, painter); + } painter.draw(g, new PairFunction>() { @Override diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorHistoryManager.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorHistoryManager.java index 32169662ba2a..ddd5395805ff 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorHistoryManager.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorHistoryManager.java @@ -287,7 +287,7 @@ public final class EditorHistoryManager extends AbstractProjectComponent impleme } } - public FileEditorState getState(final VirtualFile file, final FileEditorProvider provider) { + public FileEditorState getState(@NotNull VirtualFile file, final FileEditorProvider provider) { validateEntries(); final HistoryEntry entry = getEntry(file); return entry != null ? entry.getState(provider) : null; @@ -302,7 +302,7 @@ public final class EditorHistoryManager extends AbstractProjectComponent impleme return entry != null ? entry.mySelectedProvider : null; } - private HistoryEntry getEntry(final VirtualFile file){ + private HistoryEntry getEntry(@NotNull VirtualFile file){ validateEntries(); for (int i = myEntriesList.size() - 1; i >= 0; i--) { final HistoryEntry entry = myEntriesList.get(i); diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabColorProvider.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabColorProvider.java index babf76dd3709..58f5470aca07 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabColorProvider.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabColorProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ package com.intellij.openapi.fileEditor.impl; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.extensions.ExtensionPointName; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.awt.*; @@ -29,5 +30,5 @@ public interface EditorTabColorProvider { ExtensionPointName EP_NAME = ExtensionPointName.create("com.intellij.editorTabColorProvider"); @Nullable - Color getEditorTabColor(Project project, VirtualFile file); + Color getEditorTabColor(@NotNull Project project, @NotNull VirtualFile file); } diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabbedContainer.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabbedContainer.java index 03b03faffe03..3fa0f751e9cb 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabbedContainer.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorTabbedContainer.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -381,7 +381,7 @@ public final class EditorTabbedContainer implements Disposable, CloseAction.Clos } @Nullable - public static Color calcTabColor(final Project project, final VirtualFile file) { + public static Color calcTabColor(@NotNull Project project, @NotNull VirtualFile file) { for (EditorTabColorProvider provider : Extensions.getExtensions(EditorTabColorProvider.EP_NAME)) { final Color result = provider.getEditorTabColor(project, file); if (result != null) { diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorWindow.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorWindow.java index 06a3966ade1d..1bf86ea5d62c 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorWindow.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorWindow.java @@ -287,7 +287,7 @@ public class EditorWindow { } } - public void closeFile(final VirtualFile file, final boolean disposeIfNeeded, final boolean transferFocus) { + public void closeFile(@NotNull final VirtualFile file, final boolean disposeIfNeeded, final boolean transferFocus) { final FileEditorManagerImpl editorManager = getManager(); editorManager.runChange(new FileEditorManagerChange() { @Override @@ -525,7 +525,7 @@ public class EditorWindow { myOwner.setCurrentWindow(this, requestFocus); } - public void updateFileBackgroundColor(final VirtualFile file) { + public void updateFileBackgroundColor(@NotNull VirtualFile file) { final int index = findEditorIndex(findFileComposite(file)); if (index != -1) { final Color color = EditorTabbedContainer.calcTabColor(getManager().getProject(), file); @@ -569,10 +569,10 @@ public class EditorWindow { } protected static class TComp extends JPanel implements DataProvider, EditorWindowHolder { - final EditorWithProviderComposite myEditor; + @NotNull final EditorWithProviderComposite myEditor; protected final EditorWindow myWindow; - TComp(final EditorWindow window, final EditorWithProviderComposite editor) { + TComp(@NotNull EditorWindow window, @NotNull EditorWithProviderComposite editor) { super(new BorderLayout()); myEditor = editor; myWindow = window; @@ -594,6 +594,7 @@ public class EditorWindow { }); } + @NotNull @Override public EditorWindow getEditorWindow() { return myWindow; @@ -613,7 +614,7 @@ public class EditorWindow { } protected static class TCompForTablessMode extends TComp implements CloseAction.CloseTarget { - TCompForTablessMode(final EditorWindow window, final EditorWithProviderComposite editor) { + TCompForTablessMode(@NotNull EditorWindow window, @NotNull EditorWithProviderComposite editor) { super(window, editor); } @@ -701,6 +702,7 @@ public class EditorWindow { public void setEditor(@Nullable final EditorWithProviderComposite editor, final boolean selectEditor, final boolean focusEditor) { if (editor != null) { + onBeforeSetEditor(editor.getFile()); if (myTabbedPane == null) { myPanel.removeAll (); myPanel.add (new TCompForTablessMode(this, editor), BorderLayout.CENTER); @@ -741,6 +743,9 @@ public class EditorWindow { myOwner.validate(); } + protected void onBeforeSetEditor(VirtualFile file) { + } + private boolean splitAvailable() { return getTabCount() >= 1; } diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorWindowHolder.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorWindowHolder.java index c708d058b63b..2d27bb853531 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorWindowHolder.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorWindowHolder.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2011 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,9 +15,12 @@ */ package com.intellij.openapi.fileEditor.impl; +import org.jetbrains.annotations.NotNull; + /** * @author Konstantin Bulenkov */ public interface EditorWindowHolder { + @NotNull EditorWindow getEditorWindow(); } diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorsSplitters.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorsSplitters.java index b9e38809faab..c380d38a5db9 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorsSplitters.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/EditorsSplitters.java @@ -51,6 +51,7 @@ import com.intellij.util.Alarm; import com.intellij.util.containers.ArrayListSet; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.ui.UIUtil; +import gnu.trove.THashSet; import org.jdom.Element; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -76,7 +77,7 @@ public class EditorsSplitters extends IdePanePanel implements UISettingsListener private static final Key DUMMY_KEY = Key.create("EditorsSplitters.dummy.key"); - private final static EditorEmptyTextPainter ourPainter = ServiceManager.getService(EditorEmptyTextPainter.class); + private static final EditorEmptyTextPainter ourPainter = ServiceManager.getService(EditorEmptyTextPainter.class); private EditorWindow myCurrentWindow; final Set myWindows = new CopyOnWriteArraySet(); @@ -346,22 +347,18 @@ public class EditorsSplitters extends IdePanePanel implements UISettingsListener return virtualFiles; } - @NotNull public FileEditor[] getSelectedEditors() { - final List editors = new ArrayList(); + @NotNull + public FileEditor[] getSelectedEditors() { + List editors = new ArrayList(); + Set windows = new THashSet(myWindows); final EditorWindow currentWindow = getCurrentWindow(); if (currentWindow != null) { - final EditorWithProviderComposite composite = currentWindow.getSelectedEditor(); - if (composite != null) { - editors.add (composite.getSelectedEditor()); - } + windows.add(currentWindow); } - - for (final EditorWindow window : myWindows) { - if (!window.equals(currentWindow)) { - final EditorWithProviderComposite composite = window.getSelectedEditor(); - if (composite != null) { - editors.add(composite.getSelectedEditor()); - } + for (final EditorWindow window : windows) { + final EditorWithProviderComposite composite = window.getSelectedEditor(); + if (composite != null) { + editors.add(composite.getSelectedEditor()); } } return editors.toArray(new FileEditor[editors.size()]); @@ -463,7 +460,7 @@ public class EditorsSplitters extends IdePanePanel implements UISettingsListener myCurrentWindow = currentWindow; } - public void updateFileBackgroundColor(final VirtualFile file) { + public void updateFileBackgroundColor(@NotNull VirtualFile file) { final EditorWindow[] windows = getWindows(); for (int i = 0; i != windows.length; ++ i) { windows [i].updateFileBackgroundColor(file); @@ -614,10 +611,14 @@ public class EditorsSplitters extends IdePanePanel implements UISettingsListener public void createCurrentWindow() { LOG.assertTrue(myCurrentWindow == null); - setCurrentWindow(new EditorWindow(this)); + setCurrentWindow(createEditorWindow()); add(myCurrentWindow.myPanel, BorderLayout.CENTER); } + protected EditorWindow createEditorWindow() { + return new EditorWindow(this); + } + /** * sets the window passed as a current ('focused') window among all splitters. All file openings will be done inside this * current window @@ -625,7 +626,7 @@ public class EditorsSplitters extends IdePanePanel implements UISettingsListener * @param requestFocus whether to request focus to the editor currently selected in this window */ public void setCurrentWindow(@Nullable final EditorWindow window, final boolean requestFocus) { - final EditorWithProviderComposite newEditor = window != null? window.getSelectedEditor() : null; + final EditorWithProviderComposite newEditor = window == null ? null : window.getSelectedEditor(); Runnable fireRunnable = new Runnable() { @Override @@ -655,7 +656,7 @@ public class EditorsSplitters extends IdePanePanel implements UISettingsListener //--------------------------------------------------------- public EditorWithProviderComposite[] getEditorsComposites() { - final ArrayList res = new ArrayList(); + List res = new ArrayList(); for (final EditorWindow myWindow : myWindows) { final EditorWithProviderComposite[] editors = myWindow.getEditors(); @@ -667,8 +668,8 @@ public class EditorsSplitters extends IdePanePanel implements UISettingsListener //--------------------------------------------------------- @NotNull - public List findEditorComposites(final VirtualFile file) { - final ArrayList res = new ArrayList(); + public List findEditorComposites(@NotNull VirtualFile file) { + List res = new ArrayList(); for (final EditorWindow window : myWindows) { final EditorWithProviderComposite fileComposite = window.findFileComposite(file); if (fileComposite != null) { @@ -680,7 +681,7 @@ public class EditorsSplitters extends IdePanePanel implements UISettingsListener @NotNull public List findWindows(final VirtualFile file) { - final ArrayList res = new ArrayList(); + List res = new ArrayList(); for (final EditorWindow window : myWindows) { if (window.findFileComposite(file) != null) { res.add(window); @@ -694,7 +695,7 @@ public class EditorsSplitters extends IdePanePanel implements UISettingsListener } @NotNull public EditorWindow[] getOrderedWindows() { - final ArrayList res = new ArrayList(); + final List res = new ArrayList(); // Collector for windows in tree ordering: class Inner{ @@ -744,6 +745,10 @@ public class EditorsSplitters extends IdePanePanel implements UISettingsListener return false; } + public boolean isPreview() { + return false; + } + private final class MyFocusWatcher extends FocusWatcher { @Override protected void focusedComponentChanged(final Component component, final AWTEvent cause) { @@ -816,8 +821,10 @@ public class EditorsSplitters extends IdePanePanel implements UISettingsListener return processFiles(children, context); } - protected abstract @Nullable T processFiles(@NotNull List fileElements, @Nullable T context); - protected abstract @Nullable T processSplitter(@NotNull Element element, @Nullable Element firstChild, @Nullable Element secondChild, @Nullable T context); + @Nullable + protected abstract T processFiles(@NotNull List fileElements, @Nullable T context); + @Nullable + protected abstract T processSplitter(@NotNull Element element, @Nullable Element firstChild, @Nullable Element secondChild, @Nullable T context); } private class UIBuilder extends ConfigTreeReader { @@ -828,7 +835,7 @@ public class EditorsSplitters extends IdePanePanel implements UISettingsListener UIUtil.invokeAndWaitIfNeeded(new Runnable() { @Override public void run() { - windowRef.set(context == null ? new EditorWindow(EditorsSplitters.this) : findWindowWith(context)); + windowRef.set(context == null ? createEditorWindow() : findWindowWith(context)); } }); final EditorWindow window = windowRef.get(); diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileEditorManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileEditorManagerImpl.java index 65adf52041b9..abf16d54c43d 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileEditorManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileEditorManagerImpl.java @@ -58,7 +58,6 @@ import com.intellij.openapi.vcs.FileStatusListener; import com.intellij.openapi.vcs.FileStatusManager; import com.intellij.openapi.vfs.*; import com.intellij.openapi.wm.IdeFocusManager; -import com.intellij.openapi.wm.ToolWindowId; import com.intellij.openapi.wm.ToolWindowManager; import com.intellij.openapi.wm.WindowManager; import com.intellij.openapi.wm.ex.StatusBarEx; @@ -91,7 +90,6 @@ import java.beans.PropertyChangeListener; import java.lang.ref.WeakReference; import java.util.*; import java.util.List; -import java.util.concurrent.atomic.AtomicBoolean; /** * @author Anton Katilin @@ -113,7 +111,6 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec private final Project myProject; private final List> mySelectionHistory = new ArrayList>(); private WeakReference myLastSelectedComposite = new WeakReference(null); - private final AtomicBoolean myPreviewBlocker = new AtomicBoolean(false); private final MergingUpdateQueue myQueue = new MergingUpdateQueue("FileEditorManagerUpdateQueue", 50, true, null); @@ -179,9 +176,9 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec public Set getAllSplitters() { HashSet all = new LinkedHashSet(); - if (Registry.is("editor.use.preview")) { - initUI(); - all.add(myPreviewPanel.getWindow().getOwner()); + EditorWindow previewWindow = getPreviewWindow(); + if (previewWindow != null) { + all.add(previewWindow.getOwner()); } all.add(getMainSplitters()); Set dockContainers = myDockManager.getContainers(); @@ -255,16 +252,23 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec } } } - if (myPreviewPanel == null && Registry.is("editor.use.preview")) { + if (myPreviewPanel == null && PreviewPanel.isAvailable()) { synchronized (myInitLock) { myPreviewPanel = new PreviewPanel(myProject, this, myDockManager); } } } + @Nullable + private EditorWindow getPreviewWindow() { + if (!PreviewPanel.isAvailable()) return null; + initUI(); + return myPreviewPanel.getWindow(); + } + private static class MyBorder implements Border { @Override - public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { + public void paintBorder(@NotNull Component c, @NotNull Graphics g, int x, int y, int width, int height) { if (UIUtil.isUnderAquaLookAndFeel()) { g.setColor(JBTabsImpl.MAC_AQUA_BG_COLOR); final Insets insets = getBorderInsets(c); @@ -274,6 +278,7 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec } } + @NotNull @Override public Insets getBorderInsets(Component c) { return JBInsets.NONE; @@ -607,7 +612,7 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec @Override @NotNull public Pair openFileWithProviders(@NotNull final VirtualFile file, - final boolean focusEditor, + boolean focusEditor, final boolean searchForSplitter) { if (!file.isValid()) { throw new IllegalArgumentException("file is not valid: " + file); @@ -642,9 +647,10 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec } if (wndToOpenIn == null || !wndToOpenIn.isFileOpen(file)) { - EditorWindow previewWindow = getPreviewWindow(file, focusEditor, searchForSplitter); + EditorWindow previewWindow = getPreviewWindow(); if (previewWindow != null) { wndToOpenIn = previewWindow; + focusEditor = true; } } @@ -658,31 +664,6 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec return openFileImpl2(wndToOpenIn, file, focusEditor); } - @Nullable - private EditorWindow getPreviewWindow(@NotNull VirtualFile virtualFile, final boolean focusEditor, final boolean searchForSplitter) { - EditorWindow wndToOpenIn = null; - if (Registry.is("editor.use.preview") && !myPreviewBlocker.get()) { - wndToOpenIn = myPreviewPanel.getWindow(); - if (virtualFile.equals(myPreviewPanel.getCurrentFile())) return wndToOpenIn; - final VirtualFile modifiedFile = myPreviewPanel.closeCurrentFile(); - ToolWindowManager.getInstance(myProject).getToolWindow(ToolWindowId.PREVIEW).activate(null, false); - if (modifiedFile != null) { - CommandProcessor.getInstance().executeCommand(myProject, new Runnable() { - @Override - public void run() { - myPreviewBlocker.set(true); - try { - openFileWithProviders(modifiedFile, focusEditor, searchForSplitter); - } finally { - myPreviewBlocker.set(false); - } - } - }, "", null); - } - } - return wndToOpenIn; - } - public Pair openFileInNewWindow(@NotNull VirtualFile file) { return ((DockManagerImpl)DockManager.getInstance(getProject())).createNewDockContainerFor(file, this); } @@ -805,7 +786,7 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec final FileEditorProvider provider = newProviders[i]; LOG.assertTrue(provider != null, "Provider for file "+file+" is null. All providers: "+Arrays.asList(newProviders)); LOG.assertTrue(provider.accept(myProject, file), "Provider " + provider + " doesn't accept file " + file); - if ((provider instanceof AsyncFileEditorProvider)) { + if (provider instanceof AsyncFileEditorProvider) { builders[i] = ((AsyncFileEditorProvider)provider).createEditorAsync(myProject, file); } } @@ -944,7 +925,7 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec return Pair.create(compositeRef.get().getEditors(), compositeRef.get().getProviders()); } - private void clearWindowIfNeeded(EditorWindow window) { + private static void clearWindowIfNeeded(EditorWindow window) { if (UISettings.getInstance().EDITOR_TAB_PLACEMENT == UISettings.TABS_NONE || UISettings.getInstance().PRESENTATION_MODE) { for (EditorWithProviderComposite composite : window.getEditors()) { Disposer.dispose(composite); @@ -1177,9 +1158,15 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec @Override public Editor getSelectedTextEditor() { - assertReadAccess(); + return getSelectedTextEditor(false); + } - final EditorWindow currentWindow = getSplitters().getCurrentWindow(); + public Editor getSelectedTextEditor(boolean lockfree) { + if (!lockfree) { + assertDispatchThread(); + } + + final EditorWindow currentWindow = lockfree ? getMainSplitters().getCurrentWindow() : getSplitters().getCurrentWindow(); if (currentWindow != null) { final EditorWithProviderComposite selectedEditor = currentWindow.getSelectedEditor(); if (selectedEditor != null && selectedEditor.getSelectedEditor() instanceof TextEditor) { @@ -1191,6 +1178,7 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec } + @Override public boolean isFileOpen(@NotNull final VirtualFile file) { return !getEditorComposites(file).isEmpty(); @@ -1760,7 +1748,7 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec private final class MyEditorPropertyChangeListener implements PropertyChangeListener { @Override - public void propertyChange(final PropertyChangeEvent e) { + public void propertyChange(@NotNull final PropertyChangeEvent e) { assertDispatchThread(); final String propertyName = e.getPropertyName(); @@ -1999,6 +1987,7 @@ public class FileEditorManagerImpl extends FileEditorManagerEx implements Projec mySelectionHistory.remove(Pair.create(file, window)); } + @NotNull @Override public ActionCallback getReady(@NotNull Object requestor) { return myBusyObject.getReady(requestor); diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/IdeDocumentHistoryImpl.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/IdeDocumentHistoryImpl.java index 752ab1228c3e..0e4f057dbeaf 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/IdeDocumentHistoryImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/IdeDocumentHistoryImpl.java @@ -145,16 +145,12 @@ public class IdeDocumentHistoryImpl extends IdeDocumentHistory implements Projec } public static class RecentlyChangedFilesState { - @Transient private List CHANGED_PATHS = new ArrayList(); + @Transient private final List CHANGED_PATHS = new ArrayList(); public List getChangedFiles() { return CHANGED_PATHS; } - public void setChangedFiles(List changed) { - CHANGED_PATHS = changed; - } - public void register(VirtualFile file) { final String path = file.getPath(); CHANGED_PATHS.remove(path); @@ -275,7 +271,7 @@ public class IdeDocumentHistoryImpl extends IdeDocumentHistory implements Projec myCurrentChangePlace = placeInfo; if (!myChangePlaces.isEmpty()) { - final PlaceInfo lastInfo = myChangePlaces.get(myChangePlaces.size() - 1); + final PlaceInfo lastInfo = myChangePlaces.getLast(); if (isSame(placeInfo, lastInfo)) { myChangePlaces.removeLast(); } @@ -430,7 +426,7 @@ public class IdeDocumentHistoryImpl extends IdeDocumentHistory implements Projec } } - private static boolean removeInvalidFilesFrom(final LinkedList backPlaces) { + private static boolean removeInvalidFilesFrom(@NotNull List backPlaces) { boolean removed = false; for (Iterator iterator = backPlaces.iterator(); iterator.hasNext();) { PlaceInfo info = iterator.next(); @@ -456,7 +452,7 @@ public class IdeDocumentHistoryImpl extends IdeDocumentHistory implements Projec myEditorManager.setSelectedEditor(info.getFile(), info.getEditorTypeId()); - final FileEditor [] editors = editorsWithProviders.getFirst(); + final FileEditor[] editors = editorsWithProviders.getFirst(); final FileEditorProvider[] providers = editorsWithProviders.getSecond(); for (int i = 0; i < editors.length; i++) { String typeId = providers [i].getEditorTypeId(); @@ -483,7 +479,7 @@ public class IdeDocumentHistoryImpl extends IdeDocumentHistory implements Projec return new PlaceInfo(file, state, fileProvider.getEditorTypeId(), myEditorManager.getCurrentWindow()); } - private static void clearPlaceList(LinkedList list) { + private static void clearPlaceList(@NotNull List list) { list.clear(); } @@ -494,9 +490,9 @@ public class IdeDocumentHistoryImpl extends IdeDocumentHistory implements Projec return "IdeDocumentHistory"; } - private static void putLastOrMerge(LinkedList list, PlaceInfo next, int limitSizeLimit) { + private static void putLastOrMerge(@NotNull LinkedList list, @NotNull PlaceInfo next, int limitSizeLimit) { if (!list.isEmpty()) { - PlaceInfo prev = list.get(list.size() - 1); + PlaceInfo prev = list.getLast(); if (isSame(prev, next)) { list.removeLast(); } @@ -522,7 +518,7 @@ public class IdeDocumentHistoryImpl extends IdeDocumentHistory implements Projec private final String myEditorTypeId; private final WeakReference myWindow; - public PlaceInfo(@NotNull VirtualFile file, FileEditorState navigationState, String editorTypeId, @Nullable EditorWindow window) { + public PlaceInfo(@NotNull VirtualFile file, @NotNull FileEditorState navigationState, @NotNull String editorTypeId, @Nullable EditorWindow window) { myNavigationState = navigationState; myFile = file; myEditorTypeId = editorTypeId; @@ -533,6 +529,7 @@ public class IdeDocumentHistoryImpl extends IdeDocumentHistory implements Projec return myWindow.get(); } + @NotNull public FileEditorState getNavigationState() { return myNavigationState; } @@ -542,24 +539,23 @@ public class IdeDocumentHistoryImpl extends IdeDocumentHistory implements Projec return myFile; } + @NotNull public String getEditorTypeId() { return myEditorTypeId; } + @Override public String toString() { return getFile().getName() + " " + getNavigationState(); } } - public LinkedList getBackPlaces() { + @NotNull + public List getBackPlaces() { return myBackPlaces; } - public LinkedList getForwardPlaces() { - return myForwardPlaces; - } - @Override public final void initComponent() { } @@ -572,7 +568,7 @@ public class IdeDocumentHistoryImpl extends IdeDocumentHistory implements Projec myCmdProcessor.executeCommand(myProject, runnable, name, groupId); } - private static boolean isSame(PlaceInfo first, PlaceInfo second) { + private static boolean isSame(@NotNull PlaceInfo first, @NotNull PlaceInfo second) { if (first.getFile().equals(second.getFile())) { FileEditorState firstState = first.getNavigationState(); FileEditorState secondState = second.getNavigationState(); diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/PreviewPanel.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/PreviewPanel.java index a9634d5a335a..999fe75523e9 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/PreviewPanel.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/PreviewPanel.java @@ -17,162 +17,276 @@ package com.intellij.openapi.fileEditor.impl; import com.intellij.icons.AllIcons; import com.intellij.ide.ui.UISettings; +import com.intellij.ide.ui.UISettingsListener; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.event.DocumentEvent; -import com.intellij.openapi.editor.event.DocumentListener; -import com.intellij.openapi.fileEditor.FileDocumentManager; +import com.intellij.openapi.actionSystem.DefaultActionGroup; +import com.intellij.openapi.actionSystem.ToggleAction; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.fileEditor.FileEditorManagerListener; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Key; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.openapi.wm.ToolWindowAnchor; -import com.intellij.openapi.wm.ToolWindowId; -import com.intellij.openapi.wm.ToolWindowManager; +import com.intellij.openapi.wm.*; import com.intellij.openapi.wm.impl.ToolWindowImpl; -import com.intellij.ui.JBColor; +import com.intellij.openapi.wm.impl.content.ToolWindowContentUi; +import com.intellij.ui.content.Content; +import com.intellij.ui.content.ContentManager; +import com.intellij.ui.content.ContentManagerAdapter; +import com.intellij.ui.content.ContentManagerEvent; import com.intellij.ui.docking.DockManager; +import com.intellij.util.ArrayUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; import java.util.ArrayList; +import java.util.EnumSet; -class PreviewPanel extends JPanel implements DocumentListener, FileEditorManagerListener.Before { +class PreviewPanel extends JPanel { + + private CardLayout myLayout; + + enum ContentType {Files, Usages, Diagrams, Documentation} + + private static final Key FILE_KEY = Key.create("v_file"); private static final int HISTORY_LIMIT = 10; private final Project myProject; private final FileEditorManagerImpl myManager; private final DockManager myDockManager; private EditorWindow myWindow; - private boolean myInitialized = false; private EditorsSplitters myEditorsSplitters; private ArrayList myHistory = new ArrayList(); private VirtualFile myModifiedFile = null; private ToolWindowImpl myToolWindow; private VirtualFile myAwaitingForOpen = null; + private ContentManager myContentManager; + private Content myStubContent; + private boolean myBlocked = false; + + private EnumSet myTypes = EnumSet.noneOf(ContentType.class); + + static boolean isAvailable() { + return UISettings.getInstance().NAVIGATE_TO_PREVIEW; + } - public PreviewPanel(Project project, FileEditorManagerImpl manager, DockManager dockManager) { + PreviewPanel(Project project, FileEditorManagerImpl manager, DockManager dockManager) { myProject = project; myManager = manager; myDockManager = dockManager; - setOpaque(true); - setBackground(JBColor.DARK_GRAY); + } + + /* + * @return null if preview is not avalable + */ + @Nullable + EditorWindow getWindow() { + if (!isAvailable() || isBlocked() || myProject.isDisposed()) return null; + initToolWindowIfNeed(); + return myWindow; + } + + boolean isBlocked() { + return myBlocked; } private void initToolWindowIfNeed() { - if (myInitialized) return; + if (!isAvailable() || ToolWindowManager.getInstance(myProject).getToolWindow(ToolWindowId.PREVIEW) != null) return; myToolWindow = (ToolWindowImpl)ToolWindowManager.getInstance(myProject) .registerToolWindow(ToolWindowId.PREVIEW, this, ToolWindowAnchor.RIGHT, myProject, false); - myToolWindow.setIcon(AllIcons.Actions.PreviewDetails); - - myEditorsSplitters = new EditorsSplitters(myManager, myDockManager, false) { + UISettings.getInstance().addUISettingsListener(new UISettingsListener() { @Override - public void updateFileName(VirtualFile updatedFile) { - super.updateFileName(updatedFile); - if (updatedFile != null && updatedFile.equals(getCurrentFile())) { - updateWindowTitle(updatedFile); - } - } - - @Override - protected void afterFileOpen(VirtualFile file) { - if (file.equals(myAwaitingForOpen)) { - updateWindowTitle(file); - Document document = FileDocumentManager.getInstance().getDocument(file); - if (document != null) { - myModifiedFile = null; - document.addDocumentListener(PreviewPanel.this, myProject); + public void uiSettingsChanged(UISettings source) { + if (!isAvailable()) { + VirtualFile[] files = myWindow.getFiles(); + for (VirtualFile file : files) { + close(file); } + ToolWindowManager.getInstance(myProject).unregisterToolWindow(ToolWindowId.PREVIEW); } - myAwaitingForOpen = null; } - + }, myProject); + myToolWindow.setIcon(AllIcons.Actions.PreviewDetails); + myToolWindow.setContentUiType(ToolWindowContentUiType.COMBO, null); + myContentManager = myToolWindow.getContentManager(); + myStubContent = myContentManager.getContent(0); + myContentManager.addContentManagerListener(new ContentManagerAdapter() { @Override - public void setTabsPlacement(int tabPlacement) { - super.setTabsPlacement(UISettings.TABS_NONE); - } + public void selectionChanged(ContentManagerEvent event) { + final VirtualFile file = event.getContent().getUserData(FILE_KEY); + if (event.getOperation() == ContentManagerEvent.ContentOperation.remove && file != null && file.equals(myModifiedFile)) { + close(file); + return; + } - @Override - protected boolean showEmptyText() { - return false; + if (event.getOperation() != ContentManagerEvent.ContentOperation.add) return; + + if (file != null) { + event.getContent().setComponent(PreviewPanel.this);//Actually we share the same component between contents + if (!file.equals(myWindow.getSelectedFile())) { + ApplicationManager.getApplication().invokeLater(new Runnable() { + @Override + public void run() { + myManager.openFileWithProviders(file, false, myWindow); + } + }); + } + } } - }; - - myProject.getMessageBus().connect().subscribe(FileEditorManagerListener.Before.FILE_EDITOR_MANAGER, this); + }); + + myEditorsSplitters = new MyEditorsSplitters(); + + myProject.getMessageBus().connect().subscribe(FileEditorManagerListener.Before.FILE_EDITOR_MANAGER, + new FileEditorManagerListener.Before() { + @Override + public void beforeFileOpened(@NotNull FileEditorManager source, + @NotNull VirtualFile file) { + myAwaitingForOpen = file; + VirtualFile currentFile = getCurrentFile(); + if (currentFile != null && + currentFile.equals(myModifiedFile) && + !currentFile.equals(file)) { + close(currentFile); + } + } + + @Override + public void beforeFileClosed(@NotNull FileEditorManager source, + @NotNull VirtualFile file) { + checkStubContent(); + } + }); myEditorsSplitters.createCurrentWindow(); - myWindow = myEditorsSplitters.getCurrentWindow(); myWindow.setTabsPlacement(UISettings.TABS_NONE); + myLayout = new CardLayout(); + setLayout(myLayout); + add(ContentType.Files.toString(), myEditorsSplitters); + //add(ContentType.Usages.toString(), myUsagesPreview);??? tree or editor ??? + //add(ContentType.Diagrams.toString(), myDiagramPanel); + //add(ContentType.Documentation.toString(), myDocumentationPanel);//todo + myToolWindow.setTitleActions(new MoveToEditorTabsAction()); + ArrayList myGearActions = new ArrayList(); + for (ContentType contentType : ContentType.values()) { + myGearActions.add(new ContentTypeToggleAction(contentType)); + } + myToolWindow.setAdditionalGearActions(new DefaultActionGroup("Preview", myGearActions)); + myToolWindow.hide(null); + } - setLayout(new GridLayout(1, 1)); - add(myEditorsSplitters); - - myToolWindow.setTitleActions(new MoveToEditorTabsAction(), new CloseFileAction()); - - myInitialized = true; + @Nullable + private VirtualFile getCurrentFile() { + VirtualFile[] files = myWindow.getFiles(); + return files.length == 1 ? files[0] : null; } - private void updateWindowTitle(VirtualFile file) { - if (myToolWindow == null) return; - if (file == null) { - myToolWindow.setTitle(": (empty)"); - } - else { - myToolWindow.setTitle(": " + - StringUtil.getShortened(EditorTabbedContainer.calcTabTitle(myProject, file), - UISettings.getInstance().EDITOR_TAB_TITLE_LIMIT)); + @NotNull + private Content addContent(VirtualFile file) { + myHistory.add(file); + while (myHistory.size() > HISTORY_LIMIT) { + myHistory.remove(0); } + String title = + StringUtil.getShortened(EditorTabbedContainer.calcTabTitle(myProject, file), UISettings.getInstance().EDITOR_TAB_TITLE_LIMIT); + + Content content = myContentManager.getFactory().createContent(this, title, false); + content.putUserData(ToolWindow.SHOW_CONTENT_ICON, Boolean.TRUE); + content.putUserData(FILE_KEY, file); + content.setIcon(file.getFileType().getIcon()); + content.setPopupIcon(file.getFileType().getIcon()); + + myContentManager.addContent(content, 0); + checkStubContent(); + return content; } - @Override - public void beforeFileOpened(@NotNull FileEditorManager source, @NotNull VirtualFile file) { - myAwaitingForOpen = file; + private void setSelected(VirtualFile file) { + Content content = getContent(file); + if (content == null) { + content = addContent(file); + } + myContentManager.setSelectedContent(content); + myContentManager.addContent(content, 0); } - @Override - public void beforeFileClosed(@NotNull FileEditorManager source, @NotNull VirtualFile file) { - if (file.equals(getCurrentFile())) { - updateWindowTitle(null); - Document document = FileDocumentManager.getInstance().getDocument(file); - if (document != null) { - document.removeDocumentListener(this); + @Nullable + private Content getContent(VirtualFile file) { + Content[] contents = myContentManager.getContents(); + for (Content content : contents) { + if (file.equals(content.getUserData(FILE_KEY))) { + return content; } } + return null; } - - @Override - public void beforeDocumentChange(DocumentEvent event) { - - } - - @Override - public void documentChanged(DocumentEvent event) { - VirtualFile file = FileDocumentManager.getInstance().getFile(event.getDocument()); - if (file != null) { - myModifiedFile = file; + private void checkStubContent() { + if (myContentManager.getContents().length == 0) { + myToolWindow.getComponent().putClientProperty(ToolWindowContentUi.HIDE_ID_LABEL, "false"); + myStubContent.setComponent(this); + myContentManager.addContent(myStubContent); + ApplicationManager.getApplication().invokeLater(new Runnable() { + @Override + public void run() { + if (myContentManager.getIndexOfContent(myStubContent) != -1) { + toggleToolWindow(false); + } + } + }); + } + else if (myContentManager.getContents().length > 1) { + myToolWindow.getComponent().putClientProperty(ToolWindowContentUi.HIDE_ID_LABEL, "true"); + myContentManager.removeContent(myStubContent, false); } } - EditorWindow getWindow() { - initToolWindowIfNeed(); - return myWindow; + private void close(@NotNull VirtualFile file) { + myHistory.remove(file); + if (ArrayUtil.find(myEditorsSplitters.getOpenFiles(), file) != -1) { + myEditorsSplitters.closeFile(file, false); + } + if (file.equals(myAwaitingForOpen)) { + myAwaitingForOpen = null; + } + if (file.equals(myModifiedFile)) { + myBlocked = true; + try { + myManager.openFileWithProviders(myModifiedFile, false, true); + } + finally { + myBlocked = false; + } + myModifiedFile = null; + } + Content content = getContent(file); + if (content != null) { + myContentManager.removeContent(content, false); + checkStubContent(); + } } - @Nullable - VirtualFile getCurrentFile() { - VirtualFile[] files = myWindow.getFiles(); - return files.length == 1 ? files[0] : null; + private void toggleToolWindow(boolean activate) { + ToolWindow toolWindow = ToolWindowManager.getInstance(myProject).getToolWindow(ToolWindowId.PREVIEW); + if (toolWindow != null) { + if (activate) { + toolWindow.activate(null, false); + } + else { + toolWindow.hide(null); + } + } } private class MoveToEditorTabsAction extends AnAction { + public MoveToEditorTabsAction() { - super(null, "Move to main tabs", AllIcons.Duplicates.SendToTheLeftGrayed); + super("Move to main tabs", "Move to main tabs", AllIcons.Duplicates.SendToTheLeftGrayed); } @Override @@ -182,63 +296,96 @@ class PreviewPanel extends JPanel implements DocumentListener, FileEditorManager return; } - myManager.openFileWithProviders(virtualFile, false, myManager.getCurrentWindow()); - closeCurrentFile(); + EditorWindow window = myManager.getCurrentWindow(); + if (window == null) { //main tab set is still not created, rare situation + myManager.getMainSplitters().createCurrentWindow(); + window = myManager.getCurrentWindow(); + } + myManager.openFileWithProviders(virtualFile, true, window); + close(virtualFile); + toggleToolWindow(false); + } + } + + private class ContentTypeToggleAction extends ToggleAction { + private final ContentType myContentType; + + ContentTypeToggleAction(ContentType contentType) { + super(contentType.toString()); + myContentType = contentType; } @Override - public void update(AnActionEvent e) { - super.update(e); - VirtualFile currentFile = getCurrentFile(); - e.getPresentation().setEnabled(currentFile != null); - if (currentFile == null) return; - - if (isModified(currentFile)) { - e.getPresentation().setIcon(AllIcons.Duplicates.SendToTheLeft); - } - else { - e.getPresentation().setIcon(AllIcons.Duplicates.SendToTheLeftGrayed); + public boolean isSelected(AnActionEvent e) { + return myTypes.contains(myContentType); + } + + @Override + public void setSelected(AnActionEvent e, boolean state) { + if (state) { + myTypes.add(myContentType); + } else { + myTypes.remove(myContentType); } } } - private boolean isModified(@NotNull VirtualFile file) { - return file.equals(myModifiedFile); - } + private class MyEditorsSplitters extends EditorsSplitters { + public MyEditorsSplitters() { + super(myManager, myDockManager, false); + } - //returns last open file if it has "modified" status - @Nullable - VirtualFile closeCurrentFile() { - VirtualFile virtualFile = getCurrentFile(); - if (virtualFile == null) return null; - if (!myHistory.contains(virtualFile)) { - myHistory.add(virtualFile); - while (myHistory.size() > HISTORY_LIMIT) { - myHistory.remove(0); + @Override + protected void afterFileOpen(VirtualFile file) { + if (file.equals(myAwaitingForOpen)) { + setSelected(file); } + myAwaitingForOpen = null; + } + + @Override + protected void afterFileClosed(VirtualFile file) { + close(file); } - myWindow.closeFile(virtualFile); - this.revalidate(); - this.repaint(); - return isModified(virtualFile) ? virtualFile : null; - } - private class CloseFileAction extends AnAction { - public CloseFileAction() { - super(null, "Close", AllIcons.Actions.Close); + @Override + public void updateFileIcon(@NotNull VirtualFile file) { + EditorWithProviderComposite composite = myWindow.findFileComposite(file); + if (composite != null && composite.isModified()) { + myModifiedFile = file; + } } @Override - public void actionPerformed(AnActionEvent e) { - if (getCurrentFile() == null) return; - closeCurrentFile(); - ToolWindowManager.getInstance(myProject).getToolWindow(ToolWindowId.PREVIEW).hide(null); + protected EditorWindow createEditorWindow() { + return new EditorWindow(this) { + @Override + protected void onBeforeSetEditor(VirtualFile file) { + VirtualFile currentFile = getCurrentFile(); + if (currentFile != null && currentFile.equals(myModifiedFile)) { + myBlocked = true; + try { + myManager.openFileWithProviders(myModifiedFile, false, true); + } + finally { + myBlocked = false; + } + } + else { + toggleToolWindow(true); + } + } + }; + } + + @Override + public void setTabsPlacement(int tabPlacement) { + super.setTabsPlacement(UISettings.TABS_NONE); } @Override - public void update(AnActionEvent e) { - super.update(e); - e.getPresentation().setEnabled(getCurrentFile() != null); + public boolean isPreview() { + return true; } } } diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/http/HttpFileEditor.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/http/HttpFileEditor.java index 00f333ef4002..a676023b6590 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/http/HttpFileEditor.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/http/HttpFileEditor.java @@ -20,6 +20,7 @@ import com.intellij.openapi.fileEditor.TextEditor; import com.intellij.openapi.fileEditor.impl.BaseRemoteFileEditor; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.impl.http.HttpVirtualFile; +import com.intellij.openapi.vfs.impl.http.RemoteFileInfo; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -28,14 +29,16 @@ import javax.swing.*; /** * @author nik */ -public class HttpFileEditor extends BaseRemoteFileEditor { +class HttpFileEditor extends BaseRemoteFileEditor { private final RemoteFilePanel myPanel; public HttpFileEditor(@NotNull Project project, @NotNull HttpVirtualFile virtualFile) { super(project); myPanel = new RemoteFilePanel(project, virtualFile, this); - virtualFile.getFileInfo().download().doWhenDone(new Runnable() { + RemoteFileInfo fileInfo = virtualFile.getFileInfo(); + assert fileInfo != null; + fileInfo.download().doWhenDone(new Runnable() { @Override public void run() { ApplicationManager.getApplication().invokeLater(new Runnable() { diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/http/HttpFileEditorProvider.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/http/HttpFileEditorProvider.java index 17354c7f4855..22234ab66a74 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/http/HttpFileEditorProvider.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/http/HttpFileEditorProvider.java @@ -31,7 +31,7 @@ import org.jetbrains.annotations.NotNull; /** * @author nik */ -public class HttpFileEditorProvider implements FileEditorProvider, DumbAware { +class HttpFileEditorProvider implements FileEditorProvider, DumbAware { @Override public boolean accept(@NotNull final Project project, @NotNull final VirtualFile file) { return file instanceof HttpVirtualFile && !file.isDirectory(); diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/http/RemoteFilePanel.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/http/RemoteFilePanel.java index 076d29e64bbe..4bd5549d85ff 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/http/RemoteFilePanel.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/http/RemoteFilePanel.java @@ -83,6 +83,7 @@ public class RemoteFilePanel { final RemoteFileInfo remoteFileInfo = virtualFile.getFileInfo(); myDownloadingListener = new MyDownloadingListener(); + assert remoteFileInfo != null; remoteFileInfo.addDownloadingListener(myDownloadingListener); myCancelButton.addActionListener(new ActionListener() { @Override diff --git a/platform/platform-impl/src/com/intellij/openapi/fileTypes/impl/FileTypeManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/fileTypes/impl/FileTypeManagerImpl.java index 837626b53246..01d278302540 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileTypes/impl/FileTypeManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileTypes/impl/FileTypeManagerImpl.java @@ -209,7 +209,7 @@ public class FileTypeManagerImpl extends FileTypeManagerEx implements NamedJDOME } @Override - public Document writeScheme(@NotNull final AbstractFileType fileType) throws WriteExternalException { + public Element writeScheme(@NotNull final AbstractFileType fileType) throws WriteExternalException { Element root = new Element(ELEMENT_FILETYPE); writeHeader(root, fileType); @@ -226,7 +226,7 @@ public class FileTypeManagerImpl extends FileTypeManagerEx implements NamedJDOME writeExtensionsMap(map, fileType, false); } - return new Document(root); + return root; } @Override @@ -263,7 +263,7 @@ public class FileTypeManagerImpl extends FileTypeManagerEx implements NamedJDOME }); } - private final TransferToPooledThreadQueue> reDetectQueue = new TransferToPooledThreadQueue>("File type re-detect", Condition.FALSE, -1, new Processor>() { + private final TransferToPooledThreadQueue> reDetectQueue = new TransferToPooledThreadQueue>("File type re-detect", Conditions.alwaysFalse(), -1, new Processor>() { @Override public boolean process(Collection files) { reDetect(files); diff --git a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/IdeKeyEventDispatcher.java b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/IdeKeyEventDispatcher.java index 9a566e9915ef..72e24d626535 100644 --- a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/IdeKeyEventDispatcher.java +++ b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/IdeKeyEventDispatcher.java @@ -55,6 +55,7 @@ import com.intellij.ui.ComponentWithMnemonics; import com.intellij.ui.SimpleTextAttributes; import com.intellij.ui.components.JBOptionButton; import com.intellij.ui.popup.list.ListPopupImpl; +import com.intellij.ui.speedSearch.SpeedSearchSupply; import com.intellij.util.Alarm; import com.intellij.util.Processor; import com.intellij.util.containers.ContainerUtil; @@ -140,6 +141,10 @@ public final class IdeKeyEventDispatcher implements Disposable { return false; } + if (isSpeedSearchEditing(e)) { + return false; + } + // http://www.jetbrains.net/jira/browse/IDEADEV-12372 if (e.getKeyCode() == KeyEvent.VK_CONTROL) { if (e.getID() == KeyEvent.KEY_PRESSED) { @@ -226,6 +231,18 @@ public final class IdeKeyEventDispatcher implements Disposable { } } + private static boolean isSpeedSearchEditing(KeyEvent e) { + int keyCode = e.getKeyCode(); + if (keyCode == KeyEvent.VK_BACK_SPACE) { + Component owner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner(); + if (owner instanceof JComponent) { + SpeedSearchSupply supply = SpeedSearchSupply.getSupply((JComponent)owner); + return supply != null && supply.isPopupActive(); + } + } + return false; + } + /** * @return true if and only if the component represents * modal context. diff --git a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/KeymapManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/KeymapManagerImpl.java index 7c30e4faedec..177aa4babad1 100644 --- a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/KeymapManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/KeymapManagerImpl.java @@ -44,11 +44,7 @@ import java.util.*; @State( name = "KeymapManager", - roamingType = RoamingType.PER_PLATFORM, - storages = { - @Storage( - file = StoragePathMacros.APP_CONFIG + "/keymap.xml" - )} + storages = {@Storage(file = StoragePathMacros.APP_CONFIG + "/keymap.xml", roamingType = RoamingType.PER_PLATFORM)} ) public class KeymapManagerImpl extends KeymapManagerEx implements PersistentStateComponent, ExportableApplicationComponent { private static final Logger LOG = Logger.getInstance("#com.intellij.keymap.KeymapManager"); @@ -75,8 +71,8 @@ public class KeymapManagerImpl extends KeymapManagerEx implements PersistentStat } @Override - public Document writeScheme(@NotNull final KeymapImpl scheme) throws WriteExternalException { - return new Document(scheme.writeExternal()); + public Element writeScheme(@NotNull final KeymapImpl scheme) throws WriteExternalException { + return scheme.writeExternal(); } @Override diff --git a/platform/platform-impl/src/com/intellij/openapi/options/SchemesManagerFactoryImpl.java b/platform/platform-impl/src/com/intellij/openapi/options/SchemesManagerFactoryImpl.java index 449a2180e3c1..c2f3b4d72b3e 100644 --- a/platform/platform-impl/src/com/intellij/openapi/options/SchemesManagerFactoryImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/options/SchemesManagerFactoryImpl.java @@ -28,8 +28,6 @@ import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import java.io.File; -import java.util.Collection; -import java.util.Collections; import java.util.List; public class SchemesManagerFactoryImpl extends SchemesManagerFactory implements SettingsSavingComponent { @@ -47,63 +45,10 @@ public class SchemesManagerFactoryImpl extends SchemesManagerFactory implements } IApplicationStore applicationStore = ((ApplicationImpl)application).getStateStore(); String baseDirPath = applicationStore.getStateStorageManager().expandMacros(fileSpec); - if (baseDirPath != null) { - StreamProvider provider = applicationStore.getStateStorageManager().getStreamProvider(); - SchemesManagerImpl manager = new SchemesManagerImpl(fileSpec, processor, roamingType, provider, new File(baseDirPath)); - myRegisteredManagers.add(manager); - return manager; - } - else { - return new AbstractSchemesManager() { - @Override - @NotNull - public Collection loadSchemes() { - return Collections.emptyList(); - } - - @Override - @NotNull - public Collection> loadSharedSchemes(final Collection currentSchemeList) { - return Collections.emptyList(); - } - - @Override - public void exportScheme(@NotNull final E scheme, final String name, final String description) { - } - - @Override - public boolean isImportAvailable() { - return false; - } - - @Override - public boolean isShared(final Scheme scheme) { - return false; - } - - @Override - public void save() { - } - - @Override - protected void onSchemeDeleted(final Scheme toDelete) { - } - - @Override - protected void onSchemeAdded(final T scheme) { - } - - @Override - public boolean isExportAvailable() { - return false; - } - - @Override - public File getRootDirectory() { - return null; - } - }; - } + StreamProvider provider = applicationStore.getStateStorageManager().getStreamProvider(); + SchemesManagerImpl manager = new SchemesManagerImpl(fileSpec, processor, roamingType, provider, new File(baseDirPath)); + myRegisteredManagers.add(manager); + return manager; } @Override diff --git a/platform/platform-impl/src/com/intellij/openapi/options/SchemesManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/options/SchemesManagerImpl.java index 4ff3ebba1ee9..6142b696ff24 100644 --- a/platform/platform-impl/src/com/intellij/openapi/options/SchemesManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/options/SchemesManagerImpl.java @@ -43,6 +43,7 @@ import gnu.trove.THashSet; import org.jdom.Document; import org.jdom.Element; import org.jdom.JDOMException; +import org.jdom.Parent; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -79,7 +80,7 @@ public class SchemesManagerImpl names = new THashSet(getAllSchemeNames(currentSchemeList)); Map> result = new THashMap>(); - for (String subPath : provider.listSubFiles(myFileSpec, RoamingType.GLOBAL)) { + for (String subPath : provider.listSubFiles(myFileSpec, getRoamingType(provider))) { try { - final Document subDocument = StorageUtil.loadDocument(provider.loadContent(getFileFullPath(subPath), RoamingType.GLOBAL)); + final Document subDocument = StorageUtil.loadDocument(provider.loadContent(getFileFullPath(subPath), getRoamingType(provider))); if (subDocument != null) { SharedSchemeData original = unwrap(subDocument); final E scheme = myProcessor.readScheme(original.original); @@ -669,6 +673,7 @@ public class SchemesManagerImpl idToConfigurable = new HashMap(); + final Map idToConfigurable = new LinkedHashMap(); for (ConfigurableEP ep : extensions) { final Configurable configurable = ConfigurableWrapper.wrapConfigurable(ep); if (isSuppressed(configurable, filter)) continue; @@ -75,16 +75,13 @@ public class ConfigurableExtensionPointUtil { } } } - //leave only roots (i.e. configurables without parents) - for (final Iterator iterator = idToConfigurable.keySet().iterator(); iterator.hasNext(); ) { - final String key = iterator.next(); - final ConfigurableWrapper wrapper = idToConfigurable.get(key); - final String parentId = wrapper.getParentId(); - if (parentId != null && idToConfigurable.containsKey(parentId)) { - iterator.remove(); // remove only processed parents + // add roots only (i.e. configurables without parents) + for (ConfigurableWrapper wrapper : idToConfigurable.values()) { + String parentId = wrapper.getParentId(); + if (parentId == null || !idToConfigurable.containsKey(parentId)) { + result.add(wrapper); } } - ContainerUtil.addAll(result, idToConfigurable.values()); return result; } @@ -164,7 +161,7 @@ public class ConfigurableExtensionPointUtil { @NotNull private static T findConfigurable(ConfigurableEP[] extensions, Class configurableClass) { for (ConfigurableEP extension : extensions) { - if (extension.providerClass != null || extension.instanceClass != null || extension.implementationClass != null) { + if (extension.canCreateConfigurable()) { final Configurable configurable = extension.createConfigurable(); if (configurableClass.isInstance(configurable)) { return configurableClass.cast(configurable); diff --git a/platform/platform-impl/src/com/intellij/openapi/options/ex/ConfigurableVisitor.java b/platform/platform-impl/src/com/intellij/openapi/options/ex/ConfigurableVisitor.java new file mode 100644 index 000000000000..7d820a255fb3 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/options/ex/ConfigurableVisitor.java @@ -0,0 +1,144 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.openapi.options.ex; + +import com.intellij.openapi.options.Configurable; +import com.intellij.openapi.options.ConfigurableGroup; +import com.intellij.openapi.options.SearchableConfigurable; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Sergey.Malenkov + */ +public abstract class ConfigurableVisitor { + public static final ConfigurableVisitor ALL = new ConfigurableVisitor() { + @Override + protected boolean accept(Configurable configurable) { + return true; + } + }; + + protected abstract boolean accept(Configurable configurable); + + public final Configurable find(@NotNull ConfigurableGroup... groups) { + for (ConfigurableGroup group : groups) { + Configurable result = find(group.getConfigurables()); + if (result != null) { + return result; + } + } + return null; + } + + public final Configurable find(@NotNull Configurable... configurables) { + for (Configurable configurable : configurables) { + if (accept(configurable)) { + return configurable; + } + } + for (Configurable configurable : configurables) { + if (configurable instanceof Configurable.Composite) { + Configurable.Composite composite = (Configurable.Composite)configurable; + Configurable result = find(composite.getConfigurables()); + if (result != null) { + return result; + } + } + } + return null; + } + + public final List findAll(@NotNull ConfigurableGroup... groups) { + List list = new ArrayList(); + for (ConfigurableGroup group : groups) { + add(list, group.getConfigurables()); + } + return list; + } + + public final List findAll(@NotNull Configurable... configurables) { + List list = new ArrayList(); + add(list, configurables); + return list; + } + + private void add(List list, Configurable... configurables) { + for (Configurable configurable : configurables) { + if (accept(configurable)) { + list.add(configurable); + } + if (configurable instanceof Configurable.Composite) { + Configurable.Composite composite = (Configurable.Composite)configurable; + add(list, composite.getConfigurables()); + } + } + } + + public static final class ByID extends ConfigurableVisitor { + private final String myID; + + public ByID(@NotNull String id) { + myID = id; + } + + @Override + protected boolean accept(Configurable configurable) { + return myID.equals(getID(configurable)); + } + + public static String getID(Configurable configurable) { + return configurable instanceof SearchableConfigurable + ? ((SearchableConfigurable)configurable).getId() + : configurable.getClass().getName(); + } + } + + public static final class ByName extends ConfigurableVisitor { + private final String myName; + + public ByName(@NotNull String name) { + myName = name; + } + + @Override + protected boolean accept(Configurable configurable) { + return myName.equals(configurable.getDisplayName()); + } + } + + public static final class ByType extends ConfigurableVisitor { + private final Class myType; + + public ByType(@NotNull Class type) { + myType = type; + } + + @Override + protected boolean accept(Configurable configurable) { + if (myType.isInstance(configurable)) { + return true; + } + if (configurable instanceof ConfigurableWrapper) { + ConfigurableWrapper wrapper = (ConfigurableWrapper)configurable; + return myType.isInstance(wrapper.getConfigurable()); + } + return false; + } + } +} diff --git a/platform/platform-impl/src/com/intellij/openapi/options/ex/ConfigurableWrapper.java b/platform/platform-impl/src/com/intellij/openapi/options/ex/ConfigurableWrapper.java index 3b67a3478368..dd8169e159a7 100644 --- a/platform/platform-impl/src/com/intellij/openapi/options/ex/ConfigurableWrapper.java +++ b/platform/platform-impl/src/com/intellij/openapi/options/ex/ConfigurableWrapper.java @@ -47,6 +47,9 @@ public class ConfigurableWrapper implements SearchableConfigurable { @Nullable public static T wrapConfigurable(ConfigurableEP ep) { + if (!ep.canCreateConfigurable()) { + return null; + } if (ep.displayName != null || ep.key != null || ep.groupId != null) { T configurable = null; if (ep.providerClass != null) { @@ -55,9 +58,9 @@ public class ConfigurableWrapper implements SearchableConfigurable { return null; // it is allowed to return null from provider } } - return ep.children != null || ep.childrenEPName != null || ep.dynamic - ? (T)new CompositeWrapper(ep, configurable) - : (T)new ConfigurableWrapper(ep, configurable); + return !ep.dynamic && ep.children == null && ep.childrenEPName == null + ? (T)new ConfigurableWrapper(ep, configurable) + : (T)new CompositeWrapper(ep, configurable); } else { return ep.createConfigurable(); @@ -157,7 +160,16 @@ public class ConfigurableWrapper implements SearchableConfigurable { @NotNull @Override public String getId() { - return myEp.id == null ? myEp.instanceClass == null ? myEp.providerClass : myEp.instanceClass : myEp.id; + if (myEp.id != null) { + return myEp.id; + } + UnnamedConfigurable configurable = getConfigurable(); + if (configurable instanceof SearchableConfigurable) { + return ((SearchableConfigurable)configurable).getId(); + } + return myEp.instanceClass != null + ? myEp.instanceClass + : myEp.providerClass; } @NotNull diff --git a/platform/platform-impl/src/com/intellij/openapi/options/ex/MixedConfigurableGroup.java b/platform/platform-impl/src/com/intellij/openapi/options/ex/MixedConfigurableGroup.java deleted file mode 100644 index e3404ec017cd..000000000000 --- a/platform/platform-impl/src/com/intellij/openapi/options/ex/MixedConfigurableGroup.java +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.intellij.openapi.options.ex; - -import com.intellij.openapi.options.Configurable; -import com.intellij.openapi.options.ConfigurableGroup; -import com.intellij.openapi.options.ConfigurationException; -import com.intellij.openapi.options.OptionsBundle; -import com.intellij.openapi.options.SearchableConfigurable; -import com.intellij.openapi.util.text.StringUtil; -import org.jetbrains.annotations.NotNull; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Comparator; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map.Entry; -import javax.swing.JComponent; - -public final class MixedConfigurableGroup implements SearchableConfigurable, ConfigurableGroup { - private final String myGroupId; - private Configurable[] myConfigurables; - - private MixedConfigurableGroup(String groupId, ArrayList configurables) { - myGroupId = groupId; - myConfigurables = (configurables != null) - ? configurables.toArray(new Configurable[configurables.size()]) - : new Configurable[0]; - Arrays.sort(myConfigurables, COMPARATOR); - } - - private MixedConfigurableGroup(String groupId, HashMap> configurables) { - this(groupId, configurables.remove(groupId)); - } - - @Override - public JComponent createComponent() { - return null; - } - - @Override - public boolean isModified() { - return false; - } - - @Override - public void apply() throws ConfigurationException { - } - - @Override - public void reset() { - } - - @Override - public void disposeUIResources() { - myConfigurables = null; - } - - @Override - public Runnable enableSearch(String option) { - return null; - } - - @NotNull - @Override - public String getId() { - return "configurable.group." + myGroupId; - } - - @Override - public String getHelpTopic() { - return "configurable.group." + myGroupId + ".help.topic"; - } - - @Override - public String getDisplayName() { - return OptionsBundle.message("configurable.group." + myGroupId + ".settings.display.name"); - } - - @Override - public String getShortName() { - return getDisplayName(); - } - - @Override - public Configurable[] getConfigurables() { - return myConfigurables; - } - - public static ConfigurableGroup[] getGroups(Configurable... configurables) { - HashMap> map = new HashMap>(); - for (Configurable configurable : configurables) { - String groupId = null; - if (configurable instanceof ConfigurableWrapper) { - groupId = ((ConfigurableWrapper)configurable).getExtensionPoint().groupId; - } - ArrayList list = map.get(groupId); - if (list == null) { - map.put(groupId, list = new ArrayList()); - } - list.add(configurable); - } - ArrayList buildList = map.get("build"); - if (buildList != null) { - NodeConfigurable buildTools = new NodeConfigurable("build.tools", 1000); - buildTools.add(find("MavenSettings", buildList.iterator())); - buildTools.add(find("reference.settingsdialog.project.gradle", buildList.iterator())); - buildTools.add(find("reference.settingsdialog.project.gant", buildList.iterator())); - if (buildTools.getConfigurables() != null) { - buildList.add(0, buildTools); - } - } - ArrayList groups = new ArrayList(map.size()); - groups.add(new MixedConfigurableGroup("appearance", map)); - groups.add(new MixedConfigurableGroup("editor", map)); - groups.add(new MixedConfigurableGroup("project", map)); - groups.add(new MixedConfigurableGroup("build", map)); - groups.add(new MixedConfigurableGroup("language", map)); - groups.add(new MixedConfigurableGroup("tools", map)); - ConfigurableGroup other = new MixedConfigurableGroup(null, map); - for(Entry>entry: map.entrySet()){ - groups.add(new MixedConfigurableGroup(entry.getKey(), entry.getValue())); - } - groups.add(other); - return groups.toArray(new ConfigurableGroup[groups.size()]); - } - - private static Configurable find(String id, Iterator iterator) { - while (iterator.hasNext()) { - Configurable configurable = iterator.next(); - if (configurable instanceof SearchableConfigurable) { - SearchableConfigurable sc = (SearchableConfigurable)configurable; - if (id.equals(sc.getId())) { - iterator.remove(); - return configurable; - } - } - } - return null; - } - - public static int getGroupWeight(Configurable configurable) { - if (configurable instanceof NodeConfigurable) { - return ((NodeConfigurable)configurable).getGroupWeight(); - } - if (configurable instanceof ConfigurableWrapper) { - return ((ConfigurableWrapper)configurable).getExtensionPoint().groupWeight; - } - return 0; - } - - private static final Comparator COMPARATOR = new Comparator() { - @Override - public int compare(Configurable configurable1, Configurable configurable2) { - if (configurable1 == null || configurable2 == null) { - return configurable2 != null ? -1 : configurable1 != null ? 1 : 0; - } - int weight1 = getGroupWeight(configurable1); - int weight2 = getGroupWeight(configurable2); - return weight1 > weight2 ? -1 : weight1 < weight2 ? 1 : StringUtil.naturalCompare(configurable1.getDisplayName(), - configurable2.getDisplayName()); - } - }; -} diff --git a/platform/platform-impl/src/com/intellij/openapi/options/ex/NodeConfigurable.java b/platform/platform-impl/src/com/intellij/openapi/options/ex/NodeConfigurable.java deleted file mode 100644 index 609943d02943..000000000000 --- a/platform/platform-impl/src/com/intellij/openapi/options/ex/NodeConfigurable.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.intellij.openapi.options.ex; - -import com.intellij.openapi.options.Configurable; -import com.intellij.openapi.options.OptionsBundle; -import com.intellij.openapi.options.SearchableConfigurable; -import org.jetbrains.annotations.Nls; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.ArrayList; - -public final class NodeConfigurable extends SearchableConfigurable.Parent.Abstract { - private final ArrayList myConfigurables = new ArrayList(); - private final String myId; - private final int myWeight; - - public NodeConfigurable(@NotNull String id, int weight) { - myId = id; - myWeight = weight; - } - - public int getGroupWeight() { - return myWeight; - } - - public void add(Configurable configurable) { - if (configurable != null) { - super.disposeUIResources(); - myConfigurables.add(configurable); - } - } - - @Override - public void disposeUIResources() { - super.disposeUIResources(); - myConfigurables.clear(); - } - - @NotNull - @Override - public String getId() { - return "node.configurable." + myId; - } - - @Nullable - @Override - public String getHelpTopic() { - return "node.configurable." + myId + ".help.topic"; - } - - @Nls - @Override - public String getDisplayName() { - return OptionsBundle.message("node.configurable." + myId + ".display.name"); - } - - @Override - protected Configurable[] buildConfigurables() { - int size = myConfigurables.size(); - return size == 0 ? null : myConfigurables.toArray(new Configurable[size]); - } -} diff --git a/platform/platform-impl/src/com/intellij/openapi/options/ex/SortedConfigurableGroup.java b/platform/platform-impl/src/com/intellij/openapi/options/ex/SortedConfigurableGroup.java new file mode 100644 index 000000000000..3d87b7679359 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/options/ex/SortedConfigurableGroup.java @@ -0,0 +1,195 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.openapi.options.ex; + +import com.intellij.openapi.options.Configurable; +import com.intellij.openapi.options.ConfigurableGroup; +import com.intellij.openapi.options.OptionsBundle; +import com.intellij.openapi.options.SearchableConfigurable; +import com.intellij.openapi.util.text.StringUtil; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; + +/** + * @author Sergey.Malenkov + */ +public final class SortedConfigurableGroup + extends SearchableConfigurable.Parent.Abstract + implements SearchableConfigurable, ConfigurableGroup, Configurable.NoScroll { + + public static ConfigurableGroup getGroup(Configurable... configurables) { + SortedConfigurableGroup root = new SortedConfigurableGroup("root"); + HashMap map = new HashMap(); + map.put("root", root); + for (Configurable configurable : configurables) { + int weight = 0; + String groupId = null; + if (configurable instanceof ConfigurableWrapper) { + ConfigurableWrapper wrapper = (ConfigurableWrapper)configurable; + weight = wrapper.getExtensionPoint().groupWeight; + groupId = wrapper.getExtensionPoint().groupId; + } + SortedConfigurableGroup composite = map.get(groupId); + if (composite == null) { + composite = new SortedConfigurableGroup(groupId); + map.put(groupId, composite); + } + composite.add(weight, configurable); + } + // process supported groups + root.add(60, map.remove("appearance")); + root.add(50, map.remove("editor")); + root.add(40, map.remove("project")); + SortedConfigurableGroup build = map.remove("build"); + if (build == null) { + build = map.remove("build.tools"); + } + else { + build.add(1000, map.remove("build.tools")); + } + root.add(30, build); + root.add(20, map.remove("language")); + root.add(10, map.remove("tools")); + root.add(-10, map.remove(null)); + // process unsupported groups + if (1 < map.size()) { + for (SortedConfigurableGroup group : map.values()) { + if (root != group) { + group.myDisplayName = "Category: " + group.myGroupId; + root.add(0, group); + } + } + } + return root; + } + + private final ArrayList myList = new ArrayList(); + private final String myGroupId; + private String myDisplayName; + + private SortedConfigurableGroup(String groupId) { + myGroupId = groupId; + } + + public SortedConfigurableGroup(Configurable... configurables) { + myGroupId = "root"; + // create groups from configurations + HashMap map = new HashMap(); + map.put(myGroupId, this); + for (Configurable configurable : configurables) { + int weight = 0; + String groupId = null; + if (configurable instanceof ConfigurableWrapper) { + ConfigurableWrapper wrapper = (ConfigurableWrapper)configurable; + weight = wrapper.getExtensionPoint().groupWeight; + groupId = wrapper.getExtensionPoint().groupId; + } + SortedConfigurableGroup composite = map.get(groupId); + if (composite == null) { + composite = new SortedConfigurableGroup(groupId); + map.put(groupId, composite); + } + composite.add(weight, configurable); + } + // process supported groups + add(60, map.remove("appearance")); + add(50, map.remove("editor")); + add(40, map.remove("project")); + SortedConfigurableGroup build = map.remove("build"); + if (build == null) { + build = map.remove("build.tools"); + } + else { + build.add(1000, map.remove("build.tools")); + } + add(30, build); + add(20, map.remove("language")); + add(10, map.remove("tools")); + add(-10, map.remove(null)); + // process unsupported groups + if (1 < map.size()) { + for (SortedConfigurableGroup group : map.values()) { + if (this != group) { + group.myDisplayName = "Category: " + group.myGroupId; + add(0, group); + } + } + } + } + + private void add(int weight, Configurable configurable) { + if (configurable != null) { + myList.add(new WeightConfigurable(configurable, weight)); + } + } + + @Override + protected Configurable[] buildConfigurables() { + Collections.sort(myList); + int length = myList.size(); + Configurable[] result = new Configurable[length]; + for (int i = 0; i < result.length; i++) { + result[i] = myList.get(i).myConfigurable; + } + myList.clear(); + return result; + } + + @NotNull + @Override + public String getId() { + return "configurable.group." + myGroupId; + } + + @Nullable + @Override + public String getHelpTopic() { + return "configurable.group." + myGroupId + ".help.topic"; + } + + @Nls + @Override + public String getDisplayName() { + return myDisplayName != null ? myDisplayName : OptionsBundle.message("configurable.group." + myGroupId + ".settings.display.name"); + } + + @Override + public String getShortName() { + return getDisplayName(); + } + + private static final class WeightConfigurable implements Comparable { + private final Configurable myConfigurable; + private final int myWeight; + + private WeightConfigurable(@NotNull Configurable configurable, int weight) { + myConfigurable = configurable; + myWeight = weight; + } + + @Override + public int compareTo(@NotNull WeightConfigurable pair) { + return myWeight > pair.myWeight ? -1 : + myWeight < pair.myWeight ? 1 : + StringUtil.naturalCompare(myConfigurable.getDisplayName(), pair.myConfigurable.getDisplayName()); + } + } +} diff --git a/platform/platform-impl/src/com/intellij/openapi/options/newEditor/IdeSettingsDialog.java b/platform/platform-impl/src/com/intellij/openapi/options/newEditor/IdeSettingsDialog.java index c38bf6ff2033..a995ea372d99 100644 --- a/platform/platform-impl/src/com/intellij/openapi/options/newEditor/IdeSettingsDialog.java +++ b/platform/platform-impl/src/com/intellij/openapi/options/newEditor/IdeSettingsDialog.java @@ -16,7 +16,6 @@ package com.intellij.openapi.options.newEditor; import com.intellij.CommonBundle; -import com.intellij.ide.ui.search.SearchUtil; import com.intellij.ide.util.PropertiesComponent; import com.intellij.openapi.actionSystem.DataProvider; import com.intellij.openapi.application.ApplicationManager; @@ -25,6 +24,7 @@ import com.intellij.openapi.options.Configurable; import com.intellij.openapi.options.ConfigurableGroup; import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.options.SearchableConfigurable; +import com.intellij.openapi.options.ex.ConfigurableVisitor; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.util.ActionCallback; @@ -45,7 +45,6 @@ import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; import java.util.Collection; -import java.util.List; import java.util.Map; /** @@ -114,7 +113,7 @@ public class IdeSettingsDialog extends DialogWrapper implements DataProvider { @Nullable private static Configurable getPreselectedByDisplayName(final ConfigurableGroup[] groups, final String preselectedConfigurableDisplayName, final Project project) { - Configurable result = findPreselectedByDisplayName(preselectedConfigurableDisplayName, groups); + Configurable result = new ConfigurableVisitor.ByName(preselectedConfigurableDisplayName).find(groups); return result == null ? findLastSavedConfigurable(groups, project) : result; } @@ -230,44 +229,7 @@ public class IdeSettingsDialog extends DialogWrapper implements DataProvider { final String id = PropertiesComponent.getInstance(project).getValue(LAST_SELECTED_CONFIGURABLE); if (id == null) return null; - return findConfigurableInGroups(id, groups); - } - - @Nullable - private static Configurable findConfigurableInGroups(String id, Configurable.Composite... groups) { - // avoid unnecessary group expand: check top-level configurables in all groups before looking at children - for (Configurable.Composite group : groups) { - final Configurable[] configurables = group.getConfigurables(); - for (Configurable c : configurables) { - if (c instanceof SearchableConfigurable && id.equals(((SearchableConfigurable)c).getId())) { - return c; - } - else if (id.equals(c.getClass().getName())) { - return c; - } - } - } - for (Configurable.Composite group : groups) { - final Configurable[] configurables = group.getConfigurables(); - for (Configurable c : configurables) { - if (c instanceof Configurable.Composite) { - Configurable result = findConfigurableInGroups(id, (Configurable.Composite)c); - if (result != null) { - return result; - } - } - } - } - return null; - } - - @Nullable - private static Configurable findPreselectedByDisplayName(final String preselectedConfigurableDisplayName, ConfigurableGroup[] groups) { - final List all = SearchUtil.expand(groups); - for (Configurable each : all) { - if (preselectedConfigurableDisplayName.equals(each.getDisplayName())) return each; - } - return null; + return new ConfigurableVisitor.ByID(id).find(groups); } @Override diff --git a/platform/platform-impl/src/com/intellij/openapi/options/newEditor/OptionsEditor.java b/platform/platform-impl/src/com/intellij/openapi/options/newEditor/OptionsEditor.java index c2119596a06e..c1e55a1419a2 100644 --- a/platform/platform-impl/src/com/intellij/openapi/options/newEditor/OptionsEditor.java +++ b/platform/platform-impl/src/com/intellij/openapi/options/newEditor/OptionsEditor.java @@ -17,7 +17,6 @@ package com.intellij.openapi.options.newEditor; import com.intellij.AbstractBundle; import com.intellij.CommonBundle; -import com.intellij.icons.AllIcons; import com.intellij.ide.ui.laf.darcula.ui.DarculaTextBorder; import com.intellij.ide.ui.laf.darcula.ui.DarculaTextFieldUI; import com.intellij.ide.ui.search.SearchUtil; @@ -1219,27 +1218,31 @@ public class OptionsEditor extends JPanel implements DataProvider, Place.Navigat * @return default view for the specified configurable */ private JComponent createDefaultComponent(SearchableConfigurable searchable) { - JPanel box = new JPanel(); - box.setLayout(new BoxLayout(box, BoxLayout.Y_AXIS)); + JPanel panel = new JPanel(new BorderLayout(0, 9)); try { - box.add(new JLabel(getDefaultDescription(searchable))); + panel.add(BorderLayout.NORTH, new JLabel(getDefaultDescription(searchable))); } catch (AssertionError error) { return null; // description is not set } if (searchable instanceof Configurable.Composite) { - box.add(Box.createVerticalStrut(10)); + JPanel box = new JPanel(); + box.setLayout(new BoxLayout(box, BoxLayout.Y_AXIS)); + panel.add(BorderLayout.CENTER, box); + Configurable.Composite composite = (Configurable.Composite)searchable; for (final Configurable configurable : composite.getConfigurables()) { - box.add(new LinkLabel(configurable.getDisplayName(), AllIcons.Ide.Link) { + LinkLabel label = new LinkLabel(configurable.getDisplayName(), null) { @Override public void doClick() { select(configurable, null); } - }); + }; + label.setBorder(BorderFactory.createEmptyBorder(1, 17, 1, 1)); + box.add(label); } } - return box; + return panel; } @NotNull diff --git a/platform/platform-impl/src/com/intellij/openapi/options/newEditor/OptionsTree.java b/platform/platform-impl/src/com/intellij/openapi/options/newEditor/OptionsTree.java index aee9e889fdf1..ed52a7f50bf6 100644 --- a/platform/platform-impl/src/com/intellij/openapi/options/newEditor/OptionsTree.java +++ b/platform/platform-impl/src/com/intellij/openapi/options/newEditor/OptionsTree.java @@ -23,7 +23,6 @@ import com.intellij.openapi.options.ConfigurableGroup; import com.intellij.openapi.options.OptionsBundle; import com.intellij.openapi.options.SearchableConfigurable; import com.intellij.openapi.options.ex.ConfigurableWrapper; -import com.intellij.openapi.options.ex.NodeConfigurable; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.ActionCallback; import com.intellij.openapi.util.Disposer; @@ -383,17 +382,6 @@ public class OptionsTree extends JPanel implements Disposable, OptionsEditorColl SimpleNode parent = base.getParent(); if (parent == myRoot) { project = getConfigurableProject(base); // show icon for top-level nodes - if (base.getConfigurable() instanceof NodeConfigurable) { // special case for custom subgroups (build.tools) - Configurable[] configurables = ((NodeConfigurable)base.getConfigurable()).getConfigurables(); - if (configurables != null) { // assume that all configurables have the same project - project = getConfigurableProject(configurables[0]); - } - } - } - else if (parent instanceof Base && ((Base)parent).getConfigurable() instanceof NodeConfigurable) { - if (((Base)base.getParent()).getConfigurable() instanceof NodeConfigurable) { - project = getConfigurableProject(base); // special case for custom subgroups - } } } if (project != null) { diff --git a/platform/platform-impl/src/com/intellij/openapi/options/newEditor/SettingsTreeView.java b/platform/platform-impl/src/com/intellij/openapi/options/newEditor/SettingsTreeView.java index e10367d583bf..817a042c9bc9 100644 --- a/platform/platform-impl/src/com/intellij/openapi/options/newEditor/SettingsTreeView.java +++ b/platform/platform-impl/src/com/intellij/openapi/options/newEditor/SettingsTreeView.java @@ -20,7 +20,7 @@ import com.intellij.ide.util.treeView.NodeDescriptor; import com.intellij.openapi.Disposable; import com.intellij.openapi.options.*; import com.intellij.openapi.options.ex.ConfigurableWrapper; -import com.intellij.openapi.options.ex.NodeConfigurable; +import com.intellij.openapi.options.ex.SortedConfigurableGroup; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.ActionCallback; import com.intellij.openapi.util.Disposer; @@ -49,6 +49,7 @@ import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; import javax.swing.plaf.TreeUI; import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.TreeCellRenderer; import javax.swing.tree.TreePath; import javax.swing.tree.TreeSelectionModel; import java.awt.*; @@ -63,6 +64,7 @@ import java.util.List; * @author Sergey.Malenkov */ final class SettingsTreeView extends JComponent implements Disposable, OptionsEditorColleague { + private static final String NODE_ICON = "settings.tree.view.icon"; private static final Color NORMAL_NODE = new JBColor(Gray._60, Gray._140); private static final Color WRONG_CONTENT = JBColor.RED; private static final Color MODIFIED_CONTENT = JBColor.BLUE; @@ -74,8 +76,9 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd private final MyRoot myRoot; private final JScrollPane myScroller; private JLabel mySeparator; - private final MyRenderer myRenderer = new MyRenderer(); private final IdentityHashMap myConfigurableToNodeMap = new IdentityHashMap(); + private final IdentityHashMap myConfigurableToWrapperMap + = new IdentityHashMap(); private final MergingUpdateQueue myQueue = new MergingUpdateQueue("SettingsTreeView", 150, false, this, this, this) .setRestartTimerOnAdd(true); @@ -96,9 +99,10 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd myTree.setRowHeight(-1); myTree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); - myTree.setCellRenderer(myRenderer); + myTree.setCellRenderer(new MyRenderer()); myTree.setRootVisible(false); myTree.setShowsRootHandles(false); + myTree.setExpandableItemsEnabled(false); myScroller = ScrollPaneFactory.createScrollPane(myTree, true); myScroller.getVerticalScrollBar().setUI(ButtonlessScrollBarUI.createTransparent()); @@ -139,7 +143,7 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd @NotNull String[] getPathNames(Configurable configurable) { ArrayDeque path = new ArrayDeque(); - MyNode node = myConfigurableToNodeMap.get(configurable); + MyNode node = findNode(configurable); while (node != null) { path.push(node.myDisplayName); SimpleNode parent = node.getParent(); @@ -157,8 +161,9 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd } @Nullable - SimpleNode findNode(Configurable configurable) { - return myConfigurableToNodeMap.get(configurable); + MyNode findNode(Configurable configurable) { + ConfigurableWrapper wrapper = myConfigurableToWrapperMap.get(configurable); + return myConfigurableToNodeMap.get(wrapper != null ? wrapper : configurable); } @Nullable @@ -180,6 +185,7 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd if (configurable instanceof ConfigurableWrapper) { ConfigurableWrapper wrapper = (ConfigurableWrapper)configurable; configurable = wrapper.getConfigurable(); + myConfigurableToWrapperMap.put(configurable, wrapper); } if (type.isInstance(configurable)) { return type.cast(configurable); @@ -194,7 +200,7 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd ConfigurableWrapper wrapper = (ConfigurableWrapper)configurable; return wrapper.getExtensionPoint().getProject(); } - return findConfigurableProject(myConfigurableToNodeMap.get(configurable)); + return findConfigurableProject(findNode(configurable)); } @Nullable @@ -214,15 +220,15 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd } @Nullable - private ConfigurableGroup findConfigurableGroupAt(int x, int y) { + private String findGroupNameAt(int x, int y) { TreePath path = myTree.getClosestPathForLocation(x - myTree.getX(), y - myTree.getY()); while (path != null) { MyNode node = extractNode(path); if (node == null) { return null; } - if (node.myComposite instanceof ConfigurableGroup) { - return (ConfigurableGroup)node.myComposite; + if (myRoot == node.getParent()) { + return node.myDisplayName; } path = path.getParentPath(); } @@ -267,10 +273,10 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd mySeparator.setFont(getFont().deriveFont(Font.BOLD)); } int height = mySeparator.getPreferredSize().height; - ConfigurableGroup group = findConfigurableGroupAt(0, height); - if (group != null && group == findConfigurableGroupAt(0, -myRenderer.getSeparatorHeight())) { + String group = findGroupNameAt(0, height); + if (group != null && group.equals(findGroupNameAt(0, 0))) { mySeparator.setBorder(BorderFactory.createEmptyBorder(0, 18, 0, 0)); - mySeparator.setText(group.getDisplayName()); + mySeparator.setText(group); Rectangle bounds = myScroller.getViewport().getBounds(); if (bounds.height > height) { @@ -319,7 +325,7 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd public void run() { if (configurable != myQueuedConfigurable) return; - MyNode editorNode = myConfigurableToNodeMap.get(configurable); + MyNode editorNode = findNode(configurable); FilteringTreeStructure.FilteringNode editorUiNode = myBuilder.getVisibleNodeFor(editorNode); if (editorUiNode == null) return; @@ -353,7 +359,8 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd } private void fireSelected(Configurable configurable, ActionCallback callback) { - myFilter.myContext.fireSelected(configurable, this).doWhenProcessed(callback.createSetDoneRunnable()); + ConfigurableWrapper wrapper = myConfigurableToWrapperMap.get(configurable); + myFilter.myContext.fireSelected(wrapper != null ? wrapper : configurable, this).doWhenProcessed(callback.createSetDoneRunnable()); } @Override @@ -396,11 +403,13 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd if (myGroups == null || myGroups.length == 0) { return NO_CHILDREN; } - SimpleNode[] result = new SimpleNode[myGroups.length]; - for (int i = 0; i < myGroups.length; i++) { - result[i] = new MyNode(this, myGroups[i]); + ArrayList list = new ArrayList(); + for (ConfigurableGroup group : myGroups) { + for (Configurable configurable : group.getConfigurables()) { + list.add(new MyNode(this, configurable, 0)); + } } - return result; + return list.toArray(new SimpleNode[list.size()]); } } @@ -408,21 +417,15 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd private final Configurable.Composite myComposite; private final Configurable myConfigurable; private final String myDisplayName; + private final int myLevel; - private MyNode(CachingSimpleNode parent, Configurable configurable) { + private MyNode(CachingSimpleNode parent, Configurable configurable, int level) { super(parent); myComposite = configurable instanceof Configurable.Composite ? (Configurable.Composite)configurable : null; myConfigurable = configurable; String name = configurable.getDisplayName(); myDisplayName = name != null ? name.replace("\n", " ") : "{ " + configurable.getClass().getSimpleName() + " }"; - } - - private MyNode(CachingSimpleNode parent, ConfigurableGroup group) { - super(parent); - myComposite = group; - myConfigurable = group instanceof Configurable ? (Configurable)group : null; - String name = group.getDisplayName(); - myDisplayName = name != null ? name.replace("\n", " ") : "{ " + group.getClass().getSimpleName() + " }"; + myLevel = level; } @Override @@ -439,7 +442,7 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd } SimpleNode[] result = new SimpleNode[configurables.length]; for (int i = 0; i < configurables.length; i++) { - result[i] = new MyNode(this, configurables[i]); + result[i] = new MyNode(this, configurables[i], myLevel + 1); if (myConfigurable != null) { myFilter.myContext.registerKid(myConfigurable, configurables[i]); } @@ -453,25 +456,17 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd } } - private final class MyRenderer extends GroupedElementsRenderer.Tree { - private JLabel myNodeIcon; - private JLabel myProjectIcon; - - protected JComponent createItemComponent() { - myTextLabel = new ErrorLabel(); - return myTextLabel; - } + private final class MyRenderer extends JPanel implements TreeCellRenderer { + private final JLabel myTextLabel = new ErrorLabel(); + private final JLabel myNodeIcon = new JLabel(" ", SwingConstants.RIGHT); + private final JLabel myProjectIcon = new JLabel(" ", SwingConstants.LEFT); - @Override - protected void layout() { - myNodeIcon = new JLabel(" ", SwingConstants.RIGHT); - myProjectIcon = new JLabel(" ", SwingConstants.LEFT); - myNodeIcon.setOpaque(false); - myTextLabel.setOpaque(false); - myProjectIcon.setOpaque(false); - myRendererComponent.add(BorderLayout.CENTER, myComponent); - myRendererComponent.add(BorderLayout.WEST, myNodeIcon); - myRendererComponent.add(BorderLayout.EAST, myProjectIcon); + public MyRenderer() { + super(new BorderLayout()); + myNodeIcon.setName(NODE_ICON); + add(BorderLayout.CENTER, myTextLabel); + add(BorderLayout.WEST, myNodeIcon); + add(BorderLayout.EAST, myProjectIcon); } public Component getTreeCellRendererComponent(JTree tree, @@ -482,7 +477,7 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd int row, boolean focused) { myTextLabel.setFont(UIUtil.getLabelFont()); - myRendererComponent.setBackground(selected ? UIUtil.getTreeSelectionBackground() : myTree.getBackground()); + setPreferredSize(null); MyNode node = extractNode(value); if (node == null) { @@ -494,28 +489,19 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd if (myRoot == node.getParent()) { myTextLabel.setFont(myTextLabel.getFont().deriveFont(Font.BOLD)); } - TreePath path = tree.getPathForRow(row); - if (path == null) { - if (value instanceof DefaultMutableTreeNode) { - path = new TreePath(((DefaultMutableTreeNode)value).getPath()); - } - } - int forcedWidth = 2000; - if (path != null && tree.isVisible()) { - Rectangle visibleRect = tree.getVisibleRect(); - - int nestingLevel = tree.isRootVisible() ? path.getPathCount() - 1 : path.getPathCount() - 2; - - int left = UIUtil.getTreeLeftChildIndent(); - int right = UIUtil.getTreeRightChildIndent(); - + if (tree.isVisible()) { + int indent = node.myLevel * (UIUtil.getTreeLeftChildIndent() + UIUtil.getTreeRightChildIndent()); Insets treeInsets = tree.getInsets(); - - int indent = (left + right) * nestingLevel + (treeInsets != null ? treeInsets.left + treeInsets.right : 0); - - forcedWidth = visibleRect.width > 0 ? visibleRect.width - indent : forcedWidth; + if (treeInsets != null) { + indent += treeInsets.left + treeInsets.right; + } + int visibleWidth = tree.getVisibleRect().width; + if (visibleWidth > indent) { + Dimension size = getPreferredSize(); + size.width = visibleWidth - indent; + //setPreferredSize(size); + } } - myRendererComponent.setPrefereedWidth(forcedWidth - 4); } // update font color for modified configurables myTextLabel.setForeground(selected ? UIUtil.getTreeSelectionForeground() : NORMAL_NODE); @@ -537,15 +523,15 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd if (parent instanceof MyNode) { if (myRoot == parent.getParent()) { project = findConfigurableProject(node); // show icon for top-level nodes - if (node.myConfigurable instanceof NodeConfigurable) { // special case for custom subgroups (build.tools) - Configurable[] configurables = ((NodeConfigurable)node.myConfigurable).getConfigurables(); + if (node.myConfigurable instanceof SortedConfigurableGroup) { // special case for custom subgroups (build.tools) + Configurable[] configurables = ((SortedConfigurableGroup)node.myConfigurable).getConfigurables(); if (configurables != null) { // assume that all configurables have the same project project = findConfigurableProject(configurables[0]); } } } - else if (((MyNode)parent).myConfigurable instanceof NodeConfigurable) { - if (((MyNode)node.getParent()).myConfigurable instanceof NodeConfigurable) { + else if (((MyNode)parent).myConfigurable instanceof SortedConfigurableGroup) { + if (((MyNode)node.getParent()).myConfigurable instanceof SortedConfigurableGroup) { project = findConfigurableProject(node); // special case for custom subgroups } } @@ -564,25 +550,20 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd myProjectIcon.setVisible(false); } // configure node icon - if (value instanceof DefaultMutableTreeNode) { - DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode)value; - TreePath treePath = new TreePath(treeNode.getPath()); - myNodeIcon.setIcon(myTree.getHandleIcon(treeNode, treePath)); - } - else { - myNodeIcon.setIcon(null); + Icon nodeIcon = null; + if (node != null) { + if (0 == node.getChildCount()) { + nodeIcon = myTree.getEmptyHandle(); + } + else if (value instanceof DefaultMutableTreeNode) { + DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode)value; + nodeIcon = myTree.isExpanded(new TreePath(treeNode.getPath())) + ? myTree.getExpandedHandle() + : myTree.getCollapsedHandle(); + } } - return myRendererComponent; - } - - int getSeparatorHeight() { - return mySeparatorComponent.getParent() == null ? 0 : mySeparatorComponent.getPreferredSize().height; - } - - public boolean isUnderHandle(Point point) { - Point handlePoint = SwingUtilities.convertPoint(myRendererComponent, point, myNodeIcon); - Rectangle bounds = myNodeIcon.getBounds(); - return bounds.x < handlePoint.x && bounds.getMaxX() >= handlePoint.x; + myNodeIcon.setIcon(nodeIcon); + return this; } } @@ -616,11 +597,7 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd @Override public void setUI(TreeUI ui) { - TreeUI actualUI = ui; - if (!(ui instanceof MyTreeUi)) { - actualUI = new MyTreeUi(); - } - super.setUI(actualUI); + super.setUI(ui instanceof MyTreeUi ? ui : new MyTreeUi()); } @Override @@ -659,84 +636,92 @@ final class SettingsTreeView extends JComponent implements Disposable, OptionsEd } @Override - protected void processMouseEvent(MouseEvent e) { + protected void processMouseEvent(MouseEvent event) { MyTreeUi ui = (MyTreeUi)myTree.getUI(); - boolean toggleNow = MouseEvent.MOUSE_RELEASED == e.getID() - && UIUtil.isActionClick(e, MouseEvent.MOUSE_RELEASED) - && !ui.isToggleEvent(e); - - if (toggleNow || MouseEvent.MOUSE_PRESSED == e.getID()) { - TreePath path = getPathForLocation(e.getX(), e.getY()); - if (path != null) { - Rectangle bounds = getPathBounds(path); - if (bounds != null && path.getLastPathComponent() instanceof DefaultMutableTreeNode) { - DefaultMutableTreeNode node = (DefaultMutableTreeNode)path.getLastPathComponent(); - boolean selected = isPathSelected(path); - boolean expanded = isExpanded(path); - Component comp = - myRenderer.getTreeCellRendererComponent(this, node, selected, expanded, node.isLeaf(), getRowForPath(path), isFocusOwner()); - - comp.setBounds(bounds); - comp.validate(); - - Point point = new Point(e.getX() - bounds.x, e.getY() - bounds.y); - if (myRenderer.isUnderHandle(point)) { - if (toggleNow) { - ui.toggleExpandState(path); - } - e.consume(); - return; - } - } - } + if (!ui.processMouseEvent(event)) { + super.processMouseEvent(event); } - - super.processMouseEvent(e); } + } - private final class MyTreeUi extends WideSelectionTreeUI { + private static final class MyTreeUi extends WideSelectionTreeUI { + boolean processMouseEvent(MouseEvent event) { + if (super.tree instanceof SimpleTree) { + SimpleTree tree = (SimpleTree)super.tree; - @Override - public void toggleExpandState(TreePath path) { - super.toggleExpandState(path); - } + boolean toggleNow = MouseEvent.MOUSE_RELEASED == event.getID() + && UIUtil.isActionClick(event, MouseEvent.MOUSE_RELEASED) + && !isToggleEvent(event); - @Override - public boolean isToggleEvent(MouseEvent event) { - return super.isToggleEvent(event); + if (toggleNow || MouseEvent.MOUSE_PRESSED == event.getID()) { + Component component = tree.getDeepestRendererComponentAt(event.getX(), event.getY()); + if (component != null && NODE_ICON.equals(component.getName())) { + if (toggleNow) { + toggleExpandState(tree.getPathForLocation(event.getX(), event.getY())); + } + event.consume(); + return true; + } + } } + return false; + } - @Override - protected boolean shouldPaintExpandControl(TreePath path, - int row, - boolean isExpanded, - boolean hasBeenExpanded, - boolean isLeaf) { - return false; - } + @Override + protected boolean shouldPaintExpandControl(TreePath path, + int row, + boolean isExpanded, + boolean hasBeenExpanded, + boolean isLeaf) { + return false; + } - @Override - protected void paintHorizontalPartOfLeg(Graphics g, - Rectangle clipBounds, - Insets insets, - Rectangle bounds, - TreePath path, - int row, - boolean isExpanded, - boolean hasBeenExpanded, - boolean isLeaf) { + @Override + protected void paintHorizontalPartOfLeg(Graphics g, + Rectangle clipBounds, + Insets insets, + Rectangle bounds, + TreePath path, + int row, + boolean isExpanded, + boolean hasBeenExpanded, + boolean isLeaf) { - } + } - @Override - protected void paintVerticalPartOfLeg(Graphics g, Rectangle clipBounds, Insets insets, TreePath path) { - } + @Override + protected void paintVerticalPartOfLeg(Graphics g, Rectangle clipBounds, Insets insets, TreePath path) { + } - @Override - public void paint(Graphics g, JComponent c) { - GraphicsUtil.setupAntialiasing(g); - super.paint(g, c); + @Override + public void paint(Graphics g, JComponent c) { + GraphicsUtil.setupAntialiasing(g); + super.paint(g, c); + } + + @Override + protected void paintRow(Graphics g, + Rectangle clipBounds, + Insets insets, + Rectangle bounds, + TreePath path, + int row, + boolean isExpanded, + boolean hasBeenExpanded, + boolean isLeaf) { + if (tree != null) { + int width = tree.getWidth(); + Container parent = tree.getParent(); + if (parent instanceof JViewport) { + JViewport viewport = (JViewport)parent; + width = viewport.getWidth() - viewport.getViewPosition().x; + } + width -= bounds.x; + if (bounds.width < width) { + bounds.width = width; + } } + super.paintRow(g, clipBounds, insets, bounds, path, row, isExpanded, hasBeenExpanded, isLeaf); } } diff --git a/platform/platform-impl/src/com/intellij/openapi/project/DumbServiceImpl.java b/platform/platform-impl/src/com/intellij/openapi/project/DumbServiceImpl.java index 308edb2d81a6..c66eb5eb1a27 100644 --- a/platform/platform-impl/src/com/intellij/openapi/project/DumbServiceImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/project/DumbServiceImpl.java @@ -34,7 +34,6 @@ import com.intellij.openapi.wm.IdeFrame; import com.intellij.openapi.wm.WindowManager; import com.intellij.openapi.wm.ex.ProgressIndicatorEx; import com.intellij.openapi.wm.ex.StatusBarEx; -import com.intellij.psi.impl.DebugUtil; import com.intellij.ui.AppIcon; import com.intellij.util.concurrency.Semaphore; import com.intellij.util.containers.ContainerUtil; @@ -81,7 +80,7 @@ public class DumbServiceImpl extends DumbService implements Disposable { @Override public void cancelTask(@NotNull DumbModeTask task) { - if (ApplicationManager.getApplication().isInternal()) LOG.info("cancel " + task + "\n" + DebugUtil.currentStackTrace()); + if (ApplicationManager.getApplication().isInternal()) LOG.info("cancel " + task); ProgressIndicatorEx indicator = myProgresses.get(task); if (indicator != null) { indicator.cancel(); diff --git a/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectImpl.java b/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectImpl.java index 978e20f6b532..8725dc003da5 100644 --- a/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectImpl.java @@ -38,7 +38,6 @@ import com.intellij.openapi.components.impl.stores.IProjectStore; import com.intellij.openapi.components.impl.stores.StoreUtil; import com.intellij.openapi.components.impl.stores.UnknownMacroNotification; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.util.pico.ConstructorInjectionComponentAdapter; import com.intellij.openapi.extensions.ExtensionPointName; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.progress.ProgressIndicator; @@ -58,6 +57,8 @@ import com.intellij.openapi.wm.impl.FrameTitleBuilder; import com.intellij.util.Function; import com.intellij.util.TimedReference; import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.pico.ConstructorInjectionComponentAdapter; +import gnu.trove.THashSet; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -488,7 +489,7 @@ public class ProjectImpl extends PlatformComponentManagerImpl implements Project } if (!macros2invalidate.isEmpty()) { - final Set components = new HashSet(); + final Set components = new THashSet(); for (TrackingPathMacroSubstitutor substitutor : substitutors) { components.addAll(substitutor.getComponents(macros2invalidate)); } diff --git a/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectManagerImpl.java index e7b20959369e..b5243e7c7ec9 100644 --- a/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectManagerImpl.java @@ -57,7 +57,6 @@ import com.intellij.util.ArrayUtil; import com.intellij.util.TimeoutUtil; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.HashMap; -import com.intellij.util.io.fs.IFile; import com.intellij.util.messages.MessageBus; import com.intellij.util.messages.MessageBusConnection; import com.intellij.util.ui.UIUtil; @@ -922,9 +921,9 @@ public class ProjectManagerImpl extends ProjectManagerEx implements NamedJDOMExt IProjectStore projectStore = projectImpl.getStateStore(); final String location = projectImpl.getPresentableUrl(); - final List original; + final List original; try { - final IComponentStore.SaveSession saveSession = projectStore.startSave(); + IComponentStore.SaveSession saveSession = projectStore.startSave(); original = saveSession.getAllStorageFiles(true); saveSession.finishSave(); } @@ -937,7 +936,7 @@ public class ProjectManagerImpl extends ProjectManagerEx implements NamedJDOMExt application.runWriteAction(new Runnable() { @Override public void run() { - for (final IFile originalFile : original) { + for (File originalFile : original) { restoreCopy(LocalFileSystem.getInstance().refreshAndFindFileByIoFile(originalFile)); } } diff --git a/platform/platform-impl/src/com/intellij/openapi/ui/popup/MultiSelectionListPopupStep.java b/platform/platform-impl/src/com/intellij/openapi/ui/popup/MultiSelectionListPopupStep.java index 815eafd56ab4..96fbccc87054 100644 --- a/platform/platform-impl/src/com/intellij/openapi/ui/popup/MultiSelectionListPopupStep.java +++ b/platform/platform-impl/src/com/intellij/openapi/ui/popup/MultiSelectionListPopupStep.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,8 +28,8 @@ import java.util.List; public abstract class MultiSelectionListPopupStep extends BaseListPopupStep { private int[] myDefaultOptionIndices = ArrayUtil.EMPTY_INT_ARRAY; - protected MultiSelectionListPopupStep(@Nullable String aTitle, List aValues) { - super(aTitle, aValues); + protected MultiSelectionListPopupStep(@Nullable String title, List values) { + super(title, values); } public abstract PopupStep onChosen(List selectedValues, boolean finalChoice); @@ -54,8 +54,8 @@ public abstract class MultiSelectionListPopupStep extends BaseListPopupStep - diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/ex/dummy/DummyCachingFileSystem.java b/platform/platform-impl/src/com/intellij/openapi/vfs/ex/dummy/DummyCachingFileSystem.java index 071a260fe68c..a95597514bb1 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/ex/dummy/DummyCachingFileSystem.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/ex/dummy/DummyCachingFileSystem.java @@ -15,12 +15,14 @@ */ package com.intellij.openapi.vfs.ex.dummy; +import com.intellij.openapi.Disposable; import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectManager; import com.intellij.openapi.project.ProjectManagerAdapter; +import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.containers.BidirectionalMap; @@ -32,7 +34,6 @@ import org.jetbrains.annotations.TestOnly; import java.io.IOException; import java.util.Collection; -import java.util.Iterator; import java.util.List; /** @@ -76,11 +77,6 @@ public abstract class DummyCachingFileSystem extends Dumm public void projectOpened(final Project project) { onProjectOpened(project); } - - @Override - public void projectClosed(final Project project) { - onProjectClosed(project); - } }); initProjectMap(); } @@ -135,7 +131,16 @@ public abstract class DummyCachingFileSystem extends Dumm clearCache(); } - public void onProjectOpened(Project project) { + public void onProjectOpened(final Project project) { + // use Disposer instead of ProjectManagerListener#projectClosed() because Disposer.dispose(project) + // is called later and some cached files should stay valid till the last moment + Disposer.register(project, new Disposable() { + @Override + public void dispose() { + onProjectClosed(project); + } + }); + clearCache(); String projectId = project.getLocationHash(); myProject2Id.put(project, projectId); @@ -157,13 +162,11 @@ public abstract class DummyCachingFileSystem extends Dumm } protected void clearInvalidFiles() { - for (Iterator it = myCachedFiles.keySet().iterator(); it.hasNext(); ) { - String path = it.next(); - T t = myCachedFiles.get(path); - if (t == null || !t.isValid()) { - it.remove(); - } + for (T t : myCachedFiles.notNullValues()) { + if (!t.isValid()) myCachedFiles.removeValue(t); } + //noinspection StatementWithEmptyBody + while (myCachedFiles.removeValue(null)) ; } @TestOnly diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/impl/VfsRootAccess.java b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/impl/VfsRootAccess.java index b6fa97092059..5a0f433c7828 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/impl/VfsRootAccess.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/impl/VfsRootAccess.java @@ -49,7 +49,7 @@ import java.util.Set; public class VfsRootAccess { private static final boolean SHOULD_PERFORM_ACCESS_CHECK = System.getenv("NO_FS_ROOTS_ACCESS_CHECK") == null; // we don't want test subclasses to accidentally remove allowed files, added by base classes - private static final Set ourAdditionalRoots = new THashSet(); + private static final Set ourAdditionalRoots = new THashSet(FileUtil.PATH_HASHING_STRATEGY); private static boolean insideGettingRoots; @TestOnly @@ -109,7 +109,7 @@ public class VfsRootAccess { Project[] openProjects = ProjectManager.getInstance().getOpenProjects(); if (openProjects.length == 0) return null; - final Set allowed = new THashSet(); + final Set allowed = new THashSet(FileUtil.PATH_HASHING_STRATEGY); allowed.add(FileUtil.toSystemIndependentName(PathManager.getHomePath())); try { diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/FSRecords.java b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/FSRecords.java index b05b7a285e7b..b8a55b333203 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/FSRecords.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/FSRecords.java @@ -29,6 +29,7 @@ import com.intellij.openapi.util.io.BufferExposingByteArrayOutputStream; import com.intellij.openapi.util.io.ByteSequence; import com.intellij.openapi.util.io.FileAttributes; import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vfs.newvfs.FileAttribute; import com.intellij.openapi.vfs.newvfs.impl.FileNameCache; import com.intellij.util.ArrayUtil; import com.intellij.util.SystemProperties; @@ -57,6 +58,7 @@ public class FSRecords implements Forceable { private static final Logger LOG = Logger.getInstance("#com.intellij.vfs.persistent.FSRecords"); public static final boolean weHaveContentHashes = SystemProperties.getBooleanProperty("idea.share.contents", true); + public static final boolean lazyVfsDataCleaning = SystemProperties.getBooleanProperty("idea.lazy.vfs.data.cleaning", true); private static final int VERSION = 20 + (weHaveContentHashes ? 0x10:0) + (IOUtil.ourByteBuffersUseNativeByteOrder ? 0x37:0); private static final int PARENT_OFFSET = 0; @@ -558,6 +560,7 @@ public class FSRecords implements Forceable { return newRecord; } else { + if (lazyVfsDataCleaning) deleteContentAndAttributes(free); DbConnection.cleanRecord(free); return free; } @@ -588,7 +591,33 @@ public class FSRecords implements Forceable { try { w.lock(); incModCount(id); - doDeleteRecursively(id); + if (lazyVfsDataCleaning) { + markAsDeletedRecursively(id); + } else { + doDeleteRecursively(id); + } + } + catch (Throwable e) { + throw DbConnection.handleError(e); + } + finally { + w.unlock(); + } + } + + private static void markAsDeletedRecursively(final int id) { + for (int subrecord : list(id)) { + markAsDeletedRecursively(subrecord); + } + + markAsDeleted(id); + } + + private static void markAsDeleted(final int id) { + try { + w.lock(); + DbConnection.markDirty(); + addToFreeRecordsList(id); } catch (Throwable e) { throw DbConnection.handleError(e); @@ -1206,12 +1235,26 @@ public class FSRecords implements Forceable { } @Nullable - static DataInputStream readAttributeWithLock(int fileId, String attId) { + public static DataInputStream readAttributeWithLock(int fileId, FileAttribute att) { try { - synchronized (attId) { + synchronized (att.getId()) { try { r.lock(); - return readAttribute(fileId, attId); + DataInputStream stream = readAttribute(fileId, att.getId()); + if (stream != null) { + try { + int actualVersion = DataInputOutputUtil.readINT(stream); + if (actualVersion != att.getVersion()) { + stream.close(); + return null; + } + } + catch (IOException e) { + stream.close(); + return null; + } + } + return stream; } finally { r.unlock(); @@ -1280,7 +1323,9 @@ public class FSRecords implements Forceable { private static void checkFileIsValid(int fileId) { assert fileId > 0 : fileId; // TODO: This assertion is a bit timey, will remove when bug is caught. - assert (getFlags(fileId) & FREE_RECORD_FLAG) == 0 : "Accessing attribute of a deleted page: " + fileId + ":" + getName(fileId); + if (!lazyVfsDataCleaning) { + assert (getFlags(fileId) & FREE_RECORD_FLAG) == 0 : "Accessing attribute of a deleted page: " + fileId + ":" + getName(fileId); + } } public static int acquireFileContent(int fileId) { @@ -1374,6 +1419,18 @@ public class FSRecords implements Forceable { return new AttributeOutputStream(fileId, attId, fixedSize); } + @NotNull + public static DataOutputStream writeAttribute(final int fileId, @NotNull FileAttribute att) { + DataOutputStream stream = writeAttribute(fileId, att.getId(), att.isFixedSize()); + try { + DataInputOutputUtil.writeINT(stream, att.getVersion()); + } + catch (IOException e) { + throw new RuntimeException(e); + } + return stream; + } + private static class ContentOutputStream extends DataOutputStream { protected final int myFileId; protected final boolean myFixedSize; diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/PersistentFSImpl.java b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/PersistentFSImpl.java index db5de4258d41..6e135018d356 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/PersistentFSImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/newvfs/persistent/PersistentFSImpl.java @@ -223,13 +223,13 @@ public class PersistentFSImpl extends PersistentFS implements ApplicationCompone @Override @Nullable public DataInputStream readAttribute(@NotNull final VirtualFile file, @NotNull final FileAttribute att) { - return FSRecords.readAttributeWithLock(getFileId(file), att.getId()); + return FSRecords.readAttributeWithLock(getFileId(file), att); } @Override @NotNull public DataOutputStream writeAttribute(@NotNull final VirtualFile file, @NotNull final FileAttribute att) { - return FSRecords.writeAttribute(getFileId(file), att.getId(), att.isFixedSize()); + return FSRecords.writeAttribute(getFileId(file), att); } @Nullable diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/CommandProcessor.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/CommandProcessor.java index 30777377bc49..746030e68abd 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/CommandProcessor.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/CommandProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Condition; +import com.intellij.openapi.util.Conditions; import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.wm.impl.commands.FinalizableCommand; import org.jetbrains.annotations.NotNull; @@ -124,7 +125,7 @@ public final class CommandProcessor implements Runnable { FinalizableCommand command = myList.remove(0); if (isEmpty()) { // memory leak otherwise - myExpireCondition = Condition.TRUE; + myExpireCondition = Conditions.alwaysTrue(); } return command; } diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/InternalDecorator.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/InternalDecorator.java index e6c84f5e7768..ba5c59a9ac25 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/InternalDecorator.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/InternalDecorator.java @@ -17,6 +17,7 @@ package com.intellij.openapi.wm.impl; import com.intellij.ide.actions.ResizeToolWindowAction; import com.intellij.idea.ActionsBundle; +import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.keymap.Keymap; import com.intellij.openapi.keymap.KeymapManagerListener; @@ -28,6 +29,7 @@ import com.intellij.openapi.ui.Splitter; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.SystemInfo; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.wm.*; import com.intellij.ui.Gray; import com.intellij.ui.JBColor; @@ -54,8 +56,6 @@ import java.util.Map; */ public final class InternalDecorator extends JPanel implements Queryable, TypeSafeDataProvider { - private static final int DIVIDER_WIDTH = UIUtil.isUnderDarcula() ? 2 : 5; - private Project myProject; private WindowInfoImpl myInfo; private final ToolWindowImpl myToolWindow; @@ -169,7 +169,7 @@ public final class InternalDecorator extends JPanel implements Queryable, TypeSa else if (ToolWindowAnchor.RIGHT == anchor) { add(myDivider, BorderLayout.WEST); } - myDivider.setPreferredSize(new Dimension(DIVIDER_WIDTH, DIVIDER_WIDTH)); + myDivider.setPreferredSize(new Dimension(0, 0)); } else { // docked and floating windows don't have divider remove(myDivider); @@ -303,10 +303,16 @@ public final class InternalDecorator extends JPanel implements Queryable, TypeSa @Override public void paintBorder(final Component c, final Graphics g, final int x, final int y, final int width, final int height) { - g.setColor(UIUtil.getPanelBackground()); - doPaintBorder(c, g, x, y, width, height); - g.setColor(new Color(0, 0, 0, 90)); - doPaintBorder(c, g, x, y, width, height); + if (UIUtil.isUnderDarcula()) { + g.setColor(Gray._40); + doPaintBorder(c, g, x, y, width, height); + } + else { + g.setColor(UIUtil.getPanelBackground()); + doPaintBorder(c, g, x, y, width, height); + g.setColor(Gray._155); + doPaintBorder(c, g, x, y, width, height); + } } private void doPaintBorder(Component c, Graphics g, int x, int y, int width, int height) { @@ -314,18 +320,22 @@ public final class InternalDecorator extends JPanel implements Queryable, TypeSa if (insets.top > 0) { UIUtil.drawLine(g, x, y + insets.top - 1, x + width - 1, y + insets.top - 1); + UIUtil.drawLine(g, x, y + insets.top, x + width - 1, y + insets.top); } if (insets.left > 0) { - UIUtil.drawLine(g, x, y + insets.top, x, y + height - 1); + UIUtil.drawLine(g, x, y, x, y + height); + UIUtil.drawLine(g, x + 1, y, x + 1, y + height); } if (insets.right > 0) { - UIUtil.drawLine(g, x + width - 1, y + insets.top, x + width - 1, y + height - 1); + UIUtil.drawLine(g, x + width - 1, y + insets.top, x + width - 1, y + height); + UIUtil.drawLine(g, x + width, y + insets.top, x + width, y + height); } if (insets.bottom > 0) { - UIUtil.drawLine(g, x, y + height - 1, x + width - 1, y + height - 1); + UIUtil.drawLine(g, x, y + height - 1, x + width, y + height - 1); + UIUtil.drawLine(g, x, y + height, x + width, y + height); } } @@ -354,7 +364,7 @@ public final class InternalDecorator extends JPanel implements Queryable, TypeSa component = parent; parent = component.getParent(); } - return new Insets(0, anchor == ToolWindowAnchor.RIGHT ? 1 : 0, 0, anchor == ToolWindowAnchor.LEFT ? 1 : 0); + return new Insets(0, anchor == ToolWindowAnchor.RIGHT ? 1 : 0, anchor == ToolWindowAnchor.TOP ? 1 : 0, anchor == ToolWindowAnchor.LEFT ? 1 : 0); } @Override @@ -447,6 +457,10 @@ public final class InternalDecorator extends JPanel implements Queryable, TypeSa } } } + String separatorText = group.getTemplatePresentation().getText(); + if (children.length > 0 && !StringUtil.isEmpty(separatorText)) { + main.addAction(new Separator(separatorText), Constraints.FIRST); + } } /** @@ -609,135 +623,98 @@ public final class InternalDecorator extends JPanel implements Queryable, TypeSa private final class MyDivider extends JPanel { private boolean myDragging; private Point myLastPoint; + private Disposable myDisposable; + private IdeGlassPane myGlassPane; + + private final MouseAdapter myListener = new MyMouseAdapter(); - private MyDivider() { - myDragging = false; - enableEvents(MouseEvent.MOUSE_EVENT_MASK | MouseEvent.MOUSE_MOTION_EVENT_MASK); - setBorder(new DividerBorder()); + @Override + public void addNotify() { + super.addNotify(); + myGlassPane = IdeGlassPaneUtil.find(this); + myDisposable = Disposer.newDisposable(); + myGlassPane.addMouseMotionPreprocessor(myListener, myDisposable); + myGlassPane.addMousePreprocessor(myListener, myDisposable); } @Override - protected final void processMouseMotionEvent(final MouseEvent e) { - super.processMouseMotionEvent(e); - if (MouseEvent.MOUSE_DRAGGED == e.getID()) { - myDragging = true; - final ToolWindowAnchor anchor = myInfo.getAnchor(); - final boolean isVerticalCursor = myInfo.isDocked() ? anchor.isSplitVertically() : anchor.isHorizontal(); - setCursor(isVerticalCursor ? Cursor.getPredefinedCursor(Cursor.S_RESIZE_CURSOR) : Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR)); - final Point point = e.getPoint(); + public void removeNotify() { + super.removeNotify(); + if (myDisposable != null && !Disposer.isDisposed(myDisposable)) { + Disposer.dispose(myDisposable); + } + } + + boolean isInDragZone(MouseEvent e) { + final Point p = SwingUtilities.convertMouseEvent(e.getComponent(), e, this).getPoint(); + return Math.abs(myInfo.getAnchor().isHorizontal() ? p.y : p.x) < 6; + } + + + private class MyMouseAdapter extends MouseAdapter { + + private void updateCursor(MouseEvent e) { + if (isInDragZone(e)) { + myGlassPane.setCursor(MyDivider.this.getCursor(), MyDivider.this); + e.consume(); + } + } + + @Override + public void mousePressed(MouseEvent e) { + myDragging = isInDragZone(e); + updateCursor(e); + } + + @Override + public void mouseClicked(MouseEvent e) { + updateCursor(e); + } + + @Override + public void mouseReleased(MouseEvent e) { + updateCursor(e); + myDragging = false; + } + @Override + public void mouseMoved(MouseEvent e) { + updateCursor(e); + } + + @Override + public void mouseDragged(MouseEvent e) { + if (!myDragging) return; + MouseEvent event = SwingUtilities.convertMouseEvent(e.getComponent(), e, MyDivider.this); + final ToolWindowAnchor anchor = myInfo.getAnchor(); + final Point point = event.getPoint(); final Container windowPane = InternalDecorator.this.getParent(); - myLastPoint = SwingUtilities.convertPoint(this, point, windowPane); + myLastPoint = SwingUtilities.convertPoint(MyDivider.this, point, windowPane); myLastPoint.x = Math.min(Math.max(myLastPoint.x, 0), windowPane.getWidth()); myLastPoint.y = Math.min(Math.max(myLastPoint.y, 0), windowPane.getHeight()); final Rectangle bounds = InternalDecorator.this.getBounds(); if (anchor == ToolWindowAnchor.TOP) { - if (myLastPoint.y < DIVIDER_WIDTH) { - myLastPoint.y = DIVIDER_WIDTH; - } InternalDecorator.this.setBounds(0, 0, bounds.width, myLastPoint.y); } else if (anchor == ToolWindowAnchor.LEFT) { - if (myLastPoint.x < DIVIDER_WIDTH) { - myLastPoint.x = DIVIDER_WIDTH; - } InternalDecorator.this.setBounds(0, 0, myLastPoint.x, bounds.height); } else if (anchor == ToolWindowAnchor.BOTTOM) { - if (myLastPoint.y > windowPane.getHeight() - DIVIDER_WIDTH) { - myLastPoint.y = windowPane.getHeight() - DIVIDER_WIDTH; - } InternalDecorator.this.setBounds(0, myLastPoint.y, bounds.width, windowPane.getHeight() - myLastPoint.y); } else if (anchor == ToolWindowAnchor.RIGHT) { - if (myLastPoint.x > windowPane.getWidth() - DIVIDER_WIDTH) { - myLastPoint.x = windowPane.getWidth() - DIVIDER_WIDTH; - } InternalDecorator.this.setBounds(myLastPoint.x, 0, windowPane.getWidth() - myLastPoint.x, bounds.height); } InternalDecorator.this.validate(); + e.consume(); } } @Override - protected final void processMouseEvent(final MouseEvent e) { - super.processMouseEvent(e); + public Cursor getCursor() { final boolean isVerticalCursor = myInfo.isDocked() ? myInfo.getAnchor().isSplitVertically() : myInfo.getAnchor().isHorizontal(); - switch (e.getID()) { - case MouseEvent.MOUSE_MOVED: - default: - break; - case MouseEvent.MOUSE_ENTERED: - setCursor( - isVerticalCursor ? Cursor.getPredefinedCursor(Cursor.S_RESIZE_CURSOR) : Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR)); - break; - case MouseEvent.MOUSE_EXITED: - if (!myDragging) { - setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); - } - break; - case MouseEvent.MOUSE_PRESSED: - setCursor( - isVerticalCursor ? Cursor.getPredefinedCursor(Cursor.S_RESIZE_CURSOR) : Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR)); - break; - case MouseEvent.MOUSE_RELEASED: - myDragging = false; - myLastPoint = null; - break; - case MouseEvent.MOUSE_CLICKED: - break; - } - } - - private final class DividerBorder implements Border { - @Override - public final void paintBorder(final Component c, final Graphics g, final int x, final int y, final int width, final int height) { - final ToolWindowAnchor anchor = myInfo.getAnchor(); - final boolean isVertical = !anchor.isSplitVertically(); - final JBColor outer = new JBColor(Color.white, Color.darkGray); - if (isVertical) { - if (anchor == ToolWindowAnchor.TOP) { - g.setColor(outer); - UIUtil.drawLine(g, x, y, x + width - 1, y); - g.setColor(Color.darkGray); - UIUtil.drawLine(g, x, y + height - 1, x + width - 1, y + height - 1); - } - else { - g.setColor(Color.darkGray); - UIUtil.drawLine(g, x, y, x + width - 1, y); - g.setColor(outer); - UIUtil.drawLine(g, x, y + height - 1, x + width - 1, y + height - 1); - } - } - else { - if (anchor == ToolWindowAnchor.LEFT) { - g.setColor(outer); - UIUtil.drawLine(g, x, y, x, y + height - 1); - g.setColor(Color.darkGray); - UIUtil.drawLine(g, x + width - 1, y, x + width - 1, y + height - 1); - } - else { - g.setColor(Color.darkGray); - UIUtil.drawLine(g, x, y, x, y + height - 1); - g.setColor(outer); - UIUtil.drawLine(g, x + width - 1, y, x + width - 1, y + height - 1); - } - } - } - - @Override - public final Insets getBorderInsets(final Component c) { - if (c instanceof MyDivider) { - return new Insets(1, 1, 1, 1); - } - return new Insets(0, 0, 0, 0); - } - - @Override - public final boolean isBorderOpaque() { - return true; - } + return isVerticalCursor ? Cursor.getPredefinedCursor(Cursor.S_RESIZE_CURSOR) : Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR); } } diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/WindowManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/WindowManagerImpl.java index 19131753b5ba..e1824f775e82 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/WindowManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/WindowManagerImpl.java @@ -67,7 +67,6 @@ import java.util.Set; */ @State( name = "WindowManager", - roamingType = RoamingType.GLOBAL, storages = {@Storage(file = StoragePathMacros.APP_CONFIG + "/window.manager.xml")}) public final class WindowManagerImpl extends WindowManagerEx implements NamedComponent, PersistentStateComponent { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.wm.impl.WindowManagerImpl"); diff --git a/platform/platform-impl/src/com/intellij/ui/TableExpandableItemsHandler.java b/platform/platform-impl/src/com/intellij/ui/TableExpandableItemsHandler.java index b7704a687441..6099561c7283 100644 --- a/platform/platform-impl/src/com/intellij/ui/TableExpandableItemsHandler.java +++ b/platform/platform-impl/src/com/intellij/ui/TableExpandableItemsHandler.java @@ -16,6 +16,7 @@ package com.intellij.ui; import com.intellij.openapi.util.Pair; +import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.event.ListSelectionEvent; @@ -105,8 +106,11 @@ public class TableExpandableItemsHandler extends AbstractExpandableItemsHandler< return myComponent.getCellRect(tableCellKey.row, tableCellKey.column, false); } + @Nullable public Pair getCellRendererAndBounds(TableCell key) { - if (key.row < 0 || key.row >= myComponent.getRowCount() || key.column < 0 || key.column >= myComponent.getColumnCount()) { + if (key.row < 0 || key.row >= myComponent.getRowCount() || + key.column < 0 || key.column >= myComponent.getColumnCount() || + key.row == myComponent.getEditingRow() && key.column == myComponent.getEditingColumn()) { return null; } diff --git a/platform/platform-impl/src/com/intellij/ui/content/impl/ContentManagerImpl.java b/platform/platform-impl/src/com/intellij/ui/content/impl/ContentManagerImpl.java index d0b110f083bf..2d8bffe98d04 100644 --- a/platform/platform-impl/src/com/intellij/ui/content/impl/ContentManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/ui/content/impl/ContentManagerImpl.java @@ -33,9 +33,8 @@ import com.intellij.ui.components.panels.Wrapper; import com.intellij.ui.content.*; import com.intellij.ui.switcher.SwitchProvider; import com.intellij.ui.switcher.SwitchTarget; -import com.intellij.util.containers.ContainerUtil; import com.intellij.util.SmartList; - +import com.intellij.util.containers.ContainerUtil; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -191,7 +190,11 @@ public class ContentManagerImpl implements ContentManager, PropertyChangeListene private void doAddContent(@NotNull final Content content, final int index) { ApplicationManager.getApplication().assertIsDispatchThread(); - if (myContents.contains(content)) return; + if (myContents.contains(content)) { + myContents.remove(content); + myContents.add(index == -1 ? myContents.size() : index, content); + return; + } ((ContentImpl)content).setManager(this); final int insertIndex = index == -1 ? myContents.size() : index; @@ -703,6 +706,7 @@ public class ContentManagerImpl implements ContentManager, PropertyChangeListene myContentWithChangedComponent.clear(); myUI = null; myListeners.clear(); + dataProviders.clear(); } @Override diff --git a/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java b/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java index ee586418dbd6..37646a19292f 100644 --- a/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/ui/docking/impl/DockManagerImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -94,6 +94,7 @@ public class DockManagerImpl extends DockManager implements PersistentStateCompo myProject = project; } + @Override public void register(final DockContainer container) { myContainers.add(container); Disposer.register(container, new Disposable() { @@ -147,6 +148,7 @@ public class DockManagerImpl extends DockManager implements PersistentStateCompo return wnd != null ? key + "#" + wnd.myId : key; } + @Override public DockContainer getContainerFor(Component c) { if (c == null) return null; @@ -401,7 +403,8 @@ public class DockManagerImpl extends DockManager implements PersistentStateCompo }); } - public Pair createNewDockContainerFor(@NotNull VirtualFile file, FileEditorManagerImpl fileEditorManager) { + @NotNull + public Pair createNewDockContainerFor(@NotNull VirtualFile file, @NotNull FileEditorManagerImpl fileEditorManager) { DockContainer container = getFactory(DockableEditorContainerFactory.TYPE).createContainer(null); register(container); diff --git a/platform/platform-impl/src/com/intellij/ui/messages/JBMacMessages.java b/platform/platform-impl/src/com/intellij/ui/messages/JBMacMessages.java index 2a70b61c6e8a..47ebb829adc2 100644 --- a/platform/platform-impl/src/com/intellij/ui/messages/JBMacMessages.java +++ b/platform/platform-impl/src/com/intellij/ui/messages/JBMacMessages.java @@ -190,7 +190,7 @@ public class JBMacMessages extends MacMessagesEmulation { SheetMessage sheetMessage = new SheetMessage(window, title, message, UIUtil.getQuestionIcon(), new String [] {yesButton, noButton}, doNotAskDialogOption, yesButton, noButton); int result = sheetMessage.getResult().equals(yesButton) ? Messages.YES : Messages.NO; - if (doNotAskDialogOption != null) { + if (doNotAskDialogOption != null && (result == Messages.YES || doNotAskDialogOption.shouldSaveOptionsOnCancel())) { doNotAskDialogOption.setToBeShown(sheetMessage.toBeShown(), result); } return result; diff --git a/platform/platform-impl/src/com/intellij/ui/popup/WizardPopup.java b/platform/platform-impl/src/com/intellij/ui/popup/WizardPopup.java index 1b9190f6a740..d8b512367f26 100644 --- a/platform/platform-impl/src/com/intellij/ui/popup/WizardPopup.java +++ b/platform/platform-impl/src/com/intellij/ui/popup/WizardPopup.java @@ -189,7 +189,7 @@ public abstract class WizardPopup extends AbstractPopup implements ActionListene targetBounds.x = getParent().getBounds().x - targetBounds.width - STEP_X_PADDING; } } else { - ScreenUtil.moveToFit(targetBounds, ScreenUtil.getScreenRectangle(aScreenX, aScreenY), null); + ScreenUtil.moveToFit(targetBounds, ScreenUtil.getScreenRectangle(aScreenX + 1, aScreenY + 1), null); } if (getParent() == null) { diff --git a/platform/platform-impl/src/com/intellij/ui/win/RecentProjectApplication.java b/platform/platform-impl/src/com/intellij/ui/win/RecentProjectApplication.java index 9561c77b02d6..3ece5a6b77cd 100644 --- a/platform/platform-impl/src/com/intellij/ui/win/RecentProjectApplication.java +++ b/platform/platform-impl/src/com/intellij/ui/win/RecentProjectApplication.java @@ -17,6 +17,7 @@ package com.intellij.ui.win; import com.intellij.ide.impl.ProjectUtil; import com.intellij.openapi.diff.ApplicationStarterBase; +import org.jetbrains.annotations.Nullable; /** @@ -33,7 +34,7 @@ public class RecentProjectApplication extends ApplicationStarterBase { } @Override - protected void processCommand(String[] args) throws Exception { + protected void processCommand(String[] args, @Nullable String currentDirectory) throws Exception { ProjectUtil.openProject(args[1], null, false); } } \ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/util/IJSwingUtilities.java b/platform/platform-impl/src/com/intellij/util/IJSwingUtilities.java index 60c5f90c8b73..bd0efda42c48 100644 --- a/platform/platform-impl/src/com/intellij/util/IJSwingUtilities.java +++ b/platform/platform-impl/src/com/intellij/util/IJSwingUtilities.java @@ -22,6 +22,7 @@ import com.intellij.ui.EditorTextField; import com.intellij.ui.components.OrphanGuardian; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.FilteringIterator; +import com.intellij.util.ui.JBSwingUtilities; import com.intellij.util.ui.UIUtil; import gnu.trove.TIntStack; import org.jetbrains.annotations.NotNull; @@ -34,7 +35,7 @@ import java.net.MalformedURLException; import java.net.URL; import java.util.Iterator; -public class IJSwingUtilities { +public class IJSwingUtilities extends JBSwingUtilities { public static void invoke(Runnable runnable) { if (ApplicationManager.getApplication().isDispatchThread()) { runnable.run(); diff --git a/platform/platform-impl/src/org/jetbrains/io/ChannelRegistrar.java b/platform/platform-impl/src/org/jetbrains/io/ChannelRegistrar.java index 5565e77a8cfb..179d2db5cf81 100644 --- a/platform/platform-impl/src/org/jetbrains/io/ChannelRegistrar.java +++ b/platform/platform-impl/src/org/jetbrains/io/ChannelRegistrar.java @@ -49,8 +49,8 @@ public final class ChannelRegistrar extends ChannelInboundHandlerAdapter { } finally { if (eventLoopGroup != null) { - eventLoopGroup.shutdownGracefully(); + eventLoopGroup.shutdownGracefully(1, 2, TimeUnit.NANOSECONDS); } } } -} \ No newline at end of file +} diff --git a/platform/platform-resources-en/src/messages/ApplicationBundle.properties b/platform/platform-resources-en/src/messages/ApplicationBundle.properties index 75d0f242e38c..42fd4e94be46 100644 --- a/platform/platform-resources-en/src/messages/ApplicationBundle.properties +++ b/platform/platform-resources-en/src/messages/ApplicationBundle.properties @@ -212,6 +212,8 @@ wrapping.parameters.annotation=Parameter annotations wrapping.local.variables.annotation=Local variable annotations wrapping.enum.constants=Enum constants wrapping.long.lines=Ensure right margin is not exceeded +wrapping.comments=Comments +wrapping.comments.wrap.at.right.margin=Wrap at right margin checkbox.align.multiline.chained.methods=Chained methods checkbox.align.multiline.method.parameters=Method parameters @@ -621,6 +623,7 @@ arrangement.settings.additional.force.rearrange.always=Always arrangement.settings.additional.force.rearrange.never=Never arrangement.settings.additional.force.rearrange.according.to.dialog=Use current mode (toggled in the Reformat Code dialog) arrangement.settings.additional.title=Additional settings +arrangement.settings.validation.duplicate.matching.rule=Matching rule have to be unique in the current settings. Duplicated rules will be ignored. arrangement.settings.validation.duplicate.section.text=Section text have to be unique in the current settings. Rule with duplicated text will be ignored. arrangement.settings.validation.empty.section.rule=Section should contain at least one arrangement entry otherwise it will be never inserted. arrangement.settings.validation.end.section.rule.without.start=Can not find start section rule for the current end rule. @@ -644,4 +647,10 @@ group.richcopy=Rich-text copy combobox.richcopy.color.scheme=Color scheme combobox.richcopy.color.scheme.active=Active scheme -settings.code.style.default.general=Default (General) \ No newline at end of file +settings.code.style.default.general=Default (General) +wrapping.wrap.if.multiple.annotations=Wrap if multiple +wrapping.after.annotations=After last field annotation + +reformat.changed.text.file.too.big.notification.groupId=Reformat changed text +reformat.changed.text.file.too.big.notification.title=Couldn't calculate changed ranges +reformat.changed.text.file.too.big.notification.text=file {0} is too big or there are too many changes \ No newline at end of file diff --git a/platform/platform-resources-en/src/messages/ExecutionBundle.properties b/platform/platform-resources-en/src/messages/ExecutionBundle.properties index 183dda3d52b8..508ac2ad2801 100644 --- a/platform/platform-resources-en/src/messages/ExecutionBundle.properties +++ b/platform/platform-resources-en/src/messages/ExecutionBundle.properties @@ -40,9 +40,12 @@ jre.not.valid.error.message=''{0}'' is not valid JRE home applet.configuration.description=Applet configuration applet.configuration.name=Applet -application.configuration.description=Application configuration +application.configuration.description=Java application configuration application.configuration.name=Application +jar.application.configuration.description=Configuration to run a JAR file using 'java -jar' command +jar.application.configuration.name=JAR Application + run.configuration.norunner.selected.label=No runner selected run.configuration.configuration.tab.title=Configuration run.configuration.startup.connection.rab.title=Startup/Connection @@ -132,10 +135,10 @@ junit.runing.info.errors.count.message=E:{0} junit.runing.info.passed.count.message=P:{0} junit.runing.info.ignored.count.message=I:{0} -junit.runing.info.status.completed.from.total.failed={0} of {1} Failed: {2} -junit.runing.info.status.completed.from.total={0} of {1} -junit.runing.info.status.running.number.with.name=Running: {0} {1} -junit.runing.info.failed.to.start.error.message=Failed to start +junit.running.info.status.completed.from.total.failed={0} of {1} Failed: {2} +junit.running.info.status.completed.from.total={0} of {1} +junit.running.info.status.running.number.with.name=Running: {0} {1} +junit.running.info.failed.to.start.error.message=Failed to start junit.runing.info.tests.failed.label=Tests Failed junit.runing.info.tests.passed.label=Tests Passed tests.passed.with.warnings.message=Tests passed (with warnings) @@ -274,8 +277,8 @@ environment.variables.helper.use.arguments.jdk13.label=For JDK 1.3.x environment.variables.helper.use.arguments.jdk14.label=For JDK 1.4.x select.run.configuration.for.item.action.name=Select ''{0}'' save.run.configuration.for.item.action.name=Save ''{0}'' -junit.runing.info.status.done.count=Done: {0} -junit.runing.info.status.terminated.count=Terminated: {0} +junit.running.info.status.done.count=Done: {0} +junit.running.info.status.terminated.count=Terminated: {0} junit.runing.info.tests.in.progress.done.tree.node=Tests in Progress: Done junit.runing.info.tests.in.progress.terminated.tre.node=Tests in Progress: Terminated delete.confirmation.dialog.title=Delete Confirmation diff --git a/platform/platform-resources-en/src/messages/FeatureStatisticsBundle.properties b/platform/platform-resources-en/src/messages/FeatureStatisticsBundle.properties index 9a94f1f267bc..346399cfe60e 100644 --- a/platform/platform-resources-en/src/messages/FeatureStatisticsBundle.properties +++ b/platform/platform-resources-en/src/messages/FeatureStatisticsBundle.properties @@ -121,4 +121,4 @@ find.recent.search=Recent searches history find.completion=Completion in Find Bar switcher=Switcher dir.diff=Directory Diff -jar.diff=Jar Files Diff +jar.diff=JAR Files Diff diff --git a/platform/platform-resources-en/src/messages/IdeBundle.properties b/platform/platform-resources-en/src/messages/IdeBundle.properties index 556b246bc1b7..e5a1ecc0f649 100644 --- a/platform/platform-resources-en/src/messages/IdeBundle.properties +++ b/platform/platform-resources-en/src/messages/IdeBundle.properties @@ -641,6 +641,7 @@ checkbox.widescreen.tool.window.layout=Widescreen tool window layout checkbox.left.toolwindow.layout=Side-by-side layout on the left checkbox.right.toolwindow.layout=Side-by-side layout on the right checkbox.show.editor.preview.popup=Show editor preview tooltip +checkbox.use.preview.window=Navigate to preview checkbox.show.tool.window.numbers=Show tool window numbers checkbox.animate.windows=Animate windows group.transparency=Transparency @@ -941,10 +942,10 @@ predefined.scope.tests.name=Tests scope.editor.legend.recursively.included.label=Recursively included scope.editor.legend.partly.included.label=Partially included toolwindow.palette=Palette -jar.build.on.make=Build jars on &make -jar.build.modules.to.jar=Choose Modules to Jar -jar.build.module.0.jar.settings=Module ''{0}'' Jar Settings -jar.build.include.in.jar.file=Include in jar file: +jar.build.on.make=Build JARs on &make +jar.build.modules.to.jar=Choose Modules to JAR +jar.build.module.0.jar.settings=Module ''{0}'' JAR Settings +jar.build.include.in.jar.file=Include in JAR file: search.textfield.title=&Search: select.in.scope=Scope scope.view.title=Scopes @@ -1018,7 +1019,7 @@ button.fix=Fix\u2026 setup.library.dialog.title=Setup Library label.library.will.be.created.description.text={0} level library {1} with {2} {2, choice, 1#file|2#files} will be created new.library.file.chooser.title=New Library Files -new.library.file.chooser.description=Select jar files in which library classes are located +new.library.file.chooser.description=Select JAR files in which library classes are located create.default.library.type.action.name=Java popup.title.select.library.type=Select Library Type @@ -1141,8 +1142,9 @@ whatsnew.action.custom.text=What''s _New in {0} whatsnew.action.custom.description=Find out about the new features in this version of {0} diff.dialog.title=Diff Between ''{0}'' and ''{1}'' -goto.custom.region.command=Go to Custom Region -goto.custom.region.message.dumb.mode=Go to Custom Region action is not available until indices are built. +goto.custom.region.menu.item=Custom Folding Region... +goto.custom.region.command=Go to Custom Folding Region +goto.custom.region.message.dumb.mode=Go to Custom Folding Region action is not available until indices are built. goto.custom.region.message.unavailable=There are no custom folding regions in the current file. alphabetical.mode.is.on.warning=Alphabetical order for tabs is ON. Switch it OFF? diff --git a/platform/platform-resources-en/src/messages/InspectionsBundle.properties b/platform/platform-resources-en/src/messages/InspectionsBundle.properties index 98e0045a31f3..bccf8c48411f 100644 --- a/platform/platform-resources-en/src/messages/InspectionsBundle.properties +++ b/platform/platform-resources-en/src/messages/InspectionsBundle.properties @@ -116,6 +116,8 @@ inspection.redundant.throws.problem.descriptor2=The declared exception {0} is ne inspection.equals.hashcode.display.name=equals() and hashCode() not paired inspection.equals.hashcode.only.one.defined.problem.descriptor=Class has {0} defined but does not define {1} +inspection.equals.hashcode.generate.equals.quickfix=Generate 'equals()' +inspection.equals.hashcode.generate.hashcode.quickfix=Generate 'hashCode()' inspection.1.5.display.name=Usages of API documented as @since 1.5 (1.6|1.7) inspection.1.5.problem.descriptor=Usage of API documented as @since {0}+ diff --git a/platform/platform-resources-en/src/messages/OptionsBundle.properties b/platform/platform-resources-en/src/messages/OptionsBundle.properties index 2c06b31b53f7..537dec1d8f92 100644 --- a/platform/platform-resources-en/src/messages/OptionsBundle.properties +++ b/platform/platform-resources-en/src/messages/OptionsBundle.properties @@ -198,31 +198,35 @@ options.xml.display.name=XML settings.panel.title=Settings -node.configurable.build.tools.display.name=Build Tools -node.configurable.build.tools.settings.description=\ - Default view for Build Tools - configurable.group.appearance.settings.display.name=Appearance and Behavior configurable.group.appearance.settings.description=\ - Default view for Appearance and Behavior + Personalize IntelliJ appearance and behavior: change themes and font size, tune the keymap,\ + configure plugins and system settings, such as password policies, HTTP proxy, updates and more. configurable.group.editor.settings.display.name=Editor configurable.group.editor.settings.description=\ - Default view for Editor + Personalize source code appearance by changing fonts, highlighting styles, indents, etc.\ + Customize the Editor from line numbers, caret placement and tabs to source code inspections,\ + setting up templates and file encodings. configurable.group.project.settings.display.name=Current Project configurable.group.project.settings.description=\ Default view for Current Project configurable.group.build.settings.display.name=Build, Execution, Deployment configurable.group.build.settings.description=\ - Default view for Build, Execution, Deployment + Configure you project integration with different build tools (Maven, Gradle or Gant),\ + modify the default compiler settings, manage server access configurations, customize the Debugger behavior, etc. +configurable.group.build.tools.settings.display.name=Build Tools +configurable.group.build.tools.settings.description=\ + Configure your project integration with different build tools: Maven, Gradle or Gant. configurable.group.language.settings.display.name=Languages and Frameworks configurable.group.language.settings.description=\ - Default view for Languages and Frameworks + Configure the settings related to specific frameworks and technologies used in your project. configurable.group.tools.settings.display.name=Tools configurable.group.tools.settings.description=\ - Default view for Tools + Configure integration with third-party applications, specify the SSH Terminal connection settings,\ + manage server certificates and tasks, configure diagrams layout, etc. configurable.group.null.settings.display.name=Other Settings configurable.group.null.settings.description=\ - Default view for Other Settings + Settings that are related to non-bundled custom plugins and are not assigned to any other category. configurable.default.project.tooltip=For default project configurable.current.project.tooltip=For current project diff --git a/platform/platform-resources-en/src/messages/RefactoringBundle.properties b/platform/platform-resources-en/src/messages/RefactoringBundle.properties index 48d0e63d60c0..72b1cd7ad131 100644 --- a/platform/platform-resources-en/src/messages/RefactoringBundle.properties +++ b/platform/platform-resources-en/src/messages/RefactoringBundle.properties @@ -671,7 +671,7 @@ static.initializer.description=static initializer of class {0} instance.initializer.description=instance initializer of class {0} file.description=File {0} directory.description=Directory {0} -0.is.located.in.a.jar.file={0} is located in a jar file.\n +0.is.located.in.a.jar.file={0} is located in a JAR file.\n 0.is.read.only={0} is read-only.\n 0.is.not.a.legal.java.identifier=''{0}'' is not a legal java identifier method.0.is.already.defined.in.the.1=Method {0} is already defined in the {1} diff --git a/platform/platform-resources-en/src/messages/UIBundle.properties b/platform/platform-resources-en/src/messages/UIBundle.properties index 5dd72ae735b7..4f67ac6dcbf8 100644 --- a/platform/platform-resources-en/src/messages/UIBundle.properties +++ b/platform/platform-resources-en/src/messages/UIBundle.properties @@ -24,6 +24,7 @@ replace.prompt.replace.button=&Replace replace.prompt.skip.button=&Skip replace.prompt.all.in.this.file.button=All in This &File replace.prompt.all.files.action=&All Files +replace.prompt.review.action=Re&view replace.prompt.all.button=&All replace.prompt.replace.occurrence.label=Do you want to replace this occurrence? search.popup.search.for.label=Search for: diff --git a/platform/platform-resources/src/META-INF/LangExtensionPoints.xml b/platform/platform-resources/src/META-INF/LangExtensionPoints.xml index dcdc0e2d983c..2e2e0c80bb17 100644 --- a/platform/platform-resources/src/META-INF/LangExtensionPoints.xml +++ b/platform/platform-resources/src/META-INF/LangExtensionPoints.xml @@ -385,7 +385,8 @@ interface="com.intellij.execution.configurations.ConfigurationType"/> + interface="com.intellij.execution.configurations.RunConfigurationsSettings" + area="IDEA_PROJECT"/> diff --git a/platform/platform-resources/src/META-INF/PlatformExtensionPoints.xml b/platform/platform-resources/src/META-INF/PlatformExtensionPoints.xml index baea70a67c7a..00bd38430d68 100644 --- a/platform/platform-resources/src/META-INF/PlatformExtensionPoints.xml +++ b/platform/platform-resources/src/META-INF/PlatformExtensionPoints.xml @@ -10,7 +10,7 @@ interface="com.intellij.openapi.startup.StartupActivity"/> + beanClass="com.intellij.openapi.project.DefaultProjectTypeEP"/> diff --git a/platform/platform-resources/src/META-INF/PlatformExtensions.xml b/platform/platform-resources/src/META-INF/PlatformExtensions.xml index 51028d93edac..29dcf0645cef 100644 --- a/platform/platform-resources/src/META-INF/PlatformExtensions.xml +++ b/platform/platform-resources/src/META-INF/PlatformExtensions.xml @@ -151,6 +151,9 @@ + + - + diff --git a/platform/platform-resources/src/META-INF/XmlPlugin.xml b/platform/platform-resources/src/META-INF/XmlPlugin.xml index 566d674d0063..b6653d81eec7 100644 --- a/platform/platform-resources/src/META-INF/XmlPlugin.xml +++ b/platform/platform-resources/src/META-INF/XmlPlugin.xml @@ -548,7 +548,7 @@ - + diff --git a/platform/platform-resources/src/META-INF/xdebugger.xml b/platform/platform-resources/src/META-INF/xdebugger.xml index d28921d17ba6..821cb528b6a9 100644 --- a/platform/platform-resources/src/META-INF/xdebugger.xml +++ b/platform/platform-resources/src/META-INF/xdebugger.xml @@ -30,7 +30,8 @@ - + diff --git a/platform/platform-resources/src/brokenPlugins.txt b/platform/platform-resources/src/brokenPlugins.txt index 584c3a466615..55704f758841 100644 --- a/platform/platform-resources/src/brokenPlugins.txt +++ b/platform/platform-resources/src/brokenPlugins.txt @@ -8,7 +8,7 @@ com.jetbrains.twig 133.51 130.1639 org.jetbrains.plugins.ruby 7.0.0.20140807 6.0.0.20140207 6.5.2.20140512 7.0.0.20140704 7.0.0.20140707 7.0.0.20140724 Pythonid 3.1 4.0.25 4.0.26 Karma 138.317 138.21 134.1163 134.1039 134.686 134.31 -org.intellij.scala 0.42.28 0.42.23 0.40.20 0.40.18 0.40.16 0.32.593 0.32.562 0.32.558 0.32.550 0.32.520 0.32.512 +org.intellij.scala 1.0.16 1.0.14 1.0.13 0.42.28 0.42.23 0.40.20 0.40.18 0.40.16 0.32.593 0.32.562 0.32.558 0.32.550 0.32.520 0.32.512 org.jetbrains.kannotator 0.2.420 org.jetbrains.kotlin 0.7.1360 0.7.1376 0.8.7 SBT 1.0.0 1.1.0 1.2.0 1.3.0 1.3.1 1.4.0 1.5.0 diff --git a/platform/platform-resources/src/componentSets/Platform.xml b/platform/platform-resources/src/componentSets/Platform.xml index 0e2e60d64fd7..9dd7590ac09e 100644 --- a/platform/platform-resources/src/componentSets/Platform.xml +++ b/platform/platform-resources/src/componentSets/Platform.xml @@ -69,11 +69,6 @@ com.intellij.openapi.command.impl.DocumentReferenceManagerImpl - - com.intellij.openapi.application.PathMacros - com.intellij.application.options.PathMacrosImpl - - com.intellij.ide.ui.LafManager com.intellij.ide.ui.laf.LafManagerImpl diff --git a/platform/platform-resources/src/idea/LangActions.xml b/platform/platform-resources/src/idea/LangActions.xml index 39957743629d..a9c204b1e6e7 100644 --- a/platform/platform-resources/src/idea/LangActions.xml +++ b/platform/platform-resources/src/idea/LangActions.xml @@ -709,7 +709,6 @@ - @@ -719,6 +718,7 @@ + @@ -817,7 +817,8 @@ - + diff --git a/platform/platform-resources/src/launcher.py b/platform/platform-resources/src/launcher.py index 0a679eec98fe..440f716f8ebf 100644 --- a/platform/platform-resources/src/launcher.py +++ b/platform/platform-resources/src/launcher.py @@ -18,27 +18,19 @@ for i, arg in enumerate(sys.argv[1:]): print(('Usage:\n' + \ ' {0} -h |-? | --help\n' + \ ' {0} [-l|--line line] file[:line]\n' + \ - ' {0} diff file1 file2').format(sys.argv[0])) + ' {0} diff ' + \ + ' {0} merge [base] ').format(sys.argv[0])) exit(0) - elif arg == 'diff' and i == 0: - args.append(arg) - elif arg == '-l' or arg == '--line': - args.append(arg) - skip_next = True - elif skip_next: - args.append(arg) - skip_next = False - else: - if ':' in arg: - file_path, line_number = arg.rsplit(':', 1) - if line_number.isdigit(): - args.append('-l') - args.append(line_number) - args.append(os.path.abspath(file_path)) - else: - args.append(os.path.abspath(arg)) + elif ':' in arg: + file_path, line_number = arg.rsplit(':', 1) + if line_number.isdigit(): + args.append('-l') + args.append(line_number) + args.append(file_path) else: - args.append(os.path.abspath(arg)) + args.append(arg) + else: + args.append(arg) def launch_with_port(port): found = False @@ -54,7 +46,6 @@ def launch_with_port(port): try: path_len = struct.unpack(">h", s.recv(2))[0] path = s.recv(path_len) - path = os.path.abspath(path) if os.path.abspath(path) == os.path.abspath(CONFIG_PATH): found = True break @@ -87,13 +78,5 @@ if port == -1: else: if launch_with_port(port): exit() -if sys.platform == "darwin": - # Mac OS: RUN_PATH is *.app path - if len(args): - args.insert(0, "--args") - os.execvp("open", ["-a", RUN_PATH] + args) -else: - # unix common - bin_dir, bin_file = os.path.split(RUN_PATH) - os.chdir(bin_dir) - os.execv(bin_file, [bin_file] + args) +bin_dir, bin_file = os.path.split(RUN_PATH) +os.execv(RUN_PATH, [bin_file] + args) \ No newline at end of file diff --git a/platform/platform-tests/testData/codeStyle/autodetect/manyComments.java b/platform/platform-tests/testData/codeStyle/autodetect/manyComments.java new file mode 100644 index 000000000000..7bc7a3a46982 --- /dev/null +++ b/platform/platform-tests/testData/codeStyle/autodetect/manyComments.java @@ -0,0 +1,51 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +public interface Doable { + + + /* + * + * + * + * + * + * + * + * + * + * + */ + public void count() { + } + + + /* + * + * + * + * + * + * + * + * + * + * + */ + public void test() { + } +} diff --git a/platform/platform-tests/testData/codeStyle/autodetect/manyZeroRelativeIndent.java b/platform/platform-tests/testData/codeStyle/autodetect/manyZeroRelativeIndent.java new file mode 100644 index 000000000000..2ee4c59848ec --- /dev/null +++ b/platform/platform-tests/testData/codeStyle/autodetect/manyZeroRelativeIndent.java @@ -0,0 +1,31 @@ +class Test { + + public void test() { + // boolean function-available(string) + addFunction(decls, new FunctionImpl("function-available", XPathType.BOOLEAN, + new Parameter(XPathType.STRING, Parameter.Kind.REQUIRED))); + + // node-set current() + addFunction(decls, new FunctionImpl("current", XPathType.NODESET)); + + // EXSLT (http://www.exslt.org) extensions supported by Xalan & Saxon + addFunction(decls, EXSLT_DATE_TIME, new FunctionImpl("date", XPathType.STRING, optional_string)); + addFunction(decls, EXSLT_DATE_TIME, new FunctionImpl("date-time", XPathType.STRING)); + addFunction(decls, EXSLT_DATE_TIME, new FunctionImpl("day-abbreviation", XPathType.STRING, optional_string)); + addFunction(decls, EXSLT_DATE_TIME, new FunctionImpl("day-in-month", XPathType.NUMBER, optional_string)); + addFunction(decls, EXSLT_DATE_TIME, new FunctionImpl("day-in-week", XPathType.NUMBER, optional_string)); + addFunction(decls, EXSLT_DATE_TIME, new FunctionImpl("day-in-year", XPathType.NUMBER, optional_string)); + addFunction(decls, EXSLT_DATE_TIME, new FunctionImpl("day-name", XPathType.STRING, optional_string)); + addFunction(decls, EXSLT_DATE_TIME, new FunctionImpl("day-of-week-in-month", XPathType.NUMBER, optional_string)); + addFunction(decls, EXSLT_DATE_TIME, new FunctionImpl("hour-in-day", XPathType.NUMBER, optional_string)); + addFunction(decls, EXSLT_DATE_TIME, new FunctionImpl("leap-year", XPathType.BOOLEAN, optional_string)); + addFunction(decls, EXSLT_DATE_TIME, new FunctionImpl("minute-in-hour", XPathType.NUMBER, optional_string)); + addFunction(decls, EXSLT_DATE_TIME, new FunctionImpl("month-abbreviation", XPathType.STRING, optional_string)); + addFunction(decls, EXSLT_DATE_TIME, new FunctionImpl("month-in-year", XPathType.NUMBER, optional_string)); + addFunction(decls, EXSLT_DATE_TIME, new FunctionImpl("month-name", XPathType.STRING, optional_string)); + addFunction(decls, EXSLT_DATE_TIME, new FunctionImpl("second-in-minute", XPathType.NUMBER, optional_string)); + addFunction(decls, EXSLT_DATE_TIME, new FunctionImpl("time", XPathType.STRING, optional_string)); + addFunction(decls, EXSLT_DATE_TIME, new FunctionImpl("week-in-year", XPathType.NUMBER, optional_string)); + addFunction(decls, EXSLT_DATE_TIME, new FunctionImpl("year", XPathType.NUMBER, optional_string)); + } +} diff --git a/platform/platform-tests/testData/codeStyle/autodetect/simpleIndent.java b/platform/platform-tests/testData/codeStyle/autodetect/simpleIndent.java new file mode 100644 index 000000000000..c4cd369df732 --- /dev/null +++ b/platform/platform-tests/testData/codeStyle/autodetect/simpleIndent.java @@ -0,0 +1,14 @@ +public class A { + + public void test() { + int a = 2; + int c = 2; + int r = 3; + int rq = 3; + int rw = 3; + int re = 3; + } + + public void a() { + } +} diff --git a/platform/platform-tests/testSrc/com/intellij/formatting/GeneralCodeFormatterTest.java b/platform/platform-tests/testSrc/com/intellij/formatting/GeneralCodeFormatterTest.java index 7814e6e5f109..861a4b2c6e4b 100644 --- a/platform/platform-tests/testSrc/com/intellij/formatting/GeneralCodeFormatterTest.java +++ b/platform/platform-tests/testSrc/com/intellij/formatting/GeneralCodeFormatterTest.java @@ -109,7 +109,7 @@ import java.io.IOException; indentOptions.INDENT_SIZE = 4; indentOptions.LABEL_INDENT_SIZE = 1; final CodeStyleSettings settings = new CodeStyleSettings(false); - settings.RIGHT_MARGIN = myRightMargin; + settings.setDefaultRightMargin(myRightMargin); try { FormatterEx.getInstanceEx().adjustLineIndent(model, settings, indentOptions, initialText.length() - 1, new TextRange(0, initialText.length())); } @@ -200,7 +200,7 @@ import java.io.IOException; indentOptions.INDENT_SIZE = 4; indentOptions.LABEL_INDENT_SIZE = 1; final CodeStyleSettings settings = new CodeStyleSettings(false); - settings.RIGHT_MARGIN = myRightMargin; + settings.setDefaultRightMargin(myRightMargin); try { FormatterEx.getInstanceEx().format(model, settings, indentOptions, indentOptions, null); } diff --git a/platform/platform-tests/testSrc/com/intellij/ide/util/treeView/BaseTreeTestCase.java b/platform/platform-tests/testSrc/com/intellij/ide/util/treeView/BaseTreeTestCase.java index 53be07ee2a7d..fc5c5edec615 100644 --- a/platform/platform-tests/testSrc/com/intellij/ide/util/treeView/BaseTreeTestCase.java +++ b/platform/platform-tests/testSrc/com/intellij/ide/util/treeView/BaseTreeTestCase.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -87,7 +87,7 @@ abstract class BaseTreeTestCase extends FlyIdeaTestCase { void waitBuilderToCome() { try { - waitBuilderToCome(Condition.TRUE); + waitBuilderToCome(Conditions.alwaysTrue()); } catch (Exception e) { throw new AssertionError(e); @@ -375,7 +375,7 @@ abstract class BaseTreeTestCase extends FlyIdeaTestCase { } void doAndWaitForBuilder(final Runnable runnable) throws Exception { - doAndWaitForBuilder(runnable, Condition.TRUE); + doAndWaitForBuilder(runnable, Conditions.alwaysTrue()); } diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/components/impl/ApplicationStoreTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/components/impl/ApplicationStoreTest.java index cfbd5a12f146..719635a19a30 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/components/impl/ApplicationStoreTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/components/impl/ApplicationStoreTest.java @@ -7,13 +7,21 @@ import com.intellij.openapi.components.*; import com.intellij.openapi.components.impl.stores.*; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vfs.CharsetToolkit; import com.intellij.testFramework.LightPlatformLangTestCase; import com.intellij.util.xmlb.XmlSerializerUtil; +import gnu.trove.THashMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; +import java.io.InputStream; +import java.util.Map; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.junit.Assert.assertThat; public class ApplicationStoreTest extends LightPlatformLangTestCase { private File testAppConfig; @@ -53,8 +61,73 @@ public class ApplicationStoreTest extends LightPlatformLangTestCase { public void testStreamProviderSaveIfSeveralStoragesConfigured() throws Exception { SeveralStoragesConfigured component = new SeveralStoragesConfigured(); + + MyStreamProvider streamProvider = new MyStreamProvider(); + componentStore.getStateStorageManager().setStreamProvider(streamProvider); + componentStore.initComponent(component, false); + component.foo = "newValue"; StoreUtil.doSave(componentStore); + + assertThat(streamProvider.data.get(RoamingType.PER_USER).get(StoragePathMacros.APP_CONFIG + "/proxy.settings.xml"), equalTo("\n" + + " \n" + + " \n" + + "")); + } + + public void testLoadFromStreamProvider() throws Exception { + SeveralStoragesConfigured component = new SeveralStoragesConfigured(); + + MyStreamProvider streamProvider = new MyStreamProvider(); + THashMap map = new THashMap(); + map.put(StoragePathMacros.APP_CONFIG + "/proxy.settings.xml", "\n" + + " \n" + + " \n" + + ""); + streamProvider.data.put(RoamingType.PER_USER, map); + + componentStore.getStateStorageManager().setStreamProvider(streamProvider); + componentStore.initComponent(component, false); + assertThat(component.foo, equalTo("newValue")); + } + + private static class MyStreamProvider extends StreamProvider { + public final Map> data = new THashMap>(); + + @Override + public void saveContent(@NotNull String fileSpec, + @NotNull byte[] content, + int size, + @NotNull RoamingType roamingType, + boolean async) { + getMap(roamingType).put(fileSpec, new String(content, 0, size, CharsetToolkit.UTF8_CHARSET)); + } + + private Map getMap(@NotNull RoamingType roamingType) { + Map map = data.get(roamingType); + if (map == null) { + map = new THashMap(); + data.put(roamingType, map); + } + return map; + } + + @Nullable + @Override + public InputStream loadContent(@NotNull String fileSpec, @NotNull RoamingType roamingType) throws IOException { + String data = getMap(roamingType).get(fileSpec); + return data == null ? null : new ByteArrayInputStream(data.getBytes(CharsetToolkit.UTF8_CHARSET)); + } + + @Override + public void delete(@NotNull String fileSpec, @NotNull RoamingType roamingType) { + Map map = data.get(roamingType); + if (map != null) { + map.remove(fileSpec); + } + } } class MyComponentStore extends ComponentStoreImpl implements Disposable { @@ -88,7 +161,7 @@ public class ApplicationStoreTest extends LightPlatformLangTestCase { } }; - stateStorageManager.addMacro(StoragePathMacros.getMacroName(StoragePathMacros.APP_CONFIG), testAppConfigPath); + stateStorageManager.addMacro(StoragePathMacros.APP_CONFIG, testAppConfigPath); } @Override @@ -141,7 +214,6 @@ public class ApplicationStoreTest extends LightPlatformLangTestCase { @Nullable @Override public SeveralStoragesConfigured getState() { - foo = "newValue"; return this; } diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/components/impl/StateStorageManagerImplTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/components/impl/StateStorageManagerImplTest.java index 76c673626b26..da54b2a65e35 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/components/impl/StateStorageManagerImplTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/components/impl/StateStorageManagerImplTest.java @@ -16,6 +16,7 @@ package com.intellij.openapi.components.impl; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.components.RoamingType; import com.intellij.openapi.components.StateStorage; import com.intellij.openapi.components.StateStorageException; import com.intellij.openapi.components.StateStorageOperation; @@ -55,7 +56,7 @@ public class StateStorageManagerImplTest extends LightPlatformLangTestCase { return null; } }; - myStateStorageManager.addMacro("MACRO1", "/temp/m1"); + myStateStorageManager.addMacro("$MACRO1$", "/temp/m1"); } @Override @@ -65,23 +66,23 @@ public class StateStorageManagerImplTest extends LightPlatformLangTestCase { } public void testCreateFileStateStorageMacroSubstituted() { - StateStorage data = myStateStorageManager.getFileStateStorage("$MACRO1$/test.xml"); + StateStorage data = myStateStorageManager.getStateStorage("$MACRO1$/test.xml", RoamingType.PER_USER); assertThat(data, is(notNullValue())); } public void testCreateStateStorageAssertionThrownWhenUnknownMacro() { try { - myStateStorageManager.getFileStateStorage("$UNKNOWN_MACRO$/test.xml"); + myStateStorageManager.getStateStorage("$UNKNOWN_MACRO$/test.xml", RoamingType.PER_USER); fail("Exception expected"); } catch (IllegalArgumentException e) { - assertEquals("Unknown macro: $UNKNOWN_MACRO$ in storage spec: $UNKNOWN_MACRO$/test.xml", e.getMessage()); + assertEquals("Unknown macro: $UNKNOWN_MACRO$ in storage file spec: $UNKNOWN_MACRO$/test.xml", e.getMessage()); } } public void testCreateFileStateStorageMacroSubstitutedWhenExpansionHas$() { - myStateStorageManager.addMacro("DOLLAR_MACRO", "/temp/d$"); - StateStorage data = myStateStorageManager.getFileStateStorage("$DOLLAR_MACRO$/test.xml"); + myStateStorageManager.addMacro("$DOLLAR_MACRO$", "/temp/d$"); + StateStorage data = myStateStorageManager.getStateStorage("$DOLLAR_MACRO$/test.xml", RoamingType.PER_USER); assertThat(data, is(notNullValue())); } } diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/components/impl/XmlElementStorageTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/components/impl/XmlElementStorageTest.java index eeffb44915db..414843f69d96 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/components/impl/XmlElementStorageTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/components/impl/XmlElementStorageTest.java @@ -16,26 +16,26 @@ package com.intellij.openapi.components.impl; import com.intellij.openapi.Disposable; +import com.intellij.openapi.components.RoamingType; import com.intellij.openapi.components.StateStorage; import com.intellij.openapi.components.StateStorageException; import com.intellij.openapi.components.TrackingPathMacroSubstitutor; -import com.intellij.openapi.components.impl.stores.ComponentRoamingManager; import com.intellij.openapi.components.impl.stores.ComponentVersionProvider; import com.intellij.openapi.components.impl.stores.XmlElementStorage; import com.intellij.openapi.util.Disposer; import com.intellij.testFramework.LightPlatformLangTestCase; -import com.intellij.util.io.fs.IFile; -import org.jdom.Document; import org.jdom.Element; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.io.File; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Set; -import static com.intellij.openapi.util.JDOMBuilder.*; +import static com.intellij.openapi.util.JDOMBuilder.attr; +import static com.intellij.openapi.util.JDOMBuilder.tag; /** * @author mike @@ -57,7 +57,7 @@ public class XmlElementStorageTest extends LightPlatformLangTestCase { public void testGetStateSucceeded() throws Exception { MyXmlElementStorage storage = - new MyXmlElementStorage(document(tag("root", tag("component", attr("name", "test"), tag("foo")))), myParentDisposable); + new MyXmlElementStorage(tag("root", tag("component", attr("name", "test"), tag("foo"))), myParentDisposable); Element state = storage.getState(this, "test", Element.class, null); assertNotNull(state); assertEquals("component", state.getName()); @@ -65,36 +65,36 @@ public class XmlElementStorageTest extends LightPlatformLangTestCase { } public void testGetStateNotSucceeded() throws Exception { - MyXmlElementStorage storage = new MyXmlElementStorage(document(tag("root")), myParentDisposable); + MyXmlElementStorage storage = new MyXmlElementStorage(tag("root"), myParentDisposable); Element state = storage.getState(this, "test", Element.class, null); assertNull(state); } public void testSetStateOverridesOldState() throws Exception { MyXmlElementStorage storage = - new MyXmlElementStorage(document(tag("root", tag("component", attr("name", "test"), tag("foo")))), myParentDisposable); + new MyXmlElementStorage(tag("root", tag("component", attr("name", "test"), tag("foo"))), myParentDisposable); Element newState = tag("component", attr("name", "test"), tag("bar")); StateStorage.ExternalizationSession externalizationSession = storage.startExternalization(); externalizationSession.setState(this, "test", newState, null); storage.startSave(externalizationSession).save(); - assertNotNull(storage.mySavedDocument); - assertNotNull(storage.mySavedDocument.getRootElement().getChild("component").getChild("bar")); - assertNull(storage.mySavedDocument.getRootElement().getChild("component").getChild("foo")); + assertNotNull(storage.mySavedElement); + assertNotNull(storage.mySavedElement.getChild("component").getChild("bar")); + assertNull(storage.mySavedElement.getChild("component").getChild("foo")); } private class MyXmlElementStorage extends XmlElementStorage { - private final Document myDocument; - private Document mySavedDocument; + private final Element myElement; + private Element mySavedElement; - public MyXmlElementStorage(final Document document, final Disposable parentDisposable) throws StateStorageException { - super(new MyPathMacroManager(), parentDisposable, "root", null, "", ComponentRoamingManager.getInstance(), ComponentVersionProvider.EMPTY); - myDocument = document; + public MyXmlElementStorage(Element element, final Disposable parentDisposable) throws StateStorageException { + super("", RoamingType.PER_USER, new MyPathMacroManager(), parentDisposable, "root", null, ComponentVersionProvider.EMPTY); + myElement = element; } @Override - protected Document loadDocument() throws StateStorageException { - return myDocument; + protected Element loadLocalData() { + return myElement; } @Override @@ -102,41 +102,42 @@ public class XmlElementStorageTest extends LightPlatformLangTestCase { return new MySaveSession(externalizationSession) { @Override protected void doSave() throws StateStorageException { - mySavedDocument = getDocumentToSave().clone(); + Element elementToSave = getElementToSave(); + mySavedElement = elementToSave == null ? null : elementToSave.clone(); } @NotNull @Override - public Collection getStorageFilesToSave() throws StateStorageException { - return needsSave() ? getAllStorageFiles() : Collections.emptyList(); + public Collection getStorageFilesToSave() throws StateStorageException { + return needsSave() ? getAllStorageFiles() : Collections.emptyList(); } @NotNull @Override - public List getAllStorageFiles() { + public List getAllStorageFiles() { throw new UnsupportedOperationException("Method getAllStorageFiles not implemented in " + getClass()); } - }; } } private static class MyPathMacroManager implements TrackingPathMacroSubstitutor { @Override - public void expandPaths(final Element element) { + public void expandPaths(@NotNull final Element element) { } @Override public void reset() { } + @NotNull @Override - public Collection getComponents(Collection macros) { + public Collection getComponents(@NotNull Collection macros) { return Collections.emptyList(); } @Override - public void collapsePaths(final Element element) { + public void collapsePaths(@NotNull final Element element) { } @Override @@ -149,17 +150,18 @@ public class XmlElementStorageTest extends LightPlatformLangTestCase { throw new UnsupportedOperationException("Method collapsePath not implemented in " + getClass()); } + @NotNull @Override public Collection getUnknownMacros(final String componentName) { return Collections.emptySet(); } @Override - public void invalidateUnknownMacros(Set macros) { + public void invalidateUnknownMacros(@NotNull Set macros) { } @Override - public void addUnknownMacros(String componentName, Collection unknownMacros) { + public void addUnknownMacros(@NotNull String componentName, @NotNull Collection unknownMacros) { } } } diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/progress/util/ProgressIndicatorTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/progress/util/ProgressIndicatorTest.java new file mode 100644 index 000000000000..6e0201628a51 --- /dev/null +++ b/platform/platform-tests/testSrc/com/intellij/openapi/progress/util/ProgressIndicatorTest.java @@ -0,0 +1,329 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.openapi.progress.util; + +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.ModalityState; +import com.intellij.openapi.application.ex.ApplicationManagerEx; +import com.intellij.openapi.progress.*; +import com.intellij.openapi.progress.impl.ProgressManagerImpl; +import com.intellij.openapi.wm.ex.ProgressIndicatorEx; +import com.intellij.testFramework.LightPlatformTestCase; +import com.intellij.testFramework.PlatformTestUtil; +import com.intellij.util.Alarm; +import com.intellij.util.containers.DoubleArrayList; +import com.intellij.util.containers.Stack; +import com.intellij.util.ui.UIUtil; +import gnu.trove.TLongArrayList; +import org.jetbrains.annotations.NotNull; + +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; + +/** + * @author yole + */ +public class ProgressIndicatorTest extends LightPlatformTestCase { + public void testCheckCanceledHasNoStackFrame() { + ProgressIndicatorBase pib = new ProgressIndicatorBase(); + pib.cancel(); + boolean hadException = false; + try { + pib.checkCanceled(); + } + catch(ProcessCanceledException ex) { + hadException = true; + assertTrue("Should have no stackframe", ex.getStackTrace().length == 0); + } + assertTrue("Please restore ProgressIndicatorBase.checkCanceled() check!", hadException); + } + + public void testProgressManagerCheckCanceledWorksRightAfterIndicatorBeenCanceled() { + for (int i=0; i<1000;i++) { + final ProgressIndicatorBase indicator = new ProgressIndicatorBase(); + ProgressManager.getInstance().runProcess(new Runnable() { + @Override + public void run() { + ProgressManager.checkCanceled(); + try { + indicator.cancel(); + ProgressManager.checkCanceled(); + fail("checkCanceled() must have caught just canceled indicator"); + } + catch (ProcessCanceledException ignored) { + } + } + }, indicator); + } + } + + private volatile long prevTime; + private volatile long now; + public void testCheckCanceledGranularity() throws InterruptedException { + prevTime = now = 0; + final long warmupEnd = System.currentTimeMillis() + 1000; + final TLongArrayList times = new TLongArrayList(); + final long end = warmupEnd + 1000; + + ApplicationManagerEx.getApplicationEx().runProcessWithProgressSynchronously(new Runnable() { + @Override + public void run() { + final Alarm alarm = new Alarm(Alarm.ThreadToUse.OWN_THREAD, getTestRootDisposable()); + ProgressIndicatorEx indicator = (ProgressIndicatorEx)ProgressIndicatorProvider.getGlobalProgressIndicator(); + prevTime = System.currentTimeMillis(); + assert indicator != null; + indicator.addStateDelegate(new ProgressIndicatorStub() { + @Override + public void checkCanceled() throws ProcessCanceledException { + now = System.currentTimeMillis(); + if (now > warmupEnd) { + int delta = (int)(now - prevTime); + times.add(delta); + } + prevTime = now; + } + }); + while (System.currentTimeMillis() < end) { + ProgressManager.checkCanceled(); + } + alarm.cancelAllRequests(); + } + }, "", false, getProject(), null, ""); + long averageDelay = PlatformTestUtil.averageAmongMedians(times.toNativeArray(), 5); + System.out.println("averageDelay = " + averageDelay); + assertTrue(averageDelay < ProgressManagerImpl.CHECK_CANCELED_DELAY_MILLIS*3); + } + + public void testProgressIndicatorUtils() throws Throwable { + final AtomicBoolean run = new AtomicBoolean(true); + final AtomicBoolean insideReadAction = new AtomicBoolean(); + final AtomicReference exception = new AtomicReference(); + final ProgressIndicatorBase indicator = new ProgressIndicatorBase(); + ProgressIndicatorUtils.scheduleWithWriteActionPriority(indicator, new ReadTask() { + @Override + public void computeInReadAction(@NotNull ProgressIndicator indicator) { + insideReadAction.set(true); + while (run.get()) { + ProgressManager.checkCanceled(); + } + } + + @Override + public void onCanceled(@NotNull ProgressIndicator indicator) { + try { + assertTrue(run.get()); // cancel should happen early + run.set(false); + } + catch (Throwable e) { + exception.set(e); + } + } + }); + UIUtil.dispatchAllInvocationEvents(); + while (!insideReadAction.get()) { + ; + } + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + try { + assertTrue(indicator.isCanceled()); + } + catch (Throwable e) { + exception.set(e); + } + } + }); + assertFalse(run.get()); + if (exception.get() != null) throw exception.get(); + } + + private static class ProgressIndicatorStub implements ProgressIndicatorEx { + @Override + public void addStateDelegate(@NotNull ProgressIndicatorEx delegate) { + + } + + @Override + public boolean isModalityEntered() { + return false; + } + + @Override + public void finish(@NotNull TaskInfo task) { + + } + + @Override + public boolean isFinished(@NotNull TaskInfo task) { + return false; + } + + @Override + public boolean wasStarted() { + return false; + } + + @Override + public void processFinish() { + + } + + @Override + public void initStateFrom(@NotNull ProgressIndicator indicator) { + + } + + @NotNull + @Override + public Stack getTextStack() { + return null; + } + + @NotNull + @Override + public DoubleArrayList getFractionStack() { + return null; + } + + @NotNull + @Override + public Stack getText2Stack() { + return null; + } + + @Override + public int getNonCancelableCount() { + return 0; + } + + @Override + public void start() { + + } + + @Override + public void stop() { + + } + + @Override + public boolean isRunning() { + return false; + } + + @Override + public void cancel() { + + } + + @Override + public boolean isCanceled() { + return false; + } + + @Override + public void setText(String text) { + + } + + @Override + public String getText() { + return null; + } + + @Override + public void setText2(String text) { + + } + + @Override + public String getText2() { + return null; + } + + @Override + public double getFraction() { + return 0; + } + + @Override + public void setFraction(double fraction) { + + } + + @Override + public void pushState() { + + } + + @Override + public void popState() { + + } + + @Override + public void startNonCancelableSection() { + + } + + @Override + public void finishNonCancelableSection() { + + } + + @Override + public boolean isModal() { + return false; + } + + @NotNull + @Override + public ModalityState getModalityState() { + return null; + } + + @Override + public void setModalityProgress(ProgressIndicator modalityProgress) { + + } + + @Override + public boolean isIndeterminate() { + return false; + } + + @Override + public void setIndeterminate(boolean indeterminate) { + + } + + @Override + public void checkCanceled() throws ProcessCanceledException { + + } + + @Override + public boolean isPopupWasShown() { + return false; + } + + @Override + public boolean isShowing() { + return false; + } + } +} diff --git a/platform/platform-tests/testSrc/com/intellij/psi/autodetect/IndentAutoDetectionTest.java b/platform/platform-tests/testSrc/com/intellij/psi/autodetect/IndentAutoDetectionTest.java new file mode 100644 index 000000000000..4b59416de0d9 --- /dev/null +++ b/platform/platform-tests/testSrc/com/intellij/psi/autodetect/IndentAutoDetectionTest.java @@ -0,0 +1,135 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.psi.autodetect; + +import com.intellij.openapi.editor.Document; +import com.intellij.psi.codeStyle.autodetect.*; +import com.intellij.testFramework.LightPlatformCodeInsightTestCase; +import com.intellij.testFramework.PlatformTestCase; +import com.intellij.testFramework.PlatformTestUtil; +import org.jetbrains.annotations.NotNull; +import org.junit.Assert; + +import java.io.File; +import java.util.List; + +public class IndentAutoDetectionTest extends LightPlatformCodeInsightTestCase { + private static final String BASE_PATH = "codeStyle/autodetect/"; + + static { + PlatformTestCase.initPlatformLangPrefix(); + } + + public void testSimpleIndent() { + doTestMaxUsedIndent(2, 6); + } + + public void testManyComments() { + doTestMaxUsedIndent(2, 6); + } + + public void testManyZeroRelativeIndent() { + doTestMaxUsedIndent(2); + } + + public void testSpacesToNumbers() throws Exception { + String text = " i\n" + + " a\n" + + " t\n"; + doTestLineToIndentMapping(text, 5, 4, 10); + } + + public void testEmptyLines() throws Exception { + doTestLineToIndentMapping(" \n\n\n", -1, -1, -1); + } + + public void testSpacesInSimpleClass() { + doTestLineToIndentMapping( + "public class A {\n" + + "\n" + + " public void test() {\n" + + " int a = 2;\n" + + " }\n" + + "\n" + + " public void a() {\n" + + " }\n" + + "}", + 0, -1, 4, 6, 4, -1, 4, 4, 0 + ); + } + + public void testComplexIndents() { + doTestLineToIndentMapping( + "class Test\n" + + "{\n" + + " int a;\n" + + " int b;\n" + + " \n" + + " public void test() {\n" + + " int c;\n" + + " }\n" + + " \n" + + " public void run() {\n" + + " Runnable runnable = new Runnable() {\n" + + " @Override\n" + + " public void run() {\n" + + " System.out.println(\"Hello!\");\n" + + " }\n" + + " };\n" + + " }\n" + + "}", + 0, 0, 2, 2, -1, 2, 4, 2, -1, 2, 4, 6, 6, 8, 6, 4, 2, 0 + ); + } + + public void doTestMaxUsedIndent(int indentExpected, int timesUsedExpected) { + IndentUsageInfo maxIndentExpected = new IndentUsageInfo(indentExpected, timesUsedExpected); + IndentUsageInfo indentInfo = getMaxUsedIndentInfo(); + Assert.assertEquals("Indent size mismatch", maxIndentExpected.getIndentSize(), indentInfo.getIndentSize()); + Assert.assertEquals("Indent size usage number mismatch", maxIndentExpected.getTimesUsed(), indentInfo.getTimesUsed()); + } + + public void doTestMaxUsedIndent(int indentExpected) { + IndentUsageInfo indentInfo = getMaxUsedIndentInfo(); + Assert.assertEquals("Indent size mismatch", indentExpected, indentInfo.getIndentSize()); + } + + @NotNull + private IndentUsageInfo getMaxUsedIndentInfo() { + configureByFile(getTestName(true) + ".java"); + Document document = getDocument(myFile); + List lines = new LineIndentInfoBuilder(document.getCharsSequence()).build(); + IndentUsageStatistics statistics = new IndentUsageStatisticsImpl(lines); + return statistics.getKMostUsedIndentInfo(0); + } + + private static void doTestLineToIndentMapping(@NotNull CharSequence text, int... spacesForLine) { + List list = new LineIndentInfoBuilder(text).build(); + Assert.assertEquals(list.size(), spacesForLine.length); + for (int i = 0; i < spacesForLine.length; i++) { + int indentSize = list.get(i).getIndentSize(); + Assert.assertEquals("Mismatch on line " + i, spacesForLine[i], indentSize); + } + } + + @Override + @NotNull + public String getTestDataPath() { + return PlatformTestUtil.getCommunityPath().replace(File.separatorChar, '/') + + "/platform/platform-tests/testData/" + + BASE_PATH; + } +} diff --git a/platform/projectModel-api/src/com/intellij/openapi/application/PathMacros.java b/platform/projectModel-api/src/com/intellij/openapi/application/PathMacros.java index 160aed545b03..fac4e5e9edb1 100644 --- a/platform/projectModel-api/src/com/intellij/openapi/application/PathMacros.java +++ b/platform/projectModel-api/src/com/intellij/openapi/application/PathMacros.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,15 +15,15 @@ */ package com.intellij.openapi.application; +import com.intellij.openapi.components.ServiceManager; import org.jetbrains.annotations.NotNull; import java.util.Collection; import java.util.Set; public abstract class PathMacros { - public static PathMacros getInstance() { - return ApplicationManager.getApplication().getComponent(PathMacros.class); + return ServiceManager.getService(PathMacros.class); } public abstract Set getAllMacroNames(); @@ -33,9 +33,7 @@ public abstract class PathMacros { public abstract void setMacro(String name, String value); /** - * Obsolete macros that are to be removed gently from the project files. They can be read, but not written again. Not persisted. - * @param name - * @param value + * Obsolete macros that are to be removed gently from the project files. They can be read, but not written again. Not persisted */ public abstract void addLegacyMacro(@NotNull String name, @NotNull String value); diff --git a/platform/projectModel-api/src/com/intellij/openapi/components/LastStorageChooserForWrite.java b/platform/projectModel-api/src/com/intellij/openapi/components/LastStorageChooserForWrite.java new file mode 100644 index 000000000000..f9206dee03e3 --- /dev/null +++ b/platform/projectModel-api/src/com/intellij/openapi/components/LastStorageChooserForWrite.java @@ -0,0 +1,10 @@ +package com.intellij.openapi.components; + +public final class LastStorageChooserForWrite implements StateStorageChooser { + public static final LastStorageChooserForWrite INSTANCE = new LastStorageChooserForWrite(); + + @Override + public Storage[] selectStorages(Storage[] storages, Object component, StateStorageOperation operation) { + return operation == StateStorageOperation.WRITE ? new Storage[]{storages[storages.length - 1]} : storages; + } +} \ No newline at end of file diff --git a/platform/projectModel-api/src/com/intellij/openapi/components/State.java b/platform/projectModel-api/src/com/intellij/openapi/components/State.java index ec6b97055a48..38af50e27196 100644 --- a/platform/projectModel-api/src/com/intellij/openapi/components/State.java +++ b/platform/projectModel-api/src/com/intellij/openapi/components/State.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,14 +23,15 @@ import java.lang.annotation.RetentionPolicy; public @interface State { String name(); + @Deprecated /** - * {@link RoamingType#GLOBAL} will be ignored, use only {@link RoamingType#DISABLED} or {@link RoamingType#PER_PLATFORM} + * @deprecated Use {@link Storage#roamingType()} */ RoamingType roamingType() default RoamingType.PER_USER; Storage[] storages(); - Class storageChooser() default StorageAnnotationsDefaultValues.NullStateStorageChooser.class; + Class storageChooser() default StateStorageChooser.class; boolean reloadable() default true; -} \ No newline at end of file +} diff --git a/platform/projectModel-api/src/com/intellij/openapi/components/StateStorage.java b/platform/projectModel-api/src/com/intellij/openapi/components/StateStorage.java index 36112e5dc028..ef68ac1e3a7f 100644 --- a/platform/projectModel-api/src/com/intellij/openapi/components/StateStorage.java +++ b/platform/projectModel-api/src/com/intellij/openapi/components/StateStorage.java @@ -19,11 +19,11 @@ package com.intellij.openapi.components; import com.intellij.openapi.util.Pair; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileEvent; -import com.intellij.util.io.fs.IFile; import com.intellij.util.messages.Topic; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.io.File; import java.util.Collection; import java.util.List; import java.util.Set; @@ -32,13 +32,16 @@ public interface StateStorage { Topic STORAGE_TOPIC = new Topic("STORAGE_LISTENER", Listener.class, Topic.BroadcastDirection.TO_PARENT); @Nullable - T getState(final Object component, final String componentName, Class stateClass, @Nullable T mergeInto) throws StateStorageException; - boolean hasState(final Object component, final String componentName, final Class aClass, final boolean reloadData) throws StateStorageException; + T getState(final Object component, @NotNull String componentName, Class stateClass, @Nullable T mergeInto) throws StateStorageException; + + boolean hasState(final Object component, @NotNull String componentName, final Class aClass, final boolean reloadData) throws StateStorageException; @NotNull ExternalizationSession startExternalization(); + @NotNull SaveSession startSave(@NotNull ExternalizationSession externalizationSession); + void finishSave(@NotNull SaveSession saveSession); void reload(@NotNull Set changedComponents) throws StateStorageException; @@ -51,13 +54,13 @@ public interface StateStorage { void save() throws StateStorageException; @Nullable - Set analyzeExternalChanges(@NotNull Set> changedFiles); + Set analyzeExternalChanges(@NotNull Set> changedFiles); @NotNull - Collection getStorageFilesToSave() throws StateStorageException; + Collection getStorageFilesToSave() throws StateStorageException; @NotNull - List getAllStorageFiles(); + List getAllStorageFiles(); } interface Listener { diff --git a/platform/projectModel-api/src/com/intellij/openapi/components/Storage.java b/platform/projectModel-api/src/com/intellij/openapi/components/Storage.java index e10b97755d3a..f9ad4155b2c2 100644 --- a/platform/projectModel-api/src/com/intellij/openapi/components/Storage.java +++ b/platform/projectModel-api/src/com/intellij/openapi/components/Storage.java @@ -28,6 +28,11 @@ public @interface Storage { @NonNls String file() default ""; StorageScheme scheme() default StorageScheme.DEFAULT; - Class storageClass() default StorageAnnotationsDefaultValues.NullStateStorage.class; - Class stateSplitter() default StorageAnnotationsDefaultValues.NullStateSplitter.class; + /** + * You must not store components with different roaming types in one file ({@link #file()}). + */ + RoamingType roamingType() default RoamingType.PER_USER; + + Class storageClass() default StateStorage.class; + Class stateSplitter() default StateSplitter.class; } diff --git a/platform/projectModel-api/src/com/intellij/openapi/components/StorageAnnotationsDefaultValues.java b/platform/projectModel-api/src/com/intellij/openapi/components/StorageAnnotationsDefaultValues.java deleted file mode 100644 index 5105105c4eb4..000000000000 --- a/platform/projectModel-api/src/com/intellij/openapi/components/StorageAnnotationsDefaultValues.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2000-2012 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.intellij.openapi.components; - -import com.intellij.openapi.util.Pair; -import org.jdom.Element; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.List; -import java.util.Set; - -public interface StorageAnnotationsDefaultValues { - class NullStateStorage implements StateStorage { - @Override - @Nullable - public T getState(final Object component, final String componentName, Class stateClass, @Nullable T mergeInto) - throws StateStorageException { - throw new UnsupportedOperationException("Method getState is not supported in " + getClass()); - } - - @Override - public boolean hasState(final Object component, final String componentName, final Class aClass, final boolean reloadData) throws StateStorageException { - throw new UnsupportedOperationException("Method hasState not implemented in " + getClass()); - } - - public void save() throws StateStorageException { - throw new UnsupportedOperationException("Method save is not supported in " + getClass()); - } - - @Override - @NotNull - public ExternalizationSession startExternalization() { - throw new UnsupportedOperationException("Method startExternalization not implemented in " + getClass()); - } - - @Override - @NotNull - public SaveSession startSave(@NotNull ExternalizationSession externalizationSession) { - throw new UnsupportedOperationException("Method startSave not implemented in " + getClass()); - } - - @Override - public void finishSave(@NotNull SaveSession saveSession) { - throw new UnsupportedOperationException("Method finishSave not implemented in " + getClass()); - } - - @Override - public void reload(@NotNull final Set changedComponents) throws StateStorageException { - throw new UnsupportedOperationException("Method reload not implemented in " + getClass()); - } - - } - - class NullStateStorageChooser implements StateStorageChooser { - @Override - public Storage[] selectStorages(Storage[] storages, Object component, final StateStorageOperation operation) { - throw new UnsupportedOperationException("Method selectStorages is not supported in " + getClass()); - } - } - - class NullStateSplitter implements StateSplitter { - @Override - public List> splitState(Element e) { - throw new UnsupportedOperationException("Method splitState not implemented in " + getClass()); - } - - @Override - public void mergeStatesInto(final Element target, final Element[] elements) { - throw new UnsupportedOperationException("Method mergeStatesInto not implemented in " + getClass()); - } - } -} diff --git a/platform/projectModel-api/src/com/intellij/openapi/components/StoragePathMacros.java b/platform/projectModel-api/src/com/intellij/openapi/components/StoragePathMacros.java index 374423225694..3a292dd7175c 100644 --- a/platform/projectModel-api/src/com/intellij/openapi/components/StoragePathMacros.java +++ b/platform/projectModel-api/src/com/intellij/openapi/components/StoragePathMacros.java @@ -31,9 +31,14 @@ import org.jetbrains.annotations.NotNull; * @since 5/2/12 12:57 PM */ public class StoragePathMacros { + /** + * Points to the application-level settings root directory. + */ @NonNls @NotNull public static final String ROOT_CONFIG = "$ROOT_CONFIG$"; - /** Points to the application-level settings root directory. */ + /** + * Points to {@link #ROOT_CONFIG}/options by default. Should be used to store single files and {@link #ROOT_CONFIG} to store subdirectories with many files. + */ @NonNls @NotNull public static final String APP_CONFIG = "$APP_CONFIG$"; /** '.ipr' file path key. */ @@ -63,6 +68,7 @@ public class StoragePathMacros { * @throws IllegalArgumentException if given macro definition has unexpected format */ @NotNull + @Deprecated public static String getMacroName(@NotNull String macro) throws IllegalArgumentException { if (macro.length() < 3 || macro.charAt(0) != '$' || macro.charAt(macro.length() - 1) != '$') { throw new IllegalArgumentException("Malformed macro definition (" + macro + ")"); diff --git a/platform/projectModel-api/src/com/intellij/openapi/project/DefaultProjectTypeEP.java b/platform/projectModel-api/src/com/intellij/openapi/project/DefaultProjectTypeEP.java new file mode 100644 index 000000000000..3eb65af697c0 --- /dev/null +++ b/platform/projectModel-api/src/com/intellij/openapi/project/DefaultProjectTypeEP.java @@ -0,0 +1,36 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.openapi.project; + +import com.intellij.openapi.extensions.ExtensionPointName; +import com.intellij.util.xmlb.annotations.Attribute; +import org.jetbrains.annotations.Nullable; + +/** + * @author Dmitry Avdeev + */ +public class DefaultProjectTypeEP { + + private final static ExtensionPointName EXTENSION_POINT_NAME = ExtensionPointName.create("com.intellij.defaultProjectTypeProvider"); + @Attribute("type") + public String type; + + @Nullable + public static ProjectType getDefaultProjectType() { + DefaultProjectTypeEP[] extensions = EXTENSION_POINT_NAME.getExtensions(); + return extensions.length > 0 ? new ProjectType(extensions[0].type) : null; + } +} diff --git a/platform/projectModel-api/src/com/intellij/openapi/project/DefaultProjectTypeProvider.java b/platform/projectModel-api/src/com/intellij/openapi/project/DefaultProjectTypeProvider.java deleted file mode 100644 index 81887eba0f98..000000000000 --- a/platform/projectModel-api/src/com/intellij/openapi/project/DefaultProjectTypeProvider.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.intellij.openapi.project; - -import com.intellij.openapi.extensions.ExtensionPointName; -import org.jetbrains.annotations.Nullable; - -/** - * @author Dmitry Avdeev - */ -public abstract class DefaultProjectTypeProvider { - - private final static ExtensionPointName EXTENSION_POINT_NAME = ExtensionPointName.create("com.intellij.defaultProjectTypeProvider"); - - @Nullable - public static ProjectType getDefaultProjectType() { - DefaultProjectTypeProvider[] extensions = EXTENSION_POINT_NAME.getExtensions(); - return extensions.length > 0 ? extensions[0].getProjectType() : null; - } - - protected abstract ProjectType getProjectType(); -} diff --git a/platform/projectModel-api/src/com/intellij/openapi/project/ProjectTypeService.java b/platform/projectModel-api/src/com/intellij/openapi/project/ProjectTypeService.java index e43059390b19..6925a8c39c5e 100644 --- a/platform/projectModel-api/src/com/intellij/openapi/project/ProjectTypeService.java +++ b/platform/projectModel-api/src/com/intellij/openapi/project/ProjectTypeService.java @@ -26,8 +26,10 @@ import org.jetbrains.annotations.Nullable; @State( name = "ProjectType", storages = { - @Storage(file = StoragePathMacros.PROJECT_FILE), - @Storage(file = StoragePathMacros.PROJECT_CONFIG_DIR + "/misc.xml", scheme = StorageScheme.DIRECTORY_BASED) + @Storage( + id = "other", + file = StoragePathMacros.PROJECT_FILE + ) } ) public class ProjectTypeService implements PersistentStateComponent { @@ -41,10 +43,14 @@ public class ProjectTypeService implements PersistentStateComponent projectType = getInstance(project).myProjectType; if (projectType != null) return projectType; } - return DefaultProjectTypeProvider.getDefaultProjectType(); + return DefaultProjectTypeEP.getDefaultProjectType(); } - public static ProjectTypeService getInstance(@NotNull Project project) { + public static void setProjectType(@NotNull Project project, @Nullable ProjectType projectType) { + getInstance(project).loadState(projectType); + } + + private static ProjectTypeService getInstance(@NotNull Project project) { return ServiceManager.getService(project, ProjectTypeService.class); } diff --git a/platform/projectModel-impl/src/com/intellij/application/options/PathMacrosImpl.java b/platform/projectModel-impl/src/com/intellij/application/options/PathMacrosImpl.java index cce7a68526bd..ac70cb252f10 100644 --- a/platform/projectModel-impl/src/com/intellij/application/options/PathMacrosImpl.java +++ b/platform/projectModel-impl/src/com/intellij/application/options/PathMacrosImpl.java @@ -15,41 +15,40 @@ */ package com.intellij.application.options; -import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.PathMacros; -import com.intellij.openapi.components.ExpandMacroToPathMap; -import com.intellij.openapi.components.NamedComponent; +import com.intellij.openapi.components.*; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.util.InvalidDataException; -import com.intellij.openapi.util.NamedJDOMExternalizable; -import com.intellij.openapi.util.RoamingTypeDisabled; -import com.intellij.openapi.util.WriteExternalException; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.HashMap; +import com.intellij.util.containers.hash.LinkedHashMap; import gnu.trove.THashSet; import org.jdom.Element; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.model.serialization.JpsGlobalLoader; import org.jetbrains.jps.model.serialization.PathMacroUtil; import java.util.*; import java.util.concurrent.locks.ReentrantReadWriteLock; -/** - * @author dsl - */ -public class PathMacrosImpl extends PathMacros implements NamedComponent, NamedJDOMExternalizable, RoamingTypeDisabled { - private static final Logger LOG = Logger.getInstance("#com.intellij.application.options.PathMacrosImpl"); +@State( + name = "PathMacrosImpl", + storages = {@Storage(file = StoragePathMacros.APP_CONFIG + "/path.macros.xml", roamingType = RoamingType.PER_PLATFORM)} +) +public class PathMacrosImpl extends PathMacros implements PersistentStateComponent { + private static final Logger LOG = Logger.getInstance(PathMacrosImpl.class); + private final Map myLegacyMacros = new HashMap(); - private final Map myMacros = new HashMap(); + private final Map myMacros = new LinkedHashMap(); private int myModificationStamp = 0; private final ReentrantReadWriteLock myLock = new ReentrantReadWriteLock(); private final List myIgnoredMacros = ContainerUtil.createLockFreeCopyOnWriteList(); - public static final String MACRO_ELEMENT = JpsGlobalLoader.PathVariablesSerializer.MACRO_TAG; - public static final String NAME_ATTR = JpsGlobalLoader.PathVariablesSerializer.NAME_ATTRIBUTE; - public static final String VALUE_ATTR = JpsGlobalLoader.PathVariablesSerializer.VALUE_ATTRIBUTE; + private static final String MACRO_ELEMENT = JpsGlobalLoader.PathVariablesSerializer.MACRO_TAG; + private static final String NAME_ATTR = JpsGlobalLoader.PathVariablesSerializer.NAME_ATTRIBUTE; + private static final String VALUE_ATTR = JpsGlobalLoader.PathVariablesSerializer.VALUE_ATTRIBUTE; @NonNls public static final String IGNORED_MACRO_ELEMENT = "ignoredMacro"; @@ -75,6 +74,7 @@ public class PathMacrosImpl extends PathMacros implements NamedComponent, NamedJ SYSTEM_MACROS.add(USER_HOME_MACRO_NAME); } + @SuppressWarnings("SpellCheckingInspection") private static final Set ourToolsMacros = ContainerUtil.immutableSet( "ClasspathEntry", "Classpath", @@ -138,18 +138,7 @@ public class PathMacrosImpl extends PathMacros implements NamedComponent, NamedJ } public static PathMacrosImpl getInstanceEx() { - return (PathMacrosImpl)ApplicationManager.getApplication().getComponent(PathMacros.class); - } - - @Override - @NotNull - public String getComponentName() { - return "PathMacrosImpl"; - } - - @Override - public String getExternalFileName() { - return EXT_FILE_NAME; + return (PathMacrosImpl)getInstance(); } @Override @@ -286,8 +275,37 @@ public class PathMacrosImpl extends PathMacros implements NamedComponent, NamedJ } } + @Nullable + @Override + public Element getState() { + try { + Element element = new Element("state"); + myLock.writeLock().lock(); + + for (Map.Entry entry : myMacros.entrySet()) { + String value = entry.getValue(); + if (!StringUtil.isEmptyOrSpaces(value)) { + final Element macro = new Element(MACRO_ELEMENT); + macro.setAttribute(NAME_ATTR, entry.getKey()); + macro.setAttribute(VALUE_ATTR, value); + element.addContent(macro); + } + } + + for (final String macro : myIgnoredMacros) { + final Element macroElement = new Element(IGNORED_MACRO_ELEMENT); + macroElement.setAttribute(NAME_ATTR, macro); + element.addContent(macroElement); + } + return element; + } + finally { + myLock.writeLock().unlock(); + } + } + @Override - public void readExternal(Element element) throws InvalidDataException { + public void loadState(Element element) { try { myLock.writeLock().lock(); @@ -297,7 +315,7 @@ public class PathMacrosImpl extends PathMacros implements NamedComponent, NamedJ final String name = macro.getAttributeValue(NAME_ATTR); String value = macro.getAttributeValue(VALUE_ATTR); if (name == null || value == null) { - throw new InvalidDataException(); + continue; } if (SYSTEM_MACROS.contains(name)) { @@ -326,33 +344,6 @@ public class PathMacrosImpl extends PathMacros implements NamedComponent, NamedJ } } - @Override - public void writeExternal(Element element) throws WriteExternalException { - try { - myLock.writeLock().lock(); - - final Set> entries = myMacros.entrySet(); - for (Map.Entry entry : entries) { - final String value = entry.getValue(); - if (value != null && !value.trim().isEmpty()) { - final Element macro = new Element(MACRO_ELEMENT); - macro.setAttribute(NAME_ATTR, entry.getKey()); - macro.setAttribute(VALUE_ATTR, value); - element.addContent(macro); - } - } - - for (final String macro : myIgnoredMacros) { - final Element macroElement = new Element(IGNORED_MACRO_ELEMENT); - macroElement.setAttribute(NAME_ATTR, macro); - element.addContent(macroElement); - } - } - finally { - myLock.writeLock().unlock(); - } - } - public void addMacroReplacements(ReplacePathToMacroMap result) { for (final String name : getUserMacroNames()) { final String value = getValue(name); @@ -360,7 +351,6 @@ public class PathMacrosImpl extends PathMacros implements NamedComponent, NamedJ } } - public void addMacroExpands(ExpandMacroToPathMap result) { for (final String name : getUserMacroNames()) { final String value = getValue(name); diff --git a/platform/projectModel-impl/src/com/intellij/core/CoreModuleManager.java b/platform/projectModel-impl/src/com/intellij/core/CoreModuleManager.java index 7f7e0df4621d..8753b73144d3 100644 --- a/platform/projectModel-impl/src/com/intellij/core/CoreModuleManager.java +++ b/platform/projectModel-impl/src/com/intellij/core/CoreModuleManager.java @@ -26,7 +26,6 @@ import com.intellij.openapi.roots.impl.ModuleRootManagerImpl; import com.intellij.openapi.util.InvalidDataException; import com.intellij.openapi.vfs.StandardFileSystems; import com.intellij.openapi.vfs.VirtualFile; -import org.jdom.Element; import org.jdom.JDOMException; import java.io.IOException; @@ -52,10 +51,10 @@ public class CoreModuleManager extends ModuleManagerImpl { final ModuleEx module = createModule(filePath); VirtualFile vFile = StandardFileSystems.local().findFileByPath(filePath); try { + assert vFile != null; StorageData storageData = CoreProjectLoader.loadStorageFile(module, vFile); - final Element element = storageData.getState("NewModuleRootManager"); ModuleRootManagerImpl.ModuleRootManagerState state = new ModuleRootManagerImpl.ModuleRootManagerState(); - state.readExternal(element); + state.readExternal(storageData.getState("NewModuleRootManager")); ((ModuleRootManagerImpl) ModuleRootManager.getInstance(module)).loadState(state); } catch (JDOMException e) { diff --git a/platform/projectModel-impl/src/com/intellij/core/CoreProjectLoader.java b/platform/projectModel-impl/src/com/intellij/core/CoreProjectLoader.java index 5d8ac1fd4ed2..f05983aff69a 100644 --- a/platform/projectModel-impl/src/com/intellij/core/CoreProjectLoader.java +++ b/platform/projectModel-impl/src/com/intellij/core/CoreProjectLoader.java @@ -27,14 +27,11 @@ import com.intellij.openapi.roots.impl.ProjectRootManagerImpl; import com.intellij.openapi.roots.impl.libraries.LibraryTableBase; import com.intellij.openapi.roots.impl.libraries.ProjectLibraryTable; import com.intellij.openapi.util.InvalidDataException; -import com.intellij.openapi.util.JDOMUtil; import com.intellij.openapi.vfs.VirtualFile; -import org.jdom.Document; import org.jdom.Element; import org.jdom.JDOMException; import org.jetbrains.annotations.NotNull; -import java.io.ByteArrayInputStream; import java.io.IOException; /** @@ -56,7 +53,9 @@ public class CoreProjectLoader { private static void loadDirectoryProject(MockProject project, VirtualFile projectDir) throws IOException, JDOMException, InvalidDataException { VirtualFile dotIdea = projectDir.findChild(Project.DIRECTORY_STORE_FOLDER); + assert dotIdea != null; VirtualFile modulesXml = dotIdea.findChild("modules.xml"); + assert modulesXml != null; StorageData storageData = loadStorageFile(project, modulesXml); final Element moduleManagerState = storageData.getState("ProjectModuleManager"); if (moduleManagerState == null) { @@ -66,6 +65,7 @@ public class CoreProjectLoader { moduleManager.loadState(moduleManagerState); VirtualFile miscXml = dotIdea.findChild("misc.xml"); + assert miscXml != null; storageData = loadStorageFile(project, miscXml); final Element projectRootManagerState = storageData.getState("ProjectRootManager"); if (projectRootManagerState == null) { @@ -85,12 +85,10 @@ public class CoreProjectLoader { project.projectOpened(); } - public static StorageData loadStorageFile(ComponentManager componentManager, VirtualFile modulesXml) throws JDOMException, IOException { - final Document document = JDOMUtil.loadDocument(new ByteArrayInputStream(modulesXml.contentsToByteArray())); + @NotNull + public static StorageData loadStorageFile(@NotNull ComponentManager componentManager, @NotNull VirtualFile modulesXml) throws JDOMException, IOException { StorageData storageData = new StorageData("project"); - final Element element = document.getRootElement(); - PathMacroManager.getInstance(componentManager).expandPaths(element); - storageData.load(element); + storageData.load(StorageData.load(modulesXml), PathMacroManager.getInstance(componentManager), false); return storageData; } } diff --git a/platform/projectModel-impl/src/com/intellij/openapi/components/PathMacroManager.java b/platform/projectModel-impl/src/com/intellij/openapi/components/PathMacroManager.java index 8999bb914dea..046da9d799e6 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/components/PathMacroManager.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/components/PathMacroManager.java @@ -25,12 +25,6 @@ public abstract class PathMacroManager implements PathMacroSubstitutor { return component; } - @Override - public abstract void expandPaths(@NotNull Element element); - - @Override - public abstract void collapsePaths(@NotNull Element element); - public abstract void collapsePathsRecursively(@NotNull Element element); public abstract String collapsePathsRecursively(@NotNull String text); diff --git a/platform/projectModel-impl/src/com/intellij/openapi/components/PathMacroSubstitutor.java b/platform/projectModel-impl/src/com/intellij/openapi/components/PathMacroSubstitutor.java index c79802533a1d..bcffa0bc34ee 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/components/PathMacroSubstitutor.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/components/PathMacroSubstitutor.java @@ -16,6 +16,7 @@ package com.intellij.openapi.components; import org.jdom.Element; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public interface PathMacroSubstitutor { @@ -23,7 +24,7 @@ public interface PathMacroSubstitutor { String collapsePath(@Nullable String path); - void expandPaths(Element element); + void expandPaths(@NotNull Element element); - void collapsePaths(Element element); + void collapsePaths(@NotNull Element element); } diff --git a/platform/projectModel-impl/src/com/intellij/openapi/components/TrackingPathMacroSubstitutor.java b/platform/projectModel-impl/src/com/intellij/openapi/components/TrackingPathMacroSubstitutor.java index 63e3a89431d8..48f33feb7936 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/components/TrackingPathMacroSubstitutor.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/components/TrackingPathMacroSubstitutor.java @@ -15,15 +15,22 @@ */ package com.intellij.openapi.components; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Collection; import java.util.Set; public interface TrackingPathMacroSubstitutor extends PathMacroSubstitutor { + @NotNull Collection getUnknownMacros(@Nullable String componentName); - Collection getComponents(final Collection macros); - void addUnknownMacros(String componentName, Collection unknownMacros); - void invalidateUnknownMacros(Set macros); + + @NotNull + Collection getComponents(@NotNull Collection macros); + + void addUnknownMacros(@NotNull String componentName, @NotNull Collection unknownMacros); + + void invalidateUnknownMacros(@NotNull Set macros); + void reset(); } diff --git a/platform/projectModel-impl/src/com/intellij/openapi/components/impl/BasePathMacroManager.java b/platform/projectModel-impl/src/com/intellij/openapi/components/impl/BasePathMacroManager.java index 9707364c988b..b3d1edb42b4e 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/components/impl/BasePathMacroManager.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/components/impl/BasePathMacroManager.java @@ -31,16 +31,22 @@ import com.intellij.openapi.vfs.StandardFileSystems; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.openapi.vfs.VirtualFileSystem; -import com.intellij.util.containers.FactoryMap; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.containers.MultiMap; +import com.intellij.util.containers.SmartHashSet; +import gnu.trove.THashSet; import org.jdom.Element; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.model.serialization.PathMacroUtil; -import java.util.*; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.Set; public class BasePathMacroManager extends PathMacroManager { - private static final CompositePathMacroFilter ourFilter = new CompositePathMacroFilter(Extensions.getExtensions(PathMacrosCollector.MACRO_FILTER_EXTENSION_POINT_NAME)); + private static final CompositePathMacroFilter FILTER = new CompositePathMacroFilter(Extensions.getExtensions(PathMacrosCollector.MACRO_FILTER_EXTENSION_POINT_NAME)); private PathMacrosImpl myPathMacros; @@ -49,20 +55,26 @@ public class BasePathMacroManager extends PathMacroManager { } protected static void addFileHierarchyReplacements(ExpandMacroToPathMap result, String macroName, @Nullable String path) { - if (path == null) return; - addFileHierarchyReplacements(result, getLocalFileSystem().findFileByPath(path), "$" + macroName + "$"); + if (path != null) { + addFileHierarchyReplacements(result, getLocalFileSystem().findFileByPath(path), '$' + macroName + '$'); + } } private static void addFileHierarchyReplacements(ExpandMacroToPathMap result, @Nullable VirtualFile f, String macro) { - if (f == null) return; + if (f == null) { + return; + } + addFileHierarchyReplacements(result, f.getParent(), macro + "/.."); result.put(macro, StringUtil.trimEnd(f.getPath(), "/")); } protected static void addFileHierarchyReplacements(ReplacePathToMacroMap result, String macroName, @Nullable String path, @Nullable String stopAt) { - if (path == null) return; + if (path == null) { + return; + } - String macro = "$" + macroName + "$"; + String macro = '$' + macroName + '$'; path = StringUtil.trimEnd(FileUtil.toSystemIndependentName(path), "/"); boolean overwrite = true; while (StringUtil.isNotEmpty(path) && path.contains("/")) { @@ -140,25 +152,14 @@ public class BasePathMacroManager extends PathMacroManager { if (myPathMacros == null) { myPathMacros = PathMacrosImpl.getInstanceEx(); } - return myPathMacros; } private class MyTrackingPathMacroSubstitutor implements TrackingPathMacroSubstitutor { private final String myLock = new String("MyTrackingPathMacroSubstitutor.lock"); - private final Map> myMacroToComponentNames = new FactoryMap>() { - @Override - protected Set create(String key) { - return new HashSet(); - } - }; - private final Map> myComponentNameToMacros = new FactoryMap>() { - @Override - protected Set create(String key) { - return new HashSet(); - } - }; + private final MultiMap myMacroToComponentNames = MultiMap.createSet(); + private final MultiMap myComponentNameToMacros = MultiMap.createSet(); public MyTrackingPathMacroSubstitutor() { } @@ -182,13 +183,13 @@ public class BasePathMacroManager extends PathMacroManager { } @Override - public void expandPaths(final Element element) { + public void expandPaths(@NotNull final Element element) { getExpandMacroMap().substitute(element, SystemInfo.isFileSystemCaseSensitive); } @Override - public void collapsePaths(final Element element) { - getReplacePathMap().substitute(element, SystemInfo.isFileSystemCaseSensitive, false, ourFilter); + public void collapsePaths(@NotNull final Element element) { + getReplacePathMap().substitute(element, SystemInfo.isFileSystemCaseSensitive, false, FILTER); } public int hashCode() { @@ -196,52 +197,52 @@ public class BasePathMacroManager extends PathMacroManager { } @Override - public void invalidateUnknownMacros(final Set macros) { + public void invalidateUnknownMacros(@NotNull Set macros) { synchronized (myLock) { - for (final String macro : macros) { - final Set components = myMacroToComponentNames.get(macro); - for (final String component : components) { - myComponentNameToMacros.remove(component); + for (String macro : macros) { + Collection componentNames = myMacroToComponentNames.remove(macro); + if (!ContainerUtil.isEmpty(componentNames)) { + for (String component : componentNames) { + myComponentNameToMacros.remove(component); + } } - - myMacroToComponentNames.remove(macro); } } } + @NotNull @Override - public Collection getComponents(final Collection macros) { + public Collection getComponents(@NotNull Collection macros) { synchronized (myLock) { - final Set result = new HashSet(); - for (String macro : myMacroToComponentNames.keySet()) { - if (macros.contains(macro)) { - result.addAll(myMacroToComponentNames.get(macro)); - } + Set result = new SmartHashSet(); + for (String macro : macros) { + result.addAll(myMacroToComponentNames.get(macro)); } - return result; } } + @NotNull @Override - public Collection getUnknownMacros(final String componentName) { + public Collection getUnknownMacros(@Nullable String componentName) { synchronized (myLock) { - final Set result = new HashSet(); - result.addAll(componentName == null ? myMacroToComponentNames.keySet() : myComponentNameToMacros.get(componentName)); - return Collections.unmodifiableCollection(result); + Collection list = componentName == null ? myMacroToComponentNames.keySet() : myComponentNameToMacros.get(componentName); + return ContainerUtil.isEmpty(list) ? Collections.emptyList() : new THashSet(list); } } @Override - public void addUnknownMacros(final String componentName, final Collection unknownMacros) { - if (unknownMacros.isEmpty()) return; + public void addUnknownMacros(@NotNull String componentName, @NotNull Collection unknownMacros) { + if (unknownMacros.isEmpty()) { + return; + } synchronized (myLock) { for (String unknownMacro : unknownMacros) { - myMacroToComponentNames.get(unknownMacro).add(componentName); + myMacroToComponentNames.putValue(unknownMacro, componentName); } - myComponentNameToMacros.get(componentName).addAll(unknownMacros); + myComponentNameToMacros.putValues(componentName, unknownMacros); } } } diff --git a/platform/projectModel-impl/src/com/intellij/openapi/components/impl/ModulePathMacroManager.java b/platform/projectModel-impl/src/com/intellij/openapi/components/impl/ModulePathMacroManager.java index 704dfec04233..e8cc7bcd040a 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/components/impl/ModulePathMacroManager.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/components/impl/ModulePathMacroManager.java @@ -16,7 +16,6 @@ package com.intellij.openapi.components.impl; -import com.intellij.application.options.PathMacrosImpl; import com.intellij.application.options.ReplacePathToMacroMap; import com.intellij.openapi.application.PathMacros; import com.intellij.openapi.components.ExpandMacroToPathMap; @@ -34,25 +33,21 @@ public class ModulePathMacroManager extends BasePathMacroManager { @Override public ExpandMacroToPathMap getExpandMacroMap() { final ExpandMacroToPathMap result = new ExpandMacroToPathMap(); - if (!myModule.isDisposed()) { - addFileHierarchyReplacements(result, PathMacrosImpl.MODULE_DIR_MACRO_NAME, PathMacroUtil.getModuleDir(myModule.getModuleFilePath())); + addFileHierarchyReplacements(result, PathMacroUtil.MODULE_DIR_MACRO_NAME, PathMacroUtil.getModuleDir(myModule.getModuleFilePath())); } result.putAll(super.getExpandMacroMap()); - return result; } @Override public ReplacePathToMacroMap getReplacePathMap() { final ReplacePathToMacroMap result = super.getReplacePathMap(); - if (!myModule.isDisposed()) { final String modulePath = PathMacroUtil.getModuleDir(myModule.getModuleFilePath()); - addFileHierarchyReplacements(result, PathMacrosImpl.MODULE_DIR_MACRO_NAME, modulePath, PathMacroUtil.getUserHomePath()); + addFileHierarchyReplacements(result, PathMacroUtil.MODULE_DIR_MACRO_NAME, modulePath, PathMacroUtil.getUserHomePath()); } - return result; } } diff --git a/platform/projectModel-impl/src/com/intellij/openapi/components/impl/stores/DefaultStateSerializer.java b/platform/projectModel-impl/src/com/intellij/openapi/components/impl/stores/DefaultStateSerializer.java index 608a350d770c..d4f87027191d 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/components/impl/stores/DefaultStateSerializer.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/components/impl/stores/DefaultStateSerializer.java @@ -40,6 +40,7 @@ class DefaultStateSerializer { private DefaultStateSerializer() { } + @Nullable static Element serializeState(@NotNull Object state, final Storage storage) throws WriteExternalException { if (state instanceof Element) { return (Element)state; @@ -56,12 +57,12 @@ class DefaultStateSerializer { } catch (Throwable e) { LOG.info("Unable to serialize component state!", e); - return new Element("empty"); + return null; } return element; } else { - return XmlSerializer.serialize(state, new SkipDefaultValuesSerializationFilters() { + return XmlSerializer.serialize(state, new SkipDefaultValuesSerializationFilters() { @Override public boolean accepts(final Accessor accessor, final Object bean) { if (!super.accepts(accessor, bean)) return false; diff --git a/platform/projectModel-impl/src/com/intellij/openapi/components/impl/stores/DirectoryStorageData.java b/platform/projectModel-impl/src/com/intellij/openapi/components/impl/stores/DirectoryStorageData.java index 4a9aaed20af8..8ea0e1ef935f 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/components/impl/stores/DirectoryStorageData.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/components/impl/stores/DirectoryStorageData.java @@ -23,8 +23,6 @@ import com.intellij.openapi.util.JDOMUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.PairConsumer; -import com.intellij.util.io.fs.FileSystem; -import com.intellij.util.io.fs.IFile; import gnu.trove.THashMap; import org.jdom.Document; import org.jdom.Element; @@ -37,9 +35,9 @@ import java.io.IOException; import java.util.*; public class DirectoryStorageData { - private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.components.impl.stores.DirectoryStorageData"); + private static final Logger LOG = Logger.getInstance(DirectoryStorageData.class); - private Map> myStates = new HashMap>(); + private Map> myStates = new THashMap>(); private long myLastTimestamp = 0; private DirectoryStorageData myOriginalData; @@ -79,7 +77,7 @@ public class DirectoryStorageData { pathMacroSubstitutor.addUnknownMacros(componentName, unknownMacros); } - put(componentName, FileSystem.FILE_SYSTEM.createFile(file.getPath()), element, true); + put(componentName, new File(file.getPath()), element, true); } catch (IOException e) { LOG.info("Unable to load state", e); @@ -90,44 +88,48 @@ public class DirectoryStorageData { } } - public void put(final String componentName, final IFile file, final Element element, final boolean updateTimestamp) { + public void put(final String componentName, File file, final Element element, final boolean updateTimestamp) { LOG.assertTrue(componentName != null, String.format("Component name should not be null for file: %s", file == null ? "NULL!" : file.getPath())); - Map stateMap = myStates.get(componentName); + Map stateMap = myStates.get(componentName); if (stateMap == null) { - stateMap = new HashMap(); + stateMap = new THashMap(); myStates.put(componentName, stateMap); } stateMap.put(file, element); - if (updateTimestamp) updateLastTimestamp(file); + if (updateTimestamp) { + updateLastTimestamp(file); + } } - public void updateLastTimestamp(final IFile file) { - myLastTimestamp = Math.max(myLastTimestamp, file.getTimeStamp()); - if (myOriginalData != null) myOriginalData.myLastTimestamp = myLastTimestamp; + public void updateLastTimestamp(File file) { + myLastTimestamp = Math.max(myLastTimestamp, file.lastModified()); + if (myOriginalData != null) { + myOriginalData.myLastTimestamp = myLastTimestamp; + } } public long getLastTimeStamp() { return myLastTimestamp; } - public Map getAllStorageFiles() { - final Map allStorageFiles = new THashMap(); + public Map getAllStorageFiles() { + final Map allStorageFiles = new THashMap(); process(new StorageDataProcessor() { @Override - public void process(final String componentName, final IFile file, final Element element) { - allStorageFiles.put(file, file.getTimeStamp()); + public void process(final String componentName, final File file, final Element element) { + allStorageFiles.put(file, file.lastModified()); } }); return allStorageFiles; } - public void processComponent(@NotNull final String componentName, @NotNull final PairConsumer consumer) { - final Map map = myStates.get(componentName); + public void processComponent(@NotNull final String componentName, @NotNull final PairConsumer consumer) { + final Map map = myStates.get(componentName); if (map != null) { - for (IFile file : map.keySet()) { + for (File file : map.keySet()) { consumer.consume(file, map.get(file)); } } @@ -135,10 +137,10 @@ public class DirectoryStorageData { public void process(@NotNull final StorageDataProcessor processor) { for (final String componentName : myStates.keySet()) { - processComponent(componentName, new PairConsumer() { + processComponent(componentName, new PairConsumer() { @Override - public void consume(final IFile iFile, final Element element) { - processor.process(componentName, iFile, element); + public void consume(File file, Element element) { + processor.process(componentName, file, element); } }); } @@ -147,7 +149,7 @@ public class DirectoryStorageData { @Override protected DirectoryStorageData clone() { final DirectoryStorageData result = new DirectoryStorageData(); - result.myStates = new HashMap>(myStates); + result.myStates = new HashMap>(myStates); result.myLastTimestamp = myLastTimestamp; result.myOriginalData = this; return result; @@ -169,9 +171,9 @@ public class DirectoryStorageData { @Nullable public T getMergedState(String componentName, Class stateClass, StateSplitter splitter, @Nullable T mergeInto) { final List subElements = new ArrayList(); - processComponent(componentName, new PairConsumer() { + processComponent(componentName, new PairConsumer() { @Override - public void consume(final IFile iFile, final Element element) { + public void consume(File file, Element element) { final List children = element.getChildren(); assert children.size() == 1 : JDOMUtil.writeElement(element, File.separator); final Element subElement = (Element)children.get(0); @@ -188,6 +190,6 @@ public class DirectoryStorageData { } interface StorageDataProcessor { - void process(String componentName, IFile file, Element element); + void process(String componentName, File file, Element element); } } diff --git a/platform/projectModel-impl/src/com/intellij/openapi/components/impl/stores/StorageData.java b/platform/projectModel-impl/src/com/intellij/openapi/components/impl/stores/StorageData.java index 82dece3ba5e1..699440e22b54 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/components/impl/stores/StorageData.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/components/impl/stores/StorageData.java @@ -16,6 +16,7 @@ package com.intellij.openapi.components.impl.stores; import com.intellij.application.options.PathMacrosCollector; +import com.intellij.ide.plugins.IdeaPluginDescriptorImpl; import com.intellij.openapi.components.PathMacroSubstitutor; import com.intellij.openapi.components.TrackingPathMacroSubstitutor; import com.intellij.openapi.components.XmlConfigurationMerger; @@ -23,20 +24,23 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.extensions.ExtensionPoint; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.util.JDOMUtil; +import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.ArrayUtil; import gnu.trove.THashMap; import gnu.trove.THashSet; import org.jdom.Attribute; import org.jdom.Element; +import org.jdom.JDOMException; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.IOException; +import java.io.InputStream; import java.util.*; public class StorageData { - private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.components.impl.stores.StorageData"); + private static final Logger LOG = Logger.getInstance(StorageData.class); @NonNls public static final String COMPONENT = "component"; @NonNls public static final String NAME = "name"; @@ -44,17 +48,21 @@ public class StorageData { protected final String myRootElementName; private int myHash = -1; - public StorageData(final String rootElementName) { + public StorageData(@NotNull String rootElementName) { myComponentStates = new THashMap(); myRootElementName = rootElementName; } - StorageData(StorageData storageData) { + StorageData(@NotNull StorageData storageData) { myRootElementName = storageData.myRootElementName; myComponentStates = new THashMap(storageData.myComponentStates); } - public void load(@NotNull Element rootElement) throws IOException { + public void load(@NotNull Element rootElement, @Nullable PathMacroSubstitutor pathMacroSubstitutor, boolean intern) { + if (pathMacroSubstitutor != null) { + pathMacroSubstitutor.expandPaths(rootElement); + } + for (Iterator iterator = rootElement.getChildren(COMPONENT).iterator(); iterator.hasNext(); ) { Element element = iterator.next(); String name = element.getAttributeValue(NAME); @@ -63,33 +71,47 @@ public class StorageData { continue; } - iterator.remove(); - if (element.getAttributes().size() > 1 || !element.getChildren().isEmpty()) { assert element.getAttributeValue(NAME) != null : "No name attribute for component: " + name + " in " + this; - Element existingElement = myComponentStates.get(name); - if (existingElement != null) { - element = mergeElements(name, element, existingElement); + iterator.remove(); + if (intern) { + IdeaPluginDescriptorImpl.internJDOMElement(element); + } + + Element serverElement = myComponentStates.get(name); + if (serverElement != null) { + element = mergeElements(name, element, serverElement); } myComponentStates.put(name, element); } } + + if (pathMacroSubstitutor instanceof TrackingPathMacroSubstitutor) { + for (String componentName : myComponentStates.keySet()) { + ((TrackingPathMacroSubstitutor)pathMacroSubstitutor).addUnknownMacros(componentName, PathMacrosCollector.getMacroNames(myComponentStates.get(componentName))); + } + } } - private static Element mergeElements(final String name, final Element element1, final Element element2) { + @NotNull + private static Element mergeElements(@NotNull String name, @NotNull Element localElement, @NotNull Element serverElement) { ExtensionPoint point = Extensions.getRootArea().getExtensionPoint("com.intellij.componentConfigurationMerger"); for (XmlConfigurationMerger merger : point.getExtensions()) { if (merger.getComponentName().equals(name)) { - return merger.merge(element1, element2); + return merger.merge(serverElement, localElement); } } - return element1; + return serverElement; } - @NotNull + @Nullable protected Element save() { + if (myComponentStates.isEmpty()) { + return null; + } + Element rootElement = new Element(myRootElementName); String[] componentNames = ArrayUtil.toStringArray(myComponentStates.keySet()); Arrays.sort(componentNames); @@ -101,7 +123,6 @@ public class StorageData { rootElement.addContent(element.clone()); } - return rootElement; } @@ -197,20 +218,18 @@ public class StorageData { return myComponentStates.isEmpty(); } - public boolean hasState(final String componentName) { + public boolean hasState(@NotNull String componentName) { return myComponentStates.containsKey(componentName); } - public void checkUnknownMacros(TrackingPathMacroSubstitutor pathMacroSubstitutor) { - if (pathMacroSubstitutor == null) { - return; + @NotNull + public static Element load(@NotNull VirtualFile file) throws IOException, JDOMException { + InputStream stream = file.getInputStream(); + try { + return JDOMUtil.loadDocument(stream).getRootElement(); } - - for (String componentName : myComponentStates.keySet()) { - final Set unknownMacros = PathMacrosCollector.getMacroNames(myComponentStates.get(componentName)); - if (!unknownMacros.isEmpty()) { - pathMacroSubstitutor.addUnknownMacros(componentName, unknownMacros); - } + finally { + stream.close(); } } } diff --git a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/RootIndex.java b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/RootIndex.java index bd9531a2d888..a55f2a1e0b00 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/RootIndex.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/RootIndex.java @@ -71,6 +71,13 @@ public class RootIndex { private final TObjectIntHashMap> myRootTypeId = new TObjectIntHashMap>(); @NotNull private final Project myProject; private volatile Map myOrderEntries; + @SuppressWarnings("UnusedDeclaration") + private final LowMemoryWatcher myLowMemoryWatcher = LowMemoryWatcher.register(new Runnable() { + @Override + public void run() { + myNonExistentPackages.clear(); + } + }); // made public for Upsource public RootIndex(@NotNull Project project, @NotNull InfoCache cache) { @@ -90,12 +97,6 @@ public class RootIndex { myProjectExcludedRoots.add(root); } } - LowMemoryWatcher.register(new Runnable() { - @Override - public void run() { - myNonExistentPackages.clear(); - } - }, project); } @NotNull diff --git a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/libraries/JarDirectories.java b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/libraries/JarDirectories.java index e9e4da53dddd..c4903098fa87 100644 --- a/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/libraries/JarDirectories.java +++ b/platform/projectModel-impl/src/com/intellij/openapi/roots/impl/libraries/JarDirectories.java @@ -106,7 +106,7 @@ public class JarDirectories implements JDOMExternalizable { @Override public String toString() { - return "Jar dirs: " + myDirectories.values(); + return "JAR dirs: " + myDirectories.values(); } diff --git a/platform/projectModel-impl/src/messages/ProjectBundle.properties b/platform/projectModel-impl/src/messages/ProjectBundle.properties index a0fe65ad981a..956bb8a0d2d2 100644 --- a/platform/projectModel-impl/src/messages/ProjectBundle.properties +++ b/platform/projectModel-impl/src/messages/ProjectBundle.properties @@ -112,7 +112,7 @@ module.paths.edit.properties.title=Edit Root Properties module.javadoc.title=JavaDoc module.javadoc.add.url.button=Add JavaDoc URL... module.javadoc.add.path.title=Add Path To JavaDoc -module.javadoc.add.path.prompt=Select jar/zip files or directories in which module javadoc documentation is located +module.javadoc.add.path.prompt=Select JAR/zip files or directories in which module javadoc documentation is located jdk.combo.box.project.item= jdk.combo.box.none.item= jdk.combo.box.invalid.item={0} [Invalid] @@ -134,7 +134,7 @@ library.sources.not.found=Sources not found library.sources.not.attached=Sources not attached library.attach.sources.action=Attach Sources library.attach.sources.action.busy.text=Attaching... -library.attach.sources.description=Select jar/zip files or directories in which library sources are located +library.attach.sources.description=Select JAR/zip files or directories in which library sources are located project.sdk.not.defined=Project SDK is not defined project.sdk.setup=Setup SDK diff --git a/platform/remote-servers/agent-rt/src/com/intellij/remoteServer/agent/util/CloudAgentLoggingHandler.java b/platform/remote-servers/agent-rt/src/com/intellij/remoteServer/agent/util/CloudAgentLoggingHandler.java index 7055bace613b..029909ad9259 100644 --- a/platform/remote-servers/agent-rt/src/com/intellij/remoteServer/agent/util/CloudAgentLoggingHandler.java +++ b/platform/remote-servers/agent-rt/src/com/intellij/remoteServer/agent/util/CloudAgentLoggingHandler.java @@ -17,6 +17,8 @@ package com.intellij.remoteServer.agent.util; import com.intellij.remoteServer.agent.util.log.LogListener; +import java.io.OutputStream; + /** * @author michael.golubev */ @@ -25,4 +27,6 @@ public interface CloudAgentLoggingHandler { void println(String message); LogListener getOrCreateLogListener(String pipeName); + + LogListener createConsole(String pipeName, OutputStream consoleInput); } diff --git a/platform/remote-servers/api/remote-servers-api.iml b/platform/remote-servers/api/remote-servers-api.iml index f5559bb8e730..4f23c02e26b7 100644 --- a/platform/remote-servers/api/remote-servers-api.iml +++ b/platform/remote-servers/api/remote-servers-api.iml @@ -8,7 +8,6 @@ - diff --git a/platform/remote-servers/impl/src/com/intellij/remoteServer/util/CloudConfigurableBase.java b/platform/remote-servers/impl/src/com/intellij/remoteServer/util/CloudConfigurableBase.java index 4fb4bed03d1e..2e0797f01ee5 100644 --- a/platform/remote-servers/impl/src/com/intellij/remoteServer/util/CloudConfigurableBase.java +++ b/platform/remote-servers/impl/src/com/intellij/remoteServer/util/CloudConfigurableBase.java @@ -3,7 +3,13 @@ package com.intellij.remoteServer.util; import com.intellij.execution.configurations.RuntimeConfigurationError; import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.options.UnnamedConfigurable; +import com.intellij.openapi.util.Comparing; +import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.remoteServer.ServerType; +import com.intellij.remoteServer.configuration.RemoteServer; +import com.intellij.remoteServer.configuration.RemoteServersManager; +import com.intellij.util.text.UniqueNameGenerator; import org.jetbrains.annotations.Nullable; import javax.swing.*; @@ -13,12 +19,18 @@ import javax.swing.*; */ public abstract class CloudConfigurableBase implements UnnamedConfigurable { + private final ServerType myCloudType; protected final SC myConfiguration; - public CloudConfigurableBase(SC configuration) { + public CloudConfigurableBase(ServerType cloudType, SC configuration) { + myCloudType = cloudType; myConfiguration = configuration; } + protected final ServerType getCloudType() { + return myCloudType; + } + @Nullable @Override public JComponent createComponent() { @@ -33,6 +45,20 @@ public abstract class CloudConfigurableBase i @Override public void apply() throws ConfigurationException { + applyCoreTo(myConfiguration); + } + + @Override + public void reset() { + getEmailTextField().setText(myConfiguration.getEmail()); + getPasswordField().setText(myConfiguration.getPassword()); + } + + @Override + public void disposeUIResources() { + } + + protected void applyCoreTo(SC configuration) throws ConfigurationException { String email = getEmailTextField().getText(); if (StringUtil.isEmpty(email)) { throw new RuntimeConfigurationError("Email required"); @@ -42,18 +68,40 @@ public abstract class CloudConfigurableBase i throw new RuntimeConfigurationError("Password required"); } - myConfiguration.setEmail(email); - myConfiguration.setPassword(password); + configuration.setEmail(email); + configuration.setPassword(password); } - @Override - public void reset() { - getEmailTextField().setText(myConfiguration.getEmail()); - getPasswordField().setText(myConfiguration.getPassword()); + protected boolean isCoreConfigEqual(SC configuration1, SC configuration2) { + return Comparing.equal(configuration1.getEmail(), configuration2.getEmail()) + && Comparing.equal(configuration1.getPassword(), configuration2.getPassword()); } - @Override - public void disposeUIResources() { + private String generateServerName() { + return UniqueNameGenerator.generateUniqueName(myCloudType.getPresentableName(), new Condition() { + + @Override + public boolean value(String s) { + for (RemoteServer server : RemoteServersManager.getInstance().getServers()) { + if (server.getName().equals(s)) { + return false; + } + } + return true; + } + }); + } + + protected final RemoteServer createTempServer() { + RemoteServer tempServer = RemoteServersManager.getInstance().createServer(myCloudType, generateServerName()); + SC newConfiguration = tempServer.getConfiguration(); + try { + applyCoreTo(newConfiguration); + } + catch (ConfigurationException e) { + return null; + } + return tempServer; } protected abstract JComponent getMainPanel(); diff --git a/platform/remote-servers/impl/src/com/intellij/remoteServer/util/CloudGitApplicationRuntime.java b/platform/remote-servers/impl/src/com/intellij/remoteServer/util/CloudGitApplicationRuntime.java index 5f40b5ac4257..3a34bb456ad6 100644 --- a/platform/remote-servers/impl/src/com/intellij/remoteServer/util/CloudGitApplicationRuntime.java +++ b/platform/remote-servers/impl/src/com/intellij/remoteServer/util/CloudGitApplicationRuntime.java @@ -45,11 +45,11 @@ public class CloudGitApplicationRuntime extends CloudApplicationRuntime { myDeployment = serverRuntime.getAgent().createDeployment(applicationName, myLoggingHandler); } - protected CloudMultiSourceServerRuntimeInstance getServerRuntime() { + public CloudMultiSourceServerRuntimeInstance getServerRuntime() { return myServerRuntime; } - protected DeploymentLogManager getLogManager() { + public DeploymentLogManager getLogManager() { return myLogManager; } diff --git a/platform/remote-servers/impl/src/com/intellij/remoteServer/util/CloudLoggingHandlerImpl.java b/platform/remote-servers/impl/src/com/intellij/remoteServer/util/CloudLoggingHandlerImpl.java index 346bd0af3046..89535ab6b8e6 100644 --- a/platform/remote-servers/impl/src/com/intellij/remoteServer/util/CloudLoggingHandlerImpl.java +++ b/platform/remote-servers/impl/src/com/intellij/remoteServer/util/CloudLoggingHandlerImpl.java @@ -15,11 +15,14 @@ */ package com.intellij.remoteServer.util; +import com.intellij.execution.process.ProcessHandler; import com.intellij.remoteServer.agent.util.CloudAgentLoggingHandler; import com.intellij.remoteServer.agent.util.log.LogListener; import com.intellij.remoteServer.runtime.deployment.DeploymentLogManager; import com.intellij.remoteServer.runtime.log.LoggingHandler; +import org.jetbrains.annotations.Nullable; +import java.io.OutputStream; import java.util.HashMap; /** @@ -60,4 +63,40 @@ public class CloudLoggingHandlerImpl implements CloudAgentLoggingHandler { } return logListener; } + + @Override + public LogListener createConsole(String pipeName, final OutputStream consoleInput) { + final LoggingHandler loggingHandler = myLogManager.addAdditionalLog(pipeName); + loggingHandler.attachToProcess(new ProcessHandler() { + + @Override + protected void destroyProcessImpl() { + + } + + @Override + protected void detachProcessImpl() { + + } + + @Override + public boolean detachIsDefault() { + return false; + } + + @Nullable + @Override + public OutputStream getProcessInput() { + return consoleInput; + } + }); + + return new LogListener() { + + @Override + public void lineLogged(String line) { + loggingHandler.print(line); + } + }; + } } diff --git a/platform/remote-servers/impl/src/com/intellij/remoteServer/util/CloudSilentLoggingHandlerImpl.java b/platform/remote-servers/impl/src/com/intellij/remoteServer/util/CloudSilentLoggingHandlerImpl.java index 49321fdfc398..31825120100a 100644 --- a/platform/remote-servers/impl/src/com/intellij/remoteServer/util/CloudSilentLoggingHandlerImpl.java +++ b/platform/remote-servers/impl/src/com/intellij/remoteServer/util/CloudSilentLoggingHandlerImpl.java @@ -4,6 +4,8 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.remoteServer.agent.util.CloudAgentLoggingHandler; import com.intellij.remoteServer.agent.util.log.LogListener; +import java.io.OutputStream; + /** * @author michael.golubev */ @@ -20,4 +22,9 @@ public class CloudSilentLoggingHandlerImpl implements CloudAgentLoggingHandler { public LogListener getOrCreateLogListener(String pipeName) { return LogListener.NULL; } + + @Override + public LogListener createConsole(String pipeName, OutputStream consoleInput) { + return LogListener.NULL; + } } diff --git a/platform/script-debugger/backend/src/org/jetbrains/debugger/sourcemap/SourceMap.java b/platform/script-debugger/backend/src/org/jetbrains/debugger/sourcemap/SourceMap.java index a69b3af26f69..057d96fd01eb 100644 --- a/platform/script-debugger/backend/src/org/jetbrains/debugger/sourcemap/SourceMap.java +++ b/platform/script-debugger/backend/src/org/jetbrains/debugger/sourcemap/SourceMap.java @@ -16,16 +16,23 @@ public class SourceMap { private final String outFile; private final SourceResolver sourceResolver; + private final boolean hasNameMappings; // sources - is not originally specified, but canonicalized/normalized public SourceMap(@Nullable String outFile, @NotNull MappingList mappings, @NotNull MappingList[] sourceIndexToMappings, - @NotNull SourceResolver sourceResolver) { + @NotNull SourceResolver sourceResolver, + boolean hasNameMappings) { this.outFile = outFile; this.mappings = mappings; this.sourceIndexToMappings = sourceIndexToMappings; this.sourceResolver = sourceResolver; + this.hasNameMappings = hasNameMappings; + } + + public boolean hasNameMappings() { + return hasNameMappings; } @NotNull diff --git a/platform/script-debugger/backend/src/org/jetbrains/debugger/sourcemap/SourceMapDecoder.java b/platform/script-debugger/backend/src/org/jetbrains/debugger/sourcemap/SourceMapDecoder.java index 27bd8abfe0ce..88ed91ce0580 100644 --- a/platform/script-debugger/backend/src/org/jetbrains/debugger/sourcemap/SourceMapDecoder.java +++ b/platform/script-debugger/backend/src/org/jetbrains/debugger/sourcemap/SourceMapDecoder.java @@ -5,6 +5,7 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.util.text.StringUtilRt; import com.intellij.util.PathUtil; import com.intellij.util.SmartList; +import com.intellij.util.containers.ContainerUtil; import com.intellij.util.text.CharSequenceSubSequence; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -131,7 +132,12 @@ public final class SourceMapDecoder { if (reader.peek() != JsonToken.END_ARRAY) { sourcesContent = new SmartList(); do { - sourcesContent.add(StringUtilRt.convertLineSeparators(reader.nextString())); + if (reader.peek() == JsonToken.STRING) { + sourcesContent.add(StringUtilRt.convertLineSeparators(reader.nextString())); + } + else { + reader.skipValue(); + } } while (reader.hasNext()); } @@ -176,7 +182,7 @@ public final class SourceMapDecoder { sourceToEntries[i] = new SourceMappingList(entries); } } - return new SourceMap(file, new GeneratedMappingList(mappings), sourceToEntries, sourceResolverFactory.create(sources, sourcesContent)); + return new SourceMap(file, new GeneratedMappingList(mappings), sourceToEntries, sourceResolverFactory.create(sources, sourcesContent), !ContainerUtil.isEmpty(names)); } @Nullable @@ -429,4 +435,4 @@ public final class SourceMapDecoder { return MAPPING_COMPARATOR_BY_GENERATED_POSITION; } } -} \ No newline at end of file +} diff --git a/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/BasicDebuggerViewSupport.java b/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/BasicDebuggerViewSupport.java index a5cf778db7e2..b2fd544d0b86 100644 --- a/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/BasicDebuggerViewSupport.java +++ b/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/BasicDebuggerViewSupport.java @@ -95,6 +95,17 @@ public class BasicDebuggerViewSupport implements DebuggerViewSupport, MemberFilt return Collections.emptyList(); } + @NotNull + @Override + public String getName(@NotNull Variable variable) { + return variable.getName(); + } + + @Override + public boolean hasNameMappings() { + return false; + } + @NotNull @Override public String normalizeMemberName(@NotNull Variable variable) { diff --git a/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/LazyVariablesGroup.java b/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/LazyVariablesGroup.java index 25b8b3e11ee5..c00f6727203d 100644 --- a/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/LazyVariablesGroup.java +++ b/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/LazyVariablesGroup.java @@ -71,7 +71,7 @@ public final class LazyVariablesGroup extends XValueGroup { @Override public void consumeVariables(@NotNull List variables) { - node.addChildren(Variables.createVariablesList(variables, context), true); + node.addChildren(Variables.createVariablesList(variables, context, null), true); } }, componentType); } diff --git a/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/MemberFilter.java b/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/MemberFilter.java index fb3a7c1f175c..b9b60178e5ff 100644 --- a/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/MemberFilter.java +++ b/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/MemberFilter.java @@ -9,4 +9,9 @@ public interface MemberFilter { @NotNull Collection getAdditionalVariables(); + + @NotNull + String getName(@NotNull Variable variable); + + boolean hasNameMappings(); } \ No newline at end of file diff --git a/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/VariableView.java b/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/VariableView.java index ada850da074b..9ee54199ff8c 100644 --- a/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/VariableView.java +++ b/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/VariableView.java @@ -57,7 +57,11 @@ public final class VariableView extends XNamedValue implements VariableContext { private volatile int remainingChildrenOffset; public VariableView(@NotNull Variable variable, @NotNull VariableContext context) { - super(context.getViewSupport().normalizeMemberName(variable)); + this(context.getViewSupport().normalizeMemberName(variable), variable, context); + } + + public VariableView(@NotNull String name, @NotNull Variable variable, @NotNull VariableContext context) { + super(name); this.context = context; this.variable = variable; @@ -162,14 +166,7 @@ public final class VariableView extends XNamedValue implements VariableContext { @Override public void consume(Value value, String error) { if (!node.isObsolete()) { - value = getViewSupport().transformErrorOnGetUsedReferenceValue(value, error); - if (value == null) { - node.setPresentation(AllIcons.Debugger.Db_primitive, null, error, false); - } - else { - VariableView.this.value = value; - computePresentation(value, node); - } + setEvaluatedValue(getViewSupport().transformErrorOnGetUsedReferenceValue(value, error), error, node); } } }); @@ -187,18 +184,29 @@ public final class VariableView extends XNamedValue implements VariableContext { public void startEvaluation(@NotNull final XFullValueEvaluationCallback callback) { ValueModifier valueModifier = variable.getValueModifier(); assert valueModifier != null; - ObsolescentAsyncResults.consume(valueModifier.evaluateGet(variable, getEvaluateContext()), node, new PairConsumer() { + valueModifier.evaluateGet(variable, getEvaluateContext()).doWhenProcessed(new Consumer() { @Override - public void consume(Value value, XValueNode node) { - callback.evaluated(""); - VariableView.this.value = value; - computePresentation(value, node); + public void consume(Value value) { + if (!node.isObsolete()) { + callback.evaluated(""); + setEvaluatedValue(value, null, node); + } } }); } }.setShowValuePopup(false)); } + private void setEvaluatedValue(@Nullable Value value, @Nullable String error, @NotNull XValueNode node) { + if (value == null) { + node.setPresentation(AllIcons.Debugger.Db_primitive, null, error == null ? "Internal Error" : error, false); + } + else { + this.value = value; + computePresentation(value, node); + } + } + @NotNull static String trimFunctionDescription(@NotNull Value value) { String presentableValue = value.getValueString(); @@ -358,7 +366,7 @@ public final class VariableView extends XNamedValue implements VariableContext { @Override public void consumeVariables(@NotNull List variables) { - node.addChildren(Variables.createVariablesList(variables, VariableView.this), isLastChildren); + node.addChildren(Variables.createVariablesList(variables, VariableView.this, null), isLastChildren); } }, null); } @@ -395,7 +403,7 @@ public final class VariableView extends XNamedValue implements VariableContext { int count = variables.size(); int bucketSize = XCompositeNode.MAX_CHILDREN_TO_SHOW; if (count <= bucketSize) { - node.addChildren(Variables.createVariablesList(variables, this), true); + node.addChildren(Variables.createVariablesList(variables, this, null), true); return; } @@ -413,7 +421,7 @@ public final class VariableView extends XNamedValue implements VariableContext { int notGroupedVariablesOffset; if ((variables.size() - count) > bucketSize) { for (notGroupedVariablesOffset = variables.size(); notGroupedVariablesOffset > 0; notGroupedVariablesOffset--) { - if (!variables.get(notGroupedVariablesOffset - 1).getName().startsWith(Variables.SPECIAL_PROPERTY_PREFIX)) { + if (!variables.get(notGroupedVariablesOffset - 1).getName().startsWith("__")) { break; } } diff --git a/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/Variables.java b/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/Variables.java index b71914443eef..e85091320bfb 100644 --- a/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/Variables.java +++ b/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/Variables.java @@ -18,13 +18,11 @@ import java.util.*; import java.util.regex.Pattern; public final class Variables { - static final String SPECIAL_PROPERTY_PREFIX = "__"; - private static final Pattern UNNAMED_FUNCTION_PATTERN = Pattern.compile("^function[\\t ]*\\("); private static final Comparator NATURAL_NAME_COMPARATOR = new Comparator() { @Override - public int compare(Variable o1, Variable o2) { + public int compare(@NotNull Variable o1, @NotNull Variable o2) { return naturalCompare(o1.getName(), o2.getName()); } }; @@ -37,7 +35,7 @@ public final class Variables { AsyncResult result = ObsolescentAsyncResults.consume(scope.getVariables(), node, new PairConsumer, XCompositeNode>() { @Override public void consume(List variables, XCompositeNode node) { - MemberFilter memberFilter = context.createMemberFilter(); + final MemberFilter memberFilter = context.createMemberFilter(); Collection additionalVariables = memberFilter.getAdditionalVariables(); List properties = new ArrayList(variables.size() + additionalVariables.size()); List functions = new SmartList(); @@ -56,7 +54,12 @@ public final class Variables { } } - sort(properties); + ContainerUtil.sort(properties, memberFilter.hasNameMappings() ? new Comparator() { + @Override + public int compare(@NotNull Variable o1, @NotNull Variable o2) { + return naturalCompare(memberFilter.getName(o1), memberFilter.getName(o2)); + } + } : NATURAL_NAME_COMPARATOR); sort(functions); for (Variable variable : additionalVariables) { @@ -64,7 +67,7 @@ public final class Variables { } if (!properties.isEmpty()) { - node.addChildren(createVariablesList(properties, context), functions.isEmpty() && isLast); + node.addChildren(createVariablesList(properties, context, memberFilter), functions.isEmpty() && isLast); } if (!functions.isEmpty()) { @@ -110,6 +113,7 @@ public final class Variables { } } + @NotNull public static List filterAndSort(@NotNull List variables, @NotNull VariableContext context, boolean filterFunctions) { if (variables.isEmpty()) { return Collections.emptyList(); @@ -219,16 +223,28 @@ public final class Variables { return string1Length - string2Length; } + @NotNull public static XValueChildrenList createVariablesList(@NotNull List variables, @NotNull VariableContext variableContext) { - return createVariablesList(variables, 0, variables.size(), variableContext); + return createVariablesList(variables, variableContext, null); + } + + @NotNull + public static XValueChildrenList createVariablesList(@NotNull List variables, @NotNull VariableContext variableContext, @Nullable MemberFilter memberFilter) { + return createVariablesList(variables, 0, variables.size(), variableContext, memberFilter); } + @NotNull public static XValueChildrenList createVariablesList(@NotNull List variables, int from, int to, @NotNull VariableContext variableContext) { + return createVariablesList(variables, from, to, variableContext, null); + } + + @NotNull + public static XValueChildrenList createVariablesList(@NotNull List variables, int from, int to, @NotNull VariableContext variableContext, @Nullable MemberFilter memberFilter) { XValueChildrenList list = new XValueChildrenList(to - from); VariableContext getterOrSetterContext = null; for (int i = from; i < to; i++) { Variable variable = variables.get(i); - list.add(new VariableView(variable, variableContext)); + list.add(memberFilter == null ? new VariableView(variable, variableContext) : new VariableView(memberFilter.getName(variable), variable, variableContext)); if (variable instanceof ObjectProperty) { ObjectProperty property = (ObjectProperty)variable; if (property.getGetter() != null) { diff --git a/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/frame/CallFrameView.java b/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/frame/CallFrameView.java index 3cb993a01194..03d960362183 100644 --- a/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/frame/CallFrameView.java +++ b/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/frame/CallFrameView.java @@ -36,6 +36,11 @@ public final class CallFrameView extends StackFrameImplBase implements VariableC inLibraryContent = sourceInfo != null && debugProcess.isInLibraryContent(sourceInfo, script); } + @Nullable + public Script getScript() { + return script; + } + @Override protected boolean isInFileScope() { List scopes = callFrame.getVariableScopes(); diff --git a/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/GeneralTestEventsProcessor.java b/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/GeneralTestEventsProcessor.java index 23145d52702f..a2f594d04cb2 100644 --- a/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/GeneralTestEventsProcessor.java +++ b/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/GeneralTestEventsProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ import com.intellij.openapi.Disposable; import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.util.Condition; +import com.intellij.openapi.util.Conditions; import com.intellij.openapi.util.Key; import com.intellij.testIntegration.TestLocationProvider; import com.intellij.util.Processor; @@ -110,7 +111,7 @@ public abstract class GeneralTestEventsProcessor implements Disposable { } public Condition getDisposedCondition() { - return Condition.FALSE; + return Conditions.alwaysFalse(); } /** diff --git a/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/states/TestComparisionFailedState.java b/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/states/TestComparisionFailedState.java index 2d500115c038..461001d3b111 100644 --- a/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/states/TestComparisionFailedState.java +++ b/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/states/TestComparisionFailedState.java @@ -30,7 +30,7 @@ import org.jetbrains.annotations.Nullable; /** * @author Roman.Chernyatchik */ -public class TestComparisionFailedState extends TestFailedState implements AbstractTestProxy.AssertEqualsDiffViewerProvider { +public class TestComparisionFailedState extends TestFailedState implements AbstractTestProxy.AssertEqualsMultiDiffViewProvider { private final String myErrorMsgPresentation; private final String myStacktracePresentation; private DiffHyperlink myHyperlink; @@ -77,4 +77,14 @@ public class TestComparisionFailedState extends TestFailedState implements Abstr public String getActual() { return myHyperlink.getRight(); } + + @Override + public void openMultiDiff(Project project, AbstractTestProxy.AssertEqualsDiffChain chain) { + myHyperlink.openMultiDiff(project, chain); + } + + @Override + public String getFilePath() { + return myHyperlink.getFilePath(); + } } diff --git a/platform/structuralsearch/testSource/com/intellij/structuralsearch/StructuralReplaceTest.java b/platform/structuralsearch/testSource/com/intellij/structuralsearch/StructuralReplaceTest.java index fe1b6e09d99f..ffdc2fb3459c 100644 --- a/platform/structuralsearch/testSource/com/intellij/structuralsearch/StructuralReplaceTest.java +++ b/platform/structuralsearch/testSource/com/intellij/structuralsearch/StructuralReplaceTest.java @@ -1810,13 +1810,17 @@ public class StructuralReplaceTest extends StructuralReplaceTestCase { } public void testUseStaticImport() { - final String in = "class X {{ Math.abs(-1); }}"; + String in = "class X {{ Math.abs(-1); }}"; final String what = "Math.abs('a)"; final String by = "Math.abs($a$)"; options.setToUseStaticImport(true); - final String expected = "import static java.lang.Math.abs;class X {{ abs(-1); }}"; + String expected = "import static java.lang.Math.abs;class X {{ abs(-1); }}"; assertEquals("Replacing with static import", expected, replacer.testReplace(in, what, by, options, true)); + + in = "class X { void m(java.util.Random r) { Math.abs(r.nextInt()); }}"; + expected = "import static java.lang.Math.abs;class X { void m(java.util.Random r) { abs(r.nextInt()); }}"; + assertEquals("don't add broken static imports", expected, replacer.testReplace(in, what, by, options, true)); } public void testUseStaticStarImport() { diff --git a/platform/testFramework/src/com/intellij/mock/Mock.java b/platform/testFramework/src/com/intellij/mock/Mock.java index 73d6538c52b9..a2768f2e1851 100644 --- a/platform/testFramework/src/com/intellij/mock/Mock.java +++ b/platform/testFramework/src/com/intellij/mock/Mock.java @@ -435,7 +435,7 @@ public class Mock { } @Override - public void setSelectedEditor(@NotNull VirtualFile file, String fileEditorProviderId) { + public void setSelectedEditor(@NotNull VirtualFile file, @NotNull String fileEditorProviderId) { } } diff --git a/platform/testFramework/src/com/intellij/mock/MockVirtualFileSystem.java b/platform/testFramework/src/com/intellij/mock/MockVirtualFileSystem.java index 8550f973a8ea..9854cea82835 100644 --- a/platform/testFramework/src/com/intellij/mock/MockVirtualFileSystem.java +++ b/platform/testFramework/src/com/intellij/mock/MockVirtualFileSystem.java @@ -20,30 +20,37 @@ import com.intellij.openapi.vfs.DeprecatedVirtualFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileSystem; import com.intellij.testFramework.LightVirtualFile; +import gnu.trove.THashMap; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.io.File; import java.io.IOException; import java.util.Collection; -import java.util.HashMap; +import java.util.Map; public class MockVirtualFileSystem extends DeprecatedVirtualFileSystem { private final MyVirtualFile myRoot = new MyVirtualFile("", null); public static final String PROTOCOL = "mock"; @Override + @NotNull public VirtualFile findFileByPath(@NotNull String path) { path = path.replace(File.separatorChar, '/'); path = path.replace('/', ':'); if (StringUtil.startsWithChar(path, ':')) path = path.substring(1); - String[] components = path.split(":"); MyVirtualFile file = myRoot; - for (String component : components) { + for (String component : StringUtil.split(path, ":")) { file = file.getOrCreate(component); } return file; } + @NotNull + public VirtualFile getRoot() { + return myRoot; + } + @Override @NotNull public String getProtocol() { @@ -93,11 +100,12 @@ public class MockVirtualFileSystem extends DeprecatedVirtualFileSystem { } public class MyVirtualFile extends LightVirtualFile { - private final HashMap myChildren = new HashMap(); + private final Map myChildren = new THashMap(); private final MyVirtualFile myParent; - public MyVirtualFile(String name, MyVirtualFile parent) { + public MyVirtualFile(@NotNull String name, @Nullable MyVirtualFile parent) { super(name); + myParent = parent; } @@ -107,7 +115,8 @@ public class MockVirtualFileSystem extends DeprecatedVirtualFileSystem { return MockVirtualFileSystem.this; } - public MyVirtualFile getOrCreate(String name) { + @NotNull + public MyVirtualFile getOrCreate(@NotNull String name) { MyVirtualFile file = myChildren.get(name); if (file == null) { file = new MyVirtualFile(name, this); diff --git a/platform/testFramework/src/com/intellij/testFramework/FlyIdeaTestCase.java b/platform/testFramework/src/com/intellij/testFramework/FlyIdeaTestCase.java index 02a39f148e24..098d76599f5b 100644 --- a/platform/testFramework/src/com/intellij/testFramework/FlyIdeaTestCase.java +++ b/platform/testFramework/src/com/intellij/testFramework/FlyIdeaTestCase.java @@ -1,36 +1,35 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.intellij.testFramework; -import com.intellij.mock.MockApplicationEx; import com.intellij.openapi.Disposable; -import com.intellij.openapi.application.Application; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.application.ex.ApplicationManagerEx; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.io.FileUtil; import junit.framework.TestCase; -import org.jetbrains.annotations.NotNull; import java.io.File; import java.io.IOException; -import java.util.concurrent.Future; public abstract class FlyIdeaTestCase extends TestCase { - - private Disposable myRootDisposable; + private final Disposable myRootDisposable = Disposer.newDisposable(); private File myTempDir; @Override protected void setUp() throws Exception { - final Application old = ApplicationManagerEx.getApplication(); - myRootDisposable = Disposer.newDisposable(); - MockApplicationEx app = new MockApplicationEx(getRootDisposable()) { - @NotNull - @Override - public Future executeOnPooledThread(@NotNull Runnable action) { - return old != null ? old.executeOnPooledThread(action) : super.executeOnPooledThread(action); - } - }; - ApplicationManager.setApplication(app, myRootDisposable); + LightPlatformTestCase.initApplication(); } public File getTempDir() throws IOException { diff --git a/platform/testFramework/src/com/intellij/testFramework/PlatformTestUtil.java b/platform/testFramework/src/com/intellij/testFramework/PlatformTestUtil.java index ee6e519cb583..fcf0e225ea5f 100644 --- a/platform/testFramework/src/com/intellij/testFramework/PlatformTestUtil.java +++ b/platform/testFramework/src/com/intellij/testFramework/PlatformTestUtil.java @@ -413,7 +413,8 @@ public class PlatformTestUtil { for (int i= n /2- n / part /2; i< n /2+ n / part /2; i++) { total += time[i]; } - return total/(n / part); + int middlePartLength = n / part; + return middlePartLength == 0 ? 0 : total / middlePartLength; } public static boolean canRunTest(@NotNull Class testCaseClass) { diff --git a/platform/testFramework/src/com/intellij/testFramework/TestActionEvent.java b/platform/testFramework/src/com/intellij/testFramework/TestActionEvent.java index 764e80453ad6..0f5ab8b3c8ae 100644 --- a/platform/testFramework/src/com/intellij/testFramework/TestActionEvent.java +++ b/platform/testFramework/src/com/intellij/testFramework/TestActionEvent.java @@ -16,10 +16,7 @@ package com.intellij.testFramework; import com.intellij.ide.DataManager; -import com.intellij.openapi.actionSystem.ActionManager; -import com.intellij.openapi.actionSystem.AnAction; -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.actionSystem.DataContext; +import com.intellij.openapi.actionSystem.*; import org.jetbrains.annotations.NotNull; /** @@ -35,4 +32,8 @@ public class TestActionEvent extends AnActionEvent { public TestActionEvent(@NotNull AnAction action) { this(DataManager.getInstance().getDataContext(), action); } + + public TestActionEvent() { + super(null, DataManager.getInstance().getDataContext(), "", new Presentation(), ActionManager.getInstance(), 0); + } } diff --git a/platform/testFramework/src/com/intellij/testFramework/UsefulTestCase.java b/platform/testFramework/src/com/intellij/testFramework/UsefulTestCase.java index 6deda3d4b70a..6f03fd02446e 100644 --- a/platform/testFramework/src/com/intellij/testFramework/UsefulTestCase.java +++ b/platform/testFramework/src/com/intellij/testFramework/UsefulTestCase.java @@ -224,7 +224,7 @@ public abstract class UsefulTestCase extends TestCase { } public static CompositeException doCheckForSettingsDamage(@NotNull CodeStyleSettings oldCodeStyleSettings, - @NotNull CodeStyleSettings currentCodeStyleSettings) throws Exception { + @NotNull CodeStyleSettings currentCodeStyleSettings) throws Exception { CompositeException result = new CompositeException(); final CodeInsightSettings settings = CodeInsightSettings.getInstance(); try { @@ -234,9 +234,13 @@ public abstract class UsefulTestCase extends TestCase { } catch (AssertionError error) { CodeInsightSettings clean = new CodeInsightSettings(); - Element temp = new Element("temp"); - clean.writeExternal(temp); - settings.loadState(temp); + for (Field field : clean.getClass().getFields()) { + try { + ReflectionUtil.copyFieldValue(clean, settings, field); + } + catch (Exception ignored) { + } + } result.add(error); } diff --git a/platform/testFramework/src/com/intellij/testFramework/fixtures/IdeaTestFixtureFactory.java b/platform/testFramework/src/com/intellij/testFramework/fixtures/IdeaTestFixtureFactory.java index 010caad8fcd2..ecc5bff4dc50 100644 --- a/platform/testFramework/src/com/intellij/testFramework/fixtures/IdeaTestFixtureFactory.java +++ b/platform/testFramework/src/com/intellij/testFramework/fixtures/IdeaTestFixtureFactory.java @@ -37,6 +37,7 @@ public abstract class IdeaTestFixtureFactory { } } + @NotNull public static IdeaTestFixtureFactory getFixtureFactory() { return ourInstance; } @@ -59,6 +60,7 @@ public abstract class IdeaTestFixtureFactory { public abstract TestFixtureBuilder createFixtureBuilder(@NotNull String name); + @NotNull public abstract TestFixtureBuilder createLightFixtureBuilder(); public abstract TestFixtureBuilder createLightFixtureBuilder(@Nullable LightProjectDescriptor projectDescriptor); diff --git a/platform/testFramework/src/com/intellij/testFramework/fixtures/TestFixtureBuilder.java b/platform/testFramework/src/com/intellij/testFramework/fixtures/TestFixtureBuilder.java index b275ec521c8c..1d0e648611ef 100644 --- a/platform/testFramework/src/com/intellij/testFramework/fixtures/TestFixtureBuilder.java +++ b/platform/testFramework/src/com/intellij/testFramework/fixtures/TestFixtureBuilder.java @@ -17,11 +17,13 @@ package com.intellij.testFramework.fixtures; import com.intellij.testFramework.builders.ModuleFixtureBuilder; +import org.jetbrains.annotations.NotNull; /** * @author mike */ public interface TestFixtureBuilder { + @NotNull T getFixture(); M addModule(Class builderClass); diff --git a/platform/testFramework/src/com/intellij/testFramework/fixtures/impl/CodeInsightTestFixtureImpl.java b/platform/testFramework/src/com/intellij/testFramework/fixtures/impl/CodeInsightTestFixtureImpl.java index 8aafb17da1de..f4264f760cd9 100644 --- a/platform/testFramework/src/com/intellij/testFramework/fixtures/impl/CodeInsightTestFixtureImpl.java +++ b/platform/testFramework/src/com/intellij/testFramework/fixtures/impl/CodeInsightTestFixtureImpl.java @@ -185,7 +185,7 @@ public class CodeInsightTestFixtureImpl extends BaseFixture implements CodeInsig } VirtualFile result; - final String path = fromFile.getPath(); + final String path = fromFile.getAbsolutePath(); if (myTempDirFixture instanceof LightTempDirTestFixtureImpl) { VfsRootAccess.allowRootAccess(path); Disposer.register(myTestRootDisposable, new Disposable() { @@ -1025,13 +1025,9 @@ public class CodeInsightTestFixtureImpl extends BaseFixture implements CodeInsig final CodeCompletionHandlerBase handler = new CodeCompletionHandlerBase(type) { @Override - protected void completionFinished(int offset1, - int offset2, - CompletionProgressIndicator indicator, - LookupElement[] items, - boolean hasModifiers) { - myEmptyLookup = items.length == 0; - super.completionFinished(offset1, offset2, indicator, items, hasModifiers); + protected void completionFinished(CompletionProgressIndicator indicator, boolean hasModifiers) { + myEmptyLookup = indicator.getLookup().getItems().isEmpty(); + super.completionFinished(indicator, hasModifiers); } }; Editor editor = getCompletionEditor(); diff --git a/platform/testFramework/src/com/intellij/testFramework/fixtures/impl/HeavyTestFixtureBuilderImpl.java b/platform/testFramework/src/com/intellij/testFramework/fixtures/impl/HeavyTestFixtureBuilderImpl.java index c7b7779fe92a..412c87ee5bce 100644 --- a/platform/testFramework/src/com/intellij/testFramework/fixtures/impl/HeavyTestFixtureBuilderImpl.java +++ b/platform/testFramework/src/com/intellij/testFramework/fixtures/impl/HeavyTestFixtureBuilderImpl.java @@ -22,6 +22,7 @@ import com.intellij.testFramework.fixtures.IdeaProjectTestFixture; import com.intellij.testFramework.fixtures.TestFixtureBuilder; import com.intellij.util.pico.ConstructorInjectionComponentAdapter; import com.intellij.util.pico.IdeaPicoContainer; +import org.jetbrains.annotations.NotNull; import org.picocontainer.MutablePicoContainer; import java.lang.reflect.Field; @@ -50,6 +51,7 @@ class HeavyTestFixtureBuilderImpl implements TestFixtureBuilder createLightFixtureBuilder() { return new LightTestFixtureBuilderImpl(new LightIdeaTestFixtureImpl( diff --git a/platform/testFramework/src/com/intellij/testFramework/fixtures/impl/LightTestFixtureBuilderImpl.java b/platform/testFramework/src/com/intellij/testFramework/fixtures/impl/LightTestFixtureBuilderImpl.java index 43a2ee7c031c..a196e4211649 100644 --- a/platform/testFramework/src/com/intellij/testFramework/fixtures/impl/LightTestFixtureBuilderImpl.java +++ b/platform/testFramework/src/com/intellij/testFramework/fixtures/impl/LightTestFixtureBuilderImpl.java @@ -19,6 +19,7 @@ package com.intellij.testFramework.fixtures.impl; import com.intellij.testFramework.builders.ModuleFixtureBuilder; import com.intellij.testFramework.fixtures.IdeaProjectTestFixture; import com.intellij.testFramework.fixtures.TestFixtureBuilder; +import org.jetbrains.annotations.NotNull; /** * @author mike @@ -31,6 +32,7 @@ class LightTestFixtureBuilderImpl implements T myFixture = fixture; } + @NotNull @Override public F getFixture() { return myFixture; diff --git a/platform/testFramework/testFramework.iml b/platform/testFramework/testFramework.iml index 7985252e6abc..19a4097b1f15 100644 --- a/platform/testFramework/testFramework.iml +++ b/platform/testFramework/testFramework.iml @@ -20,7 +20,7 @@ - + diff --git a/platform/testRunner/src/com/intellij/execution/testframework/AbstractTestProxy.java b/platform/testRunner/src/com/intellij/execution/testframework/AbstractTestProxy.java index 73ecdb2ddd8c..7b84c66ae2ac 100644 --- a/platform/testRunner/src/com/intellij/execution/testframework/AbstractTestProxy.java +++ b/platform/testRunner/src/com/intellij/execution/testframework/AbstractTestProxy.java @@ -143,4 +143,16 @@ public abstract class AbstractTestProxy extends CompositePrintable { String getExpected(); String getActual(); } + + public interface AssertEqualsDiffChain { + AssertEqualsMultiDiffViewProvider getPrevious(); + AssertEqualsMultiDiffViewProvider getCurrent(); + AssertEqualsMultiDiffViewProvider getNext(); + void setCurrent(AssertEqualsMultiDiffViewProvider provider); + } + + public interface AssertEqualsMultiDiffViewProvider extends AssertEqualsDiffViewerProvider { + void openMultiDiff(Project project, AssertEqualsDiffChain chain); + String getFilePath(); + } } diff --git a/platform/testRunner/src/com/intellij/execution/testframework/Filter.java b/platform/testRunner/src/com/intellij/execution/testframework/Filter.java index 7d43fcd2a7e1..8dd6363c5940 100644 --- a/platform/testRunner/src/com/intellij/execution/testframework/Filter.java +++ b/platform/testRunner/src/com/intellij/execution/testframework/Filter.java @@ -61,42 +61,49 @@ public abstract class Filter { } public static final Filter NO_FILTER = new Filter() { + @Override public boolean shouldAccept(final AbstractTestProxy test) { return true; } }; public static final Filter DEFECT = new Filter() { + @Override public boolean shouldAccept(final AbstractTestProxy test) { return test.isDefect(); } }; public static final Filter IGNORED = new Filter() { + @Override public boolean shouldAccept(final AbstractTestProxy test) { return test.isIgnored(); } }; public static final Filter NOT_PASSED = new Filter() { + @Override public boolean shouldAccept(final AbstractTestProxy test) { return !test.isPassed(); } }; public static final Filter PASSED = new Filter() { + @Override public boolean shouldAccept(final AbstractTestProxy test) { return test.isPassed(); } }; public static final Filter FAILED_OR_INTERRUPTED = new Filter() { + @Override public boolean shouldAccept(final AbstractTestProxy test) { return test.isInterrupted() || test.isDefect(); } }; public static final Filter LEAF = new Filter() { + @Override public boolean shouldAccept(final AbstractTestProxy test) { return test.isLeaf(); } @@ -122,6 +129,7 @@ public abstract class Filter { myFilter2 = filter2; } + @Override public boolean shouldAccept(final AbstractTestProxy test) { return myFilter1.shouldAccept(test) && myFilter2.shouldAccept(test); } @@ -134,6 +142,7 @@ public abstract class Filter { myFilter = filter; } + @Override public boolean shouldAccept(final AbstractTestProxy test) { return !myFilter.shouldAccept(test); } @@ -148,6 +157,7 @@ public abstract class Filter { myFilter2 = filter2; } + @Override public boolean shouldAccept(final AbstractTestProxy test) { return myFilter1.shouldAccept(test) || myFilter2.shouldAccept(test); } diff --git a/platform/testRunner/src/com/intellij/execution/testframework/TestTreeView.java b/platform/testRunner/src/com/intellij/execution/testframework/TestTreeView.java index 839568b4c2a9..5d1d2b346a85 100644 --- a/platform/testRunner/src/com/intellij/execution/testframework/TestTreeView.java +++ b/platform/testRunner/src/com/intellij/execution/testframework/TestTreeView.java @@ -46,6 +46,8 @@ import java.util.ArrayList; import java.util.List; public abstract class TestTreeView extends Tree implements DataProvider, CopyProvider { + public static final DataKey MODEL_DATA_KEY = DataKey.create("testFrameworkModel.dataId"); + private TestFrameworkRunningModel myModel; protected abstract TreeCellRenderer getRenderer(TestConsoleProperties properties); @@ -127,7 +129,11 @@ public abstract class TestTreeView extends Tree implements DataProvider, CopyPro return locations.isEmpty() ? null : locations.toArray(new Location[locations.size()]); } } - + + if (MODEL_DATA_KEY.is(dataId)) { + return myModel; + } + final TreePath selectionPath = getSelectionPath(); if (selectionPath == null) return null; final AbstractTestProxy testProxy = getSelectedTest(selectionPath); @@ -162,7 +168,6 @@ public abstract class TestTreeView extends Tree implements DataProvider, CopyPro }); TreeUtil.installActions(this); PopupHandler.installPopupHandler(this, IdeActions.GROUP_TESTTREE_POPUP, ActionPlaces.TESTTREE_VIEW_POPUP); - ViewAssertEqualsDiffAction.registerShortcut(this); } @JdkConstants.TreeSelectionMode diff --git a/platform/testRunner/src/com/intellij/execution/testframework/TestTreeViewAction.java b/platform/testRunner/src/com/intellij/execution/testframework/TestTreeViewAction.java new file mode 100644 index 000000000000..8a700d1b5dbd --- /dev/null +++ b/platform/testRunner/src/com/intellij/execution/testframework/TestTreeViewAction.java @@ -0,0 +1,19 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.execution.testframework; + +public interface TestTreeViewAction { +} diff --git a/platform/testRunner/src/com/intellij/execution/testframework/TestTreeViewActionsPromoter.java b/platform/testRunner/src/com/intellij/execution/testframework/TestTreeViewActionsPromoter.java new file mode 100644 index 000000000000..445c33c682be --- /dev/null +++ b/platform/testRunner/src/com/intellij/execution/testframework/TestTreeViewActionsPromoter.java @@ -0,0 +1,38 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.execution.testframework; + +import com.intellij.openapi.actionSystem.ActionPromoter; +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.DataContext; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +public class TestTreeViewActionsPromoter implements ActionPromoter { + @Override + public List promote(List actions, DataContext context) { + if (AbstractTestProxy.DATA_KEY.getData(context) != null) { + for (AnAction action : actions) { + if (action instanceof TestTreeViewAction) { + return Arrays.asList(action); + } + } + } + return Collections.emptyList(); + } +} diff --git a/platform/testRunner/src/com/intellij/execution/testframework/TestsUIUtil.java b/platform/testRunner/src/com/intellij/execution/testframework/TestsUIUtil.java index 836635a58b0e..cbb0373f34c1 100644 --- a/platform/testRunner/src/com/intellij/execution/testframework/TestsUIUtil.java +++ b/platform/testRunner/src/com/intellij/execution/testframework/TestsUIUtil.java @@ -86,7 +86,7 @@ public class TestsUIUtil { } return false; } - + public static Navigatable getOpenFileDescriptor(final AbstractTestProxy testProxy, final TestFrameworkRunningModel model) { final TestConsoleProperties testConsoleProperties = model.getProperties(); return getOpenFileDescriptor(testProxy, testConsoleProperties, @@ -114,7 +114,7 @@ public class TestsUIUtil { public static void notifyByBalloon(@NotNull final Project project, boolean started, final AbstractTestProxy root, - final TestConsoleProperties properties, + final TestConsoleProperties properties, @Nullable final String comment) { if (project.isDisposed()) return; if (properties == null) return; @@ -231,7 +231,7 @@ public class TestsUIUtil { myType = MessageType.ERROR; } else if (notStartedCount > 0) { - myTitle = !notStarted.isEmpty() ? ExecutionBundle.message("junit.runing.info.failed.to.start.error.message") : "Tests Ignored"; + myTitle = !notStarted.isEmpty() ? ExecutionBundle.message("junit.running.info.failed.to.start.error.message") : "Tests Ignored"; myText = passedCount + " passed, " + notStartedCount + (!notStarted.isEmpty() ? " not started" : " ignored"); myType = notStarted.isEmpty() ? MessageType.WARNING : MessageType.ERROR; } diff --git a/platform/testRunner/src/com/intellij/execution/testframework/actions/AbstractRerunFailedTestsAction.java b/platform/testRunner/src/com/intellij/execution/testframework/actions/AbstractRerunFailedTestsAction.java index 30f8599e2aaf..a7d036b33ef7 100644 --- a/platform/testRunner/src/com/intellij/execution/testframework/actions/AbstractRerunFailedTestsAction.java +++ b/platform/testRunner/src/com/intellij/execution/testframework/actions/AbstractRerunFailedTestsAction.java @@ -34,20 +34,20 @@ import com.intellij.execution.testframework.Filter; import com.intellij.execution.testframework.TestConsoleProperties; import com.intellij.execution.testframework.TestFrameworkRunningModel; import com.intellij.idea.ActionsBundle; -import com.intellij.openapi.Disposable; -import com.intellij.openapi.actionSystem.*; +import com.intellij.openapi.actionSystem.ActionManager; +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.LangDataKeys; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.options.SettingsEditor; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.ComponentContainer; import com.intellij.openapi.ui.popup.JBPopupFactory; -import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Getter; import com.intellij.openapi.util.InvalidDataException; import com.intellij.openapi.util.WriteExternalException; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.ui.components.JBList; -import com.intellij.util.containers.ContainerUtil; import com.intellij.util.ui.UIUtil; import org.jdom.Element; import org.jetbrains.annotations.NotNull; @@ -57,40 +57,24 @@ import javax.swing.*; import java.awt.*; import java.awt.event.InputEvent; import java.awt.event.MouseEvent; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashMap; import java.util.List; -public class AbstractRerunFailedTestsAction extends AnAction implements AnAction.TransparentUpdate, Disposable { - private static final List registry = ContainerUtil.createLockFreeCopyOnWriteList(); - private static final Logger LOG = Logger.getInstance("#com.intellij.execution.junit2.ui.actions.RerunFailedTestsAction"); +public class AbstractRerunFailedTestsAction extends AnAction implements AnAction.TransparentUpdate { + private static final Logger LOG = Logger.getInstance(AbstractRerunFailedTestsAction.class); + private TestFrameworkRunningModel myModel; private Getter myModelProvider; protected TestConsoleProperties myConsoleProperties; - protected ExecutionEnvironment myEnvironment; - private final JComponent myParent; - - @SuppressWarnings("UnusedDeclaration") - public AbstractRerunFailedTestsAction() { - //We call this constructor with a little help from reflection. - myParent = null; - } protected AbstractRerunFailedTestsAction(@NotNull ComponentContainer componentContainer) { - myParent = componentContainer.getComponent(); - registry.add(this); - Disposer.register(componentContainer, this); copyFrom(ActionManager.getInstance().getAction("RerunFailedTests")); - registerCustomShortcutSet(getShortcutSet(), myParent); + registerCustomShortcutSet(getShortcutSet(), componentContainer.getComponent()); } - @Override - public void dispose() { - registry.remove(this); - } - - public void init(final TestConsoleProperties consoleProperties, - final ExecutionEnvironment environment) { - myEnvironment = environment; + public void init(TestConsoleProperties consoleProperties) { myConsoleProperties = consoleProperties; } @@ -102,48 +86,27 @@ public class AbstractRerunFailedTestsAction extends AnAction implements AnAction myModelProvider = modelProvider; } - @NotNull - private AbstractRerunFailedTestsAction findActualAction() { - if (myParent != null || registry.isEmpty()) - return this; - List candidates = new ArrayList(registry); - Collections.sort(candidates, new Comparator() { - @Override - public int compare(AbstractRerunFailedTestsAction action1, AbstractRerunFailedTestsAction action2) { - Window window1 = SwingUtilities.windowForComponent(action1.myParent); - Window window2 = SwingUtilities.windowForComponent(action2.myParent); - if (window1 == null) - return 1; - if (window2 == null) - return -1; - boolean showing1 = action1.myParent.isShowing(); - boolean showing2 = action2.myParent.isShowing(); - if (showing1 && !showing2) - return -1; - if (showing2 && !showing1) - return 1; - return (window1.isActive() ? -1 : 1); - } - }); - return candidates.get(0); - } - @Override - public final void update(AnActionEvent e) { - AbstractRerunFailedTestsAction action = findActualAction(); - e.getPresentation().setEnabled(action.isActive(e)); + public final void update(@NotNull AnActionEvent e) { + e.getPresentation().setEnabled(isActive(e)); } private boolean isActive(AnActionEvent e) { - DataContext dataContext = e.getDataContext(); - Project project = CommonDataKeys.PROJECT.getData(dataContext); - if (project == null) return false; + Project project = e.getProject(); + if (project == null) { + return false; + } + TestFrameworkRunningModel model = getModel(); - if (model == null || model.getRoot() == null) return false; - final List myAllTests = model.getRoot().getAllTests(); - final Filter filter = getFailuresFilter(); - for (Object test : myAllTests) { - if (filter.shouldAccept((AbstractTestProxy)test)) return true; + if (model == null || model.getRoot() == null) { + return false; + } + Filter filter = getFailuresFilter(); + for (AbstractTestProxy test : model.getRoot().getAllTests()) { + //noinspection unchecked + if (filter.shouldAccept(test)) { + return true; + } } return false; } @@ -151,10 +114,10 @@ public class AbstractRerunFailedTestsAction extends AnAction implements AnAction @NotNull protected List getFailedTests(Project project) { TestFrameworkRunningModel model = getModel(); - final List myAllTests = model != null - ? model.getRoot().getAllTests() - : Collections.emptyList(); - return getFilter(project, model != null ? model.getProperties().getScope() : GlobalSearchScope.allScope(project)).select(myAllTests); + //noinspection unchecked + return getFilter(project, model != null ? model.getProperties().getScope() : GlobalSearchScope.allScope(project)).select(model != null + ? model.getRoot().getAllTests() + : Collections.emptyList()); } @NotNull @@ -162,7 +125,7 @@ public class AbstractRerunFailedTestsAction extends AnAction implements AnAction return getFailuresFilter(); } - protected Filter getFailuresFilter() { + protected Filter getFailuresFilter() { if (TestConsoleProperties.INCLUDE_NON_STARTED_IN_RERUN_FAILED.value(myConsoleProperties)) { return Filter.NOT_PASSED.and(Filter.IGNORED.not()).or(Filter.FAILED_OR_INTERRUPTED); } @@ -170,30 +133,27 @@ public class AbstractRerunFailedTestsAction extends AnAction implements AnAction } @Override - public void actionPerformed(AnActionEvent e) { - findActualAction().showPopup(e); - } + public void actionPerformed(@NotNull AnActionEvent e) { + ExecutionEnvironment environment = e.getData(LangDataKeys.EXECUTION_ENVIRONMENT); + if (environment == null) { + return; + } - private void showPopup(AnActionEvent e) { - boolean isDebug = myConsoleProperties.isDebug(); - final MyRunProfile profile = getRunProfile(); + MyRunProfile profile = getRunProfile(environment); if (profile == null) { return; } - final Executor executor = isDebug ? DefaultDebugExecutor.getDebugExecutorInstance() : DefaultRunExecutor.getRunExecutorInstance(); + final ExecutionEnvironmentBuilder environmentBuilder = new ExecutionEnvironmentBuilder(environment).runProfile(profile); final InputEvent event = e.getInputEvent(); if (!(event instanceof MouseEvent) || !event.isShiftDown()) { - final ProgramRunner runner = RunnerRegistry.getInstance().getRunner(executor.getId(), profile); - LOG.assertTrue(runner != null); - performAction(runner, profile, myEnvironment.getExecutor()); + performAction(environmentBuilder); return; } final LinkedHashMap availableRunners = new LinkedHashMap(); - final Executor[] executors = new Executor[] {DefaultRunExecutor.getRunExecutorInstance(), DefaultDebugExecutor.getDebugExecutorInstance()}; - for (Executor ex : executors) { + for (Executor ex : new Executor[] {DefaultRunExecutor.getRunExecutorInstance(), DefaultDebugExecutor.getDebugExecutorInstance()}) { final ProgramRunner runner = RunnerRegistry.getInstance().getRunner(ex.getId(), profile); if (runner != null) { availableRunners.put(ex, runner); @@ -201,19 +161,20 @@ public class AbstractRerunFailedTestsAction extends AnAction implements AnAction } if (availableRunners.isEmpty()) { - LOG.error(executor.getActionName() + " is not available now"); - return; + LOG.error(environment.getExecutor().getActionName() + " is not available now"); } - - if (availableRunners.size() == 1) { - performAction(availableRunners.get(executor), profile, executor); - } else { + else if (availableRunners.size() == 1) { + //noinspection ConstantConditions + performAction(environmentBuilder.runner(availableRunners.get(environment.getExecutor()))); + } + else { final JBList list = new JBList(availableRunners.keySet()); list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - list.setSelectedValue(executor, true); + list.setSelectedValue(environment.getExecutor(), true); list.setCellRenderer(new DefaultListCellRenderer() { + @NotNull @Override - public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { + public Component getListCellRendererComponent(@NotNull JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { final Component component = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); if (value instanceof Executor) { setText(UIUtil.removeMnemonic(((Executor)value).getStartActionText())); @@ -222,6 +183,7 @@ public class AbstractRerunFailedTestsAction extends AnAction implements AnAction return component; } }); + //noinspection ConstantConditions JBPopupFactory.getInstance().createListPopupBuilder(list) .setTitle("Restart Failed Tests") .setMovable(false) @@ -232,34 +194,38 @@ public class AbstractRerunFailedTestsAction extends AnAction implements AnAction public void run() { final Object value = list.getSelectedValue(); if (value instanceof Executor) { - performAction(availableRunners.get(value), profile, (Executor)value); + //noinspection ConstantConditions + performAction(environmentBuilder.runner(availableRunners.get(value)).executor((Executor)value)); } } }).createPopup().showUnderneathOf(event.getComponent()); } } - private void performAction(ProgramRunner runner, MyRunProfile profile, Executor executor) { + private static void performAction(@NotNull ExecutionEnvironmentBuilder builder) { + ExecutionEnvironment environment = builder.build(); try { - new ExecutionEnvironmentBuilder(myEnvironment) - .runner(runner) - .executor(executor) - .runProfile(profile) - .buildAndExecute(); + environment.getRunner().execute(environment); } - catch (ExecutionException e1) { - LOG.error(e1); + catch (ExecutionException e) { + LOG.error(e); } finally { - profile.clear(); + ((MyRunProfile)environment.getRunProfile()).clear(); } } - @Nullable + @Deprecated public MyRunProfile getRunProfile() { return null; } + @Nullable + protected MyRunProfile getRunProfile(@NotNull ExecutionEnvironment environment) { + //noinspection deprecation + return getRunProfile(); + } + @Nullable public TestFrameworkRunningModel getModel() { if (myModel != null) { @@ -293,7 +259,6 @@ public class AbstractRerunFailedTestsAction extends AnAction implements AnAction public void clear() { } - @Override public void checkConfiguration() throws RuntimeConfigurationException { } @@ -351,6 +316,7 @@ public class AbstractRerunFailedTestsAction extends AnAction implements AnAction return myConfiguration.getPredefinedLogFiles(); } + @NotNull @Override public ArrayList getAllLogFiles() { return myConfiguration.getAllLogFiles(); diff --git a/platform/testRunner/src/com/intellij/execution/testframework/actions/RerunFailedTestsAction.java b/platform/testRunner/src/com/intellij/execution/testframework/actions/RerunFailedTestsAction.java new file mode 100644 index 000000000000..af15e3c14ce1 --- /dev/null +++ b/platform/testRunner/src/com/intellij/execution/testframework/actions/RerunFailedTestsAction.java @@ -0,0 +1,12 @@ +package com.intellij.execution.testframework.actions; + +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; +import org.jetbrains.annotations.NotNull; + +class RerunFailedTestsAction extends AnAction { + @Override + public void actionPerformed(@NotNull AnActionEvent e) { + throw new IllegalStateException("Action only as template"); + } +} diff --git a/platform/testRunner/src/com/intellij/execution/testframework/actions/ViewAssertEqualsDiffAction.java b/platform/testRunner/src/com/intellij/execution/testframework/actions/ViewAssertEqualsDiffAction.java index fd80ea0154db..5d19c5ee0877 100644 --- a/platform/testRunner/src/com/intellij/execution/testframework/actions/ViewAssertEqualsDiffAction.java +++ b/platform/testRunner/src/com/intellij/execution/testframework/actions/ViewAssertEqualsDiffAction.java @@ -16,13 +16,15 @@ package com.intellij.execution.testframework.actions; -import com.intellij.execution.testframework.AbstractTestProxy; +import com.intellij.execution.testframework.*; import com.intellij.openapi.actionSystem.*; +import com.intellij.openapi.project.Project; import org.jetbrains.annotations.NonNls; -import javax.swing.*; +import java.util.ArrayList; +import java.util.List; -public class ViewAssertEqualsDiffAction extends AnAction { +public class ViewAssertEqualsDiffAction extends AnAction implements TestTreeViewAction { @NonNls public static final String ACTION_ID = "openAssertEqualsDiff"; public void actionPerformed(final AnActionEvent e) { @@ -30,11 +32,35 @@ public class ViewAssertEqualsDiffAction extends AnAction { if (testProxy != null) { final AbstractTestProxy.AssertEqualsDiffViewerProvider diffViewerProvider = testProxy.getDiffViewerProvider(); if (diffViewerProvider != null) { - diffViewerProvider.openDiff(CommonDataKeys.PROJECT.getData(e.getDataContext())); + final Project project = CommonDataKeys.PROJECT.getData(e.getDataContext()); + if (diffViewerProvider instanceof AbstractTestProxy.AssertEqualsMultiDiffViewProvider) { + final TestFrameworkRunningModel runningModel = TestTreeView.MODEL_DATA_KEY.getData(e.getDataContext()); + final List providers = collectAvailableProviders(runningModel); + final MyAssertEqualsDiffChain diffChain = + providers.size() > 1 ? new MyAssertEqualsDiffChain(providers, (AbstractTestProxy.AssertEqualsMultiDiffViewProvider)diffViewerProvider) : null; + ((AbstractTestProxy.AssertEqualsMultiDiffViewProvider)diffViewerProvider).openMultiDiff(project, diffChain); + } else { + diffViewerProvider.openDiff(project); + } } } } + private static List collectAvailableProviders(TestFrameworkRunningModel model) { + final List providers = new ArrayList(); + if (model != null) { + final AbstractTestProxy root = model.getRoot(); + final List allTests = root.getAllTests(); + for (AbstractTestProxy test : allTests) { + final AbstractTestProxy.AssertEqualsDiffViewerProvider provider = test.getDiffViewerProvider(); + if (provider instanceof AbstractTestProxy.AssertEqualsMultiDiffViewProvider) { + providers.add((AbstractTestProxy.AssertEqualsMultiDiffViewProvider)provider); + } + } + } + return providers; + } + public void update(final AnActionEvent e) { final Presentation presentation = e.getPresentation(); final boolean enabled; @@ -55,7 +81,38 @@ public class ViewAssertEqualsDiffAction extends AnAction { presentation.setVisible(enabled); } - public static void registerShortcut(final JComponent component) { - ActionManager.getInstance().getAction(ACTION_ID).registerCustomShortcutSet(CommonShortcuts.ALT_ENTER, component); + private static class MyAssertEqualsDiffChain implements AbstractTestProxy.AssertEqualsDiffChain { + + + private final List myProviders; + private AbstractTestProxy.AssertEqualsMultiDiffViewProvider myProvider; + + public MyAssertEqualsDiffChain(List providers, + AbstractTestProxy.AssertEqualsMultiDiffViewProvider provider) { + myProviders = providers; + myProvider = provider; + } + + @Override + public AbstractTestProxy.AssertEqualsMultiDiffViewProvider getPrevious() { + final int prevIdx = (myProviders.size() + myProviders.indexOf(myProvider) - 1) % myProviders.size(); + return myProviders.get(prevIdx); + } + + @Override + public AbstractTestProxy.AssertEqualsMultiDiffViewProvider getCurrent() { + return myProvider; + } + + @Override + public AbstractTestProxy.AssertEqualsMultiDiffViewProvider getNext() { + final int nextIdx = (myProviders.indexOf(myProvider) + 1) % myProviders.size(); + return myProviders.get(nextIdx); + } + + @Override + public void setCurrent(AbstractTestProxy.AssertEqualsMultiDiffViewProvider provider) { + myProvider = provider; + } } } diff --git a/platform/testRunner/src/com/intellij/execution/testframework/stacktrace/DiffHyperlink.java b/platform/testRunner/src/com/intellij/execution/testframework/stacktrace/DiffHyperlink.java index b60770692d9b..f8ff62996eca 100644 --- a/platform/testRunner/src/com/intellij/execution/testframework/stacktrace/DiffHyperlink.java +++ b/platform/testRunner/src/com/intellij/execution/testframework/stacktrace/DiffHyperlink.java @@ -22,14 +22,20 @@ package com.intellij.execution.testframework.stacktrace; import com.intellij.execution.ExecutionBundle; import com.intellij.execution.filters.HyperlinkInfo; +import com.intellij.execution.testframework.AbstractTestProxy; import com.intellij.execution.testframework.Printable; import com.intellij.execution.testframework.Printer; import com.intellij.execution.ui.ConsoleViewContentType; +import com.intellij.icons.AllIcons; +import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.diff.*; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import javax.swing.*; import java.io.File; public class DiffHyperlink implements Printable { @@ -56,21 +62,93 @@ public class DiffHyperlink implements Printable { myPrintOneLine = printOneLine; } - public void openDiff(final Project project) { + public void openDiff(Project project) { + openMultiDiff(project, null); + } + + public void openMultiDiff(final Project project, + final AbstractTestProxy.AssertEqualsDiffChain chain) { + final SimpleDiffRequest diffData = createRequest(project, chain, myFilePath, myExpected, myActual); + DiffManager.getInstance().getIdeaDiffTool().show(diffData); + } + + private SimpleDiffRequest createRequest(final Project project, + final AbstractTestProxy.AssertEqualsDiffChain chain, + String filePath, String expected, String actual) { String expectedTitle = ExecutionBundle.message("diff.content.expected.title"); final DiffContent expectedContent; final VirtualFile vFile; - if (myFilePath != null && (vFile = LocalFileSystem.getInstance().findFileByPath(myFilePath)) != null) { + if (filePath != null && (vFile = LocalFileSystem.getInstance().findFileByPath(filePath)) != null) { expectedContent = DiffContent.fromFile(project, vFile); expectedTitle += " (" + vFile.getPresentableUrl() + ")"; - } else expectedContent = new SimpleContent(myExpected); + } else { + expectedContent = new SimpleContent(expected); + } final SimpleDiffRequest diffData = new SimpleDiffRequest(project, getTitle()); - diffData.setContents(expectedContent, new SimpleContent(myActual)); + if (chain != null) { + diffData.setToolbarAddons(new DiffRequest.ToolbarAddons() { + @Override + public void customize(DiffToolbar toolbar) { + toolbar.addAction(new NextPrevAction("Compare Previous Failure", AllIcons.Actions.Prevfile, chain) { + { + registerCustomShortcutSet(ActionManager.getInstance().getAction("PreviousTab").getShortcutSet(), null); + } + + @Override + protected AbstractTestProxy.AssertEqualsMultiDiffViewProvider getNextId() { + return chain.getPrevious(); + } + }); + toolbar.addAction(new NextPrevAction("Compare Next Failure", AllIcons.Actions.Nextfile, chain) { + { + registerCustomShortcutSet(ActionManager.getInstance().getAction("NextTab").getShortcutSet(), null); + } + + @Override + protected AbstractTestProxy.AssertEqualsMultiDiffViewProvider getNextId() { + return chain.getNext(); + } + }); + } + }); + } + diffData.setContents(expectedContent, new SimpleContent(actual)); diffData.setContentTitles(expectedTitle, ExecutionBundle.message("diff.content.actual.title")); diffData.addHint(DiffTool.HINT_SHOW_FRAME); diffData.addHint(DiffTool.HINT_DO_NOT_IGNORE_WHITESPACES); diffData.setGroupKey("#com.intellij.execution.junit2.states.ComparisonFailureState$DiffDialog"); - DiffManager.getInstance().getIdeaDiffTool().show(diffData); + return diffData; + } + + abstract class NextPrevAction extends AnAction { + + private final AbstractTestProxy.AssertEqualsDiffChain myChain; + + public NextPrevAction(@Nullable String text, @Nullable Icon icon, + final AbstractTestProxy.AssertEqualsDiffChain chain) { + super(text, text, icon); + myChain = chain; + } + + @Override + public void actionPerformed(@NotNull AnActionEvent e) { + final DiffViewer viewer = e.getData(PlatformDataKeys.DIFF_VIEWER); + final Project project = e.getData(CommonDataKeys.PROJECT); + final AbstractTestProxy.AssertEqualsMultiDiffViewProvider nextProvider = getNextId(); + myChain.setCurrent(nextProvider); + final SimpleDiffRequest nextRequest = + createRequest(project, myChain, nextProvider.getFilePath(), nextProvider.getExpected(), nextProvider.getActual()); + viewer.setDiffRequest(nextRequest); + } + + @Override + public void update(@NotNull AnActionEvent e) { + final DiffViewer viewer = e.getData(PlatformDataKeys.DIFF_VIEWER); + final Project project = e.getData(CommonDataKeys.PROJECT); + e.getPresentation().setEnabled(project != null && viewer != null); + } + + protected abstract AbstractTestProxy.AssertEqualsMultiDiffViewProvider getNextId(); } protected String getTitle() { diff --git a/platform/testRunner/src/com/intellij/execution/testframework/ui/TestResultsPanel.java b/platform/testRunner/src/com/intellij/execution/testframework/ui/TestResultsPanel.java index a7519429381d..1f4b93238f9c 100644 --- a/platform/testRunner/src/com/intellij/execution/testframework/ui/TestResultsPanel.java +++ b/platform/testRunner/src/com/intellij/execution/testframework/ui/TestResultsPanel.java @@ -75,6 +75,7 @@ public abstract class TestResultsPanel extends JPanel implements Disposable { Disposer.register(this, myToolbarPanel); final Splitter splitter = createSplitter(mySplitterProportionProperty, mySplitterDefaultProportion); Disposer.register(this, new Disposable(){ + @Override public void dispose() { remove(splitter); splitter.dispose(); @@ -91,6 +92,7 @@ public abstract class TestResultsPanel extends JPanel implements Disposable { rightPanel.add(SameHeightPanel.wrap(myStatusLine, myToolbarPanel), BorderLayout.NORTH); myStatisticsSplitter = createSplitter(myStatisticsSplitterProportionProperty, 0.5f); new AwtVisitor(myConsole) { + @Override public boolean visit(Component component) { if (component instanceof JScrollPane) { ((JScrollPane) component).putClientProperty(UIUtil.KEEP_BORDER_SIDES, SideBorder.TOP | SideBorder.LEFT); @@ -104,6 +106,7 @@ public abstract class TestResultsPanel extends JPanel implements Disposable { showStatistics(); } myProperties.addListener(TestConsoleProperties.SHOW_STATISTICS, new TestFrameworkPropertyListener() { + @Override public void onChanged(Boolean value) { if (value.booleanValue()) { showStatistics(); @@ -145,6 +148,7 @@ public abstract class TestResultsPanel extends JPanel implements Disposable { return outputTab; } + @Override public void dispose() { } @@ -167,9 +171,9 @@ public abstract class TestResultsPanel extends JPanel implements Disposable { } splitter.addPropertyChangeListener(new PropertyChangeListener() { - public void propertyChange(final PropertyChangeEvent evt) { - if (propertiesComponent == null) return; - if (evt.getPropertyName().equals(Splitter.PROP_PROPORTION)) { + @Override + public void propertyChange(@NotNull final PropertyChangeEvent event) { + if (event.getPropertyName().equals(Splitter.PROP_PROPORTION)) { propertiesComponent.setValue(proportionProperty, String.valueOf(splitter.getProportion())); } } diff --git a/platform/usageView/src/com/intellij/usages/ChunkExtractor.java b/platform/usageView/src/com/intellij/usages/ChunkExtractor.java index dbbaeb48b72f..b9db68c69ce3 100644 --- a/platform/usageView/src/com/intellij/usages/ChunkExtractor.java +++ b/platform/usageView/src/com/intellij/usages/ChunkExtractor.java @@ -203,7 +203,7 @@ public class ChunkExtractor { if (myDocumentStamp != myDocument.getModificationStamp()) { highlighter.restart(chars); myDocumentStamp = myDocument.getModificationStamp(); - } else if(lexer.getTokenStart() > start) { + } else if(lexer.getTokenType() == null || lexer.getTokenStart() > start) { highlighter.resetPosition(0); // todo restart from nearest position with initial state } diff --git a/platform/usageView/src/com/intellij/usages/FindUsagesProcessPresentation.java b/platform/usageView/src/com/intellij/usages/FindUsagesProcessPresentation.java index 2248182a3c5c..c303c7fd0496 100644 --- a/platform/usageView/src/com/intellij/usages/FindUsagesProcessPresentation.java +++ b/platform/usageView/src/com/intellij/usages/FindUsagesProcessPresentation.java @@ -43,6 +43,7 @@ public class FindUsagesProcessPresentation { private Collection myLargeFiles; private boolean myShowFindOptionsPrompt = true; private Runnable mySearchWithProjectFiles; + private boolean myCanceled; public FindUsagesProcessPresentation(@NotNull UsageViewPresentation presentation) { myUsageViewPresentation = presentation; @@ -112,6 +113,15 @@ public class FindUsagesProcessPresentation { public void setShowFindOptionsPrompt(boolean showFindOptionsPrompt) { myShowFindOptionsPrompt = showFindOptionsPrompt; } + + + public void setCanceled(boolean canceled) { + myCanceled = canceled; + } + + public boolean isCanceled() { + return myCanceled; + } } diff --git a/platform/usageView/src/com/intellij/usages/impl/SearchForUsagesRunnable.java b/platform/usageView/src/com/intellij/usages/impl/SearchForUsagesRunnable.java index 8771ef75502b..6e5e93f3ed96 100644 --- a/platform/usageView/src/com/intellij/usages/impl/SearchForUsagesRunnable.java +++ b/platform/usageView/src/com/intellij/usages/impl/SearchForUsagesRunnable.java @@ -122,7 +122,7 @@ class SearchForUsagesRunnable implements Runnable { return "Search in Project"; } - private static void notifyByFindBalloon(final HyperlinkListener listener, + private static void notifyByFindBalloon(@Nullable final HyperlinkListener listener, @NotNull final MessageType info, @NotNull FindUsagesProcessPresentation processPresentation, @NotNull final Project project, @@ -400,6 +400,12 @@ class SearchForUsagesRunnable implements Runnable { ApplicationManager.getApplication().invokeLater(new Runnable() { @Override public void run() { + if (myProcessPresentation.isCanceled()) { + notifyByFindBalloon(null, MessageType.WARNING, myProcessPresentation, myProject, Arrays.asList("Usage search was canceled")); + findStartedBalloonShown.set(false); + return; + } + final List notFoundActions = myProcessPresentation.getNotFoundActions(); final String message = UsageViewBundle.message("dialog.no.usages.found.in", StringUtil.decapitalize(myPresentation.getUsagesString()), diff --git a/platform/util-rt/src/com/intellij/openapi/util/Condition.java b/platform/util-rt/src/com/intellij/openapi/util/Condition.java index 7eb9c1c7a8f6..d75ec46aa8cc 100644 --- a/platform/util-rt/src/com/intellij/openapi/util/Condition.java +++ b/platform/util-rt/src/com/intellij/openapi/util/Condition.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2010 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,6 +28,7 @@ public interface Condition { boolean value(T t); Condition NOT_NULL = new Condition() { + @Override public boolean value(final Object object) { return object != null; } @@ -43,6 +44,7 @@ public interface Condition { * @see com.intellij.openapi.util.Conditions#alwaysTrue() */ Condition TRUE = new Condition() { + @Override public boolean value(final Object object) { return true; } @@ -57,6 +59,7 @@ public interface Condition { * @see com.intellij.openapi.util.Conditions#alwaysFalse() */ Condition FALSE = new Condition() { + @Override public boolean value(final Object object) { return false; } diff --git a/platform/util-rt/src/com/intellij/openapi/util/Conditions.java b/platform/util-rt/src/com/intellij/openapi/util/Conditions.java index 48bb06ad8b8e..ef3c0d2acbf6 100644 --- a/platform/util-rt/src/com/intellij/openapi/util/Conditions.java +++ b/platform/util-rt/src/com/intellij/openapi/util/Conditions.java @@ -18,6 +18,7 @@ package com.intellij.openapi.util; import com.intellij.reference.SoftReference; import com.intellij.util.ArrayUtilRt; +import org.jetbrains.annotations.NotNull; import java.util.HashMap; @@ -27,15 +28,18 @@ import java.util.HashMap; public class Conditions { private Conditions() {} + @NotNull public static Condition alwaysTrue() { return (Condition)TRUE; } + @NotNull public static Condition alwaysFalse() { return (Condition)FALSE; } public static Condition instanceOf(final Class clazz) { return new Condition() { + @Override public boolean value(T t) { return clazz.isInstance(t); } @@ -44,6 +48,7 @@ public class Conditions { public static Condition is(final T option) { return new Condition() { + @Override public boolean value(T t) { return Comparing.equal(t, option); } @@ -52,6 +57,7 @@ public class Conditions { public static Condition oneOf(final T... options) { return new Condition() { + @Override public boolean value(T t) { return ArrayUtilRt.find(options, t) >= 0; } @@ -78,6 +84,7 @@ public class Conditions { myCondition = condition; } + @Override public boolean value(T value) { return !myCondition.value(value); } @@ -91,6 +98,7 @@ public class Conditions { this.t2 = t2; } + @Override public boolean value(final T object) { return t1.value(object) && t2.value(object); } @@ -104,17 +112,20 @@ public class Conditions { this.t2 = t2; } + @Override public boolean value(final T object) { return t1.value(object) || t2.value(object); } } public static Condition TRUE = new Condition() { + @Override public boolean value(final Object object) { return true; } }; public static Condition FALSE = new Condition() { + @Override public boolean value(final Object object) { return false; } @@ -128,6 +139,7 @@ public class Conditions { myCondition = condition; } + @Override public final boolean value(T object) { final int key = object.hashCode(); final Pair, Boolean> entry = myCache.get(key); diff --git a/platform/util-rt/src/com/intellij/openapi/util/text/StringUtilRt.java b/platform/util-rt/src/com/intellij/openapi/util/text/StringUtilRt.java index 8b6e353fdbea..cc667ba8a80d 100644 --- a/platform/util-rt/src/com/intellij/openapi/util/text/StringUtilRt.java +++ b/platform/util-rt/src/com/intellij/openapi/util/text/StringUtilRt.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,6 +15,7 @@ */ package com.intellij.openapi.util.text; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -27,11 +28,13 @@ import org.jetbrains.annotations.Nullable; */ @SuppressWarnings({"UtilityClassWithoutPrivateConstructor"}) public class StringUtilRt { + @Contract(pure = true) public static boolean charsEqualIgnoreCase(char a, char b) { return a == b || toUpperCase(a) == toUpperCase(b) || toLowerCase(a) == toLowerCase(b); } @NotNull + @Contract(pure = true) public static String toUpperCase(@NotNull String s) { StringBuilder answer = null; @@ -51,6 +54,7 @@ public class StringUtilRt { return answer == null ? s : answer.toString(); } + @Contract(pure = true) public static char toUpperCase(char a) { if (a < 'a') { return a; @@ -61,6 +65,7 @@ public class StringUtilRt { return Character.toUpperCase(a); } + @Contract(pure = true) public static char toLowerCase(char a) { if (a < 'A' || a >= 'a' && a <= 'z') { return a; @@ -77,21 +82,25 @@ public class StringUtilRt { * Converts line separators to "\n" */ @NotNull + @Contract(pure = true) public static String convertLineSeparators(@NotNull String text) { return convertLineSeparators(text, false); } @NotNull + @Contract(pure = true) public static String convertLineSeparators(@NotNull String text, boolean keepCarriageReturn) { return convertLineSeparators(text, "\n", null, keepCarriageReturn); } @NotNull + @Contract(pure = true) public static String convertLineSeparators(@NotNull String text, @NotNull String newSeparator) { return convertLineSeparators(text, newSeparator, null); } @NotNull + @Contract(pure = true) public static CharSequence convertLineSeparators(@NotNull CharSequence text, @NotNull String newSeparator) { return unifyLineSeparators(text, newSeparator, null, false); } @@ -110,6 +119,7 @@ public class StringUtilRt { } @NotNull + @Contract(pure = true) public static CharSequence unifyLineSeparators(@NotNull CharSequence text) { return unifyLineSeparators(text, "\n", null, false); } @@ -189,6 +199,7 @@ public class StringUtilRt { } } + @Contract(pure = true) public static int parseInt(final String string, final int defaultValue) { try { return Integer.parseInt(string); @@ -198,6 +209,7 @@ public class StringUtilRt { } } + @Contract(pure = true) public static double parseDouble(final String string, final double defaultValue) { try { return Double.parseDouble(string); @@ -207,6 +219,7 @@ public class StringUtilRt { } } + @Contract(pure = true) public static boolean parseBoolean(final String string, final boolean defaultValue) { try { return Boolean.parseBoolean(string); @@ -217,16 +230,19 @@ public class StringUtilRt { } @NotNull + @Contract(pure = true) public static String getShortName(@NotNull Class aClass) { return getShortName(aClass.getName()); } @NotNull + @Contract(pure = true) public static String getShortName(@NotNull String fqName) { return getShortName(fqName, '.'); } @NotNull + @Contract(pure = true) public static String getShortName(@NotNull String fqName, char separator) { int lastPointIdx = fqName.lastIndexOf(separator); if (lastPointIdx >= 0) { @@ -235,16 +251,19 @@ public class StringUtilRt { return fqName; } + @Contract(pure = true) public static boolean endsWithChar(@Nullable CharSequence s, char suffix) { return s != null && s.length() != 0 && s.charAt(s.length() - 1) == suffix; } + @Contract(pure = true) public static boolean startsWithIgnoreCase(@NonNls @NotNull String str, @NonNls @NotNull String prefix) { final int stringLength = str.length(); final int prefixLength = prefix.length(); return stringLength >= prefixLength && str.regionMatches(true, 0, prefix, 0, prefixLength); } + @Contract(pure = true) public static boolean endsWithIgnoreCase(@NonNls @NotNull CharSequence text, @NonNls @NotNull CharSequence suffix) { int l1 = text.length(); int l2 = suffix.length(); @@ -269,6 +288,7 @@ public class StringUtilRt { * @return index of the last occurrence of the given symbol at the target sub-sequence of the given text if any; * -1 otherwise */ + @Contract(pure = true) public static int lastIndexOf(@NotNull CharSequence s, char c, int start, int end) { for (int i = end - 1; i >= start; i--) { if (s.charAt(i) == c) return i; diff --git a/platform/util-rt/src/com/intellij/util/ArrayUtilRt.java b/platform/util-rt/src/com/intellij/util/ArrayUtilRt.java index b18b62889623..115a6ceba1e5 100644 --- a/platform/util-rt/src/com/intellij/util/ArrayUtilRt.java +++ b/platform/util-rt/src/com/intellij/util/ArrayUtilRt.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,8 +15,9 @@ */ package com.intellij.util; -import org.jetbrains.annotations.NotNull; import com.intellij.util.containers.ContainerUtilRt; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.File; @@ -44,6 +45,7 @@ public class ArrayUtilRt { public static final Runnable[] EMPTY_RUNNABLE_ARRAY = new Runnable[0]; @NotNull + @Contract(pure=true) public static String[] toStringArray(@Nullable Collection collection) { return collection == null || collection.isEmpty() ? EMPTY_STRING_ARRAY : ContainerUtilRt.toArray(collection, new String[collection.size()]); @@ -57,6 +59,7 @@ public class ArrayUtilRt { * equals of arrays elements to compare obj with * these elements. */ + @Contract(pure=true) public static int find(@NotNull final T[] src, final T obj) { for (int i = 0; i < src.length; i++) { final T o = src[i]; diff --git a/platform/util-rt/src/com/intellij/util/containers/ContainerUtilRt.java b/platform/util-rt/src/com/intellij/util/containers/ContainerUtilRt.java index e4b3153917f7..a89de17fe6fe 100644 --- a/platform/util-rt/src/com/intellij/util/containers/ContainerUtilRt.java +++ b/platform/util-rt/src/com/intellij/util/containers/ContainerUtilRt.java @@ -18,6 +18,7 @@ package com.intellij.util.containers; import com.intellij.openapi.util.Pair; import com.intellij.util.ArrayUtilRt; import com.intellij.util.Function; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -40,16 +41,19 @@ public class ContainerUtilRt { private static final int ARRAY_COPY_THRESHOLD = 20; @NotNull + @Contract(pure=true) public static HashMap newHashMap() { return new com.intellij.util.containers.HashMap(); } @NotNull + @Contract(pure=true) public static HashMap newHashMap(@NotNull Map map) { return new com.intellij.util.containers.HashMap(map); } @NotNull + @Contract(pure=true) public static Map newHashMap(@NotNull List keys, @NotNull List values) { if (keys.size() != values.size()) { throw new IllegalArgumentException(keys + " should have same length as " + values); @@ -63,6 +67,7 @@ public class ContainerUtilRt { } @NotNull + @Contract(pure=true) public static Map newHashMap(@NotNull Pair first, Pair[] entries) { Map map = newHashMap(); map.put(first.getFirst(), first.getSecond()); @@ -73,26 +78,43 @@ public class ContainerUtilRt { } @NotNull + @Contract(pure=true) + public static Map newHashMap(int initialCapacity) { + return new com.intellij.util.containers.HashMap(initialCapacity); + } + + @NotNull + @Contract(pure=true) public static TreeMap newTreeMap() { return new TreeMap(); } @NotNull + @Contract(pure=true) public static TreeMap newTreeMap(@NotNull Map map) { return new TreeMap(map); } @NotNull + @Contract(pure=true) public static LinkedHashMap newLinkedHashMap() { return new com.intellij.util.containers.LinkedHashMap(); } @NotNull + @Contract(pure=true) + public static LinkedHashMap newLinkedHashMap(int capacity) { + return new com.intellij.util.containers.LinkedHashMap(capacity); + } + + @NotNull + @Contract(pure=true) public static LinkedHashMap newLinkedHashMap(@NotNull Map map) { return new com.intellij.util.containers.LinkedHashMap(map); } @NotNull + @Contract(pure=true) public static LinkedHashMap newLinkedHashMap(@NotNull Pair first, Pair[] entries) { LinkedHashMap map = newLinkedHashMap(); map.put(first.getFirst(), first.getSecond()); @@ -103,11 +125,13 @@ public class ContainerUtilRt { } @NotNull + @Contract(pure=true) public static LinkedList newLinkedList() { return new LinkedList(); } @NotNull + @Contract(pure=true) public static LinkedList newLinkedList(@NotNull T... elements) { final LinkedList list = newLinkedList(); Collections.addAll(list, elements); @@ -115,16 +139,19 @@ public class ContainerUtilRt { } @NotNull + @Contract(pure=true) public static LinkedList newLinkedList(@NotNull Iterable elements) { return copy(ContainerUtilRt.newLinkedList(), elements); } @NotNull + @Contract(pure=true) public static ArrayList newArrayList() { return new ArrayList(); } @NotNull + @Contract(pure=true) public static ArrayList newArrayList(@NotNull T... elements) { ArrayList list = newArrayListWithCapacity(elements.length); Collections.addAll(list, elements); @@ -132,6 +159,7 @@ public class ContainerUtilRt { } @NotNull + @Contract(pure=true) public static ArrayList newArrayList(@NotNull Iterable elements) { if (elements instanceof Collection) { @SuppressWarnings("unchecked") Collection collection = (Collection)elements; @@ -141,11 +169,13 @@ public class ContainerUtilRt { } /** @deprecated Use {@link #newArrayListWithCapacity(int)} (to remove in IDEA 15) */ + @Contract(pure=true) public static ArrayList newArrayListWithExpectedSize(int size) { return newArrayListWithCapacity(size); } @NotNull + @Contract(pure=true) public static ArrayList newArrayListWithCapacity(int size) { return new ArrayList(size); } @@ -159,21 +189,25 @@ public class ContainerUtilRt { } @NotNull + @Contract(pure=true) public static HashSet newHashSet() { return new com.intellij.util.containers.HashSet(); } @NotNull + @Contract(pure=true) public static HashSet newHashSet(int initialCapacity) { return new com.intellij.util.containers.HashSet(initialCapacity); } @NotNull + @Contract(pure=true) public static HashSet newHashSet(@NotNull T... elements) { return new com.intellij.util.containers.HashSet(Arrays.asList(elements)); } @NotNull + @Contract(pure=true) public static HashSet newHashSet(@NotNull Iterable elements) { if (elements instanceof Collection) { @SuppressWarnings("unchecked") Collection collection = (Collection)elements; @@ -183,6 +217,7 @@ public class ContainerUtilRt { } @NotNull + @Contract(pure=true) public static HashSet newHashSet(@NotNull Iterator iterator) { HashSet set = newHashSet(); while (iterator.hasNext()) set.add(iterator.next()); @@ -190,16 +225,19 @@ public class ContainerUtilRt { } @NotNull + @Contract(pure=true) public static LinkedHashSet newLinkedHashSet() { return new com.intellij.util.containers.LinkedHashSet(); } @NotNull + @Contract(pure=true) public static LinkedHashSet newLinkedHashSet(@NotNull T... elements) { return newLinkedHashSet(Arrays.asList(elements)); } @NotNull + @Contract(pure=true) public static LinkedHashSet newLinkedHashSet(@NotNull Iterable elements) { if (elements instanceof Collection) { @SuppressWarnings("unchecked") Collection collection = (Collection)elements; @@ -209,11 +247,13 @@ public class ContainerUtilRt { } @NotNull + @Contract(pure=true) public static TreeSet newTreeSet() { return new TreeSet(); } @NotNull + @Contract(pure=true) public static TreeSet newTreeSet(@NotNull T... elements) { TreeSet set = newTreeSet(); Collections.addAll(set, elements); @@ -221,26 +261,31 @@ public class ContainerUtilRt { } @NotNull + @Contract(pure=true) public static TreeSet newTreeSet(@NotNull Iterable elements) { return copy(ContainerUtilRt.newTreeSet(), elements); } @NotNull + @Contract(pure=true) public static TreeSet newTreeSet(@Nullable Comparator comparator) { return new TreeSet(comparator); } @NotNull + @Contract(pure=true) public static Stack newStack() { return new Stack(); } @NotNull + @Contract(pure=true) public static Stack newStack(@NotNull Collection elements) { return new Stack(elements); } @NotNull + @Contract(pure=true) public static Stack newStack(@NotNull T... initial) { return new Stack(Arrays.asList(initial)); } @@ -292,12 +337,14 @@ public class ContainerUtilRt { } @NotNull + @Contract(pure=true) public static List emptyList() { //noinspection unchecked return (List)EmptyList.INSTANCE; } @NotNull + @Contract(pure=true) public static CopyOnWriteArrayList createEmptyCOWList() { // does not create garbage new Object[0] return new CopyOnWriteArrayList(ContainerUtilRt.emptyList()); @@ -319,6 +366,7 @@ public class ContainerUtilRt { * @return read-only list consisting of the elements from array converted by mapper */ @NotNull + @Contract(pure=true) public static List map2List(@NotNull T[] array, @NotNull Function mapper) { return map2List(Arrays.asList(array), mapper); } @@ -327,6 +375,7 @@ public class ContainerUtilRt { * @return read-only list consisting of the elements from collection converted by mapper */ @NotNull + @Contract(pure=true) public static List map2List(@NotNull Collection collection, @NotNull Function mapper) { if (collection.isEmpty()) return emptyList(); List list = new ArrayList(collection.size()); @@ -340,6 +389,7 @@ public class ContainerUtilRt { * @return read-only set consisting of the elements from collection converted by mapper */ @NotNull + @Contract(pure=true) public static Set map2Set(@NotNull T[] collection, @NotNull Function mapper) { return map2Set(Arrays.asList(collection), mapper); } @@ -348,6 +398,7 @@ public class ContainerUtilRt { * @return read-only set consisting of the elements from collection converted by mapper */ @NotNull + @Contract(pure=true) public static Set map2Set(@NotNull Collection collection, @NotNull Function mapper) { if (collection.isEmpty()) return Collections.emptySet(); Set set = new HashSet(collection.size()); diff --git a/platform/util-rt/src/com/intellij/util/containers/HashMap.java b/platform/util-rt/src/com/intellij/util/containers/HashMap.java index af41488814d0..fa8608f50bf8 100644 --- a/platform/util-rt/src/com/intellij/util/containers/HashMap.java +++ b/platform/util-rt/src/com/intellij/util/containers/HashMap.java @@ -21,12 +21,12 @@ import java.util.Map; public class HashMap extends java.util.HashMap { public HashMap() { } - public HashMap(int i, float v) { - super(i, v); + public HashMap(int initialCapacity, float loadFactor) { + super(initialCapacity, loadFactor); } - public HashMap(int i) { - super(i); + public HashMap(int initialCapacity) { + super(initialCapacity); } public HashMap(Map map) { diff --git a/platform/util-rt/src/com/intellij/util/containers/Stack.java b/platform/util-rt/src/com/intellij/util/containers/Stack.java index 29337c9c99b1..f1a0e37b1967 100644 --- a/platform/util-rt/src/com/intellij/util/containers/Stack.java +++ b/platform/util-rt/src/com/intellij/util/containers/Stack.java @@ -15,6 +15,7 @@ */ package com.intellij.util.containers; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.*; @@ -32,11 +33,11 @@ public class Stack extends ArrayList { super(initialCapacity); } - public Stack(Collection init) { + public Stack(@NotNull Collection init) { super(init); } - public Stack(T... items) { + public Stack(@NotNull T... items) { for (T item : items) { push(item); } diff --git a/platform/util/resources/misc/registry.properties b/platform/util/resources/misc/registry.properties index 11ace8a42cad..5b37906c82ec 100644 --- a/platform/util/resources/misc/registry.properties +++ b/platform/util/resources/misc/registry.properties @@ -45,6 +45,15 @@ ide.firstStartup=true ide.debugMode=false ide.debugger.inline=false ide.debugger.inline.description=Enables variables view in editor +ide.debugger.inline.fg.color=61,128,101 +ide.debugger.inline.fg.color.description=Inline values foreground +ide.debugger.inline.dark.fg.color=61,128,101 +ide.debugger.inline.dark.fg.color.description=Inline values foreground for dark editor scheme +ide.debugger.inline.fg.modified.color=202,128,33 +ide.debugger.inline.fg.modified.color.description=Inline modified values foreground +ide.debugger.inline.dark.fg.modified.color=161,131,10 +ide.debugger.inline.dark.fg.modified.color.description=Inline modified values foreground for dark editor scheme + ide.debugMode.description=Record additional information to make bug reports more informative. ide.debug.minProgressTime=0 ide.forcedShowTooltip=alt @@ -122,7 +131,6 @@ editor.use.scrollable.tabs=true editor.smarterSelectionQuoting=true editor.skip.copy.and.cut.for.empty.selection=false editor.distraction.free.mode=false -editor.use.preview=false editor.add.carets.on.double.control.arrows=true ide.showIndexRebuildMessage=false @@ -183,9 +191,6 @@ compiler.process.use.memory.temp.cache=true compiler.process.use.memory.temp.cache.description=Store temporary data in memory for faster compilation;\ requires larger heap size for the build process. If parallel build is enabled, the option is ignored and temp data is always stored in memory. -compiler.process.use.external.javac=false -compiler.process.use.external.javac.description=Run javac compiler in a separate process (allows to run build process with smaller heap size). - compiler.process.debug.port=-1 compiler.automake.trigger.delay=300 @@ -233,6 +238,9 @@ ide.completion.autopopup.choose.by.enter=true java.completion.make.outer.variables.final=true java.completion.make.outer.variables.final.description=Make variables accessed from inner class final automatically +java.annotations.inference.nullable.method.transitivity=true +java.annotations.inference.nullable.method.transitivity.description=Restart is required; if a method result is a call to a @Nullable method, reports the caller as @Nullable as well + documentation.component.editor.font=false ide.completion.show.better.matching.classes=true @@ -414,3 +422,5 @@ ide.new.markup.markers.description=New error stripe markers spy.js.realtime.evaluation=false spy.js.realtime.evaluation.description=Enables spy-js autocomplete and realtime evaluation + +new.css.schema.enabled=false diff --git a/platform/util/src/com/intellij/execution/configurations/CommandLineTokenizer.java b/platform/util/src/com/intellij/execution/configurations/CommandLineTokenizer.java index a62af9aa62dc..3f351b9a573e 100644 --- a/platform/util/src/com/intellij/execution/configurations/CommandLineTokenizer.java +++ b/platform/util/src/com/intellij/execution/configurations/CommandLineTokenizer.java @@ -20,9 +20,9 @@ import java.util.List; import java.util.StringTokenizer; /** - * Splits input String to tokens being aware of quoted tokens ("foo bar") and escaped spaces (foo\ bar), + * Splits input String to tokens being aware of quoted tokens ("foo bar") and escaped spaces & quotes (\"foo\ bar\"), * usually used for splitting command line to separate arguments that may contain space symbols. - * Escaped symbols are not handled so there's no way to get token that itself contains quotation mark. + * Space and quote are the only symbols that can be escaped */ public class CommandLineTokenizer extends StringTokenizer { @@ -103,8 +103,10 @@ public class CommandLineTokenizer extends StringTokenizer { do { while ((i = nextToken.indexOf('"')) >= 0) { - quotationMarks++; - buffer.append(nextToken.substring(0, i)); + boolean isEscapedQuote = i > 0 && nextToken.charAt(i - 1) == '\\'; + if (!isEscapedQuote) quotationMarks++; + buffer.append(nextToken.substring(0, isEscapedQuote ? i - 1 : i)); + if (isEscapedQuote) buffer.append('"'); nextToken = nextToken.substring(i + 1); } diff --git a/platform/util/src/com/intellij/icons/AllIcons.java b/platform/util/src/com/intellij/icons/AllIcons.java index a80ffccdd58e..f31f9b94b058 100644 --- a/platform/util/src/com/intellij/icons/AllIcons.java +++ b/platform/util/src/com/intellij/icons/AllIcons.java @@ -204,6 +204,7 @@ public class AllIcons { public static final Icon AutoVariablesMode = IconLoader.getIcon("/debugger/autoVariablesMode.png"); // 16x16 public static final Icon BreakpointAlert = IconLoader.getIcon("/debugger/breakpointAlert.png"); // 16x16 public static final Icon Class_filter = IconLoader.getIcon("/debugger/class_filter.png"); // 16x16 + public static final Icon CommandLine = IconLoader.getIcon("/debugger/commandLine.png"); // 16x16 public static final Icon Console = IconLoader.getIcon("/debugger/console.png"); // 16x16 public static final Icon Db_array = IconLoader.getIcon("/debugger/db_array.png"); // 16x16 public static final Icon Db_db_object = IconLoader.getIcon("/debugger/db_db_object.png"); // 16x16 diff --git a/platform/util/src/com/intellij/openapi/application/PathManager.java b/platform/util/src/com/intellij/openapi/application/PathManager.java index 9956959f0e22..0188203cde33 100644 --- a/platform/util/src/com/intellij/openapi/application/PathManager.java +++ b/platform/util/src/com/intellij/openapi/application/PathManager.java @@ -45,7 +45,6 @@ public class PathManager { @NonNls public static final String PROPERTY_HOME_PATH = "idea.home.path"; @NonNls public static final String PROPERTY_LOG_PATH = "idea.log.path"; @NonNls public static final String PROPERTY_PATHS_SELECTOR = "idea.paths.selector"; - @NonNls public static final String PROPERTY_ORIGINAL_WORKING_DIR = "original.working.dir"; @NonNls public static final String DEFAULT_OPTIONS_FILE_NAME = "other"; @NonNls private static final String LIB_FOLDER = "lib"; @@ -253,11 +252,6 @@ public class PathManager { // misc stuff - @Nullable - public static String getOriginalWorkingDir() { - return System.getProperty(PROPERTY_ORIGINAL_WORKING_DIR); - } - /** * Attempts to detect classpath entry which contains given resource. */ @@ -418,7 +412,7 @@ public class PathManager { private static String getAbsolutePath(String path) { path = FileUtil.expandUserHome(path); - return FileUtil.toCanonicalPath(new File(FileUtil.toCanonicalPath(path)).getAbsolutePath()); + return FileUtil.toCanonicalPath(new File(path).getAbsolutePath()); } private static String trimPathQuotes(String path){ diff --git a/platform/util/src/com/intellij/openapi/util/JDOMExternalizer.java b/platform/util/src/com/intellij/openapi/util/JDOMExternalizer.java index 909c44db199b..cd5e8630a087 100644 --- a/platform/util/src/com/intellij/openapi/util/JDOMExternalizer.java +++ b/platform/util/src/com/intellij/openapi/util/JDOMExternalizer.java @@ -22,7 +22,10 @@ import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.*; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; public class JDOMExternalizer { private JDOMExternalizer() { @@ -66,7 +69,7 @@ public class JDOMExternalizer { return null; } - public static void writeMap(Element root, Mapmap, @NonNls @Nullable String rootName, @NonNls String entryName) { + public static void writeMap(Element root, Map map, @NonNls @Nullable String rootName, @NonNls String entryName) { Element mapRoot; if (StringUtil.isNotEmpty(rootName)) { mapRoot = new Element(rootName); @@ -106,6 +109,26 @@ public class JDOMExternalizer { } } + /** + * Saves a pack of strings to some attribte. I.e: [tag attr="value"] + * @param parent parent element (where to add newly created tags) + * @param nodeName node name (tag, in our example) + * @param attrName attribute name (attr, in our example) + * @param values a pack of values to add + * @see #loadStringsList(org.jdom.Element, String, String) + */ + public static void saveStringsList(@NotNull final Element parent, + @NotNull final String nodeName, + @NotNull final String attrName, + @NotNull final String... values) { + for (final String value : values) { + final Element node = new Element(nodeName); + node.setAttribute(attrName, value); + parent.addContent(node); + } + } + + @NotNull public static List loadStringsList(Element element, String rootName, String attrName) { final List paths = new LinkedList(); if (element != null) { diff --git a/platform/util/src/com/intellij/openapi/util/JDOMUtil.java b/platform/util/src/com/intellij/openapi/util/JDOMUtil.java index c82ee6df346b..dfee1dbed2df 100644 --- a/platform/util/src/com/intellij/openapi/util/JDOMUtil.java +++ b/platform/util/src/com/intellij/openapi/util/JDOMUtil.java @@ -398,9 +398,13 @@ public class JDOMUtil { } public static void writeDocument(@NotNull Document document, @NotNull File file, String lineSeparator) throws IOException { + writeParent(document, file, lineSeparator); + } + + public static void writeParent(@NotNull Parent element, @NotNull File file, String lineSeparator) throws IOException { OutputStream stream = new BufferedOutputStream(new FileOutputStream(file)); try { - writeDocument(document, stream, lineSeparator); + writeParent(element, stream, lineSeparator); } finally { stream.close(); @@ -408,9 +412,23 @@ public class JDOMUtil { } public static void writeDocument(@NotNull Document document, @NotNull OutputStream stream, String lineSeparator) throws IOException { - writeDocument(document, new OutputStreamWriter(stream, CharsetToolkit.UTF8_CHARSET), lineSeparator); + writeParent(document, stream, lineSeparator); } + public static void writeParent(@NotNull Parent element, @NotNull OutputStream stream, @NotNull String lineSeparator) throws IOException { + OutputStreamWriter writer = new OutputStreamWriter(stream, CharsetToolkit.UTF8_CHARSET); + try { + if (element instanceof Document) { + writeDocument((Document)element, writer, lineSeparator); + } + else { + writeElement((Element) element, writer, lineSeparator); + } + } + finally { + writer.close(); + } + } @NotNull public static byte[] printDocument(@NotNull Document document, String lineSeparator) throws IOException { 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 7d3d6f557249..bc41f70e933a 100644 --- a/platform/util/src/com/intellij/openapi/util/io/FileUtil.java +++ b/platform/util/src/com/intellij/openapi/util/io/FileUtil.java @@ -1249,18 +1249,15 @@ public class FileUtil extends FileUtilRt { } public static boolean isWindowsAbsolutePath(@NotNull String pathString) { - if (pathString.length() >= 2 && Character.isLetter(pathString.charAt(0)) && pathString.charAt(1) == ':') { - return true; - } - return false; + return pathString.length() >= 2 && Character.isLetter(pathString.charAt(0)) && pathString.charAt(1) == ':'; } - @Contract("null -> null") + @Contract("null -> null; !null -> !null") public static String getLocationRelativeToUserHome(@Nullable String path) { return getLocationRelativeToUserHome(path, true); } - @Contract("null,_ -> null") + @Contract("null,_ -> null; !null,_ -> !null") public static String getLocationRelativeToUserHome(@Nullable String path, boolean unixOnly) { if (path == null) return null; diff --git a/platform/util/src/com/intellij/openapi/util/text/StringUtil.java b/platform/util/src/com/intellij/openapi/util/text/StringUtil.java index af2b3dd4293e..c2b7de8ba7ed 100644 --- a/platform/util/src/com/intellij/openapi/util/text/StringUtil.java +++ b/platform/util/src/com/intellij/openapi/util/text/StringUtil.java @@ -64,6 +64,7 @@ public class StringUtil extends StringUtilRt { }; @NotNull + @Contract(pure = true) public static List getWordsInStringLongestFirst(@NotNull String find) { List words = getWordsIn(find); // hope long words are rare @@ -77,11 +78,13 @@ public class StringUtil extends StringUtilRt { } @NotNull + @Contract(pure = true) public static String escapePattern(@NotNull final String text) { return replace(replace(text, "'", "''"), "{", "'{'"); } @NotNull + @Contract(pure = true) public static Function createToStringFunction(@NotNull Class cls) { return new Function() { @Override @@ -101,11 +104,13 @@ public class StringUtil extends StringUtilRt { }; @NotNull + @Contract(pure = true) public static String replace(@NonNls @NotNull String text, @NonNls @NotNull String oldS, @NonNls @NotNull String newS) { return replace(text, oldS, newS, false); } @NotNull + @Contract(pure = true) public static String replaceIgnoreCase(@NonNls @NotNull String text, @NonNls @NotNull String oldS, @NonNls @NotNull String newS) { return replace(text, oldS, newS, true); } @@ -120,6 +125,7 @@ public class StringUtil extends StringUtilRt { } @NotNull + @Contract(pure = true) public static String replaceChar(@NotNull String buffer, char oldChar, char newChar) { StringBuilder newBuffer = null; for (int i = 0; i < buffer.length(); i++) { @@ -139,6 +145,7 @@ public class StringUtil extends StringUtilRt { return newBuffer == null ? buffer : newBuffer.toString(); } + @Contract(pure = true) public static String replace(@NonNls @NotNull final String text, @NonNls @NotNull final String oldS, @NonNls @NotNull final String newS, final boolean ignoreCase) { if (text.length() < oldS.length()) return text; @@ -146,17 +153,26 @@ public class StringUtil extends StringUtilRt { int i = 0; while (i < text.length()) { - final int i1 = ignoreCase? indexOfIgnoreCase(text, oldS, i) : text.indexOf(oldS, i); - if (i1 < 0) { - if (i == 0) return text; + final int index = ignoreCase? indexOfIgnoreCase(text, oldS, i) : text.indexOf(oldS, i); + if (index < 0) { + if (i == 0) { + return text; + } + newText.append(text, i, text.length()); break; } else { - if (newText == null) newText = new StringBuilder(text.length() - i); - newText.append(text, i, i1); + if (newText == null) { + if (text.length() == oldS.length()) { + return newS; + } + newText = new StringBuilder(text.length() - i); + } + + newText.append(text, i, index); newText.append(newS); - i = i1 + oldS.length(); + i = index + oldS.length(); } } return newText != null ? newText.toString() : ""; @@ -165,6 +181,7 @@ public class StringUtil extends StringUtilRt { /** * Implementation copied from {@link String#indexOf(String, int)} except character comparisons made case insensitive */ + @Contract(pure = true) public static int indexOfIgnoreCase(@NotNull String where, @NotNull String what, int fromIndex) { int targetCount = what.length(); int sourceCount = where.length(); @@ -206,6 +223,7 @@ public class StringUtil extends StringUtilRt { return -1; } + @Contract(pure = true) public static int indexOfIgnoreCase(@NotNull String where, char what, int fromIndex) { int sourceCount = where.length(); @@ -226,18 +244,22 @@ public class StringUtil extends StringUtilRt { return -1; } + @Contract(pure = true) public static boolean containsIgnoreCase(@NotNull String where, @NotNull String what) { return indexOfIgnoreCase(where, what, 0) >= 0; } + @Contract(pure = true) public static boolean endsWithIgnoreCase(@NonNls @NotNull String str, @NonNls @NotNull String suffix) { return StringUtilRt.endsWithIgnoreCase(str, suffix); } + @Contract(pure = true) public static boolean startsWithIgnoreCase(@NonNls @NotNull String str, @NonNls @NotNull String prefix) { return StringUtilRt.startsWithIgnoreCase(str, prefix); } + @Contract(pure = true) public static String stripHtml(@NotNull String html, boolean convertBreaks) { if (convertBreaks) { html = html.replaceAll("
", "\n\n"); @@ -246,18 +268,20 @@ public class StringUtil extends StringUtilRt { return html.replaceAll("<(.|\n)*?>", ""); } - @Contract("null -> null; !null -> !null") + @Contract(value = "null -> null; !null -> !null", pure = true) public static String toLowerCase(@Nullable final String str) { //noinspection ConstantConditions return str == null ? null : str.toLowerCase(); } @NotNull + @Contract(pure = true) public static String getPackageName(@NotNull String fqName) { return getPackageName(fqName, '.'); } @NotNull + @Contract(pure = true) public static String getPackageName(@NotNull String fqName, char separator) { int lastPointIdx = fqName.lastIndexOf(separator); if (lastPointIdx >= 0) { @@ -266,6 +290,7 @@ public class StringUtil extends StringUtilRt { return ""; } + @Contract(pure = true) public static int getLineBreakCount(@NotNull CharSequence text) { int count = 0; for (int i = 0; i < text.length(); i++) { @@ -287,6 +312,7 @@ public class StringUtil extends StringUtilRt { return count; } + @Contract(pure = true) public static boolean containsLineBreak(@NotNull CharSequence text) { for (int i = 0; i < text.length(); i++) { char c = text.charAt(i); @@ -295,11 +321,13 @@ public class StringUtil extends StringUtilRt { return false; } + @Contract(pure = true) public static boolean isLineBreak(char c) { return c == '\n' || c == '\r'; } @NotNull + @Contract(pure = true) public static String escapeLineBreak(@NotNull String text) { StringBuilder buffer = new StringBuilder(text.length()); for (int i = 0; i < text.length(); i++) { @@ -318,11 +346,13 @@ public class StringUtil extends StringUtilRt { return buffer.toString(); } + @Contract(pure = true) public static boolean endsWithLineBreak(@NotNull CharSequence text) { int len = text.length(); return len > 0 && isLineBreak(text.charAt(len - 1)); } + @Contract(pure = true) public static int lineColToOffset(@NotNull CharSequence text, int line, int col) { int curLine = 0; int offset = 0; @@ -343,6 +373,7 @@ public class StringUtil extends StringUtilRt { return offset + col; } + @Contract(pure = true) public static int offsetToLineNumber(@NotNull CharSequence text, int offset) { int curLine = 0; int curOffset = 0; @@ -366,6 +397,7 @@ public class StringUtil extends StringUtilRt { /** * Classic dynamic programming algorithm for string differences. */ + @Contract(pure = true) public static int difference(@NotNull String s1, @NotNull String s2) { int[][] a = new int[s1.length()][s2.length()]; @@ -388,11 +420,13 @@ public class StringUtil extends StringUtilRt { } @NotNull + @Contract(pure = true) public static String wordsToBeginFromUpperCase(@NotNull String s) { return toTitleCase(s, ourPrepositions); } @NotNull + @Contract(pure = true) public static String toTitleCase(@NotNull String s) { return toTitleCase(s, ArrayUtil.EMPTY_STRING_ARRAY); } @@ -435,10 +469,12 @@ public class StringUtil extends StringUtilRt { "per", "nor", "the", "to", "up", "upon", "via", "with" }; + @Contract(pure = true) public static boolean isPreposition(@NotNull String s, int firstChar, int lastChar) { return isPreposition(s, firstChar, lastChar, ourPrepositions); } + @Contract(pure = true) public static boolean isPreposition(@NotNull String s, int firstChar, int lastChar, @NotNull String[] prepositions) { for (String preposition : prepositions) { boolean found = false; @@ -458,6 +494,7 @@ public class StringUtil extends StringUtilRt { } @NotNull + @Contract(pure = true) public static NotNullFunction escaper(final boolean escapeSlash, @Nullable final String additionalChars) { return new NotNullFunction() { @NotNull @@ -538,6 +575,7 @@ public class StringUtil extends StringUtilRt { return buffer; } + @Contract(pure = true) private static boolean isPrintableUnicode(char c) { int t = Character.getType(c); return t != Character.UNASSIGNED && t != Character.LINE_SEPARATOR && t != Character.PARAGRAPH_SEPARATOR && @@ -545,6 +583,7 @@ public class StringUtil extends StringUtilRt { } @NotNull + @Contract(pure = true) public static String escapeStringCharacters(@NotNull String s) { StringBuilder buffer = new StringBuilder(s.length()); escapeStringCharacters(s.length(), s, "\"", buffer); @@ -552,6 +591,7 @@ public class StringUtil extends StringUtilRt { } @NotNull + @Contract(pure = true) public static String escapeCharCharacters(@NotNull String s) { StringBuilder buffer = new StringBuilder(s.length()); escapeStringCharacters(s.length(), s, "\'", buffer); @@ -559,6 +599,7 @@ public class StringUtil extends StringUtilRt { } @NotNull + @Contract(pure = true) public static String unescapeStringCharacters(@NotNull String s) { StringBuilder buffer = new StringBuilder(s.length()); unescapeStringCharacters(s.length(), s, buffer); @@ -566,6 +607,7 @@ public class StringUtil extends StringUtilRt { } @NotNull + @Contract(pure = true) public static String unquoteString(@NotNull String s) { char c; if (s.length() <= 1 || (c = s.charAt(0)) != '"' && c != '\'' || s.charAt(s.length() - 1) != c) { @@ -575,6 +617,7 @@ public class StringUtil extends StringUtilRt { } @NotNull + @Contract(pure = true) public static String unquoteString(@NotNull String s, char quotationChar) { char c; if (s.length() <= 1 || (c = s.charAt(0)) != quotationChar || s.charAt(s.length() - 1) != c) { @@ -587,6 +630,7 @@ public class StringUtil extends StringUtilRt { * This is just an optimized version of Matcher.quoteReplacement */ @NotNull + @Contract(pure = true) public static String quoteReplacement(@NotNull String s) { boolean needReplacements = false; @@ -693,6 +737,7 @@ public class StringUtil extends StringUtilRt { @SuppressWarnings({"HardCodedStringLiteral"}) @NotNull + @Contract(pure = true) public static String pluralize(@NotNull String suggestion) { if (suggestion.endsWith("Child") || suggestion.endsWith("child")) { return suggestion + "ren"; @@ -718,12 +763,14 @@ public class StringUtil extends StringUtilRt { } @NotNull + @Contract(pure = true) public static String capitalizeWords(@NotNull String text, boolean allWords) { return capitalizeWords(text, " \t\n\r\f", allWords, false); } @NotNull + @Contract(pure = true) public static String capitalizeWords(@NotNull String text, @NotNull String tokenizerDelim, boolean allWords, @@ -744,15 +791,18 @@ public class StringUtil extends StringUtilRt { return out.toString(); } + @Contract(pure = true) public static String decapitalize(String s) { return Introspector.decapitalize(s); } + @Contract(pure = true) public static boolean isVowel(char c) { return VOWELS.indexOf(c) >= 0; } @NotNull + @Contract(pure = true) public static String capitalize(@NotNull String s) { if (s.isEmpty()) return s; if (s.length() == 1) return StringUtilRt.toUpperCase(s); @@ -762,12 +812,13 @@ public class StringUtil extends StringUtilRt { return toUpperCase(s.charAt(0)) + s.substring(1); } - @Contract("null -> false") + @Contract(value = "null -> false", pure = true) public static boolean isCapitalized(@Nullable String s) { return s != null && !s.isEmpty() && Character.isUpperCase(s.charAt(0)); } @NotNull + @Contract(pure = true) public static String capitalizeWithJavaBeanConvention(@NotNull String s) { if (s.length() > 1 && Character.isUpperCase(s.charAt(1))) { return s; @@ -775,6 +826,7 @@ public class StringUtil extends StringUtilRt { return capitalize(s); } + @Contract(pure = true) public static int stringHashCode(@NotNull CharSequence chars) { if (chars instanceof String) return chars.hashCode(); if (chars instanceof CharSequenceWithStringHash) return chars.hashCode(); @@ -783,6 +835,7 @@ public class StringUtil extends StringUtilRt { return stringHashCode(chars, 0, chars.length()); } + @Contract(pure = true) public static int stringHashCode(@NotNull CharSequence chars, int from, int to) { int h = 0; for (int off = from; off < to; off++) { @@ -791,6 +844,7 @@ public class StringUtil extends StringUtilRt { return h; } + @Contract(pure = true) public static int stringHashCode(char[] chars, int from, int to) { int h = 0; for (int off = from; off < to; off++) { @@ -799,6 +853,7 @@ public class StringUtil extends StringUtilRt { return h; } + @Contract(pure = true) public static int stringHashCodeInsensitive(@NotNull char[] chars, int from, int to) { int h = 0; for (int off = from; off < to; off++) { @@ -807,6 +862,7 @@ public class StringUtil extends StringUtilRt { return h; } + @Contract(pure = true) public static int stringHashCodeInsensitive(@NotNull CharSequence chars, int from, int to) { int h = 0; for (int off = from; off < to; off++) { @@ -815,6 +871,7 @@ public class StringUtil extends StringUtilRt { return h; } + @Contract(pure = true) public static int stringHashCodeInsensitive(@NotNull CharSequence chars) { return stringHashCodeInsensitive(chars, 0, chars.length()); } @@ -822,6 +879,7 @@ public class StringUtil extends StringUtilRt { /** * Equivalent to string.startsWith(prefixes[0] + prefixes[1] + ...) but avoids creating an object for concatenation. */ + @Contract(pure = true) public static boolean startsWithConcatenation(@NotNull String string, @NotNull String... prefixes) { int offset = 0; for (String prefix : prefixes) { @@ -838,6 +896,7 @@ public class StringUtil extends StringUtilRt { * @deprecated use {@link #startsWithConcatenation(String, String...)} (to remove in IDEA 14). */ @SuppressWarnings("UnusedDeclaration") + @Contract(pure = true) public static boolean startsWithConcatenationOf(@NotNull String string, @NotNull String firstPrefix, @NotNull String secondPrefix) { return startsWithConcatenation(string, firstPrefix, secondPrefix); } @@ -846,6 +905,7 @@ public class StringUtil extends StringUtilRt { * @deprecated use {@link #startsWithConcatenation(String, String...)} (to remove in IDEA 14). */ @SuppressWarnings("UnusedDeclaration") + @Contract(pure = true) public static boolean startsWithConcatenationOf(@NotNull String string, @NotNull String firstPrefix, @NotNull String secondPrefix, @@ -853,12 +913,13 @@ public class StringUtil extends StringUtilRt { return startsWithConcatenation(string, firstPrefix, secondPrefix, thirdPrefix); } - @Contract("null -> null; !null -> !null") + @Contract(value = "null -> null; !null -> !null", pure = true) public static String trim(@Nullable String s) { return s == null ? null : s.trim(); } @NotNull + @Contract(pure = true) public static String trimEnd(@NotNull String s, @NonNls @NotNull String suffix) { if (s.endsWith(suffix)) { return s.substring(0, s.length() - suffix.length()); @@ -867,6 +928,7 @@ public class StringUtil extends StringUtilRt { } @NotNull + @Contract(pure = true) public static String trimLog(@NotNull final String text, final int limit) { if (limit > 5 && text.length() > limit) { return text.substring(0, limit - 5) + " ...\n"; @@ -875,6 +937,7 @@ public class StringUtil extends StringUtilRt { } @NotNull + @Contract(pure = true) public static String trimLeading(@NotNull String string) { int index = 0; while (index < string.length() && Character.isWhitespace(string.charAt(index))) index++; @@ -882,6 +945,7 @@ public class StringUtil extends StringUtilRt { } @NotNull + @Contract(pure = true) public static String trimLeading(@NotNull String string, char symbol) { int index = 0; while (index < string.length() && string.charAt(index) == symbol) index++; @@ -889,21 +953,25 @@ public class StringUtil extends StringUtilRt { } @NotNull + @Contract(pure = true) public static String trimTrailing(@NotNull String string) { int index = string.length() - 1; while (index >= 0 && Character.isWhitespace(string.charAt(index))) index--; return string.substring(0, index + 1); } + @Contract(pure = true) public static boolean startsWithChar(@Nullable CharSequence s, char prefix) { return s != null && s.length() != 0 && s.charAt(0) == prefix; } + @Contract(pure = true) public static boolean endsWithChar(@Nullable CharSequence s, char suffix) { return StringUtilRt.endsWithChar(s, suffix); } @NotNull + @Contract(pure = true) public static String trimStart(@NotNull String s, @NonNls @NotNull String prefix) { if (s.startsWith(prefix)) { return s.substring(prefix.length()); @@ -912,6 +980,7 @@ public class StringUtil extends StringUtilRt { } @NotNull + @Contract(pure = true) public static String pluralize(@NotNull String base, int n) { if (n == 1) return base; return pluralize(base); @@ -929,45 +998,51 @@ public class StringUtil extends StringUtilRt { } } + @Contract(pure = true) public static String defaultIfEmpty(@Nullable String value, String defaultValue) { return isEmpty(value) ? defaultValue : value; } - @Contract("null -> false") + @Contract(value = "null -> false", pure = true) public static boolean isNotEmpty(@Nullable String s) { return s != null && !s.isEmpty(); } - @Contract("null -> true") + @Contract(value = "null -> true", pure=true) public static boolean isEmpty(@Nullable String s) { return s == null || s.isEmpty(); } - @Contract("null -> true") + @Contract(value = "null -> true",pure = true) public static boolean isEmpty(@Nullable CharSequence cs) { return cs == null || cs.length() == 0; } + @Contract(pure = true) public static int length(@Nullable CharSequence cs) { return cs == null ? 0 : cs.length(); } @NotNull + @Contract(pure = true) public static String notNullize(@Nullable final String s) { return notNullize(s, ""); } @NotNull + @Contract(pure = true) public static String notNullize(@Nullable final String s, @NotNull String defaultValue) { return s == null ? defaultValue : s; } @Nullable + @Contract(pure = true) public static String nullize(@Nullable final String s) { return nullize(s, false); } @Nullable + @Contract(pure = true) public static String nullize(@Nullable final String s, boolean nullizeSpaces) { if (nullizeSpaces) { if (isEmptyOrSpaces(s)) return null; @@ -978,13 +1053,13 @@ public class StringUtil extends StringUtilRt { return s; } - @Contract("null -> true") + @Contract(value = "null -> true",pure = true) // we need to keep this method to preserve backward compatibility public static boolean isEmptyOrSpaces(@Nullable String s) { return isEmptyOrSpaces(((CharSequence)s)); } - @Contract("null -> true") + @Contract(value = "null -> true", pure = true) public static boolean isEmptyOrSpaces(@Nullable CharSequence s) { if (isEmpty(s)) { return true; @@ -1003,26 +1078,31 @@ public class StringUtil extends StringUtilRt { * @param c symbol to check * @return true if given symbol is white space, tabulation or line feed; false otherwise */ + @Contract(pure = true) public static boolean isWhiteSpace(char c) { return c == '\n' || c == '\t' || c == ' '; } @NotNull + @Contract(pure = true) public static String getThrowableText(@NotNull Throwable aThrowable) { return ExceptionUtil.getThrowableText(aThrowable); } @NotNull + @Contract(pure = true) public static String getThrowableText(@NotNull Throwable aThrowable, @NonNls @NotNull final String stackFrameSkipPattern) { return ExceptionUtil.getThrowableText(aThrowable, stackFrameSkipPattern); } @Nullable + @Contract(pure = true) public static String getMessage(@NotNull Throwable e) { return ExceptionUtil.getMessage(e); } @NotNull + @Contract(pure = true) public static String repeatSymbol(final char aChar, final int count) { char[] buffer = new char[count]; Arrays.fill(buffer, aChar); @@ -1030,6 +1110,7 @@ public class StringUtil extends StringUtilRt { } @NotNull + @Contract(pure = true) public static String repeat(@NotNull String s, int count) { assert count >= 0 : count; StringBuilder sb = new StringBuilder(s.length() * count); @@ -1040,6 +1121,7 @@ public class StringUtil extends StringUtilRt { } @NotNull + @Contract(pure = true) public static List splitHonorQuotes(@NotNull String s, char separator) { final List result = new ArrayList(); final StringBuilder builder = new StringBuilder(s.length()); @@ -1068,17 +1150,20 @@ public class StringUtil extends StringUtilRt { @NotNull + @Contract(pure = true) public static List split(@NotNull String s, @NotNull String separator) { return split(s, separator, true); } @NotNull + @Contract(pure = true) public static List split(@NotNull String s, @NotNull String separator, boolean excludeSeparator) { return split(s, separator, excludeSeparator, true); } @NotNull + @Contract(pure = true) public static List split(@NotNull String s, @NotNull String separator, boolean excludeSeparator, boolean excludeEmptyStrings) { if (separator.isEmpty()) { @@ -1103,6 +1188,7 @@ public class StringUtil extends StringUtilRt { } @NotNull + @Contract(pure = true) public static Iterable tokenize(@NotNull String s, @NotNull String separators) { final com.intellij.util.text.StringTokenizer tokenizer = new com.intellij.util.text.StringTokenizer(s, separators); return new Iterable() { @@ -1130,6 +1216,7 @@ public class StringUtil extends StringUtilRt { } @NotNull + @Contract(pure = true) public static Iterable tokenize(@NotNull final StringTokenizer tokenizer) { return new Iterable() { @NotNull @@ -1160,6 +1247,7 @@ public class StringUtil extends StringUtilRt { * The word here means the maximum sub-string consisting entirely of characters which are Character.isJavaIdentifierPart(c). */ @NotNull + @Contract(pure = true) public static List getWordsIn(@NotNull String text) { List result = null; int start = -1; @@ -1190,6 +1278,7 @@ public class StringUtil extends StringUtilRt { } @NotNull + @Contract(pure = true) public static List getWordIndicesIn(@NotNull String text) { List result = new SmartList(); int start = -1; @@ -1211,11 +1300,13 @@ public class StringUtil extends StringUtilRt { } @NotNull + @Contract(pure = true) public static String join(@NotNull final String[] strings, @NotNull final String separator) { return join(strings, 0, strings.length, separator); } @NotNull + @Contract(pure = true) public static String join(@NotNull final String[] strings, int startIndex, int endIndex, @NotNull final String separator) { final StringBuilder result = new StringBuilder(); for (int i = startIndex; i < endIndex; i++) { @@ -1226,6 +1317,7 @@ public class StringUtil extends StringUtilRt { } @NotNull + @Contract(pure = true) public static String[] zip(@NotNull String[] strings1, @NotNull String[] strings2, String separator) { if (strings1.length != strings2.length) throw new IllegalArgumentException(); @@ -1238,6 +1330,7 @@ public class StringUtil extends StringUtilRt { } @NotNull + @Contract(pure = true) public static String[] surround(@NotNull String[] strings1, String prefix, String suffix) { String[] result = ArrayUtil.newStringArray(strings1.length); for (int i = 0; i < result.length; i++) { @@ -1248,11 +1341,13 @@ public class StringUtil extends StringUtilRt { } @NotNull + @Contract(pure = true) public static String join(@NotNull T[] items, @NotNull Function f, @NotNull @NonNls String separator) { return join(Arrays.asList(items), f, separator); } @NotNull + @Contract(pure = true) public static String join(@NotNull Collection items, @NotNull Function f, @NotNull @NonNls String separator) { @@ -1260,6 +1355,7 @@ public class StringUtil extends StringUtilRt { return join((Iterable)items, f, separator); } + @Contract(pure = true) public static String join(@NotNull Iterable items, @NotNull @NonNls String separator) { StringBuilder result = new StringBuilder(); for (Object item : items) { @@ -1272,6 +1368,7 @@ public class StringUtil extends StringUtilRt { } @NotNull + @Contract(pure = true) public static String join(@NotNull Iterable items, @NotNull Function f, @NotNull @NonNls String separator) { @@ -1287,6 +1384,7 @@ public class StringUtil extends StringUtilRt { } @NotNull + @Contract(pure = true) public static String join(@NotNull Collection strings, @NotNull String separator) { StringBuilder result = new StringBuilder(); join(strings, separator, result); @@ -1309,6 +1407,7 @@ public class StringUtil extends StringUtilRt { } @NotNull + @Contract(pure = true) public static String join(@NotNull final int[] strings, @NotNull final String separator) { final StringBuilder result = new StringBuilder(); for (int i = 0; i < strings.length; i++) { @@ -1319,6 +1418,7 @@ public class StringUtil extends StringUtilRt { } @NotNull + @Contract(pure = true) public static String join(@Nullable final String... strings) { if (strings == null || strings.length == 0) return ""; @@ -1330,12 +1430,14 @@ public class StringUtil extends StringUtilRt { } @NotNull + @Contract(pure = true) public static String stripQuotesAroundValue(@NotNull String text) { if (startsWithChar(text, '\"') || startsWithChar(text, '\'')) text = text.substring(1); if (endsWithChar(text, '\"') || endsWithChar(text, '\'')) text = text.substring(0, text.length() - 1); return text; } + @Contract(pure = true) public static boolean isQuotedString(@NotNull String text) { if (text.length() < 2) return false; return startsWithChar(text, '\"') && endsWithChar(text, '\"') @@ -1350,6 +1452,7 @@ public class StringUtil extends StringUtilRt { * @since 5.0.1 */ @NotNull + @Contract(pure = true) public static String formatFileSize(final long fileSize) { if (fileSize < 0x400) { return CommonBundle.message("format.file.size.bytes", fileSize); @@ -1365,6 +1468,7 @@ public class StringUtil extends StringUtilRt { } @NotNull + @Contract(pure = true) public static String formatDuration(long duration) { final long minutes = duration / 60000; final long seconds = ((duration + 500L) % 60000) / 1000; @@ -1375,6 +1479,7 @@ public class StringUtil extends StringUtilRt { } @NotNull + @Contract(pure = true) private static String formatMinor(long number) { if (number > 0L && number <= 9L) { return "0" + number; @@ -1391,6 +1496,7 @@ public class StringUtil extends StringUtilRt { */ @SuppressWarnings({"HardCodedStringLiteral"}) @Nullable + @Contract(pure = true) public static String unpluralize(@NotNull final String name) { if (name.endsWith("sses") || name.endsWith("shes") || name.endsWith("ches") || name.endsWith("xes")) { //? return name.substring(0, name.length() - 2); @@ -1430,6 +1536,7 @@ public class StringUtil extends StringUtilRt { } @Nullable + @Contract(pure = true) private static String stripEnding(@NotNull String name, @NotNull String ending) { if (name.endsWith(ending)) { if (name.equals(ending)) return name; // do not return empty string @@ -1438,6 +1545,7 @@ public class StringUtil extends StringUtilRt { return null; } + @Contract(pure = true) public static boolean containsAlphaCharacters(@NotNull String value) { for (int i = 0; i < value.length(); i++) { if (Character.isLetter(value.charAt(i))) return true; @@ -1445,6 +1553,7 @@ public class StringUtil extends StringUtilRt { return false; } + @Contract(pure = true) public static boolean containsAnyChar(@NotNull final String value, @NotNull final String chars) { if (chars.length() > value.length()) { return containsAnyChar(value, chars, 0, value.length()); @@ -1454,6 +1563,7 @@ public class StringUtil extends StringUtilRt { } } + @Contract(pure = true) public static boolean containsAnyChar(@NotNull final String value, @NotNull final String chars, final int start, final int end) { @@ -1466,6 +1576,7 @@ public class StringUtil extends StringUtilRt { return false; } + @Contract(pure = true) public static boolean containsChar(@NotNull final String value, final char ch) { return value.indexOf(ch) >= 0; } @@ -1473,7 +1584,7 @@ public class StringUtil extends StringUtilRt { /** * @deprecated use #capitalize(String) */ - @Contract("null -> null; !null -> !null") + @Contract(value = "null -> null; !null -> !null", pure = true) public static String firstLetterToUpperCase(@Nullable final String displayString) { if (displayString == null || displayString.isEmpty()) return displayString; char firstChar = displayString.charAt(0); @@ -1494,6 +1605,7 @@ public class StringUtil extends StringUtilRt { * @return stripped string e.g. "mystring" */ @NotNull + @Contract(pure = true) public static String strip(@NotNull final String s, @NotNull final CharFilter filter) { final StringBuilder result = new StringBuilder(s.length()); for (int i = 0; i < s.length(); i++) { @@ -1506,11 +1618,13 @@ public class StringUtil extends StringUtilRt { } @NotNull + @Contract(pure = true) public static List findMatches(@NotNull String s, @NotNull Pattern pattern) { return findMatches(s, pattern, 1); } @NotNull + @Contract(pure = true) public static List findMatches(@NotNull String s, @NotNull Pattern pattern, int groupIndex) { List result = new SmartList(); Matcher m = pattern.matcher(s); @@ -1530,6 +1644,7 @@ public class StringUtil extends StringUtilRt { * @param filter search filter * @return position of the first character accepted or -1 if not found */ + @Contract(pure = true) public static int findFirst(@NotNull final CharSequence s, @NotNull CharFilter filter) { for (int i = 0; i < s.length(); i++) { char ch = s.charAt(i); @@ -1541,18 +1656,22 @@ public class StringUtil extends StringUtilRt { } @NotNull + @Contract(pure = true) public static String replaceSubstring(@NotNull String string, @NotNull TextRange range, @NotNull String replacement) { return range.replace(string, replacement); } + @Contract(pure = true) public static boolean startsWithWhitespace(@NotNull String text) { return !text.isEmpty() && Character.isWhitespace(text.charAt(0)); } + @Contract(pure = true) public static boolean isChar(CharSequence seq, int index, char c) { return index >= 0 && index < seq.length() && seq.charAt(index) == c; } + @Contract(pure = true) public static boolean startsWith(@NotNull CharSequence text, @NotNull CharSequence prefix) { int l1 = text.length(); int l2 = prefix.length(); @@ -1565,6 +1684,7 @@ public class StringUtil extends StringUtilRt { return true; } + @Contract(pure = true) public static boolean startsWith(@NotNull CharSequence text, int startIndex, @NotNull CharSequence prefix) { int l1 = text.length() - startIndex; int l2 = prefix.length(); @@ -1577,6 +1697,7 @@ public class StringUtil extends StringUtilRt { return true; } + @Contract(pure = true) public static boolean endsWith(@NotNull CharSequence text, @NotNull CharSequence suffix) { int l1 = text.length(); int l2 = suffix.length(); @@ -1590,10 +1711,12 @@ public class StringUtil extends StringUtilRt { } @NotNull + @Contract(pure = true) public static String commonPrefix(@NotNull String s1, @NotNull String s2) { return s1.substring(0, commonPrefixLength(s1, s2)); } + @Contract(pure = true) public static int commonPrefixLength(@NotNull CharSequence s1, @NotNull CharSequence s2) { int i; int minLength = Math.min(s1.length(), s2.length()); @@ -1606,10 +1729,12 @@ public class StringUtil extends StringUtilRt { } @NotNull + @Contract(pure = true) public static String commonSuffix(@NotNull String s1, @NotNull String s2) { return s1.substring(s1.length() - commonSuffixLength(s1, s2)); } + @Contract(pure = true) public static int commonSuffixLength(@NotNull CharSequence s1, @NotNull CharSequence s2) { int s1Length = s1.length(); int s2Length = s2.length(); @@ -1633,10 +1758,12 @@ public class StringUtil extends StringUtilRt { * @return true if given symbol is contained at the target range of the given char sequence; * false otherwise */ + @Contract(pure = true) public static boolean contains(@NotNull CharSequence s, int start, int end, char c) { return indexOf(s, c, start, end) >= 0; } + @Contract(pure = true) public static boolean containsWhitespaces(@Nullable CharSequence s) { if (s == null) return false; @@ -1646,14 +1773,17 @@ public class StringUtil extends StringUtilRt { return false; } + @Contract(pure = true) public static int indexOf(@NotNull CharSequence s, char c) { return indexOf(s, c, 0, s.length()); } + @Contract(pure = true) public static int indexOf(@NotNull CharSequence s, char c, int start) { return indexOf(s, c, start, s.length()); } + @Contract(pure = true) public static int indexOf(@NotNull CharSequence s, char c, int start, int end) { for (int i = start; i < end; i++) { if (s.charAt(i) == c) return i; @@ -1661,10 +1791,12 @@ public class StringUtil extends StringUtilRt { return -1; } + @Contract(pure = true) public static boolean contains(@NotNull CharSequence sequence, @NotNull CharSequence infix) { return indexOf(sequence, infix) >= 0; } - + + @Contract(pure = true) public static int indexOf(@NotNull CharSequence sequence, @NotNull CharSequence infix) { for (int i = 0; i < sequence.length() - infix.length(); i++) { if (startsWith(sequence, i, infix)) { @@ -1674,6 +1806,7 @@ public class StringUtil extends StringUtilRt { return -1; } + @Contract(pure = true) public static int indexOf(@NotNull CharSequence s, char c, int start, int end, boolean caseSensitive) { for (int i = start; i < end; i++) { if (charsMatch(s.charAt(i), c, !caseSensitive)) return i; @@ -1681,6 +1814,7 @@ public class StringUtil extends StringUtilRt { return -1; } + @Contract(pure = true) public static int indexOf(@NotNull char[] s, char c, int start, int end, boolean caseSensitive) { for (int i = start; i < end; i++) { if (charsMatch(s[i], c, !caseSensitive)) return i; @@ -1688,24 +1822,29 @@ public class StringUtil extends StringUtilRt { return -1; } + @Contract(pure = true) public static int indexOfSubstringEnd(@NotNull String text, @NotNull String subString) { int i = text.indexOf(subString); if (i == -1) return -1; return i + subString.length(); } + @Contract(pure = true) public static int indexOfAny(@NotNull final String s, @NotNull final String chars) { return indexOfAny(s, chars, 0, s.length()); } + @Contract(pure = true) public static int indexOfAny(@NotNull final CharSequence s, @NotNull final String chars) { return indexOfAny(s, chars, 0, s.length()); } + @Contract(pure = true) public static int indexOfAny(@NotNull final String s, @NotNull final String chars, final int start, final int end) { return indexOfAny((CharSequence)s, chars, start, end); } + @Contract(pure = true) public static int indexOfAny(@NotNull final CharSequence s, @NotNull final String chars, final int start, final int end) { for (int i = start; i < end; i++) { if (containsChar(chars, s.charAt(i))) return i; @@ -1714,6 +1853,7 @@ public class StringUtil extends StringUtilRt { } @Nullable + @Contract(pure = true) public static String substringBefore(@NotNull String text, @NotNull String subString) { int i = text.indexOf(subString); if (i == -1) return null; @@ -1721,6 +1861,7 @@ public class StringUtil extends StringUtilRt { } @Nullable + @Contract(pure = true) public static String substringAfter(@NotNull String text, @NotNull String subString) { int i = text.indexOf(subString); if (i == -1) return null; @@ -1737,26 +1878,31 @@ public class StringUtil extends StringUtilRt { * @return index of the last occurrence of the given symbol at the target sub-sequence of the given text if any; * -1 otherwise */ + @Contract(pure = true) public static int lastIndexOf(@NotNull CharSequence s, char c, int start, int end) { return StringUtilRt.lastIndexOf(s, c, start, end); } @NotNull + @Contract(pure = true) public static String first(@NotNull String text, final int maxLength, final boolean appendEllipsis) { return text.length() > maxLength ? text.substring(0, maxLength) + (appendEllipsis ? "..." : "") : text; } @NotNull + @Contract(pure = true) public static CharSequence first(@NotNull CharSequence text, final int length, final boolean appendEllipsis) { return text.length() > length ? text.subSequence(0, length) + (appendEllipsis ? "..." : "") : text; } @NotNull + @Contract(pure = true) public static CharSequence last(@NotNull CharSequence text, final int length, boolean prependEllipsis) { return text.length() > length ? (prependEllipsis ? "..." : "") + text.subSequence(text.length() - length, text.length()) : text; } @NotNull + @Contract(pure = true) public static String escapeChar(@NotNull final String str, final char character) { final StringBuilder buf = new StringBuilder(str); escapeChar(buf, character); @@ -1772,6 +1918,7 @@ public class StringUtil extends StringUtilRt { } @NotNull + @Contract(pure = true) public static String escapeQuotes(@NotNull final String str) { return escapeChar(str, '"'); } @@ -1781,11 +1928,13 @@ public class StringUtil extends StringUtilRt { } @NotNull + @Contract(pure = true) public static String escapeSlashes(@NotNull final String str) { return escapeChar(str, '/'); } @NotNull + @Contract(pure = true) public static String escapeBackSlashes(@NotNull final String str) { return escapeChar(str, '\\'); } @@ -1795,6 +1944,7 @@ public class StringUtil extends StringUtilRt { } @NotNull + @Contract(pure = true) public static String unescapeSlashes(@NotNull final String str) { final StringBuilder buf = new StringBuilder(str.length()); unescapeSlashes(buf, str); @@ -1826,6 +1976,7 @@ public class StringUtil extends StringUtilRt { } @NotNull + @Contract(pure = true) public static String wrapWithDoubleQuote(@NotNull String str) { return '\"' + str + "\""; } @@ -1833,25 +1984,27 @@ public class StringUtil extends StringUtilRt { @NonNls private static final String[] REPLACES_REFS = {"<", ">", "&", "'", """}; @NonNls private static final String[] REPLACES_DISP = {"<", ">", "&", "'", "\""}; - @Contract("null -> null; !null -> !null") + @Contract(value = "null -> null; !null -> !null",pure = true) public static String unescapeXml(@Nullable final String text) { if (text == null) return null; return replace(text, REPLACES_REFS, REPLACES_DISP); } - @Contract("null -> null; !null -> !null") + @Contract(value = "null -> null; !null -> !null",pure = true) public static String escapeXml(@Nullable final String text) { if (text == null) return null; return replace(text, REPLACES_DISP, REPLACES_REFS); } @NotNull - public static String htmlEmphasize(String text) { + @Contract(pure = true) + public static String htmlEmphasize(@NotNull String text) { return "" + escapeXml(text) + ""; } @NotNull + @Contract(pure = true) public static String escapeToRegexp(@NotNull String text) { final StringBuilder result = new StringBuilder(text.length()); return escapeToRegexp(text, result).toString(); @@ -1875,6 +2028,7 @@ public class StringUtil extends StringUtilRt { return builder; } + @Contract(pure = true) public static boolean isNotEscapedBackslash(@NotNull char[] chars, int startOffset, int backslashOffset) { if (chars[backslashOffset] != '\\') { return false; @@ -1891,6 +2045,7 @@ public class StringUtil extends StringUtilRt { return !escaped; } + @Contract(pure = true) public static boolean isNotEscapedBackslash(@NotNull CharSequence text, int startOffset, int backslashOffset) { if (text.charAt(backslashOffset) != '\\') { return false; @@ -1908,6 +2063,7 @@ public class StringUtil extends StringUtilRt { } @NotNull + @Contract(pure = true) public static String replace(@NotNull String text, @NotNull String[] from, @NotNull String[] to) { final StringBuilder result = new StringBuilder(text.length()); replace: @@ -1929,6 +2085,7 @@ public class StringUtil extends StringUtilRt { } @NotNull + @Contract(pure = true) public static String[] filterEmptyStrings(@NotNull String[] strings) { int emptyCount = 0; for (String string : strings) { @@ -1946,14 +2103,17 @@ public class StringUtil extends StringUtilRt { return result; } + @Contract(pure = true) public static int countNewLines(@NotNull CharSequence text) { return countChars(text, '\n'); } + @Contract(pure = true) public static int countChars(@NotNull CharSequence text, char c) { return countChars(text, c, 0, false); } + @Contract(pure = true) public static int countChars(@NotNull CharSequence text, char c, int offset, boolean continuous) { int count = 0; for (int i = offset; i < text.length(); ++i) { @@ -1968,6 +2128,7 @@ public class StringUtil extends StringUtilRt { } @NotNull + @Contract(pure = true) public static String capitalsOnly(@NotNull String s) { StringBuilder b = new StringBuilder(); for (int i = 0; i < s.length(); i++) { @@ -1984,6 +2145,7 @@ public class StringUtil extends StringUtilRt { * @return {@code null} if any of given Strings is {@code null}. */ @Nullable + @Contract(pure = true) public static String joinOrNull(@NotNull String... args) { StringBuilder r = new StringBuilder(); for (String arg : args) { @@ -1994,6 +2156,7 @@ public class StringUtil extends StringUtilRt { } @Nullable + @Contract(pure = true) public static String getPropertyName(@NonNls @NotNull String methodName) { if (methodName.startsWith("get")) { return Introspector.decapitalize(methodName.substring(3)); @@ -2009,14 +2172,17 @@ public class StringUtil extends StringUtilRt { } } + @Contract(pure = true) public static boolean isJavaIdentifierStart(char c) { return c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || Character.isJavaIdentifierStart(c); } + @Contract(pure = true) public static boolean isJavaIdentifierPart(char c) { return c >= '0' && c <= '9' || isJavaIdentifierStart(c); } + @Contract(pure = true) public static boolean isJavaIdentifier(@NotNull String text) { int len = text.length(); if (len == 0) return false; @@ -2038,6 +2204,7 @@ public class StringUtil extends StringUtilRt { * @return an escaped string */ @NotNull + @Contract(pure = true) public static String escapeProperty(@NotNull String input, final boolean isKey) { final StringBuilder escaped = new StringBuilder(input.length()); for (int i = 0; i < input.length(); i++) { @@ -2087,6 +2254,7 @@ public class StringUtil extends StringUtilRt { return escaped.toString(); } + @Contract(pure = true) public static String getQualifiedName(@Nullable String packageName, String className) { if (packageName == null || packageName.isEmpty()) { return className; @@ -2094,6 +2262,7 @@ public class StringUtil extends StringUtilRt { return packageName + '.' + className; } + @Contract(pure = true) public static int compareVersionNumbers(@Nullable String v1, @Nullable String v2) { // todo duplicates com.intellij.util.text.VersionComparatorUtil.compare // todo please refactor next time you make changes here @@ -2147,6 +2316,7 @@ public class StringUtil extends StringUtilRt { } } + @Contract(pure = true) public static int getOccurrenceCount(@NotNull String text, final char c) { int res = 0; int i = 0; @@ -2163,6 +2333,7 @@ public class StringUtil extends StringUtilRt { return res; } + @Contract(pure = true) public static int getOccurrenceCount(@NotNull String text, @NotNull String s) { int res = 0; int i = 0; @@ -2180,6 +2351,7 @@ public class StringUtil extends StringUtilRt { } @NotNull + @Contract(pure = true) public static String fixVariableNameDerivedFromPropertyName(@NotNull String name) { if (isEmptyOrSpaces(name)) return name; char c = name.charAt(0); @@ -2190,6 +2362,7 @@ public class StringUtil extends StringUtilRt { } @NotNull + @Contract(pure = true) public static String sanitizeJavaIdentifier(@NotNull String name) { final StringBuilder result = new StringBuilder(name.length()); @@ -2236,6 +2409,7 @@ public class StringUtil extends StringUtilRt { } @NotNull + @Contract(pure = true) public static String tail(@NotNull String s, final int idx) { return idx >= s.length() ? "" : s.substring(idx, s.length()); } @@ -2247,6 +2421,7 @@ public class StringUtil extends StringUtilRt { * @return array of strings */ @NotNull + @Contract(pure = true) public static String[] splitByLines(@NotNull String string) { return splitByLines(string, true); } @@ -2259,11 +2434,13 @@ public class StringUtil extends StringUtilRt { * @return array of strings */ @NotNull + @Contract(pure = true) public static String[] splitByLines(@NotNull String string, boolean excludeEmptyStrings) { return (excludeEmptyStrings ? EOL_SPLIT_PATTERN : EOL_SPLIT_PATTERN_WITH_EMPTY).split(string); } @NotNull + @Contract(pure = true) public static String[] splitByLinesDontTrim(@NotNull String string) { return EOL_SPLIT_DONT_TRIM_PATTERN.split(string); } @@ -2280,14 +2457,16 @@ public class StringUtil extends StringUtilRt { * \r
* * will return the following array: foo\r\n, \n, bar\n, \r\n, baz\r, \r - * + * */ @NotNull + @Contract(pure = true) public static String[] splitByLinesKeepSeparators(@NotNull String string) { return EOL_SPLIT_KEEP_SEPARATORS.split(string); } @NotNull + @Contract(pure = true) public static List> getWordsWithOffset(@NotNull String s) { List> res = ContainerUtil.newArrayList(); s += " "; @@ -2315,10 +2494,12 @@ public class StringUtil extends StringUtilRt { * Implementation of "Sorting for Humans: Natural Sort Order": * http://www.codinghorror.com/blog/2007/12/sorting-for-humans-natural-sort-order.html */ + @Contract(pure = true) public static int naturalCompare(@Nullable String string1, @Nullable String string2) { return naturalCompare(string1, string2, false); } + @Contract(pure = true) private static int naturalCompare(@Nullable String string1, @Nullable String string2, boolean caseSensitive) { //noinspection StringEquality if (string1 == string2) { @@ -2333,7 +2514,8 @@ public class StringUtil extends StringUtilRt { final int string1Length = string1.length(); final int string2Length = string2.length(); - int i = 0, j = 0; + int i = 0; + int j = 0; for (; i < string1Length && j < string2Length; i++, j++) { char ch1 = string1.charAt(i); char ch2 = string2.charAt(j); @@ -2404,10 +2586,12 @@ public class StringUtil extends StringUtilRt { return string1Length - string2Length; } + @Contract(pure = true) public static boolean isDecimalDigit(char c) { return c >= '0' && c <= '9'; } + @Contract(pure = true) public static int compare(@Nullable String s1, @Nullable String s2, boolean ignoreCase) { //noinspection StringEquality if (s1 == s2) return 0; @@ -2416,15 +2600,18 @@ public class StringUtil extends StringUtilRt { return ignoreCase ? s1.compareToIgnoreCase(s2) : s1.compareTo(s2); } + @Contract(pure = true) public static int comparePairs(@Nullable String s1, @Nullable String t1, @Nullable String s2, @Nullable String t2, boolean ignoreCase) { final int compare = compare(s1, s2, ignoreCase); return compare != 0 ? compare : compare(t1, t2, ignoreCase); } + @Contract(pure = true) public static int hashCode(@NotNull CharSequence s) { return stringHashCode(s); } + @Contract(pure = true) public static boolean equals(@Nullable CharSequence s1, @Nullable CharSequence s2) { if (s1 == null ^ s2 == null) { return false; @@ -2445,6 +2632,7 @@ public class StringUtil extends StringUtilRt { return true; } + @Contract(pure = true) public static boolean equalsIgnoreCase(@Nullable CharSequence s1, @Nullable CharSequence s2) { if (s1 == null ^ s2 == null) { return false; @@ -2465,6 +2653,7 @@ public class StringUtil extends StringUtilRt { return true; } + @Contract(pure = true) public static int compare(char c1, char c2, boolean ignoreCase) { // duplicating String.equalsIgnoreCase logic int d = c1 - c2; @@ -2488,11 +2677,13 @@ public class StringUtil extends StringUtilRt { return d; } + @Contract(pure = true) public static boolean charsMatch(char c1, char c2, boolean ignoreCase) { return compare(c1, c2, ignoreCase) == 0; } @NotNull + @Contract(pure = true) public static String formatLinks(@NotNull String message) { Pattern linkPattern = Pattern.compile("http://[a-zA-Z0-9\\./\\-\\+]+"); StringBuffer result = new StringBuffer(); @@ -2504,25 +2695,30 @@ public class StringUtil extends StringUtilRt { return result.toString(); } + @Contract(pure = true) public static boolean isHexDigit(char c) { return '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F'; } + @Contract(pure = true) public static boolean isOctalDigit(char c) { return '0' <= c && c <= '7'; } @NotNull + @Contract(pure = true) public static String shortenTextWithEllipsis(@NotNull final String text, final int maxLength, final int suffixLength) { return shortenTextWithEllipsis(text, maxLength, suffixLength, false); } @NotNull + @Contract(pure = true) public static String trimMiddle(@NotNull String text, int maxLength) { return shortenTextWithEllipsis(text, maxLength, maxLength >> 1, true); } @NotNull + @Contract(pure = true) public static String shortenTextWithEllipsis(@NotNull final String text, final int maxLength, final int suffixLength, @@ -2539,6 +2735,7 @@ public class StringUtil extends StringUtilRt { } @NotNull + @Contract(pure = true) public static String shortenTextWithEllipsis(@NotNull final String text, final int maxLength, final int suffixLength, @@ -2548,47 +2745,57 @@ public class StringUtil extends StringUtilRt { } @NotNull + @Contract(pure = true) public static String shortenPathWithEllipsis(@NotNull final String path, final int maxLength, boolean useEllipsisSymbol) { return shortenTextWithEllipsis(path, maxLength, (int)(maxLength * 0.7), useEllipsisSymbol); } @NotNull + @Contract(pure = true) public static String shortenPathWithEllipsis(@NotNull final String path, final int maxLength) { return shortenPathWithEllipsis(path, maxLength, false); } + @Contract(pure = true) public static boolean charsEqual(char a, char b, boolean ignoreCase) { return ignoreCase ? charsEqualIgnoreCase(a, b) : a == b; } + @Contract(pure = true) public static boolean charsEqualIgnoreCase(char a, char b) { return StringUtilRt.charsEqualIgnoreCase(a, b); } + @Contract(pure = true) public static char toUpperCase(char a) { return StringUtilRt.toUpperCase(a); } @NotNull + @Contract(pure = true) public static String toUpperCase(@NotNull String a) { return StringUtilRt.toUpperCase(a); } + @Contract(pure = true) public static char toLowerCase(final char a) { return StringUtilRt.toLowerCase(a); } @NotNull + @Contract(pure = true) public static String convertLineSeparators(@NotNull String text) { return StringUtilRt.convertLineSeparators(text); } @NotNull + @Contract(pure = true) public static String convertLineSeparators(@NotNull String text, boolean keepCarriageReturn) { return StringUtilRt.convertLineSeparators(text, keepCarriageReturn); } @NotNull + @Contract(pure = true) public static String convertLineSeparators(@NotNull String text, @NotNull String newSeparator) { return StringUtilRt.convertLineSeparators(text, newSeparator); } @@ -2606,34 +2813,41 @@ public class StringUtil extends StringUtilRt { return StringUtilRt.convertLineSeparators(text, newSeparator, offsetsToKeep, keepCarriageReturn); } + @Contract(pure = true) public static int parseInt(final String string, final int defaultValue) { return StringUtilRt.parseInt(string, defaultValue); } + @Contract(pure = true) public static double parseDouble(final String string, final double defaultValue) { return StringUtilRt.parseDouble(string, defaultValue); } + @Contract(pure = true) public static boolean parseBoolean(String string, final boolean defaultValue) { return StringUtilRt.parseBoolean(string, defaultValue); } @NotNull + @Contract(pure = true) public static String getShortName(@NotNull Class aClass) { return StringUtilRt.getShortName(aClass); } @NotNull + @Contract(pure = true) public static String getShortName(@NotNull String fqName) { return StringUtilRt.getShortName(fqName); } @NotNull + @Contract(pure = true) public static String getShortName(@NotNull String fqName, char separator) { return StringUtilRt.getShortName(fqName, separator); } @NotNull + @Contract(pure = true) public static CharSequence newBombedCharSequence(@NotNull CharSequence sequence, long delay) { final long myTime = System.currentTimeMillis() + delay; return new BombedCharSequence(sequence) { @@ -2655,23 +2869,17 @@ public class StringUtil extends StringUtilRt { return false; } - private static boolean trimStart(@NotNull StringBuilder buffer, @NotNull CharSequence start) { - if (startsWith(buffer, start)) { - buffer.delete(0, start.length()); - return true; - } - return false; - } - /** * Say smallPart = "op" and bigPart="open". Method returns true for "Ope" and false for "ops" */ + @Contract(pure = true) public static boolean isBetween(@NotNull String string, @NotNull String smallPart, @NotNull String bigPart) { final String s = string.toLowerCase(); return s.startsWith(smallPart.toLowerCase()) && bigPart.toLowerCase().startsWith(s); } - public static String getShortened(String s, int maxWidth) { + @Contract(pure = true) + public static String getShortened(@NotNull String s, int maxWidth) { int length = s.length(); if (isEmpty(s) || length <= maxWidth) return s; ArrayList words = new ArrayList(); diff --git a/platform/util/src/com/intellij/util/ArrayUtil.java b/platform/util/src/com/intellij/util/ArrayUtil.java index d0419de9e820..0dcfdb307ae0 100644 --- a/platform/util/src/com/intellij/util/ArrayUtil.java +++ b/platform/util/src/com/intellij/util/ArrayUtil.java @@ -18,6 +18,7 @@ package com.intellij.util; import com.intellij.openapi.util.Comparing; import com.intellij.util.text.CharArrayCharSequence; import gnu.trove.Equality; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -64,6 +65,7 @@ public class ArrayUtil extends ArrayUtilRt { private ArrayUtil() { } @NotNull + @Contract(pure=true) public static byte[] realloc(@NotNull byte[] array, final int newSize) { if (newSize == 0) { return EMPTY_BYTE_ARRAY; @@ -79,6 +81,7 @@ public class ArrayUtil extends ArrayUtilRt { return result; } @NotNull + @Contract(pure=true) public static boolean[] realloc(@NotNull boolean[] array, final int newSize) { if (newSize == 0) { return EMPTY_BOOLEAN_ARRAY; @@ -95,6 +98,7 @@ public class ArrayUtil extends ArrayUtilRt { } @NotNull + @Contract(pure=true) public static int[] realloc(@NotNull int[] array, final int newSize) { if (newSize == 0) { return EMPTY_INT_ARRAY; @@ -110,6 +114,7 @@ public class ArrayUtil extends ArrayUtilRt { return result; } @NotNull + @Contract(pure=true) public static T[] realloc(@NotNull T[] array, final int newSize, @NotNull ArrayFactory factory) { final int oldSize = array.length; if (oldSize == newSize) { @@ -126,12 +131,14 @@ public class ArrayUtil extends ArrayUtilRt { } @NotNull + @Contract(pure=true) public static int[] append(@NotNull int[] array, int value) { array = realloc(array, array.length + 1); array[array.length - 1] = value; return array; } @NotNull + @Contract(pure=true) public static int[] insert(@NotNull int[] array, int index, int value) { int[] result = new int[array.length + 1]; System.arraycopy(array, 0, result, 0, index); @@ -141,12 +148,14 @@ public class ArrayUtil extends ArrayUtilRt { } @NotNull + @Contract(pure=true) public static byte[] append(@NotNull byte[] array, byte value) { array = realloc(array, array.length + 1); array[array.length - 1] = value; return array; } @NotNull + @Contract(pure=true) public static boolean[] append(@NotNull boolean[] array, boolean value) { array = realloc(array, array.length + 1); array[array.length - 1] = value; @@ -154,6 +163,7 @@ public class ArrayUtil extends ArrayUtilRt { } @NotNull + @Contract(pure=true) public static char[] realloc(@NotNull char[] array, final int newSize) { if (newSize == 0) { return EMPTY_CHAR_ARRAY; @@ -170,12 +180,14 @@ public class ArrayUtil extends ArrayUtilRt { } @NotNull + @Contract(pure=true) public static T[] toObjectArray(@NotNull Collection collection, @NotNull Class aClass) { @SuppressWarnings("unchecked") T[] array = (T[])Array.newInstance(aClass, collection.size()); return collection.toArray(array); } @NotNull + @Contract(pure=true) public static T[] toObjectArray(@NotNull Class aClass, @NotNull Object... source) { @SuppressWarnings("unchecked") T[] array = (T[])Array.newInstance(aClass, source.length); System.arraycopy(source, 0, array, 0, array.length); @@ -183,6 +195,7 @@ public class ArrayUtil extends ArrayUtilRt { } @NotNull + @Contract(pure=true) public static Object[] toObjectArray(@NotNull Collection collection) { if (collection.isEmpty()) return EMPTY_OBJECT_ARRAY; //noinspection SSBasedInspection @@ -190,6 +203,7 @@ public class ArrayUtil extends ArrayUtilRt { } @NotNull + @Contract(pure=true) public static int[] toIntArray(@NotNull Collection list) { int[] ret = newIntArray(list.size()); int i = 0; @@ -200,6 +214,7 @@ public class ArrayUtil extends ArrayUtilRt { } @NotNull + @Contract(pure=true) public static T[] mergeArrays(@NotNull T[] a1, @NotNull T[] a2) { if (a1.length == 0) { return a2; @@ -219,6 +234,7 @@ public class ArrayUtil extends ArrayUtilRt { } @NotNull + @Contract(pure=true) public static T[] mergeCollections(@NotNull Collection c1, @NotNull Collection c2, @NotNull ArrayFactory factory) { T[] res = factory.create(c1.size() + c2.size()); @@ -236,6 +252,7 @@ public class ArrayUtil extends ArrayUtilRt { } @NotNull + @Contract(pure=true) public static T[] mergeArrays(@NotNull T[] a1, @NotNull T[] a2, @NotNull ArrayFactory factory) { if (a1.length == 0) { return a2; @@ -250,11 +267,13 @@ public class ArrayUtil extends ArrayUtilRt { } @NotNull + @Contract(pure=true) public static String[] mergeArrays(@NotNull String[] a1, @NotNull String... a2) { return mergeArrays(a1, a2, STRING_ARRAY_FACTORY); } @NotNull + @Contract(pure=true) public static int[] mergeArrays(@NotNull int[] a1, @NotNull int[] a2) { if (a1.length == 0) { return a2; @@ -269,6 +288,7 @@ public class ArrayUtil extends ArrayUtilRt { } @NotNull + @Contract(pure=true) public static byte[] mergeArrays(@NotNull byte[] a1, @NotNull byte[] a2) { if (a1.length == 0) { return a2; @@ -292,6 +312,7 @@ public class ArrayUtil extends ArrayUtilRt { * @return destination array */ @NotNull + @Contract(pure=true) public static T[] mergeArrayAndCollection(@NotNull T[] array, @NotNull Collection collection, @NotNull final ArrayFactory factory) { @@ -326,16 +347,19 @@ public class ArrayUtil extends ArrayUtilRt { * @return new array */ @NotNull + @Contract(pure=true) public static T[] append(@NotNull final T[] src, @Nullable final T element) { return append(src, element, (Class)src.getClass().getComponentType()); } @NotNull + @Contract(pure=true) public static T[] prepend(final T element, @NotNull final T[] array) { return prepend(element, array, (Class)array.getClass().getComponentType()); } @NotNull + @Contract(pure=true) public static T[] prepend(T element, @NotNull T[] array, @NotNull Class type) { int length = array.length; T[] result = (T[])Array.newInstance(type, length + 1); @@ -345,6 +369,7 @@ public class ArrayUtil extends ArrayUtilRt { } @NotNull + @Contract(pure=true) public static byte[] prepend(byte element, @NotNull byte[] array) { int length = array.length; final byte[] result = new byte[length + 1]; @@ -353,8 +378,8 @@ public class ArrayUtil extends ArrayUtilRt { return result; } + @Contract(pure=true) public static T[] append(@NotNull final T[] src, final T element, @NotNull ArrayFactory factory) { - int length = src.length; T[] result = factory.create(length + 1); System.arraycopy(src, 0, result, 0, length); @@ -363,6 +388,7 @@ public class ArrayUtil extends ArrayUtilRt { } @NotNull + @Contract(pure=true) public static T[] append(@NotNull T[] src, @Nullable final T element, @NotNull Class componentType) { int length = src.length; T[] result = (T[])Array.newInstance(componentType, length + 1); @@ -379,6 +405,7 @@ public class ArrayUtil extends ArrayUtilRt { * @return modified array. */ @NotNull + @Contract(pure=true) public static T[] remove(@NotNull final T[] src, int idx) { int length = src.length; if (idx < 0 || idx >= length) { @@ -391,6 +418,7 @@ public class ArrayUtil extends ArrayUtilRt { } @NotNull + @Contract(pure=true) public static T[] remove(@NotNull final T[] src, int idx, @NotNull ArrayFactory factory) { int length = src.length; if (idx < 0 || idx >= length) { @@ -403,6 +431,7 @@ public class ArrayUtil extends ArrayUtilRt { } @NotNull + @Contract(pure=true) public static T[] remove(@NotNull final T[] src, T element) { final int idx = find(src, element); if (idx == -1) return src; @@ -411,6 +440,7 @@ public class ArrayUtil extends ArrayUtilRt { } @NotNull + @Contract(pure=true) public static T[] remove(@NotNull final T[] src, T element, @NotNull ArrayFactory factory) { final int idx = find(src, element); if (idx == -1) return src; @@ -419,6 +449,7 @@ public class ArrayUtil extends ArrayUtilRt { } @NotNull + @Contract(pure=true) public static int[] remove(@NotNull final int[] src, int idx) { int length = src.length; if (idx < 0 || idx >= length) { @@ -430,6 +461,7 @@ public class ArrayUtil extends ArrayUtilRt { return result; } @NotNull + @Contract(pure=true) public static short[] remove(@NotNull final short[] src, int idx) { int length = src.length; if (idx < 0 || idx >= length) { @@ -441,6 +473,7 @@ public class ArrayUtil extends ArrayUtilRt { return result; } + @Contract(pure=true) public static int lastIndexOf(@NotNull final T[] src, final T obj) { for (int i = src.length - 1; i >= 0; i--) { final T o = src[i]; @@ -458,14 +491,17 @@ public class ArrayUtil extends ArrayUtilRt { return -1; } + @Contract(pure=true) public static int find(@NotNull int[] src, int obj) { return indexOf(src, obj); } + @Contract(pure=true) public static int find(@NotNull final T[] src, final T obj) { return ArrayUtilRt.find(src, obj); } + @Contract(pure=true) public static boolean startsWith(@NotNull byte[] array, @NotNull byte[] prefix) { if (array == prefix) { return true; @@ -484,6 +520,7 @@ public class ArrayUtil extends ArrayUtilRt { return true; } + @Contract(pure=true) public static boolean startsWith(@NotNull E[] array, @NotNull E[] subArray) { if (array == subArray) { return true; @@ -502,6 +539,7 @@ public class ArrayUtil extends ArrayUtilRt { return true; } + @Contract(pure=true) public static boolean startsWith(@NotNull byte[] array, int start, @NotNull byte[] subArray) { int length = subArray.length; if (array.length - start < length) { @@ -517,6 +555,7 @@ public class ArrayUtil extends ArrayUtilRt { return true; } + @Contract(pure=true) public static boolean equals(@NotNull T[] a1, @NotNull T[] a2, @NotNull Equality comparator) { if (a1 == a2) { return true; @@ -535,6 +574,7 @@ public class ArrayUtil extends ArrayUtilRt { return true; } + @Contract(pure=true) public static boolean equals(@NotNull T[] a1, @NotNull T[] a2, @NotNull Comparator comparator) { if (a1 == a2) { return true; @@ -553,6 +593,7 @@ public class ArrayUtil extends ArrayUtilRt { } @NotNull + @Contract(pure=true) public static T[] reverseArray(@NotNull T[] array) { T[] newArray = array.clone(); for (int i = 0; i < array.length; i++) { @@ -562,6 +603,7 @@ public class ArrayUtil extends ArrayUtilRt { } @NotNull + @Contract(pure=true) public static int[] reverseArray(@NotNull int[] array) { int[] newArray = array.clone(); for (int i = 0; i < array.length; i++) { @@ -576,6 +618,7 @@ public class ArrayUtil extends ArrayUtilRt { } } + @Contract(pure=true) public static int lexicographicCompare(@NotNull String[] obj1, @NotNull String[] obj2) { for (int i = 0; i < Math.max(obj1.length, obj2.length); i++) { String o1 = i < obj1.length ? obj1[i] : null; @@ -589,6 +632,7 @@ public class ArrayUtil extends ArrayUtilRt { } //must be Comparables + @Contract(pure=true) public static int lexicographicCompare(@NotNull T[] obj1, @NotNull T[] obj2) { for (int i = 0; i < Math.max(obj1.length, obj2.length); i++) { T o1 = i < obj1.length ? obj1[i] : null; @@ -637,10 +681,12 @@ public class ArrayUtil extends ArrayUtilRt { array[i1] = t; } + @Contract(pure=true) public static int indexOf(@NotNull Object[] objects, @Nullable Object object) { return indexOf(objects, object, 0, objects.length); } + @Contract(pure=true) public static int indexOf(@NotNull Object[] objects, Object object, int start, int end) { if (object == null) { for (int i = start; i < end; i++) { @@ -655,6 +701,7 @@ public class ArrayUtil extends ArrayUtilRt { return -1; } + @Contract(pure=true) public static int indexOf(@NotNull List objects, T object, @NotNull Equality comparator) { for (int i = 0; i < objects.size(); i++) { if (comparator.equals(objects.get(i), object)) return i; @@ -662,6 +709,7 @@ public class ArrayUtil extends ArrayUtilRt { return -1; } + @Contract(pure=true) public static int indexOf(@NotNull List objects, T object, @NotNull Comparator comparator) { for (int i = 0; i < objects.size(); i++) { if (comparator.compare(objects.get(i), object) == 0) return i; @@ -669,6 +717,7 @@ public class ArrayUtil extends ArrayUtilRt { return -1; } + @Contract(pure=true) public static int indexOf(@NotNull T[] objects, T object, @NotNull Equality comparator) { for (int i = 0; i < objects.length; i++) { if (comparator.equals(objects[i], object)) return i; @@ -676,6 +725,7 @@ public class ArrayUtil extends ArrayUtilRt { return -1; } + @Contract(pure=true) public static int indexOf(@NotNull int[] ints, int value) { for (int i = 0; i < ints.length; i++) { if (ints[i] == value) return i; @@ -683,6 +733,7 @@ public class ArrayUtil extends ArrayUtilRt { return -1; } + @Contract(pure=true) public static int indexOf(@NotNull short[] ints, short value) { for (int i = 0; i < ints.length; i++) { if (ints[i] == value) return i; @@ -691,10 +742,12 @@ public class ArrayUtil extends ArrayUtilRt { return -1; } + @Contract(pure=true) public static boolean contains(@Nullable final Object o, @NotNull Object... objects) { return indexOf(objects, o) >= 0; } + @Contract(pure=true) public static boolean contains(@Nullable final String s, @NotNull String... strings) { if (s == null) { for (String str : strings) { @@ -711,26 +764,31 @@ public class ArrayUtil extends ArrayUtilRt { } @NotNull + @Contract(pure=true) public static int[] newIntArray(int count) { return count == 0 ? EMPTY_INT_ARRAY : new int[count]; } @NotNull + @Contract(pure=true) public static long[] newLongArray(int count) { return count == 0 ? EMPTY_LONG_ARRAY : new long[count]; } @NotNull + @Contract(pure=true) public static String[] newStringArray(int count) { return count == 0 ? EMPTY_STRING_ARRAY : new String[count]; } @NotNull + @Contract(pure=true) public static Object[] newObjectArray(int count) { return count == 0 ? EMPTY_OBJECT_ARRAY : new Object[count]; } @NotNull + @Contract(pure=true) public static E[] ensureExactSize(int count, @NotNull E[] sample) { if (count == sample.length) return sample; @SuppressWarnings({"unchecked"}) final E[] array = (E[])Array.newInstance(sample.getClass().getComponentType(), count); @@ -738,16 +796,19 @@ public class ArrayUtil extends ArrayUtilRt { } @Nullable + @Contract(pure=true) public static T getFirstElement(@NotNull T[] array) { return array.length > 0 ? array[0] : null; } @Nullable + @Contract(pure=true) public static T getLastElement(@NotNull T[] array) { return array.length > 0 ? array[array.length - 1] : null; } @NotNull + @Contract(pure=true) public static String[] toStringArray(@Nullable Collection collection) { return ArrayUtilRt.toStringArray(collection); } diff --git a/platform/util/src/com/intellij/util/ReflectionUtil.java b/platform/util/src/com/intellij/util/ReflectionUtil.java index d5f6a58dc951..8e2204415988 100644 --- a/platform/util/src/com/intellij/util/ReflectionUtil.java +++ b/platform/util/src/com/intellij/util/ReflectionUtil.java @@ -19,17 +19,15 @@ package com.intellij.util; import com.intellij.Patches; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Condition; -import com.intellij.util.containers.ContainerUtil; +import com.intellij.openapi.util.DifferenceFilter; +import com.intellij.util.containers.*; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import sun.reflect.ConstructorAccessor; import java.lang.reflect.*; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; +import java.util.*; public class ReflectionUtil { private static final Logger LOG = Logger.getInstance("#com.intellij.util.ReflectionUtil"); @@ -479,6 +477,50 @@ public class ReflectionUtil { return callerClass; } + public static void copyFields(@NotNull Field[] fields, @NotNull Object from, @NotNull Object to) { + copyFields(fields, from, to, null); + } + + public static boolean copyFields(@NotNull Field[] fields, @NotNull Object from, @NotNull Object to, @Nullable DifferenceFilter diffFilter) { + Set sourceFields = new com.intellij.util.containers.HashSet(Arrays.asList(from.getClass().getFields())); + boolean valuesChanged = false; + for (Field field : fields) { + if (sourceFields.contains(field)) { + if (isPublic(field) && !isFinal(field)) { + try { + if (diffFilter == null || diffFilter.isAccept(field)) { + copyFieldValue(from, to, field); + valuesChanged = true; + } + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + } + } + return valuesChanged; + } + + public static void copyFieldValue(@NotNull Object from, @NotNull Object to, @NotNull Field field) + throws IllegalAccessException { + Class fieldType = field.getType(); + if (fieldType.isPrimitive() || fieldType.equals(String.class)) { + field.set(to, field.get(from)); + } + else { + throw new RuntimeException("Field '" + field.getName()+"' not copied: unsupported type: "+field.getType()); + } + } + + private static boolean isPublic(final Field field) { + return (field.getModifiers() & Modifier.PUBLIC) != 0; + } + + private static boolean isFinal(final Field field) { + return (field.getModifiers() & Modifier.FINAL) != 0; + } + private static class MySecurityManager extends SecurityManager { private static final MySecurityManager INSTANCE = new MySecurityManager(); diff --git a/platform/util/src/com/intellij/util/Restarter.java b/platform/util/src/com/intellij/util/Restarter.java index 01e7fc494f5b..1eb1691808be 100644 --- a/platform/util/src/com/intellij/util/Restarter.java +++ b/platform/util/src/com/intellij/util/Restarter.java @@ -153,11 +153,16 @@ public class Restarter { } public static File createTempExecutable(File executable) throws IOException { - File copy = new File(System.getProperty("user.home") + "/." + System.getProperty("idea.paths.selector") + "/restart/" + executable.getName()); - if (FileUtilRt.ensureCanCreateFile(copy)) { - FileUtilRt.copy(executable, copy); - if (!copy.setExecutable(executable.canExecute())) throw new IOException("Cannot make file executable: " + copy); + File executableDir = new File(System.getProperty("user.home") + "/." + System.getProperty("idea.paths.selector") + "/restart"); + File copy = new File(executableDir.getPath() + "/" + executable.getName()); + if (!FileUtilRt.ensureCanCreateFile(copy)) { + String ext = FileUtilRt.getExtension(executable.getName()); + copy = FileUtilRt.createTempFile(executableDir, FileUtilRt.getNameWithoutExtension(copy.getName()), + StringUtil.isEmptyOrSpaces(ext) ? ".tmp" : ("." + ext), + true, false); } + FileUtilRt.copy(executable, copy); + if (!copy.setExecutable(executable.canExecute())) throw new IOException("Cannot make file executable: " + copy); return copy; } diff --git a/platform/util/src/com/intellij/util/containers/ContainerUtil.java b/platform/util/src/com/intellij/util/containers/ContainerUtil.java index 40bd75a2eff8..0047217285a5 100644 --- a/platform/util/src/com/intellij/util/containers/ContainerUtil.java +++ b/platform/util/src/com/intellij/util/containers/ContainerUtil.java @@ -33,135 +33,166 @@ import java.util.LinkedHashSet; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CopyOnWriteArrayList; -@SuppressWarnings({"UtilityClassWithoutPrivateConstructor", "MethodOverridesStaticMethodOfSuperclass", "UnusedDeclaration"}) +@SuppressWarnings({"UtilityClassWithoutPrivateConstructor", "MethodOverridesStaticMethodOfSuperclass"}) public class ContainerUtil extends ContainerUtilRt { private static final int INSERTION_SORT_THRESHOLD = 10; private static final int DEFAULT_CONCURRENCY_LEVEL = Math.min(16, Runtime.getRuntime().availableProcessors()); @NotNull + @Contract(pure=true) public static T[] ar(@NotNull T... elements) { return elements; } @NotNull + @Contract(pure=true) public static HashMap newHashMap() { return ContainerUtilRt.newHashMap(); } @NotNull + @Contract(pure=true) public static HashMap newHashMap(@NotNull Map map) { return ContainerUtilRt.newHashMap(map); } @NotNull + @Contract(pure=true) public static Map newHashMap(@NotNull Pair first, Pair... entries) { return ContainerUtilRt.newHashMap(first, entries); } @NotNull + @Contract(pure=true) public static Map newHashMap(@NotNull List keys, @NotNull List values) { return ContainerUtilRt.newHashMap(keys, values); } @NotNull + @Contract(pure=true) public static TreeMap newTreeMap() { return ContainerUtilRt.newTreeMap(); } @NotNull + @Contract(pure=true) public static TreeMap newTreeMap(@NotNull Map map) { return ContainerUtilRt.newTreeMap(map); } @NotNull + @Contract(pure=true) public static LinkedHashMap newLinkedHashMap() { return ContainerUtilRt.newLinkedHashMap(); } @NotNull + @Contract(pure=true) + public static LinkedHashMap newLinkedHashMap(int capacity) { + return ContainerUtilRt.newLinkedHashMap(capacity); + } + + @NotNull + @Contract(pure=true) public static LinkedHashMap newLinkedHashMap(@NotNull Map map) { return ContainerUtilRt.newLinkedHashMap(map); } @NotNull + @Contract(pure=true) public static LinkedHashMap newLinkedHashMap(@NotNull Pair first, Pair... entries) { return ContainerUtilRt.newLinkedHashMap(first, entries); } @NotNull + @Contract(pure=true) public static THashMap newTroveMap() { return new THashMap(); } @NotNull + @Contract(pure=true) public static THashMap newTroveMap(@NotNull TObjectHashingStrategy strategy) { return new THashMap(strategy); } @NotNull + @Contract(pure=true) public static , V> EnumMap newEnumMap(@NotNull Class keyType) { return new EnumMap(keyType); } @SuppressWarnings("unchecked") @NotNull + @Contract(pure=true) public static TObjectHashingStrategy canonicalStrategy() { return TObjectHashingStrategy.CANONICAL; } @SuppressWarnings("unchecked") @NotNull + @Contract(pure=true) public static TObjectHashingStrategy identityStrategy() { return TObjectHashingStrategy.IDENTITY; } @NotNull + @Contract(pure=true) public static IdentityHashMap newIdentityHashMap() { return new IdentityHashMap(); } @NotNull + @Contract(pure=true) public static LinkedList newLinkedList() { return ContainerUtilRt.newLinkedList(); } @NotNull + @Contract(pure=true) public static LinkedList newLinkedList(@NotNull T... elements) { return ContainerUtilRt.newLinkedList(elements); } @NotNull + @Contract(pure=true) public static LinkedList newLinkedList(@NotNull Iterable elements) { return ContainerUtilRt.newLinkedList(elements); } @NotNull + @Contract(pure=true) public static ArrayList newArrayList() { return ContainerUtilRt.newArrayList(); } @NotNull + @Contract(pure=true) public static ArrayList newArrayList(@NotNull E... array) { return ContainerUtilRt.newArrayList(array); } @NotNull + @Contract(pure=true) public static ArrayList newArrayList(@NotNull Iterable iterable) { return ContainerUtilRt.newArrayList(iterable); } /** @deprecated Use {@link #newArrayListWithCapacity(int)} (to remove in IDEA 15) */ @SuppressWarnings("deprecation") + @Contract(pure=true) public static ArrayList newArrayListWithExpectedSize(int size) { return ContainerUtilRt.newArrayListWithCapacity(size); } @NotNull + @Contract(pure=true) public static ArrayList newArrayListWithCapacity(int size) { return ContainerUtilRt.newArrayListWithCapacity(size); } @NotNull + @Contract(pure=true) public static List newArrayList(@NotNull final T[] elements, final int start, final int end) { if (start < 0 || start > end || end > elements.length) { throw new IllegalArgumentException("start:" + start + " end:" + end + " length:" + elements.length); @@ -184,31 +215,37 @@ public class ContainerUtil extends ContainerUtilRt { } @NotNull + @Contract(pure=true) public static List newSmartList(T element) { return new SmartList(element); } @NotNull + @Contract(pure=true) public static List newSmartList(@NotNull T... elements) { return new SmartList(elements); } @NotNull + @Contract(pure=true) public static HashSet newHashSet() { return ContainerUtilRt.newHashSet(); } @NotNull + @Contract(pure=true) public static HashSet newHashSet(int initialCapacity) { return ContainerUtilRt.newHashSet(initialCapacity); } @NotNull + @Contract(pure=true) public static HashSet newHashSet(@NotNull T... elements) { return ContainerUtilRt.newHashSet(elements); } @NotNull + @Contract(pure=true) public static HashSet newHashSet(@NotNull Iterable iterable) { return ContainerUtilRt.newHashSet(iterable); } @@ -219,117 +256,141 @@ public class ContainerUtil extends ContainerUtilRt { } @NotNull + @Contract(pure=true) public static Set newHashOrEmptySet(@Nullable Iterable iterable) { boolean empty = iterable == null || iterable instanceof Collection && ((Collection)iterable).isEmpty(); return empty ? Collections.emptySet() : ContainerUtilRt.newHashSet(iterable); } @NotNull + @Contract(pure=true) public static LinkedHashSet newLinkedHashSet() { return ContainerUtilRt.newLinkedHashSet(); } @NotNull + @Contract(pure=true) public static LinkedHashSet newLinkedHashSet(@NotNull Iterable elements) { return ContainerUtilRt.newLinkedHashSet(elements); } @NotNull + @Contract(pure=true) public static LinkedHashSet newLinkedHashSet(@NotNull T... elements) { return ContainerUtilRt.newLinkedHashSet(elements); } @NotNull + @Contract(pure=true) public static THashSet newTroveSet() { return new THashSet(); } @NotNull + @Contract(pure=true) public static THashSet newTroveSet(@NotNull TObjectHashingStrategy strategy) { return new THashSet(strategy); } @NotNull + @Contract(pure=true) public static THashSet newTroveSet(@NotNull T... elements) { return newTroveSet(Arrays.asList(elements)); } @NotNull + @Contract(pure=true) public static THashSet newTroveSet(@NotNull TObjectHashingStrategy strategy, @NotNull T... elements) { return new THashSet(Arrays.asList(elements), strategy); } @NotNull + @Contract(pure=true) public static THashSet newTroveSet(@NotNull TObjectHashingStrategy strategy, @NotNull Collection elements) { return new THashSet(elements, strategy); } @NotNull + @Contract(pure=true) public static THashSet newTroveSet(@NotNull Collection elements) { return new THashSet(elements); } @NotNull + @Contract(pure=true) public static THashSet newIdentityTroveSet() { return new THashSet(ContainerUtil.identityStrategy()); } @NotNull + @Contract(pure=true) public static THashSet newIdentityTroveSet(int initialCapacity) { return new THashSet(initialCapacity, ContainerUtil.identityStrategy()); } @NotNull + @Contract(pure=true) public static THashSet newIdentityTroveSet(@NotNull Collection collection) { return new THashSet(collection, ContainerUtil.identityStrategy()); } @NotNull + @Contract(pure=true) public static THashMap newIdentityTroveMap() { return new THashMap(ContainerUtil.identityStrategy()); } @NotNull + @Contract(pure=true) public static TreeSet newTreeSet() { return ContainerUtilRt.newTreeSet(); } @NotNull + @Contract(pure=true) public static TreeSet newTreeSet(@NotNull Iterable elements) { return ContainerUtilRt.newTreeSet(elements); } @NotNull + @Contract(pure=true) public static TreeSet newTreeSet(@NotNull T... elements) { return ContainerUtilRt.newTreeSet(elements); } @NotNull + @Contract(pure=true) public static TreeSet newTreeSet(@Nullable Comparator comparator) { return ContainerUtilRt.newTreeSet(comparator); } @NotNull + @Contract(pure=true) public static ConcurrentMap newConcurrentMap() { return CHM_FACTORY.createMap(); } + @Contract(pure=true) public static ConcurrentMap newConcurrentMap(@NotNull TObjectHashingStrategy hashStrategy) { return CHM_FACTORY.createMap(hashStrategy); } + @Contract(pure=true) public static ConcurrentMap newConcurrentMap(int initialCapacity) { return CHM_FACTORY.createMap(initialCapacity); } + @Contract(pure=true) public static ConcurrentMap newConcurrentMap(int initialCapacity, float loadFactor, int concurrencyLevel, @NotNull TObjectHashingStrategy hashStrategy) { return CHM_FACTORY.createMap(initialCapacity, loadFactor, concurrencyLevel, hashStrategy); } + @Contract(pure=true) public static ConcurrentMap newConcurrentMap(int initialCapacity, float loadFactor, int concurrencyLevel) { return CHM_FACTORY.createMap(initialCapacity, loadFactor, concurrencyLevel); } @NotNull + @Contract(pure=true) public static List reverse(@NotNull final List elements) { if (elements.isEmpty()) { return ContainerUtilRt.emptyList(); @@ -349,37 +410,43 @@ public class ContainerUtil extends ContainerUtilRt { } @NotNull + @Contract(pure=true) public static Map union(@NotNull Map map, @NotNull Map map2) { - THashMap result = new THashMap(map.size() + map2.size()); + Map result = new THashMap(map.size() + map2.size()); result.putAll(map); result.putAll(map2); return result; } @NotNull + @Contract(pure=true) public static Set union(@NotNull Set set, @NotNull Set set2) { - THashSet result = new THashSet(set.size() + set2.size()); + Set result = new THashSet(set.size() + set2.size()); result.addAll(set); result.addAll(set2); return result; } @NotNull + @Contract(pure=true) public static Set immutableSet(@NotNull E ... elements) { return Collections.unmodifiableSet(new THashSet(Arrays.asList(elements))); } @NotNull + @Contract(pure=true) public static ImmutableList immutableList(@NotNull E ... array) { return new ImmutableListBackedByArray(array); } @NotNull + @Contract(pure=true) public static ImmutableList immutableList(@NotNull List list) { return new ImmutableListBackedByList(list); } @NotNull + @Contract(pure=true) public static ImmutableMapBuilder immutableMapBuilder() { return new ImmutableMapBuilder(); } @@ -392,6 +459,7 @@ public class ContainerUtil extends ContainerUtilRt { return this; } + @Contract(pure=true) public Map build() { return Collections.unmodifiableMap(myMap); } @@ -434,6 +502,7 @@ public class ContainerUtil extends ContainerUtilRt { } @NotNull + @Contract(pure=true) public static Map intersection(@NotNull Map map1, @NotNull Map map2) { final Map res = newHashMap(); final Set keys = newHashSet(); @@ -450,6 +519,7 @@ public class ContainerUtil extends ContainerUtilRt { } @NotNull + @Contract(pure=true) public static Map> diff(@NotNull Map map1, @NotNull Map map2) { final Map> res = newHashMap(); final Set keys = newHashSet(); @@ -505,6 +575,7 @@ public class ContainerUtil extends ContainerUtilRt { } @NotNull + @Contract(pure=true) public static List mergeSortedLists(@NotNull List list1, @NotNull List list2, @NotNull Comparator comparator, @@ -521,6 +592,7 @@ public class ContainerUtil extends ContainerUtilRt { } @NotNull + @Contract(pure=true) public static List mergeSortedArrays(@NotNull T[] list1, @NotNull T[] list2, @NotNull Comparator comparator, boolean mergeEqualItems, @Nullable Processor filter) { int index1 = 0; int index2 = 0; @@ -572,6 +644,7 @@ public class ContainerUtil extends ContainerUtilRt { } @NotNull + @Contract(pure=true) public static List subList(@NotNull List list, int from) { return list.subList(from, list.size()); } @@ -658,16 +731,19 @@ public class ContainerUtil extends ContainerUtilRt { } @NotNull + @Contract(pure=true) public static Iterator emptyIterator() { return EmptyIterator.getInstance(); } @NotNull + @Contract(pure=true) public static Iterable emptyIterable() { return EmptyIterable.getInstance(); } @Nullable + @Contract(pure=true) public static T find(@NotNull T[] array, @NotNull Condition condition) { for (T element : array) { if (condition.value(element)) return element; @@ -714,11 +790,13 @@ public class ContainerUtil extends ContainerUtilRt { } @Nullable + @Contract(pure=true) public static V find(@NotNull Iterable iterable, @NotNull Condition condition) { return find(iterable.iterator(), condition); } @Nullable + @Contract(pure=true) public static T find(@NotNull Iterable iterable, final T equalTo) { return find(iterable, new Condition() { @Override @@ -738,11 +816,13 @@ public class ContainerUtil extends ContainerUtilRt { } @NotNull + @Contract(pure=true) public static Map map2Map(@NotNull T[] collection, @NotNull Function> mapper) { return map2Map(Arrays.asList(collection), mapper); } @NotNull + @Contract(pure=true) public static Map map2Map(@NotNull Collection collection, @NotNull Function> mapper) { final Map set = new THashMap(collection.size()); @@ -754,6 +834,7 @@ public class ContainerUtil extends ContainerUtilRt { } @NotNull + @Contract(pure=true) public static Map map2Map(@NotNull Collection> collection) { final Map result = new THashMap(collection.size()); for (Pair pair : collection) { @@ -763,21 +844,25 @@ public class ContainerUtil extends ContainerUtilRt { } @NotNull + @Contract(pure=true) public static Object[] map2Array(@NotNull T[] array, @NotNull Function mapper) { return map2Array(array, Object.class, mapper); } @NotNull + @Contract(pure=true) public static Object[] map2Array(@NotNull Collection array, @NotNull Function mapper) { return map2Array(array, Object.class, mapper); } @NotNull + @Contract(pure=true) public static V[] map2Array(@NotNull T[] array, @NotNull Class aClass, @NotNull Function mapper) { return map2Array(Arrays.asList(array), aClass, mapper); } @NotNull + @Contract(pure=true) public static V[] map2Array(@NotNull Collection collection, @NotNull Class aClass, @NotNull Function mapper) { final List list = map2List(collection, mapper); @SuppressWarnings("unchecked") V[] array = (V[])Array.newInstance(aClass, list.size()); @@ -785,16 +870,19 @@ public class ContainerUtil extends ContainerUtilRt { } @NotNull + @Contract(pure=true) public static V[] map2Array(@NotNull Collection collection, @NotNull V[] to, @NotNull Function mapper) { return map2List(collection, mapper).toArray(to); } @NotNull + @Contract(pure=true) public static List filter(@NotNull T[] collection, @NotNull Condition condition) { return findAll(collection, condition); } @NotNull + @Contract(pure=true) public static int[] filter(@NotNull int[] collection, @NotNull TIntProcedure condition) { TIntArrayList result = new TIntArrayList(); for (int t : collection) { @@ -806,11 +894,13 @@ public class ContainerUtil extends ContainerUtilRt { } @NotNull + @Contract(pure=true) public static List filter(@NotNull Condition condition, @NotNull T... collection) { return findAll(collection, condition); } @NotNull + @Contract(pure=true) public static List findAll(@NotNull T[] collection, @NotNull Condition condition) { final List result = new SmartList(); for (T t : collection) { @@ -822,11 +912,13 @@ public class ContainerUtil extends ContainerUtilRt { } @NotNull + @Contract(pure=true) public static List filter(@NotNull Collection collection, @NotNull Condition condition) { return findAll(collection, condition); } @NotNull + @Contract(pure=true) public static List findAll(@NotNull Collection collection, @NotNull Condition condition) { if (collection.isEmpty()) return emptyList(); final List result = new SmartList(); @@ -839,16 +931,19 @@ public class ContainerUtil extends ContainerUtilRt { } @NotNull + @Contract(pure=true) public static List skipNulls(@NotNull Collection collection) { return findAll(collection, Condition.NOT_NULL); } @NotNull + @Contract(pure=true) public static List findAll(@NotNull T[] collection, @NotNull Class instanceOf) { return findAll(Arrays.asList(collection), instanceOf); } @NotNull + @Contract(pure=true) public static V[] findAllAsArray(@NotNull T[] collection, @NotNull Class instanceOf) { List list = findAll(Arrays.asList(collection), instanceOf); @SuppressWarnings("unchecked") V[] array = (V[])Array.newInstance(instanceOf, list.size()); @@ -856,6 +951,7 @@ public class ContainerUtil extends ContainerUtilRt { } @NotNull + @Contract(pure=true) public static V[] findAllAsArray(@NotNull Collection collection, @NotNull Class instanceOf) { List list = findAll(collection, instanceOf); @SuppressWarnings("unchecked") V[] array = (V[])Array.newInstance(instanceOf, list.size()); @@ -863,6 +959,7 @@ public class ContainerUtil extends ContainerUtilRt { } @NotNull + @Contract(pure=true) public static T[] findAllAsArray(@NotNull T[] collection, @NotNull Condition instanceOf) { List list = findAll(collection, instanceOf); @SuppressWarnings("unchecked") T[] array = (T[])Array.newInstance(collection.getClass().getComponentType(), list.size()); @@ -870,6 +967,7 @@ public class ContainerUtil extends ContainerUtilRt { } @NotNull + @Contract(pure=true) public static List findAll(@NotNull Collection collection, @NotNull Class instanceOf) { final List result = new SmartList(); for (final T t : collection) { @@ -895,6 +993,7 @@ public class ContainerUtil extends ContainerUtilRt { } @NotNull + @Contract(pure=true) public static Map stringMap(@NotNull final String... keyValues) { final Map result = newHashMap(); for (int i = 0; i < keyValues.length - 1; i+=2) { @@ -905,11 +1004,13 @@ public class ContainerUtil extends ContainerUtilRt { } @NotNull + @Contract(pure=true) public static Iterator iterate(@NotNull T[] arrays) { return Arrays.asList(arrays).iterator(); } @NotNull + @Contract(pure=true) public static Iterator iterate(@NotNull final Enumeration enumeration) { return new Iterator() { @Override @@ -930,11 +1031,13 @@ public class ContainerUtil extends ContainerUtilRt { } @NotNull + @Contract(pure=true) public static Iterable iterate(@NotNull T[] arrays, @NotNull Condition condition) { return iterate(Arrays.asList(arrays), condition); } @NotNull + @Contract(pure=true) public static Iterable iterate(@NotNull final Collection collection, @NotNull final Condition condition) { if (collection.isEmpty()) return emptyIterable(); return new Iterable() { @@ -977,6 +1080,7 @@ public class ContainerUtil extends ContainerUtilRt { } @NotNull + @Contract(pure=true) public static Iterable iterateBackward(@NotNull final List list) { return new Iterable() { @Override @@ -1068,6 +1172,7 @@ public class ContainerUtil extends ContainerUtilRt { return modified; } + @Contract(pure=true) public static U findInstance(@NotNull Iterable iterable, @NotNull Class aClass) { return findInstance(iterable.iterator(), aClass); } @@ -1079,11 +1184,13 @@ public class ContainerUtil extends ContainerUtilRt { } @Nullable + @Contract(pure=true) public static U findInstance(@NotNull T[] array, @NotNull Class aClass) { return findInstance(Arrays.asList(array), aClass); } @NotNull + @Contract(pure=true) public static List concat(@NotNull V[] array, @NotNull Function> fun) { return concat(Arrays.asList(array), fun); } @@ -1092,6 +1199,7 @@ public class ContainerUtil extends ContainerUtilRt { * @return read-only list consisting of the elements from the collections stored in list added together */ @NotNull + @Contract(pure=true) public static List concat(@NotNull Iterable> list) { List result = new ArrayList(); for (final Collection ts : list) { @@ -1105,6 +1213,7 @@ public class ContainerUtil extends ContainerUtilRt { * @return read-only list consisting of the elements from specified list with some additional values */ @NotNull + @Contract(pure=true) public static List concat(boolean appendTail, @NotNull List list, T... values) { return appendTail ? concat(list, list(values)) : concat(list(values), list); } @@ -1113,6 +1222,7 @@ public class ContainerUtil extends ContainerUtilRt { * @return read-only list consisting of the two lists added together */ @NotNull + @Contract(pure=true) public static List concat(@NotNull final List list1, @NotNull final List list2) { if (list1.isEmpty() && list2.isEmpty()) { return Collections.emptyList(); @@ -1139,6 +1249,7 @@ public class ContainerUtil extends ContainerUtilRt { } @NotNull + @Contract(pure=true) public static Iterable concat(@NotNull final Iterable... iterables) { return new Iterable() { @Override @@ -1155,16 +1266,19 @@ public class ContainerUtil extends ContainerUtilRt { } @NotNull + @Contract(pure=true) public static Iterator concatIterators(@NotNull Iterator... iterators) { return new SequenceIterator(iterators); } @NotNull + @Contract(pure=true) public static Iterator concatIterators(@NotNull Collection> iterators) { return new SequenceIterator(iterators); } @NotNull + @Contract(pure=true) public static Iterable concat(@NotNull final T[]... iterables) { return new Iterable() { @Override @@ -1184,6 +1298,7 @@ public class ContainerUtil extends ContainerUtilRt { * @return read-only list consisting of the lists added together */ @NotNull + @Contract(pure=true) public static List concat(@NotNull final List... lists) { int size = 0; for (List each : lists) { @@ -1215,6 +1330,7 @@ public class ContainerUtil extends ContainerUtilRt { * @return read-only list consisting of the lists added together */ @NotNull + @Contract(pure=true) public static List concat(@NotNull final List> lists) { @SuppressWarnings("unchecked") List[] array = lists.toArray(new List[lists.size()]); return concat(array); @@ -1224,6 +1340,7 @@ public class ContainerUtil extends ContainerUtilRt { * @return read-only list consisting of the lists (made by listGenerator) added together */ @NotNull + @Contract(pure=true) public static List concat(@NotNull Iterable list, @NotNull Function> listGenerator) { List result = new ArrayList(); for (final V v : list) { @@ -1232,6 +1349,7 @@ public class ContainerUtil extends ContainerUtilRt { return result.isEmpty() ? ContainerUtil.emptyList() : result; } + @Contract(pure=true) public static boolean intersects(@NotNull Collection collection1, @NotNull Collection collection2) { for (T t : collection1) { //noinspection SuspiciousMethodCalls @@ -1246,6 +1364,7 @@ public class ContainerUtil extends ContainerUtilRt { * @return read-only collection consisting of elements from both collections */ @NotNull + @Contract(pure=true) public static Collection intersection(@NotNull Collection collection1, @NotNull Collection collection2) { List result = new ArrayList(); for (T t : collection1) { @@ -1257,15 +1376,18 @@ public class ContainerUtil extends ContainerUtilRt { } @Nullable + @Contract(pure=true) public static T getFirstItem(@Nullable Collection items) { return getFirstItem(items, null); } @Nullable + @Contract(pure=true) public static T getFirstItem(@Nullable List items) { return items == null || items.isEmpty() ? null : items.get(0); } + @Contract(pure=true) public static T getFirstItem(@Nullable final Collection items, @Nullable final T def) { return items == null || items.isEmpty() ? def : items.iterator().next(); } @@ -1280,11 +1402,13 @@ public class ContainerUtil extends ContainerUtilRt { * @return new list with no more than maxItems first elements */ @NotNull + @Contract(pure=true) public static List getFirstItems(@NotNull final List items, int maxItems) { return items.subList(0, Math.min(maxItems, items.size())); } @Nullable + @Contract(pure=true) public static T iterateAndGetLastItem(@NotNull Iterable items) { Iterator itr = items.iterator(); T res = null; @@ -1296,11 +1420,13 @@ public class ContainerUtil extends ContainerUtilRt { } @Nullable + @Contract(pure=true) public static > T getLastItem(@NotNull L list, @Nullable T def) { return list.isEmpty() ? def : list.get(list.size() - 1); } @Nullable + @Contract(pure=true) public static > T getLastItem(@NotNull L list) { return getLastItem(list, null); } @@ -1309,6 +1435,7 @@ public class ContainerUtil extends ContainerUtilRt { * @return read-only collection consisting of elements from the 'from' collection which are absent from the 'what' collection */ @NotNull + @Contract(pure=true) public static Collection subtract(@NotNull Collection from, @NotNull Collection what) { final Set set = newHashSet(from); set.removeAll(what); @@ -1316,16 +1443,19 @@ public class ContainerUtil extends ContainerUtilRt { } @NotNull + @Contract(pure=true) public static T[] toArray(@Nullable Collection c, @NotNull ArrayFactory factory) { return c != null ? c.toArray(factory.create(c.size())) : factory.create(0); } @NotNull + @Contract(pure=true) public static T[] toArray(@NotNull Collection c1, @NotNull Collection c2, @NotNull ArrayFactory factory) { return ArrayUtil.mergeCollections(c1, c2, factory); } @NotNull + @Contract(pure=true) public static T[] mergeCollectionsToArray(@NotNull Collection c1, @NotNull Collection c2, @NotNull ArrayFactory factory) { return ArrayUtil.mergeCollections(c1, c2, factory); } @@ -1424,6 +1554,7 @@ public class ContainerUtil extends ContainerUtilRt { } @NotNull + @Contract(pure=true) public static List sorted(@NotNull Collection list, @NotNull Comparator comparator) { List sorted = newArrayList(list); sort(sorted, comparator); @@ -1431,6 +1562,7 @@ public class ContainerUtil extends ContainerUtilRt { } @NotNull + @Contract(pure=true) public static > List sorted(@NotNull Collection list) { return sorted(list, new Comparator() { @Override @@ -1475,6 +1607,7 @@ public class ContainerUtil extends ContainerUtilRt { * @return read-only list consisting of the elements from the iterable converted by mapping */ @NotNull + @Contract(pure=true) public static List map(@NotNull Iterable iterable, @NotNull Function mapping) { List result = new ArrayList(); for (T t : iterable) { @@ -1487,6 +1620,7 @@ public class ContainerUtil extends ContainerUtilRt { * @return read-only list consisting of the elements from the iterable converted by mapping */ @NotNull + @Contract(pure=true) public static List map(@NotNull Collection iterable, @NotNull Function mapping) { if (iterable.isEmpty()) return emptyList(); List result = new ArrayList(iterable.size()); @@ -1500,6 +1634,7 @@ public class ContainerUtil extends ContainerUtilRt { * @return read-only list consisting of the elements from the array converted by mapping with nulls filtered out */ @NotNull + @Contract(pure=true) public static List mapNotNull(@NotNull T[] array, @NotNull Function mapping) { return mapNotNull(Arrays.asList(array), mapping); } @@ -1508,6 +1643,7 @@ public class ContainerUtil extends ContainerUtilRt { * @return read-only list consisting of the elements from the array converted by mapping with nulls filtered out */ @NotNull + @Contract(pure=true) public static V[] mapNotNull(@NotNull T[] array, @NotNull Function mapping, @NotNull V[] emptyArray) { List result = new ArrayList(array.length); for (T t : array) { @@ -1527,6 +1663,7 @@ public class ContainerUtil extends ContainerUtilRt { * @return read-only list consisting of the elements from the iterable converted by mapping with nulls filtered out */ @NotNull + @Contract(pure=true) public static List mapNotNull(@NotNull Iterable iterable, @NotNull Function mapping) { List result = new ArrayList(); for (T t : iterable) { @@ -1542,6 +1679,7 @@ public class ContainerUtil extends ContainerUtilRt { * @return read-only list consisting of the elements from the array converted by mapping with nulls filtered out */ @NotNull + @Contract(pure=true) public static List mapNotNull(@NotNull Collection iterable, @NotNull Function mapping) { if (iterable.isEmpty()) { return emptyList(); @@ -1561,6 +1699,7 @@ public class ContainerUtil extends ContainerUtilRt { * @return read-only list consisting of the elements with nulls filtered out */ @NotNull + @Contract(pure=true) public static List packNullables(@NotNull T... elements) { List list = new ArrayList(); for (T element : elements) { @@ -1573,6 +1712,7 @@ public class ContainerUtil extends ContainerUtilRt { * @return read-only list consisting of the elements from the array converted by mapping */ @NotNull + @Contract(pure=true) public static List map(@NotNull T[] array, @NotNull Function mapping) { List result = new ArrayList(array.length); for (T t : array) { @@ -1582,6 +1722,7 @@ public class ContainerUtil extends ContainerUtilRt { } @NotNull + @Contract(pure=true) public static V[] map(@NotNull T[] arr, @NotNull Function mapping, @NotNull V[] emptyArray) { if (arr.length==0) { assert emptyArray.length == 0 : "You must pass an empty array"; @@ -1596,6 +1737,7 @@ public class ContainerUtil extends ContainerUtilRt { } @NotNull + @Contract(pure=true) public static Set set(@NotNull T ... items) { return newHashSet(items); } @@ -1618,11 +1760,13 @@ public class ContainerUtil extends ContainerUtilRt { } @NotNull + @Contract(pure=true) public static List createMaybeSingletonList(@Nullable T element) { return element == null ? ContainerUtil.emptyList() : Collections.singletonList(element); } @NotNull + @Contract(pure=true) public static Set createMaybeSingletonSet(@Nullable T element) { return element == null ? Collections.emptySet() : Collections.singleton(element); } @@ -1645,15 +1789,18 @@ public class ContainerUtil extends ContainerUtilRt { } @NotNull + @Contract(pure=true) public static V getOrElse(@NotNull Map result, final T key, @NotNull V defValue) { V value = result.get(key); return value == null ? defValue : value; } + @Contract(pure=true) public static boolean and(@NotNull T[] iterable, @NotNull Condition condition) { return and(Arrays.asList(iterable), condition); } + @Contract(pure=true) public static boolean and(@NotNull Iterable iterable, @NotNull Condition condition) { for (final T t : iterable) { if (!condition.value(t)) return false; @@ -1661,18 +1808,22 @@ public class ContainerUtil extends ContainerUtilRt { return true; } + @Contract(pure=true) public static boolean exists(@NotNull T[] iterable, @NotNull Condition condition) { return or(Arrays.asList(iterable), condition); } + @Contract(pure=true) public static boolean exists(@NotNull Iterable iterable, @NotNull Condition condition) { return or(iterable, condition); } + @Contract(pure=true) public static boolean or(@NotNull T[] iterable, @NotNull Condition condition) { return or(Arrays.asList(iterable), condition); } + @Contract(pure=true) public static boolean or(@NotNull Iterable iterable, @NotNull Condition condition) { for (final T t : iterable) { if (condition.value(t)) return true; @@ -1681,10 +1832,11 @@ public class ContainerUtil extends ContainerUtilRt { } @NotNull + @Contract(pure=true) public static List unfold(@Nullable T t, @NotNull NullableFunction next) { if (t == null) return emptyList(); - final ArrayList list = new ArrayList(); + List list = new ArrayList(); while (t != null) { list.add(t); t = next.fun(t); @@ -1693,11 +1845,13 @@ public class ContainerUtil extends ContainerUtilRt { } @NotNull + @Contract(pure=true) public static List dropTail(@NotNull List items) { return items.subList(0, items.size() - 1); } @NotNull + @Contract(pure=true) public static List list(@NotNull T... items) { return Arrays.asList(items); } @@ -1845,6 +1999,7 @@ public class ContainerUtil extends ContainerUtilRt { * @return read-only set consisting of the only element o */ @NotNull + @Contract(pure=true) public static Set singleton(final T o, @NotNull final TObjectHashingStrategy strategy) { return new SingletonSet(o, strategy); } @@ -1853,6 +2008,7 @@ public class ContainerUtil extends ContainerUtilRt { * @return read-only list consisting of the elements from all of the collections */ @NotNull + @Contract(pure=true) public static List flatten(@NotNull Collection[] collections) { return flatten(Arrays.asList(collections)); } @@ -1861,6 +2017,7 @@ public class ContainerUtil extends ContainerUtilRt { * @return read-only list consisting of the elements from all of the collections */ @NotNull + @Contract(pure=true) public static List flatten(@NotNull Iterable> collections) { List result = new ArrayList(); for (Collection list : collections) { @@ -1874,6 +2031,7 @@ public class ContainerUtil extends ContainerUtilRt { * @return read-only list consisting of the elements from all of the collections */ @NotNull + @Contract(pure=true) public static List flattenIterables(@NotNull Iterable> collections) { List result = new ArrayList(); for (Iterable list : collections) { @@ -1885,6 +2043,7 @@ public class ContainerUtil extends ContainerUtilRt { } @NotNull + @Contract(pure=true) public static V[] convert(@NotNull K[] from, @NotNull V[] to, @NotNull Function fun) { if (to.length < from.length) { @SuppressWarnings("unchecked") V[] array = (V[])Array.newInstance(to.getClass().getComponentType(), from.length); @@ -1896,6 +2055,7 @@ public class ContainerUtil extends ContainerUtilRt { return to; } + @Contract(pure=true) public static boolean containsIdentity(@NotNull Iterable list, T element) { for (T t : list) { if (t == element) { @@ -1905,6 +2065,7 @@ public class ContainerUtil extends ContainerUtilRt { return false; } + @Contract(pure=true) public static int indexOfIdentity(@NotNull List list, T element) { for (int i = 0, listSize = list.size(); i < listSize; i++) { if (list.get(i) == element) { @@ -1914,6 +2075,7 @@ public class ContainerUtil extends ContainerUtilRt { return -1; } + @Contract(pure=true) public static boolean equalsIdentity(@NotNull List list1, @NotNull List list2) { int listSize = list1.size(); if (list2.size() != listSize) { @@ -1928,6 +2090,7 @@ public class ContainerUtil extends ContainerUtilRt { return true; } + @Contract(pure=true) public static int indexOf(@NotNull List list, @NotNull Condition condition) { for (int i = 0, listSize = list.size(); i < listSize; i++) { T t = list.get(i); @@ -1938,6 +2101,7 @@ public class ContainerUtil extends ContainerUtilRt { return -1; } + @Contract(pure=true) public static int indexOf(@NotNull List list, @NotNull final T object) { return indexOf(list, new Condition() { @Override @@ -1948,6 +2112,7 @@ public class ContainerUtil extends ContainerUtilRt { } @NotNull + @Contract(pure=true) public static Map reverseMap(@NotNull Map map) { final Map result = newHashMap(); for (Map.Entry entry : map.entrySet()) { @@ -1956,6 +2121,7 @@ public class ContainerUtil extends ContainerUtilRt { return result; } + @Contract(pure=true) public static boolean processRecursively(final T root, @NotNull PairProcessor> processor) { final LinkedList list = new LinkedList(); list.add(root); @@ -1979,26 +2145,31 @@ public class ContainerUtil extends ContainerUtilRt { } @NotNull + @Contract(pure=true) public static Stack newStack() { return ContainerUtilRt.newStack(); } @NotNull + @Contract(pure=true) public static Stack newStack(@NotNull Collection initial) { return ContainerUtilRt.newStack(initial); } @NotNull + @Contract(pure=true) public static Stack newStack(@NotNull T... initial) { return ContainerUtilRt.newStack(initial); } @NotNull + @Contract(pure=true) public static List emptyList() { return ContainerUtilRt.emptyList(); } @NotNull + @Contract(pure=true) public static CopyOnWriteArrayList createEmptyCOWList() { return ContainerUtilRt.createEmptyCOWList(); } @@ -2011,16 +2182,19 @@ public class ContainerUtil extends ContainerUtilRt { * - slower modification in highly contented case (which is the kind of situation you shouldn't use COWAL anyway) */ @NotNull + @Contract(pure=true) public static List createLockFreeCopyOnWriteList() { return createConcurrentList(); } @NotNull + @Contract(pure=true) public static List createLockFreeCopyOnWriteList(@NotNull Collection c) { return new LockFreeCopyOnWriteArrayList(c); } @NotNull + @Contract(pure=true) public static ConcurrentList createConcurrentList() { return new LockFreeCopyOnWriteArrayList(); } @@ -2034,31 +2208,37 @@ public class ContainerUtil extends ContainerUtilRt { } @NotNull + @Contract(pure=true) public static List map2List(@NotNull T[] array, @NotNull Function mapper) { return ContainerUtilRt.map2List(array, mapper); } @NotNull + @Contract(pure=true) public static List map2List(@NotNull Collection collection, @NotNull Function mapper) { return ContainerUtilRt.map2List(collection, mapper); } @NotNull + @Contract(pure=true) public static Set map2Set(@NotNull T[] collection, @NotNull Function mapper) { return ContainerUtilRt.map2Set(collection, mapper); } @NotNull + @Contract(pure=true) public static Set map2Set(@NotNull Collection collection, @NotNull Function mapper) { return ContainerUtilRt.map2Set(collection, mapper); } @NotNull + @Contract(pure=true) public static T[] toArray(@NotNull List collection, @NotNull T[] array) { return ContainerUtilRt.toArray(collection, array); } @NotNull + @Contract(pure=true) public static T[] toArray(@NotNull Collection c, @NotNull T[] sample) { return ContainerUtilRt.toArray(c, sample); } @@ -2075,6 +2255,7 @@ public class ContainerUtil extends ContainerUtilRt { } @NotNull + @Contract(pure=true) public static Collection toCollection(@NotNull Iterable iterable) { return iterable instanceof Collection ? (Collection)iterable : newArrayList(iterable); } @@ -2092,18 +2273,26 @@ public class ContainerUtil extends ContainerUtilRt { return result; } - @Contract("null -> true") + @Contract(value = "null -> true", pure = true) public static boolean isEmpty(Collection collection) { return collection == null || collection.isEmpty(); } @NotNull - public static > C notNullize(@Nullable C collection) { + @Contract(pure=true) + public static List notNullize(@Nullable List list) { + return list == null ? ContainerUtilRt.emptyList() : list; + } + + @NotNull + @Contract(pure=true) + public static Set notNullize(@Nullable Set set) { //noinspection unchecked - return collection == null ? (C)ContainerUtilRt.emptyList() : collection; + return set == null ? Collections.emptySet() : set; } @Nullable + @Contract(pure=true) public static > C nullize(@Nullable C collection) { return isEmpty(collection) ? null : collection; } @@ -2117,26 +2306,31 @@ public class ContainerUtil extends ContainerUtilRt { } private static final ConcurrentMapFactory V8_MAP_FACTORY = new ConcurrentMapFactory() { + @Override @NotNull public ConcurrentMap createMap() { return new ConcurrentHashMap(); } + @Override @NotNull public ConcurrentMap createMap(int initialCapacity) { return new ConcurrentHashMap(initialCapacity); } + @Override @NotNull public ConcurrentMap createMap(@NotNull TObjectHashingStrategy hashStrategy) { return new ConcurrentHashMap(hashStrategy); } + @Override @NotNull public ConcurrentMap createMap(int initialCapacity, float loadFactor, int concurrencyLevel) { return new ConcurrentHashMap(initialCapacity, loadFactor, concurrencyLevel); } + @Override @NotNull public ConcurrentMap createMap(int initialCapacity, float loadFactor, int concurrencyLevel, @NotNull TObjectHashingStrategy hashingStrategy) { return new ConcurrentHashMap(initialCapacity, loadFactor, concurrencyLevel, hashingStrategy); @@ -2144,16 +2338,19 @@ public class ContainerUtil extends ContainerUtilRt { }; private static final ConcurrentMapFactory PLATFORM_MAP_FACTORY = new ConcurrentMapFactory() { + @Override @NotNull public ConcurrentMap createMap() { return createMap(16, 0.75f, DEFAULT_CONCURRENCY_LEVEL); } + @Override @NotNull public ConcurrentMap createMap(int initialCapacity) { return new java.util.concurrent.ConcurrentHashMap(initialCapacity); } + @Override @NotNull public ConcurrentMap createMap(@NotNull TObjectHashingStrategy hashingStrategy) { if (hashingStrategy != canonicalStrategy()) { @@ -2163,11 +2360,13 @@ public class ContainerUtil extends ContainerUtilRt { return createMap(); } + @Override @NotNull public ConcurrentMap createMap(int initialCapacity, float loadFactor, int concurrencyLevel) { return new java.util.concurrent.ConcurrentHashMap(initialCapacity, loadFactor, concurrencyLevel); } + @Override @NotNull public ConcurrentMap createMap(int initialCapacity, float loadFactor, int concurrencyLevel, @NotNull TObjectHashingStrategy hashingStrategy) { if (hashingStrategy != canonicalStrategy()) { @@ -2185,7 +2384,8 @@ public class ContainerUtil extends ContainerUtilRt { return StringUtil.compareVersionNumbers(SystemInfo.JAVA_VERSION, "1.7") >= 0; } - public static > int compareLexicographically(List o1, List o2) { + @Contract(pure=true) + public static > int compareLexicographically(@NotNull List o1, @NotNull List o2) { for (int i = 0; i < Math.min(o1.size(), o2.size()); i++) { int result = o1.get(i).compareTo(o2.get(i)); if (result != 0) { @@ -2194,5 +2394,16 @@ public class ContainerUtil extends ContainerUtilRt { } return o1.size() < o2.size() ? -1 : o1.size() == o2.size() ? 0 : 1; } + + @Contract(pure=true) + public static int compareLexicographically(@NotNull List o1, @NotNull List o2, @NotNull Comparator comparator) { + for (int i = 0; i < Math.min(o1.size(), o2.size()); i++) { + int result = comparator.compare(o1.get(i), o2.get(i)); + if (result != 0) { + return result; + } + } + return o1.size() < o2.size() ? -1 : o1.size() == o2.size() ? 0 : 1; + } } diff --git a/platform/util/src/com/intellij/util/containers/FactoryMap.java b/platform/util/src/com/intellij/util/containers/FactoryMap.java index 30684d0e6dfa..3eea4d3dd984 100644 --- a/platform/util/src/com/intellij/util/containers/FactoryMap.java +++ b/platform/util/src/com/intellij/util/containers/FactoryMap.java @@ -15,6 +15,7 @@ */ package com.intellij.util.containers; +import com.intellij.util.ObjectUtils; import gnu.trove.THashMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -92,7 +93,7 @@ public abstract class FactoryMap implements Map { public Collection notNullValues() { if (myMap == null) return Collections.emptyList(); - final Collection values = new ArrayList(myMap.values()); + final Collection values = ContainerUtil.newArrayList(myMap.values()); for (Iterator iterator = values.iterator(); iterator.hasNext();) { if (iterator.next() == NULL) { iterator.remove(); @@ -101,6 +102,13 @@ public abstract class FactoryMap implements Map { return values; } + public boolean removeValue(Object value) { + if (myMap == null) return false; + Object t = ObjectUtils.notNull(value, NULL); + return myMap.values().remove(t); + } + + @Override public void clear() { if (myMap != null) { diff --git a/platform/util/src/com/intellij/util/containers/TransferToEDTQueue.java b/platform/util/src/com/intellij/util/containers/TransferToEDTQueue.java index 5db4de22b401..425947ba50cb 100644 --- a/platform/util/src/com/intellij/util/containers/TransferToEDTQueue.java +++ b/platform/util/src/com/intellij/util/containers/TransferToEDTQueue.java @@ -16,6 +16,7 @@ package com.intellij.util.containers; import com.intellij.openapi.util.Condition; +import com.intellij.openapi.util.Conditions; import com.intellij.util.Processor; import com.intellij.util.concurrency.Semaphore; import gnu.trove.Equality; @@ -80,7 +81,7 @@ public class TransferToEDTQueue { runnable.run(); return true; } - }, Condition.FALSE, maxUnitOfWorkThresholdMs); + }, Conditions.alwaysFalse(), maxUnitOfWorkThresholdMs); } private boolean isEmpty() { diff --git a/platform/util/src/com/intellij/util/io/AppendableStorageBackedByResizableMappedFile.java b/platform/util/src/com/intellij/util/io/AppendableStorageBackedByResizableMappedFile.java index c4381da1a406..1c43b652b02b 100644 --- a/platform/util/src/com/intellij/util/io/AppendableStorageBackedByResizableMappedFile.java +++ b/platform/util/src/com/intellij/util/io/AppendableStorageBackedByResizableMappedFile.java @@ -72,8 +72,7 @@ public class AppendableStorageBackedByResizableMappedFile extends ResizeableMapp } public boolean processAll(Processor processor, KeyDescriptor descriptor) throws IOException { - force(); - + assert !isDirty(); DataInputStream keysStream = new DataInputStream(new BufferedInputStream(new LimitedInputStream(new FileInputStream(getPagedFileStorage().getFile()), myFileLength) { @Override diff --git a/platform/util/src/com/intellij/util/io/PersistentEnumeratorBase.java b/platform/util/src/com/intellij/util/io/PersistentEnumeratorBase.java index 9f879f67d99d..eb7cdef28d69 100644 --- a/platform/util/src/com/intellij/util/io/PersistentEnumeratorBase.java +++ b/platform/util/src/com/intellij/util/io/PersistentEnumeratorBase.java @@ -443,11 +443,13 @@ public abstract class PersistentEnumeratorBase implements Forceable, Close lockStorage(); // todo locking in key storage try { - return myKeyStorage.processAll(processor, myDataDescriptor); + myKeyStorage.force(); } finally { unlockStorage(); } + + return myKeyStorage.processAll(processor, myDataDescriptor); } private File keystreamFile() { diff --git a/platform/util/src/com/intellij/util/messages/impl/MessageListenerList.java b/platform/util/src/com/intellij/util/messages/impl/MessageListenerList.java index d054416846a1..6d7bf76ccde7 100644 --- a/platform/util/src/com/intellij/util/messages/impl/MessageListenerList.java +++ b/platform/util/src/com/intellij/util/messages/impl/MessageListenerList.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,14 +15,14 @@ */ package com.intellij.util.messages.impl; +import com.intellij.openapi.Disposable; +import com.intellij.openapi.util.Disposer; +import com.intellij.util.containers.ConcurrentHashMap; import com.intellij.util.messages.MessageBus; import com.intellij.util.messages.MessageBusConnection; import com.intellij.util.messages.Topic; -import com.intellij.openapi.Disposable; -import com.intellij.openapi.util.Disposer; import org.jetbrains.annotations.NotNull; -import java.util.HashMap; import java.util.Map; /** @@ -31,7 +31,7 @@ import java.util.Map; public class MessageListenerList { private final MessageBus myMessageBus; private final Topic myTopic; - private final Map myListenerToConnectionMap = new HashMap(); + private final Map myListenerToConnectionMap = new ConcurrentHashMap(); public MessageListenerList(@NotNull MessageBus messageBus, @NotNull Topic topic) { myTopic = topic; diff --git a/platform/util/src/com/intellij/util/ui/JBSwingUtilities.java b/platform/util/src/com/intellij/util/ui/JBSwingUtilities.java new file mode 100644 index 000000000000..d48aa5fc4ad2 --- /dev/null +++ b/platform/util/src/com/intellij/util/ui/JBSwingUtilities.java @@ -0,0 +1,53 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.util.ui; + +import java.awt.event.InputEvent; +import java.awt.event.MouseEvent; + +/** + * @author gregsh + */ +public class JBSwingUtilities { + /** + * Replaces SwingUtilities#isLeftMouseButton() for consistency with other button-related methods + * + * @see javax.swing.SwingUtilities#isLeftMouseButton(java.awt.event.MouseEvent) + */ + public static boolean isLeftMouseButton(MouseEvent anEvent) { + return (anEvent.getModifiersEx() & InputEvent.BUTTON1_DOWN_MASK) > 0; + } + + /** + * Replaces SwingUtilities#isMiddleMouseButton() due to the fact that BUTTON2_MASK == Event.ALT_MASK + * + * @see javax.swing.SwingUtilities#isMiddleMouseButton(java.awt.event.MouseEvent) + * @see java.awt.event.InputEvent#BUTTON2_MASK + */ + public static boolean isMiddleMouseButton(MouseEvent anEvent) { + return (anEvent.getModifiersEx() & InputEvent.BUTTON2_DOWN_MASK) > 0; + } + + /** + * Replaces SwingUtilities#isRightMouseButton() due to the fact that BUTTON3_MASK == Event.META_MASK + * + * @see javax.swing.SwingUtilities#isRightMouseButton(java.awt.event.MouseEvent) + * @see java.awt.event.InputEvent#BUTTON3_MASK + */ + public static boolean isRightMouseButton(MouseEvent anEvent) { + return (anEvent.getModifiersEx() & InputEvent.BUTTON3_DOWN_MASK) > 0; + } +} diff --git a/platform/util/src/com/intellij/util/ui/UIUtil.java b/platform/util/src/com/intellij/util/ui/UIUtil.java index aec2a356ded0..28c95b80763c 100644 --- a/platform/util/src/com/intellij/util/ui/UIUtil.java +++ b/platform/util/src/com/intellij/util/ui/UIUtil.java @@ -2085,7 +2085,7 @@ public class UIUtil { * Invoke and wait in the event dispatch thread * or in the current thread if the current thread * is event queue thread. - * + * DO NOT INVOKE THIS METHOD FROM UNDER READ ACTION. * @param runnable a runnable to invoke * @see #invokeAndWaitIfNeeded(com.intellij.util.ThrowableRunnable) */ @@ -2103,6 +2103,14 @@ public class UIUtil { } } + /** + * Invoke and wait in the event dispatch thread + * or in the current thread if the current thread + * is event queue thread. + * DO NOT INVOKE THIS METHOD FROM UNDER READ ACTION. + * @param computable a runnable to invoke + * @see #invokeAndWaitIfNeeded(com.intellij.util.ThrowableRunnable) + */ public static T invokeAndWaitIfNeeded(@NotNull final Computable computable) { final Ref result = Ref.create(); invokeAndWaitIfNeeded(new Runnable() { @@ -2114,6 +2122,14 @@ public class UIUtil { return result.get(); } + /** + * Invoke and wait in the event dispatch thread + * or in the current thread if the current thread + * is event queue thread. + * DO NOT INVOKE THIS METHOD FROM UNDER READ ACTION. + * @param runnable a runnable to invoke + * @see #invokeAndWaitIfNeeded(com.intellij.util.ThrowableRunnable) + */ public static void invokeAndWaitIfNeeded(@NotNull final ThrowableRunnable runnable) throws Throwable { if (SwingUtilities.isEventDispatchThread()) { runnable.run(); @@ -2890,6 +2906,7 @@ public class UIUtil { new Thread(new Runnable() { // The wrapper thread is unnecessary, unless it blocks on the // Clip finishing; see comments. + @Override public void run() { try { Clip clip = AudioSystem.getClip(); diff --git a/platform/util/src/com/intellij/util/ui/tree/WideSelectionTreeUI.java b/platform/util/src/com/intellij/util/ui/tree/WideSelectionTreeUI.java index ba0eb265655e..9775ddb01be1 100644 --- a/platform/util/src/com/intellij/util/ui/tree/WideSelectionTreeUI.java +++ b/platform/util/src/com/intellij/util/ui/tree/WideSelectionTreeUI.java @@ -16,6 +16,7 @@ package com.intellij.util.ui.tree; import com.intellij.openapi.util.Condition; +import com.intellij.openapi.util.Conditions; import com.intellij.openapi.util.SystemInfo; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NonNls; @@ -53,7 +54,7 @@ public class WideSelectionTreeUI extends BasicTreeUI { @SuppressWarnings("unchecked") public WideSelectionTreeUI() { - this(true, Condition.TRUE); + this(true, Conditions.alwaysTrue()); } /** diff --git a/platform/util/testSrc/com/intellij/execution/configurations/CommandLineTokenizerTest.java b/platform/util/testSrc/com/intellij/execution/configurations/CommandLineTokenizerTest.java new file mode 100644 index 000000000000..276ca549eb30 --- /dev/null +++ b/platform/util/testSrc/com/intellij/execution/configurations/CommandLineTokenizerTest.java @@ -0,0 +1,60 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.execution.configurations; + +import junit.framework.TestCase; + +public class CommandLineTokenizerTest extends TestCase { + public void testBasic() throws Exception { + assertTokens("a b", "a", "b"); + assertTokens("\"a b\"", "a b"); + assertTokens("a\" b\"", "a b"); + assertTokens("\"a b", "a b"); + assertTokens("a b\"", "a", "b"); + assertTokens("a b\" c", "a", "b c"); + assertTokens("\"a b\" c \"d e\"", "a b", "c", "d e"); + } + + public void testEscape() throws Exception { + assertTokens("a\\ b", true, "a b"); + assertTokens("a\\ b", false, "a\\", "b"); + + assertTokens("\"a\\ b\"", true, "a\\ b"); + assertTokens("\"a\\ b\"", false, "a\\ b"); + + assertTokens("a\\ ", true, "a "); + assertTokens("a\\ ", false, "a\\"); + + assertTokens("\\\"", "\""); + assertTokens("\"\\\" a \\\"\"", "\" a \""); + assertTokens("\\\"a b\\\"", "\"a", "b\""); + assertTokens("\\\"a\\ b\\\"", true, "\"a b\""); + assertTokens("\\\"a\\ b\\\"", false, "\"a\\", "b\""); + } + + private static void assertTokens(String cmd, String... tokens) { + assertTokens(cmd, false, tokens); + } + + private static void assertTokens(String cmd, boolean handleEscapedWhitespaces, String... tokens) { + CommandLineTokenizer tokenizer = new CommandLineTokenizer(cmd, handleEscapedWhitespaces); + + assertEquals(tokens.length, tokenizer.countTokens()); + for (String token : tokens) { + assertEquals(token, tokenizer.nextToken()); + } + } +} diff --git a/platform/util/testSrc/com/intellij/util/text/StringUtilTest.java b/platform/util/testSrc/com/intellij/util/text/StringUtilTest.java index d2f593d13ff1..93ebb038cae1 100644 --- a/platform/util/testSrc/com/intellij/util/text/StringUtilTest.java +++ b/platform/util/testSrc/com/intellij/util/text/StringUtilTest.java @@ -111,7 +111,7 @@ public class StringUtilTest extends TestCase { public void testFormatLinks() { assertEquals("http://a-b+c", StringUtil.formatLinks("http://a-b+c")); } - + public void testCopyHeapCharBuffer() { String s = "abcde"; CharBuffer buffer = CharBuffer.allocate(s.length()); @@ -124,7 +124,7 @@ public class StringUtilTest extends TestCase { assertNull(CharArrayUtil.fromSequenceWithoutCopying(buffer.subSequence(1, 5))); assertNull(CharArrayUtil.fromSequenceWithoutCopying(buffer.subSequence(1, 2))); } - + public void testTitleCase() { assertEquals("Couldn't Connect to Debugger", StringUtil.wordsToBeginFromUpperCase("Couldn't connect to debugger")); } @@ -198,7 +198,7 @@ public class StringUtilTest extends TestCase { assertEquals(Arrays.asList("aa"), Arrays.asList(StringUtil.splitByLinesKeepSeparators("aa"))); assertEquals(Arrays.asList("\n", "\n", "aa\n", "\n", "bb\n", "cc\n", "\n"), Arrays.asList(StringUtil.splitByLinesKeepSeparators("\n\naa\n\nbb\ncc\n\n"))); - + assertEquals(Arrays.asList("\r", "\r\n", "\r"), Arrays.asList(StringUtil.splitByLinesKeepSeparators("\r\r\n\r"))); assertEquals(Arrays.asList("\r\n", "\r", "\r\n"), Arrays.asList(StringUtil.splitByLinesKeepSeparators("\r\n\r\r\n"))); @@ -224,4 +224,13 @@ public class StringUtilTest extends TestCase { } } } + + public void testReplaceReturnReplacementIfTextEqualsToReplacedText() { + String newS = "/tmp"; + assertSame(StringUtil.replace("$PROJECT_FILE$", "$PROJECT_FILE$".toLowerCase().toUpperCase() /* ensure new String instance */, newS), newS); + } + + public void testReplace() { + assertEquals(StringUtil.replace("$PROJECT_FILE$/filename", "$PROJECT_FILE$", "/tmp"), "/tmp/filename"); + } } diff --git a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/dir/FrameDialogWrapper.java b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/dir/FrameDialogWrapper.java new file mode 100644 index 000000000000..f89e28ea8b6b --- /dev/null +++ b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/dir/FrameDialogWrapper.java @@ -0,0 +1,135 @@ +package com.intellij.openapi.diff.impl.dir; + +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.ui.FrameWrapper; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; + +public abstract class FrameDialogWrapper { + public enum Mode {FRAME, MODAL, NON_MODAL} + + @NotNull + protected abstract JComponent getPanel(); + + @Nullable + protected String getDimensionServiceKey() { + return null; + } + + @Nullable + protected JComponent getPreferredFocusedComponent() { + return null; + } + + @Nullable + protected String getTitle() { + return null; + } + + @Nullable + protected Project getProject() { + return null; + } + + @NotNull + protected Mode getMode() { + return Mode.MODAL; + } + + public void show() { + switch (getMode()) { + case FRAME: + new MyFrameWrapper(getProject(), getMode(), getPanel(), getPreferredFocusedComponent(), getTitle(), getDimensionServiceKey()) + .show(); + return; + case MODAL: + case NON_MODAL: + new MyDialogWrapper(getProject(), getMode(), getPanel(), getPreferredFocusedComponent(), getTitle(), getDimensionServiceKey()) + .show(); + return; + default: + throw new IllegalArgumentException(getMode().toString()); + } + } + + private static class MyDialogWrapper extends DialogWrapper { + private final JComponent myComponent; + private final JComponent myPreferredFocusedComponent; + private final String myDimensionServiceKey; + + public MyDialogWrapper(@Nullable Project project, + @NotNull Mode mode, + @NotNull JComponent component, + @Nullable JComponent preferredFocusedComponent, + @Nullable String title, + @Nullable String dimensionServiceKey) { + super(project, true); + myComponent = component; + myPreferredFocusedComponent = preferredFocusedComponent; + myDimensionServiceKey = dimensionServiceKey; + + if (title != null) { + setTitle(title); + } + switch (mode) { + case MODAL: + setModal(true); + break; + case NON_MODAL: + setModal(false); + break; + default: + throw new IllegalArgumentException(mode.toString()); + } + + init(); + } + + @Override + protected JComponent createCenterPanel() { + return myComponent; + } + + @Nullable + @Override + public JComponent getPreferredFocusedComponent() { + return myPreferredFocusedComponent; + } + + @Nullable + @Override + protected String getDimensionServiceKey() { + return myDimensionServiceKey; + } + + // it is information dialog - no need to OK or Cancel. Close the dialog by clicking the cross button or pressing Esc. + @NotNull + @Override + protected Action[] createActions() { + return new Action[0]; + } + } + + private static class MyFrameWrapper extends FrameWrapper { + public MyFrameWrapper(@Nullable Project project, + @NotNull Mode mode, + @NotNull JComponent component, + @Nullable JComponent preferredFocusedComponent, + @Nullable String title, + @Nullable String dimensionServiceKey) { + super(project, dimensionServiceKey); + + assert mode == Mode.FRAME; + + if (title != null) { + setTitle(title); + } + setComponent(component); + setPreferredFocusedComponent(preferredFocusedComponent); + closeOnEsc(); + } + } +} diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotateToggleAction.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotateToggleAction.java index 291289854fa7..a347531b2363 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotateToggleAction.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotateToggleAction.java @@ -205,6 +205,7 @@ public class AnnotateToggleAction extends ToggleAction implements DumbAware, Ann handler.completed(file.getPath()); if (!exceptionRef.isNull()) { + LOG.warn(exceptionRef.get()); AbstractVcsHelper.getInstance(project).showErrors(Arrays.asList(exceptionRef.get()), VcsBundle.message("message.title.annotate")); } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangesFragmentedDiffPanel.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangesFragmentedDiffPanel.java index 53092e6a1369..6afbc181b98c 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangesFragmentedDiffPanel.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangesFragmentedDiffPanel.java @@ -187,7 +187,7 @@ public class ChangesFragmentedDiffPanel implements Disposable { current = range; } if (current == null) return null; - final Fragment at = fragments.getFragmentAt(offset, FragmentSide.SIDE1, Condition.TRUE); + final Fragment at = fragments.getFragmentAt(offset, FragmentSide.SIDE1, Conditions.alwaysTrue()); if (at == null) return null; final TextRange opposite = at.getRange(FragmentSide.SIDE2); diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsDirtyScopeManagerImpl.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsDirtyScopeManagerImpl.java index 6e44a7864b3d..0e38675a8239 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsDirtyScopeManagerImpl.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsDirtyScopeManagerImpl.java @@ -79,7 +79,11 @@ public class VcsDirtyScopeManagerImpl extends VcsDirtyScopeManager implements Pr StartupManager.getInstance(myProject).registerPostStartupActivity(new DumbAwareRunnable() { public void run() { myLife.born(); - markEverythingDirty(); + ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { + public void run() { + markEverythingDirty(); + } + }); } }); } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/committed/CommittedChangesCache.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/committed/CommittedChangesCache.java index 6619b98ea5e9..2708f5130040 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/committed/CommittedChangesCache.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/committed/CommittedChangesCache.java @@ -21,7 +21,10 @@ import com.intellij.openapi.Disposable; import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ModalityState; -import com.intellij.openapi.components.*; +import com.intellij.openapi.components.PersistentStateComponent; +import com.intellij.openapi.components.State; +import com.intellij.openapi.components.Storage; +import com.intellij.openapi.components.StoragePathMacros; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.progress.ProgressManagerQueue; @@ -64,12 +67,8 @@ import java.util.concurrent.TimeUnit; * @author yole */ @State( - name="CommittedChangesCache", - roamingType = RoamingType.DISABLED, - storages= { - @Storage( - file = StoragePathMacros.WORKSPACE_FILE - )} + name = "CommittedChangesCache", + storages = {@Storage(file = StoragePathMacros.WORKSPACE_FILE)} ) public class CommittedChangesCache implements PersistentStateComponent { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.vcs.changes.committed.CommittedChangesCache"); diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/CommitChangeListDialog.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/CommitChangeListDialog.java index 497db3da7d9f..47fae2b88339 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/CommitChangeListDialog.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/CommitChangeListDialog.java @@ -175,6 +175,11 @@ public class CommitChangeListDialog extends DialogWrapper implements CheckinProj ProjectLevelVcsManager.getInstance(project).getAllActiveVcss()); } + @NotNull + public List getAdditionalComponents() { + return Collections.unmodifiableList(myAdditionalComponents); + } + public static void commitPaths(final Project project, Collection paths, final LocalChangeList initialSelection, @Nullable final CommitExecutor executor, final String comment) { final ChangeListManager manager = ChangeListManager.getInstance(project); diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/VcsManagerConfigurableProvider.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/VcsManagerConfigurableProvider.java new file mode 100644 index 000000000000..0ff9ef6c96d6 --- /dev/null +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/VcsManagerConfigurableProvider.java @@ -0,0 +1,41 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.openapi.vcs.configurable; + +import com.intellij.openapi.options.Configurable; +import com.intellij.openapi.options.ConfigurableProvider; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vcs.ProjectLevelVcsManager; +import org.jetbrains.annotations.NotNull; + +public final class VcsManagerConfigurableProvider extends ConfigurableProvider { + private final Project myProject; + + public VcsManagerConfigurableProvider(Project project) { + myProject = project; + } + + @NotNull + @Override + public Configurable createConfigurable() { + return new VcsManagerConfigurable(myProject); + } + + @Override + public boolean canCreateConfigurable() { + return ProjectLevelVcsManager.getInstance(myProject).getAllVcss().length > 0; + } +} diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/LineStatusTracker.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/LineStatusTracker.java index daef43857c4e..abc43dd3a34d 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/LineStatusTracker.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/LineStatusTracker.java @@ -295,6 +295,23 @@ public class LineStatusTracker { } } + private void markFileUnchanged() { + ApplicationManager.getApplication().invokeLater(new Runnable() { + @Override + public void run() { + FileDocumentManager.getInstance().saveDocument(myDocument); + boolean stillEmpty; + synchronized (myLock) { + stillEmpty = myRanges.isEmpty(); + } + if (stillEmpty) { + // file was modified, and now it's not -> dirty local change + myVcsDirtyScopeManager.fileDirty(myVirtualFile); + } + } + }); + } + private class MyDocumentListener extends DocumentAdapter { // We have 3 document versions: // * VCS version @@ -457,20 +474,7 @@ public class LineStatusTracker { } if (myRanges.isEmpty() && myVirtualFile != null) { - ApplicationManager.getApplication().invokeLater(new Runnable() { - @Override - public void run() { - FileDocumentManager.getInstance().saveDocument(myDocument); - boolean stillEmpty; - synchronized (myLock) { - stillEmpty = myRanges.isEmpty(); - } - if (stillEmpty) { - // file was modified, and now it's not -> dirty local change - myVcsDirtyScopeManager.fileDirty(myVirtualFile); - } - } - }); + markFileUnchanged(); } } } @@ -764,15 +768,10 @@ public class LineStatusTracker { } } - public void rollbackChanges(@NotNull BitSet lines) { - myApplication.assertWriteAccessAllowed(); - - synchronized (myLock) { - if (myBulkUpdate) return; - - try { - mySuppressUpdate = true; - + public void rollbackChanges(@NotNull final BitSet lines) { + runBulkRollback(new Runnable() { + @Override + public void run() { Range first = null; Range last = null; @@ -816,11 +815,41 @@ public class LineStatusTracker { doUpdateRanges(beforeChangedLine1, beforeChangedLine2, shift, beforeTotalLines); } } - catch (Throwable e) { + }); + } + + public void rollbackAllChanges() { + runBulkRollback(new Runnable() { + @Override + public void run() { + myDocument.setText(myVcsDocument.getText()); + + removeAnathema(); + removeHighlightersFromMarkupModel(); + + markFileUnchanged(); + } + }); + } + + private void runBulkRollback(@NotNull Runnable task) { + myApplication.assertWriteAccessAllowed(); + + synchronized (myLock) { + if (myBulkUpdate) return; + + try { + mySuppressUpdate = true; + + task.run(); + } + catch (Error e) { + reinstallRanges(); + throw e; + } + catch (RuntimeException e) { reinstallRanges(); - if (e instanceof Error) throw ((Error)e); - if (e instanceof RuntimeException) throw ((RuntimeException)e); - throw new RuntimeException(e); + throw e; } finally { mySuppressUpdate = false; diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/RollbackLineStatusAction.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/RollbackLineStatusAction.java index 1286b6815fcf..529da7371bf7 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/RollbackLineStatusAction.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/ex/RollbackLineStatusAction.java @@ -12,8 +12,6 @@ */ package com.intellij.openapi.vcs.ex; -import com.intellij.icons.AllIcons; -import com.intellij.idea.ActionsBundle; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.application.ApplicationManager; @@ -73,6 +71,15 @@ public class RollbackLineStatusAction extends DumbAwareAction { BitSet lines = new BitSet(totalLines + 1); List carets = editor.getCaretModel().getAllCarets(); + + if (carets.size() == 1) { + Caret caret = carets.get(0); + if (caret.getSelectionStart() == 0 && caret.getSelectionEnd() == document.getTextLength()) { + doRollback(tracker); + return; + } + } + for (Caret caret : carets) { if (caret.hasSelection()) { int line1 = editor.offsetToLogicalPosition(caret.getSelectionStart()).line; @@ -107,6 +114,15 @@ public class RollbackLineStatusAction extends DumbAwareAction { }); } + private static void doRollback(@NotNull final LineStatusTracker tracker) { + execute(tracker, new Runnable() { + @Override + public void run() { + tracker.rollbackAllChanges(); + } + }); + } + private static void execute(@NotNull final LineStatusTracker tracker, @NotNull final Runnable task) { CommandProcessor.getInstance().executeCommand(tracker.getProject(), new Runnable() { public void run() { diff --git a/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogProvider.java b/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogProvider.java index 89805947869c..d9c72314edbc 100644 --- a/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogProvider.java +++ b/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogProvider.java @@ -10,6 +10,7 @@ import org.jetbrains.annotations.Nullable; import java.util.Collection; import java.util.List; +import java.util.Set; /** * Provides the information needed to build the VCS log, such as the list of most recent commits with their parents. @@ -17,20 +18,26 @@ import java.util.List; public interface VcsLogProvider { /** - * Reads the most recent correctly ordered commits from the log.
- * Commits should be at least topologically ordered, better considering commit time as well.
- * Commits will be shown in the log in this order. - * @param requirements some limitations on commit data that should be returned. + * Reads the most recent commits from the log together with all repository references.
+ * Commits should be at least topologically ordered, better considering commit time as well: they will be shown in the log in this order. + *

+ * This method is called both on the startup and on refresh. + * + * @param requirements some limitations on commit data that should be returned, e.g. the number of commits. + * @return given amount of ordered commits and all references in the repository. */ @NotNull - List readFirstBlock(@NotNull VirtualFile root, @NotNull Requirements requirements) throws VcsException; + DetailedLogData readFirstBlock(@NotNull VirtualFile root, @NotNull Requirements requirements) throws VcsException; /** - *

Reads the whole history, but only hashes & parents.

- *

Also reports authors/committers of this repository to the given user registry.

+ * Reads the whole history. + *

+ * Reports commits to the consumer to avoid creation & even temporary storage of a too large commits collection. + * + * @return all references and all authors in the repository. */ - void readAllHashes(@NotNull VirtualFile root, @NotNull Consumer userRegistry, - @NotNull Consumer commitConsumer) throws VcsException; + @NotNull + LogData readAllHashes(@NotNull VirtualFile root, @NotNull Consumer commitConsumer) throws VcsException; /** * Reads those details of the given commits, which are necessary to be shown in the log table. @@ -44,12 +51,6 @@ public interface VcsLogProvider { @NotNull List readFullDetails(@NotNull VirtualFile root, @NotNull List hashes) throws VcsException; - /** - * Read all references (branches, tags, etc.) for the given roots. - */ - @NotNull - Collection readAllRefs(@NotNull VirtualFile root) throws VcsException; - /** *

Returns the VCS which is supported by this provider.

*

If there will be several VcsLogProviders which support the same VCS, only one will be chosen. It is undefined, which one.

@@ -106,4 +107,20 @@ public interface VcsLogProvider { } + /** + * Container for references and users. + */ + interface LogData { + @NotNull Set getRefs(); + @NotNull Set getUsers(); + } + + /** + * Container for the ordered list of commits together with their details, and references. + */ + interface DetailedLogData { + @NotNull List getCommits(); + @NotNull Set getRefs(); + } + } diff --git a/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogProviderRequirementsEx.java b/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogProviderRequirementsEx.java index d69579ac00b1..68623a6852f9 100644 --- a/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogProviderRequirementsEx.java +++ b/platform/vcs-log/api/src/com/intellij/vcs/log/VcsLogProviderRequirementsEx.java @@ -15,7 +15,6 @@ */ package com.intellij.vcs.log; -import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.NotNull; import java.util.Set; @@ -24,7 +23,7 @@ import static com.intellij.vcs.log.VcsLogProvider.Requirements; /** * Extension of the standard {@link Requirements} which contains data used by some VCSs.
- * An object of this object is actually passed to {@link VcsLogProvider#readFirstBlock(VirtualFile, Requirements)}, but VcsLogProviders + * An object of this object is actually passed to {@link #readFirstBlock(com.intellij.util.Consumer}, but VcsLogProviders * which need this additional information must check for instanceof before casting & be able to fallback. */ public interface VcsLogProviderRequirementsEx extends Requirements { @@ -41,10 +40,4 @@ public interface VcsLogProviderRequirementsEx extends Requirements { @NotNull Set getPreviousRefs(); - /** - * Returns current refs. - */ - @NotNull - Set getCurrentRefs(); - } diff --git a/platform/vcs-log/api/vcs-log-api.iml b/platform/vcs-log/api/vcs-log-api.iml index 92d746615afa..a9a76e60b302 100644 --- a/platform/vcs-log/api/vcs-log-api.iml +++ b/platform/vcs-log/api/vcs-log-api.iml @@ -10,7 +10,6 @@ - diff --git a/platform/vcs-log/graph-api/src/com/intellij/vcs/log/graph/RowInfo.java b/platform/vcs-log/graph-api/src/com/intellij/vcs/log/graph/RowInfo.java index 3855467bffad..15a6cb07895d 100644 --- a/platform/vcs-log/graph-api/src/com/intellij/vcs/log/graph/RowInfo.java +++ b/platform/vcs-log/graph-api/src/com/intellij/vcs/log/graph/RowInfo.java @@ -32,4 +32,7 @@ public interface RowInfo { @NotNull Collection getPrintElements(); + @NotNull + RowType getRowType(); + } diff --git a/platform/vcs-log/graph-api/src/com/intellij/vcs/log/graph/RowType.java b/platform/vcs-log/graph-api/src/com/intellij/vcs/log/graph/RowType.java new file mode 100644 index 000000000000..b1deca566e0d --- /dev/null +++ b/platform/vcs-log/graph-api/src/com/intellij/vcs/log/graph/RowType.java @@ -0,0 +1,27 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.vcs.log.graph; + +public enum RowType { + + NORMAL, + + /** + * Indicates that the commit on this row doesn't match current filters. + */ + UNMATCHED + +} diff --git a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/facade/AbstractVisibleGraph.java b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/facade/AbstractVisibleGraph.java index 83cb53fd8c8f..315371192065 100644 --- a/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/facade/AbstractVisibleGraph.java +++ b/platform/vcs-log/graph/src/com/intellij/vcs/log/graph/impl/facade/AbstractVisibleGraph.java @@ -105,6 +105,12 @@ public abstract class AbstractVisibleGraph implements VisibleGraph getPrintElements() { return myPrintElementGenerator.getPrintElements(visibleRow); } + + @NotNull + @Override + public RowType getRowType() { + return RowType.NORMAL; + } }; } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/DataPack.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/DataPack.java index 48b25be01ab9..975808611ed4 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/DataPack.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/DataPack.java @@ -26,16 +26,14 @@ public class DataPack implements VcsLogDataPack { private boolean myFull; @NotNull - static DataPack build(@NotNull List> commits, @NotNull RefsModel refsModel, - @NotNull NotNullFunction indexGetter, @NotNull NotNullFunction hashGetter, - @NotNull Map providers, boolean full) { - return build(buildPermanentGraph(commits, refsModel, indexGetter, hashGetter, providers), providers, refsModel, full); - } - - @NotNull - static DataPack build(@NotNull PermanentGraph permanentGraph, @NotNull Map providers, - @NotNull RefsModel refsModel, boolean full) { - return new DataPack(refsModel, permanentGraph, createGraphFacade(permanentGraph), providers, full); + static DataPack build(@NotNull List> commits, + @NotNull Map> refs, + @NotNull Map providers, + @NotNull VcsLogHashMap hashMap, + boolean full) { + RefsModel refsModel = new RefsModel(refs, hashMap.asIndexGetter()); + PermanentGraph graph = buildPermanentGraph(commits, refsModel, hashMap.asIndexGetter(), hashMap.asHashGetter(), providers); + return new DataPack(refsModel, graph, createGraphFacade(graph), providers, full); } @NotNull @@ -57,11 +55,11 @@ public class DataPack implements VcsLogDataPack { } @NotNull - static PermanentGraph buildPermanentGraph(@NotNull List> commits, - @NotNull RefsModel refsModel, - @NotNull NotNullFunction indexGetter, - @NotNull NotNullFunction hashGetter, - @NotNull Map providers) { + private static PermanentGraph buildPermanentGraph(@NotNull List> commits, + @NotNull RefsModel refsModel, + @NotNull NotNullFunction indexGetter, + @NotNull NotNullFunction hashGetter, + @NotNull Map providers) { if (commits.isEmpty()) { return EmptyPermanentGraph.getInstance(); } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/EmptyDataPack.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/EmptyDataPack.java index 6262d767d071..54140afb3940 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/EmptyDataPack.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/EmptyDataPack.java @@ -22,14 +22,14 @@ import com.intellij.vcs.log.VcsLogProvider; import com.intellij.vcs.log.VcsRef; import org.jetbrains.annotations.NotNull; -import java.util.Collection; import java.util.Collections; +import java.util.Set; public class EmptyDataPack { @NotNull public static DataPack getInstance() { - RefsModel emptyModel = new RefsModel(Collections.>emptyMap(), new ConstantFunction(0)); + RefsModel emptyModel = new RefsModel(Collections.>emptyMap(), new ConstantFunction(0)); return new DataPack(emptyModel, EmptyPermanentGraph.getInstance(), new EmptyGraphFacade(), Collections.emptyMap(), false); } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/EmptyVisibleGraph.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/EmptyVisibleGraph.java index 4bdd38936361..f52b37d1f021 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/EmptyVisibleGraph.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/EmptyVisibleGraph.java @@ -17,6 +17,7 @@ package com.intellij.vcs.log.data; import com.intellij.vcs.log.graph.PrintElement; import com.intellij.vcs.log.graph.RowInfo; +import com.intellij.vcs.log.graph.RowType; import com.intellij.vcs.log.graph.VisibleGraph; import com.intellij.vcs.log.graph.actions.ActionController; import com.intellij.vcs.log.graph.actions.GraphAnswer; @@ -120,5 +121,11 @@ class EmptyVisibleGraph implements VisibleGraph { public Collection getPrintElements() { return Collections.emptyList(); } + + @NotNull + @Override + public RowType getRowType() { + return RowType.NORMAL; + } } } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/RefsModel.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/RefsModel.java index 46504b338970..c38e4c315ff0 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/RefsModel.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/RefsModel.java @@ -16,14 +16,14 @@ import java.util.*; public class RefsModel implements VcsLogRefs { - @NotNull private final Map> myRefs; + @NotNull private final Map> myRefs; @NotNull private final NotNullFunction myIndexGetter; @NotNull private final Collection myBranches; @NotNull private final MultiMap myRefsToHashes; @NotNull private final TIntObjectHashMap> myRefsToIndices; - public RefsModel(@NotNull Map> refsByRoot, @NotNull NotNullFunction indexGetter) { + public RefsModel(@NotNull Map> refsByRoot, @NotNull NotNullFunction indexGetter) { myRefs = refsByRoot; myIndexGetter = indexGetter; @@ -85,7 +85,7 @@ public class RefsModel implements VcsLogRefs { } @NotNull - public Map> getAllRefsByRoot() { + public Map> getAllRefsByRoot() { return myRefs; } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogMultiRepoJoiner.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogMultiRepoJoiner.java index 072e0e26ace1..f36b26a74d94 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogMultiRepoJoiner.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogMultiRepoJoiner.java @@ -6,31 +6,31 @@ import org.jetbrains.annotations.NotNull; import java.util.*; -public class VcsLogMultiRepoJoiner { +public class VcsLogMultiRepoJoiner> { @NotNull - public List> join(@NotNull Collection>> logsFromRepos) { + public List join(@NotNull Collection> logsFromRepos) { if (logsFromRepos.size() == 1) { return logsFromRepos.iterator().next(); } int size = 0; - for (List> repo : logsFromRepos) { + for (List repo : logsFromRepos) { size += repo.size(); } - List> result = new ArrayList>(size); + List result = new ArrayList(size); - Map, Iterator>> nextCommits = ContainerUtil.newHashMap(); - for (List> log : logsFromRepos) { - Iterator> iterator = log.iterator(); + Map> nextCommits = ContainerUtil.newHashMap(); + for (List log : logsFromRepos) { + Iterator iterator = log.iterator(); if (iterator.hasNext()) { nextCommits.put(iterator.next(), iterator); } } while (!nextCommits.isEmpty()) { - GraphCommit lastCommit = findLatestCommit(nextCommits.keySet()); - Iterator> iterator = nextCommits.get(lastCommit); + Commit lastCommit = findLatestCommit(nextCommits.keySet()); + Iterator iterator = nextCommits.get(lastCommit); result.add(lastCommit); nextCommits.remove(lastCommit); @@ -43,10 +43,10 @@ public class VcsLogMultiRepoJoiner { } @NotNull - private GraphCommit findLatestCommit(@NotNull Set> commits) { + private Commit findLatestCommit(@NotNull Set commits) { long maxTimeStamp = Long.MIN_VALUE; - GraphCommit lastCommit = null; - for (GraphCommit commit : commits) { + Commit lastCommit = null; + for (Commit commit : commits) { if (commit.getTimestamp() >= maxTimeStamp) { maxTimeStamp = commit.getTimestamp(); lastCommit = commit; diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogRefresherImpl.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogRefresherImpl.java index c7ab5e324b3d..09de51c722bf 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogRefresherImpl.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogRefresherImpl.java @@ -27,6 +27,7 @@ import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.Consumer; import com.intellij.util.Function; +import com.intellij.util.NotNullFunction; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.ui.UIUtil; import com.intellij.vcs.log.*; @@ -62,7 +63,8 @@ public class VcsLogRefresherImpl implements VcsLogRefresher { @NotNull final VcsUserRegistryImpl userRegistry, @NotNull Map topCommitsDetailsCache, @NotNull final Consumer dataPackUpdateHandler, - @NotNull Consumer exceptionHandler, int recentCommitsCount) { + @NotNull Consumer exceptionHandler, + int recentCommitsCount) { myProject = project; myHashMap = hashMap; myProviders = providers; @@ -95,16 +97,11 @@ public class VcsLogRefresherImpl implements VcsLogRefresher { @Override public DataPack readFirstBlock() { try { - Map> refs = loadRefsFromVcs(myProviders); - Set roots = myProviders.keySet(); - Map requirements = prepareSimpleRequirements(roots, myRecentCommitCount); - Map>> commits = loadRecentCommitsFromVcs(myProviders, requirements, - myUserRegistry, myTopCommitsDetailsCache, - myHashMap); - List> compoundLog = compound(commits.values()); - DataPack dataPack = DataPack.build(compoundLog, new RefsModel(refs, myHashMap.asIndexGetter()), - myHashMap.asIndexGetter(), myHashMap.asHashGetter(), myProviders, false); - mySingleTaskController.request(RefreshRequest.RELOAD_ALL); // build/rebuild the full log in bg + LogInfo data = loadRecentData(new CommitCountRequirements(myRecentCommitCount).asMap(myProviders.keySet())); + Collection>> commits = data.getCommits(); + Map> refs = data.getRefs(); + DataPack dataPack = DataPack.build(multiRepoJoin(commits), refs, myProviders, myHashMap, false); + mySingleTaskController.request(RefreshRequest.RELOAD_ALL); // build/rebuild the full log in background return dataPack; } catch (VcsException e) { @@ -113,109 +110,77 @@ public class VcsLogRefresherImpl implements VcsLogRefresher { } } - @Override - public void refresh(@NotNull Collection rootsToRefresh) { - if (!rootsToRefresh.isEmpty()) { - mySingleTaskController.request(new RefreshRequest(rootsToRefresh)); - } - } - @NotNull - private static Map prepareSimpleRequirements(@NotNull Collection roots, - final int commitCount) { - final VcsLogProvider.Requirements requirements = new VcsLogProvider.Requirements() { - @Override - public int getCommitCount() { - return commitCount; - } - }; - return ContainerUtil.map2Map(roots, new Function>() { - @Override - public Pair fun(VirtualFile file) { - return Pair.create(file, requirements); - } - }); - } - - @NotNull - private static Map> loadRefsFromVcs(@NotNull Map providers) - throws VcsException { - final StopWatch sw = StopWatch.start("loading refs"); - final Map> refs = ContainerUtil.newHashMap(); + private LogInfo loadRecentData(@NotNull final Map requirements) throws VcsException { + final StopWatch sw = StopWatch.start("loading commits"); + final LogInfo logInfo = new LogInfo(); new ProviderIterator() { @Override - void each(@NotNull VirtualFile root, @NotNull VcsLogProvider provider) throws VcsException { - refs.put(root, provider.readAllRefs(root)); + public void each(@NotNull VirtualFile root, @NotNull VcsLogProvider provider) throws VcsException { + VcsLogProvider.DetailedLogData data = provider.readFirstBlock(root, requirements.get(root)); + storeUsersAndDetails(data.getCommits()); + logInfo.put(root, compactCommits(data.getCommits())); + logInfo.put(root, data.getRefs()); sw.rootCompleted(root); } - }.iterate(providers); + }.iterate(getProvidersForRoots(requirements.keySet())); + myUserRegistry.flush(); sw.report(); - return refs; + return logInfo; } @NotNull - private static Map>> loadRecentCommitsFromVcs( - @NotNull Map providers, - @NotNull final Map requirements, - @NotNull final VcsUserRegistryImpl userRegistry, - @NotNull final Map topCommitsDetailsCache, - @NotNull final VcsLogHashMap hashMap) throws VcsException - { - final StopWatch sw = StopWatch.start("loading commits"); - final Map>> commits = ContainerUtil.newHashMap(); - new ProviderIterator() { - @Override - public void each(@NotNull VirtualFile root, @NotNull VcsLogProvider provider) throws VcsException { - List metadatas = provider.readFirstBlock(root, requirements.get(root)); - storeUsersAndDetails(metadatas, userRegistry, topCommitsDetailsCache); - commits.put(root, compactCommits(metadatas, hashMap)); - sw.rootCompleted(root); - } - }.iterate(providers); - userRegistry.flush(); - sw.report(); - return commits; + private Map getProvidersForRoots(@NotNull Set roots) { + return ContainerUtil.map2Map(roots, + new Function>() { + @Override + public Pair fun(VirtualFile root) { + return Pair.create(root, myProviders.get(root)); + } + }); + } + + @Override + public void refresh(@NotNull Collection rootsToRefresh) { + if (!rootsToRefresh.isEmpty()) { + mySingleTaskController.request(new RefreshRequest(rootsToRefresh)); + } } - /** - * Compounds logs from different repositories into a single multi-repository log. - */ @NotNull - private static List> compound(@NotNull Collection>> commits) { + private static > List multiRepoJoin(@NotNull Collection> commits) { StopWatch sw = StopWatch.start("multi-repo join"); - List> joined = new VcsLogMultiRepoJoiner().join(commits); + List joined = new VcsLogMultiRepoJoiner().join(commits); sw.report(); return joined; } @NotNull - private static List> compactCommits(@NotNull List commits, - @NotNull final VcsLogHashMap hashMap) { + private List> compactCommits(@NotNull List commits) { StopWatch sw = StopWatch.start("compacting commits"); List> map = ContainerUtil.map(commits, new Function>() { @NotNull @Override public GraphCommit fun(@NotNull TimedVcsCommit commit) { - return compactCommit(commit, hashMap); + return compactCommit(commit); } }); - hashMap.flush(); + myHashMap.flush(); sw.report(); return map; } @NotNull - private static GraphCommitImpl compactCommit(@NotNull TimedVcsCommit commit, @NotNull VcsLogHashMap hashMap) { - return new GraphCommitImpl(hashMap.getCommitIndex(commit.getId()), - ContainerUtil.map(commit.getParents(), hashMap.asIndexGetter()), commit.getTimestamp()); + private GraphCommitImpl compactCommit(@NotNull TimedVcsCommit commit) { + return new GraphCommitImpl(myHashMap.getCommitIndex(commit.getId()), + ContainerUtil.map(commit.getParents(), myHashMap.asIndexGetter()), commit.getTimestamp()); } - private static void storeUsersAndDetails(@NotNull List metadatas, @NotNull VcsUserRegistryImpl userRegistry, - @NotNull Map topCommitsDetailsCache) { + private void storeUsersAndDetails(@NotNull Collection metadatas) { for (VcsCommitMetadata detail : metadatas) { - userRegistry.addUser(detail.getAuthor()); - userRegistry.addUser(detail.getCommitter()); - topCommitsDetailsCache.put(detail.getId(), detail); + myUserRegistry.addUser(detail.getAuthor()); + myUserRegistry.addUser(detail.getCommitter()); + myTopCommitsDetailsCache.put(detail.getId(), detail); } } @@ -223,17 +188,7 @@ public class VcsLogRefresherImpl implements VcsLogRefresher { @NotNull private DataPack myCurrentDataPack; - // collects loaded info from different roots, which refresh was requested consecutively within a single task - private final Map myLoadedInfos = ContainerUtil.newHashMap(); - - private class LogAndRefs { - List> log; - Collection refs; - LogAndRefs(Collection refs, List> commits) { - this.refs = refs; - this.log = commits; - } - } + @NotNull private final LogInfo myLoadedInfo = new LogInfo(); MyRefreshTask(@NotNull DataPack currentDataPack) { super(VcsLogRefresherImpl.this.myProject, "Refreshing history...", false); @@ -271,30 +226,28 @@ public class VcsLogRefresherImpl implements VcsLogRefresher { private DataPack doRefresh(@NotNull Collection roots) { StopWatch sw = StopWatch.start("refresh"); PermanentGraph permanentGraph = myCurrentDataPack.isFull() ? myCurrentDataPack.getPermanentGraph() : null; - Map> currentRefs = myCurrentDataPack.getRefsModel().getAllRefsByRoot(); + Map> currentRefs = myCurrentDataPack.getRefsModel().getAllRefsByRoot(); try { if (permanentGraph != null) { int commitCount = myRecentCommitCount; for (int attempt = 0; attempt <= 1; attempt++) { loadLogAndRefs(roots, currentRefs, commitCount); - List> compoundLog = compoundLoadedLogs(myLoadedInfos.values()); - Map> allNewRefs = getAllNewRefs(myLoadedInfos, currentRefs); + List> compoundLog = multiRepoJoin(myLoadedInfo.getCommits()); + Map> allNewRefs = getAllNewRefs(myLoadedInfo, currentRefs); List> joinedFullLog = join(compoundLog, permanentGraph.getAllCommits(), currentRefs, allNewRefs); if (joinedFullLog == null) { commitCount *= 5; } else { - return DataPack.build(joinedFullLog, new RefsModel(allNewRefs, myHashMap.asIndexGetter()), - myHashMap.asIndexGetter(), myHashMap.asHashGetter(), myProviders, true); + return DataPack.build(joinedFullLog, allNewRefs, myProviders, myHashMap, true); } } // couldn't join => need to reload everything; if 5000 commits is still not enough, it's worth reporting: LOG.error("Couldn't join " + commitCount + " recent commits to the log (" + permanentGraph.getAllCommits().size() + " commits)", - new Attachment("recent_commits", toLogString(myLoadedInfos))); + new Attachment("recent_commits", myLoadedInfo.toLogString(myHashMap.asIndexGetter()))); } - Pair, Map>> fullLogAndRefs = loadFullLog(); - return DataPack.build(fullLogAndRefs.first, myProviders, new RefsModel(fullLogAndRefs.second, myHashMap.asIndexGetter()), true); + return loadFullLog(); } catch (Exception e) { myExceptionHandler.consume(e); @@ -305,95 +258,45 @@ public class VcsLogRefresherImpl implements VcsLogRefresher { } } - private String toLogString(Map infos) { - StringBuilder sb = new StringBuilder(); - for (Map.Entry entry : infos.entrySet()) { - sb.append(entry.getKey().getName()); - sb.append(" LOG:\n"); - sb.append(StringUtil.join(entry.getValue().log, new Function, String>() { - @Override - public String fun(GraphCommit commit) { - return commit.getId() + "<-" + StringUtil.join(commit.getParents(), ","); - } - }, "\n")); - sb.append("\nREFS:\n"); - sb.append(StringUtil.join(entry.getValue().refs, new Function() { - @Override - public String fun(VcsRef ref) { - return ref.getName() + "(" + myHashMap.getCommitIndex(ref.getCommitHash()) + ")"; - } - }, ",")); - } - return sb.toString(); - } - @NotNull - private List> compoundLoadedLogs(@NotNull Collection logsAndRefs) { - return compound(ContainerUtil.map(logsAndRefs, new Function>>() { - @Override - public List> fun(LogAndRefs refs) { - return refs.log; - } - })); - } - - @NotNull - private Map> getAllNewRefs(@NotNull Map newInfo, - @NotNull Map> previousRefs) { - Map> result = ContainerUtil.newHashMap(); + private Map> getAllNewRefs(@NotNull LogInfo newInfo, + @NotNull Map> previousRefs) { + Map> result = ContainerUtil.newHashMap(); for (VirtualFile root : previousRefs.keySet()) { - result.put(root, newInfo.containsKey(root) ? newInfo.get(root).refs : previousRefs.get(root)); + Set newInfoRefs = newInfo.getRefs(root); + result.put(root, newInfoRefs != null ? newInfoRefs : previousRefs.get(root)); } return result; } - private void loadLogAndRefs(@NotNull Collection roots, @NotNull Map> prevRefs, + private void loadLogAndRefs(@NotNull Collection roots, + @NotNull Map> prevRefs, int commitCount) throws VcsException { - Map providers = getProviders(roots); - Map> refs = loadRefsFromVcs(providers); - Map requirements = prepareRequirements(roots, commitCount, prevRefs, refs); - Map>> commits = loadRecentCommitsFromVcs(providers, requirements, - myUserRegistry, myTopCommitsDetailsCache, - myHashMap); + LogInfo logInfo = loadRecentData(prepareRequirements(roots, commitCount, prevRefs)); for (VirtualFile root : roots) { - myLoadedInfos.put(root, new LogAndRefs(refs.get(root), commits.get(root))); + myLoadedInfo.put(root, logInfo.getCommits(root)); + myLoadedInfo.put(root, logInfo.getRefs(root)); } } @NotNull private Map prepareRequirements(@NotNull Collection roots, int commitCount, - @NotNull Map> prevRefs, - @NotNull Map> newRefs) { + @NotNull Map> prevRefs) { Map requirements = ContainerUtil.newHashMap(); for (VirtualFile root : roots) { - requirements.put(root, new RequirementsImpl(commitCount, true, getRefsForRoot(prevRefs, root), getRefsForRoot(newRefs, root))); + requirements.put(root, new RequirementsImpl(commitCount, true, ContainerUtil.notNullize(prevRefs.get(root)))); } return requirements; } - @NotNull - private Set getRefsForRoot(@NotNull Map> map, @NotNull VirtualFile root) { - Collection refs = map.get(root); - return refs == null ? Collections.emptySet() : new HashSet(refs); - } - - @NotNull - private Map getProviders(@NotNull Collection roots) { - Map providers = ContainerUtil.newHashMap(); - for (VirtualFile root : roots) { - providers.put(root, myProviders.get(root)); - } - return providers; - } - @Nullable - private List> join(@NotNull List> recentCommits, @NotNull List> fullLog, - @NotNull Map> previousRefs, - @NotNull Map> newRefs) { + private List> join(@NotNull List> recentCommits, + @NotNull List> fullLog, + @NotNull Map> previousRefs, + @NotNull Map> newRefs) { StopWatch sw = StopWatch.start("joining new commits"); Function ref2Int = new Function() { - @NotNull @Override public Integer fun(@NotNull VcsRef ref) { return myHashMap.getCommitIndex(ref.getCommitHash()); @@ -418,50 +321,44 @@ public class VcsLogRefresherImpl implements VcsLogRefresher { } @NotNull - private Pair, Map>> loadFullLog() throws VcsException { + private DataPack loadFullLog() throws VcsException { StopWatch sw = StopWatch.start("full log reload"); - Collection>> commits = readFullLogFromVcs(); - List> graphCommits = compound(commits); - Map> refMap = loadRefsFromVcs(myProviders); - PermanentGraph permanentGraph = DataPack.buildPermanentGraph(graphCommits, new RefsModel(refMap, myHashMap.asIndexGetter()), - myHashMap.asIndexGetter(), - myHashMap.asHashGetter(), myProviders); + LogInfo logInfo = readFullLogFromVcs(); + List> graphCommits = multiRepoJoin(logInfo.getCommits()); + DataPack dataPack = DataPack.build(graphCommits, logInfo.getRefs(), myProviders, myHashMap, true); sw.report(); - return Pair.create(permanentGraph, refMap); + return dataPack; } @NotNull - private Collection>> readFullLogFromVcs() throws VcsException { + private LogInfo readFullLogFromVcs() throws VcsException { final StopWatch sw = StopWatch.start("read full log from VCS"); - final Collection>> logs = ContainerUtil.newArrayList(); + final LogInfo logInfo = new LogInfo(); new ProviderIterator() { @Override void each(@NotNull VirtualFile root, @NotNull VcsLogProvider provider) throws VcsException { final List> graphCommits = ContainerUtil.newArrayList(); - provider.readAllHashes(root, new Consumer() { - @Override - public void consume(@NotNull VcsUser user) { - myUserRegistry.addUser(user); - } - }, new Consumer() { + VcsLogProvider.LogData data = provider.readAllHashes(root, new Consumer() { @Override - public void consume(TimedVcsCommit commit) { - graphCommits.add(compactCommit(commit, myHashMap)); + public void consume(@NotNull TimedVcsCommit commit) { + graphCommits.add(compactCommit(commit)); } }); - logs.add(graphCommits); + logInfo.put(root, graphCommits); + logInfo.put(root, data.getRefs()); + myUserRegistry.addUsers(data.getUsers()); sw.rootCompleted(root); } }.iterate(myProviders); myUserRegistry.flush(); sw.report(); - return logs; + return logInfo; } } private static class RefreshRequest { - private static RefreshRequest RELOAD_ALL = new RefreshRequest(Collections.emptyList()); - @NotNull private final Collection rootsToRefresh; + private static final RefreshRequest RELOAD_ALL = new RefreshRequest(Collections.emptyList()); + private final Collection rootsToRefresh; RefreshRequest(@NotNull Collection rootsToRefresh) { this.rootsToRefresh = rootsToRefresh; @@ -477,4 +374,85 @@ public class VcsLogRefresherImpl implements VcsLogRefresher { } } } + + private static class CommitCountRequirements implements VcsLogProvider.Requirements { + private final int myCommitCount; + + public CommitCountRequirements(int commitCount) { + myCommitCount = commitCount; + } + + @Override + public int getCommitCount() { + return myCommitCount; + } + + @NotNull + Map asMap(@NotNull Collection roots) { + return ContainerUtil.map2Map(roots, new Function>() { + @Override + public Pair fun(VirtualFile root) { + return Pair.create(root, CommitCountRequirements.this); + } + }); + } + } + + private static class LogInfo { + private final Map> myRefs = ContainerUtil.newHashMap(); + private final Map>> myCommits = ContainerUtil.newHashMap(); + + void put(@NotNull VirtualFile root, @NotNull List> commits) { + myCommits.put(root, commits); + } + + void put(@NotNull VirtualFile root, @NotNull Set refs) { + myRefs.put(root, refs); + } + + @NotNull + Collection>> getCommits() { + return myCommits.values(); + } + + List> getCommits(@NotNull VirtualFile root) { + return myCommits.get(root); + } + + @NotNull + Map> getRefs() { + return myRefs; + } + + public Set getRefs(@NotNull VirtualFile root) { + return myRefs.get(root); + } + + @SuppressWarnings("StringConcatenationInsideStringBufferAppend") + @NotNull + public String toLogString(@NotNull final NotNullFunction indexGetter) { + StringBuilder sb = new StringBuilder(); + sb.append(" LOG:\n"); + for (Map.Entry>> entry : myCommits.entrySet()) { + sb.append(entry.getKey().getName() + "\n"); + sb.append(StringUtil.join(entry.getValue(), new Function, String>() { + @Override + public String fun(@NotNull GraphCommit commit) { + return commit.getId() + "<-" + StringUtil.join(commit.getParents(), ","); + } + }, "\n")); + } + sb.append("\nREFS:\n"); + for (Map.Entry> entry : myRefs.entrySet()) { + sb.append(entry.getKey().getName() + "\n"); + sb.append(StringUtil.join(entry.getValue(), new Function() { + @Override + public String fun(@NotNull VcsRef ref) { + return ref.getName() + "(" + indexGetter.fun(ref.getCommitHash()) + ")"; + } + }, ",")); + } + return sb.toString(); + } + } } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogSorter.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogSorter.java index d872ac236c16..3f9d742eef34 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogSorter.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsLogSorter.java @@ -20,12 +20,13 @@ import com.intellij.vcs.log.TimedVcsCommit; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; +import java.util.Collection; import java.util.List; public class VcsLogSorter { @NotNull - public static List sortByDateTopoOrder(@NotNull List commits) { + public static List sortByDateTopoOrder(@NotNull Collection commits) { return new VcsLogJoiner.NewCommitIntegrator(new ArrayList(), commits).getResultList(); } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsUserRegistryImpl.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsUserRegistryImpl.java index a5447d20f115..8621f379ce65 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsUserRegistryImpl.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/data/VcsUserRegistryImpl.java @@ -86,6 +86,12 @@ public class VcsUserRegistryImpl implements Disposable, VcsUserRegistry { } } + public void addUsers(@NotNull Collection users) { + for (VcsUser user : users) { + addUser(user); + } + } + @Override @NotNull public Set getUsers() { diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/LogDataImpl.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/LogDataImpl.java new file mode 100644 index 000000000000..82eb4e1a6336 --- /dev/null +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/LogDataImpl.java @@ -0,0 +1,74 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.vcs.log.impl; + +import com.intellij.vcs.log.VcsCommitMetadata; +import com.intellij.vcs.log.VcsLogProvider; +import com.intellij.vcs.log.VcsRef; +import com.intellij.vcs.log.VcsUser; +import org.jetbrains.annotations.NotNull; + +import java.util.Collections; +import java.util.List; +import java.util.Set; + +public class LogDataImpl implements VcsLogProvider.DetailedLogData, VcsLogProvider.LogData { + + private static final LogDataImpl EMPTY = new LogDataImpl(Collections.emptySet(), + Collections.emptySet(), + Collections.emptyList()); + + @NotNull private final List myCommits; + @NotNull private final Set myRefs; + @NotNull private final Set myUsers; + + @NotNull + public static LogDataImpl empty() { + return EMPTY; + } + + public LogDataImpl(@NotNull Set refs, @NotNull Set users) { + this(refs, users, Collections.emptyList()); + } + + public LogDataImpl(@NotNull Set refs, @NotNull List metadatas) { + this(refs, Collections.emptySet(), metadatas); + } + + private LogDataImpl(@NotNull Set refs, @NotNull Set users, @NotNull List commits) { + myRefs = refs; + myUsers = users; + myCommits = commits; + } + + @NotNull + @Override + public List getCommits() { + return myCommits; + } + + @Override + @NotNull + public Set getRefs() { + return myRefs; + } + + @NotNull + @Override + public Set getUsers() { + return myUsers; + } +} diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/RequirementsImpl.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/RequirementsImpl.java index f742e277283f..9a1e54ed3886 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/RequirementsImpl.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/RequirementsImpl.java @@ -26,13 +26,11 @@ public class RequirementsImpl implements VcsLogProviderRequirementsEx { private final int myCommitCount; private final boolean myRefresh; @NotNull private final Set myPreviousRefs; - @NotNull private final Set myCurrentRefs; - public RequirementsImpl(int count, boolean refresh, @NotNull Set previousRefs, @NotNull Set currentRefs) { + public RequirementsImpl(int count, boolean refresh, @NotNull Set previousRefs) { myCommitCount = count; myRefresh = refresh; myPreviousRefs = previousRefs; - myCurrentRefs = currentRefs; } @Override @@ -50,10 +48,4 @@ public class RequirementsImpl implements VcsLogProviderRequirementsEx { public Set getPreviousRefs() { return myPreviousRefs; } - - @NotNull - @Override - public Set getCurrentRefs() { - return myCurrentRefs; - } } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/VcsStructureChooser.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/VcsStructureChooser.java index 34c638088476..0078669cd811 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/VcsStructureChooser.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/VcsStructureChooser.java @@ -126,6 +126,7 @@ public class VcsStructureChooser extends DialogWrapper { return myModulesSet; } + @NotNull public Collection getSelectedFiles() { return ((CollectionListModel) mySelectedList.getModel()).getItems(); } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/StructureFilterPopupComponent.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/StructureFilterPopupComponent.java index c134b1aa330d..3140d647795f 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/StructureFilterPopupComponent.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/ui/filter/StructureFilterPopupComponent.java @@ -101,7 +101,7 @@ class StructureFilterPopupComponent extends FilterPopupComponent(myRoots)); if (chooser.showAndGet()) { myFiles.clear(); - myFiles.addAll(ContainerUtil.notNullize(chooser.getSelectedFiles())); + myFiles.addAll(chooser.getSelectedFiles()); setValue(myFiles); applyFilters(); } diff --git a/platform/vcs-log/impl/test/com/intellij/vcs/log/data/VcsLogRefresherTest.java b/platform/vcs-log/impl/test/com/intellij/vcs/log/data/VcsLogRefresherTest.java index 6baf2318f7a9..01705232a253 100644 --- a/platform/vcs-log/impl/test/com/intellij/vcs/log/data/VcsLogRefresherTest.java +++ b/platform/vcs-log/impl/test/com/intellij/vcs/log/data/VcsLogRefresherTest.java @@ -46,11 +46,11 @@ public class VcsLogRefresherTest extends VcsLogPlatformTest { throw new AssertionError(e); } }; - @NotNull private TestVcsLogProvider myLogProvider; - @NotNull private VcsLogDataHolder myDataHolder; - @NotNull private Map myTopDetailsCache; - @NotNull private Map myLogProviders; - @NotNull private List myCommits; + private TestVcsLogProvider myLogProvider; + private VcsLogDataHolder myDataHolder; + private Map myTopDetailsCache; + private Map myLogProviders; + private List myCommits; @Override public void setUp() throws Exception { diff --git a/platform/vcs-log/impl/test/com/intellij/vcs/log/impl/TestVcsLogProvider.java b/platform/vcs-log/impl/test/com/intellij/vcs/log/impl/TestVcsLogProvider.java index 850478b048dc..05659538da20 100644 --- a/platform/vcs-log/impl/test/com/intellij/vcs/log/impl/TestVcsLogProvider.java +++ b/platform/vcs-log/impl/test/com/intellij/vcs/log/impl/TestVcsLogProvider.java @@ -26,10 +26,8 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.awt.*; -import java.util.Collection; -import java.util.Comparator; +import java.util.*; import java.util.List; -import java.util.Set; import java.util.concurrent.Semaphore; import static org.junit.Assert.assertEquals; @@ -59,13 +57,14 @@ public class TestVcsLogProvider implements VcsLogProvider { @NotNull private final ReducibleSemaphore myRefreshSemaphore; private int myReadFirstBlockCounter; - private final Function myCommitToMetadataConvertor = new Function(){ - @Override - public VcsCommitMetadata fun(TimedVcsCommit commit) { - return new VcsCommitMetadataImpl(commit.getId(), commit.getParents(), commit.getTimestamp(), myRoot, SAMPLE_SUBJECT, STUB_USER, - SAMPLE_SUBJECT, STUB_USER, commit.getTimestamp()); - } - }; + private final Function myCommitToMetadataConvertor = + new Function() { + @Override + public VcsCommitMetadata fun(TimedVcsCommit commit) { + return new VcsCommitMetadataImpl(commit.getId(), commit.getParents(), commit.getTimestamp(), myRoot, + SAMPLE_SUBJECT, STUB_USER, SAMPLE_SUBJECT, STUB_USER, commit.getTimestamp()); + } + }; public TestVcsLogProvider(@NotNull VirtualFile root) { myRoot = root; @@ -78,7 +77,8 @@ public class TestVcsLogProvider implements VcsLogProvider { @NotNull @Override - public List readFirstBlock(@NotNull final VirtualFile root, @NotNull Requirements requirements) + public DetailedLogData readFirstBlock(@NotNull final VirtualFile root, + @NotNull Requirements requirements) throws VcsException { if (requirements instanceof VcsLogProviderRequirementsEx && ((VcsLogProviderRequirementsEx)requirements).isRefresh()) { try { @@ -90,12 +90,14 @@ public class TestVcsLogProvider implements VcsLogProvider { } myReadFirstBlockCounter++; assertRoot(root); - return ContainerUtil.map(myCommits.subList(0, requirements.getCommitCount()), myCommitToMetadataConvertor); + List metadatas = ContainerUtil.map(myCommits.subList(0, requirements.getCommitCount()), + myCommitToMetadataConvertor); + return new LogDataImpl(Collections.emptySet(), metadatas); } + @NotNull @Override - public void readAllHashes(@NotNull VirtualFile root, @NotNull Consumer userRegistry, - @NotNull Consumer commitConsumer) throws VcsException { + public LogData readAllHashes(@NotNull VirtualFile root, @NotNull Consumer commitConsumer) throws VcsException { try { myFullLogSemaphore.acquire(); } @@ -106,6 +108,7 @@ public class TestVcsLogProvider implements VcsLogProvider { for (TimedVcsCommit commit : myCommits) { commitConsumer.consume(commit); } + return new LogDataImpl(myRefs, Collections.emptySet()); } private void assertRoot(@NotNull VirtualFile root) { @@ -125,12 +128,6 @@ public class TestVcsLogProvider implements VcsLogProvider { throw new UnsupportedOperationException(); } - @NotNull - @Override - public Collection readAllRefs(@NotNull VirtualFile root) throws VcsException { - return myRefs; - } - @NotNull @Override public VcsKey getSupportedVcs() { diff --git a/platform/vcs-log/impl/vcs-log-impl.iml b/platform/vcs-log/impl/vcs-log-impl.iml index 304affc03123..4fce9792f123 100644 --- a/platform/vcs-log/impl/vcs-log-impl.iml +++ b/platform/vcs-log/impl/vcs-log-impl.iml @@ -16,7 +16,7 @@ - + diff --git a/platform/xdebugger-api/src/com/intellij/xdebugger/XDebugProcess.java b/platform/xdebugger-api/src/com/intellij/xdebugger/XDebugProcess.java index 9cfe6e4e7605..ee949a2ae9ce 100644 --- a/platform/xdebugger-api/src/com/intellij/xdebugger/XDebugProcess.java +++ b/platform/xdebugger-api/src/com/intellij/xdebugger/XDebugProcess.java @@ -193,18 +193,31 @@ public abstract class XDebugProcess { } /** - * @deprecated override {@link #createTabLayouter()} and {@link com.intellij.xdebugger.ui.XDebugTabLayouter#registerAdditionalContent} instead + * @deprecated Override {@link #createTabLayouter()} and {@link com.intellij.xdebugger.ui.XDebugTabLayouter#registerAdditionalContent} instead + * to remove in IDEA 15 */ + @SuppressWarnings("UnusedParameters") @Deprecated public void registerAdditionalContent(@NotNull RunnerLayoutUi ui) { } + @SuppressWarnings("UnusedParameters") + @Deprecated /** - * Override this method to provide additional actions in 'Debug' tool window + * @deprecated Override {@link #registerAdditionalActions(com.intellij.openapi.actionSystem.ActionGroup, com.intellij.openapi.actionSystem.ActionGroup, com.intellij.openapi.actionSystem.ActionGroup)} instead + * to remove in IDEA 15 */ public void registerAdditionalActions(@NotNull DefaultActionGroup leftToolbar, @NotNull DefaultActionGroup topToolbar) { } + /** + * Override this method to provide additional actions in 'Debug' tool window + */ + public void registerAdditionalActions(@NotNull DefaultActionGroup leftToolbar, @NotNull DefaultActionGroup topToolbar, @NotNull DefaultActionGroup settings) { + //noinspection deprecation + registerAdditionalActions(leftToolbar, topToolbar); + } + /** * @return message to show in Variables View when debugger isn't paused */ @@ -225,6 +238,7 @@ public abstract class XDebugProcess { return new XDebugTabLayouter() { @Override public void registerAdditionalContent(@NotNull RunnerLayoutUi ui) { + //noinspection deprecation XDebugProcess.this.registerAdditionalContent(ui); } }; diff --git a/platform/xdebugger-api/src/com/intellij/xdebugger/evaluation/XDebuggerEvaluator.java b/platform/xdebugger-api/src/com/intellij/xdebugger/evaluation/XDebuggerEvaluator.java index 6109e9a41598..37e2da11e0e1 100644 --- a/platform/xdebugger-api/src/com/intellij/xdebugger/evaluation/XDebuggerEvaluator.java +++ b/platform/xdebugger-api/src/com/intellij/xdebugger/evaluation/XDebuggerEvaluator.java @@ -72,6 +72,13 @@ public abstract class XDebuggerEvaluator { */ public abstract void evaluate(@NotNull String expression, @NotNull XEvaluationCallback callback, @Nullable XSourcePosition expressionPosition); + /** + * Start evaluating expression. + * + * @param expression expression to evaluate + * @param callback used to notify that the expression has been evaluated or an error occurs + * @param expressionPosition position where this expression should be evaluated + */ public void evaluate(@NotNull XExpression expression, @NotNull XEvaluationCallback callback, @Nullable XSourcePosition expressionPosition) { evaluate(expression.getExpression(), callback, expressionPosition); } @@ -83,15 +90,13 @@ public abstract class XDebuggerEvaluator { * @param expression expression to evaluate * @param callback used to notify that the expression has been evaluated or an error occurs * @param mode code fragment or expression + * @deprecated use {@link #evaluate(com.intellij.xdebugger.XExpression, com.intellij.xdebugger.evaluation.XDebuggerEvaluator.XEvaluationCallback, com.intellij.xdebugger.XSourcePosition)} ()} */ + @Deprecated public void evaluate(@NotNull String expression, @NotNull XEvaluationCallback callback, @Nullable XSourcePosition expressionPosition, @NotNull EvaluationMode mode) { evaluate(expression, callback, expressionPosition); } - public void evaluate(@NotNull XExpression expression, @NotNull XEvaluationCallback callback, @Nullable XSourcePosition expressionPosition, @NotNull EvaluationMode mode) { - evaluate(expression, callback, expressionPosition); - } - /** * If this method returns {@code true} 'Code Fragment Mode' button will be shown in 'Evaluate' dialog allowing user to execute a set of * statements diff --git a/platform/xdebugger-api/src/com/intellij/xdebugger/frame/XReferrersProvider.java b/platform/xdebugger-api/src/com/intellij/xdebugger/frame/XReferrersProvider.java index 9f9f3a8bf407..cb31a3be23b0 100644 --- a/platform/xdebugger-api/src/com/intellij/xdebugger/frame/XReferrersProvider.java +++ b/platform/xdebugger-api/src/com/intellij/xdebugger/frame/XReferrersProvider.java @@ -16,8 +16,10 @@ package com.intellij.xdebugger.frame; /** + * Provides an XValue that returns instead of children a set of objects that refer to it. + * * @author traff */ -public interface XReferrersProvider { - T getReferringObjectsValue(); +public abstract class XReferrersProvider { + public abstract XValue getReferringObjectsValue(); } diff --git a/platform/xdebugger-api/src/com/intellij/xdebugger/frame/XValue.java b/platform/xdebugger-api/src/com/intellij/xdebugger/frame/XValue.java index 9cfa2fb15af4..4d3446bbfb54 100644 --- a/platform/xdebugger-api/src/com/intellij/xdebugger/frame/XValue.java +++ b/platform/xdebugger-api/src/com/intellij/xdebugger/frame/XValue.java @@ -88,6 +88,12 @@ public abstract class XValue extends XValueContainer { navigatable.setSourcePosition(null); } + /** + * This enables showing referrers for the value + * + * @return provider that creates an XValue returning objects that refer to the current value + * or null if showing referrers for the value is disabled + */ @Nullable public XReferrersProvider getReferrersProvider() { return null; diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/DebuggerSupport.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/DebuggerSupport.java index b09c7388748f..d76d60410648 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/DebuggerSupport.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/DebuggerSupport.java @@ -16,6 +16,7 @@ package com.intellij.xdebugger.impl; import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.editor.Editor; import com.intellij.openapi.extensions.ExtensionPointName; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.project.Project; @@ -25,11 +26,15 @@ import com.intellij.xdebugger.impl.actions.DebuggerToggleActionHandler; import com.intellij.xdebugger.impl.actions.EditBreakpointActionHandler; import com.intellij.xdebugger.impl.actions.MarkObjectActionHandler; import com.intellij.xdebugger.impl.breakpoints.ui.BreakpointPanelProvider; +import com.intellij.xdebugger.impl.evaluate.quick.common.AbstractValueHint; import com.intellij.xdebugger.impl.evaluate.quick.common.QuickEvaluateHandler; +import com.intellij.xdebugger.impl.evaluate.quick.common.ValueHintType; import com.intellij.xdebugger.impl.settings.DebuggerSettingsPanelProvider; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.awt.*; + /** * @author nik */ @@ -118,7 +123,32 @@ public abstract class DebuggerSupport { public abstract DebuggerActionHandler getEvaluateHandler(); @NotNull - public abstract QuickEvaluateHandler getQuickEvaluateHandler(); + public QuickEvaluateHandler getQuickEvaluateHandler() { + return DISABLED_QUICK_EVALUATE; + } + + private static final QuickEvaluateHandler DISABLED_QUICK_EVALUATE = new QuickEvaluateHandler() { + @Override + public boolean isEnabled(@NotNull Project project) { + return false; + } + + @Nullable + @Override + public AbstractValueHint createValueHint(@NotNull Project project, @NotNull Editor editor, @NotNull Point point, ValueHintType type) { + return null; + } + + @Override + public boolean canShowHint(@NotNull Project project) { + return false; + } + + @Override + public int getValueLookupDelay(Project project) { + return 0; + } + }; @NotNull public abstract DebuggerActionHandler getAddToWatchesActionHandler(); diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/XDebugSessionImpl.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/XDebugSessionImpl.java index 1b0f0ca69a59..ddc28745934a 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/XDebugSessionImpl.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/XDebugSessionImpl.java @@ -98,7 +98,7 @@ public class XDebugSessionImpl implements XDebugSession { private XExecutionStack myCurrentExecutionStack; private XStackFrame myCurrentStackFrame; private boolean myIsTopFrame; - private XSourcePosition myTopFramePosition; + private volatile XSourcePosition myTopFramePosition; private final AtomicBoolean myPaused = new AtomicBoolean(); private MyDependentBreakpointListener myDependentBreakpointListener; private XValueMarkers myValueMarkers; diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/XDebuggerManagerImpl.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/XDebuggerManagerImpl.java index 2e6f91d43924..6ef4ca6a1252 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/XDebuggerManagerImpl.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/XDebuggerManagerImpl.java @@ -24,7 +24,11 @@ import com.intellij.execution.process.ProcessHandler; import com.intellij.execution.runners.ExecutionEnvironment; import com.intellij.execution.runners.ProgramRunner; import com.intellij.execution.runners.RunContentBuilder; -import com.intellij.execution.ui.*; +import com.intellij.execution.ui.ExecutionConsole; +import com.intellij.execution.ui.RunContentDescriptor; +import com.intellij.execution.ui.RunContentManager; +import com.intellij.execution.ui.RunContentWithExecutorListener; +import com.intellij.ide.DataManager; import com.intellij.openapi.Disposable; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.*; @@ -49,7 +53,6 @@ import com.intellij.xdebugger.impl.settings.XDebuggerSettingsManager; import com.intellij.xdebugger.impl.ui.ExecutionPointHighlighter; import com.intellij.xdebugger.impl.ui.XDebugSessionData; import com.intellij.xdebugger.impl.ui.XDebugSessionTab; -import gnu.trove.THashMap; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -70,8 +73,6 @@ public class XDebuggerManagerImpl extends XDebuggerManager private final Project myProject; private final XBreakpointManagerImpl myBreakpointManager; private final XDebuggerWatchesManager myWatchesManager; - private final Map mySessionData; - private final Map mySessionTabs; private final Map mySessions; private final ExecutionPointHighlighter myExecutionPointHighlighter; private XDebugSessionImpl myActiveSession; @@ -80,8 +81,6 @@ public class XDebuggerManagerImpl extends XDebuggerManager myProject = project; myBreakpointManager = new XBreakpointManagerImpl(project, this, startupManager); myWatchesManager = new XDebuggerWatchesManager(); - mySessionData = new THashMap(); - mySessionTabs = new THashMap(); mySessions = new LinkedHashMap(); myExecutionPointHighlighter = new ExecutionPointHighlighter(project); @@ -135,11 +134,6 @@ public class XDebuggerManagerImpl extends XDebuggerManager public void contentRemoved(@Nullable RunContentDescriptor descriptor, @NotNull Executor executor) { if (descriptor != null && executor.equals(DefaultDebugExecutor.getDebugExecutorInstance())) { mySessions.remove(descriptor.getProcessHandler()); - mySessionData.remove(descriptor); - XDebugSessionTab tab = mySessionTabs.remove(descriptor); - if (tab != null) { - Disposer.dispose(tab); - } } } }); @@ -218,7 +212,13 @@ public class XDebuggerManagerImpl extends XDebuggerManager XDebugProcess process = processStarter.start(session); myProject.getMessageBus().syncPublisher(TOPIC).processStarted(process); - XDebugSessionData oldSessionData = contentToReuse != null ? mySessionData.get(contentToReuse) : null; + XDebugSessionData oldSessionData = null; + if (contentToReuse != null) { + JComponent component = contentToReuse.getComponent(); + if (component != null) { + oldSessionData = XDebugSessionData.DATA_KEY.getData(DataManager.getInstance().getDataContext(component)); + } + } if (oldSessionData == null) { oldSessionData = new XDebugSessionData(session.getWatchExpressions()); } @@ -240,21 +240,17 @@ public class XDebuggerManagerImpl extends XDebuggerManager XDebugSessionTab sessionTab = session.getSessionTab(); mySessions.remove(session.getDebugProcess().getProcessHandler()); if (sessionTab != null) { - final RunContentDescriptor descriptor = sessionTab.getRunContentDescriptor(); - mySessionData.put(descriptor, session.getSessionData()); - mySessionTabs.put(descriptor, sessionTab); - - // in test-mode RunContentWithExecutorListener.contentRemoved events are not sent (see RunContentManagerImpl.showRunContent) - // so we make sure the mySessions and mySessionData are cleared correctly when session is disposed - - Disposer.register(sessionTab, new Disposable() { - @Override - public void dispose() { - mySessionData.remove(descriptor); - mySessionTabs.remove(descriptor); - mySessions.remove(session.getDebugProcess().getProcessHandler()); - } - }); + RunContentDescriptor descriptor = sessionTab.getRunContentDescriptor(); + if (descriptor != null) { + // in test-mode RunContentWithExecutorListener.contentRemoved events are not sent (see RunContentManagerImpl.showRunContent) + // so we make sure the mySessions and mySessionData are cleared correctly when session is disposed + Disposer.register(descriptor, new Disposable() { + @Override + public void dispose() { + mySessions.remove(session.getDebugProcess().getProcessHandler()); + } + }); + } if (!myProject.isDisposed() && !ApplicationManager.getApplication().isUnitTestMode() && XDebuggerSettingsManager.getInstanceImpl().getGeneralSettings().isHideDebuggerOnProcessTermination()) { ExecutionManager.getInstance(myProject).getContentManager().hideRunContent(DefaultDebugExecutor.getDebugExecutorInstance(), descriptor); diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/actions/handlers/XMarkObjectActionHandler.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/actions/handlers/XMarkObjectActionHandler.java index c3a9b9842661..c83b07fa09ff 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/actions/handlers/XMarkObjectActionHandler.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/actions/handlers/XMarkObjectActionHandler.java @@ -30,6 +30,8 @@ import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import static com.intellij.openapi.actionSystem.PlatformDataKeys.CONTEXT_COMPONENT; + /** * @author nik */ @@ -49,7 +51,7 @@ public class XMarkObjectActionHandler extends MarkObjectActionHandler { markers.unmarkValue(value); } else { - ValueMarkerPresentationDialog dialog = new ValueMarkerPresentationDialog(node.getName()); + ValueMarkerPresentationDialog dialog = new ValueMarkerPresentationDialog(event.getData(CONTEXT_COMPONENT), node.getName()); dialog.show(); ValueMarkup markup = dialog.getConfiguredMarkup(); if (dialog.isOK() && markup != null) { diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/evaluate/XDebuggerEditorLinePainter.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/evaluate/XDebuggerEditorLinePainter.java index e73564095b21..b9a58844585a 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/evaluate/XDebuggerEditorLinePainter.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/evaluate/XDebuggerEditorLinePainter.java @@ -18,6 +18,7 @@ package com.intellij.xdebugger.impl.evaluate; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.EditorLinePainter; import com.intellij.openapi.editor.LineExtensionInfo; +import com.intellij.openapi.editor.colors.EditorColorsManager; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Key; @@ -25,10 +26,8 @@ import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.ui.Gray; -import com.intellij.ui.JBColor; -import com.intellij.ui.SimpleColoredText; -import com.intellij.ui.SimpleTextAttributes; +import com.intellij.ui.*; +import com.intellij.util.NotNullProducer; import com.intellij.xdebugger.XDebugSession; import com.intellij.xdebugger.XSourcePosition; import com.intellij.xdebugger.frame.presentation.XValuePresentation; @@ -142,12 +141,33 @@ public class XDebuggerEditorLinePainter extends EditorLinePainter { return -1; } + private static boolean isDarkEditor() { + Color bg = EditorColorsManager.getInstance().getGlobalScheme().getDefaultBackground(); + return ColorUtil.isDark(bg); + } + public static JBColor getForeground() { - return new JBColor(new Color(61, 128, 101), new Color(61, 128, 101)); + return new JBColor(new NotNullProducer() { + @SuppressWarnings("UseJBColor") + @NotNull + @Override + public Color produce() { + return isDarkEditor() ? Registry.getColor("ide.debugger.inline.dark.fg.color", new Color(0x3d8065)) + : Registry.getColor("ide.debugger.inline.fg.color", new Color(0x3d8065)); + } + }); } public static JBColor getChangedForeground() { - return new JBColor(new Color(202, 128, 33), new Color(161, 131, 10)); + return new JBColor(new NotNullProducer() { + @SuppressWarnings("UseJBColor") + @NotNull + @Override + public Color produce() { + return isDarkEditor() ? Registry.getColor("ide.debugger.inline.dark.fg.modified.color", new Color(0xa1830a)) + : Registry.getColor("ide.debugger.inline.fg.modified.color", new Color(0xca8021)); + } + }); } static class Variable { diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/evaluate/XDebuggerEvaluationDialog.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/evaluate/XDebuggerEvaluationDialog.java index 74b3b5202dbd..5a6d19de3234 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/evaluate/XDebuggerEvaluationDialog.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/evaluate/XDebuggerEvaluationDialog.java @@ -295,7 +295,7 @@ public class XDebuggerEvaluationDialog extends DialogWrapper { evaluationCallback.errorOccurred(XDebuggerBundle.message("xdebugger.evaluate.stack.frame.has.not.evaluator")); } else { - evaluator.evaluate(expression, evaluationCallback, null, inputEditor.getMode()); + evaluator.evaluate(expression, evaluationCallback, null); } } diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/evaluate/quick/XValueHint.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/evaluate/quick/XValueHint.java index ca78932701d2..37b641e1042e 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/evaluate/quick/XValueHint.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/evaluate/quick/XValueHint.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,15 +20,23 @@ import com.intellij.execution.console.LanguageConsoleImpl; import com.intellij.execution.console.LanguageConsoleView; import com.intellij.execution.impl.ConsoleViewImpl; import com.intellij.execution.ui.ConsoleView; +import com.intellij.openapi.Disposable; +import com.intellij.openapi.actionSystem.ActionManager; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.ShortcutSet; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.fileEditor.FileDocumentManager; +import com.intellij.openapi.keymap.KeymapUtil; +import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Pair; import com.intellij.openapi.vcs.changes.issueLinks.LinkMouseListenerBase; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.ui.SimpleColoredComponent; import com.intellij.ui.SimpleColoredText; +import com.intellij.ui.SimpleTextAttributes; import com.intellij.util.Consumer; import com.intellij.xdebugger.XDebugSession; import com.intellij.xdebugger.XDebuggerUtil; @@ -68,6 +76,8 @@ public class XValueHint extends AbstractValueHint { private final String myExpression; private final String myValueName; private final @Nullable XSourcePosition myExpressionPosition; + private final ExpressionInfo myExpressionInfo; + private Disposable myDisposable; public XValueHint(@NotNull Project project, @NotNull Editor editor, @NotNull Point point, @NotNull ValueHintType type, @NotNull ExpressionInfo expressionInfo, @NotNull XDebuggerEvaluator evaluator, @@ -78,6 +88,7 @@ public class XValueHint extends AbstractValueHint { myDebugSession = session; myExpression = XDebuggerEvaluateActionHandler.getExpressionText(expressionInfo, editor.getDocument()); myValueName = XDebuggerEvaluateActionHandler.getDisplayText(expressionInfo, editor.getDocument()); + myExpressionInfo = expressionInfo; VirtualFile file; ConsoleView consoleView = ConsoleViewImpl.CONSOLE_VIEW_IN_EDITOR_VIEW.get(editor); @@ -97,6 +108,33 @@ public class XValueHint extends AbstractValueHint { return true; } + @Override + protected boolean showHint(final JComponent component) { + boolean result = super.showHint(component); + if (result && getType() == ValueHintType.MOUSE_OVER_HINT) { + myDisposable = Disposer.newDisposable(); + ShortcutSet shortcut = ActionManager.getInstance().getAction("ShowErrorDescription").getShortcutSet(); + new DumbAwareAction() { + @Override + public void actionPerformed(@NotNull AnActionEvent e) { + hideHint(); + final Point point = new Point(myPoint.x, myPoint.y + getEditor().getLineHeight()); + new XValueHint(getProject(), getEditor(), point, ValueHintType.MOUSE_CLICK_HINT, myExpressionInfo, myEvaluator, myDebugSession).invokeHint(); + } + }.registerCustomShortcutSet(shortcut, getEditor().getContentComponent(), myDisposable); + } + return result; + } + + @Override + public void hideHint() { + super.hideHint(); + if (myDisposable != null) { + Disposer.dispose(myDisposable); + + } + } + @Override protected void evaluateAndShowHint() { myEvaluator.evaluate(myExpression, new XEvaluationCallbackBase() { @@ -136,6 +174,11 @@ public class XValueHint extends AbstractValueHint { showTree(result); } else { + if (getType() == ValueHintType.MOUSE_OVER_HINT) { + text.insert(0, "(" + KeymapUtil.getFirstKeyboardShortcutText("ShowErrorDescription") + ") ", + SimpleTextAttributes.GRAYED_ATTRIBUTES); + } + JComponent component = createExpandableHintComponent(text, new Runnable() { @Override public void run() { diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/evaluate/quick/common/AbstractValueHint.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/evaluate/quick/common/AbstractValueHint.java index 7b860b49d255..c24ce74df1c6 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/evaluate/quick/common/AbstractValueHint.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/evaluate/quick/common/AbstractValueHint.java @@ -65,7 +65,7 @@ public abstract class AbstractValueHint { private final Project myProject; private final Editor myEditor; private final ValueHintType myType; - private final Point myPoint; + protected final Point myPoint; private LightweightHint myCurrentHint; private boolean myHintHidden; private TextRange myCurrentRange; @@ -132,6 +132,10 @@ public abstract class AbstractValueHint { } } + public void invokeHint() { + invokeHint(null); + } + public void invokeHint(Runnable hideRunnable) { myHideRunnable = hideRunnable; diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/evaluate/quick/common/DebuggerTreeWithHistoryContainer.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/evaluate/quick/common/DebuggerTreeWithHistoryContainer.java index c7c5af9f6fb4..2851c701ef70 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/evaluate/quick/common/DebuggerTreeWithHistoryContainer.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/evaluate/quick/common/DebuggerTreeWithHistoryContainer.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -170,7 +170,7 @@ abstract class DebuggerTreeWithHistoryContainer { } @Override - public void onFailure(Throwable t) { + public void onFailure(@NotNull Throwable t) { LOG.debug(t); } }); diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/evaluate/quick/common/DebuggerTreeWithHistoryPopup.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/evaluate/quick/common/DebuggerTreeWithHistoryPopup.java index e4ea46b69ea9..97edec9133ba 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/evaluate/quick/common/DebuggerTreeWithHistoryPopup.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/evaluate/quick/common/DebuggerTreeWithHistoryPopup.java @@ -20,6 +20,7 @@ import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.popup.JBPopup; import com.intellij.openapi.ui.popup.JBPopupFactory; +import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.Disposer; import com.intellij.ui.ScreenUtil; import com.intellij.ui.awt.RelativePoint; @@ -64,7 +65,7 @@ class DebuggerTreeWithHistoryPopup extends DebuggerTreeWithHistoryContainer extends DebuggerTreeWithHistoryContainer() { + @Override + public Boolean compute() { + Window parent = SwingUtilities.getWindowAncestor(tree); + if (parent != null) { + for (Window child : parent.getOwnedWindows()) { + if (child.isShowing()) { + return false; + } + } + } + return true; + } + }) .createPopup(); if (tree instanceof Disposable) { diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/WatchInplaceEditor.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/WatchInplaceEditor.java index 683c27e30e18..00ceef568c21 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/WatchInplaceEditor.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/WatchInplaceEditor.java @@ -39,7 +39,7 @@ public class WatchInplaceEditor extends XDebuggerTreeInplaceEditor { @Nullable private final WatchNode myOldNode; public WatchInplaceEditor(@NotNull WatchesRootNode rootNode, - @NotNull XDebugSession session, XWatchesView watchesView, final WatchNode node, + @Nullable XDebugSession session, XWatchesView watchesView, final WatchNode node, @NonNls final String historyId, final @Nullable WatchNode oldNode) { super((XDebuggerTreeNode)node, historyId); @@ -47,7 +47,9 @@ public class WatchInplaceEditor extends XDebuggerTreeInplaceEditor { myWatchesView = watchesView; myOldNode = oldNode; myExpressionEditor.setExpression(oldNode != null ? oldNode.getExpression() : null); - new WatchEditorSessionListener(session).install(); + if (session != null) { + new WatchEditorSessionListener(session).install(); + } } @Override diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XDebugView.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XDebugView.java index 0007ec2f9997..847dd9004a49 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XDebugView.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XDebugView.java @@ -19,6 +19,7 @@ import com.intellij.execution.ui.layout.ViewContext; import com.intellij.ide.DataManager; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.DataContext; +import com.intellij.openapi.actionSystem.DataKey; import com.intellij.ui.content.ContentManager; import com.intellij.util.SingleAlarm; import com.intellij.xdebugger.XDebugSession; @@ -66,15 +67,20 @@ public abstract class XDebugView implements Disposable { @Nullable public static XDebugSession getSession(@NotNull Component component) { + return getData(XDebugSession.DATA_KEY, component); + } + + @Nullable + public static T getData(DataKey key, @NotNull Component component) { DataContext dataContext = DataManager.getInstance().getDataContext(component); ViewContext viewContext = ViewContext.CONTEXT_KEY.getData(dataContext); ContentManager contentManager = viewContext == null ? null : viewContext.getContentManager(); if (contentManager != null) { - XDebugSession session = XDebugSession.DATA_KEY.getData(DataManager.getInstance().getDataContext(contentManager.getComponent())); - if (session != null) { - return session; + T data = key.getData(DataManager.getInstance().getDataContext(contentManager.getComponent())); + if (data != null) { + return data; } } - return XDebugSession.DATA_KEY.getData(dataContext); + return key.getData(dataContext); } } diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XDebugViewSessionListener.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XDebugViewSessionListener.java index d790113b1816..f124078f5a57 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XDebugViewSessionListener.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XDebugViewSessionListener.java @@ -16,7 +16,6 @@ package com.intellij.xdebugger.impl.frame; import com.intellij.ui.AppUIUtil; -import com.intellij.xdebugger.XDebugSession; import com.intellij.xdebugger.XDebugSessionAdapter; import org.jetbrains.annotations.NotNull; @@ -25,15 +24,13 @@ import org.jetbrains.annotations.NotNull; */ public class XDebugViewSessionListener extends XDebugSessionAdapter { private final XDebugView myDebugView; - private final XDebugSession session; - public XDebugViewSessionListener(@NotNull XDebugView debugView, @NotNull XDebugSession session) { + public XDebugViewSessionListener(@NotNull XDebugView debugView) { myDebugView = debugView; - this.session = session; } private void onSessionEvent(final @NotNull XDebugView.SessionEvent event) { - AppUIUtil.invokeLaterIfProjectAlive(session.getProject(), new Runnable() { + AppUIUtil.invokeOnEdt(new Runnable() { @Override public void run() { myDebugView.processSessionEvent(event); diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XDebuggerFramesList.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XDebuggerFramesList.java index 9b39ad63deb8..63a1cfeea6af 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XDebuggerFramesList.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XDebuggerFramesList.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ */ package com.intellij.xdebugger.impl.frame; +import com.intellij.openapi.actionSystem.CommonDataKeys; +import com.intellij.openapi.actionSystem.DataProvider; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiFile; @@ -27,7 +29,9 @@ import com.intellij.util.ui.UIUtil; import com.intellij.xdebugger.XDebuggerBundle; import com.intellij.xdebugger.XSourcePosition; import com.intellij.xdebugger.frame.XStackFrame; +import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; @@ -78,7 +82,7 @@ public class XDebuggerFramesList extends DebuggerFramesList { } @Override - public int getSourceActions(JComponent c) { + public int getSourceActions(@NotNull JComponent c) { return COPY; } }; @@ -90,6 +94,17 @@ public class XDebuggerFramesList extends DebuggerFramesList { doInit(); setTransferHandler(DEFAULT_TRANSFER_HANDLER); + setDataProvider(new DataProvider() { + @Nullable + @Override + public Object getData(@NonNls String dataId) { + if (CommonDataKeys.VIRTUAL_FILE.is(dataId) && mySelectedFrame != null) { + XSourcePosition position = mySelectedFrame.getSourcePosition(); + return position != null ? position.getFile() : null; + } + return null; + } + }); } @Override diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XFramesView.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XFramesView.java index 9999ac015b77..cd1fee38b074 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XFramesView.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XFramesView.java @@ -61,6 +61,7 @@ public class XFramesView extends XDebugView { private final ComboBox myThreadComboBox; private final Set myExecutionStacks = ContainerUtil.newHashSet(); private XExecutionStack mySelectedStack; + private int mySelectedFrameIndex; private boolean myListenersEnabled; private final Map myBuilders = new HashMap(); private final ActionToolbarImpl myToolbar; @@ -76,7 +77,7 @@ public class XFramesView extends XDebugView { @Override public void valueChanged(ListSelectionEvent e) { if (myListenersEnabled && !e.getValueIsAdjusting()) { - processFrameSelection(e); + processFrameSelection(getSession(e)); } } }); @@ -86,7 +87,7 @@ public class XFramesView extends XDebugView { if (myListenersEnabled) { int i = myFramesList.locationToIndex(e.getPoint()); if (i != -1 && myFramesList.isSelectedIndex(i)) { - processFrameSelection(e); + processFrameSelection(getSession(e)); } } } @@ -115,9 +116,10 @@ public class XFramesView extends XDebugView { if (e.getStateChange() == ItemEvent.SELECTED) { Object item = e.getItem(); - if (item instanceof XExecutionStack) { + if (item != mySelectedStack && item instanceof XExecutionStack) { XDebugSession session = getSession(e); if (session != null) { + mySelectedFrameIndex = 0; updateFrames((XExecutionStack)item, session); } } @@ -209,16 +211,21 @@ public class XFramesView extends XDebugView { XStackFrame currentStackFrame = session == null ? null : session.getCurrentStackFrame(); if (currentStackFrame != null) { myFramesList.setSelectedValue(currentStackFrame, true); + mySelectedFrameIndex = myFramesList.getSelectedIndex(); } return; } + if (event != SessionEvent.SETTINGS_CHANGED) { + mySelectedFrameIndex = 0; + mySelectedStack = null; + } + myListenersEnabled = false; for (StackFramesListBuilder builder : myBuilders.values()) { builder.dispose(); } myBuilders.clear(); - mySelectedStack = null; XSuspendContext suspendContext = session == null ? null : session.getSuspendContext(); if (suspendContext == null) { requestClear(); @@ -234,7 +241,7 @@ public class XFramesView extends XDebugView { XExecutionStack[] executionStacks = suspendContext.getExecutionStacks(); addExecutionStacks(Arrays.asList(executionStacks)); - XExecutionStack activeExecutionStack = suspendContext.getActiveExecutionStack(); + XExecutionStack activeExecutionStack = mySelectedStack != null ? mySelectedStack : suspendContext.getActiveExecutionStack(); myThreadComboBox.setSelectedItem(activeExecutionStack); myThreadsPanel.removeAll(); myThreadsPanel.add(myToolbar.getComponent(), BorderLayout.EAST); @@ -265,9 +272,6 @@ public class XFramesView extends XDebugView { } private void updateFrames(final XExecutionStack executionStack, @NotNull XDebugSession session) { - if (mySelectedStack == executionStack) { - return; - } if (mySelectedStack != null) { getOrCreateBuilder(mySelectedStack, session).stop(); } @@ -275,6 +279,7 @@ public class XFramesView extends XDebugView { mySelectedStack = executionStack; if (executionStack != null) { StackFramesListBuilder builder = getOrCreateBuilder(executionStack, session); + myListenersEnabled = false; builder.initModel(myFramesList.getModel()); builder.start(); } @@ -292,12 +297,12 @@ public class XFramesView extends XDebugView { return myMainPanel; } - private void processFrameSelection(@NotNull EventObject e) { + private void processFrameSelection(XDebugSession session) { + mySelectedFrameIndex = myFramesList.getSelectedIndex(); Object selected = myFramesList.getSelectedValue(); if (selected instanceof XStackFrame) { - XDebugSession session = getSession(e); if (session != null) { - session.setCurrentStackFrame(mySelectedStack, (XStackFrame)selected, myFramesList.getSelectedIndex() == 0); + session.setCurrentStackFrame(mySelectedStack, (XStackFrame)selected, mySelectedFrameIndex == 0); } } } @@ -324,9 +329,7 @@ public class XFramesView extends XDebugView { public void run() { myStackFrames.addAll(stackFrames); addFrameListElements(stackFrames, last); - if (myNextFrameIndex == 0) { - selectTopFrame(); - } + selectCurrentFrame(); myNextFrameIndex += stackFrames.size(); myAllFramesLoaded = last; if (last) { @@ -390,11 +393,13 @@ public class XFramesView extends XDebugView { myRunning = false; } - private void selectTopFrame() { - if (!myStackFrames.isEmpty() && mySelectedStack != null) { - XStackFrame topFrame = myStackFrames.get(0); - myFramesList.setSelectedValue(topFrame, true); - mySession.setCurrentStackFrame(mySelectedStack, topFrame, true); + private void selectCurrentFrame() { + if (mySelectedStack != null && + myFramesList.getSelectedIndex() != mySelectedFrameIndex && + myFramesList.getElementCount() > mySelectedFrameIndex && + myFramesList.getModel().get(mySelectedFrameIndex) != null) { + myFramesList.setSelectedIndex(mySelectedFrameIndex); + processFrameSelection(mySession); myListenersEnabled = true; } } @@ -411,7 +416,7 @@ public class XFramesView extends XDebugView { else if (!myAllFramesLoaded) { model.addElement(null); } - selectTopFrame(); + selectCurrentFrame(); } } } diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XVariablesViewBase.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XVariablesViewBase.java index d4bd27c30d9b..4bfcfa58855f 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XVariablesViewBase.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XVariablesViewBase.java @@ -15,34 +15,34 @@ */ package com.intellij.xdebugger.impl.frame; -import com.intellij.codeInsight.hint.HintManager; -import com.intellij.codeInsight.hint.HintUtil; import com.intellij.ide.dnd.DnDManager; -import com.intellij.openapi.Disposable; +import com.intellij.openapi.application.AccessToken; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.LogicalPosition; import com.intellij.openapi.editor.event.SelectionEvent; import com.intellij.openapi.editor.event.SelectionListener; +import com.intellij.openapi.editor.impl.SelectionModelImpl; import com.intellij.openapi.fileEditor.FileEditor; import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx; import com.intellij.openapi.fileEditor.impl.text.PsiAwareTextEditorImpl; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.ui.SimpleColoredComponent; -import com.intellij.ui.SimpleColoredText; +import com.intellij.xdebugger.XDebugSession; import com.intellij.xdebugger.XDebuggerBundle; import com.intellij.xdebugger.XSourcePosition; +import com.intellij.xdebugger.evaluation.ExpressionInfo; import com.intellij.xdebugger.evaluation.XDebuggerEditorsProvider; import com.intellij.xdebugger.evaluation.XDebuggerEvaluator; -import com.intellij.xdebugger.frame.XFullValueEvaluator; import com.intellij.xdebugger.frame.XStackFrame; import com.intellij.xdebugger.frame.XValue; -import com.intellij.xdebugger.frame.XValuePlace; -import com.intellij.xdebugger.frame.presentation.XValuePresentation; import com.intellij.xdebugger.impl.actions.XDebuggerActions; +import com.intellij.xdebugger.impl.evaluate.quick.XValueHint; +import com.intellij.xdebugger.impl.evaluate.quick.common.ValueHintType; import com.intellij.xdebugger.impl.ui.tree.XDebuggerTree; import com.intellij.xdebugger.impl.ui.tree.XDebuggerTreePanel; import com.intellij.xdebugger.impl.ui.tree.XDebuggerTreeRestorer; @@ -50,11 +50,11 @@ import com.intellij.xdebugger.impl.ui.tree.XDebuggerTreeState; import com.intellij.xdebugger.impl.ui.tree.nodes.XEvaluationCallbackBase; import com.intellij.xdebugger.impl.ui.tree.nodes.XStackFrameNode; import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl; -import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodePresentationConfigurator; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; +import java.awt.*; import java.util.HashMap; import java.util.Set; @@ -88,68 +88,53 @@ public abstract class XVariablesViewBase extends XDebugView { myTreeRestorer = myTreeState.restoreState(tree); } if (position != null && Registry.is("ide.debugger.inline")) { - final VirtualFile file = position.getFile(); - final FileEditor fileEditor = FileEditorManagerEx.getInstanceEx(project).getSelectedEditor(file); - if (fileEditor instanceof PsiAwareTextEditorImpl) { - final Editor editor = ((PsiAwareTextEditorImpl)fileEditor).getEditor(); - final SelectionListener listener = new SelectionListener() { - @Override - public void selectionChanged(SelectionEvent e) { - final String text = editor.getDocument().getText(e.getNewRange()); - final XDebuggerEvaluator evaluator = stackFrame.getEvaluator(); - if (evaluator != null && !StringUtil.isEmpty(text) - && !(text.contains("exec(") || text.contains("++") || text.contains("--") || text.contains("="))) { - evaluator.evaluate(text, new XEvaluationCallbackBase() { - @Override - public void evaluated(@NotNull XValue result) { - result.computePresentation(new XValueNodePresentationConfigurator.ConfigurableXValueNodeImpl() { - @Override - public void applyPresentation(@Nullable Icon icon, - @NotNull XValuePresentation valuePresenter, - boolean hasChildren) { - SimpleColoredText text = new SimpleColoredText(); - XValueNodeImpl.buildText(valuePresenter, text, false); - SimpleColoredComponent component = HintUtil.createInformationComponent(); - text.appendToComponent(component); - String str = text.toString(); - if ("undefined".equals(str) || str.startsWith("Cannot find local variable") - || str.startsWith("Invalid expression")) { - return; //todo[kb] this is temporary solution - } - HintManager.getInstance().hideAllHints(); - HintManager.getInstance().showInformationHint(editor, component); - } + registerInlineEvaluator(stackFrame, tree, position, project); + } + } - @Override - public void setFullValueEvaluator(@NotNull XFullValueEvaluator fullValueEvaluator) { - } + private void registerInlineEvaluator(final XStackFrame stackFrame, + XDebuggerTree tree, + final XSourcePosition position, + final Project project) { + final VirtualFile file = position.getFile(); + final FileEditor fileEditor = FileEditorManagerEx.getInstanceEx(project).getSelectedEditor(file); + if (fileEditor instanceof PsiAwareTextEditorImpl) { + final Editor editor = ((PsiAwareTextEditorImpl)fileEditor).getEditor(); + final SelectionListener listener = new SelectionListener() { + @Override + public void selectionChanged(final SelectionEvent e) { + final String text = editor.getDocument().getText(e.getNewRange()); + final XDebuggerEvaluator evaluator = stackFrame.getEvaluator(); + if (evaluator != null && !StringUtil.isEmpty(text) + && !(text.contains("exec(") || text.contains("++") || text.contains("--") || text.contains("="))) { + evaluator.evaluate(text, new XEvaluationCallbackBase() { + @Override + public void evaluated(@NotNull XValue result) { + final AccessToken token = ApplicationManager.getApplication().acquireReadActionLock(); + try { + final XDebugSession session = getSession(getTree()); + if (session == null) return; + final TextRange range = e.getNewRange(); + final ExpressionInfo info = new ExpressionInfo(range); + final int offset = range.getStartOffset(); + final LogicalPosition pos = editor.offsetToLogicalPosition(offset); + final Point point = editor.logicalPositionToXY(pos); - @Override - public boolean isObsolete() { - return true; - } - }, XValuePlace.TOOLTIP); + new XValueHint(project, editor, point, ValueHintType.MOUSE_OVER_HINT, info, evaluator, session).invokeHint(); } - - @Override - public void errorOccurred(@NotNull String errorMessage) { - System.out.println(errorMessage); + finally { + token.finish(); } - }, position); - } - } - }; - editor.getSelectionModel().addSelectionListener(listener); - Disposer.register(tree, new Disposable() { - @Override - public void dispose() { - final FileEditor fileEditor = FileEditorManagerEx.getInstanceEx(project).getSelectedEditor(file); - if (fileEditor instanceof PsiAwareTextEditorImpl) { - ((PsiAwareTextEditorImpl)fileEditor).getEditor().getSelectionModel().removeSelectionListener(listener); - } + } + + @Override + public void errorOccurred(@NotNull String errorMessage) { + } + }, position); } - }); - } + } + }; + ((SelectionModelImpl)editor.getSelectionModel()).addSelectionListener(listener, tree); } } diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XWatchesViewImpl.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XWatchesViewImpl.java index c510cd17c90a..8b94e2c4858a 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XWatchesViewImpl.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/frame/XWatchesViewImpl.java @@ -40,6 +40,7 @@ import com.intellij.xdebugger.frame.XStackFrame; import com.intellij.xdebugger.impl.XDebugSessionImpl; import com.intellij.xdebugger.impl.actions.XDebuggerActions; import com.intellij.xdebugger.impl.breakpoints.XExpressionImpl; +import com.intellij.xdebugger.impl.ui.XDebugSessionData; import com.intellij.xdebugger.impl.ui.XDebugSessionTab; import com.intellij.xdebugger.impl.ui.tree.XDebuggerTree; import com.intellij.xdebugger.impl.ui.tree.XDebuggerTreePanel; @@ -220,13 +221,9 @@ public class XWatchesViewImpl extends XDebugView implements DnDNativeTarget, XWa @Override public void addWatchExpression(@NotNull XExpression expression, int index, final boolean navigateToWatchNode) { XDebugSession session = getSession(getTree()); - if (session == null) { - return; - } - - myRootNode.addWatchExpression(session.getDebugProcess().getEvaluator(), expression, index, navigateToWatchNode); + myRootNode.addWatchExpression(session != null ? session.getDebugProcess().getEvaluator() : null, expression, index, navigateToWatchNode); updateSessionData(); - if (navigateToWatchNode) { + if (navigateToWatchNode && session != null) { showWatchesTab((XDebugSessionImpl)session); } } @@ -342,8 +339,15 @@ public class XWatchesViewImpl extends XDebugView implements DnDNativeTarget, XWa } XDebugSession session = getSession(getTree()); + XExpression[] expressions = watchExpressions.toArray(new XExpression[watchExpressions.size()]); if (session != null) { - ((XDebugSessionImpl)session).setWatchExpressions(watchExpressions.toArray(new XExpression[watchExpressions.size()])); + ((XDebugSessionImpl)session).setWatchExpressions(expressions); + } + else { + XDebugSessionData data = getData(XDebugSessionData.DATA_KEY, getTree()); + if (data != null) { + data.setWatchExpressions(expressions); + } } } diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/settings/DebuggerConfigurable.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/settings/DebuggerConfigurable.java index 58896a87f596..3d0db0abc7fb 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/settings/DebuggerConfigurable.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/settings/DebuggerConfigurable.java @@ -22,6 +22,7 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.SmartList; import com.intellij.util.containers.ContainerUtil; import com.intellij.xdebugger.XDebuggerBundle; +import com.intellij.xdebugger.breakpoints.XBreakpointType; import com.intellij.xdebugger.impl.DebuggerSupport; import com.intellij.xdebugger.settings.DebuggerConfigurableProvider; import com.intellij.xdebugger.settings.DebuggerSettingsCategory; @@ -159,7 +160,7 @@ public class DebuggerConfigurable implements SearchableConfigurable.Parent { @Override public boolean isVisible() { - return true; + return XBreakpointType.EXTENSION_POINT_NAME.getExtensions().length != 0; } @Override diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/settings/DebuggerConfigurableProvider.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/settings/DebuggerConfigurableProvider.java new file mode 100644 index 000000000000..bb747f92eab7 --- /dev/null +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/settings/DebuggerConfigurableProvider.java @@ -0,0 +1,34 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.xdebugger.impl.settings; + +import com.intellij.openapi.options.Configurable; +import com.intellij.openapi.options.ConfigurableProvider; +import com.intellij.xdebugger.breakpoints.XBreakpointType; +import org.jetbrains.annotations.NotNull; + +public final class DebuggerConfigurableProvider extends ConfigurableProvider { + @NotNull + @Override + public Configurable createConfigurable() { + return new DebuggerConfigurable(); + } + + @Override + public boolean canCreateConfigurable() { + return XBreakpointType.EXTENSION_POINT_NAME.getExtensions().length != 0; + } +} diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/DebuggerSessionTabBase.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/DebuggerSessionTabBase.java index 053f18dd14bf..2bdbb7faa80d 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/DebuggerSessionTabBase.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/DebuggerSessionTabBase.java @@ -16,26 +16,22 @@ package com.intellij.xdebugger.impl.ui; import com.intellij.debugger.ui.DebuggerContentInfo; -import com.intellij.diagnostic.logging.AdditionalTabComponent; -import com.intellij.diagnostic.logging.DebuggerLogConsoleManager; import com.intellij.execution.ExecutionManager; import com.intellij.execution.configurations.RunConfigurationBase; import com.intellij.execution.configurations.RunProfile; import com.intellij.execution.executors.DefaultDebugExecutor; -import com.intellij.execution.process.ProcessHandler; -import com.intellij.execution.runners.ExecutionEnvironment; import com.intellij.execution.runners.RunContentBuilder; import com.intellij.execution.runners.RunTab; -import com.intellij.execution.ui.*; +import com.intellij.execution.ui.ConsoleViewContentType; +import com.intellij.execution.ui.ExecutionConsole; +import com.intellij.execution.ui.ObservableConsoleView; import com.intellij.execution.ui.layout.LayoutAttractionPolicy; import com.intellij.execution.ui.layout.LayoutViewOptions; -import com.intellij.icons.AllIcons; import com.intellij.ide.impl.ProjectUtil; import com.intellij.ide.ui.customization.CustomActionsSchema; import com.intellij.openapi.actionSystem.ActionGroup; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.registry.Registry; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.ui.AppIcon; @@ -44,20 +40,17 @@ import com.intellij.xdebugger.XDebuggerBundle; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import javax.swing.*; import java.util.Collection; /** * @author nik */ -public abstract class DebuggerSessionTabBase extends RunTab implements DebuggerLogConsoleManager { +public abstract class DebuggerSessionTabBase extends RunTab { protected ExecutionConsole myConsole; public DebuggerSessionTabBase(@NotNull Project project, @NotNull String runnerId, @NotNull String sessionName, @NotNull GlobalSearchScope searchScope) { super(project, searchScope, runnerId, XDebuggerBundle.message("xdebugger.default.content.title"), sessionName); - Disposer.register(project, this); - myUi.getDefaults() .initTabDefaults(0, XDebuggerBundle.message("xdebugger.debugger.tab.title"), null) .initFocusContent(DebuggerContentInfo.FRAME_CONTENT, XDebuggerUIConstants.LAYOUT_VIEW_BREAKPOINT_CONDITION) @@ -68,32 +61,6 @@ public abstract class DebuggerSessionTabBase extends RunTab implements DebuggerL return (ActionGroup)CustomActionsSchema.getInstance().getCorrectedAction(id); } - public abstract RunContentDescriptor getRunContentDescriptor(); - - @Override - public ProcessHandler getProcessHandler() { - return getRunContentDescriptor().getProcessHandler(); - } - - @Override - protected Content createLogContent(AdditionalTabComponent tabComponent, String id, Icon icon) { - Content result = super.createLogContent(tabComponent, id, icon); - result.setCloseable(false); - result.setDescription(tabComponent.getTooltip()); - return result; - } - - @Override - protected Icon getDefaultIcon() { - return AllIcons.FileTypes.Text; - } - - @Override - @NotNull - public RunnerLayoutUi getUi() { - return myUi; - } - protected void attachNotificationTo(final Content content) { if (myConsole instanceof ObservableConsoleView) { ObservableConsoleView observable = (ObservableConsoleView)myConsole; @@ -107,12 +74,11 @@ public abstract class DebuggerSessionTabBase extends RunTab implements DebuggerL }, content); RunProfile profile = getRunProfile(); if (profile instanceof RunConfigurationBase && !ApplicationManager.getApplication().isUnitTestMode()) { - final RunConfigurationBase runConfigurationBase = (RunConfigurationBase)profile; - observable.addChangeListener(new RunContentBuilder.ConsoleToFrontListener(runConfigurationBase, - getProject(), + observable.addChangeListener(new RunContentBuilder.ConsoleToFrontListener((RunConfigurationBase)profile, + myProject, DefaultDebugExecutor.getDebugExecutorInstance(), myRunContentDescriptor, - getUi()), + myUi), content); } } @@ -120,21 +86,20 @@ public abstract class DebuggerSessionTabBase extends RunTab implements DebuggerL @Nullable protected RunProfile getRunProfile() { - ExecutionEnvironment environment = getEnvironment(); - return environment != null ? environment.getRunProfile() : null; + return myEnvironment != null ? myEnvironment.getRunProfile() : null; } public void toFront(boolean focus) { if (!ApplicationManager.getApplication().isUnitTestMode()) { - ExecutionManager.getInstance(getProject()).getContentManager().toFrontRunContent(DefaultDebugExecutor.getDebugExecutorInstance(), myRunContentDescriptor); + ExecutionManager.getInstance(myProject).getContentManager().toFrontRunContent(DefaultDebugExecutor.getDebugExecutorInstance(), myRunContentDescriptor); if (focus) { ApplicationManager.getApplication().invokeLater(new Runnable() { @Override public void run() { boolean focusWnd = Registry.is("debugger.mayBringFrameToFrontOnBreakpoint"); - ProjectUtil.focusProjectWindow(getProject(), focusWnd); + ProjectUtil.focusProjectWindow(myProject, focusWnd); if (!focusWnd) { - AppIcon.getInstance().requestAttention(getProject(), true); + AppIcon.getInstance().requestAttention(myProject, true); } } }); diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/XDebugSessionTab.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/XDebugSessionTab.java index 1a4505f6d85b..881ed56bad3d 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/XDebugSessionTab.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/XDebugSessionTab.java @@ -20,8 +20,8 @@ import com.intellij.execution.Executor; import com.intellij.execution.executors.DefaultDebugExecutor; import com.intellij.execution.runners.ExecutionEnvironment; import com.intellij.execution.runners.RunContentBuilder; -import com.intellij.execution.ui.ExecutionConsole; import com.intellij.execution.ui.RunContentDescriptor; +import com.intellij.execution.ui.RunnerLayoutUi; import com.intellij.execution.ui.actions.CloseAction; import com.intellij.execution.ui.layout.PlaceInGrid; import com.intellij.execution.ui.layout.impl.ViewImpl; @@ -83,6 +83,11 @@ public class XDebugSessionTab extends DebuggerSessionTabBase { return new XDebugSessionTab(session, icon, environment); } + @NotNull + public RunnerLayoutUi getUi() { + return myUi; + } + private XDebugSessionTab(@NotNull XDebugSessionImpl session, @Nullable Icon icon, @Nullable ExecutionEnvironment environment) { @@ -95,7 +100,7 @@ public class XDebugSessionTab extends DebuggerSessionTabBase { myUi.addContent(createWatchesContent(session), 0, PlaceInGrid.right, false); for (XDebugView view : myViews) { - Disposer.register(this, view); + Disposer.register(myRunContentDescriptor, view); } attachToSession(session); @@ -115,20 +120,19 @@ public class XDebugSessionTab extends DebuggerSessionTabBase { } } } - }, this); + }, myRunContentDescriptor); rebuildViews(); } private void setSession(@NotNull XDebugSessionImpl session, @Nullable ExecutionEnvironment environment, @Nullable Icon icon) { - if (environment != null) { - setEnvironment(environment); - } - + myEnvironment = environment; mySession = session; mySessionData = session.getSessionData(); myConsole = session.getConsoleView(); myRunContentDescriptor = new RunContentDescriptor(myConsole, session.getDebugProcess().getProcessHandler(), myUi.getComponent(), session.getSessionName(), icon); + Disposer.register(myRunContentDescriptor, this); + Disposer.register(myProject, myRunContentDescriptor); } @Nullable @@ -180,7 +184,7 @@ public class XDebugSessionTab extends DebuggerSessionTabBase { @NotNull private Content createFramesContent() { - XFramesView framesView = new XFramesView(getProject()); + XFramesView framesView = new XFramesView(myProject); myViews.add(framesView); Content framesContent = myUi.createContent(DebuggerContentInfo.FRAME_CONTENT, framesView.getMainPanel(), XDebuggerBundle.message("debugger.session.tab.frames.title"), AllIcons.Debugger.Frame, null); @@ -188,12 +192,8 @@ public class XDebugSessionTab extends DebuggerSessionTabBase { return framesContent; } - public ExecutionConsole getConsole() { - return myConsole; - } - public void rebuildViews() { - AppUIUtil.invokeLaterIfProjectAlive(getProject(), new Runnable() { + AppUIUtil.invokeLaterIfProjectAlive(myProject, new Runnable() { @Override public void run() { for (XDebugView view : myViews) { @@ -209,7 +209,7 @@ public class XDebugSessionTab extends DebuggerSessionTabBase { private void attachToSession(@NotNull XDebugSessionImpl session) { for (XDebugView view : myViews) { - session.addSessionListener(new XDebugViewSessionListener(view, session), this); + session.addSessionListener(new XDebugViewSessionListener(view), myRunContentDescriptor); } XDebugTabLayouter layouter = session.getDebugProcess().createTabLayouter(); @@ -225,8 +225,7 @@ public class XDebugSessionTab extends DebuggerSessionTabBase { DefaultActionGroup leftToolbar = new DefaultActionGroup(); final Executor debugExecutor = DefaultDebugExecutor.getDebugExecutorInstance(); - ExecutionEnvironment environment = getEnvironment(); - if (environment != null) { + if (myEnvironment != null) { leftToolbar.add(ActionManager.getInstance().getAction(IdeActions.ACTION_RERUN)); List additionalRestartActions = session.getRestartActions(); if (!additionalRestartActions.isEmpty()) { @@ -247,24 +246,12 @@ public class XDebugSessionTab extends DebuggerSessionTabBase { leftToolbar.add(myUi.getOptions().getLayoutActions()); final AnAction[] commonSettings = myUi.getOptions().getSettingsActionsList(); - final AnAction commonSettingsList = myUi.getOptions().getSettingsActions(); - - final DefaultActionGroup settings = new DefaultActionGroup("DebuggerSettings", commonSettings.length > 0) { - @Override - public void update(AnActionEvent e) { - e.getPresentation().setText(ActionsBundle.message("group.XDebugger.settings.text")); - e.getPresentation().setIcon(commonSettingsList.getTemplatePresentation().getIcon()); - } - - @Override - public boolean isDumbAware() { - return true; - } - }; - for (AnAction each : commonSettings) { - settings.add(each); - } + DefaultActionGroup settings = new DefaultActionGroup(ActionsBundle.message("group.XDebugger.settings.text"), true); + settings.getTemplatePresentation().setIcon(myUi.getOptions().getSettingsActions().getTemplatePresentation().getIcon()); if (commonSettings.length > 0) { + for (AnAction each : commonSettings) { + settings.add(each); + } settings.addSeparator(); } if (!session.getDebugProcess().isValuesCustomSorted()) { @@ -276,18 +263,18 @@ public class XDebugSessionTab extends DebuggerSessionTabBase { leftToolbar.addSeparator(); leftToolbar.add(PinToolwindowTabAction.getPinAction()); - leftToolbar.add(new CloseAction(environment != null ? environment.getExecutor() : debugExecutor, myRunContentDescriptor, getProject())); + leftToolbar.add(new CloseAction(myEnvironment != null ? myEnvironment.getExecutor() : debugExecutor, myRunContentDescriptor, myProject)); leftToolbar.add(new ContextHelpAction(debugExecutor.getHelpId())); DefaultActionGroup topToolbar = new DefaultActionGroup(); topToolbar.addAll(getCustomizedActionGroup(XDebuggerActions.TOOL_WINDOW_TOP_TOOLBAR_GROUP)); - session.getDebugProcess().registerAdditionalActions(leftToolbar, topToolbar); + session.getDebugProcess().registerAdditionalActions(leftToolbar, topToolbar, settings); myUi.getOptions().setLeftToolbar(leftToolbar, ActionPlaces.DEBUGGER_TOOLBAR); myUi.getOptions().setTopToolbar(topToolbar, ActionPlaces.DEBUGGER_TOOLBAR); - if (environment != null) { - initLogConsoles(environment.getRunProfile(), myRunContentDescriptor.getProcessHandler(), myConsole); + if (myEnvironment != null) { + initLogConsoles(myEnvironment.getRunProfile(), myRunContentDescriptor, myConsole); } } @@ -296,7 +283,6 @@ public class XDebugSessionTab extends DebuggerSessionTabBase { mySession = null; } - @Override @Nullable public RunContentDescriptor getRunContentDescriptor() { return myRunContentDescriptor; diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/ValueMarkerPresentationDialog.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/ValueMarkerPresentationDialog.java index 4e164cc7fd15..e135bb1f4629 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/ValueMarkerPresentationDialog.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/ValueMarkerPresentationDialog.java @@ -17,12 +17,14 @@ package com.intellij.xdebugger.impl.ui.tree; import org.jetbrains.annotations.Nullable; +import java.awt.Component; + /** * @author nik */ public class ValueMarkerPresentationDialog extends ValueMarkerPresentationDialogBase { - public ValueMarkerPresentationDialog(@Nullable String defaultText) { - super(defaultText); + public ValueMarkerPresentationDialog(@Nullable Component parent, @Nullable String defaultText) { + super(parent, defaultText); init(); } } diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/ValueMarkerPresentationDialogBase.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/ValueMarkerPresentationDialogBase.java index 8bb4327e36ff..fc7303460cbf 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/ValueMarkerPresentationDialogBase.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/ValueMarkerPresentationDialogBase.java @@ -38,8 +38,8 @@ public abstract class ValueMarkerPresentationDialogBase extends DialogWrapper { private FixedSizeButton myChooseColorButton; private JPanel mySamplePanel; - public ValueMarkerPresentationDialogBase(final @Nullable String defaultText) { - super(true); + public ValueMarkerPresentationDialogBase(@Nullable Component parent, final @Nullable String defaultText) { + super(parent, true); setTitle("Select Object Label"); setModal(true); myLabelField.getDocument().addDocumentListener(new DocumentAdapter() { diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/nodes/WatchesRootNode.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/nodes/WatchesRootNode.java index cf67f7d5fdb5..2e4e168b8d84 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/nodes/WatchesRootNode.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/nodes/WatchesRootNode.java @@ -188,9 +188,7 @@ public class WatchesRootNode extends XDebuggerTreeNode { fireNodeStructureChanged(messageNode); } XDebugSession session = XDebugView.getSession(myTree); - if (session != null) { - new WatchInplaceEditor(this, session, myWatchesView, messageNode, "watch", node).show(); - } + new WatchInplaceEditor(this, session, myWatchesView, messageNode, "watch", node).show(); } private class MyEvaluationCallback extends XEvaluationCallbackBase { diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/nodes/XValueContainerNode.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/nodes/XValueContainerNode.java index a2fb3d51d253..5a7c4bb6d0d8 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/nodes/XValueContainerNode.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/nodes/XValueContainerNode.java @@ -187,14 +187,14 @@ public abstract class XValueContainerNodeemptyList()); final int[] indices = getNodesIndices(allMessageChildren); final TreeNode[] nodes = getChildNodes(indices); - myMessageChildren = null; - myTemporaryMessageChildren = null; fireNodesRemoved(indices, nodes); if (!temporary) { myMessageChildren = messages; + myTemporaryMessageChildren = null; } else { myTemporaryMessageChildren = messages; + myMessageChildren = null; } myCachedAllChildren = null; fireNodesInserted(messages); diff --git a/platform/xdebugger-impl/testSrc/com/intellij/xdebugger/XDebuggerTestUtil.java b/platform/xdebugger-impl/testSrc/com/intellij/xdebugger/XDebuggerTestUtil.java index e47002649d5a..e11ac871e3b4 100644 --- a/platform/xdebugger-impl/testSrc/com/intellij/xdebugger/XDebuggerTestUtil.java +++ b/platform/xdebugger-impl/testSrc/com/intellij/xdebugger/XDebuggerTestUtil.java @@ -35,8 +35,8 @@ import com.intellij.xdebugger.frame.*; import com.intellij.xdebugger.frame.XNamedValue; import com.intellij.xdebugger.impl.XDebugSessionImpl; import com.intellij.xdebugger.impl.breakpoints.XBreakpointUtil; +import com.intellij.xdebugger.impl.breakpoints.XExpressionImpl; import com.intellij.xdebugger.impl.breakpoints.XLineBreakpointImpl; -import junit.framework.Assert; import org.intellij.lang.annotations.Language; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -50,6 +50,8 @@ import java.util.List; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; +import static org.junit.Assert.*; + public class XDebuggerTestUtil { private static final int TIMEOUT = 25000; @@ -65,10 +67,9 @@ public class XDebuggerTestUtil { XLineBreakpointType type = (XLineBreakpointType)XDebuggerUtil.getInstance().findBreakpointType(breakpointType); XBreakpointManager manager = XDebuggerManager.getInstance(project).getBreakpointManager(); XLineBreakpointImpl breakpoint = (XLineBreakpointImpl)manager.findBreakpointAtLine(type, file, line); - Assert.assertNotNull(breakpoint); - Assert - .assertEquals(validity ? AllIcons.Debugger.Db_verified_breakpoint : AllIcons.Debugger.Db_invalid_breakpoint, breakpoint.getIcon()); - Assert.assertEquals(errorMessage, breakpoint.getErrorMessage()); + assertNotNull(breakpoint); + assertEquals(validity ? AllIcons.Debugger.Db_verified_breakpoint : AllIcons.Debugger.Db_invalid_breakpoint, breakpoint.getIcon()); + assertEquals(errorMessage, breakpoint.getErrorMessage()); } public static void toggleBreakpoint(Project project, VirtualFile file, int line) { @@ -79,7 +80,7 @@ public class XDebuggerTestUtil { final P properties, final Class, P>> typeClass) { return new WriteAction>() { - protected void run(final Result> result) { + protected void run(@NotNull final Result> result) { result.setResult(XDebuggerManager.getInstance(project).getBreakpointManager().addBreakpoint( XBreakpointType.EXTENSION_POINT_NAME.findExtension(typeClass), properties)); } @@ -88,16 +89,16 @@ public class XDebuggerTestUtil { public static void removeBreakpoint(final Project project, final XBreakpoint breakpoint) { new WriteAction() { - protected void run(final Result result) { + protected void run(@NotNull final Result result) { XDebuggerManager.getInstance(project).getBreakpointManager().removeBreakpoint(breakpoint); } }.execute(); } public static void assertPosition(XSourcePosition pos, VirtualFile file, int line) throws IOException { - Assert.assertNotNull("No current position", pos); - Assert.assertEquals(new File(file.getPath()).getCanonicalPath(), new File(pos.getFile().getPath()).getCanonicalPath()); - if (line != -1) Assert.assertEquals(line, pos.getLine()); + assertNotNull("No current position", pos); + assertEquals(new File(file.getPath()).getCanonicalPath(), new File(pos.getFile().getPath()).getCanonicalPath()); + if (line != -1) assertEquals(line, pos.getLine()); } public static void assertCurrentPosition(XDebugSession session, VirtualFile file, int line) throws IOException { @@ -127,12 +128,23 @@ public class XDebuggerTestUtil { return container.waitFor(timeout).first; } - public static Pair evaluate(XDebugSession session, String expression) throws InterruptedException { + public static Pair evaluate(XDebugSession session, XExpression expression) { return evaluate(session, expression, TIMEOUT); } - public static Pair evaluate(XDebugSession session, String expression, long timeout) throws InterruptedException { - XDebuggerEvaluator evaluator = session.getCurrentStackFrame().getEvaluator(); + public static Pair evaluate(XDebugSession session, String expression) { + return evaluate(session, XExpressionImpl.fromText(expression), TIMEOUT); + } + + public static Pair evaluate(XDebugSession session, String expression, long timeout) { + return evaluate(session, XExpressionImpl.fromText(expression), timeout); + } + + private static Pair evaluate(XDebugSession session, XExpression expression, long timeout) { + XStackFrame frame = session.getCurrentStackFrame(); + assertNotNull(frame); + XDebuggerEvaluator evaluator = frame.getEvaluator(); + assertNotNull(evaluator); XTestEvaluationCallback callback = new XTestEvaluationCallback(); evaluator.evaluate(expression, callback, session.getCurrentPosition()); return callback.waitFor(timeout); @@ -189,10 +201,10 @@ public class XDebuggerTestUtil { @Nullable Boolean hasChildren) throws InterruptedException { XTestValueNode node = computePresentation(var); - if (name != null) Assert.assertEquals(name, node.myName); - if (type != null) Assert.assertEquals(type, node.myType); - if (value != null) Assert.assertEquals(value, node.myValue); - if (hasChildren != null) Assert.assertEquals((boolean)hasChildren, node.myHasChildren); + if (name != null) assertEquals(name, node.myName); + if (type != null) assertEquals(type, node.myType); + if (value != null) assertEquals(value, node.myValue); + if (hasChildren != null) assertEquals(hasChildren, node.myHasChildren); } public static void assertVariableValue(XValue var, @Nullable String name, @Nullable String value) throws InterruptedException { @@ -221,10 +233,10 @@ public class XDebuggerTestUtil { @Nullable String type, @Nullable @Language("RegExp") String valuePattern) throws InterruptedException { XTestValueNode node = computePresentation(var); - if (name != null) Assert.assertEquals(name, node.myName); - if (type != null) Assert.assertEquals(type, node.myType); + if (name != null) assertEquals(name, node.myName); + if (type != null) assertEquals(type, node.myType); if (valuePattern != null) { - Assert.assertTrue("Expected value" + valuePattern + " Actual value: " + node.myValue, node.myValue.matches(valuePattern)); + assertTrue("Expected value" + valuePattern + " Actual value: " + node.myValue, node.myValue.matches(valuePattern)); } } @@ -239,10 +251,10 @@ public class XDebuggerTestUtil { @Nullable @Language("RegExp") String typePattern) throws InterruptedException { XTestValueNode node = computePresentation(var); if (name != null) { - Assert.assertEquals(name, node.myName); + assertEquals(name, node.myName); } if (typePattern != null) { - Assert.assertTrue("Expected type: " + typePattern + " Actual type: " + node.myType, node.myType.matches(typePattern)); + assertTrue("Expected type: " + typePattern + " Actual type: " + node.myType, node.myType.matches(typePattern)); } } @@ -273,7 +285,7 @@ public class XDebuggerTestUtil { } }); - Assert.assertEquals(value, result[0]); + assertEquals(value, result[0]); } public static void assertVariableFullValue(Collection vars, @Nullable String name, @Nullable String value) @@ -303,9 +315,9 @@ public class XDebuggerTestUtil { } expectedNames.removeAll(actualNames); - UsefulTestCase.assertTrue("Missing variables:" + StringUtil.join(expectedNames, ", ") - + "\nAll Variables: " + StringUtil.join(actualNames, ", "), - expectedNames.isEmpty() + assertTrue("Missing variables:" + StringUtil.join(expectedNames, ", ") + + "\nAll Variables: " + StringUtil.join(actualNames, ", "), + expectedNames.isEmpty() ); } @@ -317,13 +329,23 @@ public class XDebuggerTestUtil { value.computeSourcePosition(n); } }); - Assert.assertNotNull(n.myPosition); - Assert.assertEquals(file, n.myPosition.getFile()); - Assert.assertEquals(offset, n.myPosition.getOffset()); + assertNotNull(n.myPosition); + assertEquals(file, n.myPosition.getFile()); + assertEquals(offset, n.myPosition.getOffset()); } - public static boolean waitFor(Semaphore semaphore, long timeoutInMillis) throws InterruptedException { - return semaphore.tryAcquire(timeoutInMillis, TimeUnit.MILLISECONDS); + public static boolean waitFor(Semaphore semaphore, long timeoutInMillis) { + long end = System.currentTimeMillis() + timeoutInMillis; + long remaining = timeoutInMillis; + do { + try { + return semaphore.tryAcquire(remaining, TimeUnit.MILLISECONDS); + } + catch (InterruptedException ignored) { + remaining = end - System.currentTimeMillis(); + } + } while (remaining > 0); + return false; } public static void assertVariable(Collection vars, @@ -337,7 +359,7 @@ public class XDebuggerTestUtil { @NotNull public static String getConsoleText(final @NotNull ConsoleViewImpl consoleView) { new WriteAction() { - protected void run(Result result) throws Throwable { + protected void run(@NotNull Result result) throws Throwable { consoleView.flushDeferredText(); } }.execute(); @@ -356,7 +378,7 @@ public class XDebuggerTestUtil { final T breakpointType = exceptionType.cast(type); new WriteAction() { @Override - protected void run(Result result) throws Throwable { + protected void run(@NotNull Result result) throws Throwable { breakpoint.set(breakpointManager.addBreakpoint(breakpointType, properties)); } }.execute(); @@ -372,7 +394,7 @@ public class XDebuggerTestUtil { for (final XBreakpoint b : breakpoints) { new WriteAction() { @Override - protected void run(Result result) throws Throwable { + protected void run(@NotNull Result result) throws Throwable { breakpointManager.removeBreakpoint(b); } }.execute(); @@ -406,7 +428,7 @@ public class XDebuggerTestUtil { if (lineBreakpoint.getLine() == line) { new WriteAction() { @Override - protected void run(Result result) throws Throwable { + protected void run(@NotNull Result result) throws Throwable { lineBreakpoint.setCondition(condition); } }.execute(); @@ -424,7 +446,7 @@ public class XDebuggerTestUtil { if (lineBreakpoint.getLine() == line) { new WriteAction() { @Override - protected void run(Result result) throws Throwable { + protected void run(@NotNull Result result) throws Throwable { lineBreakpoint.setLogExpression(logExpression); lineBreakpoint.setLogMessage(true); } @@ -436,7 +458,7 @@ public class XDebuggerTestUtil { public static void disposeDebugSession(final XDebugSession debugSession) { new WriteAction() { - protected void run(Result result) throws Throwable { + protected void run(@NotNull Result result) throws Throwable { XDebugSessionImpl session = (XDebugSessionImpl)debugSession; Disposer.dispose(session.getSessionTab()); Disposer.dispose(session.getConsoleView()); @@ -449,13 +471,13 @@ public class XDebuggerTestUtil { @Nullable String type, @Nullable String value, @Nullable Boolean hasChildren) throws InterruptedException { - Assert.assertNull(varAndErrorMessage.second); + assertNull(varAndErrorMessage.second); assertVariable(varAndErrorMessage.first, name, type, value, hasChildren); } public static String assertVariableExpression(XValue desc, String expectedExpression) { String expression = desc.getEvaluationExpression(); - Assert.assertEquals(expectedExpression, expression); + assertEquals(expectedExpression, expression); return expression; } diff --git a/platform/xdebugger-impl/testSrc/com/intellij/xdebugger/XTestContainer.java b/platform/xdebugger-impl/testSrc/com/intellij/xdebugger/XTestContainer.java index 32c65d8eb6fc..84c1fe8693e8 100644 --- a/platform/xdebugger-impl/testSrc/com/intellij/xdebugger/XTestContainer.java +++ b/platform/xdebugger-impl/testSrc/com/intellij/xdebugger/XTestContainer.java @@ -56,7 +56,7 @@ public class XTestContainer { return false; } - public Pair, String> waitFor(long timeout) throws InterruptedException { + public Pair, String> waitFor(long timeout) { if (!XDebuggerTestUtil.waitFor(myFinished, timeout)) { throw new AssertionError("Waiting timed out"); } diff --git a/platform/xdebugger-impl/testSrc/com/intellij/xdebugger/XTestEvaluationCallback.java b/platform/xdebugger-impl/testSrc/com/intellij/xdebugger/XTestEvaluationCallback.java index cb1b18e3795a..918648840790 100644 --- a/platform/xdebugger-impl/testSrc/com/intellij/xdebugger/XTestEvaluationCallback.java +++ b/platform/xdebugger-impl/testSrc/com/intellij/xdebugger/XTestEvaluationCallback.java @@ -2,11 +2,12 @@ package com.intellij.xdebugger; import com.intellij.openapi.util.Pair; import com.intellij.xdebugger.frame.XValue; -import junit.framework.Assert; import org.jetbrains.annotations.NotNull; import java.util.concurrent.Semaphore; +import static org.junit.Assert.*; + public class XTestEvaluationCallback extends com.intellij.xdebugger.impl.ui.tree.nodes.XEvaluationCallbackBase { private XValue myResult; private String myErrorMessage; @@ -24,8 +25,8 @@ public class XTestEvaluationCallback extends com.intellij.xdebugger.impl.ui.tree myFinished.release(); } - public Pair waitFor(long timeoutInMilliseconds) throws InterruptedException { - Assert.assertTrue("timed out", XDebuggerTestUtil.waitFor(myFinished, timeoutInMilliseconds)); + public Pair waitFor(long timeoutInMilliseconds) { + assertTrue("timed out", XDebuggerTestUtil.waitFor(myFinished, timeoutInMilliseconds)); return Pair.create(myResult, myErrorMessage); } } diff --git a/platform/xdebugger-impl/testSrc/com/intellij/xdebugger/XTestValueNode.java b/platform/xdebugger-impl/testSrc/com/intellij/xdebugger/XTestValueNode.java index e1dbca415903..45ad3306831a 100644 --- a/platform/xdebugger-impl/testSrc/com/intellij/xdebugger/XTestValueNode.java +++ b/platform/xdebugger-impl/testSrc/com/intellij/xdebugger/XTestValueNode.java @@ -41,7 +41,7 @@ public class XTestValueNode extends XValueNodePresentationConfigurator.Configura return false; } - public void waitFor(long timeoutInMillis) throws InterruptedException { + public void waitFor(long timeoutInMillis) { if (!XDebuggerTestUtil.waitFor(myFinished, timeoutInMillis)) { throw new AssertionError("Waiting timed out"); } diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/META-INF/InspectionGadgets.xml b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/META-INF/InspectionGadgets.xml index 81cc13aea292..be9f257bed05 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/META-INF/InspectionGadgets.xml +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/META-INF/InspectionGadgets.xml @@ -1046,6 +1046,10 @@ key="abstract.method.call.in.constructor.display.name" groupBundle="messages.InspectionsBundle" groupKey="group.names.initialization.issues" enabledByDefault="false" level="WARNING" implementationClass="com.siyeh.ig.initialization.AbstractMethodCallInConstructorInspection"/> + - + + @@ -1906,10 +1914,6 @@ - #ref is non-port use.sun.classes.display.name=Use of sun.* classes use.sun.classes.problem.descriptor=Use of Sun-supplied class #ref is non-portable #loc abstract.class.with.only.one.direct.inheritor.display.name=Abstract class which has only one direct inheritor -anonymous.inner.may.be.named.static.inner.class.display.name=Anonymous inner class may be a named static inner class -anonymous.inner.may.be.named.static.inner.class.problem.descriptor=Anonymous inner class #ref may be a named static inner class #loc +anonymous.inner.may.be.named.static.inner.class.display.name=Anonymous class may be a named 'static' inner class +anonymous.inner.may.be.named.static.inner.class.problem.descriptor=Anonymous class #ref may be a named 'static' inner class #loc array.length.in.loop.condition.display.name=Array.length in loop condition array.length.in.loop.condition.problem.descriptor=Check of array #ref in loop condition #loc large.array.allocation.no.outofmemoryerror.display.name=Large array allocation with no OutOfMemoryError check @@ -992,7 +992,8 @@ constant.on.lhs.of.comparison.problem.descriptor=#ref: constant on constant.on.rhs.of.comparison.problem.descriptor=#ref: constant on right side of comparison #loc control.flow.statement.without.braces.problem.descriptor=#ref without braces #loc missorted.modifiers.problem.descriptor=Missorted modifiers #ref #loc -c.style.array.declaration.problem.descriptor=C-style array declaration #ref #loc +cstyle.array.variable.declaration.problem.descriptor=C-style array declaration of {0, choice, 1#field|2#parameter|3#local variable} #ref #loc +cstyle.array.method.declaration.problem.descriptor=C-style array declaration of the return type of method #ref()#loc multiple.declaration.problem.descriptor=Multiple variables in one declaration #loc multiple.typed.declaration.problem.descriptor=Variables of different types in one declaration #loc serializable.inner.class.has.serial.version.uid.field.problem.descriptor=Inner class #ref does not define a 'serialVersionUID' field #loc @@ -1290,7 +1291,7 @@ call.to.simple.getter.in.class.inline.quickfix=Inline call to getter call.to.simple.setter.in.class.ignore.option=Ignore setter calls on other objects call.to.private.setter.in.class.option=Only report when setter is private call.to.simple.setter.in.class.inline.quickfix=Inline call to setter -make.static.quickfix=Make static +make.static.quickfix=Make 'static' length.one.strings.in.concatenation.replace.quickfix=Replace with character multiply.or.divide.by.power.of.two.replace.quickfix=Replace with shift boolean.expression.can.be.simplified.problem.descriptor=#ref can be simplified to ''{0}'' #loc @@ -1366,6 +1367,8 @@ implicit.call.to.super.make.explicit.quickfix=Make call to 'super()' explicit missorted.modifiers.require.option=Require annotations to be sorted before keywords missorted.modifiers.sort.quickfix=Sort modifiers nested.method.call.ignore.option=Ignore nested method calls in field initializers +ignore.calls.to.static.methods=Ignore calls to static methods +ignore.calls.to.property.getters=Ignore calls to property getters redundant.field.initialization.remove.quickfix=Remove initializer redundant.implements.remove.quickfix=Remove redundant interface declaration unnecessary.constructor.remove.quickfix=Remove redundant constructor @@ -1691,8 +1694,8 @@ char.used.in.arithmetic.context.cast.quickfix=Insert cast to {0} unnecessary.constant.array.creation.expression.display.name=Constant array creation expression can be replaced with array initializer unnecessary.constant.array.creation.expression.problem.descriptor=#ref can be replaced with array initializer expression #loc unnecessary.constant.array.creation.expression.quickfix=Replace with array initializer expression -ambiguous.method.call.display.name=Inherited method called, while local method might have been expected -ambiguous.method.call.problem.descriptor=Method #ref() from superclass ''{0}'' called, when method from class ''{1}'' might have been expected #loc +ambiguous.method.call.display.name=Call to inherited method looks like call to local method +ambiguous.method.call.problem.descriptor=Call to method #ref() from superclass ''{0}'' looks like call to method from class ''{1}'' #loc ambiguous.method.call.quickfix=Add 'super' qualifier to method call change.modifier.quickfix=Make ''{0}'' the.whole.project=the whole project @@ -1969,10 +1972,10 @@ throws.runtime.exception.problem.descriptor=Unchecked exception #ref#ref from superclass ''{0}'' accessed, while local variable access might be expected #loc -ambiguous.field.access.hides.parameter.problem.descriptor=Field #ref from superclass ''{0}'' accessed, while parameter access might be expected #loc -ambiguous.field.access.hides.field.problem.descriptor=Field #ref from superclass ''{0}'' accessed, while field access from surrounding class might be expected #loc +ambiguous.field.access.display.name=Access of inherited field looks like access of element in surrounding code +ambiguous.field.access.hides.local.variable.problem.descriptor=Access of field #ref from superclass ''{0}'' looks like access of local variable #loc +ambiguous.field.access.hides.parameter.problem.descriptor=Access of field #ref from superclass ''{0}'' looks like access of parameter #loc +ambiguous.field.access.hides.field.problem.descriptor=Access of field #ref from superclass ''{0}'' looks access of field from surrounding class #loc ambiguous.field.access.quickfix=Add 'super' qualifier to field access string.builder.replaceable.by.string.quickfix=Replace 'StringBuilder' with 'String' string.buffer.replaceable.by.string.quickfix=Replace 'StringBuffer' with 'String' @@ -2111,3 +2114,6 @@ junit3.method.naming.convention.display.name=JUnit 3 test method naming conventi junit3.method.naming.convention.problem.descriptor.short=JUnit 3 test method name #ref is too short ({0} < {1}) #loc junit3.method.naming.convention.problem.descriptor.long=JUnit 3 test method name #ref is too long ({0} > {1}) #loc junit3.method.naming.convention.problem.descriptor.regex.mismatch=JUnit 3 test method name #ref doesn''t match regex ''{0}'' #loc +introduce.holder.class.quickfix=Introduce holder class +double.brace.initialization.display.name=Double brace initialization +double.brace.initialization.quickfix=Replace with regular initialization diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/ResultSetIndexZeroInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/ResultSetIndexZeroInspection.java index 626d4508e731..a72f297e483c 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/ResultSetIndexZeroInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/ResultSetIndexZeroInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2012 Dave Griffith, Bas Leijdekkers + * Copyright 2003-2014 Dave Griffith, Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -87,11 +87,11 @@ public class ResultSetIndexZeroInspection extends BaseInspection { final PsiExpression qualifier = methodExpression.getQualifierExpression(); if (resultSet) { if (TypeUtils.expressionHasTypeOrSubtype(qualifier, "java.sql.ResultSet")) { - registerError(argument, Boolean.valueOf(resultSet)); + registerError(argument, Boolean.TRUE); } - } else { + } else if (arguments.length > 1) { if (TypeUtils.expressionHasTypeOrSubtype(qualifier, "java.sql.PreparedStatement")) { - registerError(argument, Boolean.valueOf(resultSet)); + registerError(argument, Boolean.FALSE); } } } diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/StringEqualityInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/StringEqualityInspection.java index 0ac07245fd8f..f94f495993d7 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/StringEqualityInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/bugs/StringEqualityInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2012 Dave Griffith, Bas Leijdekkers + * Copyright 2003-2014 Dave Griffith, Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,6 @@ package com.siyeh.ig.bugs; import com.intellij.psi.PsiBinaryExpression; import com.intellij.psi.PsiExpression; import com.intellij.psi.PsiJavaToken; -import com.intellij.psi.PsiKeyword; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; @@ -73,12 +72,7 @@ public class StringEqualityInspection extends BaseInspection { if (rhs == null || !ExpressionUtils.hasStringType(rhs)) { return; } - final String lhsText = lhs.getText(); - if (PsiKeyword.NULL.equals(lhsText)) { - return; - } - final String rhsText = rhs.getText(); - if (PsiKeyword.NULL.equals(rhsText)) { + if (ExpressionUtils.isNullLiteral(lhs) || ExpressionUtils.isNullLiteral(rhs)) { return; } final PsiJavaToken sign = expression.getOperationSign(); diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/ClassWithOnlyPrivateConstructorsInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/ClassWithOnlyPrivateConstructorsInspectionBase.java index 6d091699ce82..b6583b4ede75 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/ClassWithOnlyPrivateConstructorsInspectionBase.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/ClassWithOnlyPrivateConstructorsInspectionBase.java @@ -54,6 +54,9 @@ public class ClassWithOnlyPrivateConstructorsInspectionBase extends BaseInspecti @Override public void visitClass(PsiClass aClass) { super.visitClass(aClass); + if (aClass.isEnum()) { + return; + } final PsiMethod[] constructors = aClass.getConstructors(); if (constructors.length == 0) { return; diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/controlflow/LoopConditionNotUpdatedInsideLoopInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/controlflow/LoopConditionNotUpdatedInsideLoopInspection.java index 46969fd5bcfc..57c635b7d461 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/controlflow/LoopConditionNotUpdatedInsideLoopInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/controlflow/LoopConditionNotUpdatedInsideLoopInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2006-2013 Bas Leijdekkers + * Copyright 2006-2014 Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,6 +24,7 @@ import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; import com.siyeh.ig.psiutils.BoolUtils; +import com.siyeh.ig.psiutils.ExpressionUtils; import com.siyeh.ig.psiutils.IteratorUtils; import com.siyeh.ig.psiutils.VariableAccessUtils; import org.jetbrains.annotations.NotNull; @@ -118,8 +119,7 @@ public class LoopConditionNotUpdatedInsideLoopInspection if (condition == null) { return false; } - if (PsiUtil.isConstantExpression(condition) || - PsiKeyword.NULL.equals(condition.getText())) { + if (PsiUtil.isConstantExpression(condition) || ExpressionUtils.isNullLiteral(condition)) { return true; } if (condition instanceof PsiInstanceOfExpression) { diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/initialization/DoubleBraceInitializationInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/initialization/DoubleBraceInitializationInspection.java new file mode 100644 index 000000000000..bdf58e720d8d --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/initialization/DoubleBraceInitializationInspection.java @@ -0,0 +1,216 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.initialization; + +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.openapi.project.Project; +import com.intellij.psi.*; +import com.intellij.psi.util.PsiTreeUtil; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.InspectionGadgetsFix; +import com.siyeh.ig.psiutils.ParenthesesUtils; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author Bas Leijdekkers + */ +public class DoubleBraceInitializationInspection extends BaseInspection { + @Nls + @NotNull + @Override + public String getDisplayName() { + return InspectionGadgetsBundle.message("double.brace.initialization.display.name"); + } + + @NotNull + @Override + protected String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message("double.brace.initialization.display.name"); + } + + @Nullable + @Override + protected InspectionGadgetsFix buildFix(Object... infos) { + final PsiClass aClass = (PsiClass)infos[0]; + final PsiElement parent = PsiTreeUtil.skipParentsOfType(aClass, PsiNewExpression.class, ParenthesesUtils.class); + if (!(parent instanceof PsiVariable) && !(parent instanceof PsiAssignmentExpression)) { + return null; + } + return new DoubleBraceInitializationFix(); + } + + private static class DoubleBraceInitializationFix extends InspectionGadgetsFix { + + @NotNull + @Override + public String getName() { + return InspectionGadgetsBundle.message("double.brace.initialization.quickfix"); + } + + @NotNull + @Override + public String getFamilyName() { + return getName(); + } + + @Override + protected void doFix(Project project, ProblemDescriptor descriptor) { + final PsiElement element = descriptor.getPsiElement().getParent(); + if (!(element instanceof PsiAnonymousClass)) { + return; + } + final PsiAnonymousClass aClass = (PsiAnonymousClass)element; + final PsiElement parent = aClass.getParent(); + if (!(parent instanceof PsiNewExpression)) { + return; + } + final PsiNewExpression newExpression = (PsiNewExpression)parent; + final PsiElement ancestor = PsiTreeUtil.skipParentsOfType(newExpression, ParenthesesUtils.class); + final String qualifierText; + if (ancestor instanceof PsiVariable) { + qualifierText = ((PsiVariable)ancestor).getName(); + } + else if (ancestor instanceof PsiAssignmentExpression) { + final PsiAssignmentExpression assignmentExpression = (PsiAssignmentExpression)ancestor; + final PsiExpression lhs = ParenthesesUtils.stripParentheses(assignmentExpression.getLExpression()); + if (!(lhs instanceof PsiReferenceExpression)) { + return; + } + final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)lhs; + final PsiElement target = referenceExpression.resolve(); + if (!(target instanceof PsiVariable)) { + return; + } + qualifierText = referenceExpression.getText(); + } + else { + return; + } + final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project); + final PsiJavaCodeReferenceElement baseClassReference = aClass.getBaseClassReference(); + final PsiElement baseClassTarget = baseClassReference.resolve(); + if (!(baseClassTarget instanceof PsiClass)) { + return; + } + final PsiExpressionList argumentList = aClass.getArgumentList(); + if (argumentList == null) { + return; + } + qualifyReferences(aClass, (PsiClass) baseClassTarget, qualifierText); + final PsiClassInitializer initializer = aClass.getInitializers()[0]; + final PsiCodeBlock body = initializer.getBody(); + PsiElement child = body.getLastBodyElement(); + final PsiElement stop = body.getFirstBodyElement(); + final PsiElement anchor = PsiTreeUtil.getParentOfType(aClass, PsiMember.class, PsiStatement.class); + if (anchor == null) { + return; + } + if (anchor instanceof PsiMember) { + final PsiMember member = (PsiMember)anchor; + final PsiClassInitializer newInitializer = factory.createClassInitializer(); + if (member.hasModifierProperty(PsiModifier.STATIC)) { + final PsiModifierList modifierList = newInitializer.getModifierList(); + if (modifierList != null) { + modifierList.setModifierProperty(PsiModifier.STATIC, true); + } + } + final PsiCodeBlock initializerBody = newInitializer.getBody(); + while (child != null && !child.equals(stop)) { + initializerBody.add(child); + child = child.getPrevSibling(); + } + member.getParent().addAfter(newInitializer, member); + } + else { + final PsiElement container = anchor.getParent(); + while (child != null && !child.equals(stop)) { + container.addAfter(child, anchor); + child = child.getPrevSibling(); + } + } + final PsiExpression newNewExpression = + factory.createExpressionFromText("new " + baseClassReference.getText() + argumentList.getText(), aClass); + newExpression.replace(newNewExpression); + } + + private static void qualifyReferences(PsiElement element, final PsiClass target, final String qualifierText) { + final PsiElementFactory factory = JavaPsiFacade.getElementFactory(element.getProject()); + element.accept(new JavaRecursiveElementVisitor() { + @Override + public void visitReferenceExpression(PsiReferenceExpression expression) { + super.visitReferenceExpression(expression); + if (expression.getQualifierExpression() != null) { + return; + } + final PsiElement expressionTarget = expression.resolve(); + if (!(expressionTarget instanceof PsiMember)) { + return; + } + final PsiMember member = (PsiMember)expressionTarget; + final PsiClass containingClass = member.getContainingClass(); + if (!target.equals(containingClass)) { + return; + } + final PsiExpression newExpression = factory.createExpressionFromText(qualifierText + '.' + expression.getText(), expression); + expression.replace(newExpression); + } + }); + } + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new DoubleBraceInitializationVisitor(); + } + + private static class DoubleBraceInitializationVisitor extends BaseInspectionVisitor { + + @Override + public void visitAnonymousClass(PsiAnonymousClass aClass) { + super.visitAnonymousClass(aClass); + final PsiClassInitializer[] initializers = aClass.getInitializers(); + if (initializers.length != 1) { + return; + } + final PsiClassInitializer initializer = initializers[0]; + if (initializer.hasModifierProperty(PsiModifier.STATIC)) { + // don't warn on broken code + return; + } + final PsiField[] fields = aClass.getFields(); + if (fields.length != 0) { + return; + } + final PsiMethod[] methods = aClass.getMethods(); + if (methods.length != 0) { + return; + } + final PsiClass[] innerClasses = aClass.getInnerClasses(); + if (innerClasses.length != 0) { + return; + } + final PsiJavaCodeReferenceElement reference = aClass.getBaseClassReference(); + if (reference.resolve() == null) { + return; + } + registerClassError(aClass, aClass); + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/initialization/NonThreadSafeLazyInitializationInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/initialization/NonThreadSafeLazyInitializationInspection.java deleted file mode 100644 index 2e0bebac17b9..000000000000 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/initialization/NonThreadSafeLazyInitializationInspection.java +++ /dev/null @@ -1,246 +0,0 @@ -/* - * Copyright 2003-2007 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. - * 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.initialization; - -import com.intellij.codeInspection.ProblemDescriptor; -import com.intellij.openapi.project.Project; -import com.intellij.psi.*; -import com.intellij.psi.search.searches.ReferencesSearch; -import com.intellij.psi.tree.IElementType; -import com.intellij.psi.util.PsiTreeUtil; -import com.intellij.psi.util.PsiUtil; -import com.intellij.util.IncorrectOperationException; -import com.intellij.util.Processor; -import com.siyeh.InspectionGadgetsBundle; -import com.siyeh.ig.BaseInspection; -import com.siyeh.ig.BaseInspectionVisitor; -import com.siyeh.ig.InspectionGadgetsFix; -import org.jetbrains.annotations.NonNls; -import org.jetbrains.annotations.NotNull; - -import java.util.Collection; - -public class NonThreadSafeLazyInitializationInspection - extends BaseInspection { - - @Override - @NotNull - public String getDisplayName() { - return InspectionGadgetsBundle.message( - "non.thread.safe.lazy.initialization.display.name"); - } - - @Override - @NotNull - public String buildErrorString(Object... infos) { - return InspectionGadgetsBundle.message( - "non.thread.safe.lazy.initialization.problem.descriptor"); - } - - @Override - public BaseInspectionVisitor buildVisitor() { - return new UnsafeSafeLazyInitializationVisitor(); - } - - private static class UnsafeSafeLazyInitializationVisitor - extends BaseInspectionVisitor { - - @Override - public void visitAssignmentExpression( - @NotNull PsiAssignmentExpression expression) { - super.visitAssignmentExpression(expression); - final PsiExpression lhs = expression.getLExpression(); - if (!(lhs instanceof PsiReferenceExpression)) { - return; - } - final PsiReference reference = (PsiReference)lhs; - final PsiElement referent = reference.resolve(); - if (!(referent instanceof PsiField)) { - return; - } - final PsiField field = (PsiField)referent; - if (!field.hasModifierProperty(PsiModifier.STATIC)) { - return; - } - if (isInStaticInitializer(expression)) { - return; - } - if (isInSynchronizedContext(expression)) { - return; - } - if (!isLazy(expression, (PsiReferenceExpression)lhs)) { - return; - } - boolean assignedOnce = isAssignedOnce(referent); - boolean safeToDelete = isSafeToDeleteIfStatement(expression); - registerError(lhs, assignedOnce && safeToDelete); - } - - private static boolean isAssignedOnce(PsiElement referent) { - final int[] writeCount = new int[1]; - return ReferencesSearch.search(referent).forEach(new Processor() { - @Override - public boolean process(PsiReference reference) { - PsiElement element = reference.getElement(); - if (!(element instanceof PsiExpression)) { - return true; - } - if (!PsiUtil.isAccessedForWriting((PsiExpression)element)) { - return true; - } - return ++writeCount[0] != 2; - } - }); - } - - private static boolean isSafeToDeleteIfStatement(PsiElement expression) { - PsiIfStatement ifStatement = PsiTreeUtil.getParentOfType(expression, PsiIfStatement.class); - if (ifStatement.getElseBranch() != null) { - return false; - } - PsiStatement thenBranch = ifStatement.getThenBranch(); - if (thenBranch == null) return false; - if (!(thenBranch instanceof PsiBlockStatement)) { - return true; - } - return ((PsiBlockStatement)thenBranch).getCodeBlock().getStatements().length == 1; - } - - private static boolean isLazy(PsiAssignmentExpression expression, - PsiReferenceExpression lhs) { - final PsiIfStatement ifStatement = - PsiTreeUtil.getParentOfType(expression, - PsiIfStatement.class); - if (ifStatement == null) { - return false; - } - final PsiExpression condition = ifStatement.getCondition(); - if (condition == null) { - return false; - } - return isNullComparison(condition, lhs); - } - - private static boolean isNullComparison( - PsiExpression condition, PsiReferenceExpression reference) { - if (!(condition instanceof PsiBinaryExpression)) { - return false; - } - final PsiBinaryExpression comparison = - (PsiBinaryExpression)condition; - final IElementType tokenType = comparison.getOperationTokenType(); - if (!tokenType.equals(JavaTokenType.EQEQ)) { - return false; - } - final PsiExpression lhs = comparison.getLOperand(); - final PsiExpression rhs = comparison.getROperand(); - if (rhs == null) { - return false; - } - final String lhsText = lhs.getText(); - final String rhsText = rhs.getText(); - if (!PsiKeyword.NULL.equals(lhsText) && - !PsiKeyword.NULL.equals(rhsText)) { - return false; - } - final String referenceText = reference.getText(); - return referenceText.equals(lhsText) || - referenceText.equals(rhsText); - } - - private static boolean isInSynchronizedContext(PsiElement element) { - final PsiSynchronizedStatement syncBlock = - PsiTreeUtil.getParentOfType(element, - PsiSynchronizedStatement.class); - if (syncBlock != null) { - return true; - } - final PsiMethod method = - PsiTreeUtil.getParentOfType(element, - PsiMethod.class); - return method != null && - method.hasModifierProperty(PsiModifier.SYNCHRONIZED) - && method.hasModifierProperty(PsiModifier.STATIC); - } - - private static boolean isInStaticInitializer(PsiElement element) { - final PsiClassInitializer initializer = - PsiTreeUtil.getParentOfType(element, - PsiClassInitializer.class); - return initializer != null && - initializer.hasModifierProperty(PsiModifier.STATIC); - } - } - - @Override - protected InspectionGadgetsFix buildFix(Object... infos) { - boolean isApplicable = ((Boolean)infos[0]).booleanValue(); - return isApplicable ? new IntroduceHolderFix() : null; - } - - private static class IntroduceHolderFix extends InspectionGadgetsFix { - @Override - protected void doFix(Project project, ProblemDescriptor descriptor) throws IncorrectOperationException { - PsiReferenceExpression expression = (PsiReferenceExpression)descriptor.getPsiElement(); - PsiElement resolved = expression.resolve(); - if (!(resolved instanceof PsiField)) return; - PsiField field = (PsiField)resolved; - String holderName = suggestHolderName(field); - @NonNls String text = "private static class " + holderName - + " {" + - "private static final " + field.getType().getCanonicalText() + " " + - field.getName() + " = " + ((PsiAssignmentExpression)expression.getParent()).getRExpression().getText() + ";" - + "}"; - PsiElementFactory elementFactory = JavaPsiFacade.getInstance(field.getProject()).getElementFactory(); - PsiClass holder = elementFactory.createClassFromText(text, field).getInnerClasses()[0]; - PsiMethod method = PsiTreeUtil.getParentOfType(expression, PsiMethod.class); - method.getParent().addBefore(holder, method); - - PsiIfStatement ifStatement = PsiTreeUtil.getParentOfType(expression, PsiIfStatement.class); - ifStatement.delete(); - - final PsiExpression holderReference = elementFactory.createExpressionFromText(holderName + "." + field.getName(), field); - Collection references = ReferencesSearch.search(field).findAll(); - for (PsiReference reference : references) { - PsiElement element = reference.getElement(); - element.replace(holderReference); - } - field.delete(); - } - - @NonNls - private static String suggestHolderName(PsiField field) { - String string = field.getType().getDeepComponentType().getPresentableText(); - final int index = string.indexOf('<'); - if (index != -1) { - string = string.substring(0, index); - } - return string + "Holder"; - } - - @Override - @NotNull - public String getName() { - return "Introduce holder class"; - } - - @NotNull - @Override - public String getFamilyName() { - return getName(); - } - } -} \ No newline at end of file diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/initialization/NonThreadSafeLazyInitializationInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/initialization/NonThreadSafeLazyInitializationInspectionBase.java new file mode 100644 index 000000000000..f29c461bcf64 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/initialization/NonThreadSafeLazyInitializationInspectionBase.java @@ -0,0 +1,132 @@ +/* + * Copyright 2003-2014 Dave Griffith, Bas Leijdekkers + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * 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.initialization; + +import com.intellij.psi.*; +import com.intellij.psi.tree.IElementType; +import com.intellij.psi.util.PsiTreeUtil; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.psiutils.ExpressionUtils; +import com.siyeh.ig.psiutils.VariableAccessUtils; +import org.jetbrains.annotations.NotNull; + +public class NonThreadSafeLazyInitializationInspectionBase extends BaseInspection { + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message( + "non.thread.safe.lazy.initialization.display.name"); + } + + @Override + @NotNull + public String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message( + "non.thread.safe.lazy.initialization.problem.descriptor"); + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new UnsafeSafeLazyInitializationVisitor(); + } + + private static class UnsafeSafeLazyInitializationVisitor + extends BaseInspectionVisitor { + + @Override + public void visitAssignmentExpression( + @NotNull PsiAssignmentExpression expression) { + super.visitAssignmentExpression(expression); + final PsiExpression lhs = expression.getLExpression(); + if (!(lhs instanceof PsiReferenceExpression)) { + return; + } + final PsiReferenceExpression reference = (PsiReferenceExpression)lhs; + final PsiElement referent = reference.resolve(); + if (!(referent instanceof PsiField)) { + return; + } + final PsiField field = (PsiField)referent; + if (!field.hasModifierProperty(PsiModifier.STATIC)) { + return; + } + if (isInStaticInitializer(expression)) { + return; + } + if (isInSynchronizedContext(expression)) { + return; + } + final PsiStatement statement = PsiTreeUtil.getParentOfType(expression, PsiStatement.class); + final PsiElement parent = + PsiTreeUtil.skipParentsOfType(statement, PsiCodeBlock.class, PsiBlockStatement.class); + if (!(parent instanceof PsiIfStatement)) { + return; + } + final PsiIfStatement ifStatement = (PsiIfStatement)parent; + final PsiExpression condition = ifStatement.getCondition(); + if (condition == null|| !isNullComparison(condition, field)) { + return; + } + registerError(lhs, ifStatement, field); + } + + private static boolean isNullComparison(PsiExpression condition, PsiVariable variable) { + if (!(condition instanceof PsiBinaryExpression)) { + return false; + } + final PsiBinaryExpression comparison = (PsiBinaryExpression)condition; + final IElementType tokenType = comparison.getOperationTokenType(); + if (!tokenType.equals(JavaTokenType.EQEQ)) { + return false; + } + final PsiExpression lhs = comparison.getLOperand(); + final PsiExpression rhs = comparison.getROperand(); + if (ExpressionUtils.isNullLiteral(rhs)) { + return VariableAccessUtils.evaluatesToVariable(lhs, variable); + } + else if (ExpressionUtils.isNullLiteral(lhs)) { + return VariableAccessUtils.evaluatesToVariable(rhs, variable); + } + return false; + } + + private static boolean isInSynchronizedContext(PsiElement element) { + final PsiSynchronizedStatement syncBlock = + PsiTreeUtil.getParentOfType(element, + PsiSynchronizedStatement.class); + if (syncBlock != null) { + return true; + } + final PsiMethod method = + PsiTreeUtil.getParentOfType(element, + PsiMethod.class); + return method != null && + method.hasModifierProperty(PsiModifier.SYNCHRONIZED) + && method.hasModifierProperty(PsiModifier.STATIC); + } + + private static boolean isInStaticInitializer(PsiElement element) { + final PsiClassInitializer initializer = + PsiTreeUtil.getParentOfType(element, + PsiClassInitializer.class); + return initializer != null && + initializer.hasModifierProperty(PsiModifier.STATIC); + } + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/j2me/AnonymousInnerClassMayBeStaticInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/j2me/AnonymousInnerClassMayBeStaticInspectionBase.java deleted file mode 100644 index 4ea6a4c7290f..000000000000 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/j2me/AnonymousInnerClassMayBeStaticInspectionBase.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2000-2013 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.siyeh.ig.j2me; - -import com.intellij.psi.*; -import com.intellij.psi.util.PsiTreeUtil; -import com.siyeh.InspectionGadgetsBundle; -import com.siyeh.ig.BaseInspection; -import com.siyeh.ig.BaseInspectionVisitor; -import com.siyeh.ig.performance.InnerClassReferenceVisitor; -import org.jetbrains.annotations.NotNull; - -public class AnonymousInnerClassMayBeStaticInspectionBase extends BaseInspection { - @Override - @NotNull - public String getDisplayName() { - return InspectionGadgetsBundle.message( - "anonymous.inner.may.be.named.static.inner.class.display.name"); - } - - @Override - @NotNull - public String buildErrorString(Object... infos) { - return InspectionGadgetsBundle.message( - "anonymous.inner.may.be.named.static.inner.class.problem.descriptor"); - } - - @Override - public BaseInspectionVisitor buildVisitor() { - return new AnonymousInnerClassMayBeStaticVisitor(); - } - - private static class AnonymousInnerClassMayBeStaticVisitor - extends BaseInspectionVisitor { - - @Override - public void visitClass(@NotNull PsiClass aClass) { - if (!(aClass instanceof PsiAnonymousClass)) { - return; - } - if (aClass instanceof PsiEnumConstantInitializer) { - return; - } - final PsiMember containingMember = - PsiTreeUtil.getParentOfType(aClass, PsiMember.class); - if (containingMember == null || - containingMember.hasModifierProperty(PsiModifier.STATIC)) { - return; - } - final PsiAnonymousClass anAnonymousClass = - (PsiAnonymousClass)aClass; - final InnerClassReferenceVisitor visitor = - new InnerClassReferenceVisitor(anAnonymousClass); - anAnonymousClass.accept(visitor); - if (!visitor.canInnerClassBeStatic()) { - return; - } - registerClassError(aClass); - } - } -} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/javadoc/MissingPackageInfoInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/javadoc/MissingPackageInfoInspection.java deleted file mode 100644 index 1305663d7710..000000000000 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/javadoc/MissingPackageInfoInspection.java +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright 2000-2013 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.siyeh.ig.javadoc; - -import com.intellij.analysis.AnalysisScope; -import com.intellij.codeInspection.CommonProblemDescriptor; -import com.intellij.codeInspection.GlobalInspectionContext; -import com.intellij.codeInspection.InspectionManager; -import com.intellij.codeInspection.LocalInspectionTool; -import com.intellij.codeInspection.reference.RefClass; -import com.intellij.codeInspection.reference.RefEntity; -import com.intellij.codeInspection.reference.RefPackage; -import com.intellij.openapi.project.Project; -import com.intellij.psi.*; -import com.intellij.psi.util.PsiUtil; -import com.siyeh.InspectionGadgetsBundle; -import com.siyeh.ig.BaseGlobalInspection; -import com.siyeh.ig.BaseInspectionVisitor; -import com.siyeh.ig.BaseSharedLocalInspection; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.Nls; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.List; - -/** - * @author Bas Leijdekkers - */ -public class MissingPackageInfoInspection extends BaseGlobalInspection { - - @Nls - @NotNull - @Override - public String getDisplayName() { - return InspectionGadgetsBundle.message("missing.package.info.display.name"); - } - - @Nullable - @Override - public CommonProblemDescriptor[] checkElement(@NotNull RefEntity refEntity, - @NotNull AnalysisScope scope, - @NotNull InspectionManager manager, - @NotNull GlobalInspectionContext globalContext) { - if (!(refEntity instanceof RefPackage)) { - return null; - } - final RefPackage refPackage = (RefPackage)refEntity; - final String packageName = refPackage.getQualifiedName(); - final Project project = globalContext.getProject(); - final PsiPackage aPackage = JavaPsiFacade.getInstance(project).findPackage(packageName); - if (hasPackageInfoFile(aPackage)) { - return null; - } - final List children = refPackage.getChildren(); - boolean hasClasses = false; - for (RefEntity child : children) { - if (child instanceof RefClass) { - hasClasses = true; - break; - } - } - if (!hasClasses) { - return null; - } - if (PsiUtil.isLanguageLevel5OrHigher(aPackage)) { - return new CommonProblemDescriptor[] { - manager.createProblemDescriptor(InspectionGadgetsBundle.message("missing.package.info.problem.descriptor", packageName))}; - } - else { - return new CommonProblemDescriptor[] { - manager.createProblemDescriptor(InspectionGadgetsBundle.message("missing.package.html.problem.descriptor", packageName))}; - } - } - - @Contract("null -> true") - static boolean hasPackageInfoFile(PsiPackage aPackage) { - if (aPackage == null) { - return true; - } - final PsiDirectory[] directories = aPackage.getDirectories(); - for (PsiDirectory directory : directories) { - final boolean packageInfoFound = directory.findFile(PsiPackage.PACKAGE_INFO_FILE) != null; - final boolean packageDotHtmlFound = directory.findFile("package.html") != null; - if (packageInfoFound || packageDotHtmlFound) { - return true; - } - } - return false; - } - - @Nullable - @Override - public LocalInspectionTool getSharedLocalInspectionTool() { - return new LocalMissingPackageInfoInspection(this); - } - - private static class LocalMissingPackageInfoInspection extends BaseSharedLocalInspection { - - public LocalMissingPackageInfoInspection(MissingPackageInfoInspection settingsDelegate) { - super(settingsDelegate); - } - - @NotNull - @Override - protected String buildErrorString(Object... infos) { - final PsiPackageStatement packageStatement = (PsiPackageStatement)infos[0]; - if (PsiUtil.isLanguageLevel5OrHigher(packageStatement)) { - return InspectionGadgetsBundle.message("missing.package.info.problem.descriptor", packageStatement.getPackageName()); - } - else { - return InspectionGadgetsBundle.message("missing.package.html.problem.descriptor", packageStatement.getPackageName()); - } - } - - @Override - public BaseInspectionVisitor buildVisitor() { - return new BaseInspectionVisitor() { - @Override - public void visitJavaFile(PsiJavaFile file) { - final PsiPackageStatement packageStatement = file.getPackageStatement(); - if (packageStatement == null) { - return; - } - final PsiJavaCodeReferenceElement packageReference = packageStatement.getPackageReference(); - final PsiElement target = packageReference.resolve(); - if (!(target instanceof PsiPackage)) { - return; - } - final PsiPackage aPackage = (PsiPackage)target; - if (hasPackageInfoFile(aPackage)) { - return; - } - registerError(packageReference, packageStatement); - } - }; - } - } -} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/javadoc/MissingPackageInfoInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/javadoc/MissingPackageInfoInspectionBase.java new file mode 100644 index 000000000000..dcd737d983fd --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/javadoc/MissingPackageInfoInspectionBase.java @@ -0,0 +1,152 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.javadoc; + +import com.intellij.analysis.AnalysisScope; +import com.intellij.codeInspection.CommonProblemDescriptor; +import com.intellij.codeInspection.GlobalInspectionContext; +import com.intellij.codeInspection.InspectionManager; +import com.intellij.codeInspection.LocalInspectionTool; +import com.intellij.codeInspection.reference.RefClass; +import com.intellij.codeInspection.reference.RefEntity; +import com.intellij.codeInspection.reference.RefPackage; +import com.intellij.openapi.project.Project; +import com.intellij.psi.*; +import com.intellij.psi.util.PsiUtil; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseGlobalInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.BaseSharedLocalInspection; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +/** + * @author Bas Leijdekkers + */ +public class MissingPackageInfoInspectionBase extends BaseGlobalInspection { + + @Nls + @NotNull + @Override + public String getDisplayName() { + return InspectionGadgetsBundle.message("missing.package.info.display.name"); + } + + @Nullable + @Override + public CommonProblemDescriptor[] checkElement(@NotNull RefEntity refEntity, + @NotNull AnalysisScope scope, + @NotNull InspectionManager manager, + @NotNull GlobalInspectionContext globalContext) { + if (!(refEntity instanceof RefPackage)) { + return null; + } + final RefPackage refPackage = (RefPackage)refEntity; + final String packageName = refPackage.getQualifiedName(); + final Project project = globalContext.getProject(); + final PsiPackage aPackage = JavaPsiFacade.getInstance(project).findPackage(packageName); + if (hasPackageInfoFile(aPackage)) { + return null; + } + final List children = refPackage.getChildren(); + boolean hasClasses = false; + for (RefEntity child : children) { + if (child instanceof RefClass) { + hasClasses = true; + break; + } + } + if (!hasClasses) { + return null; + } + if (PsiUtil.isLanguageLevel5OrHigher(aPackage)) { + return new CommonProblemDescriptor[] { + manager.createProblemDescriptor(InspectionGadgetsBundle.message("missing.package.info.problem.descriptor", packageName))}; + } + else { + return new CommonProblemDescriptor[] { + manager.createProblemDescriptor(InspectionGadgetsBundle.message("missing.package.html.problem.descriptor", packageName))}; + } + } + + @Contract("null -> true") + static boolean hasPackageInfoFile(PsiPackage aPackage) { + if (aPackage == null) { + return true; + } + final PsiDirectory[] directories = aPackage.getDirectories(); + for (PsiDirectory directory : directories) { + final boolean packageInfoFound = directory.findFile(PsiPackage.PACKAGE_INFO_FILE) != null; + final boolean packageDotHtmlFound = directory.findFile("package.html") != null; + if (packageInfoFound || packageDotHtmlFound) { + return true; + } + } + return false; + } + + @Nullable + @Override + public LocalInspectionTool getSharedLocalInspectionTool() { + return new LocalMissingPackageInfoInspectionBase(this); + } + + protected static class LocalMissingPackageInfoInspectionBase extends BaseSharedLocalInspection { + + public LocalMissingPackageInfoInspectionBase(MissingPackageInfoInspectionBase settingsDelegate) { + super(settingsDelegate); + } + + @NotNull + @Override + protected String buildErrorString(Object... infos) { + final PsiPackageStatement packageStatement = (PsiPackageStatement)infos[0]; + if (PsiUtil.isLanguageLevel5OrHigher(packageStatement)) { + return InspectionGadgetsBundle.message("missing.package.info.problem.descriptor", packageStatement.getPackageName()); + } + else { + return InspectionGadgetsBundle.message("missing.package.html.problem.descriptor", packageStatement.getPackageName()); + } + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new BaseInspectionVisitor() { + @Override + public void visitJavaFile(PsiJavaFile file) { + final PsiPackageStatement packageStatement = file.getPackageStatement(); + if (packageStatement == null) { + return; + } + final PsiJavaCodeReferenceElement packageReference = packageStatement.getPackageReference(); + final PsiElement target = packageReference.resolve(); + if (!(target instanceof PsiPackage)) { + return; + } + final PsiPackage aPackage = (PsiPackage)target; + if (hasPackageInfoFile(aPackage)) { + return; + } + registerError(packageReference, packageStatement); + } + }; + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/memory/AnonymousInnerClassMayBeStaticInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/memory/AnonymousInnerClassMayBeStaticInspectionBase.java new file mode 100644 index 000000000000..974646bdcb2a --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/memory/AnonymousInnerClassMayBeStaticInspectionBase.java @@ -0,0 +1,110 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.memory; + +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 org.jetbrains.annotations.NotNull; + +public class AnonymousInnerClassMayBeStaticInspectionBase extends BaseInspection { + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message( + "anonymous.inner.may.be.named.static.inner.class.display.name"); + } + + @Override + @NotNull + public String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message( + "anonymous.inner.may.be.named.static.inner.class.problem.descriptor"); + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new AnonymousInnerClassMayBeStaticVisitor(); + } + + private static class AnonymousInnerClassMayBeStaticVisitor + extends BaseInspectionVisitor { + + @Override + public void visitAnonymousClass(@NotNull PsiAnonymousClass anonymousClass) { + if (anonymousClass instanceof PsiEnumConstantInitializer) { + return; + } + final PsiMember containingMember = PsiTreeUtil.getParentOfType(anonymousClass, PsiMember.class); + if (containingMember == null || containingMember.hasModifierProperty(PsiModifier.STATIC)) { + return; + } + final PsiJavaCodeReferenceElement reference = anonymousClass.getBaseClassReference(); + if (reference.resolve() == null) { + // don't warn on broken code + return; + } + final PsiClass containingClass = PsiTreeUtil.getParentOfType(anonymousClass, PsiClass.class); + if (containingClass == null) { + return; + } + if (containingClass.getContainingClass() != null && !containingClass.hasModifierProperty(PsiModifier.STATIC)) { + // strictly speaking can be named static inner class but not when part of the current containing class + return; + } + final InnerClassReferenceVisitor visitor = new InnerClassReferenceVisitor(anonymousClass); + anonymousClass.accept(visitor); + if (!visitor.canInnerClassBeStatic()) { + return; + } + if (hasReferenceToLocalClass(anonymousClass)) { + return; + } + registerClassError(anonymousClass); + } + + private static boolean hasReferenceToLocalClass(PsiAnonymousClass anonymousClass) { + final LocalClassReferenceVisitor visitor = new LocalClassReferenceVisitor(); + anonymousClass.accept(visitor); + return visitor.hasReferenceToLocalClass(); + } + + private static class LocalClassReferenceVisitor extends JavaRecursiveElementVisitor { + + private boolean referenceToLocalClass = false; + + @Override + public void visitReferenceElement(PsiJavaCodeReferenceElement reference) { + super.visitReferenceElement(reference); + if (reference.getQualifier() != null) { + return; + } + final PsiElement target = reference.resolve(); + if (!(target instanceof PsiClass) || !PsiUtil.isLocalClass((PsiClass)target)) { + return; + } + referenceToLocalClass = true; + } + + public boolean hasReferenceToLocalClass() { + return referenceToLocalClass; + } + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/memory/InnerClassMayBeStaticInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/memory/InnerClassMayBeStaticInspection.java new file mode 100644 index 000000000000..f588b120b8d5 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/memory/InnerClassMayBeStaticInspection.java @@ -0,0 +1,155 @@ +/* + * Copyright 2003-2014 Dave Griffith, Bas Leijdekkers + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * 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.memory; + +import com.intellij.codeInsight.FileModificationService; +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.openapi.project.Project; +import com.intellij.psi.*; +import com.intellij.psi.codeStyle.JavaCodeStyleManager; +import com.intellij.psi.search.SearchScope; +import com.intellij.psi.search.searches.ReferencesSearch; +import com.intellij.util.Query; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.InspectionGadgetsFix; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +public class InnerClassMayBeStaticInspection extends BaseInspection { + + @Override + @NotNull + public String getDisplayName() { + return InspectionGadgetsBundle.message("inner.class.may.be.static.display.name"); + } + + @Override + @NotNull + protected String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message("inner.class.may.be.static.problem.descriptor"); + } + + @Override + public boolean runForWholeFile() { + return true; + } + + @Override + protected InspectionGadgetsFix buildFix(Object... infos) { + return new InnerClassMayBeStaticFix(); + } + + private static class InnerClassMayBeStaticFix extends InspectionGadgetsFix { + + @Override + @NotNull + public String getFamilyName() { + return getName(); + } + + @Override + @NotNull + public String getName() { + return InspectionGadgetsBundle.message("make.static.quickfix"); + } + + @Override + protected boolean prepareForWriting() { + return false; + } + + @Override + public void doFix(Project project, ProblemDescriptor descriptor) { + final PsiJavaToken classNameToken = (PsiJavaToken)descriptor.getPsiElement(); + final PsiClass innerClass = (PsiClass)classNameToken.getParent(); + if (innerClass == null) { + return; + } + final SearchScope useScope = innerClass.getUseScope(); + final Query query = ReferencesSearch.search(innerClass, useScope); + final Collection references = query.findAll(); + final List elements = new ArrayList(references); + for (PsiReference reference : references) { + elements.add(reference.getElement()); + } + elements.add(innerClass); + if (!FileModificationService.getInstance().preparePsiElementsForWrite(elements)) { + return; + } + final JavaCodeStyleManager codeStyleManager = JavaCodeStyleManager.getInstance(project); + final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project); + for (final PsiReference reference : references) { + final PsiElement element = reference.getElement(); + final PsiElement parent = element.getParent(); + if (!(parent instanceof PsiNewExpression)) { + continue; + } + final PsiNewExpression newExpression = (PsiNewExpression)parent; + final PsiJavaCodeReferenceElement classReference = newExpression.getClassReference(); + if (classReference == null) { + continue; + } + final PsiExpressionList argumentList = newExpression.getArgumentList(); + if (argumentList == null) { + continue; + } + final PsiExpression expression = + factory.createExpressionFromText("new " + classReference.getQualifiedName() + argumentList.getText(), innerClass); + codeStyleManager.shortenClassReferences(newExpression.replace(expression)); + } + final PsiModifierList modifiers = innerClass.getModifierList(); + if (modifiers == null) { + return; + } + modifiers.setModifierProperty(PsiModifier.STATIC, true); + } + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new InnerClassMayBeStaticVisitor(); + } + + private static class InnerClassMayBeStaticVisitor extends BaseInspectionVisitor { + + @Override + public void visitClass(@NotNull PsiClass aClass) { + if (aClass.getContainingClass() != null && !aClass.hasModifierProperty(PsiModifier.STATIC)) { + return; + } + if (aClass instanceof PsiAnonymousClass) { + return; + } + final PsiClass[] innerClasses = aClass.getInnerClasses(); + for (final PsiClass innerClass : innerClasses) { + if (innerClass.hasModifierProperty(PsiModifier.STATIC)) { + continue; + } + final InnerClassReferenceVisitor visitor = new InnerClassReferenceVisitor(innerClass); + innerClass.accept(visitor); + if (!visitor.canInnerClassBeStatic()) { + continue; + } + registerClassError(innerClass); + } + } + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/memory/InnerClassReferenceVisitor.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/memory/InnerClassReferenceVisitor.java new file mode 100644 index 000000000000..6f1f817a7735 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/memory/InnerClassReferenceVisitor.java @@ -0,0 +1,126 @@ +/* + * Copyright 2003-2014 Dave Griffith, Bas Leijdekkers + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * 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.memory; + +import com.intellij.psi.*; +import com.intellij.psi.util.InheritanceUtil; +import com.intellij.psi.util.PsiTreeUtil; +import com.siyeh.ig.psiutils.ParenthesesUtils; +import org.jetbrains.annotations.NotNull; + +class InnerClassReferenceVisitor extends JavaRecursiveElementVisitor { + + private final PsiClass innerClass; + private boolean referencesStaticallyAccessible = true; + + public InnerClassReferenceVisitor(@NotNull PsiClass innerClass) { + this.innerClass = innerClass; + } + + public boolean canInnerClassBeStatic() { + final PsiClass superClass = innerClass.getSuperClass(); + if (!isClassStaticallyAccessible(superClass)) { + return false; + } + return referencesStaticallyAccessible; + } + + private boolean isClassStaticallyAccessible(PsiClass aClass) { + if (PsiTreeUtil.isAncestor(innerClass, aClass, false) || aClass.hasModifierProperty(PsiModifier.STATIC)) { + return true; + } + final PsiClass containingClass = aClass.getContainingClass(); + return containingClass == null || InheritanceUtil.isInheritorOrSelf(innerClass, containingClass, true); + } + + @Override + public void visitThisExpression(@NotNull PsiThisExpression expression) { + if (!referencesStaticallyAccessible) { + return; + } + super.visitThisExpression(expression); + if (hasContainingClassQualifier(expression)) { + referencesStaticallyAccessible = false; + } + } + + @Override + public void visitSuperExpression(@NotNull PsiSuperExpression expression) { + if (!referencesStaticallyAccessible) { + return; + } + super.visitSuperExpression(expression); + if (hasContainingClassQualifier(expression)) { + referencesStaticallyAccessible = false; + } + } + + private boolean hasContainingClassQualifier(PsiQualifiedExpression expression) { + final PsiJavaCodeReferenceElement qualifier = expression.getQualifier(); + if (qualifier == null) { + return false; + } + return !innerClass.equals(qualifier.resolve()); + } + + @Override + public void visitReferenceExpression(PsiReferenceExpression expression) { + if (!referencesStaticallyAccessible) { + return; + } + super.visitReferenceExpression(expression); + final PsiExpression qualifierExpression = ParenthesesUtils.stripParentheses(expression.getQualifierExpression()); + if (qualifierExpression != null) { + return; + } + final PsiElement target = expression.resolve(); + if (target instanceof PsiLocalVariable || target instanceof PsiParameter) { + return; + } + if (target instanceof PsiMethod || target instanceof PsiField) { + final PsiMember member = (PsiMember)target; + if (member.hasModifierProperty(PsiModifier.STATIC) || PsiTreeUtil.isAncestor(innerClass, member, true)) { + return; + } + if (!member.hasModifierProperty(PsiModifier.PRIVATE)) { + final PsiClass containingClass = member.getContainingClass(); + if (InheritanceUtil.isInheritorOrSelf(innerClass, containingClass, true)) { + return; + } + } + referencesStaticallyAccessible = false; + } + } + + @Override + public void visitNewExpression(PsiNewExpression expression) { + if (!referencesStaticallyAccessible) { + return; + } + super.visitNewExpression(expression); + final PsiJavaCodeReferenceElement classReference = expression.getClassOrAnonymousClassReference(); + if (classReference == null) { + return; + } + final PsiElement target = classReference.resolve(); + if (!(target instanceof PsiClass)) { + return; + } + if (!isClassStaticallyAccessible((PsiClass)target)) { + referencesStaticallyAccessible = false; + } + } +} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/performance/InnerClassMayBeStaticInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/performance/InnerClassMayBeStaticInspection.java deleted file mode 100644 index 2599327e4d7d..000000000000 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/performance/InnerClassMayBeStaticInspection.java +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright 2003-2013 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. - * 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.performance; - -import com.intellij.codeInsight.FileModificationService; -import com.intellij.codeInspection.ProblemDescriptor; -import com.intellij.openapi.project.Project; -import com.intellij.psi.*; -import com.intellij.psi.codeStyle.JavaCodeStyleManager; -import com.intellij.psi.search.SearchScope; -import com.intellij.psi.search.searches.ReferencesSearch; -import com.intellij.util.Query; -import com.siyeh.InspectionGadgetsBundle; -import com.siyeh.ig.BaseInspection; -import com.siyeh.ig.BaseInspectionVisitor; -import com.siyeh.ig.InspectionGadgetsFix; -import org.jetbrains.annotations.NotNull; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -public class InnerClassMayBeStaticInspection extends BaseInspection { - - @Override - @NotNull - public String getDisplayName() { - return InspectionGadgetsBundle.message("inner.class.may.be.static.display.name"); - } - - @Override - @NotNull - protected String buildErrorString(Object... infos) { - return InspectionGadgetsBundle.message("inner.class.may.be.static.problem.descriptor"); - } - - @Override - public boolean runForWholeFile() { - return true; - } - - @Override - protected InspectionGadgetsFix buildFix(Object... infos) { - return new InnerClassMayBeStaticFix(); - } - - private static class InnerClassMayBeStaticFix extends InspectionGadgetsFix { - - @Override - @NotNull - public String getFamilyName() { - return getName(); - } - - @Override - @NotNull - public String getName() { - return InspectionGadgetsBundle.message("make.static.quickfix"); - } - - @Override - protected boolean prepareForWriting() { - return false; - } - - @Override - public void doFix(Project project, ProblemDescriptor descriptor) { - final PsiJavaToken classNameToken = (PsiJavaToken)descriptor.getPsiElement(); - final PsiClass innerClass = (PsiClass)classNameToken.getParent(); - if (innerClass == null) { - return; - } - final SearchScope useScope = innerClass.getUseScope(); - final Query query = ReferencesSearch.search(innerClass, useScope); - final Collection references = query.findAll(); - final List elements = new ArrayList(references); - for (PsiReference reference : references) { - elements.add(reference.getElement()); - } - elements.add(innerClass); - if (!FileModificationService.getInstance().preparePsiElementsForWrite(elements)) { - return; - } - final JavaCodeStyleManager codeStyleManager = JavaCodeStyleManager.getInstance(project); - final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project); - for (final PsiReference reference : references) { - final PsiElement element = reference.getElement(); - final PsiElement parent = element.getParent(); - if (!(parent instanceof PsiNewExpression)) { - continue; - } - final PsiNewExpression newExpression = (PsiNewExpression)parent; - final PsiJavaCodeReferenceElement classReference = newExpression.getClassReference(); - if (classReference == null) { - continue; - } - final PsiExpressionList argumentList = newExpression.getArgumentList(); - if (argumentList == null) { - continue; - } - final PsiExpression expression = - factory.createExpressionFromText("new " + classReference.getQualifiedName() + argumentList.getText(), innerClass); - codeStyleManager.shortenClassReferences(newExpression.replace(expression)); - } - final PsiModifierList modifiers = innerClass.getModifierList(); - if (modifiers == null) { - return; - } - modifiers.setModifierProperty(PsiModifier.STATIC, true); - } - } - - @Override - public BaseInspectionVisitor buildVisitor() { - return new InnerClassMayBeStaticVisitor(); - } - - private static class InnerClassMayBeStaticVisitor extends BaseInspectionVisitor { - - @Override - public void visitClass(@NotNull PsiClass aClass) { - if (aClass.getContainingClass() != null && !aClass.hasModifierProperty(PsiModifier.STATIC)) { - return; - } - if (aClass instanceof PsiAnonymousClass) { - return; - } - final PsiClass[] innerClasses = aClass.getInnerClasses(); - for (final PsiClass innerClass : innerClasses) { - if (innerClass.hasModifierProperty(PsiModifier.STATIC)) { - continue; - } - final InnerClassReferenceVisitor visitor = new InnerClassReferenceVisitor(innerClass); - innerClass.accept(visitor); - if (!visitor.canInnerClassBeStatic()) { - continue; - } - registerClassError(innerClass); - } - } - } -} \ No newline at end of file diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/performance/InnerClassReferenceVisitor.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/performance/InnerClassReferenceVisitor.java deleted file mode 100644 index 45d89b4a4b1d..000000000000 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/performance/InnerClassReferenceVisitor.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright 2003-2013 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. - * 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.performance; - -import com.intellij.psi.*; -import com.intellij.psi.util.InheritanceUtil; -import com.intellij.psi.util.PsiTreeUtil; -import com.siyeh.ig.psiutils.ClassUtils; -import org.jetbrains.annotations.NotNull; - -public class InnerClassReferenceVisitor extends JavaRecursiveElementVisitor { - - private final PsiClass innerClass; - private boolean referencesStaticallyAccessible = true; - - public InnerClassReferenceVisitor(PsiClass innerClass) { - this.innerClass = innerClass; - } - - public boolean canInnerClassBeStatic() { - return referencesStaticallyAccessible; - } - - private boolean isClassStaticallyAccessible(PsiClass aClass) { - if (PsiTreeUtil.isAncestor(innerClass, aClass, false)) { - return true; - } - if (aClass.getContainingClass() != null) { - return aClass.hasModifierProperty(PsiModifier.STATIC); - } - if (InheritanceUtil.isInheritorOrSelf(innerClass, aClass, true)) { - return true; - } - PsiClass classScope = aClass; - final PsiClass outerClass = ClassUtils.getContainingClass(innerClass); - while (classScope != null) { - if (InheritanceUtil.isInheritorOrSelf(outerClass, classScope, true)) { - return false; - } - final PsiElement scope = classScope.getScope(); - if (scope instanceof PsiClass) { - classScope = (PsiClass)scope; - } - else { - classScope = null; - } - } - return true; - } - - @Override - public void visitThisExpression(@NotNull PsiThisExpression expression) { - if (!referencesStaticallyAccessible) { - return; - } - super.visitThisExpression(expression); - if (hasContainingClassQualifier(expression)) { - referencesStaticallyAccessible = false; - } - } - - @Override - public void visitSuperExpression(@NotNull PsiSuperExpression expression) { - if (!referencesStaticallyAccessible) { - return; - } - super.visitSuperExpression(expression); - if (hasContainingClassQualifier(expression)) { - referencesStaticallyAccessible = false; - } - } - - private boolean hasContainingClassQualifier(PsiQualifiedExpression expression) { - final PsiJavaCodeReferenceElement qualifier = expression.getQualifier(); - if (qualifier == null) { - return false; - } - final PsiElement element = qualifier.resolve(); - if (!(element instanceof PsiClass)) { - return false; - } - final PsiClass aClass = (PsiClass)element; - return !aClass.equals(innerClass); - } - - @Override - public void visitReferenceElement(@NotNull PsiJavaCodeReferenceElement reference) { - if (!referencesStaticallyAccessible) { - return; - } - final PsiElement parent = reference.getParent(); - if (parent instanceof PsiThisExpression || parent instanceof PsiSuperExpression) { - return; - } - super.visitReferenceElement(reference); - - final PsiElement qualifier = reference.getQualifier(); - if (qualifier instanceof PsiSuperExpression) { - return; - } - if (qualifier instanceof PsiReferenceExpression) { - final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)qualifier; - final PsiElement resolvedExpression = referenceExpression.resolve(); - if (!(resolvedExpression instanceof PsiField) && !(resolvedExpression instanceof PsiMethod)) { - return; - } - } - final PsiElement element = reference.resolve(); - if (element instanceof PsiMethod || element instanceof PsiField) { - final PsiMember member = (PsiMember)element; - if (member.hasModifierProperty(PsiModifier.STATIC)) { - return; - } - final PsiClass containingClass = member.getContainingClass(); - if (innerClass.equals(containingClass)) { - return; - } - if (member.hasModifierProperty(PsiModifier.PRIVATE)) { - referencesStaticallyAccessible = false; - return; - } - referencesStaticallyAccessible &= isClassStaticallyAccessible(containingClass); - } - else if (element instanceof PsiLocalVariable || element instanceof PsiParameter) { - final PsiElement containingMethod = PsiTreeUtil.getParentOfType(reference, PsiMethod.class); - final PsiElement referencedMethod = PsiTreeUtil.getParentOfType(element, PsiMethod.class); - if (containingMethod != null && referencedMethod != null && !containingMethod.equals(referencedMethod)) { - referencesStaticallyAccessible = false; - } - } - else if (element instanceof PsiClass) { - final PsiClass aClass = (PsiClass)element; - referencesStaticallyAccessible &= isClassStaticallyAccessible(aClass); - } - } -} diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/ClassUtils.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/ClassUtils.java index 40daa3332896..4656d0c4610a 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/ClassUtils.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/ClassUtils.java @@ -19,6 +19,7 @@ import com.intellij.psi.*; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.TypeConversionUtil; import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.Nullable; import java.util.HashSet; @@ -80,7 +81,7 @@ public class ClassUtils { private ClassUtils() {} @Nullable - public static PsiClass findClass(String fqClassName, PsiElement context) { + public static PsiClass findClass(@NonNls String fqClassName, PsiElement context) { return JavaPsiFacade.getInstance(context.getProject()).findClass(fqClassName, context.getResolveScope()); } diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/ExpressionUtils.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/ExpressionUtils.java index fc6d2580b3e9..242bec97a398 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/ExpressionUtils.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/ExpressionUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2005-2013 Bas Leijdekkers + * Copyright 2005-2014 Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -198,11 +198,8 @@ public class ExpressionUtils { } public static boolean isNullLiteral(@Nullable PsiExpression expression) { - if (!(expression instanceof PsiLiteralExpression)) { - return false; - } - final String text = expression.getText(); - return PsiKeyword.NULL.equals(text); + expression = ParenthesesUtils.stripParentheses(expression); + return expression != null && PsiType.NULL.equals(expression.getType()); } public static boolean isZero(@Nullable PsiExpression expression) { diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/CStyleArrayDeclarationInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/CStyleArrayDeclarationInspection.java index e4e91685878b..fca66288aaef 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/CStyleArrayDeclarationInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/CStyleArrayDeclarationInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2007 Dave Griffith, Bas Leijdekkers + * Copyright 2003-2014 Dave Griffith, Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,20 +16,24 @@ package com.siyeh.ig.style; import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.codeInspection.ui.SingleCheckboxOptionsPanel; import com.intellij.openapi.project.Project; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiType; -import com.intellij.psi.PsiTypeElement; -import com.intellij.psi.PsiVariable; -import com.intellij.util.IncorrectOperationException; +import com.intellij.psi.*; +import com.intellij.psi.codeStyle.CodeStyleManager; +import com.intellij.psi.tree.IElementType; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; import com.siyeh.ig.InspectionGadgetsFix; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; public class CStyleArrayDeclarationInspection extends BaseInspection { + public boolean ignoreVariables = false; + @Override @NotNull public String getDisplayName() { @@ -40,8 +44,18 @@ public class CStyleArrayDeclarationInspection extends BaseInspection { @Override @NotNull protected String buildErrorString(Object... infos) { - return InspectionGadgetsBundle.message( - "c.style.array.declaration.problem.descriptor"); + final Object info = infos[0]; + if (info instanceof PsiMethod) { + return InspectionGadgetsBundle.message("cstyle.array.method.declaration.problem.descriptor"); + } + final int choice = info instanceof PsiField ? 1 : info instanceof PsiParameter ? 2 : 3; + return InspectionGadgetsBundle.message("cstyle.array.variable.declaration.problem.descriptor", Integer.valueOf(choice)); + } + + @Nullable + @Override + public JComponent createOptionsPanel() { + return new SingleCheckboxOptionsPanel("Ignore C-style declarations in variables", this, "ignoreVariables"); } @Override @@ -65,12 +79,38 @@ public class CStyleArrayDeclarationInspection extends BaseInspection { } @Override - public void doFix(Project project, ProblemDescriptor descriptor) - throws IncorrectOperationException { - final PsiElement nameElement = descriptor.getPsiElement(); - final PsiVariable var = (PsiVariable)nameElement.getParent(); - assert var != null; - var.normalizeDeclaration(); + public void doFix(Project project, ProblemDescriptor descriptor) { + final PsiElement element = descriptor.getPsiElement().getParent(); + if (element instanceof PsiVariable) { + final PsiVariable variable = (PsiVariable)element; + variable.normalizeDeclaration(); + CodeStyleManager.getInstance(project).reformat(variable); + } + else if (element instanceof PsiMethod) { + final PsiMethod method = (PsiMethod)element; + final PsiTypeElement returnTypeElement = method.getReturnTypeElement(); + if (returnTypeElement == null) { + return; + } + final PsiType returnType = method.getReturnType(); + if (returnType == null) { + return; + } + PsiElement child = method.getParameterList(); + while (!(child instanceof PsiCodeBlock)) { + final PsiElement element1 = child; + child = child.getNextSibling(); + if (element1 instanceof PsiJavaToken) { + final PsiJavaToken token = (PsiJavaToken)element1; + final IElementType tokenType = token.getTokenType(); + if (JavaTokenType.LBRACKET.equals(tokenType) || JavaTokenType.RBRACKET.equals(tokenType)) { + token.delete(); + } + } + } + final PsiTypeElement typeElement = JavaPsiFacade.getElementFactory(project).createTypeElement(returnType); + returnTypeElement.replace(typeElement); + } } } @@ -79,17 +119,19 @@ public class CStyleArrayDeclarationInspection extends BaseInspection { return new CStyleArrayDeclarationVisitor(); } - private static class CStyleArrayDeclarationVisitor - extends BaseInspectionVisitor { + private class CStyleArrayDeclarationVisitor extends BaseInspectionVisitor { @Override - public void visitVariable(@NotNull PsiVariable var) { - super.visitVariable(var); - final PsiType declaredType = var.getType(); + public void visitVariable(@NotNull PsiVariable variable) { + super.visitVariable(variable); + if (ignoreVariables) { + return; + } + final PsiType declaredType = variable.getType(); if (declaredType.getArrayDimensions() == 0) { return; } - final PsiTypeElement typeElement = var.getTypeElement(); + final PsiTypeElement typeElement = variable.getTypeElement(); if (typeElement == null) { return; // Could be true for enum constants. } @@ -97,7 +139,25 @@ public class CStyleArrayDeclarationInspection extends BaseInspection { if (elementType.equals(declaredType)) { return; } - registerVariableError(var); + registerVariableError(variable, variable); + } + + @Override + public void visitMethod(PsiMethod method) { + super.visitMethod(method); + final PsiType returnType = method.getReturnType(); + if (returnType == null || returnType.getArrayDimensions() == 0) { + return; + } + final PsiTypeElement typeElement = method.getReturnTypeElement(); + if (typeElement == null) { + return; + } + final PsiType type = typeElement.getType(); + if (type.equals(returnType)) { + return; + } + registerMethodError(method, method); } } } \ No newline at end of file diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/NestedMethodCallInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/NestedMethodCallInspectionBase.java index 484c1f8fefe0..57659d05923a 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/NestedMethodCallInspectionBase.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/NestedMethodCallInspectionBase.java @@ -16,6 +16,7 @@ package com.siyeh.ig.style; import com.intellij.psi.*; +import com.intellij.psi.util.PropertyUtil; import com.intellij.psi.util.PsiTreeUtil; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspection; @@ -29,6 +30,12 @@ public class NestedMethodCallInspectionBase extends BaseInspection { */ public boolean m_ignoreFieldInitializations = true; + @SuppressWarnings("PublicField") + public boolean ignoreStaticMethods = false; + + @SuppressWarnings("PublicField") + public boolean ignoreGetterCalls = false; + @Override @NotNull public String getDisplayName() { @@ -82,6 +89,18 @@ public class NestedMethodCallInspectionBase extends BaseInspection { return; } } + final PsiMethod method = expression.resolveMethod(); + if (method == null) { + return; + } + if (ignoreStaticMethods || ignoreGetterCalls) { + if (ignoreStaticMethods && method.hasModifierProperty(PsiModifier.STATIC)) { + return; + } + if (ignoreGetterCalls && PropertyUtil.isSimpleGetter(method)) { + return; + } + } registerMethodCallError(expression); } } diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/threading/DoubleCheckedLockingInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/threading/DoubleCheckedLockingInspection.java index 0036f4a41f99..d9f5242d3ab6 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/threading/DoubleCheckedLockingInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/threading/DoubleCheckedLockingInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2010 Dave Griffith, Bas Leijdekkers + * Copyright 2003-2014 Dave Griffith, Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,7 +20,6 @@ import com.intellij.codeInspection.ui.SingleCheckboxOptionsPanel; import com.intellij.openapi.project.Project; import com.intellij.psi.*; import com.intellij.psi.tree.IElementType; -import com.intellij.util.IncorrectOperationException; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; @@ -74,17 +73,16 @@ public class DoubleCheckedLockingInspection extends BaseInspection { private static class DoubleCheckedLockingFix extends InspectionGadgetsFix { - private final PsiField field; + private final String myFieldName; private DoubleCheckedLockingFix(PsiField field) { - this.field = field; + myFieldName = field.getName(); } @Override @NotNull public String getName() { - return InspectionGadgetsBundle.message( - "double.checked.locking.quickfix", field.getName()); + return InspectionGadgetsBundle.message("double.checked.locking.quickfix", myFieldName); } @NotNull @@ -94,8 +92,21 @@ public class DoubleCheckedLockingInspection extends BaseInspection { } @Override - protected void doFix(Project project, ProblemDescriptor descriptor) - throws IncorrectOperationException { + protected void doFix(Project project, ProblemDescriptor descriptor) { + final PsiElement element = descriptor.getPsiElement(); + final PsiElement parent = element.getParent(); + if (!(parent instanceof PsiIfStatement)) { + return; + } + final PsiIfStatement ifStatement = (PsiIfStatement)parent; + final PsiExpression condition = ifStatement.getCondition(); + if (condition == null) { + return; + } + final PsiField field = findCheckedField(condition); + if (field == null) { + return; + } final PsiModifierList modifierList = field.getModifierList(); if (modifierList == null) { return; @@ -104,13 +115,55 @@ public class DoubleCheckedLockingInspection extends BaseInspection { } } + @Nullable + private static PsiField findCheckedField(PsiExpression expression) { + if (expression instanceof PsiReferenceExpression) { + final PsiReferenceExpression referenceExpression = + (PsiReferenceExpression)expression; + final PsiElement target = referenceExpression.resolve(); + if (!(target instanceof PsiField)) { + return null; + } + return (PsiField)target; + } + else if (expression instanceof PsiBinaryExpression) { + final PsiBinaryExpression binaryExpression = + (PsiBinaryExpression)expression; + final IElementType tokenType = + binaryExpression.getOperationTokenType(); + if (!JavaTokenType.EQEQ.equals(tokenType) + && !JavaTokenType.NE.equals(tokenType)) { + return null; + } + final PsiExpression lhs = binaryExpression.getLOperand(); + final PsiExpression rhs = binaryExpression.getROperand(); + final PsiField field = findCheckedField(lhs); + if (field != null) { + return field; + } + return findCheckedField(rhs); + } + else if (expression instanceof PsiPrefixExpression) { + final PsiPrefixExpression prefixExpression = + (PsiPrefixExpression)expression; + final IElementType tokenType = + prefixExpression.getOperationTokenType(); + if (!JavaTokenType.EXCL.equals(tokenType)) { + return null; + } + return findCheckedField(prefixExpression.getOperand()); + } + else { + return null; + } + } + @Override public BaseInspectionVisitor buildVisitor() { return new DoubleCheckedLockingVisitor(); } - private class DoubleCheckedLockingVisitor - extends BaseInspectionVisitor { + private class DoubleCheckedLockingVisitor extends BaseInspectionVisitor { @Override public void visitIfStatement( @@ -161,48 +214,5 @@ public class DoubleCheckedLockingInspection extends BaseInspection { } registerStatementError(statement, field); } - - @Nullable - private PsiField findCheckedField(PsiExpression expression) { - if (expression instanceof PsiReferenceExpression) { - final PsiReferenceExpression referenceExpression = - (PsiReferenceExpression)expression; - final PsiElement target = referenceExpression.resolve(); - if (!(target instanceof PsiField)) { - return null; - } - return (PsiField)target; - } - else if (expression instanceof PsiBinaryExpression) { - final PsiBinaryExpression binaryExpression = - (PsiBinaryExpression)expression; - final IElementType tokenType = - binaryExpression.getOperationTokenType(); - if (!JavaTokenType.EQEQ.equals(tokenType) - && !JavaTokenType.NE.equals(tokenType)) { - return null; - } - final PsiExpression lhs = binaryExpression.getLOperand(); - final PsiExpression rhs = binaryExpression.getROperand(); - final PsiField field = findCheckedField(lhs); - if (field != null) { - return field; - } - return findCheckedField(rhs); - } - else if (expression instanceof PsiPrefixExpression) { - final PsiPrefixExpression prefixExpression = - (PsiPrefixExpression)expression; - final IElementType tokenType = - prefixExpression.getOperationTokenType(); - if (!JavaTokenType.EXCL.equals(tokenType)) { - return null; - } - return findCheckedField(prefixExpression.getOperand()); - } - else { - return null; - } - } } } \ No newline at end of file diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/initialization/NonThreadSafeLazyInitializationInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/initialization/NonThreadSafeLazyInitializationInspection.java new file mode 100644 index 000000000000..7b2355930287 --- /dev/null +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/initialization/NonThreadSafeLazyInitializationInspection.java @@ -0,0 +1,216 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.initialization; + +import com.intellij.codeInsight.CodeInsightUtilCore; +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.fileEditor.FileEditorManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Pass; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.*; +import com.intellij.psi.search.searches.ReferencesSearch; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.util.PsiUtil; +import com.intellij.refactoring.rename.RenamePsiElementProcessor; +import com.intellij.refactoring.rename.inplace.MemberInplaceRenamer; +import com.intellij.util.Processor; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.InspectionGadgetsFix; +import com.siyeh.ig.psiutils.ControlFlowUtils; +import com.siyeh.ig.psiutils.ParenthesesUtils; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; + +import java.util.Arrays; +import java.util.Collection; +import java.util.LinkedHashSet; + +/** + * @author Bas Leijdekkers + */ +public class NonThreadSafeLazyInitializationInspection extends NonThreadSafeLazyInitializationInspectionBase { + + @Override + protected InspectionGadgetsFix buildFix(Object... infos) { + final PsiIfStatement ifStatement = (PsiIfStatement)infos[0]; + final PsiField field = (PsiField)infos[1]; + if (!isStaticAndAssignedOnce(field) || !isSafeToDeleteIfStatement(ifStatement, field)) { + return null; + } + return new IntroduceHolderFix(); + } + + private static boolean isStaticAndAssignedOnce(PsiField field) { + if (!field.hasModifierProperty(PsiModifier.STATIC)) { + return false; + } + final int[] writeCount = new int[1]; + return ReferencesSearch.search(field).forEach(new Processor() { + @Override + public boolean process(PsiReference reference) { + final PsiElement element = reference.getElement(); + if (!(element instanceof PsiExpression) || !PsiUtil.isAccessedForWriting((PsiExpression)element)) { + return true; + } + return ++writeCount[0] != 2; + } + }); + } + + private static boolean isSafeToDeleteIfStatement(PsiIfStatement ifStatement, PsiField field) { + if (ifStatement.getElseBranch() != null) { + return false; + } + final PsiStatement thenBranch = ifStatement.getThenBranch(); + if (thenBranch == null) { + return false; + } + final PsiStatement statement = ControlFlowUtils.stripBraces(thenBranch); + if (!(statement instanceof PsiExpressionStatement)) { + return false; + } + final PsiExpressionStatement expressionStatement = (PsiExpressionStatement)statement; + return isSimpleAssignment(expressionStatement, field); + } + + private static boolean isSimpleAssignment(PsiExpressionStatement expressionStatement, PsiField field) { + final PsiExpression expression = expressionStatement.getExpression(); + if (!(expression instanceof PsiAssignmentExpression)) { + return false; + } + final PsiAssignmentExpression assignmentExpression = (PsiAssignmentExpression)expression; + final PsiExpression lhs = ParenthesesUtils.stripParentheses(assignmentExpression.getLExpression()); + if (!(lhs instanceof PsiReferenceExpression)) { + return false; + } + final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)lhs; + final PsiElement target = referenceExpression.resolve(); + if (!field.equals(target)) { + return false; + } + final Collection referenceChildren = + PsiTreeUtil.findChildrenOfType(assignmentExpression.getRExpression(), PsiReferenceExpression.class); + for (PsiReferenceExpression child : referenceChildren) { + final PsiElement target2 = child.resolve(); + if (!(target2 instanceof PsiMember)) { + return false; + } + final PsiMember member = (PsiMember)target2; + if (!member.hasModifierProperty(PsiModifier.STATIC)) { + return false; + } + } + return true; + } + + private static class IntroduceHolderFix extends InspectionGadgetsFix { + + @Override + protected void doFix(Project project, ProblemDescriptor descriptor) { + final PsiReferenceExpression expression = (PsiReferenceExpression)descriptor.getPsiElement(); + final PsiElement resolved = expression.resolve(); + if (!(resolved instanceof PsiField)) { + return; + } + final PsiField field = (PsiField)resolved; + @NonNls final String holderName = StringUtil.capitalize(field.getName()) + "Holder"; + final PsiElement expressionParent = expression.getParent(); + if (!(expressionParent instanceof PsiAssignmentExpression)) { + return; + } + final PsiAssignmentExpression assignmentExpression = (PsiAssignmentExpression)expressionParent; + final PsiExpression rhs = assignmentExpression.getRExpression(); + if (rhs == null) { + return; + } + @NonNls final String text = "private static class " + holderName + " {" + + "private static final " + field.getType().getCanonicalText() + " " + + field.getName() + " = " + rhs.getText() + ";}"; + final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(field.getProject()); + final PsiClass holder = elementFactory.createClassFromText(text, field).getInnerClasses()[0]; + final PsiMethod method = PsiTreeUtil.getParentOfType(expression, PsiMethod.class); + if (method == null) { + return; + } + final PsiClass holderClass = (PsiClass)method.getParent().addBefore(holder, method); + + final PsiIfStatement ifStatement = PsiTreeUtil.getParentOfType(expression, PsiIfStatement.class); + if (ifStatement != null) { + ifStatement.delete(); + } + + final PsiExpression holderReference = elementFactory.createExpressionFromText(holderName + "." + field.getName(), field); + for (PsiReference reference : ReferencesSearch.search(field).findAll()) { + reference.getElement().replace(holderReference); + } + field.delete(); + + if (!isOnTheFly()) { + return; + } + invokeInplaceRename(holderClass, holderName, suggestHolderName(field)); + } + + private static void invokeInplaceRename(PsiNameIdentifierOwner nameIdentifierOwner, final String... suggestedNames) { + final PsiNameIdentifierOwner elementToRename = CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(nameIdentifierOwner); + final Editor editor = FileEditorManager.getInstance(nameIdentifierOwner.getProject()).getSelectedTextEditor(); + if (editor == null) { + return; + } + final PsiElement identifier = elementToRename.getNameIdentifier(); + if (identifier == null) { + return; + } + editor.getCaretModel().moveToOffset(identifier.getTextOffset()); + final RenamePsiElementProcessor processor = RenamePsiElementProcessor.forElement(elementToRename); + if (!processor.isInplaceRenameSupported()) { + return; + } + processor.substituteElementToRename(elementToRename, editor, new Pass() { + @Override + public void pass(PsiElement substitutedElement) { + final MemberInplaceRenamer renamer = new MemberInplaceRenamer(elementToRename, substitutedElement, editor); + final LinkedHashSet nameSuggestions = new LinkedHashSet(Arrays.asList(suggestedNames)); + renamer.performInplaceRefactoring(nameSuggestions); + } + }); + } + + @NonNls + private static String suggestHolderName(PsiField field) { + String string = field.getType().getDeepComponentType().getPresentableText(); + final int index = string.indexOf('<'); + if (index != -1) { + string = string.substring(0, index); + } + return string + "Holder"; + } + + @Override + @NotNull + public String getName() { + return InspectionGadgetsBundle.message("introduce.holder.class.quickfix"); + } + + @NotNull + @Override + public String getFamilyName() { + return getName(); + } + } +} diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/j2me/AnonymousInnerClassMayBeStaticInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/j2me/AnonymousInnerClassMayBeStaticInspection.java deleted file mode 100644 index 6862eaf3fd14..000000000000 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/j2me/AnonymousInnerClassMayBeStaticInspection.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2003-2008 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. - * 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.j2me; - -import com.siyeh.InspectionGadgetsBundle; -import com.siyeh.ig.InspectionGadgetsFix; -import com.siyeh.ig.fixes.MoveAnonymousToInnerClassFix; - -public class AnonymousInnerClassMayBeStaticInspection extends AnonymousInnerClassMayBeStaticInspectionBase { - - @Override - protected InspectionGadgetsFix buildFix(Object... infos) { - return new MoveAnonymousToInnerClassFix( - InspectionGadgetsBundle.message( - "anonymous.inner.may.be.named.static.inner.class.quickfix")); - } -} \ No newline at end of file diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/javadoc/MissingPackageInfoInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/javadoc/MissingPackageInfoInspection.java new file mode 100644 index 000000000000..2c498c207913 --- /dev/null +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/javadoc/MissingPackageInfoInspection.java @@ -0,0 +1,83 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.javadoc; + +import com.intellij.codeInspection.LocalInspectionTool; +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.ide.DataManager; +import com.intellij.ide.actions.CreatePackageInfoAction; +import com.intellij.openapi.actionSystem.ActionManager; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.DataContext; +import com.intellij.openapi.actionSystem.Presentation; +import com.intellij.openapi.project.Project; +import com.intellij.util.Consumer; +import com.siyeh.ig.InspectionGadgetsFix; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author Bas Leijdekkers + */ +public class MissingPackageInfoInspection extends MissingPackageInfoInspectionBase { + + @Nullable + @Override + public LocalInspectionTool getSharedLocalInspectionTool() { + return new LocalMissingPackageInfoInspection(this); + } + + private class LocalMissingPackageInfoInspection extends LocalMissingPackageInfoInspectionBase { + + public LocalMissingPackageInfoInspection(MissingPackageInfoInspectionBase settingsDelegate) { + super(settingsDelegate); + } + + @Nullable + @Override + protected InspectionGadgetsFix buildFix(Object... infos) { + return new InspectionGadgetsFix() { + @NotNull + @Override + public String getName() { + return "Create 'package-info.java'"; + } + + @NotNull + @Override + public String getFamilyName() { + return getName(); + } + + @Override + protected boolean prepareForWriting() { + return false; + } + + @Override + protected void doFix(Project project, ProblemDescriptor descriptor) { + DataManager.getInstance().getDataContextFromFocus().doWhenDone(new Consumer() { + @Override + public void consume(DataContext context) { + final AnActionEvent event = new AnActionEvent(null, context, "", new Presentation(), ActionManager.getInstance(), 0); + new CreatePackageInfoAction().actionPerformed(event); + } + }); + } + }; + } + } +} diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/memory/AnonymousInnerClassMayBeStaticInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/memory/AnonymousInnerClassMayBeStaticInspection.java new file mode 100644 index 000000000000..f403f1326bd0 --- /dev/null +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/memory/AnonymousInnerClassMayBeStaticInspection.java @@ -0,0 +1,30 @@ +/* + * Copyright 2003-2014 Dave Griffith, Bas Leijdekkers + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * 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.memory; + +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.InspectionGadgetsFix; +import com.siyeh.ig.fixes.MoveAnonymousToInnerClassFix; + +public class AnonymousInnerClassMayBeStaticInspection extends AnonymousInnerClassMayBeStaticInspectionBase { + + @Override + protected InspectionGadgetsFix buildFix(Object... infos) { + return new MoveAnonymousToInnerClassFix( + InspectionGadgetsBundle.message( + "anonymous.inner.may.be.named.static.inner.class.quickfix")); + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/style/NestedMethodCallInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/style/NestedMethodCallInspection.java index bcdcf61c9175..097a24573328 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/style/NestedMethodCallInspection.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/style/NestedMethodCallInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2012 Dave Griffith, Bas Leijdekkers + * Copyright 2003-2014 Dave Griffith, Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +15,7 @@ */ package com.siyeh.ig.style; -import com.intellij.codeInspection.ui.SingleCheckboxOptionsPanel; +import com.intellij.codeInspection.ui.MultipleCheckboxOptionsPanel; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.InspectionGadgetsFix; import com.siyeh.ig.fixes.IntroduceVariableFix; @@ -26,8 +26,11 @@ public class NestedMethodCallInspection extends NestedMethodCallInspectionBase { @Override public JComponent createOptionsPanel() { - return new SingleCheckboxOptionsPanel(InspectionGadgetsBundle.message("nested.method.call.ignore.option"), - this, "m_ignoreFieldInitializations"); + final MultipleCheckboxOptionsPanel panel = new MultipleCheckboxOptionsPanel(this); + panel.addCheckbox(InspectionGadgetsBundle.message("nested.method.call.ignore.option"), "m_ignoreFieldInitializations"); + panel.addCheckbox(InspectionGadgetsBundle.message("ignore.calls.to.static.methods"), "ignoreStaticMethods"); + panel.addCheckbox(InspectionGadgetsBundle.message("ignore.calls.to.property.getters"), "ignoreGetterCalls"); + return panel; } @Override diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/AmbiguousFieldAccess.html b/plugins/InspectionGadgets/src/inspectionDescriptions/AmbiguousFieldAccess.html index b0f698b74e7e..c8c714b78f40 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/AmbiguousFieldAccess.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/AmbiguousFieldAccess.html @@ -1,9 +1,9 @@ -Reports field accesses of a super class where a local variable, parameter or field of the same name is available -in the surrounding class. In this case a cursory reader of the code may think that a variable in the surrounding class is -accessed, when in fact a field from the super class is accessed. To make the intent of the code more clear it is recommended to add a -this qualifier to the field access call. +Reports ambiguous accesses of a super class field from an inner or anonymous class, where a local variable, parameter or field with identical name is available +in the surrounding code. In this situation a cursory examination of the code may suggest that an element in the surrounding code is +accessed, when in actual fact a field from the super class is accessed. To clarify the intent of the code it is recommended to add a +super qualifier to the field access.

Example:


 class X {
diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/AmbiguousMethodCall.html b/plugins/InspectionGadgets/src/inspectionDescriptions/AmbiguousMethodCall.html
index 47bb71c5aee4..e1384463c5e1 100644
--- a/plugins/InspectionGadgets/src/inspectionDescriptions/AmbiguousMethodCall.html
+++ b/plugins/InspectionGadgets/src/inspectionDescriptions/AmbiguousMethodCall.html
@@ -1,10 +1,10 @@
 
 
-Reports any method calls to super methods where a method
-with the same name is available in the surrounding class. In this case a cursory
-reader of the code may think that a method in the surrounding class is
-called, when in fact a method from the super class is called. To make the
-intent of the code more clear it is recommended to add a
+Reports ambiguous method calls to a super method from an inner or anonymous class, where a method
+with identical name is available in the surrounding class. In this situation a cursory
+examination of the code may suggest a method in the surrounding class is
+called, when in actual fact a method from the super class is called. To clarify the
+intent of the code it is recommended to add a
 super qualifier to the method call.
 
 

diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/AnonymousInnerClassMayBeStatic.html b/plugins/InspectionGadgets/src/inspectionDescriptions/AnonymousInnerClassMayBeStatic.html index 9311966c365b..a0794ca5dfc8 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/AnonymousInnerClassMayBeStatic.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/AnonymousInnerClassMayBeStatic.html @@ -1,11 +1,10 @@ -This inspection is intended for J2ME and other highly resource constrained environments. -Applying the results of this inspection without consideration might have negative effects on code clarity and design. +Reports any anonymous classes which may safely be made into a named static inner class. +An anonymous class may be static if it doesn't explicitly reference its enclosing instance or local variables from its surrounding method.

-Reports any anonymous inner classes which may safely be made into a named -static inner class. An inner class may be static if it doesn't reference -its enclosing class instance or local variables. A static inner class uses slightly less memory. +A static inner class does not keep an implicit reference to its enclosing instance. +This prevents a common cause of memory leaks and uses less memory per instance of the class.

diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/CStyleArrayDeclaration.html b/plugins/InspectionGadgets/src/inspectionDescriptions/CStyleArrayDeclaration.html index 6b8f3ff43b2a..7320abc37f19 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/CStyleArrayDeclaration.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/CStyleArrayDeclaration.html @@ -1,9 +1,18 @@ -Reports array declarations made using C-style syntax, with the array indicator attached to the variable, -rather than Java-style syntax, with the array indicator attached to the type. +Reports array declarations made using C-style syntax, +with the array indicator brackets positioned after the variable name or after the method parameter list. +For example: +

+  public String process(String value[])[] {
+    return value;
+  }
+
+Most code styles prefer Java-style array declarations, with the array indicator brackets attached to the type name.

+Use the checkbox below to only report C-style array declaration of method return types. +

\ No newline at end of file diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/DoubleBraceInitialization.html b/plugins/InspectionGadgets/src/inspectionDescriptions/DoubleBraceInitialization.html new file mode 100644 index 000000000000..e2cf8b607f66 --- /dev/null +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/DoubleBraceInitialization.html @@ -0,0 +1,13 @@ + + +Reports Double Brace Initialization. +Double brace initialization can cause memory leaks when used from a non-static context, +because the anonymous class created will maintain a reference to the surrounding object. +It has worse performance than regular initialization because of the additional class loading required. +It can cause equals() comparisons to fail, if the equals() method does not accept subclasses as parameter (see link above). +And finally, it cannot be combined with Java 7 diamond operator, because that cannot be used with anonymous classes. + +

+ New in 14 + + \ No newline at end of file diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/DoubleCheckedLocking.html b/plugins/InspectionGadgets/src/inspectionDescriptions/DoubleCheckedLocking.html index efa4d7a0ec34..e50edd8a9fc3 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/DoubleCheckedLocking.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/DoubleCheckedLocking.html @@ -6,9 +6,9 @@ discussion of double-checked locking and why it is unsafe, see ">http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html

-Use the checkbox below to ignore double-checked locking on volatile fields. Using -a volatile field for double-checked locking works correctly on virtual machines which -implement the new Java Memory Model. +Use the checkbox below to ignore double-checked locking on volatile fields. Using +a volatile field for double-checked locking works correctly on virtual machines which +implement the Java Memory Model.

diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/InnerClassMayBeStatic.html b/plugins/InspectionGadgets/src/inspectionDescriptions/InnerClassMayBeStatic.html index 8d81ee6a4292..578f60b2d618 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/InnerClassMayBeStatic.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/InnerClassMayBeStatic.html @@ -1,8 +1,10 @@ -Reports any inner classes which may safely be made -static. An inner class may be static if it doesn't reference -its enclosing class instance. A static inner class uses slightly less memory. +Reports any inner classes which may safely be made static. +An inner class may be static if it doesn't reference its enclosing instance. +

+A static inner class does not keep an implicit reference to its enclosing instance. +This prevents a common cause of memory leaks and uses less memory per instance of the class.

diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/NonThreadSafeLazyInitialization.html b/plugins/InspectionGadgets/src/inspectionDescriptions/NonThreadSafeLazyInitialization.html index f203aa949764..a1d87f3e9d1b 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/NonThreadSafeLazyInitialization.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/NonThreadSafeLazyInitialization.html @@ -1,16 +1,16 @@ -Reports static variables being lazily initialized -in an non-thread-safe manner. Lazy initialization of static variables should be done +Reports static variables being lazily initialized +in an non-thread-safe manner. Lazy initialization of static variables should be done in an appropriate synchronization construct, to prevent different threads from performing conflicting initialization.

-If applicable, quick-fix is suggested -which introduces static holder pattern described in +If applicable, a quickfix is suggested +which introduces the static holder pattern described in http://en.wikipedia.org/wiki/Initialization_on_demand_holder_idiom -where the JVM guarantees thread-safety of such initializations. +where the JVM guarantees the thread-safety of such initializations.

diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/PropertyValueSetToItself.html b/plugins/InspectionGadgets/src/inspectionDescriptions/PropertyValueSetToItself.html index c72e160a9b8c..60964e59716c 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/PropertyValueSetToItself.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/PropertyValueSetToItself.html @@ -1,8 +1,8 @@ -Reports calls on a setter with the value of same objects getter. +Reports calls on a setter with the value of the same object's getter. For example: bean.setPayerId(bean.getPayerId()); -In regular circumstances this code is a no-op and probably not what was intented.. +In regular circumstances this code is a no-op and probably not what was intended.

New in 14 diff --git a/plugins/InspectionGadgets/test/com/siyeh/igfixes/initialization/double_brace_initialization/Argument.java b/plugins/InspectionGadgets/test/com/siyeh/igfixes/initialization/double_brace_initialization/Argument.java new file mode 100644 index 000000000000..766ea62ea51d --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igfixes/initialization/double_brace_initialization/Argument.java @@ -0,0 +1,12 @@ +public class Argument { + + void m(A a) {} + void n() { + m(new A() {{ setI(1); setJ(2); }}); + } + + class A { + void setI(int i) {} + void setJ(int j) {} + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igfixes/initialization/double_brace_initialization/Field.after.java b/plugins/InspectionGadgets/test/com/siyeh/igfixes/initialization/double_brace_initialization/Field.after.java new file mode 100644 index 000000000000..804a5e59d66b --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igfixes/initialization/double_brace_initialization/Field.after.java @@ -0,0 +1,12 @@ +import java.util.ArrayList; +import java.util.List; + +public class Field { + static final List list = new ArrayList(); + + static { + for (int i = 0; i < 10; i++) { + list.add(i); + } + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igfixes/initialization/double_brace_initialization/Field.java b/plugins/InspectionGadgets/test/com/siyeh/igfixes/initialization/double_brace_initialization/Field.java new file mode 100644 index 000000000000..4f75edf20f18 --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igfixes/initialization/double_brace_initialization/Field.java @@ -0,0 +1,10 @@ +import java.util.ArrayList; +import java.util.List; + +public class Field { + static final List list = new ArrayList() {{ + for (int i = 0; i < 10; i++) { + add(i); + } + }}; +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igfixes/initialization/double_brace_initialization/LocalVariable.after.java b/plugins/InspectionGadgets/test/com/siyeh/igfixes/initialization/double_brace_initialization/LocalVariable.after.java new file mode 100644 index 000000000000..92f47c31f39c --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igfixes/initialization/double_brace_initialization/LocalVariable.after.java @@ -0,0 +1,13 @@ +import java.util.HashMap; + +public class LocalVariable { + + void m() { + final HashMap map = new HashMap();// comment + map.put("a", "b"); + map.put("a", "b"); + map.put("a", "b"); + map.put("a", "b"); + + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igfixes/initialization/double_brace_initialization/LocalVariable.java b/plugins/InspectionGadgets/test/com/siyeh/igfixes/initialization/double_brace_initialization/LocalVariable.java new file mode 100644 index 000000000000..12b6f415a4a9 --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igfixes/initialization/double_brace_initialization/LocalVariable.java @@ -0,0 +1,14 @@ +import java.util.HashMap; + +public class LocalVariable { + + void m() { + final HashMap map = new HashMap() {{ + // comment + put("a", "b"); + put("a", "b"); + put("a", "b"); + put("a", "b"); + }}; + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igfixes/memory/inner_class_static/Simple.after.java b/plugins/InspectionGadgets/test/com/siyeh/igfixes/memory/inner_class_static/Simple.after.java new file mode 100644 index 000000000000..2b2ebd86b79a --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igfixes/memory/inner_class_static/Simple.after.java @@ -0,0 +1,18 @@ +package memory.inner_class_static; + +class Simple { + static class Inner {} + + void m() { + new Inner(); + } + + static void s(Simple s) { + new Inner(); + } +} +class X { + X() { + new Simple.Inner(); + } +} diff --git a/plugins/InspectionGadgets/test/com/siyeh/igfixes/memory/inner_class_static/Simple.java b/plugins/InspectionGadgets/test/com/siyeh/igfixes/memory/inner_class_static/Simple.java new file mode 100644 index 000000000000..e08255ca8562 --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igfixes/memory/inner_class_static/Simple.java @@ -0,0 +1,18 @@ +package memory.inner_class_static; + +class Simple { + class Inner {} + + void m() { + new Inner(); + } + + static void s(Simple s) { + s.new Inner(); + } +} +class X { + X() { + new Simple().new Inner(); + } +} diff --git a/plugins/InspectionGadgets/test/com/siyeh/igfixes/performance/inner_class_static/Simple.after.java b/plugins/InspectionGadgets/test/com/siyeh/igfixes/performance/inner_class_static/Simple.after.java deleted file mode 100644 index 973bfc5b6e06..000000000000 --- a/plugins/InspectionGadgets/test/com/siyeh/igfixes/performance/inner_class_static/Simple.after.java +++ /dev/null @@ -1,18 +0,0 @@ -package performance.inner_class_static; - -class Simple { - static class Inner {} - - void m() { - new Inner(); - } - - static void s(Simple s) { - new Inner(); - } -} -class X { - X() { - new Simple.Inner(); - } -} diff --git a/plugins/InspectionGadgets/test/com/siyeh/igfixes/performance/inner_class_static/Simple.java b/plugins/InspectionGadgets/test/com/siyeh/igfixes/performance/inner_class_static/Simple.java deleted file mode 100644 index c183bdb0986f..000000000000 --- a/plugins/InspectionGadgets/test/com/siyeh/igfixes/performance/inner_class_static/Simple.java +++ /dev/null @@ -1,18 +0,0 @@ -package performance.inner_class_static; - -class Simple { - class Inner {} - - void m() { - new Inner(); - } - - static void s(Simple s) { - s.new Inner(); - } -} -class X { - X() { - new Simple().new Inner(); - } -} diff --git a/plugins/InspectionGadgets/test/com/siyeh/igfixes/style/cstyle_array_declaration/FieldWithWhitespace.after.java b/plugins/InspectionGadgets/test/com/siyeh/igfixes/style/cstyle_array_declaration/FieldWithWhitespace.after.java new file mode 100644 index 000000000000..3a3984b1a64c --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igfixes/style/cstyle_array_declaration/FieldWithWhitespace.after.java @@ -0,0 +1,4 @@ +class FieldWithWhitespace { + + String[] s; +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igfixes/style/cstyle_array_declaration/FieldWithWhitespace.java b/plugins/InspectionGadgets/test/com/siyeh/igfixes/style/cstyle_array_declaration/FieldWithWhitespace.java new file mode 100644 index 000000000000..1f02831d6632 --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igfixes/style/cstyle_array_declaration/FieldWithWhitespace.java @@ -0,0 +1,4 @@ +class FieldWithWhitespace { + + String s []; +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igfixes/style/cstyle_array_declaration/SimpleMethod.after.java b/plugins/InspectionGadgets/test/com/siyeh/igfixes/style/cstyle_array_declaration/SimpleMethod.after.java new file mode 100644 index 000000000000..0b918a955215 --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igfixes/style/cstyle_array_declaration/SimpleMethod.after.java @@ -0,0 +1,6 @@ +public class SimpleMethod { + + public String[] ohGod(String[] a) { + return a; + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igfixes/style/cstyle_array_declaration/SimpleMethod.java b/plugins/InspectionGadgets/test/com/siyeh/igfixes/style/cstyle_array_declaration/SimpleMethod.java new file mode 100644 index 000000000000..5d0ac265b2fc --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igfixes/style/cstyle_array_declaration/SimpleMethod.java @@ -0,0 +1,6 @@ +public class SimpleMethod { + + public String ohGod(String[] a)[] { + return a; + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igfixes/threading/make_field_volatile/Simple.after.java b/plugins/InspectionGadgets/test/com/siyeh/igfixes/threading/make_field_volatile/Simple.after.java new file mode 100644 index 000000000000..f33e30331623 --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igfixes/threading/make_field_volatile/Simple.after.java @@ -0,0 +1,19 @@ +public class Simple +{ + private static volatile Object s_instance; + + public static Object foo() + { + if(s_instance == null) + { + synchronized(Simple.class) + { + if(s_instance == null) + { + s_instance = new Object(); + } + } + } + return s_instance; + } +} diff --git a/plugins/InspectionGadgets/test/com/siyeh/igfixes/threading/make_field_volatile/Simple.java b/plugins/InspectionGadgets/test/com/siyeh/igfixes/threading/make_field_volatile/Simple.java new file mode 100644 index 000000000000..b514dc20378a --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igfixes/threading/make_field_volatile/Simple.java @@ -0,0 +1,19 @@ +public class Simple +{ + private static Object s_instance; + + public static Object foo() + { + if(s_instance == null) + { + synchronized(Simple.class) + { + if(s_instance == null) + { + s_instance = new Object(); + } + } + } + return s_instance; + } +} diff --git a/plugins/InspectionGadgets/test/com/siyeh/igfixes/threading/non_thread_safe_lazy_initialization/InstanceVariableReferenced.java b/plugins/InspectionGadgets/test/com/siyeh/igfixes/threading/non_thread_safe_lazy_initialization/InstanceVariableReferenced.java new file mode 100644 index 000000000000..f72c45f2f395 --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igfixes/threading/non_thread_safe_lazy_initialization/InstanceVariableReferenced.java @@ -0,0 +1,16 @@ +public class InstanceVariableReferenced { + + private static Object example; + private String s = "yes"; + + public Object getInstance() { + if (example == null) { + example = getString(s); + } + return example + } + + private static String getString(String s) { + return new String(s); + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igfixes/threading/non_thread_safe_lazy_initialization/LocalVariableReferenced.java b/plugins/InspectionGadgets/test/com/siyeh/igfixes/threading/non_thread_safe_lazy_initialization/LocalVariableReferenced.java new file mode 100644 index 000000000000..929994a18ffe --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igfixes/threading/non_thread_safe_lazy_initialization/LocalVariableReferenced.java @@ -0,0 +1,11 @@ +public class LocalVariableReferenced { + + private static Object o; + + public static Object getInstance(int i) { + if (o == null) { + o = "" + i; + } + return o; + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igfixes/threading/non_thread_safe_lazy_initialization/NestedAssignment.java b/plugins/InspectionGadgets/test/com/siyeh/igfixes/threading/non_thread_safe_lazy_initialization/NestedAssignment.java new file mode 100644 index 000000000000..89f229fbd3f0 --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igfixes/threading/non_thread_safe_lazy_initialization/NestedAssignment.java @@ -0,0 +1,12 @@ +public class NestedAssignment { + + private static Object o; + + public static Object getInstance() { + Object local = null; + if (o == null) { + local = o = new Object(); + } + return o; + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igfixes/threading/non_thread_safe_lazy_initialization/Normal.after.java b/plugins/InspectionGadgets/test/com/siyeh/igfixes/threading/non_thread_safe_lazy_initialization/Normal.after.java new file mode 100644 index 000000000000..c4eefd2315d1 --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igfixes/threading/non_thread_safe_lazy_initialization/Normal.after.java @@ -0,0 +1,10 @@ +public class Normal { + + private static class ExampleHolder { + private static final Object example = new Object(); + } + + public static Object getInstance() { + return ExampleHolder.example + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igfixes/threading/non_thread_safe_lazy_initialization/Normal.java b/plugins/InspectionGadgets/test/com/siyeh/igfixes/threading/non_thread_safe_lazy_initialization/Normal.java new file mode 100644 index 000000000000..44587c1653bc --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igfixes/threading/non_thread_safe_lazy_initialization/Normal.java @@ -0,0 +1,10 @@ +public class Normal { + private static Object example; + + public static Object getInstance() { + if (example == null) { + example = new Object(); + } + return example + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igfixes/threading/non_thread_safe_lazy_initialization/StaticVariableReferenced.after.java b/plugins/InspectionGadgets/test/com/siyeh/igfixes/threading/non_thread_safe_lazy_initialization/StaticVariableReferenced.after.java new file mode 100644 index 000000000000..0120971b7081 --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igfixes/threading/non_thread_safe_lazy_initialization/StaticVariableReferenced.after.java @@ -0,0 +1,16 @@ +public class StaticVariableReferenced { + + private static String s = "yes"; + + private static class ExampleHolder { + private static final Object example = getString(s); + } + + public static Object getInstance() { + return ExampleHolder.example + } + + private static String getString(String s) { + return new String(s); + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igfixes/threading/non_thread_safe_lazy_initialization/StaticVariableReferenced.java b/plugins/InspectionGadgets/test/com/siyeh/igfixes/threading/non_thread_safe_lazy_initialization/StaticVariableReferenced.java new file mode 100644 index 000000000000..a13113785c0d --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igfixes/threading/non_thread_safe_lazy_initialization/StaticVariableReferenced.java @@ -0,0 +1,16 @@ +public class StaticVariableReferenced { + + private static Object example; + private static String s = "yes"; + + public static Object getInstance() { + if (example == null) { + example = getString(s); + } + return example + } + + private static String getString(String s) { + return new String(s); + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/ResultSetIndexZeroInspection.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/ResultSetIndexZeroInspection.java deleted file mode 100644 index cbb5e0eaf8a8..000000000000 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/ResultSetIndexZeroInspection.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.siyeh.igtest.bugs; - -import java.sql.ResultSet; -import java.sql.SQLException; - -public class ResultSetIndexZeroInspection { - private static final int COLUMN_INDEX = 0; - - public void foo(ResultSet resultSet) throws SQLException { - resultSet.getInt(0); - resultSet.getInt(COLUMN_INDEX); - resultSet.getInt(3); - } -} diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/result_set_index_zero/ResultSetIndexZero.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/result_set_index_zero/ResultSetIndexZero.java new file mode 100644 index 000000000000..de087faef79e --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/result_set_index_zero/ResultSetIndexZero.java @@ -0,0 +1,21 @@ +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +public class ResultSetIndexZero { + private static final int COLUMN_INDEX = 0; + + public void foo(ResultSet resultSet) throws SQLException { + resultSet.getInt(0); + resultSet.getInt(COLUMN_INDEX); + resultSet.getInt(3); + } + + void foo(PreparedStatement ps) throws SQLException { + ps.setQueryTimeout(0); + ps.setFetchDirection(0); + ps.setFetchSize(0); + ps.setMaxFieldSize(0); + ps.setMaxRows(0); + } +} diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/string_equality/StringEquality.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/string_equality/StringEquality.java index d32c969b9261..17c4b5127c3b 100644 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/string_equality/StringEquality.java +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/string_equality/StringEquality.java @@ -4,7 +4,11 @@ public class StringEquality { void foo(String s, String t) { final boolean a = s == null; - final boolean b = t == s; - final boolean c = t == + final boolean b = t == s; + final boolean c = t == + } + + void notEquals(String s, String t) { + boolean a = s != t; } } diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/string_equality/expected.xml b/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/string_equality/expected.xml deleted file mode 100644 index 0eb665c7cff4..000000000000 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/bugs/string_equality/expected.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - StringEquality.java - 7 - String comparison using '==', instead of 'equals()' - String values are compared using <code>==</code>, not '.equals()' #loc - - \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/confusing/NestedMethodCallInspection.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/confusing/NestedMethodCallInspection.java deleted file mode 100644 index e700bc5c683f..000000000000 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/confusing/NestedMethodCallInspection.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.siyeh.igtest.confusing; - -import java.util.ArrayList; - -public class NestedMethodCallInspection extends ArrayList{ - private int baz = bar(foo()); - - public NestedMethodCallInspection(int initialCapacity) { - super(Math.abs(initialCapacity)); - } - - public NestedMethodCallInspection() { - this(Math.abs(3)); - } - - public int foo() - { - return 3; - } - public int bar(int val) - { - return 3+val; - } - - public int baz() - { - bar(Math.abs(3)); - return bar(foo()); - } - - public int barangus() - { - return bar(foo()+3); - } -} diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/initialization/double_brace_initialization/DoubleBraceInitialization.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/initialization/double_brace_initialization/DoubleBraceInitialization.java new file mode 100644 index 000000000000..f216f84e2f67 --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/initialization/double_brace_initialization/DoubleBraceInitialization.java @@ -0,0 +1,32 @@ +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +public class DoubleBraceInitialization { + + void foo() { + final HashMap map = new HashMap() {{ + // comment + put("a", "b"); + put("a", "b"); + put("a", "b"); + put("a", "b"); + }}; + } + + static final List list = new ArrayList() {{ + for (int i = 0; i < 10; i++) { + add(i); + } + }}; + + void m(A a) {} + void n() { + m(new A() {{ setI(1); setJ(2); }}); + } + + class A { + void setI(int i) {} + void setJ(int j) {} + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/initialization/non_thread_safe_lazy_initialization/NonThreadSafeLazyInitialization.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/initialization/non_thread_safe_lazy_initialization/NonThreadSafeLazyInitialization.java new file mode 100644 index 000000000000..9b5987966ca6 --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/initialization/non_thread_safe_lazy_initialization/NonThreadSafeLazyInitialization.java @@ -0,0 +1,64 @@ +package com.siyeh.igtest.threading; + +public class NonThreadSafeLazyInitialization { + private static Object foo; + private Object instance; + + public Object getInstance() { + if (instance == null) { + instance = new Object(); + } + return instance; + } + + static + { + if (foo == null) { + foo = new Object(); + } + + } + + { + if (foo == null) { + foo = new Object(); + } + + } + + public void instMethod() { + if (foo == null) { + foo = new Object(); + } + } + + public static void staticMethod() { + if (foo == null) { + foo = new Object(); + } + } + + public void lockedInstMethod() { + synchronized (NonThreadSafeLazyInitialization.class) { + if (foo == null) { + foo = new Object(); + } + } + } + + private static String example = null; + + public Object getInstance2() { + if (foo == null) { + while (true) { + foo = ""; + } + } + return foo; + } + + public Object getInstance3() { + if (foo == null) foo = ""; + return foo; + } +} diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/memory/anonymous_inner_class_may_be_static/AnonymousInnerClassMayBeStatic.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/memory/anonymous_inner_class_may_be_static/AnonymousInnerClassMayBeStatic.java new file mode 100644 index 000000000000..4641642c09c5 --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/memory/anonymous_inner_class_may_be_static/AnonymousInnerClassMayBeStatic.java @@ -0,0 +1,93 @@ +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +public class AnonymousInnerClassMayBeStatic { + + public void foo() + { + final Runnable runnable = new Runnable(){ + public void run() { + } + }; + runnable.run(); + new A() {}; + new B() {}; + new C() {}; + String localVar = ""; + new B () { + void f() { + System.out.println(localVar); + } + }; + } + + class A {} + static class B {} + + void m() { + class C { + } + new B() { + void bla() { + C b; // reference to local class + } + }; + new B() { + void bla() { + AnonymousInnerClassMayBeStatic.n(); + } + }; + } + + static void n() {} + + class D { + {new E().m();} + class E { + private void m() {} + } + } + + class CC {} + static class BB { + void m() { + new BB() { + class Z {} + }; + } + } + + String t = ""; + + void m(int p) { + String s = null; + new Object() { + private int a = 1; + void f() { + System.out.println(a); + System.out.println(p); + System.out.println(s); + this.g(); + } + + private void g() {} + }; + } + + void sort(List list) { + Collections.sort(list, new Comparator() { + @Override + public int compare(String o1, String o2) { + return o1.toString().compareToIgnoreCase(o2.toString()); + } + }); + } +} +class One { + class Two { + void foo() { + new Object() {}; + } + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/memory/inner_class_may_be_static/InnerClassMayBeStatic.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/memory/inner_class_may_be_static/InnerClassMayBeStatic.java new file mode 100644 index 000000000000..dc0b97443b4e --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/memory/inner_class_may_be_static/InnerClassMayBeStatic.java @@ -0,0 +1,140 @@ +package com.siyeh.igtest.memory.inner_class_may_be_static; + +import javax.swing.*; + +public class InnerClassMayBeStatic { + class Nested { + public void foo() { + bar("InnerClassMayBeStaticInspection.this"); + } + + private void bar(String string) { + } + } +} + +class IDEADEV_5513 { + + private static class Inner { + + private boolean b = false; + + private class InnerInner { + + public void foo() { + b = true; + } + } + } +} + +class C extends JComponent { + private class I { + public void foo() { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + repaint(); + } + }); + } + } +} +class D { + + void foo() { + new Object() { + class Y {} + }; + } +} +class StaticInnerClass { + + private int foo; + int bar; + + public class Baz extends StaticInnerClass { + Baz() { + foo = -1; + } + } + class C extends StaticInnerClass {{ + bar = 1; + }} +} +class SomeBeanUnitTest { + + private class BeanCreator { + + public BeanCreator withQuery() { + return null; + } + } +} +class Outer { + class A { // may be static + B b; + } + class B extends A {} // may not be static + + class C { // may be static + D b; + class D extends C {} + } + + static class E { + G.F b; + class G { // may be static + class F extends E {} + } + } + + class H { // may be static + J.I b; + class J { + class I extends H {} + } + } +} +class Complex { + class C { + void m() { + Complex.super.toString(); + } + } + int i; + static void n() { + } + + private class A { + private A() { + } + } + + class B { + } + + class F extends Complex { + class G { + } + + { + A a = (A) null; + G g = (G) null; + new A() {}; + new B(); + + i = 10; + new E().m(); + Complex.n(); + } + + void m(A a) { + a.toString(); + } + + class E { + private void m() { + } + } + } +} diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/performance/inner_class_may_be_static/InnerClassMayBeStaticInspection.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/performance/inner_class_may_be_static/InnerClassMayBeStaticInspection.java deleted file mode 100644 index 0db367ce4757..000000000000 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/performance/inner_class_may_be_static/InnerClassMayBeStaticInspection.java +++ /dev/null @@ -1,97 +0,0 @@ -package com.siyeh.igtest.performance.inner_class_may_be_static; - -import javax.swing.*; - -public class InnerClassMayBeStaticInspection { - class Nested { - public void foo() { - bar("InnerClassMayBeStaticInspection.this"); - } - - private void bar(String string) { - } - } -} - -class IDEADEV_5513 { - - private static class Inner { - - private boolean b = false; - - private class InnerInner { - - public void foo() { - b = true; - } - } - } -} - -class C extends JComponent { - private class I { - public void foo() { - SwingUtilities.invokeLater(new Runnable() { - public void run() { - repaint(); - } - }); - } - } -} -class D { - - void foo() { - new Object() { - class Y {} - }; - } -} -class StaticInnerClass { - - private int foo; - int bar; - - public class Baz extends StaticInnerClass { - Baz() { - foo = -1; - } - } - class C extends StaticInnerClass {{ - bar = 1; - }} -} -class SomeBeanUnitTest { - - private class BeanCreator { - - public BeanCreator withQuery() { - return null; - } - } -} -class Outer { - class A { // may not be static - B b; - } - class B extends A {} // may not be static - - class C { // may be static - D b; - class D extends C {} - } - - static class E { - G.F b; - class G { // may be static - class F extends E {} - } - } - - class H { // may be static - J.I b; - class J { - class I extends H {} - } - } -} diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/performance/inner_class_may_be_static/expected.xml b/plugins/InspectionGadgets/test/com/siyeh/igtest/performance/inner_class_may_be_static/expected.xml deleted file mode 100644 index 0eb4ed32494c..000000000000 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/performance/inner_class_may_be_static/expected.xml +++ /dev/null @@ -1,44 +0,0 @@ - - - - InnerClassMayBeStaticInspection.java - 6 - Inner class may be 'static' - Inner class <code>Nested</code> may be 'static' #loc - - - - InnerClassMayBeStaticInspection.java - 60 - Inner class may be 'static' - Inner class <code>C</code> may be 'static' #loc - - - - InnerClassMayBeStaticInspection.java - 66 - Inner class may be 'static' - Inner class <code>BeanCreator</code> may be 'static' #loc - - - - InnerClassMayBeStaticInspection.java - 79 - Inner class may be 'static' - Inner class <code>C</code> may be 'static' #loc - - - - InnerClassMayBeStaticInspection.java - 86 - Inner class may be 'static' - Inner class <code>G</code> may be 'static' #loc - - - - InnerClassMayBeStaticInspection.java - 91 - Inner class may be 'static' - Inner class <code>H</code> may be 'static' #loc - - \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/style/CStyleArrayDeclarationInspection.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/style/CStyleArrayDeclarationInspection.java deleted file mode 100644 index 00d3e0ec97d4..000000000000 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/style/CStyleArrayDeclarationInspection.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.siyeh.igtest.style; - -public class CStyleArrayDeclarationInspection -{ - private int[] m_foo; - private int m_bar[]; - - public CStyleArrayDeclarationInspection(int[] bar, int[] foo) - { - m_bar = bar; - m_foo = foo; - for(int i = 0; i < bar.length; i++) - { - m_foo[i] = m_bar[i]; - } - - } - - public void foo() - { - final int foo[] = new int[3]; - final int[] bar = new int[3]; - - for(int i = 0; i < bar.length; i++) - { - foo[i] = bar[i]; - } - } - - public void bar(int foo[], int[] bar) - { - - } -} diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/style/cstyle_array_declaration/CStyleArrayDeclaration.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/style/cstyle_array_declaration/CStyleArrayDeclaration.java new file mode 100644 index 000000000000..8a01835a2106 --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/style/cstyle_array_declaration/CStyleArrayDeclaration.java @@ -0,0 +1,38 @@ +package com.siyeh.igtest.style; + +public class CStyleArrayDeclaration +{ + private int[] m_foo; + private int m_bar[]; + + public CStyleArrayDeclaration(int[] bar, int[] foo) + { + m_bar = bar; + m_foo = foo; + for(int i = 0; i < bar.length; i++) + { + m_foo[i] = m_bar[i]; + } + + } + + public void foo() + { + final int foo[] = new int[3]; + final int[] bar = new int[3]; + + for(int i = 0; i < bar.length; i++) + { + foo[i] = bar[i]; + } + } + + public void bar(int foo[], int[] bar) + { + + } + + String ohGod(String[] a)[] { + return a; + } +} diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/style/nested_method_call/NestedMethodCall.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/style/nested_method_call/NestedMethodCall.java new file mode 100644 index 000000000000..2548443e805b --- /dev/null +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/style/nested_method_call/NestedMethodCall.java @@ -0,0 +1,43 @@ + +import java.util.ArrayList; + +public class NestedMethodCall extends ArrayList{ + private int baz = bar(foo()); + + public NestedMethodCall(int initialCapacity) { + super(Math.abs(initialCapacity)); + } + + public NestedMethodCall() { + this(Math.abs(3)); + } + + public int foo() + { + return 3; + } + public int bar(int val) + { + return 3+val; + } + + public int baz() + { + bar(Math.abs(3)); + return bar(foo()); + } + + public int barangus() + { + return bar(foo()+3); + } + + private int value = 1; + public int getValue() { + return value; + } + + public int apex() { + return bar(getValue()); + } +} diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/threading/NonThreadSafeLazyInitializationInspection.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/threading/NonThreadSafeLazyInitializationInspection.java deleted file mode 100644 index 20d62f94cb4e..000000000000 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/threading/NonThreadSafeLazyInitializationInspection.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.siyeh.igtest.threading; - -public class NonThreadSafeLazyInitializationInspection { - private static Object foo; - - static - { - if (foo == null) { - foo = new Object(); - } - - } - - { - if (foo == null) { - foo = new Object(); - } - - } - - public void instMethod() { - if (foo == null) { - foo = new Object(); - } - } - - public void lockedInstMethod() { - synchronized (NonThreadSafeLazyInitializationInspection.class) { - if (foo == null) { - foo = new Object(); - } - } - } -} diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/visibility/ambiguous/AmbiguousMethodCall.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/visibility/ambiguous/AmbiguousMethodCall.java index 1d1eb0ad0369..20c181077969 100644 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/visibility/ambiguous/AmbiguousMethodCall.java +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/visibility/ambiguous/AmbiguousMethodCall.java @@ -8,7 +8,7 @@ public class AmbiguousMethodCall { class Inner extends Y { { - m(); // ambiguous + m(); // ambiguous } } } diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/visibility/ambiguous/expected.xml b/plugins/InspectionGadgets/test/com/siyeh/igtest/visibility/ambiguous/expected.xml deleted file mode 100644 index a0ee7b7d2789..000000000000 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/visibility/ambiguous/expected.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - AmbiguousMethodCall.java - 11 - Inherited method called which hides method in outer class - Method <code>m()</code> from super class 'Y' called, when method from class 'X' might have been expected #loc - - - \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/visibility/ambiguous_field_access/AmbiguousFieldAccess.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/visibility/ambiguous_field_access/AmbiguousFieldAccess.java index 3292bfd60a0b..3090a6415135 100644 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/visibility/ambiguous_field_access/AmbiguousFieldAccess.java +++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/visibility/ambiguous_field_access/AmbiguousFieldAccess.java @@ -11,11 +11,26 @@ class Bar { void foo(java.util.List name) { for(String name1: name) { doSome(new Foo() {{ - set(name); + set(name); }}); } } + void foo() { + String name = "name"; + new Foo() {{ + System.out.println(name); + }}; + } + + void bar() { + new Foo() { + void foo() { + System.out.println(name); + } + }; + } + private void doSome(Foo foo) { } } \ No newline at end of file diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/visibility/ambiguous_field_access/expected.xml b/plugins/InspectionGadgets/test/com/siyeh/igtest/visibility/ambiguous_field_access/expected.xml deleted file mode 100644 index 9929fd781f8b..000000000000 --- a/plugins/InspectionGadgets/test/com/siyeh/igtest/visibility/ambiguous_field_access/expected.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - AmbiguousFieldAccess.java - 14 - Inherited field accessed while local variable, parameter or field access from surrounding class might be expected - Field <code>name</code> from super class 'Foo' accessed, while parameter access might be expected #loc - - \ No newline at end of file diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/IGQuickFixesTestCase.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/IGQuickFixesTestCase.java index 2ce63ec4814e..47fd152debec 100644 --- a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/IGQuickFixesTestCase.java +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/IGQuickFixesTestCase.java @@ -16,15 +16,21 @@ package com.siyeh.ig; import com.intellij.codeInsight.intention.IntentionAction; +import com.intellij.codeInspection.ex.QuickFixWrapper; import com.intellij.ide.highlighter.JavaFileType; import com.intellij.openapi.application.PluginPathManager; +import com.intellij.openapi.util.Condition; import com.intellij.pom.java.LanguageLevel; import com.intellij.testFramework.builders.JavaModuleFixtureBuilder; import com.intellij.testFramework.fixtures.JavaCodeInsightFixtureTestCase; import com.intellij.util.ArrayUtil; +import com.intellij.util.containers.ContainerUtil; import org.intellij.lang.annotations.Language; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; +import org.junit.Assert; + +import java.util.List; /** * @author anna @@ -73,6 +79,17 @@ public abstract class IGQuickFixesTestCase extends JavaCodeInsightFixtureTestCas return PluginPathManager.getPluginHomePath("InspectionGadgets") + "/test/com/siyeh/igfixes/"; } + protected void assertQuickfixNotAvailable() { + assertQuickfixNotAvailable(myDefaultHint); + } + + protected void assertQuickfixNotAvailable(final String quickfixName) { + final String testName = getTestName(false); + myFixture.configureByFile(getRelativePath() + "/" + testName + ".java"); + assertEmpty("Quickfix \'" + quickfixName + "\' is available but should not", + myFixture.filterAvailableIntentions(quickfixName)); + } + protected void doTest() { assertNotNull(myDefaultHint); final String testName = getTestName(false); @@ -86,12 +103,36 @@ public abstract class IGQuickFixesTestCase extends JavaCodeInsightFixtureTestCas protected void doTest(final String testName, final String hint) { myFixture.configureByFile(getRelativePath() + "/" + testName + ".java"); - final IntentionAction action = myFixture.findSingleIntention(hint); + final IntentionAction action = findIntention(hint); assertNotNull(action); myFixture.launchAction(action); myFixture.checkResultByFile(getRelativePath() + "/" + testName + ".after.java"); } + public IntentionAction findIntention(@NotNull final String hint) { + final List availableIntentions = + ContainerUtil.findAll(myFixture.getAvailableIntentions(), new Condition() { + @Override + public boolean value(final IntentionAction intentionAction) { + return intentionAction instanceof QuickFixWrapper; + } + }); + final List list = ContainerUtil.findAll(availableIntentions, new Condition() { + @Override + public boolean value(IntentionAction intentionAction) { + return intentionAction.getText().equals(hint); + } + }); + if (list.isEmpty()) { + Assert.fail("\"" + hint + "\" not in " + list); + } + else if (list.size() > 1) { + Assert.fail("Too many quickfixes found for \"" + hint + "\": " + list + "]"); + } + return list.get(0); + } + + protected void doExpressionTest( String hint, @Language(value = "JAVA", prefix = "class $X$ {{System.out.print(", suffix = ");}}") @NotNull @NonNls String before, @@ -109,7 +150,7 @@ public abstract class IGQuickFixesTestCase extends JavaCodeInsightFixtureTestCas protected void doTest(String hint, @Language("JAVA") @NotNull @NonNls String before, @Language("JAVA") @NotNull @NonNls String after) { before = before.replace("/**/", ""); myFixture.configureByText(JavaFileType.INSTANCE, before); - final IntentionAction intention = myFixture.findSingleIntention(hint); + final IntentionAction intention = findIntention(hint); assertNotNull(intention); myFixture.launchAction(intention); myFixture.checkResult(after); diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/bugs/ResultSetIndexZeroInspectionTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/bugs/ResultSetIndexZeroInspectionTest.java new file mode 100644 index 000000000000..07a1c7d7150e --- /dev/null +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/bugs/ResultSetIndexZeroInspectionTest.java @@ -0,0 +1,34 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.bugs; + +import com.intellij.codeInspection.InspectionProfileEntry; +import com.siyeh.ig.LightInspectionTestCase; +import org.jetbrains.annotations.Nullable; + +/** + * @author Bas Leijdekkers + */ +public class ResultSetIndexZeroInspectionTest extends LightInspectionTestCase { + + public void testResultSetIndexZero() { doTest(); } + + @Nullable + @Override + protected InspectionProfileEntry getInspection() { + return new ResultSetIndexZeroInspection(); + } +} diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/bugs/StringEqualityInspectionTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/bugs/StringEqualityInspectionTest.java index 13aa1fa972ae..d17c0b31e19b 100644 --- a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/bugs/StringEqualityInspectionTest.java +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/bugs/StringEqualityInspectionTest.java @@ -1,10 +1,18 @@ package com.siyeh.ig.bugs; -import com.siyeh.ig.IGInspectionTestCase; +import com.intellij.codeInspection.InspectionProfileEntry; +import com.siyeh.ig.LightInspectionTestCase; +import org.jetbrains.annotations.Nullable; -public class StringEqualityInspectionTest extends IGInspectionTestCase { +public class StringEqualityInspectionTest extends LightInspectionTestCase { - public void test() throws Exception { - doTest("com/siyeh/igtest/bugs/string_equality", new StringEqualityInspection()); + public void testStringEquality() { + doTest(); + } + + @Nullable + @Override + protected InspectionProfileEntry getInspection() { + return new StringEqualityInspection(); } } \ No newline at end of file diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/classlayout/ClassWithOnlyPrivateConstructorsInspectionTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/classlayout/ClassWithOnlyPrivateConstructorsInspectionTest.java index a177242537d2..397a2fe1d37e 100644 --- a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/classlayout/ClassWithOnlyPrivateConstructorsInspectionTest.java +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/classlayout/ClassWithOnlyPrivateConstructorsInspectionTest.java @@ -41,6 +41,14 @@ public class ClassWithOnlyPrivateConstructorsInspectionTest extends LightInspect doTest("class X {}"); } + public void testEnum() { + doTest("enum Currencies {\n" + + " EURO, DOLLAR;\n" + + " private Currencies() {\n" + + " }\n" + + "}"); + } + @Nullable @Override protected InspectionProfileEntry getInspection() { diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/initialization/DoubleBraceInitializationFixTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/initialization/DoubleBraceInitializationFixTest.java new file mode 100644 index 000000000000..afce138a7a82 --- /dev/null +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/initialization/DoubleBraceInitializationFixTest.java @@ -0,0 +1,38 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.fixes.initialization; + +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.IGQuickFixesTestCase; +import com.siyeh.ig.initialization.DoubleBraceInitializationInspection; + +/** + * @author Bas Leijdekkers + */ +public class DoubleBraceInitializationFixTest extends IGQuickFixesTestCase { + + public void testLocalVariable() { doTest(); } + public void testField() { doTest(); } + public void testArgument() { assertQuickfixNotAvailable(); } + + @Override + protected void setUp() throws Exception { + super.setUp(); + myFixture.enableInspections(new DoubleBraceInitializationInspection()); + myRelativePath = "initialization/double_brace_initialization"; + myDefaultHint = InspectionGadgetsBundle.message("double.brace.initialization.quickfix"); + } +} diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/memory/InnerClassMayBeStaticFixTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/memory/InnerClassMayBeStaticFixTest.java new file mode 100644 index 000000000000..42c36bf212fb --- /dev/null +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/memory/InnerClassMayBeStaticFixTest.java @@ -0,0 +1,37 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.fixes.memory; + +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.IGQuickFixesTestCase; +import com.siyeh.ig.memory.InnerClassMayBeStaticInspection; + +/** + * @author Bas Leijdekkers + */ +public class InnerClassMayBeStaticFixTest extends IGQuickFixesTestCase { + + @Override + protected void setUp() throws Exception { + super.setUp(); + myFixture.enableInspections(new InnerClassMayBeStaticInspection()); + myRelativePath = "memory/inner_class_static"; + myDefaultHint = InspectionGadgetsBundle.message("make.static.quickfix"); + } + + public void testSimple() { doTest(); } + +} diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/performance/InnerClassMayBeStaticFixTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/performance/InnerClassMayBeStaticFixTest.java deleted file mode 100644 index 92a27c5ac236..000000000000 --- a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/performance/InnerClassMayBeStaticFixTest.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2000-2013 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.siyeh.ig.fixes.performance; - -import com.siyeh.InspectionGadgetsBundle; -import com.siyeh.ig.IGQuickFixesTestCase; -import com.siyeh.ig.performance.InnerClassMayBeStaticInspection; - -/** - * @author Bas Leijdekkers - */ -public class InnerClassMayBeStaticFixTest extends IGQuickFixesTestCase { - - @Override - protected void setUp() throws Exception { - super.setUp(); - myFixture.enableInspections(new InnerClassMayBeStaticInspection()); - myRelativePath = "performance/inner_class_static"; - myDefaultHint = InspectionGadgetsBundle.message("make.static.quickfix"); - } - - public void testSimple() { doTest(); } - -} diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/style/CStyleArrayDeclarationFixTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/style/CStyleArrayDeclarationFixTest.java new file mode 100644 index 000000000000..85a07f8e08c3 --- /dev/null +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/style/CStyleArrayDeclarationFixTest.java @@ -0,0 +1,37 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.fixes.style; + +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.IGQuickFixesTestCase; +import com.siyeh.ig.style.CStyleArrayDeclarationInspection; + +/** + * @author Bas Leijdekkers + */ +public class CStyleArrayDeclarationFixTest extends IGQuickFixesTestCase { + + public void testSimpleMethod() { doTest(); } + public void testFieldWithWhitespace() { doTest(); } + + @Override + public void setUp() throws Exception { + super.setUp(); + myFixture.enableInspections(new CStyleArrayDeclarationInspection()); + myRelativePath = "style/cstyle_array_declaration"; + myDefaultHint = InspectionGadgetsBundle.message("c.style.array.declaration.replace.quickfix"); + } +} diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/threading/IntroduceHolderFixTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/threading/IntroduceHolderFixTest.java new file mode 100644 index 000000000000..28557e5f35a1 --- /dev/null +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/threading/IntroduceHolderFixTest.java @@ -0,0 +1,41 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.fixes.threading; + +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.IGQuickFixesTestCase; +import com.siyeh.ig.initialization.NonThreadSafeLazyInitializationInspection; + +/** + * @author Bas Leijdekkers + */ +public class IntroduceHolderFixTest extends IGQuickFixesTestCase { + + public void testNestedAssignment() { assertQuickfixNotAvailable(); } + public void testLocalVariableReferenced() { assertQuickfixNotAvailable(); } + public void testInstanceVariableReferenced() { assertQuickfixNotAvailable(); } + public void testStaticVariableReferenced() { doTest(); } + public void testNormal() { doTest(); } + + @Override + protected void setUp() throws Exception { + super.setUp(); + myFixture.enableInspections(new NonThreadSafeLazyInitializationInspection() + ); + myDefaultHint = InspectionGadgetsBundle.message("introduce.holder.class.quickfix"); + myRelativePath = "threading/non_thread_safe_lazy_initialization"; + } +} diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/threading/MakeFieldVolatileFixTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/threading/MakeFieldVolatileFixTest.java new file mode 100644 index 000000000000..4c9a623be4d5 --- /dev/null +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/fixes/threading/MakeFieldVolatileFixTest.java @@ -0,0 +1,37 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.fixes.threading; + +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.IGQuickFixesTestCase; +import com.siyeh.ig.threading.DoubleCheckedLockingInspection; + +/** + * @author Bas Leijdekkers + */ +public class MakeFieldVolatileFixTest extends IGQuickFixesTestCase { + + public void testSimple() { doTest(InspectionGadgetsBundle.message("double.checked.locking.quickfix", "s_instance")); } + + @Override + protected void setUp() throws Exception { + super.setUp(); + final DoubleCheckedLockingInspection inspection = new DoubleCheckedLockingInspection(); + inspection.ignoreOnVolatileVariables = true; + myFixture.enableInspections(inspection); + myRelativePath = "threading/make_field_volatile"; + } +} diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/initialization/DoubleBraceInitializationInspectionTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/initialization/DoubleBraceInitializationInspectionTest.java new file mode 100644 index 000000000000..532018d5c26e --- /dev/null +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/initialization/DoubleBraceInitializationInspectionTest.java @@ -0,0 +1,34 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.initialization; + +import com.intellij.codeInspection.InspectionProfileEntry; +import com.siyeh.ig.LightInspectionTestCase; +import org.jetbrains.annotations.Nullable; + +/** + * @author Bas Leijdekkers + */ +public class DoubleBraceInitializationInspectionTest extends LightInspectionTestCase { + + public void testDoubleBraceInitialization() { doTest(); } + + @Nullable + @Override + protected InspectionProfileEntry getInspection() { + return new DoubleBraceInitializationInspection(); + } +} diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/initialization/NonThreadSafeLazyInitializationInspectionTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/initialization/NonThreadSafeLazyInitializationInspectionTest.java new file mode 100644 index 000000000000..331eb67dd5bf --- /dev/null +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/initialization/NonThreadSafeLazyInitializationInspectionTest.java @@ -0,0 +1,34 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.initialization; + +import com.intellij.codeInspection.InspectionProfileEntry; +import com.siyeh.ig.LightInspectionTestCase; +import org.jetbrains.annotations.Nullable; + +/** + * @author Bas Leijdekkers + */ +public class NonThreadSafeLazyInitializationInspectionTest extends LightInspectionTestCase { + + public void testNonThreadSafeLazyInitialization() { doTest(); } + + @Nullable + @Override + protected InspectionProfileEntry getInspection() { + return new NonThreadSafeLazyInitializationInspection(); + } +} diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/memory/AnonymousInnerClassMayBeStaticInspectionTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/memory/AnonymousInnerClassMayBeStaticInspectionTest.java new file mode 100644 index 000000000000..6fe5d7314190 --- /dev/null +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/memory/AnonymousInnerClassMayBeStaticInspectionTest.java @@ -0,0 +1,34 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.memory; + +import com.intellij.codeInspection.InspectionProfileEntry; +import com.siyeh.ig.LightInspectionTestCase; +import org.jetbrains.annotations.Nullable; + +/** + * @author Bas Leijdekkers + */ +public class AnonymousInnerClassMayBeStaticInspectionTest extends LightInspectionTestCase { + + public void testAnonymousInnerClassMayBeStatic() { doTest(); } + + @Nullable + @Override + protected InspectionProfileEntry getInspection() { + return new AnonymousInnerClassMayBeStaticInspection(); + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/memory/InnerClassMayBeStaticInspectionTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/memory/InnerClassMayBeStaticInspectionTest.java new file mode 100644 index 000000000000..36f42d1f12ef --- /dev/null +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/memory/InnerClassMayBeStaticInspectionTest.java @@ -0,0 +1,16 @@ +package com.siyeh.ig.memory; + +import com.intellij.codeInspection.InspectionProfileEntry; +import com.siyeh.ig.LightInspectionTestCase; +import org.jetbrains.annotations.Nullable; + +public class InnerClassMayBeStaticInspectionTest extends LightInspectionTestCase { + + public void testInnerClassMayBeStatic() { doTest(); } + + @Nullable + @Override + protected InspectionProfileEntry getInspection() { + return new InnerClassMayBeStaticInspection(); + } +} \ No newline at end of file diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/performance/InnerClassMayBeStaticInspectionTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/performance/InnerClassMayBeStaticInspectionTest.java deleted file mode 100644 index 02b57fd22746..000000000000 --- a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/performance/InnerClassMayBeStaticInspectionTest.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.siyeh.ig.performance; - -import com.siyeh.ig.IGInspectionTestCase; - -public class InnerClassMayBeStaticInspectionTest extends IGInspectionTestCase { - - public void test() throws Exception { - doTest("com/siyeh/igtest/performance/inner_class_may_be_static", - new InnerClassMayBeStaticInspection()); - } -} \ No newline at end of file diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/style/CStyleArrayDeclarationInspectionTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/style/CStyleArrayDeclarationInspectionTest.java new file mode 100644 index 000000000000..613a0fc3bf3a --- /dev/null +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/style/CStyleArrayDeclarationInspectionTest.java @@ -0,0 +1,34 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.style; + +import com.intellij.codeInspection.InspectionProfileEntry; +import com.siyeh.ig.LightInspectionTestCase; +import org.jetbrains.annotations.Nullable; + +/** + * @author Bas Leijdekkers + */ +public class CStyleArrayDeclarationInspectionTest extends LightInspectionTestCase { + + public void testCStyleArrayDeclaration() { doTest(); } + + @Nullable + @Override + protected InspectionProfileEntry getInspection() { + return new CStyleArrayDeclarationInspection(); + } +} diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/style/NestedMethodCallInspectionTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/style/NestedMethodCallInspectionTest.java new file mode 100644 index 000000000000..d67a0d43f7d3 --- /dev/null +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/style/NestedMethodCallInspectionTest.java @@ -0,0 +1,37 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ig.style; + +import com.intellij.codeInspection.InspectionProfileEntry; +import com.siyeh.ig.LightInspectionTestCase; +import org.jetbrains.annotations.Nullable; + +/** + * @author Bas Leijdekkers + */ +public class NestedMethodCallInspectionTest extends LightInspectionTestCase{ + + public void testNestedMethodCall() { doTest(); } + + @Nullable + @Override + protected InspectionProfileEntry getInspection() { + final NestedMethodCallInspection inspection = new NestedMethodCallInspection(); + inspection.ignoreStaticMethods = true; + inspection.ignoreGetterCalls = true; + return inspection; + } +} diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/visibility/AmbiguousFieldAccessInspectionTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/visibility/AmbiguousFieldAccessInspectionTest.java index b3a13f462339..1007f54da048 100644 --- a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/visibility/AmbiguousFieldAccessInspectionTest.java +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/visibility/AmbiguousFieldAccessInspectionTest.java @@ -1,10 +1,16 @@ package com.siyeh.ig.visibility; -import com.siyeh.ig.IGInspectionTestCase; +import com.intellij.codeInspection.InspectionProfileEntry; +import com.siyeh.ig.LightInspectionTestCase; +import org.jetbrains.annotations.Nullable; -public class AmbiguousFieldAccessInspectionTest extends IGInspectionTestCase { +public class AmbiguousFieldAccessInspectionTest extends LightInspectionTestCase { - public void test() throws Exception { - doTest("com/siyeh/igtest/visibility/ambiguous_field_access", new AmbiguousFieldAccessInspection()); + public void testAmbiguousFieldAccess() { doTest(); } + + @Nullable + @Override + protected InspectionProfileEntry getInspection() { + return new AmbiguousFieldAccessInspection(); } } \ No newline at end of file diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/visibility/AmbiguousMethodCallInspectionTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/visibility/AmbiguousMethodCallInspectionTest.java index 78610e8597d6..7c1c1327ccd7 100644 --- a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/visibility/AmbiguousMethodCallInspectionTest.java +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/visibility/AmbiguousMethodCallInspectionTest.java @@ -1,10 +1,21 @@ package com.siyeh.ig.visibility; -import com.siyeh.ig.IGInspectionTestCase; +import com.intellij.codeInspection.InspectionProfileEntry; +import com.siyeh.ig.LightInspectionTestCase; +import org.jetbrains.annotations.Nullable; -public class AmbiguousMethodCallInspectionTest extends IGInspectionTestCase { +public class AmbiguousMethodCallInspectionTest extends LightInspectionTestCase { - public void test() throws Exception { - doTest("com/siyeh/igtest/visibility/ambiguous", new AmbiguousMethodCallInspection()); + public void testAmbiguousMethodCall() { doTest(); } + + @Nullable + @Override + protected InspectionProfileEntry getInspection() { + return new AmbiguousMethodCallInspection(); + } + + @Override + protected String getBasePath() { + return "/plugins/InspectionGadgets/test/com/siyeh/igtest/visibility/ambiguous"; } } \ No newline at end of file diff --git a/plugins/IntentionPowerPak/src/META-INF/IntentionPowerPack.xml b/plugins/IntentionPowerPak/src/META-INF/IntentionPowerPack.xml index 73e279fa91a9..95c585fef36c 100644 --- a/plugins/IntentionPowerPak/src/META-INF/IntentionPowerPack.xml +++ b/plugins/IntentionPowerPak/src/META-INF/IntentionPowerPack.xml @@ -103,6 +103,12 @@ intention.category.boolean + + com.siyeh.ipp.equality.ReplaceEqualityWithSafeEqualsIntention + com.siyeh.IntentionPowerPackBundle + intention.category.boolean + + com.siyeh.ipp.trivialif.ExpandBooleanIntention com.siyeh.IntentionPowerPackBundle diff --git a/plugins/IntentionPowerPak/src/com/siyeh/IntentionPowerPackBundle.properties b/plugins/IntentionPowerPak/src/com/siyeh/IntentionPowerPackBundle.properties index 30e64929f8b2..923df3eb28f4 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/IntentionPowerPackBundle.properties +++ b/plugins/IntentionPowerPak/src/com/siyeh/IntentionPowerPackBundle.properties @@ -14,6 +14,7 @@ change.to.end.of.line.comment.intention.family.name=Replace with End Of Line Com assert.to.if.intention.name=Replace 'assert' with 'if' statement assert.to.if.intention.family.name=Replace Assert with If Statement replace.equality.with.safe.equals.intention.name=Replace '==' with safe '.equals()' +replace.equality.with.safe.not.equals.intention.name=Replace '!=' with safe '!equals()' replace.equality.with.safe.equals.intention.family.name=Replace Equality with Safe Equals replace.for.each.loop.with.indexed.for.loop.intention.name=Replace 'for each' loop with indexed 'for' loop replace.for.each.loop.with.iterator.for.loop.intention.name=Replace 'for each' loop with iterator 'for' loop @@ -40,7 +41,7 @@ replace.switch.with.if.intention.family.name=Replace Switch with If simplify.variable.intention.name=Replace with Java-style array declaration simplify.variable.intention.family.name=Replace with Java Style Array Declaration constant.expression.intention.family.name=Compute Constant Value -create.assert.intention.name=Create JUnit Assertion +create.assert.intention.name=Create JUnit assertion create.assert.intention.family.name=Create JUnit Assertion simplify.if.else.intention.name=Simplify 'if else' simplify.if.else.intention.family.name=Simplify If Else @@ -81,7 +82,8 @@ char.to.string.intention.name=Replace character literal with string char.to.string.intention.family.name=Replace Char with String replace.if.with.conditional.intention.name=Replace 'if else' with '?:' replace.if.with.conditional.intention.family.name=Replace If Else with Conditional -replace.equality.with.equals.intention.name=Replace '==' with '.equals()' +replace.equality.with.equals.intention.name=Replace '==' with 'equals()' +replace.equality.with.not.equals.intention.name=Replace '!=' with '!equals()' replace.equality.with.equals.intention.family.name=Replace Equality with Equals make.call.chain.into.call.sequence.intention.name=Make method call chain into call sequence make.call.chain.into.call.sequence.intention.family.name=Make Call Chain Into Call Sequence diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/base/Intention.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/base/Intention.java index 9e1ef7f75f7b..579f36317b4a 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/base/Intention.java +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/base/Intention.java @@ -123,14 +123,6 @@ public abstract class Intention extends BaseElementAtCaretIntentionAction { codeStyleManager.reformat(insertedElement); } - protected static void addStatementBefore(String newStatementText, PsiReturnStatement anchor) { - final Project project = anchor.getProject(); - final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project); - final PsiStatement newStatement = factory.createStatementFromText(newStatementText, anchor); - final PsiElement addedStatement = anchor.getParent().addBefore(newStatement, anchor); - CodeStyleManager.getInstance(project).reformat(addedStatement); - } - @Nullable PsiElement findMatchingElement(@Nullable PsiElement element, Editor editor) { while (element != null) { diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/decls/SimplifyVariableIntention.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/decls/SimplifyVariableIntention.java index f127b50fdd79..149d8067c882 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/decls/SimplifyVariableIntention.java +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/decls/SimplifyVariableIntention.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2005 Dave Griffith + * Copyright 2003-2014 Dave Griffith, Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,7 +17,7 @@ package com.siyeh.ipp.decls; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiVariable; -import com.intellij.util.IncorrectOperationException; +import com.intellij.psi.codeStyle.CodeStyleManager; import com.siyeh.ipp.base.Intention; import com.siyeh.ipp.base.PsiElementPredicate; import org.jetbrains.annotations.NotNull; @@ -29,9 +29,9 @@ public class SimplifyVariableIntention extends Intention { return new SimplifyVariablePredicate(); } - public void processIntention(PsiElement element) - throws IncorrectOperationException { - final PsiVariable var = (PsiVariable)element; - var.normalizeDeclaration(); + public void processIntention(PsiElement element) { + final PsiVariable variable = (PsiVariable)element; + variable.normalizeDeclaration(); + CodeStyleManager.getInstance(element.getProject()).reformat(variable); } } \ No newline at end of file diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/equality/ObjectEqualityPredicate.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/equality/ObjectEqualityPredicate.java index 18d55c35fe97..4bea8688ae32 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/equality/ObjectEqualityPredicate.java +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/equality/ObjectEqualityPredicate.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2005 Dave Griffith + * Copyright 2003-2014 Dave Griffith, Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,7 +19,6 @@ import com.intellij.psi.*; import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.TypeConversionUtil; import com.siyeh.ipp.base.PsiElementPredicate; -import com.siyeh.ipp.psiutils.ErrorUtil; class ObjectEqualityPredicate implements PsiElementPredicate { @@ -34,44 +33,15 @@ class ObjectEqualityPredicate implements PsiElementPredicate { return false; } final PsiExpression lhs = expression.getLOperand(); - final String lhsText = lhs.getText(); - if (PsiKeyword.NULL.equals(lhsText)) { - return false; - } final PsiType lhsType = lhs.getType(); - if (lhsType == null) { + if (lhsType == null || lhsType instanceof PsiPrimitiveType || TypeConversionUtil.isEnumType(lhsType)) { return false; } final PsiExpression rhs = expression.getROperand(); if (rhs == null) { return false; } - final String rhsText = rhs.getText(); - if (PsiKeyword.NULL.equals(rhsText)) { - return false; - } final PsiType rhsType = rhs.getType(); - if (rhsType == null) { - return false; - } - if (TypeConversionUtil.isPrimitiveAndNotNull(lhsType) || - TypeConversionUtil.isPrimitiveAndNotNull(rhsType)) { - return false; - } - if (rhsType instanceof PsiClassType) { - final PsiClassType rhsClassType = (PsiClassType)rhsType; - final PsiClass rhsClass = rhsClassType.resolve(); - if (rhsClass != null && rhsClass.isEnum()) { - return false; - } - } - if (lhsType instanceof PsiClassType) { - final PsiClassType lhsClassType = (PsiClassType)lhsType; - final PsiClass lhsClass = lhsClassType.resolve(); - if (lhsClass != null && lhsClass.isEnum()) { - return false; - } - } - return !ErrorUtil.containsError(element); + return !(rhsType == null || rhsType instanceof PsiPrimitiveType || TypeConversionUtil.isEnumType(rhsType)); } } \ No newline at end of file diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/equality/ReplaceEqualityWithEqualsIntention.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/equality/ReplaceEqualityWithEqualsIntention.java index 9c0119a92797..d0f56b8cc305 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/equality/ReplaceEqualityWithEqualsIntention.java +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/equality/ReplaceEqualityWithEqualsIntention.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2013 Dave Griffith, Bas Leijdekkers + * Copyright 2003-2014 Dave Griffith, Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,30 +15,37 @@ */ package com.siyeh.ipp.equality; -import com.intellij.psi.JavaTokenType; -import com.intellij.psi.PsiBinaryExpression; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiExpression; +import com.intellij.psi.*; import com.intellij.psi.tree.IElementType; -import com.intellij.util.IncorrectOperationException; +import com.siyeh.IntentionPowerPackBundle; import com.siyeh.ig.PsiReplacementUtil; import com.siyeh.ig.psiutils.ParenthesesUtils; -import com.siyeh.ipp.base.Intention; +import com.siyeh.ipp.base.MutablyNamedIntention; import com.siyeh.ipp.base.PsiElementPredicate; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; -public class ReplaceEqualityWithEqualsIntention extends Intention { +public class ReplaceEqualityWithEqualsIntention extends MutablyNamedIntention { + + @Override + protected String getTextForElement(PsiElement element) { + final PsiBinaryExpression binaryExpression = (PsiBinaryExpression)element; + final IElementType tokenType = binaryExpression.getOperationTokenType(); + if (JavaTokenType.NE.equals(tokenType)) { + return IntentionPowerPackBundle.message("replace.equality.with.not.equals.intention.name"); + } + else { + return IntentionPowerPackBundle.message("replace.equality.with.equals.intention.name"); + } + } @NotNull public PsiElementPredicate getElementPredicate() { return new ObjectEqualityPredicate(); } - public void processIntention(PsiElement element) - throws IncorrectOperationException { - final PsiBinaryExpression exp = - (PsiBinaryExpression)element; + public void processIntention(PsiElement element) { + final PsiBinaryExpression exp = (PsiBinaryExpression)element; final PsiExpression lhs = exp.getLOperand(); final PsiExpression rhs = exp.getROperand(); if (rhs == null) { diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/equality/ReplaceEqualityWithSafeEqualsIntention.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/equality/ReplaceEqualityWithSafeEqualsIntention.java index d14804ae2cda..12acaa006f54 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/equality/ReplaceEqualityWithSafeEqualsIntention.java +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/equality/ReplaceEqualityWithSafeEqualsIntention.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2013 Dave Griffith, Bas Leijdekkers + * Copyright 2003-2014 Dave Griffith, Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,25 +17,36 @@ package com.siyeh.ipp.equality; import com.intellij.psi.*; import com.intellij.psi.tree.IElementType; -import com.intellij.util.IncorrectOperationException; +import com.intellij.psi.util.PsiUtil; +import com.siyeh.IntentionPowerPackBundle; import com.siyeh.ig.PsiReplacementUtil; +import com.siyeh.ig.psiutils.ClassUtils; import com.siyeh.ig.psiutils.ParenthesesUtils; -import com.siyeh.ipp.base.Intention; +import com.siyeh.ipp.base.MutablyNamedIntention; import com.siyeh.ipp.base.PsiElementPredicate; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; -public class ReplaceEqualityWithSafeEqualsIntention extends Intention { +public class ReplaceEqualityWithSafeEqualsIntention extends MutablyNamedIntention { + + @Override + protected String getTextForElement(PsiElement element) { + final PsiBinaryExpression binaryExpression = (PsiBinaryExpression)element; + if (JavaTokenType.NE.equals(binaryExpression.getOperationTokenType())) { + return IntentionPowerPackBundle.message("replace.equality.with.safe.not.equals.intention.name"); + } + else { + return IntentionPowerPackBundle.message("replace.equality.with.safe.equals.intention.name"); + } + } @NotNull public PsiElementPredicate getElementPredicate() { return new ObjectEqualityPredicate(); } - public void processIntention(PsiElement element) - throws IncorrectOperationException { - final PsiBinaryExpression exp = - (PsiBinaryExpression)element; + public void processIntention(PsiElement element) { + final PsiBinaryExpression exp = (PsiBinaryExpression)element; final PsiExpression lhs = exp.getLOperand(); final PsiExpression rhs = exp.getROperand(); if (rhs == null) { @@ -56,26 +67,26 @@ public class ReplaceEqualityWithSafeEqualsIntention extends Intention { final PsiJavaToken operationSign = exp.getOperationSign(); final IElementType tokenType = operationSign.getTokenType(); final String signText = operationSign.getText(); - @NonNls final StringBuilder buffer = new StringBuilder(lhsText); - buffer.append("==null?"); - buffer.append(rhsText); - buffer.append(signText); - buffer.append(" null:"); - if (tokenType.equals(JavaTokenType.NE)) { - buffer.append('!'); - } - if (ParenthesesUtils.getPrecedence(strippedLhs) > - ParenthesesUtils.METHOD_CALL_PRECEDENCE) { - buffer.append('('); - buffer.append(lhsText); - buffer.append(')'); + @NonNls final StringBuilder newExpression = new StringBuilder(); + if (PsiUtil.isLanguageLevel7OrHigher(element) && ClassUtils.findClass("java.util.Objects", element) != null) { + if (tokenType.equals(JavaTokenType.NE)) { + newExpression.append('!'); + } + newExpression.append("java.util.Objects.equals(").append(lhsText).append(',').append(rhsText).append(')'); } else { - buffer.append(lhsText); + newExpression.append(lhsText).append("==null?").append(rhsText).append(signText).append(" null:"); + if (tokenType.equals(JavaTokenType.NE)) { + newExpression.append('!'); + } + if (ParenthesesUtils.getPrecedence(strippedLhs) > ParenthesesUtils.METHOD_CALL_PRECEDENCE) { + newExpression.append('(').append(lhsText).append(')'); + } + else { + newExpression.append(lhsText); + } + newExpression.append(".equals(").append(rhsText).append(')'); } - buffer.append(".equals("); - buffer.append(rhsText); - buffer.append(')'); - PsiReplacementUtil.replaceExpression(exp, buffer.toString()); + PsiReplacementUtil.replaceExpressionAndShorten(exp, newExpression.toString()); } } \ No newline at end of file diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/initialization/SplitDeclarationAndInitializationIntention.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/initialization/SplitDeclarationAndInitializationIntention.java index 3bbc7a63bd65..aebb8f568814 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/initialization/SplitDeclarationAndInitializationIntention.java +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/initialization/SplitDeclarationAndInitializationIntention.java @@ -37,12 +37,11 @@ public class SplitDeclarationAndInitializationIntention extends Intention { @Override public void processIntention(@NotNull PsiElement element) { final PsiField field = (PsiField)element.getParent(); - field.normalizeDeclaration(); - final PsiExpression initializer = RefactoringUtil.convertInitializerToNormalExpression(field.getInitializer(), field.getType()); + final PsiExpression initializer = field.getInitializer(); if (initializer == null) { return; } - final String initializerText = initializer.getText(); + final String initializerText = RefactoringUtil.convertInitializerToNormalExpression(initializer, field.getType()).getText(); final PsiClass containingClass = field.getContainingClass(); if (containingClass == null) { return; @@ -96,9 +95,7 @@ public class SplitDeclarationAndInitializationIntention extends Intention { } } initializer.delete(); - final CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(manager.getProject()); - codeStyleManager.reformat(field); - codeStyleManager.reformat(classInitializer); + CodeStyleManager.getInstance(manager.getProject()).reformat(classInitializer); HighlightUtil.highlightElement(addedElement, IntentionPowerPackBundle.message( "press.escape.to.remove.highlighting.message")); diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/junit/CreateAssertIntention.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/junit/CreateAssertIntention.java index 55ae45e3f3bf..4be5442d03c1 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/junit/CreateAssertIntention.java +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/junit/CreateAssertIntention.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2006 Dave Griffith, Bas Leijdekkers + * Copyright 2003-2014 Dave Griffith, Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,16 +16,15 @@ package com.siyeh.ipp.junit; import com.intellij.psi.*; -import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.tree.IElementType; -import com.intellij.util.IncorrectOperationException; -import com.intellij.codeInsight.AnnotationUtil; import com.siyeh.ig.PsiReplacementUtil; +import com.siyeh.ig.psiutils.ExpressionUtils; +import com.siyeh.ig.psiutils.ImportUtils; import com.siyeh.ipp.base.Intention; import com.siyeh.ipp.base.PsiElementPredicate; import com.siyeh.ipp.psiutils.BoolUtils; -import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; public class CreateAssertIntention extends Intention { @@ -34,28 +33,12 @@ public class CreateAssertIntention extends Intention { return new CreateAssertPredicate(); } - public void processIntention(PsiElement element) - throws IncorrectOperationException { - final PsiExpressionStatement statement = - (PsiExpressionStatement)element; - assert statement != null; + public void processIntention(PsiElement element) { + final PsiExpressionStatement statement = (PsiExpressionStatement)element; final PsiExpression expression = statement.getExpression(); - final PsiMethod containingMethod = - PsiTreeUtil.getParentOfType(statement, PsiMethod.class); - final String specifierString; - if (containingMethod != null && - AnnotationUtil.isAnnotated(containingMethod, - "org.junit.Test", true)) { - specifierString = "org.junit.Assert."; - } - else { - specifierString = ""; - } + final String newStatement; if (BoolUtils.isNegation(expression)) { - @NonNls final String newExpression = - specifierString + "assertFalse(" + - BoolUtils.getNegatedExpressionText(expression) + ");"; - PsiReplacementUtil.replaceStatementAndShortenClassNames(statement, newExpression); + newStatement = buildNewStatement("assertFalse", element, BoolUtils.getNegatedExpressionText(expression)); } else if (isNullComparison(expression)) { final PsiBinaryExpression binaryExpression = @@ -63,16 +46,19 @@ public class CreateAssertIntention extends Intention { final PsiExpression lhs = binaryExpression.getLOperand(); final PsiExpression rhs = binaryExpression.getROperand(); final PsiExpression comparedExpression; - if (isNull(lhs)) { + if (ExpressionUtils.isNullLiteral(lhs)) { comparedExpression = rhs; } else { comparedExpression = lhs; } assert comparedExpression != null; - @NonNls final String newExpression = specifierString + - "assertNull(" + comparedExpression.getText() + ");"; - PsiReplacementUtil.replaceStatementAndShortenClassNames(statement, newExpression); + if (JavaTokenType.EQEQ.equals(binaryExpression.getOperationTokenType())) { + newStatement = buildNewStatement("assertNull", element, comparedExpression.getText()); + } + else { + newStatement = buildNewStatement("assertNotNull", element, comparedExpression.getText()); + } } else if (isEqualityComparison(expression)) { final PsiBinaryExpression binaryExpression = @@ -91,23 +77,16 @@ public class CreateAssertIntention extends Intention { } assert comparingExpression != null; final PsiType type = lhs.getType(); - @NonNls final String newExpression; if (PsiType.DOUBLE.equals(type) || PsiType.FLOAT.equals(type)) { - newExpression = specifierString + "assertEquals(" + - comparedExpression.getText() + ", " + - comparingExpression.getText() + ", 0.0);"; + newStatement = buildNewStatement("assertEquals", + element, comparedExpression.getText(), comparingExpression.getText(), "0.0"); } else if (type instanceof PsiPrimitiveType) { - newExpression = specifierString + "assertEquals(" + - comparedExpression.getText() + ", " + - comparingExpression.getText() + ");"; + newStatement = buildNewStatement("assertEquals", element, comparedExpression.getText(), comparingExpression.getText()); } else { - newExpression = specifierString + "assertSame(" + - comparedExpression.getText() + ", " + - comparingExpression.getText() + ");"; + newStatement = buildNewStatement("assertSame", element, comparedExpression.getText(), comparingExpression.getText()); } - PsiReplacementUtil.replaceStatementAndShortenClassNames(statement, newExpression); } else if (isEqualsExpression(expression)) { final PsiMethodCallExpression call = @@ -118,26 +97,55 @@ public class CreateAssertIntention extends Intention { methodExpression.getQualifierExpression(); assert comparedExpression != null; final PsiExpressionList argList = call.getArgumentList(); - final PsiExpression comparingExpression = - argList.getExpressions()[0]; - @NonNls final String newExpression; + final PsiExpression comparingExpression = argList.getExpressions()[0]; if (comparingExpression instanceof PsiLiteralExpression) { - newExpression = specifierString + "assertEquals(" + - comparingExpression.getText() + ", " + - comparedExpression.getText() + ");"; + newStatement = buildNewStatement("assertEquals", element, comparingExpression.getText(), comparedExpression.getText()); + } + else { + newStatement = buildNewStatement("assertEquals", element, comparedExpression.getText(), comparingExpression.getText()); + } + } + else { + newStatement = buildNewStatement("assertTrue", element, expression.getText()); + } + PsiReplacementUtil.replaceStatementAndShortenClassNames(statement, newStatement); + } + + @NonNls + private static String buildNewStatement(@NonNls String memberName, PsiElement context, String... argumentTexts) { + final PsiElementFactory factory = JavaPsiFacade.getElementFactory(context.getProject()); + final StringBuilder builder = new StringBuilder(memberName).append('('); + boolean comma = false; + for (String argumentText : argumentTexts) { + if (comma) { + builder.append(','); } else { - newExpression = specifierString + "assertEquals(" + - comparedExpression.getText() + ", " + - comparingExpression.getText() + ");"; + comma = true; } - PsiReplacementUtil.replaceStatementAndShortenClassNames(statement, newExpression); + builder.append(argumentText); + } + builder.append(')'); + final String text = builder.toString(); + + final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)factory.createExpressionFromText(text, context); + final PsiMethod method = methodCallExpression.resolveMethod(); + if (method != null || hasStaticImports(context) && ImportUtils.addStaticImport("org.junit.Assert", memberName, context)) { + return text + ';'; } else { - @NonNls final String newExpression = - specifierString + "assertTrue(" + expression.getText() + ");"; - PsiReplacementUtil.replaceStatementAndShortenClassNames(statement, newExpression); + return "org.junit.Assert." + text + ';'; + } + } + + private static boolean hasStaticImports(PsiElement element) { + final PsiFile file = element.getContainingFile(); + if (!(file instanceof PsiJavaFile)) { + return false; } + final PsiJavaFile javaFile = (PsiJavaFile)file; + final PsiImportList importList = javaFile.getImportList(); + return importList != null && importList.getImportStaticStatements().length > 0; } private static boolean isEqualsExpression(PsiExpression expression) { @@ -176,25 +184,16 @@ public class CreateAssertIntention extends Intention { if (!(expression instanceof PsiBinaryExpression)) { return false; } - final PsiBinaryExpression binaryExpression = - (PsiBinaryExpression)expression; + final PsiBinaryExpression binaryExpression = (PsiBinaryExpression)expression; final IElementType tokenType = binaryExpression.getOperationTokenType(); - if (!JavaTokenType.EQEQ.equals(tokenType)) { + if (!JavaTokenType.EQEQ.equals(tokenType) && !JavaTokenType.NE.equals(tokenType)) { return false; } final PsiExpression lhs = binaryExpression.getLOperand(); - if (isNull(lhs)) { + if (ExpressionUtils.isNullLiteral(lhs)) { return true; } - final PsiExpression Rhs = binaryExpression.getROperand(); - return isNull(Rhs); - } - - private static boolean isNull(PsiExpression expression) { - if (!(expression instanceof PsiLiteralExpression)) { - return false; - } - @NonNls final String text = expression.getText(); - return PsiKeyword.NULL.equals(text); + final PsiExpression rhs = binaryExpression.getROperand(); + return ExpressionUtils.isNullLiteral(rhs); } } diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/junit/CreateAssertPredicate.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/junit/CreateAssertPredicate.java index 27f09f9c3b80..c21bb76aa1bc 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/junit/CreateAssertPredicate.java +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/junit/CreateAssertPredicate.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2009 Dave Griffith, Bas Leijdekkers + * Copyright 2003-2014 Dave Griffith, Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,14 +15,10 @@ */ package com.siyeh.ipp.junit; -import com.intellij.codeInsight.AnnotationUtil; -import com.intellij.openapi.project.Project; import com.intellij.psi.*; -import com.intellij.psi.search.GlobalSearchScope; -import com.intellij.psi.util.InheritanceUtil; import com.intellij.psi.util.PsiTreeUtil; +import com.siyeh.ig.psiutils.TestUtils; import com.siyeh.ipp.base.PsiElementPredicate; -import org.jetbrains.annotations.NonNls; class CreateAssertPredicate implements PsiElementPredicate { @@ -41,50 +37,13 @@ class CreateAssertPredicate implements PsiElementPredicate { if (!PsiType.BOOLEAN.equals(type)) { return false; } - final PsiMethod containingMethod = - PsiTreeUtil.getParentOfType(expression, PsiMethod.class); - return isTestMethod(containingMethod); - } - - private static boolean isTestMethod(PsiMethod method) { - if (method == null) { - return false; - } - if (AnnotationUtil.isAnnotated(method, "org.junit.Test", true)) { - return true; - } - if (method.hasModifierProperty(PsiModifier.ABSTRACT) || - !method.hasModifierProperty(PsiModifier.PUBLIC)) { - return false; - } - final PsiType returnType = method.getReturnType(); - if (returnType == null) { - return false; - } - if (!returnType.equals(PsiType.VOID)) { - return false; - } - final PsiParameterList parameterList = method.getParameterList(); - final PsiParameter[] parameters = parameterList.getParameters(); - if (parameters.length != 0) { - return false; - } - @NonNls final String methodName = method.getName(); - if (!methodName.startsWith("test")) { - return false; - } - final PsiClass containingClass = method.getContainingClass(); - return isTestClass(containingClass); - } - - private static boolean isTestClass(PsiClass aClass) { - if (aClass == null) { - return false; + PsiMethod containingMethod = PsiTreeUtil.getParentOfType(expression, PsiMethod.class); + while (containingMethod != null) { + if (TestUtils.isJUnitTestMethod(containingMethod)) { + return true; + } + containingMethod = PsiTreeUtil.getParentOfType(containingMethod, PsiMethod.class); } - final Project project = aClass.getProject(); - final GlobalSearchScope scope = GlobalSearchScope.allScope(project); - final JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(project); - final PsiClass ancestorClass = psiFacade.findClass("junit.framework.TestCase", scope); - return InheritanceUtil.isInheritorOrSelf(aClass, ancestorClass, true); + return false; } } diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/decls/simplify_variable/FieldWithWhitespace.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/decls/simplify_variable/FieldWithWhitespace.java new file mode 100644 index 000000000000..38de2d7356b7 --- /dev/null +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/decls/simplify_variable/FieldWithWhitespace.java @@ -0,0 +1,4 @@ +public class FieldWithWhitespace { + + private String ss []; +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/decls/simplify_variable/FieldWithWhitespace_after.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/decls/simplify_variable/FieldWithWhitespace_after.java new file mode 100644 index 000000000000..c53875ed975c --- /dev/null +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/decls/simplify_variable/FieldWithWhitespace_after.java @@ -0,0 +1,4 @@ +public class FieldWithWhitespace { + + private String[] ss; +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/equality/replace_equality_with_equals/EnumComparison.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/equality/replace_equality_with_equals/EnumComparison.java new file mode 100644 index 000000000000..bf1f08c198c5 --- /dev/null +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/equality/replace_equality_with_equals/EnumComparison.java @@ -0,0 +1,8 @@ +public class EnumComparison { + + enum E { A, B } + + boolean a(E a, E b) { + return a == b; + } +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/equality/replace_equality_with_equals/NegatedObjectComparison.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/equality/replace_equality_with_equals/NegatedObjectComparison.java new file mode 100644 index 000000000000..863fa48d0c78 --- /dev/null +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/equality/replace_equality_with_equals/NegatedObjectComparison.java @@ -0,0 +1,6 @@ +public class NegatedObjectComparison { + + boolean a(Object a, Object b) { + return a != b; + } +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/equality/replace_equality_with_equals/NegatedObjectComparison_after.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/equality/replace_equality_with_equals/NegatedObjectComparison_after.java new file mode 100644 index 000000000000..72eda7377647 --- /dev/null +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/equality/replace_equality_with_equals/NegatedObjectComparison_after.java @@ -0,0 +1,6 @@ +public class NegatedObjectComparison { + + boolean a(Object a, Object b) { + return !a.equals(b); + } +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/equality/replace_equality_with_equals/NullComparison.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/equality/replace_equality_with_equals/NullComparison.java new file mode 100644 index 000000000000..559ec3aec144 --- /dev/null +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/equality/replace_equality_with_equals/NullComparison.java @@ -0,0 +1,6 @@ +public class NullComparison { + + boolean a(Object a) { + return a == null; + } +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/equality/replace_equality_with_equals/PrimitiveComparison.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/equality/replace_equality_with_equals/PrimitiveComparison.java new file mode 100644 index 000000000000..0f25977323ab --- /dev/null +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/equality/replace_equality_with_equals/PrimitiveComparison.java @@ -0,0 +1,6 @@ +public class PrimitiveComparison { + + boolean a(int i, int j) { + return i == j; + } +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/equality/replace_equality_with_equals/SimpleObjectComparison.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/equality/replace_equality_with_equals/SimpleObjectComparison.java new file mode 100644 index 000000000000..baccea789e04 --- /dev/null +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/equality/replace_equality_with_equals/SimpleObjectComparison.java @@ -0,0 +1,6 @@ +public class SimpleObjectComparison { + + boolean a(Object a, Object b) { + return a == b; + } +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/equality/replace_equality_with_equals/SimpleObjectComparison_after.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/equality/replace_equality_with_equals/SimpleObjectComparison_after.java new file mode 100644 index 000000000000..ab1bcce4c87b --- /dev/null +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/equality/replace_equality_with_equals/SimpleObjectComparison_after.java @@ -0,0 +1,6 @@ +public class SimpleObjectComparison { + + boolean a(Object a, Object b) { + return a.equals(b); + } +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/equality/replace_equality_with_safe_equals/EnumComparison.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/equality/replace_equality_with_safe_equals/EnumComparison.java new file mode 100644 index 000000000000..43b16a421c25 --- /dev/null +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/equality/replace_equality_with_safe_equals/EnumComparison.java @@ -0,0 +1,10 @@ +package com.siyeh.ipp.equality.replace_equality_with_safe_equals; + +public class EnumComparison { + + enum E { A, B } + + boolean a(E a, E b) { + return a == b; + } +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/equality/replace_equality_with_safe_equals/NegatedObjectComparison.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/equality/replace_equality_with_safe_equals/NegatedObjectComparison.java new file mode 100644 index 000000000000..018def909d05 --- /dev/null +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/equality/replace_equality_with_safe_equals/NegatedObjectComparison.java @@ -0,0 +1,8 @@ +package com.siyeh.ipp.equality.replace_equality_with_safe_equals; + +public class NegatedObjectComparison { + + boolean a(Object a, Object b) { + return a != b; + } +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/equality/replace_equality_with_safe_equals/NegatedObjectComparison_after.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/equality/replace_equality_with_safe_equals/NegatedObjectComparison_after.java new file mode 100644 index 000000000000..b7f48289a4b8 --- /dev/null +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/equality/replace_equality_with_safe_equals/NegatedObjectComparison_after.java @@ -0,0 +1,8 @@ +package com.siyeh.ipp.equality.replace_equality_with_safe_equals; + +public class NegatedObjectComparison { + + boolean a(Object a, Object b) { + return a == null ? b != null : !a.equals(b); + } +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/equality/replace_equality_with_safe_equals/NullComparison.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/equality/replace_equality_with_safe_equals/NullComparison.java new file mode 100644 index 000000000000..10aeb19f6fed --- /dev/null +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/equality/replace_equality_with_safe_equals/NullComparison.java @@ -0,0 +1,8 @@ +package com.siyeh.ipp.equality.replace_equality_with_safe_equals; + +public class NullComparison { + + boolean a(Object a) { + return a == null; + } +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/equality/replace_equality_with_safe_equals/PrimitiveComparison.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/equality/replace_equality_with_safe_equals/PrimitiveComparison.java new file mode 100644 index 000000000000..8ae058592d0a --- /dev/null +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/equality/replace_equality_with_safe_equals/PrimitiveComparison.java @@ -0,0 +1,8 @@ +package com.siyeh.ipp.equality.replace_equality_with_safe_equals; + +public class PrimitiveComparison { + + boolean a(int i, int j) { + return i == j; + } +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/equality/replace_equality_with_safe_equals/SimpleObjectComparison.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/equality/replace_equality_with_safe_equals/SimpleObjectComparison.java new file mode 100644 index 000000000000..304b06ff67a1 --- /dev/null +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/equality/replace_equality_with_safe_equals/SimpleObjectComparison.java @@ -0,0 +1,8 @@ +package com.siyeh.ipp.equality.replace_equality_with_safe_equals; + +public class SimpleObjectComparison { + + boolean a(Object a, Object b) { + return a == b; + } +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/equality/replace_equality_with_safe_equals/SimpleObjectComparison_after.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/equality/replace_equality_with_safe_equals/SimpleObjectComparison_after.java new file mode 100644 index 000000000000..5ee0ddf5d6df --- /dev/null +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/equality/replace_equality_with_safe_equals/SimpleObjectComparison_after.java @@ -0,0 +1,8 @@ +package com.siyeh.ipp.equality.replace_equality_with_safe_equals; + +public class SimpleObjectComparison { + + boolean a(Object a, Object b) { + return a == null ? b == null : a.equals(b); + } +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/initialization/Array.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/initialization/Array.java new file mode 100644 index 000000000000..486097f3f88a --- /dev/null +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/initialization/Array.java @@ -0,0 +1,3 @@ +class Array { + String s [] = {}; +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/initialization/ArrayInitializer.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/initialization/ArrayInitializer.java index 54eaa025b8e8..6ea7f3cf2dd7 100644 --- a/plugins/IntentionPowerPak/test/com/siyeh/ipp/initialization/ArrayInitializer.java +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/initialization/ArrayInitializer.java @@ -1,3 +1,3 @@ class X { - String s = {}; + String s = {}; } \ No newline at end of file diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/initialization/Array_after.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/initialization/Array_after.java new file mode 100644 index 000000000000..6aeac61776f9 --- /dev/null +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/initialization/Array_after.java @@ -0,0 +1,7 @@ +class Array { + String s []; + + { + s = new String[]{}; + } +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/junit/create_assert/AnonymousClassJUnit3.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/junit/create_assert/AnonymousClassJUnit3.java new file mode 100644 index 000000000000..d6c02fcfbe66 --- /dev/null +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/junit/create_assert/AnonymousClassJUnit3.java @@ -0,0 +1,12 @@ +import junit.framework.TestCase; + +public class AnonymousClassJUnit3 extends TestCase { + + public void test2BiggerThan1() { + new Object() { + void foo() { + 2 > 1 + } + }; + } +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/junit/create_assert/AnonymousClassJUnit3_after.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/junit/create_assert/AnonymousClassJUnit3_after.java new file mode 100644 index 000000000000..9821c9431820 --- /dev/null +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/junit/create_assert/AnonymousClassJUnit3_after.java @@ -0,0 +1,12 @@ +import junit.framework.TestCase; + +public class AnonymousClassJUnit3 extends TestCase { + + public void test2BiggerThan1() { + new Object() { + void foo() { + assertTrue(2 > 1); + } + }; + } +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/junit/create_assert/AnonymousClassJUnit4.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/junit/create_assert/AnonymousClassJUnit4.java new file mode 100644 index 000000000000..e7b11eef3a87 --- /dev/null +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/junit/create_assert/AnonymousClassJUnit4.java @@ -0,0 +1,11 @@ +public class AnonymousClassJUnit4 { + + @org.junit.Test + public void test2BiggerThan1() { + new Object() { + void foo() { + 2 > 1 + } + } + } +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/junit/create_assert/AnonymousClassJUnit4_after.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/junit/create_assert/AnonymousClassJUnit4_after.java new file mode 100644 index 000000000000..787d0e6d6115 --- /dev/null +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/junit/create_assert/AnonymousClassJUnit4_after.java @@ -0,0 +1,13 @@ +import org.junit.Assert; + +public class AnonymousClassJUnit4 { + + @org.junit.Test + public void test2BiggerThan1() { + new Object() { + void foo() { + Assert.assertTrue(2 > 1); + } + } + } +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/junit/create_assert/AssertFalse.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/junit/create_assert/AssertFalse.java new file mode 100644 index 000000000000..76bddf465b6f --- /dev/null +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/junit/create_assert/AssertFalse.java @@ -0,0 +1,12 @@ +import junit.framework.TestCase; + +public class AssertFalse extends TestCase { + + public void testOne() { + !result() + } + + boolean result() { + return false; + } +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/junit/create_assert/AssertFalse_after.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/junit/create_assert/AssertFalse_after.java new file mode 100644 index 000000000000..6c1ad31f57a6 --- /dev/null +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/junit/create_assert/AssertFalse_after.java @@ -0,0 +1,12 @@ +import junit.framework.TestCase; + +public class AssertFalse extends TestCase { + + public void testOne() { + assertFalse(result()); + } + + boolean result() { + return false; + } +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/junit/create_assert/StaticImportJUnit4.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/junit/create_assert/StaticImportJUnit4.java new file mode 100644 index 000000000000..24abc14797f4 --- /dev/null +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/junit/create_assert/StaticImportJUnit4.java @@ -0,0 +1,9 @@ +import static java.util.Collections.EMPTY_LIST; + +public class AnonymousClassJUnit4 { + + @org.junit.Test + public void testNotNull() { + EMPTY_LIST != null + } +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/junit/create_assert/StaticImportJUnit4_after.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/junit/create_assert/StaticImportJUnit4_after.java new file mode 100644 index 000000000000..792a4f778816 --- /dev/null +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/junit/create_assert/StaticImportJUnit4_after.java @@ -0,0 +1,10 @@ +import static java.util.Collections.EMPTY_LIST; +import static org.junit.Assert.assertNotNull; + +public class AnonymousClassJUnit4 { + + @org.junit.Test + public void testNotNull() { + assertNotNull(EMPTY_LIST); + } +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/types/lambda2anonymous/Ambiguity.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/types/lambda2anonymous/Ambiguity.java index 6df5c30ae69a..e8be7a9d0b0c 100644 --- a/plugins/IntentionPowerPak/test/com/siyeh/ipp/types/lambda2anonymous/Ambiguity.java +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/types/lambda2anonymous/Ambiguity.java @@ -2,14 +2,14 @@ interface I1 { void m(); } -interface I2 { - X m(); +interface I2 { + void m(); } class Ambiguity1 { static void m(I1 i1) {} - static void m(I2 i2) {} + static void m(I2 i2) {} { m(()->{throw new AssertionError();}); diff --git a/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/decls/SimplifyVariableIntentionTest.java b/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/decls/SimplifyVariableIntentionTest.java new file mode 100644 index 000000000000..1d340724138b --- /dev/null +++ b/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/decls/SimplifyVariableIntentionTest.java @@ -0,0 +1,38 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ipp.decls; + +import com.siyeh.IntentionPowerPackBundle; +import com.siyeh.ipp.IPPTestCase; +import junit.framework.TestCase; + +/** + * @author Bas Leijdekkers + */ +public class SimplifyVariableIntentionTest extends IPPTestCase { + + public void testFieldWithWhitespace() { doTest(); } + + @Override + protected String getIntentionName() { + return IntentionPowerPackBundle.message("simplify.variable.intention.name"); + } + + @Override + protected String getRelativePath() { + return "decls/simplify_variable"; + } +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/equality/ReplaceEqualityWithEqualsIntentionTest.java b/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/equality/ReplaceEqualityWithEqualsIntentionTest.java new file mode 100644 index 000000000000..f9150050fbdd --- /dev/null +++ b/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/equality/ReplaceEqualityWithEqualsIntentionTest.java @@ -0,0 +1,42 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ipp.equality; + +import com.siyeh.IntentionPowerPackBundle; +import com.siyeh.ipp.IPPTestCase; + +/** + * @see com.siyeh.ipp.equality.ReplaceEqualityWithEqualsIntention + * @author Bas Leijdekkers + */ +public class ReplaceEqualityWithEqualsIntentionTest extends IPPTestCase { + + public void testEnumComparison() { assertIntentionNotAvailable(); } + public void testNullComparison() { assertIntentionNotAvailable(); } + public void testPrimitiveComparison() { assertIntentionNotAvailable(); } + public void testSimpleObjectComparison() { doTest(); } + public void testNegatedObjectComparison() { doTest(IntentionPowerPackBundle.message("replace.equality.with.not.equals.intention.name")); } + + @Override + protected String getIntentionName() { + return IntentionPowerPackBundle.message("replace.equality.with.equals.intention.name"); + } + + @Override + protected String getRelativePath() { + return "equality/replace_equality_with_equals"; + } +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/equality/ReplaceEqualityWithSafeEqualsIntentionTest.java b/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/equality/ReplaceEqualityWithSafeEqualsIntentionTest.java new file mode 100644 index 000000000000..c2fe09148f9a --- /dev/null +++ b/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/equality/ReplaceEqualityWithSafeEqualsIntentionTest.java @@ -0,0 +1,42 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ipp.equality; + +import com.siyeh.IntentionPowerPackBundle; +import com.siyeh.ipp.IPPTestCase; + +/** + * @see com.siyeh.ipp.equality.ReplaceEqualityWithSafeEqualsIntention + * @author Bas Leijdekkers + */ +public class ReplaceEqualityWithSafeEqualsIntentionTest extends IPPTestCase { + + public void testEnumComparison() { assertIntentionNotAvailable(); } + public void testNullComparison() { assertIntentionNotAvailable(); } + public void testPrimitiveComparison() { assertIntentionNotAvailable(); } + public void testSimpleObjectComparison() { doTest(); } + public void testNegatedObjectComparison() { doTest(IntentionPowerPackBundle.message("replace.equality.with.safe.not.equals.intention.name")); } + + @Override + protected String getIntentionName() { + return IntentionPowerPackBundle.message("replace.equality.with.safe.equals.intention.name"); + } + + @Override + protected String getRelativePath() { + return "equality/replace_equality_with_safe_equals"; + } +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/initialization/SplitDeclarationAndInitializationIntentionTest.java b/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/initialization/SplitDeclarationAndInitializationIntentionTest.java index 63ac3c9acf9f..933d5e2e7964 100644 --- a/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/initialization/SplitDeclarationAndInitializationIntentionTest.java +++ b/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/initialization/SplitDeclarationAndInitializationIntentionTest.java @@ -25,6 +25,7 @@ import com.siyeh.ipp.IPPTestCase; public class SplitDeclarationAndInitializationIntentionTest extends IPPTestCase { public void testArrayInitializer() { doTest(); } + public void testArray() { doTest(); } @Override protected String getRelativePath() { diff --git a/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/junit/CreateAssertIntentionTest.java b/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/junit/CreateAssertIntentionTest.java new file mode 100644 index 000000000000..b67a451ce947 --- /dev/null +++ b/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/junit/CreateAssertIntentionTest.java @@ -0,0 +1,60 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.siyeh.ipp.junit; + +import com.siyeh.IntentionPowerPackBundle; +import com.siyeh.ipp.IPPTestCase; + +/** + * @see CreateAssertIntention + * @author Bas Leijdekkers + */ +public class CreateAssertIntentionTest extends IPPTestCase { + + public void testAnonymousClassJUnit3() { doTest(); } + public void testAnonymousClassJUnit4() { doTest(); } + public void testStaticImportJUnit4() { doTest(); } + public void testAssertFalse() { doTest(); } + + @Override + protected String getRelativePath() { + return "junit/create_assert"; + } + + @Override + protected String getIntentionName() { + return IntentionPowerPackBundle.message("create.assert.intention.name"); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + myFixture.addClass("package org.junit;" + + "class Assert {" + + " public static void assertTrue(java.lang.String message, boolean condition) {}" + + " public static void assertNotNull(boolean condition) {}" + + "}"); + myFixture.addClass("package org.junit;" + + "@Retention(RetentionPolicy.RUNTIME)" + + "@Target({ElementType.METHOD})" + + "public @interface Test {}"); + myFixture.addClass("package junit.framework;" + + "public abstract class TestCase {" + + " static public void assertTrue(boolean condition) {}" + + " static public void assertFalse(boolean condition) {}" + ); + } +} diff --git a/plugins/ShortcutPromoter/src/com/intellij/promoter/ShortcutPromoterManager.java b/plugins/ShortcutPromoter/src/com/intellij/promoter/ShortcutPromoterManager.java index 227d96245080..3baafb1fe510 100644 --- a/plugins/ShortcutPromoter/src/com/intellij/promoter/ShortcutPromoterManager.java +++ b/plugins/ShortcutPromoter/src/com/intellij/promoter/ShortcutPromoterManager.java @@ -22,11 +22,7 @@ import java.util.LinkedHashMap; */ @State( name = "ShortcutPromoterManager", - roamingType = RoamingType.PER_PLATFORM, - storages = { - @Storage( - file = StoragePathMacros.APP_CONFIG + "/promoter.xml" - )} + storages = {@Storage(file = StoragePathMacros.APP_CONFIG + "/promoter.xml", roamingType = RoamingType.PER_PLATFORM)} ) public class ShortcutPromoterManager implements ApplicationComponent, AnActionListener, ExportableApplicationComponent, PersistentStateComponent { @@ -97,10 +93,6 @@ public class ShortcutPromoterManager implements ApplicationComponent, AnActionLi @Nullable @Override public Element getState() { - if (myState.isEmpty()) { - return null; - } - final Element actions = new Element("actions"); for (String id : myState.keySet()) { final Element action = new Element("action"); diff --git a/plugins/cvs/cvs-plugin/src/META-INF/plugin.xml b/plugins/cvs/cvs-plugin/src/META-INF/plugin.xml index 1cefc9748ae4..d42cab21ddac 100644 --- a/plugins/cvs/cvs-plugin/src/META-INF/plugin.xml +++ b/plugins/cvs/cvs-plugin/src/META-INF/plugin.xml @@ -63,7 +63,7 @@ - + @@ -72,7 +72,7 @@ - + @@ -80,8 +80,8 @@ - - + + diff --git a/plugins/devkit/resources/org/jetbrains/idea/devkit/DevKitBundle.properties b/plugins/devkit/resources/org/jetbrains/idea/devkit/DevKitBundle.properties index 6799f6d2415f..2d1f3de4e3d6 100644 --- a/plugins/devkit/resources/org/jetbrains/idea/devkit/DevKitBundle.properties +++ b/plugins/devkit/resources/org/jetbrains/idea/devkit/DevKitBundle.properties @@ -25,7 +25,7 @@ prepare.for.deployment.common=Preparing For Deployment prepare.for.deployment=Prepare Plugin Module ''{0}'' For Deployment prepare.for.deployment.all=Prepare All Plugin Modules For Deployment saved.message.common={0} will be saved in selected directory -saved.message= {0, choice, 1#Zip|2#Jar} for module ''{1}'' was saved to {2} +saved.message= {0, choice, 1#Zip|2#JAR} for module ''{1}'' was saved to {2} success.deployment.message=Plugin Module ''{0}'' Successfully Prepared For Deployment success.deployment.message.all=All Plugins Successfully Prepared For Deployment diff --git a/plugins/devkit/src/dom/Action.java b/plugins/devkit/src/dom/Action.java index ac879140fd4b..e28f2d13b9b4 100644 --- a/plugins/devkit/src/dom/Action.java +++ b/plugins/devkit/src/dom/Action.java @@ -165,4 +165,7 @@ public interface Action extends DomElement { @NotNull GenericAttributeValue getKeymap(); + + @NotNull + GenericAttributeValue getProjectType(); } diff --git a/plugins/devkit/src/dom/impl/ExtensionDomExtender.java b/plugins/devkit/src/dom/impl/ExtensionDomExtender.java index f5c83ccb66bc..538ee7e70243 100644 --- a/plugins/devkit/src/dom/impl/ExtensionDomExtender.java +++ b/plugins/devkit/src/dom/impl/ExtensionDomExtender.java @@ -15,7 +15,7 @@ */ package org.jetbrains.idea.devkit.dom.impl; -import com.intellij.ide.plugins.PluginManager; +import com.intellij.ide.plugins.PluginManagerCore; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; @@ -50,57 +50,6 @@ import java.util.*; public class ExtensionDomExtender extends DomExtender { private static final PsiClassConverter CLASS_CONVERTER = new PluginPsiClassConverter(); private static final Converter LANGUAGE_CONVERTER = new LanguageResolvingConverter(); - - private static class MyRequired implements Required { - @Override - public boolean value() { - return true; - } - - @Override - public boolean nonEmpty() { - return true; - } - - @Override - public boolean identifier() { - return false; - } - - @Override - public Class annotationType() { - return Required.class; - } - } - - private static class MyExtendClass extends ExtendClassImpl { - private final String myInterfaceName; - - private MyExtendClass(String interfaceName) { - myInterfaceName = interfaceName; - } - - @Override - public boolean allowAbstract() { - return false; - } - - @Override - public boolean allowInterface() { - return false; - } - - @Override - public boolean allowEnum() { - return false; - } - - @Override - public String value() { - return myInterfaceName; - } - } - private static final DomExtender EXTENSION_EXTENDER = new DomExtender() { public void registerExtensions(@NotNull final DomElement domElement, @NotNull final DomExtensionsRegistrar registrar) { final ExtensionPoint extensionPoint = (ExtensionPoint)domElement.getChildDescription().getDomDeclaration(); @@ -127,31 +76,6 @@ public class ExtensionDomExtender extends DomExtender { } }; - - public void registerExtensions(@NotNull final Extensions extensions, @NotNull final DomExtensionsRegistrar registrar) { - final XmlElement xmlElement = extensions.getXmlElement(); - if (xmlElement == null) return; - - IdeaPlugin ideaPlugin = extensions.getParentOfType(IdeaPlugin.class, true); - - if (ideaPlugin == null) return; - - String epPrefix = extensions.getEpPrefix(); - for (IdeaPlugin plugin : getVisiblePlugins(ideaPlugin)) { - final String pluginId = StringUtil.notNullize(plugin.getPluginId(), "com.intellij"); - for (ExtensionPoints points : plugin.getExtensionPoints()) { - for (ExtensionPoint point : points.getExtensionPoints()) { - registerExtensionPoint(registrar, point, epPrefix, pluginId); - } - } - } - } - - @Override - public boolean supportsStubs() { - return false; - } - private static Set getVisiblePlugins(IdeaPlugin ideaPlugin) { Set result = ContainerUtil.newHashSet(); MultiMap byId = getPluginMap(ideaPlugin.getManager().getProject()); @@ -385,11 +309,10 @@ public class ExtensionDomExtender extends DomExtender { return PsiTypesUtil.getPsiClass(elementType); } - public static Collection getDependencies(IdeaPlugin ideaPlugin) { Set result = new HashSet(); - result.add(PluginManager.CORE_PLUGIN_ID); + result.add(PluginManagerCore.CORE_PLUGIN_ID); for (Dependency dependency : ideaPlugin.getDependencies()) { ContainerUtil.addIfNotNull(dependency.getStringValue(), result); @@ -422,10 +345,84 @@ public class ExtensionDomExtender extends DomExtender { return result; } + public void registerExtensions(@NotNull final Extensions extensions, @NotNull final DomExtensionsRegistrar registrar) { + final XmlElement xmlElement = extensions.getXmlElement(); + if (xmlElement == null) return; + + IdeaPlugin ideaPlugin = extensions.getParentOfType(IdeaPlugin.class, true); + + if (ideaPlugin == null) return; + + String epPrefix = extensions.getEpPrefix(); + for (IdeaPlugin plugin : getVisiblePlugins(ideaPlugin)) { + final String pluginId = StringUtil.notNullize(plugin.getPluginId(), "com.intellij"); + for (ExtensionPoints points : plugin.getExtensionPoints()) { + for (ExtensionPoint point : points.getExtensionPoints()) { + registerExtensionPoint(registrar, point, epPrefix, pluginId); + } + } + } + } + + @Override + public boolean supportsStubs() { + return false; + } + interface SimpleTagValue extends DomElement { @SuppressWarnings("UnusedDeclaration") @TagValue String getTagValue(); } + private static class MyRequired implements Required { + @Override + public boolean value() { + return true; + } + + @Override + public boolean nonEmpty() { + return true; + } + + @Override + public boolean identifier() { + return false; + } + + @Override + public Class annotationType() { + return Required.class; + } + } + + private static class MyExtendClass extends ExtendClassImpl { + private final String myInterfaceName; + + private MyExtendClass(String interfaceName) { + myInterfaceName = interfaceName; + } + + @Override + public boolean allowAbstract() { + return false; + } + + @Override + public boolean allowInterface() { + return false; + } + + @Override + public boolean allowEnum() { + return false; + } + + @Override + public String value() { + return myInterfaceName; + } + } + } diff --git a/plugins/devkit/src/dom/impl/PluginXmlDomFileDescription.java b/plugins/devkit/src/dom/impl/PluginXmlDomFileDescription.java index b3d7fda2c19a..091c172d0a60 100644 --- a/plugins/devkit/src/dom/impl/PluginXmlDomFileDescription.java +++ b/plugins/devkit/src/dom/impl/PluginXmlDomFileDescription.java @@ -15,109 +15,18 @@ */ package org.jetbrains.idea.devkit.dom.impl; -import com.intellij.codeInspection.ProblemHighlightType; import com.intellij.icons.AllIcons; -import com.intellij.lang.annotation.Annotation; -import com.intellij.lang.annotation.HighlightSeverity; import com.intellij.openapi.util.Iconable; -import com.intellij.psi.PsiClass; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiField; -import com.intellij.util.xml.DomElement; import com.intellij.util.xml.DomFileDescription; -import com.intellij.util.xml.DomUtil; -import com.intellij.util.xml.GenericAttributeValue; -import com.intellij.util.xml.highlighting.DomElementAnnotationHolder; -import com.intellij.util.xml.highlighting.DomElementsAnnotator; -import com.intellij.util.xml.reflect.DomAttributeChildDescription; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.idea.devkit.dom.*; +import org.jetbrains.idea.devkit.dom.IdeaPlugin; import javax.swing.*; -import java.util.List; /** * @author mike */ public class PluginXmlDomFileDescription extends DomFileDescription { - private static final DomElementsAnnotator ANNOTATOR = new DomElementsAnnotator() { - @Override - public void annotate(DomElement element, DomElementAnnotationHolder holder) { - if (element instanceof Extension) { - annotateExtension((Extension)element, holder); - } - else if (element instanceof Vendor) { - annotateVendor((Vendor)element, holder); - } - else if (element instanceof IdeaVersion) { - annotateIdeaVersion((IdeaVersion)element, holder); - } - else if (element instanceof Extensions) { - annotateExtensions((Extensions)element, holder); - } - } - - private void annotateExtensions(Extensions extensions, DomElementAnnotationHolder holder) { - final GenericAttributeValue xmlnsAttribute = extensions.getXmlns(); - if (!DomUtil.hasXml(xmlnsAttribute)) return; - - final Annotation annotation = holder.createAnnotation(xmlnsAttribute, - HighlightSeverity.WARNING, - "Use defaultExtensionNs instead"); - annotation.setHighlightType(ProblemHighlightType.LIKE_DEPRECATED); - } - - private void annotateIdeaVersion(IdeaVersion ideaVersion, DomElementAnnotationHolder holder) { - highlightNotUsedAnymore(ideaVersion.getMin(), holder); - highlightNotUsedAnymore(ideaVersion.getMax(), holder); - } - - private void annotateExtension(Extension extension, DomElementAnnotationHolder holder) { - final ExtensionPoint extensionPoint = extension.getExtensionPoint(); - if (extensionPoint == null) return; - final GenericAttributeValue interfaceAttribute = extensionPoint.getInterface(); - if (DomUtil.hasXml(interfaceAttribute)) { - final PsiClass value = interfaceAttribute.getValue(); - if (value != null && value.isDeprecated()) { - final Annotation annotation = holder.createAnnotation(extension, HighlightSeverity.WARNING, "Deprecated EP"); - annotation.setHighlightType(ProblemHighlightType.LIKE_DEPRECATED); - return; - } - } - - final List descriptions = extension.getGenericInfo().getAttributeChildrenDescriptions(); - for (DomAttributeChildDescription attributeDescription : descriptions) { - final GenericAttributeValue attributeValue = attributeDescription.getDomAttributeValue(extension); - if (attributeValue == null || !DomUtil.hasXml(attributeValue)) continue; - - final PsiElement declaration = attributeDescription.getDeclaration(extension.getManager().getProject()); - if (declaration instanceof PsiField) { - PsiField psiField = (PsiField)declaration; - if (psiField.isDeprecated()) { - final Annotation annotation = holder.createAnnotation(attributeValue, HighlightSeverity.WARNING, - "Deprecated attribute '" + attributeDescription.getName() + "'"); - annotation.setHighlightType(ProblemHighlightType.LIKE_DEPRECATED); - } - } - } - } - - private void annotateVendor(Vendor vendor, DomElementAnnotationHolder holder) { - highlightNotUsedAnymore(vendor.getLogo(), holder); - } - - private void highlightNotUsedAnymore(GenericAttributeValue attributeValue, - DomElementAnnotationHolder holder) { - if (!DomUtil.hasXml(attributeValue)) return; - - final Annotation annotation = holder.createAnnotation(attributeValue, - HighlightSeverity.WARNING, - "Not used anymore"); - annotation.setHighlightType(ProblemHighlightType.LIKE_DEPRECATED); - } - }; - public PluginXmlDomFileDescription() { super(IdeaPlugin.class, "idea-plugin"); } @@ -127,12 +36,6 @@ public class PluginXmlDomFileDescription extends DomFileDescription return AllIcons.Nodes.Plugin; } - @Nullable - @Override - public DomElementsAnnotator createAnnotator() { - return ANNOTATOR; - } - @Override public boolean hasStubs() { return true; diff --git a/plugins/devkit/src/inspections/PluginXmlDomInspection.java b/plugins/devkit/src/inspections/PluginXmlDomInspection.java index b160cbf5dbd1..af691d283e0e 100644 --- a/plugins/devkit/src/inspections/PluginXmlDomInspection.java +++ b/plugins/devkit/src/inspections/PluginXmlDomInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,12 +15,28 @@ */ package org.jetbrains.idea.devkit.inspections; +import com.intellij.codeInspection.ProblemHighlightType; +import com.intellij.openapi.module.Module; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiField; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.xml.DomElement; +import com.intellij.util.xml.DomUtil; +import com.intellij.util.xml.GenericAttributeValue; import com.intellij.util.xml.highlighting.BasicDomElementsInspection; +import com.intellij.util.xml.highlighting.DomElementAnnotationHolder; +import com.intellij.util.xml.highlighting.DomHighlightingHelper; +import com.intellij.util.xml.highlighting.RemoveDomElementQuickFix; +import com.intellij.util.xml.reflect.DomAttributeChildDescription; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.idea.devkit.DevKitBundle; -import org.jetbrains.idea.devkit.dom.IdeaPlugin; +import org.jetbrains.idea.devkit.dom.*; +import org.jetbrains.idea.devkit.util.PsiUtil; + +import java.util.List; /** * @author mike @@ -47,4 +63,110 @@ public class PluginXmlDomInspection extends BasicDomElementsInspection definedEps = DomUtil.getDefinedChildrenOfType(extensions, Extension.class, true, false); + for (Extension extension : definedEps) { + final ExtensionPoint extensionPoint = extension.getExtensionPoint(); + if (extensionPoint == null) continue; + if ("com.intellij.errorHandler".equals(extensionPoint.getEffectiveQualifiedName())) { + return; + } + } + } + + holder.createProblem(DomUtil.getFileElement(ideaPlugin), + "JetBrains plugin should provide "); + } + + private static void annotateExtensions(Extensions extensions, DomElementAnnotationHolder holder) { + final GenericAttributeValue xmlnsAttribute = extensions.getXmlns(); + if (!DomUtil.hasXml(xmlnsAttribute)) return; + + holder.createProblem(xmlnsAttribute, + ProblemHighlightType.LIKE_DEPRECATED, + "Use defaultExtensionNs instead", null).highlightWholeElement(); + } + + private static void annotateIdeaVersion(IdeaVersion ideaVersion, DomElementAnnotationHolder holder) { + highlightNotUsedAnymore(ideaVersion.getMin(), holder); + highlightNotUsedAnymore(ideaVersion.getMax(), holder); + } + + private static void annotateExtension(Extension extension, DomElementAnnotationHolder holder) { + final ExtensionPoint extensionPoint = extension.getExtensionPoint(); + if (extensionPoint == null) return; + final GenericAttributeValue interfaceAttribute = extensionPoint.getInterface(); + if (DomUtil.hasXml(interfaceAttribute)) { + final PsiClass value = interfaceAttribute.getValue(); + if (value != null && value.isDeprecated()) { + holder.createProblem(extension, ProblemHighlightType.LIKE_DEPRECATED, "Deprecated EP", null); + return; + } + } + + final List descriptions = extension.getGenericInfo().getAttributeChildrenDescriptions(); + for (DomAttributeChildDescription attributeDescription : descriptions) { + final GenericAttributeValue attributeValue = attributeDescription.getDomAttributeValue(extension); + if (attributeValue == null || !DomUtil.hasXml(attributeValue)) continue; + + final PsiElement declaration = attributeDescription.getDeclaration(extension.getManager().getProject()); + if (declaration instanceof PsiField) { + PsiField psiField = (PsiField)declaration; + if (psiField.isDeprecated()) { + holder.createProblem(attributeValue, ProblemHighlightType.LIKE_DEPRECATED, + "Deprecated attribute '" + attributeDescription.getName() + "'", + null) + .highlightWholeElement(); + } + } + } + } + + private static void annotateVendor(Vendor vendor, DomElementAnnotationHolder holder) { + highlightNotUsedAnymore(vendor.getLogo(), holder); + } + + private static void highlightNotUsedAnymore(GenericAttributeValue attributeValue, + DomElementAnnotationHolder holder) { + if (!DomUtil.hasXml(attributeValue)) return; + + holder.createProblem(attributeValue, + ProblemHighlightType.LIKE_DEPRECATED, + "Not used anymore", + null, new RemoveDomElementQuickFix(attributeValue)) + .highlightWholeElement(); + } } diff --git a/plugins/devkit/src/navigation/DescriptionTypeRelatedItemLineMarkerProvider.java b/plugins/devkit/src/navigation/DescriptionTypeRelatedItemLineMarkerProvider.java index 1d759b6a8669..a5c4fba2db15 100644 --- a/plugins/devkit/src/navigation/DescriptionTypeRelatedItemLineMarkerProvider.java +++ b/plugins/devkit/src/navigation/DescriptionTypeRelatedItemLineMarkerProvider.java @@ -16,7 +16,6 @@ package org.jetbrains.idea.devkit.navigation; import com.intellij.codeInsight.daemon.RelatedItemLineMarkerInfo; -import com.intellij.codeInsight.daemon.RelatedItemLineMarkerProvider; import com.intellij.codeInsight.navigation.NavigationGutterIconBuilder; import com.intellij.icons.AllIcons; import com.intellij.navigation.GotoRelatedItem; @@ -24,8 +23,10 @@ import com.intellij.openapi.editor.markup.GutterIconRenderer; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleUtilCore; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.psi.*; -import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiDirectory; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; import com.intellij.psi.util.InheritanceUtil; import com.intellij.util.NotNullFunction; import com.intellij.util.containers.ContainerUtil; @@ -41,7 +42,7 @@ import java.util.Collections; import java.util.Comparator; import java.util.List; -public class DescriptionTypeRelatedItemLineMarkerProvider extends RelatedItemLineMarkerProvider { +public class DescriptionTypeRelatedItemLineMarkerProvider extends DevkitRelatedLineMarkerProviderBase { private static final NotNullFunction> CONVERTER = new NotNullFunction>() { @@ -74,11 +75,6 @@ public class DescriptionTypeRelatedItemLineMarkerProvider extends RelatedItemLin Module module = ModuleUtilCore.findModuleForPsiElement(psiClass); if (module == null) return; - final GlobalSearchScope scope = GlobalSearchScope.moduleRuntimeScope(module, false); - final PsiClass actionClass = JavaPsiFacade.getInstance(psiClass.getProject()) - .findClass(DescriptionType.INSPECTION.getClassName(), scope); - if (actionClass == null) return; - PsiElement highlightingElement = psiClass.getNameIdentifier(); if (highlightingElement == null) return; diff --git a/plugins/devkit/src/navigation/DevkitRelatedLineMarkerProviderBase.java b/plugins/devkit/src/navigation/DevkitRelatedLineMarkerProviderBase.java new file mode 100644 index 000000000000..e81c38ec6936 --- /dev/null +++ b/plugins/devkit/src/navigation/DevkitRelatedLineMarkerProviderBase.java @@ -0,0 +1,44 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.idea.devkit.navigation; + +import com.intellij.codeInsight.daemon.RelatedItemLineMarkerInfo; +import com.intellij.codeInsight.daemon.RelatedItemLineMarkerProvider; +import com.intellij.psi.PsiElement; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.idea.devkit.util.PsiUtil; + +import java.util.Collection; +import java.util.List; + +/** + * Do not process when current project is not a Plugin project. + */ +public abstract class DevkitRelatedLineMarkerProviderBase extends RelatedItemLineMarkerProvider { + + @Override + public void collectNavigationMarkers(List elements, + Collection result, + boolean forNavigation) { + final PsiElement psiElement = ContainerUtil.getFirstItem(elements); + if (psiElement == null || + !PsiUtil.isPluginProject(psiElement.getProject())) { + return; + } + + super.collectNavigationMarkers(elements, result, forNavigation); + } +} diff --git a/plugins/devkit/src/navigation/ExtensionPointDeclarationRelatedItemLineMarkerProvider.java b/plugins/devkit/src/navigation/ExtensionPointDeclarationRelatedItemLineMarkerProvider.java index 8ecdbac61291..d71b54a540f6 100644 --- a/plugins/devkit/src/navigation/ExtensionPointDeclarationRelatedItemLineMarkerProvider.java +++ b/plugins/devkit/src/navigation/ExtensionPointDeclarationRelatedItemLineMarkerProvider.java @@ -16,7 +16,6 @@ package org.jetbrains.idea.devkit.navigation; import com.intellij.codeInsight.daemon.RelatedItemLineMarkerInfo; -import com.intellij.codeInsight.daemon.RelatedItemLineMarkerProvider; import com.intellij.codeInsight.navigation.NavigationGutterIconBuilder; import com.intellij.icons.AllIcons; import com.intellij.navigation.GotoRelatedItem; @@ -37,7 +36,7 @@ import java.util.Collection; import java.util.Collections; import java.util.List; -public class ExtensionPointDeclarationRelatedItemLineMarkerProvider extends RelatedItemLineMarkerProvider { +public class ExtensionPointDeclarationRelatedItemLineMarkerProvider extends DevkitRelatedLineMarkerProviderBase { private static final NotNullFunction> CONVERTER = new NotNullFunction>() { diff --git a/plugins/devkit/src/projectRoots/IdeaJdk.java b/plugins/devkit/src/projectRoots/IdeaJdk.java index 1539a893dbad..a5fabf928c5d 100644 --- a/plugins/devkit/src/projectRoots/IdeaJdk.java +++ b/plugins/devkit/src/projectRoots/IdeaJdk.java @@ -86,6 +86,15 @@ public class IdeaJdk extends JavaDependentSdkType implements JavaSdkType { return PathManager.getHomePath().replace(File.separatorChar, '/'); } + @Override + public String adjustSelectedSdkHome(String homePath) { + if (SystemInfo.isMac) { + File home = new File(homePath, "Contents"); + if (home.exists()) return home.getPath(); + } + return super.adjustSelectedSdkHome(homePath); + } + public boolean isValidSdkHome(String path) { if (isFromIDEAProject(path)) { return true; @@ -153,6 +162,9 @@ public class IdeaJdk extends JavaDependentSdkType implements JavaSdkType { else if (new File(sdkHome, "license/AppCode_license.txt").exists()) { productName = "AppCode "; } + else if (new File(sdkHome, "license/CLion_Preview_License.txt").exists()) { + productName = "CLion "; + } else { productName = "IDEA "; } diff --git a/plugins/devkit/src/run/PluginRunConfigurationEditor.java b/plugins/devkit/src/run/PluginRunConfigurationEditor.java index f65bd28a8fc4..9749d3adca18 100644 --- a/plugins/devkit/src/run/PluginRunConfigurationEditor.java +++ b/plugins/devkit/src/run/PluginRunConfigurationEditor.java @@ -15,6 +15,7 @@ */ package org.jetbrains.idea.devkit.run; +import com.intellij.application.options.ModulesComboBox; import com.intellij.execution.ExecutionBundle; import com.intellij.execution.configurations.LogFileOptions; import com.intellij.execution.ui.AlternativeJREPanel; @@ -24,7 +25,6 @@ import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.options.SettingsEditor; import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.roots.ModuleRootManager; -import com.intellij.application.options.ModulesComboBox; import com.intellij.openapi.ui.LabeledComponent; import com.intellij.ui.PanelWithAnchor; import com.intellij.ui.RawCommandLineEditor; @@ -46,7 +46,6 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; import java.io.IOException; -import java.util.ArrayList; public class PluginRunConfigurationEditor extends SettingsEditor implements PanelWithAnchor { private final ModulesComboBox myModules = new ModulesComboBox(); @@ -54,7 +53,7 @@ public class PluginRunConfigurationEditor extends SettingsEditor myVMParameters = new LabeledComponent(); private final LabeledComponent myProgramParameters = new LabeledComponent(); private JComponent anchor; - private AlternativeJREPanel myAlternativeJREPanel = new AlternativeJREPanel(); + private final AlternativeJREPanel myAlternativeJREPanel = new AlternativeJREPanel(); @NonNls private final JCheckBox myShowLogs = new JCheckBox(DevKitBundle.message("show.smth", "idea.log")); @@ -65,12 +64,14 @@ public class PluginRunConfigurationEditor extends SettingsEditor logFiles = prc.getLogFiles(); - for (LogFileOptions logFile: logFiles) { + for (LogFileOptions logFile: prc.getLogFiles()) { logFile.setEnable(show); } } private static boolean isShow(PluginRunConfiguration prc){ - final ArrayList logFiles = prc.getLogFiles(); - for (LogFileOptions logFile : logFiles) { + for (LogFileOptions logFile : prc.getLogFiles()) { if (logFile.isEnabled()) return true; } return false; } + @Override public void resetEditorFrom(PluginRunConfiguration prc) { myModules.setSelectedModule(prc.getModule()); getVMParameters().setText(prc.VM_PARAMETERS); @@ -135,6 +135,7 @@ public class PluginRunConfigurationEditor extends SettingsEditor collectRelativeLocations(Project project, VirtualFile file) { + if (DumbService.isDumb(project)) return Collections.emptyList(); + final List locations = new ArrayList(); final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(project).getFileIndex(); if (fileIndex.isInContent(file) && !fileIndex.isInSource(file) && !fileIndex.isInLibraryClasses(file)) { diff --git a/plugins/devkit/src/util/PsiUtil.java b/plugins/devkit/src/util/PsiUtil.java index 8d093acb8017..2be3719c214a 100644 --- a/plugins/devkit/src/util/PsiUtil.java +++ b/plugins/devkit/src/util/PsiUtil.java @@ -125,6 +125,11 @@ public class PsiUtil { return flag; } + public static boolean isPluginProject(Project project) { + return JavaPsiFacade.getInstance(project).findClass(IDE_PROJECT_MARKER_CLASS, + GlobalSearchScope.allScope(project)) != null; + } + private static boolean isIntelliJBasedDir(VirtualFile baseDir) { if (baseDir == null) { return false; diff --git a/plugins/devkit/testData/codeInsight/ExtensionsHighlighting.xml b/plugins/devkit/testData/codeInsight/ExtensionsHighlighting.xml index 9486bf492542..d0e7a9b31296 100644 --- a/plugins/devkit/testData/codeInsight/ExtensionsHighlighting.xml +++ b/plugins/devkit/testData/codeInsight/ExtensionsHighlighting.xml @@ -14,7 +14,7 @@ java.util.concurrent.TimeUnit"/> java.lang.String"/> - + <myPlugin.deprecatedEP implementation="foo.MyDeprecatedEPImpl"/> diff --git a/plugins/devkit/testData/codeInsight/deprecatedAttributes.xml b/plugins/devkit/testData/codeInsight/deprecatedAttributes.xml index d77d1c0732df..e9f52b6242e4 100644 --- a/plugins/devkit/testData/codeInsight/deprecatedAttributes.xml +++ b/plugins/devkit/testData/codeInsight/deprecatedAttributes.xml @@ -1,13 +1,13 @@ com.intellij.myPlugin - logo="deprecated"/> + logo="deprecated"/> min="deprecated" - max="deprecated" + min="deprecated" + max="deprecated" /> - xmlns="com.intellij"/> + xmlns="com.intellij"/> \ No newline at end of file diff --git a/plugins/devkit/testData/codeInsight/deprecatedExtensionAttribute.xml b/plugins/devkit/testData/codeInsight/deprecatedExtensionAttribute.xml index 5466b7ee4485..0400e73a0d02 100644 --- a/plugins/devkit/testData/codeInsight/deprecatedExtensionAttribute.xml +++ b/plugins/devkit/testData/codeInsight/deprecatedExtensionAttribute.xml @@ -6,7 +6,7 @@ - old="java.lang.Runnable" attr="value"/> + old="java.lang.Runnable" attr="value"/> \ No newline at end of file diff --git a/plugins/devkit/testData/pluginXmlDomStubs/pluginWithXInclude-extensionPoints.xml b/plugins/devkit/testData/pluginXmlDomStubs/pluginWithXInclude-extensionPoints.xml new file mode 100644 index 000000000000..813122a13176 --- /dev/null +++ b/plugins/devkit/testData/pluginXmlDomStubs/pluginWithXInclude-extensionPoints.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/plugins/devkit/testData/pluginXmlDomStubs/pluginWithXInclude-main.xml b/plugins/devkit/testData/pluginXmlDomStubs/pluginWithXInclude-main.xml new file mode 100644 index 000000000000..280b9b93c555 --- /dev/null +++ b/plugins/devkit/testData/pluginXmlDomStubs/pluginWithXInclude-main.xml @@ -0,0 +1,10 @@ + + + com.intellij + + + + + + + \ No newline at end of file diff --git a/plugins/devkit/testData/pluginXmlDomStubs/pluginWithXInclude.xml b/plugins/devkit/testData/pluginXmlDomStubs/pluginWithXInclude.xml new file mode 100644 index 000000000000..849086d4989f --- /dev/null +++ b/plugins/devkit/testData/pluginXmlDomStubs/pluginWithXInclude.xml @@ -0,0 +1,15 @@ + + + foo + + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/devkit/testSources/codeInsight/PluginXmlDomStubsTest.java b/plugins/devkit/testSources/codeInsight/PluginXmlDomStubsTest.java index 685d926b1b2a..100b956bdb4c 100644 --- a/plugins/devkit/testSources/codeInsight/PluginXmlDomStubsTest.java +++ b/plugins/devkit/testSources/codeInsight/PluginXmlDomStubsTest.java @@ -44,6 +44,13 @@ public class PluginXmlDomStubsTest extends DomStubTest { ); } + public void testXInclude() throws Exception { + prepareFile("pluginWithXInclude-extensionPoints.xml"); + prepareFile("pluginWithXInclude-main.xml"); + prepareFile("pluginWithXInclude.xml"); + myFixture.testHighlighting("pluginWithXInclude.xml"); + } + @Override protected String getBasePath() { return PluginPathManager.getPluginHomePathRelative("devkit") + "/testData/pluginXmlDomStubs"; diff --git a/plugins/devkit/testSources/navigation/DescriptionTypeRelatedItemLineMarkerProviderTest.java b/plugins/devkit/testSources/navigation/DescriptionTypeRelatedItemLineMarkerProviderTest.java index f42a62993db9..c885d0a09212 100644 --- a/plugins/devkit/testSources/navigation/DescriptionTypeRelatedItemLineMarkerProviderTest.java +++ b/plugins/devkit/testSources/navigation/DescriptionTypeRelatedItemLineMarkerProviderTest.java @@ -22,6 +22,7 @@ import com.intellij.openapi.application.PluginPathManager; import com.intellij.testFramework.TestDataPath; import com.intellij.testFramework.builders.JavaModuleFixtureBuilder; import com.intellij.testFramework.fixtures.JavaCodeInsightFixtureTestCase; +import com.intellij.ui.components.JBList; import com.intellij.util.PathUtil; import java.util.Collection; @@ -39,6 +40,8 @@ public class DescriptionTypeRelatedItemLineMarkerProviderTest extends JavaCodeIn protected void tuneFixture(JavaModuleFixtureBuilder moduleBuilder) throws Exception { String pathForClass = PathUtil.getJarPathForClass(LocalInspectionEP.class); moduleBuilder.addLibrary("lang-api", pathForClass); + String platformApiJar = PathUtil.getJarPathForClass(JBList.class); + moduleBuilder.addLibrary("platform-api", platformApiJar); } public void testInspectionDescription() { diff --git a/plugins/devkit/testSources/navigation/ExtensionPointDeclarationRelatedItemLineMarkerProviderTest.java b/plugins/devkit/testSources/navigation/ExtensionPointDeclarationRelatedItemLineMarkerProviderTest.java index 220c1959fd36..6b21d8455152 100644 --- a/plugins/devkit/testSources/navigation/ExtensionPointDeclarationRelatedItemLineMarkerProviderTest.java +++ b/plugins/devkit/testSources/navigation/ExtensionPointDeclarationRelatedItemLineMarkerProviderTest.java @@ -22,6 +22,7 @@ import com.intellij.openapi.extensions.ExtensionPointName; import com.intellij.testFramework.TestDataPath; import com.intellij.testFramework.builders.JavaModuleFixtureBuilder; import com.intellij.testFramework.fixtures.JavaCodeInsightFixtureTestCase; +import com.intellij.ui.components.JBList; import com.intellij.util.PathUtil; @TestDataPath("$CONTENT_ROOT/testData/navigation/extensionPointDeclaration") @@ -34,8 +35,10 @@ public class ExtensionPointDeclarationRelatedItemLineMarkerProviderTest extends @Override protected void tuneFixture(JavaModuleFixtureBuilder moduleBuilder) throws Exception { - String pathForClass = PathUtil.getJarPathForClass(ExtensionPointName.class); - moduleBuilder.addLibrary("extensions", pathForClass); + String extensionsJar = PathUtil.getJarPathForClass(ExtensionPointName.class); + moduleBuilder.addLibrary("extensions", extensionsJar); + String platformApiJar = PathUtil.getJarPathForClass(JBList.class); + moduleBuilder.addLibrary("platform-api", platformApiJar); } public void testMyStringEP() { diff --git a/plugins/git4idea/git4idea.iml b/plugins/git4idea/git4idea.iml index 53ee70f60eca..c109bd529797 100644 --- a/plugins/git4idea/git4idea.iml +++ b/plugins/git4idea/git4idea.iml @@ -54,7 +54,7 @@ - + diff --git a/plugins/git4idea/src/git4idea/GitUtil.java b/plugins/git4idea/src/git4idea/GitUtil.java index e96d52de8ea0..3f344482f5c6 100644 --- a/plugins/git4idea/src/git4idea/GitUtil.java +++ b/plugins/git4idea/src/git4idea/GitUtil.java @@ -1016,6 +1016,7 @@ public class GitUtil { @NotNull public static String cleanupErrorPrefixes(@NotNull String msg) { final String[] PREFIXES = { "fatal:", "error:" }; + msg = msg.trim(); for (String prefix : PREFIXES) { if (msg.startsWith(prefix)) { return msg.substring(prefix.length()).trim(); diff --git a/plugins/git4idea/src/git4idea/annotate/GitAnnotationProvider.java b/plugins/git4idea/src/git4idea/annotate/GitAnnotationProvider.java index 5ebdefd74b3a..39241b3660f8 100644 --- a/plugins/git4idea/src/git4idea/annotate/GitAnnotationProvider.java +++ b/plugins/git4idea/src/git4idea/annotate/GitAnnotationProvider.java @@ -101,7 +101,6 @@ public class GitAnnotationProvider implements AnnotationProvider, VcsCacheableAn command.run(); } if (exception[0] != null) { - LOG.warn(exception[0]); throw new VcsException("Failed to annotate: " + exception[0], exception[0]); } return annotation[0]; diff --git a/plugins/git4idea/src/git4idea/branch/GitBranchUtil.java b/plugins/git4idea/src/git4idea/branch/GitBranchUtil.java index 6e178b9cbc0f..2504c345b559 100644 --- a/plugins/git4idea/src/git4idea/branch/GitBranchUtil.java +++ b/plugins/git4idea/src/git4idea/branch/GitBranchUtil.java @@ -314,6 +314,9 @@ public class GitBranchUtil { else if (branchName.startsWith(GitBranch.REFS_REMOTES_PREFIX)) { return branchName.substring(GitBranch.REFS_REMOTES_PREFIX.length()); } + else if (branchName.startsWith(GitTag.REFS_TAGS_PREFIX)) { + return branchName.substring(GitTag.REFS_TAGS_PREFIX.length()); + } return branchName; } diff --git a/plugins/git4idea/src/git4idea/branch/GitBranchWorker.java b/plugins/git4idea/src/git4idea/branch/GitBranchWorker.java index a6ae9d288dee..bcf993b21723 100644 --- a/plugins/git4idea/src/git4idea/branch/GitBranchWorker.java +++ b/plugins/git4idea/src/git4idea/branch/GitBranchWorker.java @@ -19,11 +19,15 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Couple; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.changes.Change; +import com.intellij.openapi.vfs.VfsUtil; +import com.intellij.util.containers.ContainerUtil; import git4idea.GitCommit; import git4idea.GitExecutionException; +import git4idea.GitLocalBranch; import git4idea.GitPlatformFacade; import git4idea.changes.GitChangeUtils; import git4idea.commands.Git; @@ -61,15 +65,27 @@ public final class GitBranchWorker { myUiHandler = uiHandler; } - public void checkoutNewBranch(@NotNull final String name, @NotNull final List repositories) { + public void checkoutNewBranch(@NotNull final String name, @NotNull List repositories) { updateInfo(repositories); - new GitCheckoutNewBranchOperation(myProject, myFacade, myGit, myUiHandler, repositories, name).execute(); + repositories = ContainerUtil.filter(repositories, new Condition() { + @Override + public boolean value(GitRepository repository) { + GitLocalBranch currentBranch = repository.getCurrentBranch(); + return currentBranch == null || !currentBranch.getName().equals(name); + } + }); + if (!repositories.isEmpty()) { + new GitCheckoutNewBranchOperation(myProject, myFacade, myGit, myUiHandler, repositories, name).execute(); + } + else { + LOG.error("Creating new branch the same as current in all repositories: " + name); + } } public void createNewTag(@NotNull final String name, @NotNull final String reference, @NotNull final List repositories) { - updateInfo(repositories); for (GitRepository repository : repositories) { myGit.createNewTag(repository, name, null, reference); + VfsUtil.markDirtyAndRefresh(true, true, false, repository.getGitDir()); } } diff --git a/plugins/git4idea/src/git4idea/checkin/GitCheckinHandlerFactory.java b/plugins/git4idea/src/git4idea/checkin/GitCheckinHandlerFactory.java index 8bfc52ca261a..c31966176ae0 100644 --- a/plugins/git4idea/src/git4idea/checkin/GitCheckinHandlerFactory.java +++ b/plugins/git4idea/src/git4idea/checkin/GitCheckinHandlerFactory.java @@ -15,12 +15,14 @@ */ package git4idea.checkin; +import com.intellij.CommonBundle; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.Couple; @@ -281,7 +283,7 @@ public class GitCheckinHandlerFactory extends VcsCheckinHandlerFactory { private ReturnResult warnAboutDetachedHeadIfNeeded() { // Warning: commit on a detached HEAD DetachedRoot detachedRoot = getDetachedRoot(); - if (detachedRoot == null) { + if (detachedRoot == null || !GitVcsSettings.getInstance(myProject).warnAboutDetachedHead()) { return ReturnResult.COMMIT; } @@ -303,10 +305,38 @@ public class GitCheckinHandlerFactory extends VcsCheckinHandlerFactory { readMore("http://gitolite.com/detached-head.html", "Read more about detached HEAD"); } - final int choice = Messages.showOkCancelDialog(myPanel.getComponent(), XmlStringUtil.wrapInHtml(message), title, - "Cancel", "Commit", - Messages.getWarningIcon()); - if (choice != Messages.OK) { + DialogWrapper.DoNotAskOption dontAskAgain = new DialogWrapper.DoNotAskOption() { + @Override + public boolean isToBeShown() { + return true; + } + + @Override + public void setToBeShown(boolean toBeShown, int exitCode) { + if (exitCode == Messages.OK) { + GitVcsSettings.getInstance(myProject).setWarnAboutDetachedHead(toBeShown); + } + } + + @Override + public boolean canBeHidden() { + return true; + } + + @Override + public boolean shouldSaveOptionsOnCancel() { + return false; + } + + @NotNull + @Override + public String getDoNotShowMessage() { + return "Don't warn again"; + } + }; + int choice = Messages.showOkCancelDialog(myProject, XmlStringUtil.wrapInHtml(message), title, "Commit", + CommonBundle.getCancelButtonText(), Messages.getWarningIcon(), dontAskAgain); + if (choice == Messages.OK) { return ReturnResult.COMMIT; } else { return ReturnResult.CLOSE_WINDOW; diff --git a/plugins/git4idea/src/git4idea/config/GitVcsPanel.form b/plugins/git4idea/src/git4idea/config/GitVcsPanel.form index 0f24aaf99d56..87d3578f7c0a 100644 --- a/plugins/git4idea/src/git4idea/config/GitVcsPanel.form +++ b/plugins/git4idea/src/git4idea/config/GitVcsPanel.form @@ -91,7 +91,7 @@ - + @@ -134,6 +134,15 @@ + + + + + + + + + diff --git a/plugins/git4idea/src/git4idea/config/GitVcsPanel.java b/plugins/git4idea/src/git4idea/config/GitVcsPanel.java index a8ebfe66140f..c8c2ad46a274 100644 --- a/plugins/git4idea/src/git4idea/config/GitVcsPanel.java +++ b/plugins/git4idea/src/git4idea/config/GitVcsPanel.java @@ -15,18 +15,17 @@ */ package git4idea.config; +import com.intellij.dvcs.branch.DvcsBranchSync; import com.intellij.openapi.application.ApplicationNamesInfo; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.ui.TextFieldWithBrowseButton; -import com.intellij.openapi.util.SystemInfo; import com.intellij.ui.components.JBCheckBox; import git4idea.GitVcs; import git4idea.i18n.GitBundle; import git4idea.repo.GitRepositoryManager; -import git4idea.ui.branch.GitBranchSyncSetting; import org.jetbrains.annotations.NotNull; import javax.swing.*; @@ -52,6 +51,7 @@ public class GitVcsPanel { private JBCheckBox mySyncBranchControl; private JCheckBox myAutoCommitOnCherryPick; private JBCheckBox myWarnAboutCrlf; + private JCheckBox myWarnAboutDetachedHead; public GitVcsPanel(@NotNull Project project) { myVcs = GitVcs.getInstance(project); @@ -119,9 +119,10 @@ public class GitVcsPanel { myGitField.setText(settings.getAppSettings().getPathToGit()); mySSHExecutableComboBox.setSelectedItem(settings.isIdeaSsh() ? IDEA_SSH : NATIVE_SSH); myAutoUpdateIfPushRejected.setSelected(settings.autoUpdateIfPushRejected()); - mySyncBranchControl.setSelected(settings.getSyncSetting() == GitBranchSyncSetting.SYNC); + mySyncBranchControl.setSelected(settings.getSyncSetting() == DvcsBranchSync.SYNC); myAutoCommitOnCherryPick.setSelected(settings.isAutoCommitOnCherryPick()); myWarnAboutCrlf.setSelected(settings.warnAboutCrlf()); + myWarnAboutDetachedHead.setSelected(settings.warnAboutDetachedHead()); } /** @@ -133,9 +134,10 @@ public class GitVcsPanel { return !settings.getAppSettings().getPathToGit().equals(getCurrentExecutablePath()) || (settings.isIdeaSsh() != IDEA_SSH.equals(mySSHExecutableComboBox.getSelectedItem())) || !settings.autoUpdateIfPushRejected() == myAutoUpdateIfPushRejected.isSelected() || - ((settings.getSyncSetting() == GitBranchSyncSetting.SYNC) != mySyncBranchControl.isSelected() || + ((settings.getSyncSetting() == DvcsBranchSync.SYNC) != mySyncBranchControl.isSelected() || settings.isAutoCommitOnCherryPick() != myAutoCommitOnCherryPick.isSelected() || - settings.warnAboutCrlf() != myWarnAboutCrlf.isSelected()); + settings.warnAboutCrlf() != myWarnAboutCrlf.isSelected() || + settings.warnAboutDetachedHead() != myWarnAboutDetachedHead.isSelected()); } /** @@ -151,9 +153,10 @@ public class GitVcsPanel { GitVcsApplicationSettings.SshExecutable.NATIVE_SSH); settings.setAutoUpdateIfPushRejected(myAutoUpdateIfPushRejected.isSelected()); - settings.setSyncSetting(mySyncBranchControl.isSelected() ? GitBranchSyncSetting.SYNC : GitBranchSyncSetting.DONT); + settings.setSyncSetting(mySyncBranchControl.isSelected() ? DvcsBranchSync.SYNC : DvcsBranchSync.DONT); settings.setAutoCommitOnCherryPick(myAutoCommitOnCherryPick.isSelected()); settings.setWarnAboutCrlf(myWarnAboutCrlf.isSelected()); + settings.setWarnAboutDetachedHead(myWarnAboutDetachedHead.isSelected()); } } diff --git a/plugins/git4idea/src/git4idea/config/GitVcsSettings.java b/plugins/git4idea/src/git4idea/config/GitVcsSettings.java index 1b5860631e59..7dd7f9b1004b 100644 --- a/plugins/git4idea/src/git4idea/config/GitVcsSettings.java +++ b/plugins/git4idea/src/git4idea/config/GitVcsSettings.java @@ -15,12 +15,16 @@ */ package git4idea.config; +import com.intellij.dvcs.branch.DvcsBranchSync; +import com.intellij.dvcs.branch.DvcsSyncBranchSettings; import com.intellij.lifecycle.PeriodicalTasksCloser; -import com.intellij.openapi.components.*; +import com.intellij.openapi.components.PersistentStateComponent; +import com.intellij.openapi.components.State; +import com.intellij.openapi.components.Storage; +import com.intellij.openapi.components.StoragePathMacros; import com.intellij.openapi.project.Project; import com.intellij.util.ArrayUtil; import git4idea.reset.GitResetMode; -import git4idea.ui.branch.GitBranchSyncSetting; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -32,8 +36,8 @@ import java.util.Map; /** * Git VCS settings */ -@State(name = "Git.Settings", roamingType = RoamingType.DISABLED, storages = {@Storage(file = StoragePathMacros.WORKSPACE_FILE)}) -public class GitVcsSettings implements PersistentStateComponent { +@State(name = "Git.Settings", storages = {@Storage(file = StoragePathMacros.WORKSPACE_FILE)}) +public class GitVcsSettings implements PersistentStateComponent, DvcsSyncBranchSettings { private static final int PREVIOUS_COMMIT_AUTHORS_LIMIT = 16; // Limit for previous commit authors @@ -56,12 +60,13 @@ public class GitVcsSettings implements PersistentStateComponent RECENT_BRANCH_BY_REPOSITORY = new HashMap(); public String RECENT_COMMON_BRANCH = null; public boolean AUTO_COMMIT_ON_CHERRY_PICK = false; public boolean WARN_ABOUT_CRLF = true; + public boolean WARN_ABOUT_DETACHED_HEAD = true; public GitResetMode RESET_MODE = null; } @@ -72,7 +77,7 @@ public class GitVcsSettings implements PersistentStateComponent readCommits(@NotNull final Project project, - @NotNull VirtualFile root, - @NotNull final Consumer userRegistry, - @NotNull List parameters) throws VcsException { - List collector = ContainerUtil.newArrayList(); - readCommits(project, root, userRegistry, parameters, new CollectConsumer(collector)); - return collector; + @Nullable + public static List readLastCommits(@NotNull Project project, + @NotNull final VirtualFile root, + @NotNull String... refs) + throws VcsException { + final VcsLogObjectsFactory factory = getObjectsFactoryWithDisposeCheck(project); + if (factory == null) { + return null; + } + + GitSimpleHandler h = new GitSimpleHandler(project, root, GitCommand.LOG); + GitLogParser parser = new GitLogParser(project, GitLogParser.NameStatus.NONE, HASH, PARENTS, COMMIT_TIME, SUBJECT, AUTHOR_NAME, + AUTHOR_EMAIL, RAW_BODY, COMMITTER_NAME, COMMITTER_EMAIL, AUTHOR_TIME); + + h.setSilent(true); + // git show can show either -p, or --name-status, or --name-only, but we need nothing, just details => using git log --no-walk + h.addParameters("--no-walk"); + h.addParameters(parser.getPretty(), "--encoding=UTF-8"); + h.addParameters(refs); + + String output = h.run(); + List records = parser.parse(output); + if (records.size() != refs.length) return null; + + return ContainerUtil.map(records, new Function() { + @Override + public VcsCommitMetadata fun(GitLogRecord record) { + return factory.createCommitMetadata(factory.createHash(record.getHash()), getParentHashes(factory, record), record.getCommitTime(), + root, record.getSubject(), record.getAuthorName(), record.getAuthorEmail(), + record.getFullMessage(), record.getCommitterName(), record.getCommitterEmail(), + record.getAuthorTimeStamp()); + } + }); } public static void readCommits(@NotNull final Project project, - @NotNull VirtualFile root, - @NotNull final Consumer userRegistry, - @NotNull List parameters, - @NotNull final Consumer commitConsumer) throws VcsException { + @NotNull final VirtualFile root, + @NotNull List parameters, + @NotNull final Consumer userConsumer, + @NotNull final Consumer refConsumer, + @NotNull final Consumer commitConsumer) throws VcsException { final VcsLogObjectsFactory factory = getObjectsFactoryWithDisposeCheck(project); if (factory == null) { return; @@ -538,11 +565,12 @@ public class GitHistoryUtils { final int COMMIT_BUFFER = 1000; GitLineHandler h = new GitLineHandler(project, root, GitCommand.LOG); final GitLogParser parser = new GitLogParser(project, GitLogParser.NameStatus.NONE, HASH, PARENTS, COMMIT_TIME, - AUTHOR_NAME, AUTHOR_EMAIL); + AUTHOR_NAME, AUTHOR_EMAIL, REF_NAMES); h.setStdoutSuppressed(true); h.addParameters(parser.getPretty(), "--encoding=UTF-8"); h.addParameters("--full-history"); h.addParameters("--date-order"); + h.addParameters("--decorate=full"); h.addParameters(parameters); h.endOptions(); @@ -568,7 +596,7 @@ public class GitHistoryUtils { afterParseRemainder = line.substring(recordEnd + 1); } if (afterParseRemainder != null && records.incrementAndGet() > COMMIT_BUFFER) { // null means can't parse now - List commits = parseCommit(parser, record, userRegistry, factory); + List commits = parseCommit(parser, record, userConsumer, refConsumer, factory, root); for (TimedVcsCommit commit : commits) { commitConsumer.consume(commit); } @@ -584,7 +612,7 @@ public class GitHistoryUtils { @Override public void processTerminated(int exitCode) { try { - List commits = parseCommit(parser, record, userRegistry, factory); + List commits = parseCommit(parser, record, userConsumer, refConsumer, factory, root); for (TimedVcsCommit commit : commits) { commitConsumer.consume(commit); } @@ -606,9 +634,12 @@ public class GitHistoryUtils { } @NotNull - private static List parseCommit(@NotNull GitLogParser parser, @NotNull StringBuilder record, + private static List parseCommit(@NotNull GitLogParser parser, + @NotNull StringBuilder record, @NotNull final Consumer userRegistry, - @NotNull final VcsLogObjectsFactory factory) { + @NotNull final Consumer refConsumer, + @NotNull final VcsLogObjectsFactory factory, + @NotNull final VirtualFile root) { List rec = parser.parse(record.toString()); return ContainerUtil.mapNotNull(rec, new Function() { @Override @@ -616,7 +647,11 @@ public class GitHistoryUtils { if (record == null) { return null; } - TimedVcsCommit commit = convert(record, factory); + Pair> pair = convert(record, factory, root); + TimedVcsCommit commit = pair.first; + for (VcsRef ref : pair.second) { + refConsumer.consume(ref); + } userRegistry.consume(factory.createUser(record.getAuthorName(), record.getAuthorEmail())); return commit; } @@ -624,9 +659,28 @@ public class GitHistoryUtils { } @NotNull - private static TimedVcsCommit convert(@NotNull GitLogRecord rec, @NotNull VcsLogObjectsFactory factory) { + private static Pair> convert(@NotNull GitLogRecord rec, + @NotNull VcsLogObjectsFactory factory, + @NotNull VirtualFile root) { + Hash hash = HashImpl.build(rec.getHash()); List parents = getParentHashes(factory, rec); - return factory.createTimedCommit(HashImpl.build(rec.getHash()), parents, rec.getCommitTime()); + TimedVcsCommit commit = factory.createTimedCommit(hash, parents, rec.getCommitTime()); + return Pair.create(commit, parseRefs(rec.getRefs(), hash, factory, root)); + } + + @NotNull + private static Collection parseRefs(@NotNull Collection refs, + @NotNull final Hash hash, + @NotNull final VcsLogObjectsFactory factory, + @NotNull final VirtualFile root) { + return ContainerUtil.mapNotNull(refs, new Function() { + @Override + public VcsRef fun(String refName) { + VcsRefType type = GitRefManager.getRefType(refName); + refName = GitBranchUtil.stripRefsPrefix(refName); + return factory.createRef(hash, refName, type, root); + } + }); } @Nullable @@ -749,23 +803,28 @@ public class GitHistoryUtils { } @NotNull - public static List loadMetadata(@NotNull Project project, @NotNull final VirtualFile root, - @NotNull String... parameters) throws VcsException { - + public static VcsLogProvider.DetailedLogData loadMetadata(@NotNull final Project project, + @NotNull final VirtualFile root, + final boolean withRefs, + String... params) throws VcsException { final VcsLogObjectsFactory factory = getObjectsFactoryWithDisposeCheck(project); if (factory == null) { - return Collections.emptyList(); + return LogDataImpl.empty(); } - return loadDetails(project, root, false, new NullableFunction() { - @Nullable - @Override - public VcsCommitMetadata fun(GitLogRecord record) { - return factory.createCommitMetadata(factory.createHash(record.getHash()), getParentHashes(factory, record), record.getCommitTime(), - root, record.getSubject(), record.getAuthorName(), record.getAuthorEmail(), - record.getFullMessage(), record.getCommitterName(), record.getCommitterEmail(), - record.getAuthorTimeStamp()); - } - }, parameters); + final Set refs = ContainerUtil.newHashSet(); + final List commits = + loadDetails(project, root, withRefs, false, new NullableFunction() { + @Nullable + @Override + public VcsCommitMetadata fun(GitLogRecord record) { + GitCommit commit = createCommit(project, root, record, factory); + if (withRefs) { + refs.addAll(parseRefs(record.getRefs(), commit.getId(), factory, root)); + } + return commit; + } + }, params); + return new LogDataImpl(refs, commits); } /** @@ -781,35 +840,39 @@ public class GitHistoryUtils { if (factory == null) { return Collections.emptyList(); } - return loadDetails(project, root, true, new NullableFunction() { + return loadDetails(project, root, false, true, new NullableFunction() { @Override @Nullable public GitCommit fun(GitLogRecord record) { - try { - return createCommit(project, root, record, factory); - } - catch (VcsException e) { - LOG.error(e); - return null; - } + return createCommit(project, root, record, factory); } }, parameters); } @NotNull - public static List loadDetails(@NotNull final Project project, @NotNull final VirtualFile root, boolean withChanges, - @NotNull NullableFunction converter, String... parameters) + public static List loadDetails(@NotNull final Project project, + @NotNull final VirtualFile root, + boolean withRefs, + boolean withChanges, + @NotNull NullableFunction converter, + String... parameters) throws VcsException { GitSimpleHandler h = new GitSimpleHandler(project, root, GitCommand.LOG); GitLogParser.NameStatus status = withChanges ? GitLogParser.NameStatus.STATUS : GitLogParser.NameStatus.NONE; - GitLogParser parser = new GitLogParser(project, status, HASH, COMMIT_TIME, AUTHOR_NAME, AUTHOR_TIME, - AUTHOR_EMAIL, COMMITTER_NAME, COMMITTER_EMAIL, PARENTS, SUBJECT, BODY, RAW_BODY); + GitLogParser.GitLogOption[] options = { HASH, COMMIT_TIME, AUTHOR_NAME, AUTHOR_TIME, AUTHOR_EMAIL, COMMITTER_NAME, COMMITTER_EMAIL, + PARENTS, SUBJECT, BODY, RAW_BODY }; + if (withRefs) { + options = ArrayUtil.append(options, REF_NAMES); + } + GitLogParser parser = new GitLogParser(project, status, options); h.setStdoutSuppressed(true); h.addParameters(parameters); h.addParameters(parser.getPretty(), "--encoding=UTF-8"); - h.addParameters("--full-history", "--sparse"); + if (withRefs) { + h.addParameters("--decorate=full"); + } if (withChanges) { - h.addParameters("-M", "--name-status"); + h.addParameters("-M", "--name-status", "-m"); } h.endOptions(); @@ -828,7 +891,7 @@ public class GitHistoryUtils { } private static GitCommit createCommit(@NotNull Project project, @NotNull VirtualFile root, @NotNull GitLogRecord record, - @NotNull VcsLogObjectsFactory factory) throws VcsException { + @NotNull VcsLogObjectsFactory factory) { List parents = getParentHashes(factory, record); return new GitCommit(project, HashImpl.build(record.getHash()), parents, record.getCommitTime(), root, record.getSubject(), factory.createUser(record.getAuthorName(), record.getAuthorEmail()), record.getFullMessage(), @@ -1091,40 +1154,6 @@ public class GitHistoryUtils { return rc; } - @NotNull - public static List commitsDetails(@NotNull Project project, @NotNull VirtualFile root, - @NotNull final Collection hashes) throws VcsException { - List params = new ArrayList(hashes); - GitVcs vcs = GitVcs.getInstance(project); - String noWalk = vcs != null && GitVersionSpecialty.NO_WALK_UNSORTED.existsIn(vcs.getVersion()) ? "--no-walk=unsorted" : "--no-walk"; - params.add(0, noWalk); - return getAllDetails(project, root, params); - } - - @NotNull - public static List getAllDetails(@NotNull Project project, @NotNull VirtualFile root, - @NotNull List parameters) throws VcsException { - VcsLogObjectsFactory factory = getObjectsFactoryWithDisposeCheck(project); - if (factory == null) { - return Collections.emptyList(); - } - - GitSimpleHandler h = new GitSimpleHandler(project, root, GitCommand.LOG); - GitLogParser parser = new GitLogParser(project, GitLogParser.NameStatus.STATUS, - HASH, HASH, COMMIT_TIME, AUTHOR_NAME, AUTHOR_TIME, AUTHOR_EMAIL, COMMITTER_NAME, - COMMITTER_EMAIL, PARENTS, REF_NAMES, SUBJECT, BODY, RAW_BODY); - h.setSilent(true); - h.addParameters("--name-status", "-M", parser.getPretty(), "--encoding=UTF-8"); - h.addParameters(parameters); - - String output = h.run(); - final List rc = new ArrayList(); - for (GitLogRecord record : parser.parse(output)) { - rc.add(createCommit(project, root, record, factory)); - } - return rc; - } - public static long getAuthorTime(Project project, FilePath path, final String commitsId) throws VcsException { // adjust path using change manager path = getLastCommitName(project, path); diff --git a/plugins/git4idea/src/git4idea/history/GitLogRecord.java b/plugins/git4idea/src/git4idea/history/GitLogRecord.java index e5b8735e0d4e..a9857963124f 100644 --- a/plugins/git4idea/src/git4idea/history/GitLogRecord.java +++ b/plugins/git4idea/src/git4idea/history/GitLogRecord.java @@ -22,10 +22,13 @@ import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.changes.Change; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.ArrayUtil; +import com.intellij.util.Function; +import com.intellij.util.containers.ContainerUtil; import com.intellij.vcsUtil.VcsUtil; import git4idea.GitUtil; import git4idea.commands.GitHandler; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.*; @@ -130,12 +133,7 @@ class GitLogRecord { public Collection getRefs() { final String decorate = myOptions.get(REF_NAMES); - final String[] refNames = parseRefNames(decorate); - final List result = new ArrayList(refNames.length); - for (String refName : refNames) { - result.add(shortBuffer(refName)); - } - return result; + return parseRefNames(decorate); } /** * Returns the list of tags and the list of branches. @@ -163,12 +161,23 @@ class GitLogRecord { return Pair.create(tags, branches); }*/ - private static String[] parseRefNames(final String decorate) { - final int startParentheses = decorate.indexOf("("); - final int endParentheses = decorate.indexOf(")"); - if ((startParentheses == -1) || (endParentheses == -1)) return ArrayUtil.EMPTY_STRING_ARRAY; - final String refs = decorate.substring(startParentheses + 1, endParentheses); - return refs.split(", "); + @NotNull + private static List parseRefNames(@Nullable final String decoration) { + if (decoration == null) { + return ContainerUtil.emptyList(); + } + final int startParentheses = decoration.indexOf("("); + final int endParentheses = decoration.indexOf(")"); + if ((startParentheses == -1) || (endParentheses == -1)) return Collections.emptyList(); + String refs = decoration.substring(startParentheses + 1, endParentheses); + String[] names = refs.split(", "); + return ContainerUtil.map(names, new Function() { + @Override + public String fun(String item) { + int colon = item.indexOf(':'); + return shortBuffer(colon > 0 ? item.substring(colon + 1).trim() : item); + } + }); } private static String shortBuffer(String raw) { diff --git a/plugins/git4idea/src/git4idea/log/GitLogProvider.java b/plugins/git4idea/src/git4idea/log/GitLogProvider.java index b5096a94f03b..72aef054a785 100644 --- a/plugins/git4idea/src/git4idea/log/GitLogProvider.java +++ b/plugins/git4idea/src/git4idea/log/GitLogProvider.java @@ -15,34 +15,27 @@ */ package git4idea.log; -import com.intellij.openapi.diagnostic.Attachment; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.VcsKey; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.util.ArrayUtil; -import com.intellij.util.Consumer; -import com.intellij.util.ExceptionUtil; -import com.intellij.util.Function; +import com.intellij.util.*; import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.containers.OpenTHashSet; import com.intellij.vcs.log.*; import com.intellij.vcs.log.data.VcsLogSorter; +import com.intellij.vcs.log.impl.LogDataImpl; import com.intellij.vcs.log.impl.HashImpl; -import git4idea.GitLocalBranch; -import git4idea.GitRemoteBranch; -import git4idea.GitUserRegistry; -import git4idea.GitVcs; +import git4idea.*; import git4idea.branch.GitBranchUtil; -import git4idea.commands.GitCommand; -import git4idea.commands.GitSimpleHandler; +import git4idea.config.GitVersionSpecialty; import git4idea.history.GitHistoryUtils; -import git4idea.history.GitLogParser; import git4idea.repo.GitRepository; import git4idea.repo.GitRepositoryChangeListener; import git4idea.repo.GitRepositoryManager; +import gnu.trove.TObjectHashingStrategy; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -51,8 +44,26 @@ import java.util.*; public class GitLogProvider implements VcsLogProvider { private static final Logger LOG = Logger.getInstance(GitLogProvider.class); + public static final Function GET_TAG_NAME = new Function() { + @Override + public String fun(VcsRef ref) { + return ref.getType() == GitRefManager.TAG ? ref.getName() : null; + } + }; + private static final TObjectHashingStrategy REF_ONLY_NAME_STRATEGY = new TObjectHashingStrategy() { + @Override + public int computeHashCode(@NotNull VcsRef ref) { + return ref.getName().hashCode(); + } + + @Override + public boolean equals(@NotNull VcsRef ref1, @NotNull VcsRef ref2) { + return ref1.getName().equals(ref2.getName()); + } + }; @NotNull private final Project myProject; + @NotNull private final GitVcs myVcs; @NotNull private final GitRepositoryManager myRepositoryManager; @NotNull private final GitUserRegistry myUserRegistry; @NotNull private final VcsLogRefManager myRefSorter; @@ -65,112 +76,127 @@ public class GitLogProvider implements VcsLogProvider { myUserRegistry = userRegistry; myRefSorter = new GitRefManager(myRepositoryManager); myVcsObjectsFactory = factory; + myVcs = ObjectUtils.assertNotNull(GitVcs.getInstance(project)); } @NotNull @Override - public List readFirstBlock(@NotNull VirtualFile root, @NotNull Requirements requirements) throws VcsException { + public DetailedLogData readFirstBlock(@NotNull VirtualFile root, @NotNull Requirements requirements) throws VcsException { if (!isRepositoryReady(root)) { - return Collections.emptyList(); + return LogDataImpl.empty(); } + GitRepository repository = ObjectUtils.assertNotNull(myRepositoryManager.getRepositoryForRoot(root)); // need to query more to sort them manually; this doesn't affect performance: it is equal for -1000 and -2000 int commitCount = requirements.getCommitCount() * 2; String[] params = new String[]{"HEAD", "--branches", "--remotes", "--max-count=" + commitCount}; // NB: not specifying --tags, because it introduces great slowdown if there are many tags, - // but makes sense only if there are heads without branch or HEAD labels (rare case). Such cases are partially handles below. - List firstBlock = GitHistoryUtils.loadMetadata(myProject, root, params); + // but makes sense only if there are heads without branch or HEAD labels (rare case). Such cases are partially handled below. - if (requirements instanceof VcsLogProviderRequirementsEx) { - VcsLogProviderRequirementsEx rex = (VcsLogProviderRequirementsEx)requirements; + boolean refresh = requirements instanceof VcsLogProviderRequirementsEx && ((VcsLogProviderRequirementsEx)requirements).isRefresh(); + + DetailedLogData data = GitHistoryUtils.loadMetadata(myProject, root, refresh, params); + + Set safeRefs = data.getRefs(); + Set allRefs = new OpenTHashSet(safeRefs, REF_ONLY_NAME_STRATEGY); + addNewElements(allRefs, readBranches(repository)); + + Collection allDetails; + if (!refresh) { + allDetails = data.getCommits(); + } + else { // on refresh: get new tags, which point to commits not from the first block; then get history, walking down just from these tags // on init: just ignore such tagged-only branches. The price for speed-up. - if (rex.isRefresh()) { - Collection newTags = getNewTags(rex.getCurrentRefs(), rex.getPreviousRefs()); - if (!newTags.isEmpty()) { - final Set firstBlockHashes = ContainerUtil.map2Set(firstBlock, new Function() { - @Override - public Hash fun(VcsCommitMetadata metadata) { - return metadata.getId(); - } - }); - List unmatchedHeads = getUnmatchedHeads(firstBlockHashes, newTags); - if (!unmatchedHeads.isEmpty()) { - List detailsFromTaggedBranches = loadSomeCommitsOnTaggedBranches(root, commitCount, unmatchedHeads); - Collection unmatchedCommits = getUnmatchedCommits(firstBlockHashes, detailsFromTaggedBranches); - firstBlock.addAll(unmatchedCommits); - } - } + VcsLogProviderRequirementsEx rex = (VcsLogProviderRequirementsEx)requirements; + + Set currentTags = readCurrentTagNames(root); + addOldStillExistingTags(allRefs, currentTags, rex.getPreviousRefs()); + + allDetails = ContainerUtil.newHashSet(data.getCommits()); + + Set previousTags = new HashSet(ContainerUtil.mapNotNull(rex.getPreviousRefs(), GET_TAG_NAME)); + Set safeTags = new HashSet(ContainerUtil.mapNotNull(safeRefs, GET_TAG_NAME)); + Set newUnmatchedTags = remove(currentTags, previousTags, safeTags); + + if (!newUnmatchedTags.isEmpty()) { + DetailedLogData commitsFromTags = loadSomeCommitsOnTaggedBranches(root, commitCount, newUnmatchedTags); + addNewElements(allDetails, commitsFromTags.getCommits()); + addNewElements(allRefs, commitsFromTags.getRefs()); } } - firstBlock = VcsLogSorter.sortByDateTopoOrder(firstBlock); - firstBlock = new ArrayList(firstBlock.subList(0, Math.min(firstBlock.size(), requirements.getCommitCount()))); - return firstBlock; + List sortedCommits = VcsLogSorter.sortByDateTopoOrder(allDetails); + sortedCommits = sortedCommits.subList(0, Math.min(sortedCommits.size(), requirements.getCommitCount())); + + return new LogDataImpl(allRefs, sortedCommits); } - @NotNull - private static Collection getNewTags(@NotNull Set currentRefs, @NotNull final Set previousRefs) { - return ContainerUtil.filter(currentRefs, new Condition() { - @Override - public boolean value(VcsRef ref) { - return !ref.getType().isBranch() && !previousRefs.contains(ref); + private static void addOldStillExistingTags(@NotNull Set allRefs, + @NotNull Set currentTags, + @NotNull Set previousRefs) { + for (VcsRef ref : previousRefs) { + if (!allRefs.contains(ref) && currentTags.contains(ref.getName())) { + allRefs.add(ref); } - }); + } } @NotNull - private List loadSomeCommitsOnTaggedBranches(@NotNull VirtualFile root, int commitCount, - @NotNull List unmatchedHeads) throws VcsException { - List params = new ArrayList(ContainerUtil.map(unmatchedHeads, new Function() { - @Override - public String fun(VcsRef ref) { - return ref.getCommitHash().asString(); - } - })); - params.add("--max-count=" + commitCount); - return GitHistoryUtils.loadMetadata(myProject, root, ArrayUtil.toStringArray(params)); + private Set readCurrentTagNames(@NotNull VirtualFile root) throws VcsException { + Set tags = ContainerUtil.newHashSet(); + GitTag.listAsStrings(myProject, root, tags, null); + return tags; } @NotNull - private static List getUnmatchedHeads(@NotNull final Set firstBlockHashes, @NotNull Collection refs) { - return ContainerUtil.filter(refs, new Condition() { - @Override - public boolean value(VcsRef ref) { - return !firstBlockHashes.contains(ref.getCommitHash()); + private static Set remove(@NotNull Set original, @NotNull Set... toRemove) { + Set result = ContainerUtil.newHashSet(original); + for (Set set : toRemove) { + result.removeAll(set); + } + return result; + } + + private static void addNewElements(@NotNull Collection original, @NotNull Collection toAdd) { + for (T item : toAdd) { + if (!original.contains(item)) { + original.add(item); } - }); + } } @NotNull - private static Collection getUnmatchedCommits(@NotNull final Set firstBlockHashes, - @NotNull List detailsFromTaggedBranches) { - return ContainerUtil.filter(detailsFromTaggedBranches, new Condition() { - @Override - public boolean value(VcsCommitMetadata metadata) { - return !firstBlockHashes.contains(metadata.getId()); - } - }); + private DetailedLogData loadSomeCommitsOnTaggedBranches(@NotNull VirtualFile root, int commitCount, + @NotNull Collection unmatchedTags) throws VcsException { + List params = new ArrayList(); + params.add("--max-count=" + commitCount); + params.addAll(unmatchedTags); + return GitHistoryUtils.loadMetadata(myProject, root, true, ArrayUtil.toStringArray(params)); } @Override - public void readAllHashes(@NotNull VirtualFile root, @NotNull Consumer userRegistry, - @NotNull final Consumer commitConsumer) throws VcsException { + @NotNull + public LogData readAllHashes(@NotNull VirtualFile root, @NotNull final Consumer commitConsumer) throws VcsException { if (!isRepositoryReady(root)) { - return; + return LogDataImpl.empty(); } List parameters = new ArrayList(GitHistoryUtils.LOG_ALL); parameters.add("--sparse"); final GitBekParentFixer parentFixer = GitBekParentFixer.prepare(root, this); - GitHistoryUtils.readCommits(myProject, root, userRegistry, parameters, new Consumer() { + Set userRegistry = ContainerUtil.newHashSet(); + Set refs = ContainerUtil.newHashSet(); + GitHistoryUtils.readCommits(myProject, root, parameters, new CollectConsumer(userRegistry), + new CollectConsumer(refs), new Consumer() { @Override public void consume(TimedVcsCommit commit) { commitConsumer.consume(parentFixer.fixCommit(commit)); } }); + return new LogDataImpl(refs, userRegistry); } @NotNull @@ -183,21 +209,19 @@ public class GitLogProvider implements VcsLogProvider { @NotNull @Override public List readFullDetails(@NotNull VirtualFile root, @NotNull List hashes) throws VcsException { - return GitHistoryUtils.commitsDetails(myProject, root, hashes); + String noWalk = GitVersionSpecialty.NO_WALK_UNSORTED.existsIn(myVcs.getVersion()) ? "--no-walk=unsorted" : "--no-walk"; + List params = new ArrayList(); + params.add(noWalk); + params.addAll(hashes); + return GitHistoryUtils.history(myProject, root, ArrayUtil.toStringArray(params)); } @NotNull - @Override - public Collection readAllRefs(@NotNull VirtualFile root) throws VcsException { - if (!isRepositoryReady(root)) { - return Collections.emptyList(); - } - - GitRepository repository = getRepository(root); - repository.update(); + private Set readBranches(@NotNull GitRepository repository) { + VirtualFile root = repository.getRoot(); Collection localBranches = repository.getBranches().getLocalBranches(); Collection remoteBranches = repository.getBranches().getRemoteBranches(); - Collection refs = new ArrayList(localBranches.size() + remoteBranches.size()); + Set refs = new HashSet(localBranches.size() + remoteBranches.size()); for (GitLocalBranch localBranch : localBranches) { refs.add( myVcsObjectsFactory.createRef(HashImpl.build(localBranch.getHash()), localBranch.getName(), GitRefManager.LOCAL_BRANCH, root)); @@ -210,29 +234,6 @@ public class GitLogProvider implements VcsLogProvider { if (currentRevision != null) { // null => fresh repository refs.add(myVcsObjectsFactory.createRef(HashImpl.build(currentRevision), "HEAD", GitRefManager.HEAD, root)); } - - refs.addAll(readTags(root)); - return refs; - } - - // TODO this is to be removed when tags will be supported by the GitRepositoryReader - private Collection readTags(@NotNull VirtualFile root) throws VcsException { - GitSimpleHandler tagHandler = new GitSimpleHandler(myProject, root, GitCommand.LOG); - tagHandler.setSilent(true); - tagHandler.addParameters("--tags", "--no-walk", "--format=%H%d" + GitLogParser.RECORD_START_GIT, "--decorate=full"); - String out = tagHandler.run(); - Collection refs = new ArrayList(); - try { - for (String record : out.split(GitLogParser.RECORD_START)) { - if (!StringUtil.isEmptyOrSpaces(record)) { - refs.addAll(new RefParser(myVcsObjectsFactory).parseCommitRefs(record.trim(), root)); - } - } - } - catch (Exception e) { - LOG.error("Error during tags parsing", new Attachment("stack_trace.txt", ExceptionUtil.getThrowableText(e)), - new Attachment("git_output.txt", out)); - } return refs; } @@ -327,7 +328,10 @@ public class GitLogProvider implements VcsLogProvider { } } - return GitHistoryUtils.readCommits(myProject, root, Consumer.EMPTY_CONSUMER, filterParameters); + List commits = ContainerUtil.newArrayList(); + GitHistoryUtils.readCommits(myProject, root, filterParameters, Consumer.EMPTY_CONSUMER, Consumer.EMPTY_CONSUMER, + new CollectConsumer(commits)); + return commits; } @Nullable diff --git a/plugins/git4idea/src/git4idea/log/GitRefManager.java b/plugins/git4idea/src/git4idea/log/GitRefManager.java index eb9e79c69767..6469a1fef1bd 100644 --- a/plugins/git4idea/src/git4idea/log/GitRefManager.java +++ b/plugins/git4idea/src/git4idea/log/GitRefManager.java @@ -15,6 +15,7 @@ import com.intellij.vcs.log.VcsRefType; import com.intellij.vcs.log.impl.SingletonRefGroup; import git4idea.GitBranch; import git4idea.GitRemoteBranch; +import git4idea.GitTag; import git4idea.repo.GitBranchTrackInfo; import git4idea.repo.GitRemote; import git4idea.repo.GitRepository; @@ -38,9 +39,10 @@ public class GitRefManager implements VcsLogRefManager { public static final VcsRefType LOCAL_BRANCH = new SimpleRefType(true, LOCAL_BRANCH_COLOR); public static final VcsRefType REMOTE_BRANCH = new SimpleRefType(true, REMOTE_BRANCH_COLOR); public static final VcsRefType TAG = new SimpleRefType(false, TAG_COLOR); + public static final VcsRefType OTHER = new SimpleRefType(false, TAG_COLOR); // first has the highest priority - private static final List REF_TYPE_PRIORITIES = Arrays.asList(HEAD, LOCAL_BRANCH, REMOTE_BRANCH, TAG); + private static final List REF_TYPE_PRIORITIES = Arrays.asList(HEAD, LOCAL_BRANCH, REMOTE_BRANCH, TAG, OTHER); // -1 => higher priority public static final Comparator REF_TYPE_COMPARATOR = new Comparator() { @@ -173,7 +175,7 @@ public class GitRefManager implements VcsLogRefManager { remoteRefGroups.putValue(nonTracked.get(refName), ref); } else { - LOG.warn("Didn't find ref neither in local nor in remote branches: " + ref); + LOG.debug("Didn't find ref neither in local nor in remote branches: " + ref); } } } @@ -243,6 +245,23 @@ public class GitRefManager implements VcsLogRefManager { return grouped; } + @NotNull + public static VcsRefType getRefType(@NotNull String refName) { + if (refName.startsWith(GitBranch.REFS_HEADS_PREFIX)) { + return LOCAL_BRANCH; + } + if (refName.startsWith(GitBranch.REFS_REMOTES_PREFIX)) { + return REMOTE_BRANCH; + } + if (refName.startsWith(GitTag.REFS_TAGS_PREFIX)) { + return TAG; + } + if (refName.startsWith("HEAD")) { + return HEAD; + } + return OTHER; + } + private static class SimpleRefType implements VcsRefType { private final boolean myIsBranch; @NotNull private final Color myColor; diff --git a/plugins/git4idea/src/git4idea/log/RefParser.java b/plugins/git4idea/src/git4idea/log/RefParser.java deleted file mode 100644 index 619cfcd5d4da..000000000000 --- a/plugins/git4idea/src/git4idea/log/RefParser.java +++ /dev/null @@ -1,73 +0,0 @@ -package git4idea.log; - -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.vcs.log.Hash; -import com.intellij.vcs.log.VcsLogObjectsFactory; -import com.intellij.vcs.log.VcsRef; -import com.intellij.vcs.log.impl.HashImpl; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - - -/** - * TODO: remove when tags are supported by the {@link git4idea.repo.GitRepositoryReader}. - * - * @author erokhins - */ -class RefParser { - - private final VcsLogObjectsFactory myFactory; - - public RefParser(VcsLogObjectsFactory factory) { - myFactory = factory; - } - - // e25b7d8f (HEAD, refs/remotes/origin/master, refs/remotes/origin/HEAD, refs/heads/master) - public List parseCommitRefs(@NotNull String input, @NotNull VirtualFile root) { - int firstSpaceIndex = input.indexOf(' '); - if (firstSpaceIndex < 0) { - return Collections.emptyList(); - } - String strHash = input.substring(0, firstSpaceIndex); - Hash hash = HashImpl.build(strHash); - String refPaths = input.substring(firstSpaceIndex + 2, input.length() - 1); - String[] longRefPaths = refPaths.split(", "); - List refs = new ArrayList(); - for (String longRefPatch : longRefPaths) { - VcsRef ref = createRef(hash, longRefPatch, root); - if (ref != null) { - refs.add(ref); - } - } - return refs; - } - - @Nullable - private static String getRefName(@NotNull String longRefPath, @NotNull String startPatch) { - String tagPrefix = "tag: "; - if (longRefPath.startsWith(tagPrefix)) { - longRefPath = longRefPath.substring(tagPrefix.length()); - } - if (longRefPath.startsWith(startPatch)) { - return longRefPath.substring(startPatch.length()); - } - else { - return null; - } - } - - // example input: fb29c80 refs/tags/92.29 - @Nullable - private VcsRef createRef(@NotNull Hash hash, @NotNull String longRefPath, @NotNull VirtualFile root) { - String name = getRefName(longRefPath, "refs/tags/"); - if (name != null) { - return myFactory.createRef(hash, name, GitRefManager.TAG, root); - } - - return null; - } -} diff --git a/plugins/git4idea/src/git4idea/repo/GitRepositoryFiles.java b/plugins/git4idea/src/git4idea/repo/GitRepositoryFiles.java index d32a0e12f28d..cb0bd9d3dd71 100644 --- a/plugins/git4idea/src/git4idea/repo/GitRepositoryFiles.java +++ b/plugins/git4idea/src/git4idea/repo/GitRepositoryFiles.java @@ -45,6 +45,7 @@ public class GitRepositoryFiles { public static final String PACKED_REFS = "packed-refs"; public static final String REFS_HEADS = "refs/heads"; public static final String REFS_REMOTES = "refs/remotes"; + public static final String REFS_TAGS = "refs/tags"; public static final String SQUASH_MSG = "SQUASH_MSG"; public static final String GIT_HEAD = DOT_GIT + slash(HEAD); @@ -64,6 +65,7 @@ public class GitRepositoryFiles { private final String myPackedRefsPath; private final String myRefsHeadsDirPath; private final String myRefsRemotesDirPath; + private final String myRefsTagsPath; private final String myCommitMessagePath; private final String myExcludePath; @@ -85,6 +87,7 @@ public class GitRepositoryFiles { myRebaseMergePath = gitDirPath + slash(REBASE_MERGE); myPackedRefsPath = gitDirPath + slash(PACKED_REFS); myRefsHeadsDirPath = gitDirPath + slash(REFS_HEADS); + myRefsTagsPath = gitDirPath + slash(REFS_TAGS); myRefsRemotesDirPath = gitDirPath + slash(REFS_REMOTES); myExcludePath = gitDirPath + slash(INFO_EXCLUDE); } @@ -99,19 +102,24 @@ public class GitRepositoryFiles { */ @NotNull static Collection getSubDirRelativePaths() { - return Arrays.asList(slash(REFS_HEADS), slash(REFS_REMOTES), slash(INFO)); + return Arrays.asList(slash(REFS_HEADS), slash(REFS_REMOTES), slash(REFS_TAGS), slash(INFO)); } @NotNull String getRefsHeadsPath() { return myRefsHeadsDirPath; } - + @NotNull String getRefsRemotesPath() { return myRefsRemotesDirPath; } + @NotNull + String getRefsTagsPath() { + return myRefsTagsPath; + } + /** * {@code .git/config} */ @@ -147,6 +155,13 @@ public class GitRepositoryFiles { return filePath.startsWith(myRefsRemotesDirPath); } + /** + * .git/refs/tags/* + */ + public boolean isTagFile(@NotNull String path) { + return path.startsWith(myRefsTagsPath); + } + /** * .git/rebase-merge or .git/rebase-apply */ diff --git a/plugins/git4idea/src/git4idea/repo/GitRepositoryImpl.java b/plugins/git4idea/src/git4idea/repo/GitRepositoryImpl.java index 1444f44c0288..30951670e079 100644 --- a/plugins/git4idea/src/git4idea/repo/GitRepositoryImpl.java +++ b/plugins/git4idea/src/git4idea/repo/GitRepositoryImpl.java @@ -137,6 +137,13 @@ public class GitRepositoryImpl extends RepositoryImpl implements GitRepository { return myInfo.getState(); } + @Nullable + @Override + public String getCurrentBranchName() { + GitLocalBranch currentBranch = getCurrentBranch(); + return currentBranch == null ? null : currentBranch.getName(); + } + @Nullable @Override public AbstractVcs getVcs() { @@ -235,5 +242,4 @@ public class GitRepositoryImpl extends RepositoryImpl implements GitRepository { myBranches = branches; } } - } diff --git a/plugins/git4idea/src/git4idea/repo/GitRepositoryUpdater.java b/plugins/git4idea/src/git4idea/repo/GitRepositoryUpdater.java index 70a41d3d2da8..a5af8fca6352 100644 --- a/plugins/git4idea/src/git4idea/repo/GitRepositoryUpdater.java +++ b/plugins/git4idea/src/git4idea/repo/GitRepositoryUpdater.java @@ -34,19 +34,21 @@ import java.util.List; /** * Listens to .git service files changes and updates {@link GitRepository} when needed. - * @author Kirill Likhodedov */ final class GitRepositoryUpdater implements Disposable, BulkFileListener { + + @NotNull private final GitRepository myRepository; @NotNull private final GitRepositoryFiles myRepositoryFiles; @Nullable private final MessageBusConnection myMessageBusConnection; @NotNull private final QueueProcessor myUpdateQueue; @NotNull private final Object DUMMY_UPDATE_OBJECT = new Object(); @Nullable private final VirtualFile myRemotesDir; @Nullable private final VirtualFile myHeadsDir; + @Nullable private final VirtualFile myTagsDir; @Nullable private final LocalFileSystem.WatchRequest myWatchRequest; - GitRepositoryUpdater(@NotNull GitRepository repository) { + myRepository = repository; VirtualFile gitDir = repository.getGitDir(); myWatchRequest = LocalFileSystem.getInstance().addRootToWatch(gitDir.getPath(), true); @@ -54,6 +56,7 @@ final class GitRepositoryUpdater implements Disposable, BulkFileListener { RepositoryUtil.visitVcsDirVfs(gitDir, GitRepositoryFiles.getSubDirRelativePaths()); myHeadsDir = VcsUtil.getVirtualFile(myRepositoryFiles.getRefsHeadsPath()); myRemotesDir = VcsUtil.getVirtualFile(myRepositoryFiles.getRefsRemotesPath()); + myTagsDir = VcsUtil.getVirtualFile(myRepositoryFiles.getRefsTagsPath()); Project project = repository.getProject(); myUpdateQueue = new QueueProcessor(new RepositoryUtil.Updater(repository), project.getDisposed()); @@ -66,8 +69,6 @@ final class GitRepositoryUpdater implements Disposable, BulkFileListener { } } - - @Override public void dispose() { if (myWatchRequest != null) { @@ -92,19 +93,16 @@ final class GitRepositoryUpdater implements Disposable, BulkFileListener { boolean packedRefsChanged = false; boolean rebaseFileChanged = false; boolean mergeFileChanged = false; + boolean tagChanged = false; for (VFileEvent event : events) { - String filePath = event.getPath(); - if (filePath == null) { - continue; - } - filePath = GitFileUtils.stripFileProtocolPrefix(filePath); + String filePath = GitFileUtils.stripFileProtocolPrefix(event.getPath()); if (myRepositoryFiles.isConfigFile(filePath)) { configChanged = true; } else if (myRepositoryFiles.isHeadFile(filePath)) { headChanged = true; } else if (myRepositoryFiles.isBranchFile(filePath)) { // it is also possible, that a local branch with complex name ("folder/branch") was created => the folder also to be watched. - branchFileChanged = true; + branchFileChanged = true; RepositoryUtil.visitAllChildrenRecursively(myHeadsDir); } else if (myRepositoryFiles.isRemoteBranchFile(filePath)) { // it is possible, that a branch from a new remote was fetch => we need to add new remote folder to the VFS @@ -116,12 +114,18 @@ final class GitRepositoryUpdater implements Disposable, BulkFileListener { rebaseFileChanged = true; } else if (myRepositoryFiles.isMergeFile(filePath)) { mergeFileChanged = true; + } else if (myRepositoryFiles.isTagFile(filePath)) { + RepositoryUtil.visitAllChildrenRecursively(myTagsDir); + tagChanged = true; } } if (headChanged || configChanged || branchFileChanged || packedRefsChanged || rebaseFileChanged || mergeFileChanged) { myUpdateQueue.add(DUMMY_UPDATE_OBJECT); } + else if (tagChanged) { + myRepository.getProject().getMessageBus().syncPublisher(GitRepository.GIT_REPO_CHANGE).repositoryChanged(myRepository); + } } } diff --git a/plugins/git4idea/src/git4idea/reset/GitResetAction.java b/plugins/git4idea/src/git4idea/reset/GitResetAction.java index 749acd7e7c7c..97b5b4ffeae8 100644 --- a/plugins/git4idea/src/git4idea/reset/GitResetAction.java +++ b/plugins/git4idea/src/git4idea/reset/GitResetAction.java @@ -16,7 +16,6 @@ package git4idea.reset; import com.intellij.dvcs.ui.VcsLogOneCommitPerRepoAction; -import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.Project; @@ -25,18 +24,19 @@ import com.intellij.util.ObjectUtils; import com.intellij.vcs.log.VcsFullCommitDetails; import git4idea.config.GitVcsSettings; import git4idea.repo.GitRepository; -import git4idea.repo.GitRepositoryManager; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Map; +import static git4idea.GitUtil.getRepositoryManager; + public class GitResetAction extends VcsLogOneCommitPerRepoAction { @Nullable @Override protected GitRepository getRepositoryForRoot(@NotNull Project project, @NotNull VirtualFile root) { - return getRepoManager(project).getRepositoryForRoot(root); + return getRepositoryManager(project).getRepositoryForRoot(root); } @Override @@ -57,9 +57,4 @@ public class GitResetAction extends VcsLogOneCommitPerRepoAction } } - @NotNull - private static GitRepositoryManager getRepoManager(@NotNull Project project) { - return ServiceManager.getService(project, GitRepositoryManager.class); - } - } diff --git a/plugins/git4idea/src/git4idea/ui/branch/GitBranchPopup.java b/plugins/git4idea/src/git4idea/ui/branch/GitBranchPopup.java index 1cb7cf6a66c9..38188b2b7b21 100644 --- a/plugins/git4idea/src/git4idea/ui/branch/GitBranchPopup.java +++ b/plugins/git4idea/src/git4idea/ui/branch/GitBranchPopup.java @@ -16,23 +16,14 @@ package git4idea.ui.branch; import com.intellij.dvcs.DvcsUtil; -import com.intellij.dvcs.ui.BranchActionGroupPopup; +import com.intellij.dvcs.branch.DvcsBranchPopup; +import com.intellij.dvcs.repo.AbstractRepositoryManager; import com.intellij.dvcs.ui.RootAction; -import com.intellij.notification.Notification; -import com.intellij.notification.NotificationListener; -import com.intellij.openapi.actionSystem.ActionGroup; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.DefaultActionGroup; -import com.intellij.openapi.options.ShowSettingsUtil; import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.popup.ListPopup; import com.intellij.openapi.util.Condition; -import com.intellij.openapi.vcs.VcsNotifier; -import com.intellij.ui.popup.list.ListPopupImpl; -import com.intellij.util.containers.ContainerUtil; -import git4idea.GitLocalBranch; import git4idea.GitUtil; -import git4idea.GitVcs; import git4idea.branch.GitBranchUtil; import git4idea.config.GitVcsSettings; import git4idea.repo.GitRepository; @@ -41,51 +32,25 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; -import javax.swing.event.HyperlinkEvent; import java.util.List; /** * The popup which allows to quickly switch and control Git branches. *

*/ -class GitBranchPopup { - - private final Project myProject; - private final GitRepositoryManager myRepositoryManager; - private final GitVcsSettings myVcsSettings; - private final GitVcs myVcs; - private final GitMultiRootBranchConfig myMultiRootBranchConfig; - - private final GitRepository myCurrentRepository; - private final ListPopupImpl myPopup; - - ListPopup asListPopup() { - return myPopup; - } +class GitBranchPopup extends DvcsBranchPopup { /** * @param currentRepository Current repository, which means the repository of the currently open or selected file. * In the case of synchronized branch operations current repository matter much less, but sometimes is used, * for example, it is preselected in the repositories combobox in the compare branches dialog. */ - static GitBranchPopup getInstance(@NotNull Project project, @NotNull GitRepository currentRepository) { - return new GitBranchPopup(project, currentRepository); - } - - private GitBranchPopup(@NotNull Project project, @NotNull GitRepository currentRepository) { - myProject = project; - myCurrentRepository = currentRepository; - myRepositoryManager = GitUtil.getRepositoryManager(project); - myVcs = GitVcs.getInstance(project); - myVcsSettings = GitVcsSettings.getInstance(myProject); - - myMultiRootBranchConfig = new GitMultiRootBranchConfig(myRepositoryManager.getRepositories()); - - String title = createPopupTitle(currentRepository); - + static GitBranchPopup getInstance(@NotNull final Project project, @NotNull GitRepository currentRepository) { + final GitVcsSettings vcsSettings = GitVcsSettings.getInstance(project); Condition preselectActionCondition = new Condition() { @Override public boolean value(AnAction action) { + if (action instanceof GitBranchPopupActions.LocalBranchActions) { GitBranchPopupActions.LocalBranchActions branchAction = (GitBranchPopupActions.LocalBranchActions)action; String branchName = branchAction.getBranchName(); @@ -93,10 +58,10 @@ class GitBranchPopup { String recentBranch; List repositories = branchAction.getRepositories(); if (repositories.size() == 1) { - recentBranch = myVcsSettings.getRecentBranchesByRepository().get(repositories.iterator().next().getRoot().getPath()); + recentBranch = vcsSettings.getRecentBranchesByRepository().get(repositories.iterator().next().getRoot().getPath()); } else { - recentBranch = myVcsSettings.getRecentCommonBranch(); + recentBranch = vcsSettings.getRecentCommonBranch(); } if (recentBranch != null && recentBranch.equals(branchName)) { @@ -106,37 +71,19 @@ class GitBranchPopup { return false; } }; - - myPopup = new BranchActionGroupPopup(title, project, preselectActionCondition, createActions()); - - initBranchSyncPolicyIfNotInitialized(); - setCurrentBranchInfo(); - warnThatBranchesDivergedIfNeeded(); + return new GitBranchPopup(currentRepository, GitUtil.getRepositoryManager(project), vcsSettings, preselectActionCondition); } - private void initBranchSyncPolicyIfNotInitialized() { - if (myRepositoryManager.moreThanOneRoot() && myVcsSettings.getSyncSetting() == GitBranchSyncSetting.NOT_DECIDED) { - if (!myMultiRootBranchConfig.diverged()) { - notifyAboutSyncedBranches(); - myVcsSettings.setSyncSetting(GitBranchSyncSetting.SYNC); - } - else { - myVcsSettings.setSyncSetting(GitBranchSyncSetting.DONT); - } - } - } - - @NotNull - private String createPopupTitle(@NotNull GitRepository currentRepository) { - String title = "Git Branches"; - if (myRepositoryManager.moreThanOneRoot() && - (myMultiRootBranchConfig.diverged() || myVcsSettings.getSyncSetting() == GitBranchSyncSetting.DONT)) { - title += " in " + DvcsUtil.getShortRepositoryName(currentRepository); - } - return title; + private GitBranchPopup(@NotNull GitRepository currentRepository, + @NotNull GitRepositoryManager repositoryManager, + @NotNull GitVcsSettings vcsSettings, + @NotNull Condition preselectActionCondition) { + super(currentRepository, repositoryManager, new GitMultiRootBranchConfig(repositoryManager.getRepositories()), vcsSettings, + preselectActionCondition); } - private void setCurrentBranchInfo() { + @Override + protected void setCurrentBranchInfo() { String currentBranchText = "Current branch"; if (myRepositoryManager.moreThanOneRoot()) { if (myMultiRootBranchConfig.diverged()) { @@ -153,55 +100,16 @@ class GitBranchPopup { myPopup.setAdText(currentBranchText, SwingConstants.CENTER); } - private void notifyAboutSyncedBranches() { - String description = "You have several Git roots in the project and they all are checked out at the same branch. " + - "We've enabled synchronous branch control for the project.
" + - "If you wish to control branches in different roots separately, " + - "you may disable the setting."; - NotificationListener listener = new NotificationListener() { - @Override - public void hyperlinkUpdate(@NotNull Notification notification, @NotNull HyperlinkEvent event) { - if (event.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { - ShowSettingsUtil.getInstance().showSettingsDialog(myProject, myVcs.getConfigurable().getDisplayName()); - if (myVcsSettings.getSyncSetting() == GitBranchSyncSetting.DONT) { - notification.expire(); - } - } - } - }; - VcsNotifier.getInstance(myProject).notifyImportantInfo("Synchronous branch control enabled", description, listener); - } - - private ActionGroup createActions() { - DefaultActionGroup popupGroup = new DefaultActionGroup(null, false); - GitRepositoryManager repositoryManager = myRepositoryManager; - if (repositoryManager.moreThanOneRoot()) { - if (userWantsSyncControl()) { - fillWithCommonRepositoryActions(popupGroup, repositoryManager); - } - else { - fillPopupWithCurrentRepositoryActions(popupGroup, createRepositoriesActions()); - } - } - else { - fillPopupWithCurrentRepositoryActions(popupGroup, null); - } - popupGroup.addSeparator(); - return popupGroup; - } - - private boolean userWantsSyncControl() { - return (myVcsSettings.getSyncSetting() != GitBranchSyncSetting.DONT); - } - - private void fillWithCommonRepositoryActions(DefaultActionGroup popupGroup, GitRepositoryManager repositoryManager) { + @Override + protected void fillWithCommonRepositoryActions(@NotNull DefaultActionGroup popupGroup, + @NotNull AbstractRepositoryManager repositoryManager) { List allRepositories = repositoryManager.getRepositories(); popupGroup.add(new GitBranchPopupActions.GitNewBranchAction(myProject, allRepositories)); popupGroup.addAll(createRepositoriesActions()); popupGroup.addSeparator("Common Local Branches"); - for (String branch : myMultiRootBranchConfig.getLocalBranches()) { + for (String branch : myMultiRootBranchConfig.getLocalBranchNames()) { List repositories = filterRepositoriesNotOnThisBranch(branch, allRepositories); if (!repositories.isEmpty()) { popupGroup.add(new GitBranchPopupActions.LocalBranchActions(myProject, repositories, branch, myCurrentRepository)); @@ -209,30 +117,14 @@ class GitBranchPopup { } popupGroup.addSeparator("Common Remote Branches"); - for (String branch : myMultiRootBranchConfig.getRemoteBranches()) { + for (String branch : ((GitMultiRootBranchConfig)myMultiRootBranchConfig).getRemoteBranches()) { popupGroup.add(new GitBranchPopupActions.RemoteBranchActions(myProject, allRepositories, branch, myCurrentRepository)); } } @NotNull - private static List filterRepositoriesNotOnThisBranch(@NotNull final String branch, - @NotNull List allRepositories) { - return ContainerUtil.filter(allRepositories, new Condition() { - @Override - public boolean value(GitRepository repository) { - GitLocalBranch currentBranch = repository.getCurrentBranch(); - return currentBranch == null || !branch.equals(currentBranch.getName()); - } - }); - } - - private void warnThatBranchesDivergedIfNeeded() { - if (myRepositoryManager.moreThanOneRoot() && myMultiRootBranchConfig.diverged() && userWantsSyncControl()) { - myPopup.setWarning("Branches have diverged"); - } - } - - private DefaultActionGroup createRepositoriesActions() { + @Override + protected DefaultActionGroup createRepositoriesActions() { DefaultActionGroup popupGroup = new DefaultActionGroup(null, false); popupGroup.addSeparator("Repositories"); for (GitRepository repository : myRepositoryManager.getRepositories()) { @@ -243,11 +135,8 @@ class GitBranchPopup { return popupGroup; } - private boolean highlightCurrentRepo() { - return !userWantsSyncControl() || myMultiRootBranchConfig.diverged(); - } - - private void fillPopupWithCurrentRepositoryActions(@NotNull DefaultActionGroup popupGroup, @Nullable DefaultActionGroup actions) { + @Override + protected void fillPopupWithCurrentRepositoryActions(@NotNull DefaultActionGroup popupGroup, @Nullable DefaultActionGroup actions) { popupGroup.addAll(new GitBranchPopupActions(myCurrentRepository.getProject(), myCurrentRepository).createActions(actions)); } } diff --git a/plugins/git4idea/src/git4idea/ui/branch/GitBranchSyncSetting.java b/plugins/git4idea/src/git4idea/ui/branch/GitBranchSyncSetting.java deleted file mode 100644 index bb9c7592f028..000000000000 --- a/plugins/git4idea/src/git4idea/ui/branch/GitBranchSyncSetting.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2000-2012 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package git4idea.ui.branch; - -/** - * @author Kirill Likhodedov - */ -public enum GitBranchSyncSetting { - SYNC, - DONT, - NOT_DECIDED -} diff --git a/plugins/git4idea/src/git4idea/ui/branch/GitCompareBranchesDialog.java b/plugins/git4idea/src/git4idea/ui/branch/GitCompareBranchesDialog.java index 89ff9f47268f..23588670048d 100644 --- a/plugins/git4idea/src/git4idea/ui/branch/GitCompareBranchesDialog.java +++ b/plugins/git4idea/src/git4idea/ui/branch/GitCompareBranchesDialog.java @@ -17,35 +17,46 @@ package git4idea.ui.branch; import com.intellij.dvcs.DvcsUtil; import com.intellij.icons.AllIcons; +import com.intellij.openapi.diff.impl.dir.FrameDialogWrapper; import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.FrameWrapper; import com.intellij.ui.TabbedPaneImpl; import git4idea.GitUtil; import git4idea.repo.GitRepository; import git4idea.util.GitCommitCompareInfo; import icons.Git4ideaIcons; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import javax.swing.*; /** * Dialog for comparing two Git branches. */ -public class GitCompareBranchesDialog extends FrameWrapper { +public class GitCompareBranchesDialog extends FrameDialogWrapper { @NotNull private final Project myProject; - @NotNull private final String myBranchName; - @NotNull private final String myCurrentBranchName; - @NotNull private final GitCommitCompareInfo myCompareInfo; + @NotNull private final JPanel myLogPanel; + @NotNull private final TabbedPaneImpl myTabbedPane; + @NotNull private final String myTitle; + + @NotNull private final Mode myMode; - public GitCompareBranchesDialog(@NotNull Project project, @NotNull String branchName, @NotNull String currentBranchName, - @NotNull GitCommitCompareInfo compareInfo, @NotNull GitRepository initialRepo) { - super(project, GitCompareBranchesDialog.class.getName()); - myCurrentBranchName = currentBranchName; - myCompareInfo = compareInfo; + public GitCompareBranchesDialog(@NotNull Project project, + @NotNull String branchName, + @NotNull String currentBranchName, + @NotNull GitCommitCompareInfo compareInfo, + @NotNull GitRepository initialRepo) { + this(project, branchName, currentBranchName, compareInfo, initialRepo, false); + } + + public GitCompareBranchesDialog(@NotNull Project project, + @NotNull String branchName, + @NotNull String currentBranchName, + @NotNull GitCommitCompareInfo compareInfo, + @NotNull GitRepository initialRepo, + boolean dialog) { myProject = project; - myBranchName = branchName; String rootString; if (compareInfo.getRepositories().size() == 1 && GitUtil.getRepositoryManager(myProject).moreThanOneRoot()) { @@ -54,23 +65,51 @@ public class GitCompareBranchesDialog extends FrameWrapper { else { rootString = ""; } - setTitle(String.format("Comparing %s with %s%s", currentBranchName, branchName, rootString)); + myTitle = String.format("Comparing %s with %s%s", currentBranchName, branchName, rootString); + myMode = dialog ? Mode.MODAL : Mode.FRAME; + + JPanel diffPanel = new GitCompareBranchesDiffPanel(myProject, branchName, currentBranchName, compareInfo); + myLogPanel = new GitCompareBranchesLogPanel(myProject, branchName, currentBranchName, compareInfo, initialRepo); - myLogPanel = new GitCompareBranchesLogPanel(myProject, myBranchName, myCurrentBranchName, myCompareInfo, initialRepo); - setPreferredFocusedComponent(myLogPanel); - setComponent(createCenterPanel()); - closeOnEsc(); + myTabbedPane = new TabbedPaneImpl(SwingConstants.TOP); + myTabbedPane.addTab("Log", Git4ideaIcons.Branch, myLogPanel); + myTabbedPane.addTab("Diff", AllIcons.Actions.Diff, diffPanel); + myTabbedPane.setKeyboardNavigation(TabbedPaneImpl.DEFAULT_PREV_NEXT_SHORTCUTS); } @NotNull - protected JComponent createCenterPanel() { - JPanel diffPanel = new GitCompareBranchesDiffPanel(myProject, myBranchName, myCurrentBranchName, myCompareInfo); - - TabbedPaneImpl tabbedPane = new TabbedPaneImpl(SwingConstants.TOP); - tabbedPane.addTab("Log", Git4ideaIcons.Branch, myLogPanel); - tabbedPane.addTab("Diff", AllIcons.Actions.Diff, diffPanel); - tabbedPane.setKeyboardNavigation(TabbedPaneImpl.DEFAULT_PREV_NEXT_SHORTCUTS); - return tabbedPane; + @Override + protected JComponent getPanel() { + return myTabbedPane; + } + + @Nullable + @Override + protected JComponent getPreferredFocusedComponent() { + return myLogPanel; + } + + @Nullable + @Override + protected String getDimensionServiceKey() { + return GitCompareBranchesDialog.class.getName(); } + @NotNull + @Override + protected String getTitle() { + return myTitle; + } + + @NotNull + @Override + protected Project getProject() { + return myProject; + } + + @NotNull + @Override + protected Mode getMode() { + return myMode; + } } diff --git a/plugins/git4idea/src/git4idea/ui/branch/GitMultiRootBranchConfig.java b/plugins/git4idea/src/git4idea/ui/branch/GitMultiRootBranchConfig.java index ba4dd2aab369..18076a1b9a61 100644 --- a/plugins/git4idea/src/git4idea/ui/branch/GitMultiRootBranchConfig.java +++ b/plugins/git4idea/src/git4idea/ui/branch/GitMultiRootBranchConfig.java @@ -15,8 +15,8 @@ */ package git4idea.ui.branch; +import com.intellij.dvcs.branch.DvcsMultiRootBranchConfig; import com.intellij.util.containers.ContainerUtil; -import git4idea.GitBranch; import git4idea.GitLocalBranch; import git4idea.GitRemoteBranch; import git4idea.branch.GitBranchUtil; @@ -32,54 +32,17 @@ import java.util.Collections; /** * @author Kirill Likhodedov */ -public class GitMultiRootBranchConfig { - - private final Collection myRepositories; +public class GitMultiRootBranchConfig extends DvcsMultiRootBranchConfig { public GitMultiRootBranchConfig(@NotNull Collection repositories) { - myRepositories = repositories; + super(repositories); } - boolean diverged() { - return getCurrentBranch() == null; - } - - @Nullable - public String getCurrentBranch() { - String commonBranch = null; - for (GitRepository repository : myRepositories) { - GitBranch branch = repository.getCurrentBranch(); - if (branch == null) { - return null; - } - // NB: if all repositories are in the rebasing state on the same branches, this branch is returned - if (commonBranch == null) { - commonBranch = branch.getName(); - } else if (!commonBranch.equals(branch.getName())) { - return null; - } - } - return commonBranch; - } - - @Nullable - GitRepository.State getState() { - GitRepository.State commonState = null; - for (GitRepository repository : myRepositories) { - GitRepository.State state = repository.getState(); - if (commonState == null) { - commonState = state; - } else if (!commonState.equals(state)) { - return null; - } - } - return commonState; - } - + @Override @NotNull - Collection getLocalBranches() { + public Collection getLocalBranchNames() { return GitBranchUtil.getCommonBranches(myRepositories, true); - } + } @NotNull Collection getRemoteBranches() { diff --git a/plugins/git4idea/src/git4idea/validators/GitNewBranchNameValidator.java b/plugins/git4idea/src/git4idea/validators/GitNewBranchNameValidator.java index 69b06d04cc9b..40beb2686f84 100644 --- a/plugins/git4idea/src/git4idea/validators/GitNewBranchNameValidator.java +++ b/plugins/git4idea/src/git4idea/validators/GitNewBranchNameValidator.java @@ -51,7 +51,7 @@ public final class GitNewBranchNameValidator implements InputValidatorEx { } @Override - public boolean checkInput(String inputString) { + public boolean checkInput(@NotNull String inputString) { if (!GitRefNameValidator.getInstance().checkInput(inputString)){ myErrorText = "Invalid name for branch"; return false; @@ -59,7 +59,7 @@ public final class GitNewBranchNameValidator implements InputValidatorEx { return checkBranchConflict(inputString); } - private boolean checkBranchConflict(String inputString) { + private boolean checkBranchConflict(@NotNull String inputString) { if (isNotPermitted(inputString) || conflictsWithLocalBranch(inputString) || conflictsWithRemoteBranch(inputString)) { return false; } @@ -75,28 +75,38 @@ public final class GitNewBranchNameValidator implements InputValidatorEx { return false; } - private boolean conflictsWithLocalBranch(String inputString) { + private boolean conflictsWithLocalBranch(@NotNull String inputString) { return conflictsWithLocalOrRemote(inputString, true, " already exists"); } - private boolean conflictsWithRemoteBranch(String inputString) { + private boolean conflictsWithRemoteBranch(@NotNull String inputString) { return conflictsWithLocalOrRemote(inputString, false, " clashes with remote branch with the same name"); } - private boolean conflictsWithLocalOrRemote(String inputString, boolean local, String message) { + private boolean conflictsWithLocalOrRemote(@NotNull String inputString, boolean local, @NotNull String message) { + int conflictsWithCurrentName = 0; for (GitRepository repository : myRepositories) { - GitBranchesCollection branchesCollection = repository.getBranches(); - Collection branches = local ? branchesCollection.getLocalBranches() : branchesCollection.getRemoteBranches(); - for (GitBranch branch : branches) { - if (branch.getName().equals(inputString)) { - myErrorText = "Branch name " + inputString + message; - if (myRepositories.size() > 1 && !allReposHaveBranch(inputString, local)) { - myErrorText += " in repository " + repository.getPresentableUrl(); + if (inputString.equals(repository.getCurrentBranchName())) { + conflictsWithCurrentName++; + } + else { + GitBranchesCollection branchesCollection = repository.getBranches(); + Collection branches = local ? branchesCollection.getLocalBranches() : branchesCollection.getRemoteBranches(); + for (GitBranch branch : branches) { + if (branch.getName().equals(inputString)) { + myErrorText = "Branch name " + inputString + message; + if (myRepositories.size() > 1 && !allReposHaveBranch(inputString, local)) { + myErrorText += " in repository " + repository.getPresentableUrl(); + } + return true; } - return true; } } } + if (conflictsWithCurrentName == myRepositories.size()) { + myErrorText = "You are already on branch " + inputString; + return true; + } return false; } diff --git a/plugins/git4idea/tests/git4idea/log/GitLogProviderTest.java b/plugins/git4idea/tests/git4idea/log/GitLogProviderTest.java index 0bb67671622b..313852f118b0 100644 --- a/plugins/git4idea/tests/git4idea/log/GitLogProviderTest.java +++ b/plugins/git4idea/tests/git4idea/log/GitLogProviderTest.java @@ -15,13 +15,13 @@ */ package git4idea.log; +import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vcs.VcsException; import com.intellij.util.ArrayUtil; import com.intellij.util.CollectConsumer; -import com.intellij.util.Consumer; import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; import com.intellij.vcs.log.*; @@ -40,7 +40,8 @@ import static git4idea.test.GitExecutor.*; public class GitLogProviderTest extends GitSingleRepoTest { - @NotNull private GitLogProvider myLogProvider; + private GitLogProvider myLogProvider; + private VcsLogObjectsFactory myObjectsFactory; public void setUp() throws Exception { super.setUp(); @@ -53,6 +54,7 @@ public class GitLogProviderTest extends GitSingleRepoTest { }); assertEquals("Incorrect number of GitLogProviders", 1, providers.size()); myLogProvider = (GitLogProvider)providers.get(0); + myObjectsFactory = ServiceManager.getService(myProject, VcsLogObjectsFactory.class); } public void tearDown() throws Exception { @@ -64,21 +66,51 @@ public class GitLogProviderTest extends GitSingleRepoTest { List expectedLogWithoutTaggedBranch = log(); createTaggedBranch(); - Set noRefs = Collections.emptySet(); - List block = myLogProvider.readFirstBlock(myProjectRoot, new RequirementsImpl(1000, false, noRefs, noRefs)); - assertOrderedEquals(block, expectedLogWithoutTaggedBranch); + VcsLogProvider.DetailedLogData block = myLogProvider.readFirstBlock(myProjectRoot,new RequirementsImpl(1000, false, Collections.emptySet())); + assertOrderedEquals(block.getCommits(), expectedLogWithoutTaggedBranch); } public void test_refresh_with_new_tagged_branch() throws VcsException { prepareSomeHistory(); - Set prevRefs = ContainerUtil.newHashSet(myLogProvider.readAllRefs(myProjectRoot)); + Set prevRefs = readAllRefs(); createTaggedBranch(); - Set newRefs = ContainerUtil.newHashSet(myLogProvider.readAllRefs(myProjectRoot)); List expectedLog = log(); - List block = myLogProvider.readFirstBlock(myProjectRoot, - new RequirementsImpl(1000, true, prevRefs, newRefs)); - assertSameElements(block, expectedLog); + VcsLogProvider.DetailedLogData block = myLogProvider.readFirstBlock(myProjectRoot, new RequirementsImpl(1000, true, prevRefs)); + assertSameElements(block.getCommits(), expectedLog); + } + + public void test_refresh_when_new_tag_moved() throws VcsException { + prepareSomeHistory(); + Set prevRefs = readAllRefs(); + git("tag -f ATAG"); + + List expectedLog = log(); + Set refs = readAllRefs(); + VcsLogProvider.DetailedLogData block = myLogProvider.readFirstBlock(myProjectRoot, new RequirementsImpl(1000, true, prevRefs)); + assertSameElements(block.getCommits(), expectedLog); + assertSameElements(block.getRefs(), refs); + } + + public void test_new_tag_on_old_commit() throws VcsException { + prepareSomeHistory(); + Set prevRefs = readAllRefs(); + List log = log(); + String firstCommit = log.get(log.size() - 1).getId().asString(); + git("tag NEW_TAG " + firstCommit); + + Set refs = readAllRefs(); + VcsLogProvider.DetailedLogData block = myLogProvider.readFirstBlock(myProjectRoot, new RequirementsImpl(1000, true, prevRefs)); + assertSameElements(block.getRefs(), refs); + } + + private Set readAllRefs() { + String[] refs = StringUtil.splitByLines(git("log --branches --tags --no-walk --format=%H%d --decorate=full")); + Set result = ContainerUtil.newHashSet(); + for (String ref : refs) { + result.addAll(new RefParser(myObjectsFactory).parseCommitRefs(ref, myProjectRoot)); + } + return result; } public void test_all_log_with_tagged_branch() throws VcsException { @@ -87,7 +119,7 @@ public class GitLogProviderTest extends GitSingleRepoTest { List expectedLog = log(); List collector = ContainerUtil.newArrayList(); //noinspection unchecked - myLogProvider.readAllHashes(myProjectRoot, Consumer.EMPTY_CONSUMER, new CollectConsumer(collector)); + myLogProvider.readAllHashes(myProjectRoot, new CollectConsumer(collector)); assertOrderedEquals(expectedLog, collector); } diff --git a/plugins/git4idea/tests/git4idea/log/GitRefManagerTest.java b/plugins/git4idea/tests/git4idea/log/GitRefManagerTest.java index 2ce560800066..39d5946c90ff 100644 --- a/plugins/git4idea/tests/git4idea/log/GitRefManagerTest.java +++ b/plugins/git4idea/tests/git4idea/log/GitRefManagerTest.java @@ -284,6 +284,12 @@ public class GitRefManagerTest extends UsefulTestCase { throw new UnsupportedOperationException(); } + @Nullable + @Override + public String getCurrentBranchName() { + throw new UnsupportedOperationException(); + } + @Nullable @Override public AbstractVcs getVcs() { diff --git a/plugins/git4idea/tests/git4idea/log/RefParser.java b/plugins/git4idea/tests/git4idea/log/RefParser.java new file mode 100644 index 000000000000..9a4cc962c6e7 --- /dev/null +++ b/plugins/git4idea/tests/git4idea/log/RefParser.java @@ -0,0 +1,63 @@ +package git4idea.log; + +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.vcs.log.Hash; +import com.intellij.vcs.log.VcsLogObjectsFactory; +import com.intellij.vcs.log.VcsRef; +import com.intellij.vcs.log.VcsRefType; +import com.intellij.vcs.log.impl.HashImpl; +import git4idea.branch.GitBranchUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +class RefParser { + + private final VcsLogObjectsFactory myFactory; + + public RefParser(@NotNull VcsLogObjectsFactory factory) { + myFactory = factory; + } + + // e25b7d8f (HEAD, refs/remotes/origin/master, refs/remotes/origin/HEAD, refs/heads/master) + public List parseCommitRefs(@NotNull String input, @NotNull VirtualFile root) { + int firstSpaceIndex = input.indexOf(' '); + if (firstSpaceIndex < 0) { + return Collections.emptyList(); + } + String strHash = input.substring(0, firstSpaceIndex); + Hash hash = HashImpl.build(strHash); + String refPaths = input.substring(firstSpaceIndex + 2, input.length() - 1); + String[] longRefPaths = refPaths.split(", "); + List refs = new ArrayList(); + for (String longRefPatch : longRefPaths) { + VcsRef ref = createRef(hash, longRefPatch, root); + if (ref != null) { + refs.add(ref); + } + } + return refs; + } + + @NotNull + private static String getRefName(@NotNull String longRefPath) { + String tagPrefix = "tag: "; + if (longRefPath.startsWith(tagPrefix)) { + longRefPath = longRefPath.substring(tagPrefix.length()); + } + return longRefPath; + } + + // example input: fb29c80 refs/tags/92.29 + @Nullable + private VcsRef createRef(@NotNull Hash hash, @NotNull String longRefPath, @NotNull VirtualFile root) { + String name = getRefName(longRefPath); + VcsRefType type = GitRefManager.getRefType(name); + assert type != null; + return myFactory.createRef(hash, GitBranchUtil.stripRefsPrefix(name), type, root); + } + +} diff --git a/plugins/git4idea/tests/git4idea/log/RefParserTest.java b/plugins/git4idea/tests/git4idea/log/RefParserTest.java deleted file mode 100644 index 4cd22e0add5c..000000000000 --- a/plugins/git4idea/tests/git4idea/log/RefParserTest.java +++ /dev/null @@ -1,125 +0,0 @@ -package git4idea.log; - -import com.intellij.openapi.util.ThrowableComputable; -import com.intellij.openapi.vcs.changes.Change; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.openapi.vfs.newvfs.impl.NullVirtualFile; -import com.intellij.vcs.log.*; -import com.intellij.vcs.log.impl.VcsRefImpl; -import org.jetbrains.annotations.NotNull; -import org.junit.Test; - -import java.util.Collection; -import java.util.List; - -import static junit.framework.Assert.assertEquals; - -/** - * @author erokhins - */ -public class RefParserTest { - - public String toStr(VcsRef ref) { - return String.format("%s TAG %s", ref.getCommitHash().asString(), ref.getName()); - } - - public void runTest(String inputStr, String outStr) { - List refs = new RefParser(new TestLogObjectsFactory()).parseCommitRefs(inputStr, NullVirtualFile.INSTANCE); - StringBuilder s = new StringBuilder(); - for (VcsRef ref : refs) { - if (s.length() > 0) { - s.append("\n"); - } - s.append(toStr(ref)); - } - assertEquals(outStr, s.toString()); - } - - @Test - public void tagTest() { - runTest("22762ebf7203f6a2888425a3207d2ddc63085dd7 (tag: refs/tags/v3.6-rc1, refs/heads/br)", - "22762ebf7203f6a2888425a3207d2ddc63085dd7 TAG v3.6-rc1"); - } - - @Test - public void severalRefsTest() { - runTest("f85125c (refs/tags/v3.6-rc1, HEAD)", "f85125c TAG v3.6-rc1"); - } - - @Test - public void severalRefsTest2() { - runTest("ed7a0d14da090ea256d68a06c6f8dd7311de192e (refs/tags/category/v3.6-rc1, HEAD, refs/remotes/origin/graph_fix)", - "ed7a0d14da090ea256d68a06c6f8dd7311de192e TAG category/v3.6-rc1"); - } - - @Test - public void severalRefsTest3() { - runTest("ed7a0d14da090ea256d68a06c6f8dd7311de192e (tag: refs/tags/web/130.1599, tag: refs/tags/ruby/130.1597, " + - "tag: refs/tags/py/130.1598, " + - "tag: refs/tags/php/130.1596, tag: refs/tags/idea/130.1601, tag: refs/tags/app/130.1600)", - "ed7a0d14da090ea256d68a06c6f8dd7311de192e TAG web/130.1599\n" + - "ed7a0d14da090ea256d68a06c6f8dd7311de192e TAG ruby/130.1597\n" + - "ed7a0d14da090ea256d68a06c6f8dd7311de192e TAG py/130.1598\n" + - "ed7a0d14da090ea256d68a06c6f8dd7311de192e TAG php/130.1596\n" + - "ed7a0d14da090ea256d68a06c6f8dd7311de192e TAG idea/130.1601\n" + - "ed7a0d14da090ea256d68a06c6f8dd7311de192e TAG app/130.1600" - ); - } - - @Test - public void noTagName() { - runTest("787ec72f340d740433ba068d4d58a6e58f6226bf", ""); - } - - private static class TestLogObjectsFactory implements VcsLogObjectsFactory { - @NotNull - @Override - public Hash createHash(@NotNull String stringHash) { - throw new UnsupportedOperationException(); - } - - @NotNull - @Override - public TimedVcsCommit createTimedCommit(@NotNull Hash hash, @NotNull List parents, long timeStamp) { - throw new UnsupportedOperationException(); - } - - @NotNull - @Override - public VcsShortCommitDetails createShortDetails(@NotNull Hash hash, @NotNull List parents, long timeStamp, VirtualFile root, - @NotNull String subject, @NotNull String authorName, String authorEmail) { - throw new UnsupportedOperationException(); - } - - @NotNull - @Override - public VcsFullCommitDetails createCommitMetadata(@NotNull Hash hash, @NotNull List parents, long time, VirtualFile root, - @NotNull String subject, @NotNull String authorName, @NotNull String authorEmail, - @NotNull String message, @NotNull String committerName, @NotNull String committerEmail, - long authorTime) { - throw new UnsupportedOperationException(); - } - - @NotNull - @Override - public VcsFullCommitDetails createFullDetails(@NotNull Hash hash, @NotNull List parents, long time, VirtualFile root, - @NotNull String subject, @NotNull String authorName, @NotNull String authorEmail, - @NotNull String message, @NotNull String committerName, @NotNull String committerEmail, - long authorTime, - @NotNull ThrowableComputable, ? extends Exception> changesGetter) { - throw new UnsupportedOperationException(); - } - - @NotNull - @Override - public VcsUser createUser(@NotNull String name, @NotNull String email) { - throw new UnsupportedOperationException(); - } - - @NotNull - @Override - public VcsRef createRef(@NotNull Hash commitHash, @NotNull String name, @NotNull VcsRefType type, @NotNull VirtualFile root) { - return new VcsRefImpl(commitHash, name, type, root); - } - } -} diff --git a/plugins/github/github.iml b/plugins/github/github.iml index 7866631c1521..1b14d6b1e8a9 100644 --- a/plugins/github/github.iml +++ b/plugins/github/github.iml @@ -11,7 +11,7 @@ - + diff --git a/plugins/github/src/org/jetbrains/plugins/github/GithubCreateGistAction.java b/plugins/github/src/org/jetbrains/plugins/github/GithubCreateGistAction.java index 43753142a5b2..ae7b271ce3d6 100644 --- a/plugins/github/src/org/jetbrains/plugins/github/GithubCreateGistAction.java +++ b/plugins/github/src/org/jetbrains/plugins/github/GithubCreateGistAction.java @@ -38,6 +38,7 @@ import icons.GithubIcons; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.github.api.GithubApiUtil; +import org.jetbrains.plugins.github.api.GithubConnection; import org.jetbrains.plugins.github.api.GithubGist; import org.jetbrains.plugins.github.exceptions.GithubOperationCanceledException; import org.jetbrains.plugins.github.ui.GithubCreateGistDialog; @@ -219,11 +220,11 @@ public class GithubCreateGistAction extends DumbAwareAction { } try { final List finalContents = contents; - return GithubUtil.runTask(project, auth, indicator, new ThrowableConvertor() { + return GithubUtil.runTask(project, auth, indicator, new ThrowableConvertor() { @NotNull @Override - public GithubGist convert(@NotNull GithubAuthData auth) throws IOException { - return GithubApiUtil.createGist(auth, finalContents, description, isPrivate); + public GithubGist convert(@NotNull GithubConnection connection) throws IOException { + return GithubApiUtil.createGist(connection, finalContents, description, isPrivate); } }).getHtmlUrl(); } diff --git a/plugins/github/src/org/jetbrains/plugins/github/GithubCreatePullRequestAction.java b/plugins/github/src/org/jetbrains/plugins/github/GithubCreatePullRequestAction.java index 636415e5fcb6..d3fa265dc389 100644 --- a/plugins/github/src/org/jetbrains/plugins/github/GithubCreatePullRequestAction.java +++ b/plugins/github/src/org/jetbrains/plugins/github/GithubCreatePullRequestAction.java @@ -26,7 +26,7 @@ import icons.GithubIcons; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.github.ui.GithubCreatePullRequestDialog; -import org.jetbrains.plugins.github.util.*; +import org.jetbrains.plugins.github.util.GithubUtil; import static org.jetbrains.plugins.github.util.GithubUtil.setVisibleEnabled; @@ -73,12 +73,12 @@ public class GithubCreatePullRequestAction extends DumbAwareAction { } static void createPullRequest(@NotNull Project project, @Nullable VirtualFile file) { - GithubCreatePullRequestWorker worker = GithubCreatePullRequestWorker.createPullRequestWorker(project, file); + GithubCreatePullRequestWorker worker = GithubCreatePullRequestWorker.create(project, file); if (worker == null) { return; } - GithubCreatePullRequestDialog dialog = new GithubCreatePullRequestDialog(worker); + GithubCreatePullRequestDialog dialog = new GithubCreatePullRequestDialog(project, worker); DialogManager.show(dialog); } } \ No newline at end of file diff --git a/plugins/github/src/org/jetbrains/plugins/github/GithubCreatePullRequestWorker.java b/plugins/github/src/org/jetbrains/plugins/github/GithubCreatePullRequestWorker.java index bf9803b6f46f..07ed51bfca7d 100644 --- a/plugins/github/src/org/jetbrains/plugins/github/GithubCreatePullRequestWorker.java +++ b/plugins/github/src/org/jetbrains/plugins/github/GithubCreatePullRequestWorker.java @@ -1,30 +1,16 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ package org.jetbrains.plugins.github; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.progress.EmptyProgressIndicator; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.Couple; import com.intellij.openapi.util.Pair; -import com.intellij.openapi.util.Ref; +import com.intellij.openapi.util.ThrowableComputable; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.changes.Change; @@ -34,11 +20,10 @@ import com.intellij.util.Function; import com.intellij.util.ThrowableConvertor; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.Convertor; -import com.intellij.util.containers.HashMap; +import com.intellij.vcs.log.VcsCommitMetadata; import git4idea.DialogManager; import git4idea.GitCommit; import git4idea.GitLocalBranch; -import git4idea.GitRemoteBranch; import git4idea.changes.GitChangeUtils; import git4idea.commands.Git; import git4idea.commands.GitCommandResult; @@ -52,18 +37,19 @@ import git4idea.util.GitCommitCompareInfo; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.github.api.*; +import org.jetbrains.plugins.github.exceptions.GithubOperationCanceledException; import org.jetbrains.plugins.github.ui.GithubSelectForkDialog; import org.jetbrains.plugins.github.util.*; import java.io.IOException; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; import java.util.concurrent.Callable; +import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; -/** - * @author Aleksey Pivovarov - */ public class GithubCreatePullRequestWorker { private static final Logger LOG = GithubUtil.LOG; private static final String CANNOT_CREATE_PULL_REQUEST = "Can't Create Pull Request"; @@ -71,40 +57,36 @@ public class GithubCreatePullRequestWorker { @NotNull private final Project myProject; @NotNull private final Git myGit; @NotNull private final GitRepository myGitRepository; + @NotNull private final GithubAuthDataHolder myAuthHolder; + @NotNull private final GithubFullPath myPath; @NotNull private final String myRemoteName; @NotNull private final String myRemoteUrl; @NotNull private final String myCurrentBranch; - @NotNull private final GithubAuthDataHolder myAuthHolder; - @NotNull private final Map> myDiffInfos; + @NotNull private GithubFullPath mySource; - private volatile GithubFullPath myForkPath; - private volatile String myTargetRemote; + @NotNull private final List myForks; + @Nullable private List myAvailableForks; private GithubCreatePullRequestWorker(@NotNull Project project, @NotNull Git git, @NotNull GitRepository gitRepository, + @NotNull GithubAuthDataHolder authHolder, @NotNull GithubFullPath path, @NotNull String remoteName, @NotNull String remoteUrl, - @NotNull String currentBranch, - @NotNull GithubAuthDataHolder authHolder) { + @NotNull String currentBranch) { myProject = project; myGit = git; myGitRepository = gitRepository; + myAuthHolder = authHolder; myPath = path; myRemoteName = remoteName; myRemoteUrl = remoteUrl; myCurrentBranch = currentBranch; - myAuthHolder = authHolder; - - myDiffInfos = new HashMap>(); - } - @NotNull - public Project getProject() { - return myProject; + myForks = new ArrayList(); } @NotNull @@ -112,231 +94,416 @@ public class GithubCreatePullRequestWorker { return myCurrentBranch; } - public boolean canShowDiff() { - return myTargetRemote != null; + @NotNull + public List getForks() { + return myForks; } @Nullable - public static GithubCreatePullRequestWorker createPullRequestWorker(@NotNull final Project project, @Nullable final VirtualFile file) { - Git git = ServiceManager.getService(Git.class); + public static GithubCreatePullRequestWorker create(@NotNull final Project project, @Nullable final VirtualFile file) { + return GithubUtil.computeValueInModal(project, "Loading data...", new Convertor() { + @Override + public GithubCreatePullRequestWorker convert(ProgressIndicator indicator) { + Git git = ServiceManager.getService(Git.class); - GitRepository gitRepository = GithubUtil.getGitRepository(project, file); - if (gitRepository == null) { - GithubNotifications.showError(project, CANNOT_CREATE_PULL_REQUEST, "Can't find git repository"); - return null; + GitRepository gitRepository = GithubUtil.getGitRepository(project, file); + if (gitRepository == null) { + GithubNotifications.showError(project, CANNOT_CREATE_PULL_REQUEST, "Can't find git repository"); + return null; + } + gitRepository.update(); + + Pair remote = GithubUtil.findGithubRemote(gitRepository); + if (remote == null) { + GithubNotifications.showError(project, CANNOT_CREATE_PULL_REQUEST, "Can't find GitHub remote"); + return null; + } + String remoteName = remote.getFirst().getName(); + String remoteUrl = remote.getSecond(); + + GithubFullPath path = GithubUrlUtil.getUserAndRepositoryFromRemoteUrl(remoteUrl); + if (path == null) { + GithubNotifications.showError(project, CANNOT_CREATE_PULL_REQUEST, "Can't process remote: " + remoteUrl); + return null; + } + + GitLocalBranch currentBranch = gitRepository.getCurrentBranch(); + if (currentBranch == null) { + GithubNotifications.showError(project, CANNOT_CREATE_PULL_REQUEST, "No current branch"); + return null; + } + + GithubAuthDataHolder authHolder; + try { + authHolder = GithubUtil.getValidAuthDataHolderFromConfig(project, indicator); + } + catch (IOException e) { + GithubNotifications.showError(project, CANNOT_CREATE_PULL_REQUEST, e); + return null; + } + + GithubCreatePullRequestWorker worker = + new GithubCreatePullRequestWorker(project, git, gitRepository, authHolder, path, remoteName, remoteUrl, currentBranch.getName()); + + try { + worker.initForks(indicator); + } + catch (IOException e) { + GithubNotifications.showError(project, CANNOT_CREATE_PULL_REQUEST, e); + return null; + } + + return worker; + } + }); + } + + private void initForks(@NotNull ProgressIndicator indicator) throws IOException { + doLoadForksFromGithub(indicator); + doLoadForksFromGit(indicator); + doLoadForksFromSettings(indicator); + } + + @Nullable + private ForkInfo doAddFork(@NotNull GithubFullPath path, + @Nullable String remoteName, + @NotNull ProgressIndicator indicator) { + for (ForkInfo fork : myForks) { + if (fork.getPath().equals(path)) { + if (fork.getRemoteName() == null && remoteName != null) { + fork.setRemoteName(remoteName); + } + return fork; + } } - gitRepository.update(); - Pair remote = GithubUtil.findGithubRemote(gitRepository); - if (remote == null) { - GithubNotifications.showError(project, CANNOT_CREATE_PULL_REQUEST, "Can't find GitHub remote"); - return null; + try { + List branches = loadBranches(path, indicator); + String defaultBranch = doLoadDefaultBranch(path, indicator); + + ForkInfo fork = new ForkInfo(path, branches, defaultBranch); + myForks.add(fork); + if (remoteName != null) { + fork.setRemoteName(remoteName); + } + return fork; } - String remoteName = remote.getFirst().getName(); - String remoteUrl = remote.getSecond(); - GithubFullPath path = GithubUrlUtil.getUserAndRepositoryFromRemoteUrl(remoteUrl); - if (path == null) { - GithubNotifications.showError(project, CANNOT_CREATE_PULL_REQUEST, "Can't process remote: " + remoteUrl); + catch (IOException e) { + GithubNotifications.showWarning(myProject, "Can't load branches for " + path.getFullName(), e); return null; } + } - GitLocalBranch currentBranch = gitRepository.getCurrentBranch(); - if (currentBranch == null) { - GithubNotifications.showError(project, CANNOT_CREATE_PULL_REQUEST, "No current branch"); - return null; + @Nullable + private ForkInfo doAddFork(@NotNull GithubRepo repo, @NotNull ProgressIndicator indicator) { + GithubFullPath path = repo.getFullPath(); + for (ForkInfo fork : myForks) { + if (fork.getPath().equals(path)) { + return fork; + } } - GithubAuthDataHolder authHolder; try { - authHolder = GithubUtil - .computeValueInModal(project, "Access to GitHub", new ThrowableConvertor() { - @NotNull - @Override - public GithubAuthDataHolder convert(ProgressIndicator indicator) throws IOException { - return GithubUtil.getValidAuthDataHolderFromConfig(project, indicator); - } - }); + List branches = loadBranches(path, indicator); + String defaultBranch = repo.getDefaultBranch(); + + ForkInfo fork = new ForkInfo(path, branches, defaultBranch); + myForks.add(fork); + return fork; } catch (IOException e) { - GithubNotifications.showError(project, CANNOT_CREATE_PULL_REQUEST, e); + GithubNotifications.showWarning(myProject, "Can't load branches for " + path.getFullName(), e); return null; } + } - return new GithubCreatePullRequestWorker(project, git, gitRepository, path, remoteName, remoteUrl, currentBranch.getName(), authHolder); + private void doLoadForksFromSettings(@NotNull ProgressIndicator indicator) throws IOException { + GithubFullPath savedRepo = GithubProjectSettings.getInstance(myProject).getCreatePullRequestDefaultRepo(); + if (savedRepo != null) { + doAddFork(savedRepo, null, indicator); + } } - @Nullable - public GithubTargetInfo setTarget(@NotNull final GithubFullPath forkPath) { - try { - GithubInfo info = - GithubUtil.computeValueInModal(myProject, "Access to GitHub", new ThrowableConvertor() { - @NotNull - @Override - public GithubInfo convert(ProgressIndicator indicator) throws IOException { - // configure remote - GitRemote targetRemote = GithubUtil.findGithubRemote(myGitRepository, forkPath); - String targetRemoteName = targetRemote == null ? null : targetRemote.getName(); - if (targetRemoteName == null) { - final Ref responseRef = new Ref(); - ApplicationManager.getApplication().invokeAndWait(new Runnable() { - @Override - public void run() { - responseRef.set(GithubNotifications - .showYesNoDialog(myProject, "Can't Find Remote", "Configure remote for '" + forkPath.getUser() + "'?")); - } - }, indicator.getModalityState()); - if (responseRef.get() == Messages.YES) { - targetRemoteName = configureRemote(myProject, myGitRepository, forkPath); - } - } + private void doLoadForksFromGit(@NotNull ProgressIndicator indicator) { + for (GitRemote remote : myGitRepository.getRemotes()) { + for (String url : remote.getUrls()) { + if (GithubUrlUtil.isGithubUrl(url)) { + GithubFullPath path = GithubUrlUtil.getUserAndRepositoryFromRemoteUrl(url); + if (path != null) { + doAddFork(path, remote.getName(), indicator); + break; + } + } + } + } + } - // load available branches - List branches = ContainerUtil.map(GithubUtil.runTask(myProject, myAuthHolder, indicator, - new ThrowableConvertor, IOException>() { - @Override - public List convert(@NotNull GithubAuthData auth) - throws IOException { - return GithubApiUtil.getRepoBranches(auth, forkPath.getUser(), - forkPath.getRepository()); - } - } - ), new Function() { - @Override - public String fun(GithubBranch githubBranch) { - return githubBranch.getName(); - } - }); + private void doLoadForksFromGithub(@NotNull ProgressIndicator indicator) throws IOException { + GithubRepoDetailed repo = + GithubUtil.runTask(myProject, myAuthHolder, indicator, new ThrowableConvertor() { + @NotNull + @Override + public GithubRepoDetailed convert(@NotNull GithubConnection connection) throws IOException { + return GithubApiUtil.getDetailedRepoInfo(connection, myPath.getUser(), myPath.getRepository()); + } + }); - // fetch - if (targetRemoteName != null) { - GitFetchResult result = new GitFetcher(myProject, indicator, false).fetch(myGitRepository.getRoot(), targetRemoteName, null); - if (!result.isSuccess()) { - GitFetcher.displayFetchResult(myProject, result, null, result.getErrors()); - targetRemoteName = null; - } - } + doAddFork(repo, indicator); + if (repo.getParent() != null) { + doAddFork(repo.getParent(), indicator); + } + if (repo.getSource() != null) { + doAddFork(repo.getSource(), indicator); + } - return new GithubInfo(branches, targetRemoteName); - } - }); + mySource = repo.getSource() == null ? repo.getFullPath() : repo.getSource().getFullPath(); + } - myForkPath = forkPath; - myTargetRemote = info.getTargetRemote(); - - myDiffInfos.clear(); - if (canShowDiff()) { - for (final String branch : info.getBranches()) { - myDiffInfos.put(branch, new FutureTask(new Callable() { - @Nullable - @Override - public DiffInfo call() throws Exception { - return loadDiffInfo(myProject, myGitRepository, myCurrentBranch, myTargetRemote + "/" + branch); - } - })); + @NotNull + private List loadBranches(@NotNull final GithubFullPath fork, @NotNull ProgressIndicator indicator) throws IOException { + return ContainerUtil.map( + GithubUtil.runTask(myProject, myAuthHolder, indicator, new ThrowableConvertor, IOException>() { + @Override + public List convert(@NotNull GithubConnection connection) throws IOException { + return GithubApiUtil.getRepoBranches(connection, fork.getUser(), fork.getRepository()); + } + }), + new Function() { + @Override + public String fun(@NotNull GithubBranch branch) { + return branch.getName(); } } + ); + } + + @Nullable + private String doLoadDefaultBranch(@NotNull final GithubFullPath fork, @NotNull ProgressIndicator indicator) throws IOException { + GithubRepo repo = + GithubUtil.runTask(myProject, myAuthHolder, indicator, new ThrowableConvertor() { + @Override + public GithubRepo convert(@NotNull GithubConnection connection) throws IOException { + return GithubApiUtil.getDetailedRepoInfo(connection, fork.getUser(), fork.getRepository()); + } + }); + return repo.getDefaultBranch(); + } + + public void launchFetchRemote(@NotNull final ForkInfo fork) { + if (fork.getRemoteName() == null) return; + + if (fork.getFetchTask() != null) return; + synchronized (fork.LOCK) { + if (fork.getFetchTask() != null) return; - return new GithubTargetInfo(info.getBranches()); + final MasterFutureTask task = new MasterFutureTask(new Callable() { + @Override + public Void call() throws Exception { + doFetchRemote(fork); + return null; + } + }); + fork.setFetchTask(task); + + ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { + @Override + public void run() { + task.run(); + } + }); } - catch (IOException e) { - GithubNotifications.showErrorDialog(myProject, CANNOT_CREATE_PULL_REQUEST, e); + } + + public void launchLoadDiffInfo(@NotNull final BranchInfo branch) { + if (branch.getForkInfo().getRemoteName() == null) return; + + if (branch.getDiffInfoTask() != null) return; + synchronized (branch.LOCK) { + if (branch.getDiffInfoTask() != null) return; + + launchFetchRemote(branch.getForkInfo()); + MasterFutureTask masterTask = branch.getForkInfo().getFetchTask(); + assert masterTask != null; + + final SlaveFutureTask task = new SlaveFutureTask(masterTask, new Callable() { + @Override + public DiffInfo call() throws VcsException { + return doLoadDiffInfo(branch); + } + }); + branch.setDiffInfoTask(task); + + ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { + @Override + public void run() { + task.run(); + } + }); + } + } + + @Nullable + public DiffInfo getDiffInfo(@NotNull final BranchInfo branch) throws IOException { + if (branch.getForkInfo().getRemoteName() == null) return null; + + launchLoadDiffInfo(branch); + + assert branch.getDiffInfoTask() != null; + try { + return branch.getDiffInfoTask().get(); + } + catch (InterruptedException e) { + throw new GithubOperationCanceledException(e); + } + catch (ExecutionException e) { + Throwable ex = e.getCause(); + if (ex instanceof VcsException) throw new IOException(ex); + LOG.error(ex); return null; } } - public void showDiffDialog(@NotNull String branch) { - if (canShowDiff()) { - DiffInfo info = getDiffInfoWithModal(branch); - if (info == null) { - GithubNotifications.showErrorDialog(myProject, "Can't Show Diff", "Can't get diff info"); - return; - } + private boolean doFetchRemote(@NotNull ForkInfo fork) { + if (fork.getRemoteName() == null) return false; - GitCompareBranchesDialog dialog = - new GitCompareBranchesDialog(myProject, info.getTo(), info.getFrom(), info.getInfo(), myGitRepository); - dialog.show(); + GitFetchResult result = + new GitFetcher(myProject, new EmptyProgressIndicator(), false).fetch(myGitRepository.getRoot(), fork.getRemoteName(), null); + if (!result.isSuccess()) { + GitFetcher.displayFetchResult(myProject, result, null, result.getErrors()); + return false; } + return true; } - @Nullable - public GithubFullPath showTargetDialog() { - return showTargetDialog(false); + @NotNull + private DiffInfo doLoadDiffInfo(@NotNull final BranchInfo branch) throws VcsException { + // TODO: make cancelable and abort old speculative requests (when git4idea will allow to do so) + String currentBranch = myCurrentBranch; + String targetBranch = branch.getForkInfo().getRemoteName() + "/" + branch.getRemoteName(); + + List commits1 = GitHistoryUtils.history(myProject, myGitRepository.getRoot(), ".." + targetBranch); + List commits2 = GitHistoryUtils.history(myProject, myGitRepository.getRoot(), targetBranch + ".."); + Collection diff = GitChangeUtils.getDiff(myProject, myGitRepository.getRoot(), targetBranch, myCurrentBranch, null); + GitCommitCompareInfo info = new GitCommitCompareInfo(GitCommitCompareInfo.InfoType.BRANCH_TO_HEAD); + info.put(myGitRepository, diff); + info.put(myGitRepository, Couple.of(commits1, commits2)); + + return new DiffInfo(info, currentBranch, targetBranch); } - @Nullable - public GithubFullPath showTargetDialog(boolean firstTime) { - final GithubInfo2 info = getAvailableForksInModal(myProject, myGitRepository, myAuthHolder, myPath); - if (info == null) { - return null; + private void doConfigureRemote(@NotNull ForkInfo fork) { + if (fork.getRemoteName() != null) return; + + GithubFullPath path = fork.getPath(); + String url = GithubUrlUtil.getCloneUrl(path); + + if (GithubUtil.addGithubRemote(myProject, myGitRepository, path.getUser(), url)) { + fork.setRemoteName(path.getUser()); } + } - if (firstTime) { - if (info.getForks().size() == 1) { - return info.getForks().iterator().next(); + public void configureRemote(@NotNull final ForkInfo fork) { + GithubUtil.computeValueInModal(myProject, "Creating remote..", false, new Consumer() { + @Override + public void consume(ProgressIndicator indicator) { + doConfigureRemote(fork); } - if (info.getForks().size() == 2) { - Iterator it = info.getForks().iterator(); - GithubFullPath path1 = it.next(); - GithubFullPath path2 = it.next(); + }); + } - if (myPath.equals(path1)) { - return path2; - } - if (myPath.equals(path2)) { - return path1; + @NotNull + public Couple getDefaultDescriptionMessage(@NotNull final BranchInfo branch) { + Couple message = branch.getDefaultMessage(); + if (message != null) return message; + + if (branch.getForkInfo().getRemoteName() == null) { + return getSimpleDefaultDescriptionMessage(branch); + } + + return GithubUtil + .computeValueInModal(myProject, "Collecting additional data...", false, new Convertor>() { + @Override + public Couple convert(ProgressIndicator o) { + String localBranch = myCurrentBranch; + String targetBranch = branch.getForkInfo().getRemoteName() + "/" + branch.getRemoteName(); + try { + List commits = + GitHistoryUtils.readLastCommits(myProject, myGitRepository.getRoot(), localBranch, targetBranch); + if (commits == null) return getSimpleDefaultDescriptionMessage(branch); + + VcsCommitMetadata localCommit = commits.get(0); + VcsCommitMetadata targetCommit = commits.get(1); + + if (localCommit.getParents().contains(targetCommit.getId())) { + return Couple.of(localCommit.getSubject(), localCommit.getFullMessage()); + } + return getSimpleDefaultDescriptionMessage(branch); + } + catch (VcsException e) { + GithubNotifications.showWarning(myProject, "Can't collect additional data", e); + return getSimpleDefaultDescriptionMessage(branch); + } } - } + }); + } + + @NotNull + public Couple getSimpleDefaultDescriptionMessage(@NotNull final BranchInfo branch) { + Couple message = Couple.of(myCurrentBranch, ""); + branch.setDefaultMessage(message); + return message; + } + + public boolean checkAction(@Nullable final BranchInfo branch) { + if (branch == null) { + GithubNotifications.showWarningDialog(myProject, CANNOT_CREATE_PULL_REQUEST, "Target branch is not selected"); + return false; } - Convertor getForkPath = new Convertor() { - @Nullable - @Override - public GithubFullPath convert(@NotNull final String user) { - return GithubUtil.computeValueInModal(myProject, "Access to GitHub", new Convertor() { - @Nullable + DiffInfo info; + try { + info = GithubUtil + .computeValueInModal(myProject, "Collecting diff data...", new ThrowableConvertor() { @Override - public GithubFullPath convert(ProgressIndicator indicator) { - return findRepositoryByUser(myProject, myAuthHolder, indicator, user, info.getForks(), info.getSource()); + public DiffInfo convert(ProgressIndicator indicator) throws IOException { + return GithubUtil.runInterruptable(indicator, new ThrowableComputable() { + @Override + public DiffInfo compute() throws IOException { + return getDiffInfo(branch); + } + }); } }); - } - }; - GithubSelectForkDialog dialog = new GithubSelectForkDialog(myProject, info.getForks(), getForkPath); - DialogManager.show(dialog); - if (!dialog.isOK()) { - return null; } - return dialog.getPath(); - } - - public boolean checkAction(@NotNull String targetBranch) { - DiffInfo info = getDiffInfoWithModal(targetBranch); + catch (IOException e) { + GithubNotifications.showError(myProject, "Can't collect diff data", e); + return true; + } if (info == null) { return true; } - String localBranchName = "'" + getCurrentBranch() + "'"; - String targetBranchName = "'" + myTargetRemote + ":" + targetBranch + "'"; + ForkInfo fork = branch.getForkInfo(); + + String localBranchName = "'" + myCurrentBranch + "'"; + String targetBranchName = "'" + fork.getRemoteName() + "/" + branch.getRemoteName() + "'"; if (info.getInfo().getBranchToHeadCommits(myGitRepository).isEmpty()) { - GithubNotifications - .showWarningDialog(myProject, CANNOT_CREATE_PULL_REQUEST, - "Can't create empty pull request: the branch " + localBranchName + - " is fully merged to the branch " + targetBranchName - ); - return false; + return Messages.YES == GithubNotifications + .showYesNoDialog(myProject, "Do you want to proceed anyway?", + "Empty pull request: the branch " + localBranchName + " is fully merged to the branch " + targetBranchName); } if (!info.getInfo().getHeadToBranchCommits(myGitRepository).isEmpty()) { - return GithubNotifications - .showYesNoDialog(myProject, "Do you want to proceed anyway?", - "The branch " + targetBranchName + " is not fully merged to the branch " + localBranchName) == Messages.YES; + return Messages.YES == GithubNotifications + .showYesNoDialog(myProject, "Do you want to proceed anyway?", + "The branch " + targetBranchName + " is not fully merged to the branch " + localBranchName); } return true; } - public void performAction(@NotNull final String title, @NotNull final String description, @NotNull final String targetBranch) { - @NotNull final Project project = myProject; - + public void createPullRequest(@NotNull final BranchInfo branch, + @NotNull final String title, + @NotNull final String description) { new Task.Backgroundable(myProject, "Creating pull request...") { @Override public void run(@NotNull ProgressIndicator indicator) { @@ -344,298 +511,322 @@ public class GithubCreatePullRequestWorker { indicator.setText("Pushing current branch..."); GitCommandResult result = myGit.push(myGitRepository, myRemoteName, myRemoteUrl, myCurrentBranch, true); if (!result.success()) { - GithubNotifications.showError(project, CANNOT_CREATE_PULL_REQUEST, "Push failed:
" + result.getErrorOutputAsHtmlString()); + GithubNotifications.showError(myProject, CANNOT_CREATE_PULL_REQUEST, "Push failed:
" + result.getErrorOutputAsHtmlString()); return; } - String headBranch = myPath.getUser() + ":" + myCurrentBranch; - LOG.info("Creating pull request"); indicator.setText("Creating pull request..."); - GithubPullRequest request = - createPullRequest(project, myAuthHolder, indicator, myForkPath, title, description, headBranch, targetBranch); + GithubPullRequest request = doCreatePullRequest(indicator, branch, title, description); if (request == null) { return; } - GithubNotifications - .showInfoURL(project, "Successfully created pull request", "Pull request #" + request.getNumber(), request.getHtmlUrl()); + GithubNotifications.showInfoURL(myProject, "Successfully created pull request", + "Pull request #" + request.getNumber(), request.getHtmlUrl()); } }.queue(); } @Nullable - private static String configureRemote(@NotNull Project project, @NotNull GitRepository gitRepository, @NotNull GithubFullPath forkPath) { - String url = GithubUrlUtil.getCloneUrl(forkPath); + private GithubPullRequest doCreatePullRequest(@NotNull ProgressIndicator indicator, + @NotNull final BranchInfo branch, + @NotNull final String title, + @NotNull final String description) { + final ForkInfo fork = branch.getForkInfo(); - if (GithubUtil.addGithubRemote(project, gitRepository, forkPath.getUser(), url)) { - return forkPath.getUser(); - } - else { - return null; - } - } + final String head = myPath.getUser() + ":" + myCurrentBranch; + final String base = branch.getRemoteName(); - @Nullable - private static GithubPullRequest createPullRequest(@NotNull Project project, - @NotNull GithubAuthDataHolder authHolder, - @NotNull ProgressIndicator indicator, - @NotNull final GithubFullPath targetRepo, - @NotNull final String title, - @NotNull final String description, - @NotNull final String head, - @NotNull final String base) { try { - return GithubUtil.runTask(project, authHolder, indicator, new ThrowableConvertor() { - @NotNull - @Override - public GithubPullRequest convert(@NotNull GithubAuthData auth) throws IOException { - return GithubApiUtil.createPullRequest(auth, targetRepo.getUser(), targetRepo.getRepository(), title, description, head, base); - } - }); + return GithubUtil + .runTask(myProject, myAuthHolder, indicator, new ThrowableConvertor() { + @NotNull + @Override + public GithubPullRequest convert(@NotNull GithubConnection connection) throws IOException { + return GithubApiUtil + .createPullRequest(connection, fork.getPath().getUser(), fork.getPath().getRepository(), title, description, head, base); + } + }); } catch (IOException e) { - GithubNotifications.showError(project, CANNOT_CREATE_PULL_REQUEST, e); + GithubNotifications.showError(myProject, CANNOT_CREATE_PULL_REQUEST, e); return null; } } - @Nullable - private DiffInfo getDiffInfo(@NotNull String branch) { + public void showDiffDialog(@Nullable final BranchInfo branch) { + if (branch == null) { + GithubNotifications.showWarningDialog(myProject, "Can't Show Diff", "Target branch is not selected"); + return; + } + + DiffInfo info; try { - FutureTask future = myDiffInfos.get(branch); - if (future == null) { - return null; - } - future.run(); - return future.get(); + info = GithubUtil + .computeValueInModal(myProject, "Collecting diff data...", new ThrowableConvertor() { + @Override + public DiffInfo convert(ProgressIndicator indicator) throws IOException { + return GithubUtil.runInterruptable(indicator, new ThrowableComputable() { + @Override + public DiffInfo compute() throws IOException { + return getDiffInfo(branch); + } + }); + } + }); } - catch (InterruptedException e) { - LOG.error(e); - return null; + catch (IOException e) { + GithubNotifications.showError(myProject, "Can't collect diff data", e); + return; } - catch (ExecutionException e) { - LOG.error(e); - return null; + if (info == null) { + GithubNotifications.showErrorDialog(myProject, "Can't Show Diff", "Can't collect diff data"); + return; } + + GitCompareBranchesDialog dialog = + new GitCompareBranchesDialog(myProject, info.getTo(), info.getFrom(), info.getInfo(), myGitRepository, true); + dialog.show(); } @Nullable - private DiffInfo getDiffInfoWithModal(@NotNull final String branch) { - return GithubUtil.computeValueInModal(myProject, "Collecting diff data...", new Convertor() { - @Override - @Nullable - public DiffInfo convert(ProgressIndicator indicator) { - return getDiffInfo(branch); - } - }); - } + public ForkInfo showTargetDialog() { + if (myAvailableForks == null) { + myAvailableForks = GithubUtil + .computeValueInModal(myProject, myCurrentBranch, new Convertor>() { + @Override + public List convert(ProgressIndicator indicator) { + return getAvailableForks(indicator); + } + }); + } - public void getDiffDescriptionInPooledThread(@NotNull final String branch, @NotNull final Consumer after) { - ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { + Convertor getForkPath = new Convertor() { + @Nullable @Override - public void run() { - after.consume(getDefaultDescriptionMessage(branch, getDiffInfo(branch), myGitRepository)); + public ForkInfo convert(@NotNull final String user) { + return GithubUtil.computeValueInModal(myProject, "Access to GitHub", new Convertor() { + @Nullable + @Override + public ForkInfo convert(ProgressIndicator indicator) { + return findRepositoryByUser(indicator, user); + } + }); } - }); + }; + GithubSelectForkDialog dialog = new GithubSelectForkDialog(myProject, myAvailableForks, getForkPath); + DialogManager.show(dialog); + if (!dialog.isOK()) { + return null; + } + return dialog.getPath(); } @Nullable - private static DiffInfo loadDiffInfo(@NotNull final Project project, - @NotNull final GitRepository repository, - @NotNull final String currentBranch, - @NotNull final String targetBranch) { + private List getAvailableForks(@NotNull ProgressIndicator indicator) { try { - List commits1 = GitHistoryUtils.history(project, repository.getRoot(), ".." + targetBranch); - List commits2 = GitHistoryUtils.history(project, repository.getRoot(), targetBranch + ".."); - Collection diff = GitChangeUtils.getDiff(repository.getProject(), repository.getRoot(), targetBranch, currentBranch, null); - GitCommitCompareInfo info = new GitCommitCompareInfo(GitCommitCompareInfo.InfoType.BRANCH_TO_HEAD); - info.put(repository, diff); - info.put(repository, Couple.of(commits1, commits2)); - return new DiffInfo(info, currentBranch, targetBranch); - } - catch (VcsException e) { - LOG.info(e); + List forks = ContainerUtil.map( + GithubUtil.runTask(myProject, myAuthHolder, indicator, + new ThrowableConvertor, IOException>() { + @NotNull + @Override + public List convert(@NotNull GithubConnection connection) + throws IOException { + return GithubApiUtil.getForks(connection, mySource.getUser(), mySource.getRepository()); + } + } + ), + new Function() { + @Override + public GithubFullPath fun(GithubRepo repo) { + return repo.getFullPath(); + } + } + ); + if (!forks.contains(mySource)) forks.add(mySource); + return forks; + } + catch (IOException e) { + GithubNotifications.showWarning(myProject, "Can't load available forks", e); return null; } } - @NotNull - private static DiffDescription getDefaultDescriptionMessage(@NotNull String branch, - @Nullable DiffInfo info, - @NotNull GitRepository gitRepository) { - if (info == null) { - return new DiffDescription(branch, null, null); - } - - if (info.getInfo().getBranchToHeadCommits(gitRepository).size() != 1) { - return new DiffDescription(branch, info.getFrom(), null); + @Nullable + private ForkInfo findRepositoryByUser(@NotNull final ProgressIndicator indicator, @NotNull final String user) { + for (ForkInfo fork : myForks) { + if (StringUtil.equalsIgnoreCase(user, fork.getPath().getUser())) { + return fork; + } } - GitCommit commit = info.getInfo().getBranchToHeadCommits(gitRepository).get(0); - return new DiffDescription(branch, commit.getSubject(), commit.getFullMessage()); - } - - @Nullable - private static GithubInfo2 getAvailableForksInModal(@NotNull final Project project, - @NotNull final GitRepository gitRepository, - @NotNull final GithubAuthDataHolder authHolder, - @NotNull final GithubFullPath path) { try { - return GithubUtil - .computeValueInModal(project, "Access to GitHub", new ThrowableConvertor() { - @NotNull + GithubRepo repo = + GithubUtil.runTask(myProject, myAuthHolder, indicator, new ThrowableConvertor() { + @Nullable @Override - public GithubInfo2 convert(ProgressIndicator indicator) throws IOException { - final Set forks = new HashSet(); - - // GitHub - GithubRepoDetailed repo = - GithubUtil.runTask(project, authHolder, indicator, new ThrowableConvertor() { - @NotNull - @Override - public GithubRepoDetailed convert(@NotNull GithubAuthData auth) throws IOException { - return GithubApiUtil.getDetailedRepoInfo(auth, path.getUser(), path.getRepository()); - } - }); - - forks.add(path); - if (repo.getParent() != null) { - forks.add(repo.getParent().getFullPath()); + public GithubRepo convert(@NotNull GithubConnection connection) throws IOException { + try { + GithubRepoDetailed target = GithubApiUtil.getDetailedRepoInfo(connection, user, mySource.getRepository()); + if (target.getSource() != null && StringUtil.equals(target.getSource().getUserName(), mySource.getUser())) { + return target; + } } - if (repo.getSource() != null) { - forks.add(repo.getSource().getFullPath()); + catch (IOException ignore) { + // such repo may not exist } - // Git - forks.addAll(getAvailableForksFromGit(gitRepository)); - - GithubRepo forkTreeRoot = repo.getSource() == null ? repo : repo.getSource(); - return new GithubInfo2(forks, forkTreeRoot); + return GithubApiUtil.findForkByUser(connection, mySource.getUser(), mySource.getRepository(), user); } }); + + if (repo == null) return null; + return doAddFork(repo, indicator); } catch (IOException e) { - GithubNotifications.showErrorDialog(project, CANNOT_CREATE_PULL_REQUEST, e); + GithubNotifications.showError(myProject, "Can't find repository", e); return null; } } - @NotNull - private static List getAvailableForksFromGit(@NotNull GitRepository gitRepository) { - List forks = new ArrayList(); - for (GitRemoteBranch remoteBranch : gitRepository.getBranches().getRemoteBranches()) { - for (String url : remoteBranch.getRemote().getUrls()) { - if (GithubUrlUtil.isGithubUrl(url)) { - GithubFullPath path = GithubUrlUtil.getUserAndRepositoryFromRemoteUrl(url); - if (path != null) { - forks.add(path); - break; - } - } + public static class ForkInfo { + @NotNull public final Object LOCK = new Object(); + + // initial loading + @NotNull private final GithubFullPath myPath; + + @NotNull private final String myDefaultBranch; + @NotNull private final List myBranches; + + @Nullable private String myRemoteName; + private boolean myProposedToCreateRemote; + + @Nullable private MasterFutureTask myFetchTask; + + public ForkInfo(@NotNull GithubFullPath path, @NotNull List branches, @Nullable String defaultBranch) { + myPath = path; + myDefaultBranch = defaultBranch == null ? "master" : defaultBranch; + myBranches = new ArrayList(); + for (String branchName : branches) { + myBranches.add(new BranchInfo(branchName, this)); } } - return forks; - } - @Nullable - private static GithubFullPath findRepositoryByUser(@NotNull Project project, - @NotNull GithubAuthDataHolder authHolder, - @NotNull ProgressIndicator indicator, - @NotNull final String user, - @NotNull Set forks, - @NotNull final GithubRepo source) { - for (GithubFullPath path : forks) { - if (StringUtil.equalsIgnoreCase(user, path.getUser())) { - return path; - } + @NotNull + public GithubFullPath getPath() { + return myPath; } - try { - return GithubUtil.runTask(project, authHolder, indicator, new ThrowableConvertor() { - @Nullable - @Override - public GithubFullPath convert(@NotNull GithubAuthData auth) throws IOException { - try { - GithubRepoDetailed target = GithubApiUtil.getDetailedRepoInfo(auth, user, source.getName()); - if (target.getSource() != null && StringUtil.equals(target.getSource().getUserName(), source.getUserName())) { - return target.getFullPath(); - } - } - catch (IOException ignore) { - // such repo may not exist - } + @Nullable + public String getRemoteName() { + return myRemoteName; + } - GithubRepo fork = GithubApiUtil.findForkByUser(auth, source.getUserName(), source.getName(), user); - if (fork != null) { - return fork.getFullPath(); - } + @NotNull + public String getDefaultBranch() { + return myDefaultBranch; + } - return null; - } - }); + @NotNull + public List getBranches() { + return myBranches; } - catch (IOException e) { - GithubNotifications.showError(project, CANNOT_CREATE_PULL_REQUEST, e); + + public void setRemoteName(@NotNull String remoteName) { + myRemoteName = remoteName; } - return null; - } + public boolean isProposedToCreateRemote() { + return myProposedToCreateRemote; + } + + public void setProposedToCreateRemote(boolean proposedToCreateRemote) { + myProposedToCreateRemote = proposedToCreateRemote; + } - private static class GithubInfo { - @NotNull private final List myBranches; - @Nullable private final String myTargetRemote; + @Nullable + public MasterFutureTask getFetchTask() { + return myFetchTask; + } - private GithubInfo(@NotNull List repo, @Nullable String targetRemote) { - myBranches = repo; - myTargetRemote = targetRemote; + public void setFetchTask(@NotNull MasterFutureTask fetchTask) { + myFetchTask = fetchTask; } - @NotNull - public List getBranches() { - return myBranches; + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ForkInfo info = (ForkInfo)o; + + if (!myPath.equals(info.myPath)) return false; + + return true; } - @Nullable - public String getTargetRemote() { - return myTargetRemote; + @Override + public int hashCode() { + return myPath.hashCode(); + } + + @Override + public String toString() { + return myPath.getUser() + ":" + myPath.getRepository(); } } - private static class GithubInfo2 { - @NotNull private final Set myForks; - @NotNull private final GithubRepo mySource; + public static class BranchInfo { + @NotNull public final Object LOCK = new Object(); - private GithubInfo2(@NotNull Set forks, @NotNull GithubRepo source) { - myForks = forks; - mySource = source; + @NotNull private final ForkInfo myForkInfo; + @NotNull private final String myRemoteName; + + @Nullable private SlaveFutureTask myDiffInfoTask; + + @Nullable private Couple myDefaultMessage; + + public BranchInfo(@NotNull String remoteName, @NotNull ForkInfo fork) { + myRemoteName = remoteName; + myForkInfo = fork; } @NotNull - public Set getForks() { - return myForks; + public ForkInfo getForkInfo() { + return myForkInfo; } @NotNull - public GithubRepo getSource() { - return mySource; + public String getRemoteName() { + return myRemoteName; } - } - public static class GithubTargetInfo { - @NotNull private final List myBranches; + @Nullable + public SlaveFutureTask getDiffInfoTask() { + return myDiffInfoTask; + } - private GithubTargetInfo(@NotNull List branches) { - myBranches = branches; + public void setDiffInfoTask(@NotNull SlaveFutureTask diffInfoTask) { + myDiffInfoTask = diffInfoTask; } - @NotNull - public List getBranches() { - return myBranches; + @Nullable + public Couple getDefaultMessage() { + return myDefaultMessage; + } + + public void setDefaultMessage(@NotNull Couple message) { + myDefaultMessage = message; + } + + @Override + public String toString() { + return myRemoteName; } } - private static class DiffInfo { + public static class DiffInfo { @NotNull private final GitCommitCompareInfo myInfo; @NotNull private final String myFrom; @NotNull private final String myTo; @@ -662,30 +853,86 @@ public class GithubCreatePullRequestWorker { } } - public static class DiffDescription { - @NotNull private final String myBranch; - @Nullable private final String myTitle; - @Nullable private final String myDescription; + public static class SlaveFutureTask extends FutureTask { + @NotNull private final MasterFutureTask myMaster; - public DiffDescription(@NotNull String branch, @Nullable String title, @Nullable String description) { - myBranch = branch; - myTitle = title; - myDescription = description; + public SlaveFutureTask(@NotNull MasterFutureTask master, @NotNull Callable callable) { + super(callable); + myMaster = master; } - @NotNull - public String getBranch() { - return myBranch; + @Override + public void run() { + if (myMaster.isDone()) { + super.run(); + } + else { + if (!myMaster.addSlave(this)) { + super.run(); + } + } } - @Nullable - public String getTitle() { - return myTitle; + public T safeGet() { + try { + return super.get(); + } + catch (InterruptedException e) { + return null; + } + catch (CancellationException e) { + return null; + } + catch (ExecutionException e) { + return null; + } } + } - @Nullable - public String getDescription() { - return myDescription; + public static class MasterFutureTask extends FutureTask { + @NotNull private final Object LOCK = new Object(); + private boolean myDone = false; + + @Nullable private List mySlaves; + + public MasterFutureTask(@NotNull Callable callable) { + super(callable); + } + + boolean addSlave(@NotNull SlaveFutureTask slave) { + if (isDone()) { + return false; + } + else { + synchronized (LOCK) { + if (myDone) return false; + if (mySlaves == null) mySlaves = new ArrayList(); + mySlaves.add(slave); + return true; + } + } + } + + @Override + protected void done() { + synchronized (LOCK) { + myDone = true; + if (mySlaves != null) { + for (final SlaveFutureTask slave : mySlaves) { + runSlave(slave); + } + mySlaves = null; + } + } + } + + protected void runSlave(@NotNull final SlaveFutureTask slave) { + ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { + @Override + public void run() { + slave.run(); + } + }); } } } diff --git a/plugins/github/src/org/jetbrains/plugins/github/GithubRebaseAction.java b/plugins/github/src/org/jetbrains/plugins/github/GithubRebaseAction.java index 719f8b956432..23d34e3178d3 100644 --- a/plugins/github/src/org/jetbrains/plugins/github/GithubRebaseAction.java +++ b/plugins/github/src/org/jetbrains/plugins/github/GithubRebaseAction.java @@ -41,6 +41,7 @@ import icons.GithubIcons; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.github.api.GithubApiUtil; +import org.jetbrains.plugins.github.api.GithubConnection; import org.jetbrains.plugins.github.api.GithubFullPath; import org.jetbrains.plugins.github.api.GithubRepoDetailed; import org.jetbrains.plugins.github.util.*; @@ -197,11 +198,11 @@ public class GithubRebaseAction extends DumbAwareAction { try { return GithubUtil.runTask(project, GithubAuthDataHolder.createFromSettings(), indicator, - new ThrowableConvertor() { + new ThrowableConvertor() { @NotNull @Override - public GithubRepoDetailed convert(@NotNull GithubAuthData auth) throws IOException { - return GithubApiUtil.getDetailedRepoInfo(auth, userAndRepo.getUser(), userAndRepo.getRepository()); + public GithubRepoDetailed convert(@NotNull GithubConnection connection) throws IOException { + return GithubApiUtil.getDetailedRepoInfo(connection, userAndRepo.getUser(), userAndRepo.getRepository()); } }); } diff --git a/plugins/github/src/org/jetbrains/plugins/github/GithubShareAction.java b/plugins/github/src/org/jetbrains/plugins/github/GithubShareAction.java index cca5afa0f5e5..c372b09805a1 100644 --- a/plugins/github/src/org/jetbrains/plugins/github/GithubShareAction.java +++ b/plugins/github/src/org/jetbrains/plugins/github/GithubShareAction.java @@ -52,6 +52,7 @@ import icons.GithubIcons; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.github.api.GithubApiUtil; +import org.jetbrains.plugins.github.api.GithubConnection; import org.jetbrains.plugins.github.api.GithubRepo; import org.jetbrains.plugins.github.api.GithubUserDetailed; import org.jetbrains.plugins.github.ui.GithubShareDialog; @@ -211,15 +212,15 @@ public class GithubShareAction extends DumbAwareAction { @Override public GithubInfo convert(ProgressIndicator indicator) throws IOException { // get existing github repos (network) and validate auth data - return GithubUtil.runTask(project, authHolder, indicator, new ThrowableConvertor() { + return GithubUtil.runTask(project, authHolder, indicator, new ThrowableConvertor() { @NotNull @Override - public GithubInfo convert(@NotNull GithubAuthData auth) throws IOException { + public GithubInfo convert(@NotNull GithubConnection connection) throws IOException { // check access to private repos (network) - GithubUserDetailed userInfo = GithubApiUtil.getCurrentUserDetailed(auth); + GithubUserDetailed userInfo = GithubApiUtil.getCurrentUserDetailed(connection); HashSet names = new HashSet(); - for (GithubRepo info : GithubApiUtil.getUserRepos(auth)) { + for (GithubRepo info : GithubApiUtil.getUserRepos(connection)) { names.add(info.getName()); } return new GithubInfo(userInfo, names); @@ -243,11 +244,11 @@ public class GithubShareAction extends DumbAwareAction { final boolean isPrivate) { try { - return GithubUtil.runTask(project, authHolder, indicator, new ThrowableConvertor() { + return GithubUtil.runTask(project, authHolder, indicator, new ThrowableConvertor() { @NotNull @Override - public GithubRepo convert(@NotNull GithubAuthData auth) throws IOException { - return GithubApiUtil.createRepo(auth, name, description, isPrivate); + public GithubRepo convert(@NotNull GithubConnection connection) throws IOException { + return GithubApiUtil.createRepo(connection, name, description, isPrivate); } }).getHtmlUrl(); } diff --git a/plugins/github/src/org/jetbrains/plugins/github/api/GithubApiUtil.java b/plugins/github/src/org/jetbrains/plugins/github/api/GithubApiUtil.java index b8cf537f7709..063ad3bc7bb6 100644 --- a/plugins/github/src/org/jetbrains/plugins/github/api/GithubApiUtil.java +++ b/plugins/github/src/org/jetbrains/plugins/github/api/GithubApiUtil.java @@ -1,60 +1,32 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ package org.jetbrains.plugins.github.api; import com.google.gson.*; -import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.util.net.HttpConfigurable; -import org.apache.commons.httpclient.*; -import org.apache.commons.httpclient.auth.AuthScope; -import org.apache.commons.httpclient.methods.*; -import org.apache.commons.httpclient.params.HttpConnectionManagerParams; +import org.apache.http.Header; +import org.apache.http.HeaderElement; +import org.apache.http.message.BasicHeader; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.plugins.github.exceptions.*; -import org.jetbrains.plugins.github.util.GithubAuthData; -import org.jetbrains.plugins.github.util.GithubSettings; -import org.jetbrains.plugins.github.util.GithubUrlUtil; +import org.jetbrains.plugins.github.api.GithubConnection.PagedRequest; +import org.jetbrains.plugins.github.exceptions.GithubConfusingException; +import org.jetbrains.plugins.github.exceptions.GithubJsonException; +import org.jetbrains.plugins.github.exceptions.GithubStatusCodeException; import org.jetbrains.plugins.github.util.GithubUtil; -import sun.security.validator.ValidatorException; -import javax.net.ssl.SSLHandshakeException; -import java.awt.*; import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; import java.net.URLEncoder; import java.util.*; -import java.util.List; -/** - * @author Kirill Likhodedov - */ public class GithubApiUtil { + private static final Logger LOG = GithubUtil.LOG; public static final String DEFAULT_GITHUB_HOST = "github.com"; private static final String PER_PAGE = "per_page=100"; - private static final Logger LOG = GithubUtil.LOG; - private static final Header ACCEPT_V3_JSON_HTML_MARKUP = new Header("Accept", "application/vnd.github.v3.html+json"); - private static final Header ACCEPT_V3_JSON = new Header("Accept", "application/vnd.github.v3+json"); + private static final Header ACCEPT_V3_JSON_HTML_MARKUP = new BasicHeader("Accept", "application/vnd.github.v3.html+json"); + private static final Header ACCEPT_V3_JSON = new BasicHeader("Accept", "application/vnd.github.v3+json"); @NotNull private static final Gson gson = initGson(); @@ -65,344 +37,8 @@ public class GithubApiUtil { return builder.create(); } - private enum HttpVerb { - GET, POST, DELETE, HEAD, PATCH - } - - @Nullable - private static JsonElement postRequest(@NotNull GithubAuthData auth, - @NotNull String path, - @Nullable String requestBody, - @NotNull Header... headers) throws IOException { - return request(auth, path, requestBody, Arrays.asList(headers), HttpVerb.POST).getJsonElement(); - } - - @Nullable - private static JsonElement patchRequest(@NotNull GithubAuthData auth, - @NotNull String path, - @Nullable String requestBody, - @NotNull Header... headers) throws IOException { - return request(auth, path, requestBody, Arrays.asList(headers), HttpVerb.PATCH).getJsonElement(); - } - - @Nullable - private static JsonElement deleteRequest(@NotNull GithubAuthData auth, @NotNull String path, @NotNull Header... headers) - throws IOException { - return request(auth, path, null, Arrays.asList(headers), HttpVerb.DELETE).getJsonElement(); - } - - @Nullable - private static JsonElement getRequest(@NotNull GithubAuthData auth, @NotNull String path, @NotNull Header... headers) throws IOException { - return request(auth, path, null, Arrays.asList(headers), HttpVerb.GET).getJsonElement(); - } - - @NotNull - private static ResponsePage request(@NotNull GithubAuthData auth, - @NotNull String path, - @Nullable String requestBody, - @NotNull Collection

headers, - @NotNull HttpVerb verb) throws IOException { - if (EventQueue.isDispatchThread() && !ApplicationManager.getApplication().isUnitTestMode()) { - LOG.warn("Network operation in EDT"); // TODO: fix - } - - HttpMethod method = null; - try { - String uri = GithubUrlUtil.getApiUrl(auth.getHost()) + path; - method = doREST(auth, uri, requestBody, headers, verb); - - checkStatusCode(method, requestBody); - - InputStream resp = method.getResponseBodyAsStream(); - if (resp == null) { - return new ResponsePage(); - } - - JsonElement ret = parseResponse(resp); - if (ret.isJsonNull()) { - return new ResponsePage(); - } - - Header header = method.getResponseHeader("Link"); - if (header != null) { - String value = header.getValue(); - int end = value.indexOf(">; rel=\"next\""); - int begin = value.lastIndexOf('<', end); - if (begin >= 0 && end >= 0) { - String newPath = GithubUrlUtil.removeProtocolPrefix(value.substring(begin + 1, end)); - int index = newPath.indexOf('/'); - - return new ResponsePage(ret, newPath.substring(index)); - } - } - - return new ResponsePage(ret); - } - finally { - if (method != null) { - method.releaseConnection(); - } - } - } - - @NotNull - private static HttpMethod doREST(@NotNull final GithubAuthData auth, - @NotNull final String uri, - @Nullable final String requestBody, - @NotNull final Collection
headers, - @NotNull final HttpVerb verb) throws IOException { - HttpClient client = getHttpClient(auth.getBasicAuth(), auth.isUseProxy()); - HttpMethod method; - switch (verb) { - case POST: - method = new PostMethod(uri); - if (requestBody != null) { - ((PostMethod)method).setRequestEntity(new StringRequestEntity(requestBody, "application/json", "UTF-8")); - } - break; - case PATCH: - method = new PostMethod(uri) { // TODO: httpclient 4.x - @Override - public String getName() { - return "PATCH"; - } - }; - if (requestBody != null) { - ((PostMethod)method).setRequestEntity(new StringRequestEntity(requestBody, "application/json", "UTF-8")); - } - break; - case GET: - method = new GetMethod(uri); - break; - case DELETE: - method = new DeleteMethod(uri); - break; - case HEAD: - method = new HeadMethod(uri); - break; - default: - throw new IllegalStateException("Wrong HttpVerb: unknown method: " + verb.toString()); - } - - GithubAuthData.TokenAuth tokenAuth = auth.getTokenAuth(); - if (tokenAuth != null) { - method.addRequestHeader("Authorization", "token " + tokenAuth.getToken()); - } - GithubAuthData.BasicAuth basicAuth = auth.getBasicAuth(); - if (basicAuth != null && basicAuth.getCode() != null) { - method.addRequestHeader("X-GitHub-OTP", basicAuth.getCode()); - } - for (Header header : headers) { - method.addRequestHeader(header); - } - - try { - client.executeMethod(method); - } - catch (SSLHandshakeException e) { // User canceled operation from CertificateManager - if (e.getCause() instanceof ValidatorException) { - LOG.info("Host SSL certificate is not trusted", e); - throw new GithubOperationCanceledException("Host SSL certificate is not trusted", e); - } - throw e; - } - return method; - } - - @NotNull - private static HttpClient getHttpClient(@Nullable GithubAuthData.BasicAuth basicAuth, boolean useProxy) { - int timeout = GithubSettings.getInstance().getConnectionTimeout(); - final HttpClient client = new HttpClient(); - HttpConnectionManagerParams params = client.getHttpConnectionManager().getParams(); - params.setConnectionTimeout(timeout); //set connection timeout (how long it takes to connect to remote host) - params.setSoTimeout(timeout); //set socket timeout (how long it takes to retrieve data from remote host) - - client.getParams().setContentCharset("UTF-8"); - // Configure proxySettings if it is required - final HttpConfigurable proxySettings = HttpConfigurable.getInstance(); - if (useProxy && proxySettings.USE_HTTP_PROXY && !StringUtil.isEmptyOrSpaces(proxySettings.PROXY_HOST)) { - client.getHostConfiguration().setProxy(proxySettings.PROXY_HOST, proxySettings.PROXY_PORT); - if (proxySettings.PROXY_AUTHENTICATION) { - client.getState().setProxyCredentials(AuthScope.ANY, new UsernamePasswordCredentials(proxySettings.PROXY_LOGIN, - proxySettings.getPlainProxyPassword())); - } - } - if (basicAuth != null) { - client.getParams().setCredentialCharset("UTF-8"); - client.getParams().setAuthenticationPreemptive(true); - client.getState().setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(basicAuth.getLogin(), basicAuth.getPassword())); - } - return client; - } - - private static void checkStatusCode(@NotNull HttpMethod method, @Nullable String body) throws IOException { - int code = method.getStatusCode(); - switch (code) { - case HttpStatus.SC_OK: - case HttpStatus.SC_CREATED: - case HttpStatus.SC_ACCEPTED: - case HttpStatus.SC_NO_CONTENT: - return; - case HttpStatus.SC_UNAUTHORIZED: - case HttpStatus.SC_PAYMENT_REQUIRED: - case HttpStatus.SC_FORBIDDEN: - String message = getErrorMessage(method); - - Header headerOTP = method.getResponseHeader("X-GitHub-OTP"); - if (headerOTP != null) { - if (headerOTP.getValue().startsWith("required")) { - throw new GithubTwoFactorAuthenticationException(message); - } - } - - if (message.contains("API rate limit exceeded")) { - throw new GithubRateLimitExceededException(message); - } - - throw new GithubAuthenticationException("Request response: " + message); - case HttpStatus.SC_BAD_REQUEST: - case HttpStatus.SC_UNPROCESSABLE_ENTITY: - if (body != null) { - LOG.info(body); - } - throw new GithubStatusCodeException(code + ": " + getErrorMessage(method), code); - default: - throw new GithubStatusCodeException(code + ": " + getErrorMessage(method), code); - } - } - - @NotNull - private static String getErrorMessage(@NotNull HttpMethod method) { - try { - InputStream resp = method.getResponseBodyAsStream(); - if (resp != null) { - GithubErrorMessageRaw error = fromJson(parseResponse(resp), GithubErrorMessageRaw.class); - return method.getStatusText() + " - " + error.getMessage(); - } - } - catch (IOException e) { - LOG.info(e); - } - return method.getStatusText(); - } - - @NotNull - private static JsonElement parseResponse(@NotNull InputStream githubResponse) throws IOException { - Reader reader = new InputStreamReader(githubResponse, "UTF-8"); - try { - return new JsonParser().parse(reader); - } - catch (JsonParseException jse) { - throw new GithubJsonException("Couldn't parse GitHub response", jse); - } - finally { - reader.close(); - } - } - - private static class ResponsePage { - @Nullable private final JsonElement response; - @Nullable private final String nextPage; - - public ResponsePage() { - this(null, null); - } - - public ResponsePage(@Nullable JsonElement response) { - this(response, null); - } - - public ResponsePage(@Nullable JsonElement response, @Nullable String next) { - this.response = response; - this.nextPage = next; - } - - @Nullable - public JsonElement getJsonElement() { - return response; - } - - @Nullable - public String getNextPage() { - return nextPage; - } - } - - /* - * Json API - */ - - static Result createDataFromRaw(@NotNull Raw rawObject, @NotNull Class resultClass) - throws GithubJsonException { - try { - return rawObject.create(resultClass); - } - catch (Exception e) { - throw new GithubJsonException("Json parse error", e); - } - } - - public static class PagedRequest { - @Nullable private String myNextPage; - @NotNull private final Collection
myHeaders; - @NotNull private final Class myResult; - @NotNull private final Class myRawArray; - - @SuppressWarnings("NullableProblems") - public PagedRequest(@NotNull String path, - @NotNull Class result, - @NotNull Class rawArray, - @NotNull Header... headers) { - myNextPage = path; - myResult = result; - myRawArray = rawArray; - myHeaders = Arrays.asList(headers); - } - - @NotNull - public List next(@NotNull GithubAuthData auth) throws IOException { - if (myNextPage == null) { - throw new NoSuchElementException(); - } - - String page = myNextPage; - myNextPage = null; - - ResponsePage response = request(auth, page, null, myHeaders, HttpVerb.GET); - - if (response.getJsonElement() == null) { - throw new HttpException("Empty response"); - } - - if (!response.getJsonElement().isJsonArray()) { - throw new GithubJsonException("Wrong json type: expected JsonArray", new Exception(response.getJsonElement().toString())); - } - - myNextPage = response.getNextPage(); - - List result = new ArrayList(); - for (DataConstructor raw : fromJson(response.getJsonElement().getAsJsonArray(), myRawArray)) { - result.add(createDataFromRaw(raw, myResult)); - } - return result; - } - - public boolean hasNext() { - return myNextPage != null; - } - - @NotNull - public List getAll(@NotNull GithubAuthData auth) throws IOException { - List result = new ArrayList(); - while (hasNext()) { - result.addAll(next(auth)); - } - return result; - } - } - @NotNull - private static T fromJson(@Nullable JsonElement json, @NotNull Class classT) throws IOException { + public static T fromJson(@Nullable JsonElement json, @NotNull Class classT) throws IOException { if (json == null) { throw new GithubJsonException("Unexpected empty response"); } @@ -425,13 +61,24 @@ public class GithubApiUtil { return res; } - /* - * Github API + @NotNull + public static Result createDataFromRaw(@NotNull Raw rawObject, @NotNull Class resultClass) + throws GithubJsonException { + try { + return rawObject.create(resultClass); + } + catch (Exception e) { + throw new GithubJsonException("Json parse error", e); + } + } + + /* + * Operations */ - public static void askForTwoFactorCodeSMS(@NotNull GithubAuthData auth) { + public static void askForTwoFactorCodeSMS(@NotNull GithubConnection connection) { try { - postRequest(auth, "/authorizations", null, ACCEPT_V3_JSON); + connection.postRequest("/authorizations", null, ACCEPT_V3_JSON); } catch (IOException e) { LOG.info(e); @@ -439,47 +86,42 @@ public class GithubApiUtil { } @NotNull - public static Collection getTokenScopes(@NotNull GithubAuthData auth) throws IOException { - HttpMethod method = null; - try { - String uri = GithubUrlUtil.getApiUrl(auth.getHost()) + "/user"; - method = doREST(auth, uri, null, Collections.
emptyList(), HttpVerb.HEAD); - - checkStatusCode(method, null); + public static Collection getTokenScopes(@NotNull GithubConnection connection) throws IOException { + Header[] headers = connection.headRequest("/user", ACCEPT_V3_JSON); - Header header = method.getResponseHeader("X-OAuth-Scopes"); - if (header == null) { - throw new HttpException("No scopes header"); - } - - Collection scopes = new ArrayList(); - for (HeaderElement elem : header.getElements()) { - scopes.add(elem.getName()); + Header scopesHeader = null; + for (Header header : headers) { + if (header.getName().equals("X-OAuth-Scopes")) { + scopesHeader = header; + break; } - return scopes; } - finally { - if (method != null) { - method.releaseConnection(); - } + if (scopesHeader == null) { + throw new GithubConfusingException("No scopes header"); } + + Collection scopes = new ArrayList(); + for (HeaderElement elem : scopesHeader.getElements()) { + scopes.add(elem.getName()); + } + return scopes; } @NotNull - public static String getScopedToken(@NotNull GithubAuthData auth, @NotNull Collection scopes, @NotNull String note) + public static String getScopedToken(@NotNull GithubConnection connection, @NotNull Collection scopes, @NotNull String note) throws IOException { - GithubAuthorization token = findToken(auth, note); + GithubAuthorization token = findToken(connection, note); if (token == null) { - return getNewScopedToken(auth, scopes, note).getToken(); + return getNewScopedToken(connection, scopes, note).getToken(); } if (token.getScopes().containsAll(scopes)) { return token.getToken(); } - return updateTokenScopes(auth, token, scopes).getToken(); + return updateTokenScopes(connection, token, scopes).getToken(); } @NotNull - private static GithubAuthorization updateTokenScopes(@NotNull GithubAuthData auth, + private static GithubAuthorization updateTokenScopes(@NotNull GithubConnection connection, @NotNull GithubAuthorization token, @NotNull Collection scopes) throws IOException { try { @@ -487,7 +129,7 @@ public class GithubApiUtil { GithubAuthorizationUpdateRequest request = new GithubAuthorizationUpdateRequest(new ArrayList(scopes)); - return createDataFromRaw(fromJson(patchRequest(auth, path, gson.toJson(request), ACCEPT_V3_JSON), GithubAuthorizationRaw.class), + return createDataFromRaw(fromJson(connection.patchRequest(path, gson.toJson(request), ACCEPT_V3_JSON), GithubAuthorizationRaw.class), GithubAuthorization.class); } catch (GithubConfusingException e) { @@ -497,7 +139,7 @@ public class GithubApiUtil { } @NotNull - private static GithubAuthorization getNewScopedToken(@NotNull GithubAuthData auth, + private static GithubAuthorization getNewScopedToken(@NotNull GithubConnection connection, @NotNull Collection scopes, @NotNull String note) throws IOException { @@ -506,7 +148,7 @@ public class GithubApiUtil { GithubAuthorizationCreateRequest request = new GithubAuthorizationCreateRequest(new ArrayList(scopes), note, null); - return createDataFromRaw(fromJson(postRequest(auth, path, gson.toJson(request), ACCEPT_V3_JSON), GithubAuthorizationRaw.class), + return createDataFromRaw(fromJson(connection.postRequest(path, gson.toJson(request), ACCEPT_V3_JSON), GithubAuthorizationRaw.class), GithubAuthorization.class); } catch (GithubConfusingException e) { @@ -516,14 +158,14 @@ public class GithubApiUtil { } @Nullable - private static GithubAuthorization findToken(@NotNull GithubAuthData auth, @NotNull String note) throws IOException { + private static GithubAuthorization findToken(@NotNull GithubConnection connection, @NotNull String note) throws IOException { try { String path = "/authorizations"; PagedRequest request = new PagedRequest(path, GithubAuthorization.class, GithubAuthorizationRaw[].class, ACCEPT_V3_JSON); - List tokens = request.getAll(auth); + List tokens = request.getAll(connection); for (GithubAuthorization token : tokens) { if (note.equals(token.getNote())) return token; @@ -537,29 +179,32 @@ public class GithubApiUtil { } @NotNull - public static String getMasterToken(@NotNull GithubAuthData auth, @NotNull String note) throws IOException { + public static String getMasterToken(@NotNull GithubConnection connection, @NotNull String note) throws IOException { // "repo" - read/write access to public/private repositories // "gist" - create/delete gists List scopes = Arrays.asList("repo", "gist"); - return getScopedToken(auth, scopes, note); + return getScopedToken(connection, scopes, note); } @NotNull - public static String getReadOnlyToken(@NotNull GithubAuthData auth, @NotNull String user, @NotNull String repo, @NotNull String note) + public static String getReadOnlyToken(@NotNull GithubConnection connection, + @NotNull String user, + @NotNull String repo, + @NotNull String note) throws IOException { - GithubRepo repository = getDetailedRepoInfo(auth, user, repo); + GithubRepo repository = getDetailedRepoInfo(connection, user, repo); // TODO: use read-only token for private repos when it will be available List scopes = repository.isPrivate() ? Collections.singletonList("repo") : Collections.emptyList(); - return getScopedToken(auth, scopes, note); + return getScopedToken(connection, scopes, note); } @NotNull - public static GithubUser getCurrentUser(@NotNull GithubAuthData auth) throws IOException { + public static GithubUser getCurrentUser(@NotNull GithubConnection connection) throws IOException { try { - JsonElement result = getRequest(auth, "/user", ACCEPT_V3_JSON); + JsonElement result = connection.getRequest("/user", ACCEPT_V3_JSON); return createDataFromRaw(fromJson(result, GithubUserRaw.class), GithubUser.class); } catch (GithubConfusingException e) { @@ -569,9 +214,9 @@ public class GithubApiUtil { } @NotNull - public static GithubUserDetailed getCurrentUserDetailed(@NotNull GithubAuthData auth) throws IOException { + public static GithubUserDetailed getCurrentUserDetailed(@NotNull GithubConnection connection) throws IOException { try { - JsonElement result = getRequest(auth, "/user", ACCEPT_V3_JSON); + JsonElement result = connection.getRequest("/user", ACCEPT_V3_JSON); return createDataFromRaw(fromJson(result, GithubUserRaw.class), GithubUserDetailed.class); } catch (GithubConfusingException e) { @@ -581,13 +226,13 @@ public class GithubApiUtil { } @NotNull - public static List getUserRepos(@NotNull GithubAuthData auth) throws IOException { + public static List getUserRepos(@NotNull GithubConnection connection) throws IOException { try { String path = "/user/repos?" + PER_PAGE; PagedRequest request = new PagedRequest(path, GithubRepo.class, GithubRepoRaw[].class, ACCEPT_V3_JSON); - return request.getAll(auth); + return request.getAll(connection); } catch (GithubConfusingException e) { e.setDetails("Can't get user repositories"); @@ -596,13 +241,13 @@ public class GithubApiUtil { } @NotNull - public static List getUserRepos(@NotNull GithubAuthData auth, @NotNull String user) throws IOException { + public static List getUserRepos(@NotNull GithubConnection connection, @NotNull String user) throws IOException { try { String path = "/users/" + user + "/repos?" + PER_PAGE; PagedRequest request = new PagedRequest(path, GithubRepo.class, GithubRepoRaw[].class, ACCEPT_V3_JSON); - return request.getAll(auth); + return request.getAll(connection); } catch (GithubConfusingException e) { e.setDetails("Can't get user repositories: " + user); @@ -611,21 +256,21 @@ public class GithubApiUtil { } @NotNull - public static List getAvailableRepos(@NotNull GithubAuthData auth) throws IOException { + public static List getAvailableRepos(@NotNull GithubConnection connection) throws IOException { try { List repos = new ArrayList(); - repos.addAll(getUserRepos(auth)); + repos.addAll(getUserRepos(connection)); // We already can return something useful from getUserRepos, so let's ignore errors. // One of this may not exist in GitHub enterprise try { - repos.addAll(getMembershipRepos(auth)); + repos.addAll(getMembershipRepos(connection)); } catch (GithubStatusCodeException ignore) { } try { - repos.addAll(getWatchedRepos(auth)); + repos.addAll(getWatchedRepos(connection)); } catch (GithubStatusCodeException ignore) { } @@ -639,36 +284,36 @@ public class GithubApiUtil { } @NotNull - public static List getMembershipRepos(@NotNull GithubAuthData auth) throws IOException { + public static List getMembershipRepos(@NotNull GithubConnection connection) throws IOException { String orgsPath = "/user/orgs?" + PER_PAGE; PagedRequest orgsRequest = new PagedRequest(orgsPath, GithubOrg.class, GithubOrgRaw[].class); List repos = new ArrayList(); - for (GithubOrg org : orgsRequest.getAll(auth)) { + for (GithubOrg org : orgsRequest.getAll(connection)) { String path = "/orgs/" + org.getLogin() + "/repos?type=member&" + PER_PAGE; PagedRequest request = new PagedRequest(path, GithubRepoOrg.class, GithubRepoRaw[].class, ACCEPT_V3_JSON); - repos.addAll(request.getAll(auth)); + repos.addAll(request.getAll(connection)); } return repos; } @NotNull - public static List getWatchedRepos(@NotNull GithubAuthData auth) throws IOException { + public static List getWatchedRepos(@NotNull GithubConnection connection) throws IOException { String pathWatched = "/user/subscriptions?" + PER_PAGE; PagedRequest requestWatched = new PagedRequest(pathWatched, GithubRepo.class, GithubRepoRaw[].class, ACCEPT_V3_JSON); - return requestWatched.getAll(auth); + return requestWatched.getAll(connection); } @NotNull - public static GithubRepoDetailed getDetailedRepoInfo(@NotNull GithubAuthData auth, @NotNull String owner, @NotNull String name) + public static GithubRepoDetailed getDetailedRepoInfo(@NotNull GithubConnection connection, @NotNull String owner, @NotNull String name) throws IOException { try { final String request = "/repos/" + owner + "/" + name; - JsonElement jsonObject = getRequest(auth, request, ACCEPT_V3_JSON); + JsonElement jsonObject = connection.getRequest(request, ACCEPT_V3_JSON); return createDataFromRaw(fromJson(jsonObject, GithubRepoRaw.class), GithubRepoDetailed.class); } @@ -678,11 +323,11 @@ public class GithubApiUtil { } } - public static void deleteGithubRepository(@NotNull GithubAuthData auth, @NotNull String username, @NotNull String repo) + public static void deleteGithubRepository(@NotNull GithubConnection connection, @NotNull String username, @NotNull String repo) throws IOException { try { String path = "/repos/" + username + "/" + repo; - deleteRequest(auth, path); + connection.deleteRequest(path); } catch (GithubConfusingException e) { e.setDetails("Can't delete repository: " + username + "/" + repo); @@ -690,10 +335,10 @@ public class GithubApiUtil { } } - public static void deleteGist(@NotNull GithubAuthData auth, @NotNull String id) throws IOException { + public static void deleteGist(@NotNull GithubConnection connection, @NotNull String id) throws IOException { try { String path = "/gists/" + id; - deleteRequest(auth, path); + connection.deleteRequest(path); } catch (GithubConfusingException e) { e.setDetails("Can't delete gist: id - " + id); @@ -702,10 +347,10 @@ public class GithubApiUtil { } @NotNull - public static GithubGist getGist(@NotNull GithubAuthData auth, @NotNull String id) throws IOException { + public static GithubGist getGist(@NotNull GithubConnection connection, @NotNull String id) throws IOException { try { String path = "/gists/" + id; - JsonElement result = getRequest(auth, path, ACCEPT_V3_JSON); + JsonElement result = connection.getRequest(path, ACCEPT_V3_JSON); return createDataFromRaw(fromJson(result, GithubGistRaw.class), GithubGist.class); } @@ -716,13 +361,13 @@ public class GithubApiUtil { } @NotNull - public static GithubGist createGist(@NotNull GithubAuthData auth, + public static GithubGist createGist(@NotNull GithubConnection connection, @NotNull List contents, @NotNull String description, boolean isPrivate) throws IOException { try { String request = gson.toJson(new GithubGistRequest(contents, description, !isPrivate)); - return createDataFromRaw(fromJson(postRequest(auth, "/gists", request, ACCEPT_V3_JSON), GithubGistRaw.class), GithubGist.class); + return createDataFromRaw(fromJson(connection.postRequest("/gists", request, ACCEPT_V3_JSON), GithubGistRaw.class), GithubGist.class); } catch (GithubConfusingException e) { e.setDetails("Can't create gist"); @@ -731,7 +376,16 @@ public class GithubApiUtil { } @NotNull - public static GithubPullRequest createPullRequest(@NotNull GithubAuthData auth, + public static List getForks(@NotNull GithubConnection connection, @NotNull String owner, @NotNull String name) + throws IOException { + String path = "/repos/" + owner + "/" + name + "/forks?" + PER_PAGE; + PagedRequest requestWatched = + new PagedRequest(path, GithubRepo.class, GithubRepoRaw[].class, ACCEPT_V3_JSON); + return requestWatched.getAll(connection); + } + + @NotNull + public static GithubPullRequest createPullRequest(@NotNull GithubConnection connection, @NotNull String user, @NotNull String repo, @NotNull String title, @@ -741,7 +395,7 @@ public class GithubApiUtil { try { String request = gson.toJson(new GithubPullRequestRequest(title, description, head, base)); return createDataFromRaw( - fromJson(postRequest(auth, "/repos/" + user + "/" + repo + "/pulls", request, ACCEPT_V3_JSON), GithubPullRequestRaw.class), + fromJson(connection.postRequest("/repos/" + user + "/" + repo + "/pulls", request, ACCEPT_V3_JSON), GithubPullRequestRaw.class), GithubPullRequest.class); } catch (GithubConfusingException e) { @@ -751,14 +405,17 @@ public class GithubApiUtil { } @NotNull - public static GithubRepo createRepo(@NotNull GithubAuthData auth, @NotNull String name, @NotNull String description, boolean isPrivate) + public static GithubRepo createRepo(@NotNull GithubConnection connection, + @NotNull String name, + @NotNull String description, + boolean isPrivate) throws IOException { try { String path = "/user/repos"; GithubRepoRequest request = new GithubRepoRequest(name, description, isPrivate); - return createDataFromRaw(fromJson(postRequest(auth, path, gson.toJson(request), ACCEPT_V3_JSON), GithubRepoRaw.class), + return createDataFromRaw(fromJson(connection.postRequest(path, gson.toJson(request), ACCEPT_V3_JSON), GithubRepoRaw.class), GithubRepo.class); } catch (GithubConfusingException e) { @@ -771,7 +428,7 @@ public class GithubApiUtil { * Open issues only */ @NotNull - public static List getIssuesAssigned(@NotNull GithubAuthData auth, + public static List getIssuesAssigned(@NotNull GithubConnection connection, @NotNull String user, @NotNull String repo, @Nullable String assigned, @@ -791,7 +448,7 @@ public class GithubApiUtil { List result = new ArrayList(); while (request.hasNext() && max > result.size()) { - result.addAll(request.next(auth)); + result.addAll(request.next(connection)); } return result; } @@ -805,7 +462,7 @@ public class GithubApiUtil { /* * All issues - open and closed */ - public static List getIssuesQueried(@NotNull GithubAuthData auth, + public static List getIssuesQueried(@NotNull GithubConnection connection, @NotNull String user, @NotNull String repo, @Nullable String query, @@ -816,7 +473,7 @@ public class GithubApiUtil { String path = "/search/issues?q=" + query; //TODO: Use bodyHtml for issues - GitHub does not support this feature for SearchApi yet - JsonElement result = getRequest(auth, path, ACCEPT_V3_JSON); + JsonElement result = connection.getRequest(path, ACCEPT_V3_JSON); return createDataFromRaw(fromJson(result, GithubIssuesSearchResultRaw.class), GithubIssuesSearchResult.class).getIssues(); } @@ -827,12 +484,12 @@ public class GithubApiUtil { } @NotNull - public static GithubIssue getIssue(@NotNull GithubAuthData auth, @NotNull String user, @NotNull String repo, @NotNull String id) + public static GithubIssue getIssue(@NotNull GithubConnection connection, @NotNull String user, @NotNull String repo, @NotNull String id) throws IOException { try { String path = "/repos/" + user + "/" + repo + "/issues/" + id; - JsonElement result = getRequest(auth, path, ACCEPT_V3_JSON); + JsonElement result = connection.getRequest(path, ACCEPT_V3_JSON); return createDataFromRaw(fromJson(result, GithubIssueRaw.class), GithubIssue.class); } @@ -843,7 +500,10 @@ public class GithubApiUtil { } @NotNull - public static List getIssueComments(@NotNull GithubAuthData auth, @NotNull String user, @NotNull String repo, long id) + public static List getIssueComments(@NotNull GithubConnection connection, + @NotNull String user, + @NotNull String repo, + long id) throws IOException { try { String path = "/repos/" + user + "/" + repo + "/issues/" + id + "/comments?" + PER_PAGE; @@ -851,7 +511,7 @@ public class GithubApiUtil { PagedRequest request = new PagedRequest(path, GithubIssueComment.class, GithubIssueCommentRaw[].class, ACCEPT_V3_JSON_HTML_MARKUP); - return request.getAll(auth); + return request.getAll(connection); } catch (GithubConfusingException e) { e.setDetails("Can't get issue comments: " + user + "/" + repo + " - " + id); @@ -859,15 +519,37 @@ public class GithubApiUtil { } } + public static void setIssueState(@NotNull GithubConnection connection, + @NotNull String user, + @NotNull String repo, + @NotNull String id, + boolean open) + throws IOException { + try { + String path = "/repos/" + user + "/" + repo + "/issues/" + id; + + GithubChangeIssueStateRequest request = new GithubChangeIssueStateRequest(open ? "open" : "closed"); + + JsonElement result = connection.patchRequest(path, gson.toJson(request), ACCEPT_V3_JSON); + + createDataFromRaw(fromJson(result, GithubIssueRaw.class), GithubIssue.class); + } + catch (GithubConfusingException e) { + e.setDetails("Can't set issue state: " + user + "/" + repo + " - " + id + "@" + (open ? "open" : "closed")); + throw e; + } + } + + @NotNull - public static GithubCommitDetailed getCommit(@NotNull GithubAuthData auth, + public static GithubCommitDetailed getCommit(@NotNull GithubConnection connection, @NotNull String user, @NotNull String repo, @NotNull String sha) throws IOException { try { String path = "/repos/" + user + "/" + repo + "/commits/" + sha; - JsonElement result = getRequest(auth, path, ACCEPT_V3_JSON); + JsonElement result = connection.getRequest(path, ACCEPT_V3_JSON); return createDataFromRaw(fromJson(result, GithubCommitRaw.class), GithubCommitDetailed.class); } catch (GithubConfusingException e) { @@ -877,7 +559,7 @@ public class GithubApiUtil { } @NotNull - public static List getCommitComments(@NotNull GithubAuthData auth, + public static List getCommitComments(@NotNull GithubConnection connection, @NotNull String user, @NotNull String repo, @NotNull String sha) throws IOException { @@ -887,7 +569,7 @@ public class GithubApiUtil { PagedRequest request = new PagedRequest(path, GithubCommitComment.class, GithubCommitCommentRaw[].class, ACCEPT_V3_JSON_HTML_MARKUP); - return request.getAll(auth); + return request.getAll(connection); } catch (GithubConfusingException e) { e.setDetails("Can't get commit comments: " + user + "/" + repo + " - " + sha); @@ -896,7 +578,7 @@ public class GithubApiUtil { } @NotNull - public static List getPullRequestComments(@NotNull GithubAuthData auth, + public static List getPullRequestComments(@NotNull GithubConnection connection, @NotNull String user, @NotNull String repo, long id) throws IOException { @@ -906,7 +588,7 @@ public class GithubApiUtil { PagedRequest request = new PagedRequest(path, GithubCommitComment.class, GithubCommitCommentRaw[].class, ACCEPT_V3_JSON_HTML_MARKUP); - return request.getAll(auth); + return request.getAll(connection); } catch (GithubConfusingException e) { e.setDetails("Can't get pull request comments: " + user + "/" + repo + " - " + id); @@ -915,11 +597,11 @@ public class GithubApiUtil { } @NotNull - public static GithubPullRequest getPullRequest(@NotNull GithubAuthData auth, @NotNull String user, @NotNull String repo, int id) + public static GithubPullRequest getPullRequest(@NotNull GithubConnection connection, @NotNull String user, @NotNull String repo, int id) throws IOException { try { String path = "/repos/" + user + "/" + repo + "/pulls/" + id; - return createDataFromRaw(fromJson(getRequest(auth, path, ACCEPT_V3_JSON_HTML_MARKUP), GithubPullRequestRaw.class), + return createDataFromRaw(fromJson(connection.getRequest(path, ACCEPT_V3_JSON_HTML_MARKUP), GithubPullRequestRaw.class), GithubPullRequest.class); } catch (GithubConfusingException e) { @@ -929,7 +611,7 @@ public class GithubApiUtil { } @NotNull - public static List getPullRequests(@NotNull GithubAuthData auth, @NotNull String user, @NotNull String repo) + public static List getPullRequests(@NotNull GithubConnection connection, @NotNull String user, @NotNull String repo) throws IOException { try { String path = "/repos/" + user + "/" + repo + "/pulls?" + PER_PAGE; @@ -937,7 +619,7 @@ public class GithubApiUtil { PagedRequest request = new PagedRequest(path, GithubPullRequest.class, GithubPullRequestRaw[].class, ACCEPT_V3_JSON_HTML_MARKUP); - return request.getAll(auth); + return request.getAll(connection); } catch (GithubConfusingException e) { e.setDetails("Can't get pull requests" + user + "/" + repo); @@ -953,7 +635,10 @@ public class GithubApiUtil { } @NotNull - public static List getPullRequestCommits(@NotNull GithubAuthData auth, @NotNull String user, @NotNull String repo, long id) + public static List getPullRequestCommits(@NotNull GithubConnection connection, + @NotNull String user, + @NotNull String repo, + long id) throws IOException { try { String path = "/repos/" + user + "/" + repo + "/pulls/" + id + "/commits?" + PER_PAGE; @@ -961,7 +646,7 @@ public class GithubApiUtil { PagedRequest request = new PagedRequest(path, GithubCommit.class, GithubCommitRaw[].class, ACCEPT_V3_JSON); - return request.getAll(auth); + return request.getAll(connection); } catch (GithubConfusingException e) { e.setDetails("Can't get pull request commits: " + user + "/" + repo + " - " + id); @@ -970,14 +655,17 @@ public class GithubApiUtil { } @NotNull - public static List getPullRequestFiles(@NotNull GithubAuthData auth, @NotNull String user, @NotNull String repo, long id) + public static List getPullRequestFiles(@NotNull GithubConnection connection, + @NotNull String user, + @NotNull String repo, + long id) throws IOException { try { String path = "/repos/" + user + "/" + repo + "/pulls/" + id + "/files?" + PER_PAGE; PagedRequest request = new PagedRequest(path, GithubFile.class, GithubFileRaw[].class, ACCEPT_V3_JSON); - return request.getAll(auth); + return request.getAll(connection); } catch (GithubConfusingException e) { e.setDetails("Can't get pull request files: " + user + "/" + repo + " - " + id); @@ -986,7 +674,7 @@ public class GithubApiUtil { } @NotNull - public static List getRepoBranches(@NotNull GithubAuthData auth, @NotNull String user, @NotNull String repo) + public static List getRepoBranches(@NotNull GithubConnection connection, @NotNull String user, @NotNull String repo) throws IOException { try { String path = "/repos/" + user + "/" + repo + "/branches?" + PER_PAGE; @@ -994,7 +682,7 @@ public class GithubApiUtil { PagedRequest request = new PagedRequest(path, GithubBranch.class, GithubBranchRaw[].class, ACCEPT_V3_JSON); - return request.getAll(auth); + return request.getAll(connection); } catch (GithubConfusingException e) { e.setDetails("Can't get repository branches: " + user + "/" + repo); @@ -1003,7 +691,7 @@ public class GithubApiUtil { } @Nullable - public static GithubRepo findForkByUser(@NotNull GithubAuthData auth, + public static GithubRepo findForkByUser(@NotNull GithubConnection connection, @NotNull String user, @NotNull String repo, @NotNull String forkUser) throws IOException { @@ -1013,7 +701,7 @@ public class GithubApiUtil { PagedRequest request = new PagedRequest(path, GithubRepo.class, GithubRepoRaw[].class, ACCEPT_V3_JSON); while (request.hasNext()) { - for (GithubRepo fork : request.next(auth)) { + for (GithubRepo fork : request.next(connection)) { if (StringUtil.equalsIgnoreCase(fork.getUserName(), forkUser)) { return fork; } @@ -1027,4 +715,4 @@ public class GithubApiUtil { throw e; } } -} \ No newline at end of file +} diff --git a/plugins/github/src/org/jetbrains/plugins/github/api/GithubChangeIssueStateRequest.java b/plugins/github/src/org/jetbrains/plugins/github/api/GithubChangeIssueStateRequest.java new file mode 100644 index 000000000000..9e21f64046e5 --- /dev/null +++ b/plugins/github/src/org/jetbrains/plugins/github/api/GithubChangeIssueStateRequest.java @@ -0,0 +1,12 @@ +package org.jetbrains.plugins.github.api; + +import org.jetbrains.annotations.NotNull; + +@SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"}) +public class GithubChangeIssueStateRequest { + @NotNull private final String state; + + public GithubChangeIssueStateRequest(@NotNull String state) { + this.state = state; + } +} diff --git a/plugins/github/src/org/jetbrains/plugins/github/api/GithubConnection.java b/plugins/github/src/org/jetbrains/plugins/github/api/GithubConnection.java new file mode 100644 index 000000000000..6358b79e38ee --- /dev/null +++ b/plugins/github/src/org/jetbrains/plugins/github/api/GithubConnection.java @@ -0,0 +1,491 @@ +package org.jetbrains.plugins.github.api; + +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; +import com.google.gson.JsonParser; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.util.net.HttpConfigurable; +import com.intellij.util.net.ssl.CertificateManager; +import org.apache.http.*; +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.Credentials; +import org.apache.http.auth.UsernamePasswordCredentials; +import org.apache.http.client.CredentialsProvider; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.*; +import org.apache.http.client.protocol.HttpClientContext; +import org.apache.http.config.ConnectionConfig; +import org.apache.http.conn.ssl.X509HostnameVerifier; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.auth.BasicScheme; +import org.apache.http.impl.client.BasicCredentialsProvider; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.message.BasicHeader; +import org.apache.http.protocol.HttpContext; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.TestOnly; +import org.jetbrains.plugins.github.exceptions.*; +import org.jetbrains.plugins.github.util.GithubAuthData; +import org.jetbrains.plugins.github.util.GithubSettings; +import org.jetbrains.plugins.github.util.GithubUrlUtil; +import org.jetbrains.plugins.github.util.GithubUtil; +import sun.security.validator.ValidatorException; + +import javax.net.ssl.SSLHandshakeException; +import java.awt.*; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.util.*; +import java.util.List; + +import static org.jetbrains.plugins.github.api.GithubApiUtil.createDataFromRaw; +import static org.jetbrains.plugins.github.api.GithubApiUtil.fromJson; + +public class GithubConnection { + private static final Logger LOG = GithubUtil.LOG; + + private static final HttpRequestInterceptor PREEMPTIVE_BASIC_AUTH = new PreemptiveBasicAuthInterceptor(); + + @NotNull private final String myHost; + @NotNull private final CloseableHttpClient myClient; + private final boolean myReusable; + + private volatile HttpUriRequest myRequest; + private volatile boolean myAborted; + + @TestOnly + public GithubConnection(@NotNull GithubAuthData auth) { + this(auth, false); + } + + public GithubConnection(@NotNull GithubAuthData auth, boolean reusable) { + myHost = auth.getHost(); + myClient = createClient(auth); + myReusable = reusable; + } + + private enum HttpVerb { + GET, POST, DELETE, HEAD, PATCH + } + + @Nullable + public JsonElement getRequest(@NotNull String path, + @NotNull Header... headers) throws IOException { + return request(path, null, Arrays.asList(headers), HttpVerb.GET).getJsonElement(); + } + + @Nullable + public JsonElement postRequest(@NotNull String path, + @Nullable String requestBody, + @NotNull Header... headers) throws IOException { + return request(path, requestBody, Arrays.asList(headers), HttpVerb.POST).getJsonElement(); + } + + @Nullable + public JsonElement patchRequest(@NotNull String path, + @Nullable String requestBody, + @NotNull Header... headers) throws IOException { + return request(path, requestBody, Arrays.asList(headers), HttpVerb.PATCH).getJsonElement(); + } + + @Nullable + public JsonElement deleteRequest(@NotNull String path, + @NotNull Header... headers) throws IOException { + return request(path, null, Arrays.asList(headers), HttpVerb.DELETE).getJsonElement(); + } + + @NotNull + public Header[] headRequest(@NotNull String path, + @NotNull Header... headers) throws IOException { + return request(path, null, Arrays.asList(headers), HttpVerb.HEAD).getHeaders(); + } + + public void abort() { + if (myAborted) return; + myAborted = true; + + HttpUriRequest request = myRequest; + if (request != null) request.abort(); + } + + public void close() throws IOException { + myClient.close(); + } + + @NotNull + private static CloseableHttpClient createClient(@NotNull GithubAuthData auth) { + HttpClientBuilder builder = HttpClients.custom(); + + return builder + .setDefaultRequestConfig(createRequestConfig(auth)) + .setDefaultConnectionConfig(createConnectionConfig(auth)) + .setDefaultCredentialsProvider(createCredentialsProvider(auth)) + .setDefaultHeaders(createHeaders(auth)) + .addInterceptorFirst(PREEMPTIVE_BASIC_AUTH) + .setSslcontext(CertificateManager.getInstance().getSslContext()) + .setHostnameVerifier((X509HostnameVerifier)CertificateManager.HOSTNAME_VERIFIER) + .build(); + } + + @NotNull + private static RequestConfig createRequestConfig(@NotNull GithubAuthData auth) { + RequestConfig.Builder builder = RequestConfig.custom(); + + int timeout = GithubSettings.getInstance().getConnectionTimeout(); + builder + .setConnectTimeout(timeout) + .setSocketTimeout(timeout); + + final HttpConfigurable proxySettings = HttpConfigurable.getInstance(); + if (auth.isUseProxy() && proxySettings.USE_HTTP_PROXY && !StringUtil.isEmptyOrSpaces(proxySettings.PROXY_HOST)) { + builder + .setProxy(new HttpHost(proxySettings.PROXY_HOST, proxySettings.PROXY_PORT)); + } + + return builder.build(); + } + + @NotNull + private static ConnectionConfig createConnectionConfig(@NotNull GithubAuthData auth) { + return ConnectionConfig.custom() + .setCharset(Consts.UTF_8) + .build(); + } + + + @NotNull + private static CredentialsProvider createCredentialsProvider(@NotNull GithubAuthData auth) { + CredentialsProvider provider = new BasicCredentialsProvider(); + // Basic authentication + GithubAuthData.BasicAuth basicAuth = auth.getBasicAuth(); + if (basicAuth != null) { + provider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(basicAuth.getLogin(), basicAuth.getPassword())); + } + + final HttpConfigurable proxySettings = HttpConfigurable.getInstance(); + //proxySettings.USE_HTTP_PROXY + if (auth.isUseProxy() && proxySettings.USE_HTTP_PROXY && !StringUtil.isEmptyOrSpaces(proxySettings.PROXY_HOST)) { + if (proxySettings.PROXY_AUTHENTICATION) { + provider.setCredentials(new AuthScope(proxySettings.PROXY_HOST, proxySettings.PROXY_PORT), + new UsernamePasswordCredentials(proxySettings.PROXY_LOGIN, proxySettings.getPlainProxyPassword())); + } + } + return provider; + } + + @NotNull + private static Collection createHeaders(@NotNull GithubAuthData auth) { + List
headers = new ArrayList
(); + GithubAuthData.TokenAuth tokenAuth = auth.getTokenAuth(); + if (tokenAuth != null) { + headers.add(new BasicHeader("Authorization", "token " + tokenAuth.getToken())); + } + GithubAuthData.BasicAuth basicAuth = auth.getBasicAuth(); + if (basicAuth != null && basicAuth.getCode() != null) { + headers.add(new BasicHeader("X-GitHub-OTP", basicAuth.getCode())); + } + return headers; + } + + @NotNull + private ResponsePage request(@NotNull String path, + @Nullable String requestBody, + @NotNull Collection
headers, + @NotNull HttpVerb verb) throws IOException { + if (myAborted) throw new GithubOperationCanceledException(); + + if (EventQueue.isDispatchThread() && !ApplicationManager.getApplication().isUnitTestMode()) { + LOG.warn("Network operation in EDT"); // TODO: fix + } + + CloseableHttpResponse response = null; + try { + String uri = GithubUrlUtil.getApiUrl(myHost) + path; + response = doREST(uri, requestBody, headers, verb); + + if (myAborted) throw new GithubOperationCanceledException(); + + checkStatusCode(response, requestBody); + + HttpEntity entity = response.getEntity(); + if (entity == null) { + return createResponse(response); + } + + JsonElement ret = parseResponse(entity.getContent()); + if (ret.isJsonNull()) { + return createResponse(response); + } + + String newPath = null; + Header pageHeader = response.getFirstHeader("Link"); + if (pageHeader != null) { + for (HeaderElement element : pageHeader.getElements()) { + NameValuePair rel = element.getParameterByName("rel"); + if (rel != null && "next".equals(rel.getValue())) { + String urlString = element.toString(); + int begin = urlString.indexOf('<'); + int end = urlString.lastIndexOf('>'); + if (begin == -1 || end == -1) { + LOG.error("Invalid 'Link' header", "{" + pageHeader.toString() + "}"); + break; + } + + String url = urlString.substring(begin + 1, end); + String newUrl = GithubUrlUtil.removeProtocolPrefix(url); + int index = newUrl.indexOf('/'); + newPath = newUrl.substring(index); + break; + } + } + } + + return createResponse(ret, newPath, response); + } + catch (SSLHandshakeException e) { // User canceled operation from CertificateManager + if (e.getCause() instanceof ValidatorException) { + LOG.info("Host SSL certificate is not trusted", e); + throw new GithubOperationCanceledException("Host SSL certificate is not trusted", e); + } + throw e; + } + catch (IOException e) { + if (myAborted) throw new GithubOperationCanceledException("Operation canceled", e); + throw e; + } + finally { + myRequest = null; + if (response != null) { + response.close(); + } + if (!myReusable) { + myClient.close(); + } + } + } + + @NotNull + private CloseableHttpResponse doREST(@NotNull final String uri, + @Nullable final String requestBody, + @NotNull final Collection
headers, + @NotNull final HttpVerb verb) throws IOException { + HttpRequestBase request; + switch (verb) { + case POST: + request = new HttpPost(uri); + if (requestBody != null) { + ((HttpPost)request).setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON)); + } + break; + case PATCH: + request = new HttpPatch(uri); + if (requestBody != null) { + ((HttpPatch)request).setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON)); + } + break; + case GET: + request = new HttpGet(uri); + break; + case DELETE: + request = new HttpDelete(uri); + break; + case HEAD: + request = new HttpHead(uri); + break; + default: + throw new IllegalStateException("Unknown HttpVerb: " + verb.toString()); + } + + for (Header header : headers) { + request.addHeader(header); + } + + myRequest = request; + return myClient.execute(request); + } + + private static void checkStatusCode(@NotNull CloseableHttpResponse response, @Nullable String body) throws IOException { + int code = response.getStatusLine().getStatusCode(); + switch (code) { + case HttpStatus.SC_OK: + case HttpStatus.SC_CREATED: + case HttpStatus.SC_ACCEPTED: + case HttpStatus.SC_NO_CONTENT: + return; + case HttpStatus.SC_UNAUTHORIZED: + case HttpStatus.SC_PAYMENT_REQUIRED: + case HttpStatus.SC_FORBIDDEN: + String message = getErrorMessage(response); + + Header headerOTP = response.getFirstHeader("X-GitHub-OTP"); + if (headerOTP != null) { + for (HeaderElement element : headerOTP.getElements()) { + if ("required".equals(element.getName())) { + throw new GithubTwoFactorAuthenticationException(message); + } + } + } + + if (message.contains("API rate limit exceeded")) { + throw new GithubRateLimitExceededException(message); + } + + throw new GithubAuthenticationException("Request response: " + message); + case HttpStatus.SC_BAD_REQUEST: + case HttpStatus.SC_UNPROCESSABLE_ENTITY: + if (body != null) { + LOG.info(body); + } + throw new GithubStatusCodeException(code + ": " + getErrorMessage(response), code); + default: + throw new GithubStatusCodeException(code + ": " + getErrorMessage(response), code); + } + } + + @NotNull + private static String getErrorMessage(@NotNull CloseableHttpResponse response) { + try { + HttpEntity entity = response.getEntity(); + if (entity != null) { + GithubErrorMessageRaw error = fromJson(parseResponse(entity.getContent()), GithubErrorMessageRaw.class); + return response.getStatusLine().getReasonPhrase() + " - " + error.getMessage(); + } + } + catch (IOException e) { + LOG.info(e); + } + return response.getStatusLine().getReasonPhrase(); + } + + @NotNull + private static JsonElement parseResponse(@NotNull InputStream githubResponse) throws IOException { + Reader reader = new InputStreamReader(githubResponse, "UTF-8"); + try { + return new JsonParser().parse(reader); + } + catch (JsonParseException jse) { + throw new GithubJsonException("Couldn't parse GitHub response", jse); + } + finally { + reader.close(); + } + } + + public static class PagedRequest { + @Nullable private String myNextPage; + @NotNull private final Collection
myHeaders; + @NotNull private final Class myResult; + @NotNull private final Class myRawArray; + + @SuppressWarnings("NullableProblems") + public PagedRequest(@NotNull String path, + @NotNull Class result, + @NotNull Class rawArray, + @NotNull Header... headers) { + myNextPage = path; + myResult = result; + myRawArray = rawArray; + myHeaders = Arrays.asList(headers); + } + + @NotNull + public List next(@NotNull GithubConnection connection) throws IOException { + if (myNextPage == null) { + throw new NoSuchElementException(); + } + + String page = myNextPage; + myNextPage = null; + + ResponsePage response = connection.request(page, null, myHeaders, HttpVerb.GET); + + if (response.getJsonElement() == null) { + throw new GithubConfusingException("Empty response"); + } + + if (!response.getJsonElement().isJsonArray()) { + throw new GithubJsonException("Wrong json type: expected JsonArray", new Exception(response.getJsonElement().toString())); + } + + myNextPage = response.getNextPage(); + + List result = new ArrayList(); + for (DataConstructor raw : fromJson(response.getJsonElement().getAsJsonArray(), myRawArray)) { + result.add(createDataFromRaw(raw, myResult)); + } + return result; + } + + public boolean hasNext() { + return myNextPage != null; + } + + @NotNull + public List getAll(@NotNull GithubConnection connection) throws IOException { + List result = new ArrayList(); + while (hasNext()) { + result.addAll(next(connection)); + } + return result; + } + } + + private ResponsePage createResponse(@NotNull CloseableHttpResponse response) throws GithubOperationCanceledException { + if (myAborted) throw new GithubOperationCanceledException(); + + return new ResponsePage(null, null, response.getAllHeaders()); + } + + private ResponsePage createResponse(@NotNull JsonElement ret, @Nullable String path, @NotNull CloseableHttpResponse response) + throws GithubOperationCanceledException { + if (myAborted) throw new GithubOperationCanceledException(); + + return new ResponsePage(ret, path, response.getAllHeaders()); + } + + private static class ResponsePage { + @Nullable private final JsonElement myResponse; + @Nullable private final String myNextPage; + @NotNull private final Header[] myHeaders; + + public ResponsePage(@Nullable JsonElement response, @Nullable String next, @NotNull Header[] headers) { + myResponse = response; + myNextPage = next; + myHeaders = headers; + } + + @Nullable + public JsonElement getJsonElement() { + return myResponse; + } + + @Nullable + public String getNextPage() { + return myNextPage; + } + + @NotNull + public Header[] getHeaders() { + return myHeaders; + } + } + + private static class PreemptiveBasicAuthInterceptor implements HttpRequestInterceptor { + @Override + public void process(HttpRequest request, HttpContext context) throws HttpException, IOException { + CredentialsProvider provider = (CredentialsProvider)context.getAttribute(HttpClientContext.CREDS_PROVIDER); + Credentials credentials = provider.getCredentials(AuthScope.ANY); + if (credentials != null) { + request.addHeader(new BasicScheme(Consts.UTF_8).authenticate(credentials, request, context)); + } + } + } +} diff --git a/plugins/github/src/org/jetbrains/plugins/github/api/GithubFullPath.java b/plugins/github/src/org/jetbrains/plugins/github/api/GithubFullPath.java index ee0134e4fbc3..1575caee25db 100644 --- a/plugins/github/src/org/jetbrains/plugins/github/api/GithubFullPath.java +++ b/plugins/github/src/org/jetbrains/plugins/github/api/GithubFullPath.java @@ -46,6 +46,11 @@ public class GithubFullPath { return myUserName + '/' + myRepositoryName; } + @Override + public String toString() { + return "'" + getFullName() + "'"; + } + @Override public boolean equals(@Nullable Object o) { if (this == o) return true; @@ -53,8 +58,8 @@ public class GithubFullPath { GithubFullPath that = (GithubFullPath)o; - if (!StringUtil.endsWithIgnoreCase(myRepositoryName, that.myRepositoryName)) return false; - if (!StringUtil.endsWithIgnoreCase(myUserName, that.myUserName)) return false; + if (!StringUtil.equalsIgnoreCase(myRepositoryName, that.myRepositoryName)) return false; + if (!StringUtil.equalsIgnoreCase(myUserName, that.myUserName)) return false; return true; } diff --git a/plugins/github/src/org/jetbrains/plugins/github/api/GithubUser.java b/plugins/github/src/org/jetbrains/plugins/github/api/GithubUser.java index 29ef1acdfd78..bf81b8d62502 100644 --- a/plugins/github/src/org/jetbrains/plugins/github/api/GithubUser.java +++ b/plugins/github/src/org/jetbrains/plugins/github/api/GithubUser.java @@ -26,12 +26,12 @@ public class GithubUser { @NotNull private final String myHtmlUrl; - @Nullable private final String myGravatarId; + @Nullable private final String myAvatarUrl; - public GithubUser(@NotNull String login, @NotNull String htmlUrl, @Nullable String gravatarId) { + public GithubUser(@NotNull String login, @NotNull String htmlUrl, @Nullable String avatarUrl) { myLogin = login; myHtmlUrl = htmlUrl; - myGravatarId = gravatarId; + myAvatarUrl = avatarUrl; } @NotNull @@ -45,7 +45,7 @@ public class GithubUser { } @Nullable - public String getGravatarId() { - return myGravatarId; + public String getAvatarUrl() { + return myAvatarUrl; } } diff --git a/plugins/github/src/org/jetbrains/plugins/github/api/GithubUserDetailed.java b/plugins/github/src/org/jetbrains/plugins/github/api/GithubUserDetailed.java index 2c82c078e339..3940b80f360a 100644 --- a/plugins/github/src/org/jetbrains/plugins/github/api/GithubUserDetailed.java +++ b/plugins/github/src/org/jetbrains/plugins/github/api/GithubUserDetailed.java @@ -55,13 +55,13 @@ public class GithubUserDetailed extends GithubUser { public GithubUserDetailed(@NotNull String login, @NotNull String htmlUrl, - @Nullable String gravatarId, + @Nullable String avatarUrl, @Nullable String name, @Nullable String email, @Nullable Integer ownedPrivateRepos, @Nullable String type, @Nullable UserPlan plan) { - super(login, htmlUrl, gravatarId); + super(login, htmlUrl, avatarUrl); myName = name; myEmail = email; myOwnedPrivateRepos = ownedPrivateRepos; diff --git a/plugins/github/src/org/jetbrains/plugins/github/api/GithubUserRaw.java b/plugins/github/src/org/jetbrains/plugins/github/api/GithubUserRaw.java index 04d642f8eb2e..0bc673299847 100644 --- a/plugins/github/src/org/jetbrains/plugins/github/api/GithubUserRaw.java +++ b/plugins/github/src/org/jetbrains/plugins/github/api/GithubUserRaw.java @@ -74,14 +74,14 @@ class GithubUserRaw implements DataConstructor { @SuppressWarnings("ConstantConditions") @NotNull public GithubUser createUser() { - return new GithubUser(login, htmlUrl, gravatarId); + return new GithubUser(login, htmlUrl, avatarUrl); } @SuppressWarnings("ConstantConditions") @NotNull public GithubUserDetailed createUserDetailed() { GithubUserDetailed.UserPlan plan = this.plan == null ? null : this.plan.create(); - return new GithubUserDetailed(login, htmlUrl, gravatarId, name, email, ownedPrivateRepos, type, plan); + return new GithubUserDetailed(login, htmlUrl, avatarUrl, name, email, ownedPrivateRepos, type, plan); } @SuppressWarnings("unchecked") diff --git a/plugins/github/src/org/jetbrains/plugins/github/extensions/GithubCheckoutProvider.java b/plugins/github/src/org/jetbrains/plugins/github/extensions/GithubCheckoutProvider.java index 6b3f73f5f8be..911314c4f72d 100644 --- a/plugins/github/src/org/jetbrains/plugins/github/extensions/GithubCheckoutProvider.java +++ b/plugins/github/src/org/jetbrains/plugins/github/extensions/GithubCheckoutProvider.java @@ -29,6 +29,7 @@ import git4idea.commands.Git; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.github.api.GithubApiUtil; +import org.jetbrains.plugins.github.api.GithubConnection; import org.jetbrains.plugins.github.api.GithubRepo; import org.jetbrains.plugins.github.exceptions.GithubOperationCanceledException; import org.jetbrains.plugins.github.util.GithubAuthData; @@ -62,11 +63,11 @@ public class GithubCheckoutProvider implements CheckoutProvider { @Override public List convert(ProgressIndicator indicator) throws IOException { return GithubUtil.runTask(project, GithubAuthDataHolder.createFromSettings(), indicator, - new ThrowableConvertor, IOException>() { + new ThrowableConvertor, IOException>() { @NotNull @Override - public List convert(@NotNull GithubAuthData auth) throws IOException { - return GithubApiUtil.getAvailableRepos(auth); + public List convert(@NotNull GithubConnection connection) throws IOException { + return GithubApiUtil.getAvailableRepos(connection); } } ); diff --git a/plugins/github/src/org/jetbrains/plugins/github/tasks/GithubComment.java b/plugins/github/src/org/jetbrains/plugins/github/tasks/GithubComment.java index d65a95044a9c..e3115f71975c 100644 --- a/plugins/github/src/org/jetbrains/plugins/github/tasks/GithubComment.java +++ b/plugins/github/src/org/jetbrains/plugins/github/tasks/GithubComment.java @@ -26,16 +26,16 @@ import java.util.Date; * @author Dennis.Ushakov */ public class GithubComment extends SimpleComment { - @Nullable private final String myGravatarId; + @Nullable private final String myAvatarUrl; @NotNull private final String myUserHtmlUrl; public GithubComment(@Nullable Date date, @Nullable String author, @NotNull String text, - @Nullable String gravatarId, + @Nullable String avatarUrl, @NotNull String userHtmlUrl) { super(date, author, text); - myGravatarId = gravatarId; + myAvatarUrl = avatarUrl; myUserHtmlUrl = userHtmlUrl; } @@ -43,9 +43,9 @@ public class GithubComment extends SimpleComment { builder.append("
"); builder.append(""); builder.append("
"); - if (myGravatarId != null) { - builder.append("
"); - } + if (myAvatarUrl != null) { + builder.append("
"); + } builder.append("
"); if (getAuthor() != null) { builder.append("Author: ").append(getAuthor()).append("
"); diff --git a/plugins/github/src/org/jetbrains/plugins/github/tasks/GithubRepository.java b/plugins/github/src/org/jetbrains/plugins/github/tasks/GithubRepository.java index 7ad3238f8a12..a8445b7f76ac 100644 --- a/plugins/github/src/org/jetbrains/plugins/github/tasks/GithubRepository.java +++ b/plugins/github/src/org/jetbrains/plugins/github/tasks/GithubRepository.java @@ -5,10 +5,7 @@ import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.PasswordUtil; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.tasks.Comment; -import com.intellij.tasks.Task; -import com.intellij.tasks.TaskRepository; -import com.intellij.tasks.TaskType; +import com.intellij.tasks.*; import com.intellij.tasks.impl.BaseRepository; import com.intellij.tasks.impl.BaseRepositoryImpl; import com.intellij.util.Function; @@ -19,12 +16,10 @@ import icons.TasksIcons; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.github.api.GithubApiUtil; +import org.jetbrains.plugins.github.api.GithubConnection; import org.jetbrains.plugins.github.api.GithubIssue; import org.jetbrains.plugins.github.api.GithubIssueComment; -import org.jetbrains.plugins.github.exceptions.GithubAuthenticationException; -import org.jetbrains.plugins.github.exceptions.GithubJsonException; -import org.jetbrains.plugins.github.exceptions.GithubRateLimitExceededException; -import org.jetbrains.plugins.github.exceptions.GithubStatusCodeException; +import org.jetbrains.plugins.github.exceptions.*; import org.jetbrains.plugins.github.util.GithubAuthData; import org.jetbrains.plugins.github.util.GithubUtil; @@ -67,14 +62,20 @@ public class GithubRepository extends BaseRepositoryImpl { @Override public CancellableConnection createCancellableConnection() { return new CancellableConnection() { + private final GithubConnection myConnection = new GithubConnection(getAuthData(), false); + @Override protected void doTest() throws Exception { - getIssues("", 10, false); + try { + GithubApiUtil.getIssuesQueried(myConnection, getRepoAuthor(), getRepoName(), "", false); + } + catch (GithubOperationCanceledException ignore) { + } } @Override public void cancel() { - // TODO + myConnection.abort(); } }; } @@ -122,23 +123,30 @@ public class GithubRepository extends BaseRepositoryImpl { @NotNull private Task[] getIssues(@Nullable String query, int max, boolean withClosed) throws Exception { - List issues; - if (StringUtil.isEmptyOrSpaces(query)) { - if (StringUtil.isEmptyOrSpaces(myUser)) { - myUser = GithubApiUtil.getCurrentUser(getAuthData()).getLogin(); + GithubConnection connection = getConnection(); + + try { + List issues; + if (StringUtil.isEmptyOrSpaces(query)) { + if (StringUtil.isEmptyOrSpaces(myUser)) { + myUser = GithubApiUtil.getCurrentUser(connection).getLogin(); + } + issues = GithubApiUtil.getIssuesAssigned(connection, getRepoAuthor(), getRepoName(), myUser, max, withClosed); + } + else { + issues = GithubApiUtil.getIssuesQueried(connection, getRepoAuthor(), getRepoName(), query, withClosed); } - issues = GithubApiUtil.getIssuesAssigned(getAuthData(), getRepoAuthor(), getRepoName(), myUser, max, withClosed); + + return ContainerUtil.map2Array(issues, Task.class, new Function() { + @Override + public Task fun(GithubIssue issue) { + return createTask(issue); + } + }); } - else { - issues = GithubApiUtil.getIssuesQueried(getAuthData(), getRepoAuthor(), getRepoName(), query, withClosed); + finally { + connection.close(); } - - return ContainerUtil.map2Array(issues, Task.class, new Function() { - @Override - public Task fun(GithubIssue issue) { - return createTask(issue); - } - }); } @NotNull @@ -224,16 +232,22 @@ public class GithubRepository extends BaseRepositoryImpl { } private Comment[] fetchComments(final long id) throws Exception { - List result = GithubApiUtil.getIssueComments(getAuthData(), getRepoAuthor(), getRepoName(), id); - - return ContainerUtil.map2Array(result, Comment.class, new Function() { - @Override - public Comment fun(GithubIssueComment comment) { - return new GithubComment(comment.getCreatedAt(), comment.getUser().getLogin(), comment.getBodyHtml(), - comment.getUser().getGravatarId(), - comment.getUser().getHtmlUrl()); - } - }); + GithubConnection connection = getConnection(); + try { + List result = GithubApiUtil.getIssueComments(connection, getRepoAuthor(), getRepoName(), id); + + return ContainerUtil.map2Array(result, Comment.class, new Function() { + @Override + public Comment fun(GithubIssueComment comment) { + return new GithubComment(comment.getCreatedAt(), comment.getUser().getLogin(), comment.getBodyHtml(), + comment.getUser().getAvatarUrl(), + comment.getUser().getHtmlUrl()); + } + }); + } + finally { + connection.close(); + } } @Nullable @@ -245,7 +259,35 @@ public class GithubRepository extends BaseRepositoryImpl { @Nullable @Override public Task findTask(@NotNull String id) throws Exception { - return createTask(GithubApiUtil.getIssue(getAuthData(), getRepoAuthor(), getRepoName(), id)); + GithubConnection connection = getConnection(); + try { + return createTask(GithubApiUtil.getIssue(connection, getRepoAuthor(), getRepoName(), id)); + } + finally { + connection.close(); + } + } + + @Override + public void setTaskState(@NotNull Task task, @NotNull TaskState state) throws Exception { + GithubConnection connection = getConnection(); + try { + boolean isOpen; + switch (state) { + case OPEN: + isOpen = true; + break; + case RESOLVED: + isOpen = false; + break; + default: + throw new IllegalStateException("Unknown state: " + state); + } + GithubApiUtil.setIssueState(connection, getRepoAuthor(), getRepoName(), task.getNumber(), isOpen); + } + finally { + connection.close(); + } } @NotNull @@ -311,6 +353,10 @@ public class GithubRepository extends BaseRepositoryImpl { return GithubAuthData.createTokenAuth(getUrl(), getToken(), isUseProxy()); } + private GithubConnection getConnection() { + return new GithubConnection(getAuthData(), true); + } + @Override public boolean equals(Object o) { if (!super.equals(o)) return false; @@ -326,6 +372,6 @@ public class GithubRepository extends BaseRepositoryImpl { @Override protected int getFeatures() { - return super.getFeatures() | BASIC_HTTP_AUTHORIZATION; + return super.getFeatures() | STATE_UPDATING; } } diff --git a/plugins/github/src/org/jetbrains/plugins/github/tasks/GithubRepositoryEditor.java b/plugins/github/src/org/jetbrains/plugins/github/tasks/GithubRepositoryEditor.java index 5333d82e3baf..ba863621c969 100644 --- a/plugins/github/src/org/jetbrains/plugins/github/tasks/GithubRepositoryEditor.java +++ b/plugins/github/src/org/jetbrains/plugins/github/tasks/GithubRepositoryEditor.java @@ -14,8 +14,7 @@ import com.intellij.util.ui.GridBag; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.github.api.GithubApiUtil; -import org.jetbrains.plugins.github.exceptions.GithubOperationCanceledException; -import org.jetbrains.plugins.github.util.GithubAuthData; +import org.jetbrains.plugins.github.api.GithubConnection; import org.jetbrains.plugins.github.util.GithubAuthDataHolder; import org.jetbrains.plugins.github.util.GithubNotifications; import org.jetbrains.plugins.github.util.GithubUtil; @@ -126,12 +125,12 @@ public class GithubRepositoryEditor extends BaseRepositoryEditor() { + new ThrowableConvertor() { @NotNull @Override - public String convert(@NotNull GithubAuthData auth) throws IOException { + public String convert(@NotNull GithubConnection connection) throws IOException { return GithubApiUtil - .getReadOnlyToken(auth, getRepoAuthor(), getRepoName(), "IntelliJ tasks plugin"); + .getReadOnlyToken(connection, getRepoAuthor(), getRepoName(), "IntelliJ tasks plugin"); } } ); diff --git a/plugins/github/src/org/jetbrains/plugins/github/tasks/GithubRepositoryType.java b/plugins/github/src/org/jetbrains/plugins/github/tasks/GithubRepositoryType.java index a677fad0d781..eefab9065add 100644 --- a/plugins/github/src/org/jetbrains/plugins/github/tasks/GithubRepositoryType.java +++ b/plugins/github/src/org/jetbrains/plugins/github/tasks/GithubRepositoryType.java @@ -2,6 +2,7 @@ package org.jetbrains.plugins.github.tasks; import com.intellij.openapi.project.Project; import com.intellij.tasks.TaskRepository; +import com.intellij.tasks.TaskState; import com.intellij.tasks.config.TaskRepositoryEditor; import com.intellij.tasks.impl.BaseRepositoryType; import com.intellij.util.Consumer; @@ -9,6 +10,7 @@ import icons.TasksIcons; import org.jetbrains.annotations.NotNull; import javax.swing.*; +import java.util.EnumSet; /** * @author Dennis.Ushakov @@ -45,4 +47,9 @@ public class GithubRepositoryType extends BaseRepositoryType { Consumer changeListener) { return new GithubRepositoryEditor(project, repository, changeListener); } + + public EnumSet getPossibleTaskStates() { + return EnumSet.of(TaskState.OPEN, TaskState.RESOLVED); + } + } diff --git a/plugins/github/src/org/jetbrains/plugins/github/ui/GithubCreatePullRequestDialog.java b/plugins/github/src/org/jetbrains/plugins/github/ui/GithubCreatePullRequestDialog.java index f062742d191c..482ef9492fdc 100644 --- a/plugins/github/src/org/jetbrains/plugins/github/ui/GithubCreatePullRequestDialog.java +++ b/plugins/github/src/org/jetbrains/plugins/github/ui/GithubCreatePullRequestDialog.java @@ -15,142 +15,159 @@ */ package org.jetbrains.plugins.github.ui; +import com.intellij.CommonBundle; +import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.ui.Messages; import com.intellij.openapi.ui.ValidationInfo; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.util.Consumer; -import com.intellij.util.ui.UIUtil; +import com.intellij.util.ThreeState; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.TestOnly; import org.jetbrains.plugins.github.GithubCreatePullRequestWorker; import org.jetbrains.plugins.github.api.GithubFullPath; +import org.jetbrains.plugins.github.util.GithubNotifications; import org.jetbrains.plugins.github.util.GithubProjectSettings; +import org.jetbrains.plugins.github.util.GithubSettings; import javax.swing.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; -import java.util.Collection; import java.util.Collections; +import static org.jetbrains.plugins.github.GithubCreatePullRequestWorker.BranchInfo; +import static org.jetbrains.plugins.github.GithubCreatePullRequestWorker.ForkInfo; + /** * @author Aleksey Pivovarov */ public class GithubCreatePullRequestDialog extends DialogWrapper { - @NotNull private final GithubCreatePullRequestPanel myGithubCreatePullRequestPanel; + @NotNull private final GithubCreatePullRequestPanel myPanel; @NotNull private final GithubCreatePullRequestWorker myWorker; @NotNull private final GithubProjectSettings myProjectSettings; + @NotNull private static final CreateRemoteDoNotAskOption ourDoNotAskOption = new CreateRemoteDoNotAskOption(); - public GithubCreatePullRequestDialog(@NotNull GithubCreatePullRequestWorker worker) { - super(worker.getProject(), true); + public GithubCreatePullRequestDialog(@NotNull final Project project, @NotNull GithubCreatePullRequestWorker worker) { + super(project, true); myWorker = worker; - myProjectSettings = GithubProjectSettings.getInstance(myWorker.getProject()); - - myGithubCreatePullRequestPanel = new GithubCreatePullRequestPanel(); + myProjectSettings = GithubProjectSettings.getInstance(project); - myGithubCreatePullRequestPanel.getShowDiffButton().addActionListener(new ActionListener() { + myPanel = new GithubCreatePullRequestPanel(); + myPanel.getShowDiffButton().addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - myWorker.showDiffDialog(myGithubCreatePullRequestPanel.getBranch()); + myWorker.showDiffDialog(myPanel.getSelectedBranch()); } }); - myGithubCreatePullRequestPanel.getSelectForkButton().addActionListener(new ActionListener() { + myPanel.getSelectForkButton().addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - showTargetDialog(); + ForkInfo fork = myWorker.showTargetDialog(); + if (fork != null) { + myPanel.setForks(myWorker.getForks()); + myPanel.setSelectedFork(fork.getPath()); + } } }); - myGithubCreatePullRequestPanel.getBranchComboBox().addItemListener(new ItemListener() { + + myPanel.getForkComboBox().addItemListener(new ItemListener() { @Override public void itemStateChanged(ItemEvent e) { + if (e.getStateChange() == ItemEvent.DESELECTED) { + myPanel.setBranches(Collections.emptyList()); + } if (e.getStateChange() == ItemEvent.SELECTED) { - if (myWorker.canShowDiff()) { - myGithubCreatePullRequestPanel.setBusy(true); - myWorker.getDiffDescriptionInPooledThread(getTargetBranch(), new Consumer() { - @Override - public void consume(final GithubCreatePullRequestWorker.DiffDescription info) { - UIUtil.invokeLaterIfNeeded(new Runnable() { - @Override - public void run() { - if (info == null) { - myGithubCreatePullRequestPanel.setBusy(false); - return; - } - if (getTargetBranch().equals(info.getBranch())) { - myGithubCreatePullRequestPanel.setBusy(false); - if (myGithubCreatePullRequestPanel.isTitleDescriptionEmptyOrNotModified()) { - myGithubCreatePullRequestPanel.setTitle(info.getTitle()); - myGithubCreatePullRequestPanel.setDescription(info.getDescription()); - } - } - } - }); - } - }); + final ForkInfo fork = (ForkInfo)e.getItem(); + if (fork == null) return; + + myPanel.setBranches(fork.getBranches()); + myPanel.setSelectedBranch(fork.getDefaultBranch()); + + if (fork.getRemoteName() == null && !fork.isProposedToCreateRemote()) { + fork.setProposedToCreateRemote(true); + boolean createRemote = false; + + switch (GithubSettings.getInstance().getCreatePullRequestCreateRemote()) { + case YES: + createRemote = true; + break; + case NO: + createRemote = false; + break; + case UNSURE: + createRemote = GithubNotifications.showYesNoDialog(project, + "Can't Find Remote", + "Configure remote for '" + fork.getPath().getUser() + "'?", + ourDoNotAskOption) == Messages.YES; + break; + } + + if (createRemote) { + myWorker.configureRemote(fork); + } + } + + if (fork.getRemoteName() == null) { + myPanel.setDiffEnabled(false); + } + else { + myPanel.setDiffEnabled(true); + myWorker.launchFetchRemote(fork); } } } }); - setTitle("Create Pull Request - " + myWorker.getCurrentBranch()); - init(); - } - - @Override - public void show() { - GithubFullPath defaultForkPath = myProjectSettings.getCreatePullRequestDefaultRepo(); - if (defaultForkPath != null) { - setTarget(defaultForkPath); - } - else { - if (!showTargetDialog(true)) { - close(CANCEL_EXIT_CODE); - return; - } - } - super.show(); - } - - private boolean showTargetDialog() { - return showTargetDialog(false); - } + myPanel.getBranchComboBox().addItemListener(new ItemListener() { + @Override + public void itemStateChanged(ItemEvent e) { + if (e.getStateChange() == ItemEvent.SELECTED) { + BranchInfo branch = (BranchInfo)e.getItem(); + if (branch == null) return; - private boolean showTargetDialog(boolean firstTime) { - GithubFullPath forkPath = myWorker.showTargetDialog(firstTime); - if (forkPath == null) { - return false; - } - return setTarget(forkPath); - } + if (branch.getForkInfo().getRemoteName() != null) { + if (branch.getDiffInfoTask() != null && branch.getDiffInfoTask().isDone() && branch.getDiffInfoTask().safeGet() == null) { + myPanel.setDiffEnabled(false); + } + else { + myPanel.setDiffEnabled(true); + } + } - private boolean setTarget(@NotNull GithubFullPath forkPath) { - GithubCreatePullRequestWorker.GithubTargetInfo forkInfo = myWorker.setTarget(forkPath); - if (forkInfo == null) { - return false; - } - myProjectSettings.setCreatePullRequestDefaultRepo(forkPath); - myGithubCreatePullRequestPanel.setDiffEnabled(myWorker.canShowDiff()); - updateBranches(forkInfo.getBranches(), forkPath); - return true; - } + if (myPanel.isTitleDescriptionEmptyOrNotModified()) { + Pair description = myWorker.getDefaultDescriptionMessage(branch); + myPanel.setTitle(description.getFirst()); + myPanel.setDescription(description.getSecond()); + } - private void updateBranches(@NotNull Collection branches, @NotNull GithubFullPath forkPath) { - myGithubCreatePullRequestPanel.setBranches(branches); + myWorker.launchLoadDiffInfo(branch); + } + } + }); - String configBranch = myProjectSettings.getCreatePullRequestDefaultBranch(); - if (configBranch != null) myGithubCreatePullRequestPanel.setSelectedBranch(configBranch); + myPanel.setForks(myWorker.getForks()); + myPanel.setSelectedFork(myProjectSettings.getCreatePullRequestDefaultRepo()); + myPanel.setSelectedBranch(myProjectSettings.getCreatePullRequestDefaultBranch()); - myGithubCreatePullRequestPanel.setForkName(forkPath.getFullName()); + setTitle("Create Pull Request - " + myWorker.getCurrentBranch()); + init(); } @Override protected void doOKAction() { - if (myWorker.checkAction(getTargetBranch())) { - myProjectSettings.setCreatePullRequestDefaultBranch(getTargetBranch()); - myWorker.performAction(getRequestTitle(), getDescription(), getTargetBranch()); + BranchInfo branch = myPanel.getSelectedBranch(); + if (myWorker.checkAction(branch)) { + assert branch != null; + myWorker.createPullRequest(branch, getRequestTitle(), getDescription()); + + myProjectSettings.setCreatePullRequestDefaultBranch(branch.getRemoteName()); + myProjectSettings.setCreatePullRequestDefaultRepo(branch.getForkInfo().getPath()); + super.doOKAction(); } } @@ -158,13 +175,13 @@ public class GithubCreatePullRequestDialog extends DialogWrapper { @Nullable @Override protected JComponent createCenterPanel() { - return myGithubCreatePullRequestPanel.getPanel(); + return myPanel.getPanel(); } @Nullable @Override public JComponent getPreferredFocusedComponent() { - return myGithubCreatePullRequestPanel.getPreferredComponent(); + return myPanel.getPreferredComponent(); } @Override @@ -179,50 +196,76 @@ public class GithubCreatePullRequestDialog extends DialogWrapper { @NotNull private String getRequestTitle() { - return myGithubCreatePullRequestPanel.getTitle(); + return myPanel.getTitle(); } @NotNull private String getDescription() { - return myGithubCreatePullRequestPanel.getDescription(); - } - - @NotNull - private String getTargetBranch() { - return myGithubCreatePullRequestPanel.getBranch(); + return myPanel.getDescription(); } @Nullable @Override protected ValidationInfo doValidate() { if (StringUtil.isEmptyOrSpaces(getRequestTitle())) { - return new ValidationInfo("Title can't be empty'", myGithubCreatePullRequestPanel.getTitleTextField()); + return new ValidationInfo("Title can't be empty'", myPanel.getTitleTextField()); } return null; } @TestOnly public void testSetRequestTitle(String title) { - myGithubCreatePullRequestPanel.setTitle(title); + myPanel.setTitle(title); } @TestOnly public void testSetBranch(String branch) { - myGithubCreatePullRequestPanel.setBranches(Collections.singleton(branch)); + myPanel.setSelectedBranch(branch); } @TestOnly public void testCreatePullRequest() { - myWorker.performAction(getRequestTitle(), getDescription(), getTargetBranch()); + myWorker.createPullRequest(myPanel.getSelectedBranch(), getRequestTitle(), getDescription()); } @TestOnly - public void testSetTarget(@NotNull GithubFullPath forkPath) { - GithubCreatePullRequestWorker.GithubTargetInfo forkInfo = myWorker.setTarget(forkPath); - if (forkInfo == null) { - doCancelAction(); - return; + public void testSetFork(@NotNull GithubFullPath forkPath) { + myPanel.setSelectedFork(forkPath); + } + + private static class CreateRemoteDoNotAskOption implements DoNotAskOption { + @Override + public boolean isToBeShown() { + return true; + } + + @Override + public void setToBeShown(boolean value, int exitCode) { + if (value) { + GithubSettings.getInstance().setCreatePullRequestCreateRemote(ThreeState.UNSURE); + } + else if (exitCode == DialogWrapper.OK_EXIT_CODE) { + GithubSettings.getInstance().setCreatePullRequestCreateRemote(ThreeState.YES); + } + else { + GithubSettings.getInstance().setCreatePullRequestCreateRemote(ThreeState.NO); + } + } + + @Override + public boolean canBeHidden() { + return true; + } + + @Override + public boolean shouldSaveOptionsOnCancel() { + return false; + } + + @NotNull + @Override + public String getDoNotShowMessage() { + return CommonBundle.message("dialog.options.do.not.ask"); } - myGithubCreatePullRequestPanel.setDiffEnabled(myWorker.canShowDiff()); } } diff --git a/plugins/github/src/org/jetbrains/plugins/github/ui/GithubCreatePullRequestPanel.form b/plugins/github/src/org/jetbrains/plugins/github/ui/GithubCreatePullRequestPanel.form index e19b8601fa01..342fcfaecde7 100644 --- a/plugins/github/src/org/jetbrains/plugins/github/ui/GithubCreatePullRequestPanel.form +++ b/plugins/github/src/org/jetbrains/plugins/github/ui/GithubCreatePullRequestPanel.form @@ -1,6 +1,6 @@
- + @@ -34,7 +34,7 @@ - + @@ -42,7 +42,7 @@ - + @@ -57,7 +57,7 @@ - + @@ -73,26 +73,12 @@ - + - + - - - - - - - - - - - - - - @@ -102,6 +88,12 @@ + + + + + + diff --git a/plugins/github/src/org/jetbrains/plugins/github/ui/GithubCreatePullRequestPanel.java b/plugins/github/src/org/jetbrains/plugins/github/ui/GithubCreatePullRequestPanel.java index 41512465eeea..f17562f12600 100644 --- a/plugins/github/src/org/jetbrains/plugins/github/ui/GithubCreatePullRequestPanel.java +++ b/plugins/github/src/org/jetbrains/plugins/github/ui/GithubCreatePullRequestPanel.java @@ -19,9 +19,9 @@ import com.intellij.openapi.ui.ComboBox; import com.intellij.openapi.util.text.StringUtil; import com.intellij.ui.DocumentAdapter; import com.intellij.ui.SortedComboBoxModel; -import com.intellij.util.ui.AsyncProcessIcon; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.github.api.GithubFullPath; import javax.swing.*; import javax.swing.event.DocumentEvent; @@ -29,6 +29,9 @@ import javax.swing.event.DocumentListener; import java.util.Collection; import java.util.Comparator; +import static org.jetbrains.plugins.github.GithubCreatePullRequestWorker.BranchInfo; +import static org.jetbrains.plugins.github.GithubCreatePullRequestWorker.ForkInfo; + /** * @author Aleksey Pivovarov */ @@ -36,25 +39,35 @@ public class GithubCreatePullRequestPanel { private JTextField myTitleTextField; private JTextArea myDescriptionTextArea; private ComboBox myBranchComboBox; - private SortedComboBoxModel myBranchModel; + private SortedComboBoxModel myForkModel; + private SortedComboBoxModel myBranchModel; private JPanel myPanel; private JButton myShowDiffButton; private JButton mySelectForkButton; private JLabel myForkLabel; - private AsyncProcessIcon myBusyIcon; + private ComboBox myForkComboBox; private boolean myTitleDescriptionUserModified = false; public GithubCreatePullRequestPanel() { myDescriptionTextArea.setBorder(BorderFactory.createEtchedBorder()); - myBranchModel = new SortedComboBoxModel(new Comparator() { + + myBranchModel = new SortedComboBoxModel(new Comparator() { @Override - public int compare(String o1, String o2) { - return StringUtil.naturalCompare(o1, o2); + public int compare(BranchInfo o1, BranchInfo o2) { + return StringUtil.naturalCompare(o1.getRemoteName(), o2.getRemoteName()); } }); myBranchComboBox.setModel(myBranchModel); + myForkModel = new SortedComboBoxModel(new Comparator() { + @Override + public int compare(ForkInfo o1, ForkInfo o2) { + return StringUtil.naturalCompare(o1.getPath().getUser(), o2.getPath().getUser()); + } + }); + myForkComboBox.setModel(myForkModel); + DocumentListener userModifiedDocumentListener = new DocumentAdapter() { @Override protected void textChanged(DocumentEvent e) { @@ -75,43 +88,69 @@ public class GithubCreatePullRequestPanel { return myDescriptionTextArea.getText(); } - @NotNull - public String getBranch() { - return myBranchComboBox.getSelectedItem().toString(); + @Nullable + public ForkInfo getSelectedFork() { + return myForkModel.getSelectedItem(); } - public void setDiffEnabled(boolean enabled) { - myShowDiffButton.setEnabled(enabled); + @Nullable + public BranchInfo getSelectedBranch() { + return myBranchModel.getSelectedItem(); } - public void setSelectedBranch(@Nullable String branch) { - if (StringUtil.isEmptyOrSpaces(branch)) { - return; + public void setSelectedFork(@Nullable GithubFullPath path) { + if (path != null) { + for (ForkInfo info : myForkModel.getItems()) { + if (path.equals(info.getPath())) { + myForkModel.setSelectedItem(info); + return; + } + } } - myBranchComboBox.setSelectedItem(branch); + if (myForkModel.getSize() > 0) myForkModel.setSelectedItem(myForkModel.get(0)); } - public void setBranches(@NotNull Collection branches) { - myBranchModel.clear(); - myBranchModel.addAll(branches); - if (branches.size() > 0) { - myBranchComboBox.setSelectedIndex(0); + public void setSelectedBranch(@Nullable String branch) { + if (branch != null) { + for (BranchInfo info : myBranchModel.getItems()) { + if (branch.equals(info.getRemoteName())) { + myBranchModel.setSelectedItem(info); + return; + } + } } + + if (myBranchModel.getSize() > 0) myBranchModel.setSelectedItem(myBranchModel.get(0)); } - public JPanel getPanel() { - return myPanel; + public void setForks(@NotNull Collection forks) { + myForkModel.setSelectedItem(null); + myForkModel.setAll(forks); } - @NotNull - public JComponent getPreferredComponent() { - return myTitleTextField; + public void setBranches(@NotNull Collection branches) { + myBranchModel.setSelectedItem(null); + myBranchModel.setAll(branches); } - @NotNull - public JComponent getBranchEditor() { - return myBranchComboBox; + public void setTitle(@Nullable String title) { + myTitleTextField.setText(title); + myTitleDescriptionUserModified = false; + } + + public void setDescription(@Nullable String title) { + myDescriptionTextArea.setText(title); + myTitleDescriptionUserModified = false; + } + + public boolean isTitleDescriptionEmptyOrNotModified() { + return !myTitleDescriptionUserModified || + (StringUtil.isEmptyOrSpaces(myTitleTextField.getText()) && StringUtil.isEmptyOrSpaces(myDescriptionTextArea.getText())); + } + + public void setDiffEnabled(boolean enabled) { + myShowDiffButton.setEnabled(enabled); } @NotNull @@ -119,14 +158,19 @@ public class GithubCreatePullRequestPanel { return myTitleTextField; } + @NotNull + public JButton getSelectForkButton() { + return mySelectForkButton; + } + @NotNull public JButton getShowDiffButton() { return myShowDiffButton; } @NotNull - public JButton getSelectForkButton() { - return mySelectForkButton; + public ComboBox getForkComboBox() { + return myForkComboBox; } @NotNull @@ -134,36 +178,12 @@ public class GithubCreatePullRequestPanel { return myBranchComboBox; } - public void setTitle(@Nullable String title) { - myTitleTextField.setText(title); - myTitleDescriptionUserModified = false; - } - - public void setDescription(@Nullable String title) { - myDescriptionTextArea.setText(title); - myTitleDescriptionUserModified = false; - } - - public boolean isTitleDescriptionEmptyOrNotModified() { - return !myTitleDescriptionUserModified || - (StringUtil.isEmptyOrSpaces(myTitleTextField.getText()) && StringUtil.isEmptyOrSpaces(myDescriptionTextArea.getText())); - } - - public void setForkName(@NotNull String forkName) { - myForkLabel.setText(forkName); - } - - public void setBusy(boolean enabled) { - if (enabled) { - myBusyIcon.resume(); - } - else { - myBusyIcon.suspend(); - } + public JPanel getPanel() { + return myPanel; } - private void createUIComponents() { - myBusyIcon = new AsyncProcessIcon("Loading diff..."); - myBusyIcon.suspend(); + @NotNull + public JComponent getPreferredComponent() { + return myTitleTextField; } } diff --git a/plugins/github/src/org/jetbrains/plugins/github/ui/GithubSelectForkDialog.java b/plugins/github/src/org/jetbrains/plugins/github/ui/GithubSelectForkDialog.java index d8ce684eeb38..cea8d206a845 100644 --- a/plugins/github/src/org/jetbrains/plugins/github/ui/GithubSelectForkDialog.java +++ b/plugins/github/src/org/jetbrains/plugins/github/ui/GithubSelectForkDialog.java @@ -27,7 +27,9 @@ import org.jetbrains.plugins.github.api.GithubFullPath; import org.jetbrains.plugins.github.util.GithubNotifications; import javax.swing.*; -import java.util.Set; +import java.util.List; + +import static org.jetbrains.plugins.github.GithubCreatePullRequestWorker.ForkInfo; /** * @author Aleksey Pivovarov @@ -35,25 +37,27 @@ import java.util.Set; public class GithubSelectForkDialog extends DialogWrapper { @NotNull private final GithubSelectForkPanel myPanel; @NotNull private final Project myProject; - @NotNull private final Convertor myCheckFork; - private GithubFullPath myFullPath; + @NotNull private final Convertor myCheckFork; + private ForkInfo mySelectedFork; public GithubSelectForkDialog(@NotNull Project project, - @NotNull Set forks, - @NotNull Convertor checkFork) { + @Nullable List forks, + @NotNull Convertor checkFork) { super(project); myProject = project; myCheckFork = checkFork; myPanel = new GithubSelectForkPanel(); - myPanel.setUsers(ContainerUtil.map(forks, new Function() { - @Override - public String fun(GithubFullPath path) { - return path.getUser(); - } - })); + if (forks != null) { + myPanel.setUsers(ContainerUtil.map(forks, new Function() { + @Override + public String fun(GithubFullPath path) { + return path.getUser(); + } + })); + } setTitle("Select Base Fork Repository"); init(); @@ -61,12 +65,12 @@ public class GithubSelectForkDialog extends DialogWrapper { @Override protected void doOKAction() { - GithubFullPath path = myCheckFork.convert(myPanel.getUser()); - if (path == null) { + ForkInfo fork = myCheckFork.convert(myPanel.getUser()); + if (fork == null) { GithubNotifications.showErrorDialog(myProject, "Can't Find Repository", "Can't find fork for selected user"); } else { - myFullPath = path; + mySelectedFork = fork; super.doOKAction(); } } @@ -78,12 +82,7 @@ public class GithubSelectForkDialog extends DialogWrapper { } @NotNull - public GithubFullPath getPath() { - return myFullPath; - } - - @TestOnly - public void testSetUser(@NotNull String user) { - myPanel.setSelectedUser(user); + public ForkInfo getPath() { + return mySelectedFork; } } diff --git a/plugins/github/src/org/jetbrains/plugins/github/ui/GithubSelectForkPanel.java b/plugins/github/src/org/jetbrains/plugins/github/ui/GithubSelectForkPanel.java index 754afc617057..cb3e014ee7dc 100644 --- a/plugins/github/src/org/jetbrains/plugins/github/ui/GithubSelectForkPanel.java +++ b/plugins/github/src/org/jetbrains/plugins/github/ui/GithubSelectForkPanel.java @@ -57,14 +57,6 @@ public class GithubSelectForkPanel { return myComboBox.getSelectedItem().toString(); } - public void setSelectedUser(@Nullable String user) { - if (StringUtil.isEmptyOrSpaces(user)) { - return; - } - - myComboBox.setSelectedItem(user); - } - public JPanel getPanel() { return myPanel; } diff --git a/plugins/github/src/org/jetbrains/plugins/github/ui/GithubSettingsPanel.java b/plugins/github/src/org/jetbrains/plugins/github/ui/GithubSettingsPanel.java index 7de0dae30d69..9bcf2aa78fc8 100644 --- a/plugins/github/src/org/jetbrains/plugins/github/ui/GithubSettingsPanel.java +++ b/plugins/github/src/org/jetbrains/plugins/github/ui/GithubSettingsPanel.java @@ -30,6 +30,7 @@ import com.intellij.util.ThrowableConvertor; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.github.api.GithubApiUtil; +import org.jetbrains.plugins.github.api.GithubConnection; import org.jetbrains.plugins.github.api.GithubUser; import org.jetbrains.plugins.github.exceptions.GithubAuthenticationException; import org.jetbrains.plugins.github.exceptions.GithubOperationCanceledException; @@ -112,11 +113,10 @@ public class GithubSettingsPanel { } } catch (GithubAuthenticationException ex) { - GithubNotifications.showErrorDialog(myPane, "Login Failure", "Can't login using given credentials: " + ex.getMessage()); + GithubNotifications.showErrorDialog(myPane, "Login Failure", "Can't login using given credentials: ", ex); } catch (IOException ex) { - LOG.info(ex); - GithubNotifications.showErrorDialog(myPane, "Login Failure", "Can't login: " + GithubUtil.getErrorTextFromException(ex)); + GithubNotifications.showErrorDialog(myPane, "Login Failure", "Can't login: ", ex); } } }); @@ -131,11 +131,12 @@ public class GithubSettingsPanel { @Override public String convert(ProgressIndicator indicator) throws IOException { return GithubUtil.runTaskWithBasicAuthForHost(project, GithubAuthDataHolder.createFromSettings(), indicator, getHost(), - new ThrowableConvertor() { + new ThrowableConvertor() { @NotNull @Override - public String convert(@NotNull GithubAuthData auth) throws IOException { - return GithubApiUtil.getMasterToken(auth, "IntelliJ plugin"); + public String convert(@NotNull GithubConnection connection) + throws IOException { + return GithubApiUtil.getMasterToken(connection, "IntelliJ plugin"); } } ); diff --git a/plugins/github/src/org/jetbrains/plugins/github/util/GithubNotifications.java b/plugins/github/src/org/jetbrains/plugins/github/util/GithubNotifications.java index 767c620c93a4..d34441bf1ba3 100644 --- a/plugins/github/src/org/jetbrains/plugins/github/util/GithubNotifications.java +++ b/plugins/github/src/org/jetbrains/plugins/github/util/GithubNotifications.java @@ -18,6 +18,7 @@ package org.jetbrains.plugins.github.util; import com.intellij.notification.NotificationListener; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.vcs.VcsNotifier; import org.jetbrains.annotations.NotNull; @@ -41,6 +42,12 @@ public class GithubNotifications { VcsNotifier.getInstance(project).notifyImportantWarning(title, message); } + public static void showWarning(@NotNull Project project, @NotNull String title, @NotNull Exception e) { + LOG.info(title + "; ", e); + if (e instanceof GithubOperationCanceledException) return; + VcsNotifier.getInstance(project).notifyImportantWarning(title, getErrorTextFromException(e)); + } + public static void showError(@NotNull Project project, @NotNull String title, @NotNull String message) { LOG.info(title + "; " + message); VcsNotifier.getInstance(project).notifyError(title, message); @@ -116,19 +123,28 @@ public class GithubNotifications { Messages.showErrorDialog(project, getErrorTextFromException(e), title); } - public static void showErrorDialog(@NotNull Component component, @NotNull String title, @NotNull String message) { - LOG.info(title + "; " + message); - Messages.showErrorDialog(component, message, title); - } - public static void showErrorDialog(@NotNull Component component, @NotNull String title, @NotNull Exception e) { LOG.info(title, e); if (e instanceof GithubOperationCanceledException) return; Messages.showErrorDialog(component, getErrorTextFromException(e), title); } + public static void showErrorDialog(@NotNull Component component, @NotNull String title, @NotNull String prefix, @NotNull Exception e) { + LOG.info(title, e); + if (e instanceof GithubOperationCanceledException) return; + Messages.showErrorDialog(component, prefix + getErrorTextFromException(e), title); + } + @Messages.YesNoResult public static int showYesNoDialog(@Nullable Project project, @NotNull String title, @NotNull String message) { return Messages.showYesNoDialog(project, message, title, Messages.getQuestionIcon()); } + + @Messages.YesNoResult + public static int showYesNoDialog(@Nullable Project project, + @NotNull String title, + @NotNull String message, + @NotNull DialogWrapper.DoNotAskOption doNotAskOption) { + return Messages.showYesNoDialog(project, message, title, Messages.getQuestionIcon(), doNotAskOption); + } } diff --git a/plugins/github/src/org/jetbrains/plugins/github/util/GithubSettings.java b/plugins/github/src/org/jetbrains/plugins/github/util/GithubSettings.java index 3523e412a95e..7e98d47d8220 100644 --- a/plugins/github/src/org/jetbrains/plugins/github/util/GithubSettings.java +++ b/plugins/github/src/org/jetbrains/plugins/github/util/GithubSettings.java @@ -22,6 +22,7 @@ import com.intellij.ide.passwordSafe.impl.PasswordSafeImpl; import com.intellij.openapi.components.*; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.util.ThreeState; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.github.api.GithubApiUtil; @@ -60,6 +61,7 @@ public class GithubSettings implements PersistentStateComponent T runTask(@NotNull Project project, @NotNull GithubAuthDataHolder authHolder, - @NotNull ProgressIndicator indicator, - @NotNull ThrowableConvertor task) throws IOException { + @NotNull final ProgressIndicator indicator, + @NotNull ThrowableConvertor task) throws IOException { GithubAuthData auth = authHolder.getAuthData(); try { - return task.convert(auth); + final GithubConnection connection = new GithubConnection(auth, true); + ScheduledFuture future = null; + + try { + future = addCancellationListener(indicator, connection); + return task.convert(connection); + } + finally { + connection.close(); + if (future != null) future.cancel(true); + } } catch (GithubTwoFactorAuthenticationException e) { getTwoFactorAuthData(project, authHolder, indicator, auth); @@ -90,11 +106,21 @@ public class GithubUtil { public static void runTask(@NotNull Project project, @NotNull GithubAuthDataHolder authHolder, - @NotNull ProgressIndicator indicator, - @NotNull ThrowableConsumer task) throws IOException { + @NotNull final ProgressIndicator indicator, + @NotNull ThrowableConsumer task) throws IOException { GithubAuthData auth = authHolder.getAuthData(); try { - task.consume(auth); + final GithubConnection connection = new GithubConnection(auth, true); + ScheduledFuture future = null; + + try { + future = addCancellationListener(indicator, connection); + task.consume(connection); + } + finally { + connection.close(); + if (future != null) future.cancel(true); + } } catch (GithubTwoFactorAuthenticationException e) { getTwoFactorAuthData(project, authHolder, indicator, auth); @@ -108,15 +134,26 @@ public class GithubUtil { public static T runTaskWithBasicAuthForHost(@NotNull Project project, @NotNull GithubAuthDataHolder authHolder, - @NotNull ProgressIndicator indicator, + @NotNull final ProgressIndicator indicator, @NotNull String host, - @NotNull ThrowableConvertor task) throws IOException { + @NotNull ThrowableConvertor task) throws IOException { GithubAuthData auth = authHolder.getAuthData(); try { if (auth.getAuthType() != GithubAuthData.AuthType.BASIC) { throw new GithubAuthenticationException("Expected basic authentication"); } - return task.convert(auth); + + final GithubConnection connection = new GithubConnection(auth, true); + ScheduledFuture future = null; + + try { + future = addCancellationListener(indicator, connection); + return task.convert(connection); + } + finally { + connection.close(); + if (future != null) future.cancel(true); + } } catch (GithubTwoFactorAuthenticationException e) { getTwoFactorAuthData(project, authHolder, indicator, auth); @@ -131,10 +168,20 @@ public class GithubUtil { @NotNull private static GithubUserDetailed testConnection(@NotNull Project project, @NotNull GithubAuthDataHolder authHolder, - @NotNull ProgressIndicator indicator) throws IOException { + @NotNull final ProgressIndicator indicator) throws IOException { GithubAuthData auth = authHolder.getAuthData(); try { - return GithubApiUtil.getCurrentUserDetailed(auth); + final GithubConnection connection = new GithubConnection(auth, true); + ScheduledFuture future = null; + + try { + future = addCancellationListener(indicator, connection); + return GithubApiUtil.getCurrentUserDetailed(connection); + } + finally { + connection.close(); + if (future != null) future.cancel(true); + } } catch (GithubTwoFactorAuthenticationException e) { getTwoFactorAuthData(project, authHolder, indicator, auth); @@ -142,6 +189,33 @@ public class GithubUtil { } } + @NotNull + private static ScheduledFuture addCancellationListener(@NotNull Runnable run) { + return JobScheduler.getScheduler().scheduleWithFixedDelay(run, 1000, 300, TimeUnit.MILLISECONDS); + } + + @NotNull + private static ScheduledFuture addCancellationListener(@NotNull final ProgressIndicator indicator, + @NotNull final GithubConnection connection) { + return addCancellationListener(new Runnable() { + @Override + public void run() { + if (indicator.isCanceled()) connection.abort(); + } + }); + } + + @NotNull + private static ScheduledFuture addCancellationListener(@NotNull final ProgressIndicator indicator, + @NotNull final Thread thread) { + return addCancellationListener(new Runnable() { + @Override + public void run() { + if (indicator.isCanceled()) thread.interrupt(); + } + }); + } + public static void getValidAuthData(@NotNull final Project project, @NotNull final GithubAuthDataHolder authHolder, @NotNull final ProgressIndicator indicator, @@ -220,7 +294,7 @@ public class GithubUtil { throw new GithubOperationCanceledException("Two factor authentication can be used only with Login/Password"); } - GithubApiUtil.askForTwoFactorCodeSMS(oldAuth); + GithubApiUtil.askForTwoFactorCodeSMS(new GithubConnection(oldAuth, false)); final Ref codeRef = new Ref(); ApplicationManager.getApplication().invokeAndWait(new Runnable() { @@ -301,13 +375,7 @@ public class GithubUtil { try { dataRef.set(task.convert(indicator)); } - catch (IOException e) { - exceptionRef.set(e); - } - catch (Error e) { - exceptionRef.set(e); - } - catch (RuntimeException e) { + catch (Throwable e) { exceptionRef.set(e); } } @@ -325,17 +393,21 @@ public class GithubUtil { public static T computeValueInModal(@NotNull Project project, @NotNull String caption, @NotNull final Convertor task) { + return computeValueInModal(project, caption, true, task); + } + + public static T computeValueInModal(@NotNull Project project, + @NotNull String caption, + boolean canBeCancelled, + @NotNull final Convertor task) { final Ref dataRef = new Ref(); final Ref exceptionRef = new Ref(); - ProgressManager.getInstance().run(new Task.Modal(project, caption, true) { + ProgressManager.getInstance().run(new Task.Modal(project, caption, canBeCancelled) { public void run(@NotNull ProgressIndicator indicator) { try { dataRef.set(task.convert(indicator)); } - catch (Error e) { - exceptionRef.set(e); - } - catch (RuntimeException e) { + catch (Throwable e) { exceptionRef.set(e); } } @@ -352,16 +424,20 @@ public class GithubUtil { public static void computeValueInModal(@NotNull Project project, @NotNull String caption, @NotNull final Consumer task) { + computeValueInModal(project, caption, true, task); + } + + public static void computeValueInModal(@NotNull Project project, + @NotNull String caption, + boolean canBeCancelled, + @NotNull final Consumer task) { final Ref exceptionRef = new Ref(); - ProgressManager.getInstance().run(new Task.Modal(project, caption, true) { + ProgressManager.getInstance().run(new Task.Modal(project, caption, canBeCancelled) { public void run(@NotNull ProgressIndicator indicator) { try { task.consume(indicator); } - catch (Error e) { - exceptionRef.set(e); - } - catch (RuntimeException e) { + catch (Throwable e) { exceptionRef.set(e); } } @@ -374,6 +450,49 @@ public class GithubUtil { } } + public static T runInterruptable(@NotNull final ProgressIndicator indicator, + @NotNull ThrowableComputable task) throws IOException { + ScheduledFuture future = null; + try { + final Thread thread = Thread.currentThread(); + future = addCancellationListener(indicator, thread); + + return task.compute(); + } + finally { + if (future != null) future.cancel(true); + Thread.interrupted(); + } + } + + public static T runInterruptable(@NotNull final ProgressIndicator indicator, @NotNull Computable task) { + ScheduledFuture future = null; + try { + final Thread thread = Thread.currentThread(); + future = addCancellationListener(indicator, thread); + + return task.compute(); + } + finally { + if (future != null) future.cancel(true); + Thread.interrupted(); + } + } + + public static void runInterruptable(@NotNull final ProgressIndicator indicator, @NotNull Runnable task) { + ScheduledFuture future = null; + try { + final Thread thread = Thread.currentThread(); + future = addCancellationListener(indicator, thread); + + task.run(); + } + finally { + if (future != null) future.cancel(true); + Thread.interrupted(); + } + } + /* * Git utils */ diff --git a/plugins/github/test/org/jetbrains/plugins/github/GithubCreateGistTestBase.java b/plugins/github/test/org/jetbrains/plugins/github/GithubCreateGistTestBase.java index b54bd8c62b4e..4dabe58bebc7 100644 --- a/plugins/github/test/org/jetbrains/plugins/github/GithubCreateGistTestBase.java +++ b/plugins/github/test/org/jetbrains/plugins/github/GithubCreateGistTestBase.java @@ -22,6 +22,7 @@ import com.intellij.util.text.DateFormatUtil; import git4idea.test.TestDialogHandler; import org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.github.api.GithubApiUtil; +import org.jetbrains.plugins.github.api.GithubConnection; import org.jetbrains.plugins.github.api.GithubGist; import org.jetbrains.plugins.github.test.GithubTest; import org.jetbrains.plugins.github.ui.GithubLoginDialog; @@ -59,7 +60,7 @@ public abstract class GithubCreateGistTestBase extends GithubTest { protected void deleteGist() throws IOException { if (GIST_ID != null) { - GithubApiUtil.deleteGist(myGitHubSettings.getAuthData(), GIST_ID); + GithubApiUtil.deleteGist(new GithubConnection(myGitHubSettings.getAuthData()), GIST_ID); GIST = null; GIST_ID = null; } @@ -82,7 +83,7 @@ public abstract class GithubCreateGistTestBase extends GithubTest { if (GIST == null) { try { - GIST = GithubApiUtil.getGist(myGitHubSettings.getAuthData(), GIST_ID); + GIST = GithubApiUtil.getGist(new GithubConnection(myGitHubSettings.getAuthData()), GIST_ID); } catch (IOException e) { System.err.println(e.getMessage()); diff --git a/plugins/github/test/org/jetbrains/plugins/github/GithubCreatePullRequestTestBase.java b/plugins/github/test/org/jetbrains/plugins/github/GithubCreatePullRequestTestBase.java index 643c3d2db5f0..b6a2058cd24f 100644 --- a/plugins/github/test/org/jetbrains/plugins/github/GithubCreatePullRequestTestBase.java +++ b/plugins/github/test/org/jetbrains/plugins/github/GithubCreatePullRequestTestBase.java @@ -76,8 +76,8 @@ public abstract class GithubCreatePullRequestTestBase extends GithubTest { @Override public int handleDialog(GithubCreatePullRequestDialog dialog) { dialog.testSetRequestTitle(BRANCH_NAME); + dialog.testSetFork(new GithubFullPath(user, PROJECT_NAME)); dialog.testSetBranch(branch); - dialog.testSetTarget(new GithubFullPath(user, PROJECT_NAME)); dialog.testCreatePullRequest(); return DialogWrapper.OK_EXIT_CODE; } diff --git a/plugins/github/test/org/jetbrains/plugins/github/GithubIssuesTest.java b/plugins/github/test/org/jetbrains/plugins/github/GithubIssuesTest.java index 1921b9ecc055..1cd499f53c89 100644 --- a/plugins/github/test/org/jetbrains/plugins/github/GithubIssuesTest.java +++ b/plugins/github/test/org/jetbrains/plugins/github/GithubIssuesTest.java @@ -19,6 +19,7 @@ import com.intellij.openapi.util.Comparing; import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.plugins.github.api.GithubApiUtil; +import org.jetbrains.plugins.github.api.GithubConnection; import org.jetbrains.plugins.github.api.GithubIssue; import org.jetbrains.plugins.github.test.GithubTest; @@ -32,7 +33,7 @@ public class GithubIssuesTest extends GithubTest { private static final String REPO_NAME = "IssuesTest"; public void testAssigneeIssues1() throws Exception { - List result = GithubApiUtil.getIssuesAssigned(myAuth, myLogin2, REPO_NAME, myLogin1, 100, false); + List result = GithubApiUtil.getIssuesAssigned(new GithubConnection(myAuth), myLogin2, REPO_NAME, myLogin1, 100, false); List issues = ContainerUtil.map(result, new Function() { @Override public Long fun(GithubIssue githubIssue) { @@ -46,7 +47,7 @@ public class GithubIssuesTest extends GithubTest { } public void testAssigneeIssues2() throws Exception { - List result = GithubApiUtil.getIssuesAssigned(myAuth, myLogin2, REPO_NAME, myLogin2, 100, false); + List result = GithubApiUtil.getIssuesAssigned(new GithubConnection(myAuth), myLogin2, REPO_NAME, myLogin2, 100, false); List issues = ContainerUtil.map(result, new Function() { @Override public Long fun(GithubIssue githubIssue) { @@ -60,7 +61,7 @@ public class GithubIssuesTest extends GithubTest { } public void testAssigneeIssues3() throws Exception { - List result = GithubApiUtil.getIssuesAssigned(myAuth, myLogin2, REPO_NAME, "", 100, false); + List result = GithubApiUtil.getIssuesAssigned(new GithubConnection(myAuth), myLogin2, REPO_NAME, "", 100, false); List issues = ContainerUtil.map(result, new Function() { @Override public Long fun(GithubIssue githubIssue) { @@ -74,7 +75,7 @@ public class GithubIssuesTest extends GithubTest { } public void testAssigneeIssues4() throws Exception { - List result = GithubApiUtil.getIssuesAssigned(myAuth, myLogin2, REPO_NAME, myLogin1, 100, true); + List result = GithubApiUtil.getIssuesAssigned(new GithubConnection(myAuth), myLogin2, REPO_NAME, myLogin1, 100, true); List issues = ContainerUtil.map(result, new Function() { @Override public Long fun(GithubIssue githubIssue) { @@ -88,7 +89,7 @@ public class GithubIssuesTest extends GithubTest { } public void testAssigneeIssues5() throws Exception { - List result = GithubApiUtil.getIssuesAssigned(myAuth, myLogin2, REPO_NAME, myLogin2, 100, true); + List result = GithubApiUtil.getIssuesAssigned(new GithubConnection(myAuth), myLogin2, REPO_NAME, myLogin2, 100, true); List issues = ContainerUtil.map(result, new Function() { @Override public Long fun(GithubIssue githubIssue) { @@ -102,7 +103,7 @@ public class GithubIssuesTest extends GithubTest { } public void testAssigneeIssues6() throws Exception { - List result = GithubApiUtil.getIssuesAssigned(myAuth, myLogin2, REPO_NAME, "", 100, true); + List result = GithubApiUtil.getIssuesAssigned(new GithubConnection(myAuth), myLogin2, REPO_NAME, "", 100, true); List issues = ContainerUtil.map(result, new Function() { @Override public Long fun(GithubIssue githubIssue) { @@ -116,7 +117,7 @@ public class GithubIssuesTest extends GithubTest { } public void testQueriedIssues1() throws Exception { - List result = GithubApiUtil.getIssuesQueried(myAuth, myLogin2, REPO_NAME, "abracadabra", true); + List result = GithubApiUtil.getIssuesQueried(new GithubConnection(myAuth), myLogin2, REPO_NAME, "abracadabra", true); List issues = ContainerUtil.map(result, new Function() { @Override public Long fun(GithubIssue githubIssue) { @@ -130,7 +131,7 @@ public class GithubIssuesTest extends GithubTest { } public void testQueriedIssues2() throws Exception { - List result = GithubApiUtil.getIssuesQueried(myAuth, myLogin2, REPO_NAME, "commentary", true); + List result = GithubApiUtil.getIssuesQueried(new GithubConnection(myAuth), myLogin2, REPO_NAME, "commentary", true); List issues = ContainerUtil.map(result, new Function() { @Override public Long fun(GithubIssue githubIssue) { @@ -144,7 +145,7 @@ public class GithubIssuesTest extends GithubTest { } public void testQueriedIssues3() throws Exception { - List result = GithubApiUtil.getIssuesQueried(myAuth, myLogin2, REPO_NAME, "abracadabra", false); + List result = GithubApiUtil.getIssuesQueried(new GithubConnection(myAuth), myLogin2, REPO_NAME, "abracadabra", false); List issues = ContainerUtil.map(result, new Function() { @Override public Long fun(GithubIssue githubIssue) { diff --git a/plugins/github/test/org/jetbrains/plugins/github/GithubRequestPagingTest.java b/plugins/github/test/org/jetbrains/plugins/github/GithubRequestPagingTest.java index 4fec0d1c7a5c..22b06a58ff02 100644 --- a/plugins/github/test/org/jetbrains/plugins/github/GithubRequestPagingTest.java +++ b/plugins/github/test/org/jetbrains/plugins/github/GithubRequestPagingTest.java @@ -16,8 +16,10 @@ package org.jetbrains.plugins.github; import org.jetbrains.plugins.github.api.GithubApiUtil; +import org.jetbrains.plugins.github.api.GithubConnection; import org.jetbrains.plugins.github.api.GithubRepo; import org.jetbrains.plugins.github.test.GithubTest; +import org.jetbrains.plugins.github.util.GithubAuthData; import java.util.ArrayList; import java.util.List; @@ -36,17 +38,23 @@ public class GithubRequestPagingTest extends GithubTest { public void testAvailableRepos() throws Throwable { - List availableRepos = GithubApiUtil.getUserRepos(myGitHubSettings.getAuthData(), myLogin2); - List realData = new ArrayList(); - for (GithubRepo info : availableRepos) { - realData.add(info.getName()); - } + GithubConnection connection = new GithubConnection(myGitHubSettings.getAuthData(), true); + try { + List availableRepos = GithubApiUtil.getUserRepos(connection, myLogin2); + List realData = new ArrayList(); + for (GithubRepo info : availableRepos) { + realData.add(info.getName()); + } - List expectedData = new ArrayList(); - for (int i = 1; i <= 251; i++) { - expectedData.add(String.valueOf(i)); - } + List expectedData = new ArrayList(); + for (int i = 1; i <= 251; i++) { + expectedData.add(String.valueOf(i)); + } - assertContainsElements(realData, expectedData); + assertContainsElements(realData, expectedData); + } + finally { + connection.close(); + } } } diff --git a/plugins/github/test/org/jetbrains/plugins/github/GithubShareProjectTest.java b/plugins/github/test/org/jetbrains/plugins/github/GithubShareProjectTest.java index c4063629cbbe..4e51fccc0729 100644 --- a/plugins/github/test/org/jetbrains/plugins/github/GithubShareProjectTest.java +++ b/plugins/github/test/org/jetbrains/plugins/github/GithubShareProjectTest.java @@ -4,6 +4,7 @@ import com.intellij.notification.NotificationType; import com.intellij.openapi.components.ServiceManager; import git4idea.commands.Git; import org.jetbrains.plugins.github.api.GithubApiUtil; +import org.jetbrains.plugins.github.api.GithubConnection; import org.jetbrains.plugins.github.api.GithubRepoDetailed; import org.jetbrains.plugins.github.util.GithubAuthData; @@ -100,7 +101,7 @@ public class GithubShareProjectTest extends GithubShareProjectTestBase { protected void checkGithubExists() throws IOException { GithubAuthData auth = myGitHubSettings.getAuthData(); - GithubRepoDetailed githubInfo = GithubApiUtil.getDetailedRepoInfo(auth, myLogin1, PROJECT_NAME); + GithubRepoDetailed githubInfo = GithubApiUtil.getDetailedRepoInfo(new GithubConnection(auth), myLogin1, PROJECT_NAME); assertNotNull("GitHub repository does not exist", githubInfo); } } diff --git a/plugins/github/test/org/jetbrains/plugins/github/GithubShareProjectTestBase.java b/plugins/github/test/org/jetbrains/plugins/github/GithubShareProjectTestBase.java index abfb5ece3819..ea07ca1eaf1d 100644 --- a/plugins/github/test/org/jetbrains/plugins/github/GithubShareProjectTestBase.java +++ b/plugins/github/test/org/jetbrains/plugins/github/GithubShareProjectTestBase.java @@ -22,6 +22,7 @@ import git4idea.GitUtil; import git4idea.repo.GitRepository; import git4idea.test.TestDialogHandler; import org.jetbrains.plugins.github.api.GithubApiUtil; +import org.jetbrains.plugins.github.api.GithubConnection; import org.jetbrains.plugins.github.test.GithubTest; import org.jetbrains.plugins.github.ui.GithubShareDialog; @@ -48,7 +49,7 @@ public abstract class GithubShareProjectTestBase extends GithubTest { } protected void deleteGithubRepo() throws IOException { - GithubApiUtil.deleteGithubRepository(myGitHubSettings.getAuthData(), myLogin1, PROJECT_NAME); + GithubApiUtil.deleteGithubRepository(new GithubConnection(myGitHubSettings.getAuthData()), myLogin1, PROJECT_NAME); } protected void registerDefaultShareDialogHandler() { diff --git a/plugins/gradle/resources/i18n/GradleBundle.properties b/plugins/gradle/resources/i18n/GradleBundle.properties index 88dfd899deed..0f827062fbce 100644 --- a/plugins/gradle/resources/i18n/GradleBundle.properties +++ b/plugins/gradle/resources/i18n/GradleBundle.properties @@ -12,7 +12,7 @@ gradle.settings.text.service.dir.path=Service directory path: gradle.settings.text.vm.options=Gradle VM options: gradle.settings.text.offline_work=Offline work gradle.settings.title.service.dir.path=Select gradle service directory to use -gradle.generic.text.error.jar.not.found=Gradle jars location is unknown +gradle.generic.text.error.jar.not.found=Gradle JARs location is unknown gradle.home.setting.type.explicit.incorrect=Gradle location is incorrect.\nLocation:{0} gradle.home.setting.type.explicit.empty=Gradle location is not specified diff --git a/plugins/gradle/src/META-INF/plugin.xml b/plugins/gradle/src/META-INF/plugin.xml index f00346625e81..4e4fea02a0c7 100644 --- a/plugins/gradle/src/META-INF/plugin.xml +++ b/plugins/gradle/src/META-INF/plugin.xml @@ -88,7 +88,7 @@ - diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/execution/GradleOrderEnumeratorHandler.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/execution/GradleOrderEnumeratorHandler.java index 113e49a61a25..28de6f8861d7 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/execution/GradleOrderEnumeratorHandler.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/execution/GradleOrderEnumeratorHandler.java @@ -107,6 +107,8 @@ public class GradleOrderEnumeratorHandler extends OrderEnumerationHandler { final ExternalSourceDirectorySet directorySet = externalSourceSet.getSources().get(sourceType); if (directorySet == null) return; + if (directorySet.isCompilerOutputPathInherited()) return; + result.add(VfsUtilCore.pathToUrl(directorySet.getOutputDir().getAbsolutePath())); } } diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/GradleInstallationManager.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/GradleInstallationManager.java index 07521be885d8..b363eb8b3395 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/GradleInstallationManager.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/GradleInstallationManager.java @@ -360,7 +360,7 @@ public class GradleInstallationManager { filesInfo.setLength(filesInfo.length() - 1); } GradleLog.LOG.info(String.format( - "Gradle sdk check fails. Reason: no one of the given files matches gradle jar pattern (%s). Files: %s", + "Gradle sdk check fails. Reason: no one of the given files matches gradle JAR pattern (%s). Files: %s", GRADLE_JAR_FILE_PATTERN.toString(), filesInfo )); } diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/BaseGradleProjectResolverExtension.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/BaseGradleProjectResolverExtension.java index ad7ade6a3c58..ac858e56f634 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/BaseGradleProjectResolverExtension.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/project/BaseGradleProjectResolverExtension.java @@ -230,8 +230,8 @@ public class BaseGradleProjectResolverExtension implements GradleProjectResolver File sourceCompileOutputPath = null; File testCompileOutputPath = null; - File resourceCompileOutputPath; - File testResourceCompileOutputPath; + File resourceCompileOutputPath = null; + File testResourceCompileOutputPath = null; boolean inheritOutputDirs = false; ModuleData moduleData = ideModule.getData(); @@ -244,21 +244,22 @@ public class BaseGradleProjectResolverExtension implements GradleProjectResolver ExternalProject externalProject = resolverCtx.getExtraProject(gradleModule, ExternalProject.class); if (externalProject != null) { externalProject = new DefaultExternalProject(externalProject); - } - if (!inheritOutputDirs && (sourceCompileOutputPath == null || testCompileOutputPath == null)) { - sourceCompileOutputPath = getCompileOutputPath(externalProject, MAIN_SOURCE_SET, ExternalSystemSourceType.SOURCE); - resourceCompileOutputPath = getCompileOutputPath(externalProject, MAIN_SOURCE_SET, ExternalSystemSourceType.RESOURCE); - testCompileOutputPath = getCompileOutputPath(externalProject, TEST_SOURCE_SET, ExternalSystemSourceType.TEST); - testResourceCompileOutputPath = getCompileOutputPath(externalProject, TEST_SOURCE_SET, ExternalSystemSourceType.TEST_RESOURCE); - } - else { - resourceCompileOutputPath = sourceCompileOutputPath; - testResourceCompileOutputPath = testCompileOutputPath; - - if (externalProject != null) { + if (!inheritOutputDirs && (sourceCompileOutputPath == null || testCompileOutputPath == null)) { + sourceCompileOutputPath = getCompileOutputPath(externalProject, MAIN_SOURCE_SET, ExternalSystemSourceType.SOURCE); + resourceCompileOutputPath = getCompileOutputPath(externalProject, MAIN_SOURCE_SET, ExternalSystemSourceType.RESOURCE); + testCompileOutputPath = getCompileOutputPath(externalProject, TEST_SOURCE_SET, ExternalSystemSourceType.TEST); + testResourceCompileOutputPath = getCompileOutputPath(externalProject, TEST_SOURCE_SET, ExternalSystemSourceType.TEST_RESOURCE); + } + else if (!inheritOutputDirs) { + resourceCompileOutputPath = sourceCompileOutputPath; + testResourceCompileOutputPath = testCompileOutputPath; final ExternalSourceSet mainSourceSet = externalProject.getSourceSets().get(MAIN_SOURCE_SET); if (mainSourceSet != null) { + final ExternalSourceDirectorySet sourceDirectories = mainSourceSet.getSources().get(ExternalSystemSourceType.SOURCE); + if (sourceDirectories instanceof DefaultExternalSourceDirectorySet) { + ((DefaultExternalSourceDirectorySet)sourceDirectories).setOutputDir(sourceCompileOutputPath); + } final ExternalSourceDirectorySet resourceDirectories = mainSourceSet.getSources().get(ExternalSystemSourceType.RESOURCE); if (resourceDirectories instanceof DefaultExternalSourceDirectorySet) { ((DefaultExternalSourceDirectorySet)resourceDirectories).setOutputDir(sourceCompileOutputPath); @@ -266,6 +267,10 @@ public class BaseGradleProjectResolverExtension implements GradleProjectResolver } final ExternalSourceSet testSourceSet = externalProject.getSourceSets().get(TEST_SOURCE_SET); if (testSourceSet != null) { + final ExternalSourceDirectorySet testDirectories = testSourceSet.getSources().get(ExternalSystemSourceType.TEST); + if (testDirectories instanceof DefaultExternalSourceDirectorySet) { + ((DefaultExternalSourceDirectorySet)testDirectories).setOutputDir(testCompileOutputPath); + } final ExternalSourceDirectorySet testResourceDirectories = testSourceSet.getSources().get(ExternalSystemSourceType.TEST_RESOURCE); if (testResourceDirectories instanceof DefaultExternalSourceDirectorySet) { ((DefaultExternalSourceDirectorySet)testResourceDirectories).setOutputDir(testCompileOutputPath); @@ -277,6 +282,9 @@ public class BaseGradleProjectResolverExtension implements GradleProjectResolver projectDataNode.createOrReplaceChild(ExternalProjectDataService.KEY, externalProject); } } + else { + LOG.warn(String.format("Unable to get ExternalProject model for '%s'", gradleModule.getName())); + } if (sourceCompileOutputPath != null) { moduleData.setCompileOutputPath(ExternalSystemSourceType.SOURCE, sourceCompileOutputPath.getAbsolutePath()); diff --git a/plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleFoldersImportingTest.java b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleFoldersImportingTest.java index 8c57ebb57d29..5fd64533dcc9 100644 --- a/plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleFoldersImportingTest.java +++ b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleFoldersImportingTest.java @@ -15,6 +15,7 @@ */ package org.jetbrains.plugins.gradle.importing; +import org.jetbrains.annotations.NotNull; import org.junit.Test; /** @@ -34,14 +35,39 @@ public class GradleFoldersImportingTest extends GradleImportingTestCase { assertModules("project"); assertContentRoots("project", getProjectPath()); - assertSources("project", "src/main/java"); - assertResources("project", "src/main/resources"); - assertTestSources("project", "src/test/java"); - assertTestResources("project", "src/test/resources"); - assertExcludes("project", ".gradle", "build"); + assertDefaultGradleJavaProjectFolders("project"); assertModuleOutput("project", getProjectPath() + "/build/classes/main", getProjectPath() + "/build/classes/test"); } + + @Test + public void testProjectWithInheritedOutputDirs() throws Exception { + + importProject( + "apply plugin: 'java'\n" + + "apply plugin: 'idea'\n" + + "idea {\n" + + " module {\n" + + " inheritOutputDirs = true\n" + + " }\n" + + "}" + ); + + assertModules("project"); + assertContentRoots("project", getProjectPath()); + + assertDefaultGradleJavaProjectFolders("project"); + + assertModuleInheritedOutput("project"); + } + + protected void assertDefaultGradleJavaProjectFolders(@NotNull String moduleName) { + assertSources(moduleName, "src/main/java"); + assertResources(moduleName, "src/main/resources"); + assertTestSources(moduleName, "src/test/java"); + assertTestResources(moduleName, "src/test/resources"); + assertExcludes(moduleName, ".gradle", "build"); + } } diff --git a/plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleImportingTestCase.java b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleImportingTestCase.java index f59ccee38658..2cf4c7125918 100644 --- a/plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleImportingTestCase.java +++ b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/importing/GradleImportingTestCase.java @@ -160,7 +160,7 @@ public abstract class GradleImportingTestCase extends ExternalSystemImportingTes throw new RuntimeException(e); } if (!location.getScheme().equals("file")) { - throw new RuntimeException(String.format("Cannot determine classpath for wrapper Jar from codebase '%s'.", location)); + throw new RuntimeException(String.format("Cannot determine classpath for wrapper JAR from codebase '%s'.", location)); } return new File(location.getPath()); } diff --git a/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/builder/ExternalProjectBuilderImpl.groovy b/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/builder/ExternalProjectBuilderImpl.groovy index 3bb6b471b755..1322c13ef54e 100644 --- a/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/builder/ExternalProjectBuilderImpl.groovy +++ b/plugins/gradle/tooling-extension-impl/src/org/jetbrains/plugins/gradle/tooling/builder/ExternalProjectBuilderImpl.groovy @@ -26,6 +26,7 @@ import org.gradle.api.file.FileCopyDetails import org.gradle.api.tasks.SourceSet import org.gradle.api.tasks.SourceSetContainer import org.gradle.api.tasks.util.PatternFilterable +import org.gradle.plugins.ide.idea.IdeaPlugin import org.jetbrains.annotations.NotNull import org.jetbrains.annotations.Nullable import org.jetbrains.plugins.gradle.tooling.ErrorMessageBuilder @@ -109,6 +110,9 @@ class ExternalProjectBuilderImpl implements ModelBuilderService { } static Map getSourceSets(Project project) { + final IdeaPlugin ideaPlugin = project.getPlugins().getPlugin(IdeaPlugin.class); + boolean inheritOutputDirs = ideaPlugin?.model?.module?.inheritOutputDirs ?: false + def result = [:] as Map if (!project.hasProperty("sourceSets") || !(project.sourceSets instanceof SourceSetContainer)) { return result @@ -128,11 +132,13 @@ class ExternalProjectBuilderImpl implements ModelBuilderService { resourcesDirectorySet.name = sourceSet.resources.name resourcesDirectorySet.srcDirs = sourceSet.resources.srcDirs resourcesDirectorySet.outputDir = sourceSet.output.resourcesDir + resourcesDirectorySet.inheritedCompilerOutput = inheritOutputDirs ExternalSourceDirectorySet javaDirectorySet = new DefaultExternalSourceDirectorySet() javaDirectorySet.name = sourceSet.allJava.name javaDirectorySet.srcDirs = sourceSet.allJava.srcDirs javaDirectorySet.outputDir = sourceSet.output.classesDir + javaDirectorySet.inheritedCompilerOutput = inheritOutputDirs // javaDirectorySet.excludes = javaExcludes + sourceSet.java.excludes; // javaDirectorySet.includes = javaIncludes + sourceSet.java.includes; diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/GroovyBundle.properties b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/GroovyBundle.properties index 4e3c0eb17c38..0b97a8fbaa35 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/GroovyBundle.properties +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/GroovyBundle.properties @@ -31,7 +31,7 @@ newscript.menu.action.description=Creates new Groovy Script cannot.compile.groovy.files.no.facet=Cannot find Groovy home for module ''{0}''.\nYou should either:\n\ - invoke 'Add Framework support' action for this module,\n\ or:\n\ -- add Groovy jars to module classpath manually. +- add Groovy JARs to module classpath manually. cannot.compile.groovy.files.no.sdk=Cannot Compile Groovy Files.\nPlease Set up SDK for module ''{0}''. cannot.compile.groovy.files.no.sdk.mult=Cannot Compile Groovy Files.\nPlease Set up SDK for modules ''{0}''. cannot.compile=Cannot Compile diff --git a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/dsl/GroovyClassDescriptor.java b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/dsl/GroovyClassDescriptor.java index 2281ad12c4dd..33bd366f6261 100644 --- a/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/dsl/GroovyClassDescriptor.java +++ b/plugins/groovy/groovy-psi/src/org/jetbrains/plugins/groovy/dsl/GroovyClassDescriptor.java @@ -38,7 +38,7 @@ public class GroovyClassDescriptor { final AtomicInteger integer = GroovyCategorySupport.getCategoryNameUsage("aaa"); } catch (NoSuchMethodError e) { - throw new RuntimeException("Incompatible Groovy jar in classpath: " + GroovyCategorySupport.class.getResource("/") + ", please remove it"); + throw new RuntimeException("Incompatible Groovy JAR in classpath: " + GroovyCategorySupport.class.getResource("/") + ", please remove it"); } } diff --git a/plugins/groovy/src/META-INF/plugin.xml b/plugins/groovy/src/META-INF/plugin.xml index 8e3d7103e250..db15a38a2a2c 100644 --- a/plugins/groovy/src/META-INF/plugin.xml +++ b/plugins/groovy/src/META-INF/plugin.xml @@ -273,7 +273,7 @@ - diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/config/GroovyFacetUtil.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/config/GroovyFacetUtil.java index e69eb97efdfe..952916393b02 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/config/GroovyFacetUtil.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/config/GroovyFacetUtil.java @@ -28,6 +28,7 @@ import com.intellij.openapi.roots.ModifiableRootModel; import com.intellij.openapi.roots.ModuleRootManager; import com.intellij.openapi.roots.libraries.Library; import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.util.io.FileUtil; import com.intellij.util.PathUtil; import icons.JetgroovyIcons; import org.jetbrains.plugins.groovy.GroovyBundle; @@ -76,8 +77,13 @@ public class GroovyFacetUtil { } public static File getBundledGroovyJar() { - String root = new File(PathUtil.getJarPathForClass(GroovyFacetUtil.class)).isDirectory() ? - PluginPathManager.getPluginHomePath("groovy") + "/../../lib/" : PathManager.getHomePath() + "/lib/"; + String root; + if (new File(PathUtil.getJarPathForClass(GroovyFacetUtil.class)).isDirectory()) { + root = FileUtil.toCanonicalPath(PluginPathManager.getPluginHomePath("groovy") + "/../../lib/"); + } + else { + root = PathManager.getHomePath() + "/lib/"; + } final File[] groovyJars = LibrariesUtil.getFilesInDirectoryByPattern(root, GroovyConfigUtils.GROOVY_ALL_JAR_PATTERN); assert groovyJars.length == 1; return groovyJars[0]; diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/ivy/AbstractAttachSourceProvider.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/ivy/AbstractAttachSourceProvider.java index 53ddae2cc17d..3f2139c174a8 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/ivy/AbstractAttachSourceProvider.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/ivy/AbstractAttachSourceProvider.java @@ -152,7 +152,7 @@ public abstract class AbstractAttachSourceProvider implements AttachSourcesProvi final ByteArrayOutputStream out; try { - LOG.info("Downloading sources jar: " + myUrl); + LOG.info("Downloading sources JAR: " + myUrl); indicator.checkCanceled(); diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/GroovyCompletionUtil.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/GroovyCompletionUtil.java index d93063a53ca4..0c32878b5853 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/GroovyCompletionUtil.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/GroovyCompletionUtil.java @@ -31,6 +31,7 @@ import com.intellij.openapi.editor.highlighter.HighlighterIterator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Condition; +import com.intellij.openapi.util.Conditions; import com.intellij.openapi.util.Iconable; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; @@ -311,7 +312,7 @@ public class GroovyCompletionUtil { if (element instanceof PsiClass) { return JavaClassNameCompletionContributor - .createClassLookupItems((PsiClass)element, afterNew, new GroovyClassNameInsertHandler(), Condition.TRUE); + .createClassLookupItems((PsiClass)element, afterNew, new GroovyClassNameInsertHandler(), Conditions.alwaysTrue()); } LookupElementBuilder builder = LookupElementBuilder.create(element instanceof PsiPackage ? element : candidate, name); diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcActionBase.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcActionBase.java index 1745c67bde54..58449bcc082a 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcActionBase.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/mvc/MvcActionBase.java @@ -49,7 +49,8 @@ public abstract class MvcActionBase extends DumbAwareAction { @Nullable public static Pair guessFramework(AnActionEvent event) { - final Module module = event.getData(event.getPlace().equals(ActionPlaces.MAIN_MENU) ? LangDataKeys.MODULE : LangDataKeys.MODULE_CONTEXT); + final Module module = event.getData( + ActionPlaces.isMainMenuOrActionSearch(event.getPlace()) ? LangDataKeys.MODULE : LangDataKeys.MODULE_CONTEXT); if (module != null) { MvcFramework commonPluginModuleFramework = MvcFramework.findCommonPluginModuleFramework(module); diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/compiler/GroovyDebuggerTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/compiler/GroovyDebuggerTest.groovy index 022f775dcf7f..aa7885cf22fe 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/compiler/GroovyDebuggerTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/compiler/GroovyDebuggerTest.groovy @@ -107,7 +107,7 @@ class GroovyDebuggerTest extends GroovyCompilerTestCase { cl.call() } finally { - def handler = debugProcess.executionResult.processHandler + def handler = debugProcess.processHandler resume() if (!handler.waitFor(ourTimeout)) { if (handler instanceof OSProcessHandler) { @@ -428,12 +428,12 @@ public static void main(String[] args) { int i = 0 def suspendManager = debugProcess.suspendManager - while (i++ < 1000 && !suspendManager.pausedContext && !debugProcess.executionResult.processHandler.processTerminated) { + while (i++ < 1000 && !suspendManager.pausedContext && !debugProcess.processHandler.processTerminated) { Thread.sleep(10) } def context = suspendManager.pausedContext - assert context : "too long process, terminated=$debugProcess.executionResult.processHandler.processTerminated" + assert context : "too long process, terminated=$debugProcess.processHandler.processTerminated" return context } diff --git a/plugins/hg4idea/hg4idea.iml b/plugins/hg4idea/hg4idea.iml index 93b5a5109bde..d765458336ea 100644 --- a/plugins/hg4idea/hg4idea.iml +++ b/plugins/hg4idea/hg4idea.iml @@ -20,6 +20,7 @@ + diff --git a/plugins/hg4idea/src/META-INF/plugin.xml b/plugins/hg4idea/src/META-INF/plugin.xml index 26b69326a703..dc32359a4aec 100644 --- a/plugins/hg4idea/src/META-INF/plugin.xml +++ b/plugins/hg4idea/src/META-INF/plugin.xml @@ -75,7 +75,7 @@ - + diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/HgProjectSettings.java b/plugins/hg4idea/src/org/zmlx/hg4idea/HgProjectSettings.java index 936587d19755..8abac1bb0b1e 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/HgProjectSettings.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/HgProjectSettings.java @@ -12,6 +12,8 @@ // limitations under the License. package org.zmlx.hg4idea; +import com.intellij.dvcs.branch.DvcsBranchSync; +import com.intellij.dvcs.branch.DvcsSyncBranchSettings; import com.intellij.openapi.components.PersistentStateComponent; import com.intellij.openapi.components.State; import com.intellij.openapi.components.Storage; @@ -24,7 +26,7 @@ import org.jetbrains.annotations.NotNull; name = "hg4idea.settings", storages = @Storage(file = StoragePathMacros.WORKSPACE_FILE) ) -public class HgProjectSettings implements PersistentStateComponent { +public class HgProjectSettings implements PersistentStateComponent, DvcsSyncBranchSettings { @NotNull private final HgGlobalSettings myAppSettings; @NotNull private final Project myProject; @@ -37,10 +39,12 @@ public class HgProjectSettings implements PersistentStateComponent - * The popup which allows to quickly switch and control Hg branches. - *

- *

- * Use {@link #asListPopup()} to achieve the {@link com.intellij.openapi.ui.popup.ListPopup} itself. - *

- * - * @author NadyaZabrodina - */ -public class HgBranchPopup { - - private final Project myProject; - - private final HgRepository myCurrentRepository; - private final ListPopupImpl myPopup; - - public ListPopup asListPopup() { - return myPopup; - } - - /** - * @param currentRepository Current repository, which means the repository of the currently open or selected file. - */ - public static HgBranchPopup getInstance(@NotNull Project project, @NotNull HgRepository currentRepository) { - return new HgBranchPopup(project, currentRepository); - } - - private HgBranchPopup(@NotNull Project project, @NotNull HgRepository currentRepository) { - myProject = project; - myCurrentRepository = currentRepository; - String title = createPopupTitle(currentRepository); - - Condition preselectActionCondition = new Condition() { - @Override - public boolean value(AnAction action) { - return false; - } - }; - myPopup = new BranchActionGroupPopup(title, project, preselectActionCondition, createActions()); - setCurrentBranchInfo(); - } - - - @NotNull - private static String createPopupTitle(@NotNull HgRepository currentRepository) { - String title = "Hg Branches"; - title += " in " + DvcsUtil.getShortRepositoryName(currentRepository); - return title; - } - - private void setCurrentBranchInfo() { - String branchText = "Current branch : "; - //always display heavy branch name for additional info // - myPopup.setAdText(branchText + myCurrentRepository.getCurrentBranch(), SwingConstants.CENTER); - } - - - private ActionGroup createActions() { - DefaultActionGroup popupGroup = new DefaultActionGroup(null, false); - fillPopupWithCurrentRepositoryActions(popupGroup, createRepositoriesActions()); - popupGroup.addSeparator(); - return popupGroup; - } - - - @Nullable - private DefaultActionGroup createRepositoriesActions() { - List repositories = HgUtil.getHgRepositories(myProject); - if (repositories.size() == 1) { - return null; // if project has only one repository all branches, bookmarks and actions should be inline and no repository group needed - } - DefaultActionGroup popupGroup = new DefaultActionGroup(null, false); - popupGroup.addSeparator("Repositories"); - boolean isMultiRepoConfig = repositories.size() > 1; - for (VirtualFile repository : repositories) { - HgRepository repo = HgUtil.getRepositoryManager(myProject).getRepositoryForRoot(repository); - if (repo != null) { - popupGroup.add(new RootAction(repo, isMultiRepoConfig ? myCurrentRepository : null, - new HgBranchPopupActions(repo.getProject(), repo).createActions(null), - HgUtil.getDisplayableBranchOrBookmarkText(repo))); - } - } - return popupGroup; - } - - protected void fillPopupWithCurrentRepositoryActions(@NotNull DefaultActionGroup popupGroup, @Nullable DefaultActionGroup actions) { - popupGroup.addAll(new HgBranchPopupActions(myProject, myCurrentRepository).createActions(actions)); - } -} - diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgBranchPopupActions.java b/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgBranchPopupActions.java deleted file mode 100644 index 45dd12bd9c81..000000000000 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgBranchPopupActions.java +++ /dev/null @@ -1,262 +0,0 @@ -/* - * Copyright 2000-2013 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.zmlx.hg4idea.action; - -import com.intellij.dvcs.DvcsUtil; -import com.intellij.dvcs.repo.Repository; -import com.intellij.dvcs.ui.NewBranchAction; -import com.intellij.openapi.actionSystem.ActionGroup; -import com.intellij.openapi.actionSystem.AnAction; -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.actionSystem.DefaultActionGroup; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.project.DumbAwareAction; -import com.intellij.openapi.project.Project; -import com.intellij.util.ArrayUtil; -import com.intellij.util.Function; -import com.intellij.util.PlatformIcons; -import com.intellij.util.containers.ContainerUtil; -import com.intellij.vcs.log.Hash; -import com.intellij.vcs.log.impl.HashImpl; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.zmlx.hg4idea.HgNameWithHashInfo; -import org.zmlx.hg4idea.HgRevisionNumber; -import org.zmlx.hg4idea.command.HgBookmarkCommand; -import org.zmlx.hg4idea.command.HgBranchCreateCommand; -import org.zmlx.hg4idea.command.HgWorkingCopyRevisionsCommand; -import org.zmlx.hg4idea.execution.HgCommandException; -import org.zmlx.hg4idea.execution.HgCommandResult; -import org.zmlx.hg4idea.execution.HgCommandResultHandler; -import org.zmlx.hg4idea.repo.HgRepository; -import org.zmlx.hg4idea.ui.HgBookmarkDialog; -import org.zmlx.hg4idea.util.HgErrorUtil; - -import java.util.*; - -import static org.zmlx.hg4idea.util.HgUtil.getNamesWithoutHashes; -import static org.zmlx.hg4idea.util.HgUtil.getNewBranchNameFromUser; - -public class HgBranchPopupActions { - - private final Project myProject; - private final HgRepository myRepository; - - HgBranchPopupActions(Project project, HgRepository repository) { - myProject = project; - myRepository = repository; - } - - ActionGroup createActions(@Nullable DefaultActionGroup toInsert) { - DefaultActionGroup popupGroup = new DefaultActionGroup(null, false); - popupGroup.addAction(new HgNewBranchAction(myProject, Collections.singletonList(myRepository), myRepository)); - popupGroup.addAction(new HgNewBookmarkAction(myProject, Collections.singletonList(myRepository), myRepository)); - popupGroup.addAction(new HgShowUnnamedHeadsForCurrentBranchAction(myProject, myRepository)); - if (toInsert != null) { - popupGroup.addAll(toInsert); - } - - popupGroup.addSeparator("Bookmarks"); - List bookmarkNames = getNamesWithoutHashes(myRepository.getBookmarks()); - String currentBookmark = myRepository.getCurrentBookmark(); - for (String bookmark : bookmarkNames) { - AnAction bookmarkAction = new BookmarkActions(myProject, myRepository, bookmark); - if (bookmark.equals(currentBookmark)) { - bookmarkAction.getTemplatePresentation().setIcon(PlatformIcons.CHECK_ICON); - } - popupGroup.add(bookmarkAction); - } - - popupGroup.addSeparator("Branches"); - List branchNamesList = new ArrayList(myRepository.getOpenedBranches());//only opened branches have to be shown - Collections.sort(branchNamesList); - for (String branch : branchNamesList) { - if (!branch.equals(myRepository.getCurrentBranch())) { // don't show current branch in the list - popupGroup.add(new HgCommonBranchActions(myProject, myRepository, branch)); - } - } - return popupGroup; - } - - private static class HgNewBranchAction extends NewBranchAction { - @NotNull final HgRepository myPreselectedRepo; - - HgNewBranchAction(@NotNull Project project, @NotNull List repositories, @NotNull HgRepository preselectedRepo) { - super(project, repositories); - myPreselectedRepo = preselectedRepo; - } - - @Override - public void actionPerformed(AnActionEvent e) { - final String name = getNewBranchNameFromUser(myPreselectedRepo, "Create New Branch"); - if (name == null) { - return; - } - try { - new HgBranchCreateCommand(myProject, myPreselectedRepo.getRoot(), name).execute(new HgCommandResultHandler() { - @Override - public void process(@Nullable HgCommandResult result) { - myPreselectedRepo.update(); - if (HgErrorUtil.hasErrorsInCommandExecution(result)) { - new HgCommandResultNotifier(myProject) - .notifyError(result, "Creation failed", "Branch creation [" + name + "] failed"); - } - } - }); - } - catch (HgCommandException exception) { - HgAbstractGlobalAction.handleException(myProject, "Can't create new branch: ", exception); - } - } - } - - private static class HgNewBookmarkAction extends DumbAwareAction { - @NotNull protected final List myRepositories; - @NotNull protected Project myProject; - @NotNull final HgRepository myPreselectedRepo; - - HgNewBookmarkAction(@NotNull Project project, @NotNull List repositories, @NotNull HgRepository preselectedRepo) { - super("New Bookmark", "Create new bookmark", null); - myProject = project; - myRepositories = repositories; - myPreselectedRepo = preselectedRepo; - } - - @Override - public void update(AnActionEvent e) { - if (DvcsUtil.anyRepositoryIsFresh(myRepositories)) { - e.getPresentation().setEnabled(false); - e.getPresentation().setDescription("Bookmark creation is not possible before the first commit."); - } - } - - @Override - public void actionPerformed(AnActionEvent e) { - - final HgBookmarkDialog bookmarkDialog = new HgBookmarkDialog(myPreselectedRepo); - bookmarkDialog.show(); - if (bookmarkDialog.isOK()) { - try { - final String name = bookmarkDialog.getName(); - new HgBookmarkCommand(myProject, myPreselectedRepo.getRoot(), name).createBookmark(bookmarkDialog.isActive()); - } - catch (HgCommandException exception) { - HgAbstractGlobalAction.handleException(myProject, exception); - } - } - } - } - - static private class HgShowUnnamedHeadsForCurrentBranchAction extends ActionGroup { - @NotNull final Project myProject; - @NotNull final HgRepository myRepository; - @NotNull final String myCurrentBranchName; - @NotNull Collection myHeads = new HashSet(); - - public HgShowUnnamedHeadsForCurrentBranchAction(@NotNull Project project, - @NotNull HgRepository repository) { - super(null, true); - myProject = project; - myRepository = repository; - myCurrentBranchName = repository.getCurrentBranch(); - getTemplatePresentation().setText(String.format("Unnamed heads for %s", myCurrentBranchName)); - ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { - @Override - public void run() { - myHeads = filterUnnamedHeads(); - } - }); - } - - @NotNull - private Collection filterUnnamedHeads() { - Collection branchWithHashes = myRepository.getBranches().get(myCurrentBranchName); - if (branchWithHashes == null) { - // repository is fresh or branch is fresh. - return Collections.emptySet(); - } - else { - List parents = new HgWorkingCopyRevisionsCommand(myProject).parents(myRepository.getRoot()); - if (parents.size() == 1) { - Collection bookmarkHashes = ContainerUtil.map(myRepository.getBookmarks(), new Function() { - - @Override - public Hash fun(HgNameWithHashInfo info) { - return info.getHash(); - } - }); - branchWithHashes.removeAll(bookmarkHashes); - branchWithHashes.remove(HashImpl.build(parents.get(0).getChangeset())); - } - } - return branchWithHashes; - } - - @NotNull - @Override - public AnAction[] getChildren(@Nullable AnActionEvent e) { - List branchHeadActions = new ArrayList(); - for (Hash hash : myHeads) { - branchHeadActions.add(new HgCommonBranchActions(myProject, myRepository, hash.toShortString())); - } - return ContainerUtil.toArray(branchHeadActions, new AnAction[branchHeadActions.size()]); - } - - @Override - public void update(final AnActionEvent e) { - if (myRepository.isFresh()) { - e.getPresentation().setEnabled(false); - e.getPresentation().setDescription("Checkout of a new branch is not possible before the first commit."); - } - else if (!Repository.State.NORMAL.equals(myRepository.getState())) { - e.getPresentation().setEnabled(false); - } - } - } - - /** - * Actions available for bookmarks. - */ - static class BookmarkActions extends HgCommonBranchActions { - - BookmarkActions(@NotNull Project project, @NotNull HgRepository selectedRepository, @NotNull String branchName) { - super(project, selectedRepository, branchName); - } - - @NotNull - @Override - public AnAction[] getChildren(@Nullable AnActionEvent e) { - return ArrayUtil.append(super.getChildren(e), new DeleteBookmarkAction(myProject, mySelectedRepository, myBranchName)); - } - - private static class DeleteBookmarkAction extends HgBranchAbstractAction { - - DeleteBookmarkAction(@NotNull Project project, @NotNull HgRepository selectedRepository, @NotNull String branchName) { - super(project, "Delete", selectedRepository, branchName); - } - - @Override - public void actionPerformed(AnActionEvent e) { - try { - new HgBookmarkCommand(myProject, mySelectedRepository.getRoot(), myBranchName).deleteBookmark(); - } - catch (HgCommandException exception) { - HgAbstractGlobalAction.handleException(myProject, exception); - } - } - } - } -} diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgBranchesAction.java b/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgBranchesAction.java deleted file mode 100644 index d799cde1705c..000000000000 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgBranchesAction.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2000-2013 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.zmlx.hg4idea.action; - -import com.intellij.openapi.project.Project; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.zmlx.hg4idea.repo.HgRepository; - -import java.util.Collection; - -public class HgBranchesAction extends HgAbstractGlobalSingleRepoAction { - - @Override - protected void execute(@NotNull Project project, @NotNull Collection repositories, @Nullable HgRepository selectedRepo) { - if (selectedRepo != null) { - HgBranchPopup.getInstance(project, selectedRepo).asListPopup().showInFocusCenter(); - } - } -} diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgCommonBranchActions.java b/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgCommonBranchActions.java deleted file mode 100644 index c79ae1ca0b7c..000000000000 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/action/HgCommonBranchActions.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.zmlx.hg4idea.action; - -import com.intellij.openapi.actionSystem.ActionGroup; -import com.intellij.openapi.actionSystem.AnAction; -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.fileEditor.FileDocumentManager; -import com.intellij.openapi.progress.ProgressIndicator; -import com.intellij.openapi.progress.Task; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.vcs.VcsException; -import com.intellij.openapi.vcs.VcsNotifier; -import com.intellij.openapi.vcs.update.UpdatedFiles; -import com.intellij.openapi.vfs.VirtualFile; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.zmlx.hg4idea.HgVcs; -import org.zmlx.hg4idea.HgVcsMessages; -import org.zmlx.hg4idea.command.HgMergeCommand; -import org.zmlx.hg4idea.command.HgUpdateCommand; -import org.zmlx.hg4idea.execution.HgCommandResult; -import org.zmlx.hg4idea.provider.update.HgConflictResolver; -import org.zmlx.hg4idea.provider.update.HgHeadMerger; -import org.zmlx.hg4idea.repo.HgRepository; -import org.zmlx.hg4idea.util.HgErrorUtil; - -public class HgCommonBranchActions extends ActionGroup { - - @NotNull protected final Project myProject; - @NotNull protected String myBranchName; - @NotNull protected final HgRepository mySelectedRepository; - - HgCommonBranchActions(@NotNull Project project, @NotNull HgRepository selectedRepository, @NotNull String branchName) { - super("", true); - myProject = project; - myBranchName = branchName; - mySelectedRepository = selectedRepository; - getTemplatePresentation().setText(myBranchName, false); // no mnemonics - } - - @NotNull - @Override - public AnAction[] getChildren(@Nullable AnActionEvent e) { - return new AnAction[]{ - new UpdateAction(myProject, mySelectedRepository, myBranchName), - new MergeAction(myProject, mySelectedRepository, myBranchName) - }; - } - - private static class MergeAction extends HgBranchAbstractAction { - - public MergeAction(@NotNull Project project, - @NotNull HgRepository selectedRepository, - @NotNull String branchName) { - super(project, "Merge", selectedRepository, branchName); - } - - @Override - public void actionPerformed(AnActionEvent e) { - FileDocumentManager.getInstance().saveAllDocuments(); - final UpdatedFiles updatedFiles = UpdatedFiles.create(); - final HgMergeCommand hgMergeCommand = new HgMergeCommand(myProject, mySelectedRepository.getRoot()); - hgMergeCommand.setRevision(myBranchName);//there is no difference between branch or revision or bookmark as parameter to merge, - // we need just a string - new Task.Backgroundable(myProject, "Merging changes...") { - @Override - public void run(@NotNull ProgressIndicator indicator) { - try { - new HgHeadMerger(myProject, hgMergeCommand) - .merge(mySelectedRepository.getRoot()); - new HgConflictResolver(myProject, updatedFiles).resolve(mySelectedRepository.getRoot()); - } - - catch (VcsException exception) { - assert myProject != null; // myProject couldn't be null, see annotation for Merge action - if (exception.isWarning()) { - VcsNotifier.getInstance(myProject).notifyWarning("Warning during merge", exception.getMessage()); - } - else { - VcsNotifier.getInstance(myProject).notifyError("Exception during merge", exception.getMessage()); - } - } - catch (Exception e1) { - HgAbstractGlobalAction.handleException(myProject, e1); - } - } - }.queue(); - } - } - - private static class UpdateAction extends HgBranchAbstractAction { - - public UpdateAction(@NotNull Project project, - @NotNull HgRepository selectedRepository, - @NotNull String branchName) { - super(project, "Update", selectedRepository, branchName); - } - - @Override - public void actionPerformed(AnActionEvent e) { - final VirtualFile repository = mySelectedRepository.getRoot(); - FileDocumentManager.getInstance().saveAllDocuments(); - final HgUpdateCommand hgUpdateCommand = new HgUpdateCommand(myProject, repository); - hgUpdateCommand.setBranch(myBranchName); - new Task.Backgroundable(myProject, HgVcsMessages.message("action.hg4idea.updateTo.description", myBranchName)) { - @Override - public void run(@NotNull ProgressIndicator indicator) { - HgCommandResult result = hgUpdateCommand.execute(); - assert myProject != null; // myProject couldn't be null, see annotation for updateTo action - if (HgErrorUtil.hasErrorsInCommandExecution(result)) { - new HgCommandResultNotifier(myProject).notifyError(result, "", "Update failed"); - new HgConflictResolver(myProject).resolve(repository); - } - myProject.getMessageBus().syncPublisher(HgVcs.BRANCH_TOPIC).update(myProject, null); - } - }.queue(); - } - } -} \ No newline at end of file diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/branch/HgBranchAbstractAction.java b/plugins/hg4idea/src/org/zmlx/hg4idea/branch/HgBranchAbstractAction.java new file mode 100644 index 000000000000..3998dedd0cc5 --- /dev/null +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/branch/HgBranchAbstractAction.java @@ -0,0 +1,38 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.zmlx.hg4idea.branch; + +import com.intellij.openapi.project.DumbAwareAction; +import com.intellij.openapi.project.Project; +import org.jetbrains.annotations.NotNull; +import org.zmlx.hg4idea.repo.HgRepository; + +import java.util.List; + +public abstract class HgBranchAbstractAction extends DumbAwareAction { + @NotNull protected final Project myProject; + @NotNull protected final List myRepositories; + @NotNull protected final String myBranchName; + + public HgBranchAbstractAction(@NotNull Project project, @NotNull String title, + @NotNull List repositories, + @NotNull String branchName) { + super(title); + myProject = project; + myRepositories = repositories; + myBranchName = branchName; + } +} \ No newline at end of file diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/branch/HgBranchPopup.java b/plugins/hg4idea/src/org/zmlx/hg4idea/branch/HgBranchPopup.java new file mode 100644 index 000000000000..c0d6f58d5e0e --- /dev/null +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/branch/HgBranchPopup.java @@ -0,0 +1,119 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.zmlx.hg4idea.branch; + +import com.intellij.dvcs.branch.DvcsBranchPopup; +import com.intellij.dvcs.repo.AbstractRepositoryManager; +import com.intellij.dvcs.ui.RootAction; +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.DefaultActionGroup; +import com.intellij.openapi.components.ServiceManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Condition; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.zmlx.hg4idea.HgProjectSettings; +import org.zmlx.hg4idea.repo.HgRepository; +import org.zmlx.hg4idea.repo.HgRepositoryManager; +import org.zmlx.hg4idea.util.HgUtil; + +import javax.swing.*; +import java.util.List; + +/** + *

+ * The popup which allows to quickly switch and control Hg branches. + *

+ *

+ * Use {@link #asListPopup()} to achieve the {@link com.intellij.openapi.ui.popup.ListPopup} itself. + *

+ */ +public class HgBranchPopup extends DvcsBranchPopup { + + /** + * @param currentRepository Current repository, which means the repository of the currently open or selected file. + */ + public static HgBranchPopup getInstance(@NotNull Project project, @NotNull HgRepository currentRepository) { + + HgRepositoryManager manager = HgUtil.getRepositoryManager(project); + HgProjectSettings hgProjectSettings = ServiceManager.getService(project, HgProjectSettings.class); + HgMultiRootBranchConfig hgMultiRootBranchConfig = new HgMultiRootBranchConfig(manager.getRepositories()); + + Condition preselectActionCondition = new Condition() { + @Override + public boolean value(AnAction action) { + return false; + } + }; + return new HgBranchPopup(currentRepository, manager, hgMultiRootBranchConfig, hgProjectSettings, + preselectActionCondition); + } + + private HgBranchPopup(@NotNull HgRepository currentRepository, + @NotNull HgRepositoryManager repositoryManager, + @NotNull HgMultiRootBranchConfig hgMultiRootBranchConfig, @NotNull HgProjectSettings vcsSettings, + @NotNull Condition preselectActionCondition) { + super(currentRepository, repositoryManager, hgMultiRootBranchConfig, vcsSettings, preselectActionCondition); + } + + protected void setCurrentBranchInfo() { + String branchText = "Current branch : "; + //always display heavy branch name for additional info // + myPopup.setAdText(branchText + myCurrentRepository.getCurrentBranch(), SwingConstants.CENTER); + } + + @Override + protected void fillWithCommonRepositoryActions(@NotNull DefaultActionGroup popupGroup, + @NotNull AbstractRepositoryManager repositoryManager) { + List allRepositories = repositoryManager.getRepositories(); + popupGroup.add(new HgBranchPopupActions.HgNewBranchAction(myProject, allRepositories, myCurrentRepository)); + popupGroup.addAction(new HgBranchPopupActions.HgNewBookmarkAction(myProject, allRepositories, myCurrentRepository)); + popupGroup.addAction(new HgBranchPopupActions.HgShowUnnamedHeadsForCurrentBranchAction(myProject, myCurrentRepository)); + popupGroup.addAll(createRepositoriesActions()); + + popupGroup.addSeparator("Common Branches"); + for (String branch : myMultiRootBranchConfig.getLocalBranchNames()) { + List repositories = filterRepositoriesNotOnThisBranch(branch, allRepositories); + if (!repositories.isEmpty()) { + popupGroup.add(new HgCommonBranchActions(myProject, repositories, branch)); + } + } + popupGroup.addSeparator("Common Bookmarks"); + for (String branch : ((HgMultiRootBranchConfig)myMultiRootBranchConfig).getBookmarkNames()) { + List repositories = filterRepositoriesNotOnThisBranch(branch, allRepositories); + if (!repositories.isEmpty()) { + popupGroup.add(new HgBranchPopupActions.BookmarkActions(myProject, repositories, branch)); + } + } + } + + @NotNull + protected DefaultActionGroup createRepositoriesActions() { + DefaultActionGroup popupGroup = new DefaultActionGroup(null, false); + popupGroup.addSeparator("Repositories"); + for (HgRepository repository : myRepositoryManager.getRepositories()) { + popupGroup.add(new RootAction(repository, highlightCurrentRepo() ? myCurrentRepository : null, + new HgBranchPopupActions(repository.getProject(), repository).createActions(null), + HgUtil.getDisplayableBranchOrBookmarkText(repository))); + } + return popupGroup; + } + + protected void fillPopupWithCurrentRepositoryActions(@NotNull DefaultActionGroup popupGroup, @Nullable DefaultActionGroup actions) { + popupGroup.addAll(new HgBranchPopupActions(myProject, myCurrentRepository).createActions(actions)); + } +} + diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/branch/HgBranchPopupActions.java b/plugins/hg4idea/src/org/zmlx/hg4idea/branch/HgBranchPopupActions.java new file mode 100644 index 000000000000..10b97235f559 --- /dev/null +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/branch/HgBranchPopupActions.java @@ -0,0 +1,270 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.zmlx.hg4idea.branch; + +import com.intellij.dvcs.DvcsUtil; +import com.intellij.dvcs.repo.Repository; +import com.intellij.dvcs.ui.NewBranchAction; +import com.intellij.openapi.actionSystem.ActionGroup; +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.DefaultActionGroup; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.project.DumbAwareAction; +import com.intellij.openapi.project.Project; +import com.intellij.util.ArrayUtil; +import com.intellij.util.Function; +import com.intellij.util.PlatformIcons; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.vcs.log.Hash; +import com.intellij.vcs.log.impl.HashImpl; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.zmlx.hg4idea.HgNameWithHashInfo; +import org.zmlx.hg4idea.HgRevisionNumber; +import org.zmlx.hg4idea.action.HgAbstractGlobalAction; +import org.zmlx.hg4idea.action.HgCommandResultNotifier; +import org.zmlx.hg4idea.command.HgBookmarkCommand; +import org.zmlx.hg4idea.command.HgBranchCreateCommand; +import org.zmlx.hg4idea.command.HgWorkingCopyRevisionsCommand; +import org.zmlx.hg4idea.execution.HgCommandException; +import org.zmlx.hg4idea.execution.HgCommandResult; +import org.zmlx.hg4idea.execution.HgCommandResultHandler; +import org.zmlx.hg4idea.repo.HgRepository; +import org.zmlx.hg4idea.ui.HgBookmarkDialog; +import org.zmlx.hg4idea.util.HgErrorUtil; + +import java.util.*; + +import static org.zmlx.hg4idea.util.HgUtil.getNamesWithoutHashes; +import static org.zmlx.hg4idea.util.HgUtil.getNewBranchNameFromUser; + +public class HgBranchPopupActions { + + private final Project myProject; + private final HgRepository myRepository; + + HgBranchPopupActions(Project project, HgRepository repository) { + myProject = project; + myRepository = repository; + } + + ActionGroup createActions(@Nullable DefaultActionGroup toInsert) { + DefaultActionGroup popupGroup = new DefaultActionGroup(null, false); + popupGroup.addAction(new HgNewBranchAction(myProject, Collections.singletonList(myRepository), myRepository)); + popupGroup.addAction(new HgNewBookmarkAction(myProject, Collections.singletonList(myRepository), myRepository)); + popupGroup.addAction(new HgShowUnnamedHeadsForCurrentBranchAction(myProject, myRepository)); + if (toInsert != null) { + popupGroup.addAll(toInsert); + } + + popupGroup.addSeparator("Bookmarks"); + List bookmarkNames = getNamesWithoutHashes(myRepository.getBookmarks()); + String currentBookmark = myRepository.getCurrentBookmark(); + for (String bookmark : bookmarkNames) { + AnAction bookmarkAction = new BookmarkActions(myProject, Collections.singletonList(myRepository), bookmark); + if (bookmark.equals(currentBookmark)) { + bookmarkAction.getTemplatePresentation().setIcon(PlatformIcons.CHECK_ICON); + } + popupGroup.add(bookmarkAction); + } + + popupGroup.addSeparator("Branches"); + List branchNamesList = new ArrayList(myRepository.getOpenedBranches());//only opened branches have to be shown + Collections.sort(branchNamesList); + for (String branch : branchNamesList) { + if (!branch.equals(myRepository.getCurrentBranch())) { // don't show current branch in the list + popupGroup.add(new HgCommonBranchActions(myProject, Collections.singletonList(myRepository), branch)); + } + } + return popupGroup; + } + + public static class HgNewBranchAction extends NewBranchAction { + @NotNull final HgRepository myPreselectedRepo; + + HgNewBranchAction(@NotNull Project project, @NotNull List repositories, @NotNull HgRepository preselectedRepo) { + super(project, repositories); + myPreselectedRepo = preselectedRepo; + } + + @Override + public void actionPerformed(AnActionEvent e) { + final String name = getNewBranchNameFromUser(myPreselectedRepo, "Create New Branch"); + if (name == null) { + return; + } + for (final HgRepository repository : myRepositories) { + try { + new HgBranchCreateCommand(myProject, repository.getRoot(), name).execute(new HgCommandResultHandler() { + @Override + public void process(@Nullable HgCommandResult result) { + repository.update(); + if (HgErrorUtil.hasErrorsInCommandExecution(result)) { + new HgCommandResultNotifier(myProject) + .notifyError(result, "Creation failed", "Branch creation [" + name + "] failed"); + } + } + }); + } + catch (HgCommandException exception) { + HgAbstractGlobalAction.handleException(myProject, "Can't create new branch: ", exception); + } + } + } + } + + public static class HgNewBookmarkAction extends DumbAwareAction { + @NotNull protected final List myRepositories; + @NotNull protected Project myProject; + @NotNull final HgRepository myPreselectedRepo; + + HgNewBookmarkAction(@NotNull Project project, @NotNull List repositories, @NotNull HgRepository preselectedRepo) { + super("New Bookmark", "Create new bookmark", null); + myProject = project; + myRepositories = repositories; + myPreselectedRepo = preselectedRepo; + } + + @Override + public void update(AnActionEvent e) { + if (DvcsUtil.anyRepositoryIsFresh(myRepositories)) { + e.getPresentation().setEnabled(false); + e.getPresentation().setDescription("Bookmark creation is not possible before the first commit."); + } + } + + @Override + public void actionPerformed(AnActionEvent e) { + + final HgBookmarkDialog bookmarkDialog = new HgBookmarkDialog(myPreselectedRepo); + bookmarkDialog.show(); + if (bookmarkDialog.isOK()) { + final String name = bookmarkDialog.getName(); + for (HgRepository repository : myRepositories) { + try { + new HgBookmarkCommand(myProject, repository.getRoot(), name).createBookmark(bookmarkDialog.isActive()); + } + catch (HgCommandException exception) { + HgAbstractGlobalAction.handleException(myProject, exception); + } + } + } + } + } + + public static class HgShowUnnamedHeadsForCurrentBranchAction extends ActionGroup { + @NotNull final Project myProject; + @NotNull final HgRepository myRepository; + @NotNull final String myCurrentBranchName; + @NotNull Collection myHeads = new HashSet(); + + public HgShowUnnamedHeadsForCurrentBranchAction(@NotNull Project project, + @NotNull HgRepository repository) { + super(null, true); + myProject = project; + myRepository = repository; + myCurrentBranchName = repository.getCurrentBranch(); + getTemplatePresentation().setText(String.format("Unnamed heads for %s", myCurrentBranchName)); + ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { + @Override + public void run() { + myHeads = filterUnnamedHeads(); + } + }); + } + + @NotNull + private Collection filterUnnamedHeads() { + Collection branchWithHashes = myRepository.getBranches().get(myCurrentBranchName); + if (branchWithHashes == null) { + // repository is fresh or branch is fresh. + return Collections.emptySet(); + } + else { + List parents = new HgWorkingCopyRevisionsCommand(myProject).parents(myRepository.getRoot()); + if (parents.size() == 1) { + Collection bookmarkHashes = ContainerUtil.map(myRepository.getBookmarks(), new Function() { + + @Override + public Hash fun(HgNameWithHashInfo info) { + return info.getHash(); + } + }); + branchWithHashes.removeAll(bookmarkHashes); + branchWithHashes.remove(HashImpl.build(parents.get(0).getChangeset())); + } + } + return branchWithHashes; + } + + @NotNull + @Override + public AnAction[] getChildren(@Nullable AnActionEvent e) { + List branchHeadActions = new ArrayList(); + for (Hash hash : myHeads) { + branchHeadActions.add(new HgCommonBranchActions(myProject, Collections.singletonList(myRepository), hash.toShortString())); + } + return ContainerUtil.toArray(branchHeadActions, new AnAction[branchHeadActions.size()]); + } + + @Override + public void update(final AnActionEvent e) { + if (myRepository.isFresh()) { + e.getPresentation().setEnabled(false); + e.getPresentation().setDescription("Checkout of a new branch is not possible before the first commit."); + } + else if (!Repository.State.NORMAL.equals(myRepository.getState())) { + e.getPresentation().setEnabled(false); + } + } + } + + /** + * Actions available for bookmarks. + */ + static class BookmarkActions extends HgCommonBranchActions { + + BookmarkActions(@NotNull Project project, @NotNull List repositories, @NotNull String branchName) { + super(project, repositories, branchName); + } + + @NotNull + @Override + public AnAction[] getChildren(@Nullable AnActionEvent e) { + return ArrayUtil.append(super.getChildren(e), new DeleteBookmarkAction(myProject, myRepositories, myBranchName)); + } + + private static class DeleteBookmarkAction extends HgBranchAbstractAction { + + DeleteBookmarkAction(@NotNull Project project, @NotNull List repositories, @NotNull String branchName) { + super(project, "Delete", repositories, branchName); + } + + @Override + public void actionPerformed(AnActionEvent e) { + for (HgRepository repository : myRepositories) { + try { + new HgBookmarkCommand(myProject, repository.getRoot(), myBranchName).deleteBookmark(); + } + catch (HgCommandException exception) { + HgAbstractGlobalAction.handleException(myProject, exception); + } + } + } + } + } +} diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/branch/HgBranchUtil.java b/plugins/hg4idea/src/org/zmlx/hg4idea/branch/HgBranchUtil.java new file mode 100644 index 000000000000..9dbdd3ea7f65 --- /dev/null +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/branch/HgBranchUtil.java @@ -0,0 +1,77 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.zmlx.hg4idea.branch; + +import com.intellij.util.containers.ContainerUtil; +import com.intellij.vcs.log.Hash; +import org.jetbrains.annotations.NotNull; +import org.zmlx.hg4idea.HgNameWithHashInfo; +import org.zmlx.hg4idea.repo.HgRepository; +import org.zmlx.hg4idea.util.HgUtil; + +import java.util.*; + +public class HgBranchUtil { + + /** + * Only common hg heavy branches + */ + @NotNull + public static Collection getCommonBranches(@NotNull Collection repositories) { + Collection commonBranches = null; + for (HgRepository repository : repositories) { + Map> branchesWithHashes = repository.getBranches(); + Collection names = branchesWithHashes.keySet(); + if (commonBranches == null) { + commonBranches = names; + } + else { + commonBranches = ContainerUtil.intersection(commonBranches, names); + } + } + if (commonBranches != null) { + ArrayList common = new ArrayList(commonBranches); + Collections.sort(common); + return common; + } + else { + return Collections.emptyList(); + } + } + + @NotNull + public static Collection getCommonBookmarks(@NotNull Collection repositories) { + Collection commonBookmarkNames = null; + for (HgRepository repository : repositories) { + Collection bookmarksInfo = repository.getBookmarks(); + Collection names = HgUtil.getNamesWithoutHashes(bookmarksInfo); + if (commonBookmarkNames == null) { + commonBookmarkNames = names; + } + else { + commonBookmarkNames = ContainerUtil.intersection(commonBookmarkNames, names); + } + } + if (commonBookmarkNames != null) { + ArrayList common = new ArrayList(commonBookmarkNames); + Collections.sort(common); + return common; + } + else { + return Collections.emptyList(); + } + } +} diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/branch/HgBranchesAction.java b/plugins/hg4idea/src/org/zmlx/hg4idea/branch/HgBranchesAction.java new file mode 100644 index 000000000000..b078da12548f --- /dev/null +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/branch/HgBranchesAction.java @@ -0,0 +1,34 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.zmlx.hg4idea.branch; + +import com.intellij.openapi.project.Project; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.zmlx.hg4idea.action.HgAbstractGlobalSingleRepoAction; +import org.zmlx.hg4idea.repo.HgRepository; + +import java.util.Collection; + +public class HgBranchesAction extends HgAbstractGlobalSingleRepoAction { + + @Override + protected void execute(@NotNull Project project, @NotNull Collection repositories, @Nullable HgRepository selectedRepo) { + if (selectedRepo != null) { + HgBranchPopup.getInstance(project, selectedRepo).asListPopup().showInFocusCenter(); + } + } +} diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/branch/HgCommonBranchActions.java b/plugins/hg4idea/src/org/zmlx/hg4idea/branch/HgCommonBranchActions.java new file mode 100644 index 000000000000..ecba99f7b532 --- /dev/null +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/branch/HgCommonBranchActions.java @@ -0,0 +1,141 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.zmlx.hg4idea.branch; + +import com.intellij.openapi.actionSystem.ActionGroup; +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.fileEditor.FileDocumentManager; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.Task; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vcs.VcsException; +import com.intellij.openapi.vcs.VcsNotifier; +import com.intellij.openapi.vcs.update.UpdatedFiles; +import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.zmlx.hg4idea.HgVcs; +import org.zmlx.hg4idea.HgVcsMessages; +import org.zmlx.hg4idea.action.HgAbstractGlobalAction; +import org.zmlx.hg4idea.action.HgCommandResultNotifier; +import org.zmlx.hg4idea.command.HgMergeCommand; +import org.zmlx.hg4idea.command.HgUpdateCommand; +import org.zmlx.hg4idea.execution.HgCommandResult; +import org.zmlx.hg4idea.provider.update.HgConflictResolver; +import org.zmlx.hg4idea.provider.update.HgHeadMerger; +import org.zmlx.hg4idea.repo.HgRepository; +import org.zmlx.hg4idea.util.HgErrorUtil; + +import java.util.List; + +public class HgCommonBranchActions extends ActionGroup { + + @NotNull protected final Project myProject; + @NotNull protected String myBranchName; + @NotNull List myRepositories; + + HgCommonBranchActions(@NotNull Project project, @NotNull List repositories, @NotNull String branchName) { + super("", true); + myProject = project; + myBranchName = branchName; + myRepositories = repositories; + getTemplatePresentation().setText(myBranchName, false); // no mnemonics + } + + @NotNull + @Override + public AnAction[] getChildren(@Nullable AnActionEvent e) { + return new AnAction[]{ + new UpdateAction(myProject, myRepositories, myBranchName), + new MergeAction(myProject, myRepositories, myBranchName) + }; + } + + private static class MergeAction extends HgBranchAbstractAction { + + public MergeAction(@NotNull Project project, + @NotNull List repositories, + @NotNull String branchName) { + super(project, "Merge", repositories, branchName); + } + + @Override + public void actionPerformed(AnActionEvent e) { + FileDocumentManager.getInstance().saveAllDocuments(); + final UpdatedFiles updatedFiles = UpdatedFiles.create(); + for (final HgRepository repository : myRepositories) { + final HgMergeCommand hgMergeCommand = new HgMergeCommand(myProject, repository.getRoot()); + hgMergeCommand.setRevision(myBranchName);//there is no difference between branch or revision or bookmark as parameter to merge, + // we need just a string + new Task.Backgroundable(myProject, "Merging changes...") { + @Override + public void run(@NotNull ProgressIndicator indicator) { + try { + new HgHeadMerger(myProject, hgMergeCommand) + .merge(repository.getRoot()); + new HgConflictResolver(myProject, updatedFiles).resolve(repository.getRoot()); + } + + catch (VcsException exception) { + assert myProject != null; // myProject couldn't be null, see annotation for Merge action + if (exception.isWarning()) { + VcsNotifier.getInstance(myProject).notifyWarning("Warning during merge", exception.getMessage()); + } + else { + VcsNotifier.getInstance(myProject).notifyError("Exception during merge", exception.getMessage()); + } + } + catch (Exception e1) { + HgAbstractGlobalAction.handleException(myProject, e1); + } + } + }.queue(); + } + } + } + + private static class UpdateAction extends HgBranchAbstractAction { + + public UpdateAction(@NotNull Project project, + @NotNull List repositories, + @NotNull String branchName) { + super(project, "Update", repositories, branchName); + } + + @Override + public void actionPerformed(AnActionEvent e) { + FileDocumentManager.getInstance().saveAllDocuments(); + for (HgRepository repo : myRepositories) { + final VirtualFile repository = repo.getRoot(); + final HgUpdateCommand hgUpdateCommand = new HgUpdateCommand(myProject, repository); + hgUpdateCommand.setBranch(myBranchName); + new Task.Backgroundable(myProject, HgVcsMessages.message("action.hg4idea.updateTo.description", myBranchName)) { + @Override + public void run(@NotNull ProgressIndicator indicator) { + HgCommandResult result = hgUpdateCommand.execute(); + assert myProject != null; // myProject couldn't be null, see annotation for updateTo action + if (HgErrorUtil.hasErrorsInCommandExecution(result)) { + new HgCommandResultNotifier(myProject).notifyError(result, "", "Update failed"); + new HgConflictResolver(myProject).resolve(repository); + } + myProject.getMessageBus().syncPublisher(HgVcs.BRANCH_TOPIC).update(myProject, repository); + } + }.queue(); + } + } + } +} \ No newline at end of file diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/branch/HgMultiRootBranchConfig.java b/plugins/hg4idea/src/org/zmlx/hg4idea/branch/HgMultiRootBranchConfig.java new file mode 100644 index 000000000000..e762fb8992ab --- /dev/null +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/branch/HgMultiRootBranchConfig.java @@ -0,0 +1,50 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.zmlx.hg4idea.branch; + +import com.intellij.dvcs.branch.DvcsMultiRootBranchConfig; +import org.jetbrains.annotations.NotNull; +import org.zmlx.hg4idea.repo.HgRepository; + +import java.util.Collection; + +public class HgMultiRootBranchConfig extends DvcsMultiRootBranchConfig { + + public HgMultiRootBranchConfig(@NotNull Collection repositories) { + super(repositories); + } + + @NotNull + @Override + public Collection getLocalBranchNames() { + return HgBranchUtil.getCommonBranches(myRepositories); + } + + @NotNull + Collection getBookmarkNames() { + return HgBranchUtil.getCommonBookmarks(myRepositories); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + for (HgRepository repository : myRepositories) { + sb.append(repository.getPresentableUrl()).append(":").append(repository.getCurrentBranchName()).append(":") + .append(repository.getState()); + } + return sb.toString(); + } +} diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/command/HgCommitCommand.java b/plugins/hg4idea/src/org/zmlx/hg4idea/command/HgCommitCommand.java index 5aa632e0a4ea..794c7096d207 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/command/HgCommitCommand.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/command/HgCommitCommand.java @@ -53,6 +53,7 @@ public class HgCommitCommand { private final boolean myAmend; private Set myFiles = Collections.emptySet(); + @NotNull private List mySubrepos = Collections.emptyList(); public HgCommitCommand(Project project, @NotNull VirtualFile root, String message, boolean amend) { myProject = project; @@ -70,6 +71,10 @@ public class HgCommitCommand { myFiles = files; } + public void setSubrepos(@NotNull List subrepos) { + mySubrepos = subrepos; + } + public void execute() throws HgCommandException, VcsException { if (StringUtil.isEmptyOrSpaces(myMessage)) { throw new HgCommandException(HgVcsMessages.message("hg4idea.commit.error.messageEmpty")); @@ -88,7 +93,8 @@ public class HgCommitCommand { }); List> chunkedCommits = VcsFileUtil.chunkRelativePaths(relativePaths); int size = chunkedCommits.size(); - commitChunkFiles(chunkedCommits.get(0), myAmend); + // commit with subrepo should be first, because it's not possible to amend with --subrepos argument; + commitChunkFiles(chunkedCommits.get(0), myAmend, !mySubrepos.isEmpty()); HgVcs vcs = HgVcs.getInstance(myProject); boolean amendCommit = vcs != null && vcs.getVersion().isAmendSupported(); for (int i = 1; i < size; i++) { @@ -105,14 +111,23 @@ public class HgCommitCommand { messageBus.syncPublisher(HgVcs.BRANCH_TOPIC).update(myProject, null); } - private void commitChunkFiles(List chunk, boolean amendCommit) throws VcsException { + private void commitChunkFiles(@NotNull List chunk, boolean amendCommit) throws VcsException { + commitChunkFiles(chunk, amendCommit, false); + } + + private void commitChunkFiles(@NotNull List chunk, boolean amendCommit, boolean withSubrepos) throws VcsException { List parameters = new LinkedList(); parameters.add("--logfile"); parameters.add(saveCommitMessage().getAbsolutePath()); - parameters.addAll(chunk); - if (amendCommit) { + // note: for now mercurial could not perform amend commit with -S option + if (withSubrepos) { + parameters.add("-S"); + parameters.addAll(mySubrepos); + } + else if (amendCommit) { parameters.add("--amend"); } + parameters.addAll(chunk); HgCommandExecutor executor = new HgCommandExecutor(myProject); executor.setCharset(myCharset); ensureSuccess(executor.executeInCurrentThread(myRoot, "commit", parameters)); @@ -129,5 +144,4 @@ public class HgCommitCommand { } return tempFile; } - } diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/execution/HgCommandExecutor.java b/plugins/hg4idea/src/org/zmlx/hg4idea/execution/HgCommandExecutor.java index 7cf8f65559ea..f27d6a0d6550 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/execution/HgCommandExecutor.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/execution/HgCommandExecutor.java @@ -49,7 +49,7 @@ import java.util.List; */ public class HgCommandExecutor { - protected static final Logger LOG = Logger.getInstance(HgRemoteCommandExecutor.class.getName()); + protected static final Logger LOG = Logger.getInstance(HgCommandExecutor.class.getName()); private static final List DEFAULT_OPTIONS = Arrays.asList("--config", "ui.merge=internal:merge"); protected final Project myProject; diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/log/HgHistoryUtil.java b/plugins/hg4idea/src/org/zmlx/hg4idea/log/HgHistoryUtil.java index de565484e2d5..2d3a7bce191b 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/log/HgHistoryUtil.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/log/HgHistoryUtil.java @@ -54,9 +54,9 @@ public class HgHistoryUtil { } @NotNull - public static List loadMetadata(@NotNull final Project project, - @NotNull final VirtualFile root, int limit, - @NotNull List parameters) throws VcsException { + public static List loadMetadata(@NotNull final Project project, + @NotNull final VirtualFile root, int limit, + @NotNull List parameters) throws VcsException { final VcsLogObjectsFactory factory = getObjectsFactoryWithDisposeCheck(project); if (factory == null) { diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/log/HgLogProvider.java b/plugins/hg4idea/src/org/zmlx/hg4idea/log/HgLogProvider.java index 3b989e448b61..109e83899e5b 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/log/HgLogProvider.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/log/HgLogProvider.java @@ -22,9 +22,11 @@ import com.intellij.openapi.util.Couple; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.VcsKey; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.CollectConsumer; import com.intellij.util.Consumer; import com.intellij.util.containers.ContainerUtil; import com.intellij.vcs.log.*; +import com.intellij.vcs.log.impl.LogDataImpl; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.zmlx.hg4idea.HgNameWithHashInfo; @@ -59,18 +61,23 @@ public class HgLogProvider implements VcsLogProvider { @NotNull @Override - public List readFirstBlock(@NotNull VirtualFile root, - @NotNull Requirements requirements) throws VcsException { - return HgHistoryUtil.loadMetadata(myProject, root, requirements.getCommitCount(), Collections.emptyList()); + public DetailedLogData readFirstBlock(@NotNull VirtualFile root, + @NotNull Requirements requirements) throws VcsException { + List commits = HgHistoryUtil.loadMetadata(myProject, root, requirements.getCommitCount(), + Collections.emptyList()); + return new LogDataImpl(readAllRefs(root), commits); } @Override - public void readAllHashes(@NotNull VirtualFile root, @NotNull Consumer userRegistry, - @NotNull Consumer commitConsumer) throws VcsException { - List commits = HgHistoryUtil.readAllHashes(myProject, root, userRegistry, Collections.emptyList()); + @NotNull + public LogData readAllHashes(@NotNull VirtualFile root, @NotNull final Consumer commitConsumer) throws VcsException { + Set userRegistry = ContainerUtil.newHashSet(); + List commits = HgHistoryUtil.readAllHashes(myProject, root, new CollectConsumer(userRegistry), + Collections.emptyList()); for (TimedVcsCommit commit : commits) { commitConsumer.consume(commit); } + return new LogDataImpl(readAllRefs(root), userRegistry); } @NotNull @@ -87,16 +94,15 @@ public class HgLogProvider implements VcsLogProvider { } @NotNull - @Override - public Collection readAllRefs(@NotNull VirtualFile root) throws VcsException { + private Set readAllRefs(@NotNull VirtualFile root) throws VcsException { myRepositoryManager.waitUntilInitialized(); if (myProject.isDisposed()) { - return Collections.emptyList(); + return Collections.emptySet(); } HgRepository repository = myRepositoryManager.getRepositoryForRoot(root); if (repository == null) { LOG.error("Repository not found for root " + root); - return Collections.emptyList(); + return Collections.emptySet(); } repository.update(); @@ -106,7 +112,7 @@ public class HgLogProvider implements VcsLogProvider { Collection tags = repository.getTags(); Collection localTags = repository.getLocalTags(); - Collection refs = new ArrayList(branches.size() + bookmarks.size()); + Set refs = new HashSet(branches.size() + bookmarks.size()); for (Map.Entry> entry : branches.entrySet()) { String branchName = entry.getKey(); diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/provider/HgChangeProvider.java b/plugins/hg4idea/src/org/zmlx/hg4idea/provider/HgChangeProvider.java index 02e49354f3ab..14a288171504 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/provider/HgChangeProvider.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/provider/HgChangeProvider.java @@ -17,19 +17,24 @@ import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.project.Project; import com.intellij.openapi.vcs.*; import com.intellij.openapi.vcs.changes.*; +import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.ui.JBColor; +import com.intellij.util.Function; +import com.intellij.util.containers.ContainerUtil; import com.intellij.vcsUtil.VcsUtil; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.zmlx.hg4idea.*; import org.zmlx.hg4idea.command.HgResolveCommand; import org.zmlx.hg4idea.command.HgResolveStatusEnum; import org.zmlx.hg4idea.command.HgStatusCommand; import org.zmlx.hg4idea.command.HgWorkingCopyRevisionsCommand; +import org.zmlx.hg4idea.repo.HgRepository; import org.zmlx.hg4idea.util.HgUtil; -import java.awt.*; +import java.io.File; import java.util.*; -import java.util.List; public class HgChangeProvider implements ChangeProvider { @@ -38,7 +43,7 @@ public class HgChangeProvider implements ChangeProvider { public static final FileStatus COPIED = FileStatusFactory.getInstance().createFileStatus("COPIED", "Copied", FileStatus.COLOR_ADDED); public static final FileStatus RENAMED = FileStatusFactory.getInstance().createFileStatus("RENAMED", "Renamed", - Color.cyan.darker().darker()); + JBColor.CYAN.darker().darker()); private static final EnumMap PROCESSORS = new EnumMap(HgFileStatusEnum.class); @@ -85,21 +90,42 @@ public class HgChangeProvider implements ChangeProvider { final Map list = new HgResolveCommand(myProject).getListSynchronously(repo); hgChanges.addAll(new HgStatusCommand.Builder(true).build(myProject).execute(repo, entry.getValue())); + final HgRepository hgRepo = HgUtil.getRepositoryForFile(myProject, repo); + if (hgRepo != null && hgRepo.hasSubrepos()) { + hgChanges.addAll(ContainerUtil.mapNotNull(hgRepo.getSubrepos(), new Function() { + @Override + public HgChange fun(HgNameWithHashInfo info) { + return findChange(hgRepo, info); + } + })); + } + sendChanges(builder, hgChanges, list, workingRevision, parentRevision); } return hgChanges; } + @Nullable + private HgChange findChange(@NotNull HgRepository hgRepo, @NotNull HgNameWithHashInfo info) { + File file = new File(hgRepo.getRoot().getPath(), info.getName()); + VirtualFile virtualSubrepoFile = VfsUtil.findFileByIoFile(file, false); + HgRepository subrepo = HgUtil.getRepositoryForFile(myProject, virtualSubrepoFile); + if (subrepo != null && !info.getHash().asString().equals(subrepo.getCurrentRevision())) { + return new HgChange(new HgFile(hgRepo.getRoot(), new FilePathImpl(virtualSubrepoFile)), HgFileStatusEnum.MODIFIED); + } + return null; + } + private void sendChanges(ChangelistBuilder builder, Set changes, - Map resolveStatus, HgRevisionNumber workingRevision, - HgRevisionNumber parentRevision) { + Map resolveStatus, HgRevisionNumber workingRevision, + HgRevisionNumber parentRevision) { for (HgChange change : changes) { HgFile afterFile = change.afterFile(); HgFile beforeFile = change.beforeFile(); HgFileStatusEnum status = change.getStatus(); if (resolveStatus.containsKey(afterFile) - && resolveStatus.get(afterFile) == HgResolveStatusEnum.UNRESOLVED) { + && resolveStatus.get(afterFile) == HgResolveStatusEnum.UNRESOLVED) { builder.processChange( new Change( new HgContentRevision(myProject, beforeFile, parentRevision), @@ -286,9 +312,9 @@ public class HgChangeProvider implements ChangeProvider { HgFile afterFile ); - final void processChange(ContentRevision contentRevisionBefore, - ContentRevision contentRevisionAfter, FileStatus fileStatus, - ChangelistBuilder builder, VcsKey vcsKey) { + static void processChange(ContentRevision contentRevisionBefore, + ContentRevision contentRevisionAfter, FileStatus fileStatus, + ChangelistBuilder builder, VcsKey vcsKey) { if (contentRevisionBefore == null && contentRevisionAfter == null) { return; } diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/provider/HgCurrentBinaryContentRevision.java b/plugins/hg4idea/src/org/zmlx/hg4idea/provider/HgCurrentBinaryContentRevision.java index 8be5a7b0a9dd..a11a6c54e0c2 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/provider/HgCurrentBinaryContentRevision.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/provider/HgCurrentBinaryContentRevision.java @@ -14,20 +14,28 @@ package org.zmlx.hg4idea.provider; import com.intellij.openapi.vcs.changes.CurrentBinaryContentRevision; import com.intellij.openapi.vcs.history.VcsRevisionNumber; +import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.NotNull; import org.zmlx.hg4idea.HgFile; -final class HgCurrentBinaryContentRevision extends CurrentBinaryContentRevision { - private VcsRevisionNumber revisionNumber; +final public class HgCurrentBinaryContentRevision extends CurrentBinaryContentRevision { + @NotNull private VcsRevisionNumber myRevisionNumber; + @NotNull private VirtualFile myRepositoryRoot; - HgCurrentBinaryContentRevision(HgFile hgFile, VcsRevisionNumber revisionNumber) { + HgCurrentBinaryContentRevision(@NotNull HgFile hgFile, @NotNull VcsRevisionNumber revisionNumber) { super(hgFile.toFilePath()); - this.revisionNumber = revisionNumber; + myRepositoryRoot = hgFile.getRepo(); + myRevisionNumber = revisionNumber; } @NotNull @Override public VcsRevisionNumber getRevisionNumber() { - return revisionNumber; + return myRevisionNumber; + } + + @NotNull + public VirtualFile getRepositoryRoot() { + return myRepositoryRoot; } } diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/provider/HgCurrentContentRevision.java b/plugins/hg4idea/src/org/zmlx/hg4idea/provider/HgCurrentContentRevision.java index c6c4bf72710b..3c427d8d7db9 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/provider/HgCurrentContentRevision.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/provider/HgCurrentContentRevision.java @@ -34,7 +34,7 @@ class HgCurrentContentRevision extends CurrentContentRevision { return revisionNumber; } - public static ContentRevision create(@NotNull HgFile hgFile, HgRevisionNumber revision) { + public static ContentRevision create(@NotNull HgFile hgFile, @NotNull HgRevisionNumber revision) { VirtualFile virtualFile = VcsUtil.getVirtualFile(hgFile.getFile()); if (virtualFile == null) { return null; diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/provider/commit/HgCheckinEnvironment.java b/plugins/hg4idea/src/org/zmlx/hg4idea/provider/commit/HgCheckinEnvironment.java index 06b74148156e..509e4f82eaff 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/provider/commit/HgCheckinEnvironment.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/provider/commit/HgCheckinEnvironment.java @@ -20,21 +20,21 @@ import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.util.Condition; import com.intellij.openapi.vcs.CheckinProjectPanel; import com.intellij.openapi.vcs.FilePath; import com.intellij.openapi.vcs.VcsException; -import com.intellij.openapi.vcs.changes.Change; -import com.intellij.openapi.vcs.changes.ChangeList; -import com.intellij.openapi.vcs.changes.ContentRevision; -import com.intellij.openapi.vcs.changes.VcsDirtyScopeManager; +import com.intellij.openapi.vcs.changes.*; import com.intellij.openapi.vcs.checkin.CheckinEnvironment; import com.intellij.openapi.vcs.ui.RefreshableOnComponent; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.FunctionUtil; import com.intellij.util.NullableFunction; import com.intellij.util.PairConsumer; +import com.intellij.util.containers.ContainerUtil; import com.intellij.util.ui.UIUtil; import com.intellij.vcsUtil.VcsUtil; +import com.intellij.xml.util.XmlStringUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.zmlx.hg4idea.*; @@ -43,17 +43,23 @@ import org.zmlx.hg4idea.command.*; import org.zmlx.hg4idea.execution.HgCommandException; import org.zmlx.hg4idea.execution.HgCommandExecutor; import org.zmlx.hg4idea.execution.HgCommandResult; +import org.zmlx.hg4idea.provider.HgCurrentBinaryContentRevision; import org.zmlx.hg4idea.repo.HgRepository; -import org.zmlx.hg4idea.repo.HgRepositoryManager; import org.zmlx.hg4idea.util.HgUtil; +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; import java.util.*; +import java.util.List; public class HgCheckinEnvironment implements CheckinEnvironment { private final Project myProject; private boolean myNextCommitIsPushed; private boolean myNextCommitAmend; // If true, the next commit is amended + private boolean myShouldCommitSubrepos; public HgCheckinEnvironment(Project project) { myProject = project; @@ -83,21 +89,21 @@ public class HgCheckinEnvironment implements CheckinEnvironment { @NotNull NullableFunction parametersHolder, Set feedback) { List exceptions = new LinkedList(); - Map> repositoriesMap = getFilesByRepository(changes); - for (Map.Entry> entry : repositoriesMap.entrySet()) { + Map> repositoriesMap = getFilesByRepository(changes); + for (Map.Entry> entry : repositoriesMap.entrySet()) { - VirtualFile repo = entry.getKey(); + HgRepository repo = entry.getKey(); Set selectedFiles = entry.getValue(); + HgCommitCommand command = + new HgCommitCommand(myProject, repo.getRoot(), preparedComment, myNextCommitAmend); - HgCommitCommand command = new HgCommitCommand(myProject, repo, preparedComment, myNextCommitAmend); - - if (isMergeCommit(repo)) { + if (isMergeCommit(repo.getRoot())) { //partial commits are not allowed during merges //verifyResult that all changed files in the repo are selected //If so, commit the entire repository //If not, abort - Set changedFilesNotInCommit = getChangedFilesNotInCommit(repo, selectedFiles); + Set changedFilesNotInCommit = getChangedFilesNotInCommit(repo.getRoot(), selectedFiles); boolean partial = !changedFilesNotInCommit.isEmpty(); @@ -122,6 +128,9 @@ public class HgCheckinEnvironment implements CheckinEnvironment { // so no need to set the files on the command, because then mercurial will complain } else { command.setFiles(selectedFiles); + if (myShouldCommitSubrepos && repo.hasSubrepos()) { + command.setSubrepos(HgUtil.getNamesWithoutHashes(repo.getSubrepos())); + } } try { command.execute(); @@ -134,9 +143,7 @@ public class HgCheckinEnvironment implements CheckinEnvironment { // push if needed if (myNextCommitIsPushed && exceptions.isEmpty()) { - final Set preselectedFiles = repositoriesMap.keySet(); - HgRepositoryManager repositoryManager = HgUtil.getRepositoryManager(myProject); - final List preselectedRepositories = HgActionUtil.collectRepositoriesFromFiles(repositoryManager, preselectedFiles); + final List preselectedRepositories = ContainerUtil.newArrayList(repositoriesMap.keySet()); UIUtil.invokeLaterIfNeeded(new Runnable() { public void run() { new VcsPushDialog(myProject, preselectedRepositories).show(); @@ -154,7 +161,8 @@ public class HgCheckinEnvironment implements CheckinEnvironment { private Set getChangedFilesNotInCommit(VirtualFile repo, Set selectedFiles) { List parents = new HgWorkingCopyRevisionsCommand(myProject).parents(repo); - HgStatusCommand statusCommand = new HgStatusCommand.Builder(true).unknown(false).ignored(false).baseRevision(parents.get(0)).build(myProject); + HgStatusCommand statusCommand = + new HgStatusCommand.Builder(true).unknown(false).ignored(false).baseRevision(parents.get(0)).build(myProject); Set allChangedFilesInRepo = statusCommand.execute(repo); Set filesNotIncluded = new HashSet(); @@ -229,29 +237,29 @@ public class HgCheckinEnvironment implements CheckinEnvironment { } @NotNull - private Map> getFilesByRepository(List changes) { - Map> result = new HashMap>(); + private Map> getFilesByRepository(List changes) { + Map> result = new HashMap>(); for (Change change : changes) { ContentRevision afterRevision = change.getAfterRevision(); ContentRevision beforeRevision = change.getBeforeRevision(); if (afterRevision != null) { - addFile(result, afterRevision.getFile()); + addFile(result, afterRevision); } if (beforeRevision != null) { - addFile(result, beforeRevision.getFile()); + addFile(result, beforeRevision); } } return result; } - private void addFile(Map> result, FilePath filePath) { - if (filePath == null) { - return; - } - - VirtualFile repo = VcsUtil.getVcsRootFor(myProject, filePath); - if (repo == null || filePath.isDirectory()) { + private void addFile(Map> result, ContentRevision contentRevision) { + FilePath filePath = contentRevision.getFile(); + // try to find repository from hgFile from change + HgRepository repo = HgUtil.getRepositoryForFile(myProject, contentRevision instanceof HgCurrentBinaryContentRevision + ? ((HgCurrentBinaryContentRevision)contentRevision).getRepositoryRoot() + : ChangesUtil.findValidParentAccurately(filePath)); + if (repo == null) { return; } @@ -261,7 +269,7 @@ public class HgCheckinEnvironment implements CheckinEnvironment { result.put(repo, hgFiles); } - hgFiles.add(new HgFile(repo, filePath)); + hgFiles.add(new HgFile(repo.getRoot(), filePath)); } public void setNextCommitIsPushed() { @@ -272,27 +280,55 @@ public class HgCheckinEnvironment implements CheckinEnvironment { * Commit options for hg */ private class HgCommitAdditionalComponent extends DvcsCommitAdditionalComponent { + @NotNull private final JCheckBox myCommitSubrepos; public HgCommitAdditionalComponent(@NotNull Project project, @NotNull CheckinProjectPanel panel) { super(project, panel); HgVcs myVcs = HgVcs.getInstance(myProject); myAmend.setEnabled(myVcs != null && myVcs.getVersion().isAmendSupported()); + final Insets insets = new Insets(2, 2, 2, 2); + // add commit subrepos checkbox + GridBagConstraints c = new GridBagConstraints(); + c.anchor = GridBagConstraints.CENTER; + c.insets = insets; + c.gridx = 1; + c.gridy = 2; + c.weightx = 1; + c.fill = GridBagConstraints.HORIZONTAL; + myCommitSubrepos = new JCheckBox("Commit subrepositories", false); + myCommitSubrepos.setToolTipText(XmlStringUtil.wrapInHtml( + "Commit all subrepos for selected repositories.
" + + " hg ci files -S subrepos")); + myCommitSubrepos.setMnemonic('s'); + myPanel.add(myCommitSubrepos, c); + Collection repos = + HgActionUtil.collectRepositoriesFromFiles(HgUtil.getRepositoryManager(myProject), myCheckinPanel.getRoots()); + myCommitSubrepos.setVisible(ContainerUtil.exists(repos, new Condition() { + @Override + public boolean value(HgRepository repository) { + return repository.hasSubrepos(); + } + })); + myCommitSubrepos.addActionListener(new MySelectionListener(myAmend)); + myAmend.addActionListener(new MySelectionListener(myCommitSubrepos)); } @Override public void refresh() { super.refresh(); - myNextCommitAmend = false; + restoreState(); } @Override public void saveState() { myNextCommitAmend = myAmend.isSelected(); + myShouldCommitSubrepos = myCommitSubrepos.isSelected(); } @Override public void restoreState() { myNextCommitAmend = false; + myShouldCommitSubrepos = false; } @NotNull @@ -313,5 +349,25 @@ public class HgCheckinEnvironment implements CheckinEnvironment { HgCommandResult result = commandExecutor.executeInCurrentThread(repo, "log", args); return result == null ? "" : result.getRawOutput(); } + + private class MySelectionListener implements ActionListener { + JCheckBox myUnselectedComponent; + + public MySelectionListener(JCheckBox unselectedComponent) { + myUnselectedComponent = unselectedComponent; + } + + @Override + public void actionPerformed(ActionEvent e) { + JCheckBox source = (JCheckBox)e.getSource(); + if (source.isSelected()) { + myUnselectedComponent.setSelected(false); + myUnselectedComponent.setEnabled(false); + } + else{ + myUnselectedComponent.setEnabled(true); + } + } + } } } diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/push/HgPushSupport.java b/plugins/hg4idea/src/org/zmlx/hg4idea/push/HgPushSupport.java index 13f4aaa08f6a..af584d52c32c 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/push/HgPushSupport.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/push/HgPushSupport.java @@ -15,28 +15,19 @@ */ package org.zmlx.hg4idea.push; -import com.intellij.dvcs.DvcsUtil; import com.intellij.dvcs.push.*; import com.intellij.dvcs.repo.RepositoryManager; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vcs.AbstractVcs; -import com.intellij.ui.SimpleColoredText; -import com.intellij.ui.SimpleTextAttributes; -import com.intellij.util.Function; import com.intellij.util.ObjectUtils; -import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.zmlx.hg4idea.HgVcs; import org.zmlx.hg4idea.repo.HgRepository; import org.zmlx.hg4idea.util.HgUtil; -import java.util.List; - public class HgPushSupport extends PushSupport { - private final static String ENTER_REMOTE = "Enter Remote"; @NotNull private final Project myProject; @NotNull private final HgVcs myVcs; @@ -70,30 +61,13 @@ public class HgPushSupport extends PushSupport getTargetNames(@NotNull HgRepository repository) { - return ContainerUtil.sorted(ContainerUtil.map(repository.getRepositoryConfig().getPaths(), new Function() { - @Override - public String fun(String s) { - return HgUtil.removePasswordIfNeeded(s); - } - })); - } - @NotNull @Override public HgPushSource getSource(@NotNull HgRepository repository) { - String localBranch = HgUtil.getActiveBranchName(repository); + String localBranch = repository.getCurrentBranchName(); return new HgPushSource(localBranch); } - @Override - @NotNull - public HgTarget createTarget(@NotNull HgRepository repository, @NotNull String targetName) { - return new HgTarget(targetName); - } - @NotNull @Override public RepositoryManager getRepositoryManager() { @@ -106,18 +80,8 @@ public class HgPushSupport extends PushSupport createTargetPanel(@NotNull HgRepository repository, @Nullable HgTarget defaultTarget) { + return new HgPushTargetPanel(repository, defaultTarget); } } diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/push/HgPushTargetPanel.java b/plugins/hg4idea/src/org/zmlx/hg4idea/push/HgPushTargetPanel.java new file mode 100644 index 000000000000..e3d02ea4e3fb --- /dev/null +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/push/HgPushTargetPanel.java @@ -0,0 +1,88 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.zmlx.hg4idea.push; + +import com.intellij.dvcs.DvcsUtil; +import com.intellij.dvcs.push.PushTargetPanel; +import com.intellij.dvcs.push.VcsError; +import com.intellij.openapi.ui.ValidationInfo; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.ui.ColoredTreeCellRenderer; +import com.intellij.ui.SimpleTextAttributes; +import com.intellij.ui.TextFieldWithAutoCompletion; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.zmlx.hg4idea.repo.HgRepository; +import org.zmlx.hg4idea.util.HgUtil; + +import java.awt.*; +import java.util.List; + +public class HgPushTargetPanel extends PushTargetPanel { + + private final static String ENTER_REMOTE = "Enter Remote"; + private final HgRepository myRepository; + private final TextFieldWithAutoCompletion myDestTargetPanel; + private String myOldText; + + public HgPushTargetPanel(@NotNull HgRepository repository, @Nullable HgTarget defaultTarget) { + setLayout(new BorderLayout()); + setOpaque(false); + myRepository = repository; + final List targetVariants = HgUtil.getTargetNames(repository); + myOldText = defaultTarget != null ? defaultTarget.getPresentation() : ""; + myDestTargetPanel = new PushTargetTextField(repository.getProject(), targetVariants, myOldText); + add(myDestTargetPanel, BorderLayout.CENTER); + } + + @Override + public void render(@NotNull ColoredTreeCellRenderer renderer) { + String targetText = myDestTargetPanel.getText(); + if (StringUtil.isEmptyOrSpaces(targetText)) { + renderer.append(ENTER_REMOTE, SimpleTextAttributes.GRAY_ITALIC_ATTRIBUTES, this); + } + renderer.append(targetText, SimpleTextAttributes.SYNTHETIC_ATTRIBUTES, this); + } + + @Override + @NotNull + public HgTarget getValue() { + return createValidPushTarget(); + } + + @NotNull + private HgTarget createValidPushTarget() { + return new HgTarget(myDestTargetPanel.getText()); + } + + @Override + public void fireOnCancel() { + myDestTargetPanel.setText(myOldText); + } + + @Override + public void fireOnChange() { + myOldText = myDestTargetPanel.getText(); + } + + @Nullable + public ValidationInfo verify() { + if (StringUtil.isEmptyOrSpaces(myDestTargetPanel.getText())) { + return new ValidationInfo(VcsError.createEmptyTargetError(DvcsUtil.getShortRepositoryName(myRepository)).getText(), this); + } + return null; + } +} diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/push/HgTarget.java b/plugins/hg4idea/src/org/zmlx/hg4idea/push/HgTarget.java index 8f5aca3838ca..87a5d3bf321e 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/push/HgTarget.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/push/HgTarget.java @@ -26,7 +26,6 @@ public class HgTarget implements PushTarget { myTarget = name; } - @Override @NotNull public String getPresentation() { return HgUtil.removePasswordIfNeeded(myTarget); diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/push/PushTargetTextField.java b/plugins/hg4idea/src/org/zmlx/hg4idea/push/PushTargetTextField.java new file mode 100644 index 000000000000..c05cb9133121 --- /dev/null +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/push/PushTargetTextField.java @@ -0,0 +1,63 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.zmlx.hg4idea.push; + +import com.intellij.openapi.editor.ex.EditorEx; +import com.intellij.openapi.project.Project; +import com.intellij.ui.TextFieldWithAutoCompletion; +import com.intellij.ui.TextFieldWithAutoCompletionListProvider; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.ui.UIUtil; +import org.jetbrains.annotations.NotNull; + +import java.awt.event.FocusAdapter; +import java.awt.event.FocusEvent; +import java.util.List; + +public class PushTargetTextField extends TextFieldWithAutoCompletion { + + public PushTargetTextField(@NotNull Project project, @NotNull final List targetVariants, @NotNull String defaultTargetName) { + super(project, getCompletionProvider(targetVariants), true, defaultTargetName); + setBorder(UIUtil.getTableFocusCellHighlightBorder()); + setOneLineMode(true); + addFocusListener(new FocusAdapter() { + @Override + public void focusGained(FocusEvent e) { + selectAll(); + } + }); + } + + @Override + public boolean shouldHaveBorder() { + return false; + } + + @Override + protected void updateBorder(@NotNull final EditorEx editor) { + } + + @NotNull + private static TextFieldWithAutoCompletionListProvider getCompletionProvider(@NotNull final List targetVariants) { + return new StringsCompletionProvider(targetVariants, null) { + @Override + public int compare(String item1, String item2) { + return Integer.valueOf(ContainerUtil.indexOf(targetVariants, item1)).compareTo(ContainerUtil.indexOf(targetVariants, item2)); + } + }; + } + +} diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/repo/HgRepoInfo.java b/plugins/hg4idea/src/org/zmlx/hg4idea/repo/HgRepoInfo.java index f73375f9c791..55efa0b43418 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/repo/HgRepoInfo.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/repo/HgRepoInfo.java @@ -34,6 +34,7 @@ public class HgRepoInfo { @NotNull private Set myBookmarks = Collections.emptySet(); @NotNull private Set myTags = Collections.emptySet(); @NotNull private Set myLocalTags = Collections.emptySet(); + @NotNull Set mySubrepos = Collections.emptySet(); public HgRepoInfo(@NotNull String currentBranch, @Nullable String currentRevision, @@ -43,7 +44,7 @@ public class HgRepoInfo { @NotNull Collection bookmarks, @Nullable String currentBookmark, @NotNull Collection tags, - @NotNull Collection localTags) { + @NotNull Collection localTags, @NotNull Collection subrepos) { myCurrentBranch = currentBranch; myCurrentRevision = currentRevision; myTipRevision = currentTipRevision; @@ -53,6 +54,7 @@ public class HgRepoInfo { myCurrentBookmark = currentBookmark; myTags = new LinkedHashSet(tags); myLocalTags = new LinkedHashSet(localTags); + mySubrepos = new HashSet(subrepos); } @NotNull @@ -116,6 +118,7 @@ public class HgRepoInfo { if (!myBookmarks.equals(info.myBookmarks)) return false; if (!myTags.equals(info.myTags)) return false; if (!myLocalTags.equals(info.myLocalTags)) return false; + if (!mySubrepos.equals(info.mySubrepos)) return false; return true; } @@ -123,7 +126,7 @@ public class HgRepoInfo { @Override public int hashCode() { return Objects.hashCode(myCurrentBranch, myCurrentRevision, myTipRevision, myCurrentBookmark, myState, myBranches, myBookmarks, myTags, - myLocalTags); + myLocalTags, mySubrepos); } @Override @@ -132,4 +135,13 @@ public class HgRepoInfo { return String.format("HgRepository{myCurrentBranch=%s, myCurrentRevision='%s', myState=%s}", myCurrentBranch, myCurrentRevision, myState); } + + public boolean hasSubrepos() { + return !mySubrepos.isEmpty(); + } + + @NotNull + public Collection getSubrepos() { + return mySubrepos; + } } diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/repo/HgRepository.java b/plugins/hg4idea/src/org/zmlx/hg4idea/repo/HgRepository.java index 8a5c4fc81e53..8d3b66f0ba54 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/repo/HgRepository.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/repo/HgRepository.java @@ -71,5 +71,10 @@ public interface HgRepository extends Repository { @NotNull HgConfig getRepositoryConfig(); + boolean hasSubrepos(); + + @NotNull + Collection getSubrepos(); + void updateConfig(); } diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/repo/HgRepositoryImpl.java b/plugins/hg4idea/src/org/zmlx/hg4idea/repo/HgRepositoryImpl.java index b670fb056c0c..6203f259b9bc 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/repo/HgRepositoryImpl.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/repo/HgRepositoryImpl.java @@ -21,6 +21,7 @@ import com.intellij.openapi.Disposable; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vcs.AbstractVcs; import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; @@ -97,6 +98,19 @@ public class HgRepositoryImpl extends RepositoryImpl implements HgRepository { return myInfo.getState(); } + @Nullable + @Override + /** + * Return active bookmark name if exist or heavy branch name otherwise + */ + public String getCurrentBranchName() { + String branchOrBookMarkName = getCurrentBookmark(); + if (StringUtil.isEmptyOrSpaces(branchOrBookMarkName)) { + branchOrBookMarkName = getCurrentBranch(); + } + return branchOrBookMarkName; + } + @Nullable @Override public AbstractVcs getVcs() { @@ -162,6 +176,16 @@ public class HgRepositoryImpl extends RepositoryImpl implements HgRepository { return myConfig; } + @Override + public boolean hasSubrepos() { + return myInfo.hasSubrepos(); + } + + @NotNull + public Collection getSubrepos() { + return myInfo.getSubrepos(); + } + @Override public boolean isFresh() { return myIsFresh; @@ -199,7 +223,8 @@ public class HgRepositoryImpl extends RepositoryImpl implements HgRepository { return new HgRepoInfo(myReader.readCurrentBranch(), myReader.readCurrentRevision(), myReader.readCurrentTipRevision(), myReader.readState(), myReader.readBranches(), - myReader.readBookmarks(), myReader.readCurrentBookmark(), myReader.readTags(), myReader.readLocalTags()); + myReader.readBookmarks(), myReader.readCurrentBookmark(), myReader.readTags(), myReader.readLocalTags(), + myReader.readSubrepos()); } public void updateConfig() { diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/repo/HgRepositoryReader.java b/plugins/hg4idea/src/org/zmlx/hg4idea/repo/HgRepositoryReader.java index 923087d0b3b2..fab661a99a32 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/repo/HgRepositoryReader.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/repo/HgRepositoryReader.java @@ -48,10 +48,10 @@ public class HgRepositoryReader { private static Pattern HASH_NAME = Pattern.compile("\\s*([0-9a-fA-F]+)\\s+(.+)"); private static Pattern HASH_STATUS_NAME = Pattern.compile("\\s*([0-9a-fA-F]+)\\s+\\w\\s+(.+)"); - //hash + name_or_revision_num; hash + status_character + name_or_revision_num + //hash + name_or_revision_num; hash + status_character + name_or_revision_num @NotNull private final File myHgDir; // .hg - @NotNull private File myBranchHeadsFile; // .hg/cache/branch* + part depends on version + @NotNull private File myBranchHeadsFile; // .hg/cache/branch* + part depends on version @NotNull private final File myCacheDir; // .hg/cache (does not exist before first commit) @NotNull private final File myCurrentBranch; // .hg/branch @NotNull private final File myBookmarksFile; //.hg/bookmarks @@ -59,6 +59,8 @@ public class HgRepositoryReader { @NotNull private final File myTagsFile; //.hgtags - not in .hg directory!!! @NotNull private final File myLocalTagsFile; // .hg/localtags @NotNull private final File myDirStateFile; // .hg/dirstate + @NotNull private final File mySubrepoFile; // .hgsubstate + @NotNull private final VcsLogObjectsFactory myVcsObjectsFactory; private final boolean myStatusInBranchFile; @NotNull final HgVcs myVcs; @@ -76,6 +78,7 @@ public class HgRepositoryReader { myCurrentBookmark = new File(myHgDir, "bookmarks.current"); myLocalTagsFile = new File(myHgDir, "localtags"); myTagsFile = new File(myHgDir.getParentFile(), ".hgtags"); + mySubrepoFile = new File(myHgDir.getParentFile(), ".hgsubstate"); myDirStateFile = new File(myHgDir, "dirstate"); myVcsObjectsFactory = ServiceManager.getService(vcs.getProject(), VcsLogObjectsFactory.class); } @@ -192,6 +195,10 @@ public class HgRepositoryReader { return new File(myHgDir, "merge").exists(); } + public boolean hasSubrepos() { + return mySubrepoFile.exists(); + } + public boolean isRebaseInProgress() { return new File(myHgDir, "rebasestate").exists(); } @@ -248,4 +255,11 @@ public class HgRepositoryReader { public String readCurrentBookmark() { return myCurrentBookmark.exists() ? RepositoryUtil.tryLoadFile(myCurrentBookmark) : null; } + + @NotNull + public Collection readSubrepos() { + if (!hasSubrepos()) return Collections.emptySet(); + return readReference(mySubrepoFile); + } + } diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/status/ui/HgStatusWidget.java b/plugins/hg4idea/src/org/zmlx/hg4idea/status/ui/HgStatusWidget.java index 05a62338a9ce..9c89e74f8759 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/status/ui/HgStatusWidget.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/status/ui/HgStatusWidget.java @@ -32,7 +32,7 @@ import org.jetbrains.annotations.Nullable; import org.zmlx.hg4idea.HgProjectSettings; import org.zmlx.hg4idea.HgUpdater; import org.zmlx.hg4idea.HgVcs; -import org.zmlx.hg4idea.action.HgBranchPopup; +import org.zmlx.hg4idea.branch.HgBranchPopup; import org.zmlx.hg4idea.repo.HgRepository; import org.zmlx.hg4idea.util.HgUtil; diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgConfigurationProjectPanel.form b/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgConfigurationProjectPanel.form index 38dc6eb52908..cc1ea793d2c7 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgConfigurationProjectPanel.form +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgConfigurationProjectPanel.form @@ -1,9 +1,9 @@
- + - + @@ -11,7 +11,7 @@ - + @@ -39,16 +39,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + @@ -65,7 +92,7 @@ - + @@ -80,11 +107,6 @@ - - - - - diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgConfigurationProjectPanel.java b/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgConfigurationProjectPanel.java index 2aff9073d266..619536de0f88 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgConfigurationProjectPanel.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgConfigurationProjectPanel.java @@ -12,14 +12,18 @@ // limitations under the License. package org.zmlx.hg4idea.ui; +import com.intellij.dvcs.branch.DvcsBranchSync; +import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.ui.TextFieldWithBrowseButton; +import com.intellij.ui.components.JBCheckBox; import org.jetbrains.annotations.NotNull; import org.zmlx.hg4idea.HgProjectSettings; import org.zmlx.hg4idea.HgVcs; import org.zmlx.hg4idea.HgVcsMessages; +import org.zmlx.hg4idea.repo.HgRepositoryManager; import org.zmlx.hg4idea.util.HgUtil; import org.zmlx.hg4idea.util.HgVersion; @@ -36,6 +40,8 @@ public class HgConfigurationProjectPanel { private JCheckBox myIgnoredWhitespacesInAnnotationsCbx; private TextFieldWithBrowseButton myPathSelector; private JButton myTestButton; + private JBCheckBox mySyncBranchControl; + private JPanel myRepositorySettingsPanel; private final HgVcs myVcs; public HgConfigurationProjectPanel(@NotNull HgProjectSettings projectSettings, @NotNull Project project) { @@ -58,12 +64,15 @@ public class HgConfigurationProjectPanel { ); } }); + final HgRepositoryManager repositoryManager = ServiceManager.getService(project, HgRepositoryManager.class); + myRepositorySettingsPanel.setVisible(repositoryManager != null && repositoryManager.moreThanOneRoot()); } public boolean isModified() { boolean executableModified = !getCurrentPath().equals(myProjectSettings.getHgExecutable()); return executableModified || myCheckIncomingOutgoingCbx.isSelected() != myProjectSettings.isCheckIncomingOutgoing() || + ((myProjectSettings.getSyncSetting() == DvcsBranchSync.SYNC) != mySyncBranchControl.isSelected()) || myIgnoredWhitespacesInAnnotationsCbx.isSelected() != myProjectSettings.isWhitespacesIgnoredInAnnotations(); } @@ -71,6 +80,7 @@ public class HgConfigurationProjectPanel { myProjectSettings.setCheckIncomingOutgoing(myCheckIncomingOutgoingCbx.isSelected()); myProjectSettings.setIgnoreWhitespacesInAnnotations(myIgnoredWhitespacesInAnnotationsCbx.isSelected()); myProjectSettings.setHgExecutable(getCurrentPath()); + myProjectSettings.setSyncSetting(mySyncBranchControl.isSelected() ? DvcsBranchSync.SYNC : DvcsBranchSync.DONT); myVcs.checkVersion(); } @@ -82,6 +92,7 @@ public class HgConfigurationProjectPanel { myCheckIncomingOutgoingCbx.setSelected(myProjectSettings.isCheckIncomingOutgoing()); myIgnoredWhitespacesInAnnotationsCbx.setSelected(myProjectSettings.isWhitespacesIgnoredInAnnotations()); myPathSelector.setText(myProjectSettings.getGlobalSettings().getHgExecutable()); + mySyncBranchControl.setSelected(myProjectSettings.getSyncSetting() == DvcsBranchSync.SYNC); } public JPanel getPanel() { diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/util/HgUtil.java b/plugins/hg4idea/src/org/zmlx/hg4idea/util/HgUtil.java index 3bb14721f0a8..fcf275f70703 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/util/HgUtil.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/util/HgUtil.java @@ -41,6 +41,7 @@ import com.intellij.openapi.wm.StatusBar; import com.intellij.openapi.wm.WindowManager; import com.intellij.openapi.wm.impl.status.StatusBarUtil; import com.intellij.ui.GuiUtils; +import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; import com.intellij.vcsUtil.VcsUtil; import org.jetbrains.annotations.NotNull; @@ -584,16 +585,7 @@ public abstract class HgUtil { if (state != HgRepository.State.NORMAL) { branchText += state.toString() + " "; } - return branchText + getActiveBranchName(repository); - } - - @NotNull - public static String getActiveBranchName(@NotNull HgRepository repository) { - String branchOrBookMarkName = repository.getCurrentBookmark(); - if (StringUtil.isEmptyOrSpaces(branchOrBookMarkName)) { - branchOrBookMarkName = repository.getCurrentBranch(); - } - return branchOrBookMarkName; + return branchText + repository.getCurrentBranchName(); } @NotNull @@ -716,4 +708,14 @@ public abstract class HgUtil { } return Couple.of(userName, email); } + + @NotNull + public static List getTargetNames(@NotNull HgRepository repository) { + return ContainerUtil.sorted(ContainerUtil.map(repository.getRepositoryConfig().getPaths(), new Function() { + @Override + public String fun(String s) { + return removePasswordIfNeeded(s); + } + })); + } } diff --git a/plugins/java-decompiler/engine/.gitattributes b/plugins/java-decompiler/engine/.gitattributes new file mode 100644 index 000000000000..f1df734f9f0b --- /dev/null +++ b/plugins/java-decompiler/engine/.gitattributes @@ -0,0 +1 @@ +*.dec text diff --git a/plugins/java-decompiler/engine/build.xml b/plugins/java-decompiler/engine/build.xml new file mode 100644 index 000000000000..8dde32e6adda --- /dev/null +++ b/plugins/java-decompiler/engine/build.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/java-decompiler/engine/readme.txt b/plugins/java-decompiler/engine/readme.txt new file mode 100644 index 000000000000..b0ea2733f6f3 --- /dev/null +++ b/plugins/java-decompiler/engine/readme.txt @@ -0,0 +1,88 @@ +1. About the decompiler + +Fernflower is the first actually working analytical decompiler for Java and +probably for a high-level programming language in general. Naturally it is still +under development, please send your bug reports and improvement suggestions at +fernflower.decompiler@gmail.com + + +2. License + +http://www.apache.org/licenses/LICENSE-2.0 + + +3. Running from the command line + +java -jar fernflower.jar [-